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 s2 = "bye"
|
||||
|
||||
A=X
|
||||
X=Y
|
||||
X=X
|
||||
|
||||
_vm_write_str(s1)
|
||||
s1 = s2
|
||||
_vm_write_str(s1)
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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('$')) {
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
|
Loading…
x
Reference in New Issue
Block a user