feat(v4.0.0): hosted golden model, single node
First code for StarForth v4 (JUSTIFICATION.md section 10, step 1): one node of the 32-instruction core as a C99 model, with cell width as a build parameter. - Node: P, A, B, F18 circular stacks (10 and 9 deep, D-2), word-addressed memory (D-1), 5% guard bands on every bounded list. - Instruction word: six 5-bit slots in 32 bits at every cell width. - Executor: all 32 opcodes of DECOMPOSITION.md 1.3. Cell arithmetic wraps explicitly; no signed overflow or implementation-defined shift. - Heat: per-opcode and per-call-target counters and the anti-clock, driven by instruction retirement (1.4, D-6 interim). - Slot packer and runner for tests, and a reference unsigned multiply in plain C99 with no 128-bit type. Tests run at 32- and 64-bit cells, and under ASan and UBSan. They cover every opcode and execute the first section 4 definitions (NIP SWAP OR NEGATE ROT 0< 0= 2DUP - U<) against the C operation each stands for. UM* as written in section 4 is exact only while u1 <= 2^(n-2). Two known failing cases are pinned in test_foundation.c until it is rewritten. DECOMPOSITION.md: record D-9, the instruction word is 32 bits at every cell width (ruled 2026-10-02). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
1f98034311
commit
067f317c47
+105
@@ -0,0 +1,105 @@
|
||||
# v4/Makefile -- host golden model build.
|
||||
#
|
||||
# JUSTIFICATION.md section 5: the host node runs the same mesh-defined
|
||||
# instruction set as the hardware, so the C99 model is the reference the
|
||||
# hardware is compared against -- not a host-specific reimplementation.
|
||||
# There is consequently nothing v4-specific in the architecture here; the
|
||||
# build is plain hosted C99 and the ISA is defined in DECOMPOSITION.md.
|
||||
#
|
||||
# The two builds that matter are the two cell widths. v4/Makefile builds and
|
||||
# runs the tests at both, because a width-dependent bug that only appears at
|
||||
# 32 bits is exactly the failure mode the F18's 32-bit cells invite and
|
||||
# exactly the one a 64-bit-only test run would miss.
|
||||
#
|
||||
# C99, warnings fatal. A warning here is a defect in a model whose only job
|
||||
# is to be trusted.
|
||||
|
||||
CC ?= cc
|
||||
CSTD := -std=c99
|
||||
WARN := -Wall -Wextra -Wpedantic -Werror
|
||||
OPT ?= -O2 -g
|
||||
CFLAGS ?= $(CSTD) $(WARN) $(OPT)
|
||||
|
||||
# Anchor everything to this Makefile's directory so the build behaves the same
|
||||
# whether it is invoked as `make -C v4 test` or `make -f v4/Makefile test`
|
||||
# from the repository root. Without this the wildcards below silently expand
|
||||
# to nothing from the root and the build "succeeds" having compiled no tests.
|
||||
HERE := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
|
||||
|
||||
SRCS := $(wildcard $(HERE)/src/*.c)
|
||||
TESTS := $(wildcard $(HERE)/tests/*.c)
|
||||
|
||||
# Cell widths to build and verify. 32 is the F18/Zynq case, 64 the aarch64
|
||||
# host case; see DECOMPOSITION.md D-5.
|
||||
WIDTHS := 32 64
|
||||
|
||||
BINDIR := $(HERE)/build
|
||||
|
||||
.PHONY: all test sanitize clean $(addprefix test-,$(WIDTHS))
|
||||
|
||||
all: test
|
||||
|
||||
# A build that finds no tests must fail loudly, not report a vacuous pass. A
|
||||
# loop over an empty list exits 0 having run nothing, which in CI is
|
||||
# indistinguishable from a green run -- the worst possible failure mode for a
|
||||
# test harness, and one this Makefile has already produced once.
|
||||
ifeq ($(strip $(TESTS)),)
|
||||
$(error No test sources under $(HERE)/tests -- refusing to report success)
|
||||
endif
|
||||
|
||||
# One test binary per (width, test source) pair. The cell width is a
|
||||
# preprocessor parameter rather than a runtime one, so the two widths are
|
||||
# separate compilations -- which is the point: there is no single code path
|
||||
# that could paper over a width assumption.
|
||||
#
|
||||
# `make test` builds optimised; `make sanitize` builds the same sources under
|
||||
# AddressSanitizer and UndefinedBehaviorSanitizer at both widths and runs them.
|
||||
# The optimized run catches logic errors, this one catches the out-of-bounds
|
||||
# ring index and the width-dependent shift that a logic-only test cannot see.
|
||||
define TEST_RULE
|
||||
$(BINDIR)/$(1)-$(notdir $(2)): $(2) $$(SRCS) $$(wildcard $(HERE)/include/v4/*.h)
|
||||
@mkdir -p $(BINDIR)
|
||||
$$(CC) $$(CFLAGS) -I$(HERE)/include -DV4_CELL_BITS=$(1) \
|
||||
$(2) $$(SRCS) -o $$@
|
||||
|
||||
$(BINDIR)/san-$(1)-$(notdir $(2)): $(2) $$(SRCS) $$(wildcard $(HERE)/include/v4/*.h)
|
||||
@mkdir -p $(BINDIR)
|
||||
$$(CC) $$(CSTD) $$(WARN) -O1 -g -fno-omit-frame-pointer \
|
||||
-fsanitize=address,undefined -I$(HERE)/include -DV4_CELL_BITS=$(1) \
|
||||
$(2) $$(SRCS) -o $$@
|
||||
endef
|
||||
|
||||
# One run target per (width, test) and one prerequisite line per width, so
|
||||
# adding a test file cannot redefine a recipe and the run cannot skip the
|
||||
# build. A shell loop over `$^` is avoided deliberately: `$^` is make syntax
|
||||
# that does not survive the define/eval expansion, and a silently-empty loop
|
||||
# list is exactly the vacuous-pass failure this file already made once.
|
||||
define RUN_RULE
|
||||
run-$(1)-$(notdir $(2)): $(BINDIR)/$(1)-$(notdir $(2))
|
||||
@echo " [V4_CELL_BITS=$(1) $$@]"
|
||||
@$(BINDIR)/$(1)-$(notdir $(2))
|
||||
|
||||
test-$(1): run-$(1)-$(notdir $(2))
|
||||
endef
|
||||
|
||||
# The same sources and the same assertions, under ASan+UBSan.
|
||||
define SAN_RULE
|
||||
san-$(1)-$(notdir $(2)): $(BINDIR)/san-$(1)-$(notdir $(2))
|
||||
@echo " [ASan+UBSan V4_CELL_BITS=$(1) $$@]"
|
||||
@$(BINDIR)/san-$(1)-$(notdir $(2))
|
||||
endef
|
||||
|
||||
$(foreach w,$(WIDTHS),$(foreach t,$(TESTS),$(eval $(call TEST_RULE,$(w),$(t)))))
|
||||
$(foreach w,$(WIDTHS),$(foreach t,$(TESTS),$(eval $(call RUN_RULE,$(w),$(t)))))
|
||||
$(foreach w,$(WIDTHS),$(foreach t,$(TESTS),$(eval $(call SAN_RULE,$(w),$(t)))))
|
||||
|
||||
sanitize: $(foreach w,$(WIDTHS),$(foreach t,$(TESTS),san-$(w)-$(notdir $(t))))
|
||||
@echo "all v4 tests passed under ASan+UBSan"
|
||||
|
||||
.PHONY: $(foreach w,$(WIDTHS),$(foreach t,$(TESTS),run-$(w)-$(notdir $(t)) san-$(w)-$(notdir $(t))))
|
||||
|
||||
test: $(addprefix test-,$(WIDTHS))
|
||||
@echo "all v4 tests passed"
|
||||
|
||||
clean:
|
||||
rm -rf $(BINDIR)
|
||||
Reference in New Issue
Block a user