From 438cc58b0239fc3c5d3217f8913c12108283f4fc Mon Sep 17 00:00:00 2001 From: Owen Rummage Date: Fri, 17 Jul 2026 18:21:31 -0500 Subject: [PATCH] Add PPC64 iMac G5 build support --- .github/workflows/release.yml | 10 +++++++++ Makefile | 27 +++++++++++++++++++++++-- README.md | 16 +++++++++++---- src/fossmark_ppc32.c | 38 +++++++++++++++++++++++++++++++++-- src/main.c | 7 ++++++- 5 files changed, 89 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7393dcf..ef013f0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -42,6 +42,10 @@ jobs: os: ubuntu-24.04 openssl_target: linux-ppc cross_prefix: powerpc-linux-gnu- + - target: linux-ppc64be + os: ubuntu-24.04 + openssl_target: linux-ppc64 + cross_prefix: powerpc64-linux-gnu- - target: macos-amd64 os: macos-14 openssl_target: darwin64-x86_64-cc @@ -67,6 +71,12 @@ jobs: sudo apt-get update sudo apt-get install --yes gcc-powerpc-linux-gnu + - name: Install the Linux PPC64 big-endian cross-compiler + if: matrix.target == 'linux-ppc64be' + run: | + sudo apt-get update + sudo apt-get install --yes gcc-powerpc64-linux-gnu + - name: Build static OpenSSL env: OPENSSL_VERSION: 3.5.7 diff --git a/Makefile b/Makefile index 91eb39d..3358e45 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ # src/fossmark.S AArch64 (ARM64) # src/fossmark_x86_64.S x86-64 (AMD64) # 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 # "binary that runs everywhere" is not possible - each OS/arch pair uses a # different executable format and instruction set - so output is named per @@ -13,6 +14,7 @@ # make build for the host arch (dist/fossmark--) # make linux-arm64 build the Linux/ARM64 binary # make linux-amd64 build the Linux/AMD64 binary +# make linux-ppc64be build Linux/PPC64 big-endian for an iMac G5 # make macos-arm64 build the macOS/ARM64 binary # make macos-amd64 build the macOS/AMD64 binary # make all build both Linux binaries @@ -27,6 +29,7 @@ # toolchain is named differently, e.g.: # make linux-amd64 CC_AMD64=x86_64-linux-gnu-gcc-14 # make linux-arm64 CC_ARM64="clang --target=aarch64-linux-gnu" +# make linux-ppc64be CC_PPC64BE=powerpc64-linux-gnu-gcc # # On macOS, Apple Clang can build both architectures. The macOS compiler may # be overridden for an osxcross or other cross toolchain: @@ -47,6 +50,7 @@ ASM_ARM64 := src/fossmark.S ASM_AMD64 := src/fossmark_x86_64.S SRC_PPC32 := src/fossmark_ppc32.c ASM_PPC32 := src/fossmark_ppc32_ext.S +SRC_PPC64 := src/fossmark_ppc32.c # ---- host detection: normalise `uname -m` to our arch names ---- HOST_ARCH := $(shell uname -m) @@ -59,10 +63,16 @@ else ifneq (,$(filter x86_64 amd64,$(HOST_ARCH))) else ifneq (,$(filter ppc powerpc ppc32 powerpc32,$(HOST_ARCH))) HOST_ARCHNAME := ppc32be HOST_KERNEL := $(SRC_PPC32) $(ASM_PPC32) +else ifneq (,$(filter ppc64 powerpc64,$(HOST_ARCH))) + HOST_ARCHNAME := ppc64be + HOST_KERNEL := $(SRC_PPC64) else HOST_ARCHNAME := $(HOST_ARCH) $(error unsupported host architecture '$(HOST_ARCH)') endif +ifeq ($(HOST_ARCHNAME),ppc64be) + CFLAGS += -mcpu=970 -maltivec +endif ifeq ($(HOST_ARCHNAME),arm64) HOST_KERNEL := $(ASM_ARM64) endif @@ -97,15 +107,20 @@ ifeq ($(HOST_ARCHNAME),ppc32be) else CC_PPC32BE ?= powerpc-linux-gnu-gcc endif +ifeq ($(HOST_ARCHNAME),ppc64be) + CC_PPC64BE ?= $(CC) +else + CC_PPC64BE ?= powerpc64-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 linux-ppc32be macos-arm64 macos-amd64 bench test clean +.PHONY: all native linux-arm64 linux-amd64 linux-ppc32be linux-ppc64be macos-arm64 macos-amd64 bench test clean # `make all` builds all Linux binaries. -all: linux-arm64 linux-amd64 linux-ppc32be +all: linux-arm64 linux-amd64 linux-ppc32be linux-ppc64be # `make native` (and bare `make`) build for whatever host you are on. native: $(NATIVE_BIN) @@ -113,6 +128,7 @@ native: $(NATIVE_BIN) linux-arm64: $(DIST)/fossmark-linux-arm64 linux-amd64: $(DIST)/fossmark-linux-amd64 linux-ppc32be: $(DIST)/fossmark-linux-ppc32be +linux-ppc64be: $(DIST)/fossmark-linux-ppc64be macos-arm64: $(DIST)/fossmark-macos-arm64 macos-amd64: $(DIST)/fossmark-macos-amd64 @@ -128,6 +144,10 @@ $(DIST)/fossmark-linux-ppc32be: $(DRIVER) $(SRC_PPC32) $(ASM_PPC32) | $(DIST) $(CC_PPC32BE) $(CFLAGS) $(TLS_CFLAGS) $(PTHREAD) $(LDFLAGS) -o $@ $(DRIVER) $(SRC_PPC32) $(ASM_PPC32) $(LDLIBS) @echo "built $@" +$(DIST)/fossmark-linux-ppc64be: $(DRIVER) $(SRC_PPC64) | $(DIST) + $(CC_PPC64BE) -mcpu=970 -maltivec $(CFLAGS) $(TLS_CFLAGS) $(PTHREAD) $(LDFLAGS) -o $@ $(DRIVER) $(SRC_PPC64) $(LDLIBS) + @echo "built $@" + $(DIST)/fossmark-macos-arm64: $(DRIVER) $(ASM_ARM64) | $(DIST) $(CC_MACOS_ARM64) -arch arm64 $(CFLAGS) $(TLS_CFLAGS) $(PTHREAD) $(LDFLAGS) -o $@ $(DRIVER) $(ASM_ARM64) $(LDLIBS) @echo "built $@" @@ -148,6 +168,9 @@ endif ifeq ($(OSNAME)-$(HOST_ARCHNAME),linux-ppc32be) NATIVE_HAS_RULE := yes endif +ifeq ($(OSNAME)-$(HOST_ARCHNAME),linux-ppc64be) +NATIVE_HAS_RULE := yes +endif ifeq ($(OSNAME)-$(HOST_ARCHNAME),macos-arm64) NATIVE_HAS_RULE := yes endif diff --git a/README.md b/README.md index 1bdfb77..2135c4a 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ across every available core. The final report includes separate single-core and multicore scores. The repository currently builds an executable named `fossmark` for ARM64, -x86-64, and 32-bit big-endian PowerPC. The C driver handles timing, memory, +x86-64, and 32- or 64-bit big-endian PowerPC. The C driver handles timing, memory, threads, output, and scoring. Performance-sensitive kernels live in architecture-specific backend files. @@ -51,18 +51,20 @@ Other targets are available for explicit platforms and architectures: make linux-arm64 make linux-amd64 make linux-ppc32be +make linux-ppc64be # PowerPC 970 / iMac G5 make macos-arm64 make macos-amd64 make all ``` -`make all` builds all three Linux targets. Cross-compilation requires a suitable +`make all` builds all four Linux targets. Cross-compilation requires a suitable 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 +make linux-ppc64be CC_PPC64BE=powerpc64-linux-gnu-gcc ``` Apple Clang can build either macOS architecture with `-arch`. Windows timing @@ -93,6 +95,10 @@ make CFLAGS='-O2 -Wall -Wextra -DFM_API_BASE_URL=\"https://bench.example.com\"' HTTPS uploads use OpenSSL with certificate and hostname verification. +The PPC64 target is big-endian and is compiled for the PowerPC 970 with +AltiVec, matching the CPU used by the iMac G5. It targets 64-bit Linux; use a +PowerPC64 Linux installation or live environment on the machine to run it. + Release binaries statically include OpenSSL. Linux releases are fully static; macOS releases retain only Apple's required system-library linkage because the macOS toolchain does not support fully static executables. @@ -101,7 +107,7 @@ macOS toolchain does not support fully static executables. 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 archives for AMD64, ARM64, and PPC32 big-endian, macOS archives for AMD64 +Linux archives for AMD64, ARM64, PPC32 big-endian, and PPC64 big-endian; macOS archives for AMD64 and ARM64, and a `SHA256SUMS` file. ## Scores @@ -149,6 +155,8 @@ The kernel backends use only baseline instructions for their architecture: the device-tree `compatible` property begins with `nintendo,`; otherwise it selects VSX, AltiVec, or the scalar fallback in that order according to Linux `AT_HWCAP`. +* The same C backend builds for 64-bit big-endian PowerPC. Its PPC64 path uses + the PowerPC 970's AltiVec unit and leaves out the PPC32-only assembly helpers. The PPC32 build uses a 2 MiB pointer-chase cycle, which exceeds the 750CL's L2 cache while keeping peak benchmark memory consumption below 32 MiB. Other @@ -183,7 +191,7 @@ 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/fossmark_ppc32.c PPC32/PPC64 big-endian kernels src/fossmark_ppc32_ext.S optional PPC32 PS, VSX, and AltiVec kernels src/test_kernels.c correctness suite Makefile native and cross-build targets diff --git a/src/fossmark_ppc32.c b/src/fossmark_ppc32.c index 0080197..d20a191 100644 --- a/src/fossmark_ppc32.c +++ b/src/fossmark_ppc32.c @@ -1,8 +1,8 @@ /* - * Portable kernel backend for 32-bit PowerPC. + * Portable kernel backend for big-endian PowerPC. * * Keeping this backend in C lets the compiler implement 64-bit arguments and - * returns according to the platform's PPC32 ABI. All byte-oriented formats + * returns according to the platform ABI. All byte-oriented formats * are decoded explicitly, so the code is correct on big-endian systems. */ #include @@ -64,6 +64,7 @@ uint64_t fm_primes(uint64_t limit, uint8_t *sieve) return count; } +#if !defined(__powerpc64__) static uint64_t fm_simd_scalar(uint64_t iters, void *memory) { uint32_t *v = (uint32_t *)memory; @@ -76,7 +77,39 @@ static uint64_t fm_simd_scalar(uint64_t iters, void *memory) memcpy(v, a, sizeof a); return sum; } +#endif +#if defined(__powerpc64__) +/* The PowerPC 970 in every iMac G5 implements AltiVec. Using GCC's vector + * type here lets the compiler handle whichever PPC64 ELF ABI the system uses; + * both PPC64 ABIs differ from the PPC32 assembly convention below. */ +typedef uint32_t fm_vec_u32 __attribute__((vector_size(16))); + +uint64_t fm_simd(uint64_t iters, void *memory) +{ + fm_vec_u32 a, b; + uint32_t *v = (uint32_t *)memory; + uint32_t sum = 0; + uint64_t i; + unsigned j; + + if (!iters) + return 0; + memcpy(&a, v, sizeof a); + memcpy(&b, v + 4, sizeof b); + for (i = 0; i < iters; i++) { + a = a + b; + b = b ^ a; + a = a + b; + b = b ^ a; + } + memcpy(v, &a, sizeof a); + memcpy(v + 4, &b, sizeof b); + for (j = 0; j < 8; j++) + sum ^= v[j]; + return sum; +} +#else /* These are kept in fossmark_ppc32_ext.S so this translation unit, and thus * the executable's default code path, only requires baseline PPC32. */ extern void fm_simd_ps_kernel(uint64_t iters, void *memory); @@ -159,6 +192,7 @@ uint64_t fm_simd(uint64_t iters, void *memory) sum ^= v[j]; return sum; } +#endif static uint32_t load32_native(const uint8_t *p) { diff --git a/src/main.c b/src/main.c index 54926a1..9a5b058 100644 --- a/src/main.c +++ b/src/main.c @@ -65,7 +65,12 @@ # define D_INT "64-bit ALU: imul, mul, div, bitops" # define D_FP "double: mulsd/addsd, divsd, sqrtsd" # define D_SIMD "SSE2: 128-bit integer + float" -#elif defined(__powerpc__) && !defined(__powerpc64__) +#elif defined(__powerpc64__) +# define FM_ARCH "PowerPC 64-bit big-endian" +# define D_INT "64-bit PowerPC integer ALU" +# define D_FP "PowerPC scalar double-precision floating point" +# define D_SIMD "AltiVec: 128-bit integer vectors (PowerPC 970)" +#elif defined(__powerpc__) # define FM_ARCH "PowerPC 32-bit big-endian" # define D_INT "PPC32 integer ALU and software 64-bit arithmetic" # define D_FP "PowerPC scalar double-precision floating point"