diff options
| author | Alex Rønne Petersen <alex@alexrp.com> | 2025-02-24 08:25:23 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-02-24 08:25:23 +0100 |
| commit | ecc76348e6858f00b920d8dc9e44a6e703d8e497 (patch) | |
| tree | 018e151e85958c8e1b12328865e8b18398956b5e | |
| parent | 76558f8c6b8361ab520ea77e4b4cd2bfc8f688ad (diff) | |
| parent | e11ac026623f8d0f4895cddd6a9bf3c03a866c6d (diff) | |
| download | zig-ecc76348e6858f00b920d8dc9e44a6e703d8e497.tar.gz zig-ecc76348e6858f00b920d8dc9e44a6e703d8e497.zip | |
Merge pull request #22154 from alexrp/disable-intrinsics
compiler: Implement `@disableIntrinsics()` builtin function.
| -rw-r--r-- | lib/std/zig/AstGen.zig | 2 | ||||
| -rw-r--r-- | lib/std/zig/AstRlAnnotate.zig | 1 | ||||
| -rw-r--r-- | lib/std/zig/BuiltinFn.zig | 9 | ||||
| -rw-r--r-- | lib/std/zig/Zir.zig | 9 | ||||
| -rw-r--r-- | lib/zig.h | 6 | ||||
| -rw-r--r-- | src/InternPool.zig | 19 | ||||
| -rw-r--r-- | src/Sema.zig | 22 | ||||
| -rw-r--r-- | src/codegen/c.zig | 13 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 53 | ||||
| -rw-r--r-- | src/print_zir.zig | 1 |
10 files changed, 107 insertions, 28 deletions
diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index 6aee6880b3..c7f2426b0a 100644 --- a/lib/std/zig/AstGen.zig +++ b/lib/std/zig/AstGen.zig @@ -2956,6 +2956,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, + .disable_intrinsics, .set_float_mode, .branch_hint, => break :b true, @@ -9578,6 +9579,7 @@ fn builtinCall( .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), + .disable_intrinsics => return rvalue(gz, ri, try gz.addNodeExtended(.disable_intrinsics, 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), diff --git a/lib/std/zig/AstRlAnnotate.zig b/lib/std/zig/AstRlAnnotate.zig index e790612505..ccc23b18aa 100644 --- a/lib/std/zig/AstRlAnnotate.zig +++ b/lib/std/zig/AstRlAnnotate.zig @@ -882,6 +882,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast. .frame, .breakpoint, .disable_instrumentation, + .disable_intrinsics, .in_comptime, .panic, .trap, diff --git a/lib/std/zig/BuiltinFn.zig b/lib/std/zig/BuiltinFn.zig index 7ad5bb1a87..1bf31cd165 100644 --- a/lib/std/zig/BuiltinFn.zig +++ b/lib/std/zig/BuiltinFn.zig @@ -15,6 +15,7 @@ pub const Tag = enum { branch_hint, breakpoint, disable_instrumentation, + disable_intrinsics, mul_add, byte_swap, bit_reverse, @@ -263,6 +264,14 @@ pub const list = list: { }, }, .{ + "@disableIntrinsics", + .{ + .tag = .disable_intrinsics, + .param_count = 0, + .illegal_outside_function = true, + }, + }, + .{ "@mulAdd", .{ .tag = .mul_add, diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig index 32872eeabc..5d013635cb 100644 --- a/lib/std/zig/Zir.zig +++ b/lib/std/zig/Zir.zig @@ -1587,7 +1587,11 @@ pub const Inst = struct { => false, .extended => switch (data.extended.opcode) { - .branch_hint, .breakpoint, .disable_instrumentation => true, + .branch_hint, + .breakpoint, + .disable_instrumentation, + .disable_intrinsics, + => true, else => false, }, }; @@ -2004,6 +2008,8 @@ pub const Inst = struct { breakpoint, /// Implement builtin `@disableInstrumentation`. `operand` is `src_node: i32`. disable_instrumentation, + /// Implement builtin `@disableIntrinsics`. `operand` is `src_node: i32`. + disable_intrinsics, /// Implements the `@select` builtin. /// `operand` is payload index to `Select`. select, @@ -4332,6 +4338,7 @@ fn findTrackableInner( .await_nosuspend, .breakpoint, .disable_instrumentation, + .disable_intrinsics, .select, .int_from_error, .error_from_int, @@ -223,6 +223,12 @@ #define zig_restrict #endif +#if zig_has_attribute(no_builtin) +#define zig_no_builtin __attribute__((no_builtin)) +#else +#define zig_no_builtin +#endif + #if zig_has_attribute(aligned) || defined(zig_tinyc) #define zig_under_align(alignment) __attribute__((aligned(alignment))) #elif defined(zig_msvc) diff --git a/src/InternPool.zig b/src/InternPool.zig index 391d583004..7f02b548b4 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -6045,8 +6045,9 @@ pub const FuncAnalysis = packed struct(u32) { /// True if this function has an inferred error set. inferred_error_set: bool, disable_instrumentation: bool, + disable_intrinsics: bool, - _: u24 = 0, + _: u23 = 0, }; pub const Bytes = struct { @@ -9077,6 +9078,7 @@ pub fn getFuncDecl( .has_error_trace = false, .inferred_error_set = false, .disable_instrumentation = false, + .disable_intrinsics = false, }, .owner_nav = key.owner_nav, .ty = key.ty, @@ -9186,6 +9188,7 @@ pub fn getFuncDeclIes( .has_error_trace = false, .inferred_error_set = true, .disable_instrumentation = false, + .disable_intrinsics = false, }, .owner_nav = key.owner_nav, .ty = func_ty, @@ -9382,6 +9385,7 @@ pub fn getFuncInstance( .has_error_trace = false, .inferred_error_set = false, .disable_instrumentation = false, + .disable_intrinsics = false, }, // This is populated after we create the Nav below. It is not read // by equality or hashing functions. @@ -9480,6 +9484,7 @@ pub fn getFuncInstanceIes( .has_error_trace = false, .inferred_error_set = true, .disable_instrumentation = false, + .disable_intrinsics = false, }, // This is populated after we create the Nav below. It is not read // by equality or hashing functions. @@ -12313,6 +12318,18 @@ pub fn funcSetDisableInstrumentation(ip: *InternPool, func: Index) void { @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release); } +pub fn funcSetDisableIntrinsics(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_intrinsics = true; + @atomicStore(FuncAnalysis, analysis_ptr, analysis, .release); +} + pub fn funcZirBodyInst(ip: *const InternPool, func: Index) TrackedInst.Index { const unwrapped_func = func.unwrap(ip); const item = unwrapped_func.getItem(ip); diff --git a/src/Sema.zig b/src/Sema.zig index 80eb798593..9e729a17ea 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1409,6 +1409,11 @@ fn analyzeBodyInner( i += 1; continue; }, + .disable_intrinsics => { + try sema.zirDisableIntrinsics(); + i += 1; + continue; + }, .restore_err_ret_index => { try sema.zirRestoreErrRetIndex(block, extended); i += 1; @@ -6642,6 +6647,23 @@ fn zirDisableInstrumentation(sema: *Sema) CompileError!void { sema.allow_memoize = false; } +fn zirDisableIntrinsics(sema: *Sema) CompileError!void { + const pt = sema.pt; + const zcu = pt.zcu; + const ip = &zcu.intern_pool; + const func = switch (sema.owner.unwrap()) { + .func => |func| func, + .@"comptime", + .nav_val, + .nav_ty, + .type, + .memoized_state, + => return, // does nothing outside a function + }; + ip.funcSetDisableIntrinsics(func); + sema.allow_memoize = false; +} + 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); diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 5012f7e0e8..cd4573375d 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1859,8 +1859,17 @@ pub const DeclGen = struct { else => unreachable, } } - if (fn_val.getFunction(zcu)) |func| if (func.analysisUnordered(ip).branch_hint == .cold) - try w.writeAll("zig_cold "); + + if (fn_val.getFunction(zcu)) |func| { + const func_analysis = func.analysisUnordered(ip); + + if (func_analysis.branch_hint == .cold) + try w.writeAll("zig_cold "); + + if (kind == .complete and func_analysis.disable_intrinsics or dg.mod.no_builtin) + try w.writeAll("zig_no_builtin "); + } + if (fn_info.return_type == .noreturn_type) try w.writeAll("zig_noreturn "); var trailing = try renderTypePrefix(dg.pass, &dg.ctype_pool, zcu, w, fn_ctype, .suffix, .{}); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index fe68721665..2f58dc7897 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1447,6 +1447,19 @@ pub const Object = struct { try attributes.addFnAttr(.nosanitize_coverage, &o.builder); } + const disable_intrinsics = func_analysis.disable_intrinsics or owner_mod.no_builtin; + if (disable_intrinsics) { + // The intent here is for compiler-rt and libc functions to not generate + // infinite recursion. For example, if we are compiling the memcpy function, + // and llvm detects that the body is equivalent to memcpy, it may replace the + // body of memcpy with a call to memcpy, which would then cause a stack + // overflow instead of performing memcpy. + try attributes.addFnAttr(.{ .string = .{ + .kind = try o.builder.string("no-builtins"), + .value = .empty, + } }, &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) { @@ -1750,6 +1763,7 @@ pub const Object = struct { .prev_dbg_line = 0, .prev_dbg_column = 0, .err_ret_trace = err_ret_trace, + .disable_intrinsics = disable_intrinsics, }; defer fg.deinit(); deinit_wip = false; @@ -3129,17 +3143,6 @@ pub const Object = struct { &o.builder, ); } - if (owner_mod.no_builtin) { - // The intent here is for compiler-rt and libc functions to not generate - // infinite recursion. For example, if we are compiling the memcpy function, - // and llvm detects that the body is equivalent to memcpy, it may replace the - // body of memcpy with a call to memcpy, which would then cause a stack - // overflow instead of performing memcpy. - try attributes.addFnAttr(.{ .string = .{ - .kind = try o.builder.string("no-builtins"), - .value = .empty, - } }, &o.builder); - } if (owner_mod.optimize_mode == .ReleaseSmall) { try attributes.addFnAttr(.minsize, &o.builder); try attributes.addFnAttr(.optsize, &o.builder); @@ -4918,6 +4921,8 @@ pub const FuncGen = struct { sync_scope: Builder.SyncScope, + disable_intrinsics: bool, + const Fuzz = struct { counters_variable: Builder.Variable.Index, pcs: std.ArrayListUnmanaged(Builder.Constant), @@ -5443,7 +5448,7 @@ pub const FuncGen = struct { var attributes: Builder.FunctionAttributes.Wip = .{}; defer attributes.deinit(&o.builder); - if (self.ng.ownerModule().no_builtin) { + if (self.disable_intrinsics) { try attributes.addFnAttr(.nobuiltin, &o.builder); } @@ -5770,7 +5775,7 @@ pub const FuncGen = struct { try o.builder.intValue(.i8, 0xaa), len, if (ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal, - self.ng.ownerModule().no_builtin, + self.disable_intrinsics, ); const owner_mod = self.ng.ownerModule(); if (owner_mod.valgrind) { @@ -5821,7 +5826,7 @@ pub const FuncGen = struct { try o.builder.intValue(.i8, 0xaa), len, .normal, - self.ng.ownerModule().no_builtin, + self.disable_intrinsics, ); const owner_mod = self.ng.ownerModule(); if (owner_mod.valgrind) { @@ -9735,7 +9740,7 @@ pub const FuncGen = struct { if (safety) try o.builder.intValue(.i8, 0xaa) else try o.builder.undefValue(.i8), len, if (ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal, - self.ng.ownerModule().no_builtin, + self.disable_intrinsics, ); if (safety and owner_mod.valgrind) { try self.valgrindMarkUndef(dest_ptr, len); @@ -10057,7 +10062,7 @@ pub const FuncGen = struct { fill_byte, len, access_kind, - self.ng.ownerModule().no_builtin, + self.disable_intrinsics, ); } const owner_mod = self.ng.ownerModule(); @@ -10089,7 +10094,7 @@ pub const FuncGen = struct { fill_byte, len, access_kind, - self.ng.ownerModule().no_builtin, + self.disable_intrinsics, ); } return .none; @@ -10119,7 +10124,7 @@ pub const FuncGen = struct { fill_byte, len, access_kind, - self.ng.ownerModule().no_builtin, + self.disable_intrinsics, ); } return .none; @@ -10172,7 +10177,7 @@ pub const FuncGen = struct { elem_abi_align.toLlvm(), try o.builder.intValue(llvm_usize_ty, elem_abi_size), access_kind, - self.ng.ownerModule().no_builtin, + self.disable_intrinsics, ); } else _ = try self.wip.store(access_kind, value, it_ptr.toValue(), it_ptr_align); const next_ptr = try self.wip.gep(.inbounds, elem_llvm_ty, it_ptr.toValue(), &.{ @@ -10206,7 +10211,7 @@ pub const FuncGen = struct { fill_byte, len, access_kind, - self.ng.ownerModule().no_builtin, + self.disable_intrinsics, ); _ = try self.wip.br(end_block); self.wip.cursor = .{ .block = end_block }; @@ -10249,7 +10254,7 @@ pub const FuncGen = struct { src_ptr_ty.ptrAlignment(zcu).toLlvm(), len, access_kind, - self.ng.ownerModule().no_builtin, + self.disable_intrinsics, ); _ = try self.wip.br(end_block); self.wip.cursor = .{ .block = end_block }; @@ -10263,7 +10268,7 @@ pub const FuncGen = struct { src_ptr_ty.ptrAlignment(zcu).toLlvm(), len, access_kind, - self.ng.ownerModule().no_builtin, + self.disable_intrinsics, ); return .none; } @@ -11397,7 +11402,7 @@ pub const FuncGen = struct { ptr_alignment, try o.builder.intValue(try o.lowerType(Type.usize), size_bytes), access_kind, - fg.ng.ownerModule().no_builtin, + fg.disable_intrinsics, ); return result_ptr; } @@ -11565,7 +11570,7 @@ pub const FuncGen = struct { elem_ty.abiAlignment(zcu).toLlvm(), try o.builder.intValue(try o.lowerType(Type.usize), elem_ty.abiSize(zcu)), access_kind, - self.ng.ownerModule().no_builtin, + self.disable_intrinsics, ); } diff --git a/src/print_zir.zig b/src/print_zir.zig index 24a9663376..46f399dda9 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -531,6 +531,7 @@ const Writer = struct { .frame_address, .breakpoint, .disable_instrumentation, + .disable_intrinsics, .c_va_start, .in_comptime, .value_placeholder, |
