diff options
| author | 2026-10-14 16:09:07 +0330 | |
|---|---|---|
| committer | 2026-10-14 16:09:07 +0330 | |
| commit | e228e7609929763d5acddb728ba543832fcffc6a (patch) | |
| tree | 9b4ecc4d46bf38e8d20234d631a6d41543daa211 /src/runtime | |
| parent | merge: memdebug obj_base via size classes (diff) | |
| parent | bounds checking: constant loop bound proof works, plain-op fixed-array check,... (diff) | |
merge bounds checking
Diffstat (limited to 'src/runtime')
| -rw-r--r-- | src/runtime/rt.zc | 31 | ||||
| -rw-r--r-- | src/runtime/run.zc | 3 |
2 files changed, 34 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) { diff --git a/src/runtime/run.zc b/src/runtime/run.zc index 19a5234..69fc30c 100644 --- a/src/runtime/run.zc +++ b/src/runtime/run.zc @@ -1,6 +1,9 @@ namespace run; include rt; +// the run-only runtime pokes at headers and raw memory on purpose and runs at full speed: no index bounds checks +unbounded; + // run-only image: the runtime, a collector root set, and start. there is no // resident compiler and none of its tables. put this between rt.zc and a // program that defines main. zc0 accepts the bootstrap subset; build.zc uses |
