authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-02 19:11:51-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-02 19:11:51-07:00
log97d7fddfb78d17132749cae59fea36fe661bf642
tree5b01a32f9ee35e6cef838cffb5058e5f0f777229
parent43d364afef7f0609f9d897c7ff129ba6b9b3cab0

stage2: progress towards basic structs

Introduce `ResultLoc.none_or_ref` which is used by field access expressions to avoid unnecessary loads when the field access itself will do the load. This turns: ```zig p.y - p.x - p.x ``` from ```zir %14 = load(%4) node_offset:8:12 %15 = field_val(%14, "y") node_offset:8:13 %16 = load(%4) node_offset:8:18 %17 = field_val(%16, "x") node_offset:8:19 %18 = sub(%15, %17) node_offset:8:16 %19 = load(%4) node_offset:8:24 %20 = field_val(%19, "x") node_offset:8:25 ``` to ```zir %14 = field_val(%4, "y") node_offset:8:13 %15 = field_val(%4, "x") node_offset:8:19 %16 = sub(%14, %15) node_offset:8:16 %17 = field_val(%4, "x") node_offset:8:25 ``` Much more compact. This requires `Sema.zirFieldVal` to support both pointers and non-pointers. C backend: Implement typedefs for struct types, as well as the following TZIR instructions: * mul * mulwrap * addwrap * subwrap * ref * struct_field_ptr Note that add, addwrap, sub, subwrap, mul, mulwrap instructions are all incorrect currently and need to be updated to properly handle wrapping and non wrapping for signed and unsigned. C backend: change indentation delta to 1, to make the output smaller and to process fewer bytes. I promise I will add a test case as soon as I fix those warnings that are being printed for my test case.

6 files changed, 148 insertions(+), 29 deletions(-)

src/AstGen.zig+25-13
...@@ -124,6 +124,9 @@ pub const ResultLoc = union(enum) {...@@ -124,6 +124,9 @@ pub const ResultLoc = union(enum) {
124 /// The expression must generate a pointer rather than a value. For example, the left hand side124 /// The expression must generate a pointer rather than a value. For example, the left hand side
125 /// of an assignment uses this kind of result location.125 /// of an assignment uses this kind of result location.
126 ref,126 ref,
127 /// The callee will accept a ref, but it is not necessary, and the `ResultLoc`
128 /// may be treated as `none` instead.
129 none_or_ref,
127 /// The expression will be coerced into this type, but it will be evaluated as an rvalue.130 /// The expression will be coerced into this type, but it will be evaluated as an rvalue.
128 ty: zir.Inst.Ref,131 ty: zir.Inst.Ref,
129 /// The expression must store its result into this typed pointer. The result instruction132 /// The expression must store its result into this typed pointer. The result instruction
...@@ -157,7 +160,7 @@ pub const ResultLoc = union(enum) {...@@ -157,7 +160,7 @@ pub const ResultLoc = union(enum) {
157 var elide_store_to_block_ptr_instructions = false;160 var elide_store_to_block_ptr_instructions = false;
158 switch (rl) {161 switch (rl) {
159 // In this branch there will not be any store_to_block_ptr instructions.162 // In this branch there will not be any store_to_block_ptr instructions.
160 .discard, .none, .ty, .ref => return .{163 .discard, .none, .none_or_ref, .ty, .ref => return .{
161 .tag = .break_operand,164 .tag = .break_operand,
162 .elide_store_to_block_ptr_instructions = false,165 .elide_store_to_block_ptr_instructions = false,
163 },166 },
...@@ -606,8 +609,13 @@ pub fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) Inn...@@ -606,8 +609,13 @@ pub fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) Inn
606609
607 .deref => {610 .deref => {
608 const lhs = try expr(gz, scope, .none, node_datas[node].lhs);611 const lhs = try expr(gz, scope, .none, node_datas[node].lhs);
609 const result = try gz.addUnNode(.load, lhs, node);612 switch (rl) {
610 return rvalue(gz, scope, rl, result, node);613 .ref, .none_or_ref => return lhs,
614 else => {
615 const result = try gz.addUnNode(.load, lhs, node);
616 return rvalue(gz, scope, rl, result, node);
617 },
618 }
611 },619 },
612 .address_of => {620 .address_of => {
613 const result = try expr(gz, scope, .ref, node_datas[node].lhs);621 const result = try expr(gz, scope, .ref, node_datas[node].lhs);
...@@ -816,7 +824,7 @@ pub fn structInitExpr(...@@ -816,7 +824,7 @@ pub fn structInitExpr(
816 }824 }
817 switch (rl) {825 switch (rl) {
818 .discard => return mod.failNode(scope, node, "TODO implement structInitExpr discard", .{}),826 .discard => return mod.failNode(scope, node, "TODO implement structInitExpr discard", .{}),
819 .none => return mod.failNode(scope, node, "TODO implement structInitExpr none", .{}),827 .none, .none_or_ref => return mod.failNode(scope, node, "TODO implement structInitExpr none", .{}),
820 .ref => unreachable, // struct literal not valid as l-value828 .ref => unreachable, // struct literal not valid as l-value
821 .ty => |ty_inst| {829 .ty => |ty_inst| {
822 return mod.failNode(scope, node, "TODO implement structInitExpr ty", .{});830 return mod.failNode(scope, node, "TODO implement structInitExpr ty", .{});
...@@ -1980,7 +1988,7 @@ fn orelseCatchExpr(...@@ -1980,7 +1988,7 @@ fn orelseCatchExpr(
1980 // TODO handle catch1988 // TODO handle catch
1981 const operand_rl: ResultLoc = switch (block_scope.break_result_loc) {1989 const operand_rl: ResultLoc = switch (block_scope.break_result_loc) {
1982 .ref => .ref,1990 .ref => .ref,
1983 .discard, .none, .block_ptr, .inferred_ptr => .none,1991 .discard, .none, .none_or_ref, .block_ptr, .inferred_ptr => .none,
1984 .ty => |elem_ty| blk: {1992 .ty => |elem_ty| blk: {
1985 const wrapped_ty = try block_scope.addUnNode(.optional_type, elem_ty, node);1993 const wrapped_ty = try block_scope.addUnNode(.optional_type, elem_ty, node);
1986 break :blk .{ .ty = wrapped_ty };1994 break :blk .{ .ty = wrapped_ty };
...@@ -2156,7 +2164,7 @@ pub fn fieldAccess(...@@ -2156,7 +2164,7 @@ pub fn fieldAccess(
2156 .field_name_start = str_index,2164 .field_name_start = str_index,
2157 }),2165 }),
2158 else => return rvalue(gz, scope, rl, try gz.addPlNode(.field_val, node, zir.Inst.Field{2166 else => return rvalue(gz, scope, rl, try gz.addPlNode(.field_val, node, zir.Inst.Field{
2159 .lhs = try expr(gz, scope, .none, object_node),2167 .lhs = try expr(gz, scope, .none_or_ref, object_node),
2160 .field_name_start = str_index,2168 .field_name_start = str_index,
2161 }), node),2169 }), node),
2162 }2170 }
...@@ -3474,9 +3482,13 @@ fn identifier(...@@ -3474,9 +3482,13 @@ fn identifier(
3474 .local_ptr => {3482 .local_ptr => {
3475 const local_ptr = s.cast(Scope.LocalPtr).?;3483 const local_ptr = s.cast(Scope.LocalPtr).?;
3476 if (mem.eql(u8, local_ptr.name, ident_name)) {3484 if (mem.eql(u8, local_ptr.name, ident_name)) {
3477 if (rl == .ref) return local_ptr.ptr;3485 switch (rl) {
3478 const loaded = try gz.addUnNode(.load, local_ptr.ptr, ident);3486 .ref, .none_or_ref => return local_ptr.ptr,
3479 return rvalue(gz, scope, rl, loaded, ident);3487 else => {
3488 const loaded = try gz.addUnNode(.load, local_ptr.ptr, ident);
3489 return rvalue(gz, scope, rl, loaded, ident);
3490 },
3491 }
3480 }3492 }
3481 s = local_ptr.parent;3493 s = local_ptr.parent;
3482 },3494 },
...@@ -3493,7 +3505,7 @@ fn identifier(...@@ -3493,7 +3505,7 @@ fn identifier(
3493 }3505 }
3494 const decl_index = @intCast(u32, gop.index);3506 const decl_index = @intCast(u32, gop.index);
3495 switch (rl) {3507 switch (rl) {
3496 .ref => return gz.addDecl(.decl_ref, decl_index, ident),3508 .ref, .none_or_ref => return gz.addDecl(.decl_ref, decl_index, ident),
3497 else => return rvalue(gz, scope, rl, try gz.addDecl(.decl_val, decl_index, ident), ident),3509 else => return rvalue(gz, scope, rl, try gz.addDecl(.decl_val, decl_index, ident), ident),
3498 }3510 }
3499}3511}
...@@ -3697,7 +3709,7 @@ fn as(...@@ -3697,7 +3709,7 @@ fn as(
3697) InnerError!zir.Inst.Ref {3709) InnerError!zir.Inst.Ref {
3698 const dest_type = try typeExpr(gz, scope, lhs);3710 const dest_type = try typeExpr(gz, scope, lhs);
3699 switch (rl) {3711 switch (rl) {
3700 .none, .discard, .ref, .ty => {3712 .none, .none_or_ref, .discard, .ref, .ty => {
3701 const result = try expr(gz, scope, .{ .ty = dest_type }, rhs);3713 const result = try expr(gz, scope, .{ .ty = dest_type }, rhs);
3702 return rvalue(gz, scope, rl, result, node);3714 return rvalue(gz, scope, rl, result, node);
3703 },3715 },
...@@ -3781,7 +3793,7 @@ fn bitCast(...@@ -3781,7 +3793,7 @@ fn bitCast(
3781 });3793 });
3782 return rvalue(gz, scope, rl, result, node);3794 return rvalue(gz, scope, rl, result, node);
3783 },3795 },
3784 .ref => unreachable, // `@bitCast` is not allowed as an r-value.3796 .ref, .none_or_ref => unreachable, // `@bitCast` is not allowed as an r-value.
3785 .ptr => |result_ptr| {3797 .ptr => |result_ptr| {
3786 const casted_result_ptr = try gz.addUnNode(.bitcast_result_ptr, result_ptr, node);3798 const casted_result_ptr = try gz.addUnNode(.bitcast_result_ptr, result_ptr, node);
3787 return expr(gz, scope, .{ .ptr = casted_result_ptr }, rhs);3799 return expr(gz, scope, .{ .ptr = casted_result_ptr }, rhs);
...@@ -4354,7 +4366,7 @@ fn rvalue(...@@ -4354,7 +4366,7 @@ fn rvalue(
4354 src_node: ast.Node.Index,4366 src_node: ast.Node.Index,
4355) InnerError!zir.Inst.Ref {4367) InnerError!zir.Inst.Ref {
4356 switch (rl) {4368 switch (rl) {
4357 .none => return result,4369 .none, .none_or_ref => return result,
4358 .discard => {4370 .discard => {
4359 // Emit a compile error for discarding error values.4371 // Emit a compile error for discarding error values.
4360 _ = try gz.addUnNode(.ensure_result_non_error, result, src_node);4372 _ = try gz.addUnNode(.ensure_result_non_error, result, src_node);
src/Module.zig+8
...@@ -373,6 +373,11 @@ pub const Struct = struct {...@@ -373,6 +373,11 @@ pub const Struct = struct {
373 /// Uses `unreachable_value` to indicate no default.373 /// Uses `unreachable_value` to indicate no default.
374 default_val: Value,374 default_val: Value,
375 };375 };
376
377 pub fn getFullyQualifiedName(struct_obj: *Struct, gpa: *Allocator) ![]u8 {
378 // TODO this should return e.g. "std.fs.Dir.OpenOptions"
379 return gpa.dupe(u8, mem.spanZ(struct_obj.owner_decl.name));
380 }
376};381};
377382
378/// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.383/// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
...@@ -1048,6 +1053,9 @@ pub const Scope = struct {...@@ -1048,6 +1053,9 @@ pub const Scope = struct {
1048 gz.rl_ty_inst = ty_inst;1053 gz.rl_ty_inst = ty_inst;
1049 gz.break_result_loc = parent_rl;1054 gz.break_result_loc = parent_rl;
1050 },1055 },
1056 .none_or_ref => {
1057 gz.break_result_loc = .ref;
1058 },
1051 .discard, .none, .ptr, .ref => {1059 .discard, .none, .ptr, .ref => {
1052 gz.break_result_loc = parent_rl;1060 gz.break_result_loc = parent_rl;
1053 },1061 },
src/Sema.zig+6-2
...@@ -600,6 +600,7 @@ fn zirStructDecl(...@@ -600,6 +600,7 @@ fn zirStructDecl(
600600
601 const struct_obj = try new_decl_arena.allocator.create(Module.Struct);601 const struct_obj = try new_decl_arena.allocator.create(Module.Struct);
602 const struct_ty = try Type.Tag.@"struct".create(&new_decl_arena.allocator, struct_obj);602 const struct_ty = try Type.Tag.@"struct".create(&new_decl_arena.allocator, struct_obj);
603 const struct_val = try Value.Tag.ty.create(&new_decl_arena.allocator, struct_ty);
603 struct_obj.* = .{604 struct_obj.* = .{
604 .owner_decl = sema.owner_decl,605 .owner_decl = sema.owner_decl,
605 .fields = fields_map,606 .fields = fields_map,
...@@ -611,7 +612,7 @@ fn zirStructDecl(...@@ -611,7 +612,7 @@ fn zirStructDecl(
611 };612 };
612 const new_decl = try sema.mod.createAnonymousDecl(&block.base, &new_decl_arena, .{613 const new_decl = try sema.mod.createAnonymousDecl(&block.base, &new_decl_arena, .{
613 .ty = Type.initTag(.type),614 .ty = Type.initTag(.type),
614 .val = try Value.Tag.ty.create(gpa, struct_ty),615 .val = struct_val,
615 });616 });
616 return sema.analyzeDeclVal(block, src, new_decl);617 return sema.analyzeDeclVal(block, src, new_decl);
617}618}
...@@ -2139,7 +2140,10 @@ fn zirFieldVal(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerErro...@@ -2139,7 +2140,10 @@ fn zirFieldVal(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerErro
2139 const extra = sema.code.extraData(zir.Inst.Field, inst_data.payload_index).data;2140 const extra = sema.code.extraData(zir.Inst.Field, inst_data.payload_index).data;
2140 const field_name = sema.code.nullTerminatedString(extra.field_name_start);2141 const field_name = sema.code.nullTerminatedString(extra.field_name_start);
2141 const object = try sema.resolveInst(extra.lhs);2142 const object = try sema.resolveInst(extra.lhs);
2142 const object_ptr = try sema.analyzeRef(block, src, object);2143 const object_ptr = if (object.ty.zigTypeTag() == .Pointer)
2144 object
2145 else
2146 try sema.analyzeRef(block, src, object);
2143 const result_ptr = try sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src);2147 const result_ptr = try sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src);
2144 return sema.analyzeLoad(block, src, result_ptr, result_ptr.src);2148 return sema.analyzeLoad(block, src, result_ptr, result_ptr.src);
2145}2149}
src/codegen/c.zig+106-12
...@@ -44,22 +44,34 @@ fn formatTypeAsCIdentifier(...@@ -44,22 +44,34 @@ fn formatTypeAsCIdentifier(
44 var buffer = [1]u8{0} ** 128;44 var buffer = [1]u8{0} ** 128;
45 // We don't care if it gets cut off, it's still more unique than a number45 // We don't care if it gets cut off, it's still more unique than a number
46 var buf = std.fmt.bufPrint(&buffer, "{}", .{data}) catch &buffer;46 var buf = std.fmt.bufPrint(&buffer, "{}", .{data}) catch &buffer;
47 return formatIdent(buf, "", .{}, writer);
48}
49
50pub fn typeToCIdentifier(t: Type) std.fmt.Formatter(formatTypeAsCIdentifier) {
51 return .{ .data = t };
52}
4753
48 for (buf) |c, i| {54fn formatIdent(
55 ident: []const u8,
56 comptime fmt: []const u8,
57 options: std.fmt.FormatOptions,
58 writer: anytype,
59) !void {
60 for (ident) |c, i| {
49 switch (c) {61 switch (c) {
50 0 => return writer.writeAll(buf[0..i]),62 'a'...'z', 'A'...'Z', '_' => try writer.writeByte(c),
51 'a'...'z', 'A'...'Z', '_', '$' => {},
52 '0'...'9' => if (i == 0) {63 '0'...'9' => if (i == 0) {
53 buf[i] = '_';64 try writer.print("${x:2}", .{c});
65 } else {
66 try writer.writeByte(c);
54 },67 },
55 else => buf[i] = '_',68 else => try writer.print("${x:2}", .{c}),
56 }69 }
57 }70 }
58 return writer.writeAll(buf);
59}71}
6072
61pub fn typeToCIdentifier(t: Type) std.fmt.Formatter(formatTypeAsCIdentifier) {73pub fn fmtIdent(ident: []const u8) std.fmt.Formatter(formatIdent) {
62 return .{ .data = t };74 return .{ .data = ident };
63}75}
6476
65/// This data is available when outputting .c code for a Module.77/// This data is available when outputting .c code for a Module.
...@@ -430,6 +442,36 @@ pub const DeclGen = struct {...@@ -430,6 +442,36 @@ pub const DeclGen = struct {
430 try w.writeAll(name);442 try w.writeAll(name);
431 dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered });443 dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered });
432 },444 },
445 .Struct => {
446 if (dg.typedefs.get(t)) |some| {
447 return w.writeAll(some.name);
448 }
449 const struct_obj = t.castTag(.@"struct").?.data; // Handle 0 bit types elsewhere.
450 const fqn = try struct_obj.getFullyQualifiedName(dg.typedefs.allocator);
451 defer dg.typedefs.allocator.free(fqn);
452
453 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
454 defer buffer.deinit();
455
456 try buffer.appendSlice("typedef struct {\n");
457 for (struct_obj.fields.entries.items) |entry| {
458 try buffer.append(' ');
459 try dg.renderType(buffer.writer(), entry.value.ty);
460 try buffer.writer().print(" {s};\n", .{fmtIdent(entry.key)});
461 }
462 try buffer.appendSlice("} ");
463
464 const name_start = buffer.items.len;
465 try buffer.writer().print("zig_S_{s};\n", .{fmtIdent(fqn)});
466
467 const rendered = buffer.toOwnedSlice();
468 errdefer dg.typedefs.allocator.free(rendered);
469 const name = rendered[name_start .. rendered.len - 2];
470
471 try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1);
472 try w.writeAll(name);
473 dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered });
474 },
433 .Null, .Undefined => unreachable, // must be const or comptime475 .Null, .Undefined => unreachable, // must be const or comptime
434 else => |e| return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type {s}", .{476 else => |e| return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type {s}", .{
435 @tagName(e),477 @tagName(e),
...@@ -525,8 +567,23 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi...@@ -525,8 +567,23 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
525567
526 for (body.instructions) |inst| {568 for (body.instructions) |inst| {
527 const result_value = switch (inst.tag) {569 const result_value = switch (inst.tag) {
528 .constant => unreachable, // excluded from function bodies570 // TODO use a different strategy for add that communicates to the optimizer
571 // that wrapping is UB.
529 .add => try genBinOp(o, inst.castTag(.add).?, " + "),572 .add => try genBinOp(o, inst.castTag(.add).?, " + "),
573 // TODO make this do wrapping arithmetic for signed ints
574 .addwrap => try genBinOp(o, inst.castTag(.add).?, " + "),
575 // TODO use a different strategy for sub that communicates to the optimizer
576 // that wrapping is UB.
577 .sub => try genBinOp(o, inst.castTag(.sub).?, " - "),
578 // TODO make this do wrapping arithmetic for signed ints
579 .subwrap => try genBinOp(o, inst.castTag(.sub).?, " - "),
580 // TODO use a different strategy for mul that communicates to the optimizer
581 // that wrapping is UB.
582 .mul => try genBinOp(o, inst.castTag(.sub).?, " * "),
583 // TODO make this do wrapping multiplication for signed ints
584 .mulwrap => try genBinOp(o, inst.castTag(.sub).?, " * "),
585
586 .constant => unreachable, // excluded from function bodies
530 .alloc => try genAlloc(o, inst.castTag(.alloc).?),587 .alloc => try genAlloc(o, inst.castTag(.alloc).?),
531 .arg => genArg(o),588 .arg => genArg(o),
532 .assembly => try genAsm(o, inst.castTag(.assembly).?),589 .assembly => try genAsm(o, inst.castTag(.assembly).?),
...@@ -546,7 +603,6 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi...@@ -546,7 +603,6 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
546 .ret => try genRet(o, inst.castTag(.ret).?),603 .ret => try genRet(o, inst.castTag(.ret).?),
547 .retvoid => try genRetVoid(o),604 .retvoid => try genRetVoid(o),
548 .store => try genStore(o, inst.castTag(.store).?),605 .store => try genStore(o, inst.castTag(.store).?),
549 .sub => try genBinOp(o, inst.castTag(.sub).?, " - "),
550 .unreach => try genUnreach(o, inst.castTag(.unreach).?),606 .unreach => try genUnreach(o, inst.castTag(.unreach).?),
551 .loop => try genLoop(o, inst.castTag(.loop).?),607 .loop => try genLoop(o, inst.castTag(.loop).?),
552 .condbr => try genCondBr(o, inst.castTag(.condbr).?),608 .condbr => try genCondBr(o, inst.castTag(.condbr).?),
...@@ -567,17 +623,24 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi...@@ -567,17 +623,24 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
567 .wrap_optional => try genWrapOptional(o, inst.castTag(.wrap_optional).?),623 .wrap_optional => try genWrapOptional(o, inst.castTag(.wrap_optional).?),
568 .optional_payload => try genOptionalPayload(o, inst.castTag(.optional_payload).?),624 .optional_payload => try genOptionalPayload(o, inst.castTag(.optional_payload).?),
569 .optional_payload_ptr => try genOptionalPayload(o, inst.castTag(.optional_payload_ptr).?),625 .optional_payload_ptr => try genOptionalPayload(o, inst.castTag(.optional_payload_ptr).?),
626 .ref => try genRef(o, inst.castTag(.ref).?),
627 .struct_field_ptr => try genStructFieldPtr(o, inst.castTag(.struct_field_ptr).?),
628
570 .is_err => try genIsErr(o, inst.castTag(.is_err).?),629 .is_err => try genIsErr(o, inst.castTag(.is_err).?),
571 .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?),630 .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?),
572 .error_to_int => try genErrorToInt(o, inst.castTag(.error_to_int).?),631 .error_to_int => try genErrorToInt(o, inst.castTag(.error_to_int).?),
573 .int_to_error => try genIntToError(o, inst.castTag(.int_to_error).?),632 .int_to_error => try genIntToError(o, inst.castTag(.int_to_error).?),
633
574 .unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?),634 .unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?),
575 .unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?),635 .unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?),
576 .unwrap_errunion_payload_ptr => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload_ptr).?),636 .unwrap_errunion_payload_ptr => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload_ptr).?),
577 .unwrap_errunion_err_ptr => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err_ptr).?),637 .unwrap_errunion_err_ptr => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err_ptr).?),
578 .wrap_errunion_payload => try genWrapErrUnionPay(o, inst.castTag(.wrap_errunion_payload).?),638 .wrap_errunion_payload => try genWrapErrUnionPay(o, inst.castTag(.wrap_errunion_payload).?),
579 .wrap_errunion_err => try genWrapErrUnionErr(o, inst.castTag(.wrap_errunion_err).?),639 .wrap_errunion_err => try genWrapErrUnionErr(o, inst.castTag(.wrap_errunion_err).?),
580 else => |e| return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for {}", .{e}),640 .br_block_flat => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for br_block_flat", .{}),
641 .ptrtoint => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for ptrtoint", .{}),
642 .varptr => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for varptr", .{}),
643 .floatcast => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for floatcast", .{}),
581 };644 };
582 switch (result_value) {645 switch (result_value) {
583 .none => {},646 .none => {},
...@@ -996,6 +1059,37 @@ fn genOptionalPayload(o: *Object, inst: *Inst.UnOp) !CValue {...@@ -996,6 +1059,37 @@ fn genOptionalPayload(o: *Object, inst: *Inst.UnOp) !CValue {
996 return local;1059 return local;
997}1060}
9981061
1062fn genRef(o: *Object, inst: *Inst.UnOp) !CValue {
1063 const writer = o.writer();
1064 const operand = try o.resolveInst(inst.operand);
1065
1066 const local = try o.allocLocal(inst.base.ty, .Const);
1067 try writer.writeAll(" = ");
1068 try o.writeCValue(writer, operand);
1069 try writer.writeAll(";\n");
1070 return local;
1071}
1072
1073fn genStructFieldPtr(o: *Object, inst: *Inst.StructFieldPtr) !CValue {
1074 const writer = o.writer();
1075 const struct_ptr = try o.resolveInst(inst.struct_ptr);
1076 const struct_obj = inst.struct_ptr.ty.elemType().castTag(.@"struct").?.data;
1077 const field_name = struct_obj.fields.entries.items[inst.field_index].key;
1078
1079 const local = try o.allocLocal(inst.base.ty, .Const);
1080 switch (struct_ptr) {
1081 .local_ref => |i| {
1082 try writer.print(" = &t{d}.{};\n", .{ i, fmtIdent(field_name) });
1083 },
1084 else => {
1085 try writer.writeAll(" = &");
1086 try o.writeCValue(writer, struct_ptr);
1087 try writer.print("->{};\n", .{fmtIdent(field_name)});
1088 },
1089 }
1090 return local;
1091}
1092
999// *(E!T) -> E NOT *E1093// *(E!T) -> E NOT *E
1000fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {1094fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {
1001 const writer = o.writer();1095 const writer = o.writer();
...@@ -1088,7 +1182,7 @@ fn IndentWriter(comptime UnderlyingWriter: type) type {...@@ -1088,7 +1182,7 @@ fn IndentWriter(comptime UnderlyingWriter: type) type {
1088 pub const Error = UnderlyingWriter.Error;1182 pub const Error = UnderlyingWriter.Error;
1089 pub const Writer = std.io.Writer(*Self, Error, write);1183 pub const Writer = std.io.Writer(*Self, Error, write);
10901184
1091 pub const indent_delta = 4;1185 pub const indent_delta = 1;
10921186
1093 underlying_writer: UnderlyingWriter,1187 underlying_writer: UnderlyingWriter,
1094 indent_count: usize = 0,1188 indent_count: usize = 0,
src/zir.zig+1
...@@ -338,6 +338,7 @@ pub const Inst = struct {...@@ -338,6 +338,7 @@ pub const Inst = struct {
338 field_ptr,338 field_ptr,
339 /// Given a struct or object that contains virtual fields, returns the named field.339 /// Given a struct or object that contains virtual fields, returns the named field.
340 /// The field name is stored in string_bytes. Used by a.b syntax.340 /// The field name is stored in string_bytes. Used by a.b syntax.
341 /// This instruction also accepts a pointer.
341 /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.342 /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.
342 field_val,343 field_val,
343 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer344 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer
test/stage2/cbe.zig+2-2
...@@ -489,8 +489,8 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -489,8 +489,8 @@ pub fn addCases(ctx: *TestContext) !void {
489 \\ZIG_EXTERN_C zig_noreturn void _start(void);489 \\ZIG_EXTERN_C zig_noreturn void _start(void);
490 \\490 \\
491 \\zig_noreturn void _start(void) {491 \\zig_noreturn void _start(void) {
492 \\ zig_breakpoint();492 \\ zig_breakpoint();
493 \\ zig_unreachable();493 \\ zig_unreachable();
494 \\}494 \\}
495 \\495 \\
496 );496 );