mirror of
https://github.com/irmen/prog8.git
synced 2025-01-11 13:29:45 +00:00
kotlin 1.7.20
This commit is contained in:
parent
7c1bdfe713
commit
43e31765e5
@ -117,15 +117,15 @@ internal class SymbolTableMaker: IAstVisitor {
|
|||||||
// st.origAstLinks[label] = node
|
// st.origAstLinks[label] = node
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(fcall: BuiltinFunctionCall) {
|
override fun visit(bfc: BuiltinFunctionCall) {
|
||||||
if(fcall.name=="memory") {
|
if(bfc.name=="memory") {
|
||||||
// memory slab allocations are a builtin functioncall in the program, but end up named as well in the symboltable
|
// memory slab allocations are a builtin functioncall in the program, but end up named as well in the symboltable
|
||||||
val name = (fcall.args[0] as StringLiteral).value
|
val name = (bfc.args[0] as StringLiteral).value
|
||||||
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name"}
|
require(name.all { it.isLetterOrDigit() || it=='_' }) {"memory name should be a valid symbol name"}
|
||||||
val size = (fcall.args[1] as NumericLiteral).number.toUInt()
|
val size = (bfc.args[1] as NumericLiteral).number.toUInt()
|
||||||
val align = (fcall.args[2] as NumericLiteral).number.toUInt()
|
val align = (bfc.args[2] as NumericLiteral).number.toUInt()
|
||||||
st.add(StMemorySlab("prog8_memoryslab_$name", size, align, fcall.position))
|
st.add(StMemorySlab("prog8_memoryslab_$name", size, align, bfc.position))
|
||||||
}
|
}
|
||||||
super.visit(fcall)
|
super.visit(bfc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,4 +4,4 @@ org.gradle.parallel=true
|
|||||||
org.gradle.daemon=true
|
org.gradle.daemon=true
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
javaVersion=11
|
javaVersion=11
|
||||||
kotlinVersion=1.7.10
|
kotlinVersion=1.7.20
|
@ -55,6 +55,7 @@ class IRFileReader {
|
|||||||
val zpReserved = mutableListOf<UIntRange>()
|
val zpReserved = mutableListOf<UIntRange>()
|
||||||
var loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
|
var loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
|
||||||
var dontReinitGlobals = false
|
var dontReinitGlobals = false
|
||||||
|
var optimize = true
|
||||||
var evalStackBaseAddress: UInt? = null
|
var evalStackBaseAddress: UInt? = null
|
||||||
var outputDir = Path("")
|
var outputDir = Path("")
|
||||||
if(line!="<OPTIONS>")
|
if(line!="<OPTIONS>")
|
||||||
@ -86,6 +87,7 @@ class IRFileReader {
|
|||||||
zpReserved.add(UIntRange(start.toUInt(), end.toUInt()))
|
zpReserved.add(UIntRange(start.toUInt(), end.toUInt()))
|
||||||
}
|
}
|
||||||
"outputDir" -> outputDir = Path(value)
|
"outputDir" -> outputDir = Path(value)
|
||||||
|
"optimize" -> optimize = value.toBoolean()
|
||||||
else -> throw IRParseException("illegal OPTION $name")
|
else -> throw IRParseException("illegal OPTION $name")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -101,7 +103,8 @@ class IRFileReader {
|
|||||||
loadAddress,
|
loadAddress,
|
||||||
dontReinitGlobals = dontReinitGlobals,
|
dontReinitGlobals = dontReinitGlobals,
|
||||||
evalStackBaseAddress = evalStackBaseAddress,
|
evalStackBaseAddress = evalStackBaseAddress,
|
||||||
outputDir = outputDir
|
outputDir = outputDir,
|
||||||
|
optimize = optimize
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,6 +93,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
|
|||||||
out.write("zpReserved=${range.first},${range.last}\n")
|
out.write("zpReserved=${range.first},${range.last}\n")
|
||||||
}
|
}
|
||||||
out.write("loadAddress=${irProgram.options.loadAddress}\n")
|
out.write("loadAddress=${irProgram.options.loadAddress}\n")
|
||||||
|
out.write("optimize=${irProgram.options.optimize}\n")
|
||||||
out.write("dontReinitGlobals=${irProgram.options.dontReinitGlobals}\n")
|
out.write("dontReinitGlobals=${irProgram.options.dontReinitGlobals}\n")
|
||||||
out.write("evalStackBaseAddress=${irProgram.options.evalStackBaseAddress}\n")
|
out.write("evalStackBaseAddress=${irProgram.options.evalStackBaseAddress}\n")
|
||||||
out.write("outputDir=${irProgram.options.outputDir.toAbsolutePath()}\n")
|
out.write("outputDir=${irProgram.options.outputDir.toAbsolutePath()}\n")
|
||||||
|
@ -57,8 +57,8 @@ syscall value - do a systemcall identified by call numbe
|
|||||||
return - restore last saved instruction location and continue at that instruction
|
return - restore last saved instruction location and continue at that instruction
|
||||||
|
|
||||||
|
|
||||||
BRANCHING
|
BRANCHING and CONDITIONALS
|
||||||
---------
|
--------------------------
|
||||||
All have type b or w except the branches that only check status bits.
|
All have type b or w except the branches that only check status bits.
|
||||||
|
|
||||||
bstcc address - branch to location if Status bit Carry is Clear
|
bstcc address - branch to location if Status bit Carry is Clear
|
||||||
@ -402,6 +402,29 @@ val OpcodesWithAddress = setOf(
|
|||||||
Opcode.BGES
|
Opcode.BGES
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val OpcodesThatBranch = setOf(
|
||||||
|
Opcode.JUMP,
|
||||||
|
Opcode.RETURN,
|
||||||
|
Opcode.BSTCC,
|
||||||
|
Opcode.BSTCS,
|
||||||
|
Opcode.BSTEQ,
|
||||||
|
Opcode.BSTNE,
|
||||||
|
Opcode.BSTNEG,
|
||||||
|
Opcode.BSTPOS,
|
||||||
|
Opcode.BZ,
|
||||||
|
Opcode.BNZ,
|
||||||
|
Opcode.BEQ,
|
||||||
|
Opcode.BNE,
|
||||||
|
Opcode.BLT,
|
||||||
|
Opcode.BLTS,
|
||||||
|
Opcode.BGT,
|
||||||
|
Opcode.BGTS,
|
||||||
|
Opcode.BLE,
|
||||||
|
Opcode.BLES,
|
||||||
|
Opcode.BGE,
|
||||||
|
Opcode.BGES
|
||||||
|
)
|
||||||
|
|
||||||
val OpcodesForCpuRegisters = setOf(
|
val OpcodesForCpuRegisters = setOf(
|
||||||
Opcode.LOADCPU,
|
Opcode.LOADCPU,
|
||||||
Opcode.STORECPU,
|
Opcode.STORECPU,
|
||||||
@ -416,122 +439,6 @@ enum class VmDataType {
|
|||||||
// TODO add INT (32-bit)? INT24 (24-bit)?
|
// TODO add INT (32-bit)? INT24 (24-bit)?
|
||||||
}
|
}
|
||||||
|
|
||||||
data class IRInstruction(
|
|
||||||
val opcode: Opcode,
|
|
||||||
val type: VmDataType?=null,
|
|
||||||
val reg1: Int?=null, // 0-$ffff
|
|
||||||
val reg2: Int?=null, // 0-$ffff
|
|
||||||
val fpReg1: Int?=null, // 0-$ffff
|
|
||||||
val fpReg2: Int?=null, // 0-$ffff
|
|
||||||
val value: Int?=null, // 0-$ffff
|
|
||||||
val fpValue: Float?=null,
|
|
||||||
val labelSymbol: String?=null, // symbolic label name as alternative to value (so only for Branch/jump/call Instructions!)
|
|
||||||
val binaryData: Collection<UByte>?=null
|
|
||||||
): IRCodeLine() {
|
|
||||||
// reg1 and fpreg1 can be IN/OUT/INOUT (all others are readonly INPUT)
|
|
||||||
// This knowledge is useful in IL assembly optimizers to see how registers are used.
|
|
||||||
val reg1direction: OperandDirection
|
|
||||||
val fpReg1direction: OperandDirection
|
|
||||||
|
|
||||||
init {
|
|
||||||
require(labelSymbol?.first()!='_') {"label/symbol should not start with underscore $labelSymbol"}
|
|
||||||
require(reg1==null || reg1 in 0..65536) {"reg1 out of bounds"}
|
|
||||||
require(reg2==null || reg2 in 0..65536) {"reg2 out of bounds"}
|
|
||||||
require(fpReg1==null || fpReg1 in 0..65536) {"fpReg1 out of bounds"}
|
|
||||||
require(fpReg2==null || fpReg2 in 0..65536) {"fpReg2 out of bounds"}
|
|
||||||
if(value!=null && opcode !in OpcodesWithAddress) {
|
|
||||||
when (type) {
|
|
||||||
VmDataType.BYTE -> require(value in -128..255) {"value out of range for byte: $value"}
|
|
||||||
VmDataType.WORD -> require(value in -32768..65535) {"value out of range for word: $value"}
|
|
||||||
VmDataType.FLOAT, null -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
require((opcode==Opcode.BINARYDATA && binaryData!=null) || (opcode!=Opcode.BINARYDATA && binaryData==null)) {
|
|
||||||
"binarydata inconsistency"
|
|
||||||
}
|
|
||||||
|
|
||||||
val formats = instructionFormats.getValue(opcode)
|
|
||||||
require (type != null || formats.containsKey(null)) { "missing type" }
|
|
||||||
|
|
||||||
val format = formats.getValue(type)
|
|
||||||
if(format.reg1) require(reg1!=null) { "missing reg1" }
|
|
||||||
if(format.reg2) require(reg2!=null) { "missing reg2" }
|
|
||||||
if(format.fpReg1) require(fpReg1!=null) { "missing fpReg1" }
|
|
||||||
if(format.fpReg2) require(fpReg2!=null) { "missing fpReg2" }
|
|
||||||
if(!format.reg1) require(reg1==null) { "invalid reg1" }
|
|
||||||
if(!format.reg2) require(reg2==null) { "invalid reg2" }
|
|
||||||
if(!format.fpReg1) require(fpReg1==null) { "invalid fpReg1" }
|
|
||||||
if(!format.fpReg2) require(fpReg2==null) { "invalid fpReg2" }
|
|
||||||
|
|
||||||
if (type==VmDataType.FLOAT) {
|
|
||||||
if(format.fpValue) require(fpValue!=null || labelSymbol!=null) {"missing a fp-value or labelsymbol"}
|
|
||||||
} else {
|
|
||||||
if(format.value) require(value!=null || labelSymbol!=null) {"missing a value or labelsymbol"}
|
|
||||||
require(fpReg1==null && fpReg2==null) {"integer point instruction can't use floating point registers"}
|
|
||||||
}
|
|
||||||
|
|
||||||
reg1direction = format.reg1direction
|
|
||||||
fpReg1direction = format.fpReg1direction
|
|
||||||
|
|
||||||
if(opcode in setOf(Opcode.BEQ, Opcode.BNE, Opcode.BLT, Opcode.BLTS,
|
|
||||||
Opcode.BGT, Opcode.BGTS, Opcode.BLE, Opcode.BLES,
|
|
||||||
Opcode.BGE, Opcode.BGES,
|
|
||||||
Opcode.SEQ, Opcode.SNE, Opcode.SLT, Opcode.SLTS,
|
|
||||||
Opcode.SGT, Opcode.SGTS, Opcode.SLE, Opcode.SLES,
|
|
||||||
Opcode.SGE, Opcode.SGES)) {
|
|
||||||
if(type==VmDataType.FLOAT)
|
|
||||||
require(fpReg1!=fpReg2) {"$opcode: fpReg1 and fpReg2 should be different"}
|
|
||||||
else
|
|
||||||
require(reg1!=reg2) {"$opcode: reg1 and reg2 should be different"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
val result = mutableListOf(opcode.name.lowercase())
|
|
||||||
|
|
||||||
when(type) {
|
|
||||||
VmDataType.BYTE -> result.add(".b ")
|
|
||||||
VmDataType.WORD -> result.add(".w ")
|
|
||||||
VmDataType.FLOAT -> result.add(".f ")
|
|
||||||
else -> result.add(" ")
|
|
||||||
}
|
|
||||||
reg1?.let {
|
|
||||||
result.add("r$it")
|
|
||||||
result.add(",")
|
|
||||||
}
|
|
||||||
reg2?.let {
|
|
||||||
result.add("r$it")
|
|
||||||
result.add(",")
|
|
||||||
}
|
|
||||||
fpReg1?.let {
|
|
||||||
result.add("fr$it")
|
|
||||||
result.add(",")
|
|
||||||
}
|
|
||||||
fpReg2?.let {
|
|
||||||
result.add("fr$it")
|
|
||||||
result.add(",")
|
|
||||||
}
|
|
||||||
value?.let {
|
|
||||||
result.add(it.toString())
|
|
||||||
result.add(",")
|
|
||||||
}
|
|
||||||
fpValue?.let {
|
|
||||||
result.add(it.toString())
|
|
||||||
result.add(",")
|
|
||||||
}
|
|
||||||
labelSymbol?.let {
|
|
||||||
if(it.startsWith('&'))
|
|
||||||
result.add(it) // address-of something
|
|
||||||
else
|
|
||||||
result.add("_$it")
|
|
||||||
}
|
|
||||||
if(result.last() == ",")
|
|
||||||
result.removeLast()
|
|
||||||
return result.joinToString("").trimEnd()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class OperandDirection {
|
enum class OperandDirection {
|
||||||
INPUT,
|
INPUT,
|
||||||
OUTPUT,
|
OUTPUT,
|
||||||
@ -737,3 +644,120 @@ val instructionFormats = mutableMapOf(
|
|||||||
Opcode.BREAKPOINT to InstructionFormat.from("N"),
|
Opcode.BREAKPOINT to InstructionFormat.from("N"),
|
||||||
Opcode.BINARYDATA to InstructionFormat.from("N"),
|
Opcode.BINARYDATA to InstructionFormat.from("N"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
data class IRInstruction(
|
||||||
|
val opcode: Opcode,
|
||||||
|
val type: VmDataType?=null,
|
||||||
|
val reg1: Int?=null, // 0-$ffff
|
||||||
|
val reg2: Int?=null, // 0-$ffff
|
||||||
|
val fpReg1: Int?=null, // 0-$ffff
|
||||||
|
val fpReg2: Int?=null, // 0-$ffff
|
||||||
|
val value: Int?=null, // 0-$ffff
|
||||||
|
val fpValue: Float?=null,
|
||||||
|
val labelSymbol: String?=null, // symbolic label name as alternative to value (so only for Branch/jump/call Instructions!)
|
||||||
|
val binaryData: Collection<UByte>?=null
|
||||||
|
): IRCodeLine() {
|
||||||
|
// reg1 and fpreg1 can be IN/OUT/INOUT (all others are readonly INPUT)
|
||||||
|
// This knowledge is useful in IL assembly optimizers to see how registers are used.
|
||||||
|
val reg1direction: OperandDirection
|
||||||
|
val fpReg1direction: OperandDirection
|
||||||
|
|
||||||
|
init {
|
||||||
|
require(labelSymbol?.first()!='_') {"label/symbol should not start with underscore $labelSymbol"}
|
||||||
|
require(reg1==null || reg1 in 0..65536) {"reg1 out of bounds"}
|
||||||
|
require(reg2==null || reg2 in 0..65536) {"reg2 out of bounds"}
|
||||||
|
require(fpReg1==null || fpReg1 in 0..65536) {"fpReg1 out of bounds"}
|
||||||
|
require(fpReg2==null || fpReg2 in 0..65536) {"fpReg2 out of bounds"}
|
||||||
|
if(value!=null && opcode !in OpcodesWithAddress) {
|
||||||
|
when (type) {
|
||||||
|
VmDataType.BYTE -> require(value in -128..255) {"value out of range for byte: $value"}
|
||||||
|
VmDataType.WORD -> require(value in -32768..65535) {"value out of range for word: $value"}
|
||||||
|
VmDataType.FLOAT, null -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
require((opcode==Opcode.BINARYDATA && binaryData!=null) || (opcode!=Opcode.BINARYDATA && binaryData==null)) {
|
||||||
|
"binarydata inconsistency"
|
||||||
|
}
|
||||||
|
|
||||||
|
val formats = instructionFormats.getValue(opcode)
|
||||||
|
require (type != null || formats.containsKey(null)) { "missing type" }
|
||||||
|
|
||||||
|
val format = formats.getValue(type)
|
||||||
|
if(format.reg1) require(reg1!=null) { "missing reg1" }
|
||||||
|
if(format.reg2) require(reg2!=null) { "missing reg2" }
|
||||||
|
if(format.fpReg1) require(fpReg1!=null) { "missing fpReg1" }
|
||||||
|
if(format.fpReg2) require(fpReg2!=null) { "missing fpReg2" }
|
||||||
|
if(!format.reg1) require(reg1==null) { "invalid reg1" }
|
||||||
|
if(!format.reg2) require(reg2==null) { "invalid reg2" }
|
||||||
|
if(!format.fpReg1) require(fpReg1==null) { "invalid fpReg1" }
|
||||||
|
if(!format.fpReg2) require(fpReg2==null) { "invalid fpReg2" }
|
||||||
|
|
||||||
|
if (type==VmDataType.FLOAT) {
|
||||||
|
if(format.fpValue) require(fpValue!=null || labelSymbol!=null) {"missing a fp-value or labelsymbol"}
|
||||||
|
} else {
|
||||||
|
if(format.value) require(value!=null || labelSymbol!=null) {"missing a value or labelsymbol"}
|
||||||
|
require(fpReg1==null && fpReg2==null) {"integer point instruction can't use floating point registers"}
|
||||||
|
}
|
||||||
|
|
||||||
|
reg1direction = format.reg1direction
|
||||||
|
fpReg1direction = format.fpReg1direction
|
||||||
|
|
||||||
|
if(opcode in setOf(Opcode.BEQ, Opcode.BNE, Opcode.BLT, Opcode.BLTS,
|
||||||
|
Opcode.BGT, Opcode.BGTS, Opcode.BLE, Opcode.BLES,
|
||||||
|
Opcode.BGE, Opcode.BGES,
|
||||||
|
Opcode.SEQ, Opcode.SNE, Opcode.SLT, Opcode.SLTS,
|
||||||
|
Opcode.SGT, Opcode.SGTS, Opcode.SLE, Opcode.SLES,
|
||||||
|
Opcode.SGE, Opcode.SGES)) {
|
||||||
|
if(type==VmDataType.FLOAT)
|
||||||
|
require(fpReg1!=fpReg2) {"$opcode: fpReg1 and fpReg2 should be different"}
|
||||||
|
else
|
||||||
|
require(reg1!=reg2) {"$opcode: reg1 and reg2 should be different"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
val result = mutableListOf(opcode.name.lowercase())
|
||||||
|
|
||||||
|
when(type) {
|
||||||
|
VmDataType.BYTE -> result.add(".b ")
|
||||||
|
VmDataType.WORD -> result.add(".w ")
|
||||||
|
VmDataType.FLOAT -> result.add(".f ")
|
||||||
|
else -> result.add(" ")
|
||||||
|
}
|
||||||
|
reg1?.let {
|
||||||
|
result.add("r$it")
|
||||||
|
result.add(",")
|
||||||
|
}
|
||||||
|
reg2?.let {
|
||||||
|
result.add("r$it")
|
||||||
|
result.add(",")
|
||||||
|
}
|
||||||
|
fpReg1?.let {
|
||||||
|
result.add("fr$it")
|
||||||
|
result.add(",")
|
||||||
|
}
|
||||||
|
fpReg2?.let {
|
||||||
|
result.add("fr$it")
|
||||||
|
result.add(",")
|
||||||
|
}
|
||||||
|
value?.let {
|
||||||
|
result.add(it.toString())
|
||||||
|
result.add(",")
|
||||||
|
}
|
||||||
|
fpValue?.let {
|
||||||
|
result.add(it.toString())
|
||||||
|
result.add(",")
|
||||||
|
}
|
||||||
|
labelSymbol?.let {
|
||||||
|
if(it.startsWith('&'))
|
||||||
|
result.add(it) // address-of something
|
||||||
|
else
|
||||||
|
result.add("_$it")
|
||||||
|
}
|
||||||
|
if(result.last() == ",")
|
||||||
|
result.removeLast()
|
||||||
|
return result.joinToString("").trimEnd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -7,10 +7,8 @@ import prog8.code.core.IAssemblyGenerator
|
|||||||
import prog8.code.core.IAssemblyProgram
|
import prog8.code.core.IAssemblyProgram
|
||||||
import prog8.code.core.IErrorReporter
|
import prog8.code.core.IErrorReporter
|
||||||
import prog8.codegen.intermediate.IRCodeGen
|
import prog8.codegen.intermediate.IRCodeGen
|
||||||
import prog8.intermediate.IRFileReader
|
|
||||||
import prog8.intermediate.IRFileWriter
|
import prog8.intermediate.IRFileWriter
|
||||||
import prog8.intermediate.IRProgram
|
import prog8.intermediate.IRProgram
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
class VmCodeGen(private val program: PtProgram,
|
class VmCodeGen(private val program: PtProgram,
|
||||||
private val symbolTable: SymbolTable,
|
private val symbolTable: SymbolTable,
|
||||||
@ -25,13 +23,6 @@ class VmCodeGen(private val program: PtProgram,
|
|||||||
// no need to check options.keepIR, as the VM file format *is* the IR file.
|
// no need to check options.keepIR, as the VM file format *is* the IR file.
|
||||||
return VmAssemblyProgram(irProgram.name, irProgram)
|
return VmAssemblyProgram(irProgram.name, irProgram)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun compileIR(irFile: Path): IAssemblyProgram {
|
|
||||||
val irProgram = IRFileReader().read(irFile)
|
|
||||||
return VmAssemblyProgram(irProgram.name, irProgram)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user