diff --git a/c/Makefile b/c/Makefile index f98e541..66a290b 100644 --- a/c/Makefile +++ b/c/Makefile @@ -31,6 +31,13 @@ PPC64 := $(findstring ppc64,$(UNAME_M)) endif endif +TARGET_CPU := $(firstword $(subst -, ,$(TRIPLET))) +ifeq ($(TARGET_CPU),) +TARGET_CPU := $(UNAME_M) +endif +X86_64 := $(filter x86_64 amd64,$(TARGET_CPU)) +AARCH64 := $(filter aarch64 arm64,$(TARGET_CPU)) + ifneq (,$(DARWIN)) # --- macOS / Apple Silicon --- # Apple clang non include il runtime OpenMP: se c'e' libomp di Homebrew lo usa @@ -38,7 +45,9 @@ ifneq (,$(DARWIN)) # Niente -march: su arm64 NEON e' baseline (i kernel __ARM_NEON si attivano da soli). CC = clang OMPDIR := $(shell brew --prefix libomp 2>/dev/null) -ifneq ($(OMPDIR),) +# `brew --prefix libomp` can print the formula's prospective path even when it +# is not installed, so verify both artifacts before adding unusable flags. +ifneq ($(and $(wildcard $(OMPDIR)/include/omp.h),$(wildcard $(OMPDIR)/lib/libomp.*)),) OMPC = -Xclang -fopenmp -I$(OMPDIR)/include OMPL = -L$(OMPDIR)/lib -lomp else @@ -247,9 +256,23 @@ cuda-bench: backend_cuda.cu backend_cuda.h tests/bench_tensor_core.cu olmoe$(EXE): olmoe.c st.h json.h compat.h $(CC) $(CFLAGS) olmoe.c -o olmoe$(EXE) $(LDFLAGS) -# binario portabile da distribuire su altre macchine x86-64 +# Use a baseline that matches the compiler target. macOS already targets a +# portable baseline when ARCH is empty; forcing the x86 value there breaks +# Apple Silicon. Unknown targets use native rather than an invalid x86 flag. +ifneq (,$(DARWIN)) +PORTABLE_ARCH = +else ifneq (,$(AARCH64)) +PORTABLE_ARCH = armv8-a +else ifneq (,$(PPC64)) +PORTABLE_ARCH = power8 +else ifneq (,$(X86_64)) +PORTABLE_ARCH = x86-64-v3 +else +PORTABLE_ARCH = native +endif + portable: - $(MAKE) glm$(EXE) ARCH=x86-64-v3 + $(MAKE) glm$(EXE) ARCH=$(PORTABLE_ARCH) iobench$(EXE): iobench.c compat.h $(CC) $(CFLAGS) iobench.c -o iobench$(EXE) $(LDFLAGS) diff --git a/c/tests/test_makefile_platform.py b/c/tests/test_makefile_platform.py index 5b8c558..4ca4efc 100644 --- a/c/tests/test_makefile_platform.py +++ b/c/tests/test_makefile_platform.py @@ -11,6 +11,24 @@ MAKE = shutil.which("make") @unittest.skipUnless(MAKE, "make is required") class MakefilePlatformTests(unittest.TestCase): + def _dry_run(self, target, triplet, **variables): + args = [ + MAKE, + "--no-print-directory", + "-B", + "-n", + target, + f"TRIPLET={triplet}", + ] + args.extend(f"{name}={value}" for name, value in variables.items()) + return subprocess.run( + args, + cwd=C_DIR, + text=True, + capture_output=True, + check=True, + ) + def test_windows_nt_without_uname_selects_mingw_build(self): env = os.environ.copy() env["OS"] = "Windows_NT" @@ -29,6 +47,29 @@ class MakefilePlatformTests(unittest.TestCase): self.assertIn("-fopenmp", result.stdout) self.assertIn("-static", result.stdout) + def test_portable_build_uses_target_architecture(self): + cases = ( + ("x86_64-unknown-linux-gnu", "-march=x86-64-v3"), + ("aarch64-unknown-linux-gnu", "-march=armv8-a"), + ("powerpc64le-unknown-linux-gnu", "-mcpu=power8"), + ) + + for triplet, expected_flag in cases: + with self.subTest(triplet=triplet): + result = self._dry_run("portable", triplet) + self.assertIn(expected_flag, result.stdout) + + def test_darwin_portable_build_does_not_force_x86_architecture(self): + missing_libomp = "/colibri-test/missing-libomp" + result = self._dry_run( + "portable", "arm64-apple-darwin", OMPDIR=missing_libomp + ) + + self.assertIn("clang -O3", result.stdout) + self.assertNotIn("-mcpu=x86-64-v3", result.stdout) + self.assertNotIn(missing_libomp, result.stdout) + self.assertNotIn("-fopenmp", result.stdout) + if __name__ == "__main__": unittest.main()