Commit 1c24ef0d0b

Andrew Kelley <andrew@ziglang.org>
2022-01-05 02:12:45
stage2: introduce std.builtin.CompilerBackend
This allows Zig code to perform conditional compilation based on a tag by which a Zig compiler implementation identifies itself. See the doc comment in this commit for more details.
1 parent 5087ec6
Changed files (3)
lib/std/builtin.zig
@@ -694,6 +694,61 @@ pub const ExternOptions = struct {
     is_thread_local: bool = false,
 };
 
+/// This enum is set by the compiler and communicates which compiler backend is
+/// used to produce machine code.
+/// Think carefully before deciding to observe this value. Nearly all code should
+/// be agnostic to the backend that implements the language. The use case
+/// to use this value is to **work around problems with compiler implementations.**
+///
+/// Avoid failing the compilation if the compiler backend does not match a
+/// whitelist of backends; rather one should detect that a known problem would
+/// occur in a blacklist of backends.
+///
+/// The enum is nonexhaustive so that alternate Zig language implementations may
+/// choose a number as their tag (please use a random number generator rather
+/// than a "cute" number) and codebases can interact with these values even if
+/// this upstream enum does not have a name for the number. Of course, upstream
+/// is happy to accept pull requests to add Zig implementations to this enum.
+///
+/// This data structure is part of the Zig language specification.
+pub const CompilerBackend = enum(u64) {
+    /// It is allowed for a compiler implementation to not reveal its identity,
+    /// in which case this value is appropriate. Be cool and make sure your
+    /// code supports `other` Zig compilers!
+    other = 0,
+    /// The original Zig compiler created in 2015 by Andrew Kelley.
+    /// Implemented in C++. Uses LLVM.
+    stage1 = 1,
+    /// The reference implementation self-hosted compiler of Zig, using the
+    /// LLVM backend.
+    stage2_llvm = 2,
+    /// The reference implementation self-hosted compiler of Zig, using the
+    /// backend that generates C source code.
+    /// Note that one can observe whether the compilation will output C code
+    /// directly with `object_format` value rather than the `compiler_backend` value.
+    stage2_c = 3,
+    /// The reference implementation self-hosted compiler of Zig, using the
+    /// WebAssembly backend.
+    stage2_wasm = 4,
+    /// The reference implementation self-hosted compiler of Zig, using the
+    /// arm backend.
+    stage2_arm = 5,
+    /// The reference implementation self-hosted compiler of Zig, using the
+    /// x86_64 backend.
+    stage2_x86_64 = 6,
+    /// The reference implementation self-hosted compiler of Zig, using the
+    /// aarch64 backend.
+    stage2_aarch64 = 7,
+    /// The reference implementation self-hosted compiler of Zig, using the
+    /// x86 backend.
+    stage2_x86 = 8,
+    /// The reference implementation self-hosted compiler of Zig, using the
+    /// riscv64 backend.
+    stage2_riscv64 = 9,
+
+    _,
+};
+
 /// This function type is used by the Zig language code generation and
 /// therefore must be kept in sync with the compiler implementation.
 pub const TestFn = struct {
src/Compilation.zig
@@ -4538,12 +4538,28 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca
     const stage2_x86_cx16 = target.cpu.arch == .x86_64 and
         std.Target.x86.featureSetHas(target.cpu.features, .cx16);
 
+    const zig_backend: std.builtin.CompilerBackend = blk: {
+        if (use_stage1) break :blk .stage1;
+        if (build_options.have_llvm and comp.bin_file.options.use_llvm) break :blk .stage2_llvm;
+        if (comp.bin_file.options.object_format == .c) break :blk .stage2_c;
+        break :blk switch (target.cpu.arch) {
+            .wasm32, .wasm64 => std.builtin.CompilerBackend.stage2_wasm,
+            .arm, .armeb, .thumb, .thumbeb => .stage2_arm,
+            .x86_64 => .stage2_x86_64,
+            .i386 => .stage2_x86,
+            .aarch64, .aarch64_be, .aarch64_32 => .stage2_aarch64,
+            .riscv64 => .stage2_riscv64,
+            else => .other,
+        };
+    };
+
     @setEvalBranchQuota(4000);
     try buffer.writer().print(
         \\const std = @import("std");
         \\/// Zig version. When writing code that supports multiple versions of Zig, prefer
         \\/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks.
         \\pub const zig_version = std.SemanticVersion.parse("{s}") catch unreachable;
+        \\pub const zig_backend = std.builtin.CompilerBackend.{};
         \\/// Temporary until self-hosted is feature complete.
         \\pub const zig_is_stage2 = {};
         \\/// Temporary until self-hosted supports the `cpu.arch` value.
@@ -4563,6 +4579,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca
         \\
     , .{
         build_options.version,
+        std.zig.fmtId(@tagName(zig_backend)),
         !use_stage1,
         std.zig.fmtId(@tagName(target.cpu.arch)),
         stage2_x86_cx16,
test/behavior.zig
@@ -7,7 +7,7 @@ test {
     _ = @import("behavior/bugs/3586.zig");
     _ = @import("behavior/slice_sentinel_comptime.zig");
 
-    if (!builtin.zig_is_stage2 or builtin.stage2_arch != .x86_64) {
+    if (builtin.zig_backend != .stage2_x86_64) {
         // Tests that pass for stage1, llvm backend, C backend, wasm backend, and arm backend.
         _ = @import("behavior/bugs/679.zig");
         _ = @import("behavior/bugs/4560.zig");
@@ -19,7 +19,7 @@ test {
         _ = @import("behavior/type_info.zig");
         _ = @import("behavior/type.zig");
 
-        if (!builtin.zig_is_stage2 or builtin.stage2_arch != .arm) {
+        if (builtin.zig_backend != .stage2_arm) {
             // Tests that pass for stage1, llvm backend, C backend, wasm backend.
             _ = @import("behavior/basic.zig");
             _ = @import("behavior/bitcast.zig");
@@ -57,7 +57,7 @@ test {
             _ = @import("behavior/void.zig");
             _ = @import("behavior/while.zig");
 
-            if (!builtin.zig_is_stage2 or builtin.stage2_arch != .wasm32) {
+            if (builtin.zig_backend != .stage2_wasm) {
                 // Tests that pass for stage1, llvm backend, C backend
                 _ = @import("behavior/align.zig");
                 _ = @import("behavior/array.zig");
@@ -67,7 +67,7 @@ test {
                 _ = @import("behavior/optional.zig");
                 _ = @import("behavior/translate_c_macros.zig");
 
-                if (builtin.object_format != .c) {
+                if (builtin.zig_backend != .stage2_c) {
                     // Tests that pass for stage1 and the llvm backend.
                     _ = @import("behavior/align_llvm.zig");
                     _ = @import("behavior/alignof.zig");
@@ -109,7 +109,7 @@ test {
                     _ = @import("behavior/union.zig");
                     _ = @import("behavior/widening.zig");
 
-                    if (builtin.zig_is_stage2) {
+                    if (builtin.zig_backend != .stage1) {
                         // When all comptime_memory.zig tests pass, #9646 can be closed.
                         // _ = @import("behavior/comptime_memory.zig");
                         _ = @import("behavior/slice_stage2.zig");