From 9d102104660fcc6bbf2cd6fb6ad0ab9322bf5173 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 8 Oct 2018 02:24:35 +0200 Subject: [PATCH] optimized var copying --- compiler/examples/test.p8 | 4 ++ compiler/src/prog8/CompilerMain.kt | 3 ++ compiler/src/prog8/compiler/Compiler.kt | 65 +++++++++++++++++++++++-- compiler/src/prog8/stackvm/Program.kt | 4 ++ compiler/src/prog8/stackvm/StackVm.kt | 32 +++++++++++- compiler/test/StackVMOpcodeTests.kt | 26 ++++++++++ 6 files changed, 127 insertions(+), 7 deletions(-) diff --git a/compiler/examples/test.p8 b/compiler/examples/test.p8 index a36e1ccaa..9bf06afc3 100644 --- a/compiler/examples/test.p8 +++ b/compiler/examples/test.p8 @@ -9,6 +9,10 @@ sub start() { str s1 = "hello" str s2 = "bye" + A=X + X=Y + X=X + _vm_write_str(s1) s1 = s2 _vm_write_str(s1) diff --git a/compiler/src/prog8/CompilerMain.kt b/compiler/src/prog8/CompilerMain.kt index fb2b3c6f7..fbd421e1c 100644 --- a/compiler/src/prog8/CompilerMain.kt +++ b/compiler/src/prog8/CompilerMain.kt @@ -79,6 +79,9 @@ fun main(args: Array) { val compiler = Compiler(compilerOptions) val intermediate = compiler.compile(moduleAst, heap) intermediate.optimize() + println("Debug: ${intermediate.numVariables} allocated variables and constants") + println("Debug: ${heap.size()} heap values") + println("Debug: ${intermediate.numInstructions} vm instructions") val stackVmFilename = intermediate.name + "_stackvm.txt" val stackvmFile = PrintStream(File(stackVmFilename), "utf-8") diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 18e21402d..cbd2e4ada 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -72,6 +72,8 @@ class HeapValues { private val heap = mutableListOf() + fun size(): Int = heap.size + fun add(type: DataType, str: String): Int { if (str.isEmpty() || str.length > 255) throw IllegalArgumentException("string length must be 1-255") @@ -129,19 +131,72 @@ class StackVmProgram(val name: String, val heap: HeapValues) { private val memory = mutableMapOf>() private val labels = mutableMapOf() val numVariables: Int - get() {return variables.size} + get() {return variables.flatMap { it -> it.value.keys }.size} val numInstructions: Int - get() {return instructions.size} + get() {return instructions.filter { it.opcode!=Opcode.LINE }.size} fun optimize() { - println("\nOptimizing stackVM code...") + println("Optimizing stackVM code...") optimizeDataConversionAndUselessDiscards() + optimizeVariableCopying() + optimizeMultipleSequentialLineInstrs() // todo optimize stackvm code more // remove nops (that are not a label) this.instructions.removeIf { it.opcode==Opcode.NOP && it !is LabelInstr } } + private fun optimizeMultipleSequentialLineInstrs() { + val instructionsToReplace = mutableMapOf() + + instructions.asSequence().withIndex().windowed(2).toList().forEach { + if(it[0].value.opcode==Opcode.LINE && it[1].value.opcode==Opcode.LINE) + instructionsToReplace[it[0].index] = Instruction(Opcode.NOP) + } + + for(rins in instructionsToReplace) { + instructions[rins.key] = rins.value + } + } + + private fun optimizeVariableCopying() { + val instructionsToReplace = mutableMapOf() + + instructions.asSequence().withIndex().windowed(2).toList().forEach { + when(it[0].value.opcode) { + Opcode.PUSH_VAR -> + if(it[1].value.opcode==Opcode.POP_VAR) { + if(it[0].value.callLabel!=it[1].value.callLabel) + instructionsToReplace[it[0].index] = Instruction(Opcode.COPY_VAR, null, it[0].value.callLabel, it[1].value.callLabel) + else + instructionsToReplace[it[0].index] = Instruction(Opcode.NOP) + instructionsToReplace[it[1].index] = Instruction(Opcode.NOP) + } + Opcode.PUSH_VAR_W -> + if(it[1].value.opcode==Opcode.POP_VAR_W) { + if(it[0].value.callLabel!=it[1].value.callLabel) + instructionsToReplace[it[0].index] = Instruction(Opcode.COPY_VAR_W, null, it[0].value.callLabel, it[1].value.callLabel) + else + instructionsToReplace[it[0].index] = Instruction(Opcode.NOP) + instructionsToReplace[it[1].index] = Instruction(Opcode.NOP) + } + Opcode.PUSH_VAR_F -> + if(it[1].value.opcode==Opcode.POP_VAR_F) { + if(it[0].value.callLabel!=it[1].value.callLabel) + instructionsToReplace[it[0].index] = Instruction(Opcode.COPY_VAR_F, null, it[0].value.callLabel, it[1].value.callLabel) + else + instructionsToReplace[it[0].index] = Instruction(Opcode.NOP) + instructionsToReplace[it[1].index] = Instruction(Opcode.NOP) + } + else -> {} + } + } + + for(rins in instructionsToReplace) { + instructions[rins.key] = rins.value + } + } + private fun optimizeDataConversionAndUselessDiscards() { // - push value followed by a data type conversion -> push the value in the correct type and remove the conversion // - push something followed by a discard -> remove both @@ -327,11 +382,9 @@ class Compiler(private val options: CompilationOptions) { // create the heap of all variables used in all blocks and scopes val varGather = VarGatherer(intermediate) varGather.process(module) - println(" ${intermediate.numVariables} allocated variables and constants") val translator = StatementTranslator(intermediate, namespace, heap) translator.process(module) - println(" ${intermediate.numInstructions} vm instructions") return intermediate } @@ -361,6 +414,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, override fun process(subroutine: Subroutine): IStatement { if(subroutine.asmAddress==null) { stackvmProg.label(subroutine.scopedname) + stackvmProg.line(subroutine.position) // note: the caller has already written the arguments into the subroutine's parameter variables. translate(subroutine.statements) } else { @@ -371,6 +425,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram, override fun process(block: Block): IStatement { stackvmProg.label(block.scopedname) + stackvmProg.line(block.position) translate(block.statements) return super.process(block) } diff --git a/compiler/src/prog8/stackvm/Program.kt b/compiler/src/prog8/stackvm/Program.kt index e9844be7e..719e6a153 100644 --- a/compiler/src/prog8/stackvm/Program.kt +++ b/compiler/src/prog8/stackvm/Program.kt @@ -98,6 +98,10 @@ class Program (val name: String, val args = if(parts.size==2) parts[1] else null val instruction = when(opcode) { Opcode.LINE -> Instruction(opcode, null, callLabel = args) + Opcode.COPY_VAR, Opcode.COPY_VAR_W, Opcode.COPY_VAR_F -> { + val (v1, v2) = args!!.split(splitpattern, limit = 2) + Instruction(opcode, null, v1, v2) + } Opcode.JUMP, Opcode.CALL, Opcode.BNEG, Opcode.BPOS, Opcode.BZ, Opcode.BNZ, Opcode.BCS, Opcode.BCC -> { if(args!!.startsWith('$')) { diff --git a/compiler/src/prog8/stackvm/StackVm.kt b/compiler/src/prog8/stackvm/StackVm.kt index 8787932ad..68b2a656c 100644 --- a/compiler/src/prog8/stackvm/StackVm.kt +++ b/compiler/src/prog8/stackvm/StackVm.kt @@ -32,6 +32,11 @@ enum class Opcode { POP_VAR_W, // pop word value into variable POP_VAR_F, // pop float value into variable + // optimized copying of one var to another (replaces push+pop) + COPY_VAR, + COPY_VAR_W, + COPY_VAR_F, + // numeric arithmetic ADD_B, ADD_W, @@ -198,6 +203,7 @@ val opcodesWithVarArgument = setOf( Opcode.ROL2_VAR, Opcode.ROL2_VAR_W, Opcode.ROR2_VAR, Opcode.ROR2_VAR_W, Opcode.POP_VAR, Opcode.POP_VAR_W, Opcode.POP_VAR_F, Opcode.PUSH_VAR, Opcode.PUSH_VAR_W, Opcode.PUSH_VAR_F, + Opcode.COPY_VAR, Opcode.COPY_VAR_W, Opcode.COPY_VAR_F, Opcode.READ_INDEXED_VAR, Opcode.READ_INDEXED_VAR_W, Opcode.READ_INDEXED_VAR_F, Opcode.WRITE_INDEXED_VAR, Opcode.WRITE_INDEXED_VAR_W, Opcode.WRITE_INDEXED_VAR_F ) @@ -251,7 +257,8 @@ enum class Syscall(val callNr: Short) { open class Instruction(val opcode: Opcode, val arg: Value? = null, - val callLabel: String? = null) + val callLabel: String? = null, + val callLabel2: String? = null) { lateinit var next: Instruction var nextAlt: Instruction? = null @@ -267,7 +274,7 @@ open class Instruction(val opcode: Opcode, } opcode in opcodesWithVarArgument -> { // opcodes that manipulate a variable - "${opcode.toString().toLowerCase()} $callLabel" + "${opcode.toString().toLowerCase()} ${callLabel?:""} ${callLabel2?:""}".trimEnd() } callLabel==null -> "${opcode.toString().toLowerCase()} $argStr" else -> "${opcode.toString().toLowerCase()} $callLabel $argStr" @@ -906,6 +913,27 @@ class StackVm(private var traceOutputFile: String?) { checkDt(variable, setOf(DataType.WORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS)) variables[ins.callLabel!!] = value } + Opcode.COPY_VAR -> { + val source = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") + val dest = variables[ins.callLabel2] ?: throw VmExecutionException("unknown variable: ${ins.callLabel2}") + checkDt(source, DataType.BYTE) + checkDt(dest, DataType.BYTE) + variables[ins.callLabel2!!] = source + } + Opcode.COPY_VAR_W -> { + val source = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") + val dest = variables[ins.callLabel2] ?: throw VmExecutionException("unknown variable: ${ins.callLabel2}") + checkDt(source, setOf(DataType.WORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS)) + checkDt(dest, setOf(DataType.WORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS)) + variables[ins.callLabel2!!] = source + } + Opcode.COPY_VAR_F -> { + val source = variables[ins.callLabel] ?: throw VmExecutionException("unknown variable: ${ins.callLabel}") + val dest = variables[ins.callLabel2] ?: throw VmExecutionException("unknown variable: ${ins.callLabel2}") + checkDt(source, DataType.FLOAT) + checkDt(dest, DataType.FLOAT) + variables[ins.callLabel2!!] = source + } Opcode.POP_VAR_F -> { val value = evalstack.pop() checkDt(value, DataType.FLOAT) diff --git a/compiler/test/StackVMOpcodeTests.kt b/compiler/test/StackVMOpcodeTests.kt index 760f8fd83..9ae665f78 100644 --- a/compiler/test/StackVMOpcodeTests.kt +++ b/compiler/test/StackVMOpcodeTests.kt @@ -251,6 +251,32 @@ class TestStackVmOpcodes { } } + @Test + fun testCopyVar() { + val ins = mutableListOf( + Instruction(Opcode.COPY_VAR, null, "bvar1", "bvar2"), + Instruction(Opcode.COPY_VAR_W, null, "wvar1", "wvar2"), + Instruction(Opcode.COPY_VAR_F, null, "fvar1", "fvar2"), + Instruction(Opcode.COPY_VAR_W, null, "wvar1", "bvar2")) + val vars = mapOf( + "bvar1" to Value(DataType.BYTE, 1), + "bvar2" to Value(DataType.BYTE, 2), + "wvar1" to Value(DataType.WORD, 1111), + "wvar2" to Value(DataType.WORD, 2222), + "fvar1" to Value(DataType.FLOAT, 11.11), + "fvar2" to Value(DataType.FLOAT, 22.22) + ) + vm.load(makeProg(ins, vars), null) + assertEquals(12, vm.variables.size) + vm.step(3) + assertEquals(Value(DataType.BYTE, 1), vm.variables["bvar2"]) + assertEquals(Value(DataType.WORD, 1111), vm.variables["wvar2"]) + assertEquals(Value(DataType.FLOAT, 11.11), vm.variables["fvar2"]) + assertFailsWith { + vm.step(1) + } + } + @Test fun testAdd() { testBinaryOperator(Value(DataType.BYTE, 140), Opcode.ADD_B, Value(DataType.BYTE, 222), Value(DataType.BYTE, 106))