Compare commits

..
78 Commits
Author SHA1 Message Date
Irmen de Jong a3a6812608 version 7.8 2022-02-12 17:40:32 +01:00
Irmen de Jong 2725c4ad4d slight tweaks to zp and allocator 2022-02-12 00:15:52 +01:00
Irmen de Jong c8cd6e9460 removed old @"screencodes" string encoding syntax (use sc:"hello" instead) 2022-02-11 22:07:14 +01:00
Irmen de Jong 0cd27d6129 fix empty lines in subroutine ast printing 2022-02-11 21:44:38 +01:00
Irmen de Jong b47fc1c020 renames of some Ast node classes 2022-02-11 00:34:36 +01:00
Irmen de Jong de6ef7ef5e doc 2022-02-11 00:16:39 +01:00
Irmen de Jong f95fe8f1da note about removing VarDecls 2022-02-10 23:20:19 +01:00
Irmen de Jong bd0dee5db5 cleanup 2022-02-10 22:22:50 +01:00
Irmen de Jong c13b7fd883 report free/occupied Zeropage space at end of compilation 2022-02-10 21:59:44 +01:00
Irmen de Jong f7e74b3088 naming 2022-02-10 03:18:56 +01:00
Irmen de Jong 343f01d5e1 re-enabled unused variable removal from library modules (+fixed some @shared vars in libraries) 2022-02-10 03:10:47 +01:00
Irmen de Jong 08bacdd090 temp vars are now dynamically added to AST as needed 2022-02-10 02:52:47 +01:00
Irmen de Jong 41b1c80492 label name from memory() no longer interned as string var 2022-02-10 00:45:20 +01:00
Irmen de Jong e5d7316e5d streamlining non-zpvars asmgen using new mechanism 2022-02-10 00:09:09 +01:00
Irmen de Jong b043c3a6da streamlining vars asmgen using new mechanism 2022-02-09 21:58:25 +01:00
Irmen de Jong 98b2855b9c cleanups 2022-02-09 16:35:52 +01:00
Irmen de Jong f3c52c409f variable zp allocation now only done in the allocator 2022-02-08 23:44:21 +01:00
Irmen de Jong 1307bdc612 more cleanups to the allocator 2022-02-08 22:46:49 +01:00
Irmen de Jong 8c2e6971fc start using vars instead of callgraph (2) 2022-02-08 21:09:00 +01:00
Irmen de Jong 1903990f30 start using vars instead of callgraph 2022-02-08 20:40:10 +01:00
Irmen de Jong 7d67005709 more rewrite variable allocation 2022-02-08 20:40:10 +01:00
Irmen de Jong 9acc2f92d1 start to rewrite variable allocation 2022-02-08 20:40:10 +01:00
Irmen de Jong 72dfb0bda3 fix: undefined sys.memcopy when initializing array on cx16 2022-02-08 20:29:47 +01:00
Irmen de Jong 1635612430 tiny tweak in asm optimizer 2022-02-08 02:19:50 +01:00
Irmen de Jong abda837d2f split program structure codegen out of AsmGen into separate class ProgramGen 2022-02-07 00:12:25 +01:00
Irmen de Jong 101fb0b8aa some naming changes and cleanups 2022-02-06 23:14:44 +01:00
Irmen de Jong 10de7dc1f9 fixed the concurrent modification issue on zeropage when running unit tests in parallel, by not having machine targets be static objects 2022-02-06 21:29:06 +01:00
Irmen de Jong d2309b8114 introducing IVariableAllocation (WIP) 2022-02-06 18:57:23 +01:00
Irmen de Jong 6bdd81623f cleaning up AsmGen interface 2022-02-06 17:07:03 +01:00
Irmen de Jong f538c9f0c3 remove bogus double var decl check 2022-02-06 14:04:54 +01:00
Irmen de Jong 8ae3bad6f7 fix rts in empty asmsub 2022-02-06 05:05:58 +01:00
Irmen de Jong 77de99b383 rts-check for non-inlined subroutines + var init adjustment when noreinit, moved out of codegen 2022-02-06 04:03:03 +01:00
Irmen de Jong 312949f336 added experimental codegen backend option 2022-02-05 21:42:03 +01:00
Irmen de Jong 1ab635bd7e small tweak of parse messages 2022-02-05 14:02:24 +01:00
Irmen de Jong b35abd548c less noisy output about what module files are being imported. 2022-02-05 04:25:34 +01:00
Irmen de Jong 30e1c3307c simplify SourceCode: just read the full text immediately. Also optimized imports. 2022-02-05 03:50:54 +01:00
Irmen de Jong 08e052380a comments 2022-02-05 03:14:26 +01:00
Irmen de Jong 0e824c35cc Merge pull request #73 from akumanatt/master
Codegen and runtime library optimizations
2022-02-05 02:21:53 +01:00
Irmen de Jong 548374ac2d fix: do proper sign exension when multiplying signed word and byte vars 2022-02-05 01:52:13 +01:00
Natt Akuma 9ad79fefc9 Merge branch 'master' of https://github.com/irmen/prog8 2022-02-04 22:55:41 +07:00
Natt Akuma 49d37c016e Optimize strcmp_mem 2022-02-04 22:07:03 +07:00
Natt Akuma 7c70c79a84 Optimize in-place word subtraction and negation 2022-02-04 21:21:06 +07:00
Irmen de Jong 6916b8bff7 remove redundant properties 2022-02-03 23:59:24 +01:00
Natt Akuma 73dfb5f443 Optimize sign extension to AY 2022-02-04 00:59:44 +07:00
Irmen de Jong 69b9dfa468 fix invalid recursion warning for code referencing subroutine but not via a call 2022-02-01 23:09:52 +01:00
Irmen de Jong ab61b8ba0a doc ref 2022-02-01 21:47:53 +01:00
Irmen de Jong 5c8c64242f callgraph: nameInAssemblyCode searches smarter (for unused()) 2022-02-01 00:33:05 +01:00
Irmen de Jong ddf96943f0 remove Nop ast node. 2022-01-31 22:36:10 +01:00
Irmen de Jong e773be2f58 remove no longer needed asmSymbol scoping prefixing, now asmSymbolName are identical to asmVarName 2022-01-31 01:47:22 +01:00
Irmen de Jong f965804e6d fix invalid optimization of returning a parameter variable in a subroutine 2022-01-28 16:44:42 +01:00
Irmen de Jong ec078eba72 optimize w=msb(w) => w>>=8, w=lsb(w) ==> w&=$00ff 2022-01-28 16:11:52 +01:00
Irmen de Jong 1815cb1bc3 fixed bug in assembly optimizer removing too many instructions 2022-01-28 15:19:08 +01:00
Irmen de Jong 7b3cd71085 fixed improper optimization of word<<8 and word>>8 2022-01-28 13:54:06 +01:00
Irmen de Jong 06128b5d07 optimize word&=$ff00 and word&=$00ff 2022-01-28 13:40:28 +01:00
Irmen de Jong 990c8e1f18 split out 6502 codegen module from various compilertargets module. 2022-01-28 00:32:09 +01:00
Irmen de Jong a170506356 simplify IdentifierReference equality check back to default (name+pos) 2022-01-27 23:32:55 +01:00
Irmen de Jong 5ecf2a3357 enable more optimizations for typecasted assignments. Fixed missing codegen for assigning bytes to words in certain cases. 2022-01-27 18:05:25 +01:00
Irmen de Jong fa48746ba9 increase internal buffer for diskio.list_files to be able to list larger directories 2022-01-26 03:17:33 +01:00
Irmen de Jong e2b8c069d7 check for missing '&' in string + value expressions (can't just add a value to a string) 2022-01-24 23:30:40 +01:00
Irmen de Jong 14407bd1aa fix memory() existing check typo 2022-01-24 23:21:31 +01:00
Irmen de Jong 08db72903c for long containment checks use a subroutine instead of huge cmp-table 2022-01-24 22:40:22 +01:00
Irmen de Jong 46f9fab140 library API change: string.find now returns index of character + carry bit status (instead of substring address) 2022-01-24 21:37:04 +01:00
Irmen de Jong b7d06f2c0a API change: added alignment parameter to memory() function 2022-01-24 18:58:57 +01:00
Irmen de Jong 118196a0bf library API change: moved cx16.vload() to cx16diskio module 2022-01-24 18:31:18 +01:00
Irmen de Jong 586ce1fc80 tweak return's use of intermediate variable 2022-01-24 01:10:04 +01:00
Irmen de Jong adb979df38 tweak comment 2022-01-23 22:34:05 +01:00
Irmen de Jong 3401cb5b4a fixed compiler recursion crash when returning certain typecasted value 2022-01-23 19:13:20 +01:00
Irmen de Jong ebf1f12e97 inferred type for len() is now more precise 2022-01-23 17:24:39 +01:00
Irmen de Jong 5766208207 fix compiler crash when initializing an array var with another array var 2022-01-23 14:23:34 +01:00
Irmen de Jong 4bf4771f08 fix @requirezp in astToSource. Fix sometimes allocating zeropage variables in normal ram. 2022-01-23 13:42:52 +01:00
Irmen de Jong 0e87db9eb7 fix invalid size copied when initializing arrays in Zeropage 2022-01-23 13:00:01 +01:00
Irmen de Jong 1e053783f3 fix invalid size copied when assigning non-byte arrays 2022-01-23 02:42:36 +01:00
Irmen de Jong 7afc96112b now correctly requires using & (address-of) when assigning the address of a label or subroutine, used to generate invalid code when it was omitted 2022-01-23 02:23:30 +01:00
Irmen de Jong 7bb41a30ed fixed compiler crash when assigning number larger than 65535 2022-01-23 01:44:16 +01:00
Irmen de Jong 3d1b0eb843 fixed compiler crash when using cx16.r0H as function call argument 2022-01-23 01:28:16 +01:00
Irmen de Jong 5b9af0b5ae tweaks 2022-01-21 23:38:54 +01:00
Irmen de Jong 9219ec539d allow "goto pointervar" for indirect jumps 2022-01-21 22:55:59 +01:00
Irmen de Jong c8bd57cd4d fixed signature of mouse_get(): it returns the buttonstatus in A. Added convenience cx16.mouse_pos() routine. 2022-01-21 22:06:17 +01:00
161 changed files with 4278 additions and 3191 deletions

No files matched your search

-6
View File
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Kotlin2JvmCompilerArguments">
<option name="jvmTarget" value="11" />
</component>
</project>
+3 -1
View File
@@ -2,7 +2,9 @@
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/codeGeneration/codeGeneration.iml" filepath="$PROJECT_DIR$/codeGeneration/codeGeneration.iml" />
<module fileurl="file://$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" filepath="$PROJECT_DIR$/codeGenCpu6502/codeGenCpu6502.iml" />
<module fileurl="file://$PROJECT_DIR$/codeGenExperimental6502/codeGenExperimental6502.iml" filepath="$PROJECT_DIR$/codeGenExperimental6502/codeGenExperimental6502.iml" />
<module fileurl="file://$PROJECT_DIR$/codeGenTargets/codeGenTargets.iml" filepath="$PROJECT_DIR$/codeGenTargets/codeGenTargets.iml" />
<module fileurl="file://$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" filepath="$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" />
<module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" />
<module fileurl="file://$PROJECT_DIR$/compilerAst/compilerAst.iml" filepath="$PROJECT_DIR$/compilerAst/compilerAst.iml" />
+3
View File
@@ -12,6 +12,9 @@
* The same argument applies to `IMemSizer`, and - not entirely sure about that - `IBuiltinFunctions`.
#### Steps to take, in conceptual (!) order:
(note: all these steps have been implemented, rejected or otherwise solved now.)
1. introduce an abstraction `SourceCode` that encapsulates the origin and actual loading of Prog8 source code
- from the local file system (use case: user programs)
- from resources (prog8lib)
File renamed without changes.
+16
View File
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="module" module-name="compilerAst" />
<orderEntry type="module" module-name="compilerInterfaces" />
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
</component>
</module>
@@ -1,8 +1,8 @@
package prog8.codegen.target.cpu6502.codegen
package prog8.codegen.cpu6502
import prog8.ast.Program
import prog8.ast.base.VarDeclType
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.VarDecl
import prog8.compilerinterface.IMachineDefinition
@@ -10,7 +10,7 @@ import prog8.compilerinterface.IMachineDefinition
// note: see https://wiki.nesdev.org/w/index.php/6502_assembly_optimisations
fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefinition, program: Program): Int {
internal fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefinition, program: Program): Int {
var numberOfOptimizations = 0
@@ -64,6 +64,11 @@ fun optimizeAssembly(lines: MutableList<String>, machine: IMachineDefinition, pr
return numberOfOptimizations
}
private fun String.isBranch() = this.startsWith("b")
private fun String.isStoreReg() = this.startsWith("sta") || this.startsWith("sty") || this.startsWith("stx")
private fun String.isStoreRegOrZero() = this.isStoreReg() || this.startsWith("stz")
private fun String.isLoadReg() = this.startsWith("lda") || this.startsWith("ldy") || this.startsWith("ldx")
private class Modification(val lineIndex: Int, val remove: Boolean, val replacement: String?)
private fun apply(modifications: List<Modification>, lines: MutableList<String>) {
@@ -196,9 +201,9 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
sta A1
sty A2
*/
if(first.startsWith("st") && second.startsWith("st")
&& third.startsWith("ld") && fourth.startsWith("ld")
&& fifth.startsWith("st") && sixth.startsWith("st")) {
if(first.isStoreReg() && second.isStoreReg()
&& third.isLoadReg() && fourth.isLoadReg()
&& fifth.isStoreReg() && sixth.isStoreReg()) {
val reg1 = first[2]
val reg2 = second[2]
val reg3 = third[2]
@@ -227,8 +232,8 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
lda A1 ; can be removed
ldy A2 ; can be removed if not followed by a branch instuction
*/
if(!overlappingMods && first.startsWith("st") && second.startsWith("st")
&& third.startsWith("ld") && fourth.startsWith("ld")) {
if(!overlappingMods && first.isStoreReg() && second.isStoreReg()
&& third.isLoadReg() && fourth.isLoadReg()) {
val reg1 = first[2]
val reg2 = second[2]
val reg3 = third[2]
@@ -249,13 +254,14 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
}
}
}
/*
sta A1
sty A2
sty A2 ; ... or stz
lda A1 ; can be removed if not followed by a branch instruction
*/
if(!overlappingMods && first.startsWith("st") && second.startsWith("st")
&& third.startsWith("ld") && !fourth.startsWith("b")) {
if(!overlappingMods && first.isStoreReg() && second.isStoreRegOrZero()
&& third.isLoadReg() && !fourth.isBranch()) {
val reg1 = first[2]
val reg3 = third[2]
if(reg1==reg3) {
@@ -276,7 +282,7 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
ldy A1 ; make tay
sta A1 ; remove
*/
if(!overlappingMods && first.startsWith("sta") && second.startsWith("ld")
if(!overlappingMods && first.startsWith("sta") && second.isLoadReg()
&& third.startsWith("sta") && second.length>4) {
val firstvalue = first.substring(4)
val secondvalue = second.substring(4)
@@ -293,10 +299,10 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
}
/*
sta A
sta A
sta A ; or stz double store, remove this first one
sta A ; or stz
*/
if(!overlappingMods && first.startsWith("st") && second.startsWith("st")) {
if(!overlappingMods && first.isStoreRegOrZero() && second.isStoreRegOrZero()) {
if(first[2]==second[2]) {
val firstvalue = first.substring(4)
val secondvalue = second.substring(4)
@@ -304,7 +310,7 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
val address = getAddressArg(first, program)
if(address==null || !machine.isIOAddress(address)) {
overlappingMods = true
mods.add(Modification(lines[1].index, true, null))
mods.add(Modification(lines[0].index, true, null))
}
}
}
@@ -333,7 +339,7 @@ private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>,
) {
val third = lines[3].value.trimStart()
val attemptRemove =
if(third.startsWith("b")) {
if(third.isBranch()) {
// a branch instruction follows, we can only remove the load instruction if
// another load instruction of the same register precedes the store instruction
// (otherwise wrong cpu flags are used)
@@ -400,7 +406,7 @@ private fun getAddressArg(line: String, program: Program): UInt? {
when(decl.type){
VarDeclType.VAR -> null
VarDeclType.CONST,
VarDeclType.MEMORY -> (decl.value as NumericLiteralValue).number.toUInt()
VarDeclType.MEMORY -> (decl.value as NumericLiteral).number.toUInt()
}
}
else null
@@ -1,12 +1,9 @@
package prog8.codegen.target.cbm
package prog8.codegen.cpu6502
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import com.github.michaelbull.result.mapError
import prog8.codegen.target.cpu6502.codegen.generatedLabelPrefix
import prog8.compilerinterface.CompilationOptions
import prog8.compilerinterface.IAssemblyProgram
import prog8.compilerinterface.OutputType
import prog8.compilerinterface.*
import prog8.parser.SourceCode
import java.io.File
import java.nio.file.Path
@@ -14,13 +11,10 @@ import kotlin.io.path.Path
import kotlin.io.path.isRegularFile
internal fun viceMonListName(baseFilename: String) = "$baseFilename.vice-mon-list"
class AssemblyProgram(
override val valid: Boolean,
internal class AssemblyProgram(
override val name: String,
outputDir: Path,
private val compTarget: String) : IAssemblyProgram {
private val compTarget: ICompilationTarget) : IAssemblyProgram {
private val assemblyFile = outputDir.resolve("$name.asm")
private val prgFile = outputDir.resolve("$name.prg")
@@ -28,7 +22,7 @@ class AssemblyProgram(
private val viceMonListFile = outputDir.resolve(viceMonListName(name))
private val listFile = outputDir.resolve("$name.list")
override fun assemble(options: CompilationOptions): Int {
override fun assemble(options: CompilationOptions): Boolean {
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently)
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
"-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
@@ -44,12 +38,12 @@ class AssemblyProgram(
val outFile = when (options.output) {
OutputType.PRG -> {
command.add("--cbm-prg")
println("\nCreating prg for target $compTarget.")
println("\nCreating prg for target ${compTarget.name}.")
prgFile
}
OutputType.RAW -> {
command.add("--nostart")
println("\nCreating raw binary for target $compTarget.")
println("\nCreating raw binary for target ${compTarget.name}.")
binFile
}
}
@@ -61,7 +55,7 @@ class AssemblyProgram(
removeGeneratedLabelsFromMonlist()
generateBreakpointList()
}
return result
return result==0
}
private fun removeGeneratedLabelsFromMonlist() {
@@ -96,13 +90,13 @@ class AssemblyProgram(
internal fun loadAsmIncludeFile(filename: String, source: SourceCode): Result<String, NoSuchFileException> {
return if (filename.startsWith(SourceCode.libraryFilePrefix)) {
return com.github.michaelbull.result.runCatching {
SourceCode.Resource("/prog8lib/${filename.substring(SourceCode.libraryFilePrefix.length)}").readText()
SourceCode.Resource("/prog8lib/${filename.substring(SourceCode.libraryFilePrefix.length)}").text
}.mapError { NoSuchFileException(File(filename)) }
} else {
val sib = Path(source.origin).resolveSibling(filename)
if (sib.isRegularFile())
Ok(SourceCode.File(sib).readText())
Ok(SourceCode.File(sib).text)
else
Ok(SourceCode.File(Path(filename)).readText())
Ok(SourceCode.File(Path(filename)).text)
}
}
@@ -1,4 +1,4 @@
package prog8.codegen.target.cpu6502.codegen
package prog8.codegen.cpu6502
import prog8.ast.IFunctionCall
import prog8.ast.Node
@@ -10,15 +10,17 @@ import prog8.ast.statements.DirectMemoryWrite
import prog8.ast.statements.FunctionCallStatement
import prog8.ast.statements.Subroutine
import prog8.ast.toHex
import prog8.codegen.target.AssemblyError
import prog8.codegen.target.Cx16Target
import prog8.codegen.target.cpu6502.codegen.assignment.*
import prog8.codegen.cpu6502.assignment.*
import prog8.compilerinterface.AssemblyError
import prog8.compilerinterface.BuiltinFunctions
import prog8.compilerinterface.CpuType
import prog8.compilerinterface.FSignature
internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen: AsmGen, private val assignAsmGen: AssignmentAsmGen) {
internal class BuiltinFunctionsAsmGen(private val program: Program,
private val asmgen: AsmGen,
private val assignAsmGen: AssignmentAsmGen,
private val allocations: VariableAllocator) {
internal fun translateFunctioncallExpression(fcall: FunctionCallExpression, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
translateFunctioncall(fcall, func, discardResult = false, resultToStack = resultToStack, resultRegister = resultRegister)
@@ -276,7 +278,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
private fun funcCallFar(fcall: IFunctionCall) {
if(asmgen.options.compTarget !is Cx16Target)
if(asmgen.options.compTarget.name != "cx16")
throw AssemblyError("callfar only works on cx16 target at this time")
val bank = fcall.args[0].constValue(program)?.number?.toInt()
@@ -307,7 +309,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
.byte ${bank.toHex()}
sta ${asmgen.asmVariableName(argAddrArg.identifier)}""")
}
is NumericLiteralValue -> {
is NumericLiteral -> {
asmgen.out("""
lda ${argAddrArg.number.toHex()}
jsr cx16.jsrfar
@@ -321,7 +323,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
private fun funcCallRom(fcall: IFunctionCall) {
if(asmgen.options.compTarget !is Cx16Target)
if(asmgen.options.compTarget.name != "cx16")
throw AssemblyError("callrom only works on cx16 target at this time")
val bank = fcall.args[0].constValue(program)?.number?.toInt()
@@ -360,7 +362,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
pla
sta $01""")
}
is NumericLiteralValue -> {
is NumericLiteral -> {
asmgen.out("""
lda $01
pha
@@ -389,12 +391,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp ${asmgen.asmVariableName(arg2)}")
}
is NumericLiteralValue -> {
is NumericLiteral -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp #${arg2.number.toInt()}")
}
is DirectMemoryRead -> {
if(arg2.addressExpression is NumericLiteralValue) {
if(arg2.addressExpression is NumericLiteral) {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.A)
asmgen.out(" cmp ${arg2.addressExpression.constValue(program)!!.number.toHex()}")
} else {
@@ -423,7 +425,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
cmp ${asmgen.asmVariableName(arg2)}
+""")
}
is NumericLiteralValue -> {
is NumericLiteral -> {
asmgen.assignExpressionToRegister(arg1, RegisterOrPair.AY)
asmgen.out("""
cpy #>${arg2.number.toInt()}
@@ -449,14 +451,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
private fun funcMemory(fcall: IFunctionCall, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
if(discardResult || fcall !is FunctionCallExpression)
throw AssemblyError("should not discard result of memory allocation at $fcall")
val nameRef = fcall.args[0] as IdentifierReference
val name = (nameRef.targetVarDecl(program)!!.value as StringLiteralValue).value
val name = (fcall.args[0] as StringLiteral).value
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name"}
val size = (fcall.args[1] as NumericLiteralValue).number.toUInt()
val size = (fcall.args[1] as NumericLiteral).number.toUInt()
val align = (fcall.args[2] as NumericLiteral).number.toUInt()
val existingSize = asmgen.slabs[name]
if(existingSize!=null && existingSize!=size)
throw AssemblyError("memory slab '$name' already exists with a different size ($size) at ${fcall.position}")
val existing = allocations.getMemorySlab(name)
if(existing!=null && (existing.first!=size || existing.second!=align))
throw AssemblyError("memory slab '$name' already exists with a different size or alignment at ${fcall.position}")
val slabname = IdentifierReference(listOf("prog8_slabs", name), fcall.position)
slabname.linkParents(fcall)
@@ -468,7 +470,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, null, program, asmgen)
val assign = AsmAssignment(src, target, false, program.memsizer, fcall.position)
asmgen.translateNormalAssignment(assign)
asmgen.slabs[name] = size
allocations.allocateMemorySlab(name, size, align)
}
private fun funcSqrt16(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
@@ -580,8 +582,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
asmgen.out(" jsr prog8_lib.ror2_array_ub")
}
is DirectMemoryRead -> {
if (what.addressExpression is NumericLiteralValue) {
val number = (what.addressExpression as NumericLiteralValue).number
if (what.addressExpression is NumericLiteral) {
val number = (what.addressExpression as NumericLiteral).number
asmgen.out(" lda ${number.toHex()} | lsr a | bcc + | ora #\$80 |+ | sta ${number.toHex()}")
} else {
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
@@ -623,8 +625,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
asmgen.out(" jsr prog8_lib.ror_array_ub")
}
is DirectMemoryRead -> {
if (what.addressExpression is NumericLiteralValue) {
val number = (what.addressExpression as NumericLiteralValue).number
if (what.addressExpression is NumericLiteral) {
val number = (what.addressExpression as NumericLiteral).number
asmgen.out(" ror ${number.toHex()}")
} else {
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression)
@@ -681,8 +683,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
asmgen.out(" jsr prog8_lib.rol2_array_ub")
}
is DirectMemoryRead -> {
if (what.addressExpression is NumericLiteralValue) {
val number = (what.addressExpression as NumericLiteralValue).number
if (what.addressExpression is NumericLiteral) {
val number = (what.addressExpression as NumericLiteral).number
asmgen.out(" lda ${number.toHex()} | cmp #\$80 | rol a | sta ${number.toHex()}")
} else {
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
@@ -724,8 +726,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
asmgen.out(" jsr prog8_lib.rol_array_ub")
}
is DirectMemoryRead -> {
if (what.addressExpression is NumericLiteralValue) {
val number = (what.addressExpression as NumericLiteralValue).number
if (what.addressExpression is NumericLiteral) {
val number = (what.addressExpression as NumericLiteral).number
asmgen.out(" rol ${number.toHex()}")
} else {
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression)
@@ -954,8 +956,8 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
// optimized simple case: swap two memory locations
if(first is DirectMemoryRead && second is DirectMemoryRead) {
val addr1 = (first.addressExpression as? NumericLiteralValue)?.number?.toHex()
val addr2 = (second.addressExpression as? NumericLiteralValue)?.number?.toHex()
val addr1 = (first.addressExpression as? NumericLiteral)?.number?.toHex()
val addr2 = (second.addressExpression as? NumericLiteral)?.number?.toHex()
val name1 = if(first.addressExpression is IdentifierReference) asmgen.asmVariableName(first.addressExpression as IdentifierReference) else null
val name2 = if(second.addressExpression is IdentifierReference) asmgen.asmVariableName(second.addressExpression as IdentifierReference) else null
@@ -986,10 +988,10 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
if(pointerVariable != null
&& pointerVariable isSameAs secondExpr.left
&& firstExpr.operator == "+" && secondExpr.operator == "+"
&& (firstOffset is NumericLiteralValue || firstOffset is IdentifierReference || firstOffset is TypecastExpression)
&& (secondOffset is NumericLiteralValue || secondOffset is IdentifierReference || secondOffset is TypecastExpression)
&& (firstOffset is NumericLiteral || firstOffset is IdentifierReference || firstOffset is TypecastExpression)
&& (secondOffset is NumericLiteral || secondOffset is IdentifierReference || secondOffset is TypecastExpression)
) {
if(firstOffset is NumericLiteralValue && secondOffset is NumericLiteralValue) {
if(firstOffset is NumericLiteral && secondOffset is NumericLiteral) {
if(firstOffset!=secondOffset) {
swapArrayValues(
DataType.UBYTE,
@@ -1028,9 +1030,9 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
val elementIDt = first.inferType(program)
val elementDt = elementIDt.getOrElse { throw AssemblyError("unknown dt") }
val firstNum = first.indexer.indexExpr as? NumericLiteralValue
val firstNum = first.indexer.indexExpr as? NumericLiteral
val firstVar = first.indexer.indexExpr as? IdentifierReference
val secondNum = second.indexer.indexExpr as? NumericLiteralValue
val secondNum = second.indexer.indexExpr as? NumericLiteral
val secondVar = second.indexer.indexExpr as? IdentifierReference
if(firstNum!=null && secondNum!=null) {
@@ -1099,7 +1101,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
}
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteralValue, arrayVarName2: String, indexValue2: NumericLiteralValue) {
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteral, arrayVarName2: String, indexValue2: NumericLiteral) {
val index1 = indexValue1.number.toInt() * program.memsizer.memorySize(elementDt)
val index2 = indexValue2.number.toInt() * program.memsizer.memorySize(elementDt)
when(elementDt) {
@@ -1213,7 +1215,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
}
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteralValue, arrayVarName2: String, indexName2: IdentifierReference) {
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteral, arrayVarName2: String, indexName2: IdentifierReference) {
val index1 = indexValue1.number.toInt() * program.memsizer.memorySize(elementDt)
val idxAsmName2 = asmgen.asmVariableName(indexName2)
when(elementDt) {
@@ -1271,7 +1273,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
}
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexName1: IdentifierReference, arrayVarName2: String, indexValue2: NumericLiteralValue) {
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexName1: IdentifierReference, arrayVarName2: String, indexValue2: NumericLiteral) {
val idxAsmName1 = asmgen.asmVariableName(indexName1)
val index2 = indexValue2.number.toInt() * program.memsizer.memorySize(elementDt)
when(elementDt) {
@@ -1382,7 +1384,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
private fun funcPokeW(fcall: IFunctionCall) {
when(val addrExpr = fcall.args[0]) {
is NumericLiteralValue -> {
is NumericLiteral -> {
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AY)
val addr = addrExpr.number.toHex()
asmgen.out(" sta $addr | sty ${addr}+1")
@@ -1413,13 +1415,13 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
}
is BinaryExpression -> {
if(addrExpr.operator=="+" && addrExpr.left is IdentifierReference && addrExpr.right is NumericLiteralValue) {
if(addrExpr.operator=="+" && addrExpr.left is IdentifierReference && addrExpr.right is NumericLiteral) {
val varname = asmgen.asmVariableName(addrExpr.left as IdentifierReference)
if(asmgen.isZpVar(addrExpr.left as IdentifierReference)) {
// pointervar is already in the zero page, no need to copy
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!)
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.AX)
val index = (addrExpr.right as NumericLiteralValue).number.toHex()
val index = (addrExpr.right as NumericLiteral).number.toHex()
asmgen.out("""
ldy #$index
sta ($varname),y
@@ -1441,7 +1443,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
private fun funcPeekW(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
when(val addrExpr = fcall.args[0]) {
is NumericLiteralValue -> {
is NumericLiteral -> {
val addr = addrExpr.number.toHex()
asmgen.out(" lda $addr | ldy ${addr}+1")
}
@@ -1471,11 +1473,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
}
}
is BinaryExpression -> {
if(addrExpr.operator=="+" && addrExpr.left is IdentifierReference && addrExpr.right is NumericLiteralValue) {
if(addrExpr.operator=="+" && addrExpr.left is IdentifierReference && addrExpr.right is NumericLiteral) {
val varname = asmgen.asmVariableName(addrExpr.left as IdentifierReference)
if(asmgen.isZpVar(addrExpr.left as IdentifierReference)) {
// pointervar is already in the zero page, no need to copy
val index = (addrExpr.right as NumericLiteralValue).number.toHex()
val index = (addrExpr.right as NumericLiteral).number.toHex()
asmgen.out("""
ldy #$index
lda ($varname),y
@@ -1522,14 +1524,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
} else {
val reg = resultRegister ?: RegisterOrPair.AY
var needAsave = !(fcall.args[0] is DirectMemoryRead || fcall.args[0] is NumericLiteralValue || fcall.args[0] is IdentifierReference)
var needAsave = !(fcall.args[0] is DirectMemoryRead || fcall.args[0] is NumericLiteral || fcall.args[0] is IdentifierReference)
if(!needAsave) {
val mr0 = fcall.args[0] as? DirectMemoryRead
val mr1 = fcall.args[1] as? DirectMemoryRead
if (mr0 != null)
needAsave = mr0.addressExpression !is NumericLiteralValue && mr0.addressExpression !is IdentifierReference
needAsave = mr0.addressExpression !is NumericLiteral && mr0.addressExpression !is IdentifierReference
if (mr1 != null)
needAsave = needAsave or (mr1.addressExpression !is NumericLiteralValue && mr1.addressExpression !is IdentifierReference)
needAsave = needAsave or (mr1.addressExpression !is NumericLiteral && mr1.addressExpression !is IdentifierReference)
}
when(reg) {
RegisterOrPair.AX -> {
@@ -1572,7 +1574,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
val arg = fcall.args.single()
if (!arg.inferType(program).isWords)
throw AssemblyError("msb required word argument")
if (arg is NumericLiteralValue)
if (arg is NumericLiteral)
throw AssemblyError("msb(const) should have been const-folded away")
if (arg is IdentifierReference) {
val sourceName = asmgen.asmVariableName(arg)
@@ -1583,6 +1585,16 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
null, RegisterOrPair.A -> asmgen.out(" lda $sourceName+1")
RegisterOrPair.X -> asmgen.out(" ldx $sourceName+1")
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName+1")
RegisterOrPair.AX -> asmgen.out(" lda $sourceName+1 | ldx #0")
RegisterOrPair.AY -> asmgen.out(" lda $sourceName+1 | ldy #0")
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName+1 | ldy #0")
in Cx16VirtualRegisters -> {
val regname = resultRegister.name.lowercase()
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" lda $sourceName+1 | sta cx16.$regname | stz cx16.$regname+1")
else
asmgen.out(" lda $sourceName+1 | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
}
else -> throw AssemblyError("invalid reg")
}
}
@@ -1616,7 +1628,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
val arg = fcall.args.single()
if (!arg.inferType(program).isWords)
throw AssemblyError("lsb required word argument")
if (arg is NumericLiteralValue)
if (arg is NumericLiteral)
throw AssemblyError("lsb(const) should have been const-folded away")
if (arg is IdentifierReference) {
@@ -1628,6 +1640,16 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
null, RegisterOrPair.A -> asmgen.out(" lda $sourceName")
RegisterOrPair.X -> asmgen.out(" ldx $sourceName")
RegisterOrPair.Y -> asmgen.out(" ldy $sourceName")
RegisterOrPair.AX -> asmgen.out(" lda $sourceName | ldx #0")
RegisterOrPair.AY -> asmgen.out(" lda $sourceName | ldy #0")
RegisterOrPair.XY -> asmgen.out(" ldx $sourceName | ldy #0")
in Cx16VirtualRegisters -> {
val regname = resultRegister.name.lowercase()
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" lda $sourceName | sta cx16.$regname | stz cx16.$regname+1")
else
asmgen.out(" lda $sourceName | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
}
else -> throw AssemblyError("invalid reg")
}
}
@@ -1688,14 +1710,14 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
val addr = AddressOf(value, value.position)
AsmAssignSource.fromAstSource(addr, program, asmgen)
}
is NumericLiteralValue -> {
is NumericLiteral -> {
throw AssemblyError("float literals should have been converted into autovar")
}
else -> {
if(scope==null)
throw AssemblyError("cannot use float arguments outside of a subroutine scope")
scope.asmGenInfo.usedFloatEvalResultVar2 = true
allocations.subroutineExtra(scope).usedFloatEvalResultVar2 = true
val variable = IdentifierReference(listOf(subroutineFloatEvalResultVar2), value.position)
val addr = AddressOf(variable, value.position)
addr.linkParents(value)
@@ -1,4 +1,4 @@
package prog8.codegen.target.cpu6502.codegen
package prog8.codegen.cpu6502
import prog8.ast.Program
import prog8.ast.base.*
@@ -6,12 +6,14 @@ import prog8.ast.expressions.*
import prog8.ast.statements.BuiltinFunctionPlaceholder
import prog8.ast.statements.Subroutine
import prog8.ast.toHex
import prog8.codegen.target.AssemblyError
import prog8.compilerinterface.AssemblyError
import prog8.compilerinterface.BuiltinFunctions
import prog8.compilerinterface.CpuType
import kotlin.math.absoluteValue
internal class ExpressionsAsmGen(private val program: Program, private val asmgen: AsmGen, private val functioncallAsmGen: FunctionCallAsmGen) {
internal class ExpressionsAsmGen(private val program: Program,
private val asmgen: AsmGen,
private val allocator: VariableAllocator) {
@Deprecated("avoid calling this as it generates slow evalstack based code")
internal fun translateExpression(expression:Expression) {
@@ -34,12 +36,12 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
is TypecastExpression -> translateExpression(expression)
is AddressOf -> translateExpression(expression)
is DirectMemoryRead -> asmgen.translateDirectMemReadExpressionToRegAorStack(expression, true)
is NumericLiteralValue -> translateExpression(expression)
is NumericLiteral -> translateExpression(expression)
is IdentifierReference -> translateExpression(expression)
is FunctionCallExpression -> translateFunctionCallResultOntoStack(expression)
is PipeExpression -> asmgen.translatePipeExpression(expression.expressions, expression,false, true)
is ContainmentCheck -> throw AssemblyError("containment check as complex expression value is not supported")
is ArrayLiteralValue, is StringLiteralValue -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable")
is ArrayLiteral, is StringLiteral -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable")
is RangeExpression -> throw AssemblyError("range expression should have been changed into array values")
is CharLiteral -> throw AssemblyError("charliteral should have been replaced by ubyte using certain encoding")
else -> TODO("missing expression asmgen for $expression")
@@ -212,7 +214,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
asmgen.out(" lda #<$name | sta P8ESTACK_LO,x | lda #>$name | sta P8ESTACK_HI,x | dex")
}
private fun translateExpression(expr: NumericLiteralValue) {
private fun translateExpression(expr: NumericLiteral) {
when(expr.type) {
DataType.UBYTE, DataType.BYTE -> asmgen.out(" lda #${expr.number.toHex()} | sta P8ESTACK_LO,x | dex")
DataType.UWORD, DataType.WORD -> asmgen.out("""
@@ -223,7 +225,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
dex
""")
DataType.FLOAT -> {
val floatConst = asmgen.getFloatAsmConst(expr.number)
val floatConst = allocator.getFloatAsmConst(expr.number)
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float")
}
else -> throw AssemblyError("weird type")
@@ -590,7 +592,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
}
"<" -> {
if(dt==DataType.UBYTE || dt==DataType.UWORD)
return translateExpressionInternal(NumericLiteralValue.fromBoolean(false, expr.position))
return translateExpressionInternal(NumericLiteral.fromBoolean(false, expr.position))
when(dt) {
DataType.BYTE -> asmgen.out(" jsr prog8_lib.lesszero_b")
DataType.WORD -> asmgen.out(" jsr prog8_lib.lesszero_w")
@@ -620,7 +622,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
}
">=" -> {
if(dt==DataType.UBYTE || dt==DataType.UWORD)
return translateExpressionInternal(NumericLiteralValue.fromBoolean(true, expr.position))
return translateExpressionInternal(NumericLiteral.fromBoolean(true, expr.position))
when(dt) {
DataType.BYTE -> asmgen.out(" jsr prog8_lib.greaterequalzero_sb")
DataType.WORD -> asmgen.out(" jsr prog8_lib.greaterequalzero_sw")
@@ -798,7 +800,8 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
"<=" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.lesseq_uw" else " jsr prog8_lib.lesseq_w")
">=" -> asmgen.out(if(dt==DataType.UWORD) " jsr prog8_lib.greatereq_uw" else " jsr prog8_lib.greatereq_w")
"==" -> asmgen.out(" jsr prog8_lib.equal_w")
"!=" -> asmgen.out(" jsr prog8_lib.notequal_w") "&" -> asmgen.out(" jsr prog8_lib.bitand_w")
"!=" -> asmgen.out(" jsr prog8_lib.notequal_w")
"&" -> asmgen.out(" jsr prog8_lib.bitand_w")
"^" -> asmgen.out(" jsr prog8_lib.bitxor_w")
"|" -> asmgen.out(" jsr prog8_lib.bitor_w")
"and" -> asmgen.out(" jsr prog8_lib.and_w")
@@ -1,4 +1,4 @@
package prog8.codegen.target.cpu6502.codegen
package prog8.codegen.cpu6502
import com.github.michaelbull.result.fold
import prog8.ast.Program
@@ -9,10 +9,11 @@ import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.RangeExpression
import prog8.ast.statements.ForLoop
import prog8.ast.toHex
import prog8.codegen.target.AssemblyError
import prog8.compilerinterface.AssemblyError
import prog8.compilerinterface.Zeropage
import kotlin.math.absoluteValue
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen) {
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen, private val zeropage: Zeropage) {
internal fun translate(stmt: ForLoop) {
val iterableDt = stmt.iterable.inferType(program)
@@ -291,7 +292,7 @@ $loopLabel sty $indexVar
}
if(length>=16) {
// allocate index var on ZP if possible
val result = asmgen.zeropage.allocate(listOf(indexVar), DataType.UBYTE, null, stmt.position, asmgen.errors)
val result = zeropage.allocate(listOf(indexVar), DataType.UBYTE, stmt.definingScope, null, null, stmt.position, asmgen.errors)
result.fold(
success = { (address,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
failure = { asmgen.out("$indexVar .byte 0") }
@@ -332,7 +333,7 @@ $loopLabel sty $indexVar
}
if(length>=16) {
// allocate index var on ZP if possible
val result = asmgen.zeropage.allocate(listOf(indexVar), DataType.UBYTE, null, stmt.position, asmgen.errors)
val result = zeropage.allocate(listOf(indexVar), DataType.UBYTE, stmt.definingScope, null, null, stmt.position, asmgen.errors)
result.fold(
success = { (address,_)-> asmgen.out("""$indexVar = $address ; auto zp UBYTE""") },
failure = { asmgen.out("$indexVar .byte 0") }
@@ -1,4 +1,4 @@
package prog8.codegen.target.cpu6502.codegen
package prog8.codegen.cpu6502
import prog8.ast.IFunctionCall
import prog8.ast.Node
@@ -7,13 +7,13 @@ import prog8.ast.base.*
import prog8.ast.expressions.AddressOf
import prog8.ast.expressions.Expression
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.*
import prog8.codegen.target.AssemblyError
import prog8.codegen.target.cpu6502.codegen.assignment.AsmAssignSource
import prog8.codegen.target.cpu6502.codegen.assignment.AsmAssignTarget
import prog8.codegen.target.cpu6502.codegen.assignment.AsmAssignment
import prog8.codegen.target.cpu6502.codegen.assignment.TargetStorageKind
import prog8.codegen.cpu6502.assignment.AsmAssignSource
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
import prog8.codegen.cpu6502.assignment.AsmAssignment
import prog8.codegen.cpu6502.assignment.TargetStorageKind
import prog8.compilerinterface.AssemblyError
import prog8.compilerinterface.CpuType
@@ -96,8 +96,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
// NOTE: *if* there is a return statement, it will be the only one, and the very last statement of the subroutine
// (this condition has been enforced by an ast check earlier)
asmgen.out(" \t; inlined routine follows: ${sub.name}")
val assembly = sub.statements.single() as InlineAssembly
asmgen.translate(assembly)
sub.statements.forEach { asmgen.translate(it as InlineAssembly) }
asmgen.out(" \t; inlined routine end: ${sub.name}")
} else {
asmgen.out(" jsr $subAsmName")
@@ -366,7 +365,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
// this param needs to be set last, right before the jsr
// for now, this is already enforced on the subroutine definition by the Ast Checker
when(value) {
is NumericLiteralValue -> {
is NumericLiteral -> {
val carrySet = value.number.toInt() != 0
asmgen.out(if(carrySet) " sec" else " clc")
}
@@ -1,12 +1,12 @@
package prog8.codegen.target.cpu6502.codegen
package prog8.codegen.cpu6502
import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.PostIncrDecr
import prog8.ast.toHex
import prog8.codegen.target.AssemblyError
import prog8.compilerinterface.AssemblyError
internal class PostIncrDecrAsmGen(private val program: Program, private val asmgen: AsmGen) {
@@ -41,7 +41,7 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
}
targetMemory!=null -> {
when (val addressExpr = targetMemory.addressExpression) {
is NumericLiteralValue -> {
is NumericLiteral -> {
val what = addressExpr.number.toHex()
asmgen.out(if(incr) " inc $what" else " dec $what")
}
@@ -0,0 +1,590 @@
package prog8.codegen.cpu6502
import prog8.ast.INameScope
import prog8.ast.Program
import prog8.ast.antlr.escape
import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.toHex
import prog8.codegen.cpu6502.assignment.AsmAssignTarget
import prog8.codegen.cpu6502.assignment.TargetStorageKind
import prog8.compilerinterface.*
import java.time.LocalDate
import java.time.LocalDateTime
import kotlin.math.absoluteValue
/**
* Generates the main parts of the program:
* - entry/exit code
* - initialization routines
* - blocks
* - subroutines
* - all variables (note: VarDecl ast nodes are *NOT* used anymore for this! now uses IVariablesAndConsts data tables!)
*/
internal class ProgramAndVarsGen(
val program: Program,
val variables: IVariablesAndConsts,
val options: CompilationOptions,
val errors: IErrorReporter,
private val functioncallAsmGen: FunctionCallAsmGen,
private val asmgen: AsmGen,
private val allocator: VariableAllocator,
private val zeropage: Zeropage
) {
private val compTarget = options.compTarget
private val callGraph = CallGraph(program, true)
private val blockVariableInitializers = program.allBlocks.associateWith { it.statements.filterIsInstance<Assignment>() }
internal fun generate() {
val allInitializers = blockVariableInitializers.asSequence().flatMap { it.value }
require(allInitializers.all { it.origin==AssignmentOrigin.VARINIT }) {"all block-level assignments must be a variable initializer"}
allocator.allocateZeropageVariables()
header()
val allBlocks = program.allBlocks
if(allBlocks.first().name != "main")
throw AssemblyError("first block should be 'main'")
if(errors.noErrors()) {
program.allBlocks.forEach { block2asm(it) }
memorySlabs()
footer()
}
}
private fun header() {
val ourName = this.javaClass.name
val cpu = when(compTarget.machine.cpu) {
CpuType.CPU6502 -> "6502"
CpuType.CPU65c02 -> "w65c02"
else -> "unsupported"
}
asmgen.out("; $cpu assembly code for '${program.name}'")
asmgen.out("; generated by $ourName on ${LocalDateTime.now().withNano(0)}")
asmgen.out("; assembler syntax is for the 64tasm cross-assembler")
asmgen.out("; output options: output=${options.output} launcher=${options.launcher} zp=${options.zeropage}")
asmgen.out("")
asmgen.out(".cpu '$cpu'\n.enc 'none'\n")
program.actualLoadAddress = program.definedLoadAddress
if (program.actualLoadAddress == 0u) // fix load address
program.actualLoadAddress = if (options.launcher == LauncherType.BASIC)
compTarget.machine.BASIC_LOAD_ADDRESS else compTarget.machine.RAW_LOAD_ADDRESS
// the global prog8 variables needed
val zp = zeropage
asmgen.out("P8ZP_SCRATCH_B1 = ${zp.SCRATCH_B1}")
asmgen.out("P8ZP_SCRATCH_REG = ${zp.SCRATCH_REG}")
asmgen.out("P8ZP_SCRATCH_W1 = ${zp.SCRATCH_W1} ; word")
asmgen.out("P8ZP_SCRATCH_W2 = ${zp.SCRATCH_W2} ; word")
asmgen.out("P8ESTACK_LO = ${compTarget.machine.ESTACK_LO.toHex()}")
asmgen.out("P8ESTACK_HI = ${compTarget.machine.ESTACK_HI.toHex()}")
when {
options.launcher == LauncherType.BASIC -> {
if (program.actualLoadAddress != options.compTarget.machine.BASIC_LOAD_ADDRESS)
throw AssemblyError("BASIC output must have correct load address")
asmgen.out("; ---- basic program with sys call ----")
asmgen.out("* = ${program.actualLoadAddress.toHex()}")
val year = LocalDate.now().year
asmgen.out(" .word (+), $year")
asmgen.out(" .null $9e, format(' %d ', prog8_entrypoint), $3a, $8f, ' prog8'")
asmgen.out("+\t.word 0")
asmgen.out("prog8_entrypoint\t; assembly code starts here\n")
if(!options.noSysInit)
asmgen.out(" jsr ${compTarget.name}.init_system")
asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
}
options.output == OutputType.PRG -> {
asmgen.out("; ---- program without basic sys call ----")
asmgen.out("* = ${program.actualLoadAddress.toHex()}\n")
if(!options.noSysInit)
asmgen.out(" jsr ${compTarget.name}.init_system")
asmgen.out(" jsr ${compTarget.name}.init_system_phase2")
}
options.output == OutputType.RAW -> {
asmgen.out("; ---- raw assembler program ----")
asmgen.out("* = ${program.actualLoadAddress.toHex()}\n")
}
}
if(options.zeropage !in arrayOf(ZeropageType.BASICSAFE, ZeropageType.DONTUSE)) {
asmgen.out("""
; zeropage is clobbered so we need to reset the machine at exit
lda #>sys.reset_system
pha
lda #<sys.reset_system
pha""")
}
// make sure that on the cx16 and c64, basic rom is banked in again when we exit the program
when(compTarget.name) {
"cx16" -> {
if(options.floats)
asmgen.out(" lda #4 | sta $01") // to use floats, make sure Basic rom is banked in
asmgen.out(" jsr main.start | lda #4 | sta $01 | rts")
}
"c64" -> asmgen.out(" jsr main.start | lda #31 | sta $01 | rts")
else -> asmgen.jmp("main.start")
}
}
private fun memorySlabs() {
asmgen.out("; memory slabs")
asmgen.out("prog8_slabs\t.block")
for((name, info) in allocator.memorySlabs) {
if(info.second>1u)
asmgen.out("\t.align ${info.second.toHex()}")
asmgen.out("$name\t.fill ${info.first}")
}
asmgen.out("\t.bend")
}
private fun footer() {
// the global list of all floating point constants for the whole program
asmgen.out("; global float constants")
for (flt in allocator.globalFloatConsts) {
val floatFill = compTarget.machine.getFloat(flt.key).makeFloatFillAsm()
val floatvalue = flt.key
asmgen.out("${flt.value}\t.byte $floatFill ; float $floatvalue")
}
// program end
asmgen.out("prog8_program_end\t; end of program label for progend()")
}
private fun block2asm(block: Block) {
asmgen.out("")
asmgen.out("; ---- block: '${block.name}' ----")
if(block.address!=null)
asmgen.out("* = ${block.address!!.toHex()}")
else {
if("align_word" in block.options())
asmgen.out("\t.align 2")
else if("align_page" in block.options())
asmgen.out("\t.align $100")
}
asmgen.out("${block.name}\t" + (if("force_output" in block.options()) ".block\n" else ".proc\n"))
asmgen.outputSourceLine(block)
zeropagevars2asm(block)
memdefsAndConsts2asm(block)
asmsubs2asm(block.statements)
nonZpVariables2asm(block)
asmgen.out("")
asmgen.out("; subroutines in this block")
// First translate regular statements, and then put the subroutines at the end.
// (regular statements = everything except the initialization assignments;
// these will be part of the prog8_init_vars init routine generated below)
val initializers = blockVariableInitializers.getValue(block)
val statements = block.statements.filterNot { it in initializers }
val (subroutine, stmts) = statements.partition { it is Subroutine }
stmts.forEach { asmgen.translate(it) }
subroutine.forEach { asmgen.translate(it) }
if(!options.dontReinitGlobals) {
// generate subroutine to initialize block-level (global) variables
if (initializers.isNotEmpty()) {
asmgen.out("prog8_init_vars\t.proc\n")
initializers.forEach { assign -> asmgen.translate(assign) }
asmgen.out(" rts\n .pend")
}
}
asmgen.out(if("force_output" in block.options()) "\n\t.bend\n" else "\n\t.pend\n")
}
internal fun translateSubroutine(sub: Subroutine) {
var onlyVariables = false
if(sub.inline) {
if(options.optimize) {
if(sub.isAsmSubroutine || callGraph.unused(sub))
return
// from an inlined subroutine only the local variables are generated,
// all other code statements are omitted in the subroutine itself
// (they've been inlined at the call site, remember?)
onlyVariables = true
}
}
asmgen.out("")
if(sub.isAsmSubroutine) {
if(sub.asmAddress!=null)
return // already done at the memvars section
// asmsub with most likely just an inline asm in it
asmgen.out("${sub.name}\t.proc")
sub.statements.forEach { asmgen.translate(it) }
asmgen.out(" .pend\n")
} else {
// regular subroutine
asmgen.out("${sub.name}\t.proc")
zeropagevars2asm(sub)
memdefsAndConsts2asm(sub)
asmsubs2asm(sub.statements)
// the main.start subroutine is the program's entrypoint and should perform some initialization logic
if(sub.name=="start" && sub.definingBlock.name=="main")
entrypointInitialization()
if(functioncallAsmGen.optimizeIntArgsViaRegisters(sub)) {
asmgen.out("; simple int arg(s) passed via register(s)")
if(sub.parameters.size==1) {
val dt = sub.parameters[0].type
val target = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, dt, sub, variableAsmName = sub.parameters[0].name)
if(dt in ByteDatatypes)
asmgen.assignRegister(RegisterOrPair.A, target)
else
asmgen.assignRegister(RegisterOrPair.AY, target)
} else {
require(sub.parameters.size==2)
// 2 simple byte args, first in A, second in Y
val target1 = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, sub.parameters[0].type, sub, variableAsmName = sub.parameters[0].name)
val target2 = AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, sub.parameters[1].type, sub, variableAsmName = sub.parameters[1].name)
asmgen.assignRegister(RegisterOrPair.A, target1)
asmgen.assignRegister(RegisterOrPair.Y, target2)
}
}
if(!onlyVariables) {
asmgen.out("; statements")
sub.statements.forEach { asmgen.translate(it) }
}
asmgen.out("; variables")
val asmGenInfo = allocator.subroutineExtra(sub)
for((dt, name, addr) in asmGenInfo.extraVars) {
if(addr!=null)
asmgen.out("$name = $addr")
else when(dt) {
DataType.UBYTE -> asmgen.out("$name .byte 0")
DataType.UWORD -> asmgen.out("$name .word 0")
else -> throw AssemblyError("weird dt")
}
}
if(asmGenInfo.usedRegsaveA) // will probably never occur
asmgen.out("prog8_regsaveA .byte 0")
if(asmGenInfo.usedRegsaveX)
asmgen.out("prog8_regsaveX .byte 0")
if(asmGenInfo.usedRegsaveY)
asmgen.out("prog8_regsaveY .byte 0")
if(asmGenInfo.usedFloatEvalResultVar1)
asmgen.out("$subroutineFloatEvalResultVar1 .byte 0,0,0,0,0")
if(asmGenInfo.usedFloatEvalResultVar2)
asmgen.out("$subroutineFloatEvalResultVar2 .byte 0,0,0,0,0")
nonZpVariables2asm(sub)
asmgen.out(" .pend\n")
}
}
private fun entrypointInitialization() {
asmgen.out("; program startup initialization")
asmgen.out(" cld")
if(!options.dontReinitGlobals) {
blockVariableInitializers.forEach {
if (it.value.isNotEmpty())
asmgen.out(" jsr ${it.key.name}.prog8_init_vars")
}
}
// string and array variables in zeropage that have initializer value, should be initialized
val stringVarsWithInitInZp = allocator.zeropageVars.filter { it.value.dt==DataType.STR && it.value.initialStringValue!=null }
val arrayVarsWithInitInZp = allocator.zeropageVars.filter { it.value.dt in ArrayDatatypes && it.value.initialArrayValue!=null }
if(stringVarsWithInitInZp.isNotEmpty() || arrayVarsWithInitInZp.isNotEmpty()) {
asmgen.out("; zp str and array initializations")
stringVarsWithInitInZp.forEach {
val name = asmgen.asmVariableName(it.key)
asmgen.out("""
lda #<${name}
ldy #>${name}
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<${name}_init_value
ldy #>${name}_init_value
jsr prog8_lib.strcpy""")
}
arrayVarsWithInitInZp.forEach {
val size = it.value.size
val name = asmgen.asmVariableName(it.key)
asmgen.out("""
lda #<${name}_init_value
ldy #>${name}_init_value
sta cx16.r0L
sty cx16.r0H
lda #<${name}
ldy #>${name}
sta cx16.r1L
sty cx16.r1H
lda #<$size
ldy #>$size
jsr sys.memcopy""")
}
asmgen.out(" jmp +")
}
stringVarsWithInitInZp.forEach {
val varname = asmgen.asmVariableName(it.key)+"_init_value"
val stringvalue = it.value.initialStringValue!!
outputStringvar(varname, it.value.dt, stringvalue.encoding, stringvalue.value)
}
arrayVarsWithInitInZp.forEach {
val varname = asmgen.asmVariableName(it.key)+"_init_value"
arrayVariable2asm(varname, it.value.dt, it.value.initialArrayValue!!, null)
}
asmgen.out("""+ tsx
stx prog8_lib.orig_stackpointer ; required for sys.exit()
ldx #255 ; init estack ptr
clv
clc""")
}
private fun zeropagevars2asm(scope: INameScope) {
val zpVariables = allocator.zeropageVars.filter { it.value.originalScope==scope }
for ((scopedName, zpvar) in zpVariables) {
if (scopedName.size == 2 && scopedName[0] == "cx16" && scopedName[1][0] == 'r' && scopedName[1][1].isDigit())
continue // The 16 virtual registers of the cx16 are not actual variables in zp, they're memory mapped
asmgen.out("${scopedName.last()} \t= ${zpvar.address} \t; zp ${zpvar.dt}")
}
}
private fun nonZpVariables2asm(block: Block) {
val variables = variables.blockVars[block]?.filter { !allocator.isZpVar(it.scopedname) } ?: emptyList()
nonZpVariables2asm(variables)
}
private fun nonZpVariables2asm(sub: Subroutine) {
val variables = variables.subroutineVars[sub]?.filter { !allocator.isZpVar(it.scopedname) } ?: emptyList()
nonZpVariables2asm(variables)
}
private fun nonZpVariables2asm(variables: List<IVariablesAndConsts.StaticVariable>) {
asmgen.out("")
asmgen.out("; non-zeropage variables")
val (stringvars, othervars) = variables.partition { it.type==DataType.STR }
stringvars.forEach {
val stringvalue = it.initialValue as StringLiteral
outputStringvar(it.scopedname.last(), it.type, stringvalue.encoding, stringvalue.value)
}
othervars.sortedBy { it.type }.forEach {
staticVariable2asm(it)
}
}
private fun staticVariable2asm(variable: IVariablesAndConsts.StaticVariable) {
val name = variable.scopedname.last()
val value = variable.initialValue
val staticValue: Number =
if(value!=null) {
if(value is NumericLiteral) {
if(value.type== DataType.FLOAT)
value.number
else
value.number.toInt()
} else {
if(variable.type in NumericDatatypes)
throw AssemblyError("can only deal with constant numeric values for global vars")
else 0
}
} else 0
when (variable.type) {
DataType.UBYTE -> asmgen.out("$name\t.byte ${staticValue.toHex()}")
DataType.BYTE -> asmgen.out("$name\t.char $staticValue")
DataType.UWORD -> asmgen.out("$name\t.word ${staticValue.toHex()}")
DataType.WORD -> asmgen.out("$name\t.sint $staticValue")
DataType.FLOAT -> {
if(staticValue==0) {
asmgen.out("$name\t.byte 0,0,0,0,0 ; float")
} else {
val floatFill = compTarget.machine.getFloat(staticValue).makeFloatFillAsm()
asmgen.out("$name\t.byte $floatFill ; float $staticValue")
}
}
DataType.STR -> {
throw AssemblyError("all string vars should have been interned into prog")
}
in ArrayDatatypes -> arrayVariable2asm(name, variable.type, value as? ArrayLiteral, variable.arraysize)
else -> {
throw AssemblyError("weird dt")
}
}
}
private fun arrayVariable2asm(varname: String, dt: DataType, value: ArrayLiteral?, orNumberOfZeros: Int?) {
when(dt) {
DataType.ARRAY_UB -> {
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
if (data.size <= 16)
asmgen.out("$varname\t.byte ${data.joinToString()}")
else {
asmgen.out(varname)
for (chunk in data.chunked(16))
asmgen.out(" .byte " + chunk.joinToString())
}
}
DataType.ARRAY_B -> {
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
if (data.size <= 16)
asmgen.out("$varname\t.char ${data.joinToString()}")
else {
asmgen.out(varname)
for (chunk in data.chunked(16))
asmgen.out(" .char " + chunk.joinToString())
}
}
DataType.ARRAY_UW -> {
val data = makeArrayFillDataUnsigned(dt, value, orNumberOfZeros)
if (data.size <= 16)
asmgen.out("$varname\t.word ${data.joinToString()}")
else {
asmgen.out(varname)
for (chunk in data.chunked(16))
asmgen.out(" .word " + chunk.joinToString())
}
}
DataType.ARRAY_W -> {
val data = makeArrayFillDataSigned(dt, value, orNumberOfZeros)
if (data.size <= 16)
asmgen.out("$varname\t.sint ${data.joinToString()}")
else {
asmgen.out(varname)
for (chunk in data.chunked(16))
asmgen.out(" .sint " + chunk.joinToString())
}
}
DataType.ARRAY_F -> {
val array = value?.value ?:
Array(orNumberOfZeros!!) { defaultZero(ArrayToElementTypes.getValue(dt), Position.DUMMY) }
val floatFills = array.map {
val number = (it as NumericLiteral).number
compTarget.machine.getFloat(number).makeFloatFillAsm()
}
asmgen.out(varname)
for (f in array.zip(floatFills))
asmgen.out(" .byte ${f.second} ; float ${f.first}")
}
else -> throw AssemblyError("require array dt")
}
}
private fun memdefsAndConsts2asm(block: Block) {
val mvs = variables.blockMemvars[block] ?: emptySet()
val consts = variables.blockConsts[block] ?: emptySet()
memdefsAndConsts2asm(mvs, consts)
}
private fun memdefsAndConsts2asm(sub: Subroutine) {
val mvs = variables.subroutineMemvars[sub] ?: emptySet()
val consts = variables.subroutineConsts[sub] ?: emptySet()
memdefsAndConsts2asm(mvs, consts)
}
private fun memdefsAndConsts2asm(
memvars: Set<IVariablesAndConsts.MemoryMappedVariable>,
consts: Set<IVariablesAndConsts.ConstantNumberSymbol>
) {
memvars.forEach {
asmgen.out(" ${it.scopedname.last()} = ${it.address.toHex()}")
}
consts.forEach {
if(it.type==DataType.FLOAT)
asmgen.out(" ${it.scopedname.last()} = ${it.value}")
else
asmgen.out(" ${it.scopedname.last()} = ${it.value.toHex()}")
}
}
private fun asmsubs2asm(statements: List<Statement>) {
statements
.filter { it is Subroutine && it.isAsmSubroutine && it.asmAddress!=null }
.forEach { asmsub ->
asmsub as Subroutine
asmgen.out(" ${asmsub.name} = ${asmsub.asmAddress!!.toHex()}")
}
}
private fun outputStringvar(varname: String, dt: DataType, encoding: Encoding, value: String) {
asmgen.out("$varname\t; $dt $encoding:\"${escape(value).replace("\u0000", "<NULL>")}\"")
val bytes = compTarget.encodeString(value, encoding).plus(0.toUByte())
val outputBytes = bytes.map { "$" + it.toString(16).padStart(2, '0') }
for (chunk in outputBytes.chunked(16))
asmgen.out(" .byte " + chunk.joinToString())
}
private fun makeArrayFillDataUnsigned(dt: DataType, value: ArrayLiteral?, orNumberOfZeros: Int?): List<String> {
val array = value?.value ?:
Array(orNumberOfZeros!!) { defaultZero(ArrayToElementTypes.getValue(dt), Position.DUMMY) }
return when (dt) {
DataType.ARRAY_UB ->
// byte array can never contain pointer-to types, so treat values as all integers
array.map {
val number = (it as NumericLiteral).number.toInt()
"$"+number.toString(16).padStart(2, '0')
}
DataType.ARRAY_UW -> array.map {
when (it) {
is NumericLiteral -> {
"$" + it.number.toInt().toString(16).padStart(4, '0')
}
is AddressOf -> {
asmgen.asmSymbolName(it.identifier)
}
is IdentifierReference -> {
asmgen.asmSymbolName(it)
}
else -> throw AssemblyError("weird array elt dt")
}
}
else -> throw AssemblyError("invalid dt")
}
}
private fun makeArrayFillDataSigned(dt: DataType, value: ArrayLiteral?, orNumberOfZeros: Int?): List<String> {
val array = value?.value ?:
Array(orNumberOfZeros!!) { defaultZero(ArrayToElementTypes.getValue(dt), Position.DUMMY) }
return when (dt) {
DataType.ARRAY_UB ->
// byte array can never contain pointer-to types, so treat values as all integers
array.map {
val number = (it as NumericLiteral).number.toInt()
"$"+number.toString(16).padStart(2, '0')
}
DataType.ARRAY_B ->
// byte array can never contain pointer-to types, so treat values as all integers
array.map {
val number = (it as NumericLiteral).number.toInt()
val hexnum = number.absoluteValue.toString(16).padStart(2, '0')
if(number>=0)
"$$hexnum"
else
"-$$hexnum"
}
DataType.ARRAY_UW -> array.map {
val number = (it as NumericLiteral).number.toInt()
"$" + number.toString(16).padStart(4, '0')
}
DataType.ARRAY_W -> array.map {
val number = (it as NumericLiteral).number.toInt()
val hexnum = number.absoluteValue.toString(16).padStart(4, '0')
if(number>=0)
"$$hexnum"
else
"-$$hexnum"
}
else -> throw AssemblyError("invalid dt")
}
}
}
@@ -0,0 +1,166 @@
package prog8.codegen.cpu6502
import com.github.michaelbull.result.fold
import com.github.michaelbull.result.onSuccess
import prog8.ast.base.ArrayDatatypes
import prog8.ast.base.DataType
import prog8.ast.base.IntegerDatatypes
import prog8.ast.expressions.StringLiteral
import prog8.ast.statements.Subroutine
import prog8.ast.statements.ZeropageWish
import prog8.compilerinterface.*
internal class VariableAllocator(private val vars: IVariablesAndConsts,
private val options: CompilationOptions,
private val errors: IErrorReporter) {
private val zeropage = options.compTarget.machine.zeropage
private val subroutineExtras = mutableMapOf<Subroutine, SubroutineExtraAsmInfo>()
private val memorySlabsInternal = mutableMapOf<String, Pair<UInt, UInt>>()
internal val memorySlabs: Map<String, Pair<UInt, UInt>> = memorySlabsInternal
internal val globalFloatConsts = mutableMapOf<Double, String>() // all float values in the entire program (value -> varname)
internal val zeropageVars: Map<List<String>, Zeropage.ZpAllocation> = zeropage.allocatedVariables
internal fun getMemorySlab(name: String) = memorySlabsInternal[name]
internal fun allocateMemorySlab(name: String, size: UInt, align: UInt) {
memorySlabsInternal[name] = Pair(size, align)
}
/**
* Allocate variables into the Zeropage.
* The result should be retrieved from the current machine's zeropage object!
*/
internal fun allocateZeropageVariables() {
if(options.zeropage== ZeropageType.DONTUSE)
return
val allVariables = (
vars.blockVars.asSequence().flatMap { it.value } +
vars.subroutineVars.asSequence().flatMap { it.value }
).toList()
val numberOfAllocatableVariables = allVariables.size
val varsRequiringZp = allVariables.filter { it.zp == ZeropageWish.REQUIRE_ZEROPAGE }
val varsPreferringZp = allVariables.filter { it.zp == ZeropageWish.PREFER_ZEROPAGE }
val varsDontCare = allVariables.filter { it.zp == ZeropageWish.DONTCARE }
val numberOfExplicitNonZpVariables = allVariables.count { it.zp == ZeropageWish.NOT_IN_ZEROPAGE }
require(varsDontCare.size + varsRequiringZp.size + varsPreferringZp.size + numberOfExplicitNonZpVariables == numberOfAllocatableVariables)
var numVariablesAllocatedInZP: Int = 0
var numberOfNonIntegerVariables: Int = 0
varsRequiringZp.forEach { variable ->
val numElements = numArrayElements(variable)
val result = zeropage.allocate(
variable.scopedname,
variable.type,
variable.scope,
numElements,
variable.initialValue,
variable.position,
errors
)
result.fold(
success = {
numVariablesAllocatedInZP++
},
failure = {
errors.err(it.message!!, variable.position)
}
)
}
if(errors.noErrors()) {
varsPreferringZp.forEach { variable ->
val numElements = numArrayElements(variable)
val result = zeropage.allocate(
variable.scopedname,
variable.type,
variable.scope,
numElements,
variable.initialValue,
variable.position,
errors
)
result.onSuccess { numVariablesAllocatedInZP++ }
// no need to check for allocation error, if there is one, just allocate in normal system ram.
}
// try to allocate any other 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()) {
for (variable in varsDontCare) {
if(variable.type in IntegerDatatypes) {
if(zeropage.free.isEmpty()) {
break
} else {
val numElements = numArrayElements(variable)
val result = zeropage.allocate(
variable.scopedname,
variable.type,
variable.scope,
numElements,
variable.initialValue,
variable.position,
errors
)
result.onSuccess { numVariablesAllocatedInZP++ }
}
} else
numberOfNonIntegerVariables++
}
}
}
println(" number of allocated vars: $numberOfAllocatableVariables")
println(" put into zeropage: $numVariablesAllocatedInZP, non-zp allocatable: ${numberOfNonIntegerVariables+numberOfExplicitNonZpVariables}")
println(" zeropage free space: ${zeropage.free.size} bytes")
}
internal fun isZpVar(scopedName: List<String>) = scopedName in zeropage.allocatedVariables
private fun numArrayElements(variable: IVariablesAndConsts.StaticVariable) =
when(variable.type) {
DataType.STR -> (variable.initialValue as StringLiteral).value.length
in ArrayDatatypes -> variable.arraysize!!
else -> null
}
internal fun subroutineExtra(sub: Subroutine): SubroutineExtraAsmInfo {
var extra = subroutineExtras[sub]
return if(extra==null) {
extra = SubroutineExtraAsmInfo()
subroutineExtras[sub] = extra
extra
}
else
extra
}
internal fun getFloatAsmConst(number: Double): String {
val asmName = globalFloatConsts[number]
if(asmName!=null)
return asmName
val newName = "prog8_float_const_${globalFloatConsts.size}"
globalFloatConsts[number] = newName
return newName
}
}
/**
* This class contains various attributes that influence the assembly code generator.
* Conceptually it should be part of any INameScope.
* But because the resulting code only creates "real" scopes on a subroutine level,
* it's more consistent to only define these attributes on a Subroutine node.
*/
internal class SubroutineExtraAsmInfo {
var usedRegsaveA = false
var usedRegsaveX = false
var usedRegsaveY = false
var usedFloatEvalResultVar1 = false
var usedFloatEvalResultVar2 = false
val extraVars = mutableListOf<Triple<DataType, String, UInt?>>()
}
@@ -1,12 +1,12 @@
package prog8.codegen.target.cpu6502.codegen.assignment
package prog8.codegen.cpu6502.assignment
import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.codegen.cpu6502.AsmGen
import prog8.compilerinterface.AssemblyError
import prog8.compilerinterface.IMemSizer
import prog8.codegen.target.AssemblyError
import prog8.codegen.target.cpu6502.codegen.AsmGen
internal enum class TargetStorageKind {
@@ -120,7 +120,7 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
val array: ArrayIndexedExpression? = null,
val memory: DirectMemoryRead? = null,
val register: RegisterOrPair? = null,
val number: NumericLiteralValue? = null,
val number: NumericLiteral? = null,
val expression: Expression? = null
)
{
@@ -142,9 +142,9 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
return AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, cv.type, number = cv)
return when(value) {
is NumericLiteralValue -> throw AssemblyError("should have been constant value")
is StringLiteralValue -> throw AssemblyError("string literal value should not occur anymore for asm generation")
is ArrayLiteralValue -> throw AssemblyError("array literal value should not occur anymore for asm generation")
is NumericLiteral -> throw AssemblyError("should have been constant value")
is StringLiteral -> throw AssemblyError("string literal value should not occur anymore for asm generation")
is ArrayLiteral -> throw AssemblyError("array literal value should not occur anymore for asm generation")
is IdentifierReference -> {
val parameter = value.targetVarDecl(program)?.subroutineParameter
if(parameter!=null && parameter.definingSubroutine!!.isAsmSubroutine)
@@ -1,20 +1,22 @@
package prog8.codegen.target.cpu6502.codegen.assignment
package prog8.codegen.cpu6502.assignment
import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.toHex
import prog8.codegen.target.AssemblyError
import prog8.codegen.target.cpu6502.codegen.AsmGen
import prog8.codegen.cpu6502.AsmGen
import prog8.codegen.cpu6502.VariableAllocator
import prog8.compilerinterface.AssemblyError
import prog8.compilerinterface.BuiltinFunctions
import prog8.compilerinterface.CpuType
import prog8.compilerinterface.builtinFunctionReturnType
internal class AssignmentAsmGen(private val program: Program, private val asmgen: AsmGen) {
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen)
internal class AssignmentAsmGen(private val program: Program,
private val asmgen: AsmGen,
private val allocator: VariableAllocator) {
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen, allocator)
fun translate(assignment: Assignment) {
val target = AsmAssignTarget.fromAstAssignment(assignment, program, asmgen)
@@ -131,8 +133,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
val value = assign.source.memory!!
when (value.addressExpression) {
is NumericLiteralValue -> {
val address = (value.addressExpression as NumericLiteralValue).number.toUInt()
is NumericLiteral -> {
val address = (value.addressExpression as NumericLiteral).number.toUInt()
assignMemoryByte(assign.target, address, null)
}
is IdentifierReference -> {
@@ -154,7 +156,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
val sourceName = asmgen.asmSymbolName(value.identifier)
assignAddressOf(assign.target, sourceName)
}
is NumericLiteralValue -> throw AssemblyError("source kind should have been literalnumber")
is NumericLiteral -> throw AssemblyError("source kind should have been literalnumber")
is IdentifierReference -> throw AssemblyError("source kind should have been variable")
is ArrayIndexedExpression -> throw AssemblyError("source kind should have been array")
is DirectMemoryRead -> throw AssemblyError("source kind should have been memory")
@@ -291,14 +293,14 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
// for now generate code for this: assign-false; if expr { assign-true }
translateNormalAssignment(
AsmAssignment(
AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, DataType.UBYTE, number=NumericLiteralValue.fromBoolean(false, assign.position)),
AsmAssignSource(SourceStorageKind.LITERALNUMBER, program, asmgen, DataType.UBYTE, number=NumericLiteral.fromBoolean(false, assign.position)),
assign.target, false, program.memsizer, assign.position
)
)
val origTarget = assign.target.origAstTarget
if(origTarget!=null) {
val assignTrue = AnonymousScope(mutableListOf(
Assignment(origTarget, NumericLiteralValue.fromBoolean(true, assign.position), AssignmentOrigin.ASMGEN, assign.position)
Assignment(origTarget, NumericLiteral.fromBoolean(true, assign.position), AssignmentOrigin.ASMGEN, assign.position)
), assign.position)
val assignFalse = AnonymousScope(mutableListOf(), assign.position)
val ifelse = IfElse(value.copy(), assignTrue, assignFalse, assign.position)
@@ -360,9 +362,20 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
when(variable.datatype) {
DataType.STR -> {
require(elementDt.isBytes)
val stringVal = variable.value as StringLiteralValue
val encoded = program.encoding.encodeString(stringVal.value, stringVal.encoding)
return containmentCheckIntoA(containment.element, elementDt.getOr(DataType.UNDEFINED), encoded.map { it.toInt() })
val stringVal = variable.value as StringLiteral
if(stringVal.value.length > ContainmentCheck.max_inlined_string_length) {
// use subroutine
val varname = asmgen.asmVariableName(containment.iterable as IdentifierReference)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname)
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE)
asmgen.out(" ldy #${stringVal.value.length}")
asmgen.out(" jsr prog8_lib.containment_bytearray")
return
} else {
// inline cmp table
val encoded = program.encoding.encodeString(stringVal.value, stringVal.encoding)
return containmentCheckIntoA(containment.element, elementDt.getOr(DataType.UNDEFINED), encoded.map { it.toInt() })
}
}
DataType.ARRAY_F -> {
// require(elementDt istype DataType.FLOAT)
@@ -370,9 +383,32 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
}
in ArrayDatatypes -> {
require(elementDt.isInteger)
val arrayVal = variable.value as ArrayLiteralValue
val values = arrayVal.value.map { it.constValue(program)!!.number.toInt() }
return containmentCheckIntoA(containment.element, elementDt.getOr(DataType.UNDEFINED), values)
val arrayVal = variable.value as ArrayLiteral
val dt = elementDt.getOr(DataType.UNDEFINED)
if(arrayVal.value.size > ContainmentCheck.max_inlined_string_length) {
// use subroutine
val varname = asmgen.asmVariableName(containment.iterable as IdentifierReference)
when(dt) {
in ByteDatatypes -> {
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname)
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE)
asmgen.out(" ldy #${arrayVal.value.size}")
asmgen.out(" jsr prog8_lib.containment_bytearray")
}
in WordDatatypes -> {
assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt.getOr(DataType.UNDEFINED), containment.definingSubroutine)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W2"), varname)
asmgen.out(" ldy #${arrayVal.value.size}")
asmgen.out(" jsr prog8_lib.containment_wordarray")
}
else -> throw AssemblyError("invalid dt")
}
return
} else {
// inline cmp table
val values = arrayVal.value.map { it.constValue(program)!!.number.toInt() }
return containmentCheckIntoA(containment.element, dt, values)
}
}
else -> throw AssemblyError("invalid dt")
}
@@ -380,16 +416,17 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
val varname = asmgen.asmVariableName(containment.iterable as IdentifierReference)
when(variable.datatype) {
DataType.STR -> {
// use subroutine
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname)
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE)
val stringVal = variable.value as StringLiteralValue
val stringVal = variable.value as StringLiteral
asmgen.out(" ldy #${stringVal.value.length}")
asmgen.out(" jsr prog8_lib.containment_bytearray")
return
}
DataType.ARRAY_F -> throw AssemblyError("containment check of floats not supported")
DataType.ARRAY_B, DataType.ARRAY_UB -> {
val arrayVal = variable.value as ArrayLiteralValue
val arrayVal = variable.value as ArrayLiteral
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W1"), varname)
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt istype DataType.BYTE)
asmgen.out(" ldy #${arrayVal.value.size}")
@@ -397,7 +434,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
return
}
DataType.ARRAY_W, DataType.ARRAY_UW -> {
val arrayVal = variable.value as ArrayLiteralValue
val arrayVal = variable.value as ArrayLiteral
assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt.getOr(DataType.UNDEFINED), containment.definingSubroutine)
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.UWORD, containment.definingSubroutine, "P8ZP_SCRATCH_W2"), varname)
asmgen.out(" ldy #${arrayVal.value.size}")
@@ -407,15 +444,19 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
else -> throw AssemblyError("invalid dt")
}
}
val stringVal = containment.iterable as? StringLiteralValue
val stringVal = containment.iterable as? StringLiteral
if(stringVal!=null) {
require(elementDt.isBytes)
if(stringVal.value.length > ContainmentCheck.max_inlined_string_length)
throw AssemblyError("string should have been inlined in if it was this long")
val encoded = program.encoding.encodeString(stringVal.value, stringVal.encoding)
return containmentCheckIntoA(containment.element, elementDt.getOr(DataType.UNDEFINED), encoded.map { it.toInt() })
}
val arrayVal = containment.iterable as? ArrayLiteralValue
val arrayVal = containment.iterable as? ArrayLiteral
if(arrayVal!=null) {
require(elementDt.isInteger)
if(arrayVal.value.size > ContainmentCheck.max_inlined_string_length)
throw AssemblyError("array should have been inlined in if it was this long")
val values = arrayVal.value.map { it.constValue(program)!!.number.toInt() }
return containmentCheckIntoA(containment.element, elementDt.getOr(DataType.UNDEFINED), values)
}
@@ -426,8 +467,6 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
private fun containmentCheckIntoA(element: Expression, dt: DataType, values: List<Number>) {
if(values.size<2)
throw AssemblyError("containment check against 0 or 1 values should have been optimized away")
// TODO don't generate a huge cmp-list when we go over a certain number of values
val containsLabel = asmgen.makeLabel("contains")
when(dt) {
in ByteDatatypes -> {
@@ -508,8 +547,8 @@ $containsLabel lda #1
}
when (value.addressExpression) {
is NumericLiteralValue -> {
val address = (value.addressExpression as NumericLiteralValue).number.toUInt()
is NumericLiteral -> {
val address = (value.addressExpression as NumericLiteral).number.toUInt()
assignMemoryByteIntoWord(target, address, null)
return
}
@@ -531,7 +570,7 @@ $containsLabel lda #1
}
}
}
is NumericLiteralValue -> throw AssemblyError("a cast of a literal value should have been const-folded away")
is NumericLiteral -> throw AssemblyError("a cast of a literal value should have been const-folded away")
else -> {}
}
@@ -609,6 +648,11 @@ $containsLabel lda #1
}
}
if(target.kind==TargetStorageKind.REGISTER) {
assignExpressionToRegister(value, target.register!!, targetDt==DataType.BYTE || targetDt==DataType.WORD)
return
}
if(targetDt==DataType.FLOAT && (target.register==RegisterOrPair.FAC1 || target.register==RegisterOrPair.FAC2)) {
when(valueDt) {
DataType.UBYTE -> {
@@ -1478,7 +1522,7 @@ $containsLabel lda #1
pha
ora #$7f
bmi +
ldx #0
lda #0
+ tax
pla""")
RegisterOrPair.AY -> asmgen.out("""
@@ -1486,7 +1530,7 @@ $containsLabel lda #1
pha
ora #$7f
bmi +
ldy #0
lda #0
+ tay
pla""")
RegisterOrPair.XY -> asmgen.out("""
@@ -1494,9 +1538,19 @@ $containsLabel lda #1
tax
ora #$7f
bmi +
ldy #0
lda #0
+ tay""")
else -> throw AssemblyError("only reg pairs are words")
in Cx16VirtualRegisters -> {
val regname = wordtarget.register.name.lowercase()
asmgen.out("""
lda $sourceName
sta cx16.$regname
ora #$7f
bmi +
lda #0
+ sta cx16.$regname+1""")
}
else -> throw AssemblyError("only reg pairs allowed as word target ${wordtarget.register}")
}
}
TargetStorageKind.STACK -> {
@@ -1547,7 +1601,14 @@ $containsLabel lda #1
RegisterOrPair.AX -> asmgen.out(" ldx #0 | lda $sourceName")
RegisterOrPair.AY -> asmgen.out(" ldy #0 | lda $sourceName")
RegisterOrPair.XY -> asmgen.out(" ldy #0 | ldx $sourceName")
else -> throw AssemblyError("only reg pairs are words")
in Cx16VirtualRegisters -> {
val regname = wordtarget.register.name.lowercase()
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" lda $sourceName | sta cx16.$regname | stz cx16.$regname+1")
else
asmgen.out(" lda $sourceName | sta cx16.$regname | lda #0 | sta cx16.$regname+1")
}
else -> throw AssemblyError("only reg pairs allowed as word target")
}
}
TargetStorageKind.STACK -> {
@@ -2060,7 +2121,7 @@ $containsLabel lda #1
}
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to memory byte")
TargetStorageKind.REGISTER -> {
val floatConst = asmgen.getFloatAsmConst(float)
val floatConst = allocator.getFloatAsmConst(float)
when(target.register!!) {
RegisterOrPair.FAC1 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.MOVFM")
RegisterOrPair.FAC2 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.CONUPK")
@@ -2068,13 +2129,13 @@ $containsLabel lda #1
}
}
TargetStorageKind.STACK -> {
val floatConst = asmgen.getFloatAsmConst(float)
val floatConst = allocator.getFloatAsmConst(float)
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float")
}
}
} else {
// non-zero value
val constFloat = asmgen.getFloatAsmConst(float)
val constFloat = allocator.getFloatAsmConst(float)
when(target.kind) {
TargetStorageKind.VARIABLE -> {
asmgen.out("""
@@ -2117,7 +2178,7 @@ $containsLabel lda #1
}
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to memory byte")
TargetStorageKind.REGISTER -> {
val floatConst = asmgen.getFloatAsmConst(float)
val floatConst = allocator.getFloatAsmConst(float)
when(target.register!!) {
RegisterOrPair.FAC1 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.MOVFM")
RegisterOrPair.FAC2 -> asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.CONUPK")
@@ -2125,7 +2186,7 @@ $containsLabel lda #1
}
}
TargetStorageKind.STACK -> {
val floatConst = asmgen.getFloatAsmConst(float)
val floatConst = allocator.getFloatAsmConst(float)
asmgen.out(" lda #<$floatConst | ldy #>$floatConst | jsr floats.push_float")
}
}
@@ -2281,11 +2342,11 @@ $containsLabel lda #1
private fun storeRegisterAInMemoryAddress(memoryAddress: DirectMemoryWrite) {
val addressExpr = memoryAddress.addressExpression
val addressLv = addressExpr as? NumericLiteralValue
val addressLv = addressExpr as? NumericLiteral
fun storeViaExprEval() {
when(addressExpr) {
is NumericLiteralValue, is IdentifierReference -> {
is NumericLiteral, is IdentifierReference -> {
assignExpressionToVariable(addressExpr, "P8ZP_SCRATCH_W2", DataType.UWORD, null)
asmgen.storeAIntoZpPointerVar("P8ZP_SCRATCH_W2")
}
@@ -1,18 +1,20 @@
package prog8.codegen.target.cpu6502.codegen.assignment
package prog8.codegen.cpu6502.assignment
import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.Subroutine
import prog8.ast.toHex
import prog8.codegen.target.AssemblyError
import prog8.codegen.target.cpu6502.codegen.AsmGen
import prog8.codegen.cpu6502.AsmGen
import prog8.codegen.cpu6502.VariableAllocator
import prog8.compilerinterface.AssemblyError
import prog8.compilerinterface.CpuType
internal class AugmentableAssignmentAsmGen(private val program: Program,
private val assignmentAsmGen: AssignmentAsmGen,
private val asmgen: AsmGen
private val asmgen: AsmGen,
private val allocator: VariableAllocator
) {
fun translate(assign: AsmAssignment) {
require(assign.isAugmentable)
@@ -160,7 +162,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
origValue
}
val valueLv = (value as? NumericLiteralValue)?.number
val valueLv = (value as? NumericLiteral)?.number
val ident = value as? IdentifierReference
val memread = value as? DirectMemoryRead
@@ -173,7 +175,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
ident != null -> inplaceModification_byte_variable_to_variable(target.asmVarname, target.datatype, operator, ident)
memread != null -> inplaceModification_byte_memread_to_variable(target.asmVarname, target.datatype, operator, memread)
value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
inplaceModification_byte_value_to_variable(target.asmVarname, target.datatype, operator, value)
}
else -> inplaceModification_byte_value_to_variable(target.asmVarname, target.datatype, operator, value)
@@ -185,7 +187,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
ident != null -> inplaceModification_word_variable_to_variable(target.asmVarname, target.datatype, operator, ident)
memread != null -> inplaceModification_word_memread_to_variable(target.asmVarname, target.datatype, operator, memread)
value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator))
return
inplaceModification_word_value_to_variable(target.asmVarname, target.datatype, operator, value)
}
else -> inplaceModification_word_value_to_variable(target.asmVarname, target.datatype, operator, value)
@@ -196,7 +199,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
valueLv != null -> inplaceModification_float_litval_to_variable(target.asmVarname, operator, valueLv.toDouble(), target.scope!!)
ident != null -> inplaceModification_float_variable_to_variable(target.asmVarname, operator, ident, target.scope!!)
value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
inplaceModification_float_value_to_variable(target.asmVarname, operator, value, target.scope!!)
}
else -> inplaceModification_float_value_to_variable(target.asmVarname, operator, value, target.scope!!)
@@ -208,15 +211,15 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
TargetStorageKind.MEMORY -> {
val memory = target.memory!!
when (memory.addressExpression) {
is NumericLiteralValue -> {
val addr = (memory.addressExpression as NumericLiteralValue).number.toInt()
is NumericLiteral -> {
val addr = (memory.addressExpression as NumericLiteral).number.toInt()
// re-use code to assign a variable, instead this time, use a direct memory address
when {
valueLv != null -> inplaceModification_byte_litval_to_variable(addr.toHex(), DataType.UBYTE, operator, valueLv.toInt())
ident != null -> inplaceModification_byte_variable_to_variable(addr.toHex(), DataType.UBYTE, operator, ident)
memread != null -> inplaceModification_byte_memread_to_variable(addr.toHex(), DataType.UBYTE, operator, value)
value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
inplaceModification_byte_value_to_variable(addr.toHex(), DataType.UBYTE, operator, value)
}
else -> inplaceModification_byte_value_to_variable(addr.toHex(), DataType.UBYTE, operator, value)
@@ -228,7 +231,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
valueLv != null -> inplaceModification_byte_litval_to_pointer(pointer, operator, valueLv.toInt())
ident != null -> inplaceModification_byte_variable_to_pointer(pointer, operator, ident)
value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
inplaceModification_byte_value_to_pointer(pointer, operator, value)
}
else -> inplaceModification_byte_value_to_pointer(pointer, operator, value)
@@ -243,7 +246,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
ident != null -> inplaceModification_byte_variable_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, ident)
memread != null -> inplaceModification_byte_memread_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, memread)
value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value)
}
else -> inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value)
@@ -254,7 +257,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
}
TargetStorageKind.ARRAY -> {
with(target.array!!.indexer) {
val indexNum = indexExpr as? NumericLiteralValue
val indexNum = indexExpr as? NumericLiteral
val indexVar = indexExpr as? IdentifierReference
when {
indexNum!=null -> {
@@ -266,7 +269,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
ident != null -> inplaceModification_byte_variable_to_variable(targetVarName, target.datatype, operator, ident)
memread != null -> inplaceModification_byte_memread_to_variable(targetVarName, target.datatype, operator, memread)
value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
inplaceModification_byte_value_to_variable(targetVarName, target.datatype, operator, value)
}
else -> inplaceModification_byte_value_to_variable(targetVarName, target.datatype, operator, value)
@@ -278,7 +281,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
ident != null -> inplaceModification_word_variable_to_variable(targetVarName, target.datatype, operator, ident)
memread != null -> inplaceModification_word_memread_to_variable(targetVarName, target.datatype, operator, memread)
value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
inplaceModification_word_value_to_variable(targetVarName, target.datatype, operator, value)
}
else -> inplaceModification_word_value_to_variable(targetVarName, target.datatype, operator, value)
@@ -289,7 +292,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
valueLv != null -> inplaceModification_float_litval_to_variable(targetVarName, operator, valueLv.toDouble(), target.scope!!)
ident != null -> inplaceModification_float_variable_to_variable(targetVarName, operator, ident, target.scope!!)
value is TypecastExpression -> {
if (tryRemoveRedundantCast(value, target, operator)) return
if (tryInplaceModifyWithRemovedRedundantCast(value, target, operator)) return
inplaceModification_float_value_to_variable(targetVarName, operator, value, target.scope!!)
}
else -> inplaceModification_float_value_to_variable(targetVarName, operator, value, target.scope!!)
@@ -348,7 +351,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
}
}
private fun tryRemoveRedundantCast(value: TypecastExpression, target: AsmAssignTarget, operator: String): Boolean {
private fun tryInplaceModifyWithRemovedRedundantCast(value: TypecastExpression, target: AsmAssignTarget, operator: String): Boolean {
if (target.datatype == value.type) {
val childIDt = value.expression.inferType(program)
val childDt = childIDt.getOrElse { throw AssemblyError("unknown dt") }
@@ -1066,6 +1069,18 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
else
asmgen.out(" lda #0 | sta $name | sta $name+1")
}
value == 0x00ff -> {
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz $name+1")
else
asmgen.out(" lda #0 | sta $name+1")
}
value == 0xff00 -> {
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz $name")
else
asmgen.out(" lda #0 | sta $name")
}
value and 255 == 0 -> {
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz $name")
@@ -1145,33 +1160,38 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
+""")
else
asmgen.out("""
ldy #0
ldy #255
lda $otherName
bpl +
dey ; sign extend
+ sty P8ZP_SCRATCH_B1
lda $name
iny ; sign extend
+ eor #255
sec
sbc $otherName
adc $name
sta $name
lda $name+1
sbc P8ZP_SCRATCH_B1
tya
adc $name+1
sta $name+1""")
}
"*" -> {
asmgen.out(" lda $otherName | sta P8ZP_SCRATCH_W1")
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz P8ZP_SCRATCH_W1+1")
else
asmgen.out(" lda #0 | sta P8ZP_SCRATCH_W1+1")
if(valueDt==DataType.UBYTE) {
asmgen.out(" lda $otherName | sta P8ZP_SCRATCH_W1")
if(asmgen.isTargetCpu(CpuType.CPU65c02))
asmgen.out(" stz P8ZP_SCRATCH_W1+1")
else
asmgen.out(" lda #0 | sta P8ZP_SCRATCH_W1+1")
} else {
asmgen.out(" lda $otherName")
asmgen.signExtendAYlsb(valueDt)
asmgen.out(" sta P8ZP_SCRATCH_W1 | sty P8ZP_SCRATCH_W1+1")
}
asmgen.out("""
lda $name
ldy $name+1
jsr math.multiply_words
lda math.multiply_words.result
sta $name
lda math.multiply_words.result+1
sta $name+1""")
lda $name
ldy $name+1
jsr math.multiply_words
lda math.multiply_words.result
sta $name
lda math.multiply_words.result+1
sta $name+1""")
}
"/" -> {
if(dt==DataType.UWORD) {
@@ -1427,29 +1447,28 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
sta $name+1""")
}
"-" -> {
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_REG", valueDt, null)
asmgen.assignExpressionToVariable(value, "P8ZP_SCRATCH_B1", valueDt, null)
if(valueDt==DataType.UBYTE)
asmgen.out("""
lda $name
sec
sbc P8ZP_SCRATCH_REG
sbc P8ZP_SCRATCH_B1
sta $name
bcs +
dec $name+1
+""")
else
asmgen.out("""
ldy #0
lda P8ZP_SCRATCH_REG
ldy #255
lda P8ZP_SCRATCH_B1
bpl +
dey ; sign extend
+ sty P8ZP_SCRATCH_B1
lda $name
iny ; sign extend
+ eor #255
sec
sbc P8ZP_SCRATCH_REG
adc $name
sta $name
lda $name+1
sbc P8ZP_SCRATCH_B1
tya
adc $name+1
sta $name+1""")
}
"*" -> {
@@ -1628,7 +1647,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
asmgen.saveRegisterLocal(CpuRegister.X, scope)
when (operator) {
"**" -> {
if(asmgen.haveFPWR()) {
if(asmgen.haveFPWRcall()) {
asmgen.out("""
lda #<$name
ldy #>$name
@@ -1701,11 +1720,11 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
}
private fun inplaceModification_float_litval_to_variable(name: String, operator: String, value: Double, scope: Subroutine) {
val constValueName = asmgen.getFloatAsmConst(value)
val constValueName = allocator.getFloatAsmConst(value)
asmgen.saveRegisterLocal(CpuRegister.X, scope)
when (operator) {
"**" -> {
if(asmgen.haveFPWR()) {
if(asmgen.haveFPWRcall()) {
asmgen.out("""
lda #<$name
ldy #>$name
@@ -1850,8 +1869,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
TargetStorageKind.MEMORY -> {
val mem = target.memory!!
when (mem.addressExpression) {
is NumericLiteralValue -> {
val addr = (mem.addressExpression as NumericLiteralValue).number.toHex()
is NumericLiteral -> {
val addr = (mem.addressExpression as NumericLiteral).number.toHex()
asmgen.out("""
lda $addr
beq +
@@ -1978,8 +1997,8 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
TargetStorageKind.MEMORY -> {
val memory = target.memory!!
when (memory.addressExpression) {
is NumericLiteralValue -> {
val addr = (memory.addressExpression as NumericLiteralValue).number.toHex()
is NumericLiteral -> {
val addr = (memory.addressExpression as NumericLiteral).number.toHex()
asmgen.out("""
lda $addr
eor #255
@@ -2086,40 +2105,38 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
when(target.register!!) { //P8ZP_SCRATCH_REG
RegisterOrPair.AX -> {
asmgen.out("""
sta P8ZP_SCRATCH_REG
stx P8ZP_SCRATCH_REG+1
lda #0
sec
sbc P8ZP_SCRATCH_REG
eor #255
adc #0
pha
lda #0
sbc P8ZP_SCRATCH_REG+1
txa
eor #255
adc #0
tax
pla""")
}
RegisterOrPair.AY -> {
asmgen.out("""
sta P8ZP_SCRATCH_REG
sty P8ZP_SCRATCH_REG+1
lda #0
sec
sbc P8ZP_SCRATCH_REG
eor #255
adc #0
pha
lda #0
sbc P8ZP_SCRATCH_REG+1
tya
eor #255
adc #0
tay
pla""")
}
RegisterOrPair.XY -> {
asmgen.out("""
stx P8ZP_SCRATCH_REG
sty P8ZP_SCRATCH_REG+1
lda #0
sec
sbc P8ZP_SCRATCH_REG
txa
eor #255
adc #0
tax
lda #0
sbc P8ZP_SCRATCH_REG+1
tya
eor #255
adc #0
tay""")
}
in Cx16VirtualRegisters -> throw AssemblyError("cx16 virtual regs should be variables, not real registers")
+46
View File
@@ -0,0 +1,46 @@
plugins {
id 'java'
id 'application'
id "org.jetbrains.kotlin.jvm"
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(javaVersion)
}
}
compileKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
dependencies {
implementation project(':compilerInterfaces')
implementation project(':compilerAst')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.12"
}
sourceSets {
main {
java {
srcDirs = ["${project.projectDir}/src"]
}
resources {
srcDirs = ["${project.projectDir}/res"]
}
}
}
// note: there are no unit tests in this module!
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="module" module-name="compilerAst" />
<orderEntry type="module" module-name="compilerInterfaces" />
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
</component>
</module>
@@ -0,0 +1,18 @@
package prog8.codegen.experimental6502
import prog8.ast.Program
import prog8.compilerinterface.*
class AsmGen(internal val program: Program,
internal val errors: IErrorReporter,
internal val variables: IVariablesAndConsts,
internal val options: CompilationOptions): IAssemblyGenerator {
override fun compileToAssembly(): IAssemblyProgram? {
println("\n** experimental 65(c)02 code generator **\n")
println("..todo: create assembly code into ${options.outputDir.toAbsolutePath()}..")
return AssemblyProgram("dummy")
}
}
@@ -0,0 +1,13 @@
package prog8.codegen.experimental6502
import prog8.compilerinterface.CompilationOptions
import prog8.compilerinterface.IAssemblyProgram
internal class AssemblyProgram(override val name: String) : IAssemblyProgram
{
override fun assemble(options: CompilationOptions): Boolean {
println("..todo: assemble code into binary..")
return false
}
}
+46
View File
@@ -0,0 +1,46 @@
plugins {
id 'java'
id 'application'
id "org.jetbrains.kotlin.jvm"
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(javaVersion)
}
}
compileKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = javaVersion
}
}
dependencies {
implementation project(':compilerInterfaces')
implementation project(':compilerAst')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.12"
}
sourceSets {
main {
java {
srcDirs = ["${project.projectDir}/src"]
}
resources {
srcDirs = ["${project.projectDir}/res"]
}
}
}
// note: there are no unit tests in this module!
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,27 @@
package prog8.codegen.target
import prog8.ast.expressions.Expression
import prog8.ast.statements.RegisterOrStatusflag
import prog8.ast.statements.Subroutine
import prog8.codegen.target.c128.C128MachineDefinition
import prog8.codegen.target.cbm.CbmMemorySizer
import prog8.codegen.target.cbm.asmsub6502ArgsEvalOrder
import prog8.codegen.target.cbm.asmsub6502ArgsHaveRegisterClobberRisk
import prog8.compilerinterface.ICompilationTarget
import prog8.compilerinterface.IMemSizer
import prog8.compilerinterface.IStringEncoding
class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
override val name = NAME
override val machine = C128MachineDefinition()
companion object {
const val NAME = "c128"
}
override fun asmsubArgsEvalOrder(sub: Subroutine): List<Int> =
asmsub6502ArgsEvalOrder(sub)
override fun asmsubArgsHaveRegisterClobberRisk(args: List<Expression>, paramRegisters: List<RegisterOrStatusflag>) =
asmsub6502ArgsHaveRegisterClobberRisk(args, paramRegisters)
}
@@ -0,0 +1,27 @@
package prog8.codegen.target
import prog8.ast.expressions.Expression
import prog8.ast.statements.RegisterOrStatusflag
import prog8.ast.statements.Subroutine
import prog8.codegen.target.c64.C64MachineDefinition
import prog8.codegen.target.cbm.CbmMemorySizer
import prog8.codegen.target.cbm.asmsub6502ArgsEvalOrder
import prog8.codegen.target.cbm.asmsub6502ArgsHaveRegisterClobberRisk
import prog8.compilerinterface.ICompilationTarget
import prog8.compilerinterface.IMemSizer
import prog8.compilerinterface.IStringEncoding
class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
override val name = NAME
override val machine = C64MachineDefinition()
companion object {
const val NAME = "c64"
}
override fun asmsubArgsEvalOrder(sub: Subroutine): List<Int> =
asmsub6502ArgsEvalOrder(sub)
override fun asmsubArgsHaveRegisterClobberRisk(args: List<Expression>, paramRegisters: List<RegisterOrStatusflag>) =
asmsub6502ArgsHaveRegisterClobberRisk(args, paramRegisters)
}
@@ -0,0 +1,27 @@
package prog8.codegen.target
import prog8.ast.expressions.Expression
import prog8.ast.statements.RegisterOrStatusflag
import prog8.ast.statements.Subroutine
import prog8.codegen.target.cbm.CbmMemorySizer
import prog8.codegen.target.cbm.asmsub6502ArgsEvalOrder
import prog8.codegen.target.cbm.asmsub6502ArgsHaveRegisterClobberRisk
import prog8.codegen.target.cx16.CX16MachineDefinition
import prog8.compilerinterface.ICompilationTarget
import prog8.compilerinterface.IMemSizer
import prog8.compilerinterface.IStringEncoding
class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by CbmMemorySizer {
override val name = NAME
override val machine = CX16MachineDefinition()
companion object {
const val NAME = "cx16"
}
override fun asmsubArgsEvalOrder(sub: Subroutine): List<Int> =
asmsub6502ArgsEvalOrder(sub)
override fun asmsubArgsHaveRegisterClobberRisk(args: List<Expression>, paramRegisters: List<RegisterOrStatusflag>) =
asmsub6502ArgsHaveRegisterClobberRisk(args, paramRegisters)
}
@@ -1,9 +1,7 @@
package prog8.codegen.target.c128
import prog8.ast.base.DataType
import prog8.codegen.target.c64.normal6502instructions
import prog8.codegen.target.cbm.Mflpt5
import prog8.codegen.target.cbm.viceMonListName
import prog8.compilerinterface.*
import java.io.IOException
import java.nio.file.Path
@@ -1,8 +1,6 @@
package prog8.codegen.target.c64
import prog8.ast.base.DataType
import prog8.codegen.target.cbm.Mflpt5
import prog8.codegen.target.cbm.viceMonListName
import prog8.compilerinterface.*
import java.io.IOException
import java.nio.file.Path
@@ -1,8 +1,10 @@
package prog8.codegen.target.cpu6502.codegen
package prog8.codegen.target.cbm
import prog8.ast.base.Cx16VirtualRegisters
import prog8.ast.base.RegisterOrPair
import prog8.ast.expressions.*
import prog8.ast.expressions.ArrayIndexedExpression
import prog8.ast.expressions.Expression
import prog8.ast.expressions.FunctionCallExpression
import prog8.ast.statements.RegisterOrStatusflag
import prog8.ast.statements.Subroutine
@@ -0,0 +1,31 @@
package prog8.codegen.target.cbm
import prog8.ast.base.*
import prog8.ast.expressions.StringLiteral
import prog8.ast.statements.VarDecl
import prog8.compilerinterface.IMemSizer
internal object CbmMemorySizer: IMemSizer {
override fun memorySize(dt: DataType): Int {
return when(dt) {
in ByteDatatypes -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> Mflpt5.FLOAT_MEM_SIZE
else -> Int.MIN_VALUE
}
}
override fun memorySize(decl: VarDecl): Int {
return when(decl.type) {
VarDeclType.CONST -> 0
VarDeclType.VAR, VarDeclType.MEMORY -> {
when(val dt = decl.datatype) {
in NumericDatatypes -> return memorySize(dt)
in ArrayDatatypes -> decl.arraysize!!.constIndex()!! * memorySize(ArrayToElementTypes.getValue(dt))
DataType.STR -> (decl.value as StringLiteral).value.length + 1
else -> 0
}
}
}
}
}
@@ -1,8 +1,8 @@
package prog8.codegen.target.cbm
import com.github.michaelbull.result.Result
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result
import java.io.CharConversionException
import java.nio.charset.Charset
@@ -1,8 +1,6 @@
package prog8.codegen.target.cx16
import prog8.ast.base.DataType
import prog8.codegen.target.cbm.Mflpt5
import prog8.codegen.target.cbm.viceMonListName
import prog8.compilerinterface.*
import java.io.IOException
import java.nio.file.Path
@@ -0,0 +1,57 @@
package prog8.codegen.target.cx16
import prog8.ast.GlobalNamespace
import prog8.ast.base.DataType
import prog8.compilerinterface.CompilationOptions
import prog8.compilerinterface.InternalCompilerException
import prog8.compilerinterface.Zeropage
import prog8.compilerinterface.ZeropageType
class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_B1 = 0x7au // temp storage for a single byte
override val SCRATCH_REG = 0x7bu // temp storage for a register, must be B1+1
override val SCRATCH_W1 = 0x7cu // temp storage 1 for a word $7c+$7d
override val SCRATCH_W2 = 0x7eu // temp storage 2 for a word $7e+$7f
init {
if (options.floats && options.zeropage !in arrayOf(ZeropageType.BASICSAFE, ZeropageType.DONTUSE))
throw InternalCompilerException("when floats are enabled, zero page type should be 'basicsafe' or 'dontuse'")
// the addresses 0x02 to 0x21 (inclusive) are taken for sixteen virtual 16-bit api registers.
synchronized(this) {
when (options.zeropage) {
ZeropageType.FULL -> {
free.addAll(0x22u..0xffu)
}
ZeropageType.KERNALSAFE -> {
free.addAll(0x22u..0x7fu)
free.addAll(0xa9u..0xffu)
}
ZeropageType.BASICSAFE -> {
free.addAll(0x22u..0x7fu)
}
ZeropageType.DONTUSE -> {
free.clear() // don't use zeropage at all
}
else -> throw InternalCompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}")
}
removeReservedFromFreePool()
// note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
// however, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
val dummyscope = GlobalNamespace(emptyList())
for(reg in 0..15) {
allocatedVariables[listOf("cx16", "r${reg}")] = ZpAllocation((2+reg*2).toUInt(), DataType.UWORD, 2, dummyscope, null, null) // cx16.r0 .. cx16.r15
allocatedVariables[listOf("cx16", "r${reg}s")] = ZpAllocation((2+reg*2).toUInt(), DataType.WORD, 2, dummyscope, null, null) // cx16.r0s .. cx16.r15s
allocatedVariables[listOf("cx16", "r${reg}L")] = ZpAllocation((2+reg*2).toUInt(), DataType.UBYTE, 1, dummyscope, null, null) // cx16.r0L .. cx16.r15L
allocatedVariables[listOf("cx16", "r${reg}H")] = ZpAllocation((3+reg*2).toUInt(), DataType.UBYTE, 1, dummyscope, null, null) // cx16.r0H .. cx16.r15H
allocatedVariables[listOf("cx16", "r${reg}sL")] = ZpAllocation((2+reg*2).toUInt(), DataType.BYTE, 1, dummyscope, null, null) // cx16.r0sL .. cx16.r15sL
allocatedVariables[listOf("cx16", "r${reg}sH")] = ZpAllocation((3+reg*2).toUInt(), DataType.BYTE, 1, dummyscope, null, null) // cx16.r0sH .. cx16.r15sH
}
}
}
}
@@ -1,3 +0,0 @@
package prog8.codegen.target
class AssemblyError(msg: String) : RuntimeException(msg)
@@ -1,34 +0,0 @@
package prog8.codegen.target
import prog8.ast.base.ByteDatatypes
import prog8.ast.base.DataType
import prog8.ast.base.PassByReferenceDatatypes
import prog8.ast.base.WordDatatypes
import prog8.ast.expressions.Expression
import prog8.ast.statements.RegisterOrStatusflag
import prog8.ast.statements.Subroutine
import prog8.codegen.target.c128.C128MachineDefinition
import prog8.codegen.target.cpu6502.codegen.asmsub6502ArgsEvalOrder
import prog8.codegen.target.cpu6502.codegen.asmsub6502ArgsHaveRegisterClobberRisk
import prog8.compilerinterface.ICompilationTarget
import prog8.compilerinterface.IStringEncoding
object C128Target: ICompilationTarget, IStringEncoding by Encoder {
override val name = "c128"
override val machine = C128MachineDefinition()
override fun asmsubArgsEvalOrder(sub: Subroutine): List<Int> =
asmsub6502ArgsEvalOrder(sub)
override fun asmsubArgsHaveRegisterClobberRisk(args: List<Expression>, paramRegisters: List<RegisterOrStatusflag>) =
asmsub6502ArgsHaveRegisterClobberRisk(args, paramRegisters)
override fun memorySize(dt: DataType): Int {
return when(dt) {
in ByteDatatypes -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
else -> Int.MIN_VALUE
}
}
}
@@ -1,34 +0,0 @@
package prog8.codegen.target
import prog8.ast.base.ByteDatatypes
import prog8.ast.base.DataType
import prog8.ast.base.PassByReferenceDatatypes
import prog8.ast.base.WordDatatypes
import prog8.ast.expressions.Expression
import prog8.ast.statements.RegisterOrStatusflag
import prog8.ast.statements.Subroutine
import prog8.codegen.target.c64.C64MachineDefinition
import prog8.codegen.target.cpu6502.codegen.asmsub6502ArgsEvalOrder
import prog8.codegen.target.cpu6502.codegen.asmsub6502ArgsHaveRegisterClobberRisk
import prog8.compilerinterface.ICompilationTarget
import prog8.compilerinterface.IStringEncoding
object C64Target: ICompilationTarget, IStringEncoding by Encoder {
override val name = "c64"
override val machine = C64MachineDefinition()
override fun asmsubArgsEvalOrder(sub: Subroutine): List<Int> =
asmsub6502ArgsEvalOrder(sub)
override fun asmsubArgsHaveRegisterClobberRisk(args: List<Expression>, paramRegisters: List<RegisterOrStatusflag>) =
asmsub6502ArgsHaveRegisterClobberRisk(args, paramRegisters)
override fun memorySize(dt: DataType): Int {
return when(dt) {
in ByteDatatypes -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
else -> Int.MIN_VALUE
}
}
}
@@ -1,35 +0,0 @@
package prog8.codegen.target
import prog8.ast.base.ByteDatatypes
import prog8.ast.base.DataType
import prog8.ast.base.PassByReferenceDatatypes
import prog8.ast.base.WordDatatypes
import prog8.ast.expressions.Expression
import prog8.ast.statements.RegisterOrStatusflag
import prog8.ast.statements.Subroutine
import prog8.codegen.target.cpu6502.codegen.asmsub6502ArgsEvalOrder
import prog8.codegen.target.cpu6502.codegen.asmsub6502ArgsHaveRegisterClobberRisk
import prog8.codegen.target.cx16.CX16MachineDefinition
import prog8.compilerinterface.ICompilationTarget
import prog8.compilerinterface.IStringEncoding
object Cx16Target: ICompilationTarget, IStringEncoding by Encoder {
override val name = "cx16"
override val machine = CX16MachineDefinition()
override fun asmsubArgsEvalOrder(sub: Subroutine): List<Int> =
asmsub6502ArgsEvalOrder(sub)
override fun asmsubArgsHaveRegisterClobberRisk(args: List<Expression>, paramRegisters: List<RegisterOrStatusflag>) =
asmsub6502ArgsHaveRegisterClobberRisk(args, paramRegisters)
override fun memorySize(dt: DataType): Int {
return when(dt) {
in ByteDatatypes -> 1
in WordDatatypes, in PassByReferenceDatatypes -> 2
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
else -> Int.MIN_VALUE
}
}
}
@@ -1,52 +0,0 @@
package prog8.codegen.target.cx16
import prog8.ast.base.DataType
import prog8.compilerinterface.CompilationOptions
import prog8.compilerinterface.InternalCompilerException
import prog8.compilerinterface.Zeropage
import prog8.compilerinterface.ZeropageType
class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
override val SCRATCH_B1 = 0x7au // temp storage for a single byte
override val SCRATCH_REG = 0x7bu // temp storage for a register, must be B1+1
override val SCRATCH_W1 = 0x7cu // temp storage 1 for a word $7c+$7d
override val SCRATCH_W2 = 0x7eu // temp storage 2 for a word $7e+$7f
init {
if (options.floats && options.zeropage !in arrayOf(ZeropageType.BASICSAFE, ZeropageType.DONTUSE))
throw InternalCompilerException("when floats are enabled, zero page type should be 'basicsafe' or 'dontuse'")
// the addresses 0x02 to 0x21 (inclusive) are taken for sixteen virtual 16-bit api registers.
when (options.zeropage) {
ZeropageType.FULL -> {
free.addAll(0x22u..0xffu)
}
ZeropageType.KERNALSAFE -> {
free.addAll(0x22u..0x7fu)
free.addAll(0xa9u..0xffu)
}
ZeropageType.BASICSAFE -> {
free.addAll(0x22u..0x7fu)
}
ZeropageType.DONTUSE -> {
free.clear() // don't use zeropage at all
}
else -> throw InternalCompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}")
}
removeReservedFromFreePool()
for(reg in 0..15) {
allocatedVariables[listOf("cx16", "r${reg}")] = (2+reg*2).toUInt() to (DataType.UWORD to 2) // cx16.r0 .. cx16.r15
allocatedVariables[listOf("cx16", "r${reg}s")] = (2+reg*2).toUInt() to (DataType.WORD to 2) // cx16.r0s .. cx16.r15s
allocatedVariables[listOf("cx16", "r${reg}L")] = (2+reg*2).toUInt() to (DataType.UBYTE to 1) // cx16.r0L .. cx16.r15L
allocatedVariables[listOf("cx16", "r${reg}H")] = (3+reg*2).toUInt() to (DataType.UBYTE to 1) // cx16.r0H .. cx16.r15H
allocatedVariables[listOf("cx16", "r${reg}sL")] = (2+reg*2).toUInt() to (DataType.BYTE to 1) // cx16.r0sL .. cx16.r15sL
allocatedVariables[listOf("cx16", "r${reg}sH")] = (3+reg*2).toUInt() to (DataType.BYTE to 1) // cx16.r0sH .. cx16.r15sH
}
}
}
@@ -4,11 +4,11 @@ import prog8.ast.IStatementContainer
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.DataType
import prog8.ast.base.FatalAstException
import prog8.ast.expressions.AugmentAssignmentOperators
import prog8.ast.expressions.BinaryExpression
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.TypecastExpression
import prog8.ast.expressions.AugmentAssignmentOperators
import prog8.ast.getTempVar
import prog8.ast.statements.AssignTarget
import prog8.ast.statements.Assignment
import prog8.ast.statements.AssignmentOrigin
@@ -98,14 +98,7 @@ X = BinExpr X = LeftExpr
// we can see if we can unwrap the binary expression by working on a new temporary variable
// (that has the type of the expression), and then finally doing the typecast.
// Once it's outside the typecast, the regular splitting can commence.
val tempVar = when(val tempDt = origExpr.inferType(program).getOr(DataType.UNDEFINED)) {
DataType.UBYTE -> listOf("prog8_lib", "retval_interm_ub")
DataType.BYTE -> listOf("prog8_lib", "retval_interm_b")
DataType.UWORD -> listOf("prog8_lib", "retval_interm_uw")
DataType.WORD -> listOf("prog8_lib", "retval_interm_w")
DataType.FLOAT -> listOf("floats", "tempvar_swap_float")
else -> throw FatalAstException("invalid dt $tempDt")
}
val tempVar = program.getTempVar(origExpr.inferType(program).getOr(DataType.UNDEFINED))
val assignTempVar = Assignment(
AssignTarget(IdentifierReference(tempVar, typecast.position), null, null, typecast.position),
typecast.expression, AssignmentOrigin.OPTIMIZER, typecast.position
@@ -2,13 +2,13 @@ package prog8.optimizer
import prog8.ast.base.*
import prog8.ast.expressions.Expression
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.NumericLiteral
import kotlin.math.pow
class ConstExprEvaluator {
fun evaluate(left: NumericLiteralValue, operator: String, right: NumericLiteralValue): Expression {
fun evaluate(left: NumericLiteral, operator: String, right: NumericLiteral): Expression {
try {
return when(operator) {
"+" -> plus(left, right)
@@ -23,12 +23,12 @@ class ConstExprEvaluator {
"and" -> logicaland(left, right)
"or" -> logicalor(left, right)
"xor" -> logicalxor(left, right)
"<" -> NumericLiteralValue.fromBoolean(left < right, left.position)
">" -> NumericLiteralValue.fromBoolean(left > right, left.position)
"<=" -> NumericLiteralValue.fromBoolean(left <= right, left.position)
">=" -> NumericLiteralValue.fromBoolean(left >= right, left.position)
"==" -> NumericLiteralValue.fromBoolean(left == right, left.position)
"!=" -> NumericLiteralValue.fromBoolean(left != right, left.position)
"<" -> NumericLiteral.fromBoolean(left < right, left.position)
">" -> NumericLiteral.fromBoolean(left > right, left.position)
"<=" -> NumericLiteral.fromBoolean(left <= right, left.position)
">=" -> NumericLiteral.fromBoolean(left >= right, left.position)
"==" -> NumericLiteral.fromBoolean(left == right, left.position)
"!=" -> NumericLiteral.fromBoolean(left != right, left.position)
"<<" -> shiftedleft(left, right)
">>" -> shiftedright(left, right)
else -> throw FatalAstException("const evaluation for invalid operator $operator")
@@ -38,7 +38,7 @@ class ConstExprEvaluator {
}
}
private fun shiftedright(left: NumericLiteralValue, amount: NumericLiteralValue): Expression {
private fun shiftedright(left: NumericLiteral, amount: NumericLiteral): Expression {
if(left.type !in IntegerDatatypes || amount.type !in IntegerDatatypes)
throw ExpressionError("cannot compute $left >> $amount", left.position)
val result =
@@ -46,168 +46,168 @@ class ConstExprEvaluator {
left.number.toInt().ushr(amount.number.toInt())
else
left.number.toInt().shr(amount.number.toInt())
return NumericLiteralValue(left.type, result.toDouble(), left.position)
return NumericLiteral(left.type, result.toDouble(), left.position)
}
private fun shiftedleft(left: NumericLiteralValue, amount: NumericLiteralValue): Expression {
private fun shiftedleft(left: NumericLiteral, amount: NumericLiteral): Expression {
if(left.type !in IntegerDatatypes || amount.type !in IntegerDatatypes)
throw ExpressionError("cannot compute $left << $amount", left.position)
val result = left.number.toInt().shl(amount.number.toInt())
return NumericLiteralValue(left.type, result.toDouble(), left.position)
return NumericLiteral(left.type, result.toDouble(), left.position)
}
private fun logicalxor(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
private fun logicalxor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot compute $left locical-bitxor $right"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue.fromBoolean((left.number.toInt() != 0) xor (right.number.toInt() != 0), left.position)
DataType.FLOAT -> NumericLiteralValue.fromBoolean((left.number.toInt() != 0) xor (right.number != 0.0), left.position)
in IntegerDatatypes -> NumericLiteral.fromBoolean((left.number.toInt() != 0) xor (right.number.toInt() != 0), left.position)
DataType.FLOAT -> NumericLiteral.fromBoolean((left.number.toInt() != 0) xor (right.number != 0.0), left.position)
else -> throw ExpressionError(error, left.position)
}
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue.fromBoolean((left.number != 0.0) xor (right.number.toInt() != 0), left.position)
DataType.FLOAT -> NumericLiteralValue.fromBoolean((left.number != 0.0) xor (right.number != 0.0), left.position)
in IntegerDatatypes -> NumericLiteral.fromBoolean((left.number != 0.0) xor (right.number.toInt() != 0), left.position)
DataType.FLOAT -> NumericLiteral.fromBoolean((left.number != 0.0) xor (right.number != 0.0), left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
}
}
private fun logicalor(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
private fun logicalor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot compute $left locical-or $right"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 || right.number.toInt() != 0, left.position)
DataType.FLOAT -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 || right.number != 0.0, left.position)
in IntegerDatatypes -> NumericLiteral.fromBoolean(left.number.toInt() != 0 || right.number.toInt() != 0, left.position)
DataType.FLOAT -> NumericLiteral.fromBoolean(left.number.toInt() != 0 || right.number != 0.0, left.position)
else -> throw ExpressionError(error, left.position)
}
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number != 0.0 || right.number.toInt() != 0, left.position)
DataType.FLOAT -> NumericLiteralValue.fromBoolean(left.number != 0.0 || right.number != 0.0, left.position)
in IntegerDatatypes -> NumericLiteral.fromBoolean(left.number != 0.0 || right.number.toInt() != 0, left.position)
DataType.FLOAT -> NumericLiteral.fromBoolean(left.number != 0.0 || right.number != 0.0, left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
}
}
private fun logicaland(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
private fun logicaland(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot compute $left locical-and $right"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 && right.number.toInt() != 0, left.position)
DataType.FLOAT -> NumericLiteralValue.fromBoolean(left.number.toInt() != 0 && right.number != 0.0, left.position)
in IntegerDatatypes -> NumericLiteral.fromBoolean(left.number.toInt() != 0 && right.number.toInt() != 0, left.position)
DataType.FLOAT -> NumericLiteral.fromBoolean(left.number.toInt() != 0 && right.number != 0.0, left.position)
else -> throw ExpressionError(error, left.position)
}
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue.fromBoolean(left.number != 0.0 && right.number.toInt() != 0, left.position)
DataType.FLOAT -> NumericLiteralValue.fromBoolean(left.number != 0.0 && right.number != 0.0, left.position)
in IntegerDatatypes -> NumericLiteral.fromBoolean(left.number != 0.0 && right.number.toInt() != 0, left.position)
DataType.FLOAT -> NumericLiteral.fromBoolean(left.number != 0.0 && right.number != 0.0, left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
}
}
private fun bitwisexor(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
private fun bitwisexor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
if(left.type== DataType.UBYTE) {
if(right.type in IntegerDatatypes) {
return NumericLiteralValue(DataType.UBYTE, (left.number.toInt() xor (right.number.toInt() and 255)).toDouble(), left.position)
return NumericLiteral(DataType.UBYTE, (left.number.toInt() xor (right.number.toInt() and 255)).toDouble(), left.position)
}
} else if(left.type== DataType.UWORD) {
if(right.type in IntegerDatatypes) {
return NumericLiteralValue(DataType.UWORD, (left.number.toInt() xor right.number.toInt()).toDouble(), left.position)
return NumericLiteral(DataType.UWORD, (left.number.toInt() xor right.number.toInt()).toDouble(), left.position)
}
}
throw ExpressionError("cannot calculate $left ^ $right", left.position)
}
private fun bitwiseor(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
private fun bitwiseor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
if(left.type== DataType.UBYTE) {
if(right.type in IntegerDatatypes) {
return NumericLiteralValue(DataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toDouble(), left.position)
return NumericLiteral(DataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toDouble(), left.position)
}
} else if(left.type== DataType.UWORD) {
if(right.type in IntegerDatatypes) {
return NumericLiteralValue(DataType.UWORD, (left.number.toInt() or right.number.toInt()).toDouble(), left.position)
return NumericLiteral(DataType.UWORD, (left.number.toInt() or right.number.toInt()).toDouble(), left.position)
}
}
throw ExpressionError("cannot calculate $left | $right", left.position)
}
private fun bitwiseand(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
private fun bitwiseand(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
if(left.type== DataType.UBYTE) {
if(right.type in IntegerDatatypes) {
return NumericLiteralValue(DataType.UBYTE, (left.number.toInt() and (right.number.toInt() and 255)).toDouble(), left.position)
return NumericLiteral(DataType.UBYTE, (left.number.toInt() and (right.number.toInt() and 255)).toDouble(), left.position)
}
} else if(left.type== DataType.UWORD) {
if(right.type in IntegerDatatypes) {
return NumericLiteralValue(DataType.UWORD, (left.number.toInt() and right.number.toInt()).toDouble(), left.position)
return NumericLiteral(DataType.UWORD, (left.number.toInt() and right.number.toInt()).toDouble(), left.position)
}
}
throw ExpressionError("cannot calculate $left & $right", left.position)
}
private fun power(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
private fun power(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot calculate $left ** $right"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt().toDouble().pow(right.number.toInt()), left.position)
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt().toDouble().pow(right.number), left.position)
in IntegerDatatypes -> NumericLiteral.optimalNumeric(left.number.toInt().toDouble().pow(right.number.toInt()), left.position)
DataType.FLOAT -> NumericLiteral(DataType.FLOAT, left.number.toInt().toDouble().pow(right.number), left.position)
else -> throw ExpressionError(error, left.position)
}
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number.pow(right.number.toInt()), left.position)
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.pow(right.number), left.position)
in IntegerDatatypes -> NumericLiteral(DataType.FLOAT, left.number.pow(right.number.toInt()), left.position)
DataType.FLOAT -> NumericLiteral(DataType.FLOAT, left.number.pow(right.number), left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
}
}
private fun plus(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
private fun plus(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot add $left and $right"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue.optimalInteger(left.number.toInt() + right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() + right.number, left.position)
in IntegerDatatypes -> NumericLiteral.optimalInteger(left.number.toInt() + right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteral(DataType.FLOAT, left.number.toInt() + right.number, left.position)
else -> throw ExpressionError(error, left.position)
}
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number + right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number + right.number, left.position)
in IntegerDatatypes -> NumericLiteral(DataType.FLOAT, left.number + right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteral(DataType.FLOAT, left.number + right.number, left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
}
}
private fun minus(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
private fun minus(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot subtract $left and $right"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue.optimalInteger(left.number.toInt() - right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() - right.number, left.position)
in IntegerDatatypes -> NumericLiteral.optimalInteger(left.number.toInt() - right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteral(DataType.FLOAT, left.number.toInt() - right.number, left.position)
else -> throw ExpressionError(error, left.position)
}
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number - right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number - right.number, left.position)
in IntegerDatatypes -> NumericLiteral(DataType.FLOAT, left.number - right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteral(DataType.FLOAT, left.number - right.number, left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
}
}
private fun multiply(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
private fun multiply(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot multiply ${left.type} and ${right.type}"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue.optimalInteger(left.number.toInt() * right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() * right.number, left.position)
in IntegerDatatypes -> NumericLiteral.optimalInteger(left.number.toInt() * right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteral(DataType.FLOAT, left.number.toInt() * right.number, left.position)
else -> throw ExpressionError(error, left.position)
}
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> NumericLiteralValue(DataType.FLOAT, left.number * right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number * right.number, left.position)
in IntegerDatatypes -> NumericLiteral(DataType.FLOAT, left.number * right.number.toInt(), left.position)
DataType.FLOAT -> NumericLiteral(DataType.FLOAT, left.number * right.number, left.position)
else -> throw ExpressionError(error, left.position)
}
else -> throw ExpressionError(error, left.position)
@@ -217,29 +217,29 @@ class ConstExprEvaluator {
private fun divideByZeroError(pos: Position): Unit =
throw ExpressionError("division by zero", pos)
private fun divide(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
private fun divide(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot divide $left by $right"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> {
if(right.number.toInt()==0) divideByZeroError(right.position)
val result: Int = left.number.toInt() / right.number.toInt()
NumericLiteralValue.optimalInteger(result, left.position)
NumericLiteral.optimalInteger(result, left.position)
}
DataType.FLOAT -> {
if(right.number==0.0) divideByZeroError(right.position)
NumericLiteralValue(DataType.FLOAT, left.number.toInt() / right.number, left.position)
NumericLiteral(DataType.FLOAT, left.number.toInt() / right.number, left.position)
}
else -> throw ExpressionError(error, left.position)
}
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> {
if(right.number.toInt()==0) divideByZeroError(right.position)
NumericLiteralValue(DataType.FLOAT, left.number / right.number.toInt(), left.position)
NumericLiteral(DataType.FLOAT, left.number / right.number.toInt(), left.position)
}
DataType.FLOAT -> {
if(right.number ==0.0) divideByZeroError(right.position)
NumericLiteralValue(DataType.FLOAT, left.number / right.number, left.position)
NumericLiteral(DataType.FLOAT, left.number / right.number, left.position)
}
else -> throw ExpressionError(error, left.position)
}
@@ -247,28 +247,28 @@ class ConstExprEvaluator {
}
}
private fun remainder(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
private fun remainder(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
val error = "cannot compute remainder of $left by $right"
return when (left.type) {
in IntegerDatatypes -> when (right.type) {
in IntegerDatatypes -> {
if(right.number.toInt()==0) divideByZeroError(right.position)
NumericLiteralValue.optimalNumeric(left.number.toInt().toDouble() % right.number.toInt().toDouble(), left.position)
NumericLiteral.optimalNumeric(left.number.toInt().toDouble() % right.number.toInt().toDouble(), left.position)
}
DataType.FLOAT -> {
if(right.number ==0.0) divideByZeroError(right.position)
NumericLiteralValue(DataType.FLOAT, left.number.toInt() % right.number, left.position)
NumericLiteral(DataType.FLOAT, left.number.toInt() % right.number, left.position)
}
else -> throw ExpressionError(error, left.position)
}
DataType.FLOAT -> when (right.type) {
in IntegerDatatypes -> {
if(right.number.toInt()==0) divideByZeroError(right.position)
NumericLiteralValue(DataType.FLOAT, left.number % right.number.toInt(), left.position)
NumericLiteral(DataType.FLOAT, left.number % right.number.toInt(), left.position)
}
DataType.FLOAT -> {
if(right.number ==0.0) divideByZeroError(right.position)
NumericLiteralValue(DataType.FLOAT, left.number % right.number, left.position)
NumericLiteral(DataType.FLOAT, left.number % right.number, left.position)
}
else -> throw ExpressionError(error, left.position)
}
@@ -35,19 +35,19 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
// Compile-time constant sub expressions will be evaluated on the spot.
// For instance, the expression for "- 4.5" will be optimized into the float literal -4.5
val subexpr = expr.expression
if (subexpr is NumericLiteralValue) {
if (subexpr is NumericLiteral) {
// accept prefixed literal values (such as -3, not true)
return when (expr.operator) {
"+" -> listOf(IAstModification.ReplaceNode(expr, subexpr, parent))
"-" -> when (subexpr.type) {
in IntegerDatatypes -> {
listOf(IAstModification.ReplaceNode(expr,
NumericLiteralValue.optimalInteger(-subexpr.number.toInt(), subexpr.position),
NumericLiteral.optimalInteger(-subexpr.number.toInt(), subexpr.position),
parent))
}
DataType.FLOAT -> {
listOf(IAstModification.ReplaceNode(expr,
NumericLiteralValue(DataType.FLOAT, -subexpr.number, subexpr.position),
NumericLiteral(DataType.FLOAT, -subexpr.number, subexpr.position),
parent))
}
else -> throw ExpressionError("can only take negative of int or float", subexpr.position)
@@ -55,29 +55,29 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
"~" -> when (subexpr.type) {
DataType.BYTE -> {
listOf(IAstModification.ReplaceNode(expr,
NumericLiteralValue(DataType.BYTE, subexpr.number.toInt().inv().toDouble(), subexpr.position),
NumericLiteral(DataType.BYTE, subexpr.number.toInt().inv().toDouble(), subexpr.position),
parent))
}
DataType.UBYTE -> {
listOf(IAstModification.ReplaceNode(expr,
NumericLiteralValue(DataType.UBYTE, (subexpr.number.toInt().inv() and 255).toDouble(), subexpr.position),
NumericLiteral(DataType.UBYTE, (subexpr.number.toInt().inv() and 255).toDouble(), subexpr.position),
parent))
}
DataType.WORD -> {
listOf(IAstModification.ReplaceNode(expr,
NumericLiteralValue(DataType.WORD, subexpr.number.toInt().inv().toDouble(), subexpr.position),
NumericLiteral(DataType.WORD, subexpr.number.toInt().inv().toDouble(), subexpr.position),
parent))
}
DataType.UWORD -> {
listOf(IAstModification.ReplaceNode(expr,
NumericLiteralValue(DataType.UWORD, (subexpr.number.toInt().inv() and 65535).toDouble(), subexpr.position),
NumericLiteral(DataType.UWORD, (subexpr.number.toInt().inv() and 65535).toDouble(), subexpr.position),
parent))
}
else -> throw ExpressionError("can only take bitwise inversion of int", subexpr.position)
}
"not" -> {
listOf(IAstModification.ReplaceNode(expr,
NumericLiteralValue.fromBoolean(subexpr.number == 0.0, subexpr.position),
NumericLiteral.fromBoolean(subexpr.number == 0.0, subexpr.position),
parent))
}
else -> throw ExpressionError(expr.operator, subexpr.position)
@@ -116,7 +116,7 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
when (leftExpr.operator) {
"+" -> {
// X + С1 == C2 --> X == C2 - C1
val newRightConst = NumericLiteralValue(rightconst.type, rightconst.number - leftRightConst.number, rightconst.position)
val newRightConst = NumericLiteral(rightconst.type, rightconst.number - leftRightConst.number, rightconst.position)
return listOf(
IAstModification.ReplaceNode(leftExpr, leftExpr.left, expr),
IAstModification.ReplaceNode(expr.right, newRightConst, expr)
@@ -124,7 +124,7 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
}
"-" -> {
// X - С1 == C2 --> X == C2 + C1
val newRightConst = NumericLiteralValue(rightconst.type, rightconst.number + leftRightConst.number, rightconst.position)
val newRightConst = NumericLiteral(rightconst.type, rightconst.number + leftRightConst.number, rightconst.position)
return listOf(
IAstModification.ReplaceNode(leftExpr, leftExpr.left, expr),
IAstModification.ReplaceNode(expr.right, newRightConst, expr)
@@ -142,16 +142,16 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
val leftDt = leftconst.inferType(program).getOr(DataType.UNDEFINED)
when (leftconst.number) {
0.0 -> {
val value = NumericLiteralValue(leftDt, 0.0, expr.position)
val value = NumericLiteral(leftDt, 0.0, expr.position)
modifications += IAstModification.ReplaceNode(expr, value, parent)
}
1.0 -> {
val value = NumericLiteralValue(leftDt, 1.0, expr.position)
val value = NumericLiteral(leftDt, 1.0, expr.position)
modifications += IAstModification.ReplaceNode(expr, value, parent)
}
2.0 -> {
if(rightconst!=null) {
val value = NumericLiteralValue(leftDt, 2.0.pow(rightconst.number), expr.position)
val value = NumericLiteral(leftDt, 2.0.pow(rightconst.number), expr.position)
modifications += IAstModification.ReplaceNode(expr, value, parent)
} else {
val rightDt = expr.right.inferType(program).getOr(DataType.UNDEFINED)
@@ -162,7 +162,7 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
is VarDecl -> parent.datatype
else -> leftDt
}
val one = NumericLiteralValue(targetDt, 1.0, expr.position)
val one = NumericLiteral(targetDt, 1.0, expr.position)
val shift = BinaryExpression(one, "<<", expr.right, expr.position)
modifications += IAstModification.ReplaceNode(expr, shift, parent)
}
@@ -284,7 +284,7 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
return modifications
}
override fun after(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> {
override fun after(array: ArrayLiteral, parent: Node): Iterable<IAstModification> {
// because constant folding can result in arrays that are now suddenly capable
// of telling the type of all their elements (for instance, when they contained -2 which
// was a prefix expression earlier), we recalculate the array's datatype.
@@ -320,7 +320,7 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
}
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
fun adjustRangeDt(rangeFrom: NumericLiteralValue, targetDt: DataType, rangeTo: NumericLiteralValue, stepLiteral: NumericLiteralValue?, range: RangeExpression): RangeExpression? {
fun adjustRangeDt(rangeFrom: NumericLiteral, targetDt: DataType, rangeTo: NumericLiteral, stepLiteral: NumericLiteral?, range: RangeExpression): RangeExpression? {
val fromCast = rangeFrom.cast(targetDt)
val toCast = rangeTo.cast(targetDt)
if(!fromCast.isValid || !toCast.isValid)
@@ -342,13 +342,13 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
// adjust the datatype of a range expression in for loops to the loop variable.
val iterableRange = forLoop.iterable as? RangeExpression ?: return noModifications
val rangeFrom = iterableRange.from as? NumericLiteralValue
val rangeTo = iterableRange.to as? NumericLiteralValue
val rangeFrom = iterableRange.from as? NumericLiteral
val rangeTo = iterableRange.to as? NumericLiteral
if(rangeFrom==null || rangeTo==null) return noModifications
val loopvar = forLoop.loopVar.targetVarDecl(program) ?: throw UndefinedSymbolError(forLoop.loopVar)
val stepLiteral = iterableRange.step as? NumericLiteralValue
val stepLiteral = iterableRange.step as? NumericLiteral
when(loopvar.datatype) {
DataType.UBYTE -> {
if(rangeFrom.type!= DataType.UBYTE) {
@@ -389,7 +389,7 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
}
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
val numval = decl.value as? NumericLiteralValue
val numval = decl.value as? NumericLiteral
if(decl.type== VarDeclType.CONST && numval!=null) {
val valueDt = numval.inferType(program)
if(valueDt isnot decl.datatype) {
@@ -93,7 +93,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
in NumericDatatypes -> listOf(
IAstModification.ReplaceNode(
identifier,
NumericLiteralValue(cval.type, cval.number, identifier.position),
NumericLiteral(cval.type, cval.number, identifier.position),
identifier.parent
)
)
@@ -118,11 +118,11 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
val arraysize = decl.arraysize
if(arraysize==null) {
// for arrays that have no size specifier attempt to deduce the size
val arrayval = decl.value as? ArrayLiteralValue
val arrayval = decl.value as? ArrayLiteral
if(arrayval!=null) {
return listOf(IAstModification.SetExpression(
{ decl.arraysize = ArrayIndex(it, decl.position) },
NumericLiteralValue.optimalInteger(arrayval.value.size, decl.position),
NumericLiteral.optimalInteger(arrayval.value.size, decl.position),
decl
))
}
@@ -132,9 +132,9 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
when(decl.datatype) {
DataType.FLOAT -> {
// vardecl: for scalar float vars, promote constant integer initialization values to floats
val litval = decl.value as? NumericLiteralValue
val litval = decl.value as? NumericLiteral
if (litval!=null && litval.type in IntegerDatatypes) {
val newValue = NumericLiteralValue(DataType.FLOAT, litval.number, litval.position)
val newValue = NumericLiteral(DataType.FLOAT, litval.number, litval.position)
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
}
}
@@ -149,18 +149,18 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
if(constRange!=null) {
val eltType = rangeExpr.inferType(program).getOr(DataType.UBYTE)
val newValue = if(eltType in ByteDatatypes) {
ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype),
constRange.map { NumericLiteralValue(eltType, it.toDouble(), decl.value!!.position) }.toTypedArray(),
ArrayLiteral(InferredTypes.InferredType.known(decl.datatype),
constRange.map { NumericLiteral(eltType, it.toDouble(), decl.value!!.position) }.toTypedArray(),
position = decl.value!!.position)
} else {
ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype),
constRange.map { NumericLiteralValue(eltType, it.toDouble(), decl.value!!.position) }.toTypedArray(),
ArrayLiteral(InferredTypes.InferredType.known(decl.datatype),
constRange.map { NumericLiteral(eltType, it.toDouble(), decl.value!!.position) }.toTypedArray(),
position = decl.value!!.position)
}
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
}
}
val numericLv = decl.value as? NumericLiteralValue
val numericLv = decl.value as? NumericLiteral
if(numericLv!=null && numericLv.type== DataType.FLOAT)
errors.err("arraysize requires only integers here", numericLv.position)
val size = decl.arraysize?.constIndex() ?: return noModifications
@@ -187,8 +187,8 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
else -> {}
}
// create the array itself, filled with the fillvalue.
val array = Array(size) {fillvalue}.map { NumericLiteralValue(ArrayToElementTypes.getValue(decl.datatype), it.toDouble(), numericLv.position) }.toTypedArray<Expression>()
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype), array, position = numericLv.position)
val array = Array(size) {fillvalue}.map { NumericLiteral(ArrayToElementTypes.getValue(decl.datatype), it.toDouble(), numericLv.position) }.toTypedArray<Expression>()
val refValue = ArrayLiteral(InferredTypes.InferredType.known(decl.datatype), array, position = numericLv.position)
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
}
}
@@ -201,14 +201,14 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!)
val constRange = rangeExpr.toConstantIntegerRange()
if(constRange!=null) {
val newValue = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_F),
constRange.map { NumericLiteralValue(DataType.FLOAT, it.toDouble(), decl.value!!.position) }.toTypedArray(),
val newValue = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_F),
constRange.map { NumericLiteral(DataType.FLOAT, it.toDouble(), decl.value!!.position) }.toTypedArray(),
position = decl.value!!.position)
return listOf(IAstModification.ReplaceNode(decl.value!!, newValue, decl))
}
}
val numericLv = decl.value as? NumericLiteralValue
val numericLv = decl.value as? NumericLiteral
val size = decl.arraysize?.constIndex() ?: return noModifications
if(rangeExpr==null && numericLv!=null) {
// arraysize initializer is a single int, and we know the size.
@@ -217,8 +217,8 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
errors.err("float value overflow", numericLv.position)
else {
// create the array itself, filled with the fillvalue.
val array = Array(size) {fillvalue}.map { NumericLiteralValue(DataType.FLOAT, it, numericLv.position) }.toTypedArray<Expression>()
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_F), array, position = numericLv.position)
val array = Array(size) {fillvalue}.map { NumericLiteral(DataType.FLOAT, it, numericLv.position) }.toTypedArray<Expression>()
val refValue = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_F), array, position = numericLv.position)
return listOf(IAstModification.ReplaceNode(decl.value!!, refValue, decl))
}
}
@@ -32,7 +32,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
val mods = mutableListOf<IAstModification>()
// try to statically convert a literal value into one of the desired type
val literal = typecast.expression as? NumericLiteralValue
val literal = typecast.expression as? NumericLiteral
if (literal != null) {
val newLiteral = literal.cast(typecast.type)
if (newLiteral.isValid && newLiteral.valueOrZero() !== literal)
@@ -142,7 +142,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
val x = expr.right
val y = determineY(x, leftBinExpr)
if (y != null) {
val yPlus1 = BinaryExpression(y, "+", NumericLiteralValue(leftDt, 1.0, y.position), y.position)
val yPlus1 = BinaryExpression(y, "+", NumericLiteral(leftDt, 1.0, y.position), y.position)
val newExpr = BinaryExpression(x, "*", yPlus1, x.position)
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
}
@@ -152,7 +152,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
val x = expr.right
val y = determineY(x, leftBinExpr)
if (y != null) {
val yMinus1 = BinaryExpression(y, "-", NumericLiteralValue(leftDt, 1.0, y.position), y.position)
val yMinus1 = BinaryExpression(y, "-", NumericLiteral(leftDt, 1.0, y.position), y.position)
val newExpr = BinaryExpression(x, "*", yMinus1, x.position)
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
}
@@ -164,7 +164,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
val x = expr.left
val y = determineY(x, rightBinExpr)
if (y != null) {
val yPlus1 = BinaryExpression(y, "+", NumericLiteralValue.optimalInteger(1, y.position), y.position)
val yPlus1 = BinaryExpression(y, "+", NumericLiteral.optimalInteger(1, y.position), y.position)
val newExpr = BinaryExpression(x, "*", yPlus1, x.position)
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
}
@@ -175,32 +175,32 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
if(leftDt!=DataType.FLOAT && expr.operator == ">=" && rightVal?.number == 1.0) {
// for integers: x >= 1 --> x > 0
expr.operator = ">"
return listOf(IAstModification.ReplaceNode(expr.right, NumericLiteralValue.optimalInteger(0, expr.right.position), expr))
return listOf(IAstModification.ReplaceNode(expr.right, NumericLiteral.optimalInteger(0, expr.right.position), expr))
}
if(expr.operator == ">=" && rightVal?.number == 0.0) {
if (leftDt == DataType.UBYTE || leftDt == DataType.UWORD) {
// unsigned >= 0 --> true
return listOf(IAstModification.ReplaceNode(expr, NumericLiteralValue.fromBoolean(true, expr.position), parent))
return listOf(IAstModification.ReplaceNode(expr, NumericLiteral.fromBoolean(true, expr.position), parent))
}
}
if(leftDt!=DataType.FLOAT && expr.operator == "<" && rightVal?.number == 1.0) {
// for integers: x < 1 --> x <= 0
expr.operator = "<="
return listOf(IAstModification.ReplaceNode(expr.right, NumericLiteralValue.optimalInteger(0, expr.right.position), expr))
return listOf(IAstModification.ReplaceNode(expr.right, NumericLiteral.optimalInteger(0, expr.right.position), expr))
}
if(expr.operator == "<" && rightVal?.number == 0.0) {
if (leftDt == DataType.UBYTE || leftDt == DataType.UWORD) {
// unsigned < 0 --> false
return listOf(IAstModification.ReplaceNode(expr, NumericLiteralValue.fromBoolean(false, expr.position), parent))
return listOf(IAstModification.ReplaceNode(expr, NumericLiteral.fromBoolean(false, expr.position), parent))
}
}
// simplify when a term is constant and directly determines the outcome
val constTrue = NumericLiteralValue.fromBoolean(true, expr.position)
val constFalse = NumericLiteralValue.fromBoolean(false, expr.position)
val constTrue = NumericLiteral.fromBoolean(true, expr.position)
val constFalse = NumericLiteral.fromBoolean(false, expr.position)
val newExpr: Expression? = when (expr.operator) {
"or" -> {
when {
@@ -231,10 +231,10 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
when {
leftVal?.number==0.0 -> expr.right
rightVal?.number==0.0 -> expr.left
rightIDt.isBytes && rightVal?.number==255.0 -> NumericLiteralValue(DataType.UBYTE, 255.0, rightVal.position)
rightIDt.isWords && rightVal?.number==65535.0 -> NumericLiteralValue(DataType.UWORD, 65535.0, rightVal.position)
leftIDt.isBytes && leftVal?.number==255.0 -> NumericLiteralValue(DataType.UBYTE, 255.0, leftVal.position)
leftIDt.isWords && leftVal?.number==65535.0 -> NumericLiteralValue(DataType.UWORD, 65535.0, leftVal.position)
rightIDt.isBytes && rightVal?.number==255.0 -> NumericLiteral(DataType.UBYTE, 255.0, rightVal.position)
rightIDt.isWords && rightVal?.number==65535.0 -> NumericLiteral(DataType.UWORD, 65535.0, rightVal.position)
leftIDt.isBytes && leftVal?.number==255.0 -> NumericLiteral(DataType.UBYTE, 255.0, leftVal.position)
leftIDt.isWords && leftVal?.number==65535.0 -> NumericLiteral(DataType.UWORD, 65535.0, leftVal.position)
else -> null
}
}
@@ -302,7 +302,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
// useless msb() of byte value that was typecasted to word, replace with 0
return listOf(IAstModification.ReplaceNode(
functionCallExpr,
NumericLiteralValue(valueDt.getOr(DataType.UBYTE), 0.0, arg.expression.position),
NumericLiteral(valueDt.getOr(DataType.UBYTE), 0.0, arg.expression.position),
parent))
}
} else {
@@ -311,7 +311,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
// useless msb() of byte value, replace with 0
return listOf(IAstModification.ReplaceNode(
functionCallExpr,
NumericLiteralValue(argDt.getOr(DataType.UBYTE), 0.0, arg.position),
NumericLiteral(argDt.getOr(DataType.UBYTE), 0.0, arg.position),
parent))
}
}
@@ -385,11 +385,11 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
}
}
private fun optimizeAdd(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression? {
private fun optimizeAdd(expr: BinaryExpression, leftVal: NumericLiteral?, rightVal: NumericLiteral?): Expression? {
if(expr.left.isSameAs(expr.right)) {
// optimize X+X into X *2
expr.operator = "*"
expr.right = NumericLiteralValue.optimalInteger(2, expr.right.position)
expr.right = NumericLiteral.optimalInteger(2, expr.right.position)
expr.right.linkParents(expr)
return expr
}
@@ -400,7 +400,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
val (expr2, _, rightVal2) = reorderAssociativeWithConstant(expr, leftVal)
if (rightVal2 != null) {
// right value is a constant, see if we can optimize
val rightConst: NumericLiteralValue = rightVal2
val rightConst: NumericLiteral = rightVal2
when (rightConst.number) {
0.0 -> {
// left
@@ -413,17 +413,17 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
val rnum = rightVal?.number
if(rnum!=null && rnum<0.0) {
expr.operator = "-"
expr.right = NumericLiteralValue(rightVal.type, -rnum, rightVal.position)
expr.right = NumericLiteral(rightVal.type, -rnum, rightVal.position)
return expr
}
return null
}
private fun optimizeSub(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression? {
private fun optimizeSub(expr: BinaryExpression, leftVal: NumericLiteral?, rightVal: NumericLiteral?): Expression? {
if(expr.left.isSameAs(expr.right)) {
// optimize X-X into 0
return NumericLiteralValue.optimalInteger(0, expr.position)
return NumericLiteral.optimalInteger(0, expr.position)
}
if (leftVal == null && rightVal == null)
@@ -439,7 +439,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
if(rnum<0.0) {
expr.operator = "+"
expr.right = NumericLiteralValue(rightVal.type, -rnum, rightVal.position)
expr.right = NumericLiteral(rightVal.type, -rnum, rightVal.position)
return expr
}
}
@@ -457,34 +457,34 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
return null
}
private fun optimizePower(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression? {
private fun optimizePower(expr: BinaryExpression, leftVal: NumericLiteral?, rightVal: NumericLiteral?): Expression? {
if (leftVal == null && rightVal == null)
return null
if (rightVal != null) {
// right value is a constant, see if we can optimize
val rightConst: NumericLiteralValue = rightVal
val rightConst: NumericLiteral = rightVal
when (rightConst.number) {
-3.0 -> {
// -1/(left*left*left)
return BinaryExpression(NumericLiteralValue(DataType.FLOAT, -1.0, expr.position), "/",
return BinaryExpression(NumericLiteral(DataType.FLOAT, -1.0, expr.position), "/",
BinaryExpression(expr.left, "*", BinaryExpression(expr.left, "*", expr.left, expr.position), expr.position),
expr.position)
}
-2.0 -> {
// -1/(left*left)
return BinaryExpression(NumericLiteralValue(DataType.FLOAT, -1.0, expr.position), "/",
return BinaryExpression(NumericLiteral(DataType.FLOAT, -1.0, expr.position), "/",
BinaryExpression(expr.left, "*", expr.left, expr.position),
expr.position)
}
-1.0 -> {
// -1/left
return BinaryExpression(NumericLiteralValue(DataType.FLOAT, -1.0, expr.position), "/",
return BinaryExpression(NumericLiteral(DataType.FLOAT, -1.0, expr.position), "/",
expr.left, expr.position)
}
0.0 -> {
// 1
return NumericLiteralValue(rightConst.type, 1.0, expr.position)
return NumericLiteral(rightConst.type, 1.0, expr.position)
}
0.5 -> {
// sqrt(left)
@@ -509,15 +509,15 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
when (leftVal.number) {
-1.0 -> {
// -1
return NumericLiteralValue(DataType.FLOAT, -1.0, expr.position)
return NumericLiteral(DataType.FLOAT, -1.0, expr.position)
}
0.0 -> {
// 0
return NumericLiteralValue(leftVal.type, 0.0, expr.position)
return NumericLiteral(leftVal.type, 0.0, expr.position)
}
1.0 -> {
//1
return NumericLiteralValue(leftVal.type, 1.0, expr.position)
return NumericLiteral(leftVal.type, 1.0, expr.position)
}
}
@@ -526,7 +526,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
return null
}
private fun optimizeRemainder(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression? {
private fun optimizeRemainder(expr: BinaryExpression, leftVal: NumericLiteral?, rightVal: NumericLiteral?): Expression? {
if (leftVal == null && rightVal == null)
return null
@@ -539,10 +539,10 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
val idt = expr.inferType(program)
if(!idt.isKnown)
throw FatalAstException("unknown dt")
return NumericLiteralValue(idt.getOr(DataType.UNDEFINED), 0.0, expr.position)
return NumericLiteral(idt.getOr(DataType.UNDEFINED), 0.0, expr.position)
} else if (cv in powersOfTwo) {
expr.operator = "&"
expr.right = NumericLiteralValue.optimalInteger(cv!!.toInt()-1, expr.position)
expr.right = NumericLiteral.optimalInteger(cv!!.toInt()-1, expr.position)
return null
}
}
@@ -551,14 +551,14 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
}
private fun optimizeDivision(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression? {
private fun optimizeDivision(expr: BinaryExpression, leftVal: NumericLiteral?, rightVal: NumericLiteral?): Expression? {
if (leftVal == null && rightVal == null)
return null
// cannot shuffle assiciativity with division!
if (rightVal != null) {
// right value is a constant, see if we can optimize
val rightConst: NumericLiteralValue = rightVal
val rightConst: NumericLiteral = rightVal
val cv = rightConst.number
val leftIDt = expr.left.inferType(program)
if (!leftIDt.isKnown)
@@ -581,25 +581,25 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
if (leftDt in IntegerDatatypes) {
// divided by a power of two => shift right
val numshifts = log2(cv).toInt()
return BinaryExpression(expr.left, ">>", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
return BinaryExpression(expr.left, ">>", NumericLiteral.optimalInteger(numshifts, expr.position), expr.position)
}
}
in negativePowersOfTwo -> {
if (leftDt in IntegerDatatypes) {
// divided by a negative power of two => negate, then shift right
val numshifts = log2(-cv).toInt()
return BinaryExpression(PrefixExpression("-", expr.left, expr.position), ">>", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
return BinaryExpression(PrefixExpression("-", expr.left, expr.position), ">>", NumericLiteral.optimalInteger(numshifts, expr.position), expr.position)
}
}
}
if (leftDt == DataType.UBYTE) {
if (abs(rightConst.number) >= 256.0) {
return NumericLiteralValue(DataType.UBYTE, 0.0, expr.position)
return NumericLiteral(DataType.UBYTE, 0.0, expr.position)
}
} else if (leftDt == DataType.UWORD) {
if (abs(rightConst.number) >= 65536.0) {
return NumericLiteralValue(DataType.UBYTE, 0.0, expr.position)
return NumericLiteral(DataType.UBYTE, 0.0, expr.position)
}
}
}
@@ -609,7 +609,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
when (leftVal.number) {
0.0 -> {
// 0
return NumericLiteralValue(leftVal.type, 0.0, expr.position)
return NumericLiteral(leftVal.type, 0.0, expr.position)
}
}
}
@@ -617,7 +617,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
return null
}
private fun optimizeMultiplication(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression? {
private fun optimizeMultiplication(expr: BinaryExpression, leftVal: NumericLiteral?, rightVal: NumericLiteral?): Expression? {
if (leftVal == null && rightVal == null)
return null
@@ -625,7 +625,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
if (rightVal2 != null) {
// right value is a constant, see if we can optimize
val leftValue: Expression = expr2.left
val rightConst: NumericLiteralValue = rightVal2
val rightConst: NumericLiteral = rightVal2
when (val cv = rightConst.number) {
-1.0 -> {
// -left
@@ -633,7 +633,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
}
0.0 -> {
// 0
return NumericLiteralValue(rightConst.type, 0.0, expr.position)
return NumericLiteral(rightConst.type, 0.0, expr.position)
}
1.0 -> {
// left
@@ -643,14 +643,14 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
if (leftValue.inferType(program).isInteger) {
// times a power of two => shift left
val numshifts = log2(cv).toInt()
return BinaryExpression(expr2.left, "<<", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
return BinaryExpression(expr2.left, "<<", NumericLiteral.optimalInteger(numshifts, expr.position), expr.position)
}
}
in negativePowersOfTwo -> {
if (leftValue.inferType(program).isInteger) {
// times a negative power of two => negate, then shift left
val numshifts = log2(-cv).toInt()
return BinaryExpression(PrefixExpression("-", expr2.left, expr.position), "<<", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
return BinaryExpression(PrefixExpression("-", expr2.left, expr.position), "<<", NumericLiteral.optimalInteger(numshifts, expr.position), expr.position)
}
}
}
@@ -660,7 +660,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
return null
}
private fun optimizeShiftLeft(expr: BinaryExpression, amountLv: NumericLiteralValue?): Expression? {
private fun optimizeShiftLeft(expr: BinaryExpression, amountLv: NumericLiteral?): Expression? {
if (amountLv == null)
return null
@@ -674,19 +674,16 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
when (val targetDt = targetIDt.getOr(DataType.UNDEFINED)) {
DataType.UBYTE, DataType.BYTE -> {
if (amount >= 8) {
return NumericLiteralValue(targetDt, 0.0, expr.position)
return NumericLiteral(targetDt, 0.0, expr.position)
}
}
DataType.UWORD, DataType.WORD -> {
if (amount >= 16) {
return NumericLiteralValue(targetDt, 0.0, expr.position)
} else if (amount >= 8) {
return NumericLiteral(targetDt, 0.0, expr.position)
} else if (amount > 8) {
val lsb = FunctionCallExpression(IdentifierReference(listOf("lsb"), expr.position), mutableListOf(expr.left), expr.position)
if (amount == 8) {
return FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(lsb, NumericLiteralValue.optimalInteger(0, expr.position)), expr.position)
}
val shifted = BinaryExpression(lsb, "<<", NumericLiteralValue.optimalInteger(amount - 8, expr.position), expr.position)
return FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(shifted, NumericLiteralValue.optimalInteger(0, expr.position)), expr.position)
val shifted = BinaryExpression(lsb, "<<", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position)
return FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(shifted, NumericLiteral.optimalInteger(0, expr.position)), expr.position)
}
}
else -> {
@@ -695,7 +692,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
return null
}
private fun optimizeShiftRight(expr: BinaryExpression, amountLv: NumericLiteralValue?): Expression? {
private fun optimizeShiftRight(expr: BinaryExpression, amountLv: NumericLiteral?): Expression? {
if (amountLv == null)
return null
@@ -709,32 +706,27 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
when (idt.getOr(DataType.UNDEFINED)) {
DataType.UBYTE -> {
if (amount >= 8) {
return NumericLiteralValue.optimalInteger(0, expr.position)
return NumericLiteral.optimalInteger(0, expr.position)
}
}
DataType.BYTE -> {
if (amount > 8) {
expr.right = NumericLiteralValue.optimalInteger(8, expr.right.position)
expr.right = NumericLiteral.optimalInteger(8, expr.right.position)
return null
}
}
DataType.UWORD -> {
if (amount >= 16) {
return NumericLiteralValue.optimalInteger(0, expr.position)
return NumericLiteral.optimalInteger(0, expr.position)
}
else if (amount >= 8) {
else if (amount > 8) {
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
if (amount == 8) {
// mkword(0, msb(v))
val zero = NumericLiteralValue(DataType.UBYTE, 0.0, expr.position)
return FunctionCallExpression(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(zero, msb), expr.position)
}
return TypecastExpression(BinaryExpression(msb, ">>", NumericLiteralValue.optimalInteger(amount - 8, expr.position), expr.position), DataType.UWORD, true, expr.position)
return TypecastExpression(BinaryExpression(msb, ">>", NumericLiteral.optimalInteger(amount - 8, expr.position), expr.position), DataType.UWORD, true, expr.position)
}
}
DataType.WORD -> {
if (amount > 16) {
expr.right = NumericLiteralValue.optimalInteger(16, expr.right.position)
expr.right = NumericLiteral.optimalInteger(16, expr.right.position)
return null
}
}
@@ -744,7 +736,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
return null
}
private fun reorderAssociativeWithConstant(expr: BinaryExpression, leftVal: NumericLiteralValue?): BinExprWithConstants {
private fun reorderAssociativeWithConstant(expr: BinaryExpression, leftVal: NumericLiteral?): BinExprWithConstants {
if (expr.operator in AssociativeOperators && leftVal != null) {
// swap left and right so that right is always the constant
val tmp = expr.left
@@ -755,6 +747,6 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
return BinExprWithConstants(expr, leftVal, expr.right.constValue(program))
}
private data class BinExprWithConstants(val expr: BinaryExpression, val leftVal: NumericLiteralValue?, val rightVal: NumericLiteralValue?)
private data class BinExprWithConstants(val expr: BinaryExpression, val leftVal: NumericLiteral?, val rightVal: NumericLiteral?)
}
@@ -69,7 +69,7 @@ fun Program.splitBinaryExpressions(options: CompilationOptions, compTarget: ICom
return opti.applyModifications()
}
fun getTempVarName(dt: InferredTypes.InferredType): List<String> {
fun getTempRegisterName(dt: InferredTypes.InferredType): List<String> {
return when {
// TODO assume (hope) cx16.r9 isn't used for anything else during the use of this temporary variable...
dt istype DataType.UBYTE -> listOf("cx16", "r9L")
@@ -1,7 +1,10 @@
package prog8.optimizer
import prog8.ast.*
import prog8.ast.base.*
import prog8.ast.base.ArrayDatatypes
import prog8.ast.base.DataType
import prog8.ast.base.IntegerDatatypes
import prog8.ast.base.VarDeclType
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
@@ -18,7 +21,7 @@ class StatementOptimizer(private val program: Program,
) : AstWalker() {
override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
// if the first instruction in the called subroutine is a return statement with a simple value,
// if the first instruction in the called subroutine is a return statement with a simple value (NOT being a parameter),
// remove the jump altogeter and inline the returnvalue directly.
fun scopePrefix(variable: IdentifierReference): IdentifierReference {
@@ -37,13 +40,18 @@ class StatementOptimizer(private val program: Program,
}
is DirectMemoryRead -> {
when(val expr = orig.addressExpression) {
is NumericLiteralValue -> DirectMemoryRead(expr.copy(), orig.position)
is NumericLiteral -> DirectMemoryRead(expr.copy(), orig.position)
else -> return noModifications
}
}
is IdentifierReference -> scopePrefix(orig)
is NumericLiteralValue -> orig.copy()
is StringLiteralValue -> orig.copy()
is IdentifierReference -> {
if(orig.targetVarDecl(program)?.origin == VarDeclOrigin.SUBROUTINEPARAM)
return noModifications
else
scopePrefix(orig)
}
is NumericLiteral -> orig.copy()
is StringLiteral -> orig.copy()
else -> return noModifications
}
return listOf(IAstModification.ReplaceNode(functionCallExpr, copy, parent))
@@ -52,8 +60,6 @@ class StatementOptimizer(private val program: Program,
return noModifications
}
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
if(functionCallStatement.target.targetStatement(program) is BuiltinFunctionPlaceholder) {
val functionName = functionCallStatement.target.nameInSource[0]
@@ -73,32 +79,38 @@ class StatementOptimizer(private val program: Program,
arg as? IdentifierReference
}
if(stringVar!=null && stringVar.wasStringLiteral(program)) {
val string = stringVar.targetVarDecl(program)?.value as? StringLiteralValue
val string = stringVar.targetVarDecl(program)?.value as? StringLiteral
if(string!=null) {
val pos = functionCallStatement.position
if (string.value.length == 1) {
val firstCharEncoded = compTarget.encodeString(string.value, string.encoding)[0]
val chrout = FunctionCallStatement(
IdentifierReference(listOf("txt", "chrout"), pos),
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstCharEncoded.toDouble(), pos)),
mutableListOf(NumericLiteral(DataType.UBYTE, firstCharEncoded.toDouble(), pos)),
functionCallStatement.void, pos
)
return listOf(IAstModification.ReplaceNode(functionCallStatement, chrout, parent))
val stringDecl = string.parent as VarDecl
return listOf(
IAstModification.ReplaceNode(functionCallStatement, chrout, parent),
IAstModification.Remove(stringDecl, stringDecl.parent as IStatementContainer)
)
} else if (string.value.length == 2) {
val firstTwoCharsEncoded = compTarget.encodeString(string.value.take(2), string.encoding)
val chrout1 = FunctionCallStatement(
IdentifierReference(listOf("txt", "chrout"), pos),
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[0].toDouble(), pos)),
mutableListOf(NumericLiteral(DataType.UBYTE, firstTwoCharsEncoded[0].toDouble(), pos)),
functionCallStatement.void, pos
)
val chrout2 = FunctionCallStatement(
IdentifierReference(listOf("txt", "chrout"), pos),
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[1].toDouble(), pos)),
mutableListOf(NumericLiteral(DataType.UBYTE, firstTwoCharsEncoded[1].toDouble(), pos)),
functionCallStatement.void, pos
)
val stringDecl = string.parent as VarDecl
return listOf(
IAstModification.InsertBefore(functionCallStatement, chrout1, parent as IStatementContainer),
IAstModification.ReplaceNode(functionCallStatement, chrout2, parent)
IAstModification.ReplaceNode(functionCallStatement, chrout2, parent),
IAstModification.Remove(stringDecl, stringDecl.parent as IStatementContainer)
)
}
}
@@ -118,7 +130,7 @@ class StatementOptimizer(private val program: Program,
if(functionCallStatement.target.nameInSource !in listOf(listOf("pop"), listOf("popw")) && functionCallStatement.args.size==1) {
val arg = functionCallStatement.args[0]
if(!arg.isSimple && arg !is TypecastExpression && arg !is IFunctionCall) {
val name = getTempVarName(arg.inferType(program))
val name = getTempRegisterName(arg.inferType(program))
val tempvar = IdentifierReference(name, functionCallStatement.position)
val assignTempvar = Assignment(AssignTarget(tempvar.copy(), null, null, functionCallStatement.position), arg, AssignmentOrigin.OPTIMIZER, functionCallStatement.position)
return listOf(
@@ -191,12 +203,12 @@ class StatementOptimizer(private val program: Program,
val iterable = (forLoop.iterable as? IdentifierReference)?.targetVarDecl(program)
if(iterable!=null) {
if(iterable.datatype==DataType.STR) {
val sv = iterable.value as StringLiteralValue
val sv = iterable.value as StringLiteral
val size = sv.value.length
if(size==1) {
// loop over string of length 1 -> just assign the single character
val character = compTarget.encodeString(sv.value, sv.encoding)[0]
val byte = NumericLiteralValue(DataType.UBYTE, character.toDouble(), iterable.position)
val byte = NumericLiteral(DataType.UBYTE, character.toDouble(), iterable.position)
val scope = AnonymousScope(mutableListOf(), forLoop.position)
scope.statements.add(Assignment(AssignTarget(forLoop.loopVar, null, null, forLoop.position), byte, AssignmentOrigin.OPTIMIZER, forLoop.position))
scope.statements.addAll(forLoop.body.statements)
@@ -207,11 +219,11 @@ class StatementOptimizer(private val program: Program,
val size = iterable.arraysize!!.constIndex()
if(size==1) {
// loop over array of length 1 -> just assign the single value
val av = (iterable.value as ArrayLiteralValue).value[0].constValue(program)?.number
val av = (iterable.value as ArrayLiteral).value[0].constValue(program)?.number
if(av!=null) {
val scope = AnonymousScope(mutableListOf(), forLoop.position)
scope.statements.add(Assignment(
AssignTarget(forLoop.loopVar, null, null, forLoop.position), NumericLiteralValue.optimalInteger(av.toInt(), iterable.position),
AssignTarget(forLoop.loopVar, null, null, 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))
@@ -304,12 +316,12 @@ class StatementOptimizer(private val program: Program,
val op1 = binExpr.operator
val op2 = rExpr.operator
if(rExpr.left is NumericLiteralValue && op2 in AssociativeOperators) {
if(rExpr.left is NumericLiteral && op2 in AssociativeOperators) {
// associative operator, make sure the constant numeric value is second (right)
return listOf(IAstModification.SwapOperands(rExpr))
}
val rNum = (rExpr.right as? NumericLiteralValue)?.number
val rNum = (rExpr.right as? NumericLiteral)?.number
if(rNum!=null) {
if (op1 == "+" || op1 == "-") {
if (op2 == "+") {
@@ -431,6 +443,28 @@ class StatementOptimizer(private val program: Program,
}
}
// word = msb(word) , word=lsb(word)
if(assignment.target.inferType(program).isWords) {
var fcall = assignment.value as? FunctionCallExpression
if (fcall == null)
fcall = (assignment.value as? TypecastExpression)?.expression as? FunctionCallExpression
if (fcall != null && (fcall.target.nameInSource == listOf("lsb") || fcall.target.nameInSource == listOf("msb"))) {
if (fcall.args.single() isSameAs assignment.target) {
return if (fcall.target.nameInSource == listOf("lsb")) {
// optimize word=lsb(word) ==> word &= $00ff
val and255 = BinaryExpression(fcall.args[0], "&", NumericLiteral(DataType.UWORD, 255.0, fcall.position), fcall.position)
val newAssign = Assignment(assignment.target, and255, AssignmentOrigin.OPTIMIZER, fcall.position)
listOf(IAstModification.ReplaceNode(assignment, newAssign, parent))
} else {
// optimize word=msb(word) ==> word >>= 8
val shift8 = BinaryExpression(fcall.args[0], ">>", NumericLiteral(DataType.UBYTE, 8.0, fcall.position), fcall.position)
val newAssign = Assignment(assignment.target, shift8, AssignmentOrigin.OPTIMIZER, fcall.position)
listOf(IAstModification.ReplaceNode(assignment, newAssign, parent))
}
}
}
}
return noModifications
}
@@ -440,14 +474,8 @@ class StatementOptimizer(private val program: Program,
val returnDt = subr.returntypes.single()
if (returnDt in IntegerDatatypes) {
// first assign to intermediary variable, then return that
val returnVarName = "retval_interm_" + when(returnDt) {
DataType.UBYTE -> "ub"
DataType.BYTE -> "b"
DataType.UWORD -> "uw"
DataType.WORD -> "w"
else -> "<undefined>"
}
val returnValueIntermediary = IdentifierReference(listOf("prog8_lib", returnVarName), returnStmt.position)
val returnVarName = program.getTempVar(returnDt)
val returnValueIntermediary = IdentifierReference(returnVarName, returnStmt.position)
val tgt = AssignTarget(returnValueIntermediary, null, null, returnStmt.position)
val assign = Assignment(tgt, value, AssignmentOrigin.OPTIMIZER, returnStmt.position)
val returnReplacement = Return(returnValueIntermediary.copy(), returnStmt.position)
@@ -459,10 +487,14 @@ class StatementOptimizer(private val program: Program,
return null
}
if(returnStmt.value is BinaryExpression) {
val mod = returnViaIntermediaryVar(returnStmt.value!!)
if(mod!=null)
return mod
// TODO decision when to use intermediary variable to calculate returnvalue seems a bit arbitrary...
val returnvalue = returnStmt.value
if (returnvalue!=null) {
if (returnvalue is BinaryExpression || (returnvalue is TypecastExpression && !returnvalue.expression.isSimple)) {
val mod = returnViaIntermediaryVar(returnvalue)
if(mod!=null)
return mod
}
}
return noModifications
@@ -3,6 +3,7 @@ package prog8.optimizer
import prog8.ast.*
import prog8.ast.base.DataType
import prog8.ast.base.VarDeclType
import prog8.ast.base.defaultZero
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
@@ -69,6 +70,7 @@ class UnusedCodeRemover(private val program: Program,
if(callgraph.unused(block)) {
if(block.statements.any{ it !is VarDecl || it.type==VarDeclType.VAR})
errors.warn("removing unused block '${block.name}'", block.position)
program.removeInternedStringsFromRemovedBlock(block)
return listOf(IAstModification.Remove(block, parent as IStatementContainer))
}
}
@@ -92,6 +94,7 @@ class UnusedCodeRemover(private val program: Program,
}
if(!subroutine.definingModule.isLibrary)
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
program.removeInternedStringsFromRemovedSubroutine(subroutine)
return listOf(IAstModification.Remove(subroutine, parent as IStatementContainer))
}
}
@@ -101,11 +104,13 @@ class UnusedCodeRemover(private val program: Program,
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
if(decl.type==VarDeclType.VAR) {
val forceOutput = "force_output" in decl.definingBlock.options()
if (!forceOutput && decl.origin==VarDeclOrigin.USERCODE && !decl.sharedWithAsm && !decl.definingBlock.isInLibrary) {
val block = decl.definingBlock
val forceOutput = "force_output" in block.options()
if (!forceOutput && decl.origin==VarDeclOrigin.USERCODE && !decl.sharedWithAsm) {
val usages = callgraph.usages(decl)
if (usages.isEmpty()) {
errors.warn("removing unused variable '${decl.name}'", decl.position)
// if(!decl.definingModule.isLibrary)
errors.warn("removing unused variable '${decl.name}'", decl.position)
return listOf(IAstModification.Remove(decl, parent as IStatementContainer))
}
else {
@@ -113,8 +118,9 @@ class UnusedCodeRemover(private val program: Program,
val singleUse = usages[0].parent
if(singleUse is AssignTarget) {
val assignment = singleUse.parent as Assignment
if(assignment.value !is IFunctionCall) {
errors.warn("removing unused variable '${decl.name}'", decl.position)
if(assignment.origin==AssignmentOrigin.VARINIT) {
if(!decl.definingModule.isLibrary)
errors.warn("removing unused variable '${decl.name}'", decl.position)
return listOf(
IAstModification.Remove(decl, parent as IStatementContainer),
IAstModification.Remove(assignment, assignment.parent as IStatementContainer)
@@ -134,7 +140,7 @@ class UnusedCodeRemover(private val program: Program,
val linesToRemove = mutableListOf<Assignment>()
val modifications = mutableListOf<IAstModification>()
fun substituteZeroInBinexpr(expr: BinaryExpression, zero: NumericLiteralValue, assign1: Assignment, assign2: Assignment) {
fun substituteZeroInBinexpr(expr: BinaryExpression, zero: NumericLiteral, assign1: Assignment, assign2: Assignment) {
if(expr.left isSameAs assign2.target) {
// X = X <oper> Right
linesToRemove.add(assign1)
@@ -185,7 +191,7 @@ class UnusedCodeRemover(private val program: Program,
}
}
fun substituteZeroInPrefixexpr(expr: PrefixExpression, zero: NumericLiteralValue, assign1: Assignment, assign2: Assignment) {
fun substituteZeroInPrefixexpr(expr: PrefixExpression, zero: NumericLiteral, assign1: Assignment, assign2: Assignment) {
if(expr.expression isSameAs assign2.target) {
linesToRemove.add(assign1)
modifications.add(IAstModification.ReplaceNode(
@@ -194,7 +200,7 @@ class UnusedCodeRemover(private val program: Program,
}
}
fun substituteZeroInTypecast(expr: TypecastExpression, zero: NumericLiteralValue, assign1: Assignment, assign2: Assignment) {
fun substituteZeroInTypecast(expr: TypecastExpression, zero: NumericLiteral, assign1: Assignment, assign2: Assignment) {
if(expr.expression isSameAs assign2.target) {
linesToRemove.add(assign1)
modifications.add(IAstModification.ReplaceNode(
@@ -217,7 +223,7 @@ class UnusedCodeRemover(private val program: Program,
val cvalue1 = assign1.value.constValue(program)
if(cvalue1!=null && cvalue1.number==0.0 && assign2.target.isSameAs(assign1.target, program) && assign2.isAugmentable) {
val value2 = assign2.value
val zero = VarDecl.defaultZero(value2.inferType(program).getOr(DataType.UNDEFINED), value2.position)
val zero = defaultZero(value2.inferType(program).getOr(DataType.UNDEFINED), value2.position)
when(value2) {
is BinaryExpression -> substituteZeroInBinexpr(value2, zero, assign1, assign2)
is PrefixExpression -> substituteZeroInPrefixexpr(value2, zero, assign1, assign2)
+3 -1
View File
@@ -30,7 +30,9 @@ dependencies {
implementation project(':compilerInterfaces')
implementation project(':codeOptimizers')
implementation project(':compilerAst')
implementation project(':codeGeneration')
implementation project(':codeGenTargets')
implementation project(':codeGenCpu6502')
implementation project(':codeGenExperimental6502')
implementation 'org.antlr:antlr4-runtime:4.9.2'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
// implementation "org.jetbrains.kotlin:kotlin-reflect"
+3 -1
View File
@@ -16,9 +16,11 @@
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
<orderEntry type="module" module-name="codeOptimizers" />
<orderEntry type="module" module-name="compilerInterfaces" />
<orderEntry type="module" module-name="codeGeneration" />
<orderEntry type="module" module-name="codeGenTargets" />
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
<orderEntry type="library" name="antlr.antlr4" level="project" />
<orderEntry type="module" module-name="codeGenCpu6502" />
<orderEntry type="module" module-name="codeGenExperimental6502" />
</component>
</module>
-2
View File
@@ -10,8 +10,6 @@ floats {
const float PI = 3.141592653589793
const float TWOPI = 6.283185307179586
float tempvar_swap_float ; used for some swap() operations
; ---- ROM float functions ----
+1
View File
@@ -555,6 +555,7 @@ sys {
}
asmsub memcopy(uword source @R0, uword target @R1, uword count @AY) clobbers(A,X,Y) {
; note: can't be inlined because is called from asm as well
%asm {{
ldx cx16.r0
stx P8ZP_SCRATCH_W1 ; source in ZP
+1
View File
@@ -520,6 +520,7 @@ sys {
}
asmsub memcopy(uword source @R0, uword target @R1, uword count @AY) clobbers(A,X,Y) {
; note: can't be inlined because is called from asm as well
%asm {{
ldx cx16.r0
stx P8ZP_SCRATCH_W1 ; source in ZP
+1 -1
View File
@@ -7,7 +7,7 @@ conv {
; ----- number conversions to decimal strings ----
str string_out = "????????????????" ; result buffer for the string conversion routines
str @shared string_out = "????????????????" ; result buffer for the string conversion routines
asmsub str_ub0 (ubyte value @ A) clobbers(A,Y) {
; ---- convert the ubyte in A in decimal string form, with left padding 0s (3 positions total)
+38
View File
@@ -34,4 +34,42 @@ cx16diskio {
sub load_size(ubyte startbank, uword startaddress, uword endaddress) -> uword {
return $2000 * (cx16.getrambank() - startbank) + endaddress - startaddress
}
asmsub vload(str name @R0, ubyte device @Y, ubyte bank @A, uword address @R1) -> ubyte @A {
; -- like the basic command VLOAD "filename",device,bank,address
; loads a file into Vera's video memory in the given bank:address, returns success in A
%asm {{
; -- load a file into video ram
phx
pha
tya
tax
lda #1
ldy #0
jsr c64.SETLFS
lda cx16.r0
ldy cx16.r0+1
jsr prog8_lib.strlen
tya
ldx cx16.r0
ldy cx16.r0+1
jsr c64.SETNAM
pla
clc
adc #2
ldx cx16.r1
ldy cx16.r1+1
stz P8ZP_SCRATCH_B1
jsr c64.LOAD
bcs +
inc P8ZP_SCRATCH_B1
+ jsr c64.CLRCHN
lda #1
jsr c64.CLOSE
plx
lda P8ZP_SCRATCH_B1
rts
}}
}
}
-2
View File
@@ -13,8 +13,6 @@ floats {
const float PI = 3.141592653589793
const float TWOPI = 6.283185307179586
float tempvar_swap_float ; used for some swap() operations
; ---- ROM float functions ----
+4 -4
View File
@@ -369,7 +369,7 @@ _done
set_both_strides(13) ; 160 increment = 1 line in 640 px 4c mode
color &= 3
color <<= gfx2.plot.shift4c[lsb(x) & 3]
ubyte mask = gfx2.plot.mask4c[lsb(x) & 3]
ubyte @shared mask = gfx2.plot.mask4c[lsb(x) & 3]
repeat height {
%asm {{
lda cx16.VERA_DATA0
@@ -545,9 +545,9 @@ _done
}
sub plot(uword @zp x, uword y, ubyte color) {
ubyte[8] bits = [128, 64, 32, 16, 8, 4, 2, 1]
ubyte[4] mask4c = [%00111111, %11001111, %11110011, %11111100]
ubyte[4] shift4c = [6,4,2,0]
ubyte[8] @shared bits = [128, 64, 32, 16, 8, 4, 2, 1]
ubyte[4] @shared mask4c = [%00111111, %11001111, %11110011, %11111100]
ubyte[4] @shared shift4c = [6,4,2,0]
when active_mode {
1 -> {
+16 -42
View File
@@ -307,13 +307,25 @@ romsub $ff7d = primm()
romsub $ff44 = macptr() clobbers(A,X,Y)
romsub $ff47 = enter_basic(ubyte cold_or_warm @Pc) clobbers(A,X,Y)
romsub $ff68 = mouse_config(ubyte shape @A, ubyte scale @X) clobbers (A, X, Y)
romsub $ff6b = mouse_get(ubyte zpdataptr @X) clobbers(A)
romsub $ff6b = mouse_get(ubyte zpdataptr @X) -> ubyte @A
romsub $ff71 = mouse_scan() clobbers(A, X, Y)
romsub $ff53 = joystick_scan() clobbers(A, X, Y)
romsub $ff56 = joystick_get(ubyte joynr @A) -> ubyte @A, ubyte @X, ubyte @Y
romsub $ff4d = clock_set_date_time(uword yearmonth @R0, uword dayhours @R1, uword minsecs @R2, ubyte jiffies @R3) clobbers(A, X, Y)
romsub $ff50 = clock_get_date_time() clobbers(A, X, Y) -> uword @R0, uword @R1, uword @R2, ubyte @R3 ; result registers see clock_set_date_time()
asmsub mouse_pos() -> ubyte @A {
; -- short wrapper around mouse_get() kernal routine:
; -- gets the position of the mouse cursor in cx16.r0 and cx16.r1 (x/y coordinate), returns mouse button status.
%asm {{
phx
ldx #cx16.r0
jsr cx16.mouse_get
plx
rts
}}
}
; It's not documented what registers are clobbered, so we assume the worst for all following kernal routines...:
@@ -521,45 +533,6 @@ asmsub vpoke_xor(ubyte bank @A, uword address @R0, ubyte value @Y) clobbers (A)
}}
}
asmsub vload(str name @R0, ubyte device @Y, ubyte bank @A, uword address @R1) -> ubyte @A {
; -- like the basic command VLOAD "filename",device,bank,address
; loads a file into video memory in the given bank:address, returns success in A
; !! NOTE !! the V38 ROMs contain a bug in the LOAD code that makes the load address not work correctly,
; it works fine when loading from local filesystem
%asm {{
; -- load a file into video ram
phx
pha
tya
tax
lda #1
ldy #0
jsr c64.SETLFS
lda cx16.r0
ldy cx16.r0+1
jsr prog8_lib.strlen
tya
ldx cx16.r0
ldy cx16.r0+1
jsr c64.SETNAM
pla
clc
adc #2
ldx cx16.r1
ldy cx16.r1+1
stz P8ZP_SCRATCH_B1
jsr c64.LOAD
bcs +
inc P8ZP_SCRATCH_B1
+ jsr c64.CLRCHN
lda #1
jsr c64.CLOSE
plx
lda P8ZP_SCRATCH_B1
rts
}}
}
inline asmsub joystick_get2(ubyte joynr @A) clobbers(Y) -> uword @AX {
; convenience routine to get the joystick state without requiring inline assembly that deals with the multiple return values.
; Also disables interrupts to avoid the IRQ race condition mentioned here: https://github.com/commanderx16/x16-rom/issues/203
@@ -848,11 +821,12 @@ sys {
}}
}
inline asmsub memcopy(uword source @R0, uword target @R1, uword count @AY) clobbers(A,X,Y) {
asmsub memcopy(uword source @R0, uword target @R1, uword count @AY) clobbers(A,X,Y) {
; note: can't be inlined because is called from asm as well
%asm {{
sta cx16.r2
sty cx16.r2+1
jsr cx16.memory_copy
jmp cx16.memory_copy
}}
}
+4 -3
View File
@@ -76,7 +76,8 @@ io_error:
sub list_files(ubyte drivenumber, uword pattern_ptr, uword name_ptrs, ubyte max_names) -> ubyte {
; -- fill the array 'name_ptrs' with (pointers to) the names of the files requested.
uword names_buffer = memory("filenames", 512)
const uword names_buf_size = 800
uword names_buffer = memory("filenames", names_buf_size, 0)
uword buffer_start = names_buffer
ubyte files_found = 0
if lf_start_list(drivenumber, pattern_ptr) {
@@ -87,7 +88,7 @@ io_error:
name_ptrs++
names_buffer += string.copy(diskio.list_filename, names_buffer) + 1
files_found++
if names_buffer - buffer_start > 512-18
if names_buffer - buffer_start > names_buf_size-18
break
if files_found == max_names
break
@@ -406,7 +407,7 @@ io_error:
sub save(ubyte drivenumber, uword filenameptr, uword address, uword size) -> ubyte {
c64.SETNAM(string.length(filenameptr), filenameptr)
c64.SETLFS(1, drivenumber, 0)
uword end_address = address + size
uword @shared end_address = address + size
first_byte = 0 ; result var reuse
%asm {{
+5 -21
View File
@@ -1121,17 +1121,15 @@ strcmp_mem .proc
; Returns -1,0,1 in A, depeding on the ordering. Clobbers Y.
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
_loop ldy #0
lda (P8ZP_SCRATCH_W1),y
ldy #0
_loop lda (P8ZP_SCRATCH_W1),y
bne +
lda (P8ZP_SCRATCH_W2),y
bne _return_minusone
beq _return
+ lda (P8ZP_SCRATCH_W2),y
sec
sbc (P8ZP_SCRATCH_W1),y
bmi _return_one
bne _return_minusone
+ cmp (P8ZP_SCRATCH_W2),y
bcc _return_minusone
bne _return_one
inc P8ZP_SCRATCH_W1
bne +
inc P8ZP_SCRATCH_W1+1
@@ -1158,20 +1156,6 @@ sign_extend_stack_byte .proc
rts
.pend
sign_extend_AY_byte .proc
; -- sign extend the (signed) byte in AY to full 16 bits
pha
and #$80
beq +
ldy #$ff
pla
rts
+ ldy #0
pla
rts
.pend
strlen .proc
; -- returns the number of bytes in the string in AY, in Y.
sta P8ZP_SCRATCH_W1
-18
View File
@@ -6,24 +6,6 @@ prog8_lib {
%asminclude "library:prog8_lib.asm"
%asminclude "library:prog8_funcs.asm"
; to store intermediary expression results for return values:
; NOTE: these variables are used in the StatementReorderer and StatementOptimizer
uword @zp retval_interm_uw
word @zp retval_interm_w
ubyte @zp retval_interm_ub
byte @zp retval_interm_b
word retval_interm_w2
byte retval_interm_b2
; prog8 "hooks" to be able to access the temporary scratch variables
; YOU SHOULD NOT USE THESE IN USER CODE - THESE ARE MEANT FOR INTERNAL COMPILER USE
; NOTE: the assembly code generator will match these names and not generate
; new variables/memdefs for them, rather, they'll point to the scratch variables directly.
&ubyte P8ZP_SCRATCH_REG = $ff
&byte P8ZP_SCRATCH_B1 = $ff
&uword P8ZP_SCRATCH_W1 = $ff
&word P8ZP_SCRATCH_W2 = $ff
asmsub pattern_match(str string @AY, str pattern @R0) clobbers(Y) -> ubyte @A {
%asm {{
+7 -14
View File
@@ -130,11 +130,11 @@ _startloop dey
}}
}
asmsub find(uword string @R0, ubyte character @A) -> uword @AY {
asmsub find(uword string @R0, ubyte character @A) -> ubyte @A, ubyte @Pc {
; Locates the first position of the given character in the string,
; returns the string starting with this character or $0000 if the character is not found.
; returns Carry set if found + index in A, or Carry clear if not found.
%asm {{
; need to copy the the cx16 virtual registers to zeropage to be compatible with C64...
; need to copy the the cx16 virtual registers to zeropage to make this run on C64...
sta P8ZP_SCRATCH_B1
lda cx16.r0
ldy cx16.r0+1
@@ -147,18 +147,11 @@ _startloop dey
beq _found
iny
bne -
_notfound lda #0
ldy #0
_notfound clc
rts
_found sty P8ZP_SCRATCH_B1
ldy P8ZP_SCRATCH_W1+1
lda P8ZP_SCRATCH_W1
clc
adc P8ZP_SCRATCH_B1
bcc +
iny
+ rts
_found tya
sec
rts
}}
}
+1 -1
View File
@@ -1 +1 @@
7.7
7.8
+7 -4
View File
@@ -3,11 +3,11 @@ package prog8
import kotlinx.cli.*
import prog8.ast.base.AstException
import prog8.codegen.target.C128Target
import prog8.codegen.target.C64Target
import prog8.codegen.target.Cx16Target
import prog8.compiler.CompilationResult
import prog8.compiler.CompilerArguments
import prog8.compiler.compileProgram
import prog8.codegen.target.C64Target
import prog8.codegen.target.Cx16Target
import java.io.File
import java.nio.file.FileSystems
import java.nio.file.Path
@@ -43,7 +43,8 @@ private fun compileMain(args: Array<String>): Boolean {
val slowCodegenWarnings by cli.option(ArgType.Boolean, fullName = "slowwarn", description="show debug warnings about slow/problematic assembly code generation")
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler output results")
val asmListfile by cli.option(ArgType.Boolean, fullName = "asmlist", description = "make the assembler produce a listing file as well")
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.name}', '${C128Target.name}', '${Cx16Target.name}')").default(C64Target.name)
val experimentalCodegen by cli.option(ArgType.Boolean, fullName = "expericodegen", description = "use experimental codegen")
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of '${C64Target.NAME}', '${C128Target.NAME}', '${Cx16Target.NAME}')").default(C64Target.NAME)
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
@@ -70,7 +71,7 @@ private fun compileMain(args: Array<String>): Boolean {
if(srcdirs.firstOrNull()!=".")
srcdirs.add(0, ".")
if (compilationTarget !in setOf(C64Target.name, C128Target.name, Cx16Target.name)) {
if (compilationTarget !in setOf(C64Target.NAME, C128Target.NAME, Cx16Target.NAME)) {
System.err.println("Invalid compilation target: $compilationTarget")
return false
}
@@ -93,6 +94,7 @@ private fun compileMain(args: Array<String>): Boolean {
slowCodegenWarnings == true,
quietAssembler == true,
asmListfile == true,
experimentalCodegen == true,
compilationTarget,
srcdirs,
outputPath
@@ -143,6 +145,7 @@ private fun compileMain(args: Array<String>): Boolean {
slowCodegenWarnings == true,
quietAssembler == true,
asmListfile == true,
experimentalCodegen == true,
compilationTarget,
srcdirs,
outputPath
+78 -49
View File
@@ -1,25 +1,28 @@
package prog8.compiler
import com.github.michaelbull.result.*
import com.github.michaelbull.result.onFailure
import prog8.ast.AstToSourceTextConverter
import prog8.ast.IBuiltinFunctions
import prog8.ast.IStatementContainer
import prog8.ast.Program
import prog8.ast.base.AstException
import prog8.ast.base.Position
import prog8.ast.expressions.Expression
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.Directive
import prog8.ast.statements.VarDecl
import prog8.ast.walk.IAstVisitor
import prog8.codegen.target.C128Target
import prog8.compiler.astprocessing.*
import prog8.codegen.target.C64Target
import prog8.codegen.target.Cx16Target
import prog8.codegen.target.cpu6502.codegen.AsmGen
import prog8.compiler.astprocessing.*
import prog8.compilerinterface.*
import prog8.optimizer.*
import prog8.parser.ParseError
import java.nio.file.Path
import kotlin.io.path.Path
import kotlin.io.path.nameWithoutExtension
import kotlin.math.round
import kotlin.system.measureTimeMillis
@@ -37,6 +40,7 @@ class CompilerArguments(val filepath: Path,
val slowCodegenWarnings: Boolean,
val quietAssembler: Boolean,
val asmListfile: Boolean,
val experimentalCodegen: Boolean,
val compilationTarget: String,
val sourceDirs: List<String> = emptyList(),
val outputDir: Path = Path(""),
@@ -52,9 +56,9 @@ fun compileProgram(args: CompilerArguments): CompilationResult {
val compTarget =
when(args.compilationTarget) {
C64Target.name -> C64Target
C128Target.name -> C128Target
Cx16Target.name -> Cx16Target
C64Target.NAME -> C64Target()
C128Target.NAME -> C128Target()
Cx16Target.NAME -> Cx16Target()
else -> throw IllegalArgumentException("invalid compilation target")
}
@@ -62,6 +66,9 @@ fun compileProgram(args: CompilerArguments): CompilationResult {
val totalTime = measureTimeMillis {
// import main module and everything it needs
val (programresult, compilationOptions, imported) = parseImports(args.filepath, args.errors, compTarget, args.sourceDirs)
print("Parsed ${args.filepath}")
ModuleImporter.ansiEraseRestOfLine(true)
with(compilationOptions) {
slowCodegenWarnings = args.slowCodegenWarnings
optimize = args.optimize
@@ -69,6 +76,8 @@ fun compileProgram(args: CompilerArguments): CompilationResult {
dontReinitGlobals = args.dontReinitGlobals
asmQuiet = args.quietAssembler
asmListfile = args.asmListfile
experimentalCodegen = args.experimentalCodegen
outputDir = args.outputDir.normalize()
}
program = programresult
importedFiles = imported
@@ -91,7 +100,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult {
// printProgram(program)
if (args.writeAssembly) {
when (val result = writeAssembly(program, args.errors, args.outputDir, compilationOptions)) {
when (val result = writeAssembly(program, args.errors, compilationOptions)) {
is WriteAssemblyResult.Ok -> programName = result.filename
is WriteAssemblyResult.Fail -> {
System.err.println(result.error)
@@ -102,34 +111,35 @@ fun compileProgram(args: CompilerArguments): CompilationResult {
}
System.out.flush()
System.err.flush()
println("\nTotal compilation+assemble time: ${totalTime / 1000.0} sec.")
val seconds = totalTime/1000.0
println("\nTotal compilation+assemble time: ${round(seconds*100.0)/100.0} sec.")
return CompilationResult(true, program, programName, compTarget, importedFiles)
} catch (px: ParseError) {
System.err.print("\u001b[91m") // bright red
System.err.print("\n\u001b[91m") // bright red
System.err.println("${px.position.toClickableStr()} parse error: ${px.message}".trim())
System.err.print("\u001b[0m") // reset
} catch (ac: AbortCompilation) {
if(!ac.message.isNullOrEmpty()) {
System.err.print("\u001b[91m") // bright red
System.err.print("\n\u001b[91m") // bright red
System.err.println(ac.message)
System.err.print("\u001b[0m") // reset
}
} catch (nsf: NoSuchFileException) {
System.err.print("\u001b[91m") // bright red
System.err.print("\n\u001b[91m") // bright red
System.err.println("File not found: ${nsf.message}")
System.err.print("\u001b[0m") // reset
} catch (ax: AstException) {
System.err.print("\u001b[91m") // bright red
System.err.print("\n\u001b[91m") // bright red
System.err.println(ax.toString())
System.err.print("\u001b[0m") // reset
} catch (x: Exception) {
print("\u001b[91m") // bright red
print("\n\u001b[91m") // bright red
println("\n* internal error *")
print("\u001b[0m") // reset
System.out.flush()
throw x
} catch (x: NotImplementedError) {
print("\u001b[91m") // bright red
print("\n\u001b[91m") // bright red
println("\n* internal error: missing feature/code *")
print("\u001b[0m") // reset
System.out.flush()
@@ -146,7 +156,7 @@ private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuilt
override val names = functions.keys
override val purefunctionNames = functions.filter { it.value.pure }.map { it.key }.toSet()
override fun constValue(name: String, args: List<Expression>, position: Position): NumericLiteralValue? {
override fun constValue(name: String, args: List<Expression>, position: Position): NumericLiteral? {
val func = BuiltinFunctions[name]
if(func!=null) {
val exprfunc = func.constExpressionFunc
@@ -174,7 +184,7 @@ fun parseImports(filepath: Path,
errors: IErrorReporter,
compTarget: ICompilationTarget,
sourceDirs: List<String>): Triple<Program, CompilationOptions, List<Path>> {
println("Compiler target: ${compTarget.name}. Parsing...")
println("Compiler target: ${compTarget.name}")
val bf = BuiltinFunctionsFacade(BuiltinFunctions)
val program = Program(filepath.nameWithoutExtension, bf, compTarget, compTarget)
bf.program = program
@@ -226,7 +236,7 @@ fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget
// error will be printed by the astchecker
}
if (zpType == ZeropageType.FLOATSAFE && compTarget.name == Cx16Target.name) {
if (zpType == ZeropageType.FLOATSAFE && compTarget.name == Cx16Target.NAME) {
System.err.println("Warning: zp option floatsafe changed to basicsafe for cx16 target")
zpType = ZeropageType.BASICSAFE
}
@@ -266,9 +276,9 @@ fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget
private fun processAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
// perform initial syntax checks and processings
println("Processing for target ${compilerOptions.compTarget.name}...")
program.preprocessAst(program, errors)
program.checkIdentifiers(errors, program, compilerOptions)
println("Analyzing code...")
program.preprocessAst(errors)
program.checkIdentifiers(errors, compilerOptions)
errors.report()
// TODO: turning char literals into UBYTEs via an encoding should really happen in code gen - but for that we'd need DataType.CHAR
// ...but what do we gain from this? We can leave it as it is now: where a char literal is no more than syntactic sugar for an UBYTE value.
@@ -283,11 +293,11 @@ private fun processAst(program: Program, errors: IErrorReporter, compilerOptions
errors.report()
program.addTypecasts(errors, compilerOptions)
errors.report()
program.variousCleanups(program, errors, compilerOptions)
program.variousCleanups(errors, compilerOptions)
errors.report()
program.checkValid(errors, compilerOptions)
errors.report()
program.checkIdentifiers(errors, program, compilerOptions)
program.checkIdentifiers(errors, compilerOptions)
errors.report()
}
@@ -316,7 +326,7 @@ private fun postprocessAst(program: Program, errors: IErrorReporter, compilerOpt
program.desugaring(errors)
program.addTypecasts(errors, compilerOptions)
errors.report()
program.variousCleanups(program, errors, compilerOptions)
program.variousCleanups(errors, compilerOptions)
val callGraph = CallGraph(program)
callGraph.checkRecursiveCalls(errors)
errors.report()
@@ -333,37 +343,56 @@ private sealed class WriteAssemblyResult {
private fun writeAssembly(program: Program,
errors: IErrorReporter,
outputDir: Path,
compilerOptions: CompilationOptions
): WriteAssemblyResult {
// asm generation directly from the Ast
program.processAstBeforeAsmGeneration(compilerOptions, errors)
compilerOptions.compTarget.machine.initializeZeropage(compilerOptions)
val variables = VariableExtractor().extractVars(program)
program.processAstBeforeAsmGeneration(compilerOptions, variables, errors)
errors.report()
// TODO make removing all VarDecls work, but this needs inferType to be able to get its information from somewhere else as the VarDecl nodes in the Ast,
// or don't use inferType at all anymore and "bake the type information" into the Ast somehow.
// Note: we don't actually *need* to remove the VarDecl nodes, but it is nice as a temporary measure
// to help clean up the code that still depends on them.
// removeAllVardeclsFromAst(program)
// println("*********** AST RIGHT BEFORE ASM GENERATION *************")
// printProgram(program)
compilerOptions.compTarget.machine.initializeZeropage(compilerOptions)
val assembly = asmGeneratorFor(compilerOptions.compTarget,
program,
errors,
compilerOptions.compTarget.machine.zeropage,
compilerOptions,
outputDir).compileToAssembly()
val assembly = asmGeneratorFor(program, errors, variables, compilerOptions).compileToAssembly()
errors.report()
return if(assembly.valid && errors.noErrors()) {
val assemblerReturnStatus = assembly.assemble(compilerOptions)
if(assemblerReturnStatus!=0)
WriteAssemblyResult.Fail("assembler step failed with return code $assemblerReturnStatus")
else {
return if(assembly!=null && errors.noErrors()) {
if(assembly.assemble(compilerOptions)) {
WriteAssemblyResult.Ok(assembly.name)
}
} else
WriteAssemblyResult.Fail("assembler step failed")
} else {
WriteAssemblyResult.Fail("compiler failed with errors")
}
}
private fun removeAllVardeclsFromAst(program: Program) {
// remove all VarDecl nodes from the AST.
// code generation doesn't require them anymore, it operates only on the 'variables' collection.
class SearchAndRemove: IAstVisitor {
private val allVars = mutableListOf<VarDecl>()
init {
visit(program)
for (it in allVars) {
require((it.parent as IStatementContainer).statements.remove(it))
}
}
override fun visit(decl: VarDecl) {
allVars.add(decl)
}
}
SearchAndRemove()
}
fun printProgram(program: Program) {
println()
val printer = AstToSourceTextConverter(::print, program)
@@ -371,15 +400,15 @@ fun printProgram(program: Program) {
println()
}
internal fun asmGeneratorFor(
compTarget: ICompilationTarget,
program: Program,
errors: IErrorReporter,
zp: Zeropage,
options: CompilationOptions,
outputDir: Path
): IAssemblyGenerator
internal fun asmGeneratorFor(program: Program, errors: IErrorReporter, variables: IVariablesAndConsts, options: CompilationOptions): IAssemblyGenerator
{
// at the moment we only have one code generation backend (for 6502 and 65c02)
return AsmGen(program, errors, zp, options, compTarget, outputDir)
if(options.experimentalCodegen) {
if (options.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02))
return prog8.codegen.experimental6502.AsmGen(program, errors, variables, options)
} else {
if (options.compTarget.machine.cpu in arrayOf(CpuType.CPU6502, CpuType.CPU65c02))
return prog8.codegen.cpu6502.AsmGen(program, errors, variables, options)
}
throw NotImplementedError("no asm generator for cpu ${options.compTarget.machine.cpu}")
}
+17 -7
View File
@@ -39,11 +39,8 @@ class ModuleImporter(private val program: Program,
else -> candidates.first() // when more candiates, pick the one from the first location
}
val logMsg = "importing '${filePath.nameWithoutExtension}' (from file $srcPath)"
println(logMsg)
val module = importModule(SourceCode.File(srcPath))
return Ok(module)
val source = SourceCode.File(srcPath)
return Ok(importModule(source))
}
fun importLibraryModule(name: String): Module? {
@@ -54,6 +51,7 @@ class ModuleImporter(private val program: Program,
}
private fun importModule(src: SourceCode) : Module {
printImportingMessage(src.name, src.origin)
val moduleAst = Prog8Parser.parseModule(src)
program.addModule(moduleAst)
@@ -91,7 +89,6 @@ class ModuleImporter(private val program: Program,
val importedModule =
moduleResourceSrc.fold(
success = {
println("importing '$moduleName' (from internal ${it.origin})")
importModule(it)
},
failure = {
@@ -99,7 +96,6 @@ class ModuleImporter(private val program: Program,
val moduleSrc = getModuleFromFile(moduleName, importingModule)
moduleSrc.fold(
success = {
println("importing '$moduleName' (from file ${it.origin})")
importModule(it)
},
failure = {
@@ -152,4 +148,18 @@ class ModuleImporter(private val program: Program,
return Err(NoSuchFileException(File("name")))
}
fun printImportingMessage(module: String, origin: String) {
print(" importing '$module' (from ${origin})")
ansiEraseRestOfLine(false)
print("\r")
}
companion object {
fun ansiEraseRestOfLine(newline: Boolean) {
print("\u001b[0K")
if(newline)
println()
}
}
}
@@ -10,6 +10,9 @@ import java.io.CharConversionException
import java.io.File
import kotlin.io.path.Path
/**
* Semantic analysis.
*/
internal class AstChecker(private val program: Program,
private val errors: IErrorReporter,
private val compilerOptions: CompilationOptions
@@ -139,8 +142,8 @@ internal class AstChecker(private val program: Program,
// check loop range values
val range = forLoop.iterable as? RangeExpression
if(range!=null) {
val from = range.from as? NumericLiteralValue
val to = range.to as? NumericLiteralValue
val from = range.from as? NumericLiteral
val to = range.to as? NumericLiteral
if(from != null)
checkValueTypeAndRange(loopvar.datatype, from)
else if(range.from.inferType(program) isnot loopvar.datatype)
@@ -205,8 +208,7 @@ internal class AstChecker(private val program: Program,
is Label,
is VarDecl,
is InlineAssembly,
is IStatementContainer,
is Nop -> true
is IStatementContainer -> true
is Assignment -> {
val target = statement.target.identifier!!.targetStatement(program)
target === statement.previousSibling() // an initializer assignment is okay
@@ -451,6 +453,10 @@ internal class AstChecker(private val program: Program,
errors.err("typecasting a float value in-place makes no sense", assignment.value.position)
}
val numvalue = assignment.value.constValue(program)
if(numvalue!=null && targetDt.isKnown)
checkValueTypeAndRange(targetDt.getOr(DataType.UNDEFINED), numvalue)
super.visit(assignment)
}
@@ -493,7 +499,7 @@ internal class AstChecker(private val program: Program,
val sourceDatatype = assignment.value.inferType(program)
if (sourceDatatype.isUnknown) {
if (assignment.value !is FunctionCallExpression)
errors.err("assignment value is invalid or has no proper datatype", assignment.value.position)
errors.err("assignment value is invalid or has no proper datatype, maybe forgot '&' (address-of)", assignment.value.position)
} else {
checkAssignmentCompatible(targetDatatype.getOr(DataType.BYTE),
sourceDatatype.getOr(DataType.BYTE), assignment.value)
@@ -535,7 +541,7 @@ internal class AstChecker(private val program: Program,
err("array variable is missing a size specification or an initialization value")
return
}
if(decl.value is NumericLiteralValue) {
if(decl.value is NumericLiteral) {
err("unsized array declaration cannot use a single literal initialization value")
return
}
@@ -550,15 +556,15 @@ internal class AstChecker(private val program: Program,
// a vardecl without an initial value, don't bother with it
}
is RangeExpression -> throw FatalAstException("range expression should have been converted to a true array value")
is StringLiteralValue -> {
checkValueTypeAndRangeString(decl.datatype, decl.value as StringLiteralValue)
is StringLiteral -> {
checkValueTypeAndRangeString(decl.datatype, decl.value as StringLiteral)
}
is ArrayLiteralValue -> {
val arraySpec = decl.arraysize ?: ArrayIndex.forArray(decl.value as ArrayLiteralValue)
checkValueTypeAndRangeArray(decl.datatype, arraySpec, decl.value as ArrayLiteralValue)
is ArrayLiteral -> {
val arraySpec = decl.arraysize ?: ArrayIndex.forArray(decl.value as ArrayLiteral)
checkValueTypeAndRangeArray(decl.datatype, arraySpec, decl.value as ArrayLiteral)
}
is NumericLiteralValue -> {
checkValueTypeAndRange(decl.datatype, decl.value as NumericLiteralValue)
is NumericLiteral -> {
checkValueTypeAndRange(decl.datatype, decl.value as NumericLiteral)
}
else -> {
if(decl.type==VarDeclType.CONST) {
@@ -586,7 +592,7 @@ internal class AstChecker(private val program: Program,
else -> {}
}
}
val numvalue = decl.value as? NumericLiteralValue
val numvalue = decl.value as? NumericLiteral
if(numvalue!=null) {
if (numvalue.type !in IntegerDatatypes || numvalue.number.toInt() < 0 || numvalue.number.toInt() > 65535) {
err("memory address must be valid integer 0..\$ffff")
@@ -636,8 +642,6 @@ internal class AstChecker(private val program: Program,
if(parameter==null)
err("string var must be initialized with a string literal")
}
else if (decl.type==VarDeclType.VAR && decl.value !is StringLiteralValue)
err("string var can only be initialized with a string literal")
}
if(compilerOptions.zeropage==ZeropageType.DONTUSE && decl.zeropage==ZeropageWish.REQUIRE_ZEROPAGE)
@@ -749,7 +753,7 @@ internal class AstChecker(private val program: Program,
errors.err("included file not found: $filename", directive.position)
}
override fun visit(array: ArrayLiteralValue) {
override fun visit(array: ArrayLiteral) {
if(array.type.isKnown) {
if (!compilerOptions.floats && array.type.oneOf(DataType.FLOAT, DataType.ARRAY_F)) {
errors.err("floating point used, but that is not enabled via options", array.position)
@@ -763,11 +767,11 @@ internal class AstChecker(private val program: Program,
val decl = e.targetVarDecl(program)!!
return decl.datatype in PassByReferenceDatatypes
}
return e is StringLiteralValue
return e is StringLiteral
}
if(array.parent is VarDecl) {
if (!array.value.all { it is NumericLiteralValue || it is AddressOf || isPassByReferenceElement(it) })
if (!array.value.all { it is NumericLiteral || it is AddressOf || isPassByReferenceElement(it) })
errors.err("array literal for variable initialization contains non-constant elements", array.position)
} else if(array.parent is ForLoop) {
if (!array.value.all { it.constValue(program) != null })
@@ -787,7 +791,7 @@ internal class AstChecker(private val program: Program,
super.visit(char)
}
override fun visit(string: StringLiteralValue) {
override fun visit(string: StringLiteral) {
checkValueTypeAndRangeString(DataType.STR, string)
try { // just *try* if it can be encoded, don't actually do it
@@ -833,6 +837,13 @@ internal class AstChecker(private val program: Program,
val leftDt = leftIDt.getOr(DataType.UNDEFINED)
val rightDt = rightIDt.getOr(DataType.UNDEFINED)
if(expr.operator=="+" || expr.operator=="-") {
if(leftDt == DataType.STR || rightDt == DataType.STR || leftDt in ArrayDatatypes || rightDt in ArrayDatatypes) {
errors.err("missing & (address-of) on the string operand", expr.position)
return
}
}
when(expr.operator){
"/", "%" -> {
val constvalRight = expr.right.constValue(program)
@@ -869,8 +880,15 @@ internal class AstChecker(private val program: Program,
errors.err("left operand is not numeric or str", expr.left.position)
if(rightDt!in NumericDatatypes && rightDt != DataType.STR)
errors.err("right operand is not numeric or str", expr.right.position)
if(leftDt!=rightDt)
errors.err("left and right operands aren't the same type", expr.left.position)
if(leftDt!=rightDt) {
if(leftDt==DataType.STR && rightDt in IntegerDatatypes) {
// only 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)
} else {
errors.err("left and right operands aren't the same type", expr.left.position)
}
}
if(expr.operator !in ComparisonOperators) {
if (leftDt == DataType.STR && rightDt == DataType.STR || leftDt in ArrayDatatypes && rightDt in ArrayDatatypes) {
@@ -888,7 +906,7 @@ internal class AstChecker(private val program: Program,
if(!typecast.expression.inferType(program).isKnown)
errors.err("this expression doesn't return a value", typecast.expression.position)
if(typecast.expression is NumericLiteralValue)
if(typecast.expression is NumericLiteral)
errors.err("can't cast the value to the requested target type", typecast.expression.position)
super.visit(typecast)
@@ -1079,9 +1097,7 @@ internal class AstChecker(private val program: Program,
}
if(ident!=null && ident.nameInSource[0] == "cx16" && ident.nameInSource[1].startsWith("r")) {
var regname = ident.nameInSource[1].uppercase()
if(regname.endsWith('L'))
regname=regname.substring(0, regname.length-1)
if(regname.endsWith('s'))
if(regname.endsWith('L') || regname.endsWith('H') || regname.endsWith('s'))
regname=regname.substring(0, regname.length-1)
val reg = RegisterOrPair.valueOf(regname)
val same = params.filter { it.value.registerOrPair==reg }
@@ -1137,9 +1153,9 @@ internal class AstChecker(private val program: Program,
if(index!=null && (index<0 || index>=arraysize))
errors.err("array index out of bounds", arrayIndexedExpression.indexer.position)
} else if(target.datatype == DataType.STR) {
if(target.value is StringLiteralValue) {
if(target.value is StringLiteral) {
// check string lengths for non-memory mapped strings
val stringLen = (target.value as StringLiteralValue).value.length
val stringLen = (target.value as StringLiteral).value.length
val index = arrayIndexedExpression.indexer.constIndex()
if (index != null && (index < 0 || index >= stringLen))
errors.err("index out of bounds", arrayIndexedExpression.indexer.position)
@@ -1308,13 +1324,23 @@ internal class AstChecker(private val program: Program,
private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: Statement): Statement? {
when (val targetStatement = target.targetStatement(program)) {
is Label, is Subroutine, is BuiltinFunctionPlaceholder -> return targetStatement
null -> errors.err("undefined function or subroutine: ${target.nameInSource.joinToString(".")}", statement.position)
else -> errors.err("cannot call that: ${target.nameInSource.joinToString(".")}", statement.position)
is VarDecl -> {
if(statement is Jump) {
if (targetStatement.datatype == DataType.UWORD)
return targetStatement
else
errors.err("wrong address variable datatype, expected uword", target.position)
}
else
errors.err("cannot call that: ${target.nameInSource.joinToString(".")}", target.position)
}
null -> errors.err("undefined function or subroutine: ${target.nameInSource.joinToString(".")}", target.position)
else -> errors.err("cannot call that: ${target.nameInSource.joinToString(".")}", target.position)
}
return null
}
private fun checkValueTypeAndRangeString(targetDt: DataType, value: StringLiteralValue) : Boolean {
private fun checkValueTypeAndRangeString(targetDt: DataType, value: StringLiteral) : Boolean {
return if (targetDt == DataType.STR) {
when {
value.value.length > 255 -> {
@@ -1335,7 +1361,7 @@ internal class AstChecker(private val program: Program,
else false
}
private fun checkValueTypeAndRangeArray(targetDt: DataType, arrayspec: ArrayIndex, value: ArrayLiteralValue) : Boolean {
private fun checkValueTypeAndRangeArray(targetDt: DataType, arrayspec: ArrayIndex, value: ArrayLiteral) : Boolean {
fun err(msg: String) : Boolean {
errors.err(msg, value.position)
return false
@@ -1412,7 +1438,7 @@ internal class AstChecker(private val program: Program,
}
}
private fun checkValueTypeAndRange(targetDt: DataType, value: NumericLiteralValue) : Boolean {
private fun checkValueTypeAndRange(targetDt: DataType, value: NumericLiteral) : Boolean {
fun err(msg: String) : Boolean {
errors.err(msg, value.position)
return false
@@ -1456,10 +1482,10 @@ internal class AstChecker(private val program: Program,
return true
}
private fun checkArrayValues(value: ArrayLiteralValue, type: DataType): Boolean {
private fun checkArrayValues(value: ArrayLiteral, type: DataType): Boolean {
val array = value.value.map {
when (it) {
is NumericLiteralValue -> it.number.toInt()
is NumericLiteral -> it.number.toInt()
is AddressOf -> it.identifier.hashCode() and 0xffff
is TypecastExpression -> {
val constVal = it.expression.constValue(program)
@@ -5,7 +5,7 @@ import prog8.ast.Program
import prog8.ast.base.DataType
import prog8.ast.expressions.CharLiteral
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.Directive
import prog8.ast.statements.VarDeclOrigin
import prog8.ast.walk.AstWalker
@@ -13,17 +13,19 @@ import prog8.ast.walk.IAstModification
import prog8.compilerinterface.CompilationOptions
import prog8.compilerinterface.IErrorReporter
import prog8.compilerinterface.IStringEncoding
import prog8.compilerinterface.IVariablesAndConsts
internal fun Program.checkValid(errors: IErrorReporter, compilerOptions: CompilationOptions) {
// semantic analysis to see if the program is valid.
val parentChecker = ParentNodeChecker()
parentChecker.visit(this)
val checker = AstChecker(this, errors, compilerOptions)
checker.visit(this)
}
internal fun Program.processAstBeforeAsmGeneration(compilerOptions: CompilationOptions, errors: IErrorReporter) {
val fixer = BeforeAsmAstChanger(this, compilerOptions, errors)
internal fun Program.processAstBeforeAsmGeneration(compilerOptions: CompilationOptions, variables: IVariablesAndConsts, errors: IErrorReporter) {
val fixer = BeforeAsmAstChanger(this, compilerOptions, variables, errors)
fixer.visit(this)
while(errors.noErrors() && fixer.applyModifications()>0) {
fixer.visit(this)
@@ -51,7 +53,7 @@ internal fun Program.charLiteralsToUByteLiterals(enc: IStringEncoding) {
override fun after(char: CharLiteral, parent: Node): Iterable<IAstModification> {
return listOf(IAstModification.ReplaceNode(
char,
NumericLiteralValue(DataType.UBYTE, enc.encodeString(char.value.toString(), char.encoding)[0].toDouble(), char.position),
NumericLiteral(DataType.UBYTE, enc.encodeString(char.value.toString(), char.encoding)[0].toDouble(), char.position),
parent
))
}
@@ -77,17 +79,17 @@ internal fun Program.verifyFunctionArgTypes() {
fixer.visit(this)
}
internal fun Program.preprocessAst(program: Program, errors: IErrorReporter) {
val transforms = AstPreprocessor(program, errors)
internal fun Program.preprocessAst(errors: IErrorReporter) {
val transforms = AstPreprocessor(this, errors)
transforms.visit(this)
var mods = transforms.applyModifications()
while(mods>0)
mods = transforms.applyModifications()
}
internal fun Program.checkIdentifiers(errors: IErrorReporter, program: Program, options: CompilationOptions) {
internal fun Program.checkIdentifiers(errors: IErrorReporter, options: CompilationOptions) {
val checker2 = AstIdentifiersChecker(errors, program, options.compTarget)
val checker2 = AstIdentifiersChecker(errors, this, options.compTarget)
checker2.visit(this)
if(errors.noErrors()) {
@@ -101,8 +103,8 @@ internal fun Program.checkIdentifiers(errors: IErrorReporter, program: Program,
}
}
internal fun Program.variousCleanups(program: Program, errors: IErrorReporter, options: CompilationOptions) {
val process = VariousCleanups(program, errors, options)
internal fun Program.variousCleanups(errors: IErrorReporter, options: CompilationOptions) {
val process = VariousCleanups(this, errors, options)
process.visit(this)
if(errors.noErrors())
process.applyModifications()
@@ -5,7 +5,7 @@ import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.Position
import prog8.ast.expressions.FunctionCallExpression
import prog8.ast.expressions.StringLiteralValue
import prog8.ast.expressions.StringLiteral
import prog8.ast.statements.*
import prog8.ast.walk.IAstVisitor
import prog8.compilerinterface.BuiltinFunctions
@@ -122,7 +122,7 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
super.visit(label)
}
override fun visit(string: StringLiteralValue) {
override fun visit(string: StringLiteral) {
if (string.value.length > 255)
errors.err("string literal length max is 255", string.position)
@@ -147,7 +147,7 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
errors.err("invalid number of arguments", pos)
}
if(func.name=="memory") {
val name = call.args[0] as? StringLiteralValue
val name = call.args[0] as? StringLiteral
if(name!=null) {
val processed = name.value.map {
if(it.isLetterOrDigit())
@@ -155,7 +155,7 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
else
'_'
}.joinToString("")
call.args[0] = StringLiteralValue(processed, Encoding.PETSCII, name.position)
call.args[0] = StringLiteral(processed, Encoding.PETSCII, name.position)
call.args[0].linkParents(call as Node)
}
}
@@ -2,7 +2,9 @@ package prog8.compiler.astprocessing
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.base.NumericDatatypes
import prog8.ast.base.SyntaxError
import prog8.ast.base.VarDeclType
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
@@ -15,7 +17,7 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter) : AstWal
override fun after(range: RangeExpression, parent: Node): Iterable<IAstModification> {
// has to be done before the constant folding, otherwise certain checks there will fail on invalid range sizes
val modifications = mutableListOf<IAstModification>()
if(range.from !is NumericLiteralValue) {
if(range.from !is NumericLiteral) {
try {
val constval = range.from.constValue(program)
if (constval != null)
@@ -24,7 +26,7 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter) : AstWal
// syntax errors will be reported later
}
}
if(range.to !is NumericLiteralValue) {
if(range.to !is NumericLiteral) {
try {
val constval = range.to.constValue(program)
if(constval!=null)
@@ -33,7 +35,7 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter) : AstWal
// syntax errors will be reported later
}
}
if(range.step !is NumericLiteralValue) {
if(range.step !is NumericLiteral) {
try {
val constval = range.step.constValue(program)
if(constval!=null)
@@ -6,8 +6,11 @@ import prog8.ast.base.DataType
import prog8.ast.expressions.ArrayIndexedExpression
import prog8.ast.expressions.BinaryExpression
import prog8.ast.expressions.DirectMemoryRead
import prog8.ast.expressions.StringLiteralValue
import prog8.ast.statements.*
import prog8.ast.expressions.StringLiteral
import prog8.ast.statements.AssignTarget
import prog8.ast.statements.DirectMemoryWrite
import prog8.ast.statements.Subroutine
import prog8.ast.statements.VarDecl
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
@@ -37,8 +40,8 @@ internal class AstVariousTransforms(private val program: Program) : AstWalker()
}
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
val leftStr = expr.left as? StringLiteralValue
val rightStr = expr.right as? StringLiteralValue
val leftStr = expr.left as? StringLiteral
val rightStr = expr.right as? StringLiteral
if(expr.operator == "+") {
val concatenatedString = concatString(expr)
if(concatenatedString!=null)
@@ -49,7 +52,7 @@ internal class AstVariousTransforms(private val program: Program) : AstWalker()
val amount = expr.right.constValue(program)
if(amount!=null) {
val string = leftStr.value.repeat(amount.number.toInt())
val strval = StringLiteralValue(string, leftStr.encoding, expr.position)
val strval = StringLiteral(string, leftStr.encoding, expr.position)
return listOf(IAstModification.ReplaceNode(expr, strval, parent))
}
}
@@ -57,7 +60,7 @@ internal class AstVariousTransforms(private val program: Program) : AstWalker()
val amount = expr.right.constValue(program)
if(amount!=null) {
val string = rightStr.value.repeat(amount.number.toInt())
val strval = StringLiteralValue(string, rightStr.encoding, expr.position)
val strval = StringLiteral(string, rightStr.encoding, expr.position)
return listOf(IAstModification.ReplaceNode(expr, strval, parent))
}
}
@@ -70,9 +73,9 @@ internal class AstVariousTransforms(private val program: Program) : AstWalker()
return replacePointerVarIndexWithMemreadOrMemwrite(program, arrayIndexedExpression, parent)
}
private fun concatString(expr: BinaryExpression): StringLiteralValue? {
val rightStrval = expr.right as? StringLiteralValue
val leftStrval = expr.left as? StringLiteralValue
private fun concatString(expr: BinaryExpression): StringLiteral? {
val rightStrval = expr.right as? StringLiteral
val leftStrval = expr.left as? StringLiteral
return when {
expr.operator!="+" -> null
expr.left is BinaryExpression && rightStrval!=null -> {
@@ -80,17 +83,17 @@ internal class AstVariousTransforms(private val program: Program) : AstWalker()
if(subStrVal==null)
null
else
StringLiteralValue("${subStrVal.value}${rightStrval.value}", subStrVal.encoding, rightStrval.position)
StringLiteral("${subStrVal.value}${rightStrval.value}", subStrVal.encoding, rightStrval.position)
}
expr.right is BinaryExpression && leftStrval!=null -> {
val subStrVal = concatString(expr.right as BinaryExpression)
if(subStrVal==null)
null
else
StringLiteralValue("${leftStrval.value}${subStrVal.value}", subStrVal.encoding, leftStrval.position)
StringLiteral("${leftStrval.value}${subStrVal.value}", subStrVal.encoding, leftStrval.position)
}
leftStrval!=null && rightStrval!=null -> {
StringLiteralValue("${leftStrval.value}${rightStrval.value}", leftStrval.encoding, leftStrval.position)
StringLiteral("${leftStrval.value}${rightStrval.value}", leftStrval.encoding, leftStrval.position)
}
else -> null
}
@@ -1,37 +1,21 @@
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.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.ast.walk.IAstVisitor
import prog8.codegen.target.AssemblyError
import prog8.compilerinterface.CompilationOptions
import prog8.compilerinterface.IErrorReporter
import prog8.compilerinterface.isIOAddress
import prog8.optimizer.getTempVarName
import prog8.compilerinterface.*
import prog8.optimizer.getTempRegisterName
internal class BeforeAsmAstChanger(val program: Program, private val options: CompilationOptions,
internal class BeforeAsmAstChanger(val program: Program,
private val options: CompilationOptions,
private val variables: IVariablesAndConsts,
private val errors: IErrorReporter
) : AstWalker() {
private val subroutineVariables = mutableMapOf<Subroutine, MutableList<Pair<String, VarDecl>>>()
private fun rememberSubroutineVar(decl: VarDecl) {
val sub = decl.definingSubroutine ?: return
var varsList = subroutineVariables[sub]
if(varsList==null) {
varsList = mutableListOf()
subroutineVariables[sub] = varsList
}
varsList.add(decl.name to decl)
}
override fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> {
throw FatalAstException("break should have been replaced by goto $breakStmt")
}
@@ -49,13 +33,28 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
val subs = block.statements.filterIsInstance<Subroutine>()
block.statements.removeAll(subs)
block.statements.addAll(subs)
// adjust global variables initialization
if(options.dontReinitGlobals) {
block.statements.asSequence().filterIsInstance<VarDecl>().forEach {
if(it.type==VarDeclType.VAR) {
it.zeropage = ZeropageWish.NOT_IN_ZEROPAGE
it.findInitializer(program)?.let { initializer ->
it.value = initializer.value // put the init value back into the vardecl
}
}
}
}
return noModifications
}
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
if(decl.type== VarDeclType.VAR && decl.value != null && decl.datatype in NumericDatatypes)
throw FatalAstException("vardecls for variables, with initial numerical value, should have been rewritten as plain vardecl + assignment $decl")
rememberSubroutineVar(decl)
if(!options.dontReinitGlobals) {
if (decl.type == VarDeclType.VAR && decl.value != null && decl.datatype in NumericDatatypes)
throw FatalAstException("vardecls for variables, with initial numerical value, should have been rewritten as plain vardecl + assignment $decl")
}
return noModifications
}
@@ -116,27 +115,17 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
}
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
val firstDeclarations = mutableMapOf<String, VarDecl>()
val rememberedSubroutineVars = subroutineVariables.getOrDefault(subroutine, mutableListOf())
for(decl in rememberedSubroutineVars) {
val existing = firstDeclarations[decl.first]
if(existing!=null && existing !== decl.second) {
errors.err("variable ${decl.first} already defined in subroutine ${subroutine.name} at ${existing.position}", decl.second.position)
} else {
firstDeclarations[decl.first] = decl.second
}
}
rememberedSubroutineVars.clear()
val mods = mutableListOf<IAstModification>()
// add the implicit return statement at the end (if it's not there yet), but only if it's not a kernal routine.
// and if an assembly block doesn't contain a rts/rti, and some other situations.
val mods = mutableListOf<IAstModification>()
val returnStmt = Return(null, subroutine.position)
if (subroutine.asmAddress == null && !subroutine.inline) {
if (!subroutine.isAsmSubroutine && !subroutine.inline) {
if(subroutine.statements.isEmpty() ||
(subroutine.amountOfRtsInAsm() == 0
&& subroutine.statements.lastOrNull { it !is VarDecl } !is Return
&& subroutine.statements.last() !is Subroutine)) {
&& subroutine.statements.last() !is Subroutine
&& subroutine.statements.last() !is Return)) {
val returnStmt = Return(null, subroutine.position)
mods += IAstModification.InsertLast(returnStmt, subroutine)
}
}
@@ -152,9 +141,19 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
&& prevStmt !is Subroutine
&& prevStmt !is Return
) {
val returnStmt = Return(null, subroutine.position)
mods += IAstModification.InsertAfter(outerStatements[subroutineStmtIdx - 1], returnStmt, outerScope)
}
}
if (!subroutine.inline) {
if (subroutine.isAsmSubroutine && subroutine.asmAddress==null && subroutine.amountOfRtsInAsm() == 0) {
// make sure the NOT INLINED asm subroutine actually has a rts at the end
// (non-asm routines get a Return statement as needed, above)
mods += IAstModification.InsertLast(InlineAssembly(" rts\n", Position.DUMMY), subroutine)
}
}
return mods
}
@@ -165,7 +164,7 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
val booleanExpr = BinaryExpression(
prefixExpr.expression,
"==",
NumericLiteralValue.optimalInteger(0, ifElse.condition.position),
NumericLiteral.optimalInteger(0, ifElse.condition.position),
ifElse.condition.position
)
return listOf(IAstModification.ReplaceNode(ifElse.condition, booleanExpr, ifElse))
@@ -177,14 +176,14 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
val booleanExpr = BinaryExpression(
ifElse.condition,
"!=",
NumericLiteralValue.optimalInteger(0, ifElse.condition.position),
NumericLiteral.optimalInteger(0, ifElse.condition.position),
ifElse.condition.position
)
return listOf(IAstModification.ReplaceNode(ifElse.condition, booleanExpr, ifElse))
}
if((binExpr.left as? NumericLiteralValue)?.number==0.0 &&
(binExpr.right as? NumericLiteralValue)?.number!=0.0)
if((binExpr.left as? NumericLiteral)?.number==0.0 &&
(binExpr.right as? NumericLiteral)?.number!=0.0)
throw FatalAstException("0==X should have been swapped to if X==0")
// simplify the conditional expression, introduce simple assignments if required.
@@ -243,7 +242,7 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
}
if(separateLeftExpr) {
val name = getTempVarName(leftDt)
val name = getTempRegisterName(leftDt)
leftOperandReplacement = IdentifierReference(name, expr.position)
leftAssignment = Assignment(
AssignTarget(IdentifierReference(name, expr.position), null, null, expr.position),
@@ -252,13 +251,9 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
)
}
if(separateRightExpr) {
val name = when {
rightDt istype DataType.UBYTE -> listOf("prog8_lib","retval_interm_ub")
rightDt istype DataType.UWORD -> listOf("prog8_lib","retval_interm_uw")
rightDt istype DataType.BYTE -> listOf("prog8_lib","retval_interm_b2")
rightDt istype DataType.WORD -> listOf("prog8_lib","retval_interm_w2")
else -> throw AssemblyError("invalid dt")
}
val name = program.getTempVar(rightDt.getOrElse { throw FatalAstException("invalid dt") }, true)
val tempvardecl = program.toplevelModule.lookup(name) as VarDecl
variables.addIfUnknown(tempvardecl.definingBlock, tempvardecl)
rightOperandReplacement = IdentifierReference(name, expr.position)
rightAssignment = Assignment(
AssignTarget(IdentifierReference(name, expr.position), null, null, expr.position),
@@ -306,7 +301,7 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
val index = arrayIndexedExpression.indexer.indexExpr
if(index !is NumericLiteralValue && index !is IdentifierReference) {
if(index !is NumericLiteral && index !is IdentifierReference) {
// replace complex indexing expression with a temp variable to hold the computed index first
return getAutoIndexerVarFor(arrayIndexedExpression)
}
@@ -320,11 +315,11 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
val complexArrayIndexedExpressions = mutableListOf<ArrayIndexedExpression>()
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
val ix = arrayIndexedExpression.indexer.indexExpr
if(ix !is NumericLiteralValue && ix !is IdentifierReference)
if(ix !is NumericLiteral && ix !is IdentifierReference)
complexArrayIndexedExpressions.add(arrayIndexedExpression)
}
override fun visit(branch: Branch) {}
override fun visit(branch: ConditionalBranch) {}
override fun visit(forLoop: ForLoop) {}
@@ -354,7 +349,9 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
val modifications = mutableListOf<IAstModification>()
val statement = expr.containingStatement
val dt = expr.indexer.indexExpr.inferType(program)
val tempvar = if(dt.isBytes) listOf("prog8_lib","retval_interm_ub") else listOf("prog8_lib","retval_interm_b")
val tempvar = program.getTempVar(dt.getOrElse { throw FatalAstException("invalid dt") })
val tempvardecl = program.toplevelModule.lookup(tempvar) as VarDecl
variables.addIfUnknown(tempvardecl.definingBlock, tempvardecl)
val target = AssignTarget(IdentifierReference(tempvar, expr.indexer.position), null, null, expr.indexer.position)
val assign = Assignment(target, expr.indexer.indexExpr, AssignmentOrigin.BEFOREASMGEN, expr.indexer.position)
modifications.add(IAstModification.InsertBefore(statement, assign, statement.parent as IStatementContainer))
@@ -368,4 +365,4 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
return modifications
}
}
}
@@ -6,11 +6,14 @@ import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.ParentSentinel
import prog8.ast.base.Position
import prog8.ast.expressions.*
import prog8.ast.expressions.DirectMemoryRead
import prog8.ast.expressions.FunctionCallExpression
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.PrefixExpression
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.compilerinterface.*
import prog8.compilerinterface.IErrorReporter
internal class CodeDesugarer(val program: Program, private val errors: IErrorReporter) : AstWalker() {
@@ -1,12 +1,13 @@
package prog8.compiler.astprocessing
import prog8.ast.IFunctionCall
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.DataType
import prog8.ast.expressions.ArrayLiteralValue
import prog8.ast.expressions.ArrayLiteral
import prog8.ast.expressions.ContainmentCheck
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.StringLiteralValue
import prog8.ast.expressions.StringLiteral
import prog8.ast.statements.VarDecl
import prog8.ast.statements.WhenChoice
import prog8.ast.walk.AstWalker
@@ -15,9 +16,19 @@ import prog8.ast.walk.IAstModification
internal class LiteralsToAutoVars(private val program: Program) : AstWalker() {
override fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> {
if(string.parent !is VarDecl && string.parent !is WhenChoice && string.parent !is ContainmentCheck) {
override fun after(string: StringLiteral, parent: Node): Iterable<IAstModification> {
if(string.parent !is VarDecl
&& string.parent !is WhenChoice
&& (string.parent !is ContainmentCheck || string.value.length>ContainmentCheck.max_inlined_string_length)) {
// replace the literal string by an identifier reference to the interned string
val parentFunc = (string.parent as? IFunctionCall)?.target
if(parentFunc!=null) {
if(parentFunc.nameInSource.size==1 && parentFunc.nameInSource[0]=="memory") {
// memory() builtin function just uses the string as a label name
return noModifications
}
}
val scopedName = program.internString(string)
val identifier = IdentifierReference(scopedName, string.position)
return listOf(IAstModification.ReplaceNode(string, identifier, parent))
@@ -25,7 +36,7 @@ internal class LiteralsToAutoVars(private val program: Program) : AstWalker() {
return noModifications
}
override fun after(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> {
override fun after(array: ArrayLiteral, parent: Node): Iterable<IAstModification> {
val vardecl = array.parent as? VarDecl
if(vardecl!=null) {
// adjust the datatype of the array (to an educated guess from the vardecl type)
@@ -36,7 +47,7 @@ internal class LiteralsToAutoVars(private val program: Program) : AstWalker() {
return listOf(IAstModification.ReplaceNode(vardecl.value!!, cast, vardecl))
}
} else {
if(array.parent is ContainmentCheck)
if(array.parent is ContainmentCheck && array.value.size<ContainmentCheck.max_inlined_string_length)
return noModifications
val arrayDt = array.guessDatatype(program)
@@ -17,7 +17,7 @@ internal class ParentNodeChecker: AstWalker() {
return noModifications
}
override fun before(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> {
override fun before(array: ArrayLiteral, parent: Node): Iterable<IAstModification> {
if(array.parent!==parent)
throw FatalAstException("parent node mismatch at $array")
return noModifications
@@ -47,7 +47,7 @@ internal class ParentNodeChecker: AstWalker() {
return noModifications
}
override fun before(branch: Branch, parent: Node): Iterable<IAstModification> {
override fun before(branch: ConditionalBranch, parent: Node): Iterable<IAstModification> {
if(branch.parent!==parent)
throw FatalAstException("parent node mismatch at $branch")
return noModifications
@@ -149,7 +149,7 @@ internal class ParentNodeChecker: AstWalker() {
return noModifications
}
override fun before(numLiteral: NumericLiteralValue, parent: Node): Iterable<IAstModification> {
override fun before(numLiteral: NumericLiteral, parent: Node): Iterable<IAstModification> {
if(numLiteral.parent!==parent)
throw FatalAstException("parent node mismatch at $numLiteral")
return noModifications
@@ -185,7 +185,7 @@ internal class ParentNodeChecker: AstWalker() {
return noModifications
}
override fun before(string: StringLiteralValue, parent: Node): Iterable<IAstModification> {
override fun before(string: StringLiteral, parent: Node): Iterable<IAstModification> {
if(string.parent!==parent)
throw FatalAstException("parent node mismatch at $string")
return noModifications
@@ -233,12 +233,6 @@ internal class ParentNodeChecker: AstWalker() {
return noModifications
}
override fun before(nop: Nop, parent: Node): Iterable<IAstModification> {
if(nop.parent!==parent)
throw FatalAstException("parent node mismatch at $nop")
return noModifications
}
override fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
if(scope.parent!==parent)
throw FatalAstException("parent node mismatch at $scope")
@@ -13,7 +13,7 @@ Too bad, because the code is very small
//import prog8.ast.Program
//import prog8.ast.base.Position
//import prog8.ast.expressions.BinaryExpression
//import prog8.ast.expressions.NumericLiteralValue
//import prog8.ast.expressions.NumericLiteral
//import kotlin.reflect.KClass
//import kotlin.reflect.KVisibility
//import kotlin.reflect.full.declaredMemberProperties
@@ -59,9 +59,9 @@ Too bad, because the code is very small
//
//fun main() {
// val ast = BinaryExpression(
// NumericLiteralValue.optimalInteger(100, Position.DUMMY),
// NumericLiteral.optimalInteger(100, Position.DUMMY),
// "+",
// NumericLiteralValue.optimalInteger(200, Position.DUMMY),
// NumericLiteral.optimalInteger(200, Position.DUMMY),
// Position.DUMMY
// )
//
@@ -44,45 +44,65 @@ internal class StatementReorderer(val program: Program,
private val declsProcessedWithInitAssignment = mutableSetOf<VarDecl>()
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
if(decl.type == VarDeclType.VAR && decl.datatype in NumericDatatypes) {
if(decl !in declsProcessedWithInitAssignment) {
declsProcessedWithInitAssignment.add(decl)
if (decl.value == null) {
if (decl.origin==VarDeclOrigin.USERCODE && decl.allowInitializeWithZero) {
// A numeric vardecl without an initial value is initialized with zero,
// unless there's already an assignment below it, that initializes the value (or a for loop that uses it as loopvar).
// This allows you to restart the program and have the same starting values of the variables
// So basically consider 'ubyte xx' as a short form for 'ubyte xx; xx=0'
if (decl.type == VarDeclType.VAR) {
if (decl.datatype in NumericDatatypes) {
if(decl !in declsProcessedWithInitAssignment) {
declsProcessedWithInitAssignment.add(decl)
if (decl.value == null) {
if (decl.origin==VarDeclOrigin.USERCODE && decl.allowInitializeWithZero) {
// A numeric vardecl without an initial value is initialized with zero,
// unless there's already an assignment below it, that initializes the value (or a for loop that uses it as loopvar).
// This allows you to restart the program and have the same starting values of the variables
// So basically consider 'ubyte xx' as a short form for 'ubyte xx; xx=0'
decl.value = null
if(decl.name.startsWith("tempvar_") && decl.definingScope.name=="prog8_lib") {
// no need to zero out the special internal temporary variables.
return noModifications
}
if(decl.findInitializer(program)!=null)
return noModifications // an initializer assignment for a vardecl is already here
val nextFor = decl.nextSibling() as? ForLoop
val hasNextForWithThisLoopvar = nextFor?.loopVar?.nameInSource==listOf(decl.name)
if (!hasNextForWithThisLoopvar) {
// Add assignment to initialize with zero
// Note: for block-level vars, this will introduce assignments in the block scope. These have to be dealt with correctly later.
val identifier = IdentifierReference(listOf(decl.name), decl.position)
val assignzero = Assignment(AssignTarget(identifier, null, null, decl.position), decl.zeroElementValue(), AssignmentOrigin.VARINIT, decl.position)
return listOf(IAstModification.InsertAfter(
decl, assignzero, parent as IStatementContainer
))
}
}
} else {
// Transform the vardecl with initvalue to a plain vardecl + assignment
// this allows for other optimizations to kick in.
// So basically consider 'ubyte xx=99' as a short form for 'ubyte xx; xx=99'
val pos = decl.value!!.position
val identifier = IdentifierReference(listOf(decl.name), pos)
val assign = Assignment(AssignTarget(identifier, null, null, pos), decl.value!!, AssignmentOrigin.VARINIT, pos)
decl.value = null
if(decl.name.startsWith("retval_interm_") && decl.definingScope.name=="prog8_lib") {
// no need to zero out the special internal returnvalue intermediates.
return noModifications
}
if(decl.findInitializer(program)!=null)
return noModifications // an initializer assignment for a vardecl is already here
val nextFor = decl.nextSibling() as? ForLoop
val hasNextForWithThisLoopvar = nextFor?.loopVar?.nameInSource==listOf(decl.name)
if (!hasNextForWithThisLoopvar) {
// Add assignment to initialize with zero
// Note: for block-level vars, this will introduce assignments in the block scope. These have to be dealt with correctly later.
val identifier = IdentifierReference(listOf(decl.name), decl.position)
val assignzero = Assignment(AssignTarget(identifier, null, null, decl.position), decl.zeroElementValue(), AssignmentOrigin.VARINIT, decl.position)
return listOf(IAstModification.InsertAfter(
decl, assignzero, parent as IStatementContainer
))
}
return listOf(IAstModification.InsertAfter(
decl, assign, parent as IStatementContainer
))
}
}
}
else if(decl.datatype in ArrayDatatypes) {
// only if the initializer expression is a reference to another array, split it into a separate assignment.
// this is so that it later can be changed into a memcopy.
// (that code only triggers on regular assignment, not on variable initializers)
val ident = decl.value as? IdentifierReference
if(ident!=null) {
val target = ident.targetVarDecl(program)
if(target!=null && target.isArray) {
val pos = decl.value!!.position
val identifier = IdentifierReference(listOf(decl.name), pos)
val assign = Assignment(AssignTarget(identifier, null, null, pos), decl.value!!, AssignmentOrigin.VARINIT, pos)
decl.value = null
return listOf(IAstModification.InsertAfter(
decl, assign, parent as IStatementContainer
))
}
} else {
// Transform the vardecl with initvalue to a plain vardecl + assignment
// this allows for other optimizations to kick in.
// So basically consider 'ubyte xx=99' as a short form for 'ubyte xx; xx=99'
val pos = decl.value!!.position
val identifier = IdentifierReference(listOf(decl.name), pos)
val assign = Assignment(AssignTarget(identifier, null, null, pos), decl.value!!, AssignmentOrigin.VARINIT, pos)
decl.value = null
return listOf(IAstModification.InsertAfter(
decl, assign, parent as IStatementContainer
))
}
}
}
@@ -236,7 +256,7 @@ internal class StatementReorderer(val program: Program,
// generating the wrong results later
fun wrapped(expr: Expression): Expression =
BinaryExpression(expr, "!=", NumericLiteralValue(DataType.UBYTE, 0.0, expr.position), expr.position)
BinaryExpression(expr, "!=", NumericLiteral(DataType.UBYTE, 0.0, expr.position), expr.position)
fun isLogicalExpr(expr: Expression?): Boolean {
if(expr is BinaryExpression && expr.operator in (LogicalOperators + ComparisonOperators))
@@ -286,7 +306,7 @@ internal class StatementReorderer(val program: Program,
val targetType = assignment.target.inferType(program)
if(targetType.isArray && valueType.isArray) {
if (assignment.value is ArrayLiteralValue) {
if (assignment.value is ArrayLiteral) {
errors.err("cannot assign array literal here, use separate assignment per element", assignment.position)
} else {
return copyArrayValue(assignment)
@@ -349,8 +369,10 @@ internal class StatementReorderer(val program: Program,
val identifier = assign.target.identifier!!
val targetVar = identifier.targetVarDecl(program)!!
if(targetVar.arraysize==null)
if(targetVar.arraysize==null) {
errors.err("array has no defined size", assign.position)
return noModifications
}
if(assign.value !is IdentifierReference) {
errors.err("invalid array value to assign to other array", assign.value.position)
@@ -370,11 +392,13 @@ internal class StatementReorderer(val program: Program,
if(!errors.noErrors())
return noModifications
val numelements = targetVar.arraysize!!.constIndex()!!
val eltsize = program.memsizer.memorySize(ArrayToElementTypes.getValue(sourceVar.datatype))
val memcopy = FunctionCallStatement(IdentifierReference(listOf("sys", "memcopy"), assign.position),
mutableListOf(
AddressOf(sourceIdent, assign.position),
AddressOf(identifier, assign.position),
NumericLiteralValue.optimalInteger(targetVar.arraysize!!.constIndex()!!, assign.position)
NumericLiteral.optimalInteger(numelements*eltsize, assign.position)
),
true,
assign.position
@@ -53,7 +53,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
val value = if(rightDt.isBytes) 256+leftCv.number else 65536+leftCv.number
return listOf(IAstModification.ReplaceNode(
expr.left,
NumericLiteralValue(rightDt.getOr(DataType.UNDEFINED), value, expr.left.position),
NumericLiteral(rightDt.getOr(DataType.UNDEFINED), value, expr.left.position),
expr))
}
val rightCv = expr.right.constValue(program)
@@ -61,7 +61,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
val value = if(leftDt.isBytes) 256+rightCv.number else 65536+rightCv.number
return listOf(IAstModification.ReplaceNode(
expr.right,
NumericLiteralValue(leftDt.getOr(DataType.UNDEFINED), value, expr.right.position),
NumericLiteral(leftDt.getOr(DataType.UNDEFINED), value, expr.right.position),
expr))
}
@@ -119,7 +119,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
addTypecastOrCastedValueModification(modifications, assignment.value, targettype, assignment)
return modifications
} else {
fun castLiteral(cvalue2: NumericLiteralValue): List<IAstModification.ReplaceNode> {
fun castLiteral(cvalue2: NumericLiteral): List<IAstModification.ReplaceNode> {
val cast = cvalue2.cast(targettype)
return if(cast.isValid)
listOf(IAstModification.ReplaceNode(assignment.value, cast.valueOrZero(), assignment))
@@ -184,8 +184,8 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
AddressOf(identifier, pair.second.position),
call as Node)
}
} else if(pair.second is NumericLiteralValue) {
val cast = (pair.second as NumericLiteralValue).cast(requiredType)
} else if(pair.second is NumericLiteral) {
val cast = (pair.second as NumericLiteral).cast(requiredType)
if(cast.isValid)
modifications += IAstModification.ReplaceNode(
pair.second,
@@ -248,7 +248,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
val modifications = mutableListOf<IAstModification>()
val dt = memread.addressExpression.inferType(program)
if(dt.isKnown && dt.getOr(DataType.UWORD)!=DataType.UWORD) {
val castedValue = (memread.addressExpression as? NumericLiteralValue)?.cast(DataType.UWORD)?.valueOrZero()
val castedValue = (memread.addressExpression as? NumericLiteral)?.cast(DataType.UWORD)?.valueOrZero()
if(castedValue!=null)
modifications += IAstModification.ReplaceNode(memread.addressExpression, castedValue, memread)
else
@@ -262,7 +262,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
val modifications = mutableListOf<IAstModification>()
val dt = memwrite.addressExpression.inferType(program)
if(dt.isKnown && dt.getOr(DataType.UWORD)!=DataType.UWORD) {
val castedValue = (memwrite.addressExpression as? NumericLiteralValue)?.cast(DataType.UWORD)?.valueOrZero()
val castedValue = (memwrite.addressExpression as? NumericLiteral)?.cast(DataType.UWORD)?.valueOrZero()
if(castedValue!=null)
modifications += IAstModification.ReplaceNode(memwrite.addressExpression, castedValue, memwrite)
else
@@ -280,7 +280,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
val subReturnType = subroutine.returntypes.first()
if (returnValue.inferType(program) istype subReturnType)
return noModifications
if (returnValue is NumericLiteralValue) {
if (returnValue is NumericLiteral) {
val cast = returnValue.cast(subroutine.returntypes.single())
if(cast.isValid)
returnStmt.value = cast.valueOrZero()
@@ -303,7 +303,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
val sourceDt = expressionToCast.inferType(program).getOr(DataType.UNDEFINED)
if(sourceDt == requiredType)
return
if(expressionToCast is NumericLiteralValue && expressionToCast.type!=DataType.FLOAT) { // refuse to automatically truncate floats
if(expressionToCast is NumericLiteral && expressionToCast.type!=DataType.FLOAT) { // refuse to automatically truncate floats
val castedValue = expressionToCast.cast(requiredType)
if (castedValue.isValid) {
modifications += IAstModification.ReplaceNode(expressionToCast, castedValue.valueOrZero(), parent)
@@ -0,0 +1,191 @@
package prog8.compiler.astprocessing
import prog8.ast.Program
import prog8.ast.base.FatalAstException
import prog8.ast.base.VarDeclType
import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.Block
import prog8.ast.statements.Subroutine
import prog8.ast.statements.VarDecl
import prog8.ast.walk.IAstVisitor
import prog8.compilerinterface.IVariablesAndConsts
internal class VariableExtractor: IAstVisitor {
private val allBlockVars = mutableMapOf<Block, MutableSet<VarDecl>>()
private val allBlockConsts = mutableMapOf<Block, MutableSet<VarDecl>>()
private val allBlockMemoryvars = mutableMapOf<Block, MutableSet<VarDecl>>()
private val allSubroutineVars = mutableMapOf<Subroutine, MutableSet<VarDecl>>()
private val allSubroutineConsts = mutableMapOf<Subroutine, MutableSet<VarDecl>>()
private val allSubroutineMemoryvars = mutableMapOf<Subroutine, MutableSet<VarDecl>>()
fun extractVars(program: Program): IVariablesAndConsts {
this.visit(program)
return VariablesAndConsts(
allBlockVars, allBlockConsts, allBlockMemoryvars,
allSubroutineVars, allSubroutineConsts, allSubroutineMemoryvars)
}
override fun visit(decl: VarDecl) {
val scope=decl.definingScope
when (decl.type) {
VarDeclType.VAR -> {
when (scope) {
is Block -> {
val decls = allBlockVars[scope] ?: mutableSetOf()
decls.add(decl)
allBlockVars[scope] = decls
}
is Subroutine -> {
val decls = allSubroutineVars[scope] ?: mutableSetOf()
decls.add(decl)
allSubroutineVars[scope] = decls
}
else -> {
throw FatalAstException("var can only occur in subroutine or block scope")
}
}
}
VarDeclType.CONST -> {
when(scope) {
is Block -> {
val decls = allBlockConsts[scope] ?: mutableSetOf()
decls.add(decl)
allBlockConsts[scope] = decls
}
is Subroutine -> {
val decls = allSubroutineConsts[scope] ?: mutableSetOf()
decls.add(decl)
allSubroutineConsts[scope] = decls
}
else -> {
throw FatalAstException("var can only occur in subroutine or block scope")
}
}
}
VarDeclType.MEMORY -> {
when(scope) {
is Block -> {
val decls = allBlockMemoryvars[scope] ?: mutableSetOf()
decls.add(decl)
allBlockMemoryvars[scope] = decls
}
is Subroutine -> {
val decls = allSubroutineMemoryvars[scope] ?: mutableSetOf()
decls.add(decl)
allSubroutineMemoryvars[scope] = decls
}
else -> {
throw FatalAstException("var can only occur in subroutine or block scope")
}
}
}
else -> {
throw FatalAstException("invalid var type")
}
}
super.visit(decl)
}
}
internal class VariablesAndConsts (
astBlockVars: Map<Block, Set<VarDecl>>,
astBlockConsts: Map<Block, Set<VarDecl>>,
astBlockMemvars: Map<Block, Set<VarDecl>>,
astSubroutineVars: Map<Subroutine, Set<VarDecl>>,
astSubroutineConsts: Map<Subroutine, Set<VarDecl>>,
astSubroutineMemvars: Map<Subroutine, Set<VarDecl>>
) : IVariablesAndConsts
{
override val blockVars: Map<Block, Set<IVariablesAndConsts.StaticVariable>>
override val blockConsts: Map<Block, Set<IVariablesAndConsts.ConstantNumberSymbol>>
override val blockMemvars: Map<Block, Set<IVariablesAndConsts.MemoryMappedVariable>>
override val subroutineVars: Map<Subroutine, Set<IVariablesAndConsts.StaticVariable>>
override val subroutineConsts: Map<Subroutine, Set<IVariablesAndConsts.ConstantNumberSymbol>>
override val subroutineMemvars: Map<Subroutine, Set<IVariablesAndConsts.MemoryMappedVariable>>
private val bv = astBlockVars.keys.associateWith { mutableSetOf<IVariablesAndConsts.StaticVariable>() }.toMutableMap()
private val bc = astBlockConsts.keys.associateWith { mutableSetOf<IVariablesAndConsts.ConstantNumberSymbol>() }
private val bmv = astBlockMemvars.keys.associateWith { mutableSetOf<IVariablesAndConsts.MemoryMappedVariable>() }
private val sv = astSubroutineVars.keys.associateWith { mutableSetOf<IVariablesAndConsts.StaticVariable>() }
private val sc = astSubroutineConsts.keys.associateWith { mutableSetOf<IVariablesAndConsts.ConstantNumberSymbol>() }
private val smv = astSubroutineMemvars.keys.associateWith { mutableSetOf<IVariablesAndConsts.MemoryMappedVariable>() }
init {
astBlockVars.forEach { (block, decls) ->
val vars = bv.getValue(block)
vars.addAll(decls.map { toStatic(it) })
}
astBlockConsts.forEach { (block, decls) ->
bc.getValue(block).addAll(
decls.map {
IVariablesAndConsts.ConstantNumberSymbol(
it.datatype,
it.scopedName,
(it.value as NumericLiteral).number,
it.position
)
})
}
astBlockMemvars.forEach { (block, decls) ->
val vars = bmv.getValue(block)
for(decl in decls) {
// make sure the 'stubs' for the scratch variables in zeropage are not included as normal variables
if(!decl.name.startsWith("P8ZP_SCRATCH_")) {
vars.add(
IVariablesAndConsts.MemoryMappedVariable(
decl.datatype,
decl.scopedName,
(decl.value as NumericLiteral).number.toUInt(),
decl.position
)
)
}
}
}
astSubroutineVars.forEach { (sub, decls) ->
val vars = sv.getValue(sub)
vars.addAll(decls.map { toStatic(it) })
}
astSubroutineConsts.forEach { (sub, decls) ->
sc.getValue(sub).addAll(
decls.map {
IVariablesAndConsts.ConstantNumberSymbol(
it.datatype,
it.scopedName,
(it.value as NumericLiteral).number,
it.position
)
})
}
astSubroutineMemvars.forEach { (sub, decls) ->
smv.getValue(sub).addAll(
decls.map {
IVariablesAndConsts.MemoryMappedVariable(
it.datatype,
it.scopedName,
(it.value as NumericLiteral).number.toUInt(),
it.position
)
})
}
blockVars = bv
blockConsts = bc
blockMemvars = bmv
subroutineVars = sv
subroutineConsts = sc
subroutineMemvars = smv
}
private fun toStatic(decl: VarDecl) =
IVariablesAndConsts.StaticVariable(decl.datatype, decl.scopedName, decl.definingScope, decl.value, decl.arraysize?.constIndex(), decl.zeropage, decl.position)
override fun addIfUnknown(definingBlock: Block, variable: VarDecl) {
var blockvars = bv[definingBlock]
if(blockvars==null) {
blockvars = mutableSetOf()
bv[definingBlock] = blockvars
}
blockvars.add(toStatic(variable))
}
}
@@ -8,7 +8,9 @@ import prog8.ast.base.ArrayDatatypes
import prog8.ast.base.DataType
import prog8.ast.base.FatalAstException
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.statements.AnonymousScope
import prog8.ast.statements.Assignment
import prog8.ast.statements.FunctionCallStatement
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.compilerinterface.CompilationOptions
@@ -17,10 +19,6 @@ import prog8.compilerinterface.IErrorReporter
internal class VariousCleanups(val program: Program, val errors: IErrorReporter, val options: CompilationOptions): AstWalker() {
override fun before(nop: Nop, parent: Node): Iterable<IAstModification> {
return listOf(IAstModification.Remove(nop, parent as IStatementContainer))
}
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
return if(parent is IStatementContainer)
listOf(ScopeFlatten(scope, parent as IStatementContainer))
@@ -40,8 +38,8 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
}
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
if(typecast.expression is NumericLiteralValue) {
val value = (typecast.expression as NumericLiteralValue).cast(typecast.type)
if(typecast.expression is NumericLiteral) {
val value = (typecast.expression as NumericLiteral).cast(typecast.type)
if(value.isValid)
return listOf(IAstModification.ReplaceNode(typecast, value.valueOrZero(), parent))
}
@@ -100,7 +98,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
val leftBinExpr = expr.left as? BinaryExpression
val rightBinExpr = expr.right as? BinaryExpression
if(leftBinExpr!=null && leftBinExpr.operator=="==" && rightBinExpr!=null && rightBinExpr.operator=="==") {
if(leftBinExpr.right is NumericLiteralValue && rightBinExpr.right is NumericLiteralValue) {
if(leftBinExpr.right is NumericLiteral && rightBinExpr.right is NumericLiteral) {
if(leftBinExpr.left isSameAs rightBinExpr.left)
errors.warn("consider using 'in' or 'when' to test for multiple values", expr.position)
}
@@ -132,7 +130,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
override fun after(containment: ContainmentCheck, parent: Node): Iterable<IAstModification> {
// replace trivial containment checks with just false or a single comparison
fun replaceWithEquals(value: NumericLiteralValue): Iterable<IAstModification> {
fun replaceWithEquals(value: NumericLiteral): Iterable<IAstModification> {
errors.warn("containment could be written as just a single comparison", containment.position)
val equals = BinaryExpression(containment.element, "==", value, containment.position)
return listOf(IAstModification.ReplaceNode(containment, equals, parent))
@@ -140,7 +138,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
fun replaceWithFalse(): Iterable<IAstModification> {
errors.warn("condition is always false", containment.position)
return listOf(IAstModification.ReplaceNode(containment, NumericLiteralValue.fromBoolean(false, containment.position), parent))
return listOf(IAstModification.ReplaceNode(containment, NumericLiteral.fromBoolean(false, containment.position), parent))
}
fun checkArray(array: Array<Expression>): Iterable<IAstModification> {
@@ -154,30 +152,30 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
return noModifications
}
fun checkString(stringVal: StringLiteralValue): Iterable<IAstModification> {
fun checkString(stringVal: StringLiteral): Iterable<IAstModification> {
if(stringVal.value.isEmpty())
return replaceWithFalse()
if(stringVal.value.length==1) {
val string = program.encoding.encodeString(stringVal.value, stringVal.encoding)
return replaceWithEquals(NumericLiteralValue(DataType.UBYTE, string[0].toDouble(), stringVal.position))
return replaceWithEquals(NumericLiteral(DataType.UBYTE, string[0].toDouble(), stringVal.position))
}
return noModifications
}
when(containment.iterable) {
is ArrayLiteralValue -> {
val array = (containment.iterable as ArrayLiteralValue).value
is ArrayLiteral -> {
val array = (containment.iterable as ArrayLiteral).value
return checkArray(array)
}
is IdentifierReference -> {
val variable = (containment.iterable as IdentifierReference).targetVarDecl(program)!!
when(variable.datatype) {
DataType.STR -> {
val stringVal = (variable.value as StringLiteralValue)
val stringVal = (variable.value as StringLiteral)
return checkString(stringVal)
}
in ArrayDatatypes -> {
val array = (variable.value as ArrayLiteralValue).value
val array = (variable.value as ArrayLiteral).value
return checkArray(array)
}
else -> {}
@@ -189,11 +187,11 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
if (constValues.isEmpty())
return replaceWithFalse()
if (constValues.count()==1)
return replaceWithEquals(NumericLiteralValue.optimalNumeric(constValues.first, containment.position))
return replaceWithEquals(NumericLiteral.optimalNumeric(constValues.first, containment.position))
}
}
is StringLiteralValue -> {
val stringVal = containment.iterable as StringLiteralValue
is StringLiteral -> {
val stringVal = containment.iterable as StringLiteral
return checkString(stringVal)
}
else -> {}
+9 -13
View File
@@ -2,13 +2,6 @@ package prog8tests
import com.github.michaelbull.result.getErrorOrElse
import com.github.michaelbull.result.getOrElse
import prog8.ast.Program
import prog8.ast.internedStringsModuleName
import prog8.compiler.ModuleImporter
import prog8.compilerinterface.IErrorReporter
import prog8.parser.ParseError
import prog8.parser.SourceCode
import kotlin.io.path.*
import io.kotest.assertions.fail
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.assertions.withClue
@@ -16,11 +9,14 @@ import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.collections.shouldBeIn
import io.kotest.matchers.shouldBe
import io.kotest.matchers.string.shouldContain
import prog8.ast.Program
import prog8.ast.internedStringsModuleName
import prog8.compiler.ModuleImporter
import prog8.compilerinterface.IErrorReporter
import prog8.parser.ParseError
import prog8.parser.SourceCode
import prog8tests.helpers.*
import prog8tests.helpers.DummyFunctions
import prog8tests.helpers.DummyMemsizer
import prog8tests.helpers.DummyStringEncoder
import prog8tests.helpers.ErrorReporterForTests
import kotlin.io.path.*
class TestModuleImporter: FunSpec({
@@ -71,7 +67,7 @@ class TestModuleImporter: FunSpec({
val searchIn = Path(".", "$srcPathRel").invariantSeparatorsPathString
val importer = makeImporter(null, searchIn)
shouldThrow<AccessDeniedException> { importer.importModule(srcPathRel) }
shouldThrow<FileSystemException> { importer.importModule(srcPathRel) }
.let {
withClue(".file should be normalized") {
"${it.file}" shouldBe "${it.file.normalize()}"
@@ -82,7 +78,7 @@ class TestModuleImporter: FunSpec({
}
program.modules.size shouldBe 1
shouldThrow<AccessDeniedException> { importer.importModule(srcPathAbs) }
shouldThrow<FileSystemException> { importer.importModule(srcPathAbs) }
.let {
withClue(".file should be normalized") {
"${it.file}" shouldBe "${it.file.normalize()}"
-8
View File
@@ -1,14 +1,6 @@
package prog8tests
import io.kotest.core.config.AbstractProjectConfig
import io.kotest.core.listeners.Listener
import io.kotest.core.listeners.TestListener
import io.kotest.core.spec.Spec
import io.kotest.extensions.system.NoSystemErrListener
import io.kotest.extensions.system.NoSystemOutListener
import java.io.ByteArrayOutputStream
import java.io.PrintStream
import kotlin.math.max
object ProjectConfig : AbstractProjectConfig() {
override val parallelism = 2 // max(2, Runtime.getRuntime().availableProcessors() / 2)
+68 -1
View File
@@ -5,6 +5,7 @@ import io.kotest.matchers.shouldBe
import io.kotest.matchers.string.shouldContain
import prog8.codegen.target.C64Target
import prog8tests.helpers.ErrorReporterForTests
import prog8tests.helpers.assertFailure
import prog8tests.helpers.assertSuccess
import prog8tests.helpers.compileText
@@ -24,10 +25,76 @@ class TestAstChecks: FunSpec({
}
"""
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
compileText(C64Target, true, text, writeAssembly = true, errors=errors).assertSuccess()
compileText(C64Target(), true, text, writeAssembly = true, errors=errors).assertSuccess()
errors.errors.size shouldBe 0
errors.warnings.size shouldBe 2
errors.warnings[0] shouldContain "converted to float"
errors.warnings[1] shouldContain "converted to float"
}
test("can't assign label or subroutine without using address-of") {
val text = """
main {
sub start() {
label:
uword @shared addr
addr = label
addr = thing
addr = &label
addr = &thing
}
sub thing() {
}
}
"""
val errors = ErrorReporterForTests()
compileText(C64Target(), true, text, writeAssembly = true, errors=errors).assertFailure()
errors.errors.size shouldBe 2
errors.warnings.size shouldBe 0
errors.errors[0] shouldContain ":7:28) assignment value is invalid"
errors.errors[1] shouldContain ":8:28) assignment value is invalid"
}
test("can't do str or array expression without using address-of") {
val text = """
%import textio
main {
sub start() {
ubyte[] array = [1,2,3,4]
str s1 = "test"
ubyte ff = 1
txt.print(s1+ff)
txt.print(array+ff)
txt.print_uwhex(s1+ff, true)
txt.print_uwhex(array+ff, true)
}
}
"""
val errors = ErrorReporterForTests()
compileText(C64Target(), false, text, writeAssembly = false, errors=errors).assertFailure()
errors.errors.filter { it.contains("missing &") }.size shouldBe 4
}
test("str or array expression with address-of") {
val text = """
%import textio
main {
sub start() {
ubyte[] array = [1,2,3,4]
str s1 = "test"
ubyte ff = 1
txt.print(&s1+ff)
txt.print(&array+ff)
txt.print_uwhex(&s1+ff, true)
txt.print_uwhex(&array+ff, true)
; also good:
ff = (s1 == "derp")
ff = (s1 != "derp")
}
}
"""
compileText(C64Target(), false, text, writeAssembly = false).assertSuccess()
}
})
+142 -12
View File
@@ -2,15 +2,18 @@ package prog8tests
import io.kotest.assertions.withClue
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.maps.shouldContainKey
import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual
import io.kotest.matchers.maps.shouldNotContainKey
import io.kotest.matchers.shouldBe
import io.kotest.matchers.string.shouldContain
import prog8.ast.Program
import prog8.ast.statements.Block
import prog8.ast.statements.Subroutine
import prog8.codegen.target.C64Target
import prog8.compilerinterface.CallGraph
import prog8tests.helpers.assertSuccess
import prog8tests.helpers.compileText
import prog8.parser.Prog8Parser.parseModule
import prog8.parser.SourceCode
import prog8tests.helpers.*
class TestCallgraph: FunSpec({
test("testGraphForEmptySubs") {
@@ -23,7 +26,7 @@ class TestCallgraph: FunSpec({
}
}
"""
val result = compileText(C64Target, false, sourcecode).assertSuccess()
val result = compileText(C64Target(), false, sourcecode).assertSuccess()
val graph = CallGraph(result.program)
graph.imports.size shouldBe 1
@@ -52,7 +55,7 @@ class TestCallgraph: FunSpec({
}
}
test("testGraphForEmptyButReferencedSub") {
test("reference to empty sub") {
val sourcecode = """
%import string
main {
@@ -64,7 +67,7 @@ class TestCallgraph: FunSpec({
}
}
"""
val result = compileText(C64Target, false, sourcecode).assertSuccess()
val result = compileText(C64Target(), false, sourcecode).assertSuccess()
val graph = CallGraph(result.program)
graph.imports.size shouldBe 1
@@ -82,17 +85,144 @@ class TestCallgraph: FunSpec({
val startSub = mainBlock.statements.filterIsInstance<Subroutine>().single{it.name=="start"}
val emptySub = mainBlock.statements.filterIsInstance<Subroutine>().single{it.name=="empty"}
withClue("start 'calls' (references) empty") {
graph.calls shouldContainKey startSub
}
graph.calls shouldNotContainKey startSub
graph.calledBy shouldNotContainKey emptySub
withClue("empty doesn't call anything") {
graph.calls shouldNotContainKey emptySub
}
withClue("empty gets 'called'") {
graph.calledBy shouldContainKey emptySub
}
withClue( "start doesn't get called (except as entrypoint ofc.)") {
graph.calledBy shouldNotContainKey startSub
}
}
test("allIdentifiers separates for different positions of the IdentifierReferences") {
val sourcecode = """
main {
sub start() {
uword x1 = &empty
uword x2 = &empty
empty()
}
sub empty() {
%asm {{
nop
}}
}
}
"""
val result = compileText(C64Target(), false, sourcecode).assertSuccess()
val graph = CallGraph(result.program)
graph.allIdentifiers.size shouldBeGreaterThanOrEqual 9
val empties = graph.allIdentifiers.keys.filter { it.nameInSource==listOf("empty") }
empties.size shouldBe 3
empties[0].position.line shouldBe 4
empties[1].position.line shouldBe 5
empties[2].position.line shouldBe 6
}
test("checking block and subroutine names usage in assembly code") {
val source = """
main {
sub start() {
%asm {{
lda #<blockname
lda #<blockname.subroutine
correctlabel:
nop
}}
}
}
blockname {
sub subroutine() {
@(1000) = 0
}
sub correctlabel() {
@(1000) = 0
}
}
; all block and subroutines below should NOT be found in asm because they're only substrings of the names in there
locknam {
sub rout() {
@(1000) = 0
}
sub orrectlab() {
@(1000) = 0
}
}"""
val module = parseModule(SourceCode.Text(source))
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
program.addModule(module)
val callgraph = CallGraph(program)
val blockMain = program.allBlocks.single { it.name=="main" }
val blockBlockname = program.allBlocks.single { it.name=="blockname" }
val blockLocknam = program.allBlocks.single { it.name=="locknam" }
val subStart = blockMain.statements.filterIsInstance<Subroutine>().single { it.name == "start" }
val subSubroutine = blockBlockname.statements.filterIsInstance<Subroutine>().single { it.name == "subroutine" }
val subCorrectlabel = blockBlockname.statements.filterIsInstance<Subroutine>().single { it.name == "correctlabel" }
val subRout = blockLocknam.statements.filterIsInstance<Subroutine>().single { it.name == "rout" }
val subOrrectlab = blockLocknam.statements.filterIsInstance<Subroutine>().single { it.name == "orrectlab" }
callgraph.unused(blockMain) shouldBe false
callgraph.unused(blockBlockname) shouldBe false
callgraph.unused(blockLocknam) shouldBe true
callgraph.unused(subStart) shouldBe false
callgraph.unused(subSubroutine) shouldBe false
callgraph.unused(subCorrectlabel) shouldBe false
callgraph.unused(subRout) shouldBe true
callgraph.unused(subOrrectlab) shouldBe true
}
test("recursion detection") {
val source="""
main {
sub start() {
recurse1()
}
sub recurse1() {
recurse2()
}
sub recurse2() {
start()
}
}"""
val module = parseModule(SourceCode.Text(source))
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
program.addModule(module)
val callgraph = CallGraph(program)
val errors = ErrorReporterForTests()
callgraph.checkRecursiveCalls(errors)
errors.errors.size shouldBe 0
errors.warnings.size shouldBe 4
errors.warnings[0] shouldContain "contains recursive subroutine calls"
errors.warnings[1] shouldContain "start at"
errors.warnings[2] shouldContain "recurse1 at"
errors.warnings[3] shouldContain "recurse2 at"
}
test("no recursion warning if reference isn't a call") {
val source="""
main {
sub start() {
recurse1()
}
sub recurse1() {
recurse2()
}
sub recurse2() {
uword @shared address = &start
}
}"""
val module = parseModule(SourceCode.Text(source))
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
program.addModule(module)
val callgraph = CallGraph(program)
val errors = ErrorReporterForTests()
callgraph.checkRecursiveCalls(errors)
errors.errors.size shouldBe 0
errors.warnings.size shouldBe 0
}
})
+10 -10
View File
@@ -9,7 +9,7 @@ import prog8.ast.IFunctionCall
import prog8.ast.base.DataType
import prog8.ast.base.VarDeclType
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.NumericLiteral
import prog8.codegen.target.Cx16Target
import prog8.compilerinterface.Encoding
import prog8tests.helpers.assertSuccess
@@ -24,7 +24,7 @@ import prog8tests.helpers.compileText
class TestCompilerOnCharLit: FunSpec({
test("testCharLitAsRomsubArg") {
val platform = Cx16Target
val platform = Cx16Target()
val result = compileText(platform, false, """
main {
romsub ${"$"}FFD2 = chrout(ubyte ch @ A)
@@ -39,15 +39,15 @@ class TestCompilerOnCharLit: FunSpec({
val funCall = startSub.statements.filterIsInstance<IFunctionCall>()[0]
withClue("char literal should have been replaced by ubyte literal") {
funCall.args[0] shouldBe instanceOf<NumericLiteralValue>()
funCall.args[0] shouldBe instanceOf<NumericLiteral>()
}
val arg = funCall.args[0] as NumericLiteralValue
val arg = funCall.args[0] as NumericLiteral
arg.type shouldBe DataType.UBYTE
arg.number shouldBe platform.encodeString("\n", Encoding.PETSCII)[0].toDouble()
}
test("testCharVarAsRomsubArg") {
val platform = Cx16Target
val platform = Cx16Target()
val result = compileText(platform, false, """
main {
romsub ${"$"}FFD2 = chrout(ubyte ch @ A)
@@ -79,15 +79,15 @@ class TestCompilerOnCharLit: FunSpec({
val assignInitialValue = decl.findInitializer(program)!!
assignInitialValue.target.identifier!!.nameInSource shouldBe listOf("ch")
withClue("char literal should have been replaced by ubyte literal") {
assignInitialValue.value shouldBe instanceOf<NumericLiteralValue>()
assignInitialValue.value shouldBe instanceOf<NumericLiteral>()
}
val initializerValue = assignInitialValue.value as NumericLiteralValue
val initializerValue = assignInitialValue.value as NumericLiteral
initializerValue.type shouldBe DataType.UBYTE
initializerValue.number shouldBe platform.encodeString("\n", Encoding.PETSCII)[0].toDouble()
}
test("testCharConstAsRomsubArg") {
val platform = Cx16Target
val platform = Cx16Target()
val result = compileText(platform, false, """
main {
romsub ${"$"}FFD2 = chrout(ubyte ch @ A)
@@ -108,9 +108,9 @@ class TestCompilerOnCharLit: FunSpec({
val decl = arg.targetVarDecl(program)!!
decl.type shouldBe VarDeclType.CONST
decl.datatype shouldBe DataType.UBYTE
(decl.value as NumericLiteralValue).number shouldBe platform.encodeString("\n", Encoding.PETSCII)[0]
(decl.value as NumericLiteral).number shouldBe platform.encodeString("\n", Encoding.PETSCII)[0]
}
is NumericLiteralValue -> {
is NumericLiteral -> {
arg.number shouldBe platform.encodeString("\n", Encoding.PETSCII)[0].toDouble()
}
else -> fail("invalid arg type") // funCall.args[0] shouldBe instanceOf<IdentifierReference>() // make test fail
+16 -12
View File
@@ -1,14 +1,13 @@
package prog8tests
import io.kotest.core.spec.style.FunSpec
import prog8.codegen.target.C64Target
import prog8.codegen.target.Cx16Target
import prog8.compiler.CompilationResult
import prog8.compiler.CompilerArguments
import prog8.compiler.compileProgram
import prog8.codegen.target.C64Target
import prog8.codegen.target.Cx16Target
import prog8.compilerinterface.ICompilationTarget
import prog8tests.helpers.*
import prog8tests.helpers.assertSuccess
import java.nio.file.Path
import kotlin.io.path.absolute
import kotlin.io.path.exists
@@ -32,6 +31,7 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
slowCodegenWarnings = false,
quietAssembler = true,
asmListfile = false,
experimentalCodegen = false,
compilationTarget = target.name,
outputDir = outputDir
)
@@ -40,7 +40,7 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
private fun prepareTestFiles(source: String, optimize: Boolean, target: ICompilationTarget): Pair<String, Path> {
val searchIn = mutableListOf(examplesDir)
if (target == Cx16Target) {
if (target is Cx16Target) {
searchIn.add(0, assumeDirectory(examplesDir, "cx16"))
}
val filepath = searchIn.asSequence()
@@ -72,9 +72,10 @@ class TestCompilerOnExamplesC64: FunSpec({
onlyC64.forEach {
val (source, optimize) = it
val (displayName, filepath) = prepareTestFiles(source, optimize, C64Target)
val target = C64Target()
val (displayName, filepath) = prepareTestFiles(source, optimize, target)
test(displayName) {
compileTheThing(filepath, optimize, C64Target).assertSuccess()
compileTheThing(filepath, optimize, target).assertSuccess()
}
}
})
@@ -103,9 +104,10 @@ class TestCompilerOnExamplesCx16: FunSpec({
onlyCx16.forEach {
val (source, optimize) = it
val (displayName, filepath) = prepareTestFiles(source, optimize, Cx16Target)
val target = Cx16Target()
val (displayName, filepath) = prepareTestFiles(source, optimize, target)
test(displayName) {
compileTheThing(filepath, optimize, Cx16Target).assertSuccess()
compileTheThing(filepath, optimize, target).assertSuccess()
}
}
})
@@ -141,13 +143,15 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
bothCx16AndC64.forEach {
val (source, optimize) = it
val (displayNameC64, filepathC64) = prepareTestFiles(source, optimize, C64Target)
val (displayNameCx16, filepathCx16) = prepareTestFiles(source, optimize, Cx16Target)
val c64target = C64Target()
val cx16target = Cx16Target()
val (displayNameC64, filepathC64) = prepareTestFiles(source, optimize, c64target)
val (displayNameCx16, filepathCx16) = prepareTestFiles(source, optimize, cx16target)
test(displayNameC64) {
compileTheThing(filepathC64, optimize, C64Target).assertSuccess()
compileTheThing(filepathC64, optimize, c64target).assertSuccess()
}
test(displayNameCx16) {
compileTheThing(filepathCx16, optimize, Cx16Target).assertSuccess()
compileTheThing(filepathCx16, optimize, cx16target).assertSuccess()
}
}
})
@@ -6,7 +6,7 @@ import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import prog8.ast.expressions.AddressOf
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.StringLiteralValue
import prog8.ast.expressions.StringLiteral
import prog8.ast.statements.FunctionCallStatement
import prog8.ast.statements.Label
import prog8.codegen.target.Cx16Target
@@ -27,7 +27,7 @@ class TestCompilerOnImportsAndIncludes: FunSpec({
val filepath = assumeReadableFile(fixturesDir, "importFromSameFolder.p8")
assumeReadableFile(fixturesDir, "foo_bar.p8")
val platform = Cx16Target
val platform = Cx16Target()
val result = compileFile(platform, optimize = false, fixturesDir, filepath.name)
.assertSuccess()
@@ -36,7 +36,7 @@ class TestCompilerOnImportsAndIncludes: FunSpec({
val strLits = startSub.statements
.filterIsInstance<FunctionCallStatement>()
.map { it.args[0] as IdentifierReference }
.map { it.targetVarDecl(program)!!.value as StringLiteralValue }
.map { it.targetVarDecl(program)!!.value as StringLiteral }
strLits[0].value shouldBe "main.bar"
strLits[1].value shouldBe "foo.bar"
@@ -50,7 +50,7 @@ class TestCompilerOnImportsAndIncludes: FunSpec({
val filepath = assumeReadableFile(fixturesDir, "asmIncludeFromSameFolder.p8")
assumeReadableFile(fixturesDir, "foo_bar.asm")
val platform = Cx16Target
val platform = Cx16Target()
val result = compileFile(platform, optimize = false, fixturesDir, filepath.name)
.assertSuccess()
@@ -60,7 +60,7 @@ class TestCompilerOnImportsAndIncludes: FunSpec({
.filterIsInstance<FunctionCallStatement>()
.map { it.args[0] }
val str0 = (args[0] as IdentifierReference).targetVarDecl(program)!!.value as StringLiteralValue
val str0 = (args[0] as IdentifierReference).targetVarDecl(program)!!.value as StringLiteral
str0.value shouldBe "main.bar"
str0.definingScope.name shouldBe "main"
@@ -76,7 +76,7 @@ class TestCompilerOnImportsAndIncludes: FunSpec({
val p8Path = assumeReadableFile(fixturesDir, "asmBinaryNonExisting.p8")
assumeNotExists(fixturesDir, "i_do_not_exist.bin")
compileFile(Cx16Target, false, p8Path.parent, p8Path.name, outputDir)
compileFile(Cx16Target(), false, p8Path.parent, p8Path.name, outputDir)
.assertFailure()
}
@@ -84,7 +84,7 @@ class TestCompilerOnImportsAndIncludes: FunSpec({
val p8Path = assumeReadableFile(fixturesDir, "asmBinaryNonReadable.p8")
assumeDirectory(fixturesDir, "subFolder")
compileFile(Cx16Target, false, p8Path.parent, p8Path.name, outputDir)
compileFile(Cx16Target(), false, p8Path.parent, p8Path.name, outputDir)
.assertFailure()
}
@@ -104,7 +104,7 @@ class TestCompilerOnImportsAndIncludes: FunSpec({
outputDir.normalize().toAbsolutePath() shouldNotBe workingDir.normalize().toAbsolutePath()
}
compileFile(Cx16Target, false, p8Path.parent, p8Path.name, outputDir)
compileFile(Cx16Target(), false, p8Path.parent, p8Path.name, outputDir)
.assertSuccess(
"argument to assembler directive .binary " +
"should be relative to the generated .asm file (in output dir), " +
+30 -31
View File
@@ -7,17 +7,16 @@ import io.kotest.matchers.string.shouldContain
import io.kotest.matchers.types.instanceOf
import prog8.ast.base.DataType
import prog8.ast.base.Position
import prog8.ast.expressions.*
import prog8.ast.expressions.ArrayLiteral
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteral
import prog8.ast.expressions.RangeExpression
import prog8.ast.statements.ForLoop
import prog8.ast.statements.VarDecl
import prog8.codegen.target.C64Target
import prog8.codegen.target.Cx16Target
import prog8.compilerinterface.Encoding
import prog8tests.helpers.*
import prog8tests.helpers.ErrorReporterForTests
import prog8tests.helpers.assertFailure
import prog8tests.helpers.assertSuccess
import prog8tests.helpers.compileText
/**
@@ -28,11 +27,11 @@ import prog8tests.helpers.compileText
class TestCompilerOnRanges: FunSpec({
test("testUByteArrayInitializerWithRange_char_to_char") {
val platform = Cx16Target
val platform = Cx16Target()
val result = compileText(platform, false, """
main {
sub start() {
ubyte[] cs = @'a' to 'z' ; values are computed at compile time
ubyte[] cs = sc:'a' to 'z' ; values are computed at compile time
cs[0] = 23 ; keep optimizer from removing it
}
}
@@ -42,9 +41,9 @@ class TestCompilerOnRanges: FunSpec({
val startSub = program.entrypoint
val decl = startSub
.statements.filterIsInstance<VarDecl>()[0]
val rhsValues = (decl.value as ArrayLiteralValue)
val rhsValues = (decl.value as ArrayLiteral)
.value // Array<Expression>
.map { (it as NumericLiteralValue).number.toInt() }
.map { (it as NumericLiteral).number.toInt() }
val expectedStart = platform.encodeString("a", Encoding.SCREENCODES)[0].toInt()
val expectedEnd = platform.encodeString("z", Encoding.PETSCII)[0].toInt()
val expectedStr = "$expectedStart .. $expectedEnd"
@@ -59,7 +58,7 @@ class TestCompilerOnRanges: FunSpec({
}
test("testFloatArrayInitializerWithRange_char_to_char") {
val platform = C64Target
val platform = C64Target()
val result = compileText(platform, optimize = false, """
%import floats
main {
@@ -74,9 +73,9 @@ class TestCompilerOnRanges: FunSpec({
val startSub = program.entrypoint
val decl = startSub
.statements.filterIsInstance<VarDecl>()[0]
val rhsValues = (decl.value as ArrayLiteralValue)
val rhsValues = (decl.value as ArrayLiteral)
.value // Array<Expression>
.map { (it as NumericLiteralValue).number.toInt() }
.map { (it as NumericLiteral).number.toInt() }
val expectedStart = platform.encodeString("a", Encoding.PETSCII)[0].toInt()
val expectedEnd = platform.encodeString("z", Encoding.PETSCII)[0].toInt()
val expectedStr = "$expectedStart .. $expectedEnd"
@@ -94,7 +93,7 @@ class TestCompilerOnRanges: FunSpec({
val combos = cartesianProduct(
listOf("", "42", "41"), // sizeInDecl
listOf("%import floats", ""), // optEnableFloats
listOf(Cx16Target, C64Target), // platform
listOf(Cx16Target(), C64Target()), // platform
listOf(false, true) // optimize
)
@@ -129,12 +128,12 @@ class TestCompilerOnRanges: FunSpec({
}
test("testForLoopWithRange_char_to_char") {
val platform = Cx16Target
val platform = Cx16Target()
val result = compileText(platform, optimize = true, """
main {
sub start() {
ubyte i
for i in @'a' to 'f' {
for i in sc:'a' to 'f' {
i += i ; keep optimizer from removing it
}
}
@@ -163,7 +162,7 @@ class TestCompilerOnRanges: FunSpec({
}
test("testForLoopWithRange_bool_to_bool") {
val platform = Cx16Target
val platform = Cx16Target()
val result = compileText(platform, optimize = true, """
main {
sub start() {
@@ -189,7 +188,7 @@ class TestCompilerOnRanges: FunSpec({
}
test("testForLoopWithRange_ubyte_to_ubyte") {
val platform = Cx16Target
val platform = Cx16Target()
val result = compileText(platform, optimize = true, """
main {
sub start() {
@@ -216,7 +215,7 @@ class TestCompilerOnRanges: FunSpec({
test("testForLoopWithRange_str_downto_str") {
val errors = ErrorReporterForTests()
compileText(Cx16Target, true, """
compileText(Cx16Target(), true, """
main {
sub start() {
ubyte i
@@ -232,7 +231,7 @@ class TestCompilerOnRanges: FunSpec({
}
test("testForLoopWithIterable_str") {
val result = compileText(Cx16Target, false, """
val result = compileText(Cx16Target(), false, """
main {
sub start() {
ubyte i
@@ -255,16 +254,16 @@ class TestCompilerOnRanges: FunSpec({
test("testRangeExprNumericSize") {
val expr = RangeExpression(
NumericLiteralValue.optimalInteger(10, Position.DUMMY),
NumericLiteralValue.optimalInteger(20, Position.DUMMY),
NumericLiteralValue.optimalInteger(2, Position.DUMMY),
NumericLiteral.optimalInteger(10, Position.DUMMY),
NumericLiteral.optimalInteger(20, Position.DUMMY),
NumericLiteral.optimalInteger(2, Position.DUMMY),
Position.DUMMY)
expr.size() shouldBe 6
expr.toConstantIntegerRange()
}
test("range with negative step should be constvalue") {
val result = compileText(C64Target, false, """
val result = compileText(C64Target(), false, """
main {
sub start() {
ubyte[] array = 100 to 50 step -2
@@ -276,15 +275,15 @@ class TestCompilerOnRanges: FunSpec({
""").assertSuccess()
val statements = result.program.entrypoint.statements
val array = (statements[0] as VarDecl).value
array shouldBe instanceOf<ArrayLiteralValue>()
(array as ArrayLiteralValue).value.size shouldBe 26
array shouldBe instanceOf<ArrayLiteral>()
(array as ArrayLiteral).value.size shouldBe 26
val forloop = (statements.dropLast(1).last() as ForLoop)
forloop.iterable shouldBe instanceOf<RangeExpression>()
(forloop.iterable as RangeExpression).step shouldBe NumericLiteralValue(DataType.UBYTE, -2.0, Position.DUMMY)
(forloop.iterable as RangeExpression).step shouldBe NumericLiteral(DataType.UBYTE, -2.0, Position.DUMMY)
}
test("range with start/end variables should be ok") {
val result = compileText(C64Target, false, """
val result = compileText(C64Target(), false, """
main {
sub start() {
byte from = 100
@@ -298,12 +297,12 @@ class TestCompilerOnRanges: FunSpec({
val statements = result.program.entrypoint.statements
val forloop = (statements.dropLast(1).last() as ForLoop)
forloop.iterable shouldBe instanceOf<RangeExpression>()
(forloop.iterable as RangeExpression).step shouldBe NumericLiteralValue(DataType.UBYTE, -2.0, Position.DUMMY)
(forloop.iterable as RangeExpression).step shouldBe NumericLiteral(DataType.UBYTE, -2.0, Position.DUMMY)
}
test("for statement on all possible iterable expressions") {
compileText(C64Target, false, """
compileText(C64Target(), false, """
main {
sub start() {
ubyte xx
@@ -344,7 +343,7 @@ class TestCompilerOnRanges: FunSpec({
}
test("if containment check on all possible iterable expressions") {
compileText(C64Target, false, """
compileText(C64Target(), false, """
main {
sub start() {
ubyte xx
@@ -405,7 +404,7 @@ class TestCompilerOnRanges: FunSpec({
}
test("containment check in expressions") {
compileText(C64Target, false, """
compileText(C64Target(), false, """
main {
sub start() {
ubyte xx
+3 -3
View File
@@ -1,12 +1,11 @@
package prog8tests
import io.kotest.core.spec.style.FunSpec
import prog8.codegen.target.Cx16Target
import prog8.compiler.CompilationResult
import prog8.compiler.CompilerArguments
import prog8.compiler.compileProgram
import prog8.codegen.target.Cx16Target
import prog8tests.helpers.*
import prog8tests.helpers.assertSuccess
import java.nio.file.Path
import kotlin.io.path.absolute
import kotlin.io.path.createTempFile
@@ -46,7 +45,8 @@ class TestCompilerOptionSourcedirs: FunSpec({
slowCodegenWarnings = false,
quietAssembler = true,
asmListfile = false,
compilationTarget = Cx16Target.name,
experimentalCodegen = false,
compilationTarget = Cx16Target.NAME,
sourceDirs,
outputDir
)
@@ -5,9 +5,9 @@ import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.string.shouldStartWith
import prog8.ast.internedStringsModuleName
import prog8.codegen.target.C64Target
import prog8.compiler.determineCompilationOptions
import prog8.compiler.parseImports
import prog8.codegen.target.C64Target
import prog8.compilerinterface.ZeropageType
import prog8tests.helpers.ErrorReporterForTests
import prog8tests.helpers.assertSuccess
@@ -18,7 +18,7 @@ import prog8tests.helpers.outputDir
class TestImportedModulesOrderAndOptions: FunSpec({
test("testImportedModuleOrderAndMainModuleCorrect") {
val result = compileText(C64Target, false, """
val result = compileText(C64Target(), false, """
%import textio
%import floats
@@ -36,7 +36,7 @@ main {
}
withClue("module order in parse tree") {
moduleNames.drop(1) shouldBe listOf(
"prog8_interned_strings",
internedStringsModuleName,
"textio",
"syslib",
"conv",
@@ -49,7 +49,7 @@ main {
}
test("testCompilationOptionsCorrectFromMain") {
val result = compileText(C64Target, false, """
val result = compileText(C64Target(), false, """
%import textio
%import floats
%zeropage dontuse
@@ -62,7 +62,7 @@ main {
}
""").assertSuccess()
result.program.toplevelModule.name shouldStartWith "on_the_fly_test"
val options = determineCompilationOptions(result.program, C64Target)
val options = determineCompilationOptions(result.program, C64Target())
options.floats shouldBe true
options.zeropage shouldBe ZeropageType.DONTUSE
options.noSysInit shouldBe true
@@ -85,7 +85,7 @@ main {
val filenameBase = "on_the_fly_test_" + sourceText.hashCode().toUInt().toString(16)
val filepath = outputDir.resolve("$filenameBase.p8")
filepath.toFile().writeText(sourceText)
val (program, options, importedfiles) = parseImports(filepath, errors, C64Target, emptyList())
val (program, options, importedfiles) = parseImports(filepath, errors, C64Target(), emptyList())
program.toplevelModule.name shouldBe filenameBase
withClue("all imports other than the test source must have been internal resources library files") {
+85 -77
View File
@@ -9,22 +9,20 @@ import prog8.ast.base.Position
import prog8.ast.base.VarDeclType
import prog8.ast.expressions.ArrayIndexedExpression
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.NumericLiteral
import prog8.ast.expressions.PrefixExpression
import prog8.ast.statements.*
import prog8.compiler.printProgram
import prog8.codegen.target.C64Target
import prog8.compiler.printProgram
import prog8.compilerinterface.isIOAddress
import prog8.parser.SourceCode
import prog8tests.helpers.*
import prog8tests.helpers.DummyFunctions
import prog8tests.helpers.DummyMemsizer
import prog8tests.helpers.DummyStringEncoder
import prog8tests.helpers.compileText
class TestMemory: FunSpec({
val c64target = C64Target()
fun wrapWithProgram(statements: List<Statement>): Program {
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
val subroutine = Subroutine("test", mutableListOf(), emptyList(), statements.toMutableList(), false, Position.DUMMY)
@@ -35,202 +33,212 @@ class TestMemory: FunSpec({
test("assignment target not in mapped IO space C64") {
var memexpr = NumericLiteralValue.optimalInteger(0x0002, Position.DUMMY)
var memexpr = NumericLiteral.optimalInteger(0x0002, Position.DUMMY)
var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
var assign = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
var assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(C64Target.machine) shouldBe false
target.isIOAddress(c64target.machine) shouldBe false
memexpr = NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY)
memexpr = NumericLiteral.optimalInteger(0x1000, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
assign = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(C64Target.machine) shouldBe false
target.isIOAddress(c64target.machine) shouldBe false
memexpr = NumericLiteralValue.optimalInteger(0x9fff, Position.DUMMY)
memexpr = NumericLiteral.optimalInteger(0x9fff, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
assign = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(C64Target.machine) shouldBe false
target.isIOAddress(c64target.machine) shouldBe false
memexpr = NumericLiteralValue.optimalInteger(0xa000, Position.DUMMY)
memexpr = NumericLiteral.optimalInteger(0xa000, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
assign = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(C64Target.machine) shouldBe false
target.isIOAddress(c64target.machine) shouldBe false
memexpr = NumericLiteralValue.optimalInteger(0xc000, Position.DUMMY)
memexpr = NumericLiteral.optimalInteger(0xc000, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
assign = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(C64Target.machine) shouldBe false
target.isIOAddress(c64target.machine) shouldBe false
memexpr = NumericLiteralValue.optimalInteger(0xcfff, Position.DUMMY)
memexpr = NumericLiteral.optimalInteger(0xcfff, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
assign = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(C64Target.machine) shouldBe false
target.isIOAddress(c64target.machine) shouldBe false
memexpr = NumericLiteralValue.optimalInteger(0xeeee, Position.DUMMY)
memexpr = NumericLiteral.optimalInteger(0xeeee, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
assign = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(C64Target.machine) shouldBe false
target.isIOAddress(c64target.machine) shouldBe false
memexpr = NumericLiteralValue.optimalInteger(0xffff, Position.DUMMY)
memexpr = NumericLiteral.optimalInteger(0xffff, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
assign = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(C64Target.machine) shouldBe false
target.isIOAddress(c64target.machine) shouldBe false
}
test("assign target in mapped IO space C64") {
var memexpr = NumericLiteralValue.optimalInteger(0x0000, Position.DUMMY)
var memexpr = NumericLiteral.optimalInteger(0x0000, Position.DUMMY)
var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
var assign = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
var assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(C64Target.machine) shouldBe true
target.isIOAddress(c64target.machine) shouldBe true
memexpr = NumericLiteralValue.optimalInteger(0x0001, Position.DUMMY)
memexpr = NumericLiteral.optimalInteger(0x0001, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
assign = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(C64Target.machine) shouldBe true
target.isIOAddress(c64target.machine) shouldBe true
memexpr = NumericLiteralValue.optimalInteger(0xd000, Position.DUMMY)
memexpr = NumericLiteral.optimalInteger(0xd000, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
assign = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(C64Target.machine) shouldBe true
target.isIOAddress(c64target.machine) shouldBe true
memexpr = NumericLiteralValue.optimalInteger(0xdfff, Position.DUMMY)
memexpr = NumericLiteral.optimalInteger(0xdfff, Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
assign = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(C64Target.machine) shouldBe true
target.isIOAddress(c64target.machine) shouldBe true
}
fun createTestProgramForMemoryRefViaVar(address: UInt, vartype: VarDeclType): AssignTarget {
val decl = VarDecl(vartype, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, null, Position.DUMMY)
val decl = VarDecl(vartype, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, null, Position.DUMMY)
val memexpr = IdentifierReference(listOf("address"), Position.DUMMY)
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(decl, assignment))
return target
}
test("identifier mapped to IO memory on C64") {
var target = createTestProgramForMemoryRefViaVar(0x1000u, VarDeclType.VAR)
target.isIOAddress(C64Target.machine) shouldBe false
target.isIOAddress(c64target.machine) shouldBe false
target = createTestProgramForMemoryRefViaVar(0xd020u, VarDeclType.VAR)
target.isIOAddress(C64Target.machine) shouldBe false
target.isIOAddress(c64target.machine) shouldBe false
target = createTestProgramForMemoryRefViaVar(0x1000u, VarDeclType.CONST)
target.isIOAddress(C64Target.machine) shouldBe false
target.isIOAddress(c64target.machine) shouldBe false
target = createTestProgramForMemoryRefViaVar(0xd020u, VarDeclType.CONST)
target.isIOAddress(C64Target.machine) shouldBe true
target.isIOAddress(c64target.machine) shouldBe true
target = createTestProgramForMemoryRefViaVar(0x1000u, VarDeclType.MEMORY)
target.isIOAddress(C64Target.machine) shouldBe false
target.isIOAddress(c64target.machine) shouldBe false
target = createTestProgramForMemoryRefViaVar(0xd020u, VarDeclType.MEMORY)
target.isIOAddress(C64Target.machine) shouldBe true
target.isIOAddress(c64target.machine) shouldBe true
}
test("memory expression mapped to IO memory on C64") {
var memexpr = PrefixExpression("+", NumericLiteralValue.optimalInteger(0x1000, Position.DUMMY), Position.DUMMY)
var memexpr = PrefixExpression("+", NumericLiteral.optimalInteger(0x1000, Position.DUMMY), Position.DUMMY)
var target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
var assign = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
var assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
target.isIOAddress(C64Target.machine) shouldBe false
target.isIOAddress(c64target.machine) shouldBe false
memexpr = PrefixExpression("+", NumericLiteralValue.optimalInteger(0xd020, Position.DUMMY), Position.DUMMY)
memexpr = PrefixExpression("+", NumericLiteral.optimalInteger(0xd020, Position.DUMMY), Position.DUMMY)
target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
assign = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
assign = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
wrapWithProgram(listOf(assign))
printProgram(target.definingModule.program)
target.isIOAddress(C64Target.machine) shouldBe true
target.isIOAddress(c64target.machine) shouldBe true
}
test("regular variable not in mapped IO ram on C64") {
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, false, false, null, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
.addModule(module)
target.isIOAddress(C64Target.machine) shouldBe false
target.isIOAddress(c64target.machine) shouldBe false
}
test("memory mapped variable not in mapped IO ram on C64") {
val address = 0x1000u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, null, Position.DUMMY)
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, null, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
.addModule(module)
target.isIOAddress(C64Target.machine) shouldBe false
target.isIOAddress(c64target.machine) shouldBe false
}
test("memory mapped variable in mapped IO ram on C64") {
val address = 0xd020u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, null, Position.DUMMY)
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, null, Position.DUMMY)
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
.addModule(module)
target.isIOAddress(C64Target.machine) shouldBe true
target.isIOAddress(c64target.machine) shouldBe true
}
test("array not in mapped IO ram") {
val decl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", null, false, false, null, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), 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, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
.addModule(module)
target.isIOAddress(C64Target.machine) shouldBe false
target.isIOAddress(c64target.machine) shouldBe false
}
test("memory mapped array not in mapped IO ram") {
val address = 0x1000u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, null, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, null, 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, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
.addModule(module)
target.isIOAddress(C64Target.machine) shouldBe false
target.isIOAddress(c64target.machine) shouldBe false
}
test("memory mapped array in mapped IO ram") {
val address = 0xd800u
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, null, Position.DUMMY)
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
val decl = VarDecl(VarDeclType.MEMORY, VarDeclOrigin.USERCODE, DataType.ARRAY_UB, ZeropageWish.DONTCARE, null, "address", NumericLiteral.optimalInteger(address, Position.DUMMY), false, false, null, 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, Position.DUMMY)
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val assignment = Assignment(target, NumericLiteral.optimalInteger(0, Position.DUMMY), AssignmentOrigin.USERCODE, Position.DUMMY)
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
.addModule(module)
target.isIOAddress(C64Target.machine) shouldBe true
target.isIOAddress(c64target.machine) shouldBe true
}
test("memory() with spaces in name works") {
compileText(C64Target, false, """
compileText(C64Target(), false, """
main {
sub start() {
uword @shared mem = memory("a b c", 100)
uword @shared mem = memory("a b c", 100, $100)
}
}
""", writeAssembly = true).assertSuccess()
}
test("memory() with invalid argument") {
compileText(C64Target(), false, """
main {
sub start() {
uword @shared mem1 = memory("abc", 100, -2)
}
}
""", writeAssembly = true).assertFailure()
}
})
+32 -3
View File
@@ -126,7 +126,7 @@ class TestNumbers: FunSpec({
}
"""
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
compileText(C64Target, true, src, writeAssembly = false, errors=errors).assertSuccess()
compileText(C64Target(), true, src, writeAssembly = false, errors=errors).assertSuccess()
errors.errors.size shouldBe 0
errors.warnings.size shouldBe 2
errors.warnings[0] shouldContain "converted to float"
@@ -145,11 +145,40 @@ class TestNumbers: FunSpec({
}
}
"""
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
compileText(C64Target, true, src, writeAssembly = false, errors=errors).assertFailure()
val errors = ErrorReporterForTests()
compileText(C64Target(), true, src, writeAssembly = false, errors=errors).assertFailure()
errors.errors.size shouldBe 2
errors.warnings.size shouldBe 0
errors.errors[0] shouldContain "converted to float"
errors.errors[1] shouldContain "converted to float"
}
test("out of range number assignments") {
val src="""
main {
sub start() {
uword @shared qq = ${'$'}2ff33
cx16.r0 = ${'$'}1fc0f
}
}
"""
val errors = ErrorReporterForTests()
compileText(C64Target(), true, src, writeAssembly = false, errors=errors).assertFailure()
errors.errors.size shouldBe 2
errors.warnings.size shouldBe 0
errors.errors[0] shouldContain "out of range"
errors.errors[1] shouldContain "out of range"
}
test("big numbers okay in const expressions if result fits") {
val src="""
main {
sub start() {
uword @shared qq = ${'$'}2ff33 >> 4
cx16.r0 = ${'$'}1fc0f >> 4
}
}
"""
compileText(C64Target(), true, src, writeAssembly = false).assertSuccess()
}
})
+154
View File
@@ -0,0 +1,154 @@
package prog8tests
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldContain
import prog8.ast.base.DataType
import prog8.ast.base.ExpressionError
import prog8.ast.base.Position
import prog8.ast.expressions.ArrayLiteral
import prog8.ast.expressions.InferredTypes
import prog8.ast.expressions.NumericLiteral
import prog8.ast.expressions.StringLiteral
import prog8.compilerinterface.Encoding
class TestNumericLiteral: FunSpec({
fun sameValueAndType(lv1: NumericLiteral, lv2: NumericLiteral): Boolean {
return lv1.type==lv2.type && lv1==lv2
}
val dummyPos = Position("test", 0, 0, 0)
test("testIdentity") {
val v = NumericLiteral(DataType.UWORD, 12345.0, dummyPos)
(v==v) shouldBe true
(v != v) shouldBe false
(v <= v) shouldBe true
(v >= v) shouldBe true
(v < v ) shouldBe false
(v > v ) shouldBe false
sameValueAndType(NumericLiteral(DataType.UWORD, 12345.0, dummyPos), NumericLiteral(DataType.UWORD, 12345.0, dummyPos)) shouldBe true
}
test("test rounding") {
shouldThrow<ExpressionError> {
NumericLiteral(DataType.BYTE, -2.345, dummyPos)
}.message shouldContain "refused rounding"
shouldThrow<ExpressionError> {
NumericLiteral(DataType.BYTE, -2.6, dummyPos)
}.message shouldContain "refused rounding"
shouldThrow<ExpressionError> {
NumericLiteral(DataType.UWORD, 2222.345, dummyPos)
}.message shouldContain "refused rounding"
NumericLiteral(DataType.UBYTE, 2.0, dummyPos).number shouldBe 2.0
NumericLiteral(DataType.BYTE, -2.0, dummyPos).number shouldBe -2.0
NumericLiteral(DataType.UWORD, 2222.0, dummyPos).number shouldBe 2222.0
NumericLiteral(DataType.FLOAT, 123.456, dummyPos)
}
test("testEqualsAndNotEquals") {
(NumericLiteral(DataType.UBYTE, 100.0, dummyPos) == NumericLiteral(DataType.UBYTE, 100.0, dummyPos)) shouldBe true
(NumericLiteral(DataType.UBYTE, 100.0, dummyPos) == NumericLiteral(DataType.UWORD, 100.0, dummyPos)) shouldBe true
(NumericLiteral(DataType.UBYTE, 100.0, dummyPos) == NumericLiteral(DataType.FLOAT, 100.0, dummyPos)) shouldBe true
(NumericLiteral(DataType.UWORD, 254.0, dummyPos) == NumericLiteral(DataType.UBYTE, 254.0, dummyPos)) shouldBe true
(NumericLiteral(DataType.UWORD, 12345.0, dummyPos) == NumericLiteral(DataType.UWORD, 12345.0, dummyPos)) shouldBe true
(NumericLiteral(DataType.UWORD, 12345.0, dummyPos) == NumericLiteral(DataType.FLOAT, 12345.0, dummyPos)) shouldBe true
(NumericLiteral(DataType.FLOAT, 100.0, dummyPos) == NumericLiteral(DataType.UBYTE, 100.0, dummyPos)) shouldBe true
(NumericLiteral(DataType.FLOAT, 22239.0, dummyPos) == NumericLiteral(DataType.UWORD, 22239.0, dummyPos)) shouldBe true
(NumericLiteral(DataType.FLOAT, 9.99, dummyPos) == NumericLiteral(DataType.FLOAT, 9.99, dummyPos)) shouldBe true
sameValueAndType(NumericLiteral(DataType.UBYTE, 100.0, dummyPos), NumericLiteral(DataType.UBYTE, 100.0, dummyPos)) shouldBe true
sameValueAndType(NumericLiteral(DataType.UBYTE, 100.0, dummyPos), NumericLiteral(DataType.UWORD, 100.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(DataType.UBYTE, 100.0, dummyPos), NumericLiteral(DataType.FLOAT, 100.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(DataType.UWORD, 254.0, dummyPos), NumericLiteral(DataType.UBYTE, 254.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(DataType.UWORD, 12345.0, dummyPos), NumericLiteral(DataType.UWORD, 12345.0, dummyPos)) shouldBe true
sameValueAndType(NumericLiteral(DataType.UWORD, 12345.0, dummyPos), NumericLiteral(DataType.FLOAT, 12345.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(DataType.FLOAT, 100.0, dummyPos), NumericLiteral(DataType.UBYTE, 100.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(DataType.FLOAT, 22239.0, dummyPos), NumericLiteral(DataType.UWORD, 22239.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(DataType.FLOAT, 9.99, dummyPos), NumericLiteral(DataType.FLOAT, 9.99, dummyPos)) shouldBe true
(NumericLiteral(DataType.UBYTE, 100.0, dummyPos) != NumericLiteral(DataType.UBYTE, 101.0, dummyPos)) shouldBe true
(NumericLiteral(DataType.UBYTE, 100.0, dummyPos) != NumericLiteral(DataType.UWORD, 101.0, dummyPos)) shouldBe true
(NumericLiteral(DataType.UBYTE, 100.0, dummyPos) != NumericLiteral(DataType.FLOAT, 101.0, dummyPos)) shouldBe true
(NumericLiteral(DataType.UWORD, 245.0, dummyPos) != NumericLiteral(DataType.UBYTE, 246.0, dummyPos)) shouldBe true
(NumericLiteral(DataType.UWORD, 12345.0, dummyPos) != NumericLiteral(DataType.UWORD, 12346.0, dummyPos)) shouldBe true
(NumericLiteral(DataType.UWORD, 12345.0, dummyPos) != NumericLiteral(DataType.FLOAT, 12346.0, dummyPos)) shouldBe true
(NumericLiteral(DataType.FLOAT, 9.99, dummyPos) != NumericLiteral(DataType.UBYTE, 9.0, dummyPos)) shouldBe true
(NumericLiteral(DataType.FLOAT, 9.99, dummyPos) != NumericLiteral(DataType.UWORD, 9.0, dummyPos)) shouldBe true
(NumericLiteral(DataType.FLOAT, 9.99, dummyPos) != NumericLiteral(DataType.FLOAT, 9.0, dummyPos)) shouldBe true
sameValueAndType(NumericLiteral(DataType.UBYTE, 100.0, dummyPos), NumericLiteral(DataType.UBYTE, 101.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(DataType.UBYTE, 100.0, dummyPos), NumericLiteral(DataType.UWORD, 101.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(DataType.UBYTE, 100.0, dummyPos), NumericLiteral(DataType.FLOAT, 101.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(DataType.UWORD, 245.0, dummyPos), NumericLiteral(DataType.UBYTE, 246.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(DataType.UWORD, 12345.0, dummyPos), NumericLiteral(DataType.UWORD, 12346.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(DataType.UWORD, 12345.0, dummyPos), NumericLiteral(DataType.FLOAT, 12346.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(DataType.FLOAT, 9.99, dummyPos), NumericLiteral(DataType.UBYTE, 9.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(DataType.FLOAT, 9.99, dummyPos), NumericLiteral(DataType.UWORD, 9.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(DataType.FLOAT, 9.99, dummyPos), NumericLiteral(DataType.FLOAT, 9.0, dummyPos)) shouldBe false
}
test("testEqualsRef") {
(StringLiteral("hello", Encoding.PETSCII, dummyPos) == StringLiteral("hello", Encoding.PETSCII, dummyPos)) shouldBe true
(StringLiteral("hello", Encoding.PETSCII, dummyPos) != StringLiteral("bye", Encoding.PETSCII, dummyPos)) shouldBe true
(StringLiteral("hello", Encoding.SCREENCODES, dummyPos) == StringLiteral("hello", Encoding.SCREENCODES, dummyPos)) shouldBe true
(StringLiteral("hello", Encoding.SCREENCODES, dummyPos) != StringLiteral("bye", Encoding.SCREENCODES, dummyPos)) shouldBe true
(StringLiteral("hello", Encoding.SCREENCODES, dummyPos) != StringLiteral("hello", Encoding.PETSCII, dummyPos)) shouldBe true
val lvOne = NumericLiteral(DataType.UBYTE, 1.0, dummyPos)
val lvTwo = NumericLiteral(DataType.UBYTE, 2.0, dummyPos)
val lvThree = NumericLiteral(DataType.UBYTE, 3.0, dummyPos)
val lvOneR = NumericLiteral(DataType.UBYTE, 1.0, dummyPos)
val lvTwoR = NumericLiteral(DataType.UBYTE, 2.0, dummyPos)
val lvThreeR = NumericLiteral(DataType.UBYTE, 3.0, dummyPos)
val lvFour= NumericLiteral(DataType.UBYTE, 4.0, dummyPos)
val lv1 = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_UB), arrayOf(lvOne, lvTwo, lvThree), dummyPos)
val lv2 = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_UB), arrayOf(lvOneR, lvTwoR, lvThreeR), dummyPos)
val lv3 = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_UB), arrayOf(lvOneR, lvTwoR, lvFour), dummyPos)
lv1 shouldBe lv2
lv1 shouldNotBe lv3
}
test("testGreaterThan") {
(NumericLiteral(DataType.UBYTE, 100.0, dummyPos) > NumericLiteral(DataType.UBYTE, 99.0, dummyPos)) shouldBe true
(NumericLiteral(DataType.UWORD, 254.0, dummyPos) > NumericLiteral(DataType.UWORD, 253.0, dummyPos)) shouldBe true
(NumericLiteral(DataType.FLOAT, 100.0, dummyPos) > NumericLiteral(DataType.FLOAT, 99.9, dummyPos)) shouldBe true
(NumericLiteral(DataType.UBYTE, 100.0, dummyPos) >= NumericLiteral(DataType.UBYTE, 100.0, dummyPos)) shouldBe true
(NumericLiteral(DataType.UWORD, 254.0, dummyPos) >= NumericLiteral(DataType.UWORD, 254.0, dummyPos)) shouldBe true
(NumericLiteral(DataType.FLOAT, 100.0, dummyPos) >= NumericLiteral(DataType.FLOAT, 100.0, dummyPos)) shouldBe true
(NumericLiteral(DataType.UBYTE, 100.0, dummyPos) > NumericLiteral(DataType.UBYTE, 100.0, dummyPos)) shouldBe false
(NumericLiteral(DataType.UWORD, 254.0, dummyPos) > NumericLiteral(DataType.UWORD, 254.0, dummyPos)) shouldBe false
(NumericLiteral(DataType.FLOAT, 100.0, dummyPos) > NumericLiteral(DataType.FLOAT, 100.0, dummyPos)) shouldBe false
(NumericLiteral(DataType.UBYTE, 100.0, dummyPos) >= NumericLiteral(DataType.UBYTE, 101.0, dummyPos)) shouldBe false
(NumericLiteral(DataType.UWORD, 254.0, dummyPos) >= NumericLiteral(DataType.UWORD, 255.0, dummyPos)) shouldBe false
(NumericLiteral(DataType.FLOAT, 100.0, dummyPos) >= NumericLiteral(DataType.FLOAT, 100.1, dummyPos)) shouldBe false
}
test("testLessThan") {
(NumericLiteral(DataType.UBYTE, 100.0, dummyPos) < NumericLiteral(DataType.UBYTE, 101.0, dummyPos)) shouldBe true
(NumericLiteral(DataType.UWORD, 254.0, dummyPos) < NumericLiteral(DataType.UWORD, 255.0, dummyPos)) shouldBe true
(NumericLiteral(DataType.FLOAT, 100.0, dummyPos) < NumericLiteral(DataType.FLOAT, 100.1, dummyPos)) shouldBe true
(NumericLiteral(DataType.UBYTE, 100.0, dummyPos) <= NumericLiteral(DataType.UBYTE, 100.0, dummyPos)) shouldBe true
(NumericLiteral(DataType.UWORD, 254.0, dummyPos) <= NumericLiteral(DataType.UWORD, 254.0, dummyPos)) shouldBe true
(NumericLiteral(DataType.FLOAT, 100.0, dummyPos) <= NumericLiteral(DataType.FLOAT, 100.0, dummyPos)) shouldBe true
(NumericLiteral(DataType.UBYTE, 100.0, dummyPos) < NumericLiteral(DataType.UBYTE, 100.0, dummyPos)) shouldBe false
(NumericLiteral(DataType.UWORD, 254.0, dummyPos) < NumericLiteral(DataType.UWORD, 254.0, dummyPos)) shouldBe false
(NumericLiteral(DataType.FLOAT, 100.0, dummyPos) < NumericLiteral(DataType.FLOAT, 100.0, dummyPos)) shouldBe false
(NumericLiteral(DataType.UBYTE, 100.0, dummyPos) <= NumericLiteral(DataType.UBYTE, 99.0, dummyPos)) shouldBe false
(NumericLiteral(DataType.UWORD, 254.0, dummyPos) <= NumericLiteral(DataType.UWORD, 253.0, dummyPos)) shouldBe false
(NumericLiteral(DataType.FLOAT, 100.0, dummyPos) <= NumericLiteral(DataType.FLOAT, 99.9, dummyPos)) shouldBe false
}
})
-154
View File
@@ -1,154 +0,0 @@
package prog8tests
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldContain
import prog8.ast.base.DataType
import prog8.ast.base.ExpressionError
import prog8.ast.base.Position
import prog8.ast.expressions.ArrayLiteralValue
import prog8.ast.expressions.InferredTypes
import prog8.ast.expressions.NumericLiteralValue
import prog8.ast.expressions.StringLiteralValue
import prog8.compilerinterface.Encoding
class TestNumericLiteralValue: FunSpec({
fun sameValueAndType(lv1: NumericLiteralValue, lv2: NumericLiteralValue): Boolean {
return lv1.type==lv2.type && lv1==lv2
}
val dummyPos = Position("test", 0, 0, 0)
test("testIdentity") {
val v = NumericLiteralValue(DataType.UWORD, 12345.0, dummyPos)
(v==v) shouldBe true
(v != v) shouldBe false
(v <= v) shouldBe true
(v >= v) shouldBe true
(v < v ) shouldBe false
(v > v ) shouldBe false
sameValueAndType(NumericLiteralValue(DataType.UWORD, 12345.0, dummyPos), NumericLiteralValue(DataType.UWORD, 12345.0, dummyPos)) shouldBe true
}
test("test rounding") {
shouldThrow<ExpressionError> {
NumericLiteralValue(DataType.BYTE, -2.345, dummyPos)
}.message shouldContain "refused rounding"
shouldThrow<ExpressionError> {
NumericLiteralValue(DataType.BYTE, -2.6, dummyPos)
}.message shouldContain "refused rounding"
shouldThrow<ExpressionError> {
NumericLiteralValue(DataType.UWORD, 2222.345, dummyPos)
}.message shouldContain "refused rounding"
NumericLiteralValue(DataType.UBYTE, 2.0, dummyPos).number shouldBe 2.0
NumericLiteralValue(DataType.BYTE, -2.0, dummyPos).number shouldBe -2.0
NumericLiteralValue(DataType.UWORD, 2222.0, dummyPos).number shouldBe 2222.0
NumericLiteralValue(DataType.FLOAT, 123.456, dummyPos)
}
test("testEqualsAndNotEquals") {
(NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos) == NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos) == NumericLiteralValue(DataType.UWORD, 100.0, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos) == NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.UWORD, 254.0, dummyPos) == NumericLiteralValue(DataType.UBYTE, 254.0, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.UWORD, 12345.0, dummyPos) == NumericLiteralValue(DataType.UWORD, 12345.0, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.UWORD, 12345.0, dummyPos) == NumericLiteralValue(DataType.FLOAT, 12345.0, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos) == NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.FLOAT, 22239.0, dummyPos) == NumericLiteralValue(DataType.UWORD, 22239.0, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos) == NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos)) shouldBe true
sameValueAndType(NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos), NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos)) shouldBe true
sameValueAndType(NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos), NumericLiteralValue(DataType.UWORD, 100.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos), NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteralValue(DataType.UWORD, 254.0, dummyPos), NumericLiteralValue(DataType.UBYTE, 254.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteralValue(DataType.UWORD, 12345.0, dummyPos), NumericLiteralValue(DataType.UWORD, 12345.0, dummyPos)) shouldBe true
sameValueAndType(NumericLiteralValue(DataType.UWORD, 12345.0, dummyPos), NumericLiteralValue(DataType.FLOAT, 12345.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos), NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteralValue(DataType.FLOAT, 22239.0, dummyPos), NumericLiteralValue(DataType.UWORD, 22239.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos), NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos) != NumericLiteralValue(DataType.UBYTE, 101.0, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos) != NumericLiteralValue(DataType.UWORD, 101.0, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos) != NumericLiteralValue(DataType.FLOAT, 101.0, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.UWORD, 245.0, dummyPos) != NumericLiteralValue(DataType.UBYTE, 246.0, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.UWORD, 12345.0, dummyPos) != NumericLiteralValue(DataType.UWORD, 12346.0, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.UWORD, 12345.0, dummyPos) != NumericLiteralValue(DataType.FLOAT, 12346.0, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos) != NumericLiteralValue(DataType.UBYTE, 9.0, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos) != NumericLiteralValue(DataType.UWORD, 9.0, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos) != NumericLiteralValue(DataType.FLOAT, 9.0, dummyPos)) shouldBe true
sameValueAndType(NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos), NumericLiteralValue(DataType.UBYTE, 101.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos), NumericLiteralValue(DataType.UWORD, 101.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos), NumericLiteralValue(DataType.FLOAT, 101.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteralValue(DataType.UWORD, 245.0, dummyPos), NumericLiteralValue(DataType.UBYTE, 246.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteralValue(DataType.UWORD, 12345.0, dummyPos), NumericLiteralValue(DataType.UWORD, 12346.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteralValue(DataType.UWORD, 12345.0, dummyPos), NumericLiteralValue(DataType.FLOAT, 12346.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos), NumericLiteralValue(DataType.UBYTE, 9.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos), NumericLiteralValue(DataType.UWORD, 9.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteralValue(DataType.FLOAT, 9.99, dummyPos), NumericLiteralValue(DataType.FLOAT, 9.0, dummyPos)) shouldBe false
}
test("testEqualsRef") {
(StringLiteralValue("hello", Encoding.PETSCII, dummyPos) == StringLiteralValue("hello", Encoding.PETSCII, dummyPos)) shouldBe true
(StringLiteralValue("hello", Encoding.PETSCII, dummyPos) != StringLiteralValue("bye", Encoding.PETSCII, dummyPos)) shouldBe true
(StringLiteralValue("hello", Encoding.SCREENCODES, dummyPos) == StringLiteralValue("hello", Encoding.SCREENCODES, dummyPos)) shouldBe true
(StringLiteralValue("hello", Encoding.SCREENCODES, dummyPos) != StringLiteralValue("bye", Encoding.SCREENCODES, dummyPos)) shouldBe true
(StringLiteralValue("hello", Encoding.SCREENCODES, dummyPos) != StringLiteralValue("hello", Encoding.PETSCII, dummyPos)) shouldBe true
val lvOne = NumericLiteralValue(DataType.UBYTE, 1.0, dummyPos)
val lvTwo = NumericLiteralValue(DataType.UBYTE, 2.0, dummyPos)
val lvThree = NumericLiteralValue(DataType.UBYTE, 3.0, dummyPos)
val lvOneR = NumericLiteralValue(DataType.UBYTE, 1.0, dummyPos)
val lvTwoR = NumericLiteralValue(DataType.UBYTE, 2.0, dummyPos)
val lvThreeR = NumericLiteralValue(DataType.UBYTE, 3.0, dummyPos)
val lvFour= NumericLiteralValue(DataType.UBYTE, 4.0, dummyPos)
val lv1 = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_UB), arrayOf(lvOne, lvTwo, lvThree), dummyPos)
val lv2 = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_UB), arrayOf(lvOneR, lvTwoR, lvThreeR), dummyPos)
val lv3 = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_UB), arrayOf(lvOneR, lvTwoR, lvFour), dummyPos)
lv1 shouldBe lv2
lv1 shouldNotBe lv3
}
test("testGreaterThan") {
(NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos) > NumericLiteralValue(DataType.UBYTE, 99.0, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.UWORD, 254.0, dummyPos) > NumericLiteralValue(DataType.UWORD, 253.0, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos) > NumericLiteralValue(DataType.FLOAT, 99.9, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos) >= NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.UWORD, 254.0, dummyPos) >= NumericLiteralValue(DataType.UWORD, 254.0, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos) >= NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos) > NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos)) shouldBe false
(NumericLiteralValue(DataType.UWORD, 254.0, dummyPos) > NumericLiteralValue(DataType.UWORD, 254.0, dummyPos)) shouldBe false
(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos) > NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos)) shouldBe false
(NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos) >= NumericLiteralValue(DataType.UBYTE, 101.0, dummyPos)) shouldBe false
(NumericLiteralValue(DataType.UWORD, 254.0, dummyPos) >= NumericLiteralValue(DataType.UWORD, 255.0, dummyPos)) shouldBe false
(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos) >= NumericLiteralValue(DataType.FLOAT, 100.1, dummyPos)) shouldBe false
}
test("testLessThan") {
(NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos) < NumericLiteralValue(DataType.UBYTE, 101.0, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.UWORD, 254.0, dummyPos) < NumericLiteralValue(DataType.UWORD, 255.0, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos) < NumericLiteralValue(DataType.FLOAT, 100.1, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos) <= NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.UWORD, 254.0, dummyPos) <= NumericLiteralValue(DataType.UWORD, 254.0, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos) <= NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos)) shouldBe true
(NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos) < NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos)) shouldBe false
(NumericLiteralValue(DataType.UWORD, 254.0, dummyPos) < NumericLiteralValue(DataType.UWORD, 254.0, dummyPos)) shouldBe false
(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos) < NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos)) shouldBe false
(NumericLiteralValue(DataType.UBYTE, 100.0, dummyPos) <= NumericLiteralValue(DataType.UBYTE, 99.0, dummyPos)) shouldBe false
(NumericLiteralValue(DataType.UWORD, 254.0, dummyPos) <= NumericLiteralValue(DataType.UWORD, 253.0, dummyPos)) shouldBe false
(NumericLiteralValue(DataType.FLOAT, 100.0, dummyPos) <= NumericLiteralValue(DataType.FLOAT, 99.9, dummyPos)) shouldBe false
}
})
+74 -70
View File
@@ -6,6 +6,7 @@ import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldContain
import io.kotest.matchers.string.shouldNotBeBlank
import io.kotest.matchers.types.instanceOf
import io.kotest.matchers.types.shouldBeSameInstanceAs
import prog8.ast.Program
@@ -15,6 +16,7 @@ import prog8.ast.base.Position
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.codegen.target.C64Target
import prog8.compiler.astprocessing.VariableExtractor
import prog8.compiler.astprocessing.processAstBeforeAsmGeneration
import prog8.compiler.printProgram
import prog8.compilerinterface.CompilationOptions
@@ -34,7 +36,7 @@ class TestOptimization: FunSpec({
}
}
"""
val result = compileText(C64Target, true, sourcecode).assertSuccess()
val result = compileText(C64Target(), true, sourcecode).assertSuccess()
val toplevelModule = result.program.toplevelModule
val mainBlock = toplevelModule.statements.single() as Block
val startSub = mainBlock.statements.single() as Subroutine
@@ -59,7 +61,7 @@ class TestOptimization: FunSpec({
}
}
"""
val result = compileText(C64Target, true, sourcecode).assertSuccess()
val result = compileText(C64Target(), true, sourcecode).assertSuccess()
val toplevelModule = result.program.toplevelModule
val mainBlock = toplevelModule.statements.single() as Block
val startSub = mainBlock.statements[0] as Subroutine
@@ -73,7 +75,7 @@ class TestOptimization: FunSpec({
}
test("generated constvalue from typecast inherits proper parent linkage") {
val number = NumericLiteralValue(DataType.UBYTE, 11.0, Position.DUMMY)
val number = NumericLiteral(DataType.UBYTE, 11.0, Position.DUMMY)
val tc = TypecastExpression(number, DataType.BYTE, false, Position.DUMMY)
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
tc.linkParents(ParentSentinel)
@@ -81,14 +83,14 @@ class TestOptimization: FunSpec({
number.parent shouldNotBe null
tc shouldBeSameInstanceAs number.parent
val constvalue = tc.constValue(program)!!
constvalue shouldBe instanceOf<NumericLiteralValue>()
constvalue shouldBe instanceOf<NumericLiteral>()
constvalue.number shouldBe 11.0
constvalue.type shouldBe DataType.BYTE
constvalue.parent shouldBeSameInstanceAs tc.parent
}
test("generated constvalue from prefixexpr inherits proper parent linkage") {
val number = NumericLiteralValue(DataType.UBYTE, 11.0, Position.DUMMY)
val number = NumericLiteral(DataType.UBYTE, 11.0, Position.DUMMY)
val pfx = PrefixExpression("-", number, Position.DUMMY)
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
pfx.linkParents(ParentSentinel)
@@ -96,7 +98,7 @@ class TestOptimization: FunSpec({
number.parent shouldNotBe null
pfx shouldBeSameInstanceAs number.parent
val constvalue = pfx.constValue(program)!!
constvalue shouldBe instanceOf<NumericLiteralValue>()
constvalue shouldBe instanceOf<NumericLiteral>()
constvalue.number shouldBe -11.0
constvalue.type shouldBe DataType.BYTE
constvalue.parent shouldBeSameInstanceAs pfx.parent
@@ -118,7 +120,7 @@ class TestOptimization: FunSpec({
cx16.r7s = llw - 900 + 999
}
}"""
val result = compileText(C64Target, true, source, writeAssembly = false).assertSuccess()
val result = compileText(C64Target(), true, source, writeAssembly = false).assertSuccess()
// expected:
// uword load_location
// load_location = 12345
@@ -140,23 +142,23 @@ class TestOptimization: FunSpec({
val addR0value = (stmts[5] as Assignment).value
val binexpr0 = addR0value as BinaryExpression
binexpr0.operator shouldBe "+"
binexpr0.right shouldBe NumericLiteralValue(DataType.UWORD, 10000.0, Position.DUMMY)
binexpr0.right shouldBe NumericLiteral(DataType.UWORD, 10000.0, Position.DUMMY)
val addR2value = (stmts[7] as Assignment).value
val binexpr2 = addR2value as BinaryExpression
binexpr2.operator shouldBe "+"
binexpr2.right shouldBe NumericLiteralValue(DataType.UWORD, 10000.0, Position.DUMMY)
binexpr2.right shouldBe NumericLiteral(DataType.UWORD, 10000.0, Position.DUMMY)
val addR4value = (stmts[9] as Assignment).value
val binexpr4 = addR4value as BinaryExpression
binexpr4.operator shouldBe "+"
binexpr4.right shouldBe NumericLiteralValue(DataType.UWORD, 22.0, Position.DUMMY)
binexpr4.right shouldBe NumericLiteral(DataType.UWORD, 22.0, Position.DUMMY)
val subR5value = (stmts[11] as Assignment).value
val binexpr5 = subR5value as BinaryExpression
binexpr5.operator shouldBe "-"
binexpr5.right shouldBe NumericLiteralValue(DataType.UWORD, 1899.0, Position.DUMMY)
binexpr5.right shouldBe NumericLiteral(DataType.UWORD, 1899.0, Position.DUMMY)
val subR7value = (stmts[13] as Assignment).value
val binexpr7 = subR7value as BinaryExpression
binexpr7.operator shouldBe "+"
binexpr7.right shouldBe NumericLiteralValue(DataType.UWORD, 99.0, Position.DUMMY)
binexpr7.right shouldBe NumericLiteral(DataType.UWORD, 99.0, Position.DUMMY)
}
test("const folding multiple scenarios * and /") {
@@ -171,7 +173,7 @@ class TestOptimization: FunSpec({
cx16.r4s = llw * 90 / 5 ; not optimized because of loss of integer division precision
}
}"""
val result = compileText(C64Target, true, source, writeAssembly = false).assertSuccess()
val result = compileText(C64Target(), true, source, writeAssembly = false).assertSuccess()
printProgram(result.program)
// expected:
// word llw
@@ -193,27 +195,27 @@ class TestOptimization: FunSpec({
val mulR0Value = (stmts[3] as Assignment).value
val binexpr0 = mulR0Value as BinaryExpression
binexpr0.operator shouldBe "*"
binexpr0.right shouldBe NumericLiteralValue(DataType.UWORD, 180.0, Position.DUMMY)
binexpr0.right shouldBe NumericLiteral(DataType.UWORD, 180.0, Position.DUMMY)
val mulR1Value = (stmts[5] as Assignment).value
val binexpr1 = mulR1Value as BinaryExpression
binexpr1.operator shouldBe "*"
binexpr1.right shouldBe NumericLiteralValue(DataType.UWORD, 180.0, Position.DUMMY)
binexpr1.right shouldBe NumericLiteral(DataType.UWORD, 180.0, Position.DUMMY)
val divR2Value = (stmts[7] as Assignment).value
val binexpr2 = divR2Value as BinaryExpression
binexpr2.operator shouldBe "/"
binexpr2.right shouldBe NumericLiteralValue(DataType.UWORD, 90.0, Position.DUMMY)
binexpr2.right shouldBe NumericLiteral(DataType.UWORD, 90.0, Position.DUMMY)
val mulR3Value = (stmts[9] as Assignment).value
val binexpr3 = mulR3Value as BinaryExpression
binexpr3.operator shouldBe "*"
binexpr3.right shouldBe NumericLiteralValue(DataType.UWORD, 5.0, Position.DUMMY)
binexpr3.right shouldBe NumericLiteral(DataType.UWORD, 5.0, Position.DUMMY)
val mulR4Value = (stmts[11] as Assignment).value
val binexpr4 = mulR4Value as BinaryExpression
binexpr4.operator shouldBe "*"
binexpr4.right shouldBe NumericLiteralValue(DataType.UWORD, 90.0, Position.DUMMY)
binexpr4.right shouldBe NumericLiteral(DataType.UWORD, 90.0, Position.DUMMY)
val divR4Value = (stmts[12] as Assignment).value
val binexpr4b = divR4Value as BinaryExpression
binexpr4b.operator shouldBe "/"
binexpr4b.right shouldBe NumericLiteralValue(DataType.UWORD, 5.0, Position.DUMMY)
binexpr4b.right shouldBe NumericLiteral(DataType.UWORD, 5.0, Position.DUMMY)
}
test("constantfolded and silently typecasted for initializervalues") {
@@ -228,7 +230,7 @@ class TestOptimization: FunSpec({
}
}
"""
val result = compileText(C64Target, false, sourcecode).assertSuccess()
val result = compileText(C64Target(), false, sourcecode).assertSuccess()
val mainsub = result.program.entrypoint
mainsub.statements.size shouldBe 10
val declTest = mainsub.statements[0] as VarDecl
@@ -241,19 +243,19 @@ class TestOptimization: FunSpec({
val declY2 = mainsub.statements[7] as VarDecl
val initY2 = mainsub.statements[8] as Assignment
mainsub.statements[9] shouldBe instanceOf<Return>()
(declTest.value as NumericLiteralValue).number shouldBe 10.0
(declTest.value as NumericLiteral).number shouldBe 10.0
declX1.value shouldBe null
declX2.value shouldBe null
declY1.value shouldBe null
declY2.value shouldBe null
(initX1.value as NumericLiteralValue).type shouldBe DataType.BYTE
(initX1.value as NumericLiteralValue).number shouldBe 11.0
(initX2.value as NumericLiteralValue).type shouldBe DataType.BYTE
(initX2.value as NumericLiteralValue).number shouldBe 11.0
(initY1.value as NumericLiteralValue).type shouldBe DataType.UBYTE
(initY1.value as NumericLiteralValue).number shouldBe 11.0
(initY2.value as NumericLiteralValue).type shouldBe DataType.UBYTE
(initY2.value as NumericLiteralValue).number shouldBe 11.0
(initX1.value as NumericLiteral).type shouldBe DataType.BYTE
(initX1.value as NumericLiteral).number shouldBe 11.0
(initX2.value as NumericLiteral).type shouldBe DataType.BYTE
(initX2.value as NumericLiteral).number shouldBe 11.0
(initY1.value as NumericLiteral).type shouldBe DataType.UBYTE
(initY1.value as NumericLiteral).number shouldBe 11.0
(initY2.value as NumericLiteral).type shouldBe DataType.UBYTE
(initY2.value as NumericLiteral).number shouldBe 11.0
}
test("typecasted assignment from ubyte logical expressoin to uword var") {
@@ -266,7 +268,7 @@ class TestOptimization: FunSpec({
}
}
"""
val result = compileText(C64Target, false, src, writeAssembly = false).assertSuccess()
val result = compileText(C64Target(), false, src, writeAssembly = false).assertSuccess()
// ww = ((( not bb as uword) or not ww) as uword)
val wwAssign = result.program.entrypoint.statements.last() as Assignment
@@ -287,7 +289,8 @@ class TestOptimization: FunSpec({
}
}
"""
val result = compileText(C64Target, false, src, writeAssembly = false).assertSuccess()
val result = compileText(C64Target(), false, src, writeAssembly = false).assertSuccess()
val variables = VariableExtractor().extractVars(result.program)
// bb = (( not bb as uword) or not ww)
val bbAssign = result.program.entrypoint.statements.last() as Assignment
@@ -299,34 +302,35 @@ class TestOptimization: FunSpec({
expr.right.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UWORD
expr.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
val options = CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, true, C64Target)
result.program.processAstBeforeAsmGeneration(options, ErrorReporterForTests())
val options = CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, true, C64Target(), outputDir= outputDir)
result.program.processAstBeforeAsmGeneration(options, DummyVarsAndConsts, ErrorReporterForTests())
// assignment is now split into:
// bb = not bb
// bb = (bb or not ww)
val assigns = result.program.entrypoint.statements.filterIsInstance<Assignment>()
val bbAssigns = assigns.filter { it.value !is NumericLiteralValue }
val bbAssigns = assigns.filter { it.value !is NumericLiteral }
bbAssigns.size shouldBe 2
bbAssigns[0].target.identifier!!.nameInSource shouldBe listOf("bb")
bbAssigns[0].value shouldBe instanceOf<PrefixExpression>()
(bbAssigns[0].value as PrefixExpression).operator shouldBe "not"
(bbAssigns[0].value as PrefixExpression).expression shouldBe IdentifierReference(listOf("bb"), Position.DUMMY)
((bbAssigns[0].value as PrefixExpression).expression as? IdentifierReference)?.nameInSource shouldBe listOf("bb")
bbAssigns[0].value.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
bbAssigns[1].target.identifier!!.nameInSource shouldBe listOf("bb")
val bbAssigns1expr = bbAssigns[1].value as BinaryExpression
bbAssigns1expr.operator shouldBe "or"
bbAssigns1expr.left shouldBe IdentifierReference(listOf("bb"), Position.DUMMY)
(bbAssigns1expr.left as? IdentifierReference)?.nameInSource shouldBe listOf("bb")
bbAssigns1expr.right shouldBe instanceOf<PrefixExpression>()
(bbAssigns1expr.right as PrefixExpression).operator shouldBe "not"
(bbAssigns1expr.right as PrefixExpression).expression shouldBe IdentifierReference(listOf("ww"), Position.DUMMY)
((bbAssigns1expr.right as PrefixExpression).expression as? IdentifierReference)?.nameInSource shouldBe listOf("ww")
bbAssigns1expr.inferType(result.program).getOrElse { fail("dt") } shouldBe DataType.UBYTE
val asm = generateAssembly(result.program, options)
asm.valid shouldBe true
val asm = generateAssembly(result.program, variables, options)
asm shouldNotBe null
asm!!.name.shouldNotBeBlank()
}
test("intermediate assignment steps generated for typecasted expression") {
@@ -338,7 +342,7 @@ class TestOptimization: FunSpec({
}
}
"""
val result = compileText(C64Target, true, src, writeAssembly = true).assertSuccess()
val result = compileText(C64Target(), true, src, writeAssembly = true).assertSuccess()
/* turned into:
ubyte r
r = 0
@@ -353,18 +357,18 @@ class TestOptimization: FunSpec({
st.size shouldBe 8
st.last() shouldBe instanceOf<Return>()
var assign = st[3] as Assignment
assign.target.identifier!!.nameInSource shouldBe listOf("prog8_lib","retval_interm_b")
assign.target.identifier!!.nameInSource shouldBe listOf("prog8_lib","tempvar_b")
assign = st[4] as Assignment
assign.target.identifier!!.nameInSource shouldBe listOf("prog8_lib","retval_interm_b")
assign.target.identifier!!.nameInSource shouldBe listOf("prog8_lib","tempvar_b")
assign = st[5] as Assignment
assign.target.identifier!!.nameInSource shouldBe listOf("prog8_lib","retval_interm_b")
assign.target.identifier!!.nameInSource shouldBe listOf("prog8_lib","tempvar_b")
assign = st[6] as Assignment
assign.target.identifier!!.nameInSource shouldBe listOf("bb")
}
test("asmgen correctly deals with float typecasting in augmented assignment") {
val src="""
%option enable_floats
%import floats
main {
sub start() {
@@ -374,17 +378,16 @@ class TestOptimization: FunSpec({
}
}
"""
val result = compileText(C64Target, optimize=false, src, writeAssembly = false).assertSuccess()
val result = compileText(C64Target(), optimize=false, src, writeAssembly = false).assertSuccess()
val assignFF = result.program.entrypoint.statements.last() as Assignment
assignFF.isAugmentable shouldBe true
assignFF.target.identifier!!.nameInSource shouldBe listOf("ff")
val value = assignFF.value as BinaryExpression
value.operator shouldBe "+"
value.left shouldBe IdentifierReference(listOf("ff"), Position.DUMMY)
(value.left as? IdentifierReference)?.nameInSource shouldBe listOf("ff")
value.right shouldBe instanceOf<TypecastExpression>()
val asm = generateAssembly(result.program)
asm.valid shouldBe true
compileText(C64Target(), optimize=false, src, writeAssembly = true).assertSuccess()
}
test("unused variable removal") {
@@ -401,7 +404,7 @@ class TestOptimization: FunSpec({
}
}
"""
val result = compileText(C64Target, optimize=true, src, writeAssembly=false).assertSuccess()
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false).assertSuccess()
result.program.entrypoint.statements.size shouldBe 7
val alldecls = result.program.entrypoint.allDefinedSymbols.toList()
alldecls.map { it.first } shouldBe listOf("unused_but_shared", "usedvar_only_written", "usedvar")
@@ -424,7 +427,7 @@ class TestOptimization: FunSpec({
}
}
}"""
val result = compileText(C64Target, optimize=true, src, writeAssembly=false).assertSuccess()
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false).assertSuccess()
printProgram(result.program)
result.program.entrypoint.statements.size shouldBe 3
val ifstmt = result.program.entrypoint.statements[0] as IfElse
@@ -453,7 +456,7 @@ class TestOptimization: FunSpec({
z6 = z1+z6-5
}
}"""
val result = compileText(C64Target, optimize=true, src, writeAssembly=false).assertSuccess()
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false).assertSuccess()
/* expected:
ubyte z1
z1 = 10
@@ -488,23 +491,23 @@ class TestOptimization: FunSpec({
val z6plus = statements[13] as Assignment
z1decl.name shouldBe "z1"
z1init.value shouldBe NumericLiteralValue(DataType.UBYTE, 10.0, Position.DUMMY)
z1init.value shouldBe NumericLiteral(DataType.UBYTE, 10.0, Position.DUMMY)
z2decl.name shouldBe "z2"
z2init.value shouldBe NumericLiteralValue(DataType.UBYTE, 255.0, Position.DUMMY)
z2init.value shouldBe NumericLiteral(DataType.UBYTE, 255.0, Position.DUMMY)
z3decl.name shouldBe "z3"
z3init.value shouldBe NumericLiteralValue(DataType.UBYTE, 1.0, Position.DUMMY)
z3init.value shouldBe NumericLiteral(DataType.UBYTE, 1.0, Position.DUMMY)
z4decl.name shouldBe "z4"
z4init.value shouldBe NumericLiteralValue(DataType.UBYTE, 0.0, Position.DUMMY)
z4init.value shouldBe NumericLiteral(DataType.UBYTE, 0.0, Position.DUMMY)
z5decl.name shouldBe "z5"
z5init.value shouldBe IdentifierReference(listOf("z1"), Position.DUMMY)
(z5init.value as? IdentifierReference)?.nameInSource shouldBe listOf("z1")
z5plus.isAugmentable shouldBe true
(z5plus.value as BinaryExpression).operator shouldBe "+"
(z5plus.value as BinaryExpression).right shouldBe NumericLiteralValue(DataType.UBYTE, 5.0, Position.DUMMY)
(z5plus.value as BinaryExpression).right shouldBe NumericLiteral(DataType.UBYTE, 5.0, Position.DUMMY)
z6decl.name shouldBe "z6"
z6init.value shouldBe IdentifierReference(listOf("z1"), Position.DUMMY)
(z6init.value as? IdentifierReference)?.nameInSource shouldBe listOf("z1")
z6plus.isAugmentable shouldBe true
(z6plus.value as BinaryExpression).operator shouldBe "-"
(z6plus.value as BinaryExpression).right shouldBe NumericLiteralValue(DataType.UBYTE, 5.0, Position.DUMMY)
(z6plus.value as BinaryExpression).right shouldBe NumericLiteral(DataType.UBYTE, 5.0, Position.DUMMY)
}
test("force_output option should work with optimizing memwrite assignment") {
@@ -520,7 +523,7 @@ class TestOptimization: FunSpec({
}
"""
val result = compileText(C64Target, optimize=true, src, writeAssembly=false).assertSuccess()
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false).assertSuccess()
val stmts = result.program.entrypoint.statements
stmts.size shouldBe 6
val assign=stmts.last() as Assignment
@@ -537,7 +540,7 @@ class TestOptimization: FunSpec({
}
}
"""
val result = compileText(C64Target, optimize=true, src, writeAssembly=false).assertSuccess()
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false).assertSuccess()
val stmts = result.program.entrypoint.statements
stmts.size shouldBe 6
val assign=stmts.last() as Assignment
@@ -556,7 +559,7 @@ class TestOptimization: FunSpec({
}
}
"""
val result = compileText(C64Target, optimize=true, src, writeAssembly=false).assertSuccess()
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false).assertSuccess()
val stmts = result.program.entrypoint.statements
stmts.size shouldBe 10
stmts.filterIsInstance<VarDecl>().size shouldBe 5
@@ -572,9 +575,10 @@ class TestOptimization: FunSpec({
}
"""
val errors = ErrorReporterForTests()
compileText(C64Target, optimize=true, src, writeAssembly=false, errors = errors).assertFailure()
errors.errors.size shouldBe 1
compileText(C64Target(), optimize=true, src, writeAssembly=false, errors = errors).assertFailure()
errors.errors.size shouldBe 2
errors.errors[0] shouldContain "type of value BYTE doesn't match target UBYTE"
errors.errors[1] shouldContain "out of range"
}
test("test augmented expression asmgen") {
@@ -590,7 +594,7 @@ class TestOptimization: FunSpec({
q=r
}
}"""
val result = compileText(C64Target, optimize=false, src, writeAssembly=true).assertSuccess()
val result = compileText(C64Target(), optimize=false, src, writeAssembly=true).assertSuccess()
result.program.entrypoint.statements.size shouldBe 11
result.program.entrypoint.statements.last() shouldBe instanceOf<Return>()
}
@@ -608,7 +612,7 @@ class TestOptimization: FunSpec({
}
}
"""
val result = compileText(C64Target, optimize=true, src, writeAssembly=false).assertSuccess()
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false).assertSuccess()
printProgram(result.program)
/* expected result:
uword yy
@@ -638,7 +642,7 @@ class TestOptimization: FunSpec({
xx = xx+10 ; so this should not be changed into xx=10
}
}"""
val result = compileText(C64Target, optimize=true, src, writeAssembly=false).assertSuccess()
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false).assertSuccess()
/*
expected result:
uword xx
@@ -657,12 +661,12 @@ class TestOptimization: FunSpec({
stmts.filterIsInstance<Assignment>().size shouldBe 5
val assignXX1 = stmts[1] as Assignment
assignXX1.target.identifier!!.nameInSource shouldBe listOf("xx")
assignXX1.value shouldBe NumericLiteralValue(DataType.UBYTE, 20.0, Position.DUMMY)
assignXX1.value shouldBe NumericLiteral(DataType.UBYTE, 20.0, Position.DUMMY)
val assignXX2 = stmts.last() as Assignment
assignXX2.target.identifier!!.nameInSource shouldBe listOf("xx")
val xxValue = assignXX2.value as BinaryExpression
xxValue.operator shouldBe "+"
xxValue.left shouldBe IdentifierReference(listOf("xx"), Position.DUMMY)
xxValue.right shouldBe NumericLiteralValue(DataType.UBYTE, 10.0, Position.DUMMY)
(xxValue.left as? IdentifierReference)?.nameInSource shouldBe listOf("xx")
xxValue.right shouldBe NumericLiteral(DataType.UBYTE, 10.0, Position.DUMMY)
}
})
Loaded 100 of 161 files, more files were not shown because too many files have changed in this diff. Show more