Compare commits

..
18 Commits
Author SHA1 Message Date
Irmen de Jong 7eb079050c fix strings.hash() on 6502 CPU's. NOTE: now takes string pointer in AY instead of R0 on all platforms 2025-05-01 22:47:43 +02:00
Irmen de Jong 2fdd5543b2 release 11.3.1 2025-04-30 22:40:54 +02:00
Irmen de Jong d04164c0a6 fix const evaluation of bitwise logical expressions (&, |, ^, <<, >>) of signed operands 2025-04-30 22:27:31 +02:00
Irmen de Jong b047731f82 removed some redundant arguments 2025-04-25 23:27:06 +02:00
Irmen de Jong 4d91f92a2e clearer error message 2025-04-25 19:38:45 +02:00
Irmen de Jong 98505d27b1 IR: fix invalid removal of label chunks and include asmsubs when looking for labels 2025-04-24 23:30:51 +02:00
Irmen de Jong cd63a58ad9 IR: get rid of problematic PREPARECALL pseudo instruction 2025-04-24 22:32:40 +02:00
Irmen de Jong 170f8dd092 IR: new CALLFAR instructions to support callfar into another bank 2025-04-24 21:17:48 +02:00
Irmen de Jong 619dcb6a84 fix error in IR register typing 2025-04-24 20:50:19 +02:00
Irmen de Jong 99ae8ea52e code cleanup 2025-04-24 18:51:03 +02:00
Irmen de Jong dc031c30eb add -version option to just print the compiler version and then exit 2025-04-24 18:13:35 +02:00
Irmen de Jong 1e702439b7 avoid JDK 21+/Kotlin method conflict of removeLast()/removeFirst()
background: see https://www.reddit.com/r/androiddev/comments/1gspjrs/dont_use_kotlins_removefirst_and_removelast_when/
https://youtrack.jetbrains.com/issue/KT-71375/Prevent-Kotlins-removeFirst-and-removeLast-from-causing-crashes-on-Android-14-and-below-after-upgrading-to-Android-API-Level-35

it's about Android but the problem also occurs on desktop JDKs for example when running a Java21 compiled prog8 with Java17
2025-04-24 18:11:42 +02:00
Irmen de Jong 8debc42381 slightly stricter assignment type compatibility checking 2025-04-24 15:05:35 +02:00
Irmen de Jong 532d719089 more optimal math.randrange() routines 2025-04-24 04:04:14 +02:00
Irmen de Jong b40860aca4 get rid of RTS after JMP 2025-04-23 17:08:16 +02:00
Irmen de Jong 2cbe6b5f7f info message when more optimal goto array[idx] is possible 2025-04-23 16:59:18 +02:00
Irmen de Jong d2cc7ccdfa remove redundant variable=0 initializations (BSS clear takes care of them) 2025-04-23 14:45:38 +02:00
Irmen de Jong 2cb183c6d8 fix regression for goto array[idx] on 6502 cpu which doesn't have jmp (ptr,x) 2025-04-23 02:56:10 +02:00
81 changed files with 546 additions and 555 deletions

No files matched your search

+2 -3
View File
@@ -68,6 +68,7 @@
<inspection_tool class="BlockingMethodInNonBlockingContext" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="BooleanConstructor" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="BooleanExpressionMayBeConditional" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="BooleanLiteralArgument" enabled="true" level="TEXT ATTRIBUTES" enabled_by_default="true" editorAttributes="CONSIDERATION_ATTRIBUTES" />
<inspection_tool class="BooleanMethodIsAlwaysInverted" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="BoxingBoxedValue" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="BusyWait" enabled="false" level="WARNING" enabled_by_default="false" />
@@ -765,7 +766,6 @@
<inspection_tool class="MathRoundingWithIntArgument" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MavenCoroutinesDeprecation" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="MeaninglessRecordAnnotationInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MemberVisibilityCanBePrivate" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="MetaAnnotationWithoutRuntimeRetention" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MethodCanBeVariableArityMethod" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="MethodNameSameAsClassName" enabled="false" level="WARNING" enabled_by_default="false" />
@@ -917,7 +917,6 @@
<inspection_tool class="PrimitiveArrayArgumentToVariableArgMethod" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ProblematicWhitespace" enabled="true" level="WEAK WARNING" enabled_by_default="true" editorAttributes="INFO_ATTRIBUTES" />
<inspection_tool class="ProtectedMemberInFinalClass" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PublicApiImplicitType" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="PublicField" enabled="false" level="INFORMATION" enabled_by_default="false">
<option name="ignoreEnums" value="false" />
<option name="ignorableAnnotations">
@@ -995,7 +994,7 @@
<option name="ignoreEmptySuperMethods" value="false" />
<option name="onlyReportWhenAnnotated" value="true" />
</inspection_tool>
<inspection_tool class="RegExpAnonymousGroup" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="RegExpAnonymousGroup" enabled="true" level="WEAK WARNING" enabled_by_default="true" editorAttributes="INFO_ATTRIBUTES" />
<inspection_tool class="RemoveLiteralUnderscores" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="ReplaceAllDot" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ReplaceAssignmentWithOperatorAssignment" enabled="false" level="INFORMATION" enabled_by_default="false">
@@ -14,5 +14,5 @@ interface IErrorReporter {
fun noErrorForLine(position: Position): Boolean
fun print_single_error(errormessage: String)
fun printSingleError(errormessage: String)
}
@@ -656,16 +656,16 @@ class AsmGen6502Internal (
}
if(expr.splitWords) {
assignExpressionToRegister(expr.index, RegisterOrPair.fromCpuRegister(register), false)
assignExpressionToRegister(expr.index, RegisterOrPair.fromCpuRegister(register))
return
}
when {
expr.type.isByteOrBool -> {
assignExpressionToRegister(expr.index, RegisterOrPair.fromCpuRegister(register), false)
assignExpressionToRegister(expr.index, RegisterOrPair.fromCpuRegister(register))
}
expr.type.isWord -> {
assignExpressionToRegister(expr.index, RegisterOrPair.A, false)
assignExpressionToRegister(expr.index, RegisterOrPair.A)
out(" asl a")
when (register) {
CpuRegister.A -> {}
@@ -675,7 +675,7 @@ class AsmGen6502Internal (
}
expr.type.isFloat -> {
require(options.compTarget.FLOAT_MEM_SIZE == 5) {"invalid float size ${expr.position}"}
assignExpressionToRegister(expr.index, RegisterOrPair.A, false)
assignExpressionToRegister(expr.index, RegisterOrPair.A)
out("""
sta P8ZP_SCRATCH_REG
asl a
@@ -750,7 +750,7 @@ class AsmGen6502Internal (
TargetStorageKind.REGISTER -> {
val zero = PtNumber(BaseDataType.UBYTE, 0.0, value.position)
zero.parent = value
assignExpressionToRegister(zero, target.register!!, false)
assignExpressionToRegister(zero, target.register!!)
return
}
else -> { }
@@ -1084,12 +1084,19 @@ $repeatLabel""")
else {
if(evaluateAddressExpression) {
val arrayIdx = jump.target as? PtArrayIndexer
if(arrayIdx!=null && !arrayIdx.splitWords) {
// if the jump target is an address in a non-split array (like a jump table of only pointers),
// more optimal assembly can be generated using JMP address,X
assignExpressionToRegister(arrayIdx.index, RegisterOrPair.A)
out(" asl a | tax")
return JumpTarget(asmSymbolName(arrayIdx.variable), true, true, false)
if (isTargetCpu(CpuType.CPU65C02) && arrayIdx!=null) {
if (!arrayIdx.splitWords) {
// if the jump target is an address in a non-split array (like a jump table of only pointers),
// on the 65c02, more optimal assembly can be generated using JMP address,X
assignExpressionToRegister(arrayIdx.index, RegisterOrPair.A)
out(" asl a | tax")
return JumpTarget(asmSymbolName(arrayIdx.variable), true, true, false)
} else {
// print a message when more optimal code is possible
val variable = symbolTable.lookup(arrayIdx.variable.name)!!
if(variable is StStaticVariable && variable.length!!<=128)
errors.info("the jump address array is @split, but @nosplit would create more efficient code here", jump.position)
}
}
// we can do the address evaluation right now and just use a temporary pointer variable
assignExpressionToVariable(jump.target, "P8ZP_SCRATCH_W1", DataType.UWORD)
@@ -1279,7 +1286,7 @@ $repeatLabel""")
}
if(addressExpr.operator=="+") {
val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr, false)
val ptrAndIndex = pointerViaIndexRegisterPossible(addressExpr)
if (ptrAndIndex == null) return false
if(write) {
@@ -1569,7 +1576,7 @@ $repeatLabel""")
val compare = if(useSbc) "sec | sbc" else "cmp"
fun cmpViaScratch() {
if(assignmentAsmGen.directIntoY(value)) {
assignExpressionToRegister(value, RegisterOrPair.Y, false)
assignExpressionToRegister(value, RegisterOrPair.Y)
out(" sty P8ZP_SCRATCH_REG")
} else {
out(" pha")
@@ -1628,7 +1635,7 @@ $repeatLabel""")
}
internal fun assignConditionValueToRegisterAndTest(condition: PtExpression) {
assignExpressionToRegister(condition, RegisterOrPair.A, false)
assignExpressionToRegister(condition, RegisterOrPair.A)
when(condition) {
is PtNumber,
is PtBool,
@@ -508,6 +508,7 @@ private fun optimizeIncDec(linesByFour: Sequence<List<IndexedValue<String>>>): L
private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<IndexedValue<String>>>): List<Modification> {
// jsr Sub + rts -> jmp Sub
// jmp Sub + rts -> jmp Sub
// rts + jmp -> remove jmp
// rts + bxx -> remove bxx
// lda + cmp #0 -> remove cmp, same for cpy and cpx.
@@ -520,7 +521,10 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<Indexe
val third = lines[2].value
if(!haslabel(second)) {
if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
if ((" jmp" in first || "\tjmp" in first ) && (" rts" in second || "\trts" in second)) {
mods += Modification(lines[1].index, true, null)
}
else if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
if("floats.pushFAC" !in first && "floats.popFAC" !in first) { // these 2 routines depend on being called with JSR!!
mods += Modification(lines[0].index, false, lines[0].value.replace("jsr", "jmp"))
mods += Modification(lines[1].index, true, null)
@@ -53,7 +53,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val memread = PtMemoryByte(fcall.position)
memread.add(fcall.args[0])
memread.parent = fcall
asmgen.assignExpressionToRegister(memread, RegisterOrPair.A, false)
asmgen.assignExpressionToRegister(memread, RegisterOrPair.A)
asmgen.out(" pha")
val memtarget = AsmAssignTarget(TargetStorageKind.MEMORY, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.position, memory=memread)
asmgen.assignExpressionTo(fcall.args[1], memtarget)
@@ -676,7 +676,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
if(fcall.args[1].asConstInteger() == 0) {
assignAsmGen.assignConstantByte(target, 0)
} else {
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A, false)
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A)
assignAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
}
}
@@ -815,7 +815,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
asmgen.out(" jsr floats.MOVFM")
if(resultRegister!=null) {
assignAsmGen.assignFAC1float(
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, fcall.definingISub(), fcall.position, null, null, null, resultRegister, null))
AsmAssignTarget(TargetStorageKind.REGISTER, asmgen, DataType.FLOAT, fcall.definingISub(), fcall.position))
}
}
@@ -90,7 +90,7 @@ internal class ForLoopsAsmGen(
// cannot use self-modifying code, cannot use cpu stack (because loop can be interrupted halfway)
// so we need to store the loop end value in a newly allocated temporary variable
val toValueVar = asmgen.getTempVarName(iterableDt.elementType().base)
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A)
asmgen.out(" sta $toValueVar")
// pre-check for end already reached
if(iterableDt.isSignedByteArray) {
@@ -136,7 +136,7 @@ internal class ForLoopsAsmGen(
// use self-modifying code to store the loop end comparison value
val modifiedLabel = asmgen.makeLabel("for_modified")
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A)
// pre-check for end already reached
if(iterableDt.isSignedByteArray) {
asmgen.out(" sta $modifiedLabel+1")
@@ -186,7 +186,7 @@ $modifiedLabel cmp #0 ; modified
val stepsize = range.step.asConstInteger()!!
val modifiedLabel = asmgen.makeLabel("for_modified")
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A)
// pre-check for end already reached
if(iterableDt.isSignedByteArray) {
asmgen.out(" sta $modifiedLabel+1")
@@ -234,9 +234,9 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
val param = sub.parameters[it]
val arg = call.args[it]
registersUsed += if(usesOtherRegistersWhileEvaluating(arg)) {
if(!registersUsed.any{it.statusflag!=null || it.registerOrPair in CpuRegisters})
if(!registersUsed.any{r -> r.statusflag!=null || r.registerOrPair in CpuRegisters})
argumentViaRegister(sub, IndexedValue(it, param.second), arg)
else if(registersUsed.any {it.statusflag!=null}) {
else if(registersUsed.any { r-> r.statusflag!=null }) {
throw AssemblyError("call argument evaluation problem: can't save cpu statusregister parameter ${call.position}")
}
else {
@@ -154,7 +154,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
val elseLabel = asmgen.makeLabel("else")
asmgen.out(" $elseBranchInstr $elseLabel")
asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false)
asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope)
} else {
@@ -171,7 +171,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
if(target.indirect) {
asmgen.out(" $falseBranch +")
if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true)
target = asmgen.getJumpTarget(jump)
asmgen.out("""
jmp (${target.asmLabel})
+""")
@@ -286,7 +286,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
if(target.indirect) {
asmgen.out(" bmi + | beq +")
if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jumpAfterIf, true)
target = asmgen.getJumpTarget(jumpAfterIf)
asmgen.out("""
jmp (${target.asmLabel})
+""")
@@ -306,7 +306,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
val elseLabel = asmgen.makeLabel("else")
asmgen.out(" bmi $elseLabel | beq $elseLabel")
asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false)
asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope)
} else {
@@ -353,7 +353,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
if(target.indirect) {
asmgen.out(" bmi + | bne ++")
if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jumpAfterIf, true)
target = asmgen.getJumpTarget(jumpAfterIf)
asmgen.out("""
+ jmp (${target.asmLabel})
+""")
@@ -375,7 +375,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
bpl $elseLabel
+""")
asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false)
asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope)
} else {
@@ -427,14 +427,14 @@ internal class IfElseAsmGen(private val program: PtProgram,
else
translateIfElseBodies("bpl", stmt)
} else {
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A, false)
asmgen.assignExpressionToRegister(condition.left, RegisterOrPair.A)
asmgen.cmpAwithByteValue(condition.right, false)
if(jumpAfterIf!=null) {
var target = asmgen.getJumpTarget(jumpAfterIf, false)
if(target.indirect) {
asmgen.out(" bcc + | beq +")
if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jumpAfterIf, true)
target = asmgen.getJumpTarget(jumpAfterIf)
asmgen.out("""
jmp (${target.asmLabel})
+""")
@@ -453,7 +453,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
val elseLabel = asmgen.makeLabel("else")
asmgen.out(" bcc $elseLabel | beq $elseLabel")
asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false)
asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope)
} else {
@@ -535,7 +535,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
eor #128
+ bpl +""")
if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true)
target = asmgen.getJumpTarget(jump)
asmgen.out("""
jmp (${target.asmLabel})
+""")
@@ -562,7 +562,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
eor #128
+ bmi $elseLabel""")
asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false)
asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope)
} else {
@@ -590,7 +590,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
cmp $valueLsb
bcs +""")
if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true)
target = asmgen.getJumpTarget(jump)
asmgen.out("""
_jump jmp (${target.asmLabel})
+""")
@@ -615,7 +615,7 @@ _jump jmp (${target.asmLabel})
sbc $valueMsb
bcs $elseLabel""")
asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false)
asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope)
} else {
@@ -667,7 +667,7 @@ _jump jmp (${target.asmLabel})
eor #128
+ bpl +""")
if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true)
target = asmgen.getJumpTarget(jump)
asmgen.out("""
jmp (${target.asmLabel})
+""")
@@ -694,7 +694,7 @@ _jump jmp (${target.asmLabel})
eor #128
+ bmi $elseLabel""")
asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false)
asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope)
} else {
@@ -721,7 +721,7 @@ _jump jmp (${target.asmLabel})
sbc $valueMsb
bcc +""")
if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true)
target = asmgen.getJumpTarget(jump)
asmgen.out("""
jmp (${target.asmLabel})
+""")
@@ -744,7 +744,7 @@ _jump jmp (${target.asmLabel})
sbc $valueMsb
bcc $elseLabel""")
asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false)
asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope)
} else {
@@ -829,7 +829,7 @@ _jump jmp (${target.asmLabel})
lda $valueLsb
bne ++""")
if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true)
target = asmgen.getJumpTarget(jump)
asmgen.out("""
+ jmp (${target.asmLabel})
+""")
@@ -857,7 +857,7 @@ _jump jmp (${target.asmLabel})
bne $elseLabel
+""")
asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false)
asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope)
} else {
@@ -908,7 +908,7 @@ _jump jmp (${target.asmLabel})
cmp #0
bne ++""")
if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true)
target = asmgen.getJumpTarget(jump)
asmgen.out("""
+ jmp (${target.asmLabel})
+""")
@@ -936,7 +936,7 @@ _jump jmp (${target.asmLabel})
bne $elseLabel
+""")
asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false)
asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope)
} else {
@@ -973,7 +973,7 @@ _jump jmp (${target.asmLabel})
lda $valueLsb
beq ++""")
if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true)
target = asmgen.getJumpTarget(jump)
asmgen.out("""
+ jmp (${target.asmLabel})
+""")
@@ -1001,7 +1001,7 @@ _jump jmp (${target.asmLabel})
beq $elseLabel
+""")
asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false)
asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope)
} else {
@@ -1053,7 +1053,7 @@ _jump jmp (${target.asmLabel})
cmp #0
beq ++""")
if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true)
target = asmgen.getJumpTarget(jump)
asmgen.out("""
+ jmp (${target.asmLabel})
+""")
@@ -1081,7 +1081,7 @@ _jump jmp (${target.asmLabel})
beq $elseLabel
+""")
asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false)
asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope)
} else {
@@ -1196,7 +1196,7 @@ _jump jmp (${target.asmLabel})
cpy $valueMsb
beq ++""")
if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true)
target = asmgen.getJumpTarget(jump)
asmgen.out("""
+ jmp (${target.asmLabel})
+""")
@@ -1221,7 +1221,7 @@ _jump jmp (${target.asmLabel})
beq $elseLabel
+""")
asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false)
asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope)
} else {
@@ -1248,7 +1248,7 @@ _jump jmp (${target.asmLabel})
cpy $valueMsb
bne +""")
if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true)
target = asmgen.getJumpTarget(jump)
asmgen.out("""
jmp (${target.asmLabel})
+""")
@@ -1273,7 +1273,7 @@ _jump jmp (${target.asmLabel})
cpy $valueMsb
bne $elseLabel""")
asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false)
asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope)
} else {
@@ -1302,7 +1302,7 @@ _jump jmp (${target.asmLabel})
cmp ${right.name}+1
beq ++""")
if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true)
target = asmgen.getJumpTarget(jump)
asmgen.out("""
+ jmp (${target.asmLabel})
+""")
@@ -1331,7 +1331,7 @@ _jump jmp (${target.asmLabel})
beq $elseLabel
+""")
asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false)
asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope)
} else {
@@ -1361,7 +1361,7 @@ _jump jmp (${target.asmLabel})
cmp ${right.name}+1
bne +""")
if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true)
target = asmgen.getJumpTarget(jump)
asmgen.out("""
jmp (${target.asmLabel})
+""")
@@ -1390,7 +1390,7 @@ _jump jmp (${target.asmLabel})
cmp ${right.name}+1
bne $elseLabel""")
asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false)
asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope)
} else {
@@ -1423,7 +1423,7 @@ _jump jmp (${target.asmLabel})
cmp #>$value
beq ++""")
if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true)
target = asmgen.getJumpTarget(jump)
asmgen.out("""
+ jmp (${target.asmLabel})
+""")
@@ -1452,7 +1452,7 @@ _jump jmp (${target.asmLabel})
beq $elseLabel
+""")
asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false)
asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope)
} else {
@@ -1481,7 +1481,7 @@ _jump jmp (${target.asmLabel})
cmp #>$value
bne +""")
if(target.needsExpressionEvaluation)
target = asmgen.getJumpTarget(jump, true)
target = asmgen.getJumpTarget(jump)
asmgen.out("""
jmp (${target.asmLabel})
+""")
@@ -1510,7 +1510,7 @@ _jump jmp (${target.asmLabel})
cmp #>$value
bne $elseLabel""")
asmgen.translate(stmt.ifScope)
asmgen.jmp(afterIfLabel, false)
asmgen.jmp(afterIfLabel)
asmgen.out(elseLabel)
asmgen.translate(stmt.elseScope)
} else {
@@ -15,18 +15,18 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
evalIfExpressionConditonAndBranchWhenFalse(expr.condition, falseLabel)
when {
expr.type.isByteOrBool -> {
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A, false)
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.A)
asmgen.jmp(endLabel)
asmgen.out(falseLabel)
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A, false)
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.A)
asmgen.out(endLabel)
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, false, false)
}
expr.type.isWord || expr.type.isString -> {
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY, false)
asmgen.assignExpressionToRegister(expr.truevalue, RegisterOrPair.AY)
asmgen.jmp(endLabel)
asmgen.out(falseLabel)
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY, false)
asmgen.assignExpressionToRegister(expr.falsevalue, RegisterOrPair.AY)
asmgen.out(endLabel)
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
}
@@ -195,7 +195,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
beq $falseLabel
+""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
asmgen.out("""
cmp $varRight
bne +
@@ -219,7 +219,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
cmp $varRight+1
bne $falseLabel""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
asmgen.out("""
cmp $varRight
bne $falseLabel
@@ -242,7 +242,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
beq $falseLabel
+""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
asmgen.out("""
cmp #<$number
bne +
@@ -265,7 +265,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
cmp #>$number
bne $falseLabel""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
asmgen.out( """
cmp #<$number
bne $falseLabel
@@ -284,7 +284,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
ora $varname+1
beq $falseLabel""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | beq $falseLabel")
}
}
@@ -299,7 +299,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
ora $varname+1
bne $falseLabel""")
} else {
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY, false)
asmgen.assignExpressionToRegister(expr, RegisterOrPair.AY)
asmgen.out(" sty P8ZP_SCRATCH_REG | ora P8ZP_SCRATCH_REG | bne $falseLabel")
}
}
@@ -353,7 +353,9 @@ internal class ProgramAndVarsGen(
initializers.forEach { assign ->
if((assign.value as? PtNumber)?.number != 0.0 || allocator.isZpVar(assign.target.identifier!!.name))
asmgen.translate(assign)
// the other variables that should be set to zero are done so as part of the BSS section.
else
throw AssemblyError("non-zp variable should not be initialized to zero; it will be zeroed as part of BSS clear")
// the other variables that should be set to zero are done so as part of the BSS section clear.
}
asmgen.out(" rts\n .bend")
}
@@ -53,7 +53,7 @@ internal class AnyExprAsmGen(
private fun assignByteBinExpr(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
when(expr.operator) {
"+" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | clc | adc P8ZP_SCRATCH_B1")
@@ -61,7 +61,7 @@ internal class AnyExprAsmGen(
return true
}
"-" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | sec | sbc P8ZP_SCRATCH_B1")
@@ -76,7 +76,7 @@ internal class AnyExprAsmGen(
"and" -> TODO("logical and (with optional shortcircuit) ${expr.position}")
"or" -> TODO("logical or (with optional shortcircuit) ${expr.position}")
"&" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | and P8ZP_SCRATCH_B1")
@@ -84,7 +84,7 @@ internal class AnyExprAsmGen(
return true
}
"|" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | ora P8ZP_SCRATCH_B1")
@@ -92,7 +92,7 @@ internal class AnyExprAsmGen(
return true
}
"^", "xor" -> {
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A, false)
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.A)
asmgen.out(" pha")
asmgen.assignExpressionToVariable(expr.right, "P8ZP_SCRATCH_B1", DataType.UBYTE)
asmgen.out(" pla | eor P8ZP_SCRATCH_B1")
@@ -1313,7 +1313,7 @@ internal class AssignmentAsmGen(
// special optimization for bytearray[y] + bytevalue : no need to use a tempvar, just use adc array,y
assignExpressionToRegister(right, RegisterOrPair.A, right.type.isSigned)
if(!leftArrayIndexer.index.isSimple()) asmgen.out(" pha")
asmgen.assignExpressionToRegister(leftArrayIndexer.index, RegisterOrPair.Y, false)
asmgen.assignExpressionToRegister(leftArrayIndexer.index, RegisterOrPair.Y)
if(!leftArrayIndexer.index.isSimple()) asmgen.out(" pla")
val arrayvarname = asmgen.asmSymbolName(leftArrayIndexer.variable)
asmgen.out(" clc | adc $arrayvarname,y")
@@ -1322,7 +1322,7 @@ internal class AssignmentAsmGen(
// special optimization for bytevalue +/- bytearray[y] : no need to use a tempvar, just use adc array,y or sbc array,y
assignExpressionToRegister(left, RegisterOrPair.A, left.type.isSigned)
if(!rightArrayIndexer.index.isSimple()) asmgen.out(" pha")
asmgen.assignExpressionToRegister(rightArrayIndexer.index, RegisterOrPair.Y, false)
asmgen.assignExpressionToRegister(rightArrayIndexer.index, RegisterOrPair.Y)
if(!rightArrayIndexer.index.isSimple()) asmgen.out(" pla")
val arrayvarname = asmgen.asmSymbolName(rightArrayIndexer.variable)
if (expr.operator == "+")
@@ -2855,7 +2855,7 @@ $endLabel""")
jsr floats.MOVMF""")
}
TargetStorageKind.ARRAY -> {
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false)
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A)
asmgen.out("""
ldy #<${target.asmVarname}
sty P8ZP_SCRATCH_W1
@@ -2887,7 +2887,7 @@ $endLabel""")
TargetStorageKind.ARRAY -> {
asmgen.out(" pha")
asmgen.saveRegisterStack(CpuRegister.Y, false)
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false)
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A)
asmgen.restoreRegisterStack(CpuRegister.Y, false)
asmgen.out(" pla")
asmgen.out("""
@@ -2924,7 +2924,7 @@ $endLabel""")
jsr floats.copy_float""")
}
TargetStorageKind.ARRAY -> {
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false)
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A)
asmgen.out("""
ldy #<$sourceName
sty P8ZP_SCRATCH_W1
@@ -3397,7 +3397,7 @@ $endLabel""")
} else {
require(target.array.index.type.isByteOrBool)
asmgen.saveRegisterStack(register, false)
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.Y, false)
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.Y)
asmgen.out(" pla | sta ${target.asmVarname},y")
}
}
@@ -3712,7 +3712,7 @@ $endLabel""")
asmgen.out(" stz ${target.asmVarname}+$indexValue")
}
else {
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.X, false)
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.X)
asmgen.out(" stz ${target.asmVarname},x")
}
}
@@ -3802,7 +3802,7 @@ $endLabel""")
sta ${target.asmVarname}+4""")
}
TargetStorageKind.ARRAY -> {
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false)
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A)
asmgen.out("""
ldy #<${target.asmVarname}
sty P8ZP_SCRATCH_W1
@@ -3836,7 +3836,7 @@ $endLabel""")
jsr floats.copy_float""")
}
TargetStorageKind.ARRAY -> {
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A, false)
asmgen.assignExpressionToRegister(target.array!!.index, RegisterOrPair.A)
asmgen.out("""
ldy #<${constFloat}
sty P8ZP_SCRATCH_W1
@@ -170,7 +170,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
if(memory.address is PtBinaryExpression && tryOptimizedMemoryInplace(memory.address as PtBinaryExpression, operator, value))
return
// slower method to calculate and use the pointer to access the memory with:
asmgen.assignExpressionToRegister(memory.address, RegisterOrPair.AY, false)
asmgen.assignExpressionToRegister(memory.address, RegisterOrPair.AY)
asmgen.saveRegisterStack(CpuRegister.A, true)
asmgen.saveRegisterStack(CpuRegister.Y, true)
if(asmgen.isTargetCpu(CpuType.CPU65C02))
@@ -583,7 +583,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
if(address.left is PtIdentifier && asmgen.isZpVar(address.left as PtIdentifier)) {
return (address.left as PtIdentifier).name
} else {
asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY, false)
asmgen.assignExpressionToRegister(address.left, RegisterOrPair.AY)
asmgen.out(" sta P8ZP_SCRATCH_W2 | sty P8ZP_SCRATCH_W2+1")
return "P8ZP_SCRATCH_W2"
}
@@ -635,9 +635,9 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
return true
}
if(rightTc!=null)
asmgen.assignExpressionToRegister(rightTc.value, RegisterOrPair.A, false)
asmgen.assignExpressionToRegister(rightTc.value, RegisterOrPair.A)
else
asmgen.assignExpressionToRegister(address.right, RegisterOrPair.A, false)
asmgen.assignExpressionToRegister(address.right, RegisterOrPair.A)
asmgen.out(" pha") // offset on stack
val zpPointerVarName = addrIntoZpPointer()
assignValueToA()
@@ -2740,7 +2740,7 @@ $shortcutLabel:""")
"+" -> {
// name += byteexpression
if(valueDt.isUnsignedByte) {
asmgen.assignExpressionToRegister(value, RegisterOrPair.A, false)
asmgen.assignExpressionToRegister(value, RegisterOrPair.A)
asmgen.out("""
clc
adc $name
+1 -1
View File
@@ -68,7 +68,7 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
override fun noErrors(): Boolean = errors.isEmpty()
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
override fun print_single_error(errormessage: String) { /* prints nothing in tests */ }
override fun printSingleError(errormessage: String) { /* prints nothing in tests */ }
override fun report() {
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
@@ -1,10 +1,10 @@
package prog8.codegen.experimental
import prog8.code.IAssemblyProgram
import prog8.code.ICodeGeneratorBackend
import prog8.code.SymbolTable
import prog8.code.ast.PtProgram
import prog8.code.core.CompilationOptions
import prog8.code.IAssemblyProgram
import prog8.code.ICodeGeneratorBackend
import prog8.code.core.IErrorReporter
import prog8.codegen.intermediate.IRCodeGen
import prog8.intermediate.IRFileWriter
@@ -26,7 +26,8 @@ class ExperiCodeGen: ICodeGeneratorBackend {
// this stub only writes the IR program to disk but doesn't generate anything else.
IRFileWriter(irProgram, null).write()
println("** experimental codegen stub: no assembly generated **")
if(!options.quiet)
println("** experimental codegen stub: no assembly generated **")
return EmptyProgram
}
}
@@ -34,7 +35,8 @@ class ExperiCodeGen: ICodeGeneratorBackend {
private object EmptyProgram : IAssemblyProgram {
override val name = "<Empty Program>"
override fun assemble(options: CompilationOptions, errors: IErrorReporter): Boolean {
println("** nothing assembled **")
if(!options.quiet)
println("** nothing assembled **")
return true
}
@@ -78,7 +78,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcCallfar(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
val bankTr = exprGen.translateExpression(call.args[0])
val addressTr = exprGen.translateExpression(call.args[1])
val argumentwordTr = exprGen.translateExpression(call.args[2])
@@ -91,7 +90,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcCallfar2(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
val bankTr = exprGen.translateExpression(call.args[0])
val addressTr = exprGen.translateExpression(call.args[1])
val argumentA = exprGen.translateExpression(call.args[2])
@@ -143,7 +141,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcStringCompare(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val left = exprGen.translateExpression(call.args[0])
val right = exprGen.translateExpression(call.args[1])
addToResult(result, left, left.resultReg, -1)
@@ -240,11 +237,11 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
}
BaseDataType.UWORD -> {
addToResult(result, tr, tr.resultReg, -1)
val resultReg = codeGen.registers.next(IRDataType.WORD)
val resultReg = codeGen.registers.next(IRDataType.BYTE) // sqrt of a word still produces just a byte result
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.SQRT, IRDataType.WORD, reg1=resultReg, reg2=tr.resultReg)
}
return ExpressionCodeResult(result, IRDataType.WORD, resultReg, -1)
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
}
BaseDataType.FLOAT -> {
addToResult(result, tr, -1, tr.resultFpReg)
@@ -278,7 +275,6 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
private fun funcClamp(call: PtBuiltinFunctionCall): ExpressionCodeResult {
val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
val type = irType(call.type)
val valueTr = exprGen.translateExpression(call.args[0])
val minimumTr = exprGen.translateExpression(call.args[1])
@@ -289,7 +289,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
val haystackVar = check.haystackHeapVar!!
when {
haystackVar.type.isString -> {
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
val elementTr = translateExpression(check.needle)
addToResult(result, elementTr, elementTr.resultReg, -1)
val iterableTr = translateExpression(haystackVar)
@@ -300,7 +299,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
}
haystackVar.type.isByteArray -> {
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
val elementTr = translateExpression(check.needle)
addToResult(result, elementTr, elementTr.resultReg, -1)
val iterableTr = translateExpression(haystackVar)
@@ -314,7 +312,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
}
haystackVar.type.isWordArray -> {
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
val elementTr = translateExpression(check.needle)
addToResult(result, elementTr, elementTr.resultReg, -1)
val iterableTr = translateExpression(haystackVar)
@@ -328,7 +325,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
}
haystackVar.type.isFloatArray -> {
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
val elementTr = translateExpression(check.needle)
addToResult(result, elementTr, -1, elementTr.resultFpReg)
val iterableTr = translateExpression(haystackVar)
@@ -618,7 +614,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
when (callTarget) {
is StSub -> {
val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = callTarget.parameters.size), null)
// assign the arguments
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
@@ -670,7 +665,6 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
}
is StExtSub -> {
val result = mutableListOf<IRCodeChunkBase>()
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = callTarget.parameters.size), null)
// assign the arguments
val argRegisters = mutableListOf<FunctionCallArgs.ArgumentSpec>()
for ((arg, parameter) in fcall.args.zip(callTarget.parameters)) {
@@ -709,9 +703,21 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
Opcode.CALL,
address = address.address.toInt(),
fcallArgs = FunctionCallArgs(argRegisters, returnRegs))
}
else {
TODO("callfar into another bank is not implemented for the selected compilation target")
} else if(address.constbank!=null) {
IRInstruction(
Opcode.CALLFAR,
address = address.address.toInt(),
immediate = address.constbank!!.toInt()
)
} else {
val tr = translateExpression(address.varbank!!)
require(tr.dt==IRDataType.BYTE)
result += tr.chunks
IRInstruction(
Opcode.CALLFARVB,
address = address.address.toInt(),
reg1 = tr.resultReg
)
}
}
addInstr(result, call, null)
@@ -48,6 +48,7 @@ class IRCodeGen(
irProg.linkChunks()
irProg.convertAsmChunks()
// the optimizer also does 1 essential step regardless of optimizations: joining adjacent chunks.
val optimizer = IRPeepholeOptimizer(irProg)
optimizer.optimize(options.optimize, errors)
irProg.validate()
@@ -64,15 +65,17 @@ class IRCodeGen(
if(variable.uninitialized && variable.parent.type==StNodeType.BLOCK) {
val block = variable.parent.astNode as PtBlock
val initialization = (block.children.firstOrNull {
it is PtAssignment && it.target.identifier?.name==variable.scopedName
it is PtAssignment && it.isVarInitializer && it.target.identifier?.name==variable.scopedName
} as PtAssignment?)
val initValue = initialization?.value
when(initValue){
is PtBool -> {
require(initValue.asInt()!=0) { "boolean var should not be initialized with false, it wil be set to false as part of BSS clear, initializer=$initialization" }
variable.setOnetimeInitNumeric(initValue.asInt().toDouble())
initsToRemove += block to initialization
}
is PtNumber -> {
require(initValue.number!=0.0) { "variable should not be initialized with 0, it will already be zeroed as part of BSS clear, initializer=$initialization" }
variable.setOnetimeInitNumeric(initValue.number)
initsToRemove += block to initialization
}
@@ -194,10 +197,7 @@ class IRCodeGen(
old.fpReg1,
old.fpReg2,
immediate = immediateValue,
null,
address = addressValue,
null,
null
address = addressValue
)
}
}
@@ -425,19 +425,19 @@ class IRCodeGen(
} else {
if(choice.statements.children.isEmpty()) {
// no statements for this choice value, jump to the end immediately
choice.values.children.map { it as PtNumber }.sortedBy { it.number }.forEach { value ->
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
it += IRInstruction(Opcode.BSTEQ, labelSymbol = endLabel)
choice.values.children.map { v -> v as PtNumber }.sortedBy { v -> v.number }.forEach { value ->
result += IRCodeChunk(null, null).also { chunk ->
chunk += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
chunk += IRInstruction(Opcode.BSTEQ, labelSymbol = endLabel)
}
}
} else {
val choiceLabel = createLabelName()
choices.add(choiceLabel to choice)
choice.values.children.map { it as PtNumber }.sortedBy { it.number }.forEach { value ->
result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
it += IRInstruction(Opcode.BSTEQ, labelSymbol = choiceLabel)
choice.values.children.map { v -> v as PtNumber }.sortedBy { v -> v.number }.forEach { value ->
result += IRCodeChunk(null, null).also { chunk ->
chunk += IRInstruction(Opcode.CMPI, valueDt, reg1=valueTr.resultReg, immediate = value.number.toInt())
chunk += IRInstruction(Opcode.BSTEQ, labelSymbol = choiceLabel)
}
}
}
@@ -1070,7 +1070,7 @@ class IRCodeGen(
}
// evaluate jump address expression into a register and jump indirectly to it
val tr = expressionEval.translateExpression(goto.target)
for(i in tr.chunks.flatMap { it.instructions }) {
for(i in tr.chunks.flatMap { c -> c.instructions }) {
it += i
}
it += IRInstruction(Opcode.JUMPI, reg1 = tr.resultReg)
@@ -23,6 +23,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
}
private fun optimizeOnlyJoinChunks() {
// this chunk-joining is REQUIRED (optimization or no) to end up with a structurally sound chunk list
irprog.foreachSub { sub ->
joinChunks(sub)
removeEmptyChunks(sub)
@@ -84,7 +85,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
/*
Empty Code chunk with label ->
If next chunk has no label -> move label to next chunk, remove original
If next chunk has label -> label name should be the same, remove original, otherwise merge both labels into 1.
If next chunk has label -> label name should be the same, in which case remove original, otherwise leave everything untouched.
If is last chunk -> keep chunk in place because of the label.
Empty Code chunk without label ->
should not have been generated! ERROR.
@@ -111,14 +112,6 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
if(index>0) {
if (chunk.label == nextchunk.label)
removeChunks += index
else {
removeChunks += index
replaceLabels[chunk.label!!] = nextchunk.label!!
replaceLabels.entries.forEach { (key, value) ->
if (value == chunk.label)
replaceLabels[key] = nextchunk.label!!
}
}
}
}
}
+1 -1
View File
@@ -66,7 +66,7 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
override fun noErrors(): Boolean = errors.isEmpty()
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
override fun print_single_error(errormessage: String) { /* prints nothing in tests */ }
override fun printSingleError(errormessage: String) { /* prints nothing in tests */ }
override fun report() {
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
+11 -8
View File
@@ -53,7 +53,7 @@ class TestIRPeepholeOpt: FunSpec({
irProg.chunks().single().instructions.size shouldBe 1
}
test("remove jmp to label below") {
test("remove jmp to label below but keep labels") {
val c1 = IRCodeChunk("main.start", null)
c1 += IRInstruction(Opcode.JUMP, labelSymbol = "label")
val c2 = IRCodeChunk("label", null)
@@ -69,13 +69,16 @@ class TestIRPeepholeOpt: FunSpec({
irProg.chunks().flatMap { it.instructions }.size shouldBe 5
val opt = IRPeepholeOptimizer(irProg)
opt.optimize(true, ErrorReporterForTests())
irProg.chunks().size shouldBe 3
irProg.chunks()[0].label shouldBe "main.start"
irProg.chunks()[1].label shouldBe "label2"
irProg.chunks()[2].label shouldBe "label3"
irProg.chunks()[0].isEmpty() shouldBe true
irProg.chunks()[1].isEmpty() shouldBe false
irProg.chunks()[2].isEmpty() shouldBe true
val chunks = irProg.chunks()
chunks.size shouldBe 4
chunks[0].label shouldBe "main.start"
chunks[1].label shouldBe "label"
chunks[2].label shouldBe "label2"
chunks[3].label shouldBe "label3"
chunks[0].isEmpty() shouldBe true
chunks[1].isEmpty() shouldBe true
chunks[2].isEmpty() shouldBe false
chunks[3].isEmpty() shouldBe true
val instr = irProg.chunks().flatMap { it.instructions }
instr.size shouldBe 2
instr[0].opcode shouldBe Opcode.JUMP
+2 -5
View File
@@ -554,11 +554,8 @@ class TestVmCodeGen: FunSpec({
val result = codegen.generate(program, st, options, errors) as VmAssemblyProgram
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
irChunks.size shouldBe 1
irChunks[0].instructions.size shouldBe 2
val preparecallInstr = irChunks[0].instructions[0]
preparecallInstr.opcode shouldBe Opcode.PREPARECALL
preparecallInstr.immediate shouldBe 0
val callInstr = irChunks[0].instructions[1]
irChunks[0].instructions.size shouldBe 1
val callInstr = irChunks[0].instructions[0]
callInstr.opcode shouldBe Opcode.CALL
callInstr.address shouldBe 0x5000
}
@@ -5,10 +5,7 @@ import prog8.ast.FatalAstException
import prog8.ast.Program
import prog8.ast.expressions.FunctionCallExpression
import prog8.ast.expressions.NumericLiteral
import prog8.code.core.BaseDataType
import prog8.code.core.Position
import prog8.code.core.isInteger
import prog8.code.core.isIntegerOrBool
import prog8.code.core.*
import kotlin.math.*
@@ -70,52 +67,45 @@ class ConstExprEvaluator {
}
private fun bitwiseXor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) {
if(right.type.isIntegerOrBool) {
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() xor (right.number.toInt() and 255)).toDouble(), left.position)
}
} else if(left.type==BaseDataType.UWORD) {
if(right.type.isInteger) {
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() xor right.number.toInt() and 65535).toDouble(), left.position)
}
} else if(left.type==BaseDataType.LONG) {
if(right.type.isInteger) {
return NumericLiteral.optimalNumeric((left.number.toInt() xor right.number.toInt()).toDouble(), left.position)
if(right.type.isIntegerOrBool) {
val leftDt = left.type
if(leftDt.isByteOrBool)
return NumericLiteral(BaseDataType.UBYTE, ((left.number.toInt() xor right.number.toInt()) and 255).toDouble(), left.position)
else if(leftDt.isInteger) {
if (leftDt == BaseDataType.UWORD || leftDt == BaseDataType.WORD)
return NumericLiteral(BaseDataType.UWORD, ((left.number.toInt() xor right.number.toInt()) and 65535).toDouble(), left.position)
else if (leftDt == BaseDataType.LONG)
return NumericLiteral.optimalNumeric((left.number.toInt() xor right.number.toInt()).toDouble(), left.position)
}
}
throw ExpressionError("cannot calculate $left ^ $right", left.position)
}
private fun bitwiseOr(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) {
if(right.type.isIntegerOrBool) {
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toDouble(), left.position)
}
} else if(left.type==BaseDataType.UWORD) {
if(right.type.isInteger) {
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() or right.number.toInt() and 65535).toDouble(), left.position)
}
} else if(left.type==BaseDataType.LONG) {
if(right.type.isInteger) {
return NumericLiteral.optimalNumeric((left.number.toInt() or right.number.toInt()).toDouble(), left.position)
if(right.type.isIntegerOrBool) {
val leftDt = left.type
if(leftDt.isByteOrBool)
return NumericLiteral(BaseDataType.UBYTE, ((left.number.toInt() or right.number.toInt()) and 255).toDouble(), left.position)
else if(leftDt.isInteger) {
if (leftDt == BaseDataType.UWORD || leftDt == BaseDataType.WORD)
return NumericLiteral(BaseDataType.UWORD, ((left.number.toInt() or right.number.toInt()) and 65535).toDouble(), left.position)
else if (leftDt == BaseDataType.LONG)
return NumericLiteral.optimalNumeric((left.number.toInt() or right.number.toInt()).toDouble(), left.position)
}
}
throw ExpressionError("cannot calculate $left | $right", left.position)
}
private fun bitwiseAnd(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
if(left.type==BaseDataType.UBYTE || left.type==BaseDataType.BOOL) {
if(right.type.isIntegerOrBool) {
return NumericLiteral(BaseDataType.UBYTE, (left.number.toInt() and (right.number.toInt() and 255)).toDouble(), left.position)
}
} else if(left.type==BaseDataType.UWORD) {
if(right.type.isInteger) {
return NumericLiteral(BaseDataType.UWORD, (left.number.toInt() and right.number.toInt() and 65535).toDouble(), left.position)
}
} else if(left.type==BaseDataType.LONG) {
if(right.type.isInteger) {
return NumericLiteral.optimalNumeric((left.number.toInt() and right.number.toInt()).toDouble(), left.position)
if(right.type.isIntegerOrBool) {
val leftDt = left.type
if(leftDt.isByteOrBool)
return NumericLiteral(BaseDataType.UBYTE, ((left.number.toInt() and right.number.toInt()) and 255).toDouble(), left.position)
else if(leftDt.isInteger) {
if (leftDt == BaseDataType.UWORD || leftDt == BaseDataType.WORD)
return NumericLiteral(BaseDataType.UWORD, ((left.number.toInt() and right.number.toInt()) and 65535).toDouble(), left.position)
else if (leftDt == BaseDataType.LONG)
return NumericLiteral.optimalNumeric((left.number.toInt() and right.number.toInt()).toDouble(), left.position)
}
}
throw ExpressionError("cannot calculate $left & $right", left.position)
@@ -129,7 +129,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
return listOf(IAstModification.ReplaceNode(expr, newArray, parent))
}
else {
val leftTarget = (expr.left as? IdentifierReference)?.targetVarDecl(program)
val leftTarget = (expr.left as? IdentifierReference)?.targetVarDecl()
if(leftTarget!=null && leftTarget.origin==VarDeclOrigin.ARRAYLITERAL)
throw FatalAstException("shouldn't see an array literal converted to an autovar here")
}
@@ -332,7 +332,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
val constIndex = arrayIndexedExpression.indexer.constIndex()
if (constIndex != null) {
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl()
if(arrayVar!=null) {
val array =arrayVar.value as? ArrayLiteral
if(array!=null) {
@@ -387,7 +387,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
val rangeTo = iterableRange.to as? NumericLiteral
if(rangeFrom==null || rangeTo==null) return noModifications
val loopvar = forLoop.loopVar.targetVarDecl(program) ?: return noModifications
val loopvar = forLoop.loopVar.targetVarDecl() ?: return noModifications
val stepLiteral = iterableRange.step as? NumericLiteral
require(loopvar.datatype.sub == null)
@@ -560,7 +560,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
}
}
else if(functionCallExpr.target.nameInSource == listOf("strings", "contains")) {
val target = (functionCallExpr.args[0] as? IdentifierReference)?.targetVarDecl(program)
val target = (functionCallExpr.args[0] as? IdentifierReference)?.targetVarDecl()
if(target?.value is StringLiteral) {
errors.info("for actual strings, use a regular containment check instead: 'char in string'", functionCallExpr.position)
val contains = ContainmentCheck(functionCallExpr.args[1], functionCallExpr.args[0], functionCallExpr.position)
@@ -16,7 +16,7 @@ import prog8.code.target.VMTarget
private fun isEmptyReturn(stmt: Statement): Boolean = stmt is Return && stmt.values.isEmpty()
// inliner potentially enables *ONE LINED* subroutines, wihtout to be inlined.
// inliner potentially enables *ONE LINED* subroutines, without to be inlined.
class Inliner(private val program: Program, private val options: CompilationOptions): AstWalker() {
@@ -105,7 +105,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
if (subroutine.inline && subroutine.statements.size > 1) {
require(subroutine.statements.size == 2 && isEmptyReturn(subroutine.statements[1]))
subroutine.statements.removeLast() // get rid of the Return, to be able to inline the (single) statement preceding it.
subroutine.statements.removeLastOrNull() // get rid of the Return, to be able to inline the (single) statement preceding it.
}
}
}
@@ -122,7 +122,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
private fun makeFullyScoped(call: FunctionCallStatement) {
makeFullyScoped(call.target)
call.target.targetSubroutine(program)?.let { sub ->
call.target.targetSubroutine()?.let { sub ->
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
val scopedArgs = makeScopedArgs(call.args)
if(scopedArgs.any()) {
@@ -134,7 +134,7 @@ class Inliner(private val program: Program, private val options: CompilationOpti
private fun makeFullyScoped(call: FunctionCallExpression) {
makeFullyScoped(call.target)
call.target.targetSubroutine(program)?.let { sub ->
call.target.targetSubroutine()?.let { sub ->
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
val scopedArgs = makeScopedArgs(call.args)
if(scopedArgs.any()) {
@@ -33,8 +33,8 @@ class StatementOptimizer(private val program: Program,
} else {
arg as? IdentifierReference
}
if(stringVar!=null && stringVar.wasStringLiteral(program)) {
val string = stringVar.targetVarDecl(program)?.value as? StringLiteral
if(stringVar!=null && stringVar.wasStringLiteral()) {
val string = stringVar.targetVarDecl()?.value as? StringLiteral
if(string!=null) {
val pos = functionCallStatement.position
if (string.value.length == 1) {
@@ -152,7 +152,7 @@ class StatementOptimizer(private val program: Program,
return listOf(IAstModification.ReplaceNode(forLoop, scope, parent))
}
}
val iterable = (forLoop.iterable as? IdentifierReference)?.targetVarDecl(program)
val iterable = (forLoop.iterable as? IdentifierReference)?.targetVarDecl()
if(iterable!=null) {
if(iterable.datatype.isString) {
val sv = iterable.value as StringLiteral
@@ -8,9 +8,9 @@ import prog8.ast.expressions.TypecastExpression
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.INTERNED_STRINGS_MODULENAME
import prog8.code.core.ICompilationTarget
import prog8.code.core.IErrorReporter
import prog8.code.INTERNED_STRINGS_MODULENAME
import prog8.compiler.CallGraph
@@ -27,10 +27,10 @@ class UnusedCodeRemover(private val program: Program,
program.allBlocks.singleOrNull { it.name=="sys" } ?.let {
val subroutines = it.statements.filterIsInstance<Subroutine>()
val push = subroutines.single { it.name == "push" }
val pushw = subroutines.single { it.name == "pushw" }
val pop = subroutines.single { it.name == "pop" }
val popw = subroutines.single { it.name == "popw" }
val push = subroutines.single { s -> s.name == "push" }
val pushw = subroutines.single { s -> s.name == "pushw" }
val pop = subroutines.single { s -> s.name == "pop" }
val popw = subroutines.single { s -> s.name == "popw" }
neverRemoveSubroutines.add(push)
neverRemoveSubroutines.add(pushw)
neverRemoveSubroutines.add(pop)
@@ -39,8 +39,8 @@ class UnusedCodeRemover(private val program: Program,
program.allBlocks.singleOrNull { it.name=="floats" } ?.let {
val subroutines = it.statements.filterIsInstance<Subroutine>()
val push = subroutines.single { it.name == "push" }
val pop = subroutines.single { it.name == "pop" }
val push = subroutines.single { s -> s.name == "push" }
val pop = subroutines.single { s -> s.name == "pop" }
neverRemoveSubroutines.add(push)
neverRemoveSubroutines.add(pop)
}
+19 -27
View File
@@ -102,43 +102,35 @@ _sinecosR8 .char trunc(127.0 * sin(range(180+45) * rad(360.0/180.0)))
}
sub randrange(ubyte n) -> ubyte {
; -- return random number uniformly distributed from 0 to n-1 (compensates for divisibility bias).
; NOTE: does not work for code in ROM
cx16.r0H = 255 / n * n
do {
cx16.r0L = math.rnd()
} until cx16.r0L < cx16.r0H
return cx16.r0L % n
; -- return random number uniformly distributed from 0 to n-1
; NOTE: does not work for code in ROM, use randrange_rom instead for that
; why this works: https://www.youtube.com/watch?v=3DvlLUWTNMY&t=347s
cx16.r0 = math.rnd() * (n as uword)
return cx16.r0H
}
sub randrange_rom(ubyte n) -> ubyte {
; -- return random number uniformly distributed from 0 to n-1 (compensates for divisibility bias).
; NOTE: works for code in ROM, make sure to initialize seed using rndseed_rom
cx16.r0H = 255 / n * n
do {
cx16.r0L = math.rnd_rom()
} until cx16.r0L < cx16.r0H
return cx16.r0L % n
; -- return random number uniformly distributed from 0 to n-1
; NOTE: works for code in ROM, make sure you have initialized the seed using rndseed_rom
; why this works: https://www.youtube.com/watch?v=3DvlLUWTNMY&t=347s
cx16.r0 = math.rnd_rom() * (n as uword)
return cx16.r0H
}
sub randrangew(uword n) -> uword {
; -- return random number uniformly distributed from 0 to n-1 (compensates for divisibility bias)
; -- return random number uniformly distributed from 0 to n-1
; NOTE: does not work for code in ROM
cx16.r1 = 65535 / n * n
do {
cx16.r0 = math.rndw()
} until cx16.r0 < cx16.r1
return cx16.r0 % n
; why this works: https://www.youtube.com/watch?v=3DvlLUWTNMY&t=347s
cx16.r0 = math.rndw() * n
return math.mul16_last_upper()
}
sub randrangew_rom(uword n) -> uword {
; -- return random number uniformly distributed from 0 to n-1 (compensates for divisibility bias)
; NOTE: works for code in ROM, make sure to initialize seed using rndseed_rom
cx16.r1 = 65535 / n * n
do {
cx16.r0 = math.rndw_rom()
} until cx16.r0 < cx16.r1
return cx16.r0 % n
; -- return random number uniformly distributed from 0 to n-1
; NOTE: works for code in ROM, make sure you have initialized the seed using rndseed_rom
; why this works: https://www.youtube.com/watch?v=3DvlLUWTNMY&t=347s
cx16.r0 = math.rndw_rom() * n
return math.mul16_last_upper()
}
asmsub rndseed(uword seed1 @AY, uword seed2 @R0) clobbers(A,Y) {
+4 -2
View File
@@ -391,16 +391,18 @@ fail clc ; yes, no match found, return with c=0
}}
}
asmsub hash(str string @R0) -> ubyte @A {
asmsub hash(str string @AY) -> ubyte @A {
; experimental 8 bit hashing function.
; hash(-1)=179; clear carry; hash(i) = ROL hash(i-1) XOR string[i]
; On the English word list in /usr/share/dict/words it seems to have a pretty even distribution
%asm {{
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #179
sta P8ZP_SCRATCH_REG
ldy #0
clc
- lda (cx16.r0),y
- lda (P8ZP_SCRATCH_W1),y
beq +
rol P8ZP_SCRATCH_REG
eor P8ZP_SCRATCH_REG
+8 -12
View File
@@ -177,21 +177,17 @@ math {
}
sub randrange(ubyte n) -> ubyte {
; -- return random number uniformly distributed from 0 to n-1 (compensates for divisibility bias)
cx16.r0H = 255 / n * n
do {
cx16.r0L = math.rnd()
} until cx16.r0L < cx16.r0H
return cx16.r0L % n
; -- return random number uniformly distributed from 0 to n-1
; why this works: https://www.youtube.com/watch?v=3DvlLUWTNMY&t=347s
cx16.r0 = math.rnd() * (n as uword)
return cx16.r0H
}
sub randrangew(uword n) -> uword {
; -- return random number uniformly distributed from 0 to n-1 (compensates for divisibility bias)
cx16.r1 = 65535 / n * n
do {
cx16.r0 = math.rndw()
} until cx16.r0 < cx16.r1
return cx16.r0 % n
; -- return random number uniformly distributed from 0 to n-1
; why this works: https://www.youtube.com/watch?v=3DvlLUWTNMY&t=347s
cx16.r0 = math.rndw() * n
return math.mul16_last_upper()
}
sub rndseed(uword seed1, uword seed2) {
+1 -1
View File
@@ -179,7 +179,7 @@ strings {
sub hash(str st) -> ubyte {
; experimental 8 bit hashing function.
; hash(-1)=179; hash(i) = ROL hash(i-1) XOR string[i]
; (experimental because the quality of the resulting hash value still has to be determined)
; On the English word list in /usr/share/dict/words it seems to have a pretty even distribution
ubyte hashcode = 179
ubyte ix
sys.clear_carry()
+13 -2
View File
@@ -57,7 +57,7 @@ private fun compileMain(args: Array<String>): Boolean {
val plainText by cli.option(ArgType.Boolean, fullName = "plaintext", description = "output only plain text, no colors or fancy symbols")
val printAst1 by cli.option(ArgType.Boolean, fullName = "printast1", description = "print out the internal compiler AST")
val printAst2 by cli.option(ArgType.Boolean, fullName = "printast2", description = "print out the simplified AST that is used for code generation")
val quietAll by cli.option(ArgType.Boolean, fullName = "quiet", description = "don't print compiler and assembler messages")
val quietAll by cli.option(ArgType.Boolean, fullName = "quiet", description = "don't print compiler and assembler messages, except warnings and errors")
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler messages")
val slabsGolden by cli.option(ArgType.Boolean, fullName = "slabsgolden", description = "put memory() slabs in 'golden ram' memory area instead of at the end of the program. On the cx16 target this is $0400-07ff. This is unavailable on other systems.")
val slabsHighBank by cli.option(ArgType.Int, fullName = "slabshigh", description = "put memory() slabs in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.")
@@ -70,7 +70,8 @@ private fun compileMain(args: Array<String>): Boolean {
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "run a .p8ir IR source file in the embedded VM")
val warnSymbolShadowing by cli.option(ArgType.Boolean, fullName = "warnshadow", description="show assembler warnings about symbol shadowing")
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
val version by cli.option(ArgType.Boolean, fullName = "version", description = "print compiler version and exit")
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").optional().multiple(999)
try {
cli.parse(args)
@@ -80,6 +81,11 @@ private fun compileMain(args: Array<String>): Boolean {
return false
}
if(version==true) {
banner()
return true
}
if(quietAll!=true)
banner()
@@ -123,6 +129,11 @@ private fun compileMain(args: Array<String>): Boolean {
return true
}
if(moduleFiles.isEmpty()) {
System.err.println("No module file(s) specified")
return false
}
if(varsHighBank==0 && compilationTarget==Cx16Target.NAME) {
System.err.println("On the Commander X16, HiRAM bank 0 is used by the kernal and can't be used.")
return false
@@ -1,8 +1,8 @@
package prog8.compiler
import prog8.ast.Program
import prog8.ast.AstException
import prog8.ast.FatalAstException
import prog8.ast.Program
import prog8.ast.SyntaxError
import prog8.ast.expressions.*
import prog8.ast.statements.VarDecl
@@ -119,12 +119,13 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
}
}
@Suppress("unused")
private fun builtinLen(args: List<Expression>, position: Position, program: Program): NumericLiteral {
// note: in some cases the length is > 255, and then we have to return a UWORD type instead of a UBYTE.
if(args.size!=1)
throw SyntaxError("len requires one argument", position)
val directMemVar = ((args[0] as? DirectMemoryRead)?.addressExpression as? IdentifierReference)?.targetVarDecl(program)
val directMemVar = ((args[0] as? DirectMemoryRead)?.addressExpression as? IdentifierReference)?.targetVarDecl()
var arraySize = directMemVar?.arraysize?.constIndex()
if(arraySize != null)
return NumericLiteral.optimalInteger(arraySize, position)
@@ -134,7 +135,7 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
return NumericLiteral.optimalInteger((args[0] as StringLiteral).value.length, position)
if(args[0] !is IdentifierReference)
throw SyntaxError("len argument should be an identifier", position)
val target = (args[0] as IdentifierReference).targetVarDecl(program)
val target = (args[0] as IdentifierReference).targetVarDecl()
?: throw CannotEvaluateException("len", "no target vardecl")
return when {
+6 -6
View File
@@ -200,7 +200,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
}
return CompilationResult(resultingProgram!!, ast, compilationOptions, importedFiles)
} catch (px: ParseError) {
args.errors.print_single_error("${px.position.toClickableStr()} parse error: ${px.message}".trim())
args.errors.printSingleError("${px.position.toClickableStr()} parse error: ${px.message}".trim())
} catch (ac: ErrorsReportedException) {
if(args.printAst1 && resultingProgram!=null) {
println("\n*********** COMPILER AST *************")
@@ -217,17 +217,17 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
}
}
if(!ac.message.isNullOrEmpty()) {
args.errors.print_single_error(ac.message!!)
args.errors.printSingleError(ac.message!!)
}
} catch (nsf: NoSuchFileException) {
args.errors.print_single_error("File not found: ${nsf.message}")
args.errors.printSingleError("File not found: ${nsf.message}")
} catch (ax: AstException) {
args.errors.print_single_error(ax.toString())
args.errors.printSingleError(ax.toString())
} catch (x: Exception) {
args.errors.print_single_error("\ninternal error")
args.errors.printSingleError("\ninternal error")
throw x
} catch (x: NotImplementedError) {
args.errors.print_single_error("\ninternal error: missing feature/code")
args.errors.printSingleError("\ninternal error: missing feature/code")
throw x
}
+1 -1
View File
@@ -95,7 +95,7 @@ internal class ErrorReporter(val colors: IConsoleColors): IErrorReporter {
override fun noErrors() = messages.none { it.severity==MessageSeverity.ERROR }
override fun noErrorForLine(position: Position) = !messages.any { it.position.line==position.line && it.severity!=MessageSeverity.INFO }
override fun print_single_error(errormessage: String) {
override fun printSingleError(errormessage: String) {
System.out.flush()
colors.error(System.err)
System.err.println(errormessage)
@@ -39,7 +39,7 @@ class ModuleImporter(private val program: Program,
println("Compiling program ${Path("").absolute().relativize(programPath)}")
println("Compiler target: $compilationTargetName")
}
val source = ImportFileSystem.getFile(programPath, false)
val source = ImportFileSystem.getFile(programPath)
return Ok(importModule(source))
}
}
@@ -158,7 +158,7 @@ class ModuleImporter(private val program: Program,
normalLocations.forEach {
try {
return Ok(ImportFileSystem.getFile(it.resolve(fileName), false))
return Ok(ImportFileSystem.getFile(it.resolve(fileName)))
} catch (_: NoSuchFileException) {
}
}
@@ -189,7 +189,7 @@ internal class AstChecker(private val program: Program,
} else if(!(iterableDt.isIterable) && forLoop.iterable !is RangeExpression) {
errors.err("can only loop over an iterable type", forLoop.position)
} else {
val loopvar = forLoop.loopVar.targetVarDecl(program)
val loopvar = forLoop.loopVar.targetVarDecl()
if(loopvar==null || loopvar.type== VarDeclType.CONST) {
errors.err("for loop requires a variable to loop with", forLoop.position)
} else {
@@ -392,7 +392,7 @@ internal class AstChecker(private val program: Program,
err("variable bank extsub has no romable code-generation for the required jsrfar call, stick to constant bank, or create a system-ram trampoline")
}
if(varbank.targetVarDecl(program)?.datatype?.isUnsignedByte!=true)
if(varbank.targetVarDecl()?.datatype?.isUnsignedByte!=true)
err("bank variable must be ubyte")
}
if(subroutine.inline && subroutine.asmAddress!=null)
@@ -558,7 +558,7 @@ internal class AstChecker(private val program: Program,
val ident = repeatLoop.iterations as? IdentifierReference
if(ident!=null) {
val targetVar = ident.targetVarDecl(program)
val targetVar = ident.targetVarDecl()
if(targetVar==null)
errors.err("invalid assignment value, maybe forgot '&' (address-of)", ident.position)
}
@@ -598,7 +598,7 @@ internal class AstChecker(private val program: Program,
}
val fcall = assignment.value as? IFunctionCall
val fcallTarget = fcall?.target?.targetSubroutine(program)
val fcallTarget = fcall?.target?.targetSubroutine()
if(assignment.target.multi!=null) {
checkMultiAssignment(assignment, fcall, fcallTarget)
} else if(fcallTarget!=null) {
@@ -681,8 +681,7 @@ internal class AstChecker(private val program: Program,
if (assignment.value !is BinaryExpression && assignment.value !is PrefixExpression && assignment.value !is ContainmentCheck && assignment.value !is IfExpression)
errors.err("invalid assignment value, maybe forgot '&' (address-of)", assignment.value.position)
} else {
checkAssignmentCompatible(assignTarget, targetDatatype.getOrUndef(),
sourceDatatype.getOrUndef(), assignment.value)
checkAssignmentCompatible(targetDatatype.getOrUndef(),sourceDatatype.getOrUndef(), assignment.value.position)
}
}
}
@@ -691,7 +690,7 @@ internal class AstChecker(private val program: Program,
fun checkRomTarget(target: AssignTarget) {
val idx=target.arrayindexed
if(idx!=null) {
val decl = idx.arrayvar.targetVarDecl(program)!!
val decl = idx.arrayvar.targetVarDecl()!!
if(decl.type!=VarDeclType.MEMORY && decl.zeropage!=ZeropageWish.REQUIRE_ZEROPAGE) {
// memory mapped arrays are assumed to be in RAM. If they're not.... well, POOF
errors.err("cannot assign to an array or string that is located in ROM (option romable is enabled)", assignTarget.position)
@@ -709,7 +708,7 @@ internal class AstChecker(private val program: Program,
override fun visit(addressOf: AddressOf) {
checkLongType(addressOf)
val variable=addressOf.identifier.targetVarDecl(program)
val variable=addressOf.identifier.targetVarDecl()
if (variable!=null) {
if (variable.type == VarDeclType.CONST && addressOf.arrayIndex == null)
errors.err("invalid pointer-of operand type", addressOf.position)
@@ -1124,14 +1123,6 @@ internal class AstChecker(private val program: Program,
errors.err("array literal for iteration must contain constants. Try using a separate array variable instead?", array.position)
}
if(array.parent is Assignment) {
val arraydt = array.inferType(program)
val assignTarget = (array.parent as Assignment).target
val targetDt = assignTarget.inferType(program)
if(arraydt!=targetDt)
errors.err("value has incompatible type ($arraydt) for the variable ($targetDt)", array.position)
}
super.visit(array)
}
@@ -1162,7 +1153,7 @@ internal class AstChecker(private val program: Program,
override fun visit(expr: PrefixExpression) {
if(expr.expression is IFunctionCall) {
val targetStatement = (expr.expression as IFunctionCall).target.targetSubroutine(program)
val targetStatement = (expr.expression as IFunctionCall).target.targetSubroutine()
if(targetStatement?.returntypes?.isEmpty()==true) {
errors.err("subroutine doesn't return a value", expr.expression.position)
}
@@ -1610,7 +1601,7 @@ internal class AstChecker(private val program: Program,
}
private fun checkPointer(pointervar: IdentifierReference) {
val vardecl = pointervar.targetVarDecl(program)
val vardecl = pointervar.targetVarDecl()
if(vardecl?.zeropage == ZeropageWish.NOT_IN_ZEROPAGE)
errors.info("pointer variable should preferrably be in zeropage but is marked nozp", vardecl.position)
}
@@ -1623,7 +1614,7 @@ internal class AstChecker(private val program: Program,
errors.err("indexing requires an iterable or address uword variable", arrayIndexedExpression.position)
val indexVariable = arrayIndexedExpression.indexer.indexExpr as? IdentifierReference
if(indexVariable!=null) {
if(indexVariable.targetVarDecl(program)?.datatype?.isSigned==true) {
if(indexVariable.targetVarDecl()?.datatype?.isSigned==true) {
errors.err("variable array indexing can't be performed with signed variables", indexVariable.position)
return
}
@@ -2015,25 +2006,22 @@ internal class AstChecker(private val program: Program,
return correct
}
private fun checkAssignmentCompatible(target: AssignTarget,
targetDatatype: DataType,
private fun checkAssignmentCompatible(targetDatatype: DataType,
sourceDatatype: DataType,
sourceValue: Expression) : Boolean {
val position = sourceValue.position
position: Position) : Boolean {
if (targetDatatype.isArray) {
if(sourceValue.inferType(program).isArray)
errors.err("cannot assign arrays directly. Maybe use sys.memcopy instead.", target.position)
if(sourceDatatype.isArray)
errors.err("cannot assign arrays directly. Maybe use sys.memcopy instead.", position)
else
errors.err("cannot assign value to array. Maybe use sys.memset/memsetw instead.", target.position)
errors.err("cannot assign value to array. Maybe use sys.memset/memsetw instead.", position)
return false
}
if (sourceValue is ArrayLiteral) {
errors.err("cannot assign array", target.position)
return false
}
if(sourceValue is RangeExpression) {
errors.err("can't assign a range value to something else", position)
if (sourceDatatype.isArray) {
if(targetDatatype.isUnsignedWord)
errors.err("invalid assignment value, maybe forgot '&' (address-of)", position)
else
errors.err("cannot assign array", position) // also includes range expressions
return false
}
if(sourceDatatype.isUndefined) {
@@ -2056,38 +2044,17 @@ internal class AstChecker(private val program: Program,
if(result)
return true
if(sourceDatatype.isWord && targetDatatype.isByte) {
errors.err("cannot assign word to byte, use msb() or lsb()?", position)
}
else if(sourceDatatype.isIterable && targetDatatype.isByte) {
errors.err("cannot assign string or array to a byte", position)
}
if(sourceDatatype.isWord && targetDatatype.isByte)
errors.err("cannot assign word to byte, maybe use msb() or lsb()", position)
else if(sourceDatatype.isFloat&& targetDatatype.isInteger)
errors.err("cannot assign float to ${targetDatatype}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position)
else if((sourceValue as? BinaryExpression)?.operator in BitwiseOperators && targetDatatype.equalsSize(sourceDatatype)) {
// this is allowed: bitwise operation between different types as long as they're the same size.
}
else if(targetDatatype.isUnsignedWord && sourceDatatype.isPassByRef) {
// this is allowed: a pass-by-reference datatype into an uword (pointer value).
}
else if(sourceDatatype.isArray && targetDatatype.isArray) {
// this is allowed (assigning array to array)
}
else if(sourceDatatype.isBool && !targetDatatype.isBool) {
else if(targetDatatype.isString && sourceDatatype.isUnsignedWord)
errors.err("can't assign uword to str. If the source is a string pointer and you actually want to overwrite the target string, use an explicit strings.copy(src,tgt) instead.", position)
else
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
}
else if(targetDatatype.isBool && !sourceDatatype.isBool) {
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
}
else if(targetDatatype.isString) {
if(sourceDatatype.isUnsignedWord)
errors.err("can't assign UWORD to STR. If the source is a string and you actually want to overwrite the target string, use an explicit strings.copy(src,tgt) instead.", position)
else
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
}
else {
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
}
return false
}
@@ -3,11 +3,7 @@ package prog8.compiler.astprocessing
import prog8.ast.IStatementContainer
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.expressions.BinaryExpression
import prog8.ast.expressions.CharLiteral
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteral
import prog8.ast.expressions.StringLiteral
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
@@ -185,8 +181,8 @@ internal fun Program.moveMainBlockAsFirst(target: ICompilationTarget) {
}
}
internal fun IdentifierReference.isSubroutineParameter(program: Program): Boolean {
val vardecl = this.targetVarDecl(program)
internal fun IdentifierReference.isSubroutineParameter(): Boolean {
val vardecl = this.targetVarDecl()
if(vardecl!=null && vardecl.origin==VarDeclOrigin.SUBROUTINEPARAM) {
return vardecl.definingSubroutine?.parameters?.any { it.name==vardecl.name } == true
}
@@ -1,12 +1,7 @@
package prog8.compiler.astprocessing
import prog8.ast.IFunctionCall
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.FatalAstException
import prog8.ast.SyntaxError
import prog8.ast.*
import prog8.ast.expressions.*
import prog8.ast.findParentNode
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
@@ -181,7 +176,7 @@ class AstPreprocessor(val program: Program,
val nextAssignment = decl.nextSibling() as? Assignment
if(nextAssignment!=null && nextAssignment.origin!=AssignmentOrigin.VARINIT) {
// check if the following assignment initializes the variable
if(decl.value==null && nextAssignment.target.identifier?.targetVarDecl(program)===decl) {
if(decl.value==null && nextAssignment.target.identifier?.targetVarDecl()===decl) {
if(!nextAssignment.value.referencesIdentifier(nextAssignment.target.identifier!!.nameInSource))
nextAssignment.origin = AssignmentOrigin.VARINIT
}
@@ -36,7 +36,7 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
if(typecast.type == BaseDataType.UWORD) {
val identifier = typecast.expression as? IdentifierReference
if(identifier!=null) {
return if(identifier.isSubroutineParameter(program)) {
return if(identifier.isSubroutineParameter()) {
listOf(
IAstModification.ReplaceNode(
typecast,
@@ -205,7 +205,7 @@ _after:
// replace pointervar[word] by @(pointervar+word) to avoid the
// "array indexing is limited to byte size 0..255" error for pointervariables.
val indexExpr = arrayIndexedExpression.indexer.indexExpr
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl()
if(arrayVar!=null && arrayVar.datatype.isUnsignedWord) {
val wordIndex = TypecastExpression(indexExpr, BaseDataType.UWORD, true, indexExpr.position)
val address = BinaryExpression(arrayIndexedExpression.arrayvar.copy(), "+", wordIndex, arrayIndexedExpression.position)
@@ -279,7 +279,7 @@ _after:
val addressOf = expr.left as? AddressOf
val offset = (expr.right as? NumericLiteral)?.number?.toInt()
if(addressOf!=null && offset==1) {
val variable = addressOf.identifier.targetVarDecl(program)
val variable = addressOf.identifier.targetVarDecl()
if(variable!=null && variable.datatype.isWord) {
val msb = FunctionCallExpression(IdentifierReference(listOf("msb"), memread.position), mutableListOf(addressOf.identifier), memread.position)
return listOf(IAstModification.ReplaceNode(memread, msb, parent))
@@ -9,7 +9,7 @@ import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.code.ast.PtContainmentCheck
import prog8.code.core.*
import prog8.code.core.IErrorReporter
internal class LiteralsToAutoVars(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
@@ -83,7 +83,7 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
if(decl.names.size>1) {
val fcallTarget = (decl.value as? IFunctionCall)?.target?.targetSubroutine(program)
val fcallTarget = (decl.value as? IFunctionCall)?.target?.targetSubroutine()
if(fcallTarget!=null) {
// ubyte a,b,c = multi() --> ubyte a,b,c / a,b,c = multi()
val modifications = mutableListOf<IAstModification>()
@@ -164,6 +164,9 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
}
}
if(srcAssign.origin == AssignmentOrigin.VARINIT && srcAssign.parent is Block && srcAssign.value.constValue(program)?.number==0.0)
throw FatalAstException("should not have a redundant block-level variable=0 assignment; it will be zeroed as part of BSS clear")
val assign = PtAssignment(srcAssign.position, srcAssign.origin==AssignmentOrigin.VARINIT)
val multi = srcAssign.target.multi
if(multi==null) {
@@ -417,7 +420,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
if(binexpr.operator=="==" || binexpr.operator=="!=") {
val fcall = binexpr.left as? FunctionCallExpression
if(fcall!=null) {
val returnRegs = fcall.target.targetSubroutine(program)?.asmReturnvaluesRegisters
val returnRegs = fcall.target.targetSubroutine()?.asmReturnvaluesRegisters
if(returnRegs!=null && returnRegs.size==1 && returnRegs[0].statusflag!=null) {
return codeForStatusflag(fcall, returnRegs[0].statusflag!!, binexpr.operator == "==")
}
@@ -426,7 +429,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
} else {
val fcall = srcIf.condition as? FunctionCallExpression
if (fcall != null) {
val returnRegs = fcall.target.targetSubroutine(program)?.asmReturnvaluesRegisters
val returnRegs = fcall.target.targetSubroutine()?.asmReturnvaluesRegisters
if(returnRegs!=null && returnRegs.size==1 && returnRegs[0].statusflag!=null) {
return codeForStatusflag(fcall, returnRegs[0].statusflag!!, false)
}
@@ -436,7 +439,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
if(prefix!=null && prefix.operator=="not") {
val prefixedFcall = prefix.expression as? FunctionCallExpression
if (prefixedFcall != null) {
val returnRegs = prefixedFcall.target.targetSubroutine(program)?.asmReturnvaluesRegisters
val returnRegs = prefixedFcall.target.targetSubroutine()?.asmReturnvaluesRegisters
if (returnRegs != null && returnRegs.size == 1 && returnRegs[0].statusflag != null) {
return codeForStatusflag(prefixedFcall, returnRegs[0].statusflag!!, true)
}
@@ -619,7 +622,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
}
private fun transform(srcArr: ArrayIndexedExpression): PtArrayIndexer {
val dt = srcArr.arrayvar.targetVarDecl(program)!!.datatype
val dt = srcArr.arrayvar.targetVarDecl()!!.datatype
if(!dt.isArray && !dt.isString)
throw FatalAstException("array indexing can only be used on array or string variables ${srcArr.position}")
val eltType = srcArr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
@@ -52,7 +52,8 @@ internal class StatementReorderer(
if (decl.datatype.isNumericOrBool) {
if(decl !in declsProcessedWithInitAssignment) {
declsProcessedWithInitAssignment.add(decl)
if (decl.value == null) {
if (decl.value == null || decl.value?.constValue(program)?.number==0.0) {
decl.value = null
if (decl.origin==VarDeclOrigin.USERCODE && decl.allowInitializeWithZero) {
if(decl.dirty) {
// no initialization at all!
@@ -62,11 +63,9 @@ internal class StatementReorderer(
// 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
val canskip = canSkipInitializationWith0(decl)
if (!canskip) {
// (this explicit initialization assignment to 0 is not required for global variables in the block scope, these are zeroed as part of the BSS section clear)
if (!canSkipInitializationWith0(decl)) {
// 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, null, false, decl.position),
decl.zeroElementValue(), AssignmentOrigin.VARINIT, decl.position)
@@ -96,7 +95,7 @@ internal class StatementReorderer(
// (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)
val target = ident.targetVarDecl()
if(target!=null && target.isArray) {
val pos = decl.value!!.position
val identifier = IdentifierReference(listOf(decl.name), pos)
@@ -115,6 +114,11 @@ internal class StatementReorderer(
}
private fun canSkipInitializationWith0(decl: VarDecl): Boolean {
// if the variable is declared in a block, we can omit the init with 0 because
// the variable will be initialized to zero when the BSS section is cleared as a whole.
if(decl.parent is Block)
return true
// if there is an assignment to the variable below it (regular assign, or For loop),
// and there is nothing important in between, we can skip the initialization.
val statements = (decl.parent as? IStatementContainer)?.statements ?: return false
@@ -123,14 +127,14 @@ internal class StatementReorderer(
when(stmt) {
is Assignment -> {
if (!stmt.isAugmentable) {
val assignTargets = stmt.target.multi?.mapNotNull { it.identifier?.targetVarDecl(program) }
val assignTargets = stmt.target.multi?.mapNotNull { it.identifier?.targetVarDecl() }
if(assignTargets!=null) {
if(decl in assignTargets) {
stmt.origin = AssignmentOrigin.VARINIT
return true
}
} else {
val assignTgt = stmt.target.identifier?.targetVarDecl(program)
val assignTgt = stmt.target.identifier?.targetVarDecl()
if (assignTgt == decl) {
stmt.origin = AssignmentOrigin.VARINIT
return true
@@ -143,11 +147,11 @@ internal class StatementReorderer(
is ChainedAssignment -> {
var chained: ChainedAssignment? = stmt
while(chained!=null) {
val assignTgt = chained.target.identifier?.targetVarDecl(program)
val assignTgt = chained.target.identifier?.targetVarDecl()
if (assignTgt == decl)
return true
if(chained.nested is Assignment) {
if ((chained.nested as Assignment).target.identifier?.targetVarDecl(program) == decl) {
if ((chained.nested as Assignment).target.identifier?.targetVarDecl() == decl) {
(chained.nested as Assignment).origin = AssignmentOrigin.VARINIT
return true
}
@@ -325,7 +329,7 @@ internal class StatementReorderer(
private fun checkCopyArrayValue(assign: Assignment) {
val identifier = assign.target.identifier!!
val targetVar = identifier.targetVarDecl(program)!!
val targetVar = identifier.targetVarDecl()!!
if(targetVar.arraysize==null) {
errors.err("array has no defined size", assign.position)
@@ -340,7 +344,7 @@ internal class StatementReorderer(
}
val sourceIdent = assign.value as IdentifierReference
val sourceVar = sourceIdent.targetVarDecl(program)!!
val sourceVar = sourceIdent.targetVarDecl()!!
if(!sourceVar.isArray) {
errors.err("value must be an array", sourceIdent.position)
} else {
@@ -185,6 +185,16 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
}
}
}
// check if shifts have a positive integer shift type
if(expr.operator=="<<" || expr.operator==">>") {
if(rightDt.isInteger) {
val rconst = expr.right.constValue(program)
if(rconst!=null && rconst.number<0)
errors.err("can only shift by a positive amount", expr.right.position)
} else
errors.err("right operand of bit shift must be an integer", expr.right.position)
}
}
return noModifications
@@ -251,7 +261,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
if(number!=null) {
addTypecastOrCastedValueModification(modifications, it.second, targetDt, call as Node)
} else if(identifier!=null && targetDt==BaseDataType.UWORD && argDt.isPassByRef) {
if(!identifier.isSubroutineParameter(program)) {
if(!identifier.isSubroutineParameter()) {
// We allow STR/ARRAY values for UWORD parameters.
// If it's an array (not STR), take the address.
if(!argDt.isString) {
@@ -84,12 +84,12 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
changeSplit = SplitWish.NOSPLIT
}
else {
changeDataType = if(decl.datatype.isSplitWordArray) null else DataType.arrayFor(decl.datatype.elementType().base, true)
changeDataType = if(decl.datatype.isSplitWordArray) null else DataType.arrayFor(decl.datatype.elementType().base)
changeSplit = SplitWish.SPLIT
}
}
SplitWish.SPLIT -> {
changeDataType = if(decl.datatype.isSplitWordArray) null else DataType.arrayFor(decl.datatype.elementType().base, true)
changeDataType = if(decl.datatype.isSplitWordArray) null else DataType.arrayFor(decl.datatype.elementType().base)
}
SplitWish.NOSPLIT -> {
changeDataType = if(decl.datatype.isSplitWordArray) DataType.arrayFor(decl.datatype.elementType().base, false) else null
@@ -285,7 +285,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
// replace x==1 or x==2 or x==3 with a containment check x in [1,2,3]
val valueCopies = values.sortedBy { it.number }.map { it.copy() }
val arrayType = DataType.arrayFor(elementType.base, true)
val arrayType = DataType.arrayFor(elementType.base)
val valuesArray = ArrayLiteral(InferredTypes.InferredType.known(arrayType), valueCopies.toTypedArray(), expr.position)
val containment = ContainmentCheck(needle, valuesArray, expr.position)
return listOf(IAstModification.ReplaceNode(expr, containment, parent))
@@ -394,7 +394,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
val index = arrayIndexedExpression.indexer.constIndex()
if(index!=null && index<0) {
val target = arrayIndexedExpression.arrayvar.targetVarDecl(program)
val target = arrayIndexedExpression.arrayvar.targetVarDecl()
val arraysize = target?.arraysize?.constIndex()
if(arraysize!=null) {
if(arraysize+index < 0) {
@@ -5,8 +5,8 @@ import prog8.ast.Program
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.walk.IAstVisitor
import prog8.code.core.*
import prog8.code.INTERNED_STRINGS_MODULENAME
import prog8.code.core.*
internal class VerifyFunctionArgTypes(val program: Program, val options: CompilationOptions, val errors: IErrorReporter) : IAstVisitor {
@@ -31,7 +31,7 @@ internal class VerifyFunctionArgTypes(val program: Program, val options: Compila
}
override fun visit(identifier: IdentifierReference) {
if(identifier.wasStringLiteral(program)) {
if(identifier.wasStringLiteral()) {
allStringRefs.add(identifier.nameInSource)
}
}
+5 -6
View File
@@ -8,7 +8,6 @@ import io.kotest.matchers.shouldBe
import io.kotest.matchers.types.instanceOf
import prog8.ast.IFunctionCall
import prog8.ast.IStatementContainer
import prog8.ast.Program
import prog8.ast.expressions.IdentifierReference
import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.Assignment
@@ -31,11 +30,11 @@ class TestCompilerOnCharLit: FunSpec({
val outputDir = tempdir().toPath()
fun findInitializer(vardecl: VarDecl, program: Program): Assignment? =
fun findInitializer(vardecl: VarDecl): Assignment? =
(vardecl.parent as IStatementContainer).statements
.asSequence()
.filterIsInstance<Assignment>()
.singleOrNull { it.origin== AssignmentOrigin.VARINIT && it.target.identifier?.targetVarDecl(program) === vardecl }
.singleOrNull { it.origin== AssignmentOrigin.VARINIT && it.target.identifier?.targetVarDecl() === vardecl }
test("testCharLitAsExtsubArg") {
@@ -79,14 +78,14 @@ class TestCompilerOnCharLit: FunSpec({
funCall.args[0] shouldBe instanceOf<IdentifierReference>()
val arg = funCall.args[0] as IdentifierReference
val decl = arg.targetVarDecl(program)!!
val decl = arg.targetVarDecl()!!
decl.type shouldBe VarDeclType.VAR
decl.datatype shouldBe DataType.UBYTE
withClue("initializer value should have been moved to separate assignment"){
decl.value shouldBe null
}
val assignInitialValue = findInitializer(decl, program)!!
val assignInitialValue = findInitializer(decl)!!
assignInitialValue.target.identifier!!.nameInSource shouldBe listOf("ch")
withClue("char literal should have been replaced by ubyte literal") {
assignInitialValue.value shouldBe instanceOf<NumericLiteral>()
@@ -115,7 +114,7 @@ class TestCompilerOnCharLit: FunSpec({
// Now, both is ok for the arg: a) still the IdRef or b) replaced by numeric literal
when (val arg = funCall.args[0]) {
is IdentifierReference -> {
val decl = arg.targetVarDecl(program)!!
val decl = arg.targetVarDecl()!!
decl.type shouldBe VarDeclType.CONST
decl.datatype shouldBe DataType.UBYTE
(decl.value as NumericLiteral).number shouldBe platform.encodeString("\n", Encoding.PETSCII)[0]
@@ -34,7 +34,7 @@ class TestCompilerOnImportsAndIncludes: FunSpec({
val strLits = startSub.statements
.filterIsInstance<FunctionCallStatement>()
.map { it.args[0] as IdentifierReference }
.map { it.targetVarDecl(program)!!.value as StringLiteral }
.map { it.targetVarDecl()!!.value as StringLiteral }
strLits[0].value shouldBe "main.bar"
strLits[1].value shouldBe "foo.bar"
@@ -57,7 +57,7 @@ class TestCompilerOnImportsAndIncludes: FunSpec({
.filterIsInstance<FunctionCallStatement>()
.map { it.args[0] }
val str0 = (args[0] as IdentifierReference).targetVarDecl(program)!!.value as StringLiteral
val str0 = (args[0] as IdentifierReference).targetVarDecl()!!.value as StringLiteral
str0.value shouldBe "main.bar"
str0.definingScope.name shouldBe "main"
+62
View File
@@ -406,4 +406,66 @@ main {
errors.errors[0] shouldContain(":4:37: no cast available")
errors.errors[1] shouldContain(":5:37: no cast available")
}
test("const evaluation of signed bitwise operations") {
val src="""
%import textio
main {
sub start() {
byte @shared a = -1
byte @shared b = -15
ubyte @shared ub = 2
const byte ca = -1
const byte cb = -15
const ubyte cub = 2
txt.print_ub( a & b )
txt.spc()
txt.print_ub( a | b )
txt.spc()
txt.print_ub( a ^ b )
txt.spc()
txt.print_b( a << ub )
txt.spc()
txt.print_b( a >> ub )
txt.nl()
txt.print_ub( ca & cb )
txt.spc()
txt.print_ub( ca | cb )
txt.spc()
txt.print_ub( ca ^ cb )
txt.spc()
txt.print_b( ca << cub )
txt.spc()
txt.print_b( ca >> cub )
txt.nl()
word @shared aw = -1
word @shared bw = -15
uword @shared uw = 2
const word caw = -1
const word cbw = -15
const uword cuw = 2
txt.print_uw( aw & bw )
txt.spc()
txt.print_uw( aw | bw )
txt.spc()
txt.print_uw( aw ^ bw )
txt.nl()
txt.print_uw( caw & cbw )
txt.spc()
txt.print_uw( caw | cbw )
txt.spc()
txt.print_uw( caw ^ cbw )
txt.nl()
txt.print_w( cbw << cuw )
txt.spc()
txt.print_w( cbw >> cuw )
txt.nl()
}
}"""
compileText(C64Target(), false, src, outputDir, writeAssembly = false) shouldNotBe null
}
})
+4 -4
View File
@@ -62,12 +62,12 @@ class TestIdentifierRef: FunSpec({
val wwref = (stmts[0] as Assignment).target.identifier!!
val mainref = ((stmts[1] as Assignment).value as AddressOf).identifier
wwref.nameInSource shouldBe listOf("ww")
wwref.wasStringLiteral(program) shouldBe false
wwref.wasStringLiteral() shouldBe false
wwref.targetStatement(program) shouldBe instanceOf<VarDecl>()
wwref.targetVarDecl(program)!!.name shouldBe "ww"
wwref.targetVarDecl(program)!!.parent shouldBe instanceOf<Block>()
wwref.targetVarDecl()!!.name shouldBe "ww"
wwref.targetVarDecl()!!.parent shouldBe instanceOf<Block>()
mainref.nameInSource shouldBe listOf("main")
mainref.wasStringLiteral(program) shouldBe false
mainref.wasStringLiteral() shouldBe false
mainref.targetStatement(program) shouldBe instanceOf<Block>()
}
})
+2 -2
View File
@@ -225,8 +225,8 @@ main {
val rept2strcopy = stmts[4] as IFunctionCall
val name2 = name2strcopy.args.first() as IdentifierReference
val rept2 = rept2strcopy.args.first() as IdentifierReference
(name2.targetVarDecl(result.compilerAst)!!.value as StringLiteral).value shouldBe "xx1xx2"
(rept2.targetVarDecl(result.compilerAst)!!.value as StringLiteral).value shouldBe "xyzxyzxyzxyz"
(name2.targetVarDecl()!!.value as StringLiteral).value shouldBe "xx1xx2"
(rept2.targetVarDecl()!!.value as StringLiteral).value shouldBe "xyzxyzxyzxyz"
}
test("char as str param is error") {
@@ -403,45 +403,6 @@ main {
cx16.r2 = &addresses
}
}
}"""
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
compileText(C64Target(), optimize=false, src, outputDir, writeAssembly=true, errors=errors) shouldNotBe null
errors.errors.size shouldBe 0
errors.warnings.size shouldBe 0
}
test("assigning address of split arrays without explicitly taking address works") {
val src="""
main {
sub start() {
uword[] texts1 = [ 1,2,3 ]
uword[] @nosplit texts2 = [ 1,2,3 ]
cx16.r4 = texts1
cx16.r5 = texts2
; txt.print_uwhex(&texts1, true)
; txt.spc()
; txt.print_uwhex(cx16.r4, true)
; txt.nl()
; txt.print_uwhex(&texts2, true)
; txt.spc()
; txt.print_uwhex(cx16.r5, true)
; txt.nl()
cx16.r4 = &texts1
cx16.r5 = &texts2
; txt.print_uwhex(&texts1, true)
; txt.spc()
; txt.print_uwhex(cx16.r4, true)
; txt.nl()
; txt.print_uwhex(&texts2, true)
; txt.spc()
; txt.print_uwhex(cx16.r5, true)
; txt.nl()
}
}"""
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
compileText(C64Target(), optimize=false, src, outputDir, writeAssembly=true, errors=errors) shouldNotBe null
+11 -16
View File
@@ -125,29 +125,24 @@ main {
val result = compileText(C64Target(), true, src, outputDir, writeAssembly = true)!!.compilerAst
val main = result.allBlocks.first { it.name=="main" }
main.statements.size shouldBe 17
main.statements.size shouldBe 15
val assigns = main.statements.filterIsInstance<Assignment>()
assigns[0].target.identifier?.nameInSource shouldBe listOf("value1")
assigns[1].target.identifier?.nameInSource shouldBe listOf("value2")
assigns[2].target.identifier?.nameInSource shouldBe listOf("value3")
assigns[3].target.identifier?.nameInSource shouldBe listOf("value4")
assigns[4].target.identifier?.nameInSource shouldBe listOf("value5")
assigns[5].target.identifier?.nameInSource shouldBe listOf("value6")
assigns[6].target.identifier?.nameInSource shouldBe listOf("value7")
assigns.size shouldBe 5
assigns[0].target.identifier?.nameInSource shouldBe listOf("value2")
assigns[1].target.identifier?.nameInSource shouldBe listOf("value3")
assigns[2].target.identifier?.nameInSource shouldBe listOf("value5")
assigns[3].target.identifier?.nameInSource shouldBe listOf("value6")
assigns[4].target.identifier?.nameInSource shouldBe listOf("value7")
assigns[0].origin shouldBe AssignmentOrigin.VARINIT
assigns[1].origin shouldBe AssignmentOrigin.VARINIT
assigns[2].origin shouldBe AssignmentOrigin.VARINIT
assigns[3].origin shouldBe AssignmentOrigin.VARINIT
assigns[4].origin shouldBe AssignmentOrigin.VARINIT
assigns[5].origin shouldBe AssignmentOrigin.VARINIT
assigns[6].origin shouldBe AssignmentOrigin.VARINIT
assigns[0].value.constValue(result)?.number shouldBe 0
assigns[1].value.constValue(result)?.number shouldBe 1
assigns[2].value.constValue(result)?.number shouldBe 1
assigns[3].value.constValue(result)?.number shouldBe 0
assigns[0].value.constValue(result)?.asBooleanValue shouldBe true
assigns[1].value.constValue(result)?.asBooleanValue shouldBe true
assigns[2].value.constValue(result) shouldBe null
assigns[3].value.constValue(result)?.number shouldBe 4242
assigns[4].value.constValue(result) shouldBe null
assigns[5].value.constValue(result)?.number shouldBe 4242
assigns[6].value.constValue(result) shouldBe null
}
@@ -33,7 +33,7 @@ internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors:
override fun noErrors(): Boolean = errors.isEmpty()
override fun noErrorForLine(position: Position) = !errors.any { ":${position.line}:" in it }
override fun print_single_error(errormessage: String) { /* prints nothing in tests */ }
override fun printSingleError(errormessage: String) { /* prints nothing in tests */ }
override fun report() {
infos.forEach { println("UNITTEST COMPILATION REPORT: INFO: $it") }
+1 -1
View File
@@ -126,7 +126,7 @@ class Program(val name: String,
private class StringSearch(val program: Program): IAstVisitor {
val removals = mutableListOf<List<String>>()
override fun visit(identifier: IdentifierReference) {
if(identifier.wasStringLiteral(program))
if(identifier.wasStringLiteral())
removals.add(identifier.nameInSource)
}
@@ -101,7 +101,7 @@ private fun StatementContext.toAst() : Statement {
val pos = it.toPosition()
// print("\u001b[92mINFO\u001B[0m ") // bright green
// println("${pos}: ++ and -- will be removed in a future version, please use +=1 or -=1 instead.") // .... if we decode to remove them one day
val addSubOne = BinaryExpression(tgt.toExpression(), if(operator=="++") "+" else "-", NumericLiteral.optimalInteger(1, pos), pos, false)
val addSubOne = BinaryExpression(tgt.toExpression(), if(operator=="++") "+" else "-", NumericLiteral.optimalInteger(1, pos), pos)
return Assignment(tgt, addSubOne, AssignmentOrigin.USERCODE, pos)
}
@@ -174,7 +174,8 @@ private fun AsmsubroutineContext.toAst(): Subroutine {
val statements = statement_block()?.toAst() ?: mutableListOf()
return Subroutine(subdecl.name, subdecl.parameters.toMutableList(), subdecl.returntypes.toMutableList(),
subdecl.asmParameterRegisters, subdecl.asmReturnvaluesRegisters,
subdecl.asmClobbers, null, true, inline, false, statements, toPosition())
subdecl.asmClobbers, null, true, inline, statements = statements, position = toPosition()
)
}
private fun ExtsubroutineContext.toAst(): Subroutine {
@@ -1164,14 +1164,14 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
override val isSimple = true
fun targetStatement(program: Program) =
if(nameInSource.singleOrNull() in program.builtinFunctions.names)
fun targetStatement(program: Program?) =
if(program!=null && nameInSource.singleOrNull() in program.builtinFunctions.names)
BuiltinFunctionPlaceholder(nameInSource[0], position, parent)
else
definingScope.lookup(nameInSource)
fun targetVarDecl(program: Program): VarDecl? = targetStatement(program) as? VarDecl
fun targetSubroutine(program: Program): Subroutine? = targetStatement(program) as? Subroutine
fun targetVarDecl(): VarDecl? = targetStatement(null) as? VarDecl
fun targetSubroutine(): Subroutine? = targetStatement(null) as? Subroutine
fun targetNameAndType(program: Program): Pair<String, DataType> {
val target = targetStatement(program) as? INamedStatement ?: throw FatalAstException("can't find target for $nameInSource")
@@ -1241,8 +1241,8 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
}
}
fun wasStringLiteral(program: Program): Boolean {
val decl = targetVarDecl(program)
fun wasStringLiteral(): Boolean {
val decl = targetVarDecl()
if(decl == null || decl.origin!=VarDeclOrigin.STRINGLITERAL)
return false
@@ -84,9 +84,9 @@ object InferredTypes {
type.isLong -> InferredType.known(BaseDataType.LONG)
type.isSplitWordArray -> {
when(type.sub) {
BaseDataType.UWORD -> InferredType.known(DataType.arrayFor(BaseDataType.UWORD, true))
BaseDataType.WORD -> InferredType.known(DataType.arrayFor(BaseDataType.WORD, true))
BaseDataType.STR -> InferredType.known(DataType.arrayFor(BaseDataType.STR, true))
BaseDataType.UWORD -> InferredType.known(DataType.arrayFor(BaseDataType.UWORD))
BaseDataType.WORD -> InferredType.known(DataType.arrayFor(BaseDataType.WORD))
BaseDataType.STR -> InferredType.known(DataType.arrayFor(BaseDataType.STR))
else -> throw IllegalArgumentException("invalid sub type")
}
}
@@ -647,7 +647,7 @@ data class AssignTarget(var identifier: IdentifierReference?,
return target.isIOAddress(addr.number.toUInt())
return when (memAddr.addressExpression) {
is IdentifierReference -> {
val decl = (memAddr.addressExpression as IdentifierReference).targetVarDecl(definingModule.program)
val decl = (memAddr.addressExpression as IdentifierReference).targetVarDecl()
val result = if ((decl?.type == VarDeclType.MEMORY || decl?.type == VarDeclType.CONST) && decl.value is NumericLiteral)
target.isIOAddress((decl.value as NumericLiteral).number.toUInt())
else
@@ -658,7 +658,7 @@ data class AssignTarget(var identifier: IdentifierReference?,
}
}
arrayIdx != null -> {
val targetStmt = arrayIdx.arrayvar.targetVarDecl(definingModule.program)
val targetStmt = arrayIdx.arrayvar.targetVarDecl()
return if (targetStmt?.type == VarDeclType.MEMORY) {
val addr = targetStmt.value as? NumericLiteral
if (addr != null)
@@ -668,7 +668,7 @@ data class AssignTarget(var identifier: IdentifierReference?,
} else false
}
ident != null -> {
val decl = ident.targetVarDecl(definingModule.program) ?: throw FatalAstException("invalid identifier ${ident.nameInSource}")
val decl = ident.targetVarDecl() ?: throw FatalAstException("invalid identifier ${ident.nameInSource}")
return if (decl.type == VarDeclType.MEMORY && decl.value is NumericLiteral)
target.isIOAddress((decl.value as NumericLiteral).number.toUInt())
else
+5 -7
View File
@@ -78,7 +78,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
}
override fun visit(functionCallExpr: FunctionCallExpression) {
val otherSub = functionCallExpr.target.targetSubroutine(program)
val otherSub = functionCallExpr.target.targetSubroutine()
if (otherSub != null) {
val definingSub = functionCallExpr.definingSubroutine
if(definingSub!=null) {
@@ -90,7 +90,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
}
override fun visit(functionCallStatement: FunctionCallStatement) {
val otherSub = functionCallStatement.target.targetSubroutine(program)
val otherSub = functionCallStatement.target.targetSubroutine()
if (otherSub != null) {
functionCallStatement.definingSubroutine?.let { thisSub ->
calls[thisSub] = calls.getValue(thisSub) + otherSub
@@ -101,12 +101,12 @@ class CallGraph(private val program: Program) : IAstVisitor {
}
override fun visit(addressOf: AddressOf) {
addressOf.identifier.targetSubroutine(program)?.let { notCalledButReferenced.add(it) }
addressOf.identifier.targetSubroutine()?.let { notCalledButReferenced.add(it) }
super.visit(addressOf)
}
override fun visit(jump: Jump) {
val otherSub = (jump.target as? IdentifierReference)?.targetSubroutine(program)
val otherSub = (jump.target as? IdentifierReference)?.targetSubroutine()
if (otherSub != null) {
jump.definingSubroutine?.let { thisSub ->
calls[thisSub] = calls.getValue(thisSub) + otherSub
@@ -126,7 +126,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
// if it's a scoped identifier, the subroutines in the name are also referenced!
val scope = identifier.definingScope
val name = identifier.nameInSource.toMutableList()
val name = ArrayDeque(identifier.nameInSource)
while(name.size>1) {
name.removeLast()
val scopeTarget = scope.lookup(name)
@@ -225,8 +225,6 @@ class CallGraph(private val program: Program) : IAstVisitor {
}
}
inline fun unused(label: Label) = false // just always output labels
fun unused(stmt: INamedStatement): Boolean {
return when(stmt) {
is Subroutine -> unused(stmt)
@@ -1,5 +1,5 @@
Prog8 compiler v11.3 by Irmen de Jong (irmen@razorvine.net)
Prog8 compiler v11.3.1 by Irmen de Jong (irmen@razorvine.net)
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
Compiling program import-all-c128.p8
@@ -238,7 +238,7 @@ strings {
endswith (str st, str suffix) -> bool
find (uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc
findstr (str haystack, str needle) -> ubyte
hash (str string @R0) -> ubyte @A
hash (str string @AY) -> ubyte @A
isdigit (ubyte petsciichar @A) -> bool @Pc
isletter (ubyte petsciichar @A) -> bool @Pc
islower (ubyte petsciichar @A) -> bool @Pc
@@ -1,5 +1,5 @@
Prog8 compiler v11.3 by Irmen de Jong (irmen@razorvine.net)
Prog8 compiler v11.3.1 by Irmen de Jong (irmen@razorvine.net)
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
Compiling program import-all-c64.p8
@@ -365,7 +365,7 @@ strings {
endswith (str st, str suffix) -> bool
find (uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc
findstr (str haystack, str needle) -> ubyte
hash (str string @R0) -> ubyte @A
hash (str string @AY) -> ubyte @A
isdigit (ubyte petsciichar @A) -> bool @Pc
isletter (ubyte petsciichar @A) -> bool @Pc
islower (ubyte petsciichar @A) -> bool @Pc
@@ -1,5 +1,5 @@
Prog8 compiler v11.3 by Irmen de Jong (irmen@razorvine.net)
Prog8 compiler v11.3.1 by Irmen de Jong (irmen@razorvine.net)
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
Compiling program import-all-cx16.p8
@@ -673,7 +673,7 @@ strings {
endswith (str st, str suffix) -> bool
find (uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc
findstr (str haystack, str needle) -> ubyte
hash (str string @R0) -> ubyte @A
hash (str string @AY) -> ubyte @A
isdigit (ubyte petsciichar @A) -> bool @Pc
isletter (ubyte petsciichar @A) -> bool @Pc
islower (ubyte petsciichar @A) -> bool @Pc
@@ -1,5 +1,5 @@
Prog8 compiler v11.3 by Irmen de Jong (irmen@razorvine.net)
Prog8 compiler v11.3.1 by Irmen de Jong (irmen@razorvine.net)
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
Compiling program import-all-pet32.p8
@@ -189,7 +189,7 @@ strings {
endswith (str st, str suffix) -> bool
find (uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc
findstr (str haystack, str needle) -> ubyte
hash (str string @R0) -> ubyte @A
hash (str string @AY) -> ubyte @A
isdigit (ubyte petsciichar @A) -> bool @Pc
isletter (ubyte petsciichar @A) -> bool @Pc
islower (ubyte petsciichar @A) -> bool @Pc
@@ -1,5 +1,5 @@
Prog8 compiler v11.3 by Irmen de Jong (irmen@razorvine.net)
Prog8 compiler v11.3.1 by Irmen de Jong (irmen@razorvine.net)
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
Compiling program import-all-virtual.p8
+5 -4
View File
@@ -145,8 +145,7 @@ One or more .p8 module files
a breakpoint halt in the debugger when this is enabled by running the emulator with -debug.
``-bytes2float <bytes>``
convert a comma separated list of bytes from the target system to a float value.
NOTE: you need to supply a target option too, and also still have to supply a dummy module file name as well!
convert a comma separated list of bytes from the specified target system to a float value.
Also see -float2bytes
``-check``
@@ -176,8 +175,7 @@ One or more .p8 module files
Use experimental code generation backend (*incomplete*).
``-float2bytes <number>``
convert floating point number to a list of bytes for the target system.
NOTE: you need to supply a target option too, and also still have to supply a dummy module file name as well!
convert floating point number to a list of bytes for the specified target system.
Also see -bytes2float
``-ignorefootguns``
@@ -259,6 +257,9 @@ One or more .p8 module files
(it's called 'BSS' section or Gap at the address mentioned above).
Assembling the program will fail if there are too many variables to fit in a single high ram bank.
``-version``
Just print the compiler version and copyright message, and exit.
``-vm``
load and run a 'p8ir' intermediate representation file in the internal VirtualMachine instead of compiling a prog8 program file.
+1
View File
@@ -190,6 +190,7 @@ memory (name, size, alignment)
with the given name. The name must be a string literal, it cannot be empty or be a variable.
The block is *uninitialized memory*; unlike other variables in Prog8 it is *not* set to zero at the start of the program!
(if that is required, you can do so yourself using ``memset``).
No *dynamic* allocation is done; the block with this name is placed in memory only once!
If you specify an alignment value >1, it means the block of memory will
be aligned to such a dividable address in memory, for instance an alignment of $100 means the
memory block is aligned on a page boundary, and $2 means word aligned (even addresses).
+2
View File
@@ -923,6 +923,8 @@ arithmetic: ``+`` ``-`` ``*`` ``/`` ``%``
bitwise arithmetic: ``&`` ``|`` ``^`` ``~`` ``<<`` ``>>``
``&`` is bitwise and, ``|`` is bitwise or, ``^`` is bitwise xor, ``~`` is bitwise invert (this one is an unary operator)
``<<`` is bitwise left shift and ``>>`` is bitwise right shift (both will not change the datatype of the value)
While the operands can be signed integers (the expression will just consider the underlying bit patterns),
the result value of a bitwise expression is always unsigned.
assignment: ``=``
Sets the target on the LHS (left hand side) of the operator to the value of the expression on the RHS (right hand side).
+3
View File
@@ -237,6 +237,9 @@ Some notes and references into the compiler's source code modules:
is more or less a machine code language for a virtual machine - and indeed this is what the built-in
prog8 VM will execute if you use the 'virtual' compilation target and use ``-emu`` to launch the VM.
(``intermediate`` and ``codeGenIntermediate`` modules, and ``virtualmachine`` module for the VM related stuff)
Note that this IR is still *targeted to one specific compilation target only*; various properties and all library
code for the selected target machine is encoded into the IR. It is *not possible* to eventually create a C64 program
from an IR file crated for the CommanderX16 target.
#. The code generator backends all implement a common interface ``ICodeGeneratorBackend`` defined in the ``codeCore`` module.
Currently they get handed the program Ast, Symboltable and several other things.
If the code generator wants it can use the ``IRCodeGen`` class from the ``codeGenIntermediate`` module
+10 -17
View File
@@ -1,17 +1,20 @@
TODO
====
...
Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^
- STRUCTS: are now being developed in their own separate branch "structs". This will be for the next major version of the compiler (v12)
- is "checkAssignmentCompatible" redundant (gets called just 1 time!) when we also have "checkValueTypeAndRange" ?
- romable: should we have a way to explicitly set the memory address for the BSS area (instead of only the highram bank number on X16, allow a memory address too for the -varshigh option?)
- Kotlin: can we use inline value classes in certain spots?
- add float support to the configurable compiler targets
- Improve the SublimeText syntax file for prog8, you can also install this for 'bat': https://github.com/sharkdp/bat?tab=readme-ov-file#adding-new-syntaxes--language-definitions
- Change scoping rules for qualified symbols so that they don't always start from the root but behave like other programming languages (look in local scope first)
- Change scoping rules for qualified symbols so that they don't always start from the root but behave like other programming languages (look in local scope first), maybe only when qualified symbol starts with '.' such as: .local.value = 33
- something to reduce the need to use fully qualified names all the time. 'with' ? Or 'using <prefix>'?
- Improve register load order in subroutine call args assignments:
in certain situations (need examples!), the "wrong" order of evaluation of function call arguments is done which results
@@ -19,7 +22,7 @@ Future Things and Ideas
Maybe this routine can be made more intelligent. See usesOtherRegistersWhileEvaluating() and argumentsViaRegisters().
- Does it make codegen easier if everything is an expression? Start with the PtProgram ast classes, change statements to expressions that have (new) VOID data type
- Can we support signed % (remainder) somehow?
- Multidimensional arrays and chained indexing, purely as syntactic sugar over regular arrays. Probaby only useful if we have typed pointers.
- Multidimensional arrays and chained indexing, purely as syntactic sugar over regular arrays. Probaby only useful if we have typed pointers. (addressed in 'struct' branch)
- make a form of "manual generics" possible like: varsub routine(T arg)->T where T is expanded to a specific type
(this is already done hardcoded for several of the builtin functions)
- [much work:] more support for (64tass) SEGMENTS in the prog8 syntax itself?
@@ -30,7 +33,8 @@ Future Things and Ideas
IR/VM
-----
- getting it in shape for code generation...: the IR file should be able to encode every detail about a prog8 program (the VM doesn't have to actually be able to run all of it though!)
- fix call() return value handling
- fix call() return value handling (... what's wrong with it again?)
- encode asmsub/extsub clobber info in the call , or maybe include these definitions in the p8ir file itself too. (return registers are already encoded in the CALL instruction)
- proper code gen for the CALLI instruction and that it (optionally) returns a word value that needs to be assigned to a reg
- implement fast code paths for TODO("inplace split....
- implement more TODOs in AssignmentGen
@@ -40,6 +44,8 @@ IR/VM
cx16.r0sL = cx16.r1s as byte }
- do something with the 'split' tag on split word arrays
- add more optimizations in IRPeepholeOptimizer
- reduce register usage via linear-scan algorithm (based on live intervals) https://anoopsarkar.github.io/compilers-class/assets/lectures/opt3-regalloc-linearscan.pdf
don't forget to take into account the data type of the register when it's going to be reused!
- idea: (but LLVM IR simply keeps the variables, so not a good idea then?...): replace all scalar variables by an allocated register. Keep a table of the variable to register mapping (including the datatype)
global initialization values are simply a list of LOAD instructions.
Variables replaced include all subroutine parameters! So the only variables that remain as variables are arrays and strings.
@@ -51,6 +57,7 @@ IR/VM
Libraries
---------
- Add split-word array sorting routines to sorting module?
- cx16: _irq_dispatcher now only dispatches a single irq source, better to ROL/BCC to handle *all* possible (multiple) sources.
- See if the raster interrupt handler on the C64 can be tweaked to be a more stable raster irq
- pet32 target: make syslib more complete (missing kernal routines)?
- need help with: PET disk routines (OPEN, SETLFS etc are not exposed as kernal calls)
@@ -67,17 +74,3 @@ Optimizations
for instance, vars used inside loops first, then loopvars, then uwords used as pointers (or these first??), then the rest
- various optimizers skip stuff if compTarget.name==VMTarget.NAME. Once 6502-codegen is done from IR code,
those checks should probably be removed, or be made permanent
STRUCTS?
--------
- declare struct *type*, or directly declare the variable itself? Problem with the latter is: you cannot easily define multiple variables of the same struct type.
- can contain only numeric types (byte,word,float) - no nested structs, no reference types (strings, arrays) inside structs, max 1 page of memory total size to allow regular indexing?
- only as a reference type (uword pointer). This removes a lot of the problems related to introducing a variable length value type.
- arrays of struct is just an array of uword pointers. Can even be @split?
- need to introduce typed pointer datatype in prog8
- STR remains the type for a string literal (so we can keep doing register-indexed addressing directly on it)
- ARRAY remains the type for an array literal (so we can keep doing register-indexed addressing directly on it)
- we probably need to have a STRBYREF and ARRAYBYREF if we deal with a pointer to a string / array (such as when passing it to a function)
the subtype of those should include the declared element type and the declared length of the string / array
+2 -2
View File
@@ -26,8 +26,8 @@ main {
BX[lp] = math.rnd() % txt.DEFAULT_WIDTH
BY[lp] = math.rnd() % txt.DEFAULT_HEIGHT
BC[lp] = math.rnd() & 15
DX[lp] = math.rnd() & 1
DY[lp] = math.rnd() & 1
DX[lp] = math.rnd() & 1 == 1
DY[lp] = math.rnd() & 1 == 1
}
; display balls
+8 -19
View File
@@ -1,26 +1,15 @@
%import textio
%import diskio
%zeropage basicsafe
%option no_sysinit
%import textio
%import strings
main {
sub start() {
uword[] @nosplit ptrs = [one, two, three]
str name1 = sc:"irmen de jong 123456789 the quick brown fox"
str name2 = sc:"jumps over the lazy frog"
ubyte @shared x =1
goto ptrs[x]
txt.print_ub(strings.hash(name1))
txt.spc()
txt.print_ub(strings.hash(name2))
txt.nl()
}
sub one() {
txt.print("one\n")
}
sub two() {
txt.print("two\n")
}
sub three() {
txt.print("three\n")
}
}
+1 -1
View File
@@ -3,4 +3,4 @@ org.gradle.console=rich
org.gradle.parallel=true
org.gradle.daemon=true
kotlin.code.style=official
version=11.3
version=11.3.1
@@ -39,8 +39,8 @@ All have type b or w or f.
load reg1, value - load immediate value into register. If you supply a symbol, loads the *address* of the symbol! (variable values are loaded from memory via the loadm instruction)
loadm reg1, address - load reg1 with value at memory address
loadi reg1, reg2 - load reg1 with value at memory indirect, memory pointed to by reg2
loadx reg1, reg2, address - load reg1 with value at memory address indexed by value in reg2 (only the lsb part used for indexing)
loadix reg1, reg2, pointeraddr - load reg1 with value at memory indirect, pointed to by pointeraddr indexed by value in reg2 (only the lsb part used for indexing)
loadx reg1, reg2, address - load reg1 with value at memory address indexed by value in reg2 (0-255, a byte)
loadix reg1, reg2, pointeraddr - load reg1 with value at memory indirect, pointed to by pointeraddr indexed by value in reg2 (0-255, a byte)
loadr reg1, reg2 - load reg1 with value in register reg2, "reg1 = reg2"
loadha reg1 - load cpu hardware register A into reg1.b
loadhx reg1 - load cpu hardware register X into reg1.b
@@ -52,11 +52,11 @@ loadfaczero fpreg1 - load "cpu hardware register" fac0 into f
loadfacone fpreg1 - load "cpu hardware register" fac1 into freg1.f
storem reg1, address - store reg1 at memory address
storei reg1, reg2 - store reg1 at memory indirect, memory pointed to by reg2
storex reg1, reg2, address - store reg1 at memory address, indexed by value in reg2 (only the lsb part used for indexing)
storeix reg1, reg2, pointeraddr - store reg1 at memory indirect, pointed to by pointeraddr indexed by value in reg2 (only the lsb part used for indexing)
storex reg1, reg2, address - store reg1 at memory address, indexed by value in reg2 (0-255, a byte)
storeix reg1, reg2, pointeraddr - store reg1 at memory indirect, pointed to by pointeraddr indexed by value in reg2 (0-255, a byte)
storezm address - store zero at memory address
storezi reg1 - store zero at memory pointed to by reg1
storezx reg1, address - store zero at memory address, indexed by value in reg1 (only the lsb part used for indexing)
storezx reg1, address - store zero at memory address, indexed by value in reg1 (0-255, a byte)
storeha reg1 - store reg1.b into cpu hardware register A
storehx reg1 - store reg1.b into cpu hardware register X
storehy reg1 - store reg1.b into cpu hardware register Y
@@ -71,7 +71,6 @@ CONTROL FLOW
------------
jump location - continue running at instruction at 'location' (label/memory address)
jumpi reg1 - continue running at memory address in reg1 (indirect jump)
preparecall numparams - indicator that the next instructions are the param setup and function call/syscall with <numparams> parameters, does nothing by itself
calli reg1 - calls a subroutine (without arguments and without return valus) at memory addres in reg1 (indirect jsr)
call label(argument register list) [: resultreg.type]
- calls a subroutine with the given arguments and return value (optional).
@@ -80,11 +79,13 @@ call label(argument register list) [: resultreg.type]
If the call is to a rom-routine, 'label' will be a hexadecimal address instead such as $ffd2
If the arguments should be passed in CPU registers, they'll have a @REGISTER postfix.
For example: call $ffd2(r5.b@A)
Always preceded by parameter setup and preparecall instructions
Always preceded by parameter setup
callfar bank, address Call a subroutine at the given memory address, in the given RAM/ROM bank (switches both banks at the same time)
callfarvb reg1 address Call a subroutine at the given memory address, in the RAM/ROM bank in reg1.b (switches both banks at the same time)
syscall number (argument register list) [: resultreg.type]
- do a systemcall identified by number, result value(s) are pushed on value stack by the syscall code so
will be POPped off into the given resultregister if any.
Always preceded by parameter setup and preparecall instructions.
Always preceded by parameter setup
All register types (arguments + result register) are ALWAYS WORDS.
return - restore last saved instruction location and continue at that instruction. No return value.
returnr reg1 - like return, but also returns the value in reg1 to the caller
@@ -281,9 +282,10 @@ enum class Opcode {
JUMP,
JUMPI,
PREPARECALL,
CALLI,
CALL,
CALLFAR,
CALLFARVB,
SYSCALL,
RETURN,
RETURNR,
@@ -428,6 +430,8 @@ val OpcodesThatBranch = arrayOf(
Opcode.RETURNI,
Opcode.CALLI,
Opcode.CALL,
Opcode.CALLFAR,
Opcode.CALLFARVB,
Opcode.SYSCALL,
Opcode.BSTCC,
Opcode.BSTCS,
@@ -620,9 +624,10 @@ val instructionFormats = mutableMapOf(
Opcode.STOREHFACONE to InstructionFormat.from("F,<fr1"),
Opcode.JUMP to InstructionFormat.from("N,<a"),
Opcode.JUMPI to InstructionFormat.from("N,<r1"),
Opcode.PREPARECALL to InstructionFormat.from("N,<i"),
Opcode.CALLI to InstructionFormat.from("N,<r1"),
Opcode.CALL to InstructionFormat.from("N,call"),
Opcode.CALLFAR to InstructionFormat.from("N,<i,<a"),
Opcode.CALLFARVB to InstructionFormat.from("N,<r1,<a"),
Opcode.SYSCALL to InstructionFormat.from("N,syscall"),
Opcode.RETURN to InstructionFormat.from("N"),
Opcode.RETURNR to InstructionFormat.from("BW,<r1 | F,<fr1"),
@@ -890,7 +895,7 @@ data class IRInstruction(
val existingType = regsTypes[reg1]
if (existingType!=null) {
if (existingType != actualtype)
throw IllegalArgumentException("register $reg1 assigned multiple types! $existingType and $actualtype in label ${chunk?.label} chunk $chunk")
throw IllegalArgumentException("register $reg1 given multiple types! $existingType and $actualtype in label ${chunk?.label} chunk $chunk")
} else
regsTypes[reg1] = actualtype
}
@@ -902,7 +907,7 @@ data class IRInstruction(
val existingType = regsTypes[reg1]
if (existingType!=null) {
if (existingType != actualtype)
throw IllegalArgumentException("register $reg1 assigned multiple types! $existingType and $actualtype in label ${chunk?.label} chunk $chunk")
throw IllegalArgumentException("register $reg1 given multiple types! $existingType and $actualtype in label ${chunk?.label} chunk $chunk")
} else
regsTypes[reg1] = actualtype
@@ -916,7 +921,7 @@ data class IRInstruction(
val existingType = regsTypes[reg1]
if (existingType!=null) {
if (existingType != actualtype)
throw IllegalArgumentException("register $reg1 assigned multiple types! $existingType and $actualtype in label ${chunk?.label} chunk $chunk")
throw IllegalArgumentException("register $reg1 given multiple types! $existingType and $actualtype in label ${chunk?.label} chunk $chunk")
} else
regsTypes[reg1] = actualtype
@@ -932,7 +937,7 @@ data class IRInstruction(
val existingType = regsTypes[reg2]
if (existingType!=null) {
if (existingType != actualtype)
throw IllegalArgumentException("register $reg2 assigned multiple types! $existingType and $actualtype in label ${chunk?.label} chunk $chunk")
throw IllegalArgumentException("register $reg2 given multiple types! $existingType and $actualtype in label ${chunk?.label} chunk $chunk")
} else
regsTypes[reg2] = actualtype
}
@@ -948,7 +953,7 @@ data class IRInstruction(
val existingType = regsTypes[reg3]
if (existingType!=null) {
if (existingType != actualtype)
throw IllegalArgumentException("register $reg3 assigned multiple types! $existingType and $actualtype in label ${chunk?.label} chunk $chunk")
throw IllegalArgumentException("register $reg3 given multiple types! $existingType and $actualtype in label ${chunk?.label} chunk $chunk")
} else
regsTypes[reg3] = actualtype
}
@@ -981,7 +986,7 @@ data class IRInstruction(
val existingType = regsTypes[it.registerNum]
if (existingType!=null) {
if (existingType != it.dt)
throw IllegalArgumentException("register ${it.registerNum} assigned multiple types! $existingType and ${it.dt} in label ${chunk?.label} chunk $chunk")
throw IllegalArgumentException("register ${it.registerNum} given multiple types! $existingType and ${it.dt} in label ${chunk?.label} chunk $chunk")
} else
regsTypes[it.registerNum] = it.dt
}
@@ -994,7 +999,7 @@ data class IRInstruction(
val existingType = regsTypes[it.reg.registerNum]
if (existingType!=null) {
if (existingType != it.reg.dt)
throw IllegalArgumentException("register ${it.reg.registerNum} assigned multiple types! $existingType and ${it.reg.dt} in label ${chunk?.label} chunk $chunk")
throw IllegalArgumentException("register ${it.reg.registerNum} given multiple types! $existingType and ${it.reg.dt} in label ${chunk?.label} chunk $chunk")
} else
regsTypes[it.reg.registerNum] = it.reg.dt
}
@@ -1005,7 +1010,7 @@ data class IRInstruction(
private fun determineReg1Type(): IRDataType? {
if(type==IRDataType.FLOAT) {
// some float instructions have an integer register as well.
return if(opcode in arrayOf(Opcode.FFROMUB, Opcode.FFROMSB, Opcode.FTOUB, Opcode.FTOSB, Opcode.FCOMP))
return if(opcode in arrayOf(Opcode.FFROMUB, Opcode.FFROMSB, Opcode.FTOUB, Opcode.FTOSB, Opcode.FCOMP, Opcode.LOADIX, Opcode.LOADX, Opcode.STOREIX, Opcode.STOREX, Opcode.STOREZX))
IRDataType.BYTE
else
IRDataType.WORD
@@ -1088,7 +1093,7 @@ data class IRInstruction(
}
}
if(result.last().endsWith(',')) {
result.add(result.removeLast().trimEnd(','))
result.add(result.removeLastOrNull()!!.trimEnd(','))
}
result.add(")")
val returns = fcallArgs.returns
@@ -1158,7 +1163,7 @@ data class IRInstruction(
}
}
if(result.last() == ",")
result.removeLast()
result.removeLastOrNull()
return result.joinToString("").trimEnd()
}
}
@@ -4,7 +4,15 @@ import prog8.code.core.*
/*
note: all symbol names are flattened so that they're a single string that is globally unique.
Intermediate Represenation of the compiled program.
Note that even though it is called IR, it is not fully "intermediate".
This IR program is targeted to a single chosen target machine; some code in it
and all included library files are for this specific single chosen compilation target.
It will *not* be possible to create a binary program for a different target machine
from this IR than the one initially chosen!
Note: all symbol names are flattened so that they're a single string that is globally unique.
PROGRAM:
@@ -57,9 +65,11 @@ class IRProgram(val name: String,
val blocks = mutableListOf<IRBlock>()
fun allSubs(): Sequence<IRSubroutine> = blocks.asSequence().flatMap { it.children.filterIsInstance<IRSubroutine>() }
fun allAsmSubs(): Sequence<IRAsmSubroutine> = blocks.asSequence().flatMap { it.children.filterIsInstance<IRAsmSubroutine>() }
fun foreachSub(operation: (sub: IRSubroutine) -> Unit) = allSubs().forEach { operation(it) }
fun foreachCodeChunk(operation: (chunk: IRCodeChunkBase) -> Unit) {
allSubs().flatMap { it.chunks }.forEach { operation(it) }
allAsmSubs().forEach { operation(it.asmChunk) }
operation(globalInits)
}
fun getChunkWithLabel(label: String): IRCodeChunkBase {
@@ -69,6 +79,10 @@ class IRProgram(val name: String,
return chunk
}
}
for(sub in allAsmSubs()) {
if(sub.asmChunk.label==label)
return sub.asmChunk
}
throw NoSuchElementException("no chunk with label '$label'")
}
@@ -209,7 +223,7 @@ class IRProgram(val name: String,
if(chunk is IRInlineAsmChunk)
require(!chunk.isIR) { "inline IR-asm should have been converted into regular code chunk"}
}
chunk.instructions.withIndex().forEach { (index, instr) ->
chunk.instructions.forEach { instr ->
if(instr.labelSymbol!=null && instr.opcode in OpcodesThatBranch) {
if(instr.opcode==Opcode.JUMPI) {
when(val pointervar = st.lookup(instr.labelSymbol)!!) {
@@ -221,26 +235,6 @@ class IRProgram(val name: String,
else if(!instr.labelSymbol.startsWith('$') && !instr.labelSymbol.first().isDigit())
require(instr.branchTarget != null) { "branching instruction to label should have branchTarget set" }
}
if(instr.opcode==Opcode.PREPARECALL) {
var i = index+1
var instr2 = chunk.instructions[i]
val registers = mutableSetOf<Int>()
while(instr2.opcode!=Opcode.SYSCALL && instr2.opcode!=Opcode.CALL && i<chunk.instructions.size-1) {
if(instr2.reg1direction==OperandDirection.WRITE || instr2.reg1direction==OperandDirection.READWRITE) registers.add(instr2.reg1!!)
if(instr2.reg2direction==OperandDirection.WRITE || instr2.reg2direction==OperandDirection.READWRITE) registers.add(instr2.reg2!!)
if(instr2.reg3direction==OperandDirection.WRITE || instr2.reg3direction==OperandDirection.READWRITE) registers.add(instr2.reg3!!)
if(instr2.fpReg1direction==OperandDirection.WRITE || instr2.fpReg1direction==OperandDirection.READWRITE) registers.add(instr2.fpReg1!!)
if(instr2.fpReg2direction==OperandDirection.WRITE || instr2.fpReg2direction==OperandDirection.READWRITE) registers.add(instr2.fpReg2!!)
i++
instr2 = chunk.instructions[i]
}
// it could be that the actual call is only in another code chunk, so IF we find one, we can check, otherwise just skip the check...
if(chunk.instructions[i].fcallArgs!=null) {
val expectedRegisterLoads = chunk.instructions[i].fcallArgs!!.arguments.map { it.reg.registerNum }
require(registers.containsAll(expectedRegisterLoads)) { "not all argument registers are given a value in the preparecall-call sequence" }
}
}
}
}
@@ -277,7 +271,7 @@ class IRProgram(val name: String,
val existingType = regsTypes[reg]
if (existingType!=null) {
if (existingType != type)
throw IllegalArgumentException("register $reg assigned multiple types! $existingType and $type ${this.name}<--${child.label ?: child}")
throw IllegalArgumentException("register $reg given multiple types! $existingType and $type ${this.name}<--${child.label ?: child}")
} else
regsTypes[reg] = type
}
+2
View File
@@ -198,6 +198,8 @@ class StStaticVariable(name: String,
// This has to do with the way Prog8 does the (re)initialization of such variables: via code assignment statements.
// Certain codegens might want to put them back into the variable directly.
// For strings and arrays this doesn't occur - these are always already specced at creation time.
require(number!=0.0) { "variable should not be initialized with 0, it will already be zeroed as part of BSS clear" }
initializationNumericValue = number
}
@@ -39,8 +39,8 @@ sealed interface IPtSubroutine {
val availableFloatRegisters = mutableListOf(RegisterOrPair.FAC1) // just one value is possible
val others = returns.drop(1).map { type ->
when {
type.isFloat -> RegisterOrStatusflag(availableFloatRegisters.removeLast(), null) to type
type.isIntegerOrBool -> RegisterOrStatusflag(availableIntegerRegisters.removeLast(), null) to type
type.isFloat -> RegisterOrStatusflag(availableFloatRegisters.removeLastOrNull()!!, null) to type
type.isIntegerOrBool -> RegisterOrStatusflag(availableIntegerRegisters.removeLastOrNull()!!, null) to type
else -> throw AssemblyError("unsupported return type $type")
}
}
@@ -216,9 +216,9 @@ class VirtualMachine(irProgram: IRProgram) {
Opcode.STOREHFACONE-> InsSTOREHFACONE(ins)
Opcode.JUMP -> InsJUMP(ins)
Opcode.JUMPI -> InsJUMPI(ins)
Opcode.PREPARECALL -> nextPc()
Opcode.CALLI -> throw IllegalArgumentException("VM cannot run code from memory bytes")
Opcode.CALL -> InsCALL(ins)
Opcode.CALLFAR, Opcode.CALLFARVB -> throw IllegalArgumentException("VM cannot run code from another ram/rombank")
Opcode.SYSCALL -> InsSYSCALL(ins)
Opcode.RETURN -> InsRETURN()
Opcode.RETURNR -> InsRETURNR(ins)
+11 -2
View File
@@ -1,10 +1,10 @@
package prog8.vm
import prog8.Either
import prog8.code.core.DataType
import prog8.intermediate.*
import prog8.left
import prog8.right
import prog8.intermediate.*
import prog8.code.core.DataType
class VmProgramLoader {
private val placeholders = mutableMapOf<Pair<IRCodeChunk, Int>, String>() // program chunk+index to symbolname
@@ -223,6 +223,15 @@ class VmProgramLoader {
else -> throw IRParseException("invalid dt")
}
}
} else {
when {
variable.dt.isUnsignedByte || variable.dt.isBool -> memory.setUB(addr, 0u)
variable.dt.isSignedByte -> memory.setSB(addr, 0)
variable.dt.isUnsignedWord -> memory.setUW(addr, 0u)
variable.dt.isSignedWord -> memory.setSW(addr, 0)
variable.dt.isFloat -> memory.setFloat(addr, 0.0)
else -> throw IRParseException("invalid dt")
}
}
}