Commit 1c35e73b61

Alex Rønne Petersen <alex@alexrp.com>
2024-07-25 22:06:29
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 parent 81a172a
Changed files (1)
src
codegen
src/codegen/llvm.zig
@@ -8997,6 +8997,21 @@ pub const FuncGen = struct {
 
         const val_is_undef = if (try self.air.value(bin_op.rhs, pt)) |val| val.isUndefDeep(mod) else false;
         if (val_is_undef) {
+            const owner_mod = self.dg.ownerModule();
+
+            // Even if safety is disabled, we still emit a memset to undefined since it conveys
+            // extra information to LLVM, and LLVM will optimize it out. Safety makes the difference
+            // between using 0xaa or actual undefined for the fill byte.
+            //
+            // However, for Debug builds specifically, we avoid emitting the memset because LLVM
+            // will neither use the information nor get rid of the memset, thus leaving an
+            // unexpected call in the user's code. This is problematic if the code in question is
+            // not ready to correctly make calls yet, such as in our early PIE startup code, or in
+            // the early stages of a dynamic linker, etc.
+            if (!safety and owner_mod.optimize_mode == .Debug) {
+                return .none;
+            }
+
             const ptr_info = ptr_ty.ptrInfo(mod);
             const needs_bitmask = (ptr_info.packed_offset.host_size != 0);
             if (needs_bitmask) {
@@ -9006,9 +9021,6 @@ pub const FuncGen = struct {
                 return .none;
             }
 
-            // Even if safety is disabled, we still emit a memset to undefined since it conveys
-            // extra information to LLVM. However, safety makes the difference between using
-            // 0xaa or actual undefined for the fill byte.
             const len = try o.builder.intValue(try o.lowerType(Type.usize), operand_ty.abiSize(pt));
             _ = try self.wip.callMemSet(
                 dest_ptr,
@@ -9017,7 +9029,6 @@ pub const FuncGen = struct {
                 len,
                 if (ptr_ty.isVolatilePtr(mod)) .@"volatile" else .normal,
             );
-            const owner_mod = self.dg.ownerModule();
             if (safety and owner_mod.valgrind) {
                 try self.valgrindMarkUndef(dest_ptr, len);
             }