mirror of
https://github.com/irmen/prog8.git
synced 2025-02-16 22:30:46 +00:00
fix various IR file and symboltable issues
This commit is contained in:
parent
8acd94fc89
commit
fd07ae5225
@ -133,7 +133,7 @@ open class StNode(val name: String,
|
||||
}
|
||||
|
||||
private val scopedNameList: List<String> by lazy {
|
||||
if(type== StNodeType.GLOBAL)
|
||||
if(type==StNodeType.GLOBAL)
|
||||
emptyList()
|
||||
else
|
||||
parent.scopedNameList + name
|
||||
@ -142,7 +142,7 @@ open class StNode(val name: String,
|
||||
private fun lookup(scopedName: List<String>): StNode? {
|
||||
// a scoped name refers to a name in another namespace, and always stars from the root.
|
||||
var node = this
|
||||
while(node.type!= StNodeType.GLOBAL)
|
||||
while(node.type!=StNodeType.GLOBAL)
|
||||
node = node.parent
|
||||
|
||||
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
|
||||
astNode: PtNode) :
|
||||
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(
|
||||
|
@ -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_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("P8ESTACK_LO", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_LO, 256u, Position.DUMMY),
|
||||
PtMemMapped("P8ESTACK_HI", DataType.ARRAY_UB, options.compTarget.machine.ESTACK_HI, 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, 128u, Position.DUMMY)
|
||||
).forEach {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -265,7 +265,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val addressReg = codeGen.registers.nextFree()
|
||||
result += exprGen.translateExpression(call.args[0], addressReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREZI, IRDataType.WORD, reg2 = addressReg)
|
||||
it += IRInstruction(Opcode.STOREZI, IRDataType.WORD, reg1 = addressReg)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -300,7 +300,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val addressReg = codeGen.registers.nextFree()
|
||||
result += exprGen.translateExpression(call.args[0], addressReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg2 = addressReg)
|
||||
it += IRInstruction(Opcode.STOREZI, IRDataType.BYTE, reg1 = addressReg)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -16,6 +16,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
require(codeGen.registers.peekNext() > resultRegister || resultRegister >= SyscallRegisterBase) {
|
||||
"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) {
|
||||
is PtMachineRegister -> {
|
||||
|
@ -358,6 +358,12 @@ class IRCodeGen(
|
||||
}
|
||||
|
||||
private fun labelFirstChunk(chunks: IRCodeChunks, label: String): IRCodeChunks {
|
||||
if(chunks.isEmpty()) {
|
||||
return listOf(
|
||||
IRCodeChunk(label, null)
|
||||
)
|
||||
}
|
||||
|
||||
require(chunks.isNotEmpty() && label.isNotBlank())
|
||||
val first = chunks[0]
|
||||
if(first.label!=null) {
|
||||
@ -883,16 +889,25 @@ class IRCodeGen(
|
||||
}
|
||||
|
||||
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")
|
||||
|
||||
val signed = ifElse.condition.left.type in SignedDatatypes
|
||||
val irDt = irType(ifElse.condition.left.type)
|
||||
|
||||
val signed = condition.left.type in SignedDatatypes
|
||||
val irDtLeft = irType(condition.left.type)
|
||||
val goto = ifElse.ifScope.children.firstOrNull() as? PtJump
|
||||
if(goto!=null && ifElse.elseScope.children.isEmpty()) {
|
||||
// special case the form: if <condition> goto <place>
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
return when {
|
||||
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>()
|
||||
if(irDtLeft==IRDataType.FLOAT) {
|
||||
TODO("float comparison followed by goto")
|
||||
} else {
|
||||
val leftRegNum = registers.nextFree()
|
||||
val rightRegNum = registers.nextFree()
|
||||
result += expressionEval.translateExpression(ifElse.condition.left, leftRegNum, -1)
|
||||
@ -900,7 +915,7 @@ class IRCodeGen(
|
||||
val opcode: Opcode
|
||||
val firstReg: Int
|
||||
val secondReg: Int
|
||||
when(ifElse.condition.operator) {
|
||||
when (ifElse.condition.operator) {
|
||||
"==" -> {
|
||||
opcode = Opcode.BEQ
|
||||
firstReg = leftRegNum
|
||||
@ -913,48 +928,93 @@ class IRCodeGen(
|
||||
}
|
||||
"<" -> {
|
||||
// swapped '>'
|
||||
opcode = if(signed) Opcode.BGTS else Opcode.BGT
|
||||
opcode = if (signed) Opcode.BGTS else Opcode.BGT
|
||||
firstReg = rightRegNum
|
||||
secondReg = leftRegNum
|
||||
}
|
||||
">" -> {
|
||||
opcode = if(signed) Opcode.BGTS else Opcode.BGT
|
||||
opcode = if (signed) Opcode.BGTS else Opcode.BGT
|
||||
firstReg = leftRegNum
|
||||
secondReg = rightRegNum
|
||||
}
|
||||
"<=" -> {
|
||||
// swapped '>='
|
||||
opcode = if(signed) Opcode.BGES else Opcode.BGE
|
||||
opcode = if (signed) Opcode.BGES else Opcode.BGE
|
||||
firstReg = rightRegNum
|
||||
secondReg = leftRegNum
|
||||
}
|
||||
">=" -> {
|
||||
opcode = if(signed) Opcode.BGES else Opcode.BGE
|
||||
opcode = if (signed) Opcode.BGES else Opcode.BGE
|
||||
firstReg = leftRegNum
|
||||
secondReg = rightRegNum
|
||||
}
|
||||
else -> throw AssemblyError("invalid comparison operator")
|
||||
}
|
||||
if(goto.address!=null)
|
||||
addInstr(result, IRInstruction(opcode, irDt, reg1=firstReg, reg2=secondReg, value = goto.address?.toInt()), null)
|
||||
else if(goto.generatedLabel!=null)
|
||||
addInstr(result, IRInstruction(opcode, irDt, reg1=firstReg, reg2=secondReg, labelSymbol = goto.generatedLabel), null)
|
||||
if (goto.address != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, value = goto.address?.toInt()), null)
|
||||
else if (goto.generatedLabel != null)
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.generatedLabel), null)
|
||||
else
|
||||
addInstr(result, IRInstruction(opcode, irDt, reg1=firstReg, reg2=secondReg, labelSymbol = goto.identifier!!.name), null)
|
||||
return result
|
||||
addInstr(result, IRInstruction(opcode, irDtLeft, reg1 = firstReg, reg2 = secondReg, labelSymbol = goto.identifier!!.name), null)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fun translateNonZeroComparison(): IRCodeChunks {
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
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 elseBranchOpcode: Opcode
|
||||
val elseBranchFirstReg: Int
|
||||
val elseBranchSecondReg: Int
|
||||
|
||||
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)
|
||||
|
||||
val elseBranchOpcode: Opcode
|
||||
val elseBranchFirstReg: Int
|
||||
val elseBranchSecondReg: Int
|
||||
when(ifElse.condition.operator) {
|
||||
when (ifElse.condition.operator) {
|
||||
"==" -> {
|
||||
elseBranchOpcode = Opcode.BNE
|
||||
elseBranchFirstReg = leftRegNum
|
||||
@ -967,36 +1027,37 @@ class IRCodeGen(
|
||||
}
|
||||
"<" -> {
|
||||
// else part when left >= right
|
||||
elseBranchOpcode = if(signed) Opcode.BGES else Opcode.BGE
|
||||
elseBranchOpcode = if (signed) Opcode.BGES else Opcode.BGE
|
||||
elseBranchFirstReg = leftRegNum
|
||||
elseBranchSecondReg = rightRegNum
|
||||
}
|
||||
">" -> {
|
||||
// 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
|
||||
elseBranchSecondReg = leftRegNum
|
||||
}
|
||||
"<=" -> {
|
||||
// else part when left > right
|
||||
elseBranchOpcode = if(signed) Opcode.BGTS else Opcode.BGT
|
||||
elseBranchOpcode = if (signed) Opcode.BGTS else Opcode.BGT
|
||||
elseBranchFirstReg = leftRegNum
|
||||
elseBranchSecondReg = rightRegNum
|
||||
}
|
||||
">=" -> {
|
||||
// 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
|
||||
elseBranchSecondReg = leftRegNum
|
||||
}
|
||||
else -> throw AssemblyError("invalid comparison operator")
|
||||
}
|
||||
|
||||
if(ifElse.elseScope.children.isNotEmpty()) {
|
||||
// if and else parts
|
||||
val elseLabel = 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)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = afterIfLabel), null)
|
||||
result += labelFirstChunk(translateNode(ifElse.elseScope), elseLabel)
|
||||
@ -1004,52 +1065,15 @@ class IRCodeGen(
|
||||
} else {
|
||||
// only if part
|
||||
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 += IRCodeChunk(afterIfLabel, null)
|
||||
}
|
||||
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()
|
||||
return result
|
||||
}
|
||||
|
||||
private fun translate(postIncrDecr: PtPostIncrDecr): IRCodeChunks {
|
||||
|
@ -100,4 +100,343 @@ class TestVmCodeGen: FunSpec({
|
||||
val irChunks = (result.irProgram.blocks.first().children.single() as IRSubroutine).chunks
|
||||
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
|
||||
}
|
||||
|
||||
})
|
@ -1 +1 @@
|
||||
8.10
|
||||
8.11-dev
|
||||
|
@ -33,7 +33,7 @@ class TestLaunchEmu: FunSpec({
|
||||
<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>
|
||||
</PROGRAM>
|
||||
""")
|
||||
|
@ -3,6 +3,14 @@ TODO
|
||||
|
||||
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
|
||||
|
||||
...
|
||||
|
||||
|
||||
|
@ -103,8 +103,8 @@ class IRFileReader {
|
||||
"output" -> outputType = OutputType.valueOf(value)
|
||||
"launcher" -> launcher = CbmPrgLauncherType.valueOf(value)
|
||||
"zeropage" -> zeropage = ZeropageType.valueOf(value)
|
||||
"loadAddress" -> loadAddress = value.toUInt()
|
||||
"evalStackBaseAddress" -> evalStackBaseAddress = if(value=="null") null else parseIRValue(value).toUInt()
|
||||
"loadAddress" -> loadAddress = parseIRValue(value).toUInt()
|
||||
"evalStackBaseAddress" -> evalStackBaseAddress = if(value=="") null else parseIRValue(value).toUInt()
|
||||
"zpReserved" -> {
|
||||
val (zpstart, zpend) = value.split(',')
|
||||
zpReserved.add(UIntRange(zpstart.toUInt(), zpend.toUInt()))
|
||||
@ -262,10 +262,10 @@ class IRFileReader {
|
||||
emptyList()
|
||||
else {
|
||||
val slabs = mutableListOf<StMemorySlab>()
|
||||
val slabPattern = Regex("SLAB (.+) (.+) (.+)")
|
||||
val slabPattern = Regex("(.+) (.+) (.+)")
|
||||
text.lineSequence().forEach { line ->
|
||||
// example: "SLAB slabname 4096 0"
|
||||
val match = slabPattern.matchEntire(line) ?: throw IRParseException("invalid SLAB $line")
|
||||
// example: "slabname 4096 0"
|
||||
val match = slabPattern.matchEntire(line) ?: throw IRParseException("invalid slab $line")
|
||||
val (name, size, align) = match.destructured
|
||||
val dummyNode = PtVariable(name, DataType.ARRAY_UB, ZeropageWish.NOT_IN_ZEROPAGE, null, null, Position.DUMMY)
|
||||
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 block = IRBlock(
|
||||
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")),
|
||||
parsePosition(attrs.getValue("POS")))
|
||||
skipText(reader)
|
||||
@ -364,7 +364,7 @@ class IRFileReader {
|
||||
skipText(reader)
|
||||
val sub = IRSubroutine(attrs.getValue("NAME"),
|
||||
parseParameters(reader),
|
||||
if(returntype=="null") null else parseDatatype(returntype, false),
|
||||
if(returntype=="") null else parseDatatype(returntype, false),
|
||||
parsePosition(attrs.getValue("POS")))
|
||||
|
||||
skipText(reader)
|
||||
@ -433,7 +433,7 @@ class IRFileReader {
|
||||
}
|
||||
return IRAsmSubroutine(
|
||||
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(),
|
||||
params,
|
||||
returns,
|
||||
|
@ -2,6 +2,7 @@ package prog8.intermediate
|
||||
|
||||
import prog8.code.core.*
|
||||
import java.nio.file.Path
|
||||
import javax.xml.stream.XMLOutputFactory
|
||||
import kotlin.io.path.bufferedWriter
|
||||
import kotlin.io.path.div
|
||||
|
||||
@ -9,23 +10,32 @@ import kotlin.io.path.div
|
||||
class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
|
||||
private val outfile = outfileOverride ?: (irProgram.options.outputDir / ("${irProgram.name}.p8ir"))
|
||||
private val out = outfile.bufferedWriter(charset=Charsets.UTF_8)
|
||||
private val xml = XMLOutputFactory.newInstance().createXMLStreamWriter(out)
|
||||
private var numChunks = 0
|
||||
private var numInstr = 0
|
||||
|
||||
|
||||
fun write(): Path {
|
||||
|
||||
println("Writing intermediate representation to $outfile")
|
||||
out.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")
|
||||
out.write("<PROGRAM NAME=\"${irProgram.name}\">\n")
|
||||
xml.writeStartDocument("utf-8", "1.0")
|
||||
xml.writeEndDocument()
|
||||
xml.writeCharacters("\n")
|
||||
xml.writeStartElement("PROGRAM")
|
||||
xml.writeAttribute("NAME", irProgram.name)
|
||||
xml.writeCharacters("\n")
|
||||
writeOptions()
|
||||
writeAsmSymbols()
|
||||
writeVariables()
|
||||
|
||||
out.write("\n<INITGLOBALS>\n")
|
||||
xml.writeStartElement("INITGLOBALS")
|
||||
xml.writeCharacters("\n")
|
||||
writeCodeChunk(irProgram.globalInits)
|
||||
out.write("</INITGLOBALS>\n")
|
||||
xml.writeEndElement()
|
||||
xml.writeCharacters("\n\n")
|
||||
writeBlocks()
|
||||
out.write("</PROGRAM>\n")
|
||||
xml.writeEndElement()
|
||||
xml.writeCharacters("\n")
|
||||
xml.close()
|
||||
out.close()
|
||||
|
||||
val used = irProgram.registersUsed()
|
||||
@ -35,14 +45,21 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
|
||||
}
|
||||
|
||||
private fun writeAsmSymbols() {
|
||||
out.write("<ASMSYMBOLS>\n")
|
||||
irProgram.asmSymbols.forEach { (name, value) -> out.write("$name=$value\n" )}
|
||||
out.write("</ASMSYMBOLS>\n")
|
||||
xml.writeStartElement("ASMSYMBOLS")
|
||||
xml.writeCharacters("\n")
|
||||
irProgram.asmSymbols.forEach { (name, value) -> xml.writeCharacters("$name=$value\n" )}
|
||||
xml.writeEndElement()
|
||||
xml.writeCharacters("\n")
|
||||
}
|
||||
|
||||
private fun writeBlocks() {
|
||||
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 ->
|
||||
when(child) {
|
||||
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()}"
|
||||
else "${ret.reg.statusflag}:${ret.dt.toString().lowercase()}"
|
||||
}.joinToString(",")
|
||||
out.write("<ASMSUB NAME=\"${child.label}\" ADDRESS=\"${child.address?.toHex()}\" CLOBBERS=\"$clobbers\" RETURNS=\"$returns\" POS=\"${child.position}\">\n")
|
||||
out.write("<ASMPARAMS>\n")
|
||||
xml.writeStartElement("ASMSUB")
|
||||
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 ->
|
||||
val reg = if(ret.reg.registerOrPair!=null) ret.reg.registerOrPair.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)
|
||||
out.write("</ASMSUB>\n")
|
||||
xml.writeEndElement()
|
||||
xml.writeCharacters("\n\n")
|
||||
}
|
||||
is IRCodeChunk -> writeCodeChunk(child)
|
||||
is IRInlineAsmChunk -> writeInlineAsm(child)
|
||||
is IRInlineBinaryChunk -> writeInlineBytes(child)
|
||||
is IRSubroutine -> {
|
||||
out.write("<SUB NAME=\"${child.label}\" RETURNTYPE=\"${child.returnType.toString().lowercase()}\" POS=\"${child.position}\">\n")
|
||||
out.write("<PARAMS>\n")
|
||||
child.parameters.forEach { param -> out.write("${getTypeString(param.dt)} ${param.name}\n") }
|
||||
out.write("</PARAMS>\n")
|
||||
xml.writeStartElement("SUB")
|
||||
xml.writeAttribute("NAME", child.label)
|
||||
xml.writeAttribute("RETURNTYPE", child.returnType?.toString()?.lowercase() ?: "")
|
||||
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 ->
|
||||
numChunks++
|
||||
when (chunk) {
|
||||
@ -79,71 +111,87 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
|
||||
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) {
|
||||
if(chunk.label!=null)
|
||||
out.write("<CODE LABEL=\"${chunk.label}\">\n")
|
||||
else
|
||||
out.write("<CODE>\n")
|
||||
xml.writeStartElement("CODE")
|
||||
chunk.label?.let { xml.writeAttribute("LABEL", chunk.label) }
|
||||
xml.writeCharacters("\n")
|
||||
chunk.instructions.forEach { instr ->
|
||||
numInstr++
|
||||
out.write(instr.toString())
|
||||
out.write("\n")
|
||||
xml.writeCharacters(instr.toString())
|
||||
xml.writeCharacters("\n")
|
||||
}
|
||||
out.write("</CODE>\n")
|
||||
xml.writeEndElement()
|
||||
xml.writeCharacters("\n")
|
||||
}
|
||||
|
||||
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) ->
|
||||
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)
|
||||
out.write("\n")
|
||||
xml.writeCharacters("\n")
|
||||
}
|
||||
out.write("\n</BYTES>\n")
|
||||
xml.writeEndElement()
|
||||
xml.writeCharacters("\n")
|
||||
}
|
||||
|
||||
private fun writeInlineAsm(chunk: IRInlineAsmChunk) {
|
||||
out.write("<INLINEASM LABEL=\"${chunk.label ?: ""}\" IR=\"${chunk.isIR}\">\n")
|
||||
out.write(chunk.assembly)
|
||||
out.write("\n</INLINEASM>\n")
|
||||
xml.writeStartElement("INLINEASM")
|
||||
xml.writeAttribute("LABEL", chunk.label ?: "")
|
||||
xml.writeAttribute("IR", chunk.isIR.toString())
|
||||
xml.writeCharacters("\n")
|
||||
xml.writeCharacters(chunk.assembly)
|
||||
xml.writeEndElement()
|
||||
xml.writeCharacters("\n")
|
||||
}
|
||||
|
||||
private fun writeOptions() {
|
||||
out.write("<OPTIONS>\n")
|
||||
out.write("compTarget=${irProgram.options.compTarget.name}\n")
|
||||
out.write("output=${irProgram.options.output}\n")
|
||||
out.write("launcher=${irProgram.options.launcher}\n")
|
||||
out.write("zeropage=${irProgram.options.zeropage}\n")
|
||||
xml.writeStartElement("OPTIONS")
|
||||
xml.writeCharacters("\n")
|
||||
xml.writeCharacters("compTarget=${irProgram.options.compTarget.name}\n")
|
||||
xml.writeCharacters("output=${irProgram.options.output}\n")
|
||||
xml.writeCharacters("launcher=${irProgram.options.launcher}\n")
|
||||
xml.writeCharacters("zeropage=${irProgram.options.zeropage}\n")
|
||||
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")
|
||||
out.write("optimize=${irProgram.options.optimize}\n")
|
||||
out.write("evalStackBaseAddress=${irProgram.options.evalStackBaseAddress?.toHex()}\n")
|
||||
out.write("outputDir=${irProgram.options.outputDir.toAbsolutePath()}\n")
|
||||
xml.writeCharacters("loadAddress=${irProgram.options.loadAddress.toHex()}\n")
|
||||
xml.writeCharacters("optimize=${irProgram.options.optimize}\n")
|
||||
xml.writeCharacters("evalStackBaseAddress=${irProgram.options.evalStackBaseAddress?.toHex() ?: ""}\n")
|
||||
xml.writeCharacters("outputDir=${irProgram.options.outputDir.toAbsolutePath()}\n")
|
||||
// other options not yet useful here?
|
||||
out.write("</OPTIONS>\n")
|
||||
xml.writeEndElement()
|
||||
xml.writeCharacters("\n\n")
|
||||
}
|
||||
|
||||
private fun writeVariables() {
|
||||
|
||||
val (variablesNoInit, variablesWithInit) = irProgram.st.allVariables().partition { it.uninitialized }
|
||||
|
||||
out.write("\n<VARIABLESNOINIT>\n")
|
||||
xml.writeStartElement("VARIABLESNOINIT")
|
||||
xml.writeCharacters("\n")
|
||||
for (variable in variablesNoInit) {
|
||||
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) {
|
||||
val typeStr = getTypeString(variable)
|
||||
val value: String = when(variable.dt) {
|
||||
@ -174,19 +222,24 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
|
||||
}
|
||||
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()) {
|
||||
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")
|
||||
irProgram.st.allMemorySlabs().forEach{ slab -> out.write("SLAB ${slab.name} ${slab.size} ${slab.align}\n") }
|
||||
out.write("</MEMORYSLABS>\n")
|
||||
xml.writeStartElement("MEMORYSLABS")
|
||||
xml.writeCharacters("\n")
|
||||
irProgram.st.allMemorySlabs().forEach{ slab -> xml.writeCharacters("${slab.name} ${slab.size} ${slab.align}\n") }
|
||||
xml.writeEndElement()
|
||||
xml.writeCharacters("\n")
|
||||
}
|
||||
}
|
@ -585,10 +585,10 @@ val instructionFormats = mutableMapOf(
|
||||
Opcode.SLES to InstructionFormat.from("BW,<>r1,<r2"),
|
||||
Opcode.SGE to InstructionFormat.from("BW,<>r1,<r2"),
|
||||
Opcode.SGES to InstructionFormat.from("BW,<>r1,<r2"),
|
||||
Opcode.INC to InstructionFormat.from("BW,<>r1"),
|
||||
Opcode.INCM to InstructionFormat.from("BW,<v"),
|
||||
Opcode.DEC to InstructionFormat.from("BW,<>r1"),
|
||||
Opcode.DECM to InstructionFormat.from("BW,<v"),
|
||||
Opcode.INC to InstructionFormat.from("BW,<>r1 | F,<>fr1"),
|
||||
Opcode.INCM to InstructionFormat.from("BW,<v | F,<v"),
|
||||
Opcode.DEC to InstructionFormat.from("BW,<>r1 | F,<>fr1"),
|
||||
Opcode.DECM to InstructionFormat.from("BW,<v | F,<v"),
|
||||
Opcode.NEG to InstructionFormat.from("BW,<>r1 | F,<>fr1"),
|
||||
Opcode.NEGM to InstructionFormat.from("BW,<v | F,<v"),
|
||||
Opcode.ADDR to InstructionFormat.from("BW,<>r1,<r2 | F,<>fr1,<fr2"),
|
||||
|
@ -95,9 +95,12 @@ class IRSymbolTable(sourceSt: SymbolTable?) {
|
||||
scopedName = variable.name
|
||||
varToadd = variable
|
||||
} else {
|
||||
scopedName = variable.scopedName
|
||||
val dummyNode = PtVariable(scopedName, variable.dt, ZeropageWish.NOT_IN_ZEROPAGE, null, null, variable.astNode.position)
|
||||
varToadd = StMemVar(scopedName, variable.dt, variable.address, variable.length, dummyNode)
|
||||
scopedName = try {
|
||||
variable.scopedName
|
||||
} catch (ux: UninitializedPropertyAccessException) {
|
||||
variable.name
|
||||
}
|
||||
varToadd = StMemVar(scopedName, variable.dt, variable.address, variable.length, variable.astNode)
|
||||
}
|
||||
table[scopedName] = varToadd
|
||||
}
|
||||
|
@ -47,8 +47,8 @@ compTarget=virtual
|
||||
output=PRG
|
||||
launcher=BASIC
|
||||
zeropage=KERNALSAFE
|
||||
loadAddress=0
|
||||
evalStackBaseAddress=null
|
||||
loadAddress=$0000
|
||||
evalStackBaseAddress=
|
||||
</OPTIONS>
|
||||
|
||||
<ASMSYMBOLS>
|
||||
@ -75,8 +75,8 @@ load.b r1,42
|
||||
</CODE>
|
||||
</INITGLOBALS>
|
||||
|
||||
<BLOCK NAME="main" ADDRESS="null" 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]">
|
||||
<BLOCK NAME="main" ADDRESS="" ALIGN="NONE" POS="[examples/test.p8: line 2 col 2-5]">
|
||||
<SUB NAME="main.start" RETURNTYPE="" POS="[examples/test.p8: line 4 col 6-8]">
|
||||
<PARAMS>
|
||||
</PARAMS>
|
||||
<CODE LABEL="main.start">
|
||||
@ -85,8 +85,8 @@ return
|
||||
</SUB>
|
||||
</BLOCK>
|
||||
|
||||
<BLOCK NAME="sys" ADDRESS="null" 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]">
|
||||
<BLOCK NAME="sys" ADDRESS="" ALIGN="NONE" POS="[library:/prog8lib/virtual/syslib.p8: line 3 col 2-4]">
|
||||
<SUB NAME="sys.wait" RETURNTYPE="" POS="[library:/prog8lib/virtual/syslib.p8: line 15 col 6-8]">
|
||||
<PARAMS>
|
||||
uword sys.wait.jiffies
|
||||
</PARAMS>
|
||||
|
@ -129,7 +129,7 @@ class TestVm: FunSpec( {
|
||||
<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>
|
||||
</PROGRAM>
|
||||
"""
|
||||
|
Loading…
x
Reference in New Issue
Block a user