diff --git a/src/Air.zig b/src/Air.zig index b9a565ad2e..dc2cd1a9c3 100644 --- a/src/Air.zig +++ b/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, diff --git a/src/AstGen.zig b/src/AstGen.zig index 8e2362ade1..78f39200f5 100644 --- a/src/AstGen.zig +++ b/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); }, diff --git a/src/Liveness.zig b/src/Liveness.zig index e2671702c4..f4ee20c49a 100644 --- a/src/Liveness.zig +++ b/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 }); diff --git a/src/Sema.zig b/src/Sema.zig index bb3a516679..71014e848e 100644 --- a/src/Sema.zig +++ b/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( diff --git a/src/Zir.zig b/src/Zir.zig index 750caad4a0..49541283d3 100644 --- a/src/Zir.zig +++ b/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, diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 39d7f6fd8a..2b863fd359 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/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; diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 332a1876d4..8ad9e980cf 100644 --- a/src/arch/arm/CodeGen.zig +++ b/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 diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 10764ad6cd..33c5b86351 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/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); diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 1fb7fc0e2f..888e78f8c2 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/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. diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 4b480c43cb..f7713a4e69 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/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 diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 48ca64d176..2a0751a202 100644 --- a/src/codegen/c.zig +++ b/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(); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 1679ee6d97..51bb713ed5 100644 --- a/src/codegen/llvm.zig +++ b/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; diff --git a/src/print_air.zig b/src/print_air.zig index 289fcca669..06ec464f91 100644 --- a/src/print_air.zig +++ b/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; diff --git a/src/print_zir.zig b/src/print_zir.zig index 7aa315b114..66c3ad7668 100644 --- a/src/print_zir.zig +++ b/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, diff --git a/src/value.zig b/src/value.zig index 538c20587b..0b3ad15469 100644 --- a/src/value.zig +++ b/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 => { diff --git a/test/behavior.zig b/test/behavior.zig index 9a434aa6ee..8603c370fa 100644 --- a/test/behavior.zig +++ b/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");