fix golden ram area for x16, remove romsub restriction

note: romsubs still won't work in the VM but at least they compile again
This commit is contained in:
Irmen de Jong 2023-04-07 22:56:23 +02:00
parent f47498888c
commit c3d74f2ae9
12 changed files with 64 additions and 66 deletions

2
.idea/kotlinc.xml generated
View File

@ -4,6 +4,6 @@
<option name="jvmTarget" value="11" />
</component>
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.8.20" />
<option name="version" value="1.8.0-release-345" />
</component>
</project>

View File

@ -131,6 +131,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
}
// TODO: this class is not yet used
class GoldenRam(options: CompilationOptions, val region: UIntRange): MemoryAllocator(options) {
private var nextLocation: UInt = region.first

View File

@ -64,7 +64,7 @@ class CX16MachineDefinition: IMachineDefinition {
override fun initializeMemoryAreas(compilerOptions: CompilationOptions) {
zeropage = CX16Zeropage(compilerOptions)
golden = GoldenRam(compilerOptions, 0x0600u until 0x0800u)
golden = GoldenRam(compilerOptions, 0x0400u until ESTACK_LO)
}
}

View File

@ -1465,9 +1465,12 @@ class IRCodeGen(
}
} else {
// regular asmsub
val assemblyChild = child.children.single() as PtInlineAssembly
if(child.children.map { (it as PtInlineAssembly).isIR }.toSet().size>1)
errors.err("asmsub mixes IR and non-IR assembly code (could be compiler-generated)", child.position)
val asmblocks = child.children.map { (it as PtInlineAssembly).assembly.trimEnd() }
val assembly = asmblocks.joinToString("\n")
val asmChunk = IRInlineAsmChunk(
child.name, assemblyChild.assembly, assemblyChild.isIR, null
child.name, assembly, (child.children[0] as PtInlineAssembly).isIR , null
)
irBlock += IRAsmSubroutine(
child.name,

View File

@ -8,6 +8,7 @@ import prog8.code.target.VMTarget
import prog8.codegen.vm.VmAssemblyProgram
import prog8.codegen.vm.VmCodeGen
import prog8.intermediate.IRSubroutine
import prog8.intermediate.Opcode
class TestVmCodeGen: FunSpec({
@ -440,4 +441,33 @@ class TestVmCodeGen: FunSpec({
irChunks.size shouldBe 1
}
test("romsub allowed in codegen") {
//main {
// romsub $5000 = routine()
//
// sub start() {
// routine()
// }
//}
val codegen = VmCodeGen()
val program = PtProgram("test", DummyMemsizer, DummyStringEncoder)
val block = PtBlock("main", null, false, false, PtBlock.BlockAlignment.NONE, SourceCode.Generated("test"), Position.DUMMY)
val romsub = PtAsmSub("routine", 0x5000u, emptySet(), emptyList(), emptyList(), false, Position.DUMMY)
block.add(romsub)
val sub = PtSub("start", emptyList(), null, Position.DUMMY)
val call = PtFunctionCall("main.routine", true, DataType.UNDEFINED, Position.DUMMY)
sub.add(call)
block.add(sub)
program.add(block)
val options = getTestOptions()
val st = SymbolTableMaker(program, options).make()
val errors = ErrorReporterForTests()
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
val callInstr = irChunks.single().instructions.single()
callInstr.opcode shouldBe Opcode.CALL
callInstr.value shouldBe 0x5000
}
})

View File

@ -326,8 +326,6 @@ internal class AstChecker(private val program: Program,
err("subroutines can only be defined in the scope of a block or within another subroutine")
if(subroutine.isAsmSubroutine) {
if(compilerOptions.compTarget.name==VMTarget.NAME)
err("cannot use asmsub for vm target, use regular subs")
if(subroutine.asmParameterRegisters.size != subroutine.parameters.size)
err("number of asm parameter registers is not the isSameAs as number of parameters")
if(subroutine.asmReturnvaluesRegisters.size != subroutine.returntypes.size)

View File

@ -355,9 +355,9 @@ class IntermediateAstMaker(private val program: Program, private val options: Co
for (asm in srcSub.statements) {
asm as InlineAssembly
if(asm.isIR)
combinedIrAsm += asm.assembly + "\n"
combinedIrAsm += asm.assembly.trimEnd() + "\n"
else
combinedTrueAsm += asm.assembly + "\n"
combinedTrueAsm += asm.assembly.trimEnd() + "\n"
}
if(combinedTrueAsm.isNotEmpty()) {

View File

@ -14,7 +14,6 @@ import prog8.intermediate.IRFileReader
import prog8.intermediate.IRSubroutine
import prog8.intermediate.Opcode
import prog8.vm.VmRunner
import prog8tests.helpers.ErrorReporterForTests
import prog8tests.helpers.compileText
import kotlin.io.path.readText
@ -219,54 +218,6 @@ main {
}
}
test("asmsub for virtual target not supported") {
val src = """
main {
sub start() {
void test(42)
}
asmsub test(ubyte xx @A) -> ubyte @Y {
%asm {{
lda #99
tay
rts
}}
}
}"""
val othertarget = Cx16Target()
compileText(othertarget, true, src, writeAssembly = true) shouldNotBe null
val target = VMTarget()
val errors = ErrorReporterForTests()
compileText(target, false, src, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 1
errors.errors[0] shouldContain "cannot use asmsub for vm target"
}
test("asmsub for virtual target not supported even with IR") {
val src = """
main {
sub start() {
void test(42)
}
asmsub test(ubyte xx @A) -> ubyte @Y {
%ir {{
return
}}
}
}"""
val othertarget = Cx16Target()
compileText(othertarget, true, src, writeAssembly = true) shouldNotBe null
val target = VMTarget()
val errors = ErrorReporterForTests()
compileText(target, false, src, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 1
errors.errors[0] shouldContain "cannot use asmsub for vm target"
}
test("inline asm for virtual target should be IR") {
val src = """
main {

View File

@ -40,6 +40,9 @@ Compiler:
- ir: peephole opt: reuse registers in chunks (but keep result registers in mind that pass values out! and don't renumber registers above SyscallRegisterBase!)
- ir: add more optimizations in IRPeepholeOptimizer
- ir: for expressions with array indexes that occur multiple times, can we avoid loading them into new virtualregs everytime and just reuse a single virtualreg as indexer? (simple form of common subexpression elimination)
- ir: write arguments for subroutine calls in IR differently?
don't load them explicitly into the variables but use a new special instruction to do it transparently.
(vm should take care of it based on the subroutine's parameter list)
- PtAst/IR: more complex common subexpression eliminations
- vm: somehow be able to load a label address as value? (VmProgramLoader) this may require storing the program as bytecodes in actual memory though...
- 6502 codegen: see if we can let for loops skip the loop if startvar>endvar, without adding a lot of code size/duplicating the loop condition.

View File

@ -414,12 +414,15 @@ private fun registersUsedInAssembly(isIR: Boolean, assembly: String): RegistersU
if(isIR) {
assembly.lineSequence().forEach { line ->
val result = parseIRCodeLine(line.trim(), null, mutableMapOf())
val t = line.trim()
if(t.isNotEmpty()) {
val result = parseIRCodeLine(t, null, mutableMapOf())
result.fold(
ifLeft = { it.addUsedRegistersCounts(readRegsCounts, writeRegsCounts,readFpRegsCounts, writeFpRegsCounts, regsTypes) },
ifRight = { /* labels can be skipped */ }
)
}
}
}
return RegistersUsed(readRegsCounts, writeRegsCounts, readFpRegsCounts, writeFpRegsCounts, regsTypes)
}

View File

@ -1,6 +1,7 @@
package prog8.vm
import prog8.code.StMemVar
import prog8.code.core.toHex
import prog8.code.target.virtual.IVirtualMachineRunner
import prog8.intermediate.*
import java.awt.Color
@ -147,7 +148,12 @@ class VirtualMachine(irProgram: IRProgram) {
pcChunk = target
pcIndex = 0
}
null -> throw IllegalArgumentException("no branchtarget in $i")
null -> {
if(i.value!=null)
throw IllegalArgumentException("vm program can't jump to system memory address (${i.opcode} ${i.value!!.toHex()})")
else
throw IllegalArgumentException("no branchtarget in $i")
}
else -> throw IllegalArgumentException("VM can't execute code in a non-codechunk: $target")
}
}

View File

@ -69,7 +69,7 @@ class VmProgramLoader {
programChunks.forEach {
it.instructions.forEach { ins ->
if (ins.labelSymbol != null && ins.opcode !in OpcodesThatBranch)
if (ins.labelSymbol != null && ins.opcode !in OpcodesThatBranch && ins.opcode !in OpcodesForCpuRegisters)
require(ins.value != null) { "instruction with labelSymbol for a var should have value set to var's memory address" }
}
}
@ -153,8 +153,11 @@ class VmProgramLoader {
// placeholder is not a variable, so it must be a label of a code chunk instead
val target: IRCodeChunk? = chunks.firstOrNull { it.label==label }
val opcode = chunk.instructions[line].opcode
if(target==null)
if(target==null) {
// exception allowed: storecpu/loadcpu instructions that refer to CPU registers
if(opcode !in OpcodesForCpuRegisters)
throw IRParseException("placeholder not found in variables nor labels: $label")
}
else if(opcode in OpcodesThatBranch) {
chunk.instructions[line] = chunk.instructions[line].copy(branchTarget = target, value = null)
} else if(opcode in OpcodesWithMemoryAddressAsValue) {