authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-11-16 17:46:39-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-11-16 17:46:39-07:00
log09588c795c08064971f61ee147d06972f0add94e
treed7d2af6503aea7aa499c1a5eabf3c2f5a3591d3a
parent68fe391de02fd4c99f39bd6e0af643e1e327e52a

stage2: LLVM backend: memset to 0xaa for undefined stores

Also support `one` and `int_big_positive` tags for const pointers.

5 files changed, 52 insertions(+), 11 deletions(-)

src/codegen/c.zig+5-3
...@@ -279,7 +279,7 @@ pub const DeclGen = struct {...@@ -279,7 +279,7 @@ pub const DeclGen = struct {
279 ty: Type,279 ty: Type,
280 val: Value,280 val: Value,
281 ) error{ OutOfMemory, AnalysisFail }!void {281 ) error{ OutOfMemory, AnalysisFail }!void {
282 if (val.isUndef()) {282 if (val.isUndefDeep()) {
283 switch (ty.zigTypeTag()) {283 switch (ty.zigTypeTag()) {
284 // Using '{}' for integer and floats seemed to error C compilers (both GCC and Clang)284 // Using '{}' for integer and floats seemed to error C compilers (both GCC and Clang)
285 // with 'error: expected expression' (including when built with 'zig cc')285 // with 'error: expected expression' (including when built with 'zig cc')
...@@ -1049,7 +1049,7 @@ pub fn genDecl(o: *Object) !void {...@@ -1049,7 +1049,7 @@ pub fn genDecl(o: *Object) !void {
1049 }1049 }
1050 try fwd_decl_writer.writeAll(";\n");1050 try fwd_decl_writer.writeAll(";\n");
10511051
1052 if (variable.init.isUndef()) {1052 if (variable.init.isUndefDeep()) {
1053 return;1053 return;
1054 }1054 }
10551055
...@@ -1602,8 +1602,10 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -1602,8 +1602,10 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
1602 const src_val = try f.resolveInst(bin_op.rhs);1602 const src_val = try f.resolveInst(bin_op.rhs);
1603 const lhs_type = f.air.typeOf(bin_op.lhs);1603 const lhs_type = f.air.typeOf(bin_op.lhs);
16041604
1605 // TODO Sema should emit a different instruction when the store should
1606 // possibly do the safety 0xaa bytes for undefined.
1605 const src_val_is_undefined =1607 const src_val_is_undefined =
1606 if (f.air.value(bin_op.rhs)) |v| v.isUndef() else false;1608 if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false;
1607 if (src_val_is_undefined)1609 if (src_val_is_undefined)
1608 return try airStoreUndefined(f, dest_ptr, lhs_type);1610 return try airStoreUndefined(f, dest_ptr, lhs_type);
16091611
src/codegen/llvm.zig+26-4
...@@ -1078,7 +1078,7 @@ pub const DeclGen = struct {...@@ -1078,7 +1078,7 @@ pub const DeclGen = struct {
1078 };1078 };
1079 return self.context.constStruct(&fields, fields.len, .False);1079 return self.context.constStruct(&fields, fields.len, .False);
1080 },1080 },
1081 .int_u64 => {1081 .int_u64, .one, .int_big_positive => {
1082 const llvm_usize = try self.llvmType(Type.usize);1082 const llvm_usize = try self.llvmType(Type.usize);
1083 const llvm_int = llvm_usize.constInt(tv.val.toUnsignedInt(), .False);1083 const llvm_int = llvm_usize.constInt(tv.val.toUnsignedInt(), .False);
1084 return llvm_int.constIntToPtr(try self.llvmType(tv.ty));1084 return llvm_int.constIntToPtr(try self.llvmType(tv.ty));
...@@ -3464,8 +3464,30 @@ pub const FuncGen = struct {...@@ -3464,8 +3464,30 @@ pub const FuncGen = struct {
3464 const bin_op = self.air.instructions.items(.data)[inst].bin_op;3464 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3465 const dest_ptr = try self.resolveInst(bin_op.lhs);3465 const dest_ptr = try self.resolveInst(bin_op.lhs);
3466 const ptr_ty = self.air.typeOf(bin_op.lhs);3466 const ptr_ty = self.air.typeOf(bin_op.lhs);
3467 const src_operand = try self.resolveInst(bin_op.rhs);3467
3468 self.store(dest_ptr, ptr_ty, src_operand, .NotAtomic);3468 // TODO Sema should emit a different instruction when the store should
3469 // possibly do the safety 0xaa bytes for undefined.
3470 const val_is_undef = if (self.air.value(bin_op.rhs)) |val| val.isUndefDeep() else false;
3471 if (val_is_undef) {
3472 const elem_ty = ptr_ty.childType();
3473 const target = self.dg.module.getTarget();
3474 const elem_size = elem_ty.abiSize(target);
3475 const u8_llvm_ty = self.context.intType(8);
3476 const ptr_u8_llvm_ty = u8_llvm_ty.pointerType(0);
3477 const dest_ptr_u8 = self.builder.buildBitCast(dest_ptr, ptr_u8_llvm_ty, "");
3478 const fill_char = u8_llvm_ty.constInt(0xaa, .False);
3479 const dest_ptr_align = ptr_ty.ptrAlignment(target);
3480 const usize_llvm_ty = try self.dg.llvmType(Type.usize);
3481 const len = usize_llvm_ty.constInt(elem_size, .False);
3482 _ = self.builder.buildMemSet(dest_ptr_u8, fill_char, len, dest_ptr_align, ptr_ty.isVolatilePtr());
3483 if (self.dg.module.comp.bin_file.options.valgrind) {
3484 // TODO generate valgrind client request to mark byte range as undefined
3485 // see gen_valgrind_undef() in codegen.cpp
3486 }
3487 } else {
3488 const src_operand = try self.resolveInst(bin_op.rhs);
3489 self.store(dest_ptr, ptr_ty, src_operand, .NotAtomic);
3490 }
3469 return null;3491 return null;
3470 }3492 }
34713493
...@@ -3651,7 +3673,7 @@ pub const FuncGen = struct {...@@ -3651,7 +3673,7 @@ pub const FuncGen = struct {
3651 const dest_ptr = try self.resolveInst(pl_op.operand);3673 const dest_ptr = try self.resolveInst(pl_op.operand);
3652 const ptr_ty = self.air.typeOf(pl_op.operand);3674 const ptr_ty = self.air.typeOf(pl_op.operand);
3653 const value = try self.resolveInst(extra.lhs);3675 const value = try self.resolveInst(extra.lhs);
3654 const val_is_undef = if (self.air.value(extra.lhs)) |val| val.isUndef() else false;3676 const val_is_undef = if (self.air.value(extra.lhs)) |val| val.isUndefDeep() else false;
3655 const len = try self.resolveInst(extra.rhs);3677 const len = try self.resolveInst(extra.rhs);
3656 const u8_llvm_ty = self.context.intType(8);3678 const u8_llvm_ty = self.context.intType(8);
3657 const ptr_u8_llvm_ty = u8_llvm_ty.pointerType(0);3679 const ptr_u8_llvm_ty = u8_llvm_ty.pointerType(0);
src/value.zig+7
...@@ -1802,6 +1802,13 @@ pub const Value = extern union {...@@ -1802,6 +1802,13 @@ pub const Value = extern union {
1802 return self.tag() == .undef;1802 return self.tag() == .undef;
1803 }1803 }
18041804
1805 /// TODO: check for cases such as array that is not marked undef but all the element
1806 /// values are marked undef, or struct that is not marked undef but all fields are marked
1807 /// undef, etc.
1808 pub fn isUndefDeep(self: Value) bool {
1809 return self.isUndef();
1810 }
1811
1805 /// Asserts the value is not undefined and not unreachable.1812 /// Asserts the value is not undefined and not unreachable.
1806 /// Integer value 0 is considered null because of C pointers.1813 /// Integer value 0 is considered null because of C pointers.
1807 pub fn isNull(self: Value) bool {1814 pub fn isNull(self: Value) bool {
test/behavior/cast.zig+7-2
...@@ -251,8 +251,13 @@ test "*const ?[*]const T to [*c]const [*c]const T" {...@@ -251,8 +251,13 @@ test "*const ?[*]const T to [*c]const [*c]const T" {
251test "array coersion to undefined at runtime" {251test "array coersion to undefined at runtime" {
252 @setRuntimeSafety(true);252 @setRuntimeSafety(true);
253253
254 // setRuntimeSafety isn't recognized on stage2254 // TODO implement @setRuntimeSafety in stage2
255 if (@import("builtin").zig_is_stage2 and @import("builtin").mode != .Debug and @import("builtin").mode != .ReleaseSafe) return error.SkipZigTest;255 if (@import("builtin").zig_is_stage2 and
256 @import("builtin").mode != .Debug and
257 @import("builtin").mode != .ReleaseSafe)
258 {
259 return error.SkipZigTest;
260 }
256261
257 var array = [4]u8{ 3, 4, 5, 6 };262 var array = [4]u8{ 3, 4, 5, 6 };
258 var undefined_val = [4]u8{ 0xAA, 0xAA, 0xAA, 0xAA };263 var undefined_val = [4]u8{ 0xAA, 0xAA, 0xAA, 0xAA };
test/behavior/int128.zig+7-2
...@@ -20,8 +20,13 @@ test "uint128" {...@@ -20,8 +20,13 @@ test "uint128" {
20test "undefined 128 bit int" {20test "undefined 128 bit int" {
21 @setRuntimeSafety(true);21 @setRuntimeSafety(true);
2222
23 // setRuntimeSafety isn't recognized on stage223 // TODO implement @setRuntimeSafety in stage2
24 if (@import("builtin").zig_is_stage2 and @import("builtin").mode != .Debug and @import("builtin").mode != .ReleaseSafe) return error.SkipZigTest;24 if (@import("builtin").zig_is_stage2 and
25 @import("builtin").mode != .Debug and
26 @import("builtin").mode != .ReleaseSafe)
27 {
28 return error.SkipZigTest;
29 }
2530
26 var undef: u128 = undefined;31 var undef: u128 = undefined;
27 var undef_signed: i128 = undefined;32 var undef_signed: i128 = undefined;