fix various IR file and symboltable issues

This commit is contained in:
Irmen de Jong 2023-03-06 21:42:08 +01:00
parent 8acd94fc89
commit fd07ae5225
15 changed files with 595 additions and 160 deletions

View File

@ -133,7 +133,7 @@ open class StNode(val name: String,
} }
private val scopedNameList: List<String> by lazy { private val scopedNameList: List<String> by lazy {
if(type== StNodeType.GLOBAL) if(type==StNodeType.GLOBAL)
emptyList() emptyList()
else else
parent.scopedNameList + name parent.scopedNameList + name
@ -142,7 +142,7 @@ open class StNode(val name: String,
private fun lookup(scopedName: List<String>): StNode? { private fun lookup(scopedName: List<String>): StNode? {
// a scoped name refers to a name in another namespace, and always stars from the root. // a scoped name refers to a name in another namespace, and always stars from the root.
var node = this var node = this
while(node.type!= StNodeType.GLOBAL) while(node.type!=StNodeType.GLOBAL)
node = node.parent node = node.parent
for(name in scopedName) { for(name in scopedName) {
@ -201,6 +201,11 @@ class StMemVar(name: String,
val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte val length: Int?, // for arrays: the number of elements, for strings: number of characters *including* the terminating 0-byte
astNode: PtNode) : astNode: PtNode) :
StNode(name, StNodeType.MEMVAR, astNode) { StNode(name, StNodeType.MEMVAR, astNode) {
init{
if(dt in ArrayDatatypes || dt == DataType.STR)
require(length!=null) { "memory mapped array or string must have known length" }
}
} }
class StMemorySlab( class StMemorySlab(

View File

@ -26,11 +26,11 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
PtMemMapped("P8ZP_SCRATCH_REG", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_REG, null, Position.DUMMY), PtMemMapped("P8ZP_SCRATCH_REG", DataType.UBYTE, options.compTarget.machine.zeropage.SCRATCH_REG, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_W1", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W1, null, Position.DUMMY), PtMemMapped("P8ZP_SCRATCH_W1", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W1, null, Position.DUMMY),
PtMemMapped("P8ZP_SCRATCH_W2", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W2, null, Position.DUMMY), PtMemMapped("P8ZP_SCRATCH_W2", DataType.UWORD, options.compTarget.machine.zeropage.SCRATCH_W2, null, Position.DUMMY),
PtMemMapped("P8ESTACK_LO", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_LO, 256u, Position.DUMMY), PtMemMapped("P8ESTACK_LO", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_LO, 128u, Position.DUMMY),
PtMemMapped("P8ESTACK_HI", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_HI, 256u, Position.DUMMY) PtMemMapped("P8ESTACK_HI", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_HI, 128u, Position.DUMMY)
).forEach { ).forEach {
it.parent = program it.parent = program
st.add(StMemVar(it.name, it.type, it.address, null, it)) st.add(StMemVar(it.name, it.type, it.address, it.arraySize?.toInt(), it))
} }
} }

View File

@ -265,7 +265,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val addressReg = codeGen.registers.nextFree() val addressReg = codeGen.registers.nextFree()
result += exprGen.translateExpression(call.args[0], addressReg, -1) result += exprGen.translateExpression(call.args[0], addressReg, -1)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREZI, IRDataType.WORD, reg2 = addressReg) it += IRInstruction(Opcode.STOREZI, IRDataType.WORD, reg1 = addressReg)
} }
} }
} else { } else {
@ -300,7 +300,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
val addressReg = codeGen.registers.nextFree() val addressReg = codeGen.registers.nextFree()
result += exprGen.translateExpression(call.args[0], addressReg, -1) result += exprGen.translateExpression(call.args[0], addressReg, -1)
result += IRCodeChunk(null, null).also { result += IRCodeChunk(null, null).also {
it += IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg2 = addressReg) it += IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg1 = addressReg)
} }
} }
} else { } else {

View File

@ -16,6 +16,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
require(codeGen.registers.peekNext() > resultRegister || resultRegister >= SyscallRegisterBase) { require(codeGen.registers.peekNext() > resultRegister || resultRegister >= SyscallRegisterBase) {
"no more registers for expression ${expr.position}" "no more registers for expression ${expr.position}"
} }
require((expr.type!=DataType.FLOAT && resultRegister>=0) || (expr.type==DataType.FLOAT && resultFpRegister>=0)) {
"mismatched result register for expression datatype ${expr.type} ${expr.position}"
}
return when (expr) { return when (expr) {
is PtMachineRegister -> { is PtMachineRegister -> {

View File

@ -358,6 +358,12 @@ class IRCodeGen(
} }
private fun labelFirstChunk(chunks: IRCodeChunks, label: String): IRCodeChunks { private fun labelFirstChunk(chunks: IRCodeChunks, label: String): IRCodeChunks {
if(chunks.isEmpty()) {
return listOf(
IRCodeChunk(label, null)
)
}
require(chunks.isNotEmpty() && label.isNotBlank()) require(chunks.isNotEmpty() && label.isNotBlank())
val first = chunks[0] val first = chunks[0]
if(first.label!=null) { if(first.label!=null) {
@ -883,16 +889,25 @@ class IRCodeGen(
} }
private fun translate(ifElse: PtIfElse): IRCodeChunks { private fun translate(ifElse: PtIfElse): IRCodeChunks {
if(ifElse.condition.operator !in ComparisonOperators) val condition = ifElse.condition
if(condition.operator !in ComparisonOperators)
throw AssemblyError("if condition should only be a binary comparison expression") throw AssemblyError("if condition should only be a binary comparison expression")
val signed = ifElse.condition.left.type in SignedDatatypes val signed = condition.left.type in SignedDatatypes
val irDt = irType(ifElse.condition.left.type) val irDtLeft = irType(condition.left.type)
val goto = ifElse.ifScope.children.firstOrNull() as? PtJump val goto = ifElse.ifScope.children.firstOrNull() as? PtJump
if(goto!=null && ifElse.elseScope.children.isEmpty()) { return when {
// special case the form: if <condition> goto <place> goto!=null && ifElse.elseScope.children.isEmpty() -> translateIfFollowedByJustGoto(ifElse, goto, irDtLeft, signed)
constValue(condition.right) == 0.0 -> translateIfElseZeroComparison(ifElse, irDtLeft, signed)
else -> translateIfElseNonZeroComparison(ifElse, irDtLeft, signed)
}
}
private fun translateIfFollowedByJustGoto(ifElse: PtIfElse, goto: PtJump, irDtLeft: IRDataType, signed: Boolean): MutableList<IRCodeChunkBase> {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
if(irDtLeft==IRDataType.FLOAT) {
TODO("float comparison followed by goto")
} else {
val leftRegNum = registers.nextFree() val leftRegNum = registers.nextFree()
val rightRegNum = registers.nextFree() val rightRegNum = registers.nextFree()
result += expressionEval.translateExpression(ifElse.condition.left, leftRegNum, -1) result += expressionEval.translateExpression(ifElse.condition.left, leftRegNum, -1)
@ -900,7 +915,7 @@ class IRCodeGen(
val opcode: Opcode val opcode: Opcode
val firstReg: Int val firstReg: Int
val secondReg: Int val secondReg: Int
when(ifElse.condition.operator) { when (ifElse.condition.operator) {
"==" -> { "==" -> {
opcode = Opcode.BEQ opcode = Opcode.BEQ
firstReg = leftRegNum firstReg = leftRegNum
@ -913,48 +928,93 @@ class IRCodeGen(
} }
"<" -> { "<" -> {
// swapped '>' // swapped '>'
opcode = if(signed) Opcode.BGTS else Opcode.BGT opcode = if (signed) Opcode.BGTS else Opcode.BGT
firstReg = rightRegNum firstReg = rightRegNum
secondReg = leftRegNum secondReg = leftRegNum
} }
">" -> { ">" -> {
opcode = if(signed) Opcode.BGTS else Opcode.BGT opcode = if (signed) Opcode.BGTS else Opcode.BGT
firstReg = leftRegNum firstReg = leftRegNum
secondReg = rightRegNum secondReg = rightRegNum
} }
"<=" -> { "<=" -> {
// swapped '>=' // swapped '>='
opcode = if(signed) Opcode.BGES else Opcode.BGE opcode = if (signed) Opcode.BGES else Opcode.BGE
firstReg = rightRegNum firstReg = rightRegNum
secondReg = leftRegNum secondReg = leftRegNum
} }
">=" -> { ">=" -> {
opcode = if(signed) Opcode.BGES else Opcode.BGE opcode = if (signed) Opcode.BGES else Opcode.BGE
firstReg = leftRegNum firstReg = leftRegNum
secondReg = rightRegNum secondReg = rightRegNum
} }
else -> throw AssemblyError("invalid comparison operator") else -> throw AssemblyError("invalid comparison operator")
} }
if(goto.address!=null) if (goto.address != null)
addInstr(result, IRInstruction(opcode, irDt, reg1=firstReg, reg2=secondReg, value = goto.address?.toInt()), null) addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, value = goto.address?.toInt()), null)
else if(goto.generatedLabel!=null) else if (goto.generatedLabel != null)
addInstr(result, IRInstruction(opcode, irDt, reg1=firstReg, reg2=secondReg, labelSymbol = goto.generatedLabel), null) addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.generatedLabel), null)
else else
addInstr(result, IRInstruction(opcode, irDt, reg1=firstReg, reg2=secondReg, labelSymbol = goto.identifier!!.name), null) addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.identifier!!.name), null)
}
return result return result
} }
fun translateNonZeroComparison(): IRCodeChunks { private fun translateIfElseZeroComparison(ifElse: PtIfElse, irDtLeft: IRDataType, signed: Boolean): IRCodeChunks {
if(irDtLeft==IRDataType.FLOAT) {
TODO("float zero compare via fcomp instruction")
} else {
// integer comparisons
fun equalOrNotEqualZero(elseBranch: Opcode): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>()
val leftReg = registers.nextFree()
result += expressionEval.translateExpression(ifElse.condition.left, leftReg, -1)
if(ifElse.elseScope.children.isNotEmpty()) {
// if and else parts
val elseLabel = createLabelName()
val afterIfLabel = createLabelName()
addInstr(result, IRInstruction(elseBranch, irDtLeft, reg1=leftReg, labelSymbol = elseLabel), null)
result += translateNode(ifElse.ifScope)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
result += IRCodeChunk(afterIfLabel, null)
} else {
// only if part
val afterIfLabel = createLabelName()
addInstr(result, IRInstruction(elseBranch, irDtLeft, reg1=leftReg, labelSymbol = afterIfLabel), null)
result += translateNode(ifElse.ifScope)
result += IRCodeChunk(afterIfLabel, null)
}
return result
}
return when (ifElse.condition.operator) {
"==" -> equalOrNotEqualZero(Opcode.BNZ)
"!=" -> equalOrNotEqualZero(Opcode.BZ)
"<" -> if (signed) equalOrNotEqualZero(Opcode.BGEZS) else throw AssemblyError("unsigned < 0 shouldn't occur in codegen")
">" -> if (signed) equalOrNotEqualZero(Opcode.BLEZS) else throw AssemblyError("unsigned > 0 shouldn't occur in codegen")
"<=" -> if (signed) equalOrNotEqualZero(Opcode.BGZS) else throw AssemblyError("unsigned <= 0 shouldn't occur in codegen")
">=" -> if (signed) equalOrNotEqualZero(Opcode.BLZS) else throw AssemblyError("unsigned >= 0 shouldn't occur in codegen")
else -> throw AssemblyError("weird operator")
}
}
}
private fun translateIfElseNonZeroComparison(ifElse: PtIfElse, irDtLeft: IRDataType, signed: Boolean): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>() val result = mutableListOf<IRCodeChunkBase>()
val leftRegNum = registers.nextFree()
val rightRegNum = registers.nextFree()
result += expressionEval.translateExpression(ifElse.condition.left, leftRegNum, -1)
result += expressionEval.translateExpression(ifElse.condition.right, rightRegNum, -1)
val elseBranchOpcode: Opcode val elseBranchOpcode: Opcode
val elseBranchFirstReg: Int val elseBranchFirstReg: Int
val elseBranchSecondReg: Int val elseBranchSecondReg: Int
when(ifElse.condition.operator) {
if(irDtLeft==IRDataType.FLOAT) {
TODO("float non-zero compare via fcomp instruction")
} else {
// integer comparisons
val leftRegNum = registers.nextFree()
val rightRegNum = registers.nextFree()
result += expressionEval.translateExpression(ifElse.condition.left, leftRegNum, -1)
result += expressionEval.translateExpression(ifElse.condition.right, rightRegNum, -1)
when (ifElse.condition.operator) {
"==" -> { "==" -> {
elseBranchOpcode = Opcode.BNE elseBranchOpcode = Opcode.BNE
elseBranchFirstReg = leftRegNum elseBranchFirstReg = leftRegNum
@ -967,36 +1027,37 @@ class IRCodeGen(
} }
"<" -> { "<" -> {
// else part when left >= right // else part when left >= right
elseBranchOpcode = if(signed) Opcode.BGES else Opcode.BGE elseBranchOpcode = if (signed) Opcode.BGES else Opcode.BGE
elseBranchFirstReg = leftRegNum elseBranchFirstReg = leftRegNum
elseBranchSecondReg = rightRegNum elseBranchSecondReg = rightRegNum
} }
">" -> { ">" -> {
// else part when left <= right --> right >= left // else part when left <= right --> right >= left
elseBranchOpcode = if(signed) Opcode.BGES else Opcode.BGE elseBranchOpcode = if (signed) Opcode.BGES else Opcode.BGE
elseBranchFirstReg = rightRegNum elseBranchFirstReg = rightRegNum
elseBranchSecondReg = leftRegNum elseBranchSecondReg = leftRegNum
} }
"<=" -> { "<=" -> {
// else part when left > right // else part when left > right
elseBranchOpcode = if(signed) Opcode.BGTS else Opcode.BGT elseBranchOpcode = if (signed) Opcode.BGTS else Opcode.BGT
elseBranchFirstReg = leftRegNum elseBranchFirstReg = leftRegNum
elseBranchSecondReg = rightRegNum elseBranchSecondReg = rightRegNum
} }
">=" -> { ">=" -> {
// else part when left < right --> right > left // else part when left < right --> right > left
elseBranchOpcode = if(signed) Opcode.BGTS else Opcode.BGT elseBranchOpcode = if (signed) Opcode.BGTS else Opcode.BGT
elseBranchFirstReg = rightRegNum elseBranchFirstReg = rightRegNum
elseBranchSecondReg = leftRegNum elseBranchSecondReg = leftRegNum
} }
else -> throw AssemblyError("invalid comparison operator") else -> throw AssemblyError("invalid comparison operator")
} }
if(ifElse.elseScope.children.isNotEmpty()) { if(ifElse.elseScope.children.isNotEmpty()) {
// if and else parts // if and else parts
val elseLabel = createLabelName() val elseLabel = createLabelName()
val afterIfLabel = createLabelName() val afterIfLabel = createLabelName()
addInstr(result, IRInstruction(elseBranchOpcode, irDt, reg1=elseBranchFirstReg, reg2=elseBranchSecondReg, labelSymbol = elseLabel), null) addInstr(result, IRInstruction(elseBranchOpcode, irDtLeft,
reg1=elseBranchFirstReg, reg2=elseBranchSecondReg,
labelSymbol = elseLabel), null)
result += translateNode(ifElse.ifScope) result += translateNode(ifElse.ifScope)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null) addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel) result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
@ -1004,54 +1065,17 @@ class IRCodeGen(
} else { } else {
// only if part // only if part
val afterIfLabel = createLabelName() val afterIfLabel = createLabelName()
addInstr(result, IRInstruction(elseBranchOpcode, irDt, reg1=elseBranchFirstReg, reg2=elseBranchSecondReg, labelSymbol = afterIfLabel), null) addInstr(result, IRInstruction(elseBranchOpcode, irDtLeft,
reg1=elseBranchFirstReg, reg2=elseBranchSecondReg,
labelSymbol = afterIfLabel), null)
result += translateNode(ifElse.ifScope) result += translateNode(ifElse.ifScope)
result += IRCodeChunk(afterIfLabel, null) result += IRCodeChunk(afterIfLabel, null)
} }
}
return result return result
} }
fun translateZeroComparison(): IRCodeChunks {
fun equalOrNotEqualZero(elseBranch: Opcode): IRCodeChunks {
val result = mutableListOf<IRCodeChunkBase>()
val leftReg = registers.nextFree()
result += expressionEval.translateExpression(ifElse.condition.left, leftReg, -1)
if(ifElse.elseScope.children.isNotEmpty()) {
// if and else parts
val elseLabel = createLabelName()
val afterIfLabel = createLabelName()
addInstr(result, IRInstruction(elseBranch, irDt, reg1=leftReg, labelSymbol = elseLabel), null)
result += translateNode(ifElse.ifScope)
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
result += IRCodeChunk(afterIfLabel, null)
} else {
// only if part
val afterIfLabel = createLabelName()
addInstr(result, IRInstruction(elseBranch, irDt, reg1=leftReg, labelSymbol = afterIfLabel), null)
result += translateNode(ifElse.ifScope)
result += IRCodeChunk(afterIfLabel, null)
}
return result
}
return when (ifElse.condition.operator) {
"==" -> equalOrNotEqualZero(Opcode.BNZ)
"!=" -> equalOrNotEqualZero(Opcode.BZ)
"<" -> if(signed) equalOrNotEqualZero(Opcode.BGEZS) else throw AssemblyError("unsigned < 0 shouldn't occur in codegen")
">" -> if(signed) equalOrNotEqualZero(Opcode.BLEZS) else throw AssemblyError("unsigned > 0 shouldn't occur in codegen")
"<=" -> if(signed) equalOrNotEqualZero(Opcode.BGZS) else throw AssemblyError("unsigned <= 0 shouldn't occur in codegen")
">=" -> if(signed) equalOrNotEqualZero(Opcode.BLZS) else throw AssemblyError("unsigned >= 0 shouldn't occur in codegen")
else -> throw AssemblyError("weird operator")
}
}
return if(constValue(ifElse.condition.right)==0.0)
translateZeroComparison()
else
translateNonZeroComparison()
}
private fun translate(postIncrDecr: PtPostIncrDecr): IRCodeChunks { private fun translate(postIncrDecr: PtPostIncrDecr): IRCodeChunks {
val operationMem: Opcode val operationMem: Opcode
val operationRegister: Opcode val operationRegister: Opcode

View File

@ -100,4 +100,343 @@ class TestVmCodeGen: FunSpec({
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
irChunks.size shouldBeGreaterThan 4 irChunks.size shouldBeGreaterThan 4
} }
test("float comparison expressions against zero") {
//main {
// sub start() {
// float @shared f1
//
// if f1==0
// nop
// if f1!=0
// nop
// if f1>0
// nop
// if f1<0
// nop
// }
//}
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 sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp1.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if1)
val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression("!=", DataType.UBYTE, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp2.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if2)
val if3 = PtIfElse(Position.DUMMY)
val cmp3 = PtBinaryExpression("<", DataType.UBYTE, Position.DUMMY)
cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp3.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
if3.add(cmp3)
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if3)
val if4 = PtIfElse(Position.DUMMY)
val cmp4 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY)
cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp4.add(PtNumber(DataType.FLOAT, 0.0, Position.DUMMY))
if4.add(cmp4)
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if4)
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 shouldBeGreaterThan 4
}
test("float comparison expressions against nonzero") {
//main {
// sub start() {
// float @shared f1
//
// if f1==42
// nop
// if f1!=42
// nop
// if f1>42
// nop
// if f1<42
// nop
// }
//}
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 sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp1.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if1)
val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression("!=", DataType.UBYTE, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp2.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if2)
val if3 = PtIfElse(Position.DUMMY)
val cmp3 = PtBinaryExpression("<", DataType.UBYTE, Position.DUMMY)
cmp3.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp3.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
if3.add(cmp3)
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if3)
val if4 = PtIfElse(Position.DUMMY)
val cmp4 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY)
cmp4.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp4.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
if4.add(cmp4)
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if4)
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 shouldBeGreaterThan 4
}
test("float conditional jump") {
//main {
// sub start() {
// float @shared f1
//
// if f1==42
// goto $c000
// if f1>42
// goto $c000
// }
//}
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 sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("f1", DataType.FLOAT, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp1.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
if1.add(PtNodeGroup())
sub.add(if1)
val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.f1", DataType.FLOAT, Position.DUMMY))
cmp2.add(PtNumber(DataType.FLOAT, 42.0, Position.DUMMY))
if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
if2.add(PtNodeGroup())
sub.add(if2)
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 shouldBeGreaterThan 4
}
test("integer comparison expressions against zero") {
//main {
// sub start() {
// byte @shared sb1
//
// if sb1==0
// nop
// if sb1!=0
// nop
// if sb1>0
// nop
// if sb1<0
// nop
// }
//}
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 sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.BYTE, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp1.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if1)
val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression("!=", DataType.BYTE, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp2.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if2)
val if3 = PtIfElse(Position.DUMMY)
val cmp3 = PtBinaryExpression("<", DataType.BYTE, Position.DUMMY)
cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp3.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
if3.add(cmp3)
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if3)
val if4 = PtIfElse(Position.DUMMY)
val cmp4 = PtBinaryExpression(">", DataType.BYTE, Position.DUMMY)
cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp4.add(PtNumber(DataType.BYTE, 0.0, Position.DUMMY))
if4.add(cmp4)
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if4)
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 shouldBeGreaterThan 4
}
test("integer comparison expressions against nonzero") {
//main {
// sub start() {
// byte @shared sb1
//
// if sb1==42
// nop
// if sb1!=42
// nop
// if sb1>42
// nop
// if sb1<42
// nop
// }
//}
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 sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("sb1", DataType.BYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.BYTE, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp1.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if1.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if1)
val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression("!=", DataType.BYTE, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp2.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if2.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if2)
val if3 = PtIfElse(Position.DUMMY)
val cmp3 = PtBinaryExpression("<", DataType.BYTE, Position.DUMMY)
cmp3.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp3.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
if3.add(cmp3)
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if3.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if3)
val if4 = PtIfElse(Position.DUMMY)
val cmp4 = PtBinaryExpression(">", DataType.BYTE, Position.DUMMY)
cmp4.add(PtIdentifier("main.start.sb1", DataType.BYTE, Position.DUMMY))
cmp4.add(PtNumber(DataType.BYTE, 42.0, Position.DUMMY))
if4.add(cmp4)
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
if4.add(PtNodeGroup().also { it.add(PtNop(Position.DUMMY)) })
sub.add(if4)
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 shouldBeGreaterThan 4
}
test("integer conditional jump") {
//main {
// sub start() {
// ubyte @shared ub1
//
// if ub1==42
// goto $c000
// if ub1>42
// goto $c000
// }
//}
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 sub = PtSub("start", emptyList(), null, Position.DUMMY)
sub.add(PtVariable("ub1", DataType.UBYTE, ZeropageWish.DONTCARE, null, null, Position.DUMMY))
val if1 = PtIfElse(Position.DUMMY)
val cmp1 = PtBinaryExpression("==", DataType.UBYTE, Position.DUMMY)
cmp1.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
cmp1.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY))
if1.add(cmp1)
if1.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
if1.add(PtNodeGroup())
sub.add(if1)
val if2 = PtIfElse(Position.DUMMY)
val cmp2 = PtBinaryExpression(">", DataType.UBYTE, Position.DUMMY)
cmp2.add(PtIdentifier("main.start.ub1", DataType.UBYTE, Position.DUMMY))
cmp2.add(PtNumber(DataType.UBYTE, 42.0, Position.DUMMY))
if2.add(cmp2)
if2.add(PtNodeGroup().also { it.add(PtJump(null, 0xc000u, null, Position.DUMMY)) })
if2.add(PtNodeGroup())
sub.add(if2)
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 shouldBeGreaterThan 4
}
}) })

View File

@ -1 +1 @@
8.10 8.11-dev

View File

@ -33,7 +33,7 @@ class TestLaunchEmu: FunSpec({
<INITGLOBALS> <INITGLOBALS>
</INITGLOBALS> </INITGLOBALS>
<BLOCK NAME="main" ADDRESS="null" ALIGN="NONE" POS="[unittest: line 42 col 1-9]"> <BLOCK NAME="main" ADDRESS="" ALIGN="NONE" POS="[unittest: line 42 col 1-9]">
</BLOCK> </BLOCK>
</PROGRAM> </PROGRAM>
""") """)

View File

@ -3,6 +3,14 @@ TODO
For next minor release For next minor release
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
- fix expericodegen compiler crashes:
float comparison <= , >= , == , != all crash with mismatched result register for FLOAT (cube3d-float uses this)
maze: thinks draw is unused
tehtriz: thinks sound.init routine is unused
textelite: thinks trader.do_local routine is unused , and debug_seed
- fix IR/VM: animals.p8 example somehow doesn't print the animal name in the first question, and exits after 1 animal instead of looping
- fix Github issue with X register https://github.com/irmen/prog8/issues/94
... ...

View File

@ -103,8 +103,8 @@ class IRFileReader {
"output" -> outputType = OutputType.valueOf(value) "output" -> outputType = OutputType.valueOf(value)
"launcher" -> launcher = CbmPrgLauncherType.valueOf(value) "launcher" -> launcher = CbmPrgLauncherType.valueOf(value)
"zeropage" -> zeropage = ZeropageType.valueOf(value) "zeropage" -> zeropage = ZeropageType.valueOf(value)
"loadAddress" -> loadAddress = value.toUInt() "loadAddress" -> loadAddress = parseIRValue(value).toUInt()
"evalStackBaseAddress" -> evalStackBaseAddress = if(value=="null") null else parseIRValue(value).toUInt() "evalStackBaseAddress" -> evalStackBaseAddress = if(value=="") null else parseIRValue(value).toUInt()
"zpReserved" -> { "zpReserved" -> {
val (zpstart, zpend) = value.split(',') val (zpstart, zpend) = value.split(',')
zpReserved.add(UIntRange(zpstart.toUInt(), zpend.toUInt())) zpReserved.add(UIntRange(zpstart.toUInt(), zpend.toUInt()))
@ -262,10 +262,10 @@ class IRFileReader {
emptyList() emptyList()
else { else {
val slabs = mutableListOf<StMemorySlab>() val slabs = mutableListOf<StMemorySlab>()
val slabPattern = Regex("SLAB (.+) (.+) (.+)") val slabPattern = Regex("(.+) (.+) (.+)")
text.lineSequence().forEach { line -> text.lineSequence().forEach { line ->
// example: "SLAB slabname 4096 0" // example: "slabname 4096 0"
val match = slabPattern.matchEntire(line) ?: throw IRParseException("invalid SLAB $line") val match = slabPattern.matchEntire(line) ?: throw IRParseException("invalid slab $line")
val (name, size, align) = match.destructured val (name, size, align) = match.destructured
val dummyNode = PtVariable(name, DataType.ARRAY_UB, ZeropageWish.NOT_IN_ZEROPAGE, null, null, Position.DUMMY) val dummyNode = PtVariable(name, DataType.ARRAY_UB, ZeropageWish.NOT_IN_ZEROPAGE, null, null, Position.DUMMY)
slabs.add(StMemorySlab(name, size.toUInt(), align.toUInt(), dummyNode)) slabs.add(StMemorySlab(name, size.toUInt(), align.toUInt(), dummyNode))
@ -331,7 +331,7 @@ class IRFileReader {
val attrs = start.attributes.asSequence().associate { it.name.localPart to it.value } val attrs = start.attributes.asSequence().associate { it.name.localPart to it.value }
val block = IRBlock( val block = IRBlock(
attrs.getValue("NAME"), attrs.getValue("NAME"),
if(attrs.getValue("ADDRESS")=="null") null else parseIRValue(attrs.getValue("ADDRESS")).toUInt(), if(attrs.getValue("ADDRESS")=="") null else parseIRValue(attrs.getValue("ADDRESS")).toUInt(),
IRBlock.BlockAlignment.valueOf(attrs.getValue("ALIGN")), IRBlock.BlockAlignment.valueOf(attrs.getValue("ALIGN")),
parsePosition(attrs.getValue("POS"))) parsePosition(attrs.getValue("POS")))
skipText(reader) skipText(reader)
@ -364,7 +364,7 @@ class IRFileReader {
skipText(reader) skipText(reader)
val sub = IRSubroutine(attrs.getValue("NAME"), val sub = IRSubroutine(attrs.getValue("NAME"),
parseParameters(reader), parseParameters(reader),
if(returntype=="null") null else parseDatatype(returntype, false), if(returntype=="") null else parseDatatype(returntype, false),
parsePosition(attrs.getValue("POS"))) parsePosition(attrs.getValue("POS")))
skipText(reader) skipText(reader)
@ -433,7 +433,7 @@ class IRFileReader {
} }
return IRAsmSubroutine( return IRAsmSubroutine(
attrs.getValue("NAME"), attrs.getValue("NAME"),
if(attrs.getValue("ADDRESS")=="null") null else parseIRValue(attrs.getValue("ADDRESS")).toUInt(), if(attrs.getValue("ADDRESS")=="") null else parseIRValue(attrs.getValue("ADDRESS")).toUInt(),
clobberRegs.toSet(), clobberRegs.toSet(),
params, params,
returns, returns,

View File

@ -2,6 +2,7 @@ package prog8.intermediate
import prog8.code.core.* import prog8.code.core.*
import java.nio.file.Path import java.nio.file.Path
import javax.xml.stream.XMLOutputFactory
import kotlin.io.path.bufferedWriter import kotlin.io.path.bufferedWriter
import kotlin.io.path.div import kotlin.io.path.div
@ -9,23 +10,32 @@ import kotlin.io.path.div
class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) { class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
private val outfile = outfileOverride ?: (irProgram.options.outputDir / ("${irProgram.name}.p8ir")) private val outfile = outfileOverride ?: (irProgram.options.outputDir / ("${irProgram.name}.p8ir"))
private val out = outfile.bufferedWriter(charset=Charsets.UTF_8) private val out = outfile.bufferedWriter(charset=Charsets.UTF_8)
private val xml = XMLOutputFactory.newInstance().createXMLStreamWriter(out)
private var numChunks = 0 private var numChunks = 0
private var numInstr = 0 private var numInstr = 0
fun write(): Path { fun write(): Path {
println("Writing intermediate representation to $outfile") println("Writing intermediate representation to $outfile")
out.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n") xml.writeStartDocument("utf-8", "1.0")
out.write("<PROGRAM NAME=\"${irProgram.name}\">\n") xml.writeEndDocument()
xml.writeCharacters("\n")
xml.writeStartElement("PROGRAM")
xml.writeAttribute("NAME", irProgram.name)
xml.writeCharacters("\n")
writeOptions() writeOptions()
writeAsmSymbols() writeAsmSymbols()
writeVariables() writeVariables()
xml.writeStartElement("INITGLOBALS")
out.write("\n<INITGLOBALS>\n") xml.writeCharacters("\n")
writeCodeChunk(irProgram.globalInits) writeCodeChunk(irProgram.globalInits)
out.write("</INITGLOBALS>\n") xml.writeEndElement()
xml.writeCharacters("\n\n")
writeBlocks() writeBlocks()
out.write("</PROGRAM>\n") xml.writeEndElement()
xml.writeCharacters("\n")
xml.close()
out.close() out.close()
val used = irProgram.registersUsed() val used = irProgram.registersUsed()
@ -35,14 +45,21 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
} }
private fun writeAsmSymbols() { private fun writeAsmSymbols() {
out.write("<ASMSYMBOLS>\n") xml.writeStartElement("ASMSYMBOLS")
irProgram.asmSymbols.forEach { (name, value) -> out.write("$name=$value\n" )} xml.writeCharacters("\n")
out.write("</ASMSYMBOLS>\n") irProgram.asmSymbols.forEach { (name, value) -> xml.writeCharacters("$name=$value\n" )}
xml.writeEndElement()
xml.writeCharacters("\n")
} }
private fun writeBlocks() { private fun writeBlocks() {
irProgram.blocks.forEach { block -> irProgram.blocks.forEach { block ->
out.write("\n<BLOCK NAME=\"${block.name}\" ADDRESS=\"${block.address?.toHex()}\" ALIGN=\"${block.alignment}\" POS=\"${block.position}\">\n") xml.writeStartElement("BLOCK")
xml.writeAttribute("NAME", block.name)
xml.writeAttribute("ADDRESS", block.address?.toHex() ?: "")
xml.writeAttribute("ALIGN", block.alignment.toString())
xml.writeAttribute("POS", block.position.toString())
xml.writeCharacters("\n")
block.children.forEach { child -> block.children.forEach { child ->
when(child) { when(child) {
is IRAsmSubroutine -> { is IRAsmSubroutine -> {
@ -51,25 +68,40 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
if(ret.reg.registerOrPair!=null) "${ret.reg.registerOrPair}:${ret.dt.toString().lowercase()}" if(ret.reg.registerOrPair!=null) "${ret.reg.registerOrPair}:${ret.dt.toString().lowercase()}"
else "${ret.reg.statusflag}:${ret.dt.toString().lowercase()}" else "${ret.reg.statusflag}:${ret.dt.toString().lowercase()}"
}.joinToString(",") }.joinToString(",")
out.write("<ASMSUB NAME=\"${child.label}\" ADDRESS=\"${child.address?.toHex()}\" CLOBBERS=\"$clobbers\" RETURNS=\"$returns\" POS=\"${child.position}\">\n") xml.writeStartElement("ASMSUB")
out.write("<ASMPARAMS>\n") xml.writeAttribute("NAME", child.label)
xml.writeAttribute("ADDRESS", child.address?.toHex() ?: "")
xml.writeAttribute("CLOBBERS", clobbers)
xml.writeAttribute("RETURNS", returns)
xml.writeAttribute("POS", child.position.toString())
xml.writeCharacters("\n")
xml.writeStartElement("ASMPARAMS")
xml.writeCharacters("\n")
child.parameters.forEach { ret -> child.parameters.forEach { ret ->
val reg = if(ret.reg.registerOrPair!=null) ret.reg.registerOrPair.toString() val reg = if(ret.reg.registerOrPair!=null) ret.reg.registerOrPair.toString()
else ret.reg.statusflag.toString() else ret.reg.statusflag.toString()
out.write("${ret.dt.toString().lowercase()} $reg\n") xml.writeCharacters("${ret.dt.toString().lowercase()} $reg\n")
} }
out.write("</ASMPARAMS>\n") xml.writeEndElement()
xml.writeCharacters("\n")
writeInlineAsm(child.asmChunk) writeInlineAsm(child.asmChunk)
out.write("</ASMSUB>\n") xml.writeEndElement()
xml.writeCharacters("\n\n")
} }
is IRCodeChunk -> writeCodeChunk(child) is IRCodeChunk -> writeCodeChunk(child)
is IRInlineAsmChunk -> writeInlineAsm(child) is IRInlineAsmChunk -> writeInlineAsm(child)
is IRInlineBinaryChunk -> writeInlineBytes(child) is IRInlineBinaryChunk -> writeInlineBytes(child)
is IRSubroutine -> { is IRSubroutine -> {
out.write("<SUB NAME=\"${child.label}\" RETURNTYPE=\"${child.returnType.toString().lowercase()}\" POS=\"${child.position}\">\n") xml.writeStartElement("SUB")
out.write("<PARAMS>\n") xml.writeAttribute("NAME", child.label)
child.parameters.forEach { param -> out.write("${getTypeString(param.dt)} ${param.name}\n") } xml.writeAttribute("RETURNTYPE", child.returnType?.toString()?.lowercase() ?: "")
out.write("</PARAMS>\n") xml.writeAttribute("POS", child.position.toString())
xml.writeCharacters("\n")
xml.writeStartElement("PARAMS")
xml.writeCharacters("\n")
child.parameters.forEach { param -> xml.writeCharacters("${getTypeString(param.dt)} ${param.name}\n") }
xml.writeEndElement()
xml.writeCharacters("\n")
child.chunks.forEach { chunk -> child.chunks.forEach { chunk ->
numChunks++ numChunks++
when (chunk) { when (chunk) {
@ -79,71 +111,87 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
else -> throw InternalCompilerException("invalid chunk") else -> throw InternalCompilerException("invalid chunk")
} }
} }
out.write("</SUB>\n") xml.writeEndElement()
xml.writeCharacters("\n\n")
} }
} }
} }
out.write("</BLOCK>\n") xml.writeEndElement()
xml.writeCharacters("\n\n")
} }
} }
private fun writeCodeChunk(chunk: IRCodeChunk) { private fun writeCodeChunk(chunk: IRCodeChunk) {
if(chunk.label!=null) xml.writeStartElement("CODE")
out.write("<CODE LABEL=\"${chunk.label}\">\n") chunk.label?.let { xml.writeAttribute("LABEL", chunk.label) }
else xml.writeCharacters("\n")
out.write("<CODE>\n")
chunk.instructions.forEach { instr -> chunk.instructions.forEach { instr ->
numInstr++ numInstr++
out.write(instr.toString()) xml.writeCharacters(instr.toString())
out.write("\n") xml.writeCharacters("\n")
} }
out.write("</CODE>\n") xml.writeEndElement()
xml.writeCharacters("\n")
} }
private fun writeInlineBytes(chunk: IRInlineBinaryChunk) { private fun writeInlineBytes(chunk: IRInlineBinaryChunk) {
out.write("<BYTES LABEL=\"${chunk.label ?: ""}\">\n") xml.writeStartElement("BYTES")
chunk.label?.let { xml.writeAttribute("LABEL", chunk.label) }
xml.writeCharacters("\n")
chunk.data.withIndex().forEach {(index, byte) -> chunk.data.withIndex().forEach {(index, byte) ->
out.write(byte.toString(16).padStart(2,'0')) xml.writeCharacters(byte.toString(16).padStart(2,'0'))
if(index and 63 == 63 && index < chunk.data.size-1) if(index and 63 == 63 && index < chunk.data.size-1)
out.write("\n") xml.writeCharacters("\n")
} }
out.write("\n</BYTES>\n") xml.writeEndElement()
xml.writeCharacters("\n")
} }
private fun writeInlineAsm(chunk: IRInlineAsmChunk) { private fun writeInlineAsm(chunk: IRInlineAsmChunk) {
out.write("<INLINEASM LABEL=\"${chunk.label ?: ""}\" IR=\"${chunk.isIR}\">\n") xml.writeStartElement("INLINEASM")
out.write(chunk.assembly) xml.writeAttribute("LABEL", chunk.label ?: "")
out.write("\n</INLINEASM>\n") xml.writeAttribute("IR", chunk.isIR.toString())
xml.writeCharacters("\n")
xml.writeCharacters(chunk.assembly)
xml.writeEndElement()
xml.writeCharacters("\n")
} }
private fun writeOptions() { private fun writeOptions() {
out.write("<OPTIONS>\n") xml.writeStartElement("OPTIONS")
out.write("compTarget=${irProgram.options.compTarget.name}\n") xml.writeCharacters("\n")
out.write("output=${irProgram.options.output}\n") xml.writeCharacters("compTarget=${irProgram.options.compTarget.name}\n")
out.write("launcher=${irProgram.options.launcher}\n") xml.writeCharacters("output=${irProgram.options.output}\n")
out.write("zeropage=${irProgram.options.zeropage}\n") xml.writeCharacters("launcher=${irProgram.options.launcher}\n")
xml.writeCharacters("zeropage=${irProgram.options.zeropage}\n")
for(range in irProgram.options.zpReserved) { for(range in irProgram.options.zpReserved) {
out.write("zpReserved=${range.first},${range.last}\n") xml.writeCharacters("zpReserved=${range.first},${range.last}\n")
} }
out.write("loadAddress=${irProgram.options.loadAddress.toHex()}\n") xml.writeCharacters("loadAddress=${irProgram.options.loadAddress.toHex()}\n")
out.write("optimize=${irProgram.options.optimize}\n") xml.writeCharacters("optimize=${irProgram.options.optimize}\n")
out.write("evalStackBaseAddress=${irProgram.options.evalStackBaseAddress?.toHex()}\n") xml.writeCharacters("evalStackBaseAddress=${irProgram.options.evalStackBaseAddress?.toHex() ?: ""}\n")
out.write("outputDir=${irProgram.options.outputDir.toAbsolutePath()}\n") xml.writeCharacters("outputDir=${irProgram.options.outputDir.toAbsolutePath()}\n")
// other options not yet useful here? // other options not yet useful here?
out.write("</OPTIONS>\n") xml.writeEndElement()
xml.writeCharacters("\n\n")
} }
private fun writeVariables() { private fun writeVariables() {
val (variablesNoInit, variablesWithInit) = irProgram.st.allVariables().partition { it.uninitialized } val (variablesNoInit, variablesWithInit) = irProgram.st.allVariables().partition { it.uninitialized }
out.write("\n<VARIABLESNOINIT>\n") xml.writeStartElement("VARIABLESNOINIT")
xml.writeCharacters("\n")
for (variable in variablesNoInit) { for (variable in variablesNoInit) {
val typeStr = getTypeString(variable) val typeStr = getTypeString(variable)
out.write("$typeStr ${variable.name} zp=${variable.zpwish}\n") xml.writeCharacters("$typeStr ${variable.name} zp=${variable.zpwish}\n")
} }
out.write("</VARIABLESNOINIT>\n<VARIABLESWITHINIT>\n") xml.writeEndElement()
xml.writeCharacters("\n")
xml.writeStartElement("VARIABLESWITHINIT")
xml.writeCharacters("\n")
for (variable in variablesWithInit) { for (variable in variablesWithInit) {
val typeStr = getTypeString(variable) val typeStr = getTypeString(variable)
val value: String = when(variable.dt) { val value: String = when(variable.dt) {
@ -174,19 +222,24 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
} }
else -> throw InternalCompilerException("weird dt") else -> throw InternalCompilerException("weird dt")
} }
out.write("$typeStr ${variable.name}=$value zp=${variable.zpwish}\n") xml.writeCharacters("$typeStr ${variable.name}=$value zp=${variable.zpwish}\n")
} }
out.write("</VARIABLESWITHINIT>\n") xml.writeEndElement()
xml.writeCharacters("\n")
out.write("\n<MEMORYMAPPEDVARIABLES>\n") xml.writeStartElement("MEMORYMAPPEDVARIABLES")
xml.writeCharacters("\n")
for (variable in irProgram.st.allMemMappedVariables()) { for (variable in irProgram.st.allMemMappedVariables()) {
val typeStr = getTypeString(variable) val typeStr = getTypeString(variable)
out.write("@$typeStr ${variable.name}=${variable.address.toHex()}\n") xml.writeCharacters("@$typeStr ${variable.name}=${variable.address.toHex()}\n")
} }
out.write("</MEMORYMAPPEDVARIABLES>\n") xml.writeEndElement()
xml.writeCharacters("\n")
out.write("\n<MEMORYSLABS>\n") xml.writeStartElement("MEMORYSLABS")
irProgram.st.allMemorySlabs().forEach{ slab -> out.write("SLAB ${slab.name} ${slab.size} ${slab.align}\n") } xml.writeCharacters("\n")
out.write("</MEMORYSLABS>\n") irProgram.st.allMemorySlabs().forEach{ slab -> xml.writeCharacters("${slab.name} ${slab.size} ${slab.align}\n") }
xml.writeEndElement()
xml.writeCharacters("\n")
} }
} }

View File

@ -585,10 +585,10 @@ val instructionFormats = mutableMapOf(
Opcode.SLES to InstructionFormat.from("BW,<>r1,<r2"), Opcode.SLES to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.SGE to InstructionFormat.from("BW,<>r1,<r2"), Opcode.SGE to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.SGES to InstructionFormat.from("BW,<>r1,<r2"), Opcode.SGES to InstructionFormat.from("BW,<>r1,<r2"),
Opcode.INC to InstructionFormat.from("BW,<>r1"), Opcode.INC to InstructionFormat.from("BW,<>r1 | F,<>fr1"),
Opcode.INCM to InstructionFormat.from("BW,<v"), Opcode.INCM to InstructionFormat.from("BW,<v | F,<v"),
Opcode.DEC to InstructionFormat.from("BW,<>r1"), Opcode.DEC to InstructionFormat.from("BW,<>r1 | F,<>fr1"),
Opcode.DECM to InstructionFormat.from("BW,<v"), Opcode.DECM to InstructionFormat.from("BW,<v | F,<v"),
Opcode.NEG to InstructionFormat.from("BW,<>r1 | F,<>fr1"), Opcode.NEG to InstructionFormat.from("BW,<>r1 | F,<>fr1"),
Opcode.NEGM to InstructionFormat.from("BW,<v | F,<v"), Opcode.NEGM to InstructionFormat.from("BW,<v | F,<v"),
Opcode.ADDR to InstructionFormat.from("BW,<>r1,<r2 | F,<>fr1,<fr2"), Opcode.ADDR to InstructionFormat.from("BW,<>r1,<r2 | F,<>fr1,<fr2"),

View File

@ -95,9 +95,12 @@ class IRSymbolTable(sourceSt: SymbolTable?) {
scopedName = variable.name scopedName = variable.name
varToadd = variable varToadd = variable
} else { } else {
scopedName = variable.scopedName scopedName = try {
val dummyNode = PtVariable(scopedName, variable.dt, ZeropageWish.NOT_IN_ZEROPAGE, null, null, variable.astNode.position) variable.scopedName
varToadd = StMemVar(scopedName, variable.dt, variable.address, variable.length, dummyNode) } catch (ux: UninitializedPropertyAccessException) {
variable.name
}
varToadd = StMemVar(scopedName, variable.dt, variable.address, variable.length, variable.astNode)
} }
table[scopedName] = varToadd table[scopedName] = varToadd
} }

View File

@ -47,8 +47,8 @@ compTarget=virtual
output=PRG output=PRG
launcher=BASIC launcher=BASIC
zeropage=KERNALSAFE zeropage=KERNALSAFE
loadAddress=0 loadAddress=$0000
evalStackBaseAddress=null evalStackBaseAddress=
</OPTIONS> </OPTIONS>
<ASMSYMBOLS> <ASMSYMBOLS>
@ -75,8 +75,8 @@ load.b r1,42
</CODE> </CODE>
</INITGLOBALS> </INITGLOBALS>
<BLOCK NAME="main" ADDRESS="null" ALIGN="NONE" POS="[examples/test.p8: line 2 col 2-5]"> <BLOCK NAME="main" ADDRESS="" ALIGN="NONE" POS="[examples/test.p8: line 2 col 2-5]">
<SUB NAME="main.start" RETURNTYPE="null" POS="[examples/test.p8: line 4 col 6-8]"> <SUB NAME="main.start" RETURNTYPE="" POS="[examples/test.p8: line 4 col 6-8]">
<PARAMS> <PARAMS>
</PARAMS> </PARAMS>
<CODE LABEL="main.start"> <CODE LABEL="main.start">
@ -85,8 +85,8 @@ return
</SUB> </SUB>
</BLOCK> </BLOCK>
<BLOCK NAME="sys" ADDRESS="null" ALIGN="NONE" POS="[library:/prog8lib/virtual/syslib.p8: line 3 col 2-4]"> <BLOCK NAME="sys" ADDRESS="" ALIGN="NONE" POS="[library:/prog8lib/virtual/syslib.p8: line 3 col 2-4]">
<SUB NAME="sys.wait" RETURNTYPE="null" POS="[library:/prog8lib/virtual/syslib.p8: line 15 col 6-8]"> <SUB NAME="sys.wait" RETURNTYPE="" POS="[library:/prog8lib/virtual/syslib.p8: line 15 col 6-8]">
<PARAMS> <PARAMS>
uword sys.wait.jiffies uword sys.wait.jiffies
</PARAMS> </PARAMS>

View File

@ -129,7 +129,7 @@ class TestVm: FunSpec( {
<INITGLOBALS> <INITGLOBALS>
</INITGLOBALS> </INITGLOBALS>
<BLOCK NAME="main" ADDRESS="null" ALIGN="NONE" POS="[unittest: line 42 col 1-9]"> <BLOCK NAME="main" ADDRESS="" ALIGN="NONE" POS="[unittest: line 42 col 1-9]">
</BLOCK> </BLOCK>
</PROGRAM> </PROGRAM>
""" """