Skip to content

Fix the build: measure.c, the Makefile targets, and an LTO type mismatch - #18

Open
Roxxik wants to merge 2 commits into
mist64:masterfrom
Roxxik:build-fixes
Open

Fix the build: measure.c, the Makefile targets, and an LTO type mismatch#18
Roxxik wants to merge 2 commits into
mist64:masterfrom
Roxxik:build-fixes

Conversation

@Roxxik

@Roxxik Roxxik commented Jul 27, 2026

Copy link
Copy Markdown

Four unrelated build defects, all pre-existing, found while trying to benchmark
the simulator. Reproduced on GCC 16.1.1 / GNU Make 4.4. Self-contained, it
touches no simulator code beyond one declaration in cbmbasic/.

measure.c does not compile under the project's own flags

$ make -f Makefile.measure
measure.c: In function 'main':
measure.c:371:60: error: 'end_x' may be used uninitialized [-Werror=maybe-uninitialized]
measure.c:382:60: error: 'end_y' may be used uninitialized [-Werror=maybe-uninitialized]

The = 0 in

uint8_t end_a, end_x, end_y, end_s = 0, end_p = 0;

binds to end_s and end_p only. It is not a live bug, every read is preceded
by a write on the j == 0 pass, and the guarding condition depends only on k,
which is fixed by the enclosing loop. GCC cannot prove that, and which of the
three variables it names shifts with the optimisation level, so it can look
intermittent. This initialises all five rather than suppressing the warning.
Different opt levels shift this. On -O0 there is no warning.

Make targets named after directories that already exist

Makefile builds a target called cbmbasic, and cbmbasic/ is a directory.
Make stats the directory, whose mtime is bumped whenever an object file is
written into it, and declares the target up to date:

$ rm -f cbmbasic/cbmbasic
$ make
make: Nothing to be done for 'all'.

make clean && make always works and touching a source file does trigger a
relink, so this stays invisible day to day. It shows up after a failed link:
every subsequent make reports "Nothing to be done for 'all'", which reads as a
successful no-op, and a stale binary can sit there while make insists the tree is
current.

Makefile.apple1basic has the same collision, and there it is fatal, because it
links with -o apple1basic:

/usr/bin/ld: cannot open output file apple1basic: Is a directory

This names each target after the file it actually produces and marks the real
phony targets as such. .PHONY: cbmbasic would also stop the misfire, but by
forcing a relink on every invocation.

apple1basic is missing netlist_sim.o

Fixing the output path above uncovers a second defect the link error had been
masking: netlist_sim.o is absent from its OBJS, so the link fails with
undefined references to recalcNodeList, readNodes, isNodeHigh and
destroyNodesAndTransistors. That target cannot have linked since
netlist_sim.c was split out.

N/Z/C are declared unsigned char and defined int

cbmbasic/glue.h declares extern unsigned char V, B, D, I, C, N, Z; while
cbmbasic/runtime_init.c defines int N, Z, C;. runtime_init.c does not
include glue.h, so no single translation unit sees both and an ordinary build
cannot notice. Under -flto GCC compares declarations across translation units,
reports -Wlto-type-mismatch for all three, and -Werror turns that into a link
failure.

Every value stored in them is 0 or 1 - N = P >> 7, Z = (P >> 1) & 1,
C = P & 1, the SETZ/SETSZ/SETNC macros, and the plain C = 0 / C = 1
assignments in runtime.c - so this narrows the definition to match the header
rather than widening the header. That also matches how A, X, Y and S are
already declared and defined.

Verification

All three targets build clean from scratch, measure characterises all 256
opcodes, and deleting a binary triggers a relink. cbmbasic --benchmark output
is byte-for-byte identical to a pristine build of master, with and without
-flto: 33155 half-cycles, same final CPU state.

Notes

  • lto does not improve runtime.
  • -march=native is slower on my machine
  • using PGO is a slight improvement, but only when done on the same workload. Profiling on cbmbasic and running on measure is worse. Same the other way around.

I used an LLM to help me out in this work, but I manually reviewed all changes made.

Roxxik added 2 commits July 26, 2026 20:38
measure.c did not compile under the shipped -Werror flags: the `= 0` in
`uint8_t end_a, end_x, end_y, end_s = 0, end_p = 0;` binds only to end_s
and end_p, so GCC reported -Wmaybe-uninitialized for the other three.
The reads are in fact always preceded by writes, but GCC cannot prove it
and the set of reported variables shifts with -O level. Initialise all
five.

Makefile and Makefile.apple1basic named their link targets after
existing directories. Make stats the directory, whose mtime is bumped
whenever an object file is written into it, and declares the target up
to date. For cbmbasic this silently skips relinking -- including after a
failed link, which then reports "Nothing to be done for 'all'" on every
retry and can leave a stale binary in place. For apple1basic it was
fatal: `-o apple1basic` cannot write to a directory. Name each target
after the file it produces and mark the phony targets as such.

Fixing the apple1basic output path exposed a second defect the link
error had been masking: netlist_sim.o was missing from its OBJS, so the
link failed with undefined references to recalcNodeList, readNodes,
isNodeHigh and destroyNodesAndTransistors. Add it.

All three targets now build clean from scratch, measure characterises
all 256 opcodes, and deleting a binary triggers a relink.
glue.h declares `extern unsigned char V, B, D, I, C, N, Z;` while
runtime_init.c defines `int N, Z, C;`. runtime_init.c does not include
glue.h, so no single translation unit sees both and the conflict is
invisible to a normal build. Under -flto GCC compares declarations
across translation units and reports -Wlto-type-mismatch for all three,
which -Werror promotes to a link failure.

Every value stored in these flags is 0 or 1 -- `N = P >> 7`,
`Z = (P >> 1) & 1`, `C = P & 1` in runtime_init.c, the SETZ/SETSZ/SETNC
macros in glue.h, and the plain `C = 0` / `C = 1` assignments throughout
runtime.c. So unsigned char is the correct width, and it matches how A,
X, Y and S are already declared and defined. Narrow the definition to
match the header rather than widening the header.

Verified behaviour-preserving: cbmbasic --benchmark output is
byte-for-byte identical to a pristine build of master, both with and
without -flto (33155 half-cycles, same final CPU state).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant