diff --git a/pkg/highway/bridge.cpp b/pkg/highway/bridge.cpp index 1e86c4934..8f607f3e6 100644 --- a/pkg/highway/bridge.cpp +++ b/pkg/highway/bridge.cpp @@ -1,8 +1,89 @@ +#include +#include #include + +#include #include +#include +#include + +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; + +// Mirror the upstream behavior closely enough for Highway's internal callers: +// format into a fixed buffer, fall back to a generic error if formatting fails, +// and then dispatch to either the registered hook or stderr. +void format_message(const char* format, va_list args, char* buffer, size_t size) { + const int written = vsnprintf(buffer, size, format, args); + if (written < 0) { + snprintf(buffer, size, "%s", "failed to format highway message"); + } +} + +} // 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, ...) { + char message[1024]; + va_list args; + va_start(args, format); + format_message(format, args, message, sizeof(message)); + va_end(args); + + if (WarnFunc func = g_warn_func) { + func(file, line, message); + return; + } + + fprintf(stderr, "%s:%d: %s\n", file, line, message); +} + +HWY_NORETURN void Abort(const char* file, int line, const char* format, ...) { + char message[1024]; + va_list args; + va_start(args, format); + format_message(format, args, message, sizeof(message)); + va_end(args); + + if (AbortFunc func = g_abort_func) { + func(file, line, message); + } else { + fprintf(stderr, "%s:%d: %s\n", file, line, message); + } + + abort(); +} + +} // 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; } diff --git a/pkg/highway/build.zig b/pkg/highway/build.zig index 49656b93e..6ed721562 100644 --- a/pkg/highway/build.zig +++ b/pkg/highway/build.zig @@ -21,14 +21,6 @@ pub fn build(b: *std.Build) !void { .linkage = .static, }); lib.linkLibC(); - // On MSVC, we must not use linkLibCpp because Zig unconditionally - // passes -nostdinc++ and then adds its bundled libc++/libc++abi - // include paths, which conflict with MSVC's own C++ runtime headers. - // The MSVC SDK include directories (added via linkLibC) contain - // both C and C++ headers, so linkLibCpp is not needed. - if (target.result.abi != .msvc) { - lib.linkLibCpp(); - } if (upstream_) |upstream| { lib.addIncludePath(upstream.path("")); module.addIncludePath(upstream.path("")); @@ -47,6 +39,10 @@ pub fn build(b: *std.Build) !void { var flags: std.ArrayList([]const u8) = .empty; defer flags.deinit(b.allocator); try flags.appendSlice(b.allocator, &.{ + // Highway can avoid libc++ entirely as long as all users compile + // against the headers with the same define. + "-DHWY_NO_LIBCXX", + // Avoid changing binaries based on the current time and date. "-Wno-builtin-macro-redefined", "-D__DATE__=\"redacted\"", @@ -103,13 +99,11 @@ pub fn build(b: *std.Build) !void { .root = upstream.path(""), .flags = flags.items, .files = &.{ - "hwy/abort.cc", - "hwy/aligned_allocator.cc", - "hwy/nanobenchmark.cc", + // These provide the runtime target selection used by + // HWY_DYNAMIC_DISPATCH. The benchmark, timer, print, and + // aligned allocator support files are unused by Ghostty. "hwy/per_target.cc", - "hwy/print.cc", "hwy/targets.cc", - "hwy/timer.cc", }, }); lib.installHeadersDirectory( diff --git a/src/build/SharedDeps.zig b/src/build/SharedDeps.zig index cb4bf7619..70ff84b8c 100644 --- a/src/build/SharedDeps.zig +++ b/src/build/SharedDeps.zig @@ -753,6 +753,7 @@ pub fn addSimd( ) !void { const target = m.resolved_target.?; const optimize = m.optimize.?; + const system_highway = b.systemIntegrationOption("highway", .{ .default = false }); // Simdutf if (b.systemIntegrationOption("simdutf", .{})) { @@ -771,7 +772,7 @@ pub fn addSimd( } // Highway - if (b.systemIntegrationOption("highway", .{ .default = false })) { + if (system_highway) { m.linkSystemLibrary("libhwy", dynamic_link_opts); } else { if (b.lazyDependency("highway", .{ @@ -830,6 +831,14 @@ pub fn addSimd( "-std=c++17", ); + // Keep our SIMD sources in the same Highway header mode as the + // vendored package build so HWY's inline dispatch/runtime helpers + // have a consistent ABI. + if (!system_highway) try flags.append( + b.allocator, + "-DHWY_NO_LIBCXX", + ); + // Disable ubsan for MSVC to avoid undefined references to // __ubsan_handle_* symbols that require a runtime we don't link // and bundle. Hopefully we can fix this one day since ubsan is nice! diff --git a/src/simd/codepoint_width.cpp b/src/simd/codepoint_width.cpp index 294922c65..e380a9dbd 100644 --- a/src/simd/codepoint_width.cpp +++ b/src/simd/codepoint_width.cpp @@ -3,8 +3,8 @@ #define HWY_TARGET_INCLUDE "simd/codepoint_width.cpp" // this file #include // must come before highway.h #include -#include +#include #include #include