mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
601 lines
21 KiB
Kotlin
601 lines
21 KiB
Kotlin
import io.kotest.assertions.throwables.shouldThrow
|
|
import io.kotest.core.spec.style.FunSpec
|
|
import io.kotest.matchers.ints.shouldBeGreaterThan
|
|
import io.kotest.matchers.shouldBe
|
|
import io.kotest.matchers.shouldNotBe
|
|
import prog8.code.core.*
|
|
import prog8.code.target.Amiga500Target
|
|
import prog8.code.target.Cx16Target
|
|
import prog8.code.target.VMTarget
|
|
import prog8.intermediate.*
|
|
import kotlin.io.path.Path
|
|
import kotlin.io.path.deleteExisting
|
|
import kotlin.io.path.readLines
|
|
|
|
class TestIRFileInOut: FunSpec({
|
|
test("IR reader requires format 4") {
|
|
shouldThrow<IRParseException> {
|
|
IRFileReader().read("""<?xml version="1.0"?><PROGRAM NAME="test" COMPILERVERSION="99.99"/>""")
|
|
}.message shouldBe "missing IRFORMAT"
|
|
shouldThrow<IRParseException> {
|
|
IRFileReader().read("""<?xml version="1.0"?><PROGRAM NAME="test" COMPILERVERSION="99.99" IRFORMAT="3"/>""")
|
|
}.message shouldBe "unsupported IR format: 3"
|
|
}
|
|
|
|
test("test IR writer") {
|
|
val target = Cx16Target()
|
|
val options = CompilationOptions.builder(target)
|
|
.output(OutputType.RAW)
|
|
.zeropage(ZeropageType.DONTUSE)
|
|
.noSysInit(true)
|
|
.compilerVersion("99.99")
|
|
.loadAddress(target.PROGRAM_LOAD_ADDRESS)
|
|
.memtopAddress(0xffffu)
|
|
.outputDir(Path(""))
|
|
.build()
|
|
val program = IRProgram("unittest-irwriter", IRSymbolTable(), options, target)
|
|
val writer = IRFileWriter(program, Path("intermediate-irwriter-test-output.p8ir"))
|
|
val generatedFile = writer.write()
|
|
val lines = generatedFile.readLines()
|
|
lines[0] shouldBe "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
|
|
lines[1] shouldBe "<PROGRAM NAME=\"unittest-irwriter\" COMPILERVERSION=\"99.99\" IRFORMAT=\"5\">"
|
|
lines.last() shouldBe "</PROGRAM>"
|
|
generatedFile.deleteExisting()
|
|
lines.size shouldBeGreaterThan 20
|
|
}
|
|
|
|
test("IR pointer arrays round-trip") {
|
|
val target = VMTarget()
|
|
val options = CompilationOptions.builder(target)
|
|
.output(OutputType.RAW)
|
|
.zeropage(ZeropageType.DONTUSE)
|
|
.noSysInit(true)
|
|
.compilerVersion("99.99")
|
|
.loadAddress(target.PROGRAM_LOAD_ADDRESS)
|
|
.memtopAddress(0xffffu)
|
|
.outputDir(Path(""))
|
|
.build()
|
|
val program = IRProgram("unittest-pointer-arrays", IRSymbolTable(), options, target)
|
|
program.st.add(IRStStaticVariable(
|
|
"main.primitive",
|
|
DataType.arrayOfPointersTo(BaseDataType.UBYTE),
|
|
null,
|
|
3u,
|
|
ZeropageWish.DONTCARE,
|
|
0u,
|
|
false
|
|
))
|
|
program.st.add(IRStStaticVariable(
|
|
"main.struct",
|
|
DataType.arrayOfPointersTo(IRSubtypePlaceholder("test.Entry")),
|
|
null,
|
|
2u,
|
|
ZeropageWish.DONTCARE,
|
|
0u,
|
|
false
|
|
))
|
|
val generatedFile = IRFileWriter(program, Path("intermediate-pointer-arrays-test-output.p8ir")).write()
|
|
try {
|
|
val lines = generatedFile.readLines()
|
|
lines.any { it.startsWith("^^ubyte[3] main.primitive") } shouldBe true
|
|
lines.any { it.startsWith("^^test.Entry[2] main.struct") } shouldBe true
|
|
val readProgram = IRFileReader().read(generatedFile)
|
|
val primitive = readProgram.st.lookup("main.primitive") as IRStStaticVariable
|
|
primitive.dt.base shouldBe BaseDataType.ARRAY
|
|
primitive.dt.sub shouldBe BaseDataType.POINTER
|
|
primitive.dt.pointeeSub shouldBe BaseDataType.UBYTE
|
|
val struct = readProgram.st.lookup("main.struct") as IRStStaticVariable
|
|
struct.dt.base shouldBe BaseDataType.ARRAY
|
|
struct.dt.sub shouldBe BaseDataType.POINTER
|
|
struct.dt.subType?.scopedNameString shouldBe "test.Entry"
|
|
} finally {
|
|
generatedFile.deleteExisting()
|
|
}
|
|
}
|
|
|
|
test("test IR reader") {
|
|
val source="""<?xml version="1.0" encoding="utf-8"?>
|
|
<PROGRAM NAME="test-ir-reader" COMPILERVERSION="99.99" IRFORMAT="4">
|
|
<OPTIONS>
|
|
compTarget=virtual
|
|
output=PRG
|
|
launcher=BASIC
|
|
zeropage=KERNALSAFE
|
|
loadAddress=$0000
|
|
</OPTIONS>
|
|
|
|
<ASMSYMBOLS>
|
|
</ASMSYMBOLS>
|
|
|
|
<VARS>
|
|
|
|
<NOINITCLEAN>
|
|
uword sys.bssvar zp=DONTCARE align=0
|
|
</NOINITCLEAN>
|
|
<NOINITDIRTY>
|
|
</NOINITDIRTY>
|
|
<INIT>
|
|
uword sys.wait.jiffies=10 zp=DONTCARE align=0
|
|
ubyte[3] sys.emptystring=0,0,0 zp=DONTCARE align=0
|
|
</INIT>
|
|
|
|
<STRUCTINSTANCESNOINIT>
|
|
</STRUCTINSTANCESNOINIT>
|
|
<STRUCTINSTANCES>
|
|
</STRUCTINSTANCES>
|
|
|
|
<CONSTANTS>
|
|
ubyte main.thing=42
|
|
</CONSTANTS>
|
|
|
|
<MEMORYMAPPED>
|
|
@uword cx16.r0=65282
|
|
</MEMORYMAPPED>
|
|
|
|
<MEMORYSLABS>
|
|
</MEMORYSLABS>
|
|
</VARS>
|
|
|
|
<INITGLOBALS>
|
|
<CHUNK><REGS>dummy</REGS><CODE>
|
|
load.b r1.b,#42.b
|
|
</CODE></CHUNK>
|
|
</INITGLOBALS>
|
|
|
|
<BLOCK NAME="main" ADDRESS="" LIBRARY="false" FORCEOUTPUT="false" NOPREFIXING="false" VERAFXMULS="false" ALIGN="NONE" POS="[examples/test.p8: line 2 col 2-5]">
|
|
<SUB NAME="main.start" RETURNS="" POS="[examples/test.p8: line 4 col 6-8]">
|
|
<PARAMS>
|
|
</PARAMS>
|
|
<CHUNK LABEL="main.start"><REGS>dummy</REGS><CODE>
|
|
return
|
|
</CODE></CHUNK>
|
|
</SUB>
|
|
</BLOCK>
|
|
|
|
<BLOCK NAME="sys" ADDRESS="" LIBRARY="false" FORCEOUTPUT="false" ALIGN="NONE" POS="[library:/prog8lib/virtual/syslib.p8: line 3 col 2-4]">
|
|
<SUB NAME="sys.wait" RETURNS="" POS="[library:/prog8lib/virtual/syslib.p8: line 15 col 6-8]">
|
|
<PARAMS>
|
|
uword sys.wait.jiffies
|
|
</PARAMS>
|
|
<ASM LABEL="sys.wait" IR="true" POS="[library:/prog8lib/virtual/syslib.p8: line 17 col 10-13]">
|
|
loadm.w r0.w,[sys.wait.jiffies]
|
|
</ASM>
|
|
<CHUNK><REGS>dummy</REGS><CODE>
|
|
return
|
|
</CODE></CHUNK>
|
|
</SUB>
|
|
</BLOCK>
|
|
</PROGRAM>
|
|
"""
|
|
val program = IRFileReader().read(source)
|
|
program.name shouldBe "test-ir-reader"
|
|
program.blocks.size shouldBe 2
|
|
program.st.allVariables().count() shouldBe 3
|
|
program.st.allConstants().count() shouldBe 1
|
|
val var1 = program.st.lookup("sys.wait.jiffies") as IRStStaticVariable
|
|
val var2 = program.st.lookup("sys.bssvar") as IRStStaticVariable
|
|
val var3 = program.st.lookup("sys.emptystring") as IRStStaticVariable
|
|
var1.uninitialized shouldBe false
|
|
var2.uninitialized shouldBe true
|
|
var3.uninitialized shouldBe true
|
|
}
|
|
|
|
test("test IR reader with struct containing pointer fields") {
|
|
val source="""<?xml version="1.0" encoding="utf-8"?>
|
|
<PROGRAM NAME="test-struct-pointer" COMPILERVERSION="99.99" IRFORMAT="4">
|
|
<OPTIONS>
|
|
compTarget=virtual
|
|
output=PRG
|
|
launcher=BASIC
|
|
zeropage=KERNALSAFE
|
|
loadAddress=$0000
|
|
</OPTIONS>
|
|
|
|
<ASMSYMBOLS>
|
|
</ASMSYMBOLS>
|
|
|
|
<VARS>
|
|
|
|
<NOINITCLEAN>
|
|
uword main.start zp=DONTCARE
|
|
</NOINITCLEAN>
|
|
|
|
<NOINITDIRTY>
|
|
</NOINITDIRTY>
|
|
|
|
<INIT>
|
|
</INIT>
|
|
|
|
<STRUCTINSTANCESNOINIT>
|
|
</STRUCTINSTANCESNOINIT>
|
|
<STRUCTINSTANCES>
|
|
re.State testinst size=8 values=uword:$0100,^^re.State:0,^^re.State:0,uword:0
|
|
</STRUCTINSTANCES>
|
|
|
|
<CONSTANTS>
|
|
</CONSTANTS>
|
|
|
|
<MEMORYMAPPED>
|
|
</MEMORYMAPPED>
|
|
|
|
<MEMORYSLABS>
|
|
</MEMORYSLABS>
|
|
</VARS>
|
|
|
|
<INITGLOBALS>
|
|
</INITGLOBALS>
|
|
|
|
<BLOCK NAME="main" ADDRESS="" LIBRARY="false" FORCEOUTPUT="false" NOPREFIXING="false" VERAFXMULS="false" ALIGN="NONE" POS="[test.p8: line 1 col 1-2]">
|
|
<SUB NAME="main.start" RETURNS="" POS="[test.p8: line 1 col 1-2]">
|
|
<PARAMS>
|
|
</PARAMS>
|
|
<CHUNK LABEL="main.start"><REGS>dummy</REGS><CODE>
|
|
load.b r1.b,#0.b
|
|
</CODE></CHUNK>
|
|
</SUB>
|
|
</BLOCK>
|
|
</PROGRAM>
|
|
"""
|
|
val program = IRFileReader().read(source)
|
|
program.name shouldBe "test-struct-pointer"
|
|
val struct = program.st.allStructInstances().first()
|
|
struct.name shouldBe "testinst"
|
|
struct.structName shouldBe "re.State"
|
|
}
|
|
|
|
test("test IR writer preserves decimal precision in float struct fields") {
|
|
// regression test: struct field floats used to be serialized via .toInt().toHex()
|
|
// which truncated the decimal portion (e.g. 1463.87 became 1463.0 in the loaded VM memory)
|
|
val target = Cx16Target()
|
|
val options = CompilationOptions.builder(target)
|
|
.output(OutputType.RAW)
|
|
.zeropage(ZeropageType.DONTUSE)
|
|
.noSysInit(true)
|
|
.compilerVersion("99.99")
|
|
.loadAddress(target.PROGRAM_LOAD_ADDRESS)
|
|
.memtopAddress(0xffffu)
|
|
.outputDir(Path(""))
|
|
.build()
|
|
val program = IRProgram("unittest-float-struct", IRSymbolTable(), options, target)
|
|
val structName = "main.Country"
|
|
val fields = listOf(
|
|
IRStStructField(DataType.forDt(BaseDataType.UWORD), "name"),
|
|
IRStStructField(DataType.forDt(BaseDataType.FLOAT), "population"),
|
|
IRStStructField(DataType.forDt(BaseDataType.UWORD), "area")
|
|
)
|
|
program.st.add(IRStStructDef(structName, fields, 8u))
|
|
val instanceName = "main.india_instance"
|
|
val population = 1463.87
|
|
val area = 3287
|
|
program.st.add(IRStStructInstance(
|
|
instanceName,
|
|
structName,
|
|
listOf(
|
|
IRStructInitValue(BaseDataType.UWORD, IRStSymbolicReference.Numeric(0.0)),
|
|
IRStructInitValue(BaseDataType.FLOAT, IRStSymbolicReference.Numeric(population)),
|
|
IRStructInitValue(BaseDataType.UWORD, IRStSymbolicReference.Numeric(area.toDouble()))
|
|
),
|
|
8u
|
|
))
|
|
val writer = IRFileWriter(program, Path("intermediate-float-struct-test-output.p8ir"))
|
|
val generatedFile = writer.write()
|
|
val program2 = IRFileReader().read(generatedFile)
|
|
generatedFile.deleteExisting()
|
|
val instance = program2.st.allStructInstances().single { it.name == instanceName }
|
|
instance.values.size shouldBe 3
|
|
val floatValue = instance.values[1].value as IRStSymbolicReference.Numeric
|
|
floatValue.value shouldBe population
|
|
val intValue = instance.values[2].value as IRStSymbolicReference.Numeric
|
|
intValue.value shouldBe area.toDouble()
|
|
}
|
|
|
|
test("test IR reader parses loadhr/storehr sN immediate encoding") {
|
|
val source="""<?xml version="1.0" encoding="utf-8"?>
|
|
<PROGRAM NAME="test-sn-immediate" COMPILERVERSION="99.99" IRFORMAT="4">
|
|
<OPTIONS>
|
|
compTarget=virtual
|
|
output=PRG
|
|
launcher=BASIC
|
|
zeropage=KERNALSAFE
|
|
loadAddress=$0000
|
|
</OPTIONS>
|
|
|
|
<ASMSYMBOLS>
|
|
</ASMSYMBOLS>
|
|
|
|
<VARS>
|
|
<NOINITCLEAN>
|
|
</NOINITCLEAN>
|
|
<NOINITDIRTY>
|
|
</NOINITDIRTY>
|
|
<INIT>
|
|
</INIT>
|
|
<STRUCTINSTANCESNOINIT>
|
|
</STRUCTINSTANCESNOINIT>
|
|
<STRUCTINSTANCES>
|
|
</STRUCTINSTANCES>
|
|
<CONSTANTS>
|
|
</CONSTANTS>
|
|
<MEMORYMAPPED>
|
|
</MEMORYMAPPED>
|
|
<MEMORYSLABS>
|
|
</MEMORYSLABS>
|
|
</VARS>
|
|
|
|
<INITGLOBALS>
|
|
</INITGLOBALS>
|
|
|
|
<BLOCK NAME="main" ADDRESS="" LIBRARY="false" FORCEOUTPUT="false" NOPREFIXING="false" VERAFXMULS="false" ALIGN="NONE" POS="[test.p8: line 1 col 1-2]">
|
|
<SUB NAME="main.start" RETURNS="" POS="[test.p8: line 1 col 1-2]">
|
|
<PARAMS>
|
|
</PARAMS>
|
|
<CHUNK LABEL="main.start"><REGS>dummy</REGS><CODE>
|
|
loadhr.b r1.b,s0.b
|
|
storehr.b r1.b,s2.b
|
|
</CODE></CHUNK>
|
|
</SUB>
|
|
</BLOCK>
|
|
</PROGRAM>
|
|
"""
|
|
val program = IRFileReader().read(source)
|
|
val sub = program.blocks.single().children.single() as IRSubroutine
|
|
val instructions = sub.chunks.flatMap { it.instructions }
|
|
instructions.size shouldBe 2
|
|
instructions[0].opcode shouldBe Opcode.LOADHR
|
|
instructions[0].type shouldBe IRDataType.BYTE
|
|
instructions[0].requireDest().registerNumber shouldBe 1
|
|
instructions[0].requireHardwareSlot() shouldBe HardwareSlotOperand(CallingConventionSlot(0), IRDataType.BYTE)
|
|
instructions[1].opcode shouldBe Opcode.STOREHR
|
|
instructions[1].type shouldBe IRDataType.BYTE
|
|
instructions[1].requireSrcA().registerNumber shouldBe 1
|
|
instructions[1].requireHardwareSlot() shouldBe HardwareSlotOperand(CallingConventionSlot(2), IRDataType.BYTE)
|
|
}
|
|
|
|
test("test IR callfar round-trip with negative amiga LVO address") {
|
|
val target = Amiga500Target()
|
|
val options = CompilationOptions.builder(target)
|
|
.zeropage(ZeropageType.DONTUSE)
|
|
.noSysInit(true)
|
|
.compilerVersion("99.99")
|
|
.loadAddress(target.PROGRAM_LOAD_ADDRESS)
|
|
.memtopAddress(target.PROGRAM_MEMTOP_ADDRESS)
|
|
.outputDir(Path(""))
|
|
.quiet(true)
|
|
.build()
|
|
val program = IRProgram("unittest-callfar-lvo", IRSymbolTable(), options, target)
|
|
val block = IRBlock("main", library=false, IRBlock.Options(), Position("unittest", 1, 1, 1))
|
|
val sub = IRSubroutine("main.start", emptyList(), emptyList(), Position("unittest", 1, 1, 1))
|
|
val chunk = IRCodeChunk("main.start", null)
|
|
chunk.instructions += IRInstructions.call(
|
|
Opcode.CALLFAR,
|
|
CallSite(CallTarget.AmigaLibrary(1, -552))
|
|
)
|
|
sub += chunk
|
|
block += sub
|
|
program.addBlock(block)
|
|
|
|
val generatedFile = IRFileWriter(program, Path("intermediate-callfar-test-output.p8ir")).write()
|
|
val readProgram = IRFileReader().read(generatedFile)
|
|
generatedFile.deleteExisting()
|
|
|
|
val readSub = readProgram.blocks.single().children.single() as IRSubroutine
|
|
val instr = readSub.chunks.flatMap { it.instructions }.single()
|
|
instr.opcode shouldBe Opcode.CALLFAR
|
|
instr.requireCallSite().target shouldBe CallTarget.AmigaLibrary(1, -552)
|
|
}
|
|
|
|
test("test IR file round-trip retains structured call details") {
|
|
val target = Cx16Target()
|
|
val options = CompilationOptions.builder(target)
|
|
.output(OutputType.RAW)
|
|
.zeropage(ZeropageType.DONTUSE)
|
|
.noSysInit(true)
|
|
.compilerVersion("99.99")
|
|
.loadAddress(target.PROGRAM_LOAD_ADDRESS)
|
|
.memtopAddress(0xffffu)
|
|
.outputDir(Path(""))
|
|
.build()
|
|
val program = IRProgram("unittest-structured-calls", IRSymbolTable(), options, target)
|
|
val block = IRBlock("main", library = false, IRBlock.Options(), Position.DUMMY)
|
|
val start = IRSubroutine("main.start", emptyList(), emptyList(), Position.DUMMY)
|
|
val startChunk = IRCodeChunk("main.start", null)
|
|
startChunk += IRInstructions.call(
|
|
CallSite(
|
|
CallTarget.Direct(codeLabel("main.callee")),
|
|
arguments = listOf(Calls.argument(1, IRDataType.BYTE, CallLocation.ParameterMemory("main.callee.arg")))
|
|
)
|
|
)
|
|
startChunk += IRInstructions.call(
|
|
CallSite(
|
|
CallTarget.Direct(codeAddress(0xffd2u), "kernal.chrout"),
|
|
arguments = listOf(Calls.argument(1, IRDataType.BYTE, CallLocation.HardwareRegister(CallingConventionSlot(0)))),
|
|
results = listOf(
|
|
Calls.result(2, IRDataType.BYTE, CallLocation.HardwareRegister(CallingConventionSlot(0))),
|
|
CallResult(null, CallLocation.StatusFlag(Statusflag.Pc))
|
|
),
|
|
effects = CallEffects(MemoryEffect.READ, StatusEffect.READS)
|
|
)
|
|
)
|
|
startChunk += IRInstructions.returnVoid()
|
|
start += startChunk
|
|
val callee = IRSubroutine("main.callee", emptyList(), emptyList(), Position.DUMMY)
|
|
val calleeChunk = IRCodeChunk("main.callee", null)
|
|
calleeChunk += IRInstructions.returnVoid()
|
|
callee += calleeChunk
|
|
block += start
|
|
block += callee
|
|
program.addBlock(block)
|
|
|
|
val generatedFile = IRFileWriter(program, Path("intermediate-structured-calls-test-output.p8ir")).write()
|
|
val readProgram = IRFileReader().read(generatedFile)
|
|
generatedFile.deleteExisting()
|
|
|
|
val calls = (readProgram.blocks.single().children[0] as IRSubroutine)
|
|
.chunks.flatMap { it.instructions }.filter { it.opcode == Opcode.CALL }
|
|
calls[0].requireCallSite().arguments.single().location shouldBe CallLocation.ParameterMemory("main.callee.arg")
|
|
val asmCall = calls[1].requireCallSite()
|
|
(asmCall.target as CallTarget.Direct).externalName shouldBe "kernal.chrout"
|
|
asmCall.results[0].location shouldBe CallLocation.HardwareRegister(CallingConventionSlot(0))
|
|
asmCall.results[1] shouldBe CallResult(null, CallLocation.StatusFlag(Statusflag.Pc))
|
|
asmCall.effects shouldBe CallEffects(MemoryEffect.READ, StatusEffect.READS)
|
|
}
|
|
|
|
test("test IR reader parses block-level CHUNK (label and align)") {
|
|
val source="""<?xml version="1.0" encoding="utf-8"?>
|
|
<PROGRAM NAME="test-block-level-chunk" COMPILERVERSION="99.99" IRFORMAT="4">
|
|
<OPTIONS>
|
|
compTarget=virtual
|
|
output=PRG
|
|
launcher=BASIC
|
|
zeropage=KERNALSAFE
|
|
loadAddress=$0000
|
|
</OPTIONS>
|
|
|
|
<ASMSYMBOLS>
|
|
</ASMSYMBOLS>
|
|
|
|
<VARS>
|
|
<NOINITCLEAN>
|
|
</NOINITCLEAN>
|
|
<NOINITDIRTY>
|
|
</NOINITDIRTY>
|
|
<INIT>
|
|
</INIT>
|
|
<STRUCTINSTANCESNOINIT>
|
|
</STRUCTINSTANCESNOINIT>
|
|
<STRUCTINSTANCES>
|
|
</STRUCTINSTANCES>
|
|
<CONSTANTS>
|
|
</CONSTANTS>
|
|
<MEMORYMAPPED>
|
|
</MEMORYMAPPED>
|
|
<MEMORYSLABS>
|
|
</MEMORYSLABS>
|
|
</VARS>
|
|
|
|
<INITGLOBALS>
|
|
</INITGLOBALS>
|
|
|
|
<BLOCK NAME="main" ADDRESS="" LIBRARY="false" POS="[test.p8: line 1 col 1-2]">
|
|
<CHUNK><REGS><![CDATA[]]></REGS><CODE>
|
|
align #$100.l
|
|
</CODE></CHUNK>
|
|
<CHUNK LABEL="main.mylabel"><REGS><![CDATA[]]></REGS><CODE>
|
|
</CODE></CHUNK>
|
|
<SUB NAME="main.start" RETURNS="" POS="[test.p8: line 2 col 2-4]">
|
|
<PARAMS>
|
|
</PARAMS>
|
|
<CHUNK LABEL="main.start"><REGS><![CDATA[]]></REGS><CODE>
|
|
return
|
|
</CODE></CHUNK>
|
|
</SUB>
|
|
</BLOCK>
|
|
</PROGRAM>
|
|
"""
|
|
val program = IRFileReader().read(source)
|
|
val block = program.blocks.single()
|
|
// 1 align chunk + 1 label chunk + 1 sub
|
|
block.children.size shouldBe 3
|
|
val alignChunk = block.children[0] as IRCodeChunk
|
|
alignChunk.label shouldBe null
|
|
alignChunk.instructions.size shouldBe 1
|
|
alignChunk.instructions[0].opcode shouldBe Opcode.ALIGN
|
|
alignChunk.instructions[0].requireImmediateInt() shouldBe 256
|
|
val labelChunk = block.children[1] as IRCodeChunk
|
|
labelChunk.label shouldBe "main.mylabel"
|
|
}
|
|
|
|
test("test IR union round-trip preserves isUnion and size") {
|
|
val target = Cx16Target()
|
|
val options = CompilationOptions.builder(target)
|
|
.output(OutputType.RAW)
|
|
.zeropage(ZeropageType.DONTUSE)
|
|
.noSysInit(true)
|
|
.compilerVersion("99.99")
|
|
.loadAddress(target.PROGRAM_LOAD_ADDRESS)
|
|
.memtopAddress(0xffffu)
|
|
.outputDir(Path(""))
|
|
.build()
|
|
val program = IRProgram("unittest-union-roundtrip", IRSymbolTable(), options, target)
|
|
val structName = "main.MyUnion"
|
|
val fields = listOf(
|
|
IRStStructField(DataType.forDt(BaseDataType.UBYTE), "byteField"),
|
|
IRStStructField(DataType.forDt(BaseDataType.UWORD), "wordField")
|
|
)
|
|
program.st.add(IRStStructDef(structName, fields, 2u, true))
|
|
program.st.add(IRStStructInstance("main.myUnion", structName, emptyList(), 2u))
|
|
val writer = IRFileWriter(program, Path("intermediate-union-roundtrip-test-output.p8ir"))
|
|
val generatedFile = writer.write()
|
|
val program2 = IRFileReader().read(generatedFile)
|
|
generatedFile.deleteExisting()
|
|
val def = program2.st.lookup(structName) as IRStStructDef
|
|
def.isUnion shouldBe true
|
|
def.size shouldBe 2u
|
|
def.fields.size shouldBe 2
|
|
val instance = program2.st.lookup("main.myUnion") as IRStStructInstance
|
|
instance.size shouldBe 2u
|
|
}
|
|
|
|
test("test IR pointer array round-trip preserves ARRAY+POINTER+pointeeSub") {
|
|
val target = Amiga500Target()
|
|
val options = CompilationOptions.builder(target)
|
|
.zeropage(ZeropageType.DONTUSE)
|
|
.noSysInit(true)
|
|
.compilerVersion("99.99")
|
|
.loadAddress(target.PROGRAM_LOAD_ADDRESS)
|
|
.memtopAddress(target.PROGRAM_MEMTOP_ADDRESS)
|
|
.outputDir(Path(""))
|
|
.quiet(true)
|
|
.build()
|
|
val program = IRProgram("unittest-pointer-array-roundtrip", IRSymbolTable(), options, target)
|
|
val structDef = IRStStructDef(
|
|
"re.State",
|
|
listOf(IRStStructField(DataType.forDt(BaseDataType.UWORD), "value")),
|
|
2u
|
|
)
|
|
program.st.add(structDef)
|
|
program.st.add(IRStStaticVariable(
|
|
"main.primptrs",
|
|
DataType.arrayOfPointersTo(BaseDataType.UBYTE),
|
|
null,
|
|
4u,
|
|
ZeropageWish.DONTCARE,
|
|
0u,
|
|
false
|
|
))
|
|
program.st.add(IRStStaticVariable(
|
|
"main.stateptrs",
|
|
DataType.arrayOfPointersTo(IRStructSubtype(structDef)),
|
|
null,
|
|
2u,
|
|
ZeropageWish.DONTCARE,
|
|
0u,
|
|
false
|
|
))
|
|
|
|
val generatedFile = IRFileWriter(program, Path("intermediate-pointer-array-roundtrip-test-output.p8ir")).write()
|
|
val lines = generatedFile.readLines()
|
|
val readProgram = IRFileReader().read(generatedFile)
|
|
generatedFile.deleteExisting()
|
|
|
|
lines.any { "^^ubyte[4]" in it } shouldBe true
|
|
lines.any { "^^re.State[2]" in it } shouldBe true
|
|
|
|
val prim = readProgram.st.lookup("main.primptrs") as IRStStaticVariable
|
|
prim.dt.isPointerArray shouldBe true
|
|
prim.dt.base shouldBe BaseDataType.ARRAY
|
|
prim.dt.sub shouldBe BaseDataType.POINTER
|
|
prim.dt.pointeeSub shouldBe BaseDataType.UBYTE
|
|
prim.length shouldBe 4u
|
|
|
|
val state = readProgram.st.lookup("main.stateptrs") as IRStStaticVariable
|
|
state.dt.isPointerArray shouldBe true
|
|
state.dt.base shouldBe BaseDataType.ARRAY
|
|
state.dt.sub shouldBe BaseDataType.POINTER
|
|
state.dt.pointeeSub shouldBe BaseDataType.STRUCT_INSTANCE
|
|
state.dt.subType shouldNotBe null
|
|
(state.dt.subType as IRStructSubtype).def.name shouldBe "re.State"
|
|
state.length shouldBe 2u
|
|
}
|
|
})
|