From c09b1395f60f2ebbde3f7b01b38d4be8c4f8a92d Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Wed, 1 Oct 2025 22:49:51 +0200 Subject: [PATCH] IR: add a unit test for the SSA basic block change --- compiler/test/vm/TestCompilerVirtual.kt | 35 ++++++++++++++++++++++--- docs/source/todo.rst | 1 - examples/test.p8 | 8 +++++- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/compiler/test/vm/TestCompilerVirtual.kt b/compiler/test/vm/TestCompilerVirtual.kt index 183036d71..eed87bb82 100644 --- a/compiler/test/vm/TestCompilerVirtual.kt +++ b/compiler/test/vm/TestCompilerVirtual.kt @@ -3,6 +3,8 @@ package prog8tests.vm import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.FunSpec import io.kotest.engine.spec.tempdir +import io.kotest.matchers.collections.shouldBeIn +import io.kotest.matchers.ints.shouldBeLessThanOrEqual import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe import io.kotest.matchers.string.shouldContain @@ -12,10 +14,7 @@ import prog8.ast.statements.Assignment import prog8.code.target.C64Target import prog8.code.target.Cx16Target import prog8.code.target.VMTarget -import prog8.intermediate.IRDataType -import prog8.intermediate.IRFileReader -import prog8.intermediate.IRSubroutine -import prog8.intermediate.Opcode +import prog8.intermediate.* import prog8.vm.VmRunner import prog8tests.helpers.ErrorReporterForTests import prog8tests.helpers.compileText @@ -558,4 +557,32 @@ main { }""" compileText(VMTarget(), false, src, outputDir) shouldNotBe null } + + test("SSA basic blocks") { + val src= """ +main { + sub start() { + func() + } + + sub func() { + if cx16.r0<10 or cx16.r0>319 { + cx16.r1++ + } + } +}""" + val result = compileText(VMTarget(), false, src, outputDir)!! + val virtfile = result.compilationOptions.outputDir.resolve(result.compilerAst.name + ".p8ir") + val irProgram = IRFileReader().read(virtfile) + val func = irProgram.blocks[0].children[1] as IRSubroutine + func.label shouldBe "main.func" + func.chunks.size shouldBe 11 + for(chunk in func.chunks) { + if(chunk.next == null) { + chunk.instructions.last().opcode shouldBeIn OpcodesThatBranchUnconditionally + } + val branches = chunk.instructions.filter { it.opcode in OpcodesThatEndSSAblock } + branches.size shouldBeLessThanOrEqual 1 + } + } }) \ No newline at end of file diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 4ddb4df38..7220d4f8f 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -50,7 +50,6 @@ Future Things and Ideas IR/VM ----- -- TestVMCodegen: add tests for SSA block split/joins/check last instruction to be a valid SSE block ender - is it possible to use LOADFIELD/STOREFIELD instructions even more? - make multiple classes of registers and maybe also categorize by life time , to prepare for better register allocation in the future SYSCALL_ARGS, // Reserved for syscall arguments (r99000-99099, r99100-99199) diff --git a/examples/test.p8 b/examples/test.p8 index 68ba799c4..9c6d8aafa 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,5 +1,11 @@ main { sub start() { - cx16.r0L= sqrt(cx16.r1) + func() + } + + sub func() { + if cx16.r0<10 or cx16.r0>319 { + cx16.r1++ + } } }