diff options
Diffstat (limited to 'tests/programs')
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; +} |
