authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-09-04 18:48:55-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-09-20 18:33:00-07:00
log354c17869a4fb1053d472d6562f4987fbaca143e
tree93c7d156a0a3b23842e19b6f7fec7bf3a129d3f8
parent3bd62e1bb62f4f490aeaa68c3cbf9c6c09c40726

frontend: replace field_val and field_val_named

with field_ptr_load and field_ptr_named_load. These avoid doing by-val load operations for structs that are runtime-known while keeping the previous semantics for comptime-known values.

4 files changed, 77 insertions(+), 40 deletions(-)

lib/std/zig/AstGen.zig+17-13
...@@ -2731,9 +2731,9 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As...@@ -2731,9 +2731,9 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2731 .elem_val_node,2731 .elem_val_node,
2732 .elem_val_imm,2732 .elem_val_imm,
2733 .field_ptr,2733 .field_ptr,
2734 .field_val,2734 .field_ptr_load,
2735 .field_ptr_named,2735 .field_ptr_named,
2736 .field_val_named,2736 .field_ptr_named_load,
2737 .func,2737 .func,
2738 .func_inferred,2738 .func_inferred,
2739 .func_fancy,2739 .func_fancy,
...@@ -6160,7 +6160,7 @@ fn fieldAccess(...@@ -6160,7 +6160,7 @@ fn fieldAccess(
6160 switch (ri.rl) {6160 switch (ri.rl) {
6161 .ref, .ref_coerced_ty => return addFieldAccess(.field_ptr, gz, scope, .{ .rl = .ref }, node),6161 .ref, .ref_coerced_ty => return addFieldAccess(.field_ptr, gz, scope, .{ .rl = .ref }, node),
6162 else => {6162 else => {
6163 const access = try addFieldAccess(.field_val, gz, scope, .{ .rl = .none }, node);6163 const access = try addFieldAccess(.field_ptr_load, gz, scope, .{ .rl = .ref }, node);
6164 return rvalue(gz, ri, access, node);6164 return rvalue(gz, ri, access, node);
6165 },6165 },
6166 }6166 }
...@@ -9286,17 +9286,21 @@ fn builtinCall(...@@ -9286,17 +9286,21 @@ fn builtinCall(
9286 return rvalue(gz, ri, result, node);9286 return rvalue(gz, ri, result, node);
9287 },9287 },
9288 .field => {9288 .field => {
9289 if (ri.rl == .ref or ri.rl == .ref_coerced_ty) {9289 switch (ri.rl) {
9290 return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{9290 .ref, .ref_coerced_ty => {
9291 .lhs = try expr(gz, scope, .{ .rl = .ref }, params[0]),9291 return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{
9292 .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1], .field_name),9292 .lhs = try expr(gz, scope, .{ .rl = .ref }, params[0]),
9293 });9293 .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1], .field_name),
9294 });
9295 },
9296 else => {
9297 const result = try gz.addPlNode(.field_ptr_named_load, node, Zir.Inst.FieldNamed{
9298 .lhs = try expr(gz, scope, .{ .rl = .ref }, params[0]),
9299 .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1], .field_name),
9300 });
9301 return rvalue(gz, ri, result, node);
9302 },
9294 }9303 }
9295 const result = try gz.addPlNode(.field_val_named, node, Zir.Inst.FieldNamed{
9296 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
9297 .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1], .field_name),
9298 });
9299 return rvalue(gz, ri, result, node);
9300 },9304 },
9301 .FieldType => {9305 .FieldType => {
9302 const ty_inst = try typeExpr(gz, scope, params[0]);9306 const ty_inst = try typeExpr(gz, scope, params[0]);
lib/std/zig/Zir.zig+20-12
...@@ -420,6 +420,7 @@ pub const Inst = struct {...@@ -420,6 +420,7 @@ pub const Inst = struct {
420 /// is the local's value.420 /// is the local's value.
421 dbg_var_val,421 dbg_var_val,
422 /// Uses a name to identify a Decl and takes a pointer to it.422 /// Uses a name to identify a Decl and takes a pointer to it.
423 ///
423 /// Uses the `str_tok` union field.424 /// Uses the `str_tok` union field.
424 decl_ref,425 decl_ref,
425 /// Uses a name to identify a Decl and uses it as a value.426 /// Uses a name to identify a Decl and uses it as a value.
...@@ -472,19 +473,26 @@ pub const Inst = struct {...@@ -472,19 +473,26 @@ pub const Inst = struct {
472 /// to the named field. The field name is stored in string_bytes. Used by a.b syntax.473 /// to the named field. The field name is stored in string_bytes. Used by a.b syntax.
473 /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.474 /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.
474 field_ptr,475 field_ptr,
475 /// Given a struct or object that contains virtual fields, returns the named field.476 /// Given a pointer to a struct or object that contains virtual fields, loads from the
477 /// named field.
478 ///
476 /// The field name is stored in string_bytes. Used by a.b syntax.479 /// The field name is stored in string_bytes. Used by a.b syntax.
480 ///
477 /// This instruction also accepts a pointer.481 /// This instruction also accepts a pointer.
482 ///
478 /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.483 /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.
479 field_val,484 field_ptr_load,
480 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer485 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer
481 /// to the named field. The field name is a comptime instruction. Used by @field.486 /// to the named field. The field name is a comptime instruction. Used by @field.
482 /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.487 /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.
483 field_ptr_named,488 field_ptr_named,
484 /// Given a struct or object that contains virtual fields, returns the named field.489 /// Given a pointer to a struct or object that contains virtual fields,
490 /// loads from the named field.
491 ///
485 /// The field name is a comptime instruction. Used by @field.492 /// The field name is a comptime instruction. Used by @field.
493 ///
486 /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.494 /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.
487 field_val_named,495 field_ptr_named_load,
488 /// Returns a function type, or a function instance, depending on whether496 /// Returns a function type, or a function instance, depending on whether
489 /// the body_len is 0. Calling convention is auto.497 /// the body_len is 0. Calling convention is auto.
490 /// Uses the `pl_node` union field. `payload_index` points to a `Func`.498 /// Uses the `pl_node` union field. `payload_index` points to a `Func`.
...@@ -1145,9 +1153,9 @@ pub const Inst = struct {...@@ -1145,9 +1153,9 @@ pub const Inst = struct {
1145 .ensure_err_union_payload_void,1153 .ensure_err_union_payload_void,
1146 .@"export",1154 .@"export",
1147 .field_ptr,1155 .field_ptr,
1148 .field_val,1156 .field_ptr_load,
1149 .field_ptr_named,1157 .field_ptr_named,
1150 .field_val_named,1158 .field_ptr_named_load,
1151 .func,1159 .func,
1152 .func_inferred,1160 .func_inferred,
1153 .func_fancy,1161 .func_fancy,
...@@ -1435,9 +1443,9 @@ pub const Inst = struct {...@@ -1435,9 +1443,9 @@ pub const Inst = struct {
1435 .elem_val_node,1443 .elem_val_node,
1436 .elem_val_imm,1444 .elem_val_imm,
1437 .field_ptr,1445 .field_ptr,
1438 .field_val,1446 .field_ptr_load,
1439 .field_ptr_named,1447 .field_ptr_named,
1440 .field_val_named,1448 .field_ptr_named_load,
1441 .func,1449 .func,
1442 .func_inferred,1450 .func_inferred,
1443 .func_fancy,1451 .func_fancy,
...@@ -1688,9 +1696,9 @@ pub const Inst = struct {...@@ -1688,9 +1696,9 @@ pub const Inst = struct {
1688 .error_value = .str_tok,1696 .error_value = .str_tok,
1689 .@"export" = .pl_node,1697 .@"export" = .pl_node,
1690 .field_ptr = .pl_node,1698 .field_ptr = .pl_node,
1691 .field_val = .pl_node,1699 .field_ptr_load = .pl_node,
1692 .field_ptr_named = .pl_node,1700 .field_ptr_named = .pl_node,
1693 .field_val_named = .pl_node,1701 .field_ptr_named_load = .pl_node,
1694 .func = .pl_node,1702 .func = .pl_node,
1695 .func_inferred = .pl_node,1703 .func_inferred = .pl_node,
1696 .func_fancy = .pl_node,1704 .func_fancy = .pl_node,
...@@ -4225,9 +4233,9 @@ fn findTrackableInner(...@@ -4225,9 +4233,9 @@ fn findTrackableInner(
4225 .error_value,4233 .error_value,
4226 .@"export",4234 .@"export",
4227 .field_ptr,4235 .field_ptr,
4228 .field_val,4236 .field_ptr_load,
4229 .field_ptr_named,4237 .field_ptr_named,
4230 .field_val_named,4238 .field_ptr_named_load,
4231 .import,4239 .import,
4232 .int,4240 .int,
4233 .int_big,4241 .int_big,
src/Sema.zig+38-13
...@@ -1187,7 +1187,7 @@ fn analyzeBodyInner(...@@ -1187,7 +1187,7 @@ fn analyzeBodyInner(
1187 .cmp_gte => try sema.zirCmp(block, inst, .gte),1187 .cmp_gte => try sema.zirCmp(block, inst, .gte),
1188 .cmp_gt => try sema.zirCmp(block, inst, .gt),1188 .cmp_gt => try sema.zirCmp(block, inst, .gt),
1189 .cmp_neq => try sema.zirCmpEq(block, inst, .neq, Air.Inst.Tag.fromCmpOp(.neq, block.float_mode == .optimized)),1189 .cmp_neq => try sema.zirCmpEq(block, inst, .neq, Air.Inst.Tag.fromCmpOp(.neq, block.float_mode == .optimized)),
1190 .decl_ref => try sema.zirDeclRef(block, inst),1190 .decl_ref => try sema.zirDeclRef(block, inst, true),
1191 .decl_val => try sema.zirDeclVal(block, inst),1191 .decl_val => try sema.zirDeclVal(block, inst),
1192 .load => try sema.zirLoad(block, inst),1192 .load => try sema.zirLoad(block, inst),
1193 .elem_ptr => try sema.zirElemPtr(block, inst),1193 .elem_ptr => try sema.zirElemPtr(block, inst),
...@@ -1211,8 +1211,8 @@ fn analyzeBodyInner(...@@ -1211,8 +1211,8 @@ fn analyzeBodyInner(
1211 .error_value => try sema.zirErrorValue(block, inst),1211 .error_value => try sema.zirErrorValue(block, inst),
1212 .field_ptr => try sema.zirFieldPtr(block, inst),1212 .field_ptr => try sema.zirFieldPtr(block, inst),
1213 .field_ptr_named => try sema.zirFieldPtrNamed(block, inst),1213 .field_ptr_named => try sema.zirFieldPtrNamed(block, inst),
1214 .field_val => try sema.zirFieldVal(block, inst),1214 .field_ptr_load => try sema.zirFieldPtrLoad(block, inst),
1215 .field_val_named => try sema.zirFieldValNamed(block, inst),1215 .field_ptr_named_load => try sema.zirFieldPtrNamedLoad(block, inst),
1216 .func => try sema.zirFunc(block, inst, false),1216 .func => try sema.zirFunc(block, inst, false),
1217 .func_inferred => try sema.zirFunc(block, inst, true),1217 .func_inferred => try sema.zirFunc(block, inst, true),
1218 .func_fancy => try sema.zirFuncFancy(block, inst),1218 .func_fancy => try sema.zirFuncFancy(block, inst),
...@@ -6524,7 +6524,7 @@ pub fn appendAirString(sema: *Sema, str: []const u8) Allocator.Error!Air.NullTer...@@ -6524,7 +6524,7 @@ pub fn appendAirString(sema: *Sema, str: []const u8) Allocator.Error!Air.NullTer
6524 return nts;6524 return nts;
6525}6525}
65266526
6527fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {6527fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index, escape: bool) CompileError!Air.Inst.Ref {
6528 const pt = sema.pt;6528 const pt = sema.pt;
6529 const zcu = pt.zcu;6529 const zcu = pt.zcu;
6530 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;6530 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
...@@ -6536,7 +6536,7 @@ fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -6536,7 +6536,7 @@ fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
6536 .no_embedded_nulls,6536 .no_embedded_nulls,
6537 );6537 );
6538 const nav_index = try sema.lookupIdentifier(block, decl_name);6538 const nav_index = try sema.lookupIdentifier(block, decl_name);
6539 return sema.analyzeNavRef(block, src, nav_index);6539 return sema.analyzeNavRefInner(block, src, nav_index, escape);
6540}6540}
65416541
6542fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {6542fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -9711,7 +9711,7 @@ fn zirIntFromPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!...@@ -9711,7 +9711,7 @@ fn zirIntFromPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
9711 return block.addBitCast(dest_ty, operand);9711 return block.addBitCast(dest_ty, operand);
9712}9712}
97139713
9714fn zirFieldVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {9714fn zirFieldPtrLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9715 const tracy = trace(@src());9715 const tracy = trace(@src());
9716 defer tracy.end();9716 defer tracy.end();
97179717
...@@ -9727,8 +9727,8 @@ fn zirFieldVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -9727,8 +9727,8 @@ fn zirFieldVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
9727 sema.code.nullTerminatedString(extra.field_name_start),9727 sema.code.nullTerminatedString(extra.field_name_start),
9728 .no_embedded_nulls,9728 .no_embedded_nulls,
9729 );9729 );
9730 const object = try sema.resolveInst(extra.lhs);9730 const object_ptr = try sema.resolveInst(extra.lhs);
9731 return sema.fieldVal(block, src, object, field_name, field_name_src);9731 return fieldPtrLoad(sema, block, src, object_ptr, field_name, field_name_src);
9732}9732}
97339733
9734fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {9734fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -9779,7 +9779,7 @@ fn zirStructInitFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi...@@ -9779,7 +9779,7 @@ fn zirStructInitFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi
9779 }9779 }
9780}9780}
97819781
9782fn zirFieldValNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {9782fn zirFieldPtrNamedLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9783 const tracy = trace(@src());9783 const tracy = trace(@src());
9784 defer tracy.end();9784 defer tracy.end();
97859785
...@@ -9787,9 +9787,9 @@ fn zirFieldValNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr...@@ -9787,9 +9787,9 @@ fn zirFieldValNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
9787 const src = block.nodeOffset(inst_data.src_node);9787 const src = block.nodeOffset(inst_data.src_node);
9788 const field_name_src = block.builtinCallArgSrc(inst_data.src_node, 1);9788 const field_name_src = block.builtinCallArgSrc(inst_data.src_node, 1);
9789 const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;9789 const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;
9790 const object = try sema.resolveInst(extra.lhs);9790 const object_ptr = try sema.resolveInst(extra.lhs);
9791 const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{ .simple = .field_name });9791 const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{ .simple = .field_name });
9792 return sema.fieldVal(block, src, object, field_name, field_name_src);9792 return fieldPtrLoad(sema, block, src, object_ptr, field_name, field_name_src);
9793}9793}
97949794
9795fn zirFieldPtrNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {9795fn zirFieldPtrNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -13612,7 +13612,6 @@ fn maybeErrorUnwrap(...@@ -13612,7 +13612,6 @@ fn maybeErrorUnwrap(
13612 .str,13612 .str,
13613 .as_node,13613 .as_node,
13614 .panic,13614 .panic,
13615 .field_val,
13616 => {},13615 => {},
13617 else => return false,13616 else => return false,
13618 }13617 }
...@@ -13631,7 +13630,6 @@ fn maybeErrorUnwrap(...@@ -13631,7 +13630,6 @@ fn maybeErrorUnwrap(
13631 },13630 },
13632 .str => try sema.zirStr(inst),13631 .str => try sema.zirStr(inst),
13633 .as_node => try sema.zirAsNode(block, inst),13632 .as_node => try sema.zirAsNode(block, inst),
13634 .field_val => try sema.zirFieldVal(block, inst),
13635 .@"unreachable" => {13633 .@"unreachable" => {
13636 try safetyPanicUnwrapError(sema, block, operand_src, operand);13634 try safetyPanicUnwrapError(sema, block, operand_src, operand);
13637 return true;13635 return true;
...@@ -26673,6 +26671,33 @@ fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void {...@@ -26673,6 +26671,33 @@ fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void {
26673 }26671 }
26674}26672}
2667526673
26674fn fieldPtrLoad(
26675 sema: *Sema,
26676 block: *Block,
26677 src: LazySrcLoc,
26678 object_ptr: Air.Inst.Ref,
26679 field_name: InternPool.NullTerminatedString,
26680 field_name_src: LazySrcLoc,
26681) CompileError!Air.Inst.Ref {
26682 const pt = sema.pt;
26683 const zcu = pt.zcu;
26684 const object_ptr_ty = sema.typeOf(object_ptr);
26685 const pointee_ty = object_ptr_ty.childType(zcu);
26686 if (try typeHasOnePossibleValue(sema, pointee_ty)) |opv| {
26687 const object: Air.Inst.Ref = .fromValue(opv);
26688 return fieldVal(sema, block, src, object, field_name, field_name_src);
26689 }
26690
26691 if (try sema.resolveDefinedValue(block, src, object_ptr)) |object_ptr_val| {
26692 if (try sema.pointerDeref(block, src, object_ptr_val, object_ptr_ty)) |object_val| {
26693 const object: Air.Inst.Ref = .fromValue(object_val);
26694 return fieldVal(sema, block, src, object, field_name, field_name_src);
26695 }
26696 }
26697 const field_ptr = try sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false);
26698 return analyzeLoad(sema, block, src, field_ptr, field_name_src);
26699}
26700
26676fn fieldVal(26701fn fieldVal(
26677 sema: *Sema,26702 sema: *Sema,
26678 block: *Block,26703 block: *Block,
src/print_zir.zig+2-2
...@@ -450,14 +450,14 @@ const Writer = struct {...@@ -450,14 +450,14 @@ const Writer = struct {
450450
451 .switch_block_err_union => try self.writeSwitchBlockErrUnion(stream, inst),451 .switch_block_err_union => try self.writeSwitchBlockErrUnion(stream, inst),
452452
453 .field_val,453 .field_ptr_load,
454 .field_ptr,454 .field_ptr,
455 .decl_literal,455 .decl_literal,
456 .decl_literal_no_coerce,456 .decl_literal_no_coerce,
457 => try self.writePlNodeField(stream, inst),457 => try self.writePlNodeField(stream, inst),
458458
459 .field_ptr_named,459 .field_ptr_named,
460 .field_val_named,460 .field_ptr_named_load,
461 => try self.writePlNodeFieldNamed(stream, inst),461 => try self.writePlNodeFieldNamed(stream, inst),
462462
463 .as_node, .as_shift_operand => try self.writeAs(stream, inst),463 .as_node, .as_shift_operand => try self.writeAs(stream, inst),