From 43e31765e5b7ab9406864280ce8e8703722b5a59 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Thu, 29 Sep 2022 18:18:11 +0200 Subject: [PATCH] kotlin 1.7.20 --- .../astprocessing/SymbolTableMaker.kt | 14 +- gradle.properties | 2 +- .../src/prog8/intermediate/IRFileReader.kt | 5 +- .../src/prog8/intermediate/IRFileWriter.kt | 1 + .../src/prog8/intermediate/IRInstructions.kt | 260 ++++++++++-------- .../src/prog8/vm/codegen/VmCodeGen.kt | 9 - 6 files changed, 155 insertions(+), 136 deletions(-) diff --git a/compiler/src/prog8/compiler/astprocessing/SymbolTableMaker.kt b/compiler/src/prog8/compiler/astprocessing/SymbolTableMaker.kt index e0c583eef..c7b8eec22 100644 --- a/compiler/src/prog8/compiler/astprocessing/SymbolTableMaker.kt +++ b/compiler/src/prog8/compiler/astprocessing/SymbolTableMaker.kt @@ -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) } } diff --git a/gradle.properties b/gradle.properties index 7e04dc5a9..705eba361 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,4 +4,4 @@ org.gradle.parallel=true org.gradle.daemon=true kotlin.code.style=official javaVersion=11 -kotlinVersion=1.7.10 \ No newline at end of file +kotlinVersion=1.7.20 \ No newline at end of file diff --git a/intermediate/src/prog8/intermediate/IRFileReader.kt b/intermediate/src/prog8/intermediate/IRFileReader.kt index 0fec6fea9..db17ceaf4 100644 --- a/intermediate/src/prog8/intermediate/IRFileReader.kt +++ b/intermediate/src/prog8/intermediate/IRFileReader.kt @@ -55,6 +55,7 @@ class IRFileReader { val zpReserved = mutableListOf() var loadAddress = target.machine.PROGRAM_LOAD_ADDRESS var dontReinitGlobals = false + var optimize = true var evalStackBaseAddress: UInt? = null var outputDir = Path("") if(line!="") @@ -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 ) } diff --git a/intermediate/src/prog8/intermediate/IRFileWriter.kt b/intermediate/src/prog8/intermediate/IRFileWriter.kt index 56ed906c0..f5064c24f 100644 --- a/intermediate/src/prog8/intermediate/IRFileWriter.kt +++ b/intermediate/src/prog8/intermediate/IRFileWriter.kt @@ -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") diff --git a/intermediate/src/prog8/intermediate/IRInstructions.kt b/intermediate/src/prog8/intermediate/IRInstructions.kt index e31d92977..08dec8e50 100644 --- a/intermediate/src/prog8/intermediate/IRInstructions.kt +++ b/intermediate/src/prog8/intermediate/IRInstructions.kt @@ -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?=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?=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() + } +} diff --git a/virtualmachine/src/prog8/vm/codegen/VmCodeGen.kt b/virtualmachine/src/prog8/vm/codegen/VmCodeGen.kt index 5b747ea6a..824bffc2e 100644 --- a/virtualmachine/src/prog8/vm/codegen/VmCodeGen.kt +++ b/virtualmachine/src/prog8/vm/codegen/VmCodeGen.kt @@ -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) - } - } }