authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-11-16 19:50:39-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-11-16 19:50:39-05:00
log0e8673f53415e367ee5db9e1384398e0905c5a35
treed7d2af6503aea7aa499c1a5eabf3c2f5a3591d3a
parent952d865bd231834adad30905c469edc5a46d000a
parent09588c795c08064971f61ee147d06972f0add94e
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10152 from drew-gpf/master

C backend: fix most cast and all pointer+generics behavior tests

8 files changed, 349 insertions(+), 101 deletions(-)

src/codegen/c.zig+165-36
...@@ -19,6 +19,7 @@ const Zir = @import("../Zir.zig");...@@ -19,6 +19,7 @@ const Zir = @import("../Zir.zig");
19const Liveness = @import("../Liveness.zig");19const Liveness = @import("../Liveness.zig");
2020
21const Mutability = enum { Const, Mut };21const Mutability = enum { Const, Mut };
22const BigIntConst = std.math.big.int.Const;
2223
23pub const CValue = union(enum) {24pub const CValue = union(enum) {
24 none: void,25 none: void,
...@@ -226,13 +227,59 @@ pub const DeclGen = struct {...@@ -226,13 +227,59 @@ pub const DeclGen = struct {
226 try dg.renderDeclName(decl, writer);227 try dg.renderDeclName(decl, writer);
227 }228 }
228229
230 fn renderInt128(
231 writer: anytype,
232 int_val: anytype,
233 ) error{ OutOfMemory, AnalysisFail }!void {
234 const int_info = @typeInfo(@TypeOf(int_val)).Int;
235 const is_signed = int_info.signedness == .signed;
236 const is_neg = int_val < 0;
237 comptime assert(int_info.bits > 64 and int_info.bits <= 128);
238
239 // Clang and GCC don't support 128-bit integer constants but will hopefully unfold them
240 // if we construct one manually.
241 const magnitude = std.math.absCast(int_val);
242
243 const high = @truncate(u64, magnitude >> 64);
244 const low = @truncate(u64, magnitude);
245
246 // (int128_t)/<->( ( (uint128_t)( val_high << 64 )u ) + (uint128_t)val_low/u )
247 if (is_signed) try writer.writeAll("(int128_t)");
248 if (is_neg) try writer.writeByte('-');
249
250 assert(high > 0);
251 try writer.print("(((uint128_t)0x{x}u<<64)", .{high});
252
253 if (low > 0)
254 try writer.print("+(uint128_t)0x{x}u", .{low});
255
256 return writer.writeByte(')');
257 }
258
259 fn renderBigIntConst(
260 dg: *DeclGen,
261 writer: anytype,
262 val: BigIntConst,
263 signed: bool,
264 ) error{ OutOfMemory, AnalysisFail }!void {
265 if (signed) {
266 try renderInt128(writer, val.to(i128) catch {
267 return dg.fail("TODO implement integer constants larger than 128 bits", .{});
268 });
269 } else {
270 try renderInt128(writer, val.to(u128) catch {
271 return dg.fail("TODO implement integer constants larger than 128 bits", .{});
272 });
273 }
274 }
275
229 fn renderValue(276 fn renderValue(
230 dg: *DeclGen,277 dg: *DeclGen,
231 writer: anytype,278 writer: anytype,
232 ty: Type,279 ty: Type,
233 val: Value,280 val: Value,
234 ) error{ OutOfMemory, AnalysisFail }!void {281 ) error{ OutOfMemory, AnalysisFail }!void {
235 if (val.isUndef()) {282 if (val.isUndefDeep()) {
236 switch (ty.zigTypeTag()) {283 switch (ty.zigTypeTag()) {
237 // 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)
238 // with 'error: expected expression' (including when built with 'zig cc')285 // with 'error: expected expression' (including when built with 'zig cc')
...@@ -240,18 +287,18 @@ pub const DeclGen = struct {...@@ -240,18 +287,18 @@ pub const DeclGen = struct {
240 const c_bits = toCIntBits(ty.intInfo(dg.module.getTarget()).bits) orelse287 const c_bits = toCIntBits(ty.intInfo(dg.module.getTarget()).bits) orelse
241 return dg.fail("TODO: C backend: implement integer types larger than 128 bits", .{});288 return dg.fail("TODO: C backend: implement integer types larger than 128 bits", .{});
242 switch (c_bits) {289 switch (c_bits) {
243 8 => return writer.writeAll("0xaaU"),290 8 => return writer.writeAll("0xaau"),
244 16 => return writer.writeAll("0xaaaaU"),291 16 => return writer.writeAll("0xaaaau"),
245 32 => return writer.writeAll("0xaaaaaaaaU"),292 32 => return writer.writeAll("0xaaaaaaaau"),
246 64 => return writer.writeAll("0xaaaaaaaaaaaaaaaaUL"),293 64 => return writer.writeAll("0xaaaaaaaaaaaaaaaau"),
247 128 => return writer.writeAll("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaULL"),294 128 => return renderInt128(writer, @as(u128, 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa)),
248 else => unreachable,295 else => unreachable,
249 }296 }
250 },297 },
251 .Float => {298 .Float => {
252 switch (ty.floatBits(dg.module.getTarget())) {299 switch (ty.floatBits(dg.module.getTarget())) {
253 32 => return writer.writeAll("zig_bitcast_f32_u32(0xaaaaaaaa)"),300 32 => return writer.writeAll("zig_bitcast_f32_u32(0xaaaaaaaau)"),
254 64 => return writer.writeAll("zig_bitcast_f64_u64(0xaaaaaaaaaaaaaaaa)"),301 64 => return writer.writeAll("zig_bitcast_f64_u64(0xaaaaaaaaaaaaaaaau)"),
255 else => return dg.fail("TODO float types > 64 bits are not support in renderValue() as of now", .{}),302 else => return dg.fail("TODO float types > 64 bits are not support in renderValue() as of now", .{}),
256 }303 }
257 },304 },
...@@ -265,10 +312,14 @@ pub const DeclGen = struct {...@@ -265,10 +312,14 @@ pub const DeclGen = struct {
265 }312 }
266 }313 }
267 switch (ty.zigTypeTag()) {314 switch (ty.zigTypeTag()) {
268 .Int => {315 .Int => switch (val.tag()) {
269 if (ty.isSignedInt())316 .int_big_positive => try dg.renderBigIntConst(writer, val.castTag(.int_big_positive).?.asBigInt(), ty.isSignedInt()),
270 return writer.print("{d}", .{val.toSignedInt()});317 .int_big_negative => try dg.renderBigIntConst(writer, val.castTag(.int_big_negative).?.asBigInt(), true),
271 return writer.print("{d}", .{val.toUnsignedInt()});318 else => {
319 if (ty.isSignedInt())
320 return writer.print("{d}", .{val.toSignedInt()});
321 return writer.print("{d}u", .{val.toUnsignedInt()});
322 },
272 },323 },
273 .Float => {324 .Float => {
274 if (ty.floatBits(dg.module.getTarget()) <= 64) {325 if (ty.floatBits(dg.module.getTarget()) <= 64) {
...@@ -286,8 +337,11 @@ pub const DeclGen = struct {...@@ -286,8 +337,11 @@ pub const DeclGen = struct {
286 return dg.fail("TODO: C backend: implement lowering large float values", .{});337 return dg.fail("TODO: C backend: implement lowering large float values", .{});
287 },338 },
288 .Pointer => switch (val.tag()) {339 .Pointer => switch (val.tag()) {
289 .null_value, .zero => try writer.writeAll("NULL"),340 .null_value => try writer.writeAll("NULL"),
290 .one => try writer.writeAll("1"),341 // Technically this should produce NULL but the integer literal 0 will always coerce
342 // to the assigned pointer type. Note this is just a hack to fix warnings from ordered comparisons (<, >, etc)
343 // between pointers and 0, which is an extension to begin with.
344 .zero => try writer.writeByte('0'),
291 .decl_ref => {345 .decl_ref => {
292 const decl = val.castTag(.decl_ref).?.data;346 const decl = val.castTag(.decl_ref).?.data;
293 return dg.renderDeclValue(writer, ty, val, decl);347 return dg.renderDeclValue(writer, ty, val, decl);
...@@ -316,6 +370,11 @@ pub const DeclGen = struct {...@@ -316,6 +370,11 @@ pub const DeclGen = struct {
316 const decl = val.castTag(.extern_fn).?.data;370 const decl = val.castTag(.extern_fn).?.data;
317 try dg.renderDeclName(decl, writer);371 try dg.renderDeclName(decl, writer);
318 },372 },
373 .int_u64, .one => {
374 try writer.writeAll("((");
375 try dg.renderType(writer, ty);
376 try writer.print(")0x{x}u)", .{val.toUnsignedInt()});
377 },
319 else => unreachable,378 else => unreachable,
320 },379 },
321 .Array => {380 .Array => {
...@@ -728,6 +787,8 @@ pub const DeclGen = struct {...@@ -728,6 +787,8 @@ pub const DeclGen = struct {
728 .i32 => try w.writeAll("int32_t"),787 .i32 => try w.writeAll("int32_t"),
729 .u64 => try w.writeAll("uint64_t"),788 .u64 => try w.writeAll("uint64_t"),
730 .i64 => try w.writeAll("int64_t"),789 .i64 => try w.writeAll("int64_t"),
790 .u128 => try w.writeAll("uint128_t"),
791 .i128 => try w.writeAll("int128_t"),
731 .usize => try w.writeAll("uintptr_t"),792 .usize => try w.writeAll("uintptr_t"),
732 .isize => try w.writeAll("intptr_t"),793 .isize => try w.writeAll("intptr_t"),
733 .c_short => try w.writeAll("short"),794 .c_short => try w.writeAll("short"),
...@@ -787,8 +848,9 @@ pub const DeclGen = struct {...@@ -787,8 +848,9 @@ pub const DeclGen = struct {
787 },848 },
788 .Array => {849 .Array => {
789 // We are referencing the array so it will decay to a C pointer.850 // We are referencing the array so it will decay to a C pointer.
790 try dg.renderType(w, t.elemType());851 // NB: arrays are not really types in C so they are either specified in the declaration
791 return w.writeAll(" *");852 // or are already pointed to; our only job is to render the element type.
853 return dg.renderType(w, t.elemType());
792 },854 },
793 .Optional => {855 .Optional => {
794 var opt_buf: Type.Payload.ElemType = undefined;856 var opt_buf: Type.Payload.ElemType = undefined;
...@@ -987,7 +1049,7 @@ pub fn genDecl(o: *Object) !void {...@@ -987,7 +1049,7 @@ pub fn genDecl(o: *Object) !void {
987 }1049 }
988 try fwd_decl_writer.writeAll(";\n");1050 try fwd_decl_writer.writeAll(";\n");
9891051
990 if (variable.init.isUndef()) {1052 if (variable.init.isUndefDeep()) {
991 return;1053 return;
992 }1054 }
9931055
...@@ -1070,10 +1132,12 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -1070,10 +1132,12 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
10701132
1071 // TODO use a different strategy for add that communicates to the optimizer1133 // TODO use a different strategy for add that communicates to the optimizer
1072 // that wrapping is UB.1134 // that wrapping is UB.
1073 .add, .ptr_add => try airBinOp (f, inst, " + "),1135 .add => try airBinOp (f, inst, " + "),
1136 .ptr_add => try airPtrAddSub (f, inst, " + "),
1074 // TODO use a different strategy for sub that communicates to the optimizer1137 // TODO use a different strategy for sub that communicates to the optimizer
1075 // that wrapping is UB.1138 // that wrapping is UB.
1076 .sub, .ptr_sub => try airBinOp (f, inst, " - "),1139 .sub => try airBinOp (f, inst, " - "),
1140 .ptr_sub => try airPtrAddSub (f, inst, " - "),
1077 // TODO use a different strategy for mul that communicates to the optimizer1141 // TODO use a different strategy for mul that communicates to the optimizer
1078 // that wrapping is UB.1142 // that wrapping is UB.
1079 .mul => try airBinOp (f, inst, " * "),1143 .mul => try airBinOp (f, inst, " * "),
...@@ -1187,7 +1251,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -1187,7 +1251,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
1187 .ptr_slice_len_ptr => try airPtrSliceFieldPtr(f, inst, ".len;\n"),1251 .ptr_slice_len_ptr => try airPtrSliceFieldPtr(f, inst, ".len;\n"),
1188 .ptr_slice_ptr_ptr => try airPtrSliceFieldPtr(f, inst, ".ptr;\n"),1252 .ptr_slice_ptr_ptr => try airPtrSliceFieldPtr(f, inst, ".ptr;\n"),
11891253
1190 .ptr_elem_val => try airPtrElemVal(f, inst, "["),1254 .ptr_elem_val => try airPtrElemVal(f, inst),
1191 .ptr_elem_ptr => try airPtrElemPtr(f, inst),1255 .ptr_elem_ptr => try airPtrElemPtr(f, inst),
1192 .slice_elem_val => try airSliceElemVal(f, inst),1256 .slice_elem_val => try airSliceElemVal(f, inst),
1193 .slice_elem_ptr => try airSliceElemPtr(f, inst),1257 .slice_elem_ptr => try airSliceElemPtr(f, inst),
...@@ -1240,20 +1304,39 @@ fn airPtrSliceFieldPtr(f: *Function, inst: Air.Inst.Index, suffix: []const u8) !...@@ -1240,20 +1304,39 @@ fn airPtrSliceFieldPtr(f: *Function, inst: Air.Inst.Index, suffix: []const u8) !
1240 return f.fail("TODO: C backend: airPtrSliceFieldPtr", .{});1304 return f.fail("TODO: C backend: airPtrSliceFieldPtr", .{});
1241}1305}
12421306
1243fn airPtrElemVal(f: *Function, inst: Air.Inst.Index, prefix: []const u8) !CValue {1307fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
1244 const is_volatile = false; // TODO1308 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
1245 if (!is_volatile and f.liveness.isUnused(inst))1309 const ptr_ty = f.air.typeOf(bin_op.lhs);
1246 return CValue.none;1310 if (!ptr_ty.isVolatilePtr() and f.liveness.isUnused(inst)) return CValue.none;
12471311
1248 _ = prefix;1312 const ptr = try f.resolveInst(bin_op.lhs);
1249 return f.fail("TODO: C backend: airPtrElemVal", .{});1313 const index = try f.resolveInst(bin_op.rhs);
1314 const writer = f.object.writer();
1315 const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const);
1316 try writer.writeAll(" = ");
1317 try f.writeCValue(writer, ptr);
1318 try writer.writeByte('[');
1319 try f.writeCValue(writer, index);
1320 try writer.writeAll("];\n");
1321 return local;
1250}1322}
12511323
1252fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {1324fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
1253 if (f.liveness.isUnused(inst))1325 if (f.liveness.isUnused(inst)) return CValue.none;
1254 return CValue.none;
12551326
1256 return f.fail("TODO: C backend: airPtrElemPtr", .{});1327 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
1328 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
1329
1330 const ptr = try f.resolveInst(bin_op.lhs);
1331 const index = try f.resolveInst(bin_op.rhs);
1332 const writer = f.object.writer();
1333 const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const);
1334 try writer.writeAll(" = &");
1335 try f.writeCValue(writer, ptr);
1336 try writer.writeByte('[');
1337 try f.writeCValue(writer, index);
1338 try writer.writeAll("];\n");
1339 return local;
1257}1340}
12581341
1259fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue {1342fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
...@@ -1317,6 +1400,10 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -1317,6 +1400,10 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
1317 const local = try f.allocLocal(elem_type, mutability);1400 const local = try f.allocLocal(elem_type, mutability);
1318 try writer.writeAll(";\n");1401 try writer.writeAll(";\n");
13191402
1403 // Arrays are already pointers so they don't need to be referenced.
1404 if (elem_type.zigTypeTag() == .Array)
1405 return CValue{ .local = local.local };
1406
1320 return CValue{ .local_ref = local.local };1407 return CValue{ .local_ref = local.local };
1321}1408}
13221409
...@@ -1344,6 +1431,8 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -1344,6 +1431,8 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
1344 if (!is_volatile and f.liveness.isUnused(inst))1431 if (!is_volatile and f.liveness.isUnused(inst))
1345 return CValue.none;1432 return CValue.none;
1346 const inst_ty = f.air.typeOfIndex(inst);1433 const inst_ty = f.air.typeOfIndex(inst);
1434 if (inst_ty.zigTypeTag() == .Array)
1435 return f.fail("TODO: C backend: implement airLoad for arrays", .{});
1347 const operand = try f.resolveInst(ty_op.operand);1436 const operand = try f.resolveInst(ty_op.operand);
1348 const writer = f.object.writer();1437 const writer = f.object.writer();
1349 const local = try f.allocLocal(inst_ty, .Const);1438 const local = try f.allocLocal(inst_ty, .Const);
...@@ -1470,7 +1559,7 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -1470,7 +1559,7 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {
1470 return local;1559 return local;
1471}1560}
14721561
1473fn airStoreUndefined(f: *Function, dest_ptr: CValue) !CValue {1562fn airStoreUndefined(f: *Function, dest_ptr: CValue, dest_type: Type) !CValue {
1474 const is_debug_build = f.object.dg.module.optimizeMode() == .Debug;1563 const is_debug_build = f.object.dg.module.optimizeMode() == .Debug;
1475 if (!is_debug_build)1564 if (!is_debug_build)
1476 return CValue.none;1565 return CValue.none;
...@@ -1494,9 +1583,11 @@ fn airStoreUndefined(f: *Function, dest_ptr: CValue) !CValue {...@@ -1494,9 +1583,11 @@ fn airStoreUndefined(f: *Function, dest_ptr: CValue) !CValue {
1494 try writer.writeAll("));\n");1583 try writer.writeAll("));\n");
1495 },1584 },
1496 else => {1585 else => {
1586 const indirection = if (dest_type.childType().zigTypeTag() == .Array) "" else "*";
1587
1497 try writer.writeAll("memset(");1588 try writer.writeAll("memset(");
1498 try f.writeCValue(writer, dest_ptr);1589 try f.writeCValue(writer, dest_ptr);
1499 try writer.writeAll(", 0xaa, sizeof(*");1590 try writer.print(", 0xaa, sizeof({s}", .{indirection});
1500 try f.writeCValue(writer, dest_ptr);1591 try f.writeCValue(writer, dest_ptr);
1501 try writer.writeAll("));\n");1592 try writer.writeAll("));\n");
1502 },1593 },
...@@ -1509,11 +1600,18 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -1509,11 +1600,18 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
1509 const bin_op = f.air.instructions.items(.data)[inst].bin_op;1600 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
1510 const dest_ptr = try f.resolveInst(bin_op.lhs);1601 const dest_ptr = try f.resolveInst(bin_op.lhs);
1511 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);
15121604
1605 // TODO Sema should emit a different instruction when the store should
1606 // possibly do the safety 0xaa bytes for undefined.
1513 const src_val_is_undefined =1607 const src_val_is_undefined =
1514 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;
1515 if (src_val_is_undefined)1609 if (src_val_is_undefined)
1516 return try airStoreUndefined(f, dest_ptr);1610 return try airStoreUndefined(f, dest_ptr, lhs_type);
1611
1612 // Don't check this for airStoreUndefined as that will work for arrays already
1613 if (lhs_type.childType().zigTypeTag() == .Array)
1614 return f.fail("TODO: C backend: implement airStore for arrays", .{});
15171615
1518 const writer = f.object.writer();1616 const writer = f.object.writer();
1519 switch (dest_ptr) {1617 switch (dest_ptr) {
...@@ -1810,6 +1908,33 @@ fn airBinOp(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue...@@ -1810,6 +1908,33 @@ fn airBinOp(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue
1810 return local;1908 return local;
1811}1909}
18121910
1911fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue {
1912 if (f.liveness.isUnused(inst))
1913 return CValue.none;
1914
1915 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
1916 const lhs = try f.resolveInst(bin_op.lhs);
1917 const rhs = try f.resolveInst(bin_op.rhs);
1918
1919 const writer = f.object.writer();
1920 const inst_ty = f.air.typeOfIndex(inst);
1921 const local = try f.allocLocal(inst_ty, .Const);
1922
1923 // We must convert to and from integer types to prevent UB if the operation results in a NULL pointer,
1924 // or if LHS is NULL. The operation is only UB if the result is NULL and then dereferenced.
1925 try writer.writeAll(" = (");
1926 try f.renderType(writer, inst_ty);
1927 try writer.writeAll(")(((uintptr_t)");
1928 try f.writeCValue(writer, lhs);
1929 try writer.print("){s}(", .{operator});
1930 try f.writeCValue(writer, rhs);
1931 try writer.writeAll("*sizeof(");
1932 try f.renderType(writer, inst_ty.childType());
1933 try writer.print(")));\n", .{});
1934
1935 return local;
1936}
1937
1813fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue {1938fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue {
1814 if (f.liveness.isUnused(inst)) return CValue.none;1939 if (f.liveness.isUnused(inst)) return CValue.none;
18151940
...@@ -2306,15 +2431,17 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc...@@ -2306,15 +2431,17 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc
2306 const writer = f.object.writer();2431 const writer = f.object.writer();
2307 const struct_obj = struct_ptr_ty.elemType().castTag(.@"struct").?.data;2432 const struct_obj = struct_ptr_ty.elemType().castTag(.@"struct").?.data;
2308 const field_name = struct_obj.fields.keys()[index];2433 const field_name = struct_obj.fields.keys()[index];
2434 const field_val = struct_obj.fields.values()[index];
2435 const addrof = if (field_val.ty.zigTypeTag() == .Array) "" else "&";
23092436
2310 const inst_ty = f.air.typeOfIndex(inst);2437 const inst_ty = f.air.typeOfIndex(inst);
2311 const local = try f.allocLocal(inst_ty, .Const);2438 const local = try f.allocLocal(inst_ty, .Const);
2312 switch (struct_ptr) {2439 switch (struct_ptr) {
2313 .local_ref => |i| {2440 .local_ref => |i| {
2314 try writer.print(" = &t{d}.{};\n", .{ i, fmtIdent(field_name) });2441 try writer.print(" = {s}t{d}.{};\n", .{ addrof, i, fmtIdent(field_name) });
2315 },2442 },
2316 else => {2443 else => {
2317 try writer.writeAll(" = &");2444 try writer.print(" = {s}", .{addrof});
2318 try f.writeCValue(writer, struct_ptr);2445 try f.writeCValue(writer, struct_ptr);
2319 try writer.print("->{};\n", .{fmtIdent(field_name)});2446 try writer.print("->{};\n", .{fmtIdent(field_name)});
2320 },2447 },
...@@ -2529,7 +2656,9 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -2529,7 +2656,9 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue {
2529 const writer = f.object.writer();2656 const writer = f.object.writer();
2530 const operand = try f.resolveInst(un_op);2657 const operand = try f.resolveInst(un_op);
25312658
2532 try writer.writeAll(" = ");2659 try writer.writeAll(" = (");
2660 try f.renderType(writer, inst_ty);
2661 try writer.writeAll(")");
2533 try f.writeCValue(writer, operand);2662 try f.writeCValue(writer, operand);
2534 try writer.writeAll(";\n");2663 try writer.writeAll(";\n");
2535 return local;2664 return local;
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.zig+6-5
...@@ -19,16 +19,18 @@ test {...@@ -19,16 +19,18 @@ test {
19 _ = @import("behavior/bugs/4769_b.zig");19 _ = @import("behavior/bugs/4769_b.zig");
20 _ = @import("behavior/bugs/6850.zig");20 _ = @import("behavior/bugs/6850.zig");
21 _ = @import("behavior/call.zig");21 _ = @import("behavior/call.zig");
22 _ = @import("behavior/cast.zig");
22 _ = @import("behavior/defer.zig");23 _ = @import("behavior/defer.zig");
23 _ = @import("behavior/enum.zig");24 _ = @import("behavior/enum.zig");
24 _ = @import("behavior/hasdecl.zig");25 _ = @import("behavior/hasdecl.zig");
25 _ = @import("behavior/hasfield.zig");26 _ = @import("behavior/hasfield.zig");
26 _ = @import("behavior/if.zig");27 _ = @import("behavior/if.zig");
27 _ = @import("behavior/struct.zig");28 _ = @import("behavior/int128.zig");
28 _ = @import("behavior/truncate.zig");
29 _ = @import("behavior/null.zig");29 _ = @import("behavior/null.zig");
30 _ = @import("behavior/pointers.zig");
30 _ = @import("behavior/ptrcast.zig");31 _ = @import("behavior/ptrcast.zig");
31 _ = @import("behavior/pub_enum.zig");32 _ = @import("behavior/pub_enum.zig");
33 _ = @import("behavior/struct.zig");
32 _ = @import("behavior/truncate.zig");34 _ = @import("behavior/truncate.zig");
33 _ = @import("behavior/underscore.zig");35 _ = @import("behavior/underscore.zig");
34 _ = @import("behavior/usingnamespace.zig");36 _ = @import("behavior/usingnamespace.zig");
...@@ -36,6 +38,7 @@ test {...@@ -36,6 +38,7 @@ test {
36 _ = @import("behavior/this.zig");38 _ = @import("behavior/this.zig");
37 _ = @import("behavior/member_func.zig");39 _ = @import("behavior/member_func.zig");
38 _ = @import("behavior/translate_c_macros.zig");40 _ = @import("behavior/translate_c_macros.zig");
41 _ = @import("behavior/generics.zig");
3942
40 if (builtin.object_format != .c) {43 if (builtin.object_format != .c) {
41 // Tests that pass for stage1 and stage2 but not the C backend.44 // Tests that pass for stage1 and stage2 but not the C backend.
...@@ -49,18 +52,16 @@ test {...@@ -49,18 +52,16 @@ test {
49 _ = @import("behavior/bugs/1741.zig");52 _ = @import("behavior/bugs/1741.zig");
50 _ = @import("behavior/bugs/2006.zig");53 _ = @import("behavior/bugs/2006.zig");
51 _ = @import("behavior/bugs/3112.zig");54 _ = @import("behavior/bugs/3112.zig");
52 _ = @import("behavior/cast.zig");55 _ = @import("behavior/cast_llvm.zig");
53 _ = @import("behavior/error.zig");56 _ = @import("behavior/error.zig");
54 _ = @import("behavior/eval.zig");57 _ = @import("behavior/eval.zig");
55 _ = @import("behavior/floatop.zig");58 _ = @import("behavior/floatop.zig");
56 _ = @import("behavior/fn.zig");59 _ = @import("behavior/fn.zig");
57 _ = @import("behavior/for.zig");60 _ = @import("behavior/for.zig");
58 _ = @import("behavior/generics.zig");
59 _ = @import("behavior/math.zig");61 _ = @import("behavior/math.zig");
60 _ = @import("behavior/maximum_minimum.zig");62 _ = @import("behavior/maximum_minimum.zig");
61 _ = @import("behavior/null_llvm.zig");63 _ = @import("behavior/null_llvm.zig");
62 _ = @import("behavior/optional.zig");64 _ = @import("behavior/optional.zig");
63 _ = @import("behavior/pointers.zig");
64 _ = @import("behavior/popcount.zig");65 _ = @import("behavior/popcount.zig");
65 _ = @import("behavior/saturating_arithmetic.zig");66 _ = @import("behavior/saturating_arithmetic.zig");
66 _ = @import("behavior/sizeof_and_typeof.zig");67 _ = @import("behavior/sizeof_and_typeof.zig");
test/behavior/cast.zig+19-52
...@@ -2,8 +2,6 @@ const std = @import("std");...@@ -2,8 +2,6 @@ const std = @import("std");
2const expect = std.testing.expect;2const expect = std.testing.expect;
3const mem = std.mem;3const mem = std.mem;
4const maxInt = std.math.maxInt;4const maxInt = std.math.maxInt;
5const Vector = std.meta.Vector;
6const native_endian = @import("builtin").target.cpu.arch.endian();
75
8test "int to ptr cast" {6test "int to ptr cast" {
9 const x = @as(usize, 13);7 const x = @as(usize, 13);
...@@ -66,18 +64,6 @@ test "implicit cast comptime_int to comptime_float" {...@@ -66,18 +64,6 @@ test "implicit cast comptime_int to comptime_float" {
66 try expect(2 == 2.0);64 try expect(2 == 2.0);
67}65}
6866
69test "pointer reinterpret const float to int" {
70 // The hex representation is 0x3fe3333333333303.
71 const float: f64 = 5.99999999999994648725e-01;
72 const float_ptr = &float;
73 const int_ptr = @ptrCast(*const i32, float_ptr);
74 const int_val = int_ptr.*;
75 if (native_endian == .Little)
76 try expect(int_val == 0x33333303)
77 else
78 try expect(int_val == 0x3fe33333);
79}
80
81test "comptime_int @intToFloat" {67test "comptime_int @intToFloat" {
82 {68 {
83 const result = @intToFloat(f16, 1234);69 const result = @intToFloat(f16, 1234);
...@@ -117,9 +103,6 @@ fn testFloatToInts() !void {...@@ -117,9 +103,6 @@ fn testFloatToInts() !void {
117 try expect(x == 10000);103 try expect(x == 10000);
118 const y = @floatToInt(i32, @as(f32, 1e4));104 const y = @floatToInt(i32, @as(f32, 1e4));
119 try expect(y == 10000);105 try expect(y == 10000);
120 try expectFloatToInt(f16, 255.1, u8, 255);
121 try expectFloatToInt(f16, 127.2, i8, 127);
122 try expectFloatToInt(f16, -128.2, i8, -128);
123 try expectFloatToInt(f32, 255.1, u8, 255);106 try expectFloatToInt(f32, 255.1, u8, 255);
124 try expectFloatToInt(f32, 127.2, i8, 127);107 try expectFloatToInt(f32, 127.2, i8, 127);
125 try expectFloatToInt(f32, -128.2, i8, -128);108 try expectFloatToInt(f32, -128.2, i8, -128);
...@@ -129,20 +112,6 @@ fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void {...@@ -129,20 +112,6 @@ fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void {
129 try expect(@floatToInt(I, f) == i);112 try expect(@floatToInt(I, f) == i);
130}113}
131114
132test "implicit cast from [*]T to ?*c_void" {
133 var a = [_]u8{ 3, 2, 1 };
134 var runtime_zero: usize = 0;
135 incrementVoidPtrArray(a[runtime_zero..].ptr, 3);
136 try expect(std.mem.eql(u8, &a, &[_]u8{ 4, 3, 2 }));
137}
138
139fn incrementVoidPtrArray(array: ?*c_void, len: usize) void {
140 var n: usize = 0;
141 while (n < len) : (n += 1) {
142 @ptrCast([*]u8, array.?)[n] += 1;
143 }
144}
145
146test "implicitly cast indirect pointer to maybe-indirect pointer" {115test "implicitly cast indirect pointer to maybe-indirect pointer" {
147 const S = struct {116 const S = struct {
148 const Self = @This();117 const Self = @This();
...@@ -232,27 +201,6 @@ test "*usize to *void" {...@@ -232,27 +201,6 @@ test "*usize to *void" {
232 v.* = {};201 v.* = {};
233}202}
234203
235test "compile time int to ptr of function" {
236 try foobar(FUNCTION_CONSTANT);
237}
238
239pub const FUNCTION_CONSTANT = @intToPtr(PFN_void, maxInt(usize));
240pub const PFN_void = fn (*c_void) callconv(.C) void;
241
242fn foobar(func: PFN_void) !void {
243 try std.testing.expect(@ptrToInt(func) == maxInt(usize));
244}
245
246test "implicit ptr to *c_void" {
247 var a: u32 = 1;
248 var ptr: *align(@alignOf(u32)) c_void = &a;
249 var b: *u32 = @ptrCast(*u32, ptr);
250 try expect(b.* == 1);
251 var ptr2: ?*align(@alignOf(u32)) c_void = &a;
252 var c: *u32 = @ptrCast(*u32, ptr2.?);
253 try expect(c.* == 1);
254}
255
256test "@intToEnum passed a comptime_int to an enum with one item" {204test "@intToEnum passed a comptime_int to an enum with one item" {
257 const E = enum { A };205 const E = enum { A };
258 const x = @intToEnum(E, 0);206 const x = @intToEnum(E, 0);
...@@ -299,3 +247,22 @@ test "*const ?[*]const T to [*c]const [*c]const T" {...@@ -299,3 +247,22 @@ test "*const ?[*]const T to [*c]const [*c]const T" {
299 try expect(b.*[0] == 'o');247 try expect(b.*[0] == 'o');
300 try expect(b[0][1] == 'k');248 try expect(b[0][1] == 'k');
301}249}
250
251test "array coersion to undefined at runtime" {
252 @setRuntimeSafety(true);
253
254 // TODO implement @setRuntimeSafety in stage2
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 }
261
262 var array = [4]u8{ 3, 4, 5, 6 };
263 var undefined_val = [4]u8{ 0xAA, 0xAA, 0xAA, 0xAA };
264
265 try expect(std.mem.eql(u8, &array, &array));
266 array = undefined;
267 try expect(std.mem.eql(u8, &array, &undefined_val));
268}
test/behavior/cast_llvm.zig created+67
...@@ -0,0 +1,67 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const mem = std.mem;
4const maxInt = std.math.maxInt;
5const native_endian = @import("builtin").target.cpu.arch.endian();
6
7test "pointer reinterpret const float to int" {
8 // The hex representation is 0x3fe3333333333303.
9 const float: f64 = 5.99999999999994648725e-01;
10 const float_ptr = &float;
11 const int_ptr = @ptrCast(*const i32, float_ptr);
12 const int_val = int_ptr.*;
13 if (native_endian == .Little)
14 try expect(int_val == 0x33333303)
15 else
16 try expect(int_val == 0x3fe33333);
17}
18
19test "@floatToInt" {
20 try testFloatToInts();
21 comptime try testFloatToInts();
22}
23
24fn testFloatToInts() !void {
25 try expectFloatToInt(f16, 255.1, u8, 255);
26 try expectFloatToInt(f16, 127.2, i8, 127);
27 try expectFloatToInt(f16, -128.2, i8, -128);
28}
29
30fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void {
31 try expect(@floatToInt(I, f) == i);
32}
33
34test "implicit cast from [*]T to ?*c_void" {
35 var a = [_]u8{ 3, 2, 1 };
36 var runtime_zero: usize = 0;
37 incrementVoidPtrArray(a[runtime_zero..].ptr, 3);
38 try expect(std.mem.eql(u8, &a, &[_]u8{ 4, 3, 2 }));
39}
40
41fn incrementVoidPtrArray(array: ?*c_void, len: usize) void {
42 var n: usize = 0;
43 while (n < len) : (n += 1) {
44 @ptrCast([*]u8, array.?)[n] += 1;
45 }
46}
47
48test "compile time int to ptr of function" {
49 try foobar(FUNCTION_CONSTANT);
50}
51
52pub const FUNCTION_CONSTANT = @intToPtr(PFN_void, maxInt(usize));
53pub const PFN_void = fn (*c_void) callconv(.C) void;
54
55fn foobar(func: PFN_void) !void {
56 try std.testing.expect(@ptrToInt(func) == maxInt(usize));
57}
58
59test "implicit ptr to *c_void" {
60 var a: u32 = 1;
61 var ptr: *align(@alignOf(u32)) c_void = &a;
62 var b: *u32 = @ptrCast(*u32, ptr);
63 try expect(b.* == 1);
64 var ptr2: ?*align(@alignOf(u32)) c_void = &a;
65 var c: *u32 = @ptrCast(*u32, ptr2.?);
66 try expect(c.* == 1);
67}
test/behavior/int128.zig created+51
...@@ -0,0 +1,51 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const maxInt = std.math.maxInt;
4const minInt = std.math.minInt;
5
6test "uint128" {
7 var buff: u128 = maxInt(u128);
8 try expect(buff == maxInt(u128));
9
10 const magic_const = 0x12341234123412341234123412341234;
11 buff = magic_const;
12
13 try expect(buff == magic_const);
14 try expect(magic_const == 0x12341234123412341234123412341234);
15
16 buff = 0;
17 try expect(buff == @as(u128, 0));
18}
19
20test "undefined 128 bit int" {
21 @setRuntimeSafety(true);
22
23 // TODO implement @setRuntimeSafety in stage2
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 }
30
31 var undef: u128 = undefined;
32 var undef_signed: i128 = undefined;
33 try expect(undef == 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa and @bitCast(u128, undef_signed) == undef);
34}
35
36test "int128" {
37 var buff: i128 = -1;
38 try expect(buff < 0 and (buff + 1) == 0);
39 try expect(@intCast(i8, buff) == @as(i8, -1));
40
41 buff = minInt(i128);
42 try expect(buff < 0);
43
44 buff = -0x12341234123412341234123412341234;
45 try expect(-buff == 0x12341234123412341234123412341234);
46}
47
48test "truncate int128" {
49 var buff: u128 = maxInt(u128);
50 try expect(@truncate(u64, buff) == maxInt(u64));
51}
test/behavior/pointers.zig+8-4
...@@ -61,12 +61,16 @@ test "initialize const optional C pointer to null" {...@@ -61,12 +61,16 @@ test "initialize const optional C pointer to null" {
6161
62test "assigning integer to C pointer" {62test "assigning integer to C pointer" {
63 var x: i32 = 0;63 var x: i32 = 0;
64 var y: i32 = 1;
64 var ptr: [*c]u8 = 0;65 var ptr: [*c]u8 = 0;
65 var ptr2: [*c]u8 = x;66 var ptr2: [*c]u8 = x;
66 if (false) {67 var ptr3: [*c]u8 = 1;
67 ptr;68 var ptr4: [*c]u8 = y;
68 ptr2;69
69 }70 try expect(ptr == ptr2);
71 try expect(ptr3 == ptr4);
72 try expect(ptr3 > ptr and ptr4 > ptr2 and y > x);
73 try expect(1 > ptr and y > ptr2 and 0 < ptr3 and x < ptr4);
70}74}
7175
72test "C pointer comparison and arithmetic" {76test "C pointer comparison and arithmetic" {