Compare commits

...

10 commits

Author SHA1 Message Date
Matthew Lugg
dcc5e0e10e
std.heap: rework c_allocator
The main goal here was to avoid allocating padding and header space if
`malloc` already guarantees the alignment we need via `max_align_t`.
Previously, the compiler was using `std.heap.raw_c_allocator` as its GPA
in some cases depending on `std.c.max_align_t`, but that's pretty
fragile (it meant we had to encode our alignment requirements into
`src/main.zig`!). Perhaps more importantly, that solution is
unnecessarily restrictive: since Zig's `Allocator` API passes the
`Alignment` not only to `alloc`, but also to `free` etc, we are able to
use a different strategy depending on its value. So `c_allocator` can
simply compare the requested align to `Alignment.of(std.c.max_align_t)`,
and use a raw `malloc` call (no header needed!) if it will guarantee a
suitable alignment (which, in practice, will be true the vast majority
of the time).

So in short, this makes `std.heap.c_allocator` more memory efficient,
and probably removes any incentive to use `std.heap.raw_c_allocator`.

I also refactored the `c_allocator` implementation while doing this,
just to neaten things up a little.
2025-12-05 10:19:26 +00:00
Alex Rønne Petersen
78cba86928
ci: set maxrss from $ZSF_MAX_RSS if provided by the runner
Some checks are pending
ci / aarch64-linux-debug (push) Waiting to run
ci / aarch64-linux-release (push) Waiting to run
ci / aarch64-macos-debug (push) Waiting to run
ci / aarch64-macos-release (push) Waiting to run
ci / loongarch64-linux-debug (push) Waiting to run
ci / loongarch64-linux-release (push) Waiting to run
ci / riscv64-linux-debug (push) Waiting to run
ci / riscv64-linux-release (push) Waiting to run
ci / s390x-linux-debug (push) Waiting to run
ci / s390x-linux-release (push) Waiting to run
ci / x86_64-freebsd-debug (push) Waiting to run
ci / x86_64-freebsd-release (push) Waiting to run
ci / x86_64-linux-debug (push) Waiting to run
ci / x86_64-linux-debug-llvm (push) Waiting to run
ci / x86_64-linux-release (push) Waiting to run
ci / x86_64-windows-debug (push) Waiting to run
ci / x86_64-windows-release (push) Waiting to run
All of our runners now define this. When running a CI script locally, this will
not be set, so we default to 0, aka "all available system memory".
2025-12-04 20:06:48 +01:00
Alex Rønne Petersen
2728eb5d5e
build: adjust max_rss on a per-CI-host basis
This avoids pessimizing concurrency on all machines due to e.g. the macOS
machine having high memory usage across the board due to 16K page size.

This also adds max_rss to test-unit and test-c-abi since those tend to eat a
decent chunk of memory too.
2025-12-04 20:06:12 +01:00
Alex Rønne Petersen
44543800a5
std.process.Child: enable rusage collection for dragonfly, netbsd, openbsd 2025-12-04 03:46:36 +01:00
Alex Rønne Petersen
2659fadb95
std.c: add rusage for dragonfly, netbsd, openbsd 2025-12-04 03:46:36 +01:00
Alex Rønne Petersen
9eed87f93e
std.process.Child: enable rusage collection for freebsd, illumos, serenity 2025-12-04 03:46:36 +01:00
Alex Rønne Petersen
e270c97ed1
ci: don't skip release mode tests on x86_64-windows-debug
pulsar is much faster than george so we don't need to do this anymore.
2025-12-04 03:46:36 +01:00
Alex Rønne Petersen
c21ce53494
ci: skip spirv and wasm tests on x86_64-freebsd
These are already built and run on x86_64-linux.
2025-12-04 03:46:36 +01:00
Alex Rønne Petersen
e2a9e568b4
build: add -Dskip-spirv and -Dskip-wasm options 2025-12-04 03:46:35 +01:00
Alex Rønne Petersen
ad9a5187ac
build: add some missing darwin tags 2025-12-04 03:46:35 +01:00
26 changed files with 437 additions and 133 deletions

182
build.zig
View file

@ -91,6 +91,8 @@ pub fn build(b: *std.Build) !void {
const skip_libc = b.option(bool, "skip-libc", "Main test suite skips tests that link libc") orelse false;
const skip_single_threaded = b.option(bool, "skip-single-threaded", "Main test suite skips tests that are single-threaded") orelse false;
const skip_compile_errors = b.option(bool, "skip-compile-errors", "Main test suite skips compile error tests") orelse false;
const skip_spirv = b.option(bool, "skip-spirv", "Main test suite skips targets with spirv32/spirv64 architecture") orelse false;
const skip_wasm = b.option(bool, "skip-wasm", "Main test suite skips targets with wasm32/wasm64 architecture") orelse false;
const skip_freebsd = b.option(bool, "skip-freebsd", "Main test suite skips targets with freebsd OS") orelse false;
const skip_netbsd = b.option(bool, "skip-netbsd", "Main test suite skips targets with netbsd OS") orelse false;
const skip_windows = b.option(bool, "skip-windows", "Main test suite skips targets with windows OS") orelse false;
@ -421,6 +423,8 @@ pub fn build(b: *std.Build) !void {
.test_target_filters = test_target_filters,
.skip_compile_errors = skip_compile_errors,
.skip_non_native = skip_non_native,
.skip_spirv = skip_spirv,
.skip_wasm = skip_wasm,
.skip_freebsd = skip_freebsd,
.skip_netbsd = skip_netbsd,
.skip_windows = skip_windows,
@ -452,6 +456,8 @@ pub fn build(b: *std.Build) !void {
.skip_single_threaded = skip_single_threaded,
.skip_non_native = skip_non_native,
.test_default_only = no_matrix,
.skip_spirv = skip_spirv,
.skip_wasm = skip_wasm,
.skip_freebsd = skip_freebsd,
.skip_netbsd = skip_netbsd,
.skip_windows = skip_windows,
@ -459,8 +465,29 @@ pub fn build(b: *std.Build) !void {
.skip_linux = skip_linux,
.skip_llvm = skip_llvm,
.skip_libc = skip_libc,
// 3888779264 was observed on an x86_64-linux-gnu host.
.max_rss = 4000000000,
.max_rss = switch (b.graph.host.result.os.tag) {
.freebsd => switch (b.graph.host.result.cpu.arch) {
.x86_64 => 1_060_217_241,
else => 1_100_000_000,
},
.linux => switch (b.graph.host.result.cpu.arch) {
.aarch64 => 659_809_075,
.loongarch64 => 598_902_374,
.riscv64 => 731_258_880,
.s390x => 580_596_121,
.x86_64 => 3_290_894_745,
else => 3_300_000_000,
},
.macos => switch (b.graph.host.result.cpu.arch) {
.aarch64 => 767_736_217,
else => 800_000_000,
},
.windows => switch (b.graph.host.result.cpu.arch) {
.x86_64 => 603_070_054,
else => 700_000_000,
},
else => 3_300_000_000,
},
}));
test_modules_step.dependOn(tests.addModuleTests(b, .{
@ -475,6 +502,8 @@ pub fn build(b: *std.Build) !void {
.skip_single_threaded = true,
.skip_non_native = skip_non_native,
.test_default_only = no_matrix,
.skip_spirv = skip_spirv,
.skip_wasm = skip_wasm,
.skip_freebsd = skip_freebsd,
.skip_netbsd = skip_netbsd,
.skip_windows = skip_windows,
@ -483,6 +512,29 @@ pub fn build(b: *std.Build) !void {
.skip_llvm = skip_llvm,
.skip_libc = true,
.no_builtin = true,
.max_rss = switch (b.graph.host.result.os.tag) {
.freebsd => switch (b.graph.host.result.cpu.arch) {
.x86_64 => 743_802_470,
else => 800_000_000,
},
.linux => switch (b.graph.host.result.cpu.arch) {
.aarch64 => 639_565_414,
.loongarch64 => 598_884_352,
.riscv64 => 636_429_516,
.s390x => 574_166_630,
.x86_64 => 764_861_644,
else => 800_000_000,
},
.macos => switch (b.graph.host.result.cpu.arch) {
.aarch64 => 701_413_785,
else => 800_000_000,
},
.windows => switch (b.graph.host.result.cpu.arch) {
.x86_64 => 536_414_208,
else => 600_000_000,
},
else => 800_000_000,
},
}));
test_modules_step.dependOn(tests.addModuleTests(b, .{
@ -497,6 +549,8 @@ pub fn build(b: *std.Build) !void {
.skip_single_threaded = true,
.skip_non_native = skip_non_native,
.test_default_only = no_matrix,
.skip_spirv = skip_spirv,
.skip_wasm = skip_wasm,
.skip_freebsd = skip_freebsd,
.skip_netbsd = skip_netbsd,
.skip_windows = skip_windows,
@ -505,6 +559,29 @@ pub fn build(b: *std.Build) !void {
.skip_llvm = skip_llvm,
.skip_libc = true,
.no_builtin = true,
.max_rss = switch (b.graph.host.result.os.tag) {
.freebsd => switch (b.graph.host.result.cpu.arch) {
.x86_64 => 557_892_403,
else => 600_000_000,
},
.linux => switch (b.graph.host.result.cpu.arch) {
.aarch64 => 615_302_758,
.loongarch64 => 598_974_464,
.riscv64 => 382_786_764,
.s390x => 395_555_635,
.x86_64 => 692_348_518,
else => 700_000_000,
},
.macos => switch (b.graph.host.result.cpu.arch) {
.aarch64 => 451_389_030,
else => 500_000_000,
},
.windows => switch (b.graph.host.result.cpu.arch) {
.x86_64 => 367_747_072,
else => 400_000_000,
},
else => 700_000_000,
},
}));
test_modules_step.dependOn(tests.addModuleTests(b, .{
@ -519,6 +596,8 @@ pub fn build(b: *std.Build) !void {
.skip_single_threaded = skip_single_threaded,
.skip_non_native = skip_non_native,
.test_default_only = no_matrix,
.skip_spirv = skip_spirv,
.skip_wasm = skip_wasm,
.skip_freebsd = skip_freebsd,
.skip_netbsd = skip_netbsd,
.skip_windows = skip_windows,
@ -526,8 +605,29 @@ pub fn build(b: *std.Build) !void {
.skip_linux = skip_linux,
.skip_llvm = skip_llvm,
.skip_libc = skip_libc,
// I observed a value of 5605064704 on the M2 CI.
.max_rss = 6165571174,
.max_rss = switch (b.graph.host.result.os.tag) {
.freebsd => switch (b.graph.host.result.cpu.arch) {
.x86_64 => 3_756_422_348,
else => 3_800_000_000,
},
.linux => switch (b.graph.host.result.cpu.arch) {
.aarch64 => 6_732_817_203,
.loongarch64 => 3_216_349_593,
.riscv64 => 3_570_899_763,
.s390x => 3_652_514_201,
.x86_64 => 3_249_546_854,
else => 6_800_000_000,
},
.macos => switch (b.graph.host.result.cpu.arch) {
.aarch64 => 8_273_795_481,
else => 8_300_000_000,
},
.windows => switch (b.graph.host.result.cpu.arch) {
.x86_64 => 3_750_236_160,
else => 3_800_000_000,
},
else => 8_300_000_000,
},
}));
const unit_tests_step = b.step("test-unit", "Run the compiler source unit tests");
@ -543,6 +643,29 @@ pub fn build(b: *std.Build) !void {
.use_llvm = use_llvm,
.use_lld = use_llvm,
.zig_lib_dir = b.path("lib"),
.max_rss = switch (b.graph.host.result.os.tag) {
.freebsd => switch (b.graph.host.result.cpu.arch) {
.x86_64 => 2_188_099_584,
else => 2_200_000_000,
},
.linux => switch (b.graph.host.result.cpu.arch) {
.aarch64 => 1_991_934_771,
.loongarch64 => 1_844_538_572,
.riscv64 => 2_459_003_289,
.s390x => 1_781_248_409,
.x86_64 => 977_192_550,
else => 2_500_000_000,
},
.macos => switch (b.graph.host.result.cpu.arch) {
.aarch64 => 2_062_393_344,
else => 2_100_000_000,
},
.windows => switch (b.graph.host.result.cpu.arch) {
.x86_64 => 1_953_087_488,
else => 2_000_000_000,
},
else => 2_500_000_000,
},
});
if (link_libc) {
unit_tests.root_module.link_libc = true;
@ -560,6 +683,7 @@ pub fn build(b: *std.Build) !void {
test_step.dependOn(tests.addCAbiTests(b, .{
.test_target_filters = test_target_filters,
.skip_non_native = skip_non_native,
.skip_wasm = skip_wasm,
.skip_freebsd = skip_freebsd,
.skip_netbsd = skip_netbsd,
.skip_windows = skip_windows,
@ -567,6 +691,29 @@ pub fn build(b: *std.Build) !void {
.skip_linux = skip_linux,
.skip_llvm = skip_llvm,
.skip_release = skip_release,
.max_rss = switch (b.graph.host.result.os.tag) {
.freebsd => switch (b.graph.host.result.cpu.arch) {
.x86_64 => 727_221_862,
else => 800_000_000,
},
.linux => switch (b.graph.host.result.cpu.arch) {
.aarch64 => 1_318_185_369,
.loongarch64 => 1_422_904_524,
.riscv64 => 449_924_710,
.s390x => 1_946_743_603,
.x86_64 => 2_139_993_292,
else => 2_200_000_000,
},
.macos => switch (b.graph.host.result.cpu.arch) {
.aarch64 => 1_813_612_134,
else => 1_900_000_000,
},
.windows => switch (b.graph.host.result.cpu.arch) {
.x86_64 => 386_287_616,
else => 400_000_000,
},
else => 2_200_000_000,
},
}));
test_step.dependOn(tests.addLinkTests(b, enable_macos_sdk, enable_ios_sdk, enable_symlinks_windows));
test_step.dependOn(tests.addStackTraceTests(b, test_filters, skip_non_native));
@ -616,6 +763,7 @@ pub fn build(b: *std.Build) !void {
.optimize_modes = optimization_modes,
.test_filters = test_filters,
.test_target_filters = test_target_filters,
.skip_wasm = skip_wasm,
// Highest RSS observed in any test case was exactly 1802878976 on x86_64-linux.
.max_rss = 2253598720,
})) |test_libc_step| test_step.dependOn(test_libc_step);
@ -721,7 +869,29 @@ fn addCompilerMod(b: *std.Build, options: AddCompilerModOptions) *std.Build.Modu
fn addCompilerStep(b: *std.Build, options: AddCompilerModOptions) *std.Build.Step.Compile {
const exe = b.addExecutable(.{
.name = "zig",
.max_rss = 7_800_000_000,
.max_rss = switch (b.graph.host.result.os.tag) {
.freebsd => switch (b.graph.host.result.cpu.arch) {
.x86_64 => 6_044_158_771,
else => 6_100_000_000,
},
.linux => switch (b.graph.host.result.cpu.arch) {
.aarch64 => 6_240_805_683,
.loongarch64 => 5_024_158_515,
.riscv64 => 6_996_309_196,
.s390x => 4_997_174_476,
.x86_64 => 5_486_090_649,
else => 7_000_000_000,
},
.macos => switch (b.graph.host.result.cpu.arch) {
.aarch64 => 6_639_145_779,
else => 6_700_000_000,
},
.windows => switch (b.graph.host.result.cpu.arch) {
.x86_64 => 5_770_394_009,
else => 5_800_000_000,
},
else => 7_000_000_000,
},
.root_module = addCompilerMod(b, options),
});
exe.stack_size = stack_size;
@ -798,7 +968,7 @@ fn addCmakeCfgOptionsToExe(
};
mod.linkSystemLibrary("unwind", .{});
},
.ios, .macos, .watchos, .tvos, .visionos => {
.driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => {
mod.link_libcpp = true;
},
.windows => {

View file

@ -44,7 +44,7 @@ ninja install
# No -fqemu and -fwasmtime here as they're covered by the x86_64-linux scripts.
stage3-debug/bin/zig build test docs \
--maxrss 44918199637 \
--maxrss ${ZSF_MAX_RSS:-0} \
-Dstatic-llvm \
-Dskip-non-native \
-Dtarget=native-native-musl \

View file

@ -44,7 +44,7 @@ ninja install
# No -fqemu and -fwasmtime here as they're covered by the x86_64-linux scripts.
stage3-release/bin/zig build test docs \
--maxrss 44918199637 \
--maxrss ${ZSF_MAX_RSS:-0} \
-Dstatic-llvm \
-Dskip-non-native \
-Dtarget=native-native-musl \

View file

@ -42,6 +42,7 @@ cmake .. \
ninja install
stage3-debug/bin/zig build test docs \
--maxrss ${ZSF_MAX_RSS:-0} \
--zig-lib-dir "$PWD/../lib" \
-Denable-macos-sdk \
-Dstatic-llvm \

View file

@ -51,6 +51,7 @@ stage3-release/bin/zig build test docs \
# Ensure that stage3 and stage4 are byte-for-byte identical.
stage3-release/bin/zig build \
--maxrss ${ZSF_MAX_RSS:-0} \
--prefix stage4-release \
-Denable-llvm \
-Dno-lib \

View file

@ -5,6 +5,7 @@ $ZIG_LLVM_CLANG_LLD_URL = "https://ziglang.org/deps/$ZIG_LLVM_CLANG_LLD_NAME.zip
$PREFIX_PATH = "$(Get-Location)\..\$ZIG_LLVM_CLANG_LLD_NAME"
$ZIG = "$PREFIX_PATH\bin\zig.exe"
$ZIG_LIB_DIR = "$(Get-Location)\lib"
$ZSF_MAX_RSS = if ($Env:ZSF_MAX_RSS) { $Env:ZSF_MAX_RSS } else { 0 }
if (!(Test-Path "..\$ZIG_LLVM_CLANG_LLD_NAME.zip")) {
Write-Output "Downloading $ZIG_LLVM_CLANG_LLD_URL"
@ -54,6 +55,7 @@ CheckLastExitCode
Write-Output "Main test suite..."
& "stage3-release\bin\zig.exe" build test docs `
--maxrss $ZSF_MAX_RSS `
--zig-lib-dir "$ZIG_LIB_DIR" `
--search-prefix "$PREFIX_PATH" `
-Dstatic-llvm `

View file

@ -45,7 +45,7 @@ ninja install
# No -fqemu and -fwasmtime here as they're covered by the x86_64-linux scripts.
stage3-debug/bin/zig build test docs \
--maxrss 60129542144 \
--maxrss ${ZSF_MAX_RSS:-0} \
-Dstatic-llvm \
-Dskip-non-native \
-Dtarget=native-native-musl \

View file

@ -45,7 +45,7 @@ ninja install
# No -fqemu and -fwasmtime here as they're covered by the x86_64-linux scripts.
stage3-release/bin/zig build test docs \
--maxrss 60129542144 \
--maxrss ${ZSF_MAX_RSS:-0} \
-Dstatic-llvm \
-Dskip-non-native \
-Dtarget=native-native-musl \

View file

@ -44,7 +44,7 @@ ninja install
# No -fqemu and -fwasmtime here as they're covered by the x86_64-linux scripts.
stage3-debug/bin/zig build test-cases test-modules test-unit test-c-abi test-stack-traces test-error-traces test-llvm-ir \
--maxrss 68719476736 \
--maxrss ${ZSF_MAX_RSS:-0} \
-Dstatic-llvm \
-Dskip-non-native \
-Dskip-single-threaded \

View file

@ -44,7 +44,7 @@ ninja install
# No -fqemu and -fwasmtime here as they're covered by the x86_64-linux scripts.
stage3-release/bin/zig build test-cases test-modules test-unit test-c-abi test-stack-traces test-error-traces test-llvm-ir \
--maxrss 68719476736 \
--maxrss ${ZSF_MAX_RSS:-0} \
-Dstatic-llvm \
-Dskip-non-native \
-Dskip-single-threaded \

View file

@ -45,7 +45,7 @@ ninja install
# No -fqemu and -fwasmtime here as they're covered by the x86_64-linux scripts.
stage3-debug/bin/zig build test docs \
--maxrss 30064771072 \
--maxrss ${ZSF_MAX_RSS:-0} \
-Dstatic-llvm \
-Dskip-non-native \
-Dtarget=native-native-musl \

View file

@ -45,7 +45,7 @@ ninja install
# No -fqemu and -fwasmtime here as they're covered by the x86_64-linux scripts.
stage3-release/bin/zig build test docs \
--maxrss 30064771072 \
--maxrss ${ZSF_MAX_RSS:-0} \
-Dstatic-llvm \
-Dskip-non-native \
-Dtarget=native-native-musl \

View file

@ -44,8 +44,10 @@ unset CXX
ninja install
stage3-debug/bin/zig build test docs \
--maxrss 42949672960 \
--maxrss ${ZSF_MAX_RSS:-0} \
-Dstatic-llvm \
-Dskip-spirv \
-Dskip-wasm \
-Dskip-linux \
-Dskip-netbsd \
-Dskip-windows \

View file

@ -44,8 +44,10 @@ unset CXX
ninja install
stage3-release/bin/zig build test docs \
--maxrss 42949672960 \
--maxrss ${ZSF_MAX_RSS:-0} \
-Dstatic-llvm \
-Dskip-spirv \
-Dskip-wasm \
-Dskip-linux \
-Dskip-netbsd \
-Dskip-windows \

View file

@ -49,7 +49,7 @@ stage3-debug/bin/zig build \
-Dno-lib
stage3-debug/bin/zig build test docs \
--maxrss 21000000000 \
--maxrss ${ZSF_MAX_RSS:-0} \
-Dlldb=$HOME/deps/lldb-zig/Debug-e0a42bb34/bin/lldb \
-fqemu \
-fwasmtime \

View file

@ -48,7 +48,7 @@ stage3-debug/bin/zig build \
-Dno-lib
stage3-debug/bin/zig build test docs \
--maxrss 21000000000 \
--maxrss ${ZSF_MAX_RSS:-0} \
-Dlldb=$HOME/deps/lldb-zig/Debug-e0a42bb34/bin/lldb \
-fqemu \
-fwasmtime \

View file

@ -54,7 +54,7 @@ stage3-release/bin/zig build \
-Dno-lib
stage3-release/bin/zig build test docs \
--maxrss 21000000000 \
--maxrss ${ZSF_MAX_RSS:-0} \
-Dlldb=$HOME/deps/lldb-zig/Release-e0a42bb34/bin/lldb \
-fqemu \
-fwasmtime \

View file

@ -5,6 +5,7 @@ $ZIG_LLVM_CLANG_LLD_URL = "https://ziglang.org/deps/$ZIG_LLVM_CLANG_LLD_NAME.zip
$PREFIX_PATH = "$($Env:USERPROFILE)\$ZIG_LLVM_CLANG_LLD_NAME"
$ZIG = "$PREFIX_PATH\bin\zig.exe"
$ZIG_LIB_DIR = "$(Get-Location)\lib"
$ZSF_MAX_RSS = if ($Env:ZSF_MAX_RSS) { $Env:ZSF_MAX_RSS } else { 0 }
if (!(Test-Path "$PREFIX_PATH.zip")) {
Write-Output "Downloading $ZIG_LLVM_CLANG_LLD_URL"
@ -54,12 +55,11 @@ CheckLastExitCode
Write-Output "Main test suite..."
& "stage3-debug\bin\zig.exe" build test docs `
--maxrss 32212254720 `
--maxrss $ZSF_MAX_RSS `
--zig-lib-dir "$ZIG_LIB_DIR" `
--search-prefix "$PREFIX_PATH" `
-Dstatic-llvm `
-Dskip-non-native `
-Dskip-release `
-Dskip-test-incremental `
-Denable-symlinks-windows `
--test-timeout 30m

View file

@ -5,6 +5,7 @@ $ZIG_LLVM_CLANG_LLD_URL = "https://ziglang.org/deps/$ZIG_LLVM_CLANG_LLD_NAME.zip
$PREFIX_PATH = "$($Env:USERPROFILE)\$ZIG_LLVM_CLANG_LLD_NAME"
$ZIG = "$PREFIX_PATH\bin\zig.exe"
$ZIG_LIB_DIR = "$(Get-Location)\lib"
$ZSF_MAX_RSS = if ($Env:ZSF_MAX_RSS) { $Env:ZSF_MAX_RSS } else { 0 }
if (!(Test-Path "$PREFIX_PATH.zip")) {
Write-Output "Downloading $ZIG_LLVM_CLANG_LLD_URL"
@ -54,7 +55,7 @@ CheckLastExitCode
Write-Output "Main test suite..."
& "stage3-release\bin\zig.exe" build test docs `
--maxrss 32212254720 `
--maxrss $ZSF_MAX_RSS `
--zig-lib-dir "$ZIG_LIB_DIR" `
--search-prefix "$PREFIX_PATH" `
-Dstatic-llvm `

View file

@ -4471,7 +4471,7 @@ pub const rusage = switch (native_os) {
pub const SELF = 1;
pub const CHILDREN = 2;
},
.freebsd => extern struct {
.freebsd, .openbsd => extern struct {
utime: timeval,
stime: timeval,
maxrss: c_long,
@ -4493,6 +4493,27 @@ pub const rusage = switch (native_os) {
pub const CHILDREN = -1;
pub const THREAD = 1;
},
.dragonfly, .netbsd => extern struct {
utime: timeval,
stime: timeval,
maxrss: c_long,
ixrss: c_long,
idrss: c_long,
isrss: c_long,
minflt: c_long,
majflt: c_long,
nswap: c_long,
inblock: c_long,
oublock: c_long,
msgsnd: c_long,
msgrcv: c_long,
nsignals: c_long,
nvcsw: c_long,
nivcsw: c_long,
pub const SELF = 0;
pub const CHILDREN = -1;
},
else => void,
};

View file

@ -141,7 +141,19 @@ test defaultQueryPageSize {
assert(std.math.isPowerOfTwo(defaultQueryPageSize()));
}
const CAllocator = struct {
/// A wrapper around the C memory allocation API which supports the full `Allocator`
/// interface, including arbitrary alignment. Simple `malloc` calls are used when
/// possible, but large requested alignments may require larger buffers in order to
/// satisfy the request. As well as `malloc`, `realloc`, and `free`, the extension
/// functions `malloc_usable_size` and `posix_memalign` are used when available.
///
/// For an allocator that directly calls `malloc`/`realloc`/`free`, with no padding
/// or special handling, see `raw_c_allocator`.
pub const c_allocator: Allocator = .{
.ptr = undefined,
.vtable = &c_allocator_impl.vtable,
};
const c_allocator_impl = struct {
comptime {
if (!builtin.link_libc) {
@compileError("C allocator is only available when linking against libc");
@ -155,67 +167,55 @@ const CAllocator = struct {
.free = free,
};
pub const supports_malloc_size = @TypeOf(malloc_size) != void;
pub const malloc_size = if (@TypeOf(c.malloc_size) != void)
c.malloc_size
else if (@TypeOf(c.malloc_usable_size) != void)
c.malloc_usable_size
else if (@TypeOf(c._msize) != void)
c._msize
else {};
pub const supports_posix_memalign = switch (builtin.os.tag) {
.dragonfly, .netbsd, .freebsd, .illumos, .openbsd, .linux, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos, .serenity => true,
const have_posix_memalign = switch (builtin.os.tag) {
.dragonfly,
.netbsd,
.freebsd,
.illumos,
.openbsd,
.linux,
.driverkit,
.ios,
.maccatalyst,
.macos,
.tvos,
.visionos,
.watchos,
.serenity,
=> true,
else => false,
};
fn getHeader(ptr: [*]u8) *[*]u8 {
return @ptrCast(@alignCast(ptr - @sizeOf(usize)));
fn allocStrat(need_align: Alignment) union(enum) {
raw,
posix_memalign: if (have_posix_memalign) void else noreturn,
manual_align: if (have_posix_memalign) noreturn else void,
} {
// If `malloc` guarantees `need_align`, always prefer a raw allocation.
if (Alignment.compare(need_align, .lte, .of(c.max_align_t))) {
return .raw;
}
// Use `posix_memalign` if available. Otherwise, we must manually align the allocation.
return if (have_posix_memalign) .posix_memalign else .manual_align;
}
fn alignedAlloc(len: usize, alignment: Alignment) ?[*]u8 {
const alignment_bytes = alignment.toByteUnits();
if (supports_posix_memalign) {
// The posix_memalign only accepts alignment values that are a
// multiple of the pointer size
const effective_alignment = @max(alignment_bytes, @sizeOf(usize));
var aligned_ptr: ?*anyopaque = undefined;
if (c.posix_memalign(&aligned_ptr, effective_alignment, len) != 0)
return null;
return @ptrCast(aligned_ptr);
}
// Thin wrapper around regular malloc, overallocate to account for
// alignment padding and store the original malloc()'ed pointer before
// the aligned address.
const unaligned_ptr = @as([*]u8, @ptrCast(c.malloc(len + alignment_bytes - 1 + @sizeOf(usize)) orelse return null));
const unaligned_addr = @intFromPtr(unaligned_ptr);
const aligned_addr = mem.alignForward(usize, unaligned_addr + @sizeOf(usize), alignment_bytes);
const aligned_ptr = unaligned_ptr + (aligned_addr - unaligned_addr);
getHeader(aligned_ptr).* = unaligned_ptr;
return aligned_ptr;
}
fn alignedFree(ptr: [*]u8) void {
if (supports_posix_memalign) {
return c.free(ptr);
}
const unaligned_ptr = getHeader(ptr).*;
c.free(unaligned_ptr);
}
fn alignedAllocSize(ptr: [*]u8) usize {
if (supports_posix_memalign) {
return CAllocator.malloc_size(ptr);
}
const unaligned_ptr = getHeader(ptr).*;
const delta = @intFromPtr(ptr) - @intFromPtr(unaligned_ptr);
return CAllocator.malloc_size(unaligned_ptr) - delta;
/// If `allocStrat(a) == .manual_align`, an allocation looks like this:
///
/// unaligned_ptr hdr_ptr aligned_ptr
/// v v v
/// +---------------+--------+--------------+
/// | padding | header | usable bytes |
/// +---------------+--------+--------------+
///
/// * `unaligned_ptr` is the raw return value of `malloc`.
/// * `aligned_ptr` is computed by aligning `unaligned_ptr` forward; it is what `alloc` returns.
/// * `hdr_ptr` points to a pointer-sized header directly before the usable space. This header
/// contains the value `unaligned_ptr`, so that we can pass it to `free` later. This is
/// necessary because the width of the padding is unknown.
///
/// This function accepts `aligned_ptr` and offsets it backwards to return `hdr_ptr`.
fn manualAlignHeader(aligned_ptr: [*]u8) *[*]u8 {
return @ptrCast(@alignCast(aligned_ptr - @sizeOf(usize)));
}
fn alloc(
@ -226,67 +226,120 @@ const CAllocator = struct {
) ?[*]u8 {
_ = return_address;
assert(len > 0);
return alignedAlloc(len, alignment);
switch (allocStrat(alignment)) {
.raw => {
// C only needs to respect `max_align_t` up to the allocation size due to object
// alignment rules. If necessary, extend the allocation size.
const actual_len = @max(len, @alignOf(std.c.max_align_t));
const ptr = c.malloc(actual_len) orelse return null;
assert(alignment.check(@intFromPtr(ptr)));
return @ptrCast(ptr);
},
.posix_memalign => {
// The posix_memalign only accepts alignment values that are a
// multiple of the pointer size
const effective_alignment = @max(alignment.toByteUnits(), @sizeOf(usize));
var aligned_ptr: ?*anyopaque = undefined;
if (c.posix_memalign(&aligned_ptr, effective_alignment, len) != 0) {
return null;
}
assert(alignment.check(@intFromPtr(aligned_ptr)));
return @ptrCast(aligned_ptr);
},
.manual_align => {
// Overallocate to account for alignment padding and store the original pointer
// returned by `malloc` before the aligned address.
const padded_len = len + @sizeOf(usize) + alignment.toByteUnits() - 1;
const unaligned_ptr: [*]u8 = @ptrCast(c.malloc(padded_len) orelse return null);
const unaligned_addr = @intFromPtr(unaligned_ptr);
const aligned_addr = alignment.forward(unaligned_addr + @sizeOf(usize));
const aligned_ptr = unaligned_ptr + (aligned_addr - unaligned_addr);
manualAlignHeader(aligned_ptr).* = unaligned_ptr;
return aligned_ptr;
},
}
}
fn resize(
_: *anyopaque,
buf: []u8,
memory: []u8,
alignment: Alignment,
new_len: usize,
return_address: usize,
) bool {
_ = alignment;
_ = return_address;
if (new_len <= buf.len) {
return true;
assert(new_len > 0);
if (new_len <= memory.len) {
return true; // in-place shrink always works
}
if (CAllocator.supports_malloc_size) {
const full_len = alignedAllocSize(buf.ptr);
if (new_len <= full_len) {
return true;
}
}
return false;
const mallocSize = func: {
if (@TypeOf(c.malloc_size) != void) break :func c.malloc_size;
if (@TypeOf(c.malloc_usable_size) != void) break :func c.malloc_usable_size;
if (@TypeOf(c._msize) != void) break :func c._msize;
return false; // we don't know how much space is actually available
};
const usable_len: usize = switch (allocStrat(alignment)) {
.raw, .posix_memalign => mallocSize(memory.ptr),
.manual_align => usable_len: {
const unaligned_ptr = manualAlignHeader(memory.ptr).*;
const full_len = mallocSize(unaligned_ptr);
const padding = @intFromPtr(memory.ptr) - @intFromPtr(unaligned_ptr);
break :usable_len full_len - padding;
},
};
return new_len <= usable_len;
}
fn remap(
context: *anyopaque,
ctx: *anyopaque,
memory: []u8,
alignment: Alignment,
new_len: usize,
return_address: usize,
) ?[*]u8 {
// realloc would potentially return a new allocation that does not
// respect the original alignment.
return if (resize(context, memory, alignment, new_len, return_address)) memory.ptr else null;
assert(new_len > 0);
// Prefer resizing in-place if possible, since `realloc` could be expensive even if legal.
if (resize(ctx, memory, alignment, new_len, return_address)) {
return memory.ptr;
}
switch (allocStrat(alignment)) {
.raw => {
// `malloc` and friends guarantee the required alignment, so we can try `realloc`.
// C only needs to respect `max_align_t` up to the allocation size due to object
// alignment rules. If necessary, extend the allocation size.
const actual_len = @max(new_len, @alignOf(std.c.max_align_t));
const new_ptr = c.realloc(memory.ptr, actual_len) orelse return null;
assert(alignment.check(@intFromPtr(new_ptr)));
return @ptrCast(new_ptr);
},
.posix_memalign, .manual_align => {
// `realloc` would potentially return a new allocation which does not respect
// the original alignment, so we can't do anything more.
return null;
},
}
}
fn free(
_: *anyopaque,
buf: []u8,
memory: []u8,
alignment: Alignment,
return_address: usize,
) void {
_ = alignment;
_ = return_address;
alignedFree(buf.ptr);
switch (allocStrat(alignment)) {
.raw, .posix_memalign => c.free(memory.ptr),
.manual_align => c.free(manualAlignHeader(memory.ptr).*),
}
}
};
/// Supports the full Allocator interface, including alignment, and exploiting
/// `malloc_usable_size` if available. For an allocator that directly calls
/// `malloc`/`free`, see `raw_c_allocator`.
pub const c_allocator: Allocator = .{
.ptr = undefined,
.vtable = &CAllocator.vtable,
};
/// Asserts allocations are within `@alignOf(std.c.max_align_t)` and directly
/// calls `malloc`/`free`. Does not attempt to utilize `malloc_usable_size`.
/// This allocator is safe to use as the backing allocator with
/// `ArenaAllocator` for example and is more optimal in such a case than
/// `c_allocator`.
/// Asserts that allocations have alignments which `malloc` can satisfy. This means that
/// the requested alignment is no greater than `@min(@alignOf(std.c.max_align_t), size)`.
///
/// This allocator is rarely appropriate to use. In general, prefer `c_allocator`, which
/// does not have any special requirements of its input, but is still highly efficient for
/// allocation requests which obey `malloc` alignment rules.
pub const raw_c_allocator: Allocator = .{
.ptr = undefined,
.vtable = &raw_c_allocator_vtable,
@ -306,13 +359,20 @@ fn rawCAlloc(
) ?[*]u8 {
_ = context;
_ = return_address;
assert(alignment.compare(.lte, .of(std.c.max_align_t)));
// Note that this pointer cannot be aligncasted to max_align_t because if
// len is < max_align_t then the alignment can be smaller. For example, if
// max_align_t is 16, but the user requests 8 bytes, there is no built-in
// type in C that is size 8 and has 16 byte alignment, so the alignment may
// be 8 bytes rather than 16. Similarly if only 1 byte is requested, malloc
// is allowed to return a 1-byte aligned pointer.
// `std.c.max_align_t` isn't the whole story, because if `len` is smaller than
// every C type with alignment `max_align_t`, the allocation can be less-aligned.
// The implementation need only guarantee that any type of length `len` would be
// suitably aligned.
//
// For instance, if `len == 8` and `alignment == .@"16"`, then `malloc` may not
// fulfil this request, because there is necessarily no C type with 8-byte size
// but 16-byte alignment.
//
// In theory, the resulting rule here would be target-specific, but in practice,
// the smallest type with an alignment of `max_align_t` has the same size (it's
// usually `c_longdouble`), so we can just check that `alignment <= len`.
assert(alignment.toByteUnits() <= len);
assert(Alignment.compare(alignment, .lte, .of(std.c.max_align_t)));
return @ptrCast(c.malloc(len));
}
@ -339,8 +399,9 @@ fn rawCRemap(
return_address: usize,
) ?[*]u8 {
_ = context;
_ = alignment;
_ = return_address;
// See `rawCMalloc` for an explanation of this `assert` call.
assert(alignment.toByteUnits() <= new_len);
return @ptrCast(c.realloc(memory.ptr, new_len));
}

View file

@ -122,7 +122,7 @@ pub const ResourceUsageStatistics = struct {
/// if available.
pub inline fn getMaxRss(rus: ResourceUsageStatistics) ?usize {
switch (native_os) {
.linux => {
.dragonfly, .freebsd, .netbsd, .openbsd, .illumos, .linux, .serenity => {
if (rus.rusage) |ru| {
return @as(usize, @intCast(ru.maxrss)) * 1024;
} else {
@ -149,7 +149,21 @@ pub const ResourceUsageStatistics = struct {
}
const rusage_init = switch (native_os) {
.linux, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => @as(?posix.rusage, null),
.dragonfly,
.freebsd,
.netbsd,
.openbsd,
.illumos,
.linux,
.serenity,
.driverkit,
.ios,
.maccatalyst,
.macos,
.tvos,
.visionos,
.watchos,
=> @as(?posix.rusage, null),
.windows => @as(?windows.VM_COUNTERS, null),
else => {},
};
@ -486,7 +500,21 @@ fn waitUnwrappedPosix(self: *ChildProcess) void {
const res: posix.WaitPidResult = res: {
if (self.request_resource_usage_statistics) {
switch (native_os) {
.linux, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => {
.dragonfly,
.freebsd,
.netbsd,
.openbsd,
.illumos,
.linux,
.serenity,
.driverkit,
.ios,
.maccatalyst,
.macos,
.tvos,
.visionos,
.watchos,
=> {
var ru: posix.rusage = undefined;
const res = posix.wait4(self.id, 0, &ru);
self.resource_usage_statistics.rusage = ru;

View file

@ -167,14 +167,7 @@ pub fn main() anyerror!void {
const gpa, const is_debug = gpa: {
if (build_options.debug_gpa) break :gpa .{ debug_allocator.allocator(), true };
if (native_os == .wasi) break :gpa .{ std.heap.wasm_allocator, false };
if (builtin.link_libc) {
// We would prefer to use raw libc allocator here, but cannot use
// it if it won't support the alignment we need.
if (@alignOf(std.c.max_align_t) < @max(@alignOf(i128), std.atomic.cache_line)) {
break :gpa .{ std.heap.c_allocator, false };
}
break :gpa .{ std.heap.raw_c_allocator, false };
}
if (builtin.link_libc) break :gpa .{ std.heap.c_allocator, false };
break :gpa switch (builtin.mode) {
.Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true },
.ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false },

View file

@ -444,6 +444,8 @@ pub const CaseTestOptions = struct {
test_target_filters: []const []const u8,
skip_compile_errors: bool,
skip_non_native: bool,
skip_spirv: bool,
skip_wasm: bool,
skip_freebsd: bool,
skip_netbsd: bool,
skip_windows: bool,
@ -472,6 +474,9 @@ pub fn lowerToBuildSteps(
if (options.skip_non_native and !case.target.query.isNative())
continue;
if (options.skip_spirv and case.target.query.cpu_arch != null and case.target.query.cpu_arch.?.isSpirV()) continue;
if (options.skip_wasm and case.target.query.cpu_arch != null and case.target.query.cpu_arch.?.isWasm()) continue;
if (options.skip_freebsd and case.target.query.os_tag == .freebsd) continue;
if (options.skip_netbsd and case.target.query.os_tag == .netbsd) continue;
if (options.skip_windows and case.target.query.os_tag == .windows) continue;

View file

@ -10,6 +10,7 @@ pub const Options = struct {
optimize_modes: []const std.builtin.OptimizeMode,
test_filters: []const []const u8,
test_target_filters: []const []const u8,
skip_wasm: bool,
max_rss: usize,
};
@ -41,6 +42,8 @@ pub fn addLibcTestCase(
}
pub fn addTarget(libc: *const Libc, target: std.Build.ResolvedTarget) void {
if (libc.options.skip_wasm and target.query.cpu_arch != null and target.query.cpu_arch.?.isWasm()) return;
if (libc.options.test_target_filters.len > 0) {
const triple_txt = target.query.zigTriple(libc.b.allocator) catch @panic("OOM");
for (libc.options.test_target_filters) |filter| {

View file

@ -2246,6 +2246,8 @@ const ModuleTestOptions = struct {
test_default_only: bool,
skip_single_threaded: bool,
skip_non_native: bool,
skip_spirv: bool,
skip_wasm: bool,
skip_freebsd: bool,
skip_netbsd: bool,
skip_windows: bool,
@ -2281,6 +2283,9 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step {
if (options.skip_non_native and !test_target.target.isNative())
continue;
if (options.skip_spirv and test_target.target.cpu_arch != null and test_target.target.cpu_arch.?.isSpirV()) continue;
if (options.skip_wasm and test_target.target.cpu_arch != null and test_target.target.cpu_arch.?.isWasm()) continue;
if (options.skip_freebsd and test_target.target.os_tag == .freebsd) continue;
if (options.skip_netbsd and test_target.target.os_tag == .netbsd) continue;
if (options.skip_windows and test_target.target.os_tag == .windows) continue;
@ -2339,8 +2344,11 @@ fn addOneModuleTest(
const libc_suffix = if (test_target.link_libc == true) "-libc" else "";
const model_txt = target.cpu.model.name;
// wasm32-wasi builds need more RAM, idk why
const max_rss = if (target.os.tag == .wasi)
// These emulated targets need a lot more RAM for unknown reasons.
const max_rss = if (mem.eql(u8, options.name, "std") and
(target.cpu.arch == .hexagon or
(target.cpu.arch.isRISCV() and !resolved_target.query.isNative()) or
target.cpu.arch.isWasm()))
options.max_rss * 2
else
options.max_rss;
@ -2519,6 +2527,7 @@ pub fn wouldUseLlvm(use_llvm: ?bool, query: std.Target.Query, optimize_mode: Opt
const CAbiTestOptions = struct {
test_target_filters: []const []const u8,
skip_non_native: bool,
skip_wasm: bool,
skip_freebsd: bool,
skip_netbsd: bool,
skip_windows: bool,
@ -2526,6 +2535,7 @@ const CAbiTestOptions = struct {
skip_linux: bool,
skip_llvm: bool,
skip_release: bool,
max_rss: usize = 0,
};
pub fn addCAbiTests(b: *std.Build, options: CAbiTestOptions) *Step {
@ -2538,6 +2548,9 @@ pub fn addCAbiTests(b: *std.Build, options: CAbiTestOptions) *Step {
for (c_abi_targets) |c_abi_target| {
if (options.skip_non_native and !c_abi_target.target.isNative()) continue;
if (options.skip_wasm and c_abi_target.target.cpu_arch != null and c_abi_target.target.cpu_arch.?.isWasm()) continue;
if (options.skip_freebsd and c_abi_target.target.os_tag == .freebsd) continue;
if (options.skip_netbsd and c_abi_target.target.os_tag == .netbsd) continue;
if (options.skip_windows and c_abi_target.target.os_tag == .windows) continue;
@ -2595,6 +2608,7 @@ pub fn addCAbiTests(b: *std.Build, options: CAbiTestOptions) *Step {
.root_module = test_mod,
.use_llvm = c_abi_target.use_llvm,
.use_lld = c_abi_target.use_lld,
.max_rss = options.max_rss,
});
// This test is intentionally trying to check if the external ABI is