authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-18 14:08:06-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-02-18 14:08:06-05:00
log60bb1d4e1c262ff36c18cefe974aa2f773483af4
tree159ff6e30d4f74006783bfcd76a9de7fd0acedb4
parent736b9dcdd60b83c07bfbdb69c62facfbe10611f1
parent1639fd4c45ee816c7c67963fbff3513a32f5737b
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10906 from topolarity/cbe-array-support

stage2 CBE: Implement 2D array support

3 files changed, 364 insertions(+), 207 deletions(-)

src/codegen/c.zig+327-207
......@@ -34,6 +34,8 @@ pub const CValue = union(enum) {
3434 /// By-value
3535 decl: *Decl,
3636 decl_ref: *Decl,
37 /// An undefined (void *) pointer (cannot be dereferenced)
38 undefined_ptr: void,
3739 /// Render the slice as an identifier (using fmtIdent)
3840 identifier: []const u8,
3941 /// Render these bytes literally.
......@@ -291,6 +293,19 @@ pub const Function = struct {
291293 }
292294 }
293295
296 fn writeCValueDeref(f: *Function, w: anytype, c_value: CValue) !void {
297 switch (c_value) {
298 .constant => |inst| {
299 const ty = f.air.typeOf(inst);
300 const val = f.air.value(inst).?;
301 try w.writeAll("(*");
302 try f.object.dg.renderValue(w, ty, val);
303 return w.writeByte(')');
304 },
305 else => return f.object.dg.writeCValueDeref(w, c_value),
306 }
307 }
308
294309 fn fail(f: *Function, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
295310 return f.object.dg.fail(format, args);
296311 }
......@@ -298,6 +313,10 @@ pub const Function = struct {
298313 fn renderType(f: *Function, w: anytype, t: Type) !void {
299314 return f.object.dg.renderType(w, t);
300315 }
316
317 fn renderTypecast(f: *Function, w: anytype, t: Type) !void {
318 return f.object.dg.renderTypecast(w, t);
319 }
301320};
302321
303322/// This data is available when outputting .c code for a `Module`.
......@@ -352,10 +371,10 @@ pub const DeclGen = struct {
352371
353372 if (ty.isSlice()) {
354373 try writer.writeByte('(');
355 try dg.renderType(writer, ty);
374 try dg.renderTypecast(writer, ty);
356375 try writer.writeAll("){");
357376 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
358 try dg.renderValue(writer, ty.slicePtrFieldType(&buf), val);
377 try dg.renderValue(writer, ty.slicePtrFieldType(&buf), val.slicePtr());
359378 try writer.writeAll(", ");
360379 try writer.print("{d}", .{val.sliceLen()});
361380 try writer.writeAll("}");
......@@ -376,7 +395,7 @@ pub const DeclGen = struct {
376395 }
377396
378397 try writer.writeAll("((");
379 try dg.renderType(writer, ty);
398 try dg.renderTypecast(writer, ty);
380399 try writer.writeAll(")&");
381400 try dg.renderDeclName(decl, writer);
382401 try writer.writeByte(')');
......@@ -432,6 +451,57 @@ pub const DeclGen = struct {
432451 }
433452 }
434453
454 // Renders a "child" pointer (e.g. ElemPtr, FieldPtr) by recursing
455 // to the root decl/variable that acts as its parent
456 //
457 // Used for .elem_ptr, .field_ptr, .opt_payload_ptr, .eu_payload_ptr, since
458 // the Type of their container cannot be retrieved from their own Type
459 fn renderChildPtr(dg: *DeclGen, writer: anytype, ptr_val: Value) error{ OutOfMemory, AnalysisFail }!Type {
460 switch (ptr_val.tag()) {
461 .decl_ref_mut, .decl_ref, .variable => {
462 const decl = switch (ptr_val.tag()) {
463 .decl_ref => ptr_val.castTag(.decl_ref).?.data,
464 .decl_ref_mut => ptr_val.castTag(.decl_ref_mut).?.data.decl,
465 .variable => ptr_val.castTag(.variable).?.data.owner_decl,
466 else => unreachable,
467 };
468 try dg.renderDeclValue(writer, decl.ty, ptr_val, decl);
469 return decl.ty;
470 },
471 .field_ptr => {
472 const field_ptr = ptr_val.castTag(.field_ptr).?.data;
473 const index = field_ptr.field_index;
474
475 try writer.writeAll("&(");
476 const container_ty = try dg.renderChildPtr(writer, field_ptr.container_ptr);
477
478 const field_name = switch (container_ty.zigTypeTag()) {
479 .Struct => container_ty.structFields().keys()[index],
480 .Union => container_ty.unionFields().keys()[index],
481 else => unreachable,
482 };
483 const field_ty = switch (container_ty.zigTypeTag()) {
484 .Struct => container_ty.structFields().values()[index].ty,
485 .Union => container_ty.unionFields().values()[index].ty,
486 else => unreachable,
487 };
488 try writer.print(").{ }", .{fmtIdent(field_name)});
489
490 return field_ty;
491 },
492 .elem_ptr => {
493 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
494 try writer.writeAll("&(*");
495 const container_ty = try dg.renderChildPtr(writer, elem_ptr.array_ptr);
496 try writer.print(")[{d}]", .{elem_ptr.index});
497 return container_ty.childType();
498 },
499 .opt_payload_ptr => return dg.fail("implement renderChildPtr for optional payload", .{}),
500 .eu_payload_ptr => return dg.fail("implement renderChildPtr for error union payload", .{}),
501 else => unreachable,
502 }
503 }
504
435505 fn renderValue(
436506 dg: *DeclGen,
437507 writer: anytype,
......@@ -518,24 +588,15 @@ pub const DeclGen = struct {
518588 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
519589
520590 try writer.writeByte('(');
521 try dg.renderType(writer, ty);
591 try dg.renderTypecast(writer, ty);
522592 try writer.writeAll("){");
523593 try dg.renderValue(writer, ty.slicePtrFieldType(&buf), slice.ptr);
524594 try writer.writeAll(", ");
525595 try dg.renderValue(writer, Type.usize, slice.len);
526596 try writer.writeAll("}");
527597 },
528 .elem_ptr => {
529 const elem_ptr = val.castTag(.elem_ptr).?.data;
530 var arena = std.heap.ArenaAllocator.init(dg.module.gpa);
531 defer arena.deinit();
532 const elem_ptr_ty = try ty.elemPtrType(arena.allocator());
533
534 try writer.writeAll("(&((");
535 try dg.renderType(writer, ty);
536 try writer.writeByte(')');
537 try dg.renderValue(writer, elem_ptr_ty, elem_ptr.array_ptr);
538 try writer.print(")[{d}])", .{elem_ptr.index});
598 .field_ptr, .elem_ptr, .opt_payload_ptr, .eu_payload_ptr => {
599 _ = try dg.renderChildPtr(writer, val);
539600 },
540601 .function => {
541602 const func = val.castTag(.function).?.data;
......@@ -547,7 +608,7 @@ pub const DeclGen = struct {
547608 },
548609 .int_u64, .one => {
549610 try writer.writeAll("((");
550 try dg.renderType(writer, ty);
611 try dg.renderTypecast(writer, ty);
551612 try writer.print(")0x{x}u)", .{val.toUnsignedInt()});
552613 },
553614 else => unreachable,
......@@ -592,7 +653,7 @@ pub const DeclGen = struct {
592653 return writer.print("{}", .{is_null});
593654 }
594655 try writer.writeByte('(');
595 try dg.renderType(writer, ty);
656 try dg.renderTypecast(writer, ty);
596657 try writer.writeAll("){");
597658 if (val.castTag(.opt_payload)) |pl| {
598659 const payload_val = pl.data;
......@@ -628,7 +689,7 @@ pub const DeclGen = struct {
628689 }
629690
630691 try writer.writeByte('(');
631 try dg.renderType(writer, ty);
692 try dg.renderTypecast(writer, ty);
632693 try writer.writeAll("){");
633694 if (val.castTag(.eu_payload)) |pl| {
634695 const payload_val = pl.data;
......@@ -690,7 +751,7 @@ pub const DeclGen = struct {
690751 const field_vals = val.castTag(.@"struct").?.data;
691752
692753 try writer.writeAll("(");
693 try dg.renderType(writer, ty);
754 try dg.renderTypecast(writer, ty);
694755 try writer.writeAll("){");
695756
696757 for (field_vals) |field_val, i| {
......@@ -710,7 +771,7 @@ pub const DeclGen = struct {
710771 const layout = ty.unionGetLayout(target);
711772
712773 try writer.writeAll("(");
713 try dg.renderType(writer, ty);
774 try dg.renderTypecast(writer, ty);
714775 try writer.writeAll("){");
715776
716777 if (ty.unionTagType()) |tag_ty| {
......@@ -785,8 +846,9 @@ pub const DeclGen = struct {
785846 if (params_written > 0) {
786847 try w.writeAll(", ");
787848 }
788 try dg.renderType(w, dg.decl.ty.fnParamType(index));
789 try w.print(" a{d}", .{index});
849 const name = CValue{ .arg = index };
850 const alignment = Value.initTag(.abi_align_default);
851 try dg.renderTypeAndName(w, dg.decl.ty.fnParamType(index), name, .Mut, alignment);
790852 params_written += 1;
791853 }
792854
......@@ -824,7 +886,7 @@ pub const DeclGen = struct {
824886 if (params_written > 0) {
825887 try bw.writeAll(", ");
826888 }
827 try dg.renderType(bw, fn_info.param_types[index]);
889 try dg.renderTypecast(bw, fn_info.param_types[index]);
828890 params_written += 1;
829891 }
830892
......@@ -855,17 +917,16 @@ pub const DeclGen = struct {
855917 const bw = buffer.writer();
856918
857919 try bw.writeAll("typedef struct { ");
858 const elem_type = t.elemType();
859 try dg.renderType(bw, elem_type);
860 if (t.isConstPtr()) {
861 try bw.writeAll(" const");
862 }
863 if (t.isVolatilePtr()) {
864 try bw.writeAll(" volatile");
865 }
866 try bw.writeAll(" *");
867 try bw.writeAll("ptr; size_t len; } ");
920
921 var ptr_type_buf: Type.SlicePtrFieldTypeBuffer = undefined;
922 const ptr_type = t.slicePtrFieldType(&ptr_type_buf);
923 const ptr_name = CValue{ .bytes = "ptr" };
924 const ptr_alignment = Value.initTag(.abi_align_default);
925 try dg.renderTypeAndName(bw, ptr_type, ptr_name, .Mut, ptr_alignment);
926
927 try bw.writeAll("; size_t len; } ");
868928 const name_index = buffer.items.len;
929 const elem_type = t.elemType();
869930 if (t.isConstPtr()) {
870931 try bw.print("zig_L_{s};\n", .{typeToCIdentifier(elem_type)});
871932 } else {
......@@ -990,8 +1051,10 @@ pub const DeclGen = struct {
9901051 const bw = buffer.writer();
9911052
9921053 try bw.writeAll("typedef struct { ");
993 try dg.renderType(bw, child_type);
994 try bw.writeAll(" payload; uint16_t error; } ");
1054 const payload_name = CValue{ .bytes = "payload" };
1055 const alignment = Value.initTag(.abi_align_default);
1056 try dg.renderTypeAndName(bw, child_type, payload_name, .Mut, alignment);
1057 try bw.writeAll("; uint16_t error; } ");
9951058 const name_index = buffer.items.len;
9961059 if (err_set_type.castTag(.error_set_inferred)) |inf_err_set_payload| {
9971060 const func = inf_err_set_payload.data.func;
......@@ -1017,14 +1080,47 @@ pub const DeclGen = struct {
10171080 return name;
10181081 }
10191082
1083 fn renderArrayTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
1084 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
1085 defer buffer.deinit();
1086 const bw = buffer.writer();
1087
1088 const elem_type = t.elemType();
1089 const sentinel_bit = @boolToInt(t.sentinel() != null);
1090 const c_len = t.arrayLen() + sentinel_bit;
1091
1092 try bw.writeAll("typedef ");
1093 try dg.renderType(bw, elem_type);
1094
1095 const name_start = buffer.items.len + 1;
1096 try bw.print(" zig_A_{s}_{d}", .{ typeToCIdentifier(elem_type), c_len });
1097 const name_end = buffer.items.len;
1098
1099 try bw.print("[{d}];\n", .{c_len});
1100
1101 const rendered = buffer.toOwnedSlice();
1102 errdefer dg.typedefs.allocator.free(rendered);
1103 const name = rendered[name_start..name_end];
1104
1105 try dg.typedefs.ensureUnusedCapacity(1);
1106 dg.typedefs.putAssumeCapacityNoClobber(
1107 try t.copy(dg.typedefs_arena),
1108 .{ .name = name, .rendered = rendered },
1109 );
1110
1111 return name;
1112 }
1113
10201114 fn renderOptionalTypedef(dg: *DeclGen, t: Type, child_type: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
10211115 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
10221116 defer buffer.deinit();
10231117 const bw = buffer.writer();
10241118
10251119 try bw.writeAll("typedef struct { ");
1026 try dg.renderType(bw, child_type);
1027 try bw.writeAll(" payload; bool is_null; } ");
1120 const payload_name = CValue{ .bytes = "payload" };
1121 const alignment = Value.initTag(.abi_align_default);
1122 try dg.renderTypeAndName(bw, child_type, payload_name, .Mut, alignment);
1123 try bw.writeAll("; bool is_null; } ");
10281124 const name_index = buffer.items.len;
10291125 try bw.print("zig_Q_{s};\n", .{typeToCIdentifier(child_type)});
10301126
......@@ -1041,6 +1137,18 @@ pub const DeclGen = struct {
10411137 return name;
10421138 }
10431139
1140 /// Renders a type as a single identifier, generating intermediate typedefs
1141 /// if necessary.
1142 ///
1143 /// This is guaranteed to be valid in both typedefs and declarations/definitions.
1144 ///
1145 /// There are three type formats in total that we support rendering:
1146 /// | Function | Example 1 (*u8) | Example 2 ([10]*u8) |
1147 /// |---------------------|-----------------|---------------------|
1148 /// | `renderTypecast` | "uint8_t *" | "uint8_t *[10]" |
1149 /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" |
1150 /// | `renderType` | "uint8_t *" | "zig_A_uint8_t_10" |
1151 ///
10441152 fn renderType(dg: *DeclGen, w: anytype, t: Type) error{ OutOfMemory, AnalysisFail }!void {
10451153 const target = dg.module.getTarget();
10461154
......@@ -1117,10 +1225,10 @@ pub const DeclGen = struct {
11171225 return w.writeAll(" *");
11181226 },
11191227 .Array => {
1120 // We are referencing the array so it will decay to a C pointer.
1121 // NB: arrays are not really types in C so they are either specified in the declaration
1122 // or are already pointed to; our only job is to render the element type.
1123 return dg.renderType(w, t.elemType());
1228 const name = dg.getTypedefName(t) orelse
1229 try dg.renderArrayTypedef(t);
1230
1231 return w.writeAll(name);
11241232 },
11251233 .Optional => {
11261234 var opt_buf: Type.Payload.ElemType = undefined;
......@@ -1195,6 +1303,39 @@ pub const DeclGen = struct {
11951303 }
11961304 }
11971305
1306 /// Renders a type in C typecast format.
1307 ///
1308 /// This is guaranteed to be valid in a typecast expression, but not
1309 /// necessarily in a variable/field declaration.
1310 ///
1311 /// There are three type formats in total that we support rendering:
1312 /// | Function | Example 1 (*u8) | Example 2 ([10]*u8) |
1313 /// |---------------------|-----------------|---------------------|
1314 /// | `renderTypecast` | "uint8_t *" | "uint8_t *[10]" |
1315 /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" |
1316 /// | `renderType` | "uint8_t *" | "zig_A_uint8_t_10" |
1317 ///
1318 fn renderTypecast(
1319 dg: *DeclGen,
1320 w: anytype,
1321 ty: Type,
1322 //mutability: Mutability,
1323 //alignment: Value,
1324 ) error{ OutOfMemory, AnalysisFail }!void {
1325 const name = CValue{ .bytes = "" };
1326 const alignment = Value.initTag(.abi_align_default);
1327 return renderTypeAndName(dg, w, ty, name, .Mut, alignment);
1328 }
1329
1330 /// Renders a type and name in field declaration/definition format.
1331 ///
1332 /// There are three type formats in total that we support rendering:
1333 /// | Function | Example 1 (*u8) | Example 2 ([10]*u8) |
1334 /// |---------------------|-----------------|---------------------|
1335 /// | `renderTypecast` | "uint8_t *" | "uint8_t *[10]" |
1336 /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" |
1337 /// | `renderType` | "uint8_t *" | "zig_A_uint8_t_10" |
1338 ///
11981339 fn renderTypeAndName(
11991340 dg: *DeclGen,
12001341 w: anytype,
......@@ -1206,6 +1347,8 @@ pub const DeclGen = struct {
12061347 var suffix = std.ArrayList(u8).init(dg.gpa);
12071348 defer suffix.deinit();
12081349
1350 // Any top-level array types are rendered here as a suffix, which
1351 // avoids creating typedefs for every array type
12091352 var render_ty = ty;
12101353 while (render_ty.zigTypeTag() == .Array) {
12111354 const sentinel_bit = @boolToInt(render_ty.sentinel() != null);
......@@ -1254,11 +1397,42 @@ pub const DeclGen = struct {
12541397 try w.writeByte('&');
12551398 return dg.renderDeclName(decl, w);
12561399 },
1400 .undefined_ptr => {
1401 const target = dg.module.getTarget();
1402 switch (target.cpu.arch.ptrBitWidth()) {
1403 32 => try w.writeAll("(void *)0xaaaaaaaa"),
1404 64 => try w.writeAll("(void *)0xaaaaaaaaaaaaaaaa"),
1405 else => unreachable,
1406 }
1407 },
12571408 .identifier => |ident| return w.print("{ }", .{fmtIdent(ident)}),
12581409 .bytes => |bytes| return w.writeAll(bytes),
12591410 }
12601411 }
12611412
1413 fn writeCValueDeref(dg: DeclGen, w: anytype, c_value: CValue) !void {
1414 switch (c_value) {
1415 .none => unreachable,
1416 .local => |i| return w.print("(*t{d})", .{i}),
1417 .local_ref => |i| return w.print("t{d}", .{i}),
1418 .constant => unreachable,
1419 .arg => |i| return w.print("(*a{d})", .{i}),
1420 .decl => |decl| {
1421 try w.writeAll("(*");
1422 try dg.renderDeclName(decl, w);
1423 return w.writeByte(')');
1424 },
1425 .decl_ref => |decl| return dg.renderDeclName(decl, w),
1426 .undefined_ptr => unreachable,
1427 .identifier => |ident| return w.print("(*{ })", .{fmtIdent(ident)}),
1428 .bytes => |bytes| {
1429 try w.writeAll("(*");
1430 try w.writeAll(bytes);
1431 return w.writeByte(')');
1432 },
1433 }
1434 }
1435
12621436 fn renderDeclName(dg: DeclGen, decl: *Decl, writer: anytype) !void {
12631437 if (dg.module.decl_exports.get(decl)) |exports| {
12641438 return writer.writeAll(exports[0].options.name);
......@@ -1493,10 +1667,10 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
14931667 .optional_payload_ptr => try airOptionalPayload(f, inst),
14941668 .optional_payload_ptr_set => try airOptionalPayloadPtrSet(f, inst),
14951669
1496 .is_err => try airIsErr(f, inst, "", ".", "!="),
1497 .is_non_err => try airIsErr(f, inst, "", ".", "=="),
1498 .is_err_ptr => try airIsErr(f, inst, "*", "->", "!="),
1499 .is_non_err_ptr => try airIsErr(f, inst, "*", "->", "=="),
1670 .is_err => try airIsErr(f, inst, false, "!="),
1671 .is_non_err => try airIsErr(f, inst, false, "=="),
1672 .is_err_ptr => try airIsErr(f, inst, true, "!="),
1673 .is_non_err_ptr => try airIsErr(f, inst, true, "=="),
15001674
15011675 .is_null => try airIsNull(f, inst, "==", ""),
15021676 .is_non_null => try airIsNull(f, inst, "!=", ""),
......@@ -1641,14 +1815,21 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
16411815
16421816 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
16431817 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
1818 const ptr_ty = f.air.typeOf(bin_op.lhs);
16441819
16451820 const ptr = try f.resolveInst(bin_op.lhs);
16461821 const index = try f.resolveInst(bin_op.rhs);
16471822 const writer = f.object.writer();
16481823 const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const);
1649 try writer.writeAll(" = &");
1650 try f.writeCValue(writer, ptr);
1651 try writer.writeByte('[');
1824
1825 try writer.writeAll(" = &(");
1826 if (ptr_ty.ptrSize() == .One) {
1827 // It's a pointer to an array, so we need to de-reference.
1828 try f.writeCValueDeref(writer, ptr);
1829 } else {
1830 try f.writeCValue(writer, ptr);
1831 }
1832 try writer.writeAll(")[");
16521833 try f.writeCValue(writer, index);
16531834 try writer.writeAll("];\n");
16541835 return local;
......@@ -1712,13 +1893,7 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
17121893 const elem_type = inst_ty.elemType();
17131894 const mutability: Mutability = if (inst_ty.isConstPtr()) .Const else .Mut;
17141895 if (!elem_type.isFnOrHasRuntimeBits()) {
1715 const target = f.object.dg.module.getTarget();
1716 const literal = switch (target.cpu.arch.ptrBitWidth()) {
1717 32 => "(void *)0xaaaaaaaa",
1718 64 => "(void *)0xaaaaaaaaaaaaaaaa",
1719 else => unreachable,
1720 };
1721 return CValue{ .bytes = literal };
1896 return CValue.undefined_ptr;
17221897 }
17231898
17241899 const target = f.object.dg.module.getTarget();
......@@ -1733,10 +1908,6 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
17331908 const local = try f.allocAlignedLocal(elem_type, mutability, alignment_value);
17341909 try writer.writeAll(";\n");
17351910
1736 // Arrays are already pointers so they don't need to be referenced.
1737 if (elem_type.zigTypeTag() == .Array)
1738 return CValue{ .local = local.local };
1739
17401911 return CValue{ .local_ref = local.local };
17411912}
17421913
......@@ -1773,38 +1944,22 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
17731944 // We need to separately initialize arrays with a memcpy so they must be mutable.
17741945 const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const);
17751946
1776 switch (operand) {
1777 .local_ref => |i| {
1778 const wrapped: CValue = .{ .local = i };
1779 try writer.writeAll(" = ");
1780 try f.writeCValue(writer, wrapped);
1781 try writer.writeAll(";\n");
1782 },
1783 .decl_ref => |decl| {
1784 const wrapped: CValue = .{ .decl = decl };
1785 try writer.writeAll(" = ");
1786 try f.writeCValue(writer, wrapped);
1787 try writer.writeAll(";\n");
1788 },
1789 else => {
1790 if (is_array) {
1791 // Insert a memcpy to initialize this array. The source operand is always a pointer
1792 // and thus we only need to know size/type information from the local type/dest.
1793 try writer.writeAll(";");
1794 try f.object.indent_writer.insertNewline();
1795 try writer.writeAll("memcpy(");
1796 try f.writeCValue(writer, local);
1797 try writer.writeAll(", ");
1798 try f.writeCValue(writer, operand);
1799 try writer.writeAll(", sizeof(");
1800 try f.writeCValue(writer, local);
1801 try writer.writeAll("));\n");
1802 } else {
1803 try writer.writeAll(" = *");
1804 try f.writeCValue(writer, operand);
1805 try writer.writeAll(";\n");
1806 }
1807 },
1947 if (is_array) {
1948 // Insert a memcpy to initialize this array. The source operand is always a pointer
1949 // and thus we only need to know size/type information from the local type/dest.
1950 try writer.writeAll(";");
1951 try f.object.indent_writer.insertNewline();
1952 try writer.writeAll("memcpy(");
1953 try f.writeCValue(writer, local);
1954 try writer.writeAll(", ");
1955 try f.writeCValue(writer, operand);
1956 try writer.writeAll(", sizeof(");
1957 try f.writeCValue(writer, local);
1958 try writer.writeAll("));\n");
1959 } else {
1960 try writer.writeAll(" = ");
1961 try f.writeCValueDeref(writer, operand);
1962 try writer.writeAll(";\n");
18081963 }
18091964 return local;
18101965}
......@@ -1849,7 +2004,7 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {
18492004 const inst_ty = f.air.typeOfIndex(inst);
18502005 const local = try f.allocLocal(inst_ty, .Const);
18512006 try writer.writeAll(" = (");
1852 try f.renderType(writer, inst_ty);
2007 try f.renderTypecast(writer, inst_ty);
18532008 try writer.writeAll(")");
18542009 try f.writeCValue(writer, operand);
18552010 try writer.writeAll(";\n");
......@@ -1910,39 +2065,17 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {
19102065 return local;
19112066}
19122067
1913fn airStoreUndefined(f: *Function, dest_ptr: CValue, dest_child_type: Type) !CValue {
2068fn airStoreUndefined(f: *Function, dest_ptr: CValue) !CValue {
19142069 const is_debug_build = f.object.dg.module.optimizeMode() == .Debug;
19152070 if (!is_debug_build)
19162071 return CValue.none;
19172072
19182073 const writer = f.object.writer();
1919 switch (dest_ptr) {
1920 .local_ref => |i| {
1921 const dest: CValue = .{ .local = i };
1922 try writer.writeAll("memset(&");
1923 try f.writeCValue(writer, dest);
1924 try writer.writeAll(", 0xaa, sizeof(");
1925 try f.writeCValue(writer, dest);
1926 try writer.writeAll("));\n");
1927 },
1928 .decl_ref => |decl| {
1929 const dest: CValue = .{ .decl = decl };
1930 try writer.writeAll("memset(&");
1931 try f.writeCValue(writer, dest);
1932 try writer.writeAll(", 0xaa, sizeof(");
1933 try f.writeCValue(writer, dest);
1934 try writer.writeAll("));\n");
1935 },
1936 else => {
1937 const indirection = if (dest_child_type.zigTypeTag() == .Array) "" else "*";
1938
1939 try writer.writeAll("memset(");
1940 try f.writeCValue(writer, dest_ptr);
1941 try writer.print(", 0xaa, sizeof({s}", .{indirection});
1942 try f.writeCValue(writer, dest_ptr);
1943 try writer.writeAll("));\n");
1944 },
1945 }
2074 try writer.writeAll("memset(");
2075 try f.writeCValue(writer, dest_ptr);
2076 try writer.writeAll(", 0xaa, sizeof(");
2077 try f.writeCValueDeref(writer, dest_ptr);
2078 try writer.writeAll("));\n");
19462079 return CValue.none;
19472080}
19482081
......@@ -1958,59 +2091,40 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
19582091 const src_val_is_undefined =
19592092 if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false;
19602093 if (src_val_is_undefined)
1961 return try airStoreUndefined(f, dest_ptr, lhs_child_type);
2094 return try airStoreUndefined(f, dest_ptr);
19622095
19632096 const writer = f.object.writer();
1964 switch (dest_ptr) {
1965 .local_ref => |i| {
1966 const dest: CValue = .{ .local = i };
1967 try f.writeCValue(writer, dest);
1968 try writer.writeAll(" = ");
1969 try f.writeCValue(writer, src_val);
1970 try writer.writeAll(";\n");
1971 },
1972 .decl_ref => |decl| {
1973 const dest: CValue = .{ .decl = decl };
1974 try f.writeCValue(writer, dest);
2097 if (lhs_child_type.zigTypeTag() == .Array) {
2098 // For this memcpy to safely work we need the rhs to have the same
2099 // underlying type as the lhs (i.e. they must both be arrays of the same underlying type).
2100 const rhs_type = f.air.typeOf(bin_op.rhs);
2101 assert(rhs_type.eql(lhs_child_type));
2102
2103 // If the source is a constant, writeCValue will emit a brace initialization
2104 // so work around this by initializing into new local.
2105 // TODO this should be done by manually initializing elements of the dest array
2106 const array_src = if (src_val == .constant) blk: {
2107 const new_local = try f.allocLocal(rhs_type, .Const);
19752108 try writer.writeAll(" = ");
19762109 try f.writeCValue(writer, src_val);
1977 try writer.writeAll(";\n");
1978 },
1979 else => {
1980 if (lhs_child_type.zigTypeTag() == .Array) {
1981 // For this memcpy to safely work we need the rhs to have the same
1982 // underlying type as the lhs (i.e. they must both be arrays of the same underlying type).
1983 const rhs_type = f.air.typeOf(bin_op.rhs);
1984 assert(rhs_type.eql(lhs_child_type));
1985
1986 // If the source is a constant, writeCValue will emit a brace initialization
1987 // so work around this by initializing into new local.
1988 // TODO this should be done by manually initializing elements of the dest array
1989 const array_src = if (src_val == .constant) blk: {
1990 const new_local = try f.allocLocal(rhs_type, .Const);
1991 try writer.writeAll(" = ");
1992 try f.writeCValue(writer, src_val);
1993 try writer.writeAll(";");
1994 try f.object.indent_writer.insertNewline();
1995
1996 break :blk new_local;
1997 } else src_val;
1998
1999 try writer.writeAll("memcpy(");
2000 try f.writeCValue(writer, dest_ptr);
2001 try writer.writeAll(", ");
2002 try f.writeCValue(writer, array_src);
2003 try writer.writeAll(", sizeof(");
2004 try f.writeCValue(writer, array_src);
2005 try writer.writeAll("));\n");
2006 } else {
2007 try writer.writeAll("*");
2008 try f.writeCValue(writer, dest_ptr);
2009 try writer.writeAll(" = ");
2010 try f.writeCValue(writer, src_val);
2011 try writer.writeAll(";\n");
2012 }
2013 },
2110 try writer.writeAll(";");
2111 try f.object.indent_writer.insertNewline();
2112
2113 break :blk new_local;
2114 } else src_val;
2115
2116 try writer.writeAll("memcpy(");
2117 try f.writeCValue(writer, dest_ptr);
2118 try writer.writeAll(", ");
2119 try f.writeCValue(writer, array_src);
2120 try writer.writeAll(", sizeof(");
2121 try f.writeCValue(writer, array_src);
2122 try writer.writeAll("));\n");
2123 } else {
2124 try f.writeCValueDeref(writer, dest_ptr);
2125 try writer.writeAll(" = ");
2126 try f.writeCValue(writer, src_val);
2127 try writer.writeAll(";\n");
20142128 }
20152129 return CValue.none;
20162130}
......@@ -2366,17 +2480,24 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CV
23662480 const writer = f.object.writer();
23672481 const inst_ty = f.air.typeOfIndex(inst);
23682482 const local = try f.allocLocal(inst_ty, .Const);
2483 const elem_ty = switch (inst_ty.ptrSize()) {
2484 .One => blk: {
2485 const array_ty = inst_ty.childType();
2486 break :blk array_ty.childType();
2487 },
2488 else => inst_ty.childType(),
2489 };
23692490
23702491 // We must convert to and from integer types to prevent UB if the operation results in a NULL pointer,
23712492 // or if LHS is NULL. The operation is only UB if the result is NULL and then dereferenced.
23722493 try writer.writeAll(" = (");
2373 try f.renderType(writer, inst_ty);
2494 try f.renderTypecast(writer, inst_ty);
23742495 try writer.writeAll(")(((uintptr_t)");
23752496 try f.writeCValue(writer, lhs);
23762497 try writer.print("){s}(", .{operator});
23772498 try f.writeCValue(writer, rhs);
23782499 try writer.writeAll("*sizeof(");
2379 try f.renderType(writer, inst_ty.childType());
2500 try f.renderTypecast(writer, elem_ty);
23802501 try writer.print(")));\n", .{});
23812502
23822503 return local;
......@@ -2564,7 +2685,7 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
25642685 {
25652686 const local = try f.allocLocal(inst_ty, .Const);
25662687 try writer.writeAll(" = (");
2567 try f.renderType(writer, inst_ty);
2688 try f.renderTypecast(writer, inst_ty);
25682689
25692690 try writer.writeAll(")");
25702691 try f.writeCValue(writer, operand);
......@@ -2852,16 +2973,15 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
28522973 return operand;
28532974 }
28542975
2855 try writer.writeAll("(");
2856 try f.writeCValue(writer, operand);
2857 try writer.writeAll(")->is_null = false;\n");
2976 try f.writeCValueDeref(writer, operand);
2977 try writer.writeAll(".is_null = false;\n");
28582978
28592979 const inst_ty = f.air.typeOfIndex(inst);
28602980 const local = try f.allocLocal(inst_ty, .Const);
2861 try writer.writeAll(" = &(");
2862 try f.writeCValue(writer, operand);
2981 try writer.writeAll(" = &");
2982 try f.writeCValueDeref(writer, operand);
28632983
2864 try writer.writeAll(")->payload;\n");
2984 try writer.writeAll(".payload;\n");
28652985 return local;
28662986}
28672987
......@@ -2907,23 +3027,14 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc
29073027 },
29083028 else => unreachable,
29093029 }
2910 const addrof = if (field_val_ty.zigTypeTag() == .Array) "" else "&";
29113030 const payload = if (struct_ty.tag() == .union_tagged) "payload." else "";
29123031
29133032 const inst_ty = f.air.typeOfIndex(inst);
29143033 const local = try f.allocLocal(inst_ty, .Const);
2915 switch (struct_ptr) {
2916 .local_ref => |i| {
2917 try writer.print(" = {s}t{d}.{s}{ };\n", .{
2918 addrof, i, payload, fmtIdent(field_name),
2919 });
2920 },
2921 else => {
2922 try writer.print(" = {s}", .{addrof});
2923 try f.writeCValue(writer, struct_ptr);
2924 try writer.print("->{s}{ };\n", .{ payload, fmtIdent(field_name) });
2925 },
2926 }
3034
3035 try writer.print(" = &", .{});
3036 try f.writeCValueDeref(writer, struct_ptr);
3037 try writer.print(".{s}{ };\n", .{ payload, fmtIdent(field_name) });
29273038 return local;
29283039}
29293040
......@@ -2975,13 +3086,14 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
29753086 }
29763087 }
29773088
2978 const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else ".";
2979
29803089 const local = try f.allocLocal(inst_ty, .Const);
2981 try writer.writeAll(" = (");
2982 try f.writeCValue(writer, operand);
2983
2984 try writer.print("){s}error;\n", .{maybe_deref});
3090 try writer.writeAll(" = ");
3091 if (operand_ty.zigTypeTag() == .Pointer) {
3092 try f.writeCValueDeref(writer, operand);
3093 } else {
3094 try f.writeCValue(writer, operand);
3095 }
3096 try writer.writeAll(".error;\n");
29853097 return local;
29863098}
29873099
......@@ -3069,8 +3181,7 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
30693181fn airIsErr(
30703182 f: *Function,
30713183 inst: Air.Inst.Index,
3072 deref_prefix: [*:0]const u8,
3073 deref_suffix: [*:0]const u8,
3184 is_ptr: bool,
30743185 op_str: [*:0]const u8,
30753186) !CValue {
30763187 if (f.liveness.isUnused(inst))
......@@ -3082,15 +3193,16 @@ fn airIsErr(
30823193 const operand_ty = f.air.typeOf(un_op);
30833194 const local = try f.allocLocal(Type.initTag(.bool), .Const);
30843195 const payload_ty = operand_ty.errorUnionPayload();
3085 if (!payload_ty.hasRuntimeBits()) {
3086 try writer.print(" = {s}", .{deref_prefix});
3087 try f.writeCValue(writer, operand);
3088 try writer.print(" {s} 0;\n", .{op_str});
3196 try writer.writeAll(" = ");
3197 if (is_ptr) {
3198 try f.writeCValueDeref(writer, operand);
30893199 } else {
3090 try writer.writeAll(" = ");
30913200 try f.writeCValue(writer, operand);
3092 try writer.print("{s}error {s} 0;\n", .{ deref_suffix, op_str });
30933201 }
3202 if (payload_ty.hasRuntimeBits()) {
3203 try writer.writeAll(".error");
3204 }
3205 try writer.print(" {s} 0;\n", .{op_str});
30943206 return local;
30953207}
30963208
......@@ -3106,7 +3218,15 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
31063218 const array_len = f.air.typeOf(ty_op.operand).elemType().arrayLen();
31073219
31083220 try writer.writeAll(" = { .ptr = ");
3109 try f.writeCValue(writer, operand);
3221 if (operand == .undefined_ptr) {
3222 // Unfortunately, C does not support any equivalent to
3223 // &(*(void *)p)[0], although LLVM does via GetElementPtr
3224 try f.writeCValue(writer, CValue.undefined_ptr);
3225 } else {
3226 try writer.writeAll("&(");
3227 try f.writeCValueDeref(writer, operand);
3228 try writer.writeAll(")[0]");
3229 }
31103230 try writer.print(", .len = {d} }};\n", .{array_len});
31113231 return local;
31123232}
......@@ -3138,7 +3258,7 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue {
31383258 const operand = try f.resolveInst(un_op);
31393259
31403260 try writer.writeAll(" = (");
3141 try f.renderType(writer, inst_ty);
3261 try f.renderTypecast(writer, inst_ty);
31423262 try writer.writeAll(")");
31433263 try f.writeCValue(writer, operand);
31443264 try writer.writeAll(";\n");
test/behavior/basic.zig+32
......@@ -436,6 +436,38 @@ test "array 2D const double ptr" {
436436 try testArray2DConstDoublePtr(&rect_2d_vertexes[0][0]);
437437}
438438
439test "array 2D const double ptr with offset" {
440 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
441 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
442 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
443 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
444
445 const rect_2d_vertexes = [_][2]f32{
446 [_]f32{ 3.0, 4.239 },
447 [_]f32{ 1.0, 2.0 },
448 };
449 try testArray2DConstDoublePtr(&rect_2d_vertexes[1][0]);
450}
451
452test "array 3D const double ptr with offset" {
453 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
454 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
455 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
456 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
457
458 const rect_3d_vertexes = [_][2][2]f32{
459 [_][2]f32{
460 [_]f32{ 3.0, 4.239 },
461 [_]f32{ 3.5, 7.2 },
462 },
463 [_][2]f32{
464 [_]f32{ 3.0, 4.239 },
465 [_]f32{ 1.0, 2.0 },
466 },
467 };
468 try testArray2DConstDoublePtr(&rect_3d_vertexes[1][1][0]);
469}
470
439471fn testArray2DConstDoublePtr(ptr: *const f32) !void {
440472 const ptr2 = @ptrCast([*]const f32, ptr);
441473 try expect(ptr2[0] == 1.0);
test/behavior/undefined.zig+5
......@@ -1,4 +1,5 @@
11const std = @import("std");
2const builtin = @import("builtin");
23const expect = std.testing.expect;
34const mem = std.mem;
45
......@@ -12,6 +13,10 @@ fn initStaticArray() [10]i32 {
1213}
1314const static_array = initStaticArray();
1415test "init static array to undefined" {
16 // This test causes `initStaticArray()` to be codegen'd, and the
17 // C backend does not yet support returning arrays, so it fails
18 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
19
1520 try expect(static_array[0] == 1);
1621 try expect(static_array[4] == 2);
1722 try expect(static_array[7] == 3);