# 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. # -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) $(HERE)/Makefile @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) $(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) \ $(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)