From b22804efaffd1b6ca10976e45ad898b630d508ab Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 31 Oct 2022 23:59:33 +0100 Subject: [PATCH] ir: fix inlineasm linking --- .../prog8/codegen/intermediate/IRCodeGen.kt | 10 +++++-- .../compiler/astprocessing/AstChecker.kt | 1 - compiler/test/TestCompilerOptionLibdirs.kt | 2 +- compiler/test/ast/TestVarious.kt | 1 - compiler/test/helpers_pathsTests.kt | 16 +++++----- compiler/test/vm/TestCompilerVirtual.kt | 30 +++++++++++++++++++ .../src/prog8/intermediate/IRFileReader.kt | 6 ++-- .../src/prog8/intermediate/IRFileWriter.kt | 5 ++-- .../src/prog8/intermediate/IRProgram.kt | 22 +++++++------- virtualmachine/test/TestVm.kt | 5 ++-- 10 files changed, 64 insertions(+), 34 deletions(-) diff --git a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt index 64e09c67b..912f0f4e5 100644 --- a/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt +++ b/codeGenIntermediate/src/prog8/codegen/intermediate/IRCodeGen.kt @@ -1083,13 +1083,17 @@ class IRCodeGen( } is PtAsmSub -> { val assemblyChild = if(child.children.isEmpty()) null else (child.children.single() as PtInlineAssembly) + val asmChunk = IRInlineAsmChunk( + child.name, assemblyChild?.assembly ?: "", assemblyChild?.isIR==true, child.position, null + ) irBlock += IRAsmSubroutine( - child.name, child.position, child.address, + child.name, + child.address, child.clobbers, child.parameters.map { Pair(it.first.type, it.second) }, // note: the name of the asmsub param is not used anymore. child.returnTypes.zip(child.retvalRegisters), - assemblyChild?.isIR==true, - assemblyChild?.assembly ?: "" + asmChunk, + child.position ) } is PtInlineAssembly -> { diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index 670cb71b9..740963d67 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -7,7 +7,6 @@ import prog8.ast.expressions.* import prog8.ast.statements.* import prog8.ast.walk.IAstVisitor import prog8.code.core.* -import prog8.code.target.VMTarget import prog8.compiler.BuiltinFunctions import prog8.compiler.InplaceModifyingBuiltinFunctions import prog8.compiler.builtinFunctionReturnType diff --git a/compiler/test/TestCompilerOptionLibdirs.kt b/compiler/test/TestCompilerOptionLibdirs.kt index ec46dc77c..ed408c777 100644 --- a/compiler/test/TestCompilerOptionLibdirs.kt +++ b/compiler/test/TestCompilerOptionLibdirs.kt @@ -87,7 +87,7 @@ class TestCompilerOptionSourcedirs: FunSpec({ test("testFilePathOutsideWorkingDirRelativeTo1stInSourcedirs") { val filepath = assumeReadableFile(fixturesDir, "ast_simple_main.p8") - val sourcedirs = listOf("${fixturesDir}") + val sourcedirs = listOf("$fixturesDir") compileFile(filepath.fileName, sourcedirs) shouldNotBe null } diff --git a/compiler/test/ast/TestVarious.kt b/compiler/test/ast/TestVarious.kt index dd58614b5..2fe8d7b60 100644 --- a/compiler/test/ast/TestVarious.kt +++ b/compiler/test/ast/TestVarious.kt @@ -10,7 +10,6 @@ import prog8.ast.statements.InlineAssembly import prog8.ast.statements.VarDecl import prog8.code.core.Position import prog8.code.target.C64Target -import prog8.compiler.printProgram import prog8tests.helpers.compileText class TestVarious: FunSpec({ diff --git a/compiler/test/helpers_pathsTests.kt b/compiler/test/helpers_pathsTests.kt index 46964b5c4..e8f814ebc 100644 --- a/compiler/test/helpers_pathsTests.kt +++ b/compiler/test/helpers_pathsTests.kt @@ -57,7 +57,7 @@ class PathsHelpersTests: FunSpec({ test("on existing directory") { shouldThrow { - assumeNotExists("${fixturesDir}") + assumeNotExists("$fixturesDir") } } } @@ -157,13 +157,13 @@ class PathsHelpersTests: FunSpec({ context("WithStringAndStringArgs") { test("on non-existing path") { shouldThrow { - assumeDirectory("${fixturesDir}", "i_do_not_exist") + assumeDirectory("$fixturesDir", "i_do_not_exist") } } test("on existing file") { shouldThrow { - assumeDirectory("${fixturesDir}", "ast_simple_main.p8") + assumeDirectory("$fixturesDir", "ast_simple_main.p8") } } @@ -178,13 +178,13 @@ class PathsHelpersTests: FunSpec({ context("WithStringAndPathArgs") { test("on non-existing path") { shouldThrow { - assumeDirectory("${fixturesDir}", Path("i_do_not_exist")) + assumeDirectory("$fixturesDir", Path("i_do_not_exist")) } } test("on existing file") { shouldThrow { - assumeDirectory("${fixturesDir}", Path("ast_simple_main.p8")) + assumeDirectory("$fixturesDir", Path("ast_simple_main.p8")) } } @@ -240,7 +240,7 @@ class PathsHelpersTests: FunSpec({ test("on directory") { shouldThrow { - assumeReadableFile("${fixturesDir}") + assumeReadableFile("$fixturesDir") } } } @@ -289,7 +289,7 @@ class PathsHelpersTests: FunSpec({ context("WithStringAndStringArgs") { test("on non-existing path") { shouldThrow { - assumeReadableFile("${fixturesDir}", "i_do_not_exist") + assumeReadableFile("$fixturesDir", "i_do_not_exist") } } @@ -301,7 +301,7 @@ class PathsHelpersTests: FunSpec({ test("on directory") { shouldThrow { - assumeReadableFile("${fixturesDir}", "..") + assumeReadableFile("$fixturesDir", "..") } } } diff --git a/compiler/test/vm/TestCompilerVirtual.kt b/compiler/test/vm/TestCompilerVirtual.kt index 5cba906ed..29c8d9c52 100644 --- a/compiler/test/vm/TestCompilerVirtual.kt +++ b/compiler/test/vm/TestCompilerVirtual.kt @@ -1,8 +1,10 @@ package prog8tests.vm +import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe +import io.kotest.matchers.string.shouldContain import prog8.ast.expressions.BuiltinFunctionCall import prog8.ast.statements.Assignment import prog8.code.target.C64Target @@ -212,4 +214,32 @@ main { vm.stepCount shouldBe 49 } } + + test("asmsub for virtual target") { + val src = """ +main { + sub start() { + void test(42) + } + + asmsub test(ubyte xx @A) -> ubyte @Y { + %asm {{ + lda #99 + tay + rts + return + }} + } +}""" + val othertarget = Cx16Target() + compileText(othertarget, true, src, writeAssembly = true, keepIR=true) shouldNotBe null + + val target = VMTarget() + val result = compileText(target, false, src, writeAssembly = true)!! + val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8ir") + val exc = shouldThrow { + VmRunner().runProgram(virtfile.readText()) + } + exc.message shouldContain("does not support asmsubs") + } }) \ No newline at end of file diff --git a/intermediate/src/prog8/intermediate/IRFileReader.kt b/intermediate/src/prog8/intermediate/IRFileReader.kt index 2ab1e7375..5486b36fc 100644 --- a/intermediate/src/prog8/intermediate/IRFileReader.kt +++ b/intermediate/src/prog8/intermediate/IRFileReader.kt @@ -368,12 +368,12 @@ class IRFileReader { } return IRAsmSubroutine( scopedname, - parsePosition(pos), if(address=="null") null else address.toUInt(), + if(address=="null") null else address.toUInt(), clobberRegs.toSet(), params, returns, - asm.isIR, - asm.assembly + asm, + parsePosition(pos) ) } diff --git a/intermediate/src/prog8/intermediate/IRFileWriter.kt b/intermediate/src/prog8/intermediate/IRFileWriter.kt index 0f653c39f..463299780 100644 --- a/intermediate/src/prog8/intermediate/IRFileWriter.kt +++ b/intermediate/src/prog8/intermediate/IRFileWriter.kt @@ -81,9 +81,8 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) { out.write("${dt.toString().lowercase()} $reg\n") } out.write("\n") - out.write("\n") - out.write(it.assembly) - out.write("\n\n\n") + writeInlineAsm(it.asmChunk) + out.write("\n") } out.write("\n") } diff --git a/intermediate/src/prog8/intermediate/IRProgram.kt b/intermediate/src/prog8/intermediate/IRProgram.kt index a5f28a83d..d1a570446 100644 --- a/intermediate/src/prog8/intermediate/IRProgram.kt +++ b/intermediate/src/prog8/intermediate/IRProgram.kt @@ -69,7 +69,12 @@ class IRProgram(val name: String, } fun linkChunks() { - val labeledChunks = blocks.flatMap { it.subroutines }.flatMap { it.chunks }.associateBy { it.label } + fun getLabeledChunks(): Map { + return blocks.flatMap { it.subroutines }.flatMap { it.chunks }.associateBy { it.label } + + blocks.flatMap { it.asmSubroutines }.map { it.asmChunk }.associateBy { it.label } + } + + val labeledChunks = getLabeledChunks() if(globalInits.isNotEmpty()) { if(globalInits.next==null) { @@ -109,10 +114,8 @@ class IRProgram(val name: String, // link all jump and branching instructions to their target chunk.instructions.forEach { - if(it.opcode in OpcodesThatBranch && it.opcode!=Opcode.RETURN && it.labelSymbol!=null) { - val targetChunk = labeledChunks.getValue(it.labelSymbol) - it.branchTarget = targetChunk - } + if(it.opcode in OpcodesThatBranch && it.opcode!=Opcode.RETURN && it.labelSymbol!=null) + it.branchTarget = labeledChunks.getValue(it.labelSymbol) // note: branches with an address value cannot be linked to something... } } @@ -240,22 +243,19 @@ class IRSubroutine(val name: String, class IRAsmSubroutine( val name: String, - val position: Position, val address: UInt?, val clobbers: Set, val parameters: List>, val returns: List>, - val isIR: Boolean, - val assembly: String + val asmChunk: IRInlineAsmChunk, + val position: Position ) { init { require('.' in name) { "subroutine name is not scoped: $name" } require(!name.startsWith("main.main.")) { "subroutine name invalid main prefix: $name" } - require(!assembly.startsWith('\n') && !assembly.startsWith('\r')) { "inline assembly should be trimmed" } - require(!assembly.endsWith('\n') && !assembly.endsWith('\r')) { "inline assembly should be trimmed" } } - private val registersUsed by lazy { registersUsedInAssembly(isIR, assembly) } + private val registersUsed by lazy { registersUsedInAssembly(asmChunk.isIR, asmChunk.assembly) } fun usedRegisters() = registersUsed } diff --git a/virtualmachine/test/TestVm.kt b/virtualmachine/test/TestVm.kt index a170d1f57..2061e17f9 100644 --- a/virtualmachine/test/TestVm.kt +++ b/virtualmachine/test/TestVm.kt @@ -87,13 +87,12 @@ class TestVm: FunSpec( { val block = IRBlock("main", null, IRBlock.BlockAlignment.NONE, Position.DUMMY) val startSub = IRAsmSubroutine( "main.asmstart", - Position.DUMMY, 0x2000u, emptySet(), emptyList(), emptyList(), - true, - "inlined asm here" + IRInlineAsmChunk("main.asmstart", "inlined asm here", true, Position.DUMMY, null), + Position.DUMMY ) block += startSub program.addBlock(block)