/* test_host_node.c -- the host node's size, and the text assembler on it. * * Tests named test_host_*.c are built with V4_NODE_WORDS = 16384 (Makefile): * the host node, which holds the compiler, the dictionary and the text being * compiled, and so cannot be a 1024-word mesh node. * * At 1024 words every slot a branch may sit in reaches the whole node. At * 16384 slot 3 does not -- its address field is 12 bits -- so this is where * the text assembler's placement rule (text.h) is exercised: a branch is put * in a slot only if that slot reaches every word of the node. */ #include "v4/text.h" #include "v4/testcode.h" #include #include 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) #define CANARY ((v4_cell)0x0C0FFEE5) #define ORIGIN 16 static v4_node n; static v4_exec_state es; static v4_heat h; static v4_text tx; static char src[8 * V4_NODE_WORDS + 1024]; static unsigned slot_op(v4_cell addr, unsigned slot) { return (unsigned)(((v4_ucell)n.mem[addr] >> V4_SLOT_LOW_BIT(slot)) & 0x1Fu); } static int assemble(const char *text) { v4_node_reset(&n); v4_text_begin(&tx, &n, ORIGIN); return v4_text_assemble(&tx, text) && v4_text_finish(&tx); } static v4_cell result(const char *name, v4_cell arg) { v4_cell r, w = v4_text_word(&tx, name); v4_dstack_reset(&n.ds); v4_rstack_reset(&n.rs); v4_exec_reset(&es); v4_heat_reset(&h); v4_dstack_push(&n.ds, CANARY); v4_dstack_push(&n.ds, arg); if (w < 0 || v4_test_call(&n, &es, &h, w, 100000) <= 0) return (v4_cell)0x0BADBAD; r = v4_dstack_pop(&n.ds); return v4_dstack_pop(&n.ds) == CANARY ? r : (v4_cell)0x0BADBAD; } int main(void) { unsigned k, i; printf("v4 host node tests: V4_CELL_BITS=%d, V4_NODE_WORDS=%u\n", V4_CELL_BITS, (unsigned)V4_NODE_WORDS); CHECK(V4_NODE_WORDS == 16384u, "the host node is 16384 words"); /* the node itself at this size */ v4_node_reset(&n); v4_node_store(&n, 0, 11); v4_node_store(&n, (v4_cell)(V4_NODE_WORDS - 1u), 22); CHECK(v4_node_load(&n, 0) == 11 && v4_node_load(&n, (v4_cell)(V4_NODE_WORDS - 1u)) == 22, "the first and last words"); CHECK(v4_node_guards_intact(&n), "guards intact around them"); CHECK(V4_TEST_HALT == (v4_cell)(V4_NODE_WORDS - 1u), "the test halt address is the last word"); /* which slots reach the whole node */ CHECK((v4_ucell)v4_iword_slot_mask(2) >= V4_NODE_WORDS - 1u, "a branch in slot 2 reaches the whole node"); CHECK((v4_ucell)v4_iword_slot_mask(3) < V4_NODE_WORDS - 1u, "a branch in slot 3 does not"); /* so a branch that would land in slot 3 starts a new word instead, and * one in slots 0 .. 2 stays where it is */ for (k = 0; k <= 3; k++) { char pad[32] = ""; for (i = 0; i < k; i++) strcat(pad, "dup "); snprintf(src, sizeof src, ": T ( n -- x ) %sif Z ; Z: ;", pad); CHECK(assemble(src), "a branch after %u opcodes assembles: %s", k, v4_text_error(&tx)); if (k < 3) { CHECK(slot_op(ORIGIN, k) == V4_OP_IF, "after %u opcodes the branch is in slot %u of the same word", k, k); } else { CHECK(slot_op(ORIGIN, 3) == V4_OP_NOP && slot_op(ORIGIN + 1, 0) == V4_OP_IF, "after 3 opcodes the branch starts the next word"); } } /* branches and calls from every slot position to the far end of the * node, forward and backward, land where they should */ for (k = 0; k <= 3; k++) { char pad[32] = ""; size_t at = 0; for (i = 0; i < k; i++) strcat(pad, "nop "); at += (size_t)snprintf(src + at, sizeof src - at, ": NEAR ( n -- 5 ) drop 5 ; "); at += (size_t)snprintf(src + at, sizeof src - at, ": FWD ( n -- x ) %sif FAR drop 7 ; ", pad); at += (size_t)snprintf(src + at, sizeof src - at, ": CALLS ( n -- x ) %sFAR 1 + ; ", pad); at += (size_t)snprintf(src + at, sizeof src - at, ": FILL "); for (i = 0; i < V4_NODE_WORDS - 400u; i++) at += (size_t)snprintf(src + at, sizeof src - at, "; "); at += (size_t)snprintf(src + at, sizeof src - at, ": FAR ( n -- 99 ) drop 99 ; "); at += (size_t)snprintf(src + at, sizeof src - at, ": BACK ( n -- x ) %sif NEAR drop 8 ; ", pad); at += (size_t)snprintf(src + at, sizeof src - at, ": BACKCALL ( n -- x ) %sNEAR 1 + ; ", pad); at += (size_t)snprintf(src + at, sizeof src - at, ": LOOP ( n -- 0 ) L: if DONE -1 + %sjump L DONE: ; ", pad); CHECK(assemble(src), "the far text assembles with %u opcodes before each branch: %s", k, v4_text_error(&tx)); CHECK(v4_text_word(&tx, "FAR") > (v4_cell)(V4_NODE_WORDS - 400u), "FAR is at %ld", (long)v4_text_word(&tx, "FAR")); CHECK(result("FWD", 0) == 99 && result("FWD", 3) == 7, "a forward branch across the node [%u]", k); CHECK(result("CALLS", 3) == 100, "a forward call across the node [%u]", k); CHECK(result("BACK", 0) == 5 && result("BACK", 3) == 8, "a backward branch across the node [%u]", k); CHECK(result("BACKCALL", 3) == 6, "a backward call across the node [%u]", k); CHECK(result("LOOP", 9) == 0, "a short backward jump near the top of the node [%u]", k); } /* the node fills up at 16384 words, not before */ { size_t at = 0; at += (size_t)snprintf(src + at, sizeof src - at, ": T "); for (i = 0; i < V4_NODE_WORDS - 100u; i++) at += (size_t)snprintf(src + at, sizeof src - at, "; "); CHECK(assemble(src), "16284 words of code fit: %s", v4_text_error(&tx)); for (i = 0; i < 200u; i++) at += (size_t)snprintf(src + at, sizeof src - at, "; "); CHECK(!assemble(src) && strstr(v4_text_error(&tx), "does not fit") != NULL, "16484 do not"); CHECK(v4_node_guards_intact(&n), "and nothing was written outside the node"); } printf(" %d checks, %d failures\n", checks, failures); return failures ? 1 : 0; }