aboutsummaryrefslogtreecommitdiff
path: root/tests/programs
diff options
context:
space:
mode:
Diffstat (limited to 'tests/programs')
-rw-r--r--tests/programs/alias.zc21
-rw-r--r--tests/programs/allocfast.zc45
-rw-r--r--tests/programs/annotate.expected33
-rw-r--r--tests/programs/annotate.zc33
-rw-r--r--tests/programs/arena.zc16
-rw-r--r--tests/programs/args.zc16
-rw-r--r--tests/programs/arrays.zc23
-rw-r--r--tests/programs/backtick.zc9
-rw-r--r--tests/programs/bareexit.zc7
-rw-r--r--tests/programs/bareio.zc28
-rw-r--r--tests/programs/basics.zc21
-rw-r--r--tests/programs/bench.zc29
-rw-r--r--tests/programs/bench_bytes.zc19
-rw-r--r--tests/programs/bench_share.zc30
-rw-r--r--tests/programs/bench_strings.zc27
-rw-r--r--tests/programs/bitops.expected2
-rw-r--r--tests/programs/bitops.zc26
-rw-r--r--tests/programs/borrowmove.zc2
-rw-r--r--tests/programs/bounds.zc28
-rw-r--r--tests/programs/bounds_hi.zc8
-rw-r--r--tests/programs/bounds_lo.zc8
-rw-r--r--tests/programs/bstr_io.zc22
-rw-r--r--tests/programs/buddy.zc16
-rw-r--r--tests/programs/bytes.zc44
-rw-r--r--tests/programs/cabi.expected5
-rw-r--r--tests/programs/cabi.zc46
-rw-r--r--tests/programs/cabi_o3.expected2
-rw-r--r--tests/programs/cabi_o3.zc30
-rw-r--r--tests/programs/cabi_sized.expected1
-rw-r--r--tests/programs/cabi_sized.zc28
-rw-r--r--tests/programs/capture.zc31
-rw-r--r--tests/programs/closure.zc52
-rw-r--r--tests/programs/csyn.zc18
-rw-r--r--tests/programs/decl_undefined.zc2
-rw-r--r--tests/programs/discard_m.zc3
-rw-r--r--tests/programs/doublefree.zc9
-rw-r--r--tests/programs/enhance.zc28
-rw-r--r--tests/programs/enumscope.zc9
-rw-r--r--tests/programs/enumtype.zc12
-rw-r--r--tests/programs/f32.expected1
-rw-r--r--tests/programs/f32.zc37
-rw-r--r--tests/programs/f32o3.zc14
-rw-r--r--tests/programs/fbench.zc37
-rw-r--r--tests/programs/float.zc32
-rw-r--r--tests/programs/fo3.zc50
-rw-r--r--tests/programs/ftext.zc37
-rw-r--r--tests/programs/ftruth.zc22
-rw-r--r--tests/programs/ftype.zc47
-rw-r--r--tests/programs/gc.zc18
-rw-r--r--tests/programs/generics.expected2
-rw-r--r--tests/programs/generics.zc54
-rw-r--r--tests/programs/genmeth.expected1
-rw-r--r--tests/programs/genmeth.zc36
-rw-r--r--tests/programs/growheap.zc32
-rw-r--r--tests/programs/growone.zc9
-rw-r--r--tests/programs/infer_return.zc3
-rw-r--r--tests/programs/interior.zc17
-rw-r--r--tests/programs/joins.zc31
-rw-r--r--tests/programs/manyerrors.zc6
-rw-r--r--tests/programs/memleak.zc11
-rw-r--r--tests/programs/memory_bounds.zc13
-rw-r--r--tests/programs/meta.zc43
-rw-r--r--tests/programs/methodown.zc15
-rw-r--r--tests/programs/misc.zc27
-rw-r--r--tests/programs/names.zc9
-rw-r--r--tests/programs/o3.zc75
-rw-r--r--tests/programs/o3_bounds.zc2
-rw-r--r--tests/programs/o3bytes.zc52
-rw-r--r--tests/programs/o3call.zc29
-rw-r--r--tests/programs/o3copy.zc12
-rw-r--r--tests/programs/o3deep.zc3
-rw-r--r--tests/programs/o3deep_over.zc3
-rw-r--r--tests/programs/o3direct.expected2
-rw-r--r--tests/programs/o3direct.zc33
-rw-r--r--tests/programs/o3fat.zc20
-rw-r--r--tests/programs/o3licm.zc46
-rw-r--r--tests/programs/o3push.expected1
-rw-r--r--tests/programs/o3push.zc38
-rw-r--r--tests/programs/o3scopes.zc17
-rw-r--r--tests/programs/o3zero.zc8
-rw-r--r--tests/programs/overflow_data.zc1
-rw-r--r--tests/programs/overflow_frame.zc2
-rw-r--r--tests/programs/overflow_return.zc2
-rw-r--r--tests/programs/packedfields.expected2
-rw-r--r--tests/programs/packedfields.zc42
-rw-r--r--tests/programs/quarantine.zc25
-rw-r--r--tests/programs/rawpid.zc6
-rw-r--r--tests/programs/rawsys.zc5
-rw-r--r--tests/programs/recycle.zc23
-rw-r--r--tests/programs/roots.zc17
-rw-r--r--tests/programs/scrub.zc9
-rw-r--r--tests/programs/share.zc34
-rw-r--r--tests/programs/share_dbg.zc10
-rw-r--r--tests/programs/sized.expected5
-rw-r--r--tests/programs/sized.zc80
-rw-r--r--tests/programs/sizedvec.expected2
-rw-r--r--tests/programs/sizedvec.zc39
-rw-r--r--tests/programs/static.zc2
-rw-r--r--tests/programs/stdbuf.zc18
-rw-r--r--tests/programs/stdmore.zc32
-rw-r--r--tests/programs/strings.zc37
-rw-r--r--tests/programs/structret.zc24
-rw-r--r--tests/programs/structval.zc26
-rw-r--r--tests/programs/sys.zc11
-rw-r--r--tests/programs/tagenum.zc26
-rw-r--r--tests/programs/tagenum_two.zc20
-rw-r--r--tests/programs/tagenum_warn.zc17
-rw-r--r--tests/programs/typedmethods.zc16
-rw-r--r--tests/programs/typeoff.zc2
-rw-r--r--tests/programs/typeok.zc21
-rw-r--r--tests/programs/types.zc55
-rw-r--r--tests/programs/uaf.zc8
-rw-r--r--tests/programs/uaf_in.zc9
-rw-r--r--tests/programs/umd.zc50
-rw-r--r--tests/programs/undefined_call.zc1
-rw-r--r--tests/programs/use_std.zc9
-rw-r--r--tests/programs/wide.zc9
-rw-r--r--tests/programs/zc0plain.zc23
-rw-r--r--tests/programs/zc0typed.zc23
119 files changed, 2477 insertions, 0 deletions
diff --git a/tests/programs/alias.zc b/tests/programs/alias.zc
new file mode 100644
index 0000000..2ebc3e5
--- /dev/null
+++ b/tests/programs/alias.zc
@@ -0,0 +1,21 @@
+// a pointer computed from a live object is a borrow of it: fine while the owner lives
+struct node { int v; node* next; };
+fn main() {
+ let p = new(4);
+ p[0] = 11; p[1] = 22;
+ let q = p + 1;
+ put_n(q[-1]); put_c(' ');
+ put_n(q[0]); put_c(' ');
+ node* a = new(node), b = new(node);
+ a.v = 3; b.v = 4; a.next = b;
+ node* c = a.next;
+ put_n(c.v); put_c(' ');
+ int** list = new(int*, 2);
+ int* item = new(int);
+ *item = 8;
+ list[1] = item;
+ int* e = list[1];
+ put_n(*e); put_c(10);
+ free(d*p); free(d*a); free(d*list);
+ return 0;
+}
diff --git a/tests/programs/allocfast.zc b/tests/programs/allocfast.zc
new file mode 100644
index 0000000..5ef2df2
--- /dev/null
+++ b/tests/programs/allocfast.zc
@@ -0,0 +1,45 @@
+// repeated allocations of a size known at compile time: new(T) of a struct of
+// numbers, and new(n) with a constant n. each pass of the loop makes an object
+// and frees it at the end of the block, so the allocator should hand back the
+// block it just took. the sums prove the objects were zeroed every time.
+struct point { int x, y; };
+struct line { point* a; int len; };
+
+fn loop_leaf(int n) {
+ let total = 0;
+ for (int i = 0; i < n; i++) {
+ point* p = new(point);
+ total += p.x + p.y; // zero from the allocator, every pass
+ p.x = i; p.y = 2;
+ }
+ return total;
+}
+fn loop_words(int n) {
+ let total = 0, i = 0;
+ while (i < n) {
+ let w = new(6);
+ total += w[0] + w[5];
+ w[0] = 7; w[5] = 9;
+ i++;
+ }
+ return total;
+}
+fn loop_owner(int n) {
+ let i = 0, total = 0;
+ while (i < n) {
+ line* l = new(line);
+ l.a = new(point);
+ l.a.x = i;
+ total += l.a.x + l.len;
+ i++;
+ }
+ return total;
+}
+fn main() {
+ let before = live_objects();
+ put_n(loop_leaf(50000)); put_c(' ');
+ put_n(loop_words(50000)); put_c(' ');
+ put_n(loop_owner(1000)); put_c(' ');
+ put_n(live_objects() - before); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/annotate.expected b/tests/programs/annotate.expected
new file mode 100644
index 0000000..553353e
--- /dev/null
+++ b/tests/programs/annotate.expected
@@ -0,0 +1,33 @@
+// zclsp --annotate writes the types the compiler can see into a program that
+// has none. this one has none: locals take the type of what they start as, a
+// parameter takes the type every call gives it, and a function the type of
+// what it returns. what it can't tell it leaves alone (twice is called with
+// a plain word, so its parameter stays), and the program still means the
+// same thing: tests/test.sh runs both and compares.
+include "src/lib/std.zc";
+struct point { int x, y; };
+int calls = 0;
+char* name = "annotate";
+int table[4];
+int add(int a, int b) { calls = calls + 1; return a + b; }
+point* scale(point* p, int k) { p.x = p.x * k; p.y = p.y * k; return p; }
+point*.u fresh(int x, int y) {
+ point* p = new(point);
+ p.x = x; p.y = y;
+ return u*p;
+}
+int twice(int v) { return v + v; }
+fn len3(char* s) { return len_w(s) + 3; }
+fn half(f) { return f / 2.0; }
+int main() {
+ int i = 0, sum = 0, buf[6];
+ let word_ = table, text = "abc";
+ point* pt = fresh(2, 3);
+ let r = 1.5;
+ while (i < 4) { table[i] = add(i, 10); sum = sum + table[i]; i++; }
+ scale(pt, 3);
+ buf[0] = twice(sum);
+ put_n(sum); put_c(32); put_n(pt.x + pt.y); put_c(32); put_n(buf[0]); put_c(32);
+ put_n(len3(text)); put_c(32); put_n(calls); put_c(32); put_n(half(r) * 4); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/annotate.zc b/tests/programs/annotate.zc
new file mode 100644
index 0000000..75dad97
--- /dev/null
+++ b/tests/programs/annotate.zc
@@ -0,0 +1,33 @@
+// zclsp --annotate writes the types the compiler can see into a program that
+// has none. this one has none: locals take the type of what they start as, a
+// parameter takes the type every call gives it, and a function the type of
+// what it returns. what it can't tell it leaves alone (twice is called with
+// a plain word, so its parameter stays), and the program still means the
+// same thing: tests/test.sh runs both and compares.
+include "src/lib/std.zc";
+struct point { int x, y; };
+let calls = 0;
+let name = "annotate";
+let table[4];
+fn add(a, b) { calls = calls + 1; return a + b; }
+fn scale(p, k) { p.x = p.x * k; p.y = p.y * k; return p; }
+.u fresh(x, y) {
+ let p = new(point);
+ p.x = x; p.y = y;
+ return u*p;
+}
+fn twice(v) { return v + v; }
+fn len3(s) { return len_w(s) + 3; }
+fn half(f) { return f / 2.0; }
+fn main() {
+ let i = 0, sum = 0, buf[6];
+ let word_ = table, text = "abc";
+ let pt = fresh(2, 3);
+ let r = 1.5;
+ while (i < 4) { table[i] = add(i, 10); sum = sum + table[i]; i++; }
+ scale(pt, 3);
+ buf[0] = twice(sum);
+ put_n(sum); put_c(32); put_n(pt.x + pt.y); put_c(32); put_n(buf[0]); put_c(32);
+ put_n(len3(text)); put_c(32); put_n(calls); put_c(32); put_n(half(r) * 4); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/arena.zc b/tests/programs/arena.zc
new file mode 100644
index 0000000..5156b80
--- /dev/null
+++ b/tests/programs/arena.zc
@@ -0,0 +1,16 @@
+// the bump arena: interior pointers, a rewind, and a full bump.
+fn main() {
+ let a = arena_new(32), p, q, r;
+ p = arena_alloc(a, 4);
+ p[0] = 7;
+ q = arena_alloc(a, 4);
+ put_n(q - p); put_c(' ');
+ put_n(p[0]); put_c(' ');
+ put_n(q[0]); put_c(' ');
+ arena_reset(a);
+ r = arena_alloc(a, 4);
+ put_n(r == p); put_c(' ');
+ put_n(arena_alloc(a, 100) == 0); put_c(10);
+ free(d*a);
+ return 0;
+}
diff --git a/tests/programs/args.zc b/tests/programs/args.zc
new file mode 100644
index 0000000..aac72ec
--- /dev/null
+++ b/tests/programs/args.zc
@@ -0,0 +1,16 @@
+// the command line and the environment, as the host hands them over. argv[0]
+// is the program path, and <eval> when the source came from stdin, so argc is
+// 1 even with nothing on the command line. run through a shell with no extra
+// arguments, which is how test.sh calls it, so the line is fixed.
+include "src/lib/std.zc";
+
+fn main() {
+ put_n(argc); put_c(32); put_s(argv[0]);
+ put_c(32); put_c(32); put_s(argv[argc - 1]);
+ // envc counts every entry, envp holds NAME=value strings
+ put_c(32); put_n(envc > 0);
+ let hit = 0;
+ for (int i = 0; i < envc; i++) if (eq_w(envp[i], "ZC_ARG_TEST=ok")) hit = 1;
+ put_c(32); put_n(hit);
+ return 0;
+} \ No newline at end of file
diff --git a/tests/programs/arrays.zc b/tests/programs/arrays.zc
new file mode 100644
index 0000000..a8a12bf
--- /dev/null
+++ b/tests/programs/arrays.zc
@@ -0,0 +1,23 @@
+// growable arrays and fixed ones
+struct point { int x, y; };
+fn main() {
+ let before = live_objects();
+ {
+ []int a = new([]int);
+ for (int i = 0; i < 100; i++) push(a, i * i);
+ put_n(len(a)); put_c(' '); put_n(cap(a)); put_c(' '); put_n(a[99]); put_c(' ');
+ put_n(pop(a)); put_c(' '); put_n(len(a)); put_c(' ');
+ resize(a, 3); put_n(len(a)); put_c(' '); put_n(a[2]); put_c(' ');
+ fixed []int f = new(fixed []int, 5);
+ f[4] = 7; put_n(f[4] + len(f)); put_c(' ');
+ []point ps = new([]point, 2);
+ ps[1].y = 9; resize(ps, 10); put_n(ps[1].y + len(ps)); put_c(' ');
+ [8]int st; put_n(len(st)); put_c(' ');
+ []point* owned = new([]point*);
+ for (int i = 0; i < 20; i++) { point* p = new(point); p.x = i; push(owned, d*p); }
+ put_n(owned[19].x); put_c(' ');
+ put_n(live_objects() - before); put_c(10);
+ }
+ put_n(live_objects() - before); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/backtick.zc b/tests/programs/backtick.zc
new file mode 100644
index 0000000..3610f70
--- /dev/null
+++ b/tests/programs/backtick.zc
@@ -0,0 +1,9 @@
+// a backtick ends the source only where the lexer would read it as a token.
+// this one, in a line comment: ` does not end anything.
+/* nor does this, in a block comment ` with a star / and a slash */
+fn main() {
+ let s = "a`b", t = "q\"`", c = '`', d = '\'';
+ put_n(len_w(s)); put_c(' '); put_n(len_w(t)); put_c(' '); put_n(c); put_c(' '); put_n(d); put_c(' ');
+ while (!eof()) put_c(get_c());
+ return 0;
+}
diff --git a/tests/programs/bareexit.zc b/tests/programs/bareexit.zc
new file mode 100644
index 0000000..905710e
--- /dev/null
+++ b/tests/programs/bareexit.zc
@@ -0,0 +1,7 @@
+// exit(3) must stop the machine before the next print.
+fn main() {
+ put_s("bye\n");
+ syscall(7, 3, 0, 0);
+ put_s("still\n");
+ return 0;
+}
diff --git a/tests/programs/bareio.zc b/tests/programs/bareio.zc
new file mode 100644
index 0000000..6da50b1
--- /dev/null
+++ b/tests/programs/bareio.zc
@@ -0,0 +1,28 @@
+// bare-metal syscalls: clock, a RAM file, random, and the machine size.
+// zc0 subset, so a run-only image can carry it. names stay off u/m/d.
+fn main() {
+ let buf[16], n, t0, t1, i, fd, r, board[8];
+ t0 = syscall(6, 0, 0, 0);
+ i = 0;
+ while (i < 20000) i = i + 1;
+ t1 = syscall(6, 0, 0, 0);
+ if (t0 >= 0 && t1 >= t0) put_s("tick ");
+ else put_s("notick ");
+ fd = syscall(1, "note", 1, 0);
+ n = syscall(4, fd, "zy", 2);
+ syscall(2, fd, 0, 0);
+ fd = syscall(1, "note", 0, 0);
+ n = syscall(3, fd, buf, 8);
+ syscall(2, fd, 0, 0);
+ buf[n] = 0;
+ put_n(n); put_c(' '); put_s(buf); put_c(' ');
+ r = syscall(8, 0, 0, 0);
+ if (r != -1) put_s("rand ");
+ else put_s("norand ");
+ n = syscall(5, "BOARD", board, 8);
+ if (n > 0) put_s("board ");
+ else put_s("noboard ");
+ put_n(syscall(9, 0, 0, 0));
+ put_c(10);
+ return 0;
+}
diff --git a/tests/programs/basics.zc b/tests/programs/basics.zc
new file mode 100644
index 0000000..9865527
--- /dev/null
+++ b/tests/programs/basics.zc
@@ -0,0 +1,21 @@
+let table[10];
+let greeting = "hey";
+const N = 10, BIG = 0x100 * 2;
+fn fib(n) { if (n < 2) return n; return fib(n - 1) + fib(n - 2); }
+fn twice(f, x) { return f(f(x)); }
+fn inc(x) { return x + 1; }
+fn main() {
+ let i, s = 0, a[5];
+ for (i = 0; i < N; i++) table[i] = i * i;
+ for (let j = 0; j < N; j++) s += table[j];
+ put_n(s); put_c(' '); put_n(fib(15)); put_c(' '); put_n(twice(inc, 40)); put_c(' '); put_n(BIG); put_c(10);
+ put_s(greeting); put_c(' '); put_n('A'); put_c(' '); put_n(-17 / 5); put_c(' '); put_n(-17 % 5); put_c(' '); put_n(1 << 10); put_c(' '); put_n(-64 >> 3); put_c(10);
+ i = 0; while (1) { i++; if (i == 3) continue; if (i > 6) break; put_n(i); } put_c(10);
+ a[0] = 5; a[1] = a[0]++; a[2] = ++a[0]; put_n(a[0]); put_n(a[1]); put_n(a[2]); put_c(10);
+ let b = { let t = 3; t * t };
+ put_n(b); put_c(' '); put_n(3 > 2 && 2 > 1); put_n(0 || 0); put_n(!5); put_n(~0); put_n(5 != 5); put_n(4 >= 4); put_c(10);
+ let x = asm { 2 3 + };
+ put_n(x); put_c(10);
+ asm { 'o' . 'k' . 10 . }
+ return 0;
+}
diff --git a/tests/programs/bench.zc b/tests/programs/bench.zc
new file mode 100644
index 0000000..7d34ead
--- /dev/null
+++ b/tests/programs/bench.zc
@@ -0,0 +1,29 @@
+// four kernels with and without o3; bench.sh runs each one on each backend
+int csum(int lim) { int s = 0; for (int n = 1; n < lim; n++) { int x = n; while (x != 1) { if (x & 1) x = 3 * x + 1; else x = x / 2; s++; } } return s; }
+o3 int csum3(int lim) { int s = 0; for (int n = 1; n < lim; n++) { int x = n; while (x != 1) { if (x & 1) x = 3 * x + 1; else x = x / 2; s++; } } return s; }
+int sieve(fixed []int f) { int c = 0, n = len(f); for (int i = 0; i < n; i++) f[i] = 1; for (int i = 2; i < n; i++) if (f[i]) { c++; for (int j = i + i; j < n; j += i) f[j] = 0; } return c; }
+o3 int sieve3(fixed []int f) { int c = 0, n = len(f); for (int i = 0; i < n; i++) f[i] = 1; for (int i = 2; i < n; i++) if (f[i]) { c++; for (int j = i + i; j < n; j += i) f[j] = 0; } return c; }
+int digits(int n) { int s = 0; for (int i = 0; i < n; i++) { int x = i; while (x) { s = s + x % 10; x = x / 10; } } return s; }
+o3 int digits3(int n) { int s = 0; for (int i = 0; i < n; i++) { int x = i; while (x) { s = s + x % 10; x = x / 10; } } return s; }
+int fib(int n) { if (n < 2) return n; return fib(n - 1) + fib(n - 2); }
+o3 int fib3(int n) { if (n < 2) return n; return fib3(n - 1) + fib3(n - 2); }
+// input: a letter (the kernel; z runs none) and how many times to run it
+fn main() {
+ fixed []int f = new(fixed []int, 50000);
+ let which = get_c(), reps = 0, ch = get_c(), r = 0;
+ while (ch >= '0' && ch <= '9') { reps = reps * 10 + ch - '0'; ch = get_c(); }
+ while (reps > 0) {
+ if (which == 'a') r = csum(30000);
+ if (which == 'b') r = csum3(30000);
+ if (which == 'c') r = sieve(f);
+ if (which == 'd') r = sieve3(f);
+ if (which == 'e') r = digits(300000);
+ if (which == 'f') r = digits3(300000);
+ if (which == 'g') r = fib(22);
+ if (which == 'h') r = fib3(22);
+ reps--;
+ }
+ put_n(r); put_c(10);
+ free(d*f);
+ return 0;
+}
diff --git a/tests/programs/bench_bytes.zc b/tests/programs/bench_bytes.zc
new file mode 100644
index 0000000..cb878f6
--- /dev/null
+++ b/tests/programs/bench_bytes.zc
@@ -0,0 +1,19 @@
+fn sumbytes(n) {
+ let buf[64], i = 0, s = 0, b, reps, ch;
+ while (i < 64) { buf[i] = i * 0x0101010101010101; i++; }
+ b = addr_b(buf);
+ ch = get_c();
+ reps = 0;
+ while (ch >= '0' && ch <= '9') { reps = reps * 10 + ch - '0'; ch = get_c(); }
+ while (reps > 0) {
+ i = 0; s = 0;
+ while (i < n) { s = s + ld8(b + (i & 511)); i++; }
+ reps--;
+ }
+ return s;
+}
+fn main() {
+ put_n(sumbytes(4000));
+ put_c(10);
+ return 0;
+}
diff --git a/tests/programs/bench_share.zc b/tests/programs/bench_share.zc
new file mode 100644
index 0000000..6fa478f
--- /dev/null
+++ b/tests/programs/bench_share.zc
@@ -0,0 +1,30 @@
+// walk one shared spine from two parents. the checksum is one repetition.
+fn build() {
+ let i = 0, spine = 0;
+ while (i < 40) { spine = cons(i, spine); i++; }
+ return cons(cons(spine, 1), cons(spine, 2));
+}
+fn sum(t) {
+ let a = 0;
+ while (t) { a = a + car(t); t = cdr(t); }
+ return a;
+}
+fn work() {
+ let ch = get_c(), reps = 0, acc = 0, root, spine, k;
+ while (ch >= '0' && ch <= '9') { reps = reps * 10 + ch - '0'; ch = get_c(); }
+ root = build();
+ spine = car(car(root));
+ while (reps > 0) {
+ k = 0;
+ acc = 0;
+ while (k < 200) { acc = acc + sum(spine); k++; }
+ acc = acc + cdr(car(root)) + cdr(cdr(root));
+ reps--;
+ }
+ return acc;
+}
+fn main() {
+ put_n(work());
+ put_c(10);
+ return 0;
+}
diff --git a/tests/programs/bench_strings.zc b/tests/programs/bench_strings.zc
new file mode 100644
index 0000000..455cff9
--- /dev/null
+++ b/tests/programs/bench_strings.zc
@@ -0,0 +1,27 @@
+fn work() {
+ let s = b"abcdefghijklmnopqrstuvwxyz0123456789";
+ let i, acc, n, t, part, k, reps, ch;
+ ch = get_c();
+ reps = 0;
+ while (ch >= '0' && ch <= '9') { reps = reps * 10 + ch - '0'; ch = get_c(); }
+ while (reps > 0) {
+ k = 0; acc = 0; n = len_s(s);
+ while (k < 200) {
+ i = 0;
+ while (i < n) { acc = acc + at_s(s, i); i++; }
+ k++;
+ }
+ t = s + b"!";
+ part = t[1:8];
+ acc = acc + len_s(t) + len_s(part) + at_s(part, 0);
+ free(part);
+ free(t);
+ reps--;
+ }
+ return acc;
+}
+fn main() {
+ put_n(work());
+ put_c(10);
+ return 0;
+}
diff --git a/tests/programs/bitops.expected b/tests/programs/bitops.expected
new file mode 100644
index 0000000..e93b905
--- /dev/null
+++ b/tests/programs/bitops.expected
@@ -0,0 +1,2 @@
+63 64 3 64 8 64 0 1 2 1 12345 -1 4 1069547520 1 1065353216 8388608
+5470960040408376558
diff --git a/tests/programs/bitops.zc b/tests/programs/bitops.zc
new file mode 100644
index 0000000..97817b9
--- /dev/null
+++ b/tests/programs/bitops.zc
@@ -0,0 +1,26 @@
+// bit tricks and float32 conversion as micro-ops
+fn show(v) { put_n(v); put_c(' '); }
+fn once() {
+ show(clz(1)); show(clz(0)); show(ctz(8)); show(ctz(0));
+ show(popcnt(255)); show(popcnt(-1)); show(popcnt(0));
+ show(bswap(1) == 72057594037927936);
+ show(rotl(1, 65)); show(rotr(1, 1) < 0); show(rotl(rotr(12345, 7), 7));
+ show(mulhi(-1, 5)); show(umulhi(-1, 5));
+ show(f2s(0x3ff8000000000000)); show(s2f(0x3fc00000) == 0x3ff8000000000000);
+ show(f2s(0x3ff0000000000000)); show(f2s(0x3810000000000000));
+ put_c(10);
+}
+// the same in a loop, so o3 lifts and fuses them
+o3 int sums(int n) {
+ int a = 0;
+ for (int i = 0; i < n; i++) {
+ a += popcnt(i) + ctz(i + 1) + clz(i + 1);
+ a ^= rotl(a, 3) + bswap(i) + f2s(s2f(i));
+ }
+ return a;
+}
+int main() {
+ once();
+ put_n(sums(1000)); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/borrowmove.zc b/tests/programs/borrowmove.zc
new file mode 100644
index 0000000..b0f0e42
--- /dev/null
+++ b/tests/programs/borrowmove.zc
@@ -0,0 +1,2 @@
+fn put(.m box, int v) { int* c = new(int); *c = v; box[0] = c; return 0; }
+fn main() { let b = new(1); put(b, 6); put_n(*(b[0] as int*)); put_c(10); return 0; }
diff --git a/tests/programs/bounds.zc b/tests/programs/bounds.zc
new file mode 100644
index 0000000..42c3997
--- /dev/null
+++ b/tests/programs/bounds.zc
@@ -0,0 +1,28 @@
+// a +1 loop whose bound is len of the fixed array: the check is gone, the sum is not
+fn acc(fixed []int f) {
+ int n = len(f), i = 0, s = 0;
+ for (; i < n; i += 1) f[i] = i + 1;
+ for (i = 0; i < n; i++) s = s + f[i];
+ return s;
+}
+o3 fn acc3(fixed []int f) {
+ int n = len(f), i = 0, s = 0;
+ for (; i < n; i++) f[i] = i + 1;
+ for (i = 0; i < n; i++) s += f[i];
+ return s;
+}
+o3 fn small(fixed []int f) {
+ int n = len(f), c = 0, i, j;
+ for (i = 2; i < n; i++) f[i] = 1;
+ for (i = 2; i * i < n; i++)
+ if (f[i]) for (j = i * i; j < n; j += i) f[j] = 0;
+ for (i = 2; i < n; i++) c += f[i];
+ return c;
+}
+fn main() {
+ fixed []int f = new(fixed []int, 10);
+ fixed []int g = new(fixed []int, 20);
+ put_n(acc(f)); put_c(32); put_n(acc3(f)); put_c(32); put_n(small(g)); put_c(10);
+ free(d*f); free(d*g);
+ return 0;
+}
diff --git a/tests/programs/bounds_hi.zc b/tests/programs/bounds_hi.zc
new file mode 100644
index 0000000..d61179d
--- /dev/null
+++ b/tests/programs/bounds_hi.zc
@@ -0,0 +1,8 @@
+// the index is not the induction variable. i + 1 walks off the end.
+o3 fn main() {
+ fixed []int f = new(fixed []int, 4);
+ int n = len(f), i;
+ for (i = 0; i < n; i++) f[i + 1] = 1;
+ free(d*f);
+ return 0;
+}
diff --git a/tests/programs/bounds_lo.zc b/tests/programs/bounds_lo.zc
new file mode 100644
index 0000000..896d864
--- /dev/null
+++ b/tests/programs/bounds_lo.zc
@@ -0,0 +1,8 @@
+// i starts negative. i < len is true for the first pass, and the index is still illegal.
+fn main() {
+ fixed []int f = new(fixed []int, 4);
+ int n = len(f), i;
+ for (i = -1; i < n; i++) f[i] = 1;
+ free(d*f);
+ return 0;
+}
diff --git a/tests/programs/bstr_io.zc b/tests/programs/bstr_io.zc
new file mode 100644
index 0000000..9cf7ca9
--- /dev/null
+++ b/tests/programs/bstr_io.zc
@@ -0,0 +1,22 @@
+fn main() {
+ let path = b"/tmp/zc-bstr.txt";
+ let msg = b"byte-io";
+ let fd = bopen(path, 1);
+ let n = bwrite(fd, msg, len_s(msg));
+ syscall(SYS_CLOSE, fd, 0, 0);
+ fd = bopen(path, 0);
+ let buf = new_s(7);
+ let g = bread(fd, buf, 7);
+ syscall(SYS_CLOSE, fd, 0, 0);
+ let name = b"ZC_BSTR";
+ let envb = new_s(16);
+ let k = bgetenv(name, envb, 16);
+ put_s(buf); put_c(32);
+ put_n(n); put_c(32);
+ put_n(g); put_c(32);
+ put_n(buf == msg); put_c(32);
+ put_n(k); put_c(32);
+ put_n(at_s(envb, 0)); put_c(32);
+ put_n(at_s(envb, 5)); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/buddy.zc b/tests/programs/buddy.zc
new file mode 100644
index 0000000..ab351d2
--- /dev/null
+++ b/tests/programs/buddy.zc
@@ -0,0 +1,16 @@
+// the buddy allocator: 20000 small blocks, freed, merge back into one big one
+let keep[20000];
+fn main() {
+ let i = 0, p;
+ while (i < 20000) { keep[i] = new(2); i++; } // 20000 8-word blocks
+ put_n(largest_free()); put_c(' ');
+ i = 0; while (i < 20000) { free(keep[i]); keep[i] = 0; i++; }
+ scrub(); // stale stack words would keep the quarantined blocks
+ gc();
+ put_n(largest_free()); put_c(' ');
+ p = new(200000); // only possible if the small blocks merged back
+ put_n(osize(p) >= 200000); put_c(' ');
+ free(d*p);
+ put_n(live_objects()); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/bytes.zc b/tests/programs/bytes.zc
new file mode 100644
index 0000000..b4e1079
--- /dev/null
+++ b/tests/programs/bytes.zc
@@ -0,0 +1,44 @@
+fn sp() { put_c(32); return 0; }
+fn litw(v) { return v + v + 1; }
+fn f() { put_c('A'); return 0; }
+fn sumbytes(n) {
+ let buf[64], i = 0, s = 0, b;
+ while (i < 64) { buf[i] = i * 0x0101010101010101; i++; }
+ b = addr_b(buf);
+ i = 0;
+ while (i < n) { s = s + ld8(b + (i & 511)); i++; }
+ return s;
+}
+fn main() {
+ let buf[3], b;
+ buf[0] = 0; buf[1] = 0; buf[2] = 0;
+ b = addr_b(buf);
+ st8(b, 0x11);
+ st8(b + 1, 0x22);
+ st8(b + 7, 0x7f);
+ st16(b + 7, 0xabcd);
+ st32(b + 10, 0x01020304);
+ put_n(ld8(b)); sp();
+ put_n(ld8(b + 1)); sp();
+ put_n(ld16(b + 7)); sp();
+ put_n(ld32(b + 10)); sp();
+ put_n(addr_w(b) == buf); sp();
+ put_n(ld8(b + 7)); sp();
+ put_n(ld8(b + 8)); sp();
+ put_n(buf[0]); sp();
+ put_n(buf[1]); sp();
+ st8(0 - 1, 99);
+ put_n(ld8(0 - 1)); sp();
+ put_n(ld16(addr_b(MW) - 1)); sp();
+ put_n(sumbytes(1000));
+ put_c(10);
+ let p = f;
+ f();
+ while (*p != litw('A')) p++;
+ st8(addr_b(p), litw('B') & 255);
+ f();
+ st8(addr_b(p), litw('C') & 255);
+ f();
+ put_c(10);
+ return 0;
+}
diff --git a/tests/programs/cabi.expected b/tests/programs/cabi.expected
new file mode 100644
index 0000000..89b168c
--- /dev/null
+++ b/tests/programs/cabi.expected
@@ -0,0 +1,5 @@
+1.414214 1024.000000 4.000000 12 42 1234 1 2 3 5 7 9
+9 7 5 3 2 1
+1 2 3 5 7 9 9 7 5 3 2 1
+6 7:2.50
+5.000000
diff --git a/tests/programs/cabi.zc b/tests/programs/cabi.zc
new file mode 100644
index 0000000..6a608ab
--- /dev/null
+++ b/tests/programs/cabi.zc
@@ -0,0 +1,46 @@
+// native calls: extern declares a c function, cfunc hands a zc function to c
+extern double sqrt(double);
+extern double pow(double x, double y);
+extern float sqrtf(float);
+extern long c_strlen(str s) = "strlen";
+extern int c_abs(int x) = "abs";
+extern int c_atoi(str s) = "atoi";
+extern void qsort(word *base, long n, long size, void *cmp);
+extern int snprintf2(word *buf, long cap, str fmt, long a, double b) = "snprintf";
+extern double hypot(double, double);
+
+fn put_f(x) { asm { x @ #1220 } return 0; }
+fn sp() { put_c(32); return 0; }
+
+int cmp(int* a, int* b) { return *a - *b; }
+// a callback that makes a native call of its own, and an o3 one
+int cmpdesc(int* a, int* b) { return c_abs(*b - *a) * (*b - *a) / c_abs(*b - *a + (*b == *a)); }
+o3 int cmpo3(int* a, int* b) { int x = *a; int y = *b; if (x < y) return 1; if (x > y) return -1; return 0; }
+
+fn main() {
+ let a[6];
+ let i = 0;
+ put_f(sqrt(2.0)); sp();
+ put_f(pow(2.0, 10.0)); sp();
+ put_f(sqrtf(16.0)); sp();
+ put_n(c_strlen(b"hello, world")); sp();
+ put_n(c_abs(0 - 42)); sp();
+ put_n(c_atoi(b"1234xyz")); sp();
+ a[0] = 5; a[1] = 3; a[2] = 9; a[3] = 1; a[4] = 7; a[5] = 2;
+ qsort(a, 6, 8, cfunc(cmp));
+ while (i < 6) { put_n(a[i]); sp(); i++; }
+ put_c(10);
+ qsort(a, 6, 8, cfunc(cmpdesc));
+ i = 0; while (i < 6) { put_n(a[i]); sp(); i++; }
+ put_c(10);
+ qsort(a, 6, 8, cfunc(cmp));
+ i = 0; while (i < 6) { put_n(a[i]); sp(); i++; }
+ qsort(a, 6, 8, cfunc(cmpo3));
+ i = 0; while (i < 6) { put_n(a[i]); sp(); i++; }
+ put_c(10);
+ put_n(snprintf2(a, 48, b"%ld:%.2f", 7, 2.5)); sp();
+ i = 0; while (i < 6) { put_c(ld8(addr_b(a) + i)); i++; }
+ put_c(10);
+ put_f(hypot(3.0, 4.0)); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/cabi_o3.expected b/tests/programs/cabi_o3.expected
new file mode 100644
index 0000000..6842510
--- /dev/null
+++ b/tests/programs/cabi_o3.expected
@@ -0,0 +1,2 @@
+921.462947 1.414214 2500
+11 1-2-3-4-5-6
diff --git a/tests/programs/cabi_o3.zc b/tests/programs/cabi_o3.zc
new file mode 100644
index 0000000..5c9a55a
--- /dev/null
+++ b/tests/programs/cabi_o3.zc
@@ -0,0 +1,30 @@
+// native calls from o3 code: lifted into the region as micro-ops, called directly by llvm
+extern double sqrt(double);
+extern double pow(double, double);
+extern double fma(double, double, double);
+extern int snprintf9(word *buf, long cap, str fmt, long a, long b, long c, long d, long e, long f) = "snprintf";
+extern long labs(long);
+
+fn put_f(x) { asm { x @ #1220 } return 0; }
+
+o3 float norm(int n) {
+ float acc = 0.0;
+ for (int i = 1; i <= n; i++) acc = acc + sqrt(i * 1.0) + fma(1.0, 2.0, 0.5);
+ return acc;
+}
+o3 int absum(int n) {
+ int t = 0;
+ for (int i = 0; i < n; i++) t = t + labs(i - 50);
+ return t;
+}
+fn main() {
+ let buf[16];
+ let i = 0;
+ put_f(norm(100)); put_c(32);
+ put_f(pow(2.0, 0.5)); put_c(32);
+ put_n(absum(100)); put_c(10);
+ put_n(snprintf9(buf, 100, b"%ld-%ld-%ld-%ld-%ld-%ld", 1, 2, 3, 4, 5, 6)); put_c(32);
+ while (i < 11) { put_c(ld8(addr_b(buf) + i)); i++; }
+ put_c(10);
+ return 0;
+}
diff --git a/tests/programs/cabi_sized.expected b/tests/programs/cabi_sized.expected
new file mode 100644
index 0000000..9be5bf0
--- /dev/null
+++ b/tests/programs/cabi_sized.expected
@@ -0,0 +1 @@
+1.414214 1 5.000000 77 65 5 1234 2.500000
diff --git a/tests/programs/cabi_sized.zc b/tests/programs/cabi_sized.zc
new file mode 100644
index 0000000..d1dc2ba
--- /dev/null
+++ b/tests/programs/cabi_sized.zc
@@ -0,0 +1,28 @@
+// extern and cfunc with f32 and the sized integers
+extern f32 sqrtf(f32);
+extern f64 hypot(f64, f64);
+extern i32 abs(i32);
+extern u8 toupper(u8);
+extern long labs(long);
+fn put_f(x) { asm { x @ #1220 } return 0; }
+fn sp() { put_c(32); return 0; }
+extern void qsort(word *base, long n, long size, void *cmp);
+i32 cmpi(int* a, int* b) { return *a - *b; }
+f32 half(f32 v) { return v / 2; }
+fn main() {
+ f32 r = sqrtf(2.0);
+ put_f(r); sp();
+ put_n(r == sqrtf(2.0)); sp();
+ put_f(hypot(3.0, 4.0)); sp();
+ put_n(abs(0 - 77)); sp();
+ put_n(toupper(97)); sp();
+ i32 big = 0 - 5;
+ put_n(abs(big)); sp();
+ int v[4];
+ v[0] = 4; v[1] = 1; v[2] = 3; v[3] = 2;
+ qsort(v, 4, 8, cfunc(cmpi));
+ put_n(v[0]); put_n(v[1]); put_n(v[2]); put_n(v[3]); sp();
+ put_f(half(5.0));
+ put_c(10);
+ return 0;
+}
diff --git a/tests/programs/capture.zc b/tests/programs/capture.zc
new file mode 100644
index 0000000..c178791
--- /dev/null
+++ b/tests/programs/capture.zc
@@ -0,0 +1,31 @@
+// capture.start() sends everything the program writes into a string instead of
+// the terminal, and buf.end() stops it and terminates the string. nothing may
+// be printed during a capture, or it lands in the buffer too.
+include "src/lib/std.zc";
+
+fn main() {
+ let buf = capture.start();
+ put_s("hello world!");
+ put_n(42);
+ put_c(10);
+ buf.end(); // stops the capture and terminates the string
+
+ // the capture is off again, so these go to the terminal
+ put_n(outmode); put_c(10); // 0
+ put_n(outbuf == 0); put_c(10); // 1, the buffer is handed over
+ put_n(len_w(buf)); put_c(10); // 15
+ put_s(buf); // hello world!42
+
+ // a second capture does not disturb the first, and both read back
+ let two = capture.start();
+ put_s("second");
+ two.end();
+ put_n(eq_w(buf, "hello world!42\n")); put_c(10); // 1
+ put_n(eq_w(two, "second")); put_c(10); // 1
+
+ // capture_stdin returns an empty string, so an unused capture is empty
+ let three = capture.start();
+ three.end();
+ put_n(len_w(three)); put_c(10); // 0
+ return 0;
+} \ No newline at end of file
diff --git a/tests/programs/closure.zc b/tests/programs/closure.zc
new file mode 100644
index 0000000..660c32a
--- /dev/null
+++ b/tests/programs/closure.zc
@@ -0,0 +1,52 @@
+// closures: calls through values, captures by value, state in a capture,
+// nested captures, closures passed as arguments and returned from functions
+fn apply(f, x) { return f(x); }
+fn twice(g, x) { return g(g(x)); }
+fn sq(x) { return x * x; }
+word.u make(n) {
+ let h = fn(x) { return x * n; };
+ return u*h;
+}
+fn main() {
+ let k = 10, m = 3;
+ let add = fn(x) { return x + k * m; };
+ put_n(add(1)); put_c(10);
+ put_n(apply(add, 5)); put_c(10);
+ let none = fn() { return 42; };
+ put_n(none()); put_c(10);
+ let n = 0;
+ let bump = fn(d) { n = n + d; return n; };
+ put_n(bump(2)); put_c(10);
+ put_n(bump(5)); put_c(10);
+ put_n(n); put_c(10);
+ let a = 2;
+ let outer = fn(x) {
+ let inner = fn(y) { return y + a + x; };
+ return inner(100);
+ };
+ put_n(outer(7)); put_c(10);
+ let base = 5;
+ let scale = fn(i) { return i * base; };
+ let s = 0, i = 0;
+ while (i < 4) { s = s + scale(i); i++; }
+ put_n(s); put_c(10);
+ let c = 3;
+ let plus = fn(v) { return v + c; };
+ put_n(twice(plus, 1)); put_c(10);
+ put_n(sq(4)); put_c(10);
+ let total = 0, j = 0;
+ while (j < 3) {
+ let kk = j;
+ let g = fn(x) { return x + kk; };
+ total = total + g(10);
+ j++;
+ }
+ put_n(total); put_c(10);
+ let f = make(4);
+ put_n(f(5)); put_c(10);
+ let h = 1;
+ let frozen = fn(x) { return x + h; };
+ h = 100;
+ put_n(frozen(1)); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/csyn.zc b/tests/programs/csyn.zc
new file mode 100644
index 0000000..0099d1c
--- /dev/null
+++ b/tests/programs/csyn.zc
@@ -0,0 +1,18 @@
+// do/while, enum constants, and a C cast
+typecheck on;
+enum color { RED, GREEN = 5, BLUE };
+fn main() {
+ int n = 0, i = 0;
+ do {
+ i++;
+ if (i == 2) continue;
+ n = n + i;
+ } while (i < 5);
+ int* p = (int*)0;
+ int z = (int)p;
+ put_n(n); put_c(' ');
+ put_n(RED); put_c(' '); put_n(GREEN); put_c(' ');
+ put_n(BLUE); put_c(' ');
+ put_n(z); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/decl_undefined.zc b/tests/programs/decl_undefined.zc
new file mode 100644
index 0000000..0ee9312
--- /dev/null
+++ b/tests/programs/decl_undefined.zc
@@ -0,0 +1,2 @@
+fn helper();
+fn main() { helper(); return 0; }
diff --git a/tests/programs/discard_m.zc b/tests/programs/discard_m.zc
new file mode 100644
index 0000000..35e9f3b
--- /dev/null
+++ b/tests/programs/discard_m.zc
@@ -0,0 +1,3 @@
+// m* on an unnamed u result lets the value be discarded; the plain call is an error
+.u mk(int v) { let p = new(1); p[0] = v; return u*p; }
+fn main() { m*mk(7); m*mk(8); put_n(7); put_c(10); return 0; }
diff --git a/tests/programs/doublefree.zc b/tests/programs/doublefree.zc
new file mode 100644
index 0000000..296b194
--- /dev/null
+++ b/tests/programs/doublefree.zc
@@ -0,0 +1,9 @@
+memdebug on;
+unchecked fn main()
+{
+ let p = new(1);
+ free(p);
+ free(p);
+ put_s("ok\n");
+ return 0;
+}
diff --git a/tests/programs/enhance.zc b/tests/programs/enhance.zc
new file mode 100644
index 0000000..327b4bd
--- /dev/null
+++ b/tests/programs/enhance.zc
@@ -0,0 +1,28 @@
+// custom opcodes: recipes, lifted vm code, several outputs, and a fallback
+enhance clampadd(a, b) micro "a = add a b; a = min a 100; a = max a 0";
+enhance cube(x) vm { 0 $ 0 $ * * };
+enhance 2 divmod(a, b) micro "r2 = sdiv a b" "\n r1 = srem a b\n r0 = r2";
+enhance div7(a) micro "a = sdiv a 7";
+enhance urem10(a) micro "a = urem a 10";
+enhance hi(a, b) micro "a = mulhu a b";
+enhance noisy(x) vm { 0 $ . } else soft_noisy; // i/o can't be lifted, so calls use the fallback
+fn soft_noisy(x) { return x + 1; }
+let qq, rr, seed = 7;
+fn rnd() { seed = seed * 1103515245 + 12345; return seed ^ (seed >> 11); }
+fn main() {
+ let i = 0, a, bad = 0;
+ put_n(clampadd(70, 50)); put_c(' '); put_n(clampadd(-50, 2)); put_c(' '); put_n(cube(7)); put_c(' ');
+ asm { 17 5 divmod rr ! qq ! }
+ put_n(qq); put_c(','); put_n(rr); put_c(' ');
+ put_n(noisy(4)); put_c(' ');
+ put_n(hi(-1, -1)); put_c(' ');
+ while (i < 5000) {
+ a = rnd();
+ if (div7(a) != zdiv(a, 7)) bad++;
+ if (urem10(a) < 0 || urem10(a) > 9) bad++;
+ if (a / (i + 1) != zdiv(a, i + 1)) bad++;
+ i++;
+ }
+ put_n(bad); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/enumscope.zc b/tests/programs/enumscope.zc
new file mode 100644
index 0000000..5c6d7d0
--- /dev/null
+++ b/tests/programs/enumscope.zc
@@ -0,0 +1,9 @@
+// enum members are reached through the enum's name as well as plainly
+enum color { RED, GREEN = 5, BLUE };
+fn main() {
+ int c = color.BLUE;
+ put_n(color.RED); put_c(' '); put_n(GREEN); put_c(' ');
+ put_n(c); put_c(' ');
+ switch (c) { case color.BLUE: put_s("blue"); break; default: put_s("other"); }
+ return 0;
+}
diff --git a/tests/programs/enumtype.zc b/tests/programs/enumtype.zc
new file mode 100644
index 0000000..9a6e89b
--- /dev/null
+++ b/tests/programs/enumtype.zc
@@ -0,0 +1,12 @@
+// enums are a type of their own in typed code: their members, and only those, take them; an enum still becomes an int
+typecheck on;
+enum color { RED, GREEN = 5, BLUE };
+int paint(color c) { return c; }
+int main() {
+ color c = color.BLUE;
+ color d = RED;
+ int n = c;
+ put_n(n); put_c(32); put_n(paint(GREEN)); put_c(32); put_n(d == color.RED);
+ put_c(10);
+ return 0;
+}
diff --git a/tests/programs/f32.expected b/tests/programs/f32.expected
new file mode 100644
index 0000000..8220b5f
--- /dev/null
+++ b/tests/programs/f32.expected
@@ -0,0 +1 @@
+16777216.000000 16777217.000000 001 1.500000 1.000000 3.600000 10 7.800000 4950.000000 450
diff --git a/tests/programs/f32.zc b/tests/programs/f32.zc
new file mode 100644
index 0000000..0f60dcd
--- /dev/null
+++ b/tests/programs/f32.zc
@@ -0,0 +1,37 @@
+// f32: single precision values and packed arrays of them
+struct vec3 { f32 x, y, z; };
+f32 gsum;
+fn put_f(x) { asm { x @ #1220 } return 0; }
+[8]f32 gbuf;
+
+f32 half(f32 v) { return v / 2; }
+
+fn main() {
+ gsum = 0.1;
+ f32 a = 16777216.0;
+ f32 b = a + 1.0; // rounds back to 16777216 in single precision
+ float c = 16777216.0 + 1.0;
+ f32 t = 0.1;
+ float u = 0.1;
+ put_f(b); put_c(' '); put_f(c); put_c(' ');
+ put_n(t == 0.1); put_n(t == u); put_n(t == (f32)u); put_c(' ');
+ put_f(half(3.0)); put_c(' ');
+ put_f(gsum * 10); put_c(' '); // 1.000000 (0.1f * 10 in double)
+ vec3 v; v.x = 1.5; v.y = 0.1; v.z = 2;
+ put_f(v.x + v.y + v.z); put_c(' ');
+ int i = 0;
+ gbuf[0] = 0.1; put_n(gbuf[0] == t); put_n(gbuf[0] == u); put_c(' ');
+ while (i < 8) { gbuf[i] = i * 0.25 + 0.1; i++; }
+ float s = 0;
+ i = 0;
+ while (i < 8) { s += gbuf[i]; i++; }
+ put_f(s); put_c(' ');
+ f32* p = new(f32, 100);
+ i = 0;
+ while (i < 100) { p[i] = i; i++; }
+ s = 0; i = 0;
+ while (i < 100) { s += p[i]; i++; }
+ put_f(s); put_c(' ');
+ put_n(sizeof(gbuf)); put_n(sizeof([100]f32)); put_c(10);
+ free(d*p);
+}
diff --git a/tests/programs/f32o3.zc b/tests/programs/f32o3.zc
new file mode 100644
index 0000000..8f1ec2b
--- /dev/null
+++ b/tests/programs/f32o3.zc
@@ -0,0 +1,14 @@
+// f32 packed arrays and rounding inside an o3 region
+fn put_f(x) { asm { x @ #1220 } return 0; }
+o3 float dot(f32* p, int n) {
+ float s = 0;
+ f32 acc = 0;
+ for (int i = 0; i < n; i++) { s += p[i] * p[i]; acc = acc + p[i]; p[i] = p[i] + 0.1; }
+ return s + acc;
+}
+fn main() {
+ f32* p = new(f32, 50);
+ for (int i = 0; i < 50; i++) p[i] = i * 0.5;
+ put_f(dot(p, 50)); put_c(' '); put_f(p[3]); put_c(' '); put_n(p[3] == 1.6); put_c(10);
+ free(d*p);
+}
diff --git a/tests/programs/fbench.zc b/tests/programs/fbench.zc
new file mode 100644
index 0000000..1658ba7
--- /dev/null
+++ b/tests/programs/fbench.zc
@@ -0,0 +1,37 @@
+// float kernel, plain and o3. bench_float.sh times them. the letter picks the
+// kernel and the digits are how many times to run it, same shape as bench.zc.
+float poly(int n) {
+ float s = 0.0;
+ float x = 1.25;
+ int i = 0;
+ while (i < n) {
+ s = s + x * 1.5;
+ x = x * 0.5 + 1.0;
+ i = i + 1;
+ }
+ return s;
+}
+o3 float poly3(int n) {
+ float s = 0.0;
+ float x = 1.25;
+ int i = 0;
+ while (i < n) {
+ s = s + x * 1.5;
+ x = x * 0.5 + 1.0;
+ i = i + 1;
+ }
+ return s;
+}
+fn main() {
+ let which = get_c(), reps = 0, ch = get_c(), r = 0;
+ while (ch >= 48 && ch <= 57) { reps = reps * 10 + ch - 48; ch = get_c(); }
+ while (reps > 0) {
+ if (which == 97) r = (int)poly(20000);
+ if (which == 98) r = (int)poly3(20000);
+ if (which == 99) r = (int)poly(2000);
+ reps = reps - 1;
+ }
+ put_n(r);
+ put_c(10);
+ return 0;
+}
diff --git a/tests/programs/float.zc b/tests/programs/float.zc
new file mode 100644
index 0000000..81b8668
--- /dev/null
+++ b/tests/programs/float.zc
@@ -0,0 +1,32 @@
+fn put_f(x) { asm { x @ #1220 } return 0; }
+fn f2i(x) { return asm { x @ #1218 }; }
+fn i2f(x) { return asm { x @ #1216 }; }
+fn sp() { put_c(32); return 0; }
+
+fn main() {
+ let a = 1.5;
+ let b = 2.5;
+ put_f(a + b); sp();
+ put_f(7.0 - 2.5); sp();
+ put_f(1.5 * 2); sp();
+ put_f(10.0 / 4.0); sp();
+ put_f(1 + 1.5); sp();
+ put_f(1.5e-3); sp();
+ put_f(0.1); sp();
+ put_f(-0.125); sp();
+ put_f(1.0 / 0.0); sp();
+ put_f(-1.0 / 0.0); sp();
+ put_f(0.0 / 0.0); sp();
+ put_n(1.5 < 2.0); sp();
+ put_n(2.0 <= 2.0); sp();
+ put_n(2.0 == 2); sp();
+ put_n(1.5 == 1); sp();
+ put_n(3.0 > 3.5); sp();
+ put_n(f2i(3.9)); sp();
+ put_n(f2i(-3.9)); sp();
+ put_f(i2f(3)); sp();
+ put_f(i2f(-2)); sp();
+ put_n(10 / 4);
+ put_c(10);
+ return 0;
+}
diff --git a/tests/programs/fo3.zc b/tests/programs/fo3.zc
new file mode 100644
index 0000000..593e88a
--- /dev/null
+++ b/tests/programs/fo3.zc
@@ -0,0 +1,50 @@
+// o3 lifts float ops: the plain kernel and the o3 kernel agree, a constant
+// float expression folds, x - x stays a NaN, and -0 is false inside o3.
+fn put_f(x) { asm { x @ #1220 } return 0; }
+fn sp() { put_c(32); return 0; }
+float poly(int n) {
+ float s = 0.0;
+ float x = 1.25;
+ int i = 0;
+ while (i < n) {
+ s = s + x * 1.5;
+ x = x * 0.5 + 1.0;
+ i = i + 1;
+ }
+ return s;
+}
+o3 float poly3(int n) {
+ float s = 0.0;
+ float x = 1.25;
+ int i = 0;
+ while (i < n) {
+ s = s + x * 1.5;
+ x = x * 0.5 + 1.0;
+ i = i + 1;
+ }
+ return s;
+}
+o3 float folded() { return 1.5 + 2.5; }
+o3 float subself(float x) { return x - x; }
+o3 int zfalse() {
+ float z = 0.0 * -1.0;
+ int n = 0;
+ if (z) n = 1;
+ while (z) n = n + 2;
+ return n;
+}
+o3 float addtwo(float a) { return a + 2; }
+o3 int below(float a, float b) { return a < b; }
+fn main() {
+ put_n((int)poly(2000)); sp();
+ put_n((int)poly3(2000)); sp();
+ put_f(folded()); sp();
+ put_f(subself(0.0 / 0.0)); sp();
+ put_n(zfalse()); sp();
+ put_f(addtwo(1.25)); sp();
+ put_n(below(1.0, 2.0)); sp();
+ put_n(below(2.0, 2.0)); sp();
+ put_n(below(0.0 / 0.0, 1.0));
+ put_c(10);
+ return 0;
+}
diff --git a/tests/programs/ftext.zc b/tests/programs/ftext.zc
new file mode 100644
index 0000000..b1c0acf
--- /dev/null
+++ b/tests/programs/ftext.zc
@@ -0,0 +1,37 @@
+fn sp() { put_c(32); return 0; }
+fn pm(x, prec, mode) { asm { x @ prec @ mode @ #1236 } return 0; }
+fn parsed(s) { return asm { s @ #1222 }; }
+fn ok(a, b) {
+ let w = asm { a @ b @ ^ };
+ if (w) put_n(0); else put_n(1);
+ return 0;
+}
+
+fn main() {
+ let z = 0.0 * -1.0;
+ let hi = parsed("9007199254740994");
+ let lo = parsed("9007199254740992");
+ pm(1.0e308, 0, 0); sp();
+ pm(1.0e-10, 0, 0); sp();
+ pm(z, 0, 0); sp();
+ pm(1.0 / 0.0, 0, 0); sp();
+ pm(-1.0 / 0.0, 0, 0); sp();
+ pm(0.0 / 0.0, 0, 0); sp();
+ pm(0.1, 0, 0); sp();
+ pm(1.5, 2, 1); sp();
+ pm(1.25, 4, 2); sp();
+ pm(1.0e20, 6, 1); sp();
+ pm(1.0e40, 0, 0); sp();
+ ok(parsed("1.5"), 3.0 / 2.0);
+ ok(parsed("0.1"), 1.0 / 10.0);
+ ok(parsed("inf"), 1.0 / 0.0);
+ ok(parsed("-inf"), -1.0 / 0.0);
+ ok(parsed("-0"), z);
+ ok(parsed("9007199254740993"), lo);
+ if (hi == lo) put_n(0); else put_n(1);
+ if (parsed("5e-324")) put_n(1); else put_n(0);
+ if (parsed("1e-400")) put_n(0); else put_n(1);
+ if (parsed("-nan") == parsed("nan")) put_n(0); else put_n(1);
+ put_c(10);
+ return 0;
+}
diff --git a/tests/programs/ftruth.zc b/tests/programs/ftruth.zc
new file mode 100644
index 0000000..9d24a42
--- /dev/null
+++ b/tests/programs/ftruth.zc
@@ -0,0 +1,22 @@
+fn main() {
+ let z = 0.0 * -1.0;
+ let p = 0.0;
+ let n = 1.5;
+ if (z) put_n(1); else put_n(0);
+ if (p) put_n(1); else put_n(0);
+ if (n) put_n(1); else put_n(0);
+ if (!z) put_n(1); else put_n(0);
+ if (!p) put_n(1); else put_n(0);
+ if (!n) put_n(1); else put_n(0);
+ if (z && n) put_n(1); else put_n(0);
+ if (n && z) put_n(1); else put_n(0);
+ if (z || n) put_n(1); else put_n(0);
+ if (n || z) put_n(1); else put_n(0);
+ if (p || z) put_n(1); else put_n(0);
+ for (; z;) put_n(9);
+ put_n(z == p);
+ put_n(z < p);
+ put_n(7);
+ put_c(10);
+ return 0;
+}
diff --git a/tests/programs/ftype.zc b/tests/programs/ftype.zc
new file mode 100644
index 0000000..6db7f5a
--- /dev/null
+++ b/tests/programs/ftype.zc
@@ -0,0 +1,47 @@
+fn put_f(x) { asm { x @ #1220 } return 0; }
+fn sp() { put_c(32); return 0; }
+
+struct cell { int tag; float field; }
+
+float g;
+
+float add(float a, float b) { return a + b; }
+float pick(cell* p) { return p.field; }
+
+fn main() {
+ float x;
+ float y;
+ cell c;
+ cell* q;
+ float buf[1];
+ let w = 0;
+ let z = 0.0 * -1.0;
+ let n = 0;
+ x = 1.5;
+ y = 2;
+ g = 10;
+ put_f(add(x, y)); sp();
+ put_f(add(g, 0.5)); sp();
+ put_f(add(1, 2) + 0.5); sp();
+ c.tag = 4;
+ c.field = 4.25;
+ q = &c;
+ put_f(pick(q)); sp();
+ put_f(q.field + 1); sp();
+ put_f(c.field); sp();
+ buf[0] = 6.5;
+ put_f(buf[0]); sp();
+ put_n(&(1.field)); sp();
+ w = (&c) as word;
+ put_n(w.tag); sp();
+ put_f(w.field); sp();
+ w.field = 8.5;
+ put_f(c.field); sp();
+ put_n(g > 3); sp();
+ if (z) n = 1;
+ do { if (z) n = n + 2; } while (z);
+ if (!z && g) n = n + 4;
+ put_n(n);
+ put_c(10);
+ return 0;
+}
diff --git a/tests/programs/gc.zc b/tests/programs/gc.zc
new file mode 100644
index 0000000..81412a3
--- /dev/null
+++ b/tests/programs/gc.zc
@@ -0,0 +1,18 @@
+let keep;
+unchecked fn churn(n) {
+ let i = 0, p;
+ while (i < n) { p = new(10); p[0] = i; if (i % 100 == 0) keep = p; i++; }
+ return 0;
+}
+fn main() {
+ let base = live_objects(), f;
+ churn(1000);
+ put_n(live_objects() - base); put_c(' ');
+ f = gc();
+ put_n(f >= 990); put_c(' '); // conservative: a stale word or two may keep one alive
+ put_n(keep[0]); put_c(' '); // the one a global still points at survives
+ churn(200000); // 3.2m words through a 393k word heap: needs the automatic collector
+ put_n(gc_runs > 1); put_c(' ');
+ put_n(keep[0]); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/generics.expected b/tests/programs/generics.expected
new file mode 100644
index 0000000..f3f5f7b
--- /dev/null
+++ b/tests/programs/generics.expected
@@ -0,0 +1,2 @@
+9 6 3 1 2 3 2
+42 pt int 42 10 1
diff --git a/tests/programs/generics.zc b/tests/programs/generics.zc
new file mode 100644
index 0000000..e841b19
--- /dev/null
+++ b/tests/programs/generics.zc
@@ -0,0 +1,54 @@
+// generics are compiled per set of type arguments: T can be any size, new(T)
+// stamps the real type, and a struct can take several parameters
+struct pt { int x, y; };
+struct record<K, V> { K key; V val; };
+struct box<T> { int n; T* items; };
+
+K rkey<K, V>(record<K, V>* r) { return r.key; }
+V* rval<K, V>(record<K, V>* r) { return &r.val; }
+int tsize<T>() { return sizeof(T); }
+T*.u make<T>(int n) { T* p = new(T, n) as T*; return u*p; }
+record<K, V>*.u mkrec<K, V>(K k) {
+ record<K, V>* r = new(record<K, V>);
+ r.key = k;
+ return u*r;
+}
+T pick<T>(T* a, int i) { return a[i]; }
+int sumn<T>(T* a, int n) {
+ int s = 0;
+ for (int i = 0; i < n; i++) s += a[i];
+ return s;
+}
+fn nameof<T>() { return tname(typeof(T)); }
+o3 int osum<T>(T* a, int n) {
+ int s = 0;
+ for (int i = 0; i < n; i++) s += a[i].x + a[i].y;
+ return s;
+}
+int depth<T>(int n) {
+ if (n == 0) return tsize<T>();
+ return depth<T>(n - 1);
+}
+int main() {
+ record<int, pt>* a = mkrec<int, pt>(9);
+ a.val.x = 5; a.val.y = 6;
+ put_n(rkey<int, pt>(a)); put_c(' ');
+ put_n(rval<int, pt>(a).y); put_c(' ');
+ put_n(sizeof(record<int, pt>)); put_c(' ');
+ put_n(tsize<int>()); put_c(' ');
+ put_n(tsize<pt>()); put_c(' ');
+ put_n(tsize<record<int, pt>>()); put_c(' ');
+ put_n(depth<pt>(5)); put_c(10);
+ pt* ps = make<pt>(3);
+ ps[2].x = 40; ps[2].y = 2;
+ put_n(pick<pt>(ps, 2).x + ps[2].y); put_c(' ');
+ put_s(nameof<pt>()); put_c(' '); put_s(nameof<int>()); put_c(' ');
+ put_n(osum<pt>(ps, 3)); put_c(' ');
+ int* is = make<int>(4);
+ is[0] = 1; is[1] = 2; is[2] = 3; is[3] = 4;
+ put_n(sumn<int>(is, 4)); put_c(' ');
+ record<pt*, int>* b = mkrec<pt*, int>(ps);
+ put_n(b.key == ps); put_c(10);
+ free(d*a); free(d*b); free(d*ps); free(d*is);
+ return 0;
+}
diff --git a/tests/programs/genmeth.expected b/tests/programs/genmeth.expected
new file mode 100644
index 0000000..3d418c1
--- /dev/null
+++ b/tests/programs/genmeth.expected
@@ -0,0 +1 @@
+3 60 11 21 12
diff --git a/tests/programs/genmeth.zc b/tests/programs/genmeth.zc
new file mode 100644
index 0000000..305c55c
--- /dev/null
+++ b/tests/programs/genmeth.zc
@@ -0,0 +1,36 @@
+// methods on generic structs: each set of type arguments gets its own copy
+struct pt { int x, y; };
+struct table<K, V> {
+ int n; K* keys; V* vals;
+ fn init(int cap) {
+ this.n = 0;
+ this.keys = new(K, cap) as K*;
+ this.vals = new(V, cap) as V*;
+ return 0;
+ }
+ fn put(K k, V v) {
+ this.keys[this.n] = k;
+ this.vals[this.n] = v;
+ this.n++;
+ return this.n;
+ }
+ fn width() { return sizeof(K) * 10 + sizeof(V); }
+ fn total() {
+ int s = 0;
+ for (int i = 0; i < this.n; i++) s += this.vals[i];
+ return s;
+ }
+};
+int main() {
+ table<int, int> a;
+ a.init(8);
+ a.put(1, 10); a.put(2, 20); a.put(3, 30);
+ put_n(a.n); put_c(' '); put_n(a.total()); put_c(' '); put_n(a.width()); put_c(' ');
+ table<pt, int>* b = new(table<pt, int>);
+ b.init(4);
+ put_n(b.width()); put_c(' ');
+ table<int, pt>* c = new(table<int, pt>);
+ c.init(4);
+ put_n(c.width()); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/growheap.zc b/tests/programs/growheap.zc
new file mode 100644
index 0000000..07e10a6
--- /dev/null
+++ b/tests/programs/growheap.zc
@@ -0,0 +1,32 @@
+// the heap grows past its initial size: eight live blocks of 300000 words
+// don't fit in the 393216-word start, so the allocator takes more memory from
+// the host, and a block bigger than the start fits once they are freed
+let keep[8];
+unchecked fn fill(n) {
+ let i = 0, a;
+ while (i < n) { a = new(300000); a[0] = i + 1; a[299999] = (i + 1) * 3; keep[i] = a; i++; }
+ return 0;
+}
+unchecked fn total(n) {
+ let i = 0, a, sum = 0;
+ while (i < n) { a = keep[i]; sum = sum + a[0] + a[299999]; i++; }
+ return sum;
+}
+unchecked fn drop(n) {
+ let i = 0;
+ while (i < n) { free(keep[i]); keep[i] = 0; i++; }
+ return 0;
+}
+fn main() {
+ let a, start = heap_size();
+ fill(8);
+ put_n(total(8)); put_c(' ');
+ put_n(heap_size() > start); put_c(' ');
+ drop(8);
+ scrub(); gc();
+ a = new(3000000);
+ a[2999999] = 7;
+ put_n(a[2999999]); put_c(' ');
+ put_n(heap_size() >= 3000000); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/growone.zc b/tests/programs/growone.zc
new file mode 100644
index 0000000..12980ff
--- /dev/null
+++ b/tests/programs/growone.zc
@@ -0,0 +1,9 @@
+// one block bigger than the initial heap: the machine grows, and the block is usable
+fn main() {
+ let a = new(400000);
+ a[0] = 5;
+ a[399999] = 7;
+ put_n(a[0] + a[399999]); put_c(' ');
+ put_n(heap_size() >= 400000); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/infer_return.zc b/tests/programs/infer_return.zc
new file mode 100644
index 0000000..a9f12bd
--- /dev/null
+++ b/tests/programs/infer_return.zc
@@ -0,0 +1,3 @@
+// a owned return with no .u on the signature: the return type is inferred from the body
+fn mk(v) { int* p = new(1); p[0] = v; return p; }
+fn main() { int* q = mk(3); put_n(q[0]); put_c(10); return 0; }
diff --git a/tests/programs/interior.zc b/tests/programs/interior.zc
new file mode 100644
index 0000000..bccf377
--- /dev/null
+++ b/tests/programs/interior.zc
@@ -0,0 +1,17 @@
+unchecked fn main() {
+ let p = new(4), r, i = 0, t, bad = 0;
+ mem_set(4);
+ p[0] = 111; p[1] = 222; p[2] = 333; p[3] = 444;
+ r = p + 1;
+ free(p);
+ gc();
+ while (i < 8) {
+ t = new(4);
+ if (t == r - 1) bad = 1;
+ i++;
+ }
+ if (r[0] != 222) bad = bad + 2;
+ put_n(bad);
+ put_c(10);
+ return 0;
+}
diff --git a/tests/programs/joins.zc b/tests/programs/joins.zc
new file mode 100644
index 0000000..98275ad
--- /dev/null
+++ b/tests/programs/joins.zc
@@ -0,0 +1,31 @@
+// joins: something given away on one path is dead afterwards, and the paths
+// that still own it free it on the way in. nothing here leaks.
+fn take(.d p) { return p[0]; }
+fn later(.d p); // not needed any more, but still allowed
+int*.u mk(int v) { int* p = new(int); *p = v; return u*p; }
+fn count(int n) { let s = 0; switch (n % 4) { case 0: s = 10; case 1, 2: s = 20; break; default: s = 30; } return s; }
+fn main() {
+ let base = live_objects(), i = 0, hits = 0, k;
+ while (i < 50) {
+ let x = mk(i);
+ if (i % 3 == 0) take(d*x); // else: freed here by the fixup
+ i++;
+ }
+ put_n(live_objects() - base); put_c(' ');
+ // give away and break: fine now; the normal exit frees it
+ i = 0;
+ let y = mk(7);
+ while (i < 10) { if (i == 4) { take(d*y); break; } i++; }
+ put_n(live_objects() - base); put_c(' ');
+ let z = mk(5);
+ while (1) { if (z[0] == 5) { hits = take(d*z); break; } }
+ put_n(hits); put_c(' ');
+ put_n(count(0) + count(1) + count(2) + count(3)); put_c(' ');
+ k = i > 3 ? i * 2 : 0 - 1;
+ put_n(k); put_c(' ');
+ put_n(forward(new(int))); put_c(' '); // tagged and defined below: the prescan knows .d
+ int** pp; int* q = new(int); *q = 9; pp = &q; put_n(**pp); free(d*q); put_c(' ');
+ put_n(live_objects() - base); put_c(10);
+ return 0;
+}
+fn forward(int*.d p) { return 3 + *p; }
diff --git a/tests/programs/manyerrors.zc b/tests/programs/manyerrors.zc
new file mode 100644
index 0000000..3ba2c5a
--- /dev/null
+++ b/tests/programs/manyerrors.zc
@@ -0,0 +1,6 @@
+// several errors in one run: a syntax error, an analyzer error, a type error
+typecheck on;
+fn f() { let a = ; return 0; }
+fn g() { let x = new(3); return 0; }
+int h() { char* s = 5; return 1; }
+fn main() { return 0 }
diff --git a/tests/programs/memleak.zc b/tests/programs/memleak.zc
new file mode 100644
index 0000000..a61c331
--- /dev/null
+++ b/tests/programs/memleak.zc
@@ -0,0 +1,11 @@
+memdebug on;
+int* g;
+fn main()
+{
+ int* p = new(int);
+ *p = 7;
+ g = d*p;
+ put_n(memreport());
+ put_c(10);
+ return 0;
+}
diff --git a/tests/programs/memory_bounds.zc b/tests/programs/memory_bounds.zc
new file mode 100644
index 0000000..0863d03
--- /dev/null
+++ b/tests/programs/memory_bounds.zc
@@ -0,0 +1,13 @@
+// raw word loads, stores, and stack picks outside memory follow portable semantics.
+unchecked int raw_pick(int depth) { return asm { depth @ #6 }; }
+unchecked fn main() {
+ int p = -1;
+ put_n(*p); put_c(32); *p = 7;
+ p = 0x7FFFFFFF;
+ put_n(*p); put_c(32); *p = 9;
+ p = 0x100000000;
+ put_n(*p); put_c(32);
+ put_n(raw_pick(0x100000000)); put_c(32);
+ put_n(raw_pick(0 - 0x100000000)); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/meta.zc b/tests/programs/meta.zc
new file mode 100644
index 0000000..6afdf36
--- /dev/null
+++ b/tests/programs/meta.zc
@@ -0,0 +1,43 @@
+fn ipow(a, b) { let r = 1; while (b > 0) { r *= a; b--; } return r; }
+// a parse word that adds an operator to the compiler's tables
+immediate fn pow_op() { next(); add_binop("**", 11, "ipow c"); return 0; }
+pow_op
+
+// unless (cond) stmt: a parse word that emits code itself
+immediate fn unless() {
+ let a;
+ next();
+ expect(intern("(")); rval(); plainuse(expr()); rval(); expect(intern(")"));
+ emitseq(useq[0], ulen[0]); // not
+ a = hole(); op(O_JZ); // skip the body when the condition was true
+ substmt();
+ setlit(a, here);
+ return 0;
+}
+// repeat N { body }: a macro, built from token lists
+immediate fn repeat() {
+ let n, body, i = 0;
+ next(); n = tv; next();
+ body = grab();
+ while (i < n) { feed(qdup(body)); i++; }
+ free(d*body);
+ return 0;
+}
+fn main() {
+ put_n(2 ** 10); put_c(' '); put_n(3 + 2 ** 3 * 2); put_c(10);
+ unless (1 > 2) put_s("unless ok\n");
+ unless (2 > 1) put_s("wrong\n");
+ repeat 3 { put_c('r'); }
+ put_c(10);
+ let q = quote { put_n(40 + 2); put_c(10); return 7; };
+ put_n(q[0]); put_c(' ');
+ put_n(eval(q)); put_c(10);
+ let f = compile(q);
+ put_n(f()); put_c(10);
+ free(d*q);
+ let defs = quote { fn sq(x) { return x * x; } };
+ load(defs);
+ free(d*defs);
+ put_n(evals("return sq(12);")); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/methodown.zc b/tests/programs/methodown.zc
new file mode 100644
index 0000000..98ba146
--- /dev/null
+++ b/tests/programs/methodown.zc
@@ -0,0 +1,15 @@
+// a struct method can return an owned object: its header declares the .u return
+struct Node { int v; }
+struct Maker {
+ static Node*.u make(int n) { Node* p = new(Node); p.v = n; return u*p; }
+}
+Node*.u mk(int n) { Node* p = new(Node); p.v = n; return u*p; }
+int main() {
+ Node*.u a = Maker.make(7);
+ put_n(a.v); put_c(32);
+ free(d*a);
+ Node*.u b = mk(9);
+ put_n(b.v); put_c(10);
+ free(d*b);
+ return 0;
+}
diff --git a/tests/programs/misc.zc b/tests/programs/misc.zc
new file mode 100644
index 0000000..530322a
--- /dev/null
+++ b/tests/programs/misc.zc
@@ -0,0 +1,27 @@
+// ct { ... } runs a block while compiling and leaves its result as a constant
+immediate fn ct() {
+ let q, v;
+ next();
+ q = grab();
+ v = eval(q);
+ free(d*q);
+ lit(v);
+ return 0;
+}
+// teach the asm syntax that p means out, like the old repl demo
+immediate fn teach() { next(); mnem[12] = 'p'; return 0; }
+teach
+
+fn countdown() {
+ asm { 5 :top 0 $ '0' + p 1 - 0 $ done z top j :done _ 10 p }
+ return 0;
+}
+fn main() {
+ put_n(ct { let i = 0, s = 0; while (i < 10) { s += i; i++; } return s; }); put_c(10);
+ countdown();
+ put_s(lazy()); put_c(10);
+ return 0;
+}
+// the analyzer is off from here on
+unchecked;
+fn lazy() { let p = new(3); p[0] = 'o'; p[1] = 'k'; return p; }
diff --git a/tests/programs/names.zc b/tests/programs/names.zc
new file mode 100644
index 0000000..6ccb2eb
--- /dev/null
+++ b/tests/programs/names.zc
@@ -0,0 +1,9 @@
+fn f(d) { let u = 3, m = 4; if (d > 0) return u + m + d; return 0; }
+fn dmul(d) { return d * 4; }
+fn main() {
+ let u = 1;
+ put_n(f(5)); put_c(' ');
+ put_n(u); put_c(' ');
+ put_n(dmul(2)); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/o3.zc b/tests/programs/o3.zc
new file mode 100644
index 0000000..c875d09
--- /dev/null
+++ b/tests/programs/o3.zc
@@ -0,0 +1,75 @@
+// o3 regions: every kind of control flow, against the same code without o3
+struct pt { int x; int y; };
+int g = 3;
+int sq(int x) { return x * x; }
+o3 int collatz(int n) {
+ int steps = 0;
+ while (n != 1) { if (n % 2 == 0) n = n / 2; else n = 3 * n + 1; steps++; }
+ return steps;
+}
+o3 int ack(int am, int n) {
+ if (am == 0) return n + 1;
+ if (n == 0) return ack(am - 1, 1);
+ return ack(am - 1, ack(am, n - 1));
+}
+o3 int many(int a, int b, int c) {
+ int dd = a + b, e = b + c, f = c + a, h = dd * e, i2 = e * f, j = f * dd, k = h + i2 + j, l = k / 7;
+ for (int t = 0; t < 5; t++) { dd += t; e -= t; f = f ^ t; l = l + dd * e - f; }
+ return dd + e + f + h + i2 + j + k + l;
+}
+o3 int mix(int n) {
+ int acc = 0;
+ for (int i = 0; i < n; i++) {
+ if (i % 3 == 0 && i % 5 == 0) acc += 15;
+ else if (i % 3 == 0 || i > 90) acc += 3;
+ else acc = acc + (i & 1 ? sq(i) : g);
+ switch (i % 4) { case 0: acc += 1; case 1: acc -= 2; default: acc = acc ^ 1; }
+ if (acc > 100000) break;
+ if (i == 7) continue;
+ acc = acc + i / 10 - i % 7 + (i << 2) - (i >> 1);
+ }
+ return acc;
+}
+o3 int dyn(int n) {
+ []int a = new([]int, n);
+ fixed []int f = new(fixed []int, n);
+ int s = 0;
+ for (int i = 0; i < len(a); i++) { a[i] = i * i; f[i] = n - i; }
+ for (int i = 0; i < n; i++) s = s + a[i] * f[i];
+ free(d*a); free(d*f);
+ return s;
+}
+o3 int pts(int n) {
+ pt* p = new(pt);
+ int s = 0;
+ p->x = 1; p->y = 2;
+ for (int i = 0; i < n; i++) { p->x = p->x + p->y; p->y = p->x - p->y; s = s + p->x % 1000; }
+ free(d*p);
+ return s;
+}
+o3 int divs(int x) {
+ int s = 0;
+ for (int i = -50; i < 50; i++) s = s + (x + i) / 7 + (x + i) % 7 + (x - i) / -3 + i / 16 + i % 16;
+ return s;
+}
+fn main() {
+ o3 int lone = 2;
+ let r = 0;
+ lone = lone * 21;
+ o3 {
+ int i = 0, s = 0;
+ while (i < 100) { s = s + collatz(i + 1); i++; }
+ put_n(s); put_c(' ');
+ put_n(lone); put_c(' ');
+ lone++;
+ }
+ put_n(lone); put_c(' ');
+ put_n(ack(2, 3)); put_c(' ');
+ put_n(many(1, 2, 3)); put_c(' ');
+ put_n(mix(100)); put_c(' ');
+ put_n(dyn(50)); put_c(' ');
+ put_n(pts(30)); put_c(' ');
+ put_n(divs(1000)); put_c(' ');
+ put_n(divs(-77)); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/o3_bounds.zc b/tests/programs/o3_bounds.zc
new file mode 100644
index 0000000..fb347c0
--- /dev/null
+++ b/tests/programs/o3_bounds.zc
@@ -0,0 +1,2 @@
+// an index outside a []T inside o3 code: caught by the inline check
+fn main() { []int a = new([]int, 5); o3 { int i = 7; a[i] = 1; } free(d*a); return 0; }
diff --git a/tests/programs/o3bytes.zc b/tests/programs/o3bytes.zc
new file mode 100644
index 0000000..bada583
--- /dev/null
+++ b/tests/programs/o3bytes.zc
@@ -0,0 +1,52 @@
+// byte ops called from o3 stay in the region. ld8/st8 and the 16- and 32-bit
+// pair are opcodes there, on the interpreter, the arm64 jit and llvm.
+fn sp() { put_c(32); return 0; }
+fn litw(v) { return v + v + 1; }
+fn f() { put_c('A'); return 0; }
+fn main() {
+ let buf[4], b, i, s, t, p, v, w;
+ i = 0;
+ while (i < 4) { buf[i] = 0; i++; }
+ b = addr_b(buf);
+ i = 0;
+ s = 0;
+ o3 {
+ while (i < 20) {
+ t = b + i;
+ st8(t, i);
+ s = s + ld8(t);
+ i++;
+ }
+ }
+ put_n(s); sp();
+ o3 { st16(b, 4660); w = ld16(b); }
+ put_n(w); sp();
+ t = b + 7;
+ o3 { w = ld8(t); }
+ put_n(w); sp();
+ o3 { w = ld16(t); }
+ put_n(w); sp();
+ t = b + 10;
+ v = 16909060;
+ o3 { st32(t, v); w = ld32(t); }
+ put_n(w); sp();
+ t = 0 - 1;
+ o3 { st8(t, v); w = ld8(t); }
+ put_n(w); sp();
+ t = addr_b(MW) - 1;
+ o3 { w = ld16(t); }
+ put_n(w);
+ put_c(10);
+ p = f;
+ f();
+ while (*p != litw('A')) p++;
+ t = addr_b(p);
+ v = litw('B') & 255;
+ o3 { st8(t, v); }
+ f();
+ v = litw('C') & 255;
+ o3 { st8(t, v); }
+ f();
+ put_c(10);
+ return 0;
+}
diff --git a/tests/programs/o3call.zc b/tests/programs/o3call.zc
new file mode 100644
index 0000000..b2be717
--- /dev/null
+++ b/tests/programs/o3call.zc
@@ -0,0 +1,29 @@
+// new stamps the header, so an o3 caller and an ordinary caller both see a type.
+// the call itself is not a boundary: no fatten, and the callee's prologue does not run.
+struct node { int v; node* c; };
+unchecked o3 node* leaf(int v) {
+ node* n = new(node);
+ n.v = v; n.c = 0;
+ return n;
+}
+unchecked o3 int inner() {
+ node* n = leaf(7);
+ int* p = n;
+ return p[-1];
+}
+unchecked o3 int add(int a, int b) { return a + b; }
+unchecked o3 int nest(int n) {
+ if (n == 0) return 0;
+ return n + nest(n - 1);
+}
+unchecked fn main() {
+ node* n = leaf(4);
+ int* p = n;
+ put_n(p[-1] < 0); put_c(' ');
+ put_n(inner() < 0); put_c(' ');
+ put_n(n.v); put_c(' ');
+ put_n(add(20, 3)); put_c(' ');
+ put_n(nest(10)); put_c(' ');
+ tprint(typeof_obj(n)); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/o3copy.zc b/tests/programs/o3copy.zc
new file mode 100644
index 0000000..90b9ade
--- /dev/null
+++ b/tests/programs/o3copy.zc
@@ -0,0 +1,12 @@
+// copies of a variable that a loop reads again: the global pass reads the variable itself
+o3 int g(int n, int b) {
+ int s = 0, c = b, i = 0;
+ while (i < n) { c = b; s = s + c * 3 + i; i++; }
+ return s + c;
+}
+o3 int h(int n, int k) {
+ int a = k, t = 0, i = 0;
+ while (i < n) { t = t + a; if (t > 1000) t = t - 1000; a = k; i++; }
+ return t + a;
+}
+fn main() { put_n(g(1000, 7)); put_c(32); put_n(h(1000, 5)); put_c(10); return 0; }
diff --git a/tests/programs/o3deep.zc b/tests/programs/o3deep.zc
new file mode 100644
index 0000000..e715d9e
--- /dev/null
+++ b/tests/programs/o3deep.zc
@@ -0,0 +1,3 @@
+// o3 recursion nests on the host stack as deep as the machine's own stacks allow
+o3 int deep(int n) { if (n == 0) return 0; return 1 + deep(n - 1); }
+fn main() { put_n(deep(30000)); put_c(10); return 0; }
diff --git a/tests/programs/o3deep_over.zc b/tests/programs/o3deep_over.zc
new file mode 100644
index 0000000..45816f6
--- /dev/null
+++ b/tests/programs/o3deep_over.zc
@@ -0,0 +1,3 @@
+// recursion can grow beyond the original operand and frame reservations
+o3 int deep(int n) { if (n == 0) return 0; return 1 + deep(n - 1); }
+fn main() { put_n(deep(100000)); put_c(10); return 0; }
diff --git a/tests/programs/o3direct.expected b/tests/programs/o3direct.expected
new file mode 100644
index 0000000..ec32091
--- /dev/null
+++ b/tests/programs/o3direct.expected
@@ -0,0 +1,2 @@
+6765 1 0 123 4567 7 9 8.000000 5050 1926
+4 3 2 1 10
diff --git a/tests/programs/o3direct.zc b/tests/programs/o3direct.zc
new file mode 100644
index 0000000..3c5d7ee
--- /dev/null
+++ b/tests/programs/o3direct.zc
@@ -0,0 +1,33 @@
+// o3 calls between functions: arguments in registers, results on the stack, and the ways a call can leave the fast path
+fn put_f(x) { asm { x @ #1220 } return 0; }
+struct node { int v; node *next; };
+o3 int fib(int n) { if (n < 2) return n; return fib(n - 1) + fib(n - 2); }
+o3 int ev(int n);
+o3 int od(int n) { if (n == 0) return 0; return ev(n - 1); }
+o3 int ev(int n) { if (n == 0) return 1; return od(n - 1); }
+o3 int three(int a, int b, int c) { return a * 100 + b * 10 + c; }
+o3 int four(int a, int b, int c, int d) { return three(a, b, c) * 10 + d; }
+o3 int zero() { return 7; }
+o3 int ack(int m, int n) { if (m == 0) return n + 1; if (n == 0) return ack(m - 1, 1); return ack(m - 1, ack(m, n - 1)); }
+o3 float half(float x, int k) { if (k == 0) return x; return half(x / 2.0, k - 1); }
+o3 int noisy(int n) { if (n == 0) return 0; put_n(n); put_c(' '); return n + noisy(n - 1); }
+unchecked o3 int sumlist(node *p) { int s = 0; while (p) { s = s + p.v; p = p.next; } return s; }
+unchecked o3 int build(int n) {
+ node *h = 0;
+ for (int i = 1; i <= n; i++) { node *x = new(node); x.v = i; x.next = h; h = x; }
+ int s = sumlist(h);
+ while (h) { node *t = h.next; free(h); h = t; }
+ return s;
+}
+unchecked o3 int mix(int a, int b) { return three(a, zero(), b) + fib(a) + four(1, 2, 3, b); }
+unchecked fn main() {
+ put_n(fib(20)); put_c(' ');
+ put_n(ev(10)); put_c(' '); put_n(ev(7)); put_c(' ');
+ put_n(three(1, 2, 3)); put_c(' '); put_n(four(4, 5, 6, 7)); put_c(' '); put_n(zero()); put_c(' ');
+ put_n(ack(2, 3)); put_c(' ');
+ put_f(half(64.0, 3)); put_c(' ');
+ put_n(build(100)); put_c(' ');
+ put_n(mix(6, 9)); put_c(10);
+ put_n(noisy(4)); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/o3fat.zc b/tests/programs/o3fat.zc
new file mode 100644
index 0000000..937b0f3
--- /dev/null
+++ b/tests/programs/o3fat.zc
@@ -0,0 +1,20 @@
+// o3 objects reaching normal code get their types: through a return (all the way down the list), an assignment, a store into a field
+struct node { int v; node* next; };
+struct box { [3]node* slots; int n; };
+unchecked o3 node* build(int n) {
+ node* head = 0;
+ for (int i = 0; i < n; i++) { node* k = new(node); k.v = i; k.next = head; head = k; }
+ return head;
+}
+unchecked fn main() {
+ node* l = build(5);
+ tprint(typeof_obj(l)); put_c(' '); tprint(typeof_obj(l.next.next.next)); put_c(' ');
+ put_n(l.next.v); put_c(' ');
+ box* b = 0;
+ o3 { b = new(box); b.slots[1] = new(node); b.n = 2; }
+ tprint(typeof_obj(b)); put_c(' '); tprint(typeof_obj(b.slots[1])); put_c(' ');
+ int* raw = 0;
+ o3 { raw = new(int, 6); }
+ tprint(typeof_obj(raw)); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/o3licm.zc b/tests/programs/o3licm.zc
new file mode 100644
index 0000000..61e6a46
--- /dev/null
+++ b/tests/programs/o3licm.zc
@@ -0,0 +1,46 @@
+// loop invariants are built once in front of the loop, and a value two blocks both build is shared.
+// every result below has to match the plain run (ZYNOSHARE=1 turns the pass off).
+unchecked o3 int nest(int n, int k) {
+ int s = 0;
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < n; j++) {
+ s = s + (k * 37 + 11) * (i + j);
+ }
+ }
+ return s;
+}
+unchecked o3 int loads(int* a, int n) {
+ int s = 0;
+ for (int i = 0; i < n; i++) s = s + a[0] * a[1] + i;
+ return s;
+}
+unchecked o3 int stores(int* a, int n) {
+ int s = 0;
+ for (int i = 0; i < n; i++) { a[0] = a[0] + 1; s = s + a[0] * 3; }
+ return s;
+}
+unchecked o3 int branchy(int x, int y, int n) {
+ int s = 0;
+ for (int i = 0; i < n; i++) {
+ if (i & 1) s = s + x * y * 5;
+ else s = s - x * y * 5 + i;
+ }
+ return s;
+}
+unchecked o3 int twice(int x, int y) {
+ int r = 0;
+ if (x > 0) r = (x * y + 3) * (x * y + 3);
+ else r = x * y + 3;
+ return r + (x * y + 3) * 7;
+}
+unchecked fn main() {
+ int* a = new(int, 4);
+ a[0] = 5; a[1] = 6;
+ put_n(nest(30, 3)); put_c(' ');
+ put_n(loads(a, 100)); put_c(' ');
+ put_n(stores(a, 10)); put_c(' ');
+ put_n(branchy(3, 4, 21)); put_c(' ');
+ put_n(twice(5, 2)); put_c(' ');
+ put_n(twice(-5, 2)); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/o3push.expected b/tests/programs/o3push.expected
new file mode 100644
index 0000000..b6857dc
--- /dev/null
+++ b/tests/programs/o3push.expected
@@ -0,0 +1 @@
+1000 1498500 2997 1005 1 10 1 1498530 0
diff --git a/tests/programs/o3push.zc b/tests/programs/o3push.zc
new file mode 100644
index 0000000..e5f0090
--- /dev/null
+++ b/tests/programs/o3push.zc
@@ -0,0 +1,38 @@
+// push in o3 code: the quick path when there is room, dpush when there is not
+o3 int fill([]int a, int n) {
+ for (int i = 0; i < n; i++) push(a, i * 3);
+ return len(a);
+}
+o3 int sum([]int a) {
+ int s = 0;
+ for (int i = 0; i < len(a); i++) s += a[i];
+ return s;
+}
+o3 float fsum([]float a) {
+ float s = 0.0;
+ for (int i = 0; i < len(a); i++) s += a[i];
+ return s;
+}
+o3 int ffill([]float a, int n) {
+ for (int i = 0; i < n; i++) push(a, 0.5 * i);
+ return len(a);
+}
+int main() {
+ []int a = new([]int, 0);
+ put_n(fill(a, 1000)); put_c(' ');
+ put_n(sum(a)); put_c(' ');
+ put_n(a[999]); put_c(' ');
+ put_n(fill(a, 5)); put_c(' ');
+ put_n(cap(a) >= 1005); put_c(' ');
+ []float f = new([]float, 0);
+ ffill(f, 10);
+ put_n(len(f)); put_c(' '); put_n(fsum(f) == 22.5); put_c(' ');
+ put_n(drain(a)); put_c(' '); put_n(len(a)); put_c(10);
+ free(d*a); free(d*f);
+ return 0;
+}
+o3 int drain([]int a) {
+ int s = 0;
+ while (len(a) > 0) s += pop(a);
+ return s;
+}
diff --git a/tests/programs/o3scopes.zc b/tests/programs/o3scopes.zc
new file mode 100644
index 0000000..4dbf68c
--- /dev/null
+++ b/tests/programs/o3scopes.zc
@@ -0,0 +1,17 @@
+// registers reused by scopes that never overlap, and a fixed array read through its cached items and length
+o3 int scopes(fixed []int f) {
+ int total = 0;
+ for (int a = 0; a < 3; a++) { int x = a * 2; total += x; }
+ for (int b = 0; b < 3; b++) { int y = b * 3; total += y; }
+ for (int c = 0; c < 3; c++) { int z = c * 5; total += z; }
+ for (int e = 0; e < len(f); e++) { int w = f[e]; total += w; }
+ for (int g = 0; g < 4; g++) { int h = g + 1, q = h * h, r = q - 1; total += r; }
+ return total;
+}
+fn main() {
+ fixed []int f = new(fixed []int, 4);
+ for (int i = 0; i < 4; i++) f[i] = 10 * i;
+ put_n(scopes(f)); put_c(10);
+ free(d*f);
+ return 0;
+}
diff --git a/tests/programs/o3zero.zc b/tests/programs/o3zero.zc
new file mode 100644
index 0000000..cfb3061
--- /dev/null
+++ b/tests/programs/o3zero.zc
@@ -0,0 +1,8 @@
+unchecked o3 int raw() {
+ int* p = new(4);
+ return p[0] + p[1] + p[2] + p[3] + (p[-1] < 0);
+}
+fn main() {
+ put_n(raw()); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/overflow_data.zc b/tests/programs/overflow_data.zc
new file mode 100644
index 0000000..db24f8d
--- /dev/null
+++ b/tests/programs/overflow_data.zc
@@ -0,0 +1 @@
+fn main() { syscall(9, 5, syscall(9, 0, 0, 0), 0); while (1) asm { 1 } return 0; }
diff --git a/tests/programs/overflow_frame.zc b/tests/programs/overflow_frame.zc
new file mode 100644
index 0000000..c017453
--- /dev/null
+++ b/tests/programs/overflow_frame.zc
@@ -0,0 +1,2 @@
+fn big(n) { let a[500]; a[0] = n; return big(n + 1) + a[0]; }
+fn main() { syscall(9, 5, syscall(9, 0, 0, 0), 0); return big(0); }
diff --git a/tests/programs/overflow_return.zc b/tests/programs/overflow_return.zc
new file mode 100644
index 0000000..91e572d
--- /dev/null
+++ b/tests/programs/overflow_return.zc
@@ -0,0 +1,2 @@
+fn deep(n) { return deep(n + 1) + 1; }
+fn main() { syscall(9, 5, syscall(9, 0, 0, 0), 0); return deep(0); }
diff --git a/tests/programs/packedfields.expected b/tests/programs/packedfields.expected
new file mode 100644
index 0000000..df81ff5
--- /dev/null
+++ b/tests/programs/packedfields.expected
@@ -0,0 +1,2 @@
+4 3 3.500000 0 200 255 44 7 -3 65535 99 44 0 -4 0 98 1000 5 3.250000 77 5 42 17 3 -9 0 12 12 123456789
+15200 11 50
diff --git a/tests/programs/packedfields.zc b/tests/programs/packedfields.zc
new file mode 100644
index 0000000..8f6fe38
--- /dev/null
+++ b/tests/programs/packedfields.zc
@@ -0,0 +1,42 @@
+// struct fields of the sized types and f32 pack into shared words
+struct vtx { f32 x, y, z; u8 r, g, b, a; i16 id; u16 flags; int next; };
+struct pair { u8 a; int b; u8 c; };
+fn put_f(x) { asm { x @ #1220 } return 0; }
+fn sp() { put_c(32); return 0; }
+vtx gv;
+fn main() {
+ vtx v;
+ vtx* p = new(vtx);
+ pair q;
+ [3]vtx arr;
+ put_n(sizeof(vtx)); sp(); put_n(sizeof(pair)); sp();
+ v.x = 1.5; v.y = 0.1; v.z = 2; v.r = 200; v.g = 255; v.b = 300; v.a = 7; v.id = 0 - 3; v.flags = 65535; v.next = 99;
+ put_f(v.x + v.z); sp(); put_n(v.y == 0.1); sp();
+ put_n(v.r); sp(); put_n(v.g); sp(); put_n(v.b); sp(); put_n(v.a); sp(); put_n(v.id); sp(); put_n(v.flags); sp(); put_n(v.next); sp();
+ v.r += 100; v.flags++; v.id--; v.g = v.g + 1;
+ put_n(v.r); sp(); put_n(v.flags); sp(); put_n(v.id); sp(); put_n(v.g); sp();
+ p.r = 9; p.a = 8; p.id = 1000; p.next = 5; p.x = 3.25;
+ put_n(p.r); put_n(p.a); sp(); put_n(p.id); sp(); put_n(p.next); sp(); put_f(p.x); sp();
+ gv.b = 77; gv.flags = 5; put_n(gv.b); sp(); put_n(gv.flags); sp();
+ arr[1].a = 42; arr[2].r = 17; arr[1].next = 3; arr[0].id = 0 - 9;
+ put_n(arr[1].a); sp(); put_n(arr[2].r); sp(); put_n(arr[1].next); sp(); put_n(arr[0].id); sp(); put_n(arr[0].a); sp();
+ put_n(sizeof(arr)); sp();
+ q.a = 1; q.b = 123456789; q.c = 2; put_n(q.a); put_n(q.c); sp(); put_n(q.b);
+ put_c(10);
+ free(d*p);
+ second();
+ return 0;
+}
+struct rgba { u8 r, g, b, a; };
+o3 int bright(rgba* px, int n) {
+ int s = 0;
+ for (int i = 0; i < n; i++) { s += px[i].r + px[i].g + px[i].b; px[i].a = px[i].r + 1; }
+ return s;
+}
+fn second() {
+ rgba* px = new(rgba, 50);
+ for (int i = 0; i < 50; i++) { px[i].r = i; px[i].g = i * 2; px[i].b = 255 - i; }
+ put_n(bright(px, 50)); put_c(' '); put_n(px[10].a); put_c(' '); put_n(px[49].a); put_c(10);
+ free(d*px);
+ return 0;
+}
diff --git a/tests/programs/quarantine.zc b/tests/programs/quarantine.zc
new file mode 100644
index 0000000..5e3e188
--- /dev/null
+++ b/tests/programs/quarantine.zc
@@ -0,0 +1,25 @@
+// free quarantines the block. a stale alias still reads it, and freeing
+// through the alias does nothing because the header already says free.
+// unchecked so the analyzer doesn't reject the use after free.
+unchecked fn main() {
+ let p = new(4), q, r, i = 0, t, bad = 0;
+ p[0] = 111; p[1] = 222; p[2] = 333; p[3] = 444;
+ q = p;
+ r = p + 2;
+ free(p);
+ while (i < 400) { t = new(4); t[0] = 999; t[1] = 888; t[2] = 777; t[3] = 666; i++; }
+ if (q[0] != 111 || q[1] != 222 || r[0] != 333 || r[1] != 444) bad = 1;
+ free(q);
+ if (q[0] != 111 || q[1] != 222) bad = 2;
+ gc(); // q still points at the block, so the sweep must not reuse it
+ if (q[0] != 111 || r[0] != 333) bad = 3;
+ p = 0; q = 0; r = 0; t = 0;
+ scrub();
+ gc();
+ t = new(4);
+ t[0] = 1234;
+ put_n(bad); put_c(' ');
+ put_n(t[0]); put_c(10);
+ free(t);
+ return 0;
+}
diff --git a/tests/programs/rawpid.zc b/tests/programs/rawpid.zc
new file mode 100644
index 0000000..b3e7c2a
--- /dev/null
+++ b/tests/programs/rawpid.zc
@@ -0,0 +1,6 @@
+include "src/lib/std.zc";
+// syscall 39 is getpid on x86-64 only; the arm64 number differs, so test.sh runs this there alone.
+fn main() {
+ put_n(Host.raw(39, 0, 0, 0, 0, 0, 0) > 0); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/rawsys.zc b/tests/programs/rawsys.zc
new file mode 100644
index 0000000..753e1b7
--- /dev/null
+++ b/tests/programs/rawsys.zc
@@ -0,0 +1,5 @@
+include "src/lib/std.zc";
+fn main() {
+ put_n(Host.raw(9999, 0, 0, 0, 0, 0, 0)); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/recycle.zc b/tests/programs/recycle.zc
new file mode 100644
index 0000000..98c9b50
--- /dev/null
+++ b/tests/programs/recycle.zc
@@ -0,0 +1,23 @@
+// a uniquely owned free returns the block to its size class at once.
+// a plain free still quarantines; that contract lives in quarantine.zc.
+struct node { node* c; int n; };
+unchecked fn addr(p) { return p; }
+fn main() {
+ let base = live_objects(), p, q, slot;
+ node* a;
+ node* b;
+ p = new(4);
+ a = new(node);
+ b = new(node);
+ p[0] = 111;
+ a.c = b; a.n = 1; b.c = 0; b.n = 2;
+ slot = addr(p);
+ free(d*p);
+ q = new(4);
+ free(d*a);
+ put_n(q == slot); put_c(' ');
+ put_n(q[0]); put_c(' ');
+ put_n(live_objects() - base); put_c(10);
+ free(d*q);
+ return 0;
+}
diff --git a/tests/programs/roots.zc b/tests/programs/roots.zc
new file mode 100644
index 0000000..22f12a5
--- /dev/null
+++ b/tests/programs/roots.zc
@@ -0,0 +1,17 @@
+// a pointer left below the live stack is not a root. scrub then clears that word.
+unchecked fn main() {
+ let p, slot, before, after;
+ p = new(2);
+ p[0] = 7;
+ p[1] = 8;
+ slot = DSLO + 8;
+ *slot = p;
+ before = live_objects();
+ p = 0;
+ gc();
+ after = live_objects();
+ put_n(after < before); put_c(32);
+ scrub();
+ put_n(*slot); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/scrub.zc b/tests/programs/scrub.zc
new file mode 100644
index 0000000..ebc6cfa
--- /dev/null
+++ b/tests/programs/scrub.zc
@@ -0,0 +1,9 @@
+unchecked fn main() {
+ mem_set(2);
+ let p = new(2);
+ p[0] = 11;
+ free(p);
+ put_n(p[0]);
+ put_c(10);
+ return 0;
+}
diff --git a/tests/programs/share.zc b/tests/programs/share.zc
new file mode 100644
index 0000000..e9c381f
--- /dev/null
+++ b/tests/programs/share.zc
@@ -0,0 +1,34 @@
+// shared cons cells, a shared string, and a cycle. the collector reclaims
+// them once nothing points at them. free of a shared cell does nothing.
+fn main() {
+ let base = live_objects(), a, b, c, s, p, q, cyc, n, tri;
+ a = cons(1, 2);
+ b = cons(a, 3);
+ c = cons(a, 4);
+ put_n(car(a)); put_c(32);
+ put_n(cdr(a)); put_c(32);
+ put_n(car(car(b))); put_c(32);
+ put_n(cdr(c)); put_c(32);
+ put_n(car(b) == a); put_c(32);
+ put_n(is_shared(a)); put_c(32);
+ n = live_objects();
+ free(a);
+ put_n(live_objects() == n); put_c(32);
+ put_n(car(a)); put_c(32);
+ s = n_to_s(42);
+ p = cons(s, 0);
+ q = cons(s, 1);
+ put_n(s_to_n(car(p))); put_c(32);
+ put_n(car(p) == car(q)); put_c(32);
+ cyc = cons(0, 1);
+ cyc[0] = cyc;
+ put_n(cdr(cyc)); put_c(32);
+ put_n(car(cyc) == cyc); put_c(32);
+ tri = triple(5, 6, 7);
+ put_n(tget(tri, 1)); put_c(32);
+ a = 0; b = 0; c = 0; s = 0; p = 0; q = 0; cyc = 0; tri = 0;
+ scrub();
+ gc();
+ put_n(live_objects() - base); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/share_dbg.zc b/tests/programs/share_dbg.zc
new file mode 100644
index 0000000..5485b51
--- /dev/null
+++ b/tests/programs/share_dbg.zc
@@ -0,0 +1,10 @@
+// memdebug still checks a shared cell. free does not quarantine it, and car
+// reads the payload through checkptr.
+memdebug on;
+fn main() {
+ let a = cons(7, 8);
+ free(a);
+ put_n(car(a)); put_c(32);
+ put_n(is_shared(a)); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/sized.expected b/tests/programs/sized.expected
new file mode 100644
index 0000000..c5f51ab
--- /dev/null
+++ b/tests/programs/sized.expected
@@ -0,0 +1,5 @@
+4 -56 -32768 0 44 -56 44 -56 44
+2008 -25536 4464 705032704 -128 1 8000000000 3 2
+124716 1 77 255 0
+255 0 -1 7 44 2
+54285
diff --git a/tests/programs/sized.zc b/tests/programs/sized.zc
new file mode 100644
index 0000000..0a18467
--- /dev/null
+++ b/tests/programs/sized.zc
@@ -0,0 +1,80 @@
+// sized integers: scalars wrap to their size, arrays and pointers of them are packed
+struct pix { u8 r, g, b, a; i16 dx; [4]u8 tag; };
+u8 gbyte = 300;
+i8 gneg = 200;
+u16 gword[5];
+[10]u8 gbuf;
+
+u8 add8(u8 a, u8 b) { return a + b; }
+
+fn scalars() {
+ u8 a = 250;
+ i8 s = 100;
+ i16 h = 32767;
+ u32 w = 4294967295;
+ a = a + 10; put_n(a); put_c(' '); // 4
+ s = s + 100; put_n(s); put_c(' '); // -56
+ h++; put_n(h); put_c(' '); // -32768
+ w++; put_n(w); put_c(' '); // 0
+ put_n(gbyte); put_c(' '); put_n(gneg); put_c(' '); // 44 -56
+ put_n(add8(200, 100)); put_c(' '); // 44
+ put_n((i8)200); put_c(' '); put_n(300 as u8); put_c(10); // -56 44
+ return 0;
+}
+fn arrays() {
+ u8 buf[20];
+ i16 sh[7];
+ u32 ws[3];
+ i8 sb[9];
+ int i = 0, sum = 0;
+ while (i < 20) { buf[i] = i * 20; i++; } // wraps past 255
+ i = 0; while (i < 20) { sum = sum + buf[i]; i++; }
+ put_n(sum); put_c(' '); // 1250
+ sh[3] = 40000; put_n(sh[3]); put_c(' '); // -25536
+ sh[3] += 30000; put_n(sh[3]); put_c(' '); // 4464
+ ws[1] = 5000000000; put_n(ws[1]); put_c(' '); // 705032704
+ sb[8] = 127; sb[8]++; put_n(sb[8]); put_c(' '); // -128
+ sb[0] = 200; put_n(sb[0] < 0); put_c(' '); // 1: -56 is negative
+ ws[0] = 4000000000; put_n(ws[0] + ws[0]); put_c(' '); // arithmetic is done in int: 8000000000
+ put_n(sizeof(buf)); put_c(' '); // 3 words
+ put_n(sizeof(sh)); put_c(10); // 2 words
+ return 0;
+}
+fn heap() {
+ u8* p = new(u8, 1000);
+ int i = 0, sum = 0;
+ while (i < 1000) { p[i] = i; i++; }
+ i = 0; while (i < 1000) { sum = sum + p[i]; i++; }
+ put_n(sum); put_c(' '); // 1000 bytes of 0..255: 3 * 32640 + 28 * ... = 127..
+ put_n(osize(p) < 256); put_c(32); // 125 words plus the header, not 1000
+ *p = 77; put_n(p[0]); put_c(' ');
+ free(d*p);
+ gbuf[9] = 255; gword[4] = 65535; gword[4]++;
+ put_n(gbuf[9]); put_c(' '); put_n(gword[4]); put_c(10);
+ return 0;
+}
+fn fields() {
+ pix* q = new(pix);
+ q.r = 255; q.g = 256; q.dx = 65535; q.tag[2] = 7; q.tag[3] = 300;
+ put_n(q.r); put_c(' '); put_n(q.g); put_c(' '); put_n(q.dx); put_c(' ');
+ put_n(q.tag[2]); put_c(' '); put_n(q.tag[3]); put_c(' '); put_n(sizeof(pix)); put_c(10);
+ free(d*q);
+ return 0;
+}
+o3 fn checksum(u8* p, int n) {
+ int i = 0, a = 1, b = 0;
+ while (i < n) { a = (a + p[i]) % 251; b = (b + a) % 251; i++; }
+ return b * 256 + a;
+}
+fn o3run() {
+ u8* p = new(u8, 5000);
+ int i = 0;
+ while (i < 5000) { p[i] = i * 7; i++; }
+ put_n(checksum(p, 5000)); put_c(10);
+ free(d*p);
+ return 0;
+}
+fn main() {
+ scalars(); arrays(); heap(); fields(); o3run();
+ return 0;
+}
diff --git a/tests/programs/sizedvec.expected b/tests/programs/sizedvec.expected
new file mode 100644
index 0000000..9c373cc
--- /dev/null
+++ b/tests/programs/sizedvec.expected
@@ -0,0 +1,2 @@
+100 11866 181 44 181 99 -25536 0 -5 4464 0 2.500000 1.100000 1400 1
+33586
diff --git a/tests/programs/sizedvec.zc b/tests/programs/sizedvec.zc
new file mode 100644
index 0000000..2055ade
--- /dev/null
+++ b/tests/programs/sizedvec.zc
@@ -0,0 +1,39 @@
+// growable arrays of the sized types and f32: []u8, []i16, []f32
+fn put_f(x) { asm { x @ #1220 } return 0; }
+fn sp() { put_c(32); return 0; }
+fn main() {
+ []u8 b = new([]u8, 0);
+ []i16 h = new([]i16, 3);
+ []f32 f = new([]f32, 0);
+ int i = 0, s = 0;
+ while (i < 100) { push(b, i * 7); i++; }
+ i = 0;
+ while (i < 100) { s = s + b[i]; i++; }
+ put_n(len(b)); sp(); put_n(s); sp(); put_n(b[99]); sp();
+ b[5] = 300; put_n(b[5]); sp();
+ put_n(pop(b)); sp(); put_n(len(b)); sp();
+ h[0] = 40000; h[2] = 0 - 5; push(h, 70000);
+ put_n(h[0]); sp(); put_n(h[1]); sp(); put_n(h[2]); sp(); put_n(h[3]); sp();
+ push(f, 0.1); push(f, 2.5); push(f, 1.0);
+ put_n(f[0] == 0.1); sp(); put_f(f[1]); sp(); put_f(f[0] + f[2]); sp();
+ resize(b, 3); resize(b, 12);
+ put_n(b[2]); put_n(b[3]); put_n(b[11]); sp();
+ put_n(cap(b) >= 12);
+ put_c(10);
+ free(d*b); free(d*h); free(d*f);
+ second();
+ return 0;
+}
+o3 int sumv([]u8 v) {
+ int s = 0;
+ for (int i = 0; i < len(v); i++) s += v[i];
+ return s;
+}
+fn second() {
+ []u8 v = new([]u8, 0);
+ int i = 0;
+ while (i < 300) { push(v, i); i++; }
+ put_n(sumv(v)); put_c(10);
+ free(d*v);
+ return 0;
+}
diff --git a/tests/programs/static.zc b/tests/programs/static.zc
new file mode 100644
index 0000000..ef54339
--- /dev/null
+++ b/tests/programs/static.zc
@@ -0,0 +1,2 @@
+struct Str { int len; static fn size(s) { return s * 2; } static fn none() { return 5; } fn get() { return this.len; } }
+int main() { Str a; a.len = 9; put_n(Str.size(7)); put_c(32); put_n(Str.none()); put_c(32); put_n(a.get()); put_c(10); return 0; }
diff --git a/tests/programs/stdbuf.zc b/tests/programs/stdbuf.zc
new file mode 100644
index 0000000..ab8f9dc
--- /dev/null
+++ b/tests/programs/stdbuf.zc
@@ -0,0 +1,18 @@
+include "src/lib/std.zc";
+fn main() {
+ let a = arena(8), p, q, b, i = 0, s = 0;
+ p = Arena.alloc(a, 4);
+ q = Arena.alloc(a, 4);
+ put_n(q - p); put_c(' ');
+ Arena.reset(a);
+ put_n(Arena.alloc(a, 4) == p); put_c(' ');
+ b = bnew(8);
+ while (i < 4) { Buf.set(b, i, 'A' + i); i++; }
+ i = 0;
+ while (i < Buf.length(b)) { s = s + Buf.get(b, i); i++; }
+ put_n(s); put_c(' ');
+ put_n(Buf.capacity(b)); put_c(10);
+ free(d*a);
+ free(d*b);
+ return 0;
+}
diff --git a/tests/programs/stdmore.zc b/tests/programs/stdmore.zc
new file mode 100644
index 0000000..fb225ec
--- /dev/null
+++ b/tests/programs/stdmore.zc
@@ -0,0 +1,32 @@
+include "src/lib/std.zc";
+fn main() {
+ let buf[32];
+ put_n(Text.len("hello")); put_c(' ');
+ put_n(Text.eq("abc", "abc")); put_c(' ');
+ put_n(Text.eq("abc", "abd")); put_c(' ');
+ put_n(Text.eq("ab", "abc")); put_c(' ');
+ put_n(Text.copy(buf, "copy me")); put_c(' ');
+ put_s(buf); put_c(' ');
+ put_n(Text.find("needle in a haystack", "hay")); put_c(' ');
+ put_n(Text.find("abc", "zz")); put_c(' ');
+ put_n(Text.find("abc", "")); put_c(10);
+ Text.copy(buf, "MiXeD 123");
+ put_n(Text.lower(buf)); put_c(' ');
+ put_s(buf); put_c(' ');
+ put_n(Text.upper(buf)); put_c(' ');
+ put_s(buf); put_c(10);
+ Text.copy(buf, " \t padded line \r\n");
+ put_n(Text.trim(buf)); put_c(' ');
+ put_c('['); put_s(buf); put_c(']'); put_c(10);
+ put_n(Num.abs(0 - 42)); put_c(' ');
+ put_n(Num.min(3, 9)); put_c(' ');
+ put_n(Num.max(3, 9)); put_c(' ');
+ put_n(Num.clamp(50, 0, 10)); put_c(' ');
+ put_n(Num.gcd(0 - 84, 36)); put_c(' ');
+ put_n(Num.pow(3, 4)); put_c(' ');
+ put_n(Num.isqrt(99)); put_c(' ');
+ put_n(Num.isqrt(100)); put_c(' ');
+ put_n(Num.sign(0 - 5)); put_c(' ');
+ put_n(Num.sign(0)); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/strings.zc b/tests/programs/strings.zc
new file mode 100644
index 0000000..431bd4e
--- /dev/null
+++ b/tests/programs/strings.zc
@@ -0,0 +1,37 @@
+fn main() {
+ let a = b"hi", b = b"!", c = a + b;
+ let mid = c[1:3];
+ let neg = s_to_n(b"-42");
+ let p = n_to_s(42);
+ let q = n_to_s(0 - 42);
+ let ten = s_to_n(b"10");
+ let big = n_to_s(1 << 63);
+ let empty = b"";
+ let tail = c[2:99];
+ let esc = b"a\nb";
+ let z = b"" + b"ab";
+ put_s(c); put_c(32);
+ put_n(len(c)); put_c(32);
+ put_n(len_s(c)); put_c(32);
+ put_n(c[0]); put_c(32);
+ put_s(mid); put_c(32);
+ put_n(c == b"hi!"); put_c(32);
+ put_n(c != b"hi"); put_c(32);
+ put_n(c < b"hj"); put_c(32);
+ put_n(neg); put_c(32);
+ put_s(p); put_c(32);
+ put_s(q); put_c(32);
+ put_n(ten + 1); put_c(32);
+ put_n(p == b"42"); put_c(32);
+ put_s(big); put_c(32);
+ put_n(len(empty)); put_c(32);
+ put_n(len(tail)); put_c(32);
+ put_n(len_s(esc)); put_c(32);
+ put_n(at_s(esc, 1)); put_c(32);
+ put_n(b"ab" > b"a"); put_c(32);
+ put_n(b"ab" >= b"ab"); put_c(32);
+ put_n(b"a" <= b"a"); put_c(32);
+ put_s("W"); put_c(32);
+ put_s(z); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/structret.zc b/tests/programs/structret.zc
new file mode 100644
index 0000000..708c83c
--- /dev/null
+++ b/tests/programs/structret.zc
@@ -0,0 +1,24 @@
+// structs returned from native functions: the stub makes the result (a new object,
+// owned by the caller) and the native call fills it in. the helper library is built
+// by test.sh (build/sbv.so).
+fn put_f(x) { asm { x @ #1220 } return 0; }
+struct Pair { int a; int b; }
+struct Vec { float x; float y; }
+struct Trio { int a; int b; int c; }
+struct Mixed { int a; float x; }
+struct Vec3 { float x; float y; float z; }
+extern struct Pair mkpair(long a, long b);
+extern struct Vec mkvec(double x, double y);
+extern struct Trio mktrio(long a, long b, long c);
+extern struct Mixed mkmixed(long a, double x);
+extern struct Vec3 mkv3(double x);
+int main() {
+ Pair* p; Vec* v; Trio* t; Mixed* m; Vec3* w;
+ cload(b"build/sbv.so");
+ p = mkpair(3, 4); put_n(p.a); put_c(32); put_n(p.b); put_c(32);
+ v = mkvec(2.5, 3.0); put_f(v.x); put_c(32); put_f(v.y); put_c(32);
+ t = mktrio(7, 8, 9); put_n(t.a + t.b + t.c); put_c(32);
+ m = mkmixed(7, 0.5); put_n(m.a); put_c(32); put_f(m.x); put_c(32);
+ w = mkv3(1.5); put_f(w.z); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/structval.zc b/tests/programs/structval.zc
new file mode 100644
index 0000000..a1bd041
--- /dev/null
+++ b/tests/programs/structval.zc
@@ -0,0 +1,26 @@
+// structs by value in native calls: a zc struct of word fields is passed to a c
+// function as its words. the helper library is built by test.sh (build/sbv.so).
+struct Pair { int a; int b; }
+struct Vec { float x; float y; }
+struct Trio { int a; int b; int c; }
+extern long pair_sum(struct Pair p);
+extern long pair_sum2(struct Pair p, struct Pair q);
+extern long vec_scaled(struct Vec v, long k);
+extern long many(long a, long b, long c, long d, long e, long f, struct Pair p);
+extern long trio_sum(long a, long b, long c, long d, long e, long f, struct Trio t);
+extern long eight(long a, long b, long c, long d, long e, long f, long g, long h, struct Pair p);
+int main() {
+ Pair p; Pair q; Vec v; Trio t;
+ cload(b"build/sbv.so");
+ p.a = 3; p.b = 4;
+ put_n(pair_sum(p)); put_c(32);
+ q.a = 10; q.b = 20;
+ put_n(pair_sum2(p, q)); put_c(32);
+ v.x = 1.5; v.y = 2.0;
+ put_n(vec_scaled(v, 2)); put_c(32);
+ put_n(many(1, 2, 3, 4, 5, 6, p)); put_c(32);
+ t.a = 7; t.b = 8; t.c = 9;
+ put_n(trio_sum(1, 2, 3, 4, 5, 6, t)); put_c(32);
+ put_n(eight(1, 2, 3, 4, 5, 6, 7, 8, p)); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/sys.zc b/tests/programs/sys.zc
new file mode 100644
index 0000000..52924fa
--- /dev/null
+++ b/tests/programs/sys.zc
@@ -0,0 +1,11 @@
+include "src/lib/std.zc";
+fn main() {
+ let s, n;
+ n = Host.writefile("/tmp/zc-sys-out.txt", "zy");
+ s = readfile("/tmp/zc-sys-out.txt");
+ put_n(n); put_c(' '); put_s(s); put_c(' ');
+ if (Host.now() > 1000000000) put_s("time\n");
+ else put_s("notime\n");
+ free(d*s);
+ return 0;
+}
diff --git a/tests/programs/tagenum.zc b/tests/programs/tagenum.zc
new file mode 100644
index 0000000..f438c5b
--- /dev/null
+++ b/tests/programs/tagenum.zc
@@ -0,0 +1,26 @@
+// tagged enums: a member can carry fields, a constructor builds it, and a
+// switch matches on the member and binds its fields
+enum shape { circle { int r; }, rect { int w; int h; }, dot };
+
+fn area(shape* s) {
+ switch (s) {
+ case circle(r): return r * r * 3;
+ case rect(w, h): return w * h;
+ case dot: return 0;
+ }
+ return -1;
+}
+
+fn main() {
+ shape* a = shape.circle(3);
+ shape* b = shape.rect(4, 5);
+ shape* c = shape.dot();
+ put_n(area(a)); put_c(32);
+ put_n(area(b)); put_c(32);
+ put_n(area(c)); put_c(32);
+ put_n(a.tag); put_c(32);
+ put_n(b.tag); put_c(32);
+ put_n(c.tag); put_c(10);
+ free(d*a); free(d*b); free(d*c);
+ return 0;
+}
diff --git a/tests/programs/tagenum_two.zc b/tests/programs/tagenum_two.zc
new file mode 100644
index 0000000..eef45dd
--- /dev/null
+++ b/tests/programs/tagenum_two.zc
@@ -0,0 +1,20 @@
+// two tagged enums may share a member name: each keeps its own fields and tag
+enum a { item { int x; }, other };
+enum b { item { int y; }, nope };
+o3 int tot(a* p, int n) {
+ int t = 0;
+ for (int i = 0; i < n; i++) {
+ switch (p) {
+ case item(x): t += x;
+ case other: t += 100;
+ }
+ }
+ return t;
+}
+int main() {
+ a* p = a.item(3);
+ b* q = b.item(8);
+ put_n(p.item_x); put_c(32); put_n(q.item_y); put_c(32); put_n(tot(p, 4)); put_c(10);
+ free(d*p); free(d*q);
+ return 0;
+}
diff --git a/tests/programs/tagenum_warn.zc b/tests/programs/tagenum_warn.zc
new file mode 100644
index 0000000..f50ab7c
--- /dev/null
+++ b/tests/programs/tagenum_warn.zc
@@ -0,0 +1,17 @@
+// a switch on a tagged enum that leaves out a member, with no default, warns
+enum shape { circle { int r; }, rect { int w; int h; }, dot };
+
+fn name(shape* s) {
+ switch (s) {
+ case circle(r): return 1;
+ case dot: return 2;
+ }
+ return 0;
+}
+
+fn main() {
+ shape* a = shape.rect(2, 2);
+ put_n(name(a)); put_c(10);
+ free(d*a);
+ return 0;
+}
diff --git a/tests/programs/typedmethods.zc b/tests/programs/typedmethods.zc
new file mode 100644
index 0000000..facd9f1
--- /dev/null
+++ b/tests/programs/typedmethods.zc
@@ -0,0 +1,16 @@
+// struct methods declare a return type and typed parameters, like functions do
+struct Str {
+ int len;
+ static int size(int s) { return s * 2; }
+ int plus(int k) { return this.len + k; }
+ static fn none() { return 5; }
+}
+int main() {
+ Str a;
+ a.len = 9;
+ put_n(Str.size(7)); put_c(32);
+ put_n(Str.none()); put_c(32);
+ put_n(a.plus(4));
+ put_c(10);
+ return 0;
+}
diff --git a/tests/programs/typeoff.zc b/tests/programs/typeoff.zc
new file mode 100644
index 0000000..d82bca8
--- /dev/null
+++ b/tests/programs/typeoff.zc
@@ -0,0 +1,2 @@
+// the typechecker is off unless asked for: this mismatch compiles and runs
+fn main() { char* s = 5; put_n(s as int + 1); put_c(10); return 0; }
diff --git a/tests/programs/typeok.zc b/tests/programs/typeok.zc
new file mode 100644
index 0000000..26ab3ad
--- /dev/null
+++ b/tests/programs/typeok.zc
@@ -0,0 +1,21 @@
+// passes the typechecker: words mix with everything, literal 0 is null,
+// arrays decay to pointers, generic calls bind their parameters
+typecheck on;
+struct pair<A, B> { A a; B b; };
+pair<A, B>*.u mk<A, B>(A x, B y) { pair<A, B>* p = new(pair<A, B>); p.a = x; p.b = y; return u*p; }
+A left<A, B>(pair<A, B>* p) { return p.a; }
+int strlen2(char* s) { int n = 0; while (s[n]) n++; return n; }
+int main() {
+ pair<int, char*>* p = mk(3, "four");
+ int a = left(p);
+ char* s = p.b;
+ [8]char buf;
+ char* q = buf;
+ int* nothing = 0;
+ let w = 5;
+ int mixed = w + 1;
+ q[0] = 'h'; q[1] = 0;
+ put_n(a + strlen2(s) + strlen2(buf) + mixed); put_c(10);
+ free(d*p);
+ return 0;
+}
diff --git a/tests/programs/types.zc b/tests/programs/types.zc
new file mode 100644
index 0000000..86488db
--- /dev/null
+++ b/tests/programs/types.zc
@@ -0,0 +1,55 @@
+struct point { int x, y; };
+struct rect { point a, b; int tag; };
+struct list<T> { int len; T* items; };
+struct node { int v; node* next; };
+type num = int;
+
+int counter = 5;
+[4]int squares;
+point origin;
+
+int area(rect* r) { return (r.b.x - r.a.x) * (r.b.y - r.a.y); }
+list<T>*.u mklist<T>(int cap) {
+ list<T>* l = new(list<T>);
+ l.len = 0;
+ l.items = new(T, cap) as T*;
+ return u*l;
+}
+T first<T>(list<T>* l) { return l.items[0]; }
+int sum(node* n) { int s = 0; while (n) { s += n.v; n = n.next; } return s; }
+
+fn main() {
+ rect r;
+ r.a.x = 1; r.a.y = 2; r.b.x = 4; r.b.y = 6;
+ put_n(area(&r)); put_c(' ');
+ put_n(sizeof(rect)); put_c(' '); put_n(sizeof(point)); put_c(' '); put_n(sizeof([3]rect)); put_c(' ');
+ let p = new(point); // let infers *point
+ p.x = 7; p->y = 8;
+ put_n(p.x * p.y); put_c(' ');
+ free(d*p);
+ list<int>* l = mklist<int>(4);
+ l.items[0] = 42; l.len = 1;
+ put_n(first(l)); put_c(' ');
+ free(d*l);
+ node* a = new(node), b = new(node);
+ a.v = 3; b.v = 4; a.next = b;
+ put_n(sum(a)); put_c(' ');
+ free(d*a); // b went with it: a.next = b made a its owner
+ [3]point pts;
+ pts[2].y = 9;
+ put_n(pts[2].y + counter); put_c(' ');
+ num k = 3;
+ put_n(k); put_c(10);
+
+ // reflection
+ tprint(typeof(r)); put_c(' '); tprint(typeof(&r)); put_c(' '); tprint(typeof(l)); put_c(' ');
+ tprint(sigof(mklist)); put_c(' '); tprint(sigof(first)); put_c(' '); tprint(sigofs("free")); put_c(10);
+ let t = typeof(r), i = 0;
+ while (i < tnfields(t)) { put_s(tfname(t, i)); put_c(':'); tprint(tftype(t, i)); put_c('@'); put_n(tfoff(t, i)); put_c(' '); i++; }
+ put_c(10);
+ point* q = new(point);
+ tprint(typeof_obj(q)); put_c(' '); put_n(typeof_obj(q) == typeof(point)); put_c(' '); put_n(tkind(typeof_obj(q)) == TY_STRUCT);
+ free(d*q);
+ put_c(10);
+ return 0;
+}
diff --git a/tests/programs/uaf.zc b/tests/programs/uaf.zc
new file mode 100644
index 0000000..4c14e91
--- /dev/null
+++ b/tests/programs/uaf.zc
@@ -0,0 +1,8 @@
+memdebug on;
+unchecked fn main()
+{
+ let p = new(2);
+ p[0] = 42;
+ free(p);
+ return p[0];
+}
diff --git a/tests/programs/uaf_in.zc b/tests/programs/uaf_in.zc
new file mode 100644
index 0000000..89ad9f9
--- /dev/null
+++ b/tests/programs/uaf_in.zc
@@ -0,0 +1,9 @@
+memdebug on;
+unchecked fn main()
+{
+ let p = new(4), r;
+ p[2] = 5;
+ r = p + 2;
+ free(p);
+ return r[0];
+}
diff --git a/tests/programs/umd.zc b/tests/programs/umd.zc
new file mode 100644
index 0000000..fb1640e
--- /dev/null
+++ b/tests/programs/umd.zc
@@ -0,0 +1,50 @@
+// lists as [len, items...], items owned by the list
+.u mklist(n) {
+ let l = new(n + 1);
+ l[0] = 0;
+ return u*l;
+}
+fn push(.m l, .d item) { l[l[0] + 1] = d*item; l[0] = l[0] + 1; return 0; }
+fn sum(.m l) { let s = 0, i = 1; while (i <= l[0]) { s += l[i][0]; i++; } return s; }
+.u box(v) { let b = new(1); b[0] = v; return u*b; }
+fn consume(.d b) { return b[0]; }
+
+fn main() {
+ let base = live_objects();
+ {
+ let l = mklist(8); // u from mklist arrives m*here
+ let i = 0;
+ while (i < 5) {
+ let b = box(i * 10); // m in the loop body
+ d*b; // give it down
+ push(l, b);
+ i++;
+ }
+ put_n(sum(l)); put_c(' ');
+ put_n(live_objects() - base); put_c(' '); // list + 5 boxes
+ }
+ put_n(live_objects() - base); put_c(' '); // freed at the block's end, children too
+
+ // u handed up one block by assignment, and as a block value
+ let outer;
+ {
+ let t = box(7);
+ u*t;
+ outer = t;
+ }
+ let v = { let w = box(8); u*w };
+ put_n(outer[0] + v[0]); put_c(' ');
+
+ // d given away to a callee
+ let g = box(99);
+ put_n(consume(d*g)); put_c(' ');
+
+ // containers: child takes its parent's tag
+ let p = new(2), c = new(1);
+ c[0] = 5;
+ p[0] = c;
+ p[1] = new(3);
+ put_n(p[0][0]); put_c(' ');
+ put_n(live_objects() - base); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/undefined_call.zc b/tests/programs/undefined_call.zc
new file mode 100644
index 0000000..e6a3b48
--- /dev/null
+++ b/tests/programs/undefined_call.zc
@@ -0,0 +1 @@
+fn main() { nope(); return 0; }
diff --git a/tests/programs/use_std.zc b/tests/programs/use_std.zc
new file mode 100644
index 0000000..c15e49b
--- /dev/null
+++ b/tests/programs/use_std.zc
@@ -0,0 +1,9 @@
+include "src/lib/std.zc";
+fn main() {
+ let out[12];
+ Text.itoa(42, out);
+ put_s(out); put_c(' ');
+ put_n(Text.atoi("-7")); put_c(' ');
+ put_n(Text.starts("hello", "he")); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/wide.zc b/tests/programs/wide.zc
new file mode 100644
index 0000000..c20004b
--- /dev/null
+++ b/tests/programs/wide.zc
@@ -0,0 +1,9 @@
+// a word is 64 bits: shifts past 31, and equality uses bit 63
+fn main() {
+ put_n(1 << 40); put_c(' ');
+ put_n((1 << 40) >> 40); put_c(' ');
+ put_n((1 << 40) == (1 << 40)); put_c(' ');
+ put_n((1 << 40) != 0); put_c(' ');
+ put_n(~0 >> 63); put_c(10);
+ return 0;
+}
diff --git a/tests/programs/zc0plain.zc b/tests/programs/zc0plain.zc
new file mode 100644
index 0000000..1092f76
--- /dev/null
+++ b/tests/programs/zc0plain.zc
@@ -0,0 +1,23 @@
+// the stage 0 compiler (src/tools/zc0.c) has no types. it reads them and throws
+// them away, so a typed program and the same program without the types come
+// out as the same image. tests/programs/zc0typed.zc is this file with the types on.
+let total;
+let table[8];
+let names;
+const LIMIT = 10;
+
+fn add(a, b) { return a + b; }
+.u grow(v, n) { return v; }
+fn keep(.d x, .m y) { return x != y; }
+fn apply(f, p, q) { return f(*p); }
+fn sq(x) { return x * x; }
+
+fn start() {
+ let i = 0, sum = 0;
+ let a[4];
+ let p = a;
+ for (let j = 0; j < LIMIT; j++) sum = add(sum, sq(j));
+ while (i < 4) { a[i] = i; i++; }
+ total = sum + a[3] + table[2];
+ return total;
+}
diff --git a/tests/programs/zc0typed.zc b/tests/programs/zc0typed.zc
new file mode 100644
index 0000000..4b7f438
--- /dev/null
+++ b/tests/programs/zc0typed.zc
@@ -0,0 +1,23 @@
+// the stage 0 compiler (src/tools/zc0.c) has no types. it reads them and throws
+// them away, so a typed program and the same program without the types come
+// out as the same image. tests/programs/zc0plain.zc is this file with the types off.
+int total;
+[8]int table;
+[]word names;
+const int LIMIT = 10;
+
+int add(int a, int b) { return a + b; }
+.u grow([]word v, int n) { return v; }
+bool keep(int.d x, .m y) { return x != y; }
+int apply(int(int) f, int* p, vec<vec<int>>*.u q) { return f(*p); }
+int sq(int x) { return x * x; }
+
+fn start() {
+ int i = 0, sum = 0;
+ [4]int a;
+ int* p = a;
+ for (int j = 0; j < LIMIT; j++) sum = add(sum, sq(j));
+ while (i < 4) { a[i] = i; i++; }
+ total = sum + a[3] + table[2];
+ return total;
+}