Commit 09588c795c

Andrew Kelley <andrew@ziglang.org>
2021-11-17 01:46:39
stage2: LLVM backend: memset to 0xaa for undefined stores
Also support `one` and `int_big_positive` tags for const pointers.
1 parent 68fe391
Changed files (5)
src/codegen/c.zig
@@ -279,7 +279,7 @@ pub const DeclGen = struct {
         ty: Type,
         val: Value,
     ) error{ OutOfMemory, AnalysisFail }!void {
-        if (val.isUndef()) {
+        if (val.isUndefDeep()) {
             switch (ty.zigTypeTag()) {
                 // Using '{}' for integer and floats seemed to error C compilers (both GCC and Clang)
                 // with 'error: expected expression' (including when built with 'zig cc')
@@ -1049,7 +1049,7 @@ pub fn genDecl(o: *Object) !void {
         }
         try fwd_decl_writer.writeAll(";\n");
 
-        if (variable.init.isUndef()) {
+        if (variable.init.isUndefDeep()) {
             return;
         }
 
@@ -1602,8 +1602,10 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
     const src_val = try f.resolveInst(bin_op.rhs);
     const lhs_type = f.air.typeOf(bin_op.lhs);
 
+    // TODO Sema should emit a different instruction when the store should
+    // possibly do the safety 0xaa bytes for undefined.
     const src_val_is_undefined =
-        if (f.air.value(bin_op.rhs)) |v| v.isUndef() else false;
+        if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false;
     if (src_val_is_undefined)
         return try airStoreUndefined(f, dest_ptr, lhs_type);
 
src/codegen/llvm.zig
@@ -1078,7 +1078,7 @@ pub const DeclGen = struct {
                     };
                     return self.context.constStruct(&fields, fields.len, .False);
                 },
-                .int_u64 => {
+                .int_u64, .one, .int_big_positive => {
                     const llvm_usize = try self.llvmType(Type.usize);
                     const llvm_int = llvm_usize.constInt(tv.val.toUnsignedInt(), .False);
                     return llvm_int.constIntToPtr(try self.llvmType(tv.ty));
@@ -3464,8 +3464,30 @@ pub const FuncGen = struct {
         const bin_op = self.air.instructions.items(.data)[inst].bin_op;
         const dest_ptr = try self.resolveInst(bin_op.lhs);
         const ptr_ty = self.air.typeOf(bin_op.lhs);
-        const src_operand = try self.resolveInst(bin_op.rhs);
-        self.store(dest_ptr, ptr_ty, src_operand, .NotAtomic);
+
+        // TODO Sema should emit a different instruction when the store should
+        // possibly do the safety 0xaa bytes for undefined.
+        const val_is_undef = if (self.air.value(bin_op.rhs)) |val| val.isUndefDeep() else false;
+        if (val_is_undef) {
+            const elem_ty = ptr_ty.childType();
+            const target = self.dg.module.getTarget();
+            const elem_size = elem_ty.abiSize(target);
+            const u8_llvm_ty = self.context.intType(8);
+            const ptr_u8_llvm_ty = u8_llvm_ty.pointerType(0);
+            const dest_ptr_u8 = self.builder.buildBitCast(dest_ptr, ptr_u8_llvm_ty, "");
+            const fill_char = u8_llvm_ty.constInt(0xaa, .False);
+            const dest_ptr_align = ptr_ty.ptrAlignment(target);
+            const usize_llvm_ty = try self.dg.llvmType(Type.usize);
+            const len = usize_llvm_ty.constInt(elem_size, .False);
+            _ = self.builder.buildMemSet(dest_ptr_u8, fill_char, len, dest_ptr_align, ptr_ty.isVolatilePtr());
+            if (self.dg.module.comp.bin_file.options.valgrind) {
+                // TODO generate valgrind client request to mark byte range as undefined
+                // see gen_valgrind_undef() in codegen.cpp
+            }
+        } else {
+            const src_operand = try self.resolveInst(bin_op.rhs);
+            self.store(dest_ptr, ptr_ty, src_operand, .NotAtomic);
+        }
         return null;
     }
 
@@ -3651,7 +3673,7 @@ pub const FuncGen = struct {
         const dest_ptr = try self.resolveInst(pl_op.operand);
         const ptr_ty = self.air.typeOf(pl_op.operand);
         const value = try self.resolveInst(extra.lhs);
-        const val_is_undef = if (self.air.value(extra.lhs)) |val| val.isUndef() else false;
+        const val_is_undef = if (self.air.value(extra.lhs)) |val| val.isUndefDeep() else false;
         const len = try self.resolveInst(extra.rhs);
         const u8_llvm_ty = self.context.intType(8);
         const ptr_u8_llvm_ty = u8_llvm_ty.pointerType(0);
src/value.zig
@@ -1802,6 +1802,13 @@ pub const Value = extern union {
         return self.tag() == .undef;
     }
 
+    /// TODO: check for cases such as array that is not marked undef but all the element
+    /// values are marked undef, or struct that is not marked undef but all fields are marked
+    /// undef, etc.
+    pub fn isUndefDeep(self: Value) bool {
+        return self.isUndef();
+    }
+
     /// Asserts the value is not undefined and not unreachable.
     /// Integer value 0 is considered null because of C pointers.
     pub fn isNull(self: Value) bool {
test/behavior/cast.zig
@@ -251,8 +251,13 @@ test "*const ?[*]const T to [*c]const [*c]const T" {
 test "array coersion to undefined at runtime" {
     @setRuntimeSafety(true);
 
-    // setRuntimeSafety isn't recognized on stage2
-    if (@import("builtin").zig_is_stage2 and @import("builtin").mode != .Debug and @import("builtin").mode != .ReleaseSafe) return error.SkipZigTest;
+    // TODO implement @setRuntimeSafety in stage2
+    if (@import("builtin").zig_is_stage2 and
+        @import("builtin").mode != .Debug and
+        @import("builtin").mode != .ReleaseSafe)
+    {
+        return error.SkipZigTest;
+    }
 
     var array = [4]u8{ 3, 4, 5, 6 };
     var undefined_val = [4]u8{ 0xAA, 0xAA, 0xAA, 0xAA };
test/behavior/int128.zig
@@ -20,8 +20,13 @@ test "uint128" {
 test "undefined 128 bit int" {
     @setRuntimeSafety(true);
 
-    // setRuntimeSafety isn't recognized on stage2
-    if (@import("builtin").zig_is_stage2 and @import("builtin").mode != .Debug and @import("builtin").mode != .ReleaseSafe) return error.SkipZigTest;
+    // TODO implement @setRuntimeSafety in stage2
+    if (@import("builtin").zig_is_stage2 and
+        @import("builtin").mode != .Debug and
+        @import("builtin").mode != .ReleaseSafe)
+    {
+        return error.SkipZigTest;
+    }
 
     var undef: u128 = undefined;
     var undef_signed: i128 = undefined;