Skip to content

Commit 453a7f4

Browse files
committed
add a little interpreter for affine expressions
it lowk kinda slow tho
1 parent d10c500 commit 453a7f4

7 files changed

Lines changed: 106 additions & 48 deletions

File tree

src/commonMain/kotlin/dev/rdh/bf/affine.kt

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ package dev.rdh.bf
66
* This IR has explicit block semantics:
77
* - offsets are normalized relative to a block base shift
88
* - writes are grouped into batches with entry-state references
9-
* - [BFAffinePrint]/[BFAffineInput] act as ordering barriers between write batches
9+
* - [BFAffineOutput]/[BFAffineInput] act as ordering barriers between write batches
1010
*/
1111
sealed interface BFAffineOp
1212

@@ -48,7 +48,7 @@ sealed interface BFAffineSegment
4848
data class BFAffineWriteBatch(val writes: List<BFAffineWrite>) : BFAffineSegment
4949

5050
/** Print the byte at [offset] relative to the block-shifted pointer. */
51-
data class BFAffinePrint(val offset: Int) : BFAffineSegment
51+
data class BFAffineOutput(val offset: Int) : BFAffineSegment
5252

5353
/** Read one byte into [offset] relative to the block-shifted pointer. */
5454
data class BFAffineInput(val offset: Int) : BFAffineSegment
@@ -62,7 +62,7 @@ data class BFAffineWrite(
6262
/**
6363
* Affine expression over referenced cell values.
6464
*
65-
* `constant + sum(terms[i].coefficient * ref(terms[i].offset))`
65+
* `constant + sum(terms[i].coefficient * tape[ptr + terms[i].offset])`
6666
*/
6767
data class BFAffineExpr(
6868
val constant: Int = 0,
@@ -82,6 +82,36 @@ private data class MutableExpr(
8282
terms = terms.toMutableMap(),
8383
constant = constant,
8484
)
85+
86+
operator fun plus(other: MutableExpr): MutableExpr {
87+
val out = copyExpr()
88+
out.constant += other.constant
89+
for ((ref, coeff) in other.terms) {
90+
val next = (out.terms[ref] ?: 0) + coeff
91+
if (next == 0) {
92+
out.terms.remove(ref)
93+
} else {
94+
out.terms[ref] = next
95+
}
96+
}
97+
return out
98+
}
99+
100+
operator fun times(multiplier: Int): MutableExpr {
101+
if (multiplier == 0) return constExpr(0)
102+
val out = copyExpr()
103+
out.constant *= multiplier
104+
val refs = out.terms.keys.toList()
105+
for (ref in refs) {
106+
val next = (out.terms[ref] ?: 0) * multiplier
107+
if (next == 0) {
108+
out.terms.remove(ref)
109+
} else {
110+
out.terms[ref] = next
111+
}
112+
}
113+
return out
114+
}
85115
}
86116

87117
private sealed interface LoweringStep
@@ -94,36 +124,6 @@ private data class StepCopy(val sourceOffset: Int, val targetOffset: Int, val mu
94124
private fun refExpr(offset: Int): MutableExpr = MutableExpr(terms = mutableMapOf(offset to 1))
95125
private fun constExpr(value: Int): MutableExpr = MutableExpr(constant = value)
96126

97-
private operator fun MutableExpr.plus(other: MutableExpr): MutableExpr {
98-
val out = copyExpr()
99-
out.constant += other.constant
100-
for ((ref, coeff) in other.terms) {
101-
val next = (out.terms[ref] ?: 0) + coeff
102-
if (next == 0) {
103-
out.terms.remove(ref)
104-
} else {
105-
out.terms[ref] = next
106-
}
107-
}
108-
return out
109-
}
110-
111-
private operator fun MutableExpr.times(multiplier: Int): MutableExpr {
112-
if (multiplier == 0) return constExpr(0)
113-
val out = copyExpr()
114-
out.constant *= multiplier
115-
val refs = out.terms.keys.toList()
116-
for (ref in refs) {
117-
val next = (out.terms[ref] ?: 0) * multiplier
118-
if (next == 0) {
119-
out.terms.remove(ref)
120-
} else {
121-
out.terms[ref] = next
122-
}
123-
}
124-
return out
125-
}
126-
127127
/**
128128
* Lowers a loop-free basic block to affine form.
129129
*
@@ -222,7 +222,7 @@ private fun lowerLinearBlock(block: List<BFOperation>): BFAffineBlock? {
222222
}
223223
is StepPrint -> {
224224
flushState()
225-
segments += BFAffinePrint(offset = eff(step.offset))
225+
segments += BFAffineOutput(offset = eff(step.offset))
226226
}
227227
is StepInput -> {
228228
flushState()
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package dev.rdh.bf
2+
3+
object AffineInterpreterRunner : BfRunner {
4+
override fun compile(program: Iterable<BFOperation>): BfExecutable {
5+
val lowered = bfLowerAffine(program.toList())
6+
return BfExecutable { i, o -> bfRunAffine(lowered, i, o) }
7+
}
8+
}
9+
10+
@OptIn(ExperimentalUnsignedTypes::class)
11+
fun bfRunAffine(program: List<BFAffineOp>, stdin: BfInput, stdout: BfOutput) {
12+
runAffineImpl(UByteArray(TAPE_SIZE), 0, program, stdin, stdout)
13+
stdout.flush()
14+
}
15+
16+
@OptIn(ExperimentalUnsignedTypes::class)
17+
private fun runAffineImpl(tape: UByteArray, pointer: Int,
18+
program: List<BFAffineOp>,
19+
stdin: BfInput, stdout: BfOutput): Int {
20+
var pointer = pointer
21+
22+
for (op in program) {
23+
when (op) {
24+
is BFAffineBlock -> {
25+
pointer += op.baseShift
26+
for (seg in op.segments) {
27+
when (seg) {
28+
is BFAffineInput -> {
29+
tape[pointer] = stdin.readByte().toUByte()
30+
}
31+
is BFAffineOutput -> {
32+
stdout.writeByte(tape[pointer].toInt())
33+
}
34+
is BFAffineWriteBatch -> {
35+
val values = seg.writes
36+
.flatMap { write -> write.expr.terms.map { it.offset } }
37+
.distinct()
38+
.associateWith { offset ->
39+
tape[pointer.wrappingAdd(offset, TAPE_SIZE)].toInt()
40+
}
41+
for (write in seg.writes) {
42+
val expr = write.expr.constant + write.expr.terms.sumOf { term ->
43+
values[term.offset]!! * term.coefficient
44+
}
45+
tape[pointer.wrappingAdd(write.offset, TAPE_SIZE)] = expr.toUByte()
46+
}
47+
}
48+
}
49+
}
50+
pointer += op.pointerDelta - op.baseShift
51+
}
52+
is BFAffineLoop -> {
53+
while (tape[pointer].toInt() != 0) {
54+
pointer = runAffineImpl(tape, pointer, op, stdin, stdout)
55+
}
56+
}
57+
}
58+
}
59+
60+
return pointer
61+
}

src/commonMain/kotlin/dev/rdh/bf/interpreter.kt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package dev.rdh.bf
22

3+
object InterpreterRunner : BfRunner {
4+
override fun compile(program: Iterable<BFOperation>): BfExecutable {
5+
return BfExecutable { i, o -> bfRun(program, i, o) }
6+
}
7+
}
8+
39
@OptIn(ExperimentalUnsignedTypes::class)
4-
fun bfRun(program: Iterable<BFOperation>,
5-
stdin: BfInput,
6-
stdout: BfOutput,
7-
) {
10+
fun bfRun(program: Iterable<BFOperation>, stdin: BfInput, stdout: BfOutput) {
811
runImpl(UByteArray(TAPE_SIZE), 0, program.toList(), stdin, stdout)
912
stdout.flush()
1013
}

src/commonMain/kotlin/dev/rdh/bf/runner.kt

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,6 @@ interface BfRunner {
1212
}
1313
}
1414

15-
object InterpreterRunner : BfRunner {
16-
override fun compile(program: Iterable<BFOperation>): BfExecutable {
17-
return BfExecutable { i, o -> bfRun(program, i, o) }
18-
}
19-
}
20-
2115
/**
2216
* Options for the system runner, which may be used to enable or disable certain features of the runner.
2317
* Not all of these options may be supported on all platforms, and may be ignored if the runner does not support them.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ fun bfCompile(program: Iterable<BFAffineOp>, opts: SystemRunnerOptions): (Reader
201201
}
202202
}
203203

204-
is BFAffinePrint -> {
204+
is BFAffineOutput -> {
205205
load(output)
206206
loadCell(segment.offset)
207207
int(0xFF)

src/jvmTest/kotlin/AffineLoweringTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import dev.rdh.bf.BFAffineBlock
22
import dev.rdh.bf.BFAffineInput
33
import dev.rdh.bf.BFAffineLoop
4-
import dev.rdh.bf.BFAffinePrint
4+
import dev.rdh.bf.BFAffineOutput
55
import dev.rdh.bf.BFAffineWrite
66
import dev.rdh.bf.BFAffineWriteBatch
77
import dev.rdh.bf.Copy
@@ -54,7 +54,7 @@ class AffineLoweringTest {
5454
assertEquals(0, block.pointerDelta)
5555
assertEquals(5, block.segments.size)
5656
assertIs<BFAffineWriteBatch>(block.segments[0])
57-
assertIs<BFAffinePrint>(block.segments[1])
57+
assertIs<BFAffineOutput>(block.segments[1])
5858
assertIs<BFAffineWriteBatch>(block.segments[2])
5959
assertIs<BFAffineInput>(block.segments[3])
6060
assertIs<BFAffineWriteBatch>(block.segments[4])

src/wasmJsMain/kotlin/dev/rdh/bf/jit.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ private fun makeProgram(ns: BinaryenNamespace, m: BinaryenModule, program: List<
196196

197197
for (segment in block.segments) {
198198
when (segment) {
199-
is BFAffinePrint -> {
199+
is BFAffineOutput -> {
200200
result += m.call(
201201
"write",
202202
listOf(load(segment.offset)),

0 commit comments

Comments
 (0)