authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2024-07-25 22:06:29+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-07-25 18:04:50-07:00
log1c35e73b614398529782f8c027366c6d8d51ac4b
tree250c777f79bd747416f105111592fc7c79a344d6
parent81a172a5064559d1f968f240204f8ce545852441

llvm: Don't emit safety memset() for stores of undef in Debug with safety off.

Before, this code: @setRuntimeSafety(false); var arr: [38]elf.Addr = undefined; would emit a call to memset() in the output code in Debug mode, while in all the release modes, LLVM optimized the memset() out as expected. Emitting the call in Debug mode is problematic in some contexts, e.g. in std.os.linux.start_pie where we are not yet ready to correctly perform calls because relocations haven't been applied yet, or in the early stages of a dynamic linker, etc.

1 files changed, 15 insertions(+), 4 deletions(-)

src/codegen/llvm.zig+15-4
...@@ -8997,6 +8997,21 @@ pub const FuncGen = struct {...@@ -8997,6 +8997,21 @@ pub const FuncGen = struct {
89978997
8998 const val_is_undef = if (try self.air.value(bin_op.rhs, pt)) |val| val.isUndefDeep(mod) else false;8998 const val_is_undef = if (try self.air.value(bin_op.rhs, pt)) |val| val.isUndefDeep(mod) else false;
8999 if (val_is_undef) {8999 if (val_is_undef) {
9000 const owner_mod = self.dg.ownerModule();
9001
9002 // Even if safety is disabled, we still emit a memset to undefined since it conveys
9003 // extra information to LLVM, and LLVM will optimize it out. Safety makes the difference
9004 // between using 0xaa or actual undefined for the fill byte.
9005 //
9006 // However, for Debug builds specifically, we avoid emitting the memset because LLVM
9007 // will neither use the information nor get rid of the memset, thus leaving an
9008 // unexpected call in the user's code. This is problematic if the code in question is
9009 // not ready to correctly make calls yet, such as in our early PIE startup code, or in
9010 // the early stages of a dynamic linker, etc.
9011 if (!safety and owner_mod.optimize_mode == .Debug) {
9012 return .none;
9013 }
9014
9000 const ptr_info = ptr_ty.ptrInfo(mod);9015 const ptr_info = ptr_ty.ptrInfo(mod);
9001 const needs_bitmask = (ptr_info.packed_offset.host_size != 0);9016 const needs_bitmask = (ptr_info.packed_offset.host_size != 0);
9002 if (needs_bitmask) {9017 if (needs_bitmask) {
...@@ -9006,9 +9021,6 @@ pub const FuncGen = struct {...@@ -9006,9 +9021,6 @@ pub const FuncGen = struct {
9006 return .none;9021 return .none;
9007 }9022 }
90089023
9009 // Even if safety is disabled, we still emit a memset to undefined since it conveys
9010 // extra information to LLVM. However, safety makes the difference between using
9011 // 0xaa or actual undefined for the fill byte.
9012 const len = try o.builder.intValue(try o.lowerType(Type.usize), operand_ty.abiSize(pt));9024 const len = try o.builder.intValue(try o.lowerType(Type.usize), operand_ty.abiSize(pt));
9013 _ = try self.wip.callMemSet(9025 _ = try self.wip.callMemSet(
9014 dest_ptr,9026 dest_ptr,
...@@ -9017,7 +9029,6 @@ pub const FuncGen = struct {...@@ -9017,7 +9029,6 @@ pub const FuncGen = struct {
9017 len,9029 len,
9018 if (ptr_ty.isVolatilePtr(mod)) .@"volatile" else .normal,9030 if (ptr_ty.isVolatilePtr(mod)) .@"volatile" else .normal,
9019 );9031 );
9020 const owner_mod = self.dg.ownerModule();
9021 if (safety and owner_mod.valgrind) {9032 if (safety and owner_mod.valgrind) {
9022 try self.valgrindMarkUndef(dest_ptr, len);9033 try self.valgrindMarkUndef(dest_ptr, len);
9023 }9034 }