/* test_host_rom_pack.c -- the ROM's slot packer, word for word with asm.c. * * Plan Task 5. (OP) ( op -- ), (LIT) ( v -- ), (BRANCH) ( op target -- ), * (BRANCH>) ( op -- ref ), (RESOLVE) ( ref target -- ) and (LABEL) * ( -- addr ) lay code down exactly as v4/src/asm.c does, with text.c's * branch placement: a branch goes in a slot only if that slot reaches the * whole address space. Random sequences are applied both ways -- asm.c on * node A, the ROM on node B -- and memory is compared word for word; a * sequence that is an error on one side must be an error on the other. */ #include "rom_harness.h" static int failures = 0, checks = 0; #define CHECK(c,...) do{checks++; if(!(c)){failures++; printf("FAIL %s:%d: ",__FILE__,__LINE__); printf(__VA_ARGS__); printf("\n");}}while(0) static v4_node a, b; static v4_asm as; static unsigned long long seed = 20261015u; static unsigned rnd(unsigned n) { seed = seed * 6364136223846793005ull + 1442695040888963407ull; return (unsigned)((seed >> 33) % n); } /* text.c's place_branch, for the asm.c side */ static void place_branch(void) { if (as.slot == 0) return; if (!v4_iword_branch_legal(as.slot) || (v4_ucell)v4_iword_slot_mask(as.slot) < (v4_ucell)ROM_ADDR_TOP) (void)v4_asm_label(&as); } /* call a ROM routine with up to two arguments; 1 if it returned */ static int rom(const char *name, int argc, v4_cell x, v4_cell y) { if (argc > 0) v4_dstack_push(&b.ds, x); if (argc > 1) v4_dstack_push(&b.ds, y); if (rom_call(&b, name, 1000000)) return 1; printf(" %s did not return: P %ld, stopped %d, fault kind %u at %ld\n", name, (long)b.p, b.stopped, b.fault_kind, (long)b.fault_addr); return 0; } static int rom_failed(void) { return b.mem[ROM_FAILED] != 0; } static const unsigned branch_ops[5] = { V4_OP_JUMP, V4_OP_CALL, V4_OP_NEXT, V4_OP_IF, V4_OP_MINUS_IF }; /* one random sequence from `origin`; 1 if both sides agree throughout */ static int trial(v4_cell origin, unsigned steps) { v4_asm_ref aref[64]; v4_cell bref[64], labels[64], ha, hb, w; unsigned nref = 0, nlab = 0, i, k; v4_node_reset(&a); v4_asm_begin(&as, &a, origin); rom_boot(&b); v4_node_console_input_attach(&b, -1, -1); /* the packer reads no input: near the top of RAM, those are words */ b.mem[ROM_HERE] = origin; for (i = 0; i < steps; i++) { unsigned what = rnd(100); int ea, eb; if (what < 45) { /* an ordinary opcode */ unsigned op; do op = rnd(32); while (v4_op_is_branch(op) || op == V4_OP_FETCH_P); v4_asm_op(&as, op); if (!rom("(OP)", 1, (v4_cell)op, 0)) return 0; } else if (what < 70) { /* a literal */ v4_cell v = (v4_cell)rnd(2000) - 1000; if (rnd(4) == 0) v = (v4_cell)((v4_ucell)0 - (v4_ucell)rnd(5)) ; v4_asm_lit(&as, v); if (!rom("(LIT)", 1, v, 0)) return 0; } else if (what < 80) { /* a branch back, or anywhere */ unsigned op = branch_ops[rnd(5)]; v4_cell t = nlab && rnd(3) ? labels[rnd(nlab)] : (v4_cell)rnd((unsigned)ROM_ADDR_TOP + 1u); if (rnd(40) == 0) t = 200000; /* out of reach of any slot but 0 */ place_branch(); v4_asm_branch(&as, op, t); if (!rom("(BRANCH)", 2, (v4_cell)op, t)) return 0; } else if (what < 90 && nref < 64) { /* a branch forward */ unsigned op = branch_ops[rnd(5)]; place_branch(); aref[nref] = v4_asm_branch_fwd(&as, op); if (!rom("(BRANCH>)", 1, (v4_cell)op, 0)) return 0; bref[nref] = v4_dstack_pop(&b.ds); nref++; } else if (nlab < 64) { /* a label, and some forward branches resolved to it */ v4_cell la = v4_asm_label(&as), lb; if (!rom("(LABEL)", 0, 0, 0)) return 0; lb = v4_dstack_pop(&b.ds); if (la != lb) { printf(" label: asm %ld, ROM %ld\n", (long)la, (long)lb); return 0; } labels[nlab++] = la; for (k = 0; k < nref; ) { if (rnd(2)) { k++; continue; } v4_asm_resolve(&as, aref[k], la); if (!rom("(RESOLVE)", 2, bref[k], la)) return 0; aref[k] = aref[nref - 1]; bref[k] = bref[nref - 1]; nref--; } } ea = as.error != 0; eb = rom_failed(); if (ea != eb) { printf(" step %u: asm error %d, ROM error %d (code %ld)\n", i, ea, eb, (long)b.mem[ROM_FAILED]); return 0; } if (ea) return 1; /* both refused: the sequence ends there */ if (b.ds.depth != 0) { printf(" step %u: the ROM left %u on the stack\n", i, b.ds.depth); return 0; } } ha = v4_asm_label(&as); if (!rom("(LABEL)", 0, 0, 0)) return 0; hb = v4_dstack_pop(&b.ds); if (as.error || rom_failed()) return (as.error != 0) == rom_failed(); if (ha != hb) { printf(" end: asm here %ld, ROM %ld\n", (long)ha, (long)hb); return 0; } for (w = origin; w < ha; w++) if (a.mem[w] != b.mem[w]) { printf(" word %ld: asm %lx, ROM %lx\n", (long)w, (unsigned long)a.mem[w], (unsigned long)b.mem[w]); return 0; } return 1; } int main(void) { unsigned t, good = 0; for (t = 0; t < 400; t++) if (trial(16, 120 + rnd(200))) good++; else { CHECK(0, "trial %u disagrees", t); break; } CHECK(good == 400, "400 random sequences agree word for word: %u", good); /* memory runs out: a sequence that starts near the top of RAM */ CHECK(trial((v4_cell)V4_NODE_WORDS - 20, 200), "memory full is an error on both sides"); /* a forward branch that cannot reach (Review Focus 2) */ { v4_asm_ref ar; v4_cell br; v4_node_reset(&a); v4_asm_begin(&as, &a, 16); rom_boot(&b); b.mem[ROM_HERE] = 16; v4_asm_op(&as, V4_OP_DUP); v4_asm_op(&as, V4_OP_DUP); rom("(OP)", 1, V4_OP_DUP, 0); rom("(OP)", 1, V4_OP_DUP, 0); place_branch(); ar = v4_asm_branch_fwd(&as, V4_OP_JUMP); rom("(BRANCH>)", 1, V4_OP_JUMP, 0); br = v4_dstack_pop(&b.ds); v4_asm_resolve(&as, ar, 400000); rom("(RESOLVE)", 2, br, 400000); CHECK(as.error && b.mem[ROM_FAILED] == ROM_ERR_FIT, "out of reach: refused by both, ROM code %ld", (long)b.mem[ROM_FAILED]); } /* a ref to a word with no branch in it is refused, not looped on */ rom_boot(&b); b.mem[ROM_HERE] = 16; rom("(OP)", 1, V4_OP_DUP, 0); rom("(LABEL)", 0, 0, 0); (void)v4_dstack_pop(&b.ds); CHECK(rom("(RESOLVE)", 2, 16, 40) && b.mem[ROM_FAILED] == ROM_ERR_FIT, "no branch at the ref: does not fit, code %ld", (long)b.mem[ROM_FAILED]); /* opcodes the packer does not take as ordinary ones */ rom_boot(&b); b.mem[ROM_HERE] = 16; rom("(OP)", 1, V4_OP_FETCH_P, 0); CHECK(b.mem[ROM_FAILED] == ROM_ERR_OPCODE, "@p is written as a literal, not an opcode"); rom_boot(&b); b.mem[ROM_HERE] = 16; rom("(OP)", 1, V4_OP_CALL, 0); CHECK(b.mem[ROM_FAILED] == ROM_ERR_OPCODE, "a branch is not an ordinary opcode"); rom_boot(&b); b.mem[ROM_HERE] = 16; rom("(OP)", 1, 32, 0); CHECK(b.mem[ROM_FAILED] == ROM_ERR_OPCODE, "32 is not an opcode"); printf("test_host_rom_pack: %d checks, %d failures\n", checks, failures); return failures != 0; }