From ec7e7c3a27e271f7896c8c574c98bc5ed549a3cb Mon Sep 17 00:00:00 2001 From: Owen Rummage Date: Fri, 17 Jul 2026 15:16:30 -0500 Subject: [PATCH] add ppc32be, and other fixes --- .github/workflows/release.yml | 8 ++ Makefile | 37 +++++++-- README.md | 21 +++-- dist/fossmark-linux-amd64 | Bin 30440 -> 30440 bytes src/fossmark_ppc32.c | 149 ++++++++++++++++++++++++++++++++++ src/main.c | 13 +-- 6 files changed, 206 insertions(+), 22 deletions(-) create mode 100644 src/fossmark_ppc32.c diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3addf59..4eb5908 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,6 +34,8 @@ jobs: os: ubuntu-24.04 - target: linux-arm64 os: ubuntu-24.04 + - target: linux-ppc32be + os: ubuntu-24.04 - target: macos-amd64 os: macos-14 - target: macos-arm64 @@ -49,6 +51,12 @@ jobs: sudo apt-get update sudo apt-get install --yes gcc-aarch64-linux-gnu + - name: Install the Linux PPC32 big-endian cross-compiler + if: matrix.target == 'linux-ppc32be' + run: | + sudo apt-get update + sudo apt-get install --yes gcc-powerpc-linux-gnu + - name: Build run: make ${{ matrix.target }} diff --git a/Makefile b/Makefile index e74f4f3..29b4bda 100644 --- a/Makefile +++ b/Makefile @@ -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_ppc32.c PowerPC 32-bit, including big-endian systems # The C driver (src/main.c) is portable across architectures and OSes. A # "binary that runs everywhere" is not possible - each OS/arch pair uses a # different executable format and instruction set - so output is named per @@ -42,6 +43,7 @@ DIST := dist DRIVER := src/main.c ASM_ARM64 := src/fossmark.S ASM_AMD64 := src/fossmark_x86_64.S +SRC_PPC32 := src/fossmark_ppc32.c # ---- host detection: normalise `uname -m` to our arch names ---- HOST_ARCH := $(shell uname -m) @@ -50,10 +52,16 @@ ifneq (,$(filter aarch64 arm64,$(HOST_ARCH))) HOST_ASM := $(ASM_ARM64) else ifneq (,$(filter x86_64 amd64,$(HOST_ARCH))) HOST_ARCHNAME := amd64 - HOST_ASM := $(ASM_AMD64) + HOST_KERNEL := $(ASM_AMD64) +else ifneq (,$(filter ppc powerpc ppc32 powerpc32,$(HOST_ARCH))) + HOST_ARCHNAME := ppc32be + HOST_KERNEL := $(SRC_PPC32) else HOST_ARCHNAME := $(HOST_ARCH) - HOST_ASM := $(ASM_ARM64) + $(error unsupported host architecture '$(HOST_ARCH)') +endif +ifeq ($(HOST_ARCHNAME),arm64) + HOST_KERNEL := $(ASM_ARM64) endif # ---- host OS name for the native binary ---- @@ -81,21 +89,27 @@ else endif CC_MACOS_ARM64 ?= $(CC) CC_MACOS_AMD64 ?= $(CC) +ifeq ($(HOST_ARCHNAME),ppc32be) + CC_PPC32BE ?= $(CC) +else + CC_PPC32BE ?= powerpc-linux-gnu-gcc +endif NATIVE_BIN := $(DIST)/fossmark-$(OSNAME)-$(HOST_ARCHNAME) # `make` with no target builds the host binary, as before. .DEFAULT_GOAL := native -.PHONY: all native linux-arm64 linux-amd64 macos-arm64 macos-amd64 bench test clean +.PHONY: all native linux-arm64 linux-amd64 linux-ppc32be macos-arm64 macos-amd64 bench test clean -# `make all` builds both Linux binaries. -all: linux-arm64 linux-amd64 +# `make all` builds all Linux binaries. +all: linux-arm64 linux-amd64 linux-ppc32be # `make native` (and bare `make`) build for whatever host you are on. native: $(NATIVE_BIN) linux-arm64: $(DIST)/fossmark-linux-arm64 linux-amd64: $(DIST)/fossmark-linux-amd64 +linux-ppc32be: $(DIST)/fossmark-linux-ppc32be macos-arm64: $(DIST)/fossmark-macos-arm64 macos-amd64: $(DIST)/fossmark-macos-amd64 @@ -107,6 +121,10 @@ $(DIST)/fossmark-linux-amd64: $(DRIVER) $(ASM_AMD64) | $(DIST) $(CC_AMD64) $(CFLAGS) $(PTHREAD) -o $@ $(DRIVER) $(ASM_AMD64) $(LDLIBS) @echo "built $@" +$(DIST)/fossmark-linux-ppc32be: $(DRIVER) $(SRC_PPC32) | $(DIST) + $(CC_PPC32BE) $(CFLAGS) $(PTHREAD) -o $@ $(DRIVER) $(SRC_PPC32) $(LDLIBS) + @echo "built $@" + $(DIST)/fossmark-macos-arm64: $(DRIVER) $(ASM_ARM64) | $(DIST) $(CC_MACOS_ARM64) -arch arm64 $(CFLAGS) $(PTHREAD) -o $@ $(DRIVER) $(ASM_ARM64) $(LDLIBS) @echo "built $@" @@ -124,6 +142,9 @@ endif ifeq ($(OSNAME)-$(HOST_ARCHNAME),linux-amd64) NATIVE_HAS_RULE := yes endif +ifeq ($(OSNAME)-$(HOST_ARCHNAME),linux-ppc32be) +NATIVE_HAS_RULE := yes +endif ifeq ($(OSNAME)-$(HOST_ARCHNAME),macos-arm64) NATIVE_HAS_RULE := yes endif @@ -131,8 +152,8 @@ ifeq ($(OSNAME)-$(HOST_ARCHNAME),macos-amd64) NATIVE_HAS_RULE := yes endif ifneq ($(NATIVE_HAS_RULE),yes) -$(NATIVE_BIN): $(DRIVER) $(HOST_ASM) | $(DIST) - $(CC) $(CFLAGS) $(PTHREAD) -o $@ $(DRIVER) $(HOST_ASM) $(LDLIBS) +$(NATIVE_BIN): $(DRIVER) $(HOST_KERNEL) | $(DIST) + $(CC) $(CFLAGS) $(PTHREAD) -o $@ $(DRIVER) $(HOST_KERNEL) $(LDLIBS) @echo "built $@" endif @@ -145,7 +166,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_ASM) $(LDLIBS) + $(CC) $(CFLAGS) $(PTHREAD) -o $(DIST)/test_kernels src/test_kernels.c $(HOST_KERNEL) $(LDLIBS) ./$(DIST)/test_kernels clean: diff --git a/README.md b/README.md index 27a4287..c6fb837 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,10 @@ small C driver. It measures each workload twice: once on a single core and once across every available core. The final report includes separate single-core and multicore scores. -The repository currently builds an executable named `fossmark` for ARM64 and -x86-64. The C driver handles timing, memory, threads, output, and scoring. The -performance-sensitive kernels live in architecture-specific assembly files. +The repository currently builds an executable named `fossmark` for ARM64, +x86-64, and 32-bit big-endian PowerPC. The C driver handles timing, memory, +threads, output, and scoring. Performance-sensitive kernels live in +architecture-specific backend files. ## Workloads @@ -48,6 +49,7 @@ Other targets are available for explicit platforms and architectures: ```sh make linux-arm64 make linux-amd64 +make linux-ppc32be make macos-arm64 make macos-amd64 make all @@ -59,6 +61,7 @@ toolchain. Override the target compiler when its name differs from the default: ```sh make linux-arm64 CC_ARM64=aarch64-linux-gnu-gcc make linux-amd64 CC_AMD64=x86_64-linux-gnu-gcc +make linux-ppc32be CC_PPC32BE=powerpc-linux-gnu-gcc ``` Apple Clang can build either macOS architecture with `-arch`. Windows timing @@ -77,7 +80,8 @@ The exact filename depends on the host platform and architecture. Pushing a Git tag runs the GitHub Actions build and correctness tests. If they succeed, the workflow creates a GitHub Release named `Release ` with -Linux and macOS archives for AMD64 and ARM64, plus a `SHA256SUMS` file. +Linux archives for AMD64, ARM64, and PPC32 big-endian, macOS archives for AMD64 +and ARM64, and a `SHA256SUMS` file. ## Scores @@ -115,13 +119,14 @@ normal. ## Architecture support -The assembly kernels use only baseline instructions for their architecture: +The kernel backends use only baseline instructions for their architecture: * `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_ppc32.c` is endian-safe and uses baseline 32-bit PowerPC operations. It avoids AltiVec so it runs on the Wii's PowerPC 750CL-class CPU. -The kernel files contain no system calls or calls into the C library. The same -ARM64 source can be assembled for Linux, macOS, Windows, and BSD object formats. +The assembly kernel files contain no system calls or calls into the C library. +The same ARM64 source can be assembled for Linux, macOS, Windows, and BSD object formats. The current x86-64 source supports Linux, macOS, and the BSDs that use the System V calling convention. @@ -149,8 +154,8 @@ with a nonzero status if any check fails. src/main.c portable benchmark driver and scoring src/fossmark.S ARM64 kernels src/fossmark_x86_64.S x86-64 kernels +src/fossmark_ppc32.c PPC32 big-endian kernels src/test_kernels.c correctness suite Makefile native and cross-build targets dist/ generated binaries ``` - diff --git a/dist/fossmark-linux-amd64 b/dist/fossmark-linux-amd64 index 94308bd71e13e6c628b40a89e41ccd800fc9cc6a..5ec57a3becbdbcdb229557076da9e6ab41b86287 100755 GIT binary patch delta 4878 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 TGWmBEhCpp(V(`LtOT_;GZ8x*! delta 5080 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 h?)zWtk&e0Adw;+^(~%c-?~gZhf%4F}z(|Ka +#include +#include +#include + +static uint32_t rotl32(uint32_t x, unsigned n) +{ + return (x << n) | (x >> (32 - n)); +} + +uint64_t fm_int_math(uint64_t iters) +{ + uint64_t a = 0x9e3779b97f4a7c15ULL, b = 0xbf58476d1ce4e5b9ULL; + uint64_t c = 0x94d049bb133111ebULL, d = 0x2545f4914f6cdd1dULL; + uint64_t i; + if (!iters) return 0; + for (i = 0; i < iters; i++) { + a = a * 0xdeadbeefU + b; b = b * 0xdeadbeefU + c; + c = c * 0xdeadbeefU + d; d = d * 0xdeadbeefU + a; + a ^= c >> 29; b ^= d << 17; c ^= (a >> 31) | (a << 33); + d ^= b >> 7; a += c / 0xdeadbeefU; b += d / 0xdeadbeefU; + } + return a ^ b ^ c ^ d; +} + +uint64_t fm_fp_math(uint64_t iters) +{ + double a = 1.5, b = 2.5, c = 3.5, d = .5, out; + uint64_t bits, i; + if (!iters) return 0; + for (i = 0; i < iters; i++) { + a = fmin(a * 1.0625 + .0009765625, 2.0); + b = fmin(b * 1.0625 + .0009765625, 2.0); + c = fmin(c * 1.0625 + .0009765625, 2.0) + sqrt(a); + d = fmax(fabs(fmin(d * 1.0625 + .0009765625, 2.0) + sqrt(b)), 1.0); + a += 1.0 / (c + 1.0); b += 1.0 / (d + 1.0); + } + out = a + b + c + d; + memcpy(&bits, &out, sizeof bits); + return bits; +} + +uint64_t fm_primes(uint64_t limit, uint8_t *sieve) +{ + uint64_t i, j, count = 0; + if (limit < 2) return 0; + memset(sieve, 0, (size_t)limit); + sieve[0] = sieve[1] = 1; + for (i = 2; i <= (limit - 1) / i; i++) + if (!sieve[i]) for (j = i * i; j < limit; j += i) sieve[j] = 1; + for (i = 2; i < limit; i++) count += !sieve[i]; + return count; +} + +uint64_t fm_simd(uint64_t iters, void *memory) +{ + uint32_t *v = (uint32_t *)memory; + uint32_t a[8]; uint64_t i; unsigned j; uint32_t sum = 0; + if (!iters) return 0; + memcpy(a, v, sizeof a); + for (i = 0; i < iters; i++) + for (j = 0; j < 8; j++) a[j] = rotl32(a[j] + a[(j + 1) & 7] * (j + 3), (j + 5) & 31); + for (j = 0; j < 8; j++) sum ^= a[j]; + memcpy(v, a, sizeof a); + return sum; +} + +static uint32_t load32_native(const uint8_t *p) +{ + uint32_t v; memcpy(&v, p, sizeof v); return v; +} + +uint64_t fm_compress(const uint8_t *src, uint64_t len, uint32_t *ht) +{ + uint64_t ip = 0, anchor = 0, out = 0, ref, ml, lit; + memset(ht, 0, (size_t)(1U << 16) * sizeof *ht); + if (len < 16) return len + 1; + while (ip < len - 12) { + uint32_t seq = load32_native(src + ip); + uint32_t h = (uint32_t)(seq * 2654435761U) >> 16; + ref = ht[h]; ht[h] = (uint32_t)ip; + if (ref >= ip || ip - ref >= 65536 || load32_native(src + ref) != seq) { ip++; continue; } + for (ml = 4; ip + ml < len && src[ip + ml] == src[ref + ml]; ml++) {} + lit = ip - anchor; out += lit + 3 + (lit >= 15) + (ml >= 19); + ip += ml; anchor = ip; + } + return out + (len - anchor) + 1; +} + +static uint32_t load32le(const uint8_t *p) +{ + return (uint32_t)p[0] | (uint32_t)p[1] << 8 | (uint32_t)p[2] << 16 | (uint32_t)p[3] << 24; +} +static void store32le(uint8_t *p, uint32_t v) +{ + p[0] = (uint8_t)v; p[1] = (uint8_t)(v >> 8); p[2] = (uint8_t)(v >> 16); p[3] = (uint8_t)(v >> 24); +} +#define QR(a,b,c,d) do { a+=b; d=rotl32(d^a,16); c+=d; b=rotl32(b^c,12); a+=b; d=rotl32(d^a,8); c+=d; b=rotl32(b^c,7); } while (0) +uint64_t fm_chacha20(uint8_t *buf, uint64_t len, const uint8_t key[32], uint64_t passes) +{ + static const uint32_t sigma[4] = {0x61707865,0x3320646e,0x79622d32,0x6b206574}; + uint32_t base[16], x[16], counter = 0, checksum = 0; uint64_t pass, off; int i, r; + len &= ~(uint64_t)63; if (!len || !passes) return 0; + memcpy(base, sigma, 16); for (i=0;i<8;i++) base[4+i]=load32le(key+4*i); + base[13]=base[14]=base[15]=0; + for (pass=0;pass=end)return; if(c+1a[c])c++; if(a[root]>=a[c])return; t=a[root];a[root]=a[c];a[c]=t;root=c; } } +uint64_t fm_sort(uint32_t *a, uint64_t n) +{ + uint64_t i,end,sum=0; uint32_t t; if(n<2)return n?a[0]:0; + for (i = n / 2; i; i--) + sift(a, i - 1, n); + for (end = n - 1; end; end--) { + t = a[0]; a[0] = a[end]; a[end] = t; + sift(a, 0, end); + } + for(i=0;i>7)|(sum<<57);sum^=a[i];sum+=a[i];} return sum; +} + +uint64_t fm_chase(void **ptrs, uint64_t steps) +{ + void **p=ptrs; uint64_t i; if(!steps)return 0; for(i=0;i