mirror of
https://github.com/irmen/prog8.git
synced 2024-05-29 01:41:32 +00:00
IR: fix problems with symbol offsets and unused subroutines/chunks
This commit is contained in:
parent
3b199a2a87
commit
968609d06d
|
@ -229,11 +229,11 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||||
if(fixedIndex!=null) {
|
if(fixedIndex!=null) {
|
||||||
val chunk = IRCodeChunk(null, null).also {
|
val chunk = IRCodeChunk(null, null).also {
|
||||||
if(targetArray.splitWords) {
|
if(targetArray.splitWords) {
|
||||||
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_lsb+$fixedIndex")
|
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
|
||||||
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_msb+$fixedIndex")
|
it += IRInstruction(Opcode.STOREZM, IRDataType.BYTE, immediate = arrayLength, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
it += IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = "$variable+${fixedIndex*itemsize}")
|
it += IRInstruction(Opcode.STOREZM, targetDt, labelSymbol = variable, symbolOffset = fixedIndex*itemsize)
|
||||||
}
|
}
|
||||||
result += chunk
|
result += chunk
|
||||||
} else {
|
} else {
|
||||||
|
@ -253,7 +253,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||||
if(fixedIndex!=null) {
|
if(fixedIndex!=null) {
|
||||||
val offset = fixedIndex*itemsize
|
val offset = fixedIndex*itemsize
|
||||||
val chunk = IRCodeChunk(null, null).also {
|
val chunk = IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = "$variable+$offset")
|
it += IRInstruction(Opcode.STOREM, targetDt, fpReg1 = valueFpRegister, labelSymbol = variable, symbolOffset = offset)
|
||||||
}
|
}
|
||||||
result += chunk
|
result += chunk
|
||||||
} else {
|
} else {
|
||||||
|
@ -268,12 +268,12 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||||
val chunk = IRCodeChunk(null, null).also {
|
val chunk = IRCodeChunk(null, null).also {
|
||||||
if(targetArray.splitWords) {
|
if(targetArray.splitWords) {
|
||||||
val msbReg = codeGen.registers.nextFree()
|
val msbReg = codeGen.registers.nextFree()
|
||||||
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = valueRegister, immediate = arrayLength, labelSymbol = "${variable}_lsb+$fixedIndex")
|
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = valueRegister, immediate = arrayLength, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
|
||||||
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = msbReg, reg2 = valueRegister)
|
it += IRInstruction(Opcode.MSIG, IRDataType.BYTE, reg1 = msbReg, reg2 = valueRegister)
|
||||||
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = msbReg, immediate = arrayLength, labelSymbol = "${variable}_msb+$fixedIndex")
|
it += IRInstruction(Opcode.STOREM, IRDataType.BYTE, reg1 = msbReg, immediate = arrayLength, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
it += IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = "$variable+${fixedIndex*itemsize}")
|
it += IRInstruction(Opcode.STOREM, targetDt, reg1 = valueRegister, labelSymbol = variable, symbolOffset = fixedIndex*itemsize)
|
||||||
}
|
}
|
||||||
result += chunk
|
result += chunk
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -188,8 +188,8 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||||
val memOffset = (arrayIx.index as PtNumber).number.toInt()
|
val memOffset = (arrayIx.index as PtNumber).number.toInt()
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
val tmpRegMsb = codeGen.registers.nextFree()
|
val tmpRegMsb = codeGen.registers.nextFree()
|
||||||
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=tmpRegMsb, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_msb+$memOffset")
|
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=tmpRegMsb, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_msb", symbolOffset = memOffset)
|
||||||
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=resultRegister, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_lsb+$memOffset")
|
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=resultRegister, immediate = arrayLength, labelSymbol= "${arrayVarSymbol}_lsb", symbolOffset = memOffset)
|
||||||
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=finalResultReg, reg2=tmpRegMsb, reg3=resultRegister)
|
it += IRInstruction(Opcode.CONCAT, IRDataType.BYTE, reg1=finalResultReg, reg2=tmpRegMsb, reg3=resultRegister)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -207,14 +207,14 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||||
|
|
||||||
var resultFpRegister = -1
|
var resultFpRegister = -1
|
||||||
if(arrayIx.index is PtNumber) {
|
if(arrayIx.index is PtNumber) {
|
||||||
val memOffset = ((arrayIx.index as PtNumber).number.toInt() * eltSize).toString()
|
val memOffset = ((arrayIx.index as PtNumber).number.toInt() * eltSize)
|
||||||
if(vmDt==IRDataType.FLOAT) {
|
if(vmDt==IRDataType.FLOAT) {
|
||||||
resultFpRegister = codeGen.registers.nextFreeFloat()
|
resultFpRegister = codeGen.registers.nextFreeFloat()
|
||||||
addInstr(result, IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1=resultFpRegister, labelSymbol = "$arrayVarSymbol+$memOffset"), null)
|
addInstr(result, IRInstruction(Opcode.LOADM, IRDataType.FLOAT, fpReg1=resultFpRegister, labelSymbol = arrayVarSymbol, symbolOffset = memOffset), null)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
resultRegister = codeGen.registers.nextFree()
|
resultRegister = codeGen.registers.nextFree()
|
||||||
addInstr(result, IRInstruction(Opcode.LOADM, vmDt, reg1=resultRegister, labelSymbol = "$arrayVarSymbol+$memOffset"), null)
|
addInstr(result, IRInstruction(Opcode.LOADM, vmDt, reg1=resultRegister, labelSymbol = arrayVarSymbol, symbolOffset = memOffset), null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val tr = translateExpression(arrayIx.index)
|
val tr = translateExpression(arrayIx.index)
|
||||||
|
|
|
@ -143,19 +143,10 @@ class IRCodeGen(
|
||||||
(idx, instr) ->
|
(idx, instr) ->
|
||||||
val symbolExpr = instr.labelSymbol
|
val symbolExpr = instr.labelSymbol
|
||||||
if(symbolExpr!=null) {
|
if(symbolExpr!=null) {
|
||||||
val symbol: String
|
val index = instr.labelSymbolOffset ?: 0
|
||||||
val index: UInt
|
val target = symbolTable.flat[symbolExpr]
|
||||||
if('+' in symbolExpr) {
|
|
||||||
val operands = symbolExpr.split('+', )
|
|
||||||
symbol = operands[0]
|
|
||||||
index = operands[1].toUInt()
|
|
||||||
} else {
|
|
||||||
symbol = symbolExpr
|
|
||||||
index = 0u
|
|
||||||
}
|
|
||||||
val target = symbolTable.flat[symbol]
|
|
||||||
if (target is StMemVar) {
|
if (target is StMemVar) {
|
||||||
replacements.add(Triple(chunk, idx, target.address+index))
|
replacements.add(Triple(chunk, idx, target.address+index.toUInt()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1451,21 +1442,21 @@ class IRCodeGen(
|
||||||
when(postIncrDecr.operator) {
|
when(postIncrDecr.operator) {
|
||||||
"++" -> {
|
"++" -> {
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = "${variable}_lsb+$fixedIndex")
|
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
|
||||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel)
|
it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel)
|
||||||
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = "${variable}_msb+$fixedIndex")
|
it += IRInstruction(Opcode.INCM, IRDataType.BYTE, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
|
||||||
}
|
}
|
||||||
result += IRCodeChunk(skipLabel, null)
|
result += IRCodeChunk(skipLabel, null)
|
||||||
}
|
}
|
||||||
"--" -> {
|
"--" -> {
|
||||||
val valueReg=registers.nextFree()
|
val valueReg=registers.nextFree()
|
||||||
result += IRCodeChunk(null, null).also {
|
result += IRCodeChunk(null, null).also {
|
||||||
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=valueReg, labelSymbol = "${variable}_lsb+$fixedIndex")
|
it += IRInstruction(Opcode.LOADM, IRDataType.BYTE, reg1=valueReg, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
|
||||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel)
|
it += IRInstruction(Opcode.BSTNE, labelSymbol = skipLabel)
|
||||||
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = "${variable}_msb+$fixedIndex")
|
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = "${variable}_msb", symbolOffset = fixedIndex)
|
||||||
}
|
}
|
||||||
result += IRCodeChunk(skipLabel, null).also {
|
result += IRCodeChunk(skipLabel, null).also {
|
||||||
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = "${variable}_lsb+$fixedIndex")
|
it += IRInstruction(Opcode.DECM, IRDataType.BYTE, labelSymbol = "${variable}_lsb", symbolOffset = fixedIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird operator")
|
else -> throw AssemblyError("weird operator")
|
||||||
|
@ -1556,7 +1547,7 @@ class IRCodeGen(
|
||||||
it += IRInstruction(Opcode.STOREIX, irDt, reg1 = dataReg, reg2 = indexReg, labelSymbol = variable)
|
it += IRInstruction(Opcode.STOREIX, irDt, reg1 = dataReg, reg2 = indexReg, labelSymbol = variable)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol = "$variable+$offset"), null)
|
addInstr(result, IRInstruction(operationMem, irDt, labelSymbol = variable, symbolOffset = offset), null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val indexTr = expressionEval.translateExpression(array.index)
|
val indexTr = expressionEval.translateExpression(array.index)
|
||||||
|
|
|
@ -167,7 +167,13 @@ class IRUnusedCodeRemover(
|
||||||
it.next?.let { next -> new += next }
|
it.next?.let { next -> new += next }
|
||||||
it.instructions.forEach { instr ->
|
it.instructions.forEach { instr ->
|
||||||
if (instr.branchTarget == null)
|
if (instr.branchTarget == null)
|
||||||
instr.labelSymbol?.let { label -> allLabeledChunks[label]?.let { chunk -> new += chunk } }
|
instr.labelSymbol?.let { label ->
|
||||||
|
val chunk = allLabeledChunks[label.substringBeforeLast('.')]
|
||||||
|
if(chunk!=null)
|
||||||
|
new+=chunk
|
||||||
|
else
|
||||||
|
allLabeledChunks[label]?.let { new += it }
|
||||||
|
}
|
||||||
else
|
else
|
||||||
new += instr.branchTarget!!
|
new += instr.branchTarget!!
|
||||||
}
|
}
|
||||||
|
@ -214,6 +220,16 @@ class IRUnusedCodeRemover(
|
||||||
linkedChunks += chunk
|
linkedChunks += chunk
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// make sure that chunks that are only used as a prefix of a label, are also marked as linked
|
||||||
|
linkedChunks.forEach { chunk ->
|
||||||
|
chunk.instructions.forEach {
|
||||||
|
if(it.labelSymbol!=null) {
|
||||||
|
val chunkName = it.labelSymbol!!.substringBeforeLast('.')
|
||||||
|
allLabeledChunks[chunkName]?.let { linkedChunks+=it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return removeUnlinkedChunks(linkedChunks)
|
return removeUnlinkedChunks(linkedChunks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,9 @@ import prog8.code.target.C64Target
|
||||||
import prog8.code.target.VMTarget
|
import prog8.code.target.VMTarget
|
||||||
import prog8.compiler.CallGraph
|
import prog8.compiler.CallGraph
|
||||||
import prog8.parser.Prog8Parser.parseModule
|
import prog8.parser.Prog8Parser.parseModule
|
||||||
|
import prog8.vm.VmRunner
|
||||||
import prog8tests.helpers.*
|
import prog8tests.helpers.*
|
||||||
|
import kotlin.io.path.readText
|
||||||
|
|
||||||
class TestCallgraph: FunSpec({
|
class TestCallgraph: FunSpec({
|
||||||
test("testGraphForEmptySubs") {
|
test("testGraphForEmptySubs") {
|
||||||
|
@ -224,13 +226,13 @@ class TestCallgraph: FunSpec({
|
||||||
errors.errors.size shouldBe 0
|
errors.errors.size shouldBe 0
|
||||||
errors.warnings.size shouldBe 0
|
errors.warnings.size shouldBe 0
|
||||||
}
|
}
|
||||||
|
|
||||||
test("subs that aren't called but only used as scope aren't unused") {
|
test("subs that aren't called but only used as scope aren't unused (6502)") {
|
||||||
val src="""
|
val src="""
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
cx16.r0L = main.scopesub.variable
|
cx16.r0L = main.scopesub.variable
|
||||||
cx16.r0L = main.scopesub.array[1]
|
cx16.r1L = main.scopesub.array[1]
|
||||||
cx16.r0++
|
cx16.r0++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,14 +240,49 @@ main {
|
||||||
ubyte variable
|
ubyte variable
|
||||||
ubyte[] array = [1,2,3]
|
ubyte[] array = [1,2,3]
|
||||||
|
|
||||||
cx16.r0++
|
variable++
|
||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
val result = compileText(VMTarget(), true, src)!!
|
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
|
||||||
|
val result = compileText(C64Target(), true, src, errors=errors)!!
|
||||||
val callgraph = CallGraph(result.compilerAst)
|
val callgraph = CallGraph(result.compilerAst)
|
||||||
val scopeSub = result.compilerAst.entrypoint.lookup(listOf("main", "scopesub")) as Subroutine
|
val scopeSub = result.compilerAst.entrypoint.lookup(listOf("main", "scopesub")) as Subroutine
|
||||||
scopeSub.name shouldBe "scopesub"
|
scopeSub.name shouldBe "scopesub"
|
||||||
callgraph.notCalledButReferenced shouldContain scopeSub
|
callgraph.notCalledButReferenced shouldContain scopeSub
|
||||||
callgraph.unused(scopeSub) shouldBe false
|
callgraph.unused(scopeSub) shouldBe false
|
||||||
|
|
||||||
|
errors.warnings.any { "unused" in it } shouldBe false
|
||||||
|
errors.infos.any { "unused" in it } shouldBe false
|
||||||
|
}
|
||||||
|
|
||||||
|
test("subs that aren't called but only used as scope aren't unused (IR/VM)") {
|
||||||
|
val src="""
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
cx16.r0L = main.scopesub.variable
|
||||||
|
cx16.r1L = main.scopesub.array[1]
|
||||||
|
cx16.r0++
|
||||||
|
}
|
||||||
|
|
||||||
|
sub scopesub() {
|
||||||
|
ubyte variable
|
||||||
|
ubyte[] array = [1,2,3]
|
||||||
|
|
||||||
|
variable++
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
|
||||||
|
val result = compileText(VMTarget(), true, src, errors=errors)!!
|
||||||
|
val callgraph = CallGraph(result.compilerAst)
|
||||||
|
val scopeSub = result.compilerAst.entrypoint.lookup(listOf("main", "scopesub")) as Subroutine
|
||||||
|
scopeSub.name shouldBe "scopesub"
|
||||||
|
callgraph.notCalledButReferenced shouldContain scopeSub
|
||||||
|
callgraph.unused(scopeSub) shouldBe false
|
||||||
|
|
||||||
|
errors.warnings.any { "unused" in it } shouldBe false
|
||||||
|
errors.infos.any { "unused" in it } shouldBe false
|
||||||
|
|
||||||
|
val virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir")
|
||||||
|
VmRunner().runProgram(virtfile.readText())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
%import textio
|
%import textio
|
||||||
%import palette
|
|
||||||
%zeropage basicsafe
|
%zeropage basicsafe
|
||||||
%option no_sysinit
|
%option no_sysinit
|
||||||
|
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
cx16.r0L = main.dummy.variable
|
cx16.r0L = main.dummy.variable
|
||||||
cx16.r0L = main.dummy.array[1]
|
cx16.r1L = main.dummy.array[1]
|
||||||
cx16.r0++
|
cx16.r0++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -732,6 +732,7 @@ data class IRInstruction(
|
||||||
val immediateFp: Double?=null,
|
val immediateFp: Double?=null,
|
||||||
val address: Int?=null, // 0-$ffff
|
val address: Int?=null, // 0-$ffff
|
||||||
val labelSymbol: String?=null, // symbolic label name as alternative to address (so only for Branch/jump/call Instructions!)
|
val labelSymbol: String?=null, // symbolic label name as alternative to address (so only for Branch/jump/call Instructions!)
|
||||||
|
private val symbolOffset: Int? = null, // offset to add on labelSymbol (used to index into an array variable)
|
||||||
var branchTarget: IRCodeChunkBase? = null, // Will be linked after loading in IRProgram.linkChunks()! This is the chunk that the branch labelSymbol points to.
|
var branchTarget: IRCodeChunkBase? = null, // Will be linked after loading in IRProgram.linkChunks()! This is the chunk that the branch labelSymbol points to.
|
||||||
val fcallArgs: FunctionCallArgs? = null // will be set for the CALL and SYSCALL instructions.
|
val fcallArgs: FunctionCallArgs? = null // will be set for the CALL and SYSCALL instructions.
|
||||||
) {
|
) {
|
||||||
|
@ -742,9 +743,16 @@ data class IRInstruction(
|
||||||
val reg3direction: OperandDirection
|
val reg3direction: OperandDirection
|
||||||
val fpReg1direction: OperandDirection
|
val fpReg1direction: OperandDirection
|
||||||
val fpReg2direction: OperandDirection
|
val fpReg2direction: OperandDirection
|
||||||
|
val labelSymbolOffset = if(symbolOffset==0) null else symbolOffset
|
||||||
|
|
||||||
init {
|
init {
|
||||||
require(labelSymbol?.first()!='_') {"label/symbol should not start with underscore $labelSymbol"}
|
if(labelSymbol!=null) {
|
||||||
|
require(labelSymbol.first() != '_') { "label/symbol should not start with underscore $labelSymbol" }
|
||||||
|
require(labelSymbol.all { it.isJavaIdentifierStart() || it.isJavaIdentifierPart() || it=='.' }) {
|
||||||
|
"label/symbol contains invalid character $labelSymbol"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(labelSymbolOffset!=null) require(labelSymbolOffset>0 && labelSymbol!=null) {"labelsymbol offset inconsistency"}
|
||||||
require(reg1==null || reg1 in 0..65536) {"reg1 out of bounds"}
|
require(reg1==null || reg1 in 0..65536) {"reg1 out of bounds"}
|
||||||
require(reg2==null || reg2 in 0..65536) {"reg2 out of bounds"}
|
require(reg2==null || reg2 in 0..65536) {"reg2 out of bounds"}
|
||||||
require(reg3==null || reg3 in 0..65536) {"reg3 out of bounds"}
|
require(reg3==null || reg3 in 0..65536) {"reg3 out of bounds"}
|
||||||
|
@ -941,7 +949,12 @@ data class IRInstruction(
|
||||||
|
|
||||||
if(this.fcallArgs!=null) {
|
if(this.fcallArgs!=null) {
|
||||||
immediate?.let { result.add(it.toHex()) } // syscall
|
immediate?.let { result.add(it.toHex()) } // syscall
|
||||||
labelSymbol?.let { result.add(it) } // regular subroutine call
|
if(labelSymbol!=null) {
|
||||||
|
// regular subroutine call
|
||||||
|
result.add(labelSymbol)
|
||||||
|
if(labelSymbolOffset!=null)
|
||||||
|
result.add("+$labelSymbolOffset")
|
||||||
|
}
|
||||||
address?.let { result.add(address.toHex()) } // romcall
|
address?.let { result.add(address.toHex()) } // romcall
|
||||||
result.add("(")
|
result.add("(")
|
||||||
fcallArgs.arguments.forEach {
|
fcallArgs.arguments.forEach {
|
||||||
|
@ -1028,6 +1041,8 @@ data class IRInstruction(
|
||||||
}
|
}
|
||||||
labelSymbol?.let {
|
labelSymbol?.let {
|
||||||
result.add(it)
|
result.add(it)
|
||||||
|
if(labelSymbolOffset!=null)
|
||||||
|
result.add("+$labelSymbolOffset")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(result.last() == ",")
|
if(result.last() == ",")
|
||||||
|
|
|
@ -196,12 +196,20 @@ fun parseIRCodeLine(line: String): Either<IRInstruction, String> {
|
||||||
if(format.address!=OperandDirection.UNUSED && address==null && labelSymbol==null)
|
if(format.address!=OperandDirection.UNUSED && address==null && labelSymbol==null)
|
||||||
throw IRParseException("requires address or symbol for $line")
|
throw IRParseException("requires address or symbol for $line")
|
||||||
|
|
||||||
|
var offset: Int? = null
|
||||||
if(labelSymbol!=null) {
|
if(labelSymbol!=null) {
|
||||||
if (labelSymbol!![0] == 'r' && labelSymbol!![1].isDigit())
|
if (labelSymbol!![0] == 'r' && labelSymbol!![1].isDigit())
|
||||||
throw IRParseException("labelsymbol confused with register?: $labelSymbol")
|
throw IRParseException("labelsymbol confused with register?: $labelSymbol")
|
||||||
|
if('+' in labelSymbol!!) {
|
||||||
|
val offsetStr = labelSymbol!!.substringAfterLast('+')
|
||||||
|
if (offsetStr.isNotEmpty()) {
|
||||||
|
offset = offsetStr.toInt()
|
||||||
|
labelSymbol = labelSymbol!!.substringBeforeLast('+')
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return left(IRInstruction(opcode, type, reg1, reg2, reg3, fpReg1, fpReg2, immediateInt, immediateFp, address, labelSymbol = labelSymbol))
|
return left(IRInstruction(opcode, type, reg1, reg2, reg3, fpReg1, fpReg2, immediateInt, immediateFp, address, labelSymbol = labelSymbol, symbolOffset = offset))
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ParsedCall(
|
private class ParsedCall(
|
||||||
|
|
|
@ -131,5 +131,19 @@ class TestInstructions: FunSpec({
|
||||||
require(format.fpReg2==OperandDirection.UNUSED || format.fpReg2==OperandDirection.READ) {"fpReg2 can only be used as input"}
|
require(format.fpReg2==OperandDirection.UNUSED || format.fpReg2==OperandDirection.READ) {"fpReg2 can only be used as input"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("with symbol offset") {
|
||||||
|
val i1 = IRInstruction(Opcode.ADDM, IRDataType.BYTE, reg1 = 1, labelSymbol = "symbol", symbolOffset = 99)
|
||||||
|
i1.labelSymbol shouldBe "symbol"
|
||||||
|
i1.labelSymbolOffset shouldBe 99
|
||||||
|
|
||||||
|
val i2 = IRInstruction(Opcode.ADDM, IRDataType.BYTE, reg1 = 1, labelSymbol = "symbol", symbolOffset = 0)
|
||||||
|
i2.labelSymbol shouldBe "symbol"
|
||||||
|
i2.labelSymbolOffset shouldBe null
|
||||||
|
|
||||||
|
shouldThrowWithMessage<IllegalArgumentException>("labelsymbol offset inconsistency") {
|
||||||
|
IRInstruction(Opcode.ADDR, IRDataType.BYTE, reg1 = 1, reg2 = 2, symbolOffset = 99)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -148,26 +148,25 @@ class VmProgramLoader {
|
||||||
for((ref, label) in placeholders) {
|
for((ref, label) in placeholders) {
|
||||||
val (chunk, line) = ref
|
val (chunk, line) = ref
|
||||||
val replacement = variableAddresses[label]
|
val replacement = variableAddresses[label]
|
||||||
|
val instr = chunk.instructions[line]
|
||||||
|
val offset = instr.labelSymbolOffset ?: 0
|
||||||
if(replacement==null) {
|
if(replacement==null) {
|
||||||
// it could be an address + index: symbol+42
|
// it could be an address + index: symbol+42
|
||||||
if('+' in label) {
|
if(offset>0) {
|
||||||
val (symbol, indexStr) = label.split('+')
|
val address = variableAddresses.getValue(label) + offset
|
||||||
val index = indexStr.toInt()
|
chunk.instructions[line] = instr.copy(address = address)
|
||||||
val address = variableAddresses.getValue(symbol) + index
|
|
||||||
chunk.instructions[line] = chunk.instructions[line].copy(address = address)
|
|
||||||
} else {
|
} else {
|
||||||
// placeholder is not a variable, so it must be a label of a code chunk instead
|
// 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 target: IRCodeChunk? = chunks.firstOrNull { it.label==label }
|
||||||
val opcode = chunk.instructions[line].opcode
|
|
||||||
if(target==null)
|
if(target==null)
|
||||||
throw IRParseException("placeholder not found in variables nor labels: $label")
|
throw IRParseException("placeholder not found in variables nor labels: $label")
|
||||||
else if(opcode in OpcodesThatBranch)
|
else if(instr.opcode in OpcodesThatBranch)
|
||||||
chunk.instructions[line] = chunk.instructions[line].copy(branchTarget = target, address = null)
|
chunk.instructions[line] = instr.copy(branchTarget = target, address = null)
|
||||||
else
|
else
|
||||||
throw IRParseException("vm cannot yet load a label address as a value: ${chunk.instructions[line]}")
|
throw IRParseException("vm cannot yet load a label address as a value: ${instr}")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
chunk.instructions[line] = chunk.instructions[line].copy(address = replacement)
|
chunk.instructions[line] = instr.copy(address = replacement + offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user