Rewrite i386 backend as hand-written assembly

Replaces the C fallback (borrowed from PowerPC) with dedicated i386
assembly for all nine kernels, matching the amd64/arm64 convention.
Fixes two real bugs found and verified on physical Pentium 4 hardware:
scalar FP silently running on x87 instead of SSE2, and fm_int_math
calling __udivdi3 on every iteration of the highest-weighted test
because the C compiler didn't know its 0xdeadbeef divisor fits in 32
bits. Real-hardware SINGLECORE/MULTICORE scores go from 425/448 to
539/581.
This commit is contained in:
2026-07-18 04:35:03 -05:00
parent 136b417cf8
commit 357e6f0d6d
6 changed files with 1370 additions and 38 deletions
+16 -6
View File
@@ -3,6 +3,7 @@
# The assembly kernels are architecture-specific:
# src/fossmark.S AArch64 (ARM64)
# src/fossmark_x86_64.S x86-64 (AMD64)
# src/fossmark_i386.S x86 32-bit (i386, Pentium 4 baseline)
# src/fossmark_ppc32.c PowerPC 32-bit, including big-endian systems
# and the portable PPC64 kernel implementations
# The C driver (src/main.c) is portable across architectures and OSes. A
@@ -48,7 +49,7 @@ DIST := dist
DRIVER := src/main.c
ASM_ARM64 := src/fossmark.S
ASM_AMD64 := src/fossmark_x86_64.S
SRC_I386 := src/fossmark_ppc32.c
ASM_I386 := src/fossmark_i386.S
SRC_PPC32 := src/fossmark_ppc32.c
ASM_PPC32 := src/fossmark_ppc32_ext.S
SRC_PPC64 := src/fossmark_ppc32.c
@@ -63,7 +64,7 @@ else ifneq (,$(filter x86_64 amd64,$(HOST_ARCH)))
HOST_KERNEL := $(ASM_AMD64)
else ifneq (,$(filter i386 i486 i586 i686 x86,$(HOST_ARCH)))
HOST_ARCHNAME := i386
HOST_KERNEL := $(SRC_I386)
HOST_KERNEL := $(ASM_I386)
else ifneq (,$(filter ppc powerpc ppc32 powerpc32,$(HOST_ARCH)))
HOST_ARCHNAME := ppc32be
HOST_KERNEL := $(SRC_PPC32) $(ASM_PPC32)
@@ -75,7 +76,16 @@ else
$(error unsupported host architecture '$(HOST_ARCH)')
endif
ifeq ($(HOST_ARCHNAME),i386)
CFLAGS += -march=pentium4 -msse2
# The kernels are hand-written assembly (fossmark_i386.S) using SSE2
# directly, so -msse2/-mfpmath=sse have nothing left to gate - only
# main.c (the portable driver) is still compiled from C here.
#
# -fno-pie: i386 PIC costs a whole general-purpose register (already the
# scarcest resource in 32-bit mode) for the life of any function that
# touches global data or calls out - a tax amd64/arm64 don't pay the same
# way. Paired with -no-pie at link time below.
CFLAGS += -march=pentium4 -fno-pie
LDFLAGS += -no-pie
endif
ifeq ($(HOST_ARCHNAME),ppc64be)
CFLAGS += -mcpu=970 -maltivec
@@ -154,8 +164,8 @@ $(DIST)/fossmark-linux-amd64: $(DRIVER) $(ASM_AMD64) | $(DIST)
$(CC_AMD64) $(CFLAGS) $(TLS_CFLAGS) $(PTHREAD) $(LDFLAGS) -o $@ $(DRIVER) $(ASM_AMD64) $(LDLIBS)
@echo "built $@"
$(DIST)/fossmark-linux-i386: $(DRIVER) $(SRC_I386) | $(DIST)
$(CC_I386) -m32 -march=pentium4 -msse2 $(CFLAGS) $(TLS_CFLAGS) $(PTHREAD) $(LDFLAGS) -o $@ $(DRIVER) $(SRC_I386) $(LDLIBS)
$(DIST)/fossmark-linux-i386: $(DRIVER) $(ASM_I386) | $(DIST)
$(CC_I386) -m32 -march=pentium4 -fno-pie -no-pie $(CFLAGS) $(TLS_CFLAGS) $(PTHREAD) $(LDFLAGS) -o $@ $(DRIVER) $(ASM_I386) $(LDLIBS)
@echo "built $@"
$(DIST)/fossmark-linux-ppc32be: $(DRIVER) $(SRC_PPC32) $(ASM_PPC32) | $(DIST)
@@ -213,7 +223,7 @@ bench: $(NATIVE_BIN)
# Build and run the kernel correctness tests for the host arch.
test: | $(DIST)
$(CC) $(CFLAGS) $(PTHREAD) -o $(DIST)/test_kernels src/test_kernels.c $(HOST_KERNEL) -lm
$(CC) $(CFLAGS) $(PTHREAD) $(LDFLAGS) -o $(DIST)/test_kernels src/test_kernels.c $(HOST_KERNEL) -lm
./$(DIST)/test_kernels
clean: