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) {...@@ -34,6 +34,8 @@ pub const CValue = union(enum) {
34 /// By-value34 /// By-value
35 decl: *Decl,35 decl: *Decl,
36 decl_ref: *Decl,36 decl_ref: *Decl,
37 /// An undefined (void *) pointer (cannot be dereferenced)
38 undefined_ptr: void,
37 /// Render the slice as an identifier (using fmtIdent)39 /// Render the slice as an identifier (using fmtIdent)
38 identifier: []const u8,40 identifier: []const u8,
39 /// Render these bytes literally.41 /// Render these bytes literally.
...@@ -311,6 +313,10 @@ pub const Function = struct {...@@ -311,6 +313,10 @@ pub const Function = struct {
311 fn renderType(f: *Function, w: anytype, t: Type) !void {313 fn renderType(f: *Function, w: anytype, t: Type) !void {
312 return f.object.dg.renderType(w, t);314 return f.object.dg.renderType(w, t);
313 }315 }
316
317 fn renderTypecast(f: *Function, w: anytype, t: Type) !void {
318 return f.object.dg.renderTypecast(w, t);
319 }
314};320};
315321
316/// This data is available when outputting .c code for a `Module`.322/// This data is available when outputting .c code for a `Module`.
...@@ -365,10 +371,10 @@ pub const DeclGen = struct {...@@ -365,10 +371,10 @@ pub const DeclGen = struct {
365371
366 if (ty.isSlice()) {372 if (ty.isSlice()) {
367 try writer.writeByte('(');373 try writer.writeByte('(');
368 try dg.renderType(writer, ty);374 try dg.renderTypecast(writer, ty);
369 try writer.writeAll("){");375 try writer.writeAll("){");
370 var buf: Type.SlicePtrFieldTypeBuffer = undefined;376 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
371 try dg.renderValue(writer, ty.slicePtrFieldType(&buf), val);377 try dg.renderValue(writer, ty.slicePtrFieldType(&buf), val.slicePtr());
372 try writer.writeAll(", ");378 try writer.writeAll(", ");
373 try writer.print("{d}", .{val.sliceLen()});379 try writer.print("{d}", .{val.sliceLen()});
374 try writer.writeAll("}");380 try writer.writeAll("}");
...@@ -389,7 +395,7 @@ pub const DeclGen = struct {...@@ -389,7 +395,7 @@ pub const DeclGen = struct {
389 }395 }
390396
391 try writer.writeAll("((");397 try writer.writeAll("((");
392 try dg.renderType(writer, ty);398 try dg.renderTypecast(writer, ty);
393 try writer.writeAll(")&");399 try writer.writeAll(")&");
394 try dg.renderDeclName(decl, writer);400 try dg.renderDeclName(decl, writer);
395 try writer.writeByte(')');401 try writer.writeByte(')');
...@@ -445,6 +451,57 @@ pub const DeclGen = struct {...@@ -445,6 +451,57 @@ pub const DeclGen = struct {
445 }451 }
446 }452 }
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
448 fn renderValue(505 fn renderValue(
449 dg: *DeclGen,506 dg: *DeclGen,
450 writer: anytype,507 writer: anytype,
...@@ -531,24 +588,15 @@ pub const DeclGen = struct {...@@ -531,24 +588,15 @@ pub const DeclGen = struct {
531 var buf: Type.SlicePtrFieldTypeBuffer = undefined;588 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
532589
533 try writer.writeByte('(');590 try writer.writeByte('(');
534 try dg.renderType(writer, ty);591 try dg.renderTypecast(writer, ty);
535 try writer.writeAll("){");592 try writer.writeAll("){");
536 try dg.renderValue(writer, ty.slicePtrFieldType(&buf), slice.ptr);593 try dg.renderValue(writer, ty.slicePtrFieldType(&buf), slice.ptr);
537 try writer.writeAll(", ");594 try writer.writeAll(", ");
538 try dg.renderValue(writer, Type.usize, slice.len);595 try dg.renderValue(writer, Type.usize, slice.len);
539 try writer.writeAll("}");596 try writer.writeAll("}");
540 },597 },
541 .elem_ptr => {598 .field_ptr, .elem_ptr, .opt_payload_ptr, .eu_payload_ptr => {
542 const elem_ptr = val.castTag(.elem_ptr).?.data;599 _ = try dg.renderChildPtr(writer, val);
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});
552 },600 },
553 .function => {601 .function => {
554 const func = val.castTag(.function).?.data;602 const func = val.castTag(.function).?.data;
...@@ -560,7 +608,7 @@ pub const DeclGen = struct {...@@ -560,7 +608,7 @@ pub const DeclGen = struct {
560 },608 },
561 .int_u64, .one => {609 .int_u64, .one => {
562 try writer.writeAll("((");610 try writer.writeAll("((");
563 try dg.renderType(writer, ty);611 try dg.renderTypecast(writer, ty);
564 try writer.print(")0x{x}u)", .{val.toUnsignedInt()});612 try writer.print(")0x{x}u)", .{val.toUnsignedInt()});
565 },613 },
566 else => unreachable,614 else => unreachable,
...@@ -605,7 +653,7 @@ pub const DeclGen = struct {...@@ -605,7 +653,7 @@ pub const DeclGen = struct {
605 return writer.print("{}", .{is_null});653 return writer.print("{}", .{is_null});
606 }654 }
607 try writer.writeByte('(');655 try writer.writeByte('(');
608 try dg.renderType(writer, ty);656 try dg.renderTypecast(writer, ty);
609 try writer.writeAll("){");657 try writer.writeAll("){");
610 if (val.castTag(.opt_payload)) |pl| {658 if (val.castTag(.opt_payload)) |pl| {
611 const payload_val = pl.data;659 const payload_val = pl.data;
...@@ -641,7 +689,7 @@ pub const DeclGen = struct {...@@ -641,7 +689,7 @@ pub const DeclGen = struct {
641 }689 }
642690
643 try writer.writeByte('(');691 try writer.writeByte('(');
644 try dg.renderType(writer, ty);692 try dg.renderTypecast(writer, ty);
645 try writer.writeAll("){");693 try writer.writeAll("){");
646 if (val.castTag(.eu_payload)) |pl| {694 if (val.castTag(.eu_payload)) |pl| {
647 const payload_val = pl.data;695 const payload_val = pl.data;
...@@ -703,7 +751,7 @@ pub const DeclGen = struct {...@@ -703,7 +751,7 @@ pub const DeclGen = struct {
703 const field_vals = val.castTag(.@"struct").?.data;751 const field_vals = val.castTag(.@"struct").?.data;
704752
705 try writer.writeAll("(");753 try writer.writeAll("(");
706 try dg.renderType(writer, ty);754 try dg.renderTypecast(writer, ty);
707 try writer.writeAll("){");755 try writer.writeAll("){");
708756
709 for (field_vals) |field_val, i| {757 for (field_vals) |field_val, i| {
...@@ -723,7 +771,7 @@ pub const DeclGen = struct {...@@ -723,7 +771,7 @@ pub const DeclGen = struct {
723 const layout = ty.unionGetLayout(target);771 const layout = ty.unionGetLayout(target);
724772
725 try writer.writeAll("(");773 try writer.writeAll("(");
726 try dg.renderType(writer, ty);774 try dg.renderTypecast(writer, ty);
727 try writer.writeAll("){");775 try writer.writeAll("){");
728776
729 if (ty.unionTagType()) |tag_ty| {777 if (ty.unionTagType()) |tag_ty| {
...@@ -798,8 +846,9 @@ pub const DeclGen = struct {...@@ -798,8 +846,9 @@ pub const DeclGen = struct {
798 if (params_written > 0) {846 if (params_written > 0) {
799 try w.writeAll(", ");847 try w.writeAll(", ");
800 }848 }
801 try dg.renderType(w, dg.decl.ty.fnParamType(index));849 const name = CValue{ .arg = index };
802 try w.print(" a{d}", .{index});850 const alignment = Value.initTag(.abi_align_default);
851 try dg.renderTypeAndName(w, dg.decl.ty.fnParamType(index), name, .Mut, alignment);
803 params_written += 1;852 params_written += 1;
804 }853 }
805854
...@@ -837,7 +886,7 @@ pub const DeclGen = struct {...@@ -837,7 +886,7 @@ pub const DeclGen = struct {
837 if (params_written > 0) {886 if (params_written > 0) {
838 try bw.writeAll(", ");887 try bw.writeAll(", ");
839 }888 }
840 try dg.renderType(bw, fn_info.param_types[index]);889 try dg.renderTypecast(bw, fn_info.param_types[index]);
841 params_written += 1;890 params_written += 1;
842 }891 }
843892
...@@ -868,17 +917,16 @@ pub const DeclGen = struct {...@@ -868,17 +917,16 @@ pub const DeclGen = struct {
868 const bw = buffer.writer();917 const bw = buffer.writer();
869918
870 try bw.writeAll("typedef struct { ");919 try bw.writeAll("typedef struct { ");
871 const elem_type = t.elemType();920
872 try dg.renderType(bw, elem_type);921 var ptr_type_buf: Type.SlicePtrFieldTypeBuffer = undefined;
873 if (t.isConstPtr()) {922 const ptr_type = t.slicePtrFieldType(&ptr_type_buf);
874 try bw.writeAll(" const");923 const ptr_name = CValue{ .bytes = "ptr" };
875 }924 const ptr_alignment = Value.initTag(.abi_align_default);
876 if (t.isVolatilePtr()) {925 try dg.renderTypeAndName(bw, ptr_type, ptr_name, .Mut, ptr_alignment);
877 try bw.writeAll(" volatile");926
878 }927 try bw.writeAll("; size_t len; } ");
879 try bw.writeAll(" *");
880 try bw.writeAll("ptr; size_t len; } ");
881 const name_index = buffer.items.len;928 const name_index = buffer.items.len;
929 const elem_type = t.elemType();
882 if (t.isConstPtr()) {930 if (t.isConstPtr()) {
883 try bw.print("zig_L_{s};\n", .{typeToCIdentifier(elem_type)});931 try bw.print("zig_L_{s};\n", .{typeToCIdentifier(elem_type)});
884 } else {932 } else {
...@@ -1003,8 +1051,10 @@ pub const DeclGen = struct {...@@ -1003,8 +1051,10 @@ pub const DeclGen = struct {
1003 const bw = buffer.writer();1051 const bw = buffer.writer();
10041052
1005 try bw.writeAll("typedef struct { ");1053 try bw.writeAll("typedef struct { ");
1006 try dg.renderType(bw, child_type);1054 const payload_name = CValue{ .bytes = "payload" };
1007 try bw.writeAll(" payload; uint16_t error; } ");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; } ");
1008 const name_index = buffer.items.len;1058 const name_index = buffer.items.len;
1009 if (err_set_type.castTag(.error_set_inferred)) |inf_err_set_payload| {1059 if (err_set_type.castTag(.error_set_inferred)) |inf_err_set_payload| {
1010 const func = inf_err_set_payload.data.func;1060 const func = inf_err_set_payload.data.func;
...@@ -1030,14 +1080,47 @@ pub const DeclGen = struct {...@@ -1030,14 +1080,47 @@ pub const DeclGen = struct {
1030 return name;1080 return name;
1031 }1081 }
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
1033 fn renderOptionalTypedef(dg: *DeclGen, t: Type, child_type: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {1114 fn renderOptionalTypedef(dg: *DeclGen, t: Type, child_type: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
1034 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);1115 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
1035 defer buffer.deinit();1116 defer buffer.deinit();
1036 const bw = buffer.writer();1117 const bw = buffer.writer();
10371118
1038 try bw.writeAll("typedef struct { ");1119 try bw.writeAll("typedef struct { ");
1039 try dg.renderType(bw, child_type);1120 const payload_name = CValue{ .bytes = "payload" };
1040 try bw.writeAll(" payload; bool is_null; } ");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; } ");
1041 const name_index = buffer.items.len;1124 const name_index = buffer.items.len;
1042 try bw.print("zig_Q_{s};\n", .{typeToCIdentifier(child_type)});1125 try bw.print("zig_Q_{s};\n", .{typeToCIdentifier(child_type)});
10431126
...@@ -1054,6 +1137,18 @@ pub const DeclGen = struct {...@@ -1054,6 +1137,18 @@ pub const DeclGen = struct {
1054 return name;1137 return name;
1055 }1138 }
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 ///
1057 fn renderType(dg: *DeclGen, w: anytype, t: Type) error{ OutOfMemory, AnalysisFail }!void {1152 fn renderType(dg: *DeclGen, w: anytype, t: Type) error{ OutOfMemory, AnalysisFail }!void {
1058 const target = dg.module.getTarget();1153 const target = dg.module.getTarget();
10591154
...@@ -1130,10 +1225,10 @@ pub const DeclGen = struct {...@@ -1130,10 +1225,10 @@ pub const DeclGen = struct {
1130 return w.writeAll(" *");1225 return w.writeAll(" *");
1131 },1226 },
1132 .Array => {1227 .Array => {
1133 // We are referencing the array so it will decay to a C pointer.1228 const name = dg.getTypedefName(t) orelse
1134 // NB: arrays are not really types in C so they are either specified in the declaration1229 try dg.renderArrayTypedef(t);
1135 // or are already pointed to; our only job is to render the element type.1230
1136 return dg.renderType(w, t.elemType());1231 return w.writeAll(name);
1137 },1232 },
1138 .Optional => {1233 .Optional => {
1139 var opt_buf: Type.Payload.ElemType = undefined;1234 var opt_buf: Type.Payload.ElemType = undefined;
...@@ -1208,6 +1303,39 @@ pub const DeclGen = struct {...@@ -1208,6 +1303,39 @@ pub const DeclGen = struct {
1208 }1303 }
1209 }1304 }
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 ///
1211 fn renderTypeAndName(1339 fn renderTypeAndName(
1212 dg: *DeclGen,1340 dg: *DeclGen,
1213 w: anytype,1341 w: anytype,
...@@ -1219,6 +1347,8 @@ pub const DeclGen = struct {...@@ -1219,6 +1347,8 @@ pub const DeclGen = struct {
1219 var suffix = std.ArrayList(u8).init(dg.gpa);1347 var suffix = std.ArrayList(u8).init(dg.gpa);
1220 defer suffix.deinit();1348 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
1222 var render_ty = ty;1352 var render_ty = ty;
1223 while (render_ty.zigTypeTag() == .Array) {1353 while (render_ty.zigTypeTag() == .Array) {
1224 const sentinel_bit = @boolToInt(render_ty.sentinel() != null);1354 const sentinel_bit = @boolToInt(render_ty.sentinel() != null);
...@@ -1267,6 +1397,14 @@ pub const DeclGen = struct {...@@ -1267,6 +1397,14 @@ pub const DeclGen = struct {
1267 try w.writeByte('&');1397 try w.writeByte('&');
1268 return dg.renderDeclName(decl, w);1398 return dg.renderDeclName(decl, w);
1269 },1399 },
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 },
1270 .identifier => |ident| return w.print("{ }", .{fmtIdent(ident)}),1408 .identifier => |ident| return w.print("{ }", .{fmtIdent(ident)}),
1271 .bytes => |bytes| return w.writeAll(bytes),1409 .bytes => |bytes| return w.writeAll(bytes),
1272 }1410 }
...@@ -1285,6 +1423,7 @@ pub const DeclGen = struct {...@@ -1285,6 +1423,7 @@ pub const DeclGen = struct {
1285 return w.writeByte(')');1423 return w.writeByte(')');
1286 },1424 },
1287 .decl_ref => |decl| return dg.renderDeclName(decl, w),1425 .decl_ref => |decl| return dg.renderDeclName(decl, w),
1426 .undefined_ptr => unreachable,
1288 .identifier => |ident| return w.print("(*{ })", .{fmtIdent(ident)}),1427 .identifier => |ident| return w.print("(*{ })", .{fmtIdent(ident)}),
1289 .bytes => |bytes| {1428 .bytes => |bytes| {
1290 try w.writeAll("(*");1429 try w.writeAll("(*");
...@@ -1676,14 +1815,21 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -1676,14 +1815,21 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
16761815
1677 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;1816 const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
1678 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;1817 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
1818 const ptr_ty = f.air.typeOf(bin_op.lhs);
16791819
1680 const ptr = try f.resolveInst(bin_op.lhs);1820 const ptr = try f.resolveInst(bin_op.lhs);
1681 const index = try f.resolveInst(bin_op.rhs);1821 const index = try f.resolveInst(bin_op.rhs);
1682 const writer = f.object.writer();1822 const writer = f.object.writer();
1683 const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const);1823 const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const);
1684 try writer.writeAll(" = &");1824
1685 try f.writeCValue(writer, ptr);1825 try writer.writeAll(" = &(");
1686 try writer.writeByte('[');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(")[");
1687 try f.writeCValue(writer, index);1833 try f.writeCValue(writer, index);
1688 try writer.writeAll("];\n");1834 try writer.writeAll("];\n");
1689 return local;1835 return local;
...@@ -1747,13 +1893,7 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -1747,13 +1893,7 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
1747 const elem_type = inst_ty.elemType();1893 const elem_type = inst_ty.elemType();
1748 const mutability: Mutability = if (inst_ty.isConstPtr()) .Const else .Mut;1894 const mutability: Mutability = if (inst_ty.isConstPtr()) .Const else .Mut;
1749 if (!elem_type.isFnOrHasRuntimeBits()) {1895 if (!elem_type.isFnOrHasRuntimeBits()) {
1750 const target = f.object.dg.module.getTarget();1896 return CValue.undefined_ptr;
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 };
1757 }1897 }
17581898
1759 const target = f.object.dg.module.getTarget();1899 const target = f.object.dg.module.getTarget();
...@@ -1768,10 +1908,6 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -1768,10 +1908,6 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
1768 const local = try f.allocAlignedLocal(elem_type, mutability, alignment_value);1908 const local = try f.allocAlignedLocal(elem_type, mutability, alignment_value);
1769 try writer.writeAll(";\n");1909 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
1775 return CValue{ .local_ref = local.local };1911 return CValue{ .local_ref = local.local };
1776}1912}
17771913
...@@ -1808,38 +1944,22 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -1808,38 +1944,22 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
1808 // We need to separately initialize arrays with a memcpy so they must be mutable.1944 // We need to separately initialize arrays with a memcpy so they must be mutable.
1809 const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const);1945 const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const);
18101946
1811 switch (operand) {1947 if (is_array) {
1812 .local_ref => |i| {1948 // Insert a memcpy to initialize this array. The source operand is always a pointer
1813 const wrapped: CValue = .{ .local = i };1949 // and thus we only need to know size/type information from the local type/dest.
1814 try writer.writeAll(" = ");1950 try writer.writeAll(";");
1815 try f.writeCValue(writer, wrapped);1951 try f.object.indent_writer.insertNewline();
1816 try writer.writeAll(";\n");1952 try writer.writeAll("memcpy(");
1817 },1953 try f.writeCValue(writer, local);
1818 .decl_ref => |decl| {1954 try writer.writeAll(", ");
1819 const wrapped: CValue = .{ .decl = decl };1955 try f.writeCValue(writer, operand);
1820 try writer.writeAll(" = ");1956 try writer.writeAll(", sizeof(");
1821 try f.writeCValue(writer, wrapped);1957 try f.writeCValue(writer, local);
1822 try writer.writeAll(";\n");1958 try writer.writeAll("));\n");
1823 },1959 } else {
1824 else => {1960 try writer.writeAll(" = ");
1825 if (is_array) {1961 try f.writeCValueDeref(writer, operand);
1826 // Insert a memcpy to initialize this array. The source operand is always a pointer1962 try writer.writeAll(";\n");
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 },
1843 }1963 }
1844 return local;1964 return local;
1845}1965}
...@@ -1884,7 +2004,7 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -1884,7 +2004,7 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {
1884 const inst_ty = f.air.typeOfIndex(inst);2004 const inst_ty = f.air.typeOfIndex(inst);
1885 const local = try f.allocLocal(inst_ty, .Const);2005 const local = try f.allocLocal(inst_ty, .Const);
1886 try writer.writeAll(" = (");2006 try writer.writeAll(" = (");
1887 try f.renderType(writer, inst_ty);2007 try f.renderTypecast(writer, inst_ty);
1888 try writer.writeAll(")");2008 try writer.writeAll(")");
1889 try f.writeCValue(writer, operand);2009 try f.writeCValue(writer, operand);
1890 try writer.writeAll(";\n");2010 try writer.writeAll(";\n");
...@@ -1945,7 +2065,7 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -1945,7 +2065,7 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {
1945 return local;2065 return local;
1946}2066}
19472067
1948fn airStoreUndefined(f: *Function, dest_ptr: CValue, dest_child_type: Type) !CValue {2068fn airStoreUndefined(f: *Function, dest_ptr: CValue) !CValue {
1949 const is_debug_build = f.object.dg.module.optimizeMode() == .Debug;2069 const is_debug_build = f.object.dg.module.optimizeMode() == .Debug;
1950 if (!is_debug_build)2070 if (!is_debug_build)
1951 return CValue.none;2071 return CValue.none;
...@@ -1954,11 +2074,7 @@ fn airStoreUndefined(f: *Function, dest_ptr: CValue, dest_child_type: Type) !CVa...@@ -1954,11 +2074,7 @@ fn airStoreUndefined(f: *Function, dest_ptr: CValue, dest_child_type: Type) !CVa
1954 try writer.writeAll("memset(");2074 try writer.writeAll("memset(");
1955 try f.writeCValue(writer, dest_ptr);2075 try f.writeCValue(writer, dest_ptr);
1956 try writer.writeAll(", 0xaa, sizeof(");2076 try writer.writeAll(", 0xaa, sizeof(");
1957 if (dest_child_type.zigTypeTag() == .Array) {2077 try f.writeCValueDeref(writer, dest_ptr);
1958 try f.writeCValue(writer, dest_ptr);
1959 } else {
1960 try f.writeCValueDeref(writer, dest_ptr);
1961 }
1962 try writer.writeAll("));\n");2078 try writer.writeAll("));\n");
1963 return CValue.none;2079 return CValue.none;
1964}2080}
...@@ -1975,58 +2091,40 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -1975,58 +2091,40 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
1975 const src_val_is_undefined =2091 const src_val_is_undefined =
1976 if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false;2092 if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false;
1977 if (src_val_is_undefined)2093 if (src_val_is_undefined)
1978 return try airStoreUndefined(f, dest_ptr, lhs_child_type);2094 return try airStoreUndefined(f, dest_ptr);
19792095
1980 const writer = f.object.writer();2096 const writer = f.object.writer();
1981 switch (dest_ptr) {2097 if (lhs_child_type.zigTypeTag() == .Array) {
1982 .local_ref => |i| {2098 // For this memcpy to safely work we need the rhs to have the same
1983 const dest: CValue = .{ .local = i };2099 // underlying type as the lhs (i.e. they must both be arrays of the same underlying type).
1984 try f.writeCValue(writer, dest);2100 const rhs_type = f.air.typeOf(bin_op.rhs);
1985 try writer.writeAll(" = ");2101 assert(rhs_type.eql(lhs_child_type));
1986 try f.writeCValue(writer, src_val);2102
1987 try writer.writeAll(";\n");2103 // If the source is a constant, writeCValue will emit a brace initialization
1988 },2104 // so work around this by initializing into new local.
1989 .decl_ref => |decl| {2105 // TODO this should be done by manually initializing elements of the dest array
1990 const dest: CValue = .{ .decl = decl };2106 const array_src = if (src_val == .constant) blk: {
1991 try f.writeCValue(writer, dest);2107 const new_local = try f.allocLocal(rhs_type, .Const);
1992 try writer.writeAll(" = ");2108 try writer.writeAll(" = ");
1993 try f.writeCValue(writer, src_val);2109 try f.writeCValue(writer, src_val);
1994 try writer.writeAll(";\n");2110 try writer.writeAll(";");
1995 },2111 try f.object.indent_writer.insertNewline();
1996 else => {2112
1997 if (lhs_child_type.zigTypeTag() == .Array) {2113 break :blk new_local;
1998 // For this memcpy to safely work we need the rhs to have the same2114 } else src_val;
1999 // underlying type as the lhs (i.e. they must both be arrays of the same underlying type).2115
2000 const rhs_type = f.air.typeOf(bin_op.rhs);2116 try writer.writeAll("memcpy(");
2001 assert(rhs_type.eql(lhs_child_type));2117 try f.writeCValue(writer, dest_ptr);
20022118 try writer.writeAll(", ");
2003 // If the source is a constant, writeCValue will emit a brace initialization2119 try f.writeCValue(writer, array_src);
2004 // so work around this by initializing into new local.2120 try writer.writeAll(", sizeof(");
2005 // TODO this should be done by manually initializing elements of the dest array2121 try f.writeCValue(writer, array_src);
2006 const array_src = if (src_val == .constant) blk: {2122 try writer.writeAll("));\n");
2007 const new_local = try f.allocLocal(rhs_type, .Const);2123 } else {
2008 try writer.writeAll(" = ");2124 try f.writeCValueDeref(writer, dest_ptr);
2009 try f.writeCValue(writer, src_val);2125 try writer.writeAll(" = ");
2010 try writer.writeAll(";");2126 try f.writeCValue(writer, src_val);
2011 try f.object.indent_writer.insertNewline();2127 try writer.writeAll(";\n");
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 },
2030 }2128 }
2031 return CValue.none;2129 return CValue.none;
2032}2130}
...@@ -2382,17 +2480,24 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CV...@@ -2382,17 +2480,24 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CV
2382 const writer = f.object.writer();2480 const writer = f.object.writer();
2383 const inst_ty = f.air.typeOfIndex(inst);2481 const inst_ty = f.air.typeOfIndex(inst);
2384 const local = try f.allocLocal(inst_ty, .Const);2482 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
2386 // We must convert to and from integer types to prevent UB if the operation results in a NULL pointer,2491 // We must convert to and from integer types to prevent UB if the operation results in a NULL pointer,
2387 // or if LHS is NULL. The operation is only UB if the result is NULL and then dereferenced.2492 // or if LHS is NULL. The operation is only UB if the result is NULL and then dereferenced.
2388 try writer.writeAll(" = (");2493 try writer.writeAll(" = (");
2389 try f.renderType(writer, inst_ty);2494 try f.renderTypecast(writer, inst_ty);
2390 try writer.writeAll(")(((uintptr_t)");2495 try writer.writeAll(")(((uintptr_t)");
2391 try f.writeCValue(writer, lhs);2496 try f.writeCValue(writer, lhs);
2392 try writer.print("){s}(", .{operator});2497 try writer.print("){s}(", .{operator});
2393 try f.writeCValue(writer, rhs);2498 try f.writeCValue(writer, rhs);
2394 try writer.writeAll("*sizeof(");2499 try writer.writeAll("*sizeof(");
2395 try f.renderType(writer, inst_ty.childType());2500 try f.renderTypecast(writer, elem_ty);
2396 try writer.print(")));\n", .{});2501 try writer.print(")));\n", .{});
23972502
2398 return local;2503 return local;
...@@ -2580,7 +2685,7 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -2580,7 +2685,7 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
2580 {2685 {
2581 const local = try f.allocLocal(inst_ty, .Const);2686 const local = try f.allocLocal(inst_ty, .Const);
2582 try writer.writeAll(" = (");2687 try writer.writeAll(" = (");
2583 try f.renderType(writer, inst_ty);2688 try f.renderTypecast(writer, inst_ty);
25842689
2585 try writer.writeAll(")");2690 try writer.writeAll(")");
2586 try f.writeCValue(writer, operand);2691 try f.writeCValue(writer, operand);
...@@ -2922,13 +3027,12 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc...@@ -2922,13 +3027,12 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc
2922 },3027 },
2923 else => unreachable,3028 else => unreachable,
2924 }3029 }
2925 const addrof = if (field_val_ty.zigTypeTag() == .Array) "" else "&";
2926 const payload = if (struct_ty.tag() == .union_tagged) "payload." else "";3030 const payload = if (struct_ty.tag() == .union_tagged) "payload." else "";
29273031
2928 const inst_ty = f.air.typeOfIndex(inst);3032 const inst_ty = f.air.typeOfIndex(inst);
2929 const local = try f.allocLocal(inst_ty, .Const);3033 const local = try f.allocLocal(inst_ty, .Const);
29303034
2931 try writer.print(" = {s}", .{addrof});3035 try writer.print(" = &", .{});
2932 try f.writeCValueDeref(writer, struct_ptr);3036 try f.writeCValueDeref(writer, struct_ptr);
2933 try writer.print(".{s}{ };\n", .{ payload, fmtIdent(field_name) });3037 try writer.print(".{s}{ };\n", .{ payload, fmtIdent(field_name) });
2934 return local;3038 return local;
...@@ -3114,7 +3218,15 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3114,7 +3218,15 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
3114 const array_len = f.air.typeOf(ty_op.operand).elemType().arrayLen();3218 const array_len = f.air.typeOf(ty_op.operand).elemType().arrayLen();
31153219
3116 try writer.writeAll(" = { .ptr = ");3220 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 }
3118 try writer.print(", .len = {d} }};\n", .{array_len});3230 try writer.print(", .len = {d} }};\n", .{array_len});
3119 return local;3231 return local;
3120}3232}
...@@ -3146,7 +3258,7 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3146,7 +3258,7 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue {
3146 const operand = try f.resolveInst(un_op);3258 const operand = try f.resolveInst(un_op);
31473259
3148 try writer.writeAll(" = (");3260 try writer.writeAll(" = (");
3149 try f.renderType(writer, inst_ty);3261 try f.renderTypecast(writer, inst_ty);
3150 try writer.writeAll(")");3262 try writer.writeAll(")");
3151 try f.writeCValue(writer, operand);3263 try f.writeCValue(writer, operand);
3152 try writer.writeAll(";\n");3264 try writer.writeAll(";\n");
test/behavior/basic.zig+32
...@@ -436,6 +436,38 @@ test "array 2D const double ptr" {...@@ -436,6 +436,38 @@ test "array 2D const double ptr" {
436 try testArray2DConstDoublePtr(&rect_2d_vertexes[0][0]);436 try testArray2DConstDoublePtr(&rect_2d_vertexes[0][0]);
437}437}
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
439fn testArray2DConstDoublePtr(ptr: *const f32) !void {471fn testArray2DConstDoublePtr(ptr: *const f32) !void {
440 const ptr2 = @ptrCast([*]const f32, ptr);472 const ptr2 = @ptrCast([*]const f32, ptr);
441 try expect(ptr2[0] == 1.0);473 try expect(ptr2[0] == 1.0);
test/behavior/undefined.zig+5
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");
2const expect = std.testing.expect;3const expect = std.testing.expect;
3const mem = std.mem;4const mem = std.mem;
45
...@@ -12,6 +13,10 @@ fn initStaticArray() [10]i32 {...@@ -12,6 +13,10 @@ fn initStaticArray() [10]i32 {
12}13}
13const static_array = initStaticArray();14const static_array = initStaticArray();
14test "init static array to undefined" {15test "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
15 try expect(static_array[0] == 1);20 try expect(static_array[0] == 1);
16 try expect(static_array[4] == 2);21 try expect(static_array[4] == 2);
17 try expect(static_array[7] == 3);22 try expect(static_array[7] == 3);