mirror of
https://github.com/irmen/prog8.git
synced 2025-04-07 16:41:46 +00:00
ir: fix inlineasm linking
This commit is contained in:
parent
890f55f91a
commit
b22804efaf
@ -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 -> {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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({
|
||||
|
@ -57,7 +57,7 @@ class PathsHelpersTests: FunSpec({
|
||||
|
||||
test("on existing directory") {
|
||||
shouldThrow<java.lang.AssertionError> {
|
||||
assumeNotExists("${fixturesDir}")
|
||||
assumeNotExists("$fixturesDir")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -157,13 +157,13 @@ class PathsHelpersTests: FunSpec({
|
||||
context("WithStringAndStringArgs") {
|
||||
test("on non-existing path") {
|
||||
shouldThrow<AssertionError> {
|
||||
assumeDirectory("${fixturesDir}", "i_do_not_exist")
|
||||
assumeDirectory("$fixturesDir", "i_do_not_exist")
|
||||
}
|
||||
}
|
||||
|
||||
test("on existing file") {
|
||||
shouldThrow<AssertionError> {
|
||||
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<AssertionError> {
|
||||
assumeDirectory("${fixturesDir}", Path("i_do_not_exist"))
|
||||
assumeDirectory("$fixturesDir", Path("i_do_not_exist"))
|
||||
}
|
||||
}
|
||||
|
||||
test("on existing file") {
|
||||
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") {
|
||||
shouldThrow<AssertionError> {
|
||||
assumeReadableFile("${fixturesDir}")
|
||||
assumeReadableFile("$fixturesDir")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -289,7 +289,7 @@ class PathsHelpersTests: FunSpec({
|
||||
context("WithStringAndStringArgs") {
|
||||
test("on non-existing path") {
|
||||
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") {
|
||||
shouldThrow<AssertionError> {
|
||||
assumeReadableFile("${fixturesDir}", "..")
|
||||
assumeReadableFile("$fixturesDir", "..")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<Exception> {
|
||||
VmRunner().runProgram(virtfile.readText())
|
||||
}
|
||||
exc.message shouldContain("does not support asmsubs")
|
||||
}
|
||||
})
|
@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -81,9 +81,8 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
|
||||
out.write("${dt.toString().lowercase()} $reg\n")
|
||||
}
|
||||
out.write("</PARAMS>\n")
|
||||
out.write("<INLINEASM IR=${it.isIR} POS=${it.position}>\n")
|
||||
out.write(it.assembly)
|
||||
out.write("\n</INLINEASM>\n</ASMSUB>\n")
|
||||
writeInlineAsm(it.asmChunk)
|
||||
out.write("</ASMSUB>\n")
|
||||
}
|
||||
out.write("</BLOCK>\n")
|
||||
}
|
||||
|
@ -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<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.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<CpuRegister>,
|
||||
val parameters: List<Pair<DataType, RegisterOrStatusflag>>,
|
||||
val returns: List<Pair<DataType, RegisterOrStatusflag>>,
|
||||
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
|
||||
}
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user