mirror of
https://github.com/irmen/prog8.git
synced 2025-01-12 04:30:03 +00:00
optimized var copying
This commit is contained in:
parent
c2a1cb956a
commit
9d10210466
@ -9,6 +9,10 @@ sub start() {
|
|||||||
str s1 = "hello"
|
str s1 = "hello"
|
||||||
str s2 = "bye"
|
str s2 = "bye"
|
||||||
|
|
||||||
|
A=X
|
||||||
|
X=Y
|
||||||
|
X=X
|
||||||
|
|
||||||
_vm_write_str(s1)
|
_vm_write_str(s1)
|
||||||
s1 = s2
|
s1 = s2
|
||||||
_vm_write_str(s1)
|
_vm_write_str(s1)
|
||||||
|
@ -79,6 +79,9 @@ fun main(args: Array<String>) {
|
|||||||
val compiler = Compiler(compilerOptions)
|
val compiler = Compiler(compilerOptions)
|
||||||
val intermediate = compiler.compile(moduleAst, heap)
|
val intermediate = compiler.compile(moduleAst, heap)
|
||||||
intermediate.optimize()
|
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 stackVmFilename = intermediate.name + "_stackvm.txt"
|
||||||
val stackvmFile = PrintStream(File(stackVmFilename), "utf-8")
|
val stackvmFile = PrintStream(File(stackVmFilename), "utf-8")
|
||||||
|
@ -72,6 +72,8 @@ class HeapValues {
|
|||||||
|
|
||||||
private val heap = mutableListOf<HeapValue>()
|
private val heap = mutableListOf<HeapValue>()
|
||||||
|
|
||||||
|
fun size(): Int = heap.size
|
||||||
|
|
||||||
fun add(type: DataType, str: String): Int {
|
fun add(type: DataType, str: String): Int {
|
||||||
if (str.isEmpty() || str.length > 255)
|
if (str.isEmpty() || str.length > 255)
|
||||||
throw IllegalArgumentException("string length must be 1-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 memory = mutableMapOf<Int, List<Value>>()
|
||||||
private val labels = mutableMapOf<String, Instruction>()
|
private val labels = mutableMapOf<String, Instruction>()
|
||||||
val numVariables: Int
|
val numVariables: Int
|
||||||
get() {return variables.size}
|
get() {return variables.flatMap { it -> it.value.keys }.size}
|
||||||
val numInstructions: Int
|
val numInstructions: Int
|
||||||
get() {return instructions.size}
|
get() {return instructions.filter { it.opcode!=Opcode.LINE }.size}
|
||||||
|
|
||||||
fun optimize() {
|
fun optimize() {
|
||||||
println("\nOptimizing stackVM code...")
|
println("Optimizing stackVM code...")
|
||||||
optimizeDataConversionAndUselessDiscards()
|
optimizeDataConversionAndUselessDiscards()
|
||||||
|
optimizeVariableCopying()
|
||||||
|
optimizeMultipleSequentialLineInstrs()
|
||||||
// todo optimize stackvm code more
|
// todo optimize stackvm code more
|
||||||
|
|
||||||
// remove nops (that are not a label)
|
// remove nops (that are not a label)
|
||||||
this.instructions.removeIf { it.opcode==Opcode.NOP && it !is LabelInstr }
|
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() {
|
private fun optimizeDataConversionAndUselessDiscards() {
|
||||||
// - push value followed by a data type conversion -> push the value in the correct type and remove the conversion
|
// - 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
|
// - 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
|
// create the heap of all variables used in all blocks and scopes
|
||||||
val varGather = VarGatherer(intermediate)
|
val varGather = VarGatherer(intermediate)
|
||||||
varGather.process(module)
|
varGather.process(module)
|
||||||
println(" ${intermediate.numVariables} allocated variables and constants")
|
|
||||||
|
|
||||||
val translator = StatementTranslator(intermediate, namespace, heap)
|
val translator = StatementTranslator(intermediate, namespace, heap)
|
||||||
translator.process(module)
|
translator.process(module)
|
||||||
println(" ${intermediate.numInstructions} vm instructions")
|
|
||||||
|
|
||||||
return intermediate
|
return intermediate
|
||||||
}
|
}
|
||||||
@ -361,6 +414,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
|
|||||||
override fun process(subroutine: Subroutine): IStatement {
|
override fun process(subroutine: Subroutine): IStatement {
|
||||||
if(subroutine.asmAddress==null) {
|
if(subroutine.asmAddress==null) {
|
||||||
stackvmProg.label(subroutine.scopedname)
|
stackvmProg.label(subroutine.scopedname)
|
||||||
|
stackvmProg.line(subroutine.position)
|
||||||
// note: the caller has already written the arguments into the subroutine's parameter variables.
|
// note: the caller has already written the arguments into the subroutine's parameter variables.
|
||||||
translate(subroutine.statements)
|
translate(subroutine.statements)
|
||||||
} else {
|
} else {
|
||||||
@ -371,6 +425,7 @@ private class StatementTranslator(private val stackvmProg: StackVmProgram,
|
|||||||
|
|
||||||
override fun process(block: Block): IStatement {
|
override fun process(block: Block): IStatement {
|
||||||
stackvmProg.label(block.scopedname)
|
stackvmProg.label(block.scopedname)
|
||||||
|
stackvmProg.line(block.position)
|
||||||
translate(block.statements)
|
translate(block.statements)
|
||||||
return super.process(block)
|
return super.process(block)
|
||||||
}
|
}
|
||||||
|
@ -98,6 +98,10 @@ class Program (val name: String,
|
|||||||
val args = if(parts.size==2) parts[1] else null
|
val args = if(parts.size==2) parts[1] else null
|
||||||
val instruction = when(opcode) {
|
val instruction = when(opcode) {
|
||||||
Opcode.LINE -> Instruction(opcode, null, callLabel = args)
|
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.JUMP, Opcode.CALL, Opcode.BNEG, Opcode.BPOS,
|
||||||
Opcode.BZ, Opcode.BNZ, Opcode.BCS, Opcode.BCC -> {
|
Opcode.BZ, Opcode.BNZ, Opcode.BCS, Opcode.BCC -> {
|
||||||
if(args!!.startsWith('$')) {
|
if(args!!.startsWith('$')) {
|
||||||
|
@ -32,6 +32,11 @@ enum class Opcode {
|
|||||||
POP_VAR_W, // pop word value into variable
|
POP_VAR_W, // pop word value into variable
|
||||||
POP_VAR_F, // pop float 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
|
// numeric arithmetic
|
||||||
ADD_B,
|
ADD_B,
|
||||||
ADD_W,
|
ADD_W,
|
||||||
@ -198,6 +203,7 @@ val opcodesWithVarArgument = setOf(
|
|||||||
Opcode.ROL2_VAR, Opcode.ROL2_VAR_W, Opcode.ROR2_VAR, Opcode.ROR2_VAR_W,
|
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.POP_VAR, Opcode.POP_VAR_W, Opcode.POP_VAR_F,
|
||||||
Opcode.PUSH_VAR, Opcode.PUSH_VAR_W, Opcode.PUSH_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.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
|
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,
|
open class Instruction(val opcode: Opcode,
|
||||||
val arg: Value? = null,
|
val arg: Value? = null,
|
||||||
val callLabel: String? = null)
|
val callLabel: String? = null,
|
||||||
|
val callLabel2: String? = null)
|
||||||
{
|
{
|
||||||
lateinit var next: Instruction
|
lateinit var next: Instruction
|
||||||
var nextAlt: Instruction? = null
|
var nextAlt: Instruction? = null
|
||||||
@ -267,7 +274,7 @@ open class Instruction(val opcode: Opcode,
|
|||||||
}
|
}
|
||||||
opcode in opcodesWithVarArgument -> {
|
opcode in opcodesWithVarArgument -> {
|
||||||
// opcodes that manipulate a variable
|
// opcodes that manipulate a variable
|
||||||
"${opcode.toString().toLowerCase()} $callLabel"
|
"${opcode.toString().toLowerCase()} ${callLabel?:""} ${callLabel2?:""}".trimEnd()
|
||||||
}
|
}
|
||||||
callLabel==null -> "${opcode.toString().toLowerCase()} $argStr"
|
callLabel==null -> "${opcode.toString().toLowerCase()} $argStr"
|
||||||
else -> "${opcode.toString().toLowerCase()} $callLabel $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))
|
checkDt(variable, setOf(DataType.WORD, DataType.STR, DataType.STR_P, DataType.STR_S, DataType.STR_PS))
|
||||||
variables[ins.callLabel!!] = value
|
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 -> {
|
Opcode.POP_VAR_F -> {
|
||||||
val value = evalstack.pop()
|
val value = evalstack.pop()
|
||||||
checkDt(value, DataType.FLOAT)
|
checkDt(value, DataType.FLOAT)
|
||||||
|
@ -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
|
@Test
|
||||||
fun testAdd() {
|
fun testAdd() {
|
||||||
testBinaryOperator(Value(DataType.BYTE, 140), Opcode.ADD_B, Value(DataType.BYTE, 222), Value(DataType.BYTE, 106))
|
testBinaryOperator(Value(DataType.BYTE, 140), Opcode.ADD_B, Value(DataType.BYTE, 222), Value(DataType.BYTE, 106))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user