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:
@@ -170,4 +170,5 @@ jobs:
|
|||||||
release/SHA256SUMS \
|
release/SHA256SUMS \
|
||||||
--repo "$GITHUB_REPOSITORY" \
|
--repo "$GITHUB_REPOSITORY" \
|
||||||
--title "Release $RELEASE_TAG" \
|
--title "Release $RELEASE_TAG" \
|
||||||
|
--notes "⚠️ **i386 (Pentium 4) support is new in this release and still being validated on real hardware.** i386 benchmark scores should not be considered accurate yet." \
|
||||||
--generate-notes
|
--generate-notes
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
# The assembly kernels are architecture-specific:
|
# The assembly kernels are architecture-specific:
|
||||||
# src/fossmark.S AArch64 (ARM64)
|
# src/fossmark.S AArch64 (ARM64)
|
||||||
# src/fossmark_x86_64.S x86-64 (AMD64)
|
# 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
|
# src/fossmark_ppc32.c PowerPC 32-bit, including big-endian systems
|
||||||
# and the portable PPC64 kernel implementations
|
# and the portable PPC64 kernel implementations
|
||||||
# The C driver (src/main.c) is portable across architectures and OSes. A
|
# The C driver (src/main.c) is portable across architectures and OSes. A
|
||||||
@@ -48,7 +49,7 @@ DIST := dist
|
|||||||
DRIVER := src/main.c
|
DRIVER := src/main.c
|
||||||
ASM_ARM64 := src/fossmark.S
|
ASM_ARM64 := src/fossmark.S
|
||||||
ASM_AMD64 := src/fossmark_x86_64.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
|
SRC_PPC32 := src/fossmark_ppc32.c
|
||||||
ASM_PPC32 := src/fossmark_ppc32_ext.S
|
ASM_PPC32 := src/fossmark_ppc32_ext.S
|
||||||
SRC_PPC64 := src/fossmark_ppc32.c
|
SRC_PPC64 := src/fossmark_ppc32.c
|
||||||
@@ -63,7 +64,7 @@ else ifneq (,$(filter x86_64 amd64,$(HOST_ARCH)))
|
|||||||
HOST_KERNEL := $(ASM_AMD64)
|
HOST_KERNEL := $(ASM_AMD64)
|
||||||
else ifneq (,$(filter i386 i486 i586 i686 x86,$(HOST_ARCH)))
|
else ifneq (,$(filter i386 i486 i586 i686 x86,$(HOST_ARCH)))
|
||||||
HOST_ARCHNAME := i386
|
HOST_ARCHNAME := i386
|
||||||
HOST_KERNEL := $(SRC_I386)
|
HOST_KERNEL := $(ASM_I386)
|
||||||
else ifneq (,$(filter ppc powerpc ppc32 powerpc32,$(HOST_ARCH)))
|
else ifneq (,$(filter ppc powerpc ppc32 powerpc32,$(HOST_ARCH)))
|
||||||
HOST_ARCHNAME := ppc32be
|
HOST_ARCHNAME := ppc32be
|
||||||
HOST_KERNEL := $(SRC_PPC32) $(ASM_PPC32)
|
HOST_KERNEL := $(SRC_PPC32) $(ASM_PPC32)
|
||||||
@@ -75,7 +76,16 @@ else
|
|||||||
$(error unsupported host architecture '$(HOST_ARCH)')
|
$(error unsupported host architecture '$(HOST_ARCH)')
|
||||||
endif
|
endif
|
||||||
ifeq ($(HOST_ARCHNAME),i386)
|
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
|
endif
|
||||||
ifeq ($(HOST_ARCHNAME),ppc64be)
|
ifeq ($(HOST_ARCHNAME),ppc64be)
|
||||||
CFLAGS += -mcpu=970 -maltivec
|
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)
|
$(CC_AMD64) $(CFLAGS) $(TLS_CFLAGS) $(PTHREAD) $(LDFLAGS) -o $@ $(DRIVER) $(ASM_AMD64) $(LDLIBS)
|
||||||
@echo "built $@"
|
@echo "built $@"
|
||||||
|
|
||||||
$(DIST)/fossmark-linux-i386: $(DRIVER) $(SRC_I386) | $(DIST)
|
$(DIST)/fossmark-linux-i386: $(DRIVER) $(ASM_I386) | $(DIST)
|
||||||
$(CC_I386) -m32 -march=pentium4 -msse2 $(CFLAGS) $(TLS_CFLAGS) $(PTHREAD) $(LDFLAGS) -o $@ $(DRIVER) $(SRC_I386) $(LDLIBS)
|
$(CC_I386) -m32 -march=pentium4 -fno-pie -no-pie $(CFLAGS) $(TLS_CFLAGS) $(PTHREAD) $(LDFLAGS) -o $@ $(DRIVER) $(ASM_I386) $(LDLIBS)
|
||||||
@echo "built $@"
|
@echo "built $@"
|
||||||
|
|
||||||
$(DIST)/fossmark-linux-ppc32be: $(DRIVER) $(SRC_PPC32) $(ASM_PPC32) | $(DIST)
|
$(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.
|
# Build and run the kernel correctness tests for the host arch.
|
||||||
test: | $(DIST)
|
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
|
./$(DIST)/test_kernels
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
|||||||
@@ -164,6 +164,11 @@ The kernel backends use only baseline instructions for their architecture:
|
|||||||
|
|
||||||
* `src/fossmark.S` uses ARMv8-A and NEON under AAPCS64.
|
* `src/fossmark.S` uses ARMv8-A and NEON under AAPCS64.
|
||||||
* `src/fossmark_x86_64.S` uses baseline x86-64 and SSE2 under the System V ABI.
|
* `src/fossmark_x86_64.S` uses baseline x86-64 and SSE2 under the System V ABI.
|
||||||
|
* `src/fossmark_i386.S` uses baseline 32-bit x86 (Pentium 4) and SSE2 under the
|
||||||
|
i386 System V (cdecl) ABI. With only six general-purpose registers, no
|
||||||
|
64-bit integer registers, and half of amd64's SSE2 register file (xmm0-7),
|
||||||
|
several kernels keep working state on the stack instead of in registers -
|
||||||
|
a real cost of the architecture, not an oversight.
|
||||||
* `src/fossmark_ppc32.c` is endian-safe and keeps a baseline 32-bit PowerPC
|
* `src/fossmark_ppc32.c` is endian-safe and keeps a baseline 32-bit PowerPC
|
||||||
fallback. At runtime, the extended-instruction test uses Paired Singles when
|
fallback. At runtime, the extended-instruction test uses Paired Singles when
|
||||||
the device-tree `compatible` property begins with `nintendo,`; otherwise it
|
the device-tree `compatible` property begins with `nintendo,`; otherwise it
|
||||||
@@ -205,6 +210,7 @@ with a nonzero status if any check fails.
|
|||||||
src/main.c portable benchmark driver and scoring
|
src/main.c portable benchmark driver and scoring
|
||||||
src/fossmark.S ARM64 kernels
|
src/fossmark.S ARM64 kernels
|
||||||
src/fossmark_x86_64.S x86-64 kernels
|
src/fossmark_x86_64.S x86-64 kernels
|
||||||
|
src/fossmark_i386.S i386 (Pentium 4) kernels
|
||||||
src/fossmark_ppc32.c PPC32/PPC64 big-endian kernels
|
src/fossmark_ppc32.c PPC32/PPC64 big-endian kernels
|
||||||
src/fossmark_ppc32_ext.S optional PPC32 PS, VSX, and AltiVec kernels
|
src/fossmark_ppc32_ext.S optional PPC32 PS, VSX, and AltiVec kernels
|
||||||
src/test_kernels.c correctness suite
|
src/test_kernels.c correctness suite
|
||||||
|
|||||||
+1344
File diff suppressed because it is too large
Load Diff
+2
-31
@@ -11,10 +11,6 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#if defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64)
|
|
||||||
#include <emmintrin.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
#include <sys/auxv.h>
|
#include <sys/auxv.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -68,7 +64,7 @@ uint64_t fm_primes(uint64_t limit, uint8_t *sieve)
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__powerpc__) && !defined(__powerpc64__)
|
#if !defined(__powerpc64__)
|
||||||
static uint64_t fm_simd_scalar(uint64_t iters, void *memory)
|
static uint64_t fm_simd_scalar(uint64_t iters, void *memory)
|
||||||
{
|
{
|
||||||
uint32_t *v = (uint32_t *)memory;
|
uint32_t *v = (uint32_t *)memory;
|
||||||
@@ -113,7 +109,7 @@ uint64_t fm_simd(uint64_t iters, void *memory)
|
|||||||
sum ^= v[j];
|
sum ^= v[j];
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
#elif defined(__powerpc__)
|
#else
|
||||||
/* These are kept in fossmark_ppc32_ext.S so this translation unit, and thus
|
/* These are kept in fossmark_ppc32_ext.S so this translation unit, and thus
|
||||||
* the executable's default code path, only requires baseline PPC32. */
|
* the executable's default code path, only requires baseline PPC32. */
|
||||||
extern void fm_simd_ps_kernel(uint64_t iters, void *memory);
|
extern void fm_simd_ps_kernel(uint64_t iters, void *memory);
|
||||||
@@ -196,31 +192,6 @@ uint64_t fm_simd(uint64_t iters, void *memory)
|
|||||||
sum ^= v[j];
|
sum ^= v[j];
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
uint64_t fm_simd(uint64_t iters, void *memory)
|
|
||||||
{
|
|
||||||
__m128i a, b;
|
|
||||||
uint32_t *v = (uint32_t *)memory;
|
|
||||||
uint32_t sum = 0;
|
|
||||||
uint64_t i;
|
|
||||||
unsigned j;
|
|
||||||
|
|
||||||
if (!iters)
|
|
||||||
return 0;
|
|
||||||
a = _mm_loadu_si128((const __m128i *)v);
|
|
||||||
b = _mm_loadu_si128((const __m128i *)(v + 4));
|
|
||||||
for (i = 0; i < iters; i++) {
|
|
||||||
a = _mm_add_epi32(a, b);
|
|
||||||
b = _mm_xor_si128(b, a);
|
|
||||||
a = _mm_add_epi32(a, b);
|
|
||||||
b = _mm_xor_si128(b, a);
|
|
||||||
}
|
|
||||||
_mm_storeu_si128((__m128i *)v, a);
|
|
||||||
_mm_storeu_si128((__m128i *)(v + 4), b);
|
|
||||||
for (j = 0; j < 8; j++)
|
|
||||||
sum ^= v[j];
|
|
||||||
return sum;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static uint32_t load32_native(const uint8_t *p)
|
static uint32_t load32_native(const uint8_t *p)
|
||||||
|
|||||||
+1
-1
@@ -42,7 +42,7 @@
|
|||||||
#ifndef FM_API_BASE_URL
|
#ifndef FM_API_BASE_URL
|
||||||
# define FM_API_BASE_URL "https://fossbench.net"
|
# define FM_API_BASE_URL "https://fossbench.net"
|
||||||
#endif
|
#endif
|
||||||
#define FM_VERSION "0.1.3-hotfix4"
|
#define FM_VERSION "0.1.4"
|
||||||
|
|
||||||
/* ---------- platform identification (for the banner only) ---------- */
|
/* ---------- platform identification (for the banner only) ---------- */
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user