Commit 25198810c8

Andrew Kelley <andrew@ziglang.org>
2024-07-22 08:50:50
add new builtin: `@disableInstrumentation`
This is needed to ensure that start code does not try to access thread local storage before it has set up thread local storage.
1 parent 7930efc
lib/std/zig/AstGen.zig
@@ -2817,6 +2817,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
 
             .extended => switch (gz.astgen.instructions.items(.data)[@intFromEnum(inst)].extended.opcode) {
                 .breakpoint,
+                .disable_instrumentation,
                 .fence,
                 .set_float_mode,
                 .set_align_stack,
@@ -9305,12 +9306,13 @@ fn builtinCall(
         },
 
         // zig fmt: off
-        .This               => return rvalue(gz, ri, try gz.addNodeExtended(.this,               node), node),
-        .return_address     => return rvalue(gz, ri, try gz.addNodeExtended(.ret_addr,           node), node),
-        .error_return_trace => return rvalue(gz, ri, try gz.addNodeExtended(.error_return_trace, node), node),
-        .frame              => return rvalue(gz, ri, try gz.addNodeExtended(.frame,              node), node),
-        .frame_address      => return rvalue(gz, ri, try gz.addNodeExtended(.frame_address,      node), node),
-        .breakpoint         => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint,         node), node),
+        .This                    => return rvalue(gz, ri, try gz.addNodeExtended(.this,                    node), node),
+        .return_address          => return rvalue(gz, ri, try gz.addNodeExtended(.ret_addr,                node), node),
+        .error_return_trace      => return rvalue(gz, ri, try gz.addNodeExtended(.error_return_trace,      node), node),
+        .frame                   => return rvalue(gz, ri, try gz.addNodeExtended(.frame,                   node), node),
+        .frame_address           => return rvalue(gz, ri, try gz.addNodeExtended(.frame_address,           node), node),
+        .breakpoint              => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint,              node), node),
+        .disable_instrumentation => return rvalue(gz, ri, try gz.addNodeExtended(.disable_instrumentation, node), node),
 
         .type_info   => return simpleUnOpType(gz, scope, ri, node, params[0], .type_info),
         .size_of     => return simpleUnOpType(gz, scope, ri, node, params[0], .size_of),
lib/std/zig/AstRlAnnotate.zig
@@ -877,6 +877,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
         .error_return_trace,
         .frame,
         .breakpoint,
+        .disable_instrumentation,
         .in_comptime,
         .panic,
         .trap,
lib/std/zig/BuiltinFn.zig
@@ -15,6 +15,7 @@ pub const Tag = enum {
     int_from_bool,
     bit_size_of,
     breakpoint,
+    disable_instrumentation,
     mul_add,
     byte_swap,
     bit_reverse,
@@ -263,6 +264,14 @@ pub const list = list: {
                 .illegal_outside_function = true,
             },
         },
+        .{
+            "@disableInstrumentation",
+            .{
+                .tag = .disable_instrumentation,
+                .param_count = 0,
+                .illegal_outside_function = true,
+            },
+        },
         .{
             "@mulAdd",
             .{
lib/std/zig/Zir.zig
@@ -1553,7 +1553,7 @@ pub const Inst = struct {
                 => false,
 
                 .extended => switch (data.extended.opcode) {
-                    .fence, .set_cold, .breakpoint => true,
+                    .fence, .set_cold, .breakpoint, .disable_instrumentation => true,
                     else => false,
                 },
             };
@@ -1973,6 +1973,8 @@ pub const Inst = struct {
         /// Implements `@breakpoint`.
         /// `operand` is `src_node: i32`.
         breakpoint,
+        /// Implement builtin `@disableInstrumentation`. `operand` is `src_node: i32`.
+        disable_instrumentation,
         /// Implements the `@select` builtin.
         /// `operand` is payload index to `Select`.
         select,
src/codegen/llvm.zig
@@ -1383,6 +1383,25 @@ pub const Object = struct {
             _ = try attributes.removeFnAttr(.cold);
         }
 
+        if (owner_mod.sanitize_thread and !func_analysis.disable_instrumentation) {
+            try attributes.addFnAttr(.sanitize_thread, &o.builder);
+        } else {
+            _ = try attributes.removeFnAttr(.sanitize_thread);
+        }
+        if (owner_mod.fuzz and !func_analysis.disable_instrumentation) {
+            try attributes.addFnAttr(.optforfuzzing, &o.builder);
+            if (comp.config.any_fuzz) {
+                _ = try attributes.removeFnAttr(.skipprofile);
+                _ = try attributes.removeFnAttr(.nosanitize_coverage);
+            }
+        } else {
+            _ = try attributes.removeFnAttr(.optforfuzzing);
+            if (comp.config.any_fuzz) {
+                try attributes.addFnAttr(.skipprofile, &o.builder);
+                try attributes.addFnAttr(.nosanitize_coverage, &o.builder);
+            }
+        }
+
         // TODO: disable this if safety is off for the function scope
         const ssp_buf_size = owner_mod.stack_protector;
         if (ssp_buf_size != 0) {
@@ -2982,12 +3001,6 @@ pub const Object = struct {
             try attributes.addFnAttr(.minsize, &o.builder);
             try attributes.addFnAttr(.optsize, &o.builder);
         }
-        if (owner_mod.sanitize_thread) {
-            try attributes.addFnAttr(.sanitize_thread, &o.builder);
-        }
-        if (owner_mod.fuzz) {
-            try attributes.addFnAttr(.optforfuzzing, &o.builder);
-        }
         const target = owner_mod.resolved_target.result;
         if (target.cpu.model.llvm_name) |s| {
             try attributes.addFnAttr(.{ .string = .{
src/InternPool.zig
@@ -5184,11 +5184,11 @@ pub const FuncAnalysis = packed struct(u32) {
     is_noinline: bool,
     calls_or_awaits_errorable_fn: bool,
     stack_alignment: Alignment,
-
     /// True if this function has an inferred error set.
     inferred_error_set: bool,
+    disable_instrumentation: bool,
 
-    _: u14 = 0,
+    _: u13 = 0,
 
     pub const State = enum(u8) {
         /// This function has not yet undergone analysis, because we have not
@@ -8111,6 +8111,7 @@ pub fn getFuncDecl(
             .calls_or_awaits_errorable_fn = false,
             .stack_alignment = .none,
             .inferred_error_set = false,
+            .disable_instrumentation = false,
         },
         .owner_decl = key.owner_decl,
         .ty = key.ty,
@@ -8214,6 +8215,7 @@ pub fn getFuncDeclIes(
             .calls_or_awaits_errorable_fn = false,
             .stack_alignment = .none,
             .inferred_error_set = true,
+            .disable_instrumentation = false,
         },
         .owner_decl = key.owner_decl,
         .ty = func_ty,
@@ -8405,6 +8407,7 @@ pub fn getFuncInstance(
             .calls_or_awaits_errorable_fn = false,
             .stack_alignment = .none,
             .inferred_error_set = false,
+            .disable_instrumentation = false,
         },
         // This is populated after we create the Decl below. It is not read
         // by equality or hashing functions.
@@ -8504,6 +8507,7 @@ pub fn getFuncInstanceIes(
             .calls_or_awaits_errorable_fn = false,
             .stack_alignment = .none,
             .inferred_error_set = true,
+            .disable_instrumentation = false,
         },
         // This is populated after we create the Decl below. It is not read
         // by equality or hashing functions.
@@ -11225,6 +11229,18 @@ pub fn funcSetCallsOrAwaitsErrorableFn(ip: *InternPool, func: Index) void {
     @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
 }
 
+pub fn funcSetDisableInstrumentation(ip: *InternPool, func: Index) void {
+    const unwrapped_func = func.unwrap(ip);
+    const extra_mutex = &ip.getLocal(unwrapped_func.tid).mutate.extra.mutex;
+    extra_mutex.lock();
+    defer extra_mutex.unlock();
+
+    const analysis_ptr = ip.funcAnalysisPtr(func);
+    var analysis = analysis_ptr.*;
+    analysis.disable_instrumentation = true;
+    @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release);
+}
+
 pub fn funcSetCold(ip: *InternPool, func: Index, is_cold: bool) void {
     const unwrapped_func = func.unwrap(ip);
     const extra_mutex = &ip.getLocal(unwrapped_func.tid).mutate.extra.mutex;
src/print_zir.zig
@@ -524,6 +524,7 @@ const Writer = struct {
             .frame,
             .frame_address,
             .breakpoint,
+            .disable_instrumentation,
             .c_va_start,
             .in_comptime,
             .value_placeholder,
src/Sema.zig
@@ -1316,6 +1316,11 @@ fn analyzeBodyInner(
                         i += 1;
                         continue;
                     },
+                    .disable_instrumentation => {
+                        try sema.zirDisableInstrumentation();
+                        i += 1;
+                        continue;
+                    },
                     .restore_err_ret_index => {
                         try sema.zirRestoreErrRetIndex(block, extended);
                         i += 1;
@@ -6576,6 +6581,14 @@ fn zirSetCold(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData)
     ip.funcSetCold(sema.func_index, is_cold);
 }
 
+fn zirDisableInstrumentation(sema: *Sema) CompileError!void {
+    const pt = sema.pt;
+    const mod = pt.zcu;
+    const ip = &mod.intern_pool;
+    if (sema.func_index == .none) return; // does nothing outside a function
+    ip.funcSetDisableInstrumentation(sema.func_index);
+}
+
 fn zirSetFloatMode(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
     const src = block.builtinCallArgSrc(extra.node, 0);