ir: fix inlineasm linking

This commit is contained in:
Irmen de Jong 2022-10-31 23:59:33 +01:00
parent 890f55f91a
commit b22804efaf
10 changed files with 64 additions and 34 deletions

View File

@ -1083,13 +1083,17 @@ class IRCodeGen(
} }
is PtAsmSub -> { is PtAsmSub -> {
val assemblyChild = if(child.children.isEmpty()) null else (child.children.single() as PtInlineAssembly) 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( irBlock += IRAsmSubroutine(
child.name, child.position, child.address, child.name,
child.address,
child.clobbers, child.clobbers,
child.parameters.map { Pair(it.first.type, it.second) }, // note: the name of the asmsub param is not used anymore. 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), child.returnTypes.zip(child.retvalRegisters),
assemblyChild?.isIR==true, asmChunk,
assemblyChild?.assembly ?: "" child.position
) )
} }
is PtInlineAssembly -> { is PtInlineAssembly -> {

View File

@ -7,7 +7,6 @@ import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.ast.walk.IAstVisitor import prog8.ast.walk.IAstVisitor
import prog8.code.core.* import prog8.code.core.*
import prog8.code.target.VMTarget
import prog8.compiler.BuiltinFunctions import prog8.compiler.BuiltinFunctions
import prog8.compiler.InplaceModifyingBuiltinFunctions import prog8.compiler.InplaceModifyingBuiltinFunctions
import prog8.compiler.builtinFunctionReturnType import prog8.compiler.builtinFunctionReturnType

View File

@ -87,7 +87,7 @@ class TestCompilerOptionSourcedirs: FunSpec({
test("testFilePathOutsideWorkingDirRelativeTo1stInSourcedirs") { test("testFilePathOutsideWorkingDirRelativeTo1stInSourcedirs") {
val filepath = assumeReadableFile(fixturesDir, "ast_simple_main.p8") val filepath = assumeReadableFile(fixturesDir, "ast_simple_main.p8")
val sourcedirs = listOf("${fixturesDir}") val sourcedirs = listOf("$fixturesDir")
compileFile(filepath.fileName, sourcedirs) shouldNotBe null compileFile(filepath.fileName, sourcedirs) shouldNotBe null
} }

View File

@ -10,7 +10,6 @@ import prog8.ast.statements.InlineAssembly
import prog8.ast.statements.VarDecl import prog8.ast.statements.VarDecl
import prog8.code.core.Position import prog8.code.core.Position
import prog8.code.target.C64Target import prog8.code.target.C64Target
import prog8.compiler.printProgram
import prog8tests.helpers.compileText import prog8tests.helpers.compileText
class TestVarious: FunSpec({ class TestVarious: FunSpec({

View File

@ -57,7 +57,7 @@ class PathsHelpersTests: FunSpec({
test("on existing directory") { test("on existing directory") {
shouldThrow<java.lang.AssertionError> { shouldThrow<java.lang.AssertionError> {
assumeNotExists("${fixturesDir}") assumeNotExists("$fixturesDir")
} }
} }
} }
@ -157,13 +157,13 @@ class PathsHelpersTests: FunSpec({
context("WithStringAndStringArgs") { context("WithStringAndStringArgs") {
test("on non-existing path") { test("on non-existing path") {
shouldThrow<AssertionError> { shouldThrow<AssertionError> {
assumeDirectory("${fixturesDir}", "i_do_not_exist") assumeDirectory("$fixturesDir", "i_do_not_exist")
} }
} }
test("on existing file") { test("on existing file") {
shouldThrow<AssertionError> { shouldThrow<AssertionError> {
assumeDirectory("${fixturesDir}", "ast_simple_main.p8") assumeDirectory("$fixturesDir", "ast_simple_main.p8")
} }
} }
@ -178,13 +178,13 @@ class PathsHelpersTests: FunSpec({
context("WithStringAndPathArgs") { context("WithStringAndPathArgs") {
test("on non-existing path") { test("on non-existing path") {
shouldThrow<AssertionError> { shouldThrow<AssertionError> {
assumeDirectory("${fixturesDir}", Path("i_do_not_exist")) assumeDirectory("$fixturesDir", Path("i_do_not_exist"))
} }
} }
test("on existing file") { test("on existing file") {
shouldThrow<AssertionError> { shouldThrow<AssertionError> {
assumeDirectory("${fixturesDir}", Path("ast_simple_main.p8")) assumeDirectory("$fixturesDir", Path("ast_simple_main.p8"))
} }
} }
@ -240,7 +240,7 @@ class PathsHelpersTests: FunSpec({
test("on directory") { test("on directory") {
shouldThrow<AssertionError> { shouldThrow<AssertionError> {
assumeReadableFile("${fixturesDir}") assumeReadableFile("$fixturesDir")
} }
} }
} }
@ -289,7 +289,7 @@ class PathsHelpersTests: FunSpec({
context("WithStringAndStringArgs") { context("WithStringAndStringArgs") {
test("on non-existing path") { test("on non-existing path") {
shouldThrow<java.lang.AssertionError> { shouldThrow<java.lang.AssertionError> {
assumeReadableFile("${fixturesDir}", "i_do_not_exist") assumeReadableFile("$fixturesDir", "i_do_not_exist")
} }
} }
@ -301,7 +301,7 @@ class PathsHelpersTests: FunSpec({
test("on directory") { test("on directory") {
shouldThrow<AssertionError> { shouldThrow<AssertionError> {
assumeReadableFile("${fixturesDir}", "..") assumeReadableFile("$fixturesDir", "..")
} }
} }
} }

View File

@ -1,8 +1,10 @@
package prog8tests.vm package prog8tests.vm
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.FunSpec import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldContain
import prog8.ast.expressions.BuiltinFunctionCall import prog8.ast.expressions.BuiltinFunctionCall
import prog8.ast.statements.Assignment import prog8.ast.statements.Assignment
import prog8.code.target.C64Target import prog8.code.target.C64Target
@ -212,4 +214,32 @@ main {
vm.stepCount shouldBe 49 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<Exception> {
VmRunner().runProgram(virtfile.readText())
}
exc.message shouldContain("does not support asmsubs")
}
}) })

View File

@ -368,12 +368,12 @@ class IRFileReader {
} }
return IRAsmSubroutine( return IRAsmSubroutine(
scopedname, scopedname,
parsePosition(pos), if(address=="null") null else address.toUInt(), if(address=="null") null else address.toUInt(),
clobberRegs.toSet(), clobberRegs.toSet(),
params, params,
returns, returns,
asm.isIR, asm,
asm.assembly parsePosition(pos)
) )
} }

View File

@ -81,9 +81,8 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
out.write("${dt.toString().lowercase()} $reg\n") out.write("${dt.toString().lowercase()} $reg\n")
} }
out.write("</PARAMS>\n") out.write("</PARAMS>\n")
out.write("<INLINEASM IR=${it.isIR} POS=${it.position}>\n") writeInlineAsm(it.asmChunk)
out.write(it.assembly) out.write("</ASMSUB>\n")
out.write("\n</INLINEASM>\n</ASMSUB>\n")
} }
out.write("</BLOCK>\n") out.write("</BLOCK>\n")
} }

View File

@ -69,7 +69,12 @@ class IRProgram(val name: String,
} }
fun linkChunks() { fun linkChunks() {
val labeledChunks = blocks.flatMap { it.subroutines }.flatMap { it.chunks }.associateBy { it.label } fun getLabeledChunks(): Map<String?, IRCodeChunkBase> {
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.isNotEmpty()) {
if(globalInits.next==null) { if(globalInits.next==null) {
@ -109,10 +114,8 @@ class IRProgram(val name: String,
// link all jump and branching instructions to their target // link all jump and branching instructions to their target
chunk.instructions.forEach { chunk.instructions.forEach {
if(it.opcode in OpcodesThatBranch && it.opcode!=Opcode.RETURN && it.labelSymbol!=null) { if(it.opcode in OpcodesThatBranch && it.opcode!=Opcode.RETURN && it.labelSymbol!=null)
val targetChunk = labeledChunks.getValue(it.labelSymbol) it.branchTarget = labeledChunks.getValue(it.labelSymbol)
it.branchTarget = targetChunk
}
// note: branches with an address value cannot be linked to something... // note: branches with an address value cannot be linked to something...
} }
} }
@ -240,22 +243,19 @@ class IRSubroutine(val name: String,
class IRAsmSubroutine( class IRAsmSubroutine(
val name: String, val name: String,
val position: Position,
val address: UInt?, val address: UInt?,
val clobbers: Set<CpuRegister>, val clobbers: Set<CpuRegister>,
val parameters: List<Pair<DataType, RegisterOrStatusflag>>, val parameters: List<Pair<DataType, RegisterOrStatusflag>>,
val returns: List<Pair<DataType, RegisterOrStatusflag>>, val returns: List<Pair<DataType, RegisterOrStatusflag>>,
val isIR: Boolean, val asmChunk: IRInlineAsmChunk,
val assembly: String val position: Position
) { ) {
init { init {
require('.' in name) { "subroutine name is not scoped: $name" } require('.' in name) { "subroutine name is not scoped: $name" }
require(!name.startsWith("main.main.")) { "subroutine name invalid main prefix: $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 fun usedRegisters() = registersUsed
} }

View File

@ -87,13 +87,12 @@ class TestVm: FunSpec( {
val block = IRBlock("main", null, IRBlock.BlockAlignment.NONE, Position.DUMMY) val block = IRBlock("main", null, IRBlock.BlockAlignment.NONE, Position.DUMMY)
val startSub = IRAsmSubroutine( val startSub = IRAsmSubroutine(
"main.asmstart", "main.asmstart",
Position.DUMMY,
0x2000u, 0x2000u,
emptySet(), emptySet(),
emptyList(), emptyList(),
emptyList(), emptyList(),
true, IRInlineAsmChunk("main.asmstart", "inlined asm here", true, Position.DUMMY, null),
"inlined asm here" Position.DUMMY
) )
block += startSub block += startSub
program.addBlock(block) program.addBlock(block)