feat(v4.0.0): a 16384-word host node for the compiler tests
Node memory is a build parameter. Tests named test_host_*.c are now built with V4_NODE_WORDS=16384, the host node that will hold the compiler, the dictionary and the text being compiled; every other test keeps the 1024-word mesh node. The first such test checks the node at that size and the text assembler's branch placement, which only matters there: a branch slot that cannot reach the whole node is not used. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
7b710aa322
commit
31fcdcd0a3
+10
-2
@@ -35,6 +35,14 @@ WIDTHS := 32 64
|
||||
|
||||
BINDIR := $(HERE)/build
|
||||
|
||||
# Node memory is a build parameter (V4_NODE_WORDS, node.h; 1024 words unless
|
||||
# set). A mesh node is small, but the host node holds the compiler, the
|
||||
# dictionary and the text being compiled (JUSTIFICATION.md section 9), which
|
||||
# do not fit 1024 words. Tests named test_host_*.c are therefore built with a
|
||||
# host node of HOST_WORDS words; every other test keeps the mesh-node size.
|
||||
HOST_WORDS := 16384
|
||||
node_size = $(if $(findstring /test_host_,$(1)),-DV4_NODE_WORDS=$(HOST_WORDS))
|
||||
|
||||
.PHONY: all test sanitize clean $(addprefix test-,$(WIDTHS))
|
||||
|
||||
all: test
|
||||
@@ -63,13 +71,13 @@ endif
|
||||
define TEST_RULE
|
||||
$(BINDIR)/$(1)-$(notdir $(2)): $(2) $$(SRCS) $$(wildcard $(HERE)/include/v4/*.h) $(HERE)/Makefile
|
||||
@mkdir -p $(BINDIR)
|
||||
$$(CC) $$(CFLAGS) -I$(HERE)/include -DV4_CELL_BITS=$(1) \
|
||||
$$(CC) $$(CFLAGS) -I$(HERE)/include -DV4_CELL_BITS=$(1) $(call node_size,$(2)) \
|
||||
$(2) $$(SRCS) -o $$@
|
||||
|
||||
$(BINDIR)/san-$(1)-$(notdir $(2)): $(2) $$(SRCS) $$(wildcard $(HERE)/include/v4/*.h) $(HERE)/Makefile
|
||||
@mkdir -p $(BINDIR)
|
||||
$$(CC) $$(CSTD) $$(WARN) -O1 -g -fno-omit-frame-pointer \
|
||||
-fsanitize=address,undefined -fno-sanitize-recover=all -I$(HERE)/include -DV4_CELL_BITS=$(1) \
|
||||
-fsanitize=address,undefined -fno-sanitize-recover=all -I$(HERE)/include -DV4_CELL_BITS=$(1) $(call node_size,$(2)) \
|
||||
$(2) $$(SRCS) -o $$@
|
||||
endef
|
||||
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/* 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 <stdio.h>
|
||||
#include <string.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)
|
||||
|
||||
#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;
|
||||
}
|
||||
Reference in New Issue
Block a user