pkg/highway: Darwin builds don't rely on Apple headers

This uses a custom fork of `hwy/targtes.cpp` that uses an extern
function written in Zig to use Zig's standard CPU detection to avoid
a dependency on Apple SDK headers.

This is on the path to removing Apple SDK requirements to build 
libghostty-vt, but will require a lot more work outside of this. The goal 
is to get this out of our external dependencies first and then we can
work on removing the internal side.
This commit is contained in:
Mitchell Hashimoto
2026-04-23 14:23:20 -07:00
parent 2f1a30ddb0
commit c642e3104b
8 changed files with 359 additions and 102 deletions

View File

@@ -0,0 +1,60 @@
#include <hwy/abort.h>
#include <hwy/base.h>
#include <hwy/targets.h>
#include <stdint.h>
namespace hwy {
namespace {
// Highway's upstream abort.cc pulls in libc++ even when the rest of the
// library is compiled with HWY_NO_LIBCXX. Ghostty only needs Highway's dynamic
// dispatch/runtime target selection, so we provide the tiny Warn/Abort surface
// that targets.cc/per_target.cc expect and keep the package free of libc++.
WarnFunc g_warn_func = nullptr;
AbortFunc g_abort_func = nullptr;
} // namespace
WarnFunc& GetWarnFunc() {
return g_warn_func;
}
AbortFunc& GetAbortFunc() {
return g_abort_func;
}
WarnFunc SetWarnFunc(WarnFunc func) {
// Highway documents these setters as thread-safe. Using the compiler builtin
// keeps that guarantee without depending on std::atomic.
return __atomic_exchange_n(&g_warn_func, func, __ATOMIC_SEQ_CST);
}
AbortFunc SetAbortFunc(AbortFunc func) {
return __atomic_exchange_n(&g_abort_func, func, __ATOMIC_SEQ_CST);
}
void Warn(const char* file, int line, const char* format, ...) {
if (WarnFunc func = __atomic_load_n(&g_warn_func, __ATOMIC_SEQ_CST)) {
func(file, line, format);
}
}
HWY_NORETURN void Abort(const char* file, int line, const char* format, ...) {
if (AbortFunc func = __atomic_load_n(&g_abort_func, __ATOMIC_SEQ_CST)) {
func(file, line, format);
}
__builtin_trap();
}
} // namespace hwy
extern "C" {
// Zig reads HWY_SUPPORTED_TARGETS via this C shim so it can keep its target
// enum in sync with the vendored Highway build without parsing C++ headers.
int64_t hwy_supported_targets() {
return HWY_SUPPORTED_TARGETS;
}
}

View File

@@ -0,0 +1,61 @@
#include <hwy/base.h>
#include <hwy/detect_targets.h>
#include <hwy/highway.h>
#include <hwy/targets.h>
namespace hwy {
extern "C" int64_t ghostty_hwy_detect_targets();
static int64_t DetectTargets() {
int64_t bits = HWY_SCALAR | HWY_EMU128;
#if (HWY_ARCH_X86 || HWY_ARCH_ARM) && HWY_HAVE_RUNTIME_DISPATCH
bits |= ghostty_hwy_detect_targets();
#else
bits |= HWY_ENABLED_BASELINE;
#endif
if ((bits & HWY_ENABLED_BASELINE) != HWY_ENABLED_BASELINE) {
const uint64_t bits_u = static_cast<uint64_t>(bits);
const uint64_t enabled = static_cast<uint64_t>(HWY_ENABLED_BASELINE);
HWY_WARN("CPU supports 0x%08x%08x, software requires 0x%08x%08x\n",
static_cast<uint32_t>(bits_u >> 32),
static_cast<uint32_t>(bits_u & 0xFFFFFFFF),
static_cast<uint32_t>(enabled >> 32),
static_cast<uint32_t>(enabled & 0xFFFFFFFF));
}
return bits;
}
static int64_t supported_targets_for_test_ = 0;
static int64_t supported_mask_ = LimitsMax<int64_t>();
HWY_DLLEXPORT void DisableTargets(int64_t disabled_targets) {
supported_mask_ = static_cast<int64_t>(~disabled_targets);
GetChosenTarget().DeInit();
}
HWY_DLLEXPORT void SetSupportedTargetsForTest(int64_t targets) {
supported_targets_for_test_ = targets;
GetChosenTarget().DeInit();
}
HWY_DLLEXPORT int64_t SupportedTargets() {
int64_t targets = supported_targets_for_test_;
if (HWY_LIKELY(targets == 0)) {
targets = DetectTargets();
GetChosenTarget().Update(targets);
}
targets &= supported_mask_;
return targets == 0 ? HWY_STATIC_TARGET : targets;
}
HWY_DLLEXPORT ChosenTarget& GetChosenTarget() {
static ChosenTarget chosen_target;
return chosen_target;
}
} // namespace hwy