Commit 4591236ae1

Andrew Kelley <andrew@ziglang.org>
2020-02-29 00:31:46
CrossTarget.cpu_model: communicate intent precisely
1 parent aa13f33
Changed files (3)
lib
src-self-hosted
lib/std/zig/cross_target.zig
@@ -7,11 +7,10 @@ const mem = std.mem;
 /// The purpose of this abstraction is to provide meaningful and unsurprising defaults.
 /// This struct does reference any resources and it is copyable.
 pub const CrossTarget = struct {
-    /// `null` means native. If this is `null` then `cpu_model` must be `null`.
+    /// `null` means native.
     cpu_arch: ?Target.Cpu.Arch = null,
 
-    /// `null` means native. If this is non-null, `cpu_arch` must be specified.
-    cpu_model: ?*const Target.Cpu.Model = null,
+    cpu_model: CpuModel = CpuModel.determined_by_cpu_arch,
 
     /// Sparse set of CPU features to add to the set from `cpu_model`.
     cpu_features_add: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty,
@@ -41,6 +40,20 @@ pub const CrossTarget = struct {
     /// based on the `os_tag`.
     dynamic_linker: DynamicLinker = DynamicLinker{},
 
+    pub const CpuModel = union(enum) {
+        /// Always native
+        native,
+
+        /// Always baseline
+        baseline,
+
+        /// If CPU Architecture is native, then the CPU model will be native. Otherwise,
+        /// it will be baseline.
+        determined_by_cpu_arch,
+
+        explicit: *const Target.Cpu.Model,
+    };
+
     pub const OsVersion = union(enum) {
         none: void,
         semver: SemVer,
@@ -54,7 +67,7 @@ pub const CrossTarget = struct {
     pub fn fromTarget(target: Target) CrossTarget {
         var result: CrossTarget = .{
             .cpu_arch = target.cpu.arch,
-            .cpu_model = target.cpu.model,
+            .cpu_model = .{ .explicit = target.cpu.model },
             .os_tag = target.os.tag,
             .os_version_min = undefined,
             .os_version_max = undefined,
@@ -266,11 +279,11 @@ pub const CrossTarget = struct {
             const add_set = &result.cpu_features_add;
             const sub_set = &result.cpu_features_sub;
             if (mem.eql(u8, cpu_name, "native")) {
-                result.cpu_model = null;
+                result.cpu_model = .native;
             } else if (mem.eql(u8, cpu_name, "baseline")) {
-                result.cpu_model = Target.Cpu.Model.baseline(arch);
+                result.cpu_model = .baseline;
             } else {
-                result.cpu_model = try arch.parseCpuModel(cpu_name);
+                result.cpu_model = .{ .explicit = try arch.parseCpuModel(cpu_name) };
             }
 
             while (index < cpu_features.len) {
@@ -300,10 +313,6 @@ pub const CrossTarget = struct {
                     return error.UnknownCpuFeature;
                 }
             }
-        } else if (arch_is_native) {
-            result.cpu_model = null;
-        } else {
-            result.cpu_model = Target.Cpu.Model.baseline(arch);
         }
 
         return result;
@@ -311,24 +320,33 @@ pub const CrossTarget = struct {
 
     /// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`.
     pub fn getCpu(self: CrossTarget) Target.Cpu {
-        if (self.cpu_arch) |arch| {
-            if (self.cpu_model) |model| {
-                var adjusted_model = model.toCpu(arch);
-                self.updateCpuFeatures(&adjusted_model.features);
-                return adjusted_model;
+        switch (self.cpu_model) {
+            .native => {
+                // This works when doing `zig build` because Zig generates a build executable using
+                // native CPU model & features. However this will not be accurate otherwise, and
+                // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.
+                return Target.current.cpu;
+            },
+            .baseline => {
+                var adjusted_baseline = Target.Cpu.baseline(self.getCpuArch());
+                self.updateCpuFeatures(&adjusted_baseline.features);
+                return adjusted_baseline;
+            },
+            .determined_by_cpu_arch => if (self.cpu_arch == null) {
+                // This works when doing `zig build` because Zig generates a build executable using
+                // native CPU model & features. However this will not be accurate otherwise, and
+                // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.
+                return Target.current.cpu;
             } else {
-                var adjusted_baseline = Target.Cpu.baseline(arch);
+                var adjusted_baseline = Target.Cpu.baseline(self.getCpuArch());
                 self.updateCpuFeatures(&adjusted_baseline.features);
                 return adjusted_baseline;
-            }
-        } else {
-            assert(self.cpu_model == null);
-            assert(self.cpu_features_sub.isEmpty());
-            assert(self.cpu_features_add.isEmpty());
-            // This works when doing `zig build` because Zig generates a build executable using
-            // native CPU model & features. However this will not be accurate otherwise, and
-            // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`.
-            return Target.current.cpu;
+            },
+            .explicit => |model| {
+                var adjusted_model = model.toCpu(self.getCpuArch());
+                self.updateCpuFeatures(&adjusted_model.features);
+                return adjusted_model;
+            },
         }
     }
 
@@ -461,7 +479,8 @@ pub const CrossTarget = struct {
     }
 
     pub fn isNative(self: CrossTarget) bool {
-        return self.cpu_arch == null and self.cpu_model == null and
+        return self.cpu_arch == null and
+            (self.cpu_model == .native or self.cpu_model == .determined_by_cpu_arch) and
             self.cpu_features_sub.isEmpty() and self.cpu_features_add.isEmpty() and
             self.os_tag == null and self.os_version_min == null and self.os_version_max == null and
             self.abi == null and self.dynamic_linker.get() == null;
lib/std/zig/system.zig
@@ -191,18 +191,18 @@ pub const NativeTargetInfo = struct {
     /// deinitialization method.
     /// TODO Remove the Allocator requirement from this function.
     pub fn detect(allocator: *Allocator, cross_target: CrossTarget) DetectError!NativeTargetInfo {
-        const cpu = blk: {
-            const arch = cross_target.getCpuArch();
-            if (cross_target.cpu_model) |model| {
-                var adjusted_model = model.toCpu(arch);
+        const cpu = switch (cross_target.cpu_model) {
+            .native => detectNativeCpuAndFeatures(cross_target),
+            .baseline => baselineCpuAndFeatures(cross_target),
+            .determined_by_cpu_arch => if (cross_target.cpu_arch == null)
+                detectNativeCpuAndFeatures(cross_target)
+            else
+                baselineCpuAndFeatures(cross_target),
+            .explicit => |model| blk: {
+                var adjusted_model = model.toCpu(cross_target.getCpuArch());
                 cross_target.updateCpuFeatures(&adjusted_model.features);
                 break :blk adjusted_model;
-            } else {
-                // TODO Detect native CPU model & features. Until that is implemented we use baseline.
-                var adjusted_baseline = Target.Cpu.baseline(arch);
-                cross_target.updateCpuFeatures(&adjusted_baseline.features);
-                break :blk adjusted_baseline;
-            }
+            },
         };
 
         var os = Target.Os.defaultVersionRange(cross_target.getOsTag());
@@ -758,4 +758,15 @@ pub const NativeTargetInfo = struct {
             }
         }
     }
+
+    fn detectNativeCpuAndFeatures(cross_target: CrossTarget) Target.Cpu {
+        // TODO Detect native CPU model & features. Until that is implemented we use baseline.
+        return baselineCpuAndFeatures(cross_target);
+    }
+
+    fn baselineCpuAndFeatures(cross_target: CrossTarget) Target.Cpu {
+        var adjusted_baseline = Target.Cpu.baseline(cross_target.getCpuArch());
+        cross_target.updateCpuFeatures(&adjusted_baseline.features);
+        return adjusted_baseline;
+    }
 };
src-self-hosted/stage2.zig
@@ -1154,7 +1154,7 @@ fn enumInt(comptime Enum: type, int: c_int) Enum {
 
 fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8) !Target {
     var info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator, cross_target);
-    if (cross_target.cpu_arch == null or cross_target.cpu_model == null) {
+    if (cross_target.cpu_arch == null or cross_target.cpu_model == .native) {
         // TODO We want to just use detected_info.target but implementing
         // CPU model & feature detection is todo so here we rely on LLVM.
         const llvm = @import("llvm.zig");