Commit b034c45b2b

Veikka Tuominen <git@vexu.eu>
2022-02-24 18:47:42
stage2: implement fieldParentPtr
1 parent 6249a24
src/arch/aarch64/CodeGen.zig
@@ -638,6 +638,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2),
             .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3),
 
+            .field_parent_ptr => try self.airFieldParentPtr(inst),
+
             .switch_br       => try self.airSwitch(inst),
             .slice_ptr       => try self.airSlicePtr(inst),
             .slice_len       => try self.airSliceLen(inst),
@@ -2118,6 +2120,13 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
     //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });
 }
 
+fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
+    const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
+    const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
+    _ = extra;
+    return self.fail("TODO implement codegen airFieldParentPtr", .{});
+}
+
 fn airArg(self: *Self, inst: Air.Inst.Index) !void {
     const arg_index = self.arg_index;
     self.arg_index += 1;
src/arch/arm/CodeGen.zig
@@ -622,6 +622,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2),
             .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3),
 
+            .field_parent_ptr => try self.airFieldParentPtr(inst),
+
             .switch_br       => try self.airSwitch(inst),
             .slice_ptr       => try self.airSlicePtr(inst),
             .slice_len       => try self.airSliceLen(inst),
@@ -1735,6 +1737,13 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
     return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });
 }
 
+fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
+    const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
+    const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
+    const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFieldParentPtr", .{});
+    return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
+}
+
 /// Don't call this function directly. Use binOp instead.
 ///
 /// Calling this function signals an intention to generate a Mir
src/arch/riscv64/CodeGen.zig
@@ -609,6 +609,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2),
             .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3),
 
+            .field_parent_ptr => try self.airFieldParentPtr(inst),
+
             .switch_br       => try self.airSwitch(inst),
             .slice_ptr       => try self.airSlicePtr(inst),
             .slice_len       => try self.airSliceLen(inst),
@@ -1360,6 +1362,12 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
     //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });
 }
 
+fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
+    _ = self;
+    _ = inst;
+    return self.fail("TODO implement codegen airFieldParentPtr", .{});
+}
+
 fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue, arg_index: u32) !void {
     const ty = self.air.instructions.items(.data)[inst].ty;
     const name = self.mod_fn.getParamName(arg_index);
src/arch/wasm/CodeGen.zig
@@ -1730,6 +1730,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
         .tag_name,
         .error_name,
         .errunion_payload_ptr_set,
+        .field_parent_ptr,
 
         // For these 4, probably best to wait until https://github.com/ziglang/zig/issues/10248
         // is implemented in the frontend before implementing them here in the wasm backend.
src/arch/x86_64/CodeGen.zig
@@ -721,6 +721,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
             .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2),
             .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3),
 
+            .field_parent_ptr => try self.airFieldParentPtr(inst),
+
             .switch_br       => try self.airSwitch(inst),
             .slice_ptr       => try self.airSlicePtr(inst),
             .slice_len       => try self.airSliceLen(inst),
@@ -2656,6 +2658,15 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
     return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });
 }
 
+fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
+    const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+    const result: MCValue = if (self.liveness.isUnused(inst))
+        .dead
+    else
+        return self.fail("TODO implement airFieldParentPtr for {}", .{self.target.cpu.arch});
+    return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
+}
+
 /// Perform "binary" operators, excluding comparisons.
 /// Currently, the following ops are supported:
 /// ADD, SUB, XOR, OR, AND
src/codegen/c.zig
@@ -1734,6 +1734,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
             .struct_field_ptr_index_2 => try airStructFieldPtrIndex(f, inst, 2),
             .struct_field_ptr_index_3 => try airStructFieldPtrIndex(f, inst, 3),
 
+            .field_parent_ptr => try airFieldParentPtr(f, inst),
+
             .struct_field_val => try airStructFieldVal(f, inst),
             .slice_ptr        => try airSliceField(f, inst, ".ptr;\n"),
             .slice_len        => try airSliceField(f, inst, ".len;\n"),
@@ -3026,6 +3028,11 @@ fn airStructFieldPtrIndex(f: *Function, inst: Air.Inst.Index, index: u8) !CValue
     return structFieldPtr(f, inst, struct_ptr_ty, struct_ptr, index);
 }
 
+fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {
+    _ = inst;
+    return f.fail("TODO: C backend: implement airFieldParentPtr", .{});
+}
+
 fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struct_ptr: CValue, index: u32) !CValue {
     const writer = f.object.writer();
     const struct_ty = struct_ptr_ty.elemType();
src/codegen/llvm.zig
@@ -2191,6 +2191,8 @@ pub const FuncGen = struct {
                 .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2),
                 .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3),
 
+                .field_parent_ptr => try self.airFieldParentPtr(inst),
+
                 .array_elem_val     => try self.airArrayElemVal(inst),
                 .slice_elem_val     => try self.airSliceElemVal(inst),
                 .slice_elem_ptr     => try self.airSliceElemPtr(inst),
@@ -2853,6 +2855,29 @@ pub const FuncGen = struct {
         }
     }
 
+    fn airFieldParentPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+        if (self.liveness.isUnused(inst)) return null;
+
+        const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
+        const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
+
+        const field_ptr = try self.resolveInst(extra.field_ptr);
+
+        const target = self.dg.module.getTarget();
+        const struct_ty = self.air.getRefType(ty_pl.ty).childType();
+        const field_offset = struct_ty.structFieldOffset(extra.field_index, target);
+
+        const res_ty = try self.dg.llvmType(self.air.getRefType(ty_pl.ty));
+        if (field_offset == 0) {
+            return self.builder.buildBitCast(field_ptr, res_ty, "");
+        }
+        const llvm_usize_ty = self.context.intType(target.cpu.arch.ptrBitWidth());
+
+        const field_ptr_int = self.builder.buildPtrToInt(field_ptr, llvm_usize_ty, "");
+        const base_ptr_int = self.builder.buildNUWSub(field_ptr_int, llvm_usize_ty.constInt(field_offset, .False), "");
+        return self.builder.buildIntToPtr(base_ptr_int, res_ty, "");
+    }
+
     fn airNot(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
         if (self.liveness.isUnused(inst))
             return null;
src/Air.zig
@@ -574,6 +574,10 @@ pub const Inst = struct {
         /// Uses the `prefetch` field.
         prefetch,
 
+        /// Implements @fieldParentPtr builtin.
+        /// Uses the `ty_pl` field.
+        field_parent_ptr,
+
         pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
             return switch (op) {
                 .lt => .cmp_lt,
@@ -703,6 +707,11 @@ pub const Bin = struct {
     rhs: Inst.Ref,
 };
 
+pub const FieldParentPtr = struct {
+    field_ptr: Inst.Ref,
+    field_index: u32,
+};
+
 /// Trailing:
 /// 0. `Inst.Ref` for every outputs_len
 /// 1. `Inst.Ref` for every inputs_len
@@ -856,6 +865,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
         .cmpxchg_strong,
         .slice,
         .vector_init,
+        .field_parent_ptr,
         => return air.getRefType(datas[inst].ty_pl.ty),
 
         .not,
src/AstGen.zig
@@ -2296,7 +2296,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
             .atomic_rmw,
             .mul_add,
             .builtin_call,
-            .field_ptr_type,
             .field_parent_ptr,
             .maximum,
             .minimum,
@@ -7403,11 +7402,10 @@ fn builtinCall(
         .field_parent_ptr => {
             const parent_type = try typeExpr(gz, scope, params[0]);
             const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]);
-            const field_ptr_type = try gz.addBin(.field_ptr_type, parent_type, field_name);
             const result = try gz.addPlNode(.field_parent_ptr, node, Zir.Inst.FieldParentPtr{
                 .parent_type = parent_type,
                 .field_name = field_name,
-                .field_ptr = try expr(gz, scope, .{ .ty = field_ptr_type }, params[2]),
+                .field_ptr = try expr(gz, scope, .none, params[2]),
             });
             return rvalue(gz, rl, result, node);
         },
src/Liveness.zig
@@ -446,6 +446,10 @@ fn analyzeInst(
             const extra = a.air.extraData(Air.StructField, inst_datas[inst].ty_pl.payload).data;
             return trackOperands(a, new_set, inst, main_tomb, .{ extra.struct_operand, .none, .none });
         },
+        .field_parent_ptr => {
+            const extra = a.air.extraData(Air.FieldParentPtr, inst_datas[inst].ty_pl.payload).data;
+            return trackOperands(a, new_set, inst, main_tomb, .{ extra.field_ptr, .none, .none });
+        },
         .ptr_elem_ptr, .slice_elem_ptr, .slice => {
             const extra = a.air.extraData(Air.Bin, inst_datas[inst].ty_pl.payload).data;
             return trackOperands(a, new_set, inst, main_tomb, .{ extra.lhs, extra.rhs, .none });
src/print_air.zig
@@ -247,6 +247,7 @@ const Writer = struct {
             .atomic_rmw => try w.writeAtomicRmw(s, inst),
             .memcpy => try w.writeMemcpy(s, inst),
             .memset => try w.writeMemset(s, inst),
+            .field_parent_ptr => try w.writeFieldParentPtr(s, inst),
 
             .add_with_overflow,
             .sub_with_overflow,
@@ -412,6 +413,14 @@ const Writer = struct {
         try w.writeOperand(s, inst, 2, extra.rhs);
     }
 
+    fn writeFieldParentPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
+        const pl_op = w.air.instructions.items(.data)[inst].ty_pl;
+        const extra = w.air.extraData(Air.FieldParentPtr, pl_op.payload).data;
+
+        try w.writeOperand(s, inst, 0, extra.field_ptr);
+        try s.print(", {d}", .{extra.field_index});
+    }
+
     fn writeMemcpy(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
         const pl_op = w.air.instructions.items(.data)[inst].pl_op;
         const extra = w.air.extraData(Air.Bin, pl_op.payload).data;
src/print_zir.zig
@@ -150,7 +150,6 @@ const Writer = struct {
             .store,
             .store_to_block_ptr,
             .store_to_inferred_ptr,
-            .field_ptr_type,
             => try self.writeBin(stream, inst),
 
             .alloc,
src/Sema.zig
@@ -732,7 +732,6 @@ fn analyzeBodyInner(
             .atomic_rmw                   => try sema.zirAtomicRmw(block, inst),
             .mul_add                      => try sema.zirMulAdd(block, inst),
             .builtin_call                 => try sema.zirBuiltinCall(block, inst),
-            .field_ptr_type               => try sema.zirFieldPtrType(block, inst),
             .field_parent_ptr             => try sema.zirFieldParentPtr(block, inst),
             .builtin_async_call           => try sema.zirBuiltinAsyncCall(block, inst),
             .@"resume"                    => try sema.zirResume(block, inst),
@@ -12839,16 +12838,68 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
     return sema.fail(block, src, "TODO: Sema.zirBuiltinCall", .{});
 }
 
-fn zirFieldPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
-    const src = inst_data.src();
-    return sema.fail(block, src, "TODO: Sema.zirFieldPtrType", .{});
-}
-
 fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const extra = sema.code.extraData(Zir.Inst.FieldParentPtr, inst_data.payload_index).data;
     const src = inst_data.src();
-    return sema.fail(block, src, "TODO: Sema.zirFieldParentPtr", .{});
+    const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+    const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
+    const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
+
+    const struct_ty = try sema.resolveType(block, ty_src, extra.parent_type);
+    const field_name = try sema.resolveConstString(block, name_src, extra.field_name);
+    const field_ptr = sema.resolveInst(extra.field_ptr);
+    const field_ptr_ty = sema.typeOf(field_ptr);
+
+    if (struct_ty.zigTypeTag() != .Struct) {
+        return sema.fail(block, ty_src, "expected struct type, found '{}'", .{struct_ty});
+    }
+    try sema.resolveTypeLayout(block, ty_src, struct_ty);
+
+    const struct_obj = struct_ty.castTag(.@"struct").?.data;
+    const field_index = struct_obj.fields.getIndex(field_name) orelse
+        return sema.failWithBadStructFieldAccess(block, struct_obj, name_src, field_name);
+
+    if (field_ptr_ty.zigTypeTag() != .Pointer) {
+        return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{field_ptr_ty});
+    }
+    const field = struct_obj.fields.values()[field_index];
+    const field_ptr_ty_info = field_ptr_ty.ptrInfo().data;
+
+    var ptr_ty_data: Type.Payload.Pointer.Data = .{
+        .pointee_type = field.ty,
+        .mutable = field_ptr_ty_info.mutable,
+        .@"addrspace" = field_ptr_ty_info.@"addrspace",
+    };
+
+    if (struct_obj.layout == .Packed) {
+        // TODO handle packed structs
+    } else if (field.abi_align.tag() != .abi_align_default) {
+        ptr_ty_data.@"align" = @intCast(u32, field.abi_align.toUnsignedInt());
+    }
+
+    const actual_field_ptr_ty = try Type.ptr(sema.arena, ptr_ty_data);
+    const casted_field_ptr = try sema.coerce(block, actual_field_ptr_ty, field_ptr, ptr_src);
+
+    ptr_ty_data.pointee_type = struct_ty;
+    const result_ptr = try Type.ptr(sema.arena, ptr_ty_data);
+
+    if (try sema.resolveDefinedValue(block, src, casted_field_ptr)) |field_ptr_val| {
+        const payload = field_ptr_val.castTag(.field_ptr).?.data;
+        return sema.addConstant(result_ptr, payload.container_ptr);
+    }
+
+    try sema.requireRuntimeBlock(block, src);
+    return block.addInst(.{
+        .tag = .field_parent_ptr,
+        .data = .{ .ty_pl = .{
+            .ty = try sema.addType(result_ptr),
+            .payload = try block.sema.addExtra(Air.FieldParentPtr{
+                .field_ptr = casted_field_ptr,
+                .field_index = @intCast(u32, field_index),
+            }),
+        } },
+    });
 }
 
 fn zirMinMax(
src/value.zig
@@ -1852,7 +1852,13 @@ pub const Value = extern union {
                 return eql(a_payload.ptr, b_payload.ptr, ptr_ty);
             },
             .elem_ptr => @panic("TODO: Implement more pointer eql cases"),
-            .field_ptr => @panic("TODO: Implement more pointer eql cases"),
+            .field_ptr => {
+                const a_payload = a.castTag(.field_ptr).?.data;
+                const b_payload = b.castTag(.field_ptr).?.data;
+                if (a_payload.field_index != b_payload.field_index) return false;
+
+                return eql(a_payload.container_ptr, b_payload.container_ptr, ty);
+            },
             .eu_payload_ptr => @panic("TODO: Implement more pointer eql cases"),
             .opt_payload_ptr => @panic("TODO: Implement more pointer eql cases"),
             .array => {
src/Zir.zig
@@ -892,10 +892,6 @@ pub const Inst = struct {
         /// Implements the `@call` builtin.
         /// Uses the `pl_node` union field with payload `BuiltinCall`.
         builtin_call,
-        /// Given a type and a field name, returns a pointer to the field type.
-        /// Assumed to be part of a `@fieldParentPtr` builtin call.
-        /// Uses the `bin` union field. LHS is type, RHS is field name.
-        field_ptr_type,
         /// Implements the `@fieldParentPtr` builtin.
         /// Uses the `pl_node` union field with payload `FieldParentPtr`.
         field_parent_ptr,
@@ -1192,7 +1188,6 @@ pub const Inst = struct {
                 .atomic_store,
                 .mul_add,
                 .builtin_call,
-                .field_ptr_type,
                 .field_parent_ptr,
                 .maximum,
                 .memcpy,
@@ -1466,7 +1461,6 @@ pub const Inst = struct {
                 .atomic_store = .pl_node,
                 .mul_add = .pl_node,
                 .builtin_call = .pl_node,
-                .field_ptr_type = .bin,
                 .field_parent_ptr = .pl_node,
                 .maximum = .pl_node,
                 .memcpy = .pl_node,
test/behavior.zig
@@ -129,6 +129,7 @@ test {
             _ = @import("behavior/switch_prong_err_enum.zig");
             _ = @import("behavior/switch_prong_implicit_cast.zig");
             _ = @import("behavior/union_with_members.zig");
+            _ = @import("behavior/field_parent_ptr.zig");
 
             if (builtin.zig_backend == .stage1) {
                 // Tests that only pass for the stage1 backend.
@@ -160,7 +161,6 @@ test {
                 _ = @import("behavior/bugs/10147.zig");
                 _ = @import("behavior/const_slice_child.zig");
                 _ = @import("behavior/export_self_referential_type_info.zig");
-                _ = @import("behavior/field_parent_ptr.zig");
                 _ = @import("behavior/misc.zig");
                 _ = @import("behavior/muladd.zig");
                 _ = @import("behavior/select.zig");