authorgravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-02-16 11:40:33-07:00
committergravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-02-16 12:57:11-07:00
log1639fd4c45ee816c7c67963fbff3513a32f5737b
treee39a4ed7294dbe274d296484485eedadf91ac17d
parentc4baa6696e3580455438c756a023cafe645b91b5

Fix 2D array support for C backend

This updates the C backend to use proper array types. In order to do that, this commit also: - fixes up elem_ptr and field_ptr handling - adds `renderTypecast` (renders in C typecast format, e.g. "int* [10]") - adds a bit special handling for undefined pointers, which is necessary to support slice/elem_ptr to undefined decls

3 files changed, 298 insertions(+), 149 deletions(-)

src/codegen/c.zig+261-149
......@@ -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.
......@@ -311,6 +313,10 @@ pub const Function = struct {
311313 fn renderType(f: *Function, w: anytype, t: Type) !void {
312314 return f.object.dg.renderType(w, t);
313315 }
316
317 fn renderTypecast(f: *Function, w: anytype, t: Type) !void {
318 return f.object.dg.renderTypecast(w, t);
319 }
314320};
315321
316322/// This data is available when outputting .c code for a `Module`.
......@@ -365,10 +371,10 @@ pub const DeclGen = struct {
365371
366372 if (ty.isSlice()) {
367373 try writer.writeByte('(');
368 try dg.renderType(writer, ty);
374 try dg.renderTypecast(writer, ty);
369375 try writer.writeAll("){");
370376 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
371 try dg.renderValue(writer, ty.slicePtrFieldType(&buf), val);
377 try dg.renderValue(writer, ty.slicePtrFieldType(&buf), val.slicePtr());
372378 try writer.writeAll(", ");
373379 try writer.print("{d}", .{val.sliceLen()});
374380 try writer.writeAll("}");
......@@ -389,7 +395,7 @@ pub const DeclGen = struct {
389395 }
390396
391397 try writer.writeAll("((");
392 try dg.renderType(writer, ty);
398 try dg.renderTypecast(writer, ty);
393399 try writer.writeAll(")&");
394400 try dg.renderDeclName(decl, writer);
395401 try writer.writeByte(')');
......@@ -445,6 +451,57 @@ pub const DeclGen = struct {
445451 }
446452 }
447453
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
448505 fn renderValue(
449506 dg: *DeclGen,
450507 writer: anytype,
......@@ -531,24 +588,15 @@ pub const DeclGen = struct {
531588 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
532589
533590 try writer.writeByte('(');
534 try dg.renderType(writer, ty);
591 try dg.renderTypecast(writer, ty);
535592 try writer.writeAll("){");
536593 try dg.renderValue(writer, ty.slicePtrFieldType(&buf), slice.ptr);
537594 try writer.writeAll(", ");
538595 try dg.renderValue(writer, Type.usize, slice.len);
539596 try writer.writeAll("}");
540597 },
541 .elem_ptr => {
542 const elem_ptr = val.castTag(.elem_ptr).?.data;
543 var arena = std.heap.ArenaAllocator.init(dg.module.gpa);
544 defer arena.deinit();
545 const elem_ptr_ty = try ty.elemPtrType(arena.allocator());
546
547 try writer.writeAll("(&((");
548 try dg.renderType(writer, ty);
549 try writer.writeByte(')');
550 try dg.renderValue(writer, elem_ptr_ty, elem_ptr.array_ptr);
551 try writer.print(")[{d}])", .{elem_ptr.index});
598 .field_ptr, .elem_ptr, .opt_payload_ptr, .eu_payload_ptr => {
599 _ = try dg.renderChildPtr(writer, val);
552600 },
553601 .function => {
554602 const func = val.castTag(.function).?.data;
......@@ -560,7 +608,7 @@ pub const DeclGen = struct {
560608 },
561609 .int_u64, .one => {
562610 try writer.writeAll("((");
563 try dg.renderType(writer, ty);
611 try dg.renderTypecast(writer, ty);
564612 try writer.print(")0x{x}u)", .{val.toUnsignedInt()});
565613 },
566614 else => unreachable,
......@@ -605,7 +653,7 @@ pub const DeclGen = struct {
605653 return writer.print("{}", .{is_null});
606654 }
607655 try writer.writeByte('(');
608 try dg.renderType(writer, ty);
656 try dg.renderTypecast(writer, ty);
609657 try writer.writeAll("){");
610658 if (val.castTag(.opt_payload)) |pl| {
611659 const payload_val = pl.data;
......@@ -641,7 +689,7 @@ pub const DeclGen = struct {
641689 }
642690
643691 try writer.writeByte('(');
644 try dg.renderType(writer, ty);
692 try dg.renderTypecast(writer, ty);
645693 try writer.writeAll("){");
646694 if (val.castTag(.eu_payload)) |pl| {
647695 const payload_val = pl.data;
......@@ -703,7 +751,7 @@ pub const DeclGen = struct {
703751 const field_vals = val.castTag(.@"struct").?.data;
704752
705753 try writer.writeAll("(");
706 try dg.renderType(writer, ty);
754 try dg.renderTypecast(writer, ty);
707755 try writer.writeAll("){");
708756
709757 for (field_vals) |field_val, i| {
......@@ -723,7 +771,7 @@ pub const DeclGen = struct {
723771 const layout = ty.unionGetLayout(target);
724772
725773 try writer.writeAll("(");
726 try dg.renderType(writer, ty);
774 try dg.renderTypecast(writer, ty);
727775 try writer.writeAll("){");
728776
729777 if (ty.unionTagType()) |tag_ty| {
......@@ -798,8 +846,9 @@ pub const DeclGen = struct {
798846 if (params_written > 0) {
799847 try w.writeAll(", ");
800848 }
801 try dg.renderType(w, dg.decl.ty.fnParamType(index));
802 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);
803852 params_written += 1;
804853 }
805854
......@@ -837,7 +886,7 @@ pub const DeclGen = struct {
837886 if (params_written > 0) {
838887 try bw.writeAll(", ");
839888 }
840 try dg.renderType(bw, fn_info.param_types[index]);
889 try dg.renderTypecast(bw, fn_info.param_types[index]);
841890 params_written += 1;
842891 }
843892
......@@ -868,17 +917,16 @@ pub const DeclGen = struct {
868917 const bw = buffer.writer();
869918
870919 try bw.writeAll("typedef struct { ");
871 const elem_type = t.elemType();
872 try dg.renderType(bw, elem_type);
873 if (t.isConstPtr()) {
874 try bw.writeAll(" const");
875 }
876 if (t.isVolatilePtr()) {
877 try bw.writeAll(" volatile");
878 }
879 try bw.writeAll(" *");
880 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; } ");
881928 const name_index = buffer.items.len;
929 const elem_type = t.elemType();
882930 if (t.isConstPtr()) {
883931 try bw.print("zig_L_{s};\n", .{typeToCIdentifier(elem_type)});
884932 } else {
......@@ -1003,8 +1051,10 @@ pub const DeclGen = struct {
10031051 const bw = buffer.writer();
10041052
10051053 try bw.writeAll("typedef struct { ");
1006 try dg.renderType(bw, child_type);
1007 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; } ");
10081058 const name_index = buffer.items.len;
10091059 if (err_set_type.castTag(.error_set_inferred)) |inf_err_set_payload| {
10101060 const func = inf_err_set_payload.data.func;
......@@ -1030,14 +1080,47 @@ pub const DeclGen = struct {
10301080 return name;
10311081 }
10321082
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
10331114 fn renderOptionalTypedef(dg: *DeclGen, t: Type, child_type: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
10341115 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
10351116 defer buffer.deinit();
10361117 const bw = buffer.writer();
10371118
10381119 try bw.writeAll("typedef struct { ");
1039 try dg.renderType(bw, child_type);
1040 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; } ");
10411124 const name_index = buffer.items.len;
10421125 try bw.print("zig_Q_{s};\n", .{typeToCIdentifier(child_type)});
10431126
......@@ -1054,6 +1137,18 @@ pub const DeclGen = struct {
10541137 return name;
10551138 }
10561139
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 ///
10571152 fn renderType(dg: *DeclGen, w: anytype, t: Type) error{ OutOfMemory, AnalysisFail }!void {
10581153 const target = dg.module.getTarget();
10591154
......@@ -1130,10 +1225,10 @@ pub const DeclGen = struct {
11301225 return w.writeAll(" *");
11311226 },
11321227 .Array => {
1133 // We are referencing the array so it will decay to a C pointer.
1134 // NB: arrays are not really types in C so they are either specified in the declaration
1135 // or are already pointed to; our only job is to render the element type.
1136 return dg.renderType(w, t.elemType());
1228 const name = dg.getTypedefName(t) orelse
1229 try dg.renderArrayTypedef(t);
1230
1231 return w.writeAll(name);
11371232 },
11381233 .Optional => {
11391234 var opt_buf: Type.Payload.ElemType = undefined;
......@@ -1208,6 +1303,39 @@ pub const DeclGen = struct {
12081303 }
12091304 }
12101305
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 ///
12111339 fn renderTypeAndName(
12121340 dg: *DeclGen,
12131341 w: anytype,
......@@ -1219,6 +1347,8 @@ pub const DeclGen = struct {
12191347 var suffix = std.ArrayList(u8).init(dg.gpa);
12201348 defer suffix.deinit();
12211349
1350 // Any top-level array types are rendered here as a suffix, which
1351 // avoids creating typedefs for every array type
12221352 var render_ty = ty;
12231353 while (render_ty.zigTypeTag() == .Array) {
12241354 const sentinel_bit = @boolToInt(render_ty.sentinel() != null);
......@@ -1267,6 +1397,14 @@ pub const DeclGen = struct {
12671397 try w.writeByte('&');
12681398 return dg.renderDeclName(decl, w);
12691399 },
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 },
12701408 .identifier => |ident| return w.print("{ }", .{fmtIdent(ident)}),
12711409 .bytes => |bytes| return w.writeAll(bytes),
12721410 }
......@@ -1285,6 +1423,7 @@ pub const DeclGen = struct {
12851423 return w.writeByte(')');
12861424 },
12871425 .decl_ref => |decl| return dg.renderDeclName(decl, w),
1426 .undefined_ptr => unreachable,
12881427 .identifier => |ident| return w.print("(*{ })", .{fmtIdent(ident)}),
12891428 .bytes => |bytes| {
12901429 try w.writeAll("(*");
......@@ -1676,14 +1815,21 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
16761815
16771816 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
16781817 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
1818 const ptr_ty = f.air.typeOf(bin_op.lhs);
16791819
16801820 const ptr = try f.resolveInst(bin_op.lhs);
16811821 const index = try f.resolveInst(bin_op.rhs);
16821822 const writer = f.object.writer();
16831823 const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const);
1684 try writer.writeAll(" = &");
1685 try f.writeCValue(writer, ptr);
1686 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(")[");
16871833 try f.writeCValue(writer, index);
16881834 try writer.writeAll("];\n");
16891835 return local;
......@@ -1747,13 +1893,7 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
17471893 const elem_type = inst_ty.elemType();
17481894 const mutability: Mutability = if (inst_ty.isConstPtr()) .Const else .Mut;
17491895 if (!elem_type.isFnOrHasRuntimeBits()) {
1750 const target = f.object.dg.module.getTarget();
1751 const literal = switch (target.cpu.arch.ptrBitWidth()) {
1752 32 => "(void *)0xaaaaaaaa",
1753 64 => "(void *)0xaaaaaaaaaaaaaaaa",
1754 else => unreachable,
1755 };
1756 return CValue{ .bytes = literal };
1896 return CValue.undefined_ptr;
17571897 }
17581898
17591899 const target = f.object.dg.module.getTarget();
......@@ -1768,10 +1908,6 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
17681908 const local = try f.allocAlignedLocal(elem_type, mutability, alignment_value);
17691909 try writer.writeAll(";\n");
17701910
1771 // Arrays are already pointers so they don't need to be referenced.
1772 if (elem_type.zigTypeTag() == .Array)
1773 return CValue{ .local = local.local };
1774
17751911 return CValue{ .local_ref = local.local };
17761912}
17771913
......@@ -1808,38 +1944,22 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
18081944 // We need to separately initialize arrays with a memcpy so they must be mutable.
18091945 const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const);
18101946
1811 switch (operand) {
1812 .local_ref => |i| {
1813 const wrapped: CValue = .{ .local = i };
1814 try writer.writeAll(" = ");
1815 try f.writeCValue(writer, wrapped);
1816 try writer.writeAll(";\n");
1817 },
1818 .decl_ref => |decl| {
1819 const wrapped: CValue = .{ .decl = decl };
1820 try writer.writeAll(" = ");
1821 try f.writeCValue(writer, wrapped);
1822 try writer.writeAll(";\n");
1823 },
1824 else => {
1825 if (is_array) {
1826 // Insert a memcpy to initialize this array. The source operand is always a pointer
1827 // and thus we only need to know size/type information from the local type/dest.
1828 try writer.writeAll(";");
1829 try f.object.indent_writer.insertNewline();
1830 try writer.writeAll("memcpy(");
1831 try f.writeCValue(writer, local);
1832 try writer.writeAll(", ");
1833 try f.writeCValue(writer, operand);
1834 try writer.writeAll(", sizeof(");
1835 try f.writeCValue(writer, local);
1836 try writer.writeAll("));\n");
1837 } else {
1838 try writer.writeAll(" = ");
1839 try f.writeCValueDeref(writer, operand);
1840 try writer.writeAll(";\n");
1841 }
1842 },
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");
18431963 }
18441964 return local;
18451965}
......@@ -1884,7 +2004,7 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {
18842004 const inst_ty = f.air.typeOfIndex(inst);
18852005 const local = try f.allocLocal(inst_ty, .Const);
18862006 try writer.writeAll(" = (");
1887 try f.renderType(writer, inst_ty);
2007 try f.renderTypecast(writer, inst_ty);
18882008 try writer.writeAll(")");
18892009 try f.writeCValue(writer, operand);
18902010 try writer.writeAll(";\n");
......@@ -1945,7 +2065,7 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {
19452065 return local;
19462066}
19472067
1948fn airStoreUndefined(f: *Function, dest_ptr: CValue, dest_child_type: Type) !CValue {
2068fn airStoreUndefined(f: *Function, dest_ptr: CValue) !CValue {
19492069 const is_debug_build = f.object.dg.module.optimizeMode() == .Debug;
19502070 if (!is_debug_build)
19512071 return CValue.none;
......@@ -1954,11 +2074,7 @@ fn airStoreUndefined(f: *Function, dest_ptr: CValue, dest_child_type: Type) !CVa
19542074 try writer.writeAll("memset(");
19552075 try f.writeCValue(writer, dest_ptr);
19562076 try writer.writeAll(", 0xaa, sizeof(");
1957 if (dest_child_type.zigTypeTag() == .Array) {
1958 try f.writeCValue(writer, dest_ptr);
1959 } else {
1960 try f.writeCValueDeref(writer, dest_ptr);
1961 }
2077 try f.writeCValueDeref(writer, dest_ptr);
19622078 try writer.writeAll("));\n");
19632079 return CValue.none;
19642080}
......@@ -1975,58 +2091,40 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
19752091 const src_val_is_undefined =
19762092 if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false;
19772093 if (src_val_is_undefined)
1978 return try airStoreUndefined(f, dest_ptr, lhs_child_type);
2094 return try airStoreUndefined(f, dest_ptr);
19792095
19802096 const writer = f.object.writer();
1981 switch (dest_ptr) {
1982 .local_ref => |i| {
1983 const dest: CValue = .{ .local = i };
1984 try f.writeCValue(writer, dest);
1985 try writer.writeAll(" = ");
1986 try f.writeCValue(writer, src_val);
1987 try writer.writeAll(";\n");
1988 },
1989 .decl_ref => |decl| {
1990 const dest: CValue = .{ .decl = decl };
1991 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);
19922108 try writer.writeAll(" = ");
19932109 try f.writeCValue(writer, src_val);
1994 try writer.writeAll(";\n");
1995 },
1996 else => {
1997 if (lhs_child_type.zigTypeTag() == .Array) {
1998 // For this memcpy to safely work we need the rhs to have the same
1999 // underlying type as the lhs (i.e. they must both be arrays of the same underlying type).
2000 const rhs_type = f.air.typeOf(bin_op.rhs);
2001 assert(rhs_type.eql(lhs_child_type));
2002
2003 // If the source is a constant, writeCValue will emit a brace initialization
2004 // so work around this by initializing into new local.
2005 // TODO this should be done by manually initializing elements of the dest array
2006 const array_src = if (src_val == .constant) blk: {
2007 const new_local = try f.allocLocal(rhs_type, .Const);
2008 try writer.writeAll(" = ");
2009 try f.writeCValue(writer, src_val);
2010 try writer.writeAll(";");
2011 try f.object.indent_writer.insertNewline();
2012
2013 break :blk new_local;
2014 } else src_val;
2015
2016 try writer.writeAll("memcpy(");
2017 try f.writeCValue(writer, dest_ptr);
2018 try writer.writeAll(", ");
2019 try f.writeCValue(writer, array_src);
2020 try writer.writeAll(", sizeof(");
2021 try f.writeCValue(writer, array_src);
2022 try writer.writeAll("));\n");
2023 } else {
2024 try f.writeCValueDeref(writer, dest_ptr);
2025 try writer.writeAll(" = ");
2026 try f.writeCValue(writer, src_val);
2027 try writer.writeAll(";\n");
2028 }
2029 },
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");
20302128 }
20312129 return CValue.none;
20322130}
......@@ -2382,17 +2480,24 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CV
23822480 const writer = f.object.writer();
23832481 const inst_ty = f.air.typeOfIndex(inst);
23842482 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 };
23852490
23862491 // We must convert to and from integer types to prevent UB if the operation results in a NULL pointer,
23872492 // or if LHS is NULL. The operation is only UB if the result is NULL and then dereferenced.
23882493 try writer.writeAll(" = (");
2389 try f.renderType(writer, inst_ty);
2494 try f.renderTypecast(writer, inst_ty);
23902495 try writer.writeAll(")(((uintptr_t)");
23912496 try f.writeCValue(writer, lhs);
23922497 try writer.print("){s}(", .{operator});
23932498 try f.writeCValue(writer, rhs);
23942499 try writer.writeAll("*sizeof(");
2395 try f.renderType(writer, inst_ty.childType());
2500 try f.renderTypecast(writer, elem_ty);
23962501 try writer.print(")));\n", .{});
23972502
23982503 return local;
......@@ -2580,7 +2685,7 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
25802685 {
25812686 const local = try f.allocLocal(inst_ty, .Const);
25822687 try writer.writeAll(" = (");
2583 try f.renderType(writer, inst_ty);
2688 try f.renderTypecast(writer, inst_ty);
25842689
25852690 try writer.writeAll(")");
25862691 try f.writeCValue(writer, operand);
......@@ -2922,13 +3027,12 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc
29223027 },
29233028 else => unreachable,
29243029 }
2925 const addrof = if (field_val_ty.zigTypeTag() == .Array) "" else "&";
29263030 const payload = if (struct_ty.tag() == .union_tagged) "payload." else "";
29273031
29283032 const inst_ty = f.air.typeOfIndex(inst);
29293033 const local = try f.allocLocal(inst_ty, .Const);
29303034
2931 try writer.print(" = {s}", .{addrof});
3035 try writer.print(" = &", .{});
29323036 try f.writeCValueDeref(writer, struct_ptr);
29333037 try writer.print(".{s}{ };\n", .{ payload, fmtIdent(field_name) });
29343038 return local;
......@@ -3114,7 +3218,15 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
31143218 const array_len = f.air.typeOf(ty_op.operand).elemType().arrayLen();
31153219
31163220 try writer.writeAll(" = { .ptr = ");
3117 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 }
31183230 try writer.print(", .len = {d} }};\n", .{array_len});
31193231 return local;
31203232}
......@@ -3146,7 +3258,7 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue {
31463258 const operand = try f.resolveInst(un_op);
31473259
31483260 try writer.writeAll(" = (");
3149 try f.renderType(writer, inst_ty);
3261 try f.renderTypecast(writer, inst_ty);
31503262 try writer.writeAll(")");
31513263 try f.writeCValue(writer, operand);
31523264 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);