Files
LithosAnanake/v4/Makefile
T
rajamesandClaude Opus 5.5 ebffa6082d feat(v4.0.0): the host node's stacks are 32 deep (D-17)
With the stacks counted and guarded (D-16) their size is a parameter of
the node.  The host node, which runs the interpreter and the compiler
under the user's programme, gets 32 values and 32 return entries; a mesh
node keeps the F18's 10 and 9.  Nothing else about the mechanism or any
word's definition changes.

- stack.h: V4_DATA_RING and V4_RET_RING are build parameters; the
  Makefile sets them for the host-node tests.
- tests/test_host_quit.c no longer assumes a size: it fills the stacks to
  whatever they are, and takes every exit with the return stack full.
- Measured on the host node now: 28 values on a line, 29 waiting between
  lines, words 31 deep from the prompt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 18:29:10 -04:00

130 lines
5.8 KiB
Makefile

# 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
# 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.
#
# The host node's stacks are deeper too (DECOMPOSITION.md D-17): 32 values and
# 32 return entries, where a mesh node has the F18's 10 and 9. The stack is
# its top registers and a ring; the ring sizes are what is set here.
HOST_WORDS := 16384
HOST_DATA_RING := 30
HOST_RET_RING := 31
node_size = $(if $(findstring /test_host_,$(1)),-DV4_NODE_WORDS=$(HOST_WORDS) -DV4_DATA_RING=$(HOST_DATA_RING) -DV4_RET_RING=$(HOST_RET_RING))
# The capsule sources: definitions as text (include/v4/text.h), which tests
# assemble onto a node. The directory is passed to the tests so that they
# find the files whatever directory make was run from.
CAPSULES := $(wildcard $(HERE)/capsule/*.v4)
capsule_dir := -DV4_CAPSULE_DIR='"$(HERE)/capsule"'
.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.
# -fno-sanitize-recover=all makes every UBSan report fatal: without it UBSan
# prints the report and carries on, and the run still says "passed".
#
# Both binaries depend on this Makefile, so a flag change rebuilds them.
define TEST_RULE
$(BINDIR)/$(1)-$(notdir $(2)): $(2) $$(SRCS) $$(wildcard $(HERE)/include/v4/*.h) $$(wildcard $(HERE)/tests/*.h) $(HERE)/Makefile
@mkdir -p $(BINDIR)
$$(CC) $$(CFLAGS) -I$(HERE)/include -DV4_CELL_BITS=$(1) $(call node_size,$(2)) $(capsule_dir) \
$(2) $$(SRCS) -o $$@
$(BINDIR)/san-$(1)-$(notdir $(2)): $(2) $$(SRCS) $$(wildcard $(HERE)/include/v4/*.h) $$(wildcard $(HERE)/tests/*.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) $(call node_size,$(2)) $(capsule_dir) \
$(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)) $$(CAPSULES)
@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)