| author | |
| committer | |
| log | 97d7fddfb78d17132749cae59fea36fe661bf642 |
| tree | 5b01a32f9ee35e6cef838cffb5058e5f0f777229 |
| parent | 43d364afef7f0609f9d897c7ff129ba6b9b3cab0 |
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 | 124 | /// The expression must generate a pointer rather than a value. For example, the left hand side |
| 125 | 125 | /// of an assignment uses this kind of result location. |
| 126 | 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 | 130 | /// The expression will be coerced into this type, but it will be evaluated as an rvalue. |
| 128 | 131 | ty: zir.Inst.Ref, |
| 129 | 132 | /// The expression must store its result into this typed pointer. The result instruction |
| ... | ... | @@ -157,7 +160,7 @@ pub const ResultLoc = union(enum) { |
| 157 | 160 | var elide_store_to_block_ptr_instructions = false; |
| 158 | 161 | switch (rl) { |
| 159 | 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 | 164 | .tag = .break_operand, |
| 162 | 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 | 609 | |
| 607 | 610 | .deref => { |
| 608 | 611 | const lhs = try expr(gz, scope, .none, node_datas[node].lhs); |
| 609 | const result = try gz.addUnNode(.load, lhs, node); | |
| 610 | return rvalue(gz, scope, rl, result, node); | |
| 612 | switch (rl) { | |
| 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 | 620 | .address_of => { |
| 613 | 621 | const result = try expr(gz, scope, .ref, node_datas[node].lhs); |
| ... | ... | @@ -816,7 +824,7 @@ pub fn structInitExpr( |
| 816 | 824 | } |
| 817 | 825 | switch (rl) { |
| 818 | 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 | 828 | .ref => unreachable, // struct literal not valid as l-value |
| 821 | 829 | .ty => |ty_inst| { |
| 822 | 830 | return mod.failNode(scope, node, "TODO implement structInitExpr ty", .{}); |
| ... | ... | @@ -1980,7 +1988,7 @@ fn orelseCatchExpr( |
| 1980 | 1988 | // TODO handle catch |
| 1981 | 1989 | const operand_rl: ResultLoc = switch (block_scope.break_result_loc) { |
| 1982 | 1990 | .ref => .ref, |
| 1983 | .discard, .none, .block_ptr, .inferred_ptr => .none, | |
| 1991 | .discard, .none, .none_or_ref, .block_ptr, .inferred_ptr => .none, | |
| 1984 | 1992 | .ty => |elem_ty| blk: { |
| 1985 | 1993 | const wrapped_ty = try block_scope.addUnNode(.optional_type, elem_ty, node); |
| 1986 | 1994 | break :blk .{ .ty = wrapped_ty }; |
| ... | ... | @@ -2156,7 +2164,7 @@ pub fn fieldAccess( |
| 2156 | 2164 | .field_name_start = str_index, |
| 2157 | 2165 | }), |
| 2158 | 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 | 2168 | .field_name_start = str_index, |
| 2161 | 2169 | }), node), |
| 2162 | 2170 | } |
| ... | ... | @@ -3474,9 +3482,13 @@ fn identifier( |
| 3474 | 3482 | .local_ptr => { |
| 3475 | 3483 | const local_ptr = s.cast(Scope.LocalPtr).?; |
| 3476 | 3484 | if (mem.eql(u8, local_ptr.name, ident_name)) { |
| 3477 | if (rl == .ref) return local_ptr.ptr; | |
| 3478 | const loaded = try gz.addUnNode(.load, local_ptr.ptr, ident); | |
| 3479 | return rvalue(gz, scope, rl, loaded, ident); | |
| 3485 | switch (rl) { | |
| 3486 | .ref, .none_or_ref => return local_ptr.ptr, | |
| 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 | 3493 | s = local_ptr.parent; |
| 3482 | 3494 | }, |
| ... | ... | @@ -3493,7 +3505,7 @@ fn identifier( |
| 3493 | 3505 | } |
| 3494 | 3506 | const decl_index = @intCast(u32, gop.index); |
| 3495 | 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 | 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 | 3709 | ) InnerError!zir.Inst.Ref { |
| 3698 | 3710 | const dest_type = try typeExpr(gz, scope, lhs); |
| 3699 | 3711 | switch (rl) { |
| 3700 | .none, .discard, .ref, .ty => { | |
| 3712 | .none, .none_or_ref, .discard, .ref, .ty => { | |
| 3701 | 3713 | const result = try expr(gz, scope, .{ .ty = dest_type }, rhs); |
| 3702 | 3714 | return rvalue(gz, scope, rl, result, node); |
| 3703 | 3715 | }, |
| ... | ... | @@ -3781,7 +3793,7 @@ fn bitCast( |
| 3781 | 3793 | }); |
| 3782 | 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 | 3797 | .ptr => |result_ptr| { |
| 3786 | 3798 | const casted_result_ptr = try gz.addUnNode(.bitcast_result_ptr, result_ptr, node); |
| 3787 | 3799 | return expr(gz, scope, .{ .ptr = casted_result_ptr }, rhs); |
| ... | ... | @@ -4354,7 +4366,7 @@ fn rvalue( |
| 4354 | 4366 | src_node: ast.Node.Index, |
| 4355 | 4367 | ) InnerError!zir.Inst.Ref { |
| 4356 | 4368 | switch (rl) { |
| 4357 | .none => return result, | |
| 4369 | .none, .none_or_ref => return result, | |
| 4358 | 4370 | .discard => { |
| 4359 | 4371 | // Emit a compile error for discarding error values. |
| 4360 | 4372 | _ = try gz.addUnNode(.ensure_result_non_error, result, src_node); |
src/Module.zig+8| ... | ... | @@ -373,6 +373,11 @@ pub const Struct = struct { |
| 373 | 373 | /// Uses `unreachable_value` to indicate no default. |
| 374 | 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 | }; |
| 377 | 382 | |
| 378 | 383 | /// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator. |
| ... | ... | @@ -1048,6 +1053,9 @@ pub const Scope = struct { |
| 1048 | 1053 | gz.rl_ty_inst = ty_inst; |
| 1049 | 1054 | gz.break_result_loc = parent_rl; |
| 1050 | 1055 | }, |
| 1056 | .none_or_ref => { | |
| 1057 | gz.break_result_loc = .ref; | |
| 1058 | }, | |
| 1051 | 1059 | .discard, .none, .ptr, .ref => { |
| 1052 | 1060 | gz.break_result_loc = parent_rl; |
| 1053 | 1061 | }, |
src/Sema.zig+6-2| ... | ... | @@ -600,6 +600,7 @@ fn zirStructDecl( |
| 600 | 600 | |
| 601 | 601 | const struct_obj = try new_decl_arena.allocator.create(Module.Struct); |
| 602 | 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 | 604 | struct_obj.* = .{ |
| 604 | 605 | .owner_decl = sema.owner_decl, |
| 605 | 606 | .fields = fields_map, |
| ... | ... | @@ -611,7 +612,7 @@ fn zirStructDecl( |
| 611 | 612 | }; |
| 612 | 613 | const new_decl = try sema.mod.createAnonymousDecl(&block.base, &new_decl_arena, .{ |
| 613 | 614 | .ty = Type.initTag(.type), |
| 614 | .val = try Value.Tag.ty.create(gpa, struct_ty), | |
| 615 | .val = struct_val, | |
| 615 | 616 | }); |
| 616 | 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 | 2140 | const extra = sema.code.extraData(zir.Inst.Field, inst_data.payload_index).data; |
| 2140 | 2141 | const field_name = sema.code.nullTerminatedString(extra.field_name_start); |
| 2141 | 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 | 2147 | const result_ptr = try sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src); |
| 2144 | 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 | 44 | var buffer = [1]u8{0} ** 128; |
| 45 | 45 | // We don't care if it gets cut off, it's still more unique than a number |
| 46 | 46 | var buf = std.fmt.bufPrint(&buffer, "{}", .{data}) catch &buffer; |
| 47 | return formatIdent(buf, "", .{}, writer); | |
| 48 | } | |
| 49 | ||
| 50 | pub fn typeToCIdentifier(t: Type) std.fmt.Formatter(formatTypeAsCIdentifier) { | |
| 51 | return .{ .data = t }; | |
| 52 | } | |
| 47 | 53 | |
| 48 | for (buf) |c, i| { | |
| 54 | fn 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 | 61 | switch (c) { |
| 50 | 0 => return writer.writeAll(buf[0..i]), | |
| 51 | 'a'...'z', 'A'...'Z', '_', '$' => {}, | |
| 62 | 'a'...'z', 'A'...'Z', '_' => try writer.writeByte(c), | |
| 52 | 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 | } |
| 60 | 72 | |
| 61 | pub fn typeToCIdentifier(t: Type) std.fmt.Formatter(formatTypeAsCIdentifier) { | |
| 62 | return .{ .data = t }; | |
| 73 | pub fn fmtIdent(ident: []const u8) std.fmt.Formatter(formatIdent) { | |
| 74 | return .{ .data = ident }; | |
| 63 | 75 | } |
| 64 | 76 | |
| 65 | 77 | /// This data is available when outputting .c code for a Module. |
| ... | ... | @@ -430,6 +442,36 @@ pub const DeclGen = struct { |
| 430 | 442 | try w.writeAll(name); |
| 431 | 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 | 475 | .Null, .Undefined => unreachable, // must be const or comptime |
| 434 | 476 | else => |e| return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type {s}", .{ |
| 435 | 477 | @tagName(e), |
| ... | ... | @@ -525,8 +567,23 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi |
| 525 | 567 | |
| 526 | 568 | for (body.instructions) |inst| { |
| 527 | 569 | const result_value = switch (inst.tag) { |
| 528 | .constant => unreachable, // excluded from function bodies | |
| 570 | // TODO use a different strategy for add that communicates to the optimizer | |
| 571 | // that wrapping is UB. | |
| 529 | 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 | 587 | .alloc => try genAlloc(o, inst.castTag(.alloc).?), |
| 531 | 588 | .arg => genArg(o), |
| 532 | 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 | 603 | .ret => try genRet(o, inst.castTag(.ret).?), |
| 547 | 604 | .retvoid => try genRetVoid(o), |
| 548 | 605 | .store => try genStore(o, inst.castTag(.store).?), |
| 549 | .sub => try genBinOp(o, inst.castTag(.sub).?, " - "), | |
| 550 | 606 | .unreach => try genUnreach(o, inst.castTag(.unreach).?), |
| 551 | 607 | .loop => try genLoop(o, inst.castTag(.loop).?), |
| 552 | 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 | 623 | .wrap_optional => try genWrapOptional(o, inst.castTag(.wrap_optional).?), |
| 568 | 624 | .optional_payload => try genOptionalPayload(o, inst.castTag(.optional_payload).?), |
| 569 | 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 | 629 | .is_err => try genIsErr(o, inst.castTag(.is_err).?), |
| 571 | 630 | .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?), |
| 572 | 631 | .error_to_int => try genErrorToInt(o, inst.castTag(.error_to_int).?), |
| 573 | 632 | .int_to_error => try genIntToError(o, inst.castTag(.int_to_error).?), |
| 633 | ||
| 574 | 634 | .unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?), |
| 575 | 635 | .unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?), |
| 576 | 636 | .unwrap_errunion_payload_ptr => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload_ptr).?), |
| 577 | 637 | .unwrap_errunion_err_ptr => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err_ptr).?), |
| 578 | 638 | .wrap_errunion_payload => try genWrapErrUnionPay(o, inst.castTag(.wrap_errunion_payload).?), |
| 579 | 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 | 645 | switch (result_value) { |
| 583 | 646 | .none => {}, |
| ... | ... | @@ -996,6 +1059,37 @@ fn genOptionalPayload(o: *Object, inst: *Inst.UnOp) !CValue { |
| 996 | 1059 | return local; |
| 997 | 1060 | } |
| 998 | 1061 | |
| 1062 | fn 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 | ||
| 1073 | fn 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 | 1093 | // *(E!T) -> E NOT *E |
| 1000 | 1094 | fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue { |
| 1001 | 1095 | const writer = o.writer(); |
| ... | ... | @@ -1088,7 +1182,7 @@ fn IndentWriter(comptime UnderlyingWriter: type) type { |
| 1088 | 1182 | pub const Error = UnderlyingWriter.Error; |
| 1089 | 1183 | pub const Writer = std.io.Writer(*Self, Error, write); |
| 1090 | 1184 | |
| 1091 | pub const indent_delta = 4; | |
| 1185 | pub const indent_delta = 1; | |
| 1092 | 1186 | |
| 1093 | 1187 | underlying_writer: UnderlyingWriter, |
| 1094 | 1188 | indent_count: usize = 0, |
src/zir.zig+1| ... | ... | @@ -338,6 +338,7 @@ pub const Inst = struct { |
| 338 | 338 | field_ptr, |
| 339 | 339 | /// Given a struct or object that contains virtual fields, returns the named field. |
| 340 | 340 | /// The field name is stored in string_bytes. Used by a.b syntax. |
| 341 | /// This instruction also accepts a pointer. | |
| 341 | 342 | /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field. |
| 342 | 343 | field_val, |
| 343 | 344 | /// 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 | 489 | \\ZIG_EXTERN_C zig_noreturn void _start(void); |
| 490 | 490 | \\ |
| 491 | 491 | \\zig_noreturn void _start(void) { |
| 492 | \\ zig_breakpoint(); | |
| 493 | \\ zig_unreachable(); | |
| 492 | \\ zig_breakpoint(); | |
| 493 | \\ zig_unreachable(); | |
| 494 | 494 | \\} |
| 495 | 495 | \\ |
| 496 | 496 | ); |