mirror of
https://github.com/irmen/prog8.git
synced 2024-09-07 03:54:27 +00:00
ir: fix inlineasm linking
This commit is contained in:
parent
890f55f91a
commit
b22804efaf
@ -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 -> {
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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({
|
||||||
|
@ -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", "..")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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")
|
||||||
|
}
|
||||||
})
|
})
|
@ -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)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user