plum

#treesitter#compiler#wasm

git clone https://git.pyrossh.dev/plum

A statically typed, imperative programming language inspired by rust, python


872454e — Peter John 2026-11-01T22:20:50+05:30
perf: bytesEqual intrinsic for Str.eq
plum-std/ByteSlice.plum CHANGED
@@ -37,6 +37,11 @@ fun makeBytes(n: Int) -> []Byte =
37
37
  fun copyBytes(dst: []Byte, dstStart: Int, src: []Byte, srcStart: Int, n: Int) -> Unit =
38
38
  todo
39
39
 
40
+ # True when `a[aStart..aStart+n]` and `b[bStart..bStart+n]` hold the same
41
+ # bytes (the memcmp analog; `n == 0` is always true). Traps if out of range.
42
+ fun bytesEqual(a: []Byte, aStart: Int, b: []Byte, bStart: Int, n: Int) -> Bool =
43
+ todo
44
+
40
45
  # Like `copyBytes`, but the source is a `Str` — ordinary Plum glue over
41
46
  # `copyBytes` itself, reaching through `Str`'s `bytes: []Byte` field.
42
47
  fun copyStrToBytes(dst: []Byte, dstStart: Int, src: Str, srcStart: Int, n: Int) -> Unit =
@@ -50,3 +55,21 @@ fun bytesToStr(src: []Byte, start: Int, n: Int) -> Str =
50
55
  data := makeBytes(n)
51
56
  copyBytes(data, 0, src, start, n)
52
57
  return Str(bytes: data)
58
+
59
+ fun testBytesOf(s: Str) -> []Byte =
60
+ out := makeBytes(s.length())
61
+ copyStrToBytes(out, 0, s, 0, s.length())
62
+ return out
63
+
64
+ test "bytesEqual: equal, differ at first, differ at last, n == 0, offsets"
65
+ a := testBytesOf("hello world")
66
+ b := testBytesOf("hello world")
67
+ assert bytesEqual(a, 0, b, 0, 11)
68
+ c := testBytesOf("jello world")
69
+ assert !bytesEqual(a, 0, c, 0, 11)
70
+ d := testBytesOf("hello worlD")
71
+ assert !bytesEqual(a, 0, d, 0, 11)
72
+ assert bytesEqual(a, 0, d, 0, 10)
73
+ assert bytesEqual(a, 3, c, 3, 0)
74
+ assert bytesEqual(a, 6, testBytesOf("xxworld"), 2, 5)
75
+ assert !bytesEqual(a, 6, testBytesOf("xxwurld"), 2, 5)
plum-std/Str.plum CHANGED
@@ -156,14 +156,7 @@ record Str(bytes: []Byte) =
156
156
  n := self.length()
157
157
  if n != o.length()
158
158
  return false
159
- a := self.bytes
160
- b := o.bytes
161
- i := 0
162
- while i < n
163
- if a.get(i) != b.get(i)
159
+ return bytesEqual(self.bytes, 0, o.bytes, 0, n)
164
- return false
165
- i = i + 1
166
- return true
167
160
 
168
161
  fun toStr(self) -> Str =
169
162
  self
plum-wasm-codegen/src/lib.rs CHANGED
@@ -2706,6 +2706,56 @@ fn compileIntrinsicFnBody(f: &ast::Fn, subst: &Subst) -> Option<Vec<u8>> {
2706
2706
  Instruction::End.encode(&mut body);
2707
2707
  Some(body)
2708
2708
  }
2709
+ // bytesEqual(a: []Byte, aStart: Int, b: []Byte, bStart: Int, n: Int) -> Bool
2710
+ // — the memcmp analog: a tight loop with early exit on mismatch.
2711
+ // Locals: 5 = i (i32), 6 = n (i32), 7 = aStart (i32), 8 = bStart (i32).
2712
+ (None, "bytesEqual") => {
2713
+ let mut body = vec![1u8, 4u8, 0x7f];
2714
+ let wrap = |b: &mut Vec<u8>, l: u32| {
2715
+ Instruction::LocalGet(l).encode(b);
2716
+ Instruction::I32WrapI64.encode(b);
2717
+ };
2718
+ wrap(&mut body, 4);
2719
+ Instruction::LocalSet(6).encode(&mut body);
2720
+ wrap(&mut body, 1);
2721
+ Instruction::LocalSet(7).encode(&mut body);
2722
+ wrap(&mut body, 3);
2723
+ Instruction::LocalSet(8).encode(&mut body);
2724
+ Instruction::Block(BlockType::Result(ValType::I32)).encode(&mut body);
2725
+ Instruction::Loop(BlockType::Empty).encode(&mut body);
2726
+ Instruction::LocalGet(5).encode(&mut body);
2727
+ Instruction::LocalGet(6).encode(&mut body);
2728
+ Instruction::I32GeS.encode(&mut body);
2729
+ Instruction::If(BlockType::Empty).encode(&mut body);
2730
+ Instruction::I32Const(1).encode(&mut body);
2731
+ Instruction::Br(2).encode(&mut body);
2732
+ Instruction::End.encode(&mut body);
2733
+ Instruction::LocalGet(0).encode(&mut body);
2734
+ Instruction::LocalGet(7).encode(&mut body);
2735
+ Instruction::LocalGet(5).encode(&mut body);
2736
+ Instruction::I32Add.encode(&mut body);
2737
+ Instruction::ArrayGetU(byte_array_type_idx).encode(&mut body);
2738
+ Instruction::LocalGet(2).encode(&mut body);
2739
+ Instruction::LocalGet(8).encode(&mut body);
2740
+ Instruction::LocalGet(5).encode(&mut body);
2741
+ Instruction::I32Add.encode(&mut body);
2742
+ Instruction::ArrayGetU(byte_array_type_idx).encode(&mut body);
2743
+ Instruction::I32Ne.encode(&mut body);
2744
+ Instruction::If(BlockType::Empty).encode(&mut body);
2745
+ Instruction::I32Const(0).encode(&mut body);
2746
+ Instruction::Br(2).encode(&mut body);
2747
+ Instruction::End.encode(&mut body);
2748
+ Instruction::LocalGet(5).encode(&mut body);
2749
+ Instruction::I32Const(1).encode(&mut body);
2750
+ Instruction::I32Add.encode(&mut body);
2751
+ Instruction::LocalSet(5).encode(&mut body);
2752
+ Instruction::Br(0).encode(&mut body);
2753
+ Instruction::End.encode(&mut body); // loop
2754
+ Instruction::Unreachable.encode(&mut body);
2755
+ Instruction::End.encode(&mut body); // block
2756
+ Instruction::End.encode(&mut body); // function
2757
+ Some(body)
2758
+ }
2709
2759
  _ => None,
2710
2760
  }
2711
2761
  }