kotlin 1.7.20

This commit is contained in:
Irmen de Jong 2022-09-29 18:18:11 +02:00
parent 7c1bdfe713
commit 43e31765e5
6 changed files with 155 additions and 136 deletions

View File

@ -117,15 +117,15 @@ internal class SymbolTableMaker: IAstVisitor {
// st.origAstLinks[label] = node
}
override fun visit(fcall: BuiltinFunctionCall) {
if(fcall.name=="memory") {
override fun visit(bfc: BuiltinFunctionCall) {
if(bfc.name=="memory") {
// 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"}
val size = (fcall.args[1] as NumericLiteral).number.toUInt()
val align = (fcall.args[2] as NumericLiteral).number.toUInt()
st.add(StMemorySlab("prog8_memoryslab_$name", size, align, fcall.position))
val size = (bfc.args[1] as NumericLiteral).number.toUInt()
val align = (bfc.args[2] as NumericLiteral).number.toUInt()
st.add(StMemorySlab("prog8_memoryslab_$name", size, align, bfc.position))
}
super.visit(fcall)
super.visit(bfc)
}
}

View File

@ -4,4 +4,4 @@ org.gradle.parallel=true
org.gradle.daemon=true
kotlin.code.style=official
javaVersion=11
kotlinVersion=1.7.10
kotlinVersion=1.7.20

View File

@ -55,6 +55,7 @@ class IRFileReader {
val zpReserved = mutableListOf<UIntRange>()
var loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
var dontReinitGlobals = false
var optimize = true
var evalStackBaseAddress: UInt? = null
var outputDir = Path("")
if(line!="<OPTIONS>")
@ -86,6 +87,7 @@ class IRFileReader {
zpReserved.add(UIntRange(start.toUInt(), end.toUInt()))
}
"outputDir" -> outputDir = Path(value)
"optimize" -> optimize = value.toBoolean()
else -> throw IRParseException("illegal OPTION $name")
}
}
@ -101,7 +103,8 @@ class IRFileReader {
loadAddress,
dontReinitGlobals = dontReinitGlobals,
evalStackBaseAddress = evalStackBaseAddress,
outputDir = outputDir
outputDir = outputDir,
optimize = optimize
)
}

View File

@ -93,6 +93,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
out.write("zpReserved=${range.first},${range.last}\n")
}
out.write("loadAddress=${irProgram.options.loadAddress}\n")
out.write("optimize=${irProgram.options.optimize}\n")
out.write("dontReinitGlobals=${irProgram.options.dontReinitGlobals}\n")
out.write("evalStackBaseAddress=${irProgram.options.evalStackBaseAddress}\n")
out.write("outputDir=${irProgram.options.outputDir.toAbsolutePath()}\n")

View File

@ -57,8 +57,8 @@ syscall value - do a systemcall identified by call numbe
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.
bstcc address - branch to location if Status bit Carry is Clear
@ -402,6 +402,29 @@ val OpcodesWithAddress = setOf(
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(
Opcode.LOADCPU,
Opcode.STORECPU,
@ -416,122 +439,6 @@ enum class VmDataType {
// 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 {
INPUT,
OUTPUT,
@ -737,3 +644,120 @@ val instructionFormats = mutableMapOf(
Opcode.BREAKPOINT 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()
}
}

View File

@ -7,10 +7,8 @@ import prog8.code.core.IAssemblyGenerator
import prog8.code.core.IAssemblyProgram
import prog8.code.core.IErrorReporter
import prog8.codegen.intermediate.IRCodeGen
import prog8.intermediate.IRFileReader
import prog8.intermediate.IRFileWriter
import prog8.intermediate.IRProgram
import java.nio.file.Path
class VmCodeGen(private val program: PtProgram,
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.
return VmAssemblyProgram(irProgram.name, irProgram)
}
companion object {
fun compileIR(irFile: Path): IAssemblyProgram {
val irProgram = IRFileReader().read(irFile)
return VmAssemblyProgram(irProgram.name, irProgram)
}
}
}