aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/rt.zc
diff options
context:
space:
mode:
authorGravatar zinc <zinc@downsta.ir> 2026-10-14 16:25:05 +0330
committerGravatar zinc <zinc@downsta.ir> 2026-10-14 16:25:05 +0330
commit8a7092df7b7fc8a5eb4f02ef14588246bd111daf (patch)
tree0d9d28ca93ccbddb1f99cb02b88ed466e0ade481 /src/runtime/rt.zc
parenthistory: 43-stdlib notes point forward to zc0 no longer reserving u, m and d (diff)
parentmerge bounds checking (diff)
Merge branch 'main' into work/xref
Diffstat (limited to 'src/runtime/rt.zc')
-rw-r--r--src/runtime/rt.zc31
1 files changed, 31 insertions, 0 deletions
diff --git a/src/runtime/rt.zc b/src/runtime/rt.zc
index b468f2a..7089e26 100644
--- a/src/runtime/rt.zc
+++ b/src/runtime/rt.zc
@@ -1,6 +1,9 @@
namespace rt;
include run;
+// the runtime pokes at headers and raw memory on purpose and runs at full speed: no index bounds checks
+unbounded;
+
// ============================================================================
// rt.zc: the runtime half of the genome. memory map, i/o, arithmetic helpers,
// the heap (size-class free lists) and the mark-sweep collector.
@@ -84,6 +87,13 @@ enhance ishl(a, b) micro "a = shl a b";
enhance lfa(k) micro "r1 = ld 589824; k = add r1 k";
// 1 when i is outside 0..n-1 (the unsigned compare also catches negative i), else 0.
enhance outside(i, n) micro "i = ltu i n; i = xor i 1";
+// the index check of a pointer, as one instruction: 1 when p is the start of a heap
+// block (its check word p[-3] is p ^ MAGIC) and the byte or word offset i * m is not
+// inside the block's payload (q units per word: 1 for word elements, 8 for packed
+// ones), else 0. a load outside memory reads 0, so any p is safe to ask about. the
+// payload is paylen (p[-5] - HDR words, at most the size an o3 raw block was asked for,
+// which is the complement of its type word p[-1] when that is negative). see bchk.
+enhance bnd(p, i, m, q) micro "r6 = sub p 3; r6 = ld r6; r7 = xor p 0x2B7E1516; r6 = eq r6 r7; r7 = sub p 5; r7 = ld r7; r7 = sub r7 5; r8 = sub p 1; r8 = ld r8; r8 = not r8; r7 = minu r7 r8; r7 = mul r7 q; r8 = mul i m; r8 = ltu r8 r7; r8 = xor r8 1; r0 = and r6 r8";
// ---- bit operations and float conversions ----
// these use single machine instructions where the cpu has them.
@@ -1100,6 +1110,27 @@ word dat(word h, int i) {
if (i < 0 || i >= h[0]) oob(i, h[0]);
return h[2] + i * h[3];
}
+// the bounds check of p[i] on a pointer (or an untyped word), emitted by the compiler
+// in front of the access. a pointer has no length, but a pointer to the start of
+// a heap block does have one: the words the block holds (paylen). for any
+// other pointer (the middle of a block, a frame, a global, memory managed by hand)
+// nothing is known and this returns at once. sz is the element size in words, or
+// minus the element size in bytes for packed elements. the length is the block's
+// payload, which a buddy block rounds up to a power of two; an o3 raw block is exact.
+int bchk(word p, int i, int sz) {
+ int n;
+ if (p < HEAP_LO + HDR || p >= hbump || p[-3] != (p ^ MAGIC)) return 0;
+ n = paylen(p);
+ if (sz > 0) {
+ if (i >= 0 && i < n && i * sz < n) return 0;
+ oob(i, (n + sz - 1) / sz);
+ return 0;
+ }
+ sz = 0 - sz; n = n * 8;
+ if (i >= 0 && i < n && i * sz < n) return 0;
+ oob(i, (n + sz - 1) / sz);
+ return 0;
+}
// add x to the end of a growable array h, growing its items when they are full. the
// array owns the items, so x is handed to it (tagged d).
unchecked int dpush(word h, .d x) {