Add simd flag for disabling SIMD functionality

pull/8840/head
Mitchell Hashimoto 2025-09-21 20:13:06 -07:00
parent f42656b0ac
commit 14eb8aa4e4
No known key found for this signature in database
GPG Key ID: 523D5DC389D273BC
8 changed files with 123 additions and 67 deletions

View File

@ -240,7 +240,8 @@ pub fn build(b: *std.Build) !void {
.optimize = config.optimize,
.filters = test_filters,
});
test_lib_vt_step.dependOn(&mod_vt_test.step);
const mod_vt_test_run = b.addRunArtifact(mod_vt_test);
test_lib_vt_step.dependOn(&mod_vt_test_run.step);
}
// Tests

View File

@ -37,6 +37,7 @@ font_backend: FontBackend = .freetype,
x11: bool = false,
wayland: bool = false,
sentry: bool = true,
simd: bool = true,
i18n: bool = true,
wasm_shared: bool = true,
@ -173,6 +174,12 @@ pub fn init(b: *std.Build) !Config {
}
};
config.simd = b.option(
bool,
"simd",
"Build with SIMD-accelerated code paths. This requires additional build dependencies and adds libc as a runtime dependency, but results in significant performance improvements.",
) orelse true;
config.wayland = b.option(
bool,
"gtk-wayland",

View File

@ -19,11 +19,14 @@ pub fn init(
.root_source_file = b.path("src/lib_vt.zig"),
.target = cfg.target,
.optimize = cfg.optimize,
.link_libc = true,
// SIMD require libc/libcpp (both) but otherwise we don't care.
.link_libc = if (cfg.simd) true else null,
.link_libcpp = if (cfg.simd) true else null,
});
deps.unicode_tables.addModuleImport(vt);
vt_options.addOptions(b, vt, .{
.artifact = .lib,
.simd = cfg.simd,
// We presently don't allow Oniguruma in our Zig module at all.
// We should expose this as a build option in the future so we can
@ -39,5 +42,13 @@ pub fn init(
},
});
// We always need unicode tables
deps.unicode_tables.addModuleImport(vt);
// If SIMD is enabled, add all our SIMD dependencies.
if (cfg.simd) {
try SharedDeps.addSimd(b, vt, null);
}
return .{ .vt = vt };
}

View File

@ -281,21 +281,6 @@ pub fn add(
}
}
// Simdutf
if (b.systemIntegrationOption("simdutf", .{})) {
step.linkSystemLibrary2("simdutf", dynamic_link_opts);
} else {
if (b.lazyDependency("simdutf", .{
.target = target,
.optimize = optimize,
})) |simdutf_dep| {
step.linkLibrary(simdutf_dep.artifact("simdutf"));
try static_libs.append(
simdutf_dep.artifact("simdutf").getEmittedBin(),
);
}
}
// Sentry
if (self.config.sentry) {
if (b.lazyDependency("sentry", .{
@ -324,6 +309,13 @@ pub fn add(
}
}
// Simd
if (self.config.simd) try addSimd(
b,
step.root_module,
&static_libs,
);
// Wasm we do manually since it is such a different build.
if (step.rootModuleTarget().cpu.arch == .wasm32) {
if (b.lazyDependency("zig_js", .{
@ -358,35 +350,8 @@ pub fn add(
step.addIncludePath(b.path("src/apprt/gtk"));
}
// C++ files
// libcpp is required for various dependencies
step.linkLibCpp();
step.addIncludePath(b.path("src"));
{
// From hwy/detect_targets.h
const HWY_AVX3_SPR: c_int = 1 << 4;
const HWY_AVX3_ZEN4: c_int = 1 << 6;
const HWY_AVX3_DL: c_int = 1 << 7;
const HWY_AVX3: c_int = 1 << 8;
// Zig 0.13 bug: https://github.com/ziglang/zig/issues/20414
// To workaround this we just disable AVX512 support completely.
// The performance difference between AVX2 and AVX512 is not
// significant for our use case and AVX512 is very rare on consumer
// hardware anyways.
const HWY_DISABLED_TARGETS: c_int = HWY_AVX3_SPR | HWY_AVX3_ZEN4 | HWY_AVX3_DL | HWY_AVX3;
step.addCSourceFiles(.{
.files = &.{
"src/simd/base64.cpp",
"src/simd/codepoint_width.cpp",
"src/simd/index_of.cpp",
"src/simd/vt.cpp",
},
.flags = if (step.rootModuleTarget().cpu.arch == .x86_64) &.{
b.fmt("-DHWY_DISABLED_TARGETS={}", .{HWY_DISABLED_TARGETS}),
} else &.{},
});
}
// We always require the system SDK so that our system headers are available.
// This makes things like `os/log.h` available for cross-compiling.
@ -496,24 +461,6 @@ pub fn add(
try static_libs.append(cimgui_dep.artifact("cimgui").getEmittedBin());
}
// Highway
if (b.lazyDependency("highway", .{
.target = target,
.optimize = optimize,
})) |highway_dep| {
step.linkLibrary(highway_dep.artifact("highway"));
try static_libs.append(highway_dep.artifact("highway").getEmittedBin());
}
// utfcpp - This is used as a dependency on our hand-written C++ code
if (b.lazyDependency("utfcpp", .{
.target = target,
.optimize = optimize,
})) |utfcpp_dep| {
step.linkLibrary(utfcpp_dep.artifact("utfcpp"));
try static_libs.append(utfcpp_dep.artifact("utfcpp").getEmittedBin());
}
// Fonts
{
// JetBrains Mono
@ -715,6 +662,77 @@ fn addGtkNg(
}
}
pub fn addSimd(
b: *std.Build,
m: *std.Build.Module,
static_libs: ?*LazyPathList,
) !void {
const target = m.resolved_target.?;
const optimize = m.optimize.?;
// Simdutf
if (b.systemIntegrationOption("simdutf", .{})) {
m.linkSystemLibrary("simdutf", dynamic_link_opts);
} else {
if (b.lazyDependency("simdutf", .{
.target = target,
.optimize = optimize,
})) |simdutf_dep| {
m.linkLibrary(simdutf_dep.artifact("simdutf"));
if (static_libs) |v| try v.append(
simdutf_dep.artifact("simdutf").getEmittedBin(),
);
}
}
// Highway
if (b.lazyDependency("highway", .{
.target = target,
.optimize = optimize,
})) |highway_dep| {
m.linkLibrary(highway_dep.artifact("highway"));
if (static_libs) |v| try v.append(highway_dep.artifact("highway").getEmittedBin());
}
// utfcpp - This is used as a dependency on our hand-written C++ code
if (b.lazyDependency("utfcpp", .{
.target = target,
.optimize = optimize,
})) |utfcpp_dep| {
m.linkLibrary(utfcpp_dep.artifact("utfcpp"));
if (static_libs) |v| try v.append(utfcpp_dep.artifact("utfcpp").getEmittedBin());
}
// SIMD C++ files
m.addIncludePath(b.path("src"));
{
// From hwy/detect_targets.h
const HWY_AVX3_SPR: c_int = 1 << 4;
const HWY_AVX3_ZEN4: c_int = 1 << 6;
const HWY_AVX3_DL: c_int = 1 << 7;
const HWY_AVX3: c_int = 1 << 8;
// Zig 0.13 bug: https://github.com/ziglang/zig/issues/20414
// To workaround this we just disable AVX512 support completely.
// The performance difference between AVX2 and AVX512 is not
// significant for our use case and AVX512 is very rare on consumer
// hardware anyways.
const HWY_DISABLED_TARGETS: c_int = HWY_AVX3_SPR | HWY_AVX3_ZEN4 | HWY_AVX3_DL | HWY_AVX3;
m.addCSourceFiles(.{
.files = &.{
"src/simd/base64.cpp",
"src/simd/codepoint_width.cpp",
"src/simd/index_of.cpp",
"src/simd/vt.cpp",
},
.flags = if (target.result.cpu.arch == .x86_64) &.{
b.fmt("-DHWY_DISABLED_TARGETS={}", .{HWY_DISABLED_TARGETS}),
} else &.{},
});
}
}
/// Creates the resources that can be prebuilt for our dist build.
pub fn gtkNgDistResources(
b: *std.Build,

View File

@ -133,6 +133,8 @@ test "unknown APC command" {
}
test "garbage Kitty command" {
if (comptime !build_options.kitty_graphics) return error.SkipZigTest;
const testing = std.testing;
const alloc = testing.allocator;
@ -143,6 +145,8 @@ test "garbage Kitty command" {
}
test "Kitty command with overflow u32" {
if (comptime !build_options.kitty_graphics) return error.SkipZigTest;
const testing = std.testing;
const alloc = testing.allocator;
@ -153,6 +157,8 @@ test "Kitty command with overflow u32" {
}
test "Kitty command with overflow i32" {
if (comptime !build_options.kitty_graphics) return error.SkipZigTest;
const testing = std.testing;
const alloc = testing.allocator;
@ -163,6 +169,8 @@ test "Kitty command with overflow i32" {
}
test "valid Kitty command" {
if (comptime !build_options.kitty_graphics) return error.SkipZigTest;
const testing = std.testing;
const alloc = testing.allocator;

View File

@ -13,6 +13,11 @@ pub const Options = struct {
///
oniguruma: bool = true,
/// Whether to build SIMD-accelerated code paths. This pulls in more
/// build-time dependencies and adds libc as a runtime dependency,
/// but results in significant performance improvements.
simd: bool = true,
/// True if we should enable the "slow" runtime safety checks. These
/// are runtime safety checks that are slower than typical and should
/// generally be disabled in production builds.
@ -36,6 +41,7 @@ pub fn addOptions(
const opts = b.addOptions();
opts.addOption(Artifact, "artifact", v.artifact);
opts.addOption(bool, "oniguruma", v.oniguruma);
opts.addOption(bool, "simd", v.simd);
opts.addOption(bool, "slow_runtime_safety", v.slow_runtime_safety);
// These are synthesized based on other options.

View File

@ -321,7 +321,10 @@ pub const Page = struct {
/// safety is disabled. This uses the libc allocator.
pub fn assertIntegrity(self: *const Page) void {
if (comptime build_options.slow_runtime_safety) {
self.verifyIntegrity(std.heap.c_allocator) catch |err| {
var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
defer _ = debug_allocator.deinit();
const alloc = debug_allocator.allocator();
self.verifyIntegrity(alloc) catch |err| {
log.err("page integrity violation, crashing. err={}", .{err});
@panic("page integrity violation");
};

View File

@ -1,4 +1,5 @@
const std = @import("std");
const build_options = @import("terminal_options");
const assert = std.debug.assert;
const testing = std.testing;
const simd = @import("../simd/main.zig");
@ -64,8 +65,9 @@ pub fn Stream(comptime Handler: type) type {
/// Process a string of characters.
pub fn nextSlice(self: *Self, input: []const u8) !void {
// Debug mode disables the SIMD optimizations
if (comptime debug) {
// Disable SIMD optimizations if build requests it or if our
// manual debug mode is on.
if (comptime debug or !build_options.simd) {
for (input) |c| try self.next(c);
return;
}