Skip to content

Commit 6735bba

Browse files
committed
simplify operation-merging passes into one, log compile time on jvm
1 parent 633206e commit 6735bba

6 files changed

Lines changed: 78 additions & 81 deletions

File tree

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package dev.rdh.bf.opt
2+
3+
import dev.rdh.bf.BFOperation
4+
import dev.rdh.bf.Copy
5+
import dev.rdh.bf.PointerMove
6+
import dev.rdh.bf.SetToConstant
7+
import dev.rdh.bf.ValueChange
8+
9+
/**
10+
* Final peephole pass that merges the current operation with the previously emitted one.
11+
*/
12+
internal object OpMerger : OptimisationPass {
13+
override fun run(program: MutableList<BFOperation>) {
14+
val merged = mutableListOf<BFOperation>()
15+
16+
for (op in program) {
17+
var current = op
18+
19+
while (merged.isNotEmpty()) {
20+
val previous = merged.last()
21+
val combined = tryMerge(previous, current) ?: break
22+
merged.removeLast()
23+
current = combined
24+
}
25+
26+
when (current) {
27+
is PointerMove -> if (current.value != 0) merged += current
28+
is ValueChange -> if (current.value != 0) merged += current
29+
else -> merged += current
30+
}
31+
}
32+
33+
program.clear()
34+
program.addAll(merged)
35+
}
36+
37+
private fun tryMerge(previous: BFOperation, current: BFOperation): BFOperation? {
38+
if (previous is PointerMove && current is PointerMove) {
39+
return PointerMove(previous.value + current.value)
40+
}
41+
42+
if (previous is ValueChange && current is ValueChange && previous.offset == current.offset) {
43+
return ValueChange(previous.value + current.value, previous.offset)
44+
}
45+
46+
if (previous is SetToConstant && current is ValueChange && previous.offset == current.offset) {
47+
val value = (previous.value.toInt() + current.value).toUByte()
48+
return SetToConstant(value = value, offset = previous.offset)
49+
}
50+
51+
if (current is SetToConstant && previous.overwritesCell(current.offset)) {
52+
return current
53+
}
54+
55+
return null
56+
}
57+
58+
private fun BFOperation.overwritesCell(offset: Int): Boolean = when (this) {
59+
is SetToConstant -> this.offset == offset
60+
is ValueChange -> this.offset == offset
61+
is Copy -> this.offset == offset
62+
else -> false
63+
}
64+
}

src/commonMain/kotlin/dev/rdh/bf/opt/RunLengthMerger.kt

Lines changed: 0 additions & 32 deletions
This file was deleted.

src/commonMain/kotlin/dev/rdh/bf/opt/WriteMerger.kt

Lines changed: 0 additions & 43 deletions
This file was deleted.

src/commonMain/kotlin/dev/rdh/bf/opt/optimise.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ fun bfOptimise(program: Iterable<BFOperation>, iterations: Int = 5): List<BFOper
1717
val program = program.toMutableList()
1818

1919
val corePasses = arrayOf(
20-
RunLengthMerger, ConstantReplacer, OffsetAdder, CopyLoopReplacer, WriteMerger
20+
ConstantReplacer, OffsetAdder, CopyLoopReplacer, OpMerger
2121
)
2222
val cleanupPasses = arrayOf(
2323
DeadStartRemover,
@@ -38,7 +38,5 @@ fun bfOptimise(program: Iterable<BFOperation>, iterations: Int = 5): List<BFOper
3838
it.run(program)
3939
}
4040

41-
LoopOptimiser(ConsecutiveLoopRemover, ZeroRemover).run(program)
42-
4341
return program
4442
}

src/jvmMain/kotlin/dev/rdh/bf/main.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import kotlin.io.path.Path
88
import kotlin.io.path.deleteRecursively
99
import kotlin.io.path.readText
1010
import kotlin.time.measureTime
11+
import kotlin.time.measureTimedValue
1112

1213
@OptIn(ExperimentalPathApi::class)
1314
fun main(args: Array<String>) {
@@ -148,11 +149,12 @@ private fun runProgram(
148149
} else {
149150
InterpreterRunner
150151
}
151-
val executable = runner.compile(program)
152+
val (executable, cTime) = measureTimedValue { runner.compile(program) }
152153

153154
val time = measureTime { executable.run(SysInInput, SysOutOutput) }
154155

155156
if (printTime) {
157+
System.err.println("Compile time: ${formatTime(cTime)}")
156158
System.err.println("Execution time: ${formatTime(time)}")
157159
}
158160
}

src/wasmJsMain/resources/runner.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,21 @@ const CTRL_DONE = 2;
2727
const OUTPUT_BUFFER_SIZE = 1024 * 1024;
2828
const supportsSab = globalThis.crossOriginIsolated === true && typeof SharedArrayBuffer === "function";
2929

30+
function formatTime(ms) {
31+
if (ms < 1000) {
32+
return `${ms.toFixed(2)} ms`;
33+
} else {
34+
return `${(ms / 1000).toFixed(2)} s`;
35+
}
36+
}
37+
3038
function finishRun(doneMsg) {
3139
if (doneMsg == null) return;
3240
running = false;
3341
runBtn.disabled = false;
3442
outputEl.value += outputDecoder.decode();
35-
const compileLabel = doneMsg.compileMs > 0.0 ? `${doneMsg.compileMs.toFixed(2)} ms` : "cached";
36-
timeEl.innerHTML = `<span title="compile: ${compileLabel} ms, execute: ${doneMsg.runMs.toFixed(2)} ms">done in ${doneMsg.workerTotalMs.toFixed(2)} ms</span>`;
43+
const compileLabel = doneMsg.compileMs > 0.0 ? formatTime(doneMsg.compileMs) : "cached";
44+
timeEl.innerHTML = `<span title="compile: ${compileLabel}, execute: ${formatTime(doneMsg.runMs)}">done in ${formatTime(doneMsg.workerTotalMs)}</span>`;
3745
}
3846

3947
function drainBufAndMaybeFinish() {

0 commit comments

Comments
 (0)