git: 716c2def90b2 - main - misc/py-pytorch: fix unknown symbol on aarch64; add pkg-message

From: Yuri Victorovich <yuri_at_FreeBSD.org>
Date: Tue, 22 Sep 2026 01:55:45 UTC
The branch main has been updated by yuri:

URL: https://cgit.FreeBSD.org/ports/commit/?id=716c2def90b228eb75591f80cd787f16a2316c0f

commit 716c2def90b228eb75591f80cd787f16a2316c0f
Author:     Yuri Victorovich <yuri@FreeBSD.org>
AuthorDate: 2026-09-21 23:58:07 +0000
Commit:     Yuri Victorovich <yuri@FreeBSD.org>
CommitDate: 2026-09-22 01:55:42 +0000

    misc/py-pytorch: fix unknown symbol on aarch64; add pkg-message
    
    Add cpuinfo patch to support aarch64 for this.
---
 misc/py-pytorch/Makefile                           |   8 +-
 misc/py-pytorch/files/example.py                   |   4 +-
 ...tch-cpuinfo-with-freebsd-support_CMakeLists.txt |  19 +++
 ...nfo-with-freebsd-support_src_arm_freebsd_init.c | 186 +++++++++++++++++++++
 ...with-freebsd-support_src_cpuinfo_internal-api.h |  17 ++
 .../patch-cpuinfo-with-freebsd-support_src_init.c  |  19 +++
 misc/py-pytorch/pkg-message                        |  26 +++
 7 files changed, 275 insertions(+), 4 deletions(-)

diff --git a/misc/py-pytorch/Makefile b/misc/py-pytorch/Makefile
index 0b8689c7c067..d52876700a99 100644
--- a/misc/py-pytorch/Makefile
+++ b/misc/py-pytorch/Makefile
@@ -1,7 +1,7 @@
 PORTNAME=	pytorch
 DISTVERSIONPREFIX=	v
 DISTVERSION=	2.14.0
-PORTREVISION=	1
+PORTREVISION=	2
 CATEGORIES=	misc # machine-learning
 MASTER_SITES=	https://github.com/pytorch/pytorch/releases/download/v${DISTVERSION}/
 PKGNAMEPREFIX=	${PYTHON_PKGNAMEPREFIX}
@@ -104,6 +104,12 @@ CONFLICTS_BUILD=	mpich # binaries are still linked with mpich when it is present
 
 .include <bsd.port.options.mk>
 
+.if ${ARCH} == aarch64
+# torch's many dlopen'd shared libraries (OpenMP, mimalloc, cpuinfo, etc.) require
+# more initial-exec TLS than rtld's small default dlopen reserve, see rtld(1)
+TEST_ENV+=	LD_STATIC_TLS_EXTRA=4096
+.endif
+
 post-extract:
 	# upstream ships a stray *.orig file that 'make makepatch' would otherwise
 	# mistake for a patch baseline and turn into a bogus binary patch
diff --git a/misc/py-pytorch/files/example.py b/misc/py-pytorch/files/example.py
index 0c8046ca603e..739c0b228e1f 100644
--- a/misc/py-pytorch/files/example.py
+++ b/misc/py-pytorch/files/example.py
@@ -24,8 +24,6 @@ class DiscriminantNN(nn.Module):
             nn.GELU(),
             nn.Linear(128, 128),
             nn.GELU(),
-            nn.Linear(128, 128),
-            nn.GELU(),
             nn.Linear(128, 64),
             nn.GELU(),
             nn.Linear(64, 1)
@@ -51,7 +49,7 @@ scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0
 
 print("Training started...")
 epoch = 0
-max_epochs = 300000
+max_epochs = 10000
 
 while epoch < max_epochs:
     #print(f"Epoch {epoch:04d} | Learning Rate: {optimizer.param_groups[0]['lr']:.6f}")
diff --git a/misc/py-pytorch/files/patch-cpuinfo-with-freebsd-support_CMakeLists.txt b/misc/py-pytorch/files/patch-cpuinfo-with-freebsd-support_CMakeLists.txt
new file mode 100644
index 000000000000..ea7224de20d9
--- /dev/null
+++ b/misc/py-pytorch/files/patch-cpuinfo-with-freebsd-support_CMakeLists.txt
@@ -0,0 +1,19 @@
+-- Build src/arm/freebsd/init.c for FreeBSD/aarch64 builds.
+-- cpuinfo has no FreeBSD backend for arm/aarch64 (only x86/freebsd exists),
+-- so cpuinfo_initialize() has no implementation to call into on aarch64 and
+-- libtorch_cpu.so fails to load with "Undefined symbol \"cpuinfo_isa\"".
+-- Paired with the new src/arm/freebsd/init.c added below.
+
+-- Upstreamed: https://github.com/pytorch/cpuinfo/pull/436
+
+--- cpuinfo-with-freebsd-support/CMakeLists.txt.orig	2026-09-21 07:02:31 UTC
++++ cpuinfo-with-freebsd-support/CMakeLists.txt
+@@ -211,6 +211,8 @@ IF(CPUINFO_SUPPORTED_PLATFORM)
+       ENDIF()
+     ELSEIF(IS_APPLE_OS AND CPUINFO_TARGET_PROCESSOR MATCHES "arm64.*")
+       LIST(APPEND CPUINFO_SRCS src/arm/mach/init.c)
++    ELSEIF(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
++      LIST(APPEND CPUINFO_SRCS src/arm/freebsd/init.c)
+     ENDIF()
+     IF(CMAKE_SYSTEM_NAME STREQUAL "Android")
+       LIST(APPEND CPUINFO_SRCS
diff --git a/misc/py-pytorch/files/patch-cpuinfo-with-freebsd-support_src_arm_freebsd_init.c b/misc/py-pytorch/files/patch-cpuinfo-with-freebsd-support_src_arm_freebsd_init.c
new file mode 100644
index 000000000000..bf9ad0623901
--- /dev/null
+++ b/misc/py-pytorch/files/patch-cpuinfo-with-freebsd-support_src_arm_freebsd_init.c
@@ -0,0 +1,186 @@
+-- New minimal FreeBSD/aarch64 backend for cpuinfo.
+-- cpuinfo upstream only ships a FreeBSD backend for x86 (src/x86/freebsd);
+-- aarch64 (this system's architecture) has no FreeBSD implementation, which
+-- leaves cpuinfo_initialize() unimplemented and cpuinfo_isa undefined,
+-- causing libtorch_cpu.so to fail to load with "Undefined symbol
+-- \"cpuinfo_isa\"" on FreeBSD/aarch64. Detects topology via the existing
+-- FreeBSD-only cpuinfo_freebsd_detect_topology() helper and ISA features via
+-- elf_aux_info(AT_HWCAP/AT_HWCAP2), whose bit layout FreeBSD keeps identical
+-- to Linux by design.
+
+-- Upstreamed: https://github.com/pytorch/cpuinfo/pull/436
+
+--- cpuinfo-with-freebsd-support/src/arm/freebsd/init.c.orig	2026-09-21 07:19:45 UTC
++++ cpuinfo-with-freebsd-support/src/arm/freebsd/init.c
+@@ -0,0 +1,171 @@
++/*
++ * Minimal AArch64/FreeBSD backend for cpuinfo.
++ *
++ * FreeBSD does not offer /proc/cpuinfo or the sysfs hwcap files that the
++ * Linux backend (src/arm/linux) relies on, so ISA feature bits are queried
++ * through elf_aux_info(AT_HWCAP/AT_HWCAP2), whose bit layout FreeBSD keeps
++ * identical to Linux by design (see sys/arm64/include/elf.h). Per-core
++ * microarchitecture identification (MIDR, vendor/uarch heuristics) and cache
++ * topology detection are not implemented; this only provides enough
++ * information (logical processor/core/package topology and ISA extensions)
++ * for callers such as ATen's cpuinfo-based capability queries.
++ */
++#include <stdbool.h>
++#include <stdint.h>
++#include <stdlib.h>
++#include <string.h>
++
++#include <sys/auxv.h>
++
++#include <cpuinfo.h>
++#include <cpuinfo/internal-api.h>
++#include <cpuinfo/log.h>
++#include <freebsd/api.h>
++
++#ifndef HWCAP_ASIMDHP
++#define HWCAP_ASIMDHP (1 << 10)
++#endif
++#ifndef HWCAP2_I8MM
++#define HWCAP2_I8MM (1 << 13)
++#endif
++#ifndef HWCAP2_BF16
++#define HWCAP2_BF16 (1 << 14)
++#endif
++#ifndef HWCAP2_SME
++#define HWCAP2_SME (1 << 23)
++#endif
++#ifndef HWCAP2_SVE2
++#define HWCAP2_SVE2 (1 << 1)
++#endif
++
++static uint64_t get_hwcap(int type) {
++	unsigned long value = 0;
++	if (elf_aux_info(type, &value, sizeof(value)) != 0) {
++		return 0;
++	}
++	return value;
++}
++
++struct cpuinfo_arm_isa cpuinfo_isa = {0};
++
++void cpuinfo_arm_freebsd_init(void) {
++	struct cpuinfo_processor* processors = NULL;
++	struct cpuinfo_core* cores = NULL;
++	struct cpuinfo_cluster* clusters = NULL;
++	struct cpuinfo_package* packages = NULL;
++	struct cpuinfo_uarch_info* uarchs = NULL;
++
++	struct cpuinfo_freebsd_topology freebsd_topology = cpuinfo_freebsd_detect_topology();
++	if (freebsd_topology.packages == 0) {
++		cpuinfo_log_error("failed to detect topology");
++		return;
++	}
++
++	processors = calloc(freebsd_topology.threads, sizeof(struct cpuinfo_processor));
++	cores = calloc(freebsd_topology.cores, sizeof(struct cpuinfo_core));
++	clusters = calloc(freebsd_topology.packages, sizeof(struct cpuinfo_cluster));
++	packages = calloc(freebsd_topology.packages, sizeof(struct cpuinfo_package));
++	uarchs = calloc(1, sizeof(struct cpuinfo_uarch_info));
++	if (processors == NULL || cores == NULL || clusters == NULL || packages == NULL || uarchs == NULL) {
++		cpuinfo_log_error("failed to allocate topology descriptors");
++		goto cleanup;
++	}
++
++	const uint32_t threads_per_package = freebsd_topology.threads / freebsd_topology.packages;
++	const uint32_t cores_per_package = freebsd_topology.cores / freebsd_topology.packages;
++	const uint32_t threads_per_core = freebsd_topology.threads_per_core;
++
++	uarchs[0] = (struct cpuinfo_uarch_info){
++		.uarch = cpuinfo_uarch_unknown,
++		.processor_count = freebsd_topology.threads,
++		.core_count = freebsd_topology.cores,
++	};
++
++	for (uint32_t i = 0; i < freebsd_topology.packages; i++) {
++		clusters[i] = (struct cpuinfo_cluster){
++			.processor_start = i * threads_per_package,
++			.processor_count = threads_per_package,
++			.core_start = i * cores_per_package,
++			.core_count = cores_per_package,
++			.cluster_id = 0,
++			.package = packages + i,
++		};
++		packages[i].processor_start = i * threads_per_package;
++		packages[i].processor_count = threads_per_package;
++		packages[i].core_start = i * cores_per_package;
++		packages[i].core_count = cores_per_package;
++		packages[i].cluster_start = i;
++		packages[i].cluster_count = 1;
++		strncpy(packages[i].name, "unknown", CPUINFO_PACKAGE_NAME_MAX - 1);
++	}
++	for (uint32_t i = 0; i < freebsd_topology.cores; i++) {
++		cores[i] = (struct cpuinfo_core){
++			.processor_start = i * threads_per_core,
++			.processor_count = threads_per_core,
++			.core_id = i % cores_per_package,
++			.cluster = clusters + i / cores_per_package,
++			.package = packages + i / cores_per_package,
++		};
++	}
++	for (uint32_t i = 0; i < freebsd_topology.threads; i++) {
++		processors[i].smt_id = i % threads_per_core;
++		processors[i].core = cores + i / threads_per_core;
++		processors[i].cluster = clusters + i / threads_per_package;
++		processors[i].package = packages + i / threads_per_package;
++	}
++
++	const uint64_t hwcap = get_hwcap(AT_HWCAP);
++	const uint64_t hwcap2 = get_hwcap(AT_HWCAP2);
++
++	struct cpuinfo_arm_isa isa;
++	memset(&isa, 0, sizeof(isa));
++	isa.aes = !!(hwcap & HWCAP_AES);
++	isa.pmull = !!(hwcap & HWCAP_PMULL);
++	isa.sha1 = !!(hwcap & HWCAP_SHA1);
++	isa.sha2 = !!(hwcap & HWCAP_SHA2);
++	isa.crc32 = !!(hwcap & HWCAP_CRC32);
++	isa.atomics = !!(hwcap & HWCAP_ATOMICS);
++	isa.rdm = !!(hwcap & HWCAP_ASIMDRDM);
++	isa.jscvt = !!(hwcap & HWCAP_JSCVT);
++	isa.fcma = !!(hwcap & HWCAP_FCMA);
++	isa.dot = !!(hwcap & HWCAP_ASIMDDP);
++	isa.fhm = !!(hwcap & HWCAP_ASIMDFHM);
++	isa.sve = !!(hwcap & HWCAP_SVE);
++	isa.fp16arith = !!(hwcap & HWCAP_FPHP) && !!(hwcap & HWCAP_ASIMDHP);
++	isa.i8mm = !!(hwcap2 & HWCAP2_I8MM);
++	isa.bf16 = !!(hwcap2 & HWCAP2_BF16);
++	isa.sve2 = !!(hwcap2 & HWCAP2_SVE2);
++	isa.sme = !!(hwcap2 & HWCAP2_SME);
++
++	/* Commit changes */
++	cpuinfo_isa = isa;
++	cpuinfo_processors = processors;
++	cpuinfo_cores = cores;
++	cpuinfo_clusters = clusters;
++	cpuinfo_packages = packages;
++
++	cpuinfo_processors_count = freebsd_topology.threads;
++	cpuinfo_cores_count = freebsd_topology.cores;
++	cpuinfo_clusters_count = freebsd_topology.packages;
++	cpuinfo_packages_count = freebsd_topology.packages;
++
++	cpuinfo_uarchs = uarchs;
++	cpuinfo_uarchs_count = 1;
++
++	__sync_synchronize();
++
++	cpuinfo_is_initialized = true;
++
++	processors = NULL;
++	cores = NULL;
++	clusters = NULL;
++	packages = NULL;
++	uarchs = NULL;
++
++cleanup:
++	free(processors);
++	free(cores);
++	free(clusters);
++	free(packages);
++	free(uarchs);
++}
diff --git a/misc/py-pytorch/files/patch-cpuinfo-with-freebsd-support_src_cpuinfo_internal-api.h b/misc/py-pytorch/files/patch-cpuinfo-with-freebsd-support_src_cpuinfo_internal-api.h
new file mode 100644
index 000000000000..76e517f2742c
--- /dev/null
+++ b/misc/py-pytorch/files/patch-cpuinfo-with-freebsd-support_src_cpuinfo_internal-api.h
@@ -0,0 +1,17 @@
+-- Declare cpuinfo_arm_freebsd_init(), the new FreeBSD/aarch64 backend entry
+-- point, alongside the other per-OS/arch init functions.
+-- Needed so src/init.c can dispatch to it on FreeBSD/aarch64; see
+-- src/arm/freebsd/init.c for why this backend is required.
+
+-- Upstreamed: https://github.com/pytorch/cpuinfo/pull/436
+
+--- cpuinfo-with-freebsd-support/src/cpuinfo/internal-api.h.orig	2026-09-21 07:02:31 UTC
++++ cpuinfo-with-freebsd-support/src/cpuinfo/internal-api.h
+@@ -59,6 +59,7 @@ CPUINFO_PRIVATE void cpuinfo_arm_linux_init(void);
+ #endif
+ CPUINFO_PRIVATE void cpuinfo_arm_mach_init(void);
+ CPUINFO_PRIVATE void cpuinfo_arm_linux_init(void);
++CPUINFO_PRIVATE void cpuinfo_arm_freebsd_init(void);
+ CPUINFO_PRIVATE void cpuinfo_riscv_linux_init(void);
+ CPUINFO_PRIVATE void cpuinfo_emscripten_init(void);
+ 
diff --git a/misc/py-pytorch/files/patch-cpuinfo-with-freebsd-support_src_init.c b/misc/py-pytorch/files/patch-cpuinfo-with-freebsd-support_src_init.c
new file mode 100644
index 000000000000..83d72d79269e
--- /dev/null
+++ b/misc/py-pytorch/files/patch-cpuinfo-with-freebsd-support_src_init.c
@@ -0,0 +1,19 @@
+-- Dispatch cpuinfo_initialize() to the new cpuinfo_arm_freebsd_init() on
+-- FreeBSD/aarch64, mirroring the existing __FreeBSD__ dispatch already
+-- present for x86. Without this, cpuinfo_initialize() silently does nothing
+-- on FreeBSD/aarch64 and libtorch_cpu.so fails to load with "Undefined
+-- symbol \"cpuinfo_isa\"".
+
+-- Upstreamed: https://github.com/pytorch/cpuinfo/pull/436
+
+--- cpuinfo-with-freebsd-support/src/init.c.orig	2026-09-21 07:02:31 UTC
++++ cpuinfo-with-freebsd-support/src/init.c
+@@ -38,6 +38,8 @@ bool CPUINFO_ABI cpuinfo_initialize(void) {
+ 	pthread_once(&init_guard, &cpuinfo_arm_linux_init);
+ #elif defined(__MACH__) && defined(__APPLE__)
+ 	pthread_once(&init_guard, &cpuinfo_arm_mach_init);
++#elif defined(__FreeBSD__)
++	pthread_once(&init_guard, &cpuinfo_arm_freebsd_init);
+ #elif defined(_WIN32)
+ 	InitOnceExecuteOnce(&init_guard, &cpuinfo_arm_windows_init, NULL, NULL);
+ #else
diff --git a/misc/py-pytorch/pkg-message b/misc/py-pytorch/pkg-message
new file mode 100644
index 000000000000..a45d4f96b31e
--- /dev/null
+++ b/misc/py-pytorch/pkg-message
@@ -0,0 +1,26 @@
+[
+{ type: install
+  message: <<EOM
+You installed PyTorch: an AI toolkit.
+
+## Need env var on amd64 Zen5/Zen6 CPU architectures
+
+Due to the bug in the OpenBLAS CPU arch detection PyTorch will
+crash on Zen5/Zen6 CPU architectures.
+
+Reference: https://github.com/OpenMathLib/OpenBLAS/issues/6029
+
+Use the following environment variable to work around this problem:
+OPENBLAS_CORETYPE=SKYLAKEX (some other values might work too)
+
+The same problem exists in all software that uses OpenBLAS, not just PyTorch.
+
+## Need env var on aarch64 due to high TLS requirements
+
+When running PyTorch on aarch64 you likely need to have this environment
+variable set:
+LD_STATIC_TLS_EXTRA=4096
+
+EOM
+}
+]