optimized var copying

This commit is contained in:
Irmen de Jong 2018-10-08 02:24:35 +02:00
parent c2a1cb956a
commit 9d10210466
6 changed files with 127 additions and 7 deletions

View File

@ -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)

View File

@ -79,6 +79,9 @@ fun main(args: Array<String>) {
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")

View File

@ -72,6 +72,8 @@ class HeapValues {
private val heap = mutableListOf<HeapValue>()
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<Int, List<Value>>()
private val labels = mutableMapOf<String, Instruction>()
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<Int, Instruction>()
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<Int, Instruction>()
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)
}

View File

@ -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('$')) {

View File

@ -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)

View File

@ -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<VmExecutionException> {
vm.step(1)
}
}
@Test
fun testAdd() {
testBinaryOperator(Value(DataType.BYTE, 140), Opcode.ADD_B, Value(DataType.BYTE, 222), Value(DataType.BYTE, 106))