make self-hosted the default compiler

stage1 is available behind the -fstage1 flag.

closes #89
This commit is contained in:
Andrew Kelley 2022-08-07 23:10:57 -07:00
parent 73bbd1069a
commit 507aae4a1a
25 changed files with 175 additions and 212 deletions

View file

@ -12,7 +12,7 @@ if(NOT CMAKE_BUILD_TYPE)
endif() endif()
if(NOT CMAKE_INSTALL_PREFIX) if(NOT CMAKE_INSTALL_PREFIX)
set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/stage1" CACHE STRING set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/stage2" CACHE STRING
"Directory to install zig to" FORCE) "Directory to install zig to" FORCE)
endif() endif()

View file

@ -64,9 +64,9 @@ pub fn build(b: *Builder) !void {
const only_install_lib_files = b.option(bool, "lib-files-only", "Only install library files") orelse false; const only_install_lib_files = b.option(bool, "lib-files-only", "Only install library files") orelse false;
const is_stage1 = b.option(bool, "stage1", "Build the stage1 compiler, put stage2 behind a feature flag") orelse false; const have_stage1 = b.option(bool, "enable-stage1", "Include the stage1 compiler behind a feature flag") orelse false;
const static_llvm = b.option(bool, "static-llvm", "Disable integration with system-installed LLVM, Clang, LLD, and libc++") orelse false; const static_llvm = b.option(bool, "static-llvm", "Disable integration with system-installed LLVM, Clang, LLD, and libc++") orelse false;
const enable_llvm = b.option(bool, "enable-llvm", "Build self-hosted compiler with LLVM backend enabled") orelse (is_stage1 or static_llvm); const enable_llvm = b.option(bool, "enable-llvm", "Build self-hosted compiler with LLVM backend enabled") orelse (have_stage1 or static_llvm);
const llvm_has_m68k = b.option( const llvm_has_m68k = b.option(
bool, bool,
"llvm-has-m68k", "llvm-has-m68k",
@ -136,7 +136,7 @@ pub fn build(b: *Builder) !void {
}; };
const main_file: ?[]const u8 = mf: { const main_file: ?[]const u8 = mf: {
if (!is_stage1) break :mf "src/main.zig"; if (!have_stage1) break :mf "src/main.zig";
if (use_zig0) break :mf null; if (use_zig0) break :mf null;
break :mf "src/stage1.zig"; break :mf "src/stage1.zig";
}; };
@ -247,7 +247,7 @@ pub fn build(b: *Builder) !void {
} }
}; };
if (is_stage1) { if (have_stage1) {
const softfloat = b.addStaticLibrary("softfloat", null); const softfloat = b.addStaticLibrary("softfloat", null);
softfloat.setBuildMode(.ReleaseFast); softfloat.setBuildMode(.ReleaseFast);
softfloat.setTarget(target); softfloat.setTarget(target);
@ -359,7 +359,7 @@ pub fn build(b: *Builder) !void {
exe_options.addOption(bool, "enable_tracy_callstack", tracy_callstack); exe_options.addOption(bool, "enable_tracy_callstack", tracy_callstack);
exe_options.addOption(bool, "enable_tracy_allocation", tracy_allocation); exe_options.addOption(bool, "enable_tracy_allocation", tracy_allocation);
exe_options.addOption(bool, "value_tracing", value_tracing); exe_options.addOption(bool, "value_tracing", value_tracing);
exe_options.addOption(bool, "is_stage1", is_stage1); exe_options.addOption(bool, "have_stage1", have_stage1);
if (tracy) |tracy_path| { if (tracy) |tracy_path| {
const client_cpp = fs.path.join( const client_cpp = fs.path.join(
b.allocator, b.allocator,
@ -394,7 +394,7 @@ pub fn build(b: *Builder) !void {
test_cases_options.addOption(bool, "enable_link_snapshots", enable_link_snapshots); test_cases_options.addOption(bool, "enable_link_snapshots", enable_link_snapshots);
test_cases_options.addOption(bool, "skip_non_native", skip_non_native); test_cases_options.addOption(bool, "skip_non_native", skip_non_native);
test_cases_options.addOption(bool, "skip_stage1", skip_stage1); test_cases_options.addOption(bool, "skip_stage1", skip_stage1);
test_cases_options.addOption(bool, "is_stage1", is_stage1); test_cases_options.addOption(bool, "have_stage1", have_stage1);
test_cases_options.addOption(bool, "have_llvm", enable_llvm); test_cases_options.addOption(bool, "have_llvm", enable_llvm);
test_cases_options.addOption(bool, "llvm_has_m68k", llvm_has_m68k); test_cases_options.addOption(bool, "llvm_has_m68k", llvm_has_m68k);
test_cases_options.addOption(bool, "llvm_has_csky", llvm_has_csky); test_cases_options.addOption(bool, "llvm_has_csky", llvm_has_csky);
@ -455,7 +455,6 @@ pub fn build(b: *Builder) !void {
skip_libc, skip_libc,
skip_stage1, skip_stage1,
false, false,
is_stage1,
)); ));
toolchain_step.dependOn(tests.addPkgTests( toolchain_step.dependOn(tests.addPkgTests(
@ -470,7 +469,6 @@ pub fn build(b: *Builder) !void {
true, // skip_libc true, // skip_libc
skip_stage1, skip_stage1,
true, // TODO get these all passing true, // TODO get these all passing
is_stage1,
)); ));
toolchain_step.dependOn(tests.addPkgTests( toolchain_step.dependOn(tests.addPkgTests(
@ -485,7 +483,6 @@ pub fn build(b: *Builder) !void {
true, // skip_libc true, // skip_libc
skip_stage1, skip_stage1,
true, // TODO get these all passing true, // TODO get these all passing
is_stage1,
)); ));
toolchain_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes)); toolchain_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes));
@ -525,7 +522,6 @@ pub fn build(b: *Builder) !void {
skip_libc, skip_libc,
skip_stage1, skip_stage1,
true, // TODO get these all passing true, // TODO get these all passing
is_stage1,
); );
const test_step = b.step("test", "Run all the tests"); const test_step = b.step("test", "Run all the tests");

View file

@ -37,9 +37,9 @@ pub fn build(b: *Builder) !void {
const docs_step = b.step("docs", "Build documentation"); const docs_step = b.step("docs", "Build documentation");
docs_step.dependOn(&docgen_cmd.step); docs_step.dependOn(&docgen_cmd.step);
const is_stage1 = b.option(bool, "stage1", "Build the stage1 compiler, put stage2 behind a feature flag") orelse false; const have_stage1 = b.option(bool, "enable-stage1", "Include the stage1 compiler behind a feature flag") orelse false;
const static_llvm = b.option(bool, "static-llvm", "Disable integration with system-installed LLVM, Clang, LLD, and libc++") orelse false; const static_llvm = b.option(bool, "static-llvm", "Disable integration with system-installed LLVM, Clang, LLD, and libc++") orelse false;
const enable_llvm = b.option(bool, "enable-llvm", "Build self-hosted compiler with LLVM backend enabled") orelse (is_stage1 or static_llvm); const enable_llvm = b.option(bool, "enable-llvm", "Build self-hosted compiler with LLVM backend enabled") orelse (have_stage1 or static_llvm);
const llvm_has_m68k = b.option( const llvm_has_m68k = b.option(
bool, bool,
"llvm-has-m68k", "llvm-has-m68k",
@ -101,7 +101,7 @@ pub fn build(b: *Builder) !void {
break :blk 4; break :blk 4;
}; };
const main_file: ?[]const u8 = if (is_stage1) null else "src/main.zig"; const main_file: ?[]const u8 = if (have_stage1) null else "src/main.zig";
const exe = b.addExecutable("zig", main_file); const exe = b.addExecutable("zig", main_file);
exe.strip = strip; exe.strip = strip;
@ -190,7 +190,7 @@ pub fn build(b: *Builder) !void {
if (enable_llvm) { if (enable_llvm) {
const cmake_cfg = if (static_llvm) null else findAndParseConfigH(b, config_h_path_option); const cmake_cfg = if (static_llvm) null else findAndParseConfigH(b, config_h_path_option);
if (is_stage1) { if (have_stage1) {
const softfloat = b.addStaticLibrary("softfloat", null); const softfloat = b.addStaticLibrary("softfloat", null);
softfloat.setBuildMode(.ReleaseFast); softfloat.setBuildMode(.ReleaseFast);
softfloat.setTarget(target); softfloat.setTarget(target);
@ -298,7 +298,7 @@ pub fn build(b: *Builder) !void {
exe_options.addOption(bool, "enable_tracy_callstack", tracy_callstack); exe_options.addOption(bool, "enable_tracy_callstack", tracy_callstack);
exe_options.addOption(bool, "enable_tracy_allocation", tracy_allocation); exe_options.addOption(bool, "enable_tracy_allocation", tracy_allocation);
exe_options.addOption(bool, "value_tracing", value_tracing); exe_options.addOption(bool, "value_tracing", value_tracing);
exe_options.addOption(bool, "is_stage1", is_stage1); exe_options.addOption(bool, "have_stage1", have_stage1);
if (tracy) |tracy_path| { if (tracy) |tracy_path| {
const client_cpp = fs.path.join( const client_cpp = fs.path.join(
b.allocator, b.allocator,

View file

@ -34,7 +34,7 @@ git fetch --tags
mkdir build mkdir build
cd build cd build
cmake .. \ cmake .. \
-DCMAKE_INSTALL_PREFIX="$(pwd)/release" \ -DCMAKE_INSTALL_PREFIX="$(pwd)/stage2" \
-DCMAKE_PREFIX_PATH="$PREFIX" \ -DCMAKE_PREFIX_PATH="$PREFIX" \
-DCMAKE_BUILD_TYPE=Release \ -DCMAKE_BUILD_TYPE=Release \
-DZIG_TARGET_TRIPLE="$TARGET" \ -DZIG_TARGET_TRIPLE="$TARGET" \
@ -52,23 +52,18 @@ make $JOBS install
# Here we rebuild zig but this time using the Zig binary we just now produced to # Here we rebuild zig but this time using the Zig binary we just now produced to
# build zig1.o rather than relying on the one built with stage0. See # build zig1.o rather than relying on the one built with stage0. See
# https://github.com/ziglang/zig/issues/6830 for more details. # https://github.com/ziglang/zig/issues/6830 for more details.
cmake .. -DZIG_EXECUTABLE="$(pwd)/release/bin/zig" cmake .. -DZIG_EXECUTABLE="$(pwd)/stage2/bin/zig"
make $JOBS install make $JOBS install
# Build stage2 standalone so that we can test stage2 against stage2 compiler-rt. stage2/bin/zig build -p release -Denable-llvm -Denable-stage1
release/bin/zig build -p stage2 -Denable-llvm
stage2/bin/zig build test-behavior
# TODO: upgrade these to test stage2 instead of stage1
# TODO: upgrade these to test stage3 instead of stage2
release/bin/zig build test-behavior -Denable-macos-sdk -Domit-stage2
release/bin/zig build test-compiler-rt -Denable-macos-sdk release/bin/zig build test-compiler-rt -Denable-macos-sdk
release/bin/zig build test-behavior -Denable-macos-sdk
release/bin/zig build test-std -Denable-macos-sdk release/bin/zig build test-std -Denable-macos-sdk
release/bin/zig build test-universal-libc -Denable-macos-sdk release/bin/zig build test-universal-libc -Denable-macos-sdk
release/bin/zig build test-compare-output -Denable-macos-sdk release/bin/zig build test-compare-output -Denable-macos-sdk
release/bin/zig build test-standalone -Denable-macos-sdk release/bin/zig build test-standalone -Denable-macos-sdk
release/bin/zig build test-stack-traces -Denable-macos-sdk release/bin/zig build test-stack-traces -Denable-macos-sdk -fstage1
release/bin/zig build test-cli -Denable-macos-sdk release/bin/zig build test-cli -Denable-macos-sdk
release/bin/zig build test-asm-link -Denable-macos-sdk release/bin/zig build test-asm-link -Denable-macos-sdk
release/bin/zig build test-translate-c -Denable-macos-sdk release/bin/zig build test-translate-c -Denable-macos-sdk
@ -76,7 +71,7 @@ release/bin/zig build test-run-translated-c -Denable-macos-sdk
release/bin/zig build docs -Denable-macos-sdk release/bin/zig build docs -Denable-macos-sdk
release/bin/zig build test-fmt -Denable-macos-sdk release/bin/zig build test-fmt -Denable-macos-sdk
release/bin/zig build test-cases -Denable-macos-sdk -Dsingle-threaded release/bin/zig build test-cases -Denable-macos-sdk -Dsingle-threaded
release/bin/zig build test-link -Denable-macos-sdk -Domit-stage2 release/bin/zig build test-link -Denable-macos-sdk
if [ "${BUILD_REASON}" != "PullRequest" ]; then if [ "${BUILD_REASON}" != "PullRequest" ]; then
mv ../LICENSE release/ mv ../LICENSE release/

View file

@ -68,9 +68,7 @@ jobs:
& "${ZIGPREFIXPATH}/bin/zig.exe" build ` & "${ZIGPREFIXPATH}/bin/zig.exe" build `
--prefix "$ZIGINSTALLDIR" ` --prefix "$ZIGINSTALLDIR" `
--search-prefix "$ZIGPREFIXPATH" ` --search-prefix "$ZIGPREFIXPATH" `
-Dstage1 ` -Denable-stage1 `
<# stage2 is omitted until we resolve https://github.com/ziglang/zig/issues/6485 #> `
-Domit-stage2 `
-Dstatic-llvm ` -Dstatic-llvm `
-Drelease ` -Drelease `
-Dstrip ` -Dstrip `

View file

@ -38,10 +38,11 @@ cmake .. \
-GNinja -GNinja
samu install samu install
# TODO ld.lld: error: undefined symbol: main # Here we rebuild zig but this time using the Zig binary we just now produced to
# >>> referenced by crt1_c.c:75 (/usr/src/lib/csu/amd64/crt1_c.c:75) # build zig1.o rather than relying on the one built with stage0. This makes it
# >>> /usr/lib/crt1.o:(_start) # a stage3 build rather than a stage2 build.
#release/bin/zig test ../test/behavior.zig -fno-stage1 -fLLVM -I ../test cmake .. -DZIG_EXECUTABLE="$PREFIX/bin/zig"
samu install
# Here we skip some tests to save time. # Here we skip some tests to save time.
release/bin/zig build test -Dskip-stage1 -Dskip-non-native release/bin/zig build test -Dskip-stage1 -Dskip-non-native

View file

@ -33,41 +33,39 @@ unset CXX
ninja install ninja install
STAGE1_ZIG="$DEBUG_STAGING/bin/zig"
# Here we rebuild zig but this time using the Zig binary we just now produced to # Here we rebuild zig but this time using the Zig binary we just now produced to
# build zig1.o rather than relying on the one built with stage0. See # build zig1.o rather than relying on the one built with stage0. See
# https://github.com/ziglang/zig/issues/6830 for more details. # https://github.com/ziglang/zig/issues/6830 for more details.
cmake .. -DZIG_EXECUTABLE="$STAGE1_ZIG" cmake .. -DZIG_EXECUTABLE="$DEBUG_STAGING/bin/zig"
ninja install ninja install
cd $WORKSPACE cd $WORKSPACE
"$DEBUG_STAGING/bin/zig" build -p stage3 -Denable-stage1 -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL"
# simultaneously test building self-hosted without LLVM and with 32-bit arm
stage3/bin/zig build -Dtarget=arm-linux-musleabihf
echo "Looking for non-conforming code formatting..." echo "Looking for non-conforming code formatting..."
echo "Formatting errors can be fixed by running 'zig fmt' on the files printed here." echo "Formatting errors can be fixed by running 'zig fmt' on the files printed here."
$STAGE1_ZIG fmt --check . --exclude test/cases/ stage3/bin/zig fmt --check . --exclude test/cases/
$STAGE1_ZIG build -p stage2 -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL" stage3/bin/zig build test-compiler-rt -fqemu -fwasmtime -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL"
stage2/bin/zig build -p stage3 -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL" stage3/bin/zig build test-behavior -fqemu -fwasmtime -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL"
stage3/bin/zig build # test building self-hosted without LLVM stage3/bin/zig build test-std -fqemu -fwasmtime -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL"
stage3/bin/zig build -Dtarget=arm-linux-musleabihf # test building self-hosted for 32-bit arm stage3/bin/zig build test-universal-libc -fqemu -fwasmtime -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL"
stage3/bin/zig build test-compare-output -fqemu -fwasmtime -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL"
stage3/bin/zig build test-compiler-rt -fqemu -fwasmtime -Denable-llvm stage3/bin/zig build test-asm-link -fqemu -fwasmtime -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL"
stage3/bin/zig build test-behavior -fqemu -fwasmtime -Denable-llvm stage3/bin/zig build test-fmt -fqemu -fwasmtime -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL"
stage3/bin/zig build test-std -fqemu -fwasmtime -Denable-llvm stage3/bin/zig build test-translate-c -fqemu -fwasmtime -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL"
stage3/bin/zig build test-universal-libc -fqemu -fwasmtime -Denable-llvm stage3/bin/zig build test-run-translated-c -fqemu -fwasmtime -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL"
stage3/bin/zig build test-compare-output -fqemu -fwasmtime -Denable-llvm stage3/bin/zig build test-standalone -fqemu -fwasmtime -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL"
stage3/bin/zig build test-asm-link -fqemu -fwasmtime -Denable-llvm stage3/bin/zig build test-cli -fqemu -fwasmtime -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL"
stage3/bin/zig build test-fmt -fqemu -fwasmtime -Denable-llvm
stage3/bin/zig build test-translate-c -fqemu -fwasmtime -Denable-llvm
stage3/bin/zig build test-run-translated-c -fqemu -fwasmtime -Denable-llvm
stage3/bin/zig build test-standalone -fqemu -fwasmtime -Denable-llvm
stage3/bin/zig build test-cli -fqemu -fwasmtime -Denable-llvm
stage3/bin/zig build test-cases -fqemu -fwasmtime -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL" stage3/bin/zig build test-cases -fqemu -fwasmtime -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL"
stage3/bin/zig build test-link -fqemu -fwasmtime -Denable-llvm stage3/bin/zig build test-link -fqemu -fwasmtime -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL"
stage3/bin/zig build docs -fqemu -fwasmtime -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL"
$STAGE1_ZIG build test-stack-traces -fqemu -fwasmtime stage3/bin/zig build test-stack-traces -fqemu -fwasmtime -fstage1
$STAGE1_ZIG build docs -fqemu -fwasmtime
# Produce the experimental std lib documentation. # Produce the experimental std lib documentation.
mkdir -p "$RELEASE_STAGING/docs/std" mkdir -p "$RELEASE_STAGING/docs/std"
@ -87,7 +85,7 @@ stage3/bin/zig build \
-Drelease \ -Drelease \
-Dstrip \ -Dstrip \
-Dtarget="$TARGET" \ -Dtarget="$TARGET" \
-Dstage1 -Denable-stage1
# Explicit exit helps show last command duration. # Explicit exit helps show last command duration.
exit exit

View file

@ -285,6 +285,7 @@ const Code = struct {
link_objects: []const []const u8, link_objects: []const []const u8,
target_str: ?[]const u8, target_str: ?[]const u8,
link_libc: bool, link_libc: bool,
backend_stage1: bool,
link_mode: ?std.builtin.LinkMode, link_mode: ?std.builtin.LinkMode,
disable_cache: bool, disable_cache: bool,
verbose_cimport: bool, verbose_cimport: bool,
@ -554,6 +555,7 @@ fn genToc(allocator: Allocator, tokenizer: *Tokenizer) !Toc {
var link_mode: ?std.builtin.LinkMode = null; var link_mode: ?std.builtin.LinkMode = null;
var disable_cache = false; var disable_cache = false;
var verbose_cimport = false; var verbose_cimport = false;
var backend_stage1 = false;
const source_token = while (true) { const source_token = while (true) {
const content_tok = try eatToken(tokenizer, Token.Id.Content); const content_tok = try eatToken(tokenizer, Token.Id.Content);
@ -586,6 +588,8 @@ fn genToc(allocator: Allocator, tokenizer: *Tokenizer) !Toc {
link_libc = true; link_libc = true;
} else if (mem.eql(u8, end_tag_name, "link_mode_dynamic")) { } else if (mem.eql(u8, end_tag_name, "link_mode_dynamic")) {
link_mode = .Dynamic; link_mode = .Dynamic;
} else if (mem.eql(u8, end_tag_name, "backend_stage1")) {
backend_stage1 = true;
} else if (mem.eql(u8, end_tag_name, "code_end")) { } else if (mem.eql(u8, end_tag_name, "code_end")) {
_ = try eatToken(tokenizer, Token.Id.BracketClose); _ = try eatToken(tokenizer, Token.Id.BracketClose);
break content_tok; break content_tok;
@ -609,6 +613,7 @@ fn genToc(allocator: Allocator, tokenizer: *Tokenizer) !Toc {
.link_objects = link_objects.toOwnedSlice(), .link_objects = link_objects.toOwnedSlice(),
.target_str = target_str, .target_str = target_str,
.link_libc = link_libc, .link_libc = link_libc,
.backend_stage1 = backend_stage1,
.link_mode = link_mode, .link_mode = link_mode,
.disable_cache = disable_cache, .disable_cache = disable_cache,
.verbose_cimport = verbose_cimport, .verbose_cimport = verbose_cimport,
@ -1187,6 +1192,9 @@ fn printShell(out: anytype, shell_content: []const u8) !void {
try out.writeAll("</samp></pre></figure>"); try out.writeAll("</samp></pre></figure>");
} }
// Override this to skip to later tests
const debug_start_line = 0;
fn genHtml( fn genHtml(
allocator: Allocator, allocator: Allocator,
tokenizer: *Tokenizer, tokenizer: *Tokenizer,
@ -1266,6 +1274,13 @@ fn genHtml(
continue; continue;
} }
if (debug_start_line > 0) {
const loc = tokenizer.getTokenLocation(code.source_token);
if (debug_start_line > loc.line) {
continue;
}
}
const raw_source = tokenizer.buffer[code.source_token.start..code.source_token.end]; const raw_source = tokenizer.buffer[code.source_token.start..code.source_token.end];
const trimmed_raw_source = mem.trim(u8, raw_source, " \n"); const trimmed_raw_source = mem.trim(u8, raw_source, " \n");
const tmp_source_file_name = try fs.path.join( const tmp_source_file_name = try fs.path.join(
@ -1311,6 +1326,10 @@ fn genHtml(
try build_args.append("-lc"); try build_args.append("-lc");
try shell_out.print("-lc ", .{}); try shell_out.print("-lc ", .{});
} }
if (code.backend_stage1) {
try build_args.append("-fstage1");
try shell_out.print("-fstage1", .{});
}
const target = try std.zig.CrossTarget.parse(.{ const target = try std.zig.CrossTarget.parse(.{
.arch_os_abi = code.target_str orelse "native", .arch_os_abi = code.target_str orelse "native",
}); });
@ -1443,6 +1462,10 @@ fn genHtml(
try test_args.append("-lc"); try test_args.append("-lc");
try shell_out.print("-lc ", .{}); try shell_out.print("-lc ", .{});
} }
if (code.backend_stage1) {
try test_args.append("-fstage1");
try shell_out.print("-fstage1", .{});
}
if (code.target_str) |triple| { if (code.target_str) |triple| {
try test_args.appendSlice(&[_][]const u8{ "-target", triple }); try test_args.appendSlice(&[_][]const u8{ "-target", triple });
try shell_out.print("-target {s} ", .{triple}); try shell_out.print("-target {s} ", .{triple});
@ -1490,6 +1513,14 @@ fn genHtml(
try shell_out.print("-O {s} ", .{@tagName(code.mode)}); try shell_out.print("-O {s} ", .{@tagName(code.mode)});
}, },
} }
if (code.link_libc) {
try test_args.append("-lc");
try shell_out.print("-lc ", .{});
}
if (code.backend_stage1) {
try test_args.append("-fstage1");
try shell_out.print("-fstage1", .{});
}
const result = try ChildProcess.exec(.{ const result = try ChildProcess.exec(.{
.allocator = allocator, .allocator = allocator,
.argv = test_args.items, .argv = test_args.items,

View file

@ -1188,6 +1188,7 @@ test "this will be skipped" {
(The evented IO mode is enabled using the <kbd>--test-evented-io</kbd> command line parameter.) (The evented IO mode is enabled using the <kbd>--test-evented-io</kbd> command line parameter.)
</p> </p>
{#code_begin|test|async_skip#} {#code_begin|test|async_skip#}
{#backend_stage1#}
const std = @import("std"); const std = @import("std");
test "async skip test" { test "async skip test" {
@ -2768,7 +2769,7 @@ test "comptime @intToPtr" {
} }
} }
{#code_end#} {#code_end#}
{#see_also|Optional Pointers|@intToPtr|@ptrToInt|C Pointers|Pointers to Zero Bit Types#} {#see_also|Optional Pointers|@intToPtr|@ptrToInt|C Pointers#}
{#header_open|volatile#} {#header_open|volatile#}
<p>Loads and stores are assumed to not have side effects. If a given load or store <p>Loads and stores are assumed to not have side effects. If a given load or store
should have side effects, such as Memory Mapped Input/Output (MMIO), use {#syntax#}volatile{#endsyntax#}. should have side effects, such as Memory Mapped Input/Output (MMIO), use {#syntax#}volatile{#endsyntax#}.
@ -2862,12 +2863,15 @@ var foo: u8 align(4) = 100;
test "global variable alignment" { test "global variable alignment" {
try expect(@typeInfo(@TypeOf(&foo)).Pointer.alignment == 4); try expect(@typeInfo(@TypeOf(&foo)).Pointer.alignment == 4);
try expect(@TypeOf(&foo) == *align(4) u8); try expect(@TypeOf(&foo) == *align(4) u8);
const as_pointer_to_array: *[1]u8 = &foo; const as_pointer_to_array: *align(4) [1]u8 = &foo;
const as_slice: []u8 = as_pointer_to_array; const as_slice: []align(4) u8 = as_pointer_to_array;
try expect(@TypeOf(as_slice) == []align(4) u8); const as_unaligned_slice: []u8 = as_slice;
try expect(as_unaligned_slice[0] == 100);
} }
fn derp() align(@sizeOf(usize) * 2) i32 { return 1234; } fn derp() align(@sizeOf(usize) * 2) i32 {
return 1234;
}
fn noop1() align(1) void {} fn noop1() align(1) void {}
fn noop4() align(4) void {} fn noop4() align(4) void {}
@ -3336,6 +3340,7 @@ fn doTheTest() !void {
Zig allows the address to be taken of a non-byte-aligned field: Zig allows the address to be taken of a non-byte-aligned field:
</p> </p>
{#code_begin|test|pointer_to_non-byte_aligned_field#} {#code_begin|test|pointer_to_non-byte_aligned_field#}
{#backend_stage1#}
const std = @import("std"); const std = @import("std");
const expect = std.testing.expect; const expect = std.testing.expect;
@ -3391,7 +3396,8 @@ fn bar(x: *const u3) u3 {
<p> <p>
Pointers to non-ABI-aligned fields share the same address as the other fields within their host integer: Pointers to non-ABI-aligned fields share the same address as the other fields within their host integer:
</p> </p>
{#code_begin|test|pointer_to_non-bit_aligned_field#} {#code_begin|test|packed_struct_field_addrs#}
{#backend_stage1#}
const std = @import("std"); const std = @import("std");
const expect = std.testing.expect; const expect = std.testing.expect;
@ -3407,7 +3413,7 @@ var bit_field = BitField{
.c = 3, .c = 3,
}; };
test "pointer to non-bit-aligned field" { test "pointers of sub-byte-aligned fields share addresses" {
try expect(@ptrToInt(&bit_field.a) == @ptrToInt(&bit_field.b)); try expect(@ptrToInt(&bit_field.a) == @ptrToInt(&bit_field.b));
try expect(@ptrToInt(&bit_field.a) == @ptrToInt(&bit_field.c)); try expect(@ptrToInt(&bit_field.a) == @ptrToInt(&bit_field.c));
} }
@ -3438,20 +3444,22 @@ test "pointer to non-bit-aligned field" {
} }
{#code_end#} {#code_end#}
<p> <p>
Packed structs have 1-byte alignment. However if you have an overaligned pointer to a packed struct, Packed structs have the same alignment as their backing integer, however, overaligned
Zig should correctly understand the alignment of fields. However there is pointers to packed structs can override this:
<a href="https://github.com/ziglang/zig/issues/1994">a bug</a>:
</p> </p>
{#code_begin|test_err|expected type '*u32', found '*align(1) u32'#} {#code_begin|test|overaligned_packed_struct#}
const std = @import("std");
const expect = std.testing.expect;
const S = packed struct { const S = packed struct {
a: u32, a: u32,
b: u32, b: u32,
}; };
test "overaligned pointer to packed struct" { test "overaligned pointer to packed struct" {
var foo: S align(4) = undefined; var foo: S align(4) = .{ .a = 1, .b = 2 };
const ptr: *align(4) S = &foo; const ptr: *align(4) S = &foo;
const ptr_to_b: *u32 = &ptr.b; const ptr_to_b: *u32 = &ptr.b;
_ = ptr_to_b; try expect(ptr_to_b.* == 2);
} }
{#code_end#} {#code_end#}
<p>When this bug is fixed, the above test in the documentation will unexpectedly pass, which will <p>When this bug is fixed, the above test in the documentation will unexpectedly pass, which will
@ -3698,7 +3706,7 @@ test "@tagName" {
<p> <p>
By default, enums are not guaranteed to be compatible with the C ABI: By default, enums are not guaranteed to be compatible with the C ABI:
</p> </p>
{#code_begin|obj_err|parameter of type 'Foo' not allowed in function with calling convention 'C'#} {#code_begin|obj_err|parameter of type 'test.Foo' not allowed in function with calling convention 'C'#}
const Foo = enum { a, b, c }; const Foo = enum { a, b, c };
export fn entry(foo: Foo) void { _ = foo; } export fn entry(foo: Foo) void { _ = foo; }
{#code_end#} {#code_end#}
@ -4004,7 +4012,7 @@ fn makeNumber() Number {
This is typically used for type safety when interacting with C code that does not expose struct details. This is typically used for type safety when interacting with C code that does not expose struct details.
Example: Example:
</p> </p>
{#code_begin|test_err|expected type '*Derp', found '*Wat'#} {#code_begin|test_err|expected type '*test.Derp', found '*test.Wat'#}
const Derp = opaque {}; const Derp = opaque {};
const Wat = opaque {}; const Wat = opaque {};
@ -4203,7 +4211,7 @@ test "switch on tagged union" {
When a {#syntax#}switch{#endsyntax#} expression does not have an {#syntax#}else{#endsyntax#} clause, When a {#syntax#}switch{#endsyntax#} expression does not have an {#syntax#}else{#endsyntax#} clause,
it must exhaustively list all the possible values. Failure to do so is a compile error: it must exhaustively list all the possible values. Failure to do so is a compile error:
</p> </p>
{#code_begin|test_err|not handled in switch#} {#code_begin|test_err|unhandled enumeration value#}
const Color = enum { const Color = enum {
auto, auto,
off, off,
@ -5026,17 +5034,9 @@ test "function" {
try expect(do_op(sub2, 5, 6) == -1); try expect(do_op(sub2, 5, 6) == -1);
} }
{#code_end#} {#code_end#}
<p>Function values are like pointers:</p> <p>There is a difference between a function <em>body</em> and a function <em>pointer</em>.
{#code_begin|obj#} Function bodies are {#link|comptime#}-only types while function {#link|Pointers#} may be
const assert = @import("std").debug.assert; runtime-known.</p>
comptime {
assert(@TypeOf(foo) == fn()void);
assert(@sizeOf(fn()void) == @sizeOf(?fn()void));
}
fn foo() void { }
{#code_end#}
{#header_open|Pass-by-value Parameters#} {#header_open|Pass-by-value Parameters#}
<p> <p>
Primitive types such as {#link|Integers#} and {#link|Floats#} passed as parameters Primitive types such as {#link|Integers#} and {#link|Floats#} passed as parameters
@ -6127,6 +6127,7 @@ test "float widening" {
<li>Cast {#syntax#}5{#endsyntax#} to {#syntax#}comptime_float{#endsyntax#} resulting in {#syntax#}@as(comptime_float, 10.8){#endsyntax#}, which is casted to {#syntax#}@as(f32, 10.8){#endsyntax#}</li> <li>Cast {#syntax#}5{#endsyntax#} to {#syntax#}comptime_float{#endsyntax#} resulting in {#syntax#}@as(comptime_float, 10.8){#endsyntax#}, which is casted to {#syntax#}@as(f32, 10.8){#endsyntax#}</li>
</ul> </ul>
{#code_begin|test_err#} {#code_begin|test_err#}
{#backend_stage1#}
// Compile time coercion of float to int // Compile time coercion of float to int
test "implicit cast to comptime_int" { test "implicit cast to comptime_int" {
var f: f32 = 54.0 / 5; var f: f32 = 54.0 / 5;
@ -6302,19 +6303,6 @@ test "coercion between unions and enums" {
{#code_end#} {#code_end#}
{#see_also|union|enum#} {#see_also|union|enum#}
{#header_close#} {#header_close#}
{#header_open|Type Coercion: Zero Bit Types#}
<p>{#link|Zero Bit Types#} may be coerced to single-item {#link|Pointers#},
regardless of const.</p>
<p>TODO document the reasoning for this</p>
<p>TODO document whether vice versa should work and why</p>
{#code_begin|test|coerce_zero_bit_types#}
test "coercion of zero bit types" {
var x: void = {};
var y: *void = x;
_ = y;
}
{#code_end#}
{#header_close#}
{#header_open|Type Coercion: undefined#} {#header_open|Type Coercion: undefined#}
<p>{#link|undefined#} can be cast to any type.</p> <p>{#link|undefined#} can be cast to any type.</p>
{#header_close#} {#header_close#}
@ -6467,7 +6455,6 @@ test "peer type resolution: *const T and ?*T" {
<li>An {#link|enum#} with only 1 tag.</li> <li>An {#link|enum#} with only 1 tag.</li>
<li>A {#link|struct#} with all fields being zero bit types.</li> <li>A {#link|struct#} with all fields being zero bit types.</li>
<li>A {#link|union#} with only 1 field which is a zero bit type.</li> <li>A {#link|union#} with only 1 field which is a zero bit type.</li>
<li>{#link|Pointers to Zero Bit Types#} are themselves zero bit types.</li>
</ul> </ul>
<p> <p>
These types can only ever have one possible value, and thus These types can only ever have one possible value, and thus
@ -6527,7 +6514,7 @@ test "turn HashMap into a set with void" {
<p> <p>
Expressions of type {#syntax#}void{#endsyntax#} are the only ones whose value can be ignored. For example: Expressions of type {#syntax#}void{#endsyntax#} are the only ones whose value can be ignored. For example:
</p> </p>
{#code_begin|test_err|expression value is ignored#} {#code_begin|test_err|ignored#}
test "ignoring expression value" { test "ignoring expression value" {
foo(); foo();
} }
@ -6553,37 +6540,6 @@ fn foo() i32 {
} }
{#code_end#} {#code_end#}
{#header_close#} {#header_close#}
{#header_open|Pointers to Zero Bit Types#}
<p>Pointers to zero bit types also have zero bits. They always compare equal to each other:</p>
{#code_begin|test|pointers_to_zero_bits#}
const std = @import("std");
const expect = std.testing.expect;
test "pointer to empty struct" {
const Empty = struct {};
var a = Empty{};
var b = Empty{};
var ptr_a = &a;
var ptr_b = &b;
comptime try expect(ptr_a == ptr_b);
}
{#code_end#}
<p>The type being pointed to can only ever be one value; therefore loads and stores are
never generated. {#link|ptrToInt#} and {#link|intToPtr#} are not allowed:</p>
{#code_begin|test_err#}
const Empty = struct {};
test "@ptrToInt for pointer to zero bit type" {
var a = Empty{};
_ = @ptrToInt(&a);
}
test "@intToPtr for pointer to zero bit type" {
_ = @intToPtr(*Empty, 0x1);
}
{#code_end#}
{#header_close#}
{#header_close#} {#header_close#}
{#header_open|Result Location Semantics#} {#header_open|Result Location Semantics#}
@ -6666,7 +6622,7 @@ fn gimmeTheBiggerInteger(a: u64, b: u64) u64 {
<p> <p>
For example, if we were to introduce another function to the above snippet: For example, if we were to introduce another function to the above snippet:
</p> </p>
{#code_begin|test_err|values of type 'type' must be comptime known#} {#code_begin|test_err|value with comptime only type 'type' depends on runtime control flow#}
fn max(comptime T: type, a: T, b: T) T { fn max(comptime T: type, a: T, b: T) T {
return if (a > b) a else b; return if (a > b) a else b;
} }
@ -6692,7 +6648,7 @@ fn foo(condition: bool) void {
<p> <p>
For example: For example:
</p> </p>
{#code_begin|test_err|operator not allowed for type 'bool'#} {#code_begin|test_err|operator > not allowed for type 'bool'#}
fn max(comptime T: type, a: T, b: T) T { fn max(comptime T: type, a: T, b: T) T {
return if (a > b) a else b; return if (a > b) a else b;
} }
@ -6837,7 +6793,7 @@ fn performFn(start_value: i32) i32 {
use a {#syntax#}comptime{#endsyntax#} expression to guarantee that the expression will be evaluated at compile-time. use a {#syntax#}comptime{#endsyntax#} expression to guarantee that the expression will be evaluated at compile-time.
If this cannot be accomplished, the compiler will emit an error. For example: If this cannot be accomplished, the compiler will emit an error. For example:
</p> </p>
{#code_begin|test_err|unable to evaluate constant expression#} {#code_begin|test_err|comptime call of extern function#}
extern fn exit() noreturn; extern fn exit() noreturn;
test "foo" { test "foo" {
@ -6889,7 +6845,7 @@ test "fibonacci" {
<p> <p>
Imagine if we had forgotten the base case of the recursive function and tried to run the tests: Imagine if we had forgotten the base case of the recursive function and tried to run the tests:
</p> </p>
{#code_begin|test_err|operation caused overflow#} {#code_begin|test_err|overflow of integer type#}
const expect = @import("std").testing.expect; const expect = @import("std").testing.expect;
fn fibonacci(index: u32) u32 { fn fibonacci(index: u32) u32 {
@ -6913,7 +6869,8 @@ test "fibonacci" {
But what would have happened if we used a signed integer? But what would have happened if we used a signed integer?
</p> </p>
{#code_begin|test_err|evaluation exceeded 1000 backwards branches#} {#code_begin|test_err|evaluation exceeded 1000 backwards branches#}
const expect = @import("std").testing.expect; {#backend_stage1#}
const assert = @import("std").debug.assert;
fn fibonacci(index: i32) i32 { fn fibonacci(index: i32) i32 {
//if (index < 2) return index; //if (index < 2) return index;
@ -6922,7 +6879,7 @@ fn fibonacci(index: i32) i32 {
test "fibonacci" { test "fibonacci" {
comptime { comptime {
try expect(fibonacci(7) == 13); try assert(fibonacci(7) == 13);
} }
} }
{#code_end#} {#code_end#}
@ -6935,8 +6892,8 @@ test "fibonacci" {
<p> <p>
What if we fix the base case, but put the wrong value in the {#syntax#}expect{#endsyntax#} line? What if we fix the base case, but put the wrong value in the {#syntax#}expect{#endsyntax#} line?
</p> </p>
{#code_begin|test_err|test "fibonacci"... FAIL (TestUnexpectedResult)#} {#code_begin|test_err|reached unreachable#}
const expect = @import("std").testing.expect; const assert = @import("std").debug.assert;
fn fibonacci(index: i32) i32 { fn fibonacci(index: i32) i32 {
if (index < 2) return index; if (index < 2) return index;
@ -6945,16 +6902,10 @@ fn fibonacci(index: i32) i32 {
test "fibonacci" { test "fibonacci" {
comptime { comptime {
try expect(fibonacci(7) == 99999); try assert(fibonacci(7) == 99999);
} }
} }
{#code_end#} {#code_end#}
<p>
What happened is Zig started interpreting the {#syntax#}expect{#endsyntax#} function with the
parameter {#syntax#}ok{#endsyntax#} set to {#syntax#}false{#endsyntax#}. When the interpreter hit
{#syntax#}@panic{#endsyntax#} it emitted a compile error because a panic during compile
causes a compile error if it is detected at compile-time.
</p>
<p> <p>
At container level (outside of any function), all expressions are implicitly At container level (outside of any function), all expressions are implicitly
@ -7280,6 +7231,7 @@ pub fn main() void {
</p> </p>
{#code_begin|exe#} {#code_begin|exe#}
{#target_linux_x86_64#} {#target_linux_x86_64#}
{#backend_stage1#}
pub fn main() noreturn { pub fn main() noreturn {
const msg = "hello world\n"; const msg = "hello world\n";
_ = syscall3(SYS_write, STDOUT_FILENO, @ptrToInt(msg), msg.len); _ = syscall3(SYS_write, STDOUT_FILENO, @ptrToInt(msg), msg.len);
@ -7497,6 +7449,7 @@ test "global assembly" {
or resumer (in the case of subsequent suspensions). or resumer (in the case of subsequent suspensions).
</p> </p>
{#code_begin|test|suspend_no_resume#} {#code_begin|test|suspend_no_resume#}
{#backend_stage1#}
const std = @import("std"); const std = @import("std");
const expect = std.testing.expect; const expect = std.testing.expect;
@ -7524,6 +7477,7 @@ fn func() void {
{#link|@frame#} provides access to the async function frame pointer. {#link|@frame#} provides access to the async function frame pointer.
</p> </p>
{#code_begin|test|async_suspend_block#} {#code_begin|test|async_suspend_block#}
{#backend_stage1#}
const std = @import("std"); const std = @import("std");
const expect = std.testing.expect; const expect = std.testing.expect;
@ -7562,6 +7516,7 @@ fn testSuspendBlock() void {
never returns to its resumer and continues executing. never returns to its resumer and continues executing.
</p> </p>
{#code_begin|test|resume_from_suspend#} {#code_begin|test|resume_from_suspend#}
{#backend_stage1#}
const std = @import("std"); const std = @import("std");
const expect = std.testing.expect; const expect = std.testing.expect;
@ -7598,6 +7553,7 @@ fn testResumeFromSuspend(my_result: *i32) void {
and the return value of the async function would be lost. and the return value of the async function would be lost.
</p> </p>
{#code_begin|test|async_await#} {#code_begin|test|async_await#}
{#backend_stage1#}
const std = @import("std"); const std = @import("std");
const expect = std.testing.expect; const expect = std.testing.expect;
@ -7642,6 +7598,7 @@ fn func() void {
return value directly from the target function's frame. return value directly from the target function's frame.
</p> </p>
{#code_begin|test|async_await_sequence#} {#code_begin|test|async_await_sequence#}
{#backend_stage1#}
const std = @import("std"); const std = @import("std");
const expect = std.testing.expect; const expect = std.testing.expect;
@ -7695,6 +7652,7 @@ fn seq(c: u8) void {
{#syntax#}async{#endsyntax#}/{#syntax#}await{#endsyntax#} usage: {#syntax#}async{#endsyntax#}/{#syntax#}await{#endsyntax#} usage:
</p> </p>
{#code_begin|exe|async#} {#code_begin|exe|async#}
{#backend_stage1#}
const std = @import("std"); const std = @import("std");
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
@ -7773,6 +7731,7 @@ fn readFile(allocator: Allocator, filename: []const u8) ![]u8 {
observe the same behavior, with one tiny difference: observe the same behavior, with one tiny difference:
</p> </p>
{#code_begin|exe|blocking#} {#code_begin|exe|blocking#}
{#backend_stage1#}
const std = @import("std"); const std = @import("std");
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
@ -7910,6 +7869,7 @@ comptime {
{#syntax#}await{#endsyntax#} will copy the result from {#syntax#}result_ptr{#endsyntax#}. {#syntax#}await{#endsyntax#} will copy the result from {#syntax#}result_ptr{#endsyntax#}.
</p> </p>
{#code_begin|test|async_struct_field_fn_pointer#} {#code_begin|test|async_struct_field_fn_pointer#}
{#backend_stage1#}
const std = @import("std"); const std = @import("std");
const expect = std.testing.expect; const expect = std.testing.expect;
@ -8677,6 +8637,7 @@ test "decl access by string" {
allows one to, for example, heap-allocate an async function frame: allows one to, for example, heap-allocate an async function frame:
</p> </p>
{#code_begin|test|heap_allocated_frame#} {#code_begin|test|heap_allocated_frame#}
{#backend_stage1#}
const std = @import("std"); const std = @import("std");
test "heap allocated frame" { test "heap allocated frame" {
@ -9423,12 +9384,6 @@ const std = @import("std");
const expect = std.testing.expect; const expect = std.testing.expect;
test "vector @reduce" { test "vector @reduce" {
// This test regressed with LLVM 14:
// https://github.com/llvm/llvm-project/issues/55522
// We'll skip this test unless the self-hosted compiler is being used.
// After LLVM 15 is released we can delete this line.
if (@import("builtin").zig_backend == .stage1) return;
const value = @Vector(4, i32){ 1, -1, 1, -1 }; const value = @Vector(4, i32){ 1, -1, 1, -1 };
const result = value > @splat(4, @as(i32, 0)); const result = value > @splat(4, @as(i32, 0));
// result is { true, false, true, false }; // result is { true, false, true, false };
@ -9938,7 +9893,7 @@ pub fn main() void {
{#header_close#} {#header_close#}
{#header_open|Index out of Bounds#} {#header_open|Index out of Bounds#}
<p>At compile-time:</p> <p>At compile-time:</p>
{#code_begin|test_err|index 5 outside array of size 5#} {#code_begin|test_err|index 5 outside array of length 5#}
comptime { comptime {
const array: [5]u8 = "hello".*; const array: [5]u8 = "hello".*;
const garbage = array[5]; const garbage = array[5];
@ -9959,9 +9914,9 @@ fn foo(x: []const u8) u8 {
{#header_close#} {#header_close#}
{#header_open|Cast Negative Number to Unsigned Integer#} {#header_open|Cast Negative Number to Unsigned Integer#}
<p>At compile-time:</p> <p>At compile-time:</p>
{#code_begin|test_err|attempt to cast negative value to unsigned integer#} {#code_begin|test_err|type 'u32' cannot represent integer value '-1'#}
comptime { comptime {
const value: i32 = -1; var value: i32 = -1;
const unsigned = @intCast(u32, value); const unsigned = @intCast(u32, value);
_ = unsigned; _ = unsigned;
} }
@ -9982,7 +9937,7 @@ pub fn main() void {
{#header_close#} {#header_close#}
{#header_open|Cast Truncates Data#} {#header_open|Cast Truncates Data#}
<p>At compile-time:</p> <p>At compile-time:</p>
{#code_begin|test_err|cast from 'u16' to 'u8' truncates bits#} {#code_begin|test_err|type 'u8' cannot represent integer value '300'#}
comptime { comptime {
const spartan_count: u16 = 300; const spartan_count: u16 = 300;
const byte = @intCast(u8, spartan_count); const byte = @intCast(u8, spartan_count);
@ -10017,7 +9972,7 @@ pub fn main() void {
<li>{#link|@divExact#} (division)</li> <li>{#link|@divExact#} (division)</li>
</ul> </ul>
<p>Example with addition at compile-time:</p> <p>Example with addition at compile-time:</p>
{#code_begin|test_err|operation caused overflow#} {#code_begin|test_err|overflow of integer type 'u8' with value '256'#}
comptime { comptime {
var byte: u8 = 255; var byte: u8 = 255;
byte += 1; byte += 1;
@ -10118,6 +10073,7 @@ test "wraparound addition and subtraction" {
{#header_open|Exact Left Shift Overflow#} {#header_open|Exact Left Shift Overflow#}
<p>At compile-time:</p> <p>At compile-time:</p>
{#code_begin|test_err|operation caused overflow#} {#code_begin|test_err|operation caused overflow#}
{#backend_stage1#}
comptime { comptime {
const x = @shlExact(@as(u8, 0b01010101), 2); const x = @shlExact(@as(u8, 0b01010101), 2);
_ = x; _ = x;
@ -10137,6 +10093,7 @@ pub fn main() void {
{#header_open|Exact Right Shift Overflow#} {#header_open|Exact Right Shift Overflow#}
<p>At compile-time:</p> <p>At compile-time:</p>
{#code_begin|test_err|exact shift shifted out 1 bits#} {#code_begin|test_err|exact shift shifted out 1 bits#}
{#backend_stage1#}
comptime { comptime {
const x = @shrExact(@as(u8, 0b10101010), 2); const x = @shrExact(@as(u8, 0b10101010), 2);
_ = x; _ = x;
@ -10200,6 +10157,7 @@ pub fn main() void {
{#header_open|Exact Division Remainder#} {#header_open|Exact Division Remainder#}
<p>At compile-time:</p> <p>At compile-time:</p>
{#code_begin|test_err|exact division had a remainder#} {#code_begin|test_err|exact division had a remainder#}
{#backend_stage1#}
comptime { comptime {
const a: u32 = 10; const a: u32 = 10;
const b: u32 = 3; const b: u32 = 3;
@ -10302,7 +10260,7 @@ fn getNumberOrFail() !i32 {
{#header_close#} {#header_close#}
{#header_open|Invalid Error Code#} {#header_open|Invalid Error Code#}
<p>At compile-time:</p> <p>At compile-time:</p>
{#code_begin|test_err|integer value 11 represents no error#} {#code_begin|test_err|integer value '11' represents no error#}
comptime { comptime {
const err = error.AnError; const err = error.AnError;
const number = @errorToInt(err) + 10; const number = @errorToInt(err) + 10;
@ -10324,7 +10282,7 @@ pub fn main() void {
{#header_close#} {#header_close#}
{#header_open|Invalid Enum Cast#} {#header_open|Invalid Enum Cast#}
<p>At compile-time:</p> <p>At compile-time:</p>
{#code_begin|test_err|has no tag matching integer value 3#} {#code_begin|test_err|enum 'test.Foo' has no tag with value '3'#}
const Foo = enum { const Foo = enum {
a, a,
b, b,
@ -10356,7 +10314,7 @@ pub fn main() void {
{#header_open|Invalid Error Set Cast#} {#header_open|Invalid Error Set Cast#}
<p>At compile-time:</p> <p>At compile-time:</p>
{#code_begin|test_err|error.B not a member of error set 'Set2'#} {#code_begin|test_err|'error.B' not a member of error set 'error{A,C}'#}
const Set1 = error{ const Set1 = error{
A, A,
B, B,
@ -10417,7 +10375,7 @@ fn foo(bytes: []u8) u32 {
{#header_close#} {#header_close#}
{#header_open|Wrong Union Field Access#} {#header_open|Wrong Union Field Access#}
<p>At compile-time:</p> <p>At compile-time:</p>
{#code_begin|test_err|accessing union field 'float' while field 'int' is set#} {#code_begin|test_err|access of union field 'float' while field 'int' is active#}
comptime { comptime {
var f = Foo{ .int = 42 }; var f = Foo{ .int = 42 };
f.float = 12.34; f.float = 12.34;
@ -10509,6 +10467,7 @@ fn bar(f: *Foo) void {
</p> </p>
<p>At compile-time:</p> <p>At compile-time:</p>
{#code_begin|test_err|null pointer casted to type#} {#code_begin|test_err|null pointer casted to type#}
{#backend_stage1#}
comptime { comptime {
const opt_ptr: ?*i32 = null; const opt_ptr: ?*i32 = null;
const ptr = @ptrCast(*i32, opt_ptr); const ptr = @ptrCast(*i32, opt_ptr);
@ -10551,7 +10510,8 @@ const expect = std.testing.expect;
test "using an allocator" { test "using an allocator" {
var buffer: [100]u8 = undefined; var buffer: [100]u8 = undefined;
const allocator = std.heap.FixedBufferAllocator.init(&buffer).allocator(); var fba = std.heap.FixedBufferAllocator.init(&buffer);
const allocator = fba.allocator();
const result = try concat(allocator, "foo", "bar"); const result = try concat(allocator, "foo", "bar");
try expect(std.mem.eql(u8, "foobar", result)); try expect(std.mem.eql(u8, "foobar", result));
} }
@ -10647,7 +10607,7 @@ pub fn main() !void {
<p>String literals such as {#syntax#}"foo"{#endsyntax#} are in the global constant data section. <p>String literals such as {#syntax#}"foo"{#endsyntax#} are in the global constant data section.
This is why it is an error to pass a string literal to a mutable slice, like this: This is why it is an error to pass a string literal to a mutable slice, like this:
</p> </p>
{#code_begin|test_err|cannot cast pointer to array literal to slice type '[]u8'#} {#code_begin|test_err|expected type '[]u8', found '*const [5:0]u8'#}
fn foo(s: []u8) void { fn foo(s: []u8) void {
_ = s; _ = s;
} }

View file

@ -866,7 +866,7 @@ pub fn panicUnwrapError(st: ?*StackTrace, err: anyerror) noreturn {
pub fn panicOutOfBounds(index: usize, len: usize) noreturn { pub fn panicOutOfBounds(index: usize, len: usize) noreturn {
@setCold(true); @setCold(true);
std.debug.panic("attempt to index out of bound: index {d}, len {d}", .{ index, len }); std.debug.panic("index out of bounds: index {d}, len {d}", .{ index, len });
} }
pub noinline fn returnError(st: *StackTrace) void { pub noinline fn returnError(st: *StackTrace) void {

View file

@ -1040,22 +1040,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
const comp = try arena.create(Compilation); const comp = try arena.create(Compilation);
const root_name = try arena.dupeZ(u8, options.root_name); const root_name = try arena.dupeZ(u8, options.root_name);
const use_stage1 = options.use_stage1 orelse blk: { const use_stage1 = options.use_stage1 orelse false;
// Even though we may have no Zig code to compile (depending on `options.main_pkg`),
// we may need to use stage1 for building compiler-rt and other dependencies.
if (options.use_llvm) |use_llvm| {
if (!use_llvm) {
break :blk false;
}
}
// If LLVM does not support the target, then we can't use it.
if (!target_util.hasLlvmSupport(options.target, options.target.ofmt))
break :blk false;
break :blk build_options.is_stage1;
};
const cache_mode = if (use_stage1 and !options.disable_lld_caching) const cache_mode = if (use_stage1 and !options.disable_lld_caching)
CacheMode.whole CacheMode.whole
@ -2211,7 +2196,7 @@ pub fn update(comp: *Compilation) !void {
comp.c_object_work_queue.writeItemAssumeCapacity(key); comp.c_object_work_queue.writeItemAssumeCapacity(key);
} }
const use_stage1 = build_options.is_stage1 and comp.bin_file.options.use_stage1; const use_stage1 = build_options.have_stage1 and comp.bin_file.options.use_stage1;
if (comp.bin_file.options.module) |module| { if (comp.bin_file.options.module) |module| {
module.compile_log_text.shrinkAndFree(module.gpa, 0); module.compile_log_text.shrinkAndFree(module.gpa, 0);
module.generation += 1; module.generation += 1;
@ -2387,7 +2372,7 @@ fn flush(comp: *Compilation, prog_node: *std.Progress.Node) !void {
}; };
comp.link_error_flags = comp.bin_file.errorFlags(); comp.link_error_flags = comp.bin_file.errorFlags();
const use_stage1 = build_options.is_stage1 and comp.bin_file.options.use_stage1; const use_stage1 = build_options.have_stage1 and comp.bin_file.options.use_stage1;
if (!use_stage1) { if (!use_stage1) {
if (comp.bin_file.options.module) |module| { if (comp.bin_file.options.module) |module| {
try link.File.C.flushEmitH(module); try link.File.C.flushEmitH(module);
@ -2845,7 +2830,7 @@ pub fn performAllTheWork(
comp.work_queue_wait_group.reset(); comp.work_queue_wait_group.reset();
defer comp.work_queue_wait_group.wait(); defer comp.work_queue_wait_group.wait();
const use_stage1 = build_options.is_stage1 and comp.bin_file.options.use_stage1; const use_stage1 = build_options.have_stage1 and comp.bin_file.options.use_stage1;
{ {
const astgen_frame = tracy.namedFrame("astgen"); const astgen_frame = tracy.namedFrame("astgen");
@ -3430,7 +3415,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
var man = comp.obtainCObjectCacheManifest(); var man = comp.obtainCObjectCacheManifest();
defer man.deinit(); defer man.deinit();
const use_stage1 = build_options.is_stage1 and comp.bin_file.options.use_stage1; const use_stage1 = build_options.have_stage1 and comp.bin_file.options.use_stage1;
man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects
man.hash.add(use_stage1); man.hash.add(use_stage1);
@ -4745,7 +4730,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca
const target = comp.getTarget(); const target = comp.getTarget();
const generic_arch_name = target.cpu.arch.genericName(); const generic_arch_name = target.cpu.arch.genericName();
const use_stage1 = build_options.is_stage1 and comp.bin_file.options.use_stage1; const use_stage1 = build_options.have_stage1 and comp.bin_file.options.use_stage1;
const zig_backend: std.builtin.CompilerBackend = blk: { const zig_backend: std.builtin.CompilerBackend = blk: {
if (use_stage1) break :blk .stage1; if (use_stage1) break :blk .stage1;
@ -5032,7 +5017,7 @@ fn buildOutputFromZig(
.link_mode = .Static, .link_mode = .Static,
.function_sections = true, .function_sections = true,
.no_builtin = true, .no_builtin = true,
.use_stage1 = build_options.is_stage1 and comp.bin_file.options.use_stage1, .use_stage1 = build_options.have_stage1 and comp.bin_file.options.use_stage1,
.want_sanitize_c = false, .want_sanitize_c = false,
.want_stack_check = false, .want_stack_check = false,
.want_stack_protector = 0, .want_stack_protector = 0,

View file

@ -8,5 +8,5 @@ pub const enable_logging: bool = @ZIG_ENABLE_LOGGING_BOOL@;
pub const enable_link_snapshots: bool = false; pub const enable_link_snapshots: bool = false;
pub const enable_tracy = false; pub const enable_tracy = false;
pub const value_tracing = false; pub const value_tracing = false;
pub const is_stage1 = true; pub const have_stage1 = true;
pub const skip_non_native = false; pub const skip_non_native = false;

View file

@ -279,7 +279,7 @@ pub const File = struct {
return &(try MachO.openPath(allocator, options)).base; return &(try MachO.openPath(allocator, options)).base;
} }
const use_stage1 = build_options.is_stage1 and options.use_stage1; const use_stage1 = build_options.have_stage1 and options.use_stage1;
if (use_stage1 or options.emit == null) { if (use_stage1 or options.emit == null) {
return switch (options.target.ofmt) { return switch (options.target.ofmt) {
.coff => &(try Coff.createEmpty(allocator, options)).base, .coff => &(try Coff.createEmpty(allocator, options)).base,
@ -817,7 +817,7 @@ pub const File = struct {
// If there is no Zig code to compile, then we should skip flushing the output file // If there is no Zig code to compile, then we should skip flushing the output file
// because it will not be part of the linker line anyway. // because it will not be part of the linker line anyway.
const module_obj_path: ?[]const u8 = if (base.options.module) |module| blk: { const module_obj_path: ?[]const u8 = if (base.options.module) |module| blk: {
const use_stage1 = build_options.is_stage1 and base.options.use_stage1; const use_stage1 = build_options.have_stage1 and base.options.use_stage1;
if (use_stage1) { if (use_stage1) {
const obj_basename = try std.zig.binNameAlloc(arena, .{ const obj_basename = try std.zig.binNameAlloc(arena, .{
.root_name = base.options.root_name, .root_name = base.options.root_name,

View file

@ -411,7 +411,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff {
}; };
const use_llvm = build_options.have_llvm and options.use_llvm; const use_llvm = build_options.have_llvm and options.use_llvm;
const use_stage1 = build_options.is_stage1 and options.use_stage1; const use_stage1 = build_options.have_stage1 and options.use_stage1;
if (use_llvm and !use_stage1) { if (use_llvm and !use_stage1) {
self.llvm_object = try LlvmObject.create(gpa, options); self.llvm_object = try LlvmObject.create(gpa, options);
} }
@ -949,7 +949,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !
// If there is no Zig code to compile, then we should skip flushing the output file because it // If there is no Zig code to compile, then we should skip flushing the output file because it
// will not be part of the linker line anyway. // will not be part of the linker line anyway.
const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1; const use_stage1 = build_options.have_stage1 and self.base.options.use_stage1;
if (use_stage1) { if (use_stage1) {
const obj_basename = try std.zig.binNameAlloc(arena, .{ const obj_basename = try std.zig.binNameAlloc(arena, .{
.root_name = self.base.options.root_name, .root_name = self.base.options.root_name,

View file

@ -328,7 +328,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {
.page_size = page_size, .page_size = page_size,
}; };
const use_llvm = build_options.have_llvm and options.use_llvm; const use_llvm = build_options.have_llvm and options.use_llvm;
const use_stage1 = build_options.is_stage1 and options.use_stage1; const use_stage1 = build_options.have_stage1 and options.use_stage1;
if (use_llvm and !use_stage1) { if (use_llvm and !use_stage1) {
self.llvm_object = try LlvmObject.create(gpa, options); self.llvm_object = try LlvmObject.create(gpa, options);
} }

View file

@ -272,7 +272,7 @@ pub const Export = struct {
pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
assert(options.target.ofmt == .macho); assert(options.target.ofmt == .macho);
const use_stage1 = build_options.is_stage1 and options.use_stage1; const use_stage1 = build_options.have_stage1 and options.use_stage1;
if (use_stage1 or options.emit == null) { if (use_stage1 or options.emit == null) {
return createEmpty(allocator, options); return createEmpty(allocator, options);
} }
@ -363,7 +363,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*MachO {
const cpu_arch = options.target.cpu.arch; const cpu_arch = options.target.cpu.arch;
const page_size: u16 = if (cpu_arch == .aarch64) 0x4000 else 0x1000; const page_size: u16 = if (cpu_arch == .aarch64) 0x4000 else 0x1000;
const use_llvm = build_options.have_llvm and options.use_llvm; const use_llvm = build_options.have_llvm and options.use_llvm;
const use_stage1 = build_options.is_stage1 and options.use_stage1; const use_stage1 = build_options.have_stage1 and options.use_stage1;
const self = try gpa.create(MachO); const self = try gpa.create(MachO);
errdefer gpa.destroy(self); errdefer gpa.destroy(self);

View file

@ -356,7 +356,7 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm {
} }
const use_llvm = build_options.have_llvm and options.use_llvm; const use_llvm = build_options.have_llvm and options.use_llvm;
const use_stage1 = build_options.is_stage1 and options.use_stage1; const use_stage1 = build_options.have_stage1 and options.use_stage1;
if (use_llvm and !use_stage1) { if (use_llvm and !use_stage1) {
self.llvm_object = try LlvmObject.create(gpa, options); self.llvm_object = try LlvmObject.create(gpa, options);
} }
@ -2593,7 +2593,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
// If there is no Zig code to compile, then we should skip flushing the output file because it // If there is no Zig code to compile, then we should skip flushing the output file because it
// will not be part of the linker line anyway. // will not be part of the linker line anyway.
const module_obj_path: ?[]const u8 = if (self.base.options.module) |mod| blk: { const module_obj_path: ?[]const u8 = if (self.base.options.module) |mod| blk: {
const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1; const use_stage1 = build_options.have_stage1 and self.base.options.use_stage1;
if (use_stage1) { if (use_stage1) {
const obj_basename = try std.zig.binNameAlloc(arena, .{ const obj_basename = try std.zig.binNameAlloc(arena, .{
.root_name = self.base.options.root_name, .root_name = self.base.options.root_name,
@ -2803,7 +2803,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
if (self.base.options.module) |mod| { if (self.base.options.module) |mod| {
// when we use stage1, we use the exports that stage1 provided us. // when we use stage1, we use the exports that stage1 provided us.
// For stage2, we can directly retrieve them from the module. // For stage2, we can directly retrieve them from the module.
const use_stage1 = build_options.is_stage1 and self.base.options.use_stage1; const use_stage1 = build_options.have_stage1 and self.base.options.use_stage1;
if (use_stage1) { if (use_stage1) {
for (comp.export_symbol_names.items) |symbol_name| { for (comp.export_symbol_names.items) |symbol_name| {
try argv.append(try std.fmt.allocPrint(arena, "--export={s}", .{symbol_name})); try argv.append(try std.fmt.allocPrint(arena, "--export={s}", .{symbol_name}));

View file

@ -2989,7 +2989,7 @@ fn buildOutputType(
return std.io.getStdOut().writeAll(try comp.generateBuiltinZigSource(arena)); return std.io.getStdOut().writeAll(try comp.generateBuiltinZigSource(arena));
} }
if (arg_mode == .translate_c) { if (arg_mode == .translate_c) {
const stage1_mode = use_stage1 orelse build_options.is_stage1; const stage1_mode = use_stage1 orelse false;
return cmdTranslateC(comp, arena, have_enable_cache, stage1_mode); return cmdTranslateC(comp, arena, have_enable_cache, stage1_mode);
} }

View file

@ -18,7 +18,7 @@ const target_util = @import("target.zig");
comptime { comptime {
assert(builtin.link_libc); assert(builtin.link_libc);
assert(build_options.is_stage1); assert(build_options.have_stage1);
assert(build_options.have_llvm); assert(build_options.have_llvm);
if (!builtin.is_test) { if (!builtin.is_test) {
@export(main, .{ .name = "main" }); @export(main, .{ .name = "main" });

View file

@ -25,7 +25,7 @@ const skip_stage1 = builtin.zig_backend != .stage1 or build_options.skip_stage1;
const hr = "=" ** 80; const hr = "=" ** 80;
test { test {
if (build_options.is_stage1) { if (build_options.have_stage1) {
@import("stage1.zig").os_init(); @import("stage1.zig").os_init();
} }

View file

@ -2,7 +2,7 @@ const std = @import("std");
pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
_ = stack_trace; _ = stack_trace;
if (std.mem.eql(u8, message, "attempt to index out of bound: index 1, len 0")) { if (std.mem.eql(u8, message, "index out of bounds: index 1, len 0")) {
std.process.exit(0); std.process.exit(0);
} }
std.process.exit(1); std.process.exit(1);

View file

@ -2,7 +2,7 @@ const std = @import("std");
pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
_ = stack_trace; _ = stack_trace;
if (std.mem.eql(u8, message, "attempt to index out of bound: index 4, len 4")) { if (std.mem.eql(u8, message, "index out of bounds: index 4, len 4")) {
std.process.exit(0); std.process.exit(0);
} }
std.process.exit(1); std.process.exit(1);

View file

@ -2,7 +2,7 @@ const std = @import("std");
pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
_ = stack_trace; _ = stack_trace;
if (std.mem.eql(u8, message, "attempt to index out of bound: index 5, len 4")) { if (std.mem.eql(u8, message, "index out of bounds: index 5, len 4")) {
std.process.exit(0); std.process.exit(0);
} }
std.process.exit(1); std.process.exit(1);

View file

@ -2,7 +2,7 @@ const std = @import("std");
pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn { pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
_ = stack_trace; _ = stack_trace;
if (std.mem.eql(u8, message, "attempt to index out of bound: index 5, len 4")) { if (std.mem.eql(u8, message, "index out of bounds: index 5, len 4")) {
std.process.exit(0); std.process.exit(0);
} }
std.process.exit(1); std.process.exit(1);

View file

@ -601,7 +601,6 @@ pub fn addPkgTests(
skip_libc: bool, skip_libc: bool,
skip_stage1: bool, skip_stage1: bool,
skip_stage2: bool, skip_stage2: bool,
is_stage1: bool,
) *build.Step { ) *build.Step {
const step = b.step(b.fmt("test-{s}", .{name}), desc); const step = b.step(b.fmt("test-{s}", .{name}), desc);
@ -630,7 +629,7 @@ pub fn addPkgTests(
if (test_target.backend) |backend| switch (backend) { if (test_target.backend) |backend| switch (backend) {
.stage1 => if (skip_stage1) continue, .stage1 => if (skip_stage1) continue,
else => if (skip_stage2) continue, else => if (skip_stage2) continue,
} else if (is_stage1 and skip_stage1) continue; } else if (skip_stage2) continue;
const want_this_mode = for (modes) |m| { const want_this_mode = for (modes) |m| {
if (m == test_target.mode) break true; if (m == test_target.mode) break true;