authorgravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2025-08-28 07:46:12-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-08-28 15:46:12+01:00
log73a0b5441be3dc6adb666ca672f3c4f7972cf73e
treeaa88a8e771daaf3ac83aa8909a5d29f444c80299
parenta31950aa578824e0933b49109f6ac55c84979b6d
signaturebadge-check Signed by PGP key B5690EEEBB952194

AstGen: forward result type through unary float builtins

Uses a new `float_op_result_ty` ZIR instruction tag.

7 files changed, 109 insertions(+), 26 deletions(-)

lib/std/zig/AstGen.zig+34-13
...@@ -9390,24 +9390,25 @@ fn builtinCall(...@@ -9390,24 +9390,25 @@ fn builtinCall(
9390 .embed_file => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .embed_file),9390 .embed_file => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .embed_file),
9391 .error_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .anyerror_type } }, params[0], .error_name),9391 .error_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .anyerror_type } }, params[0], .error_name),
9392 .set_runtime_safety => return simpleUnOp(gz, scope, ri, node, coerced_bool_ri, params[0], .set_runtime_safety),9392 .set_runtime_safety => return simpleUnOp(gz, scope, ri, node, coerced_bool_ri, params[0], .set_runtime_safety),
9393 .sqrt => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .sqrt),
9394 .sin => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .sin),
9395 .cos => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .cos),
9396 .tan => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .tan),
9397 .exp => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .exp),
9398 .exp2 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .exp2),
9399 .log => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .log),
9400 .log2 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .log2),
9401 .log10 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .log10),
9402 .abs => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .abs),9393 .abs => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .abs),
9403 .floor => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .floor),
9404 .ceil => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .ceil),
9405 .trunc => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .trunc),
9406 .round => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .round),
9407 .tag_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .tag_name),9394 .tag_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .tag_name),
9408 .type_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .type_name),9395 .type_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .type_name),
9409 .Frame => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .frame_type),9396 .Frame => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .frame_type),
94109397
9398 .sqrt => return floatUnOp(gz, scope, ri, node, params[0], .sqrt),
9399 .sin => return floatUnOp(gz, scope, ri, node, params[0], .sin),
9400 .cos => return floatUnOp(gz, scope, ri, node, params[0], .cos),
9401 .tan => return floatUnOp(gz, scope, ri, node, params[0], .tan),
9402 .exp => return floatUnOp(gz, scope, ri, node, params[0], .exp),
9403 .exp2 => return floatUnOp(gz, scope, ri, node, params[0], .exp2),
9404 .log => return floatUnOp(gz, scope, ri, node, params[0], .log),
9405 .log2 => return floatUnOp(gz, scope, ri, node, params[0], .log2),
9406 .log10 => return floatUnOp(gz, scope, ri, node, params[0], .log10),
9407 .floor => return floatUnOp(gz, scope, ri, node, params[0], .floor),
9408 .ceil => return floatUnOp(gz, scope, ri, node, params[0], .ceil),
9409 .trunc => return floatUnOp(gz, scope, ri, node, params[0], .trunc),
9410 .round => return floatUnOp(gz, scope, ri, node, params[0], .round),
9411
9411 .int_from_float => return typeCast(gz, scope, ri, node, params[0], .int_from_float, builtin_name),9412 .int_from_float => return typeCast(gz, scope, ri, node, params[0], .int_from_float, builtin_name),
9412 .float_from_int => return typeCast(gz, scope, ri, node, params[0], .float_from_int, builtin_name),9413 .float_from_int => return typeCast(gz, scope, ri, node, params[0], .float_from_int, builtin_name),
9413 .ptr_from_int => return typeCast(gz, scope, ri, node, params[0], .ptr_from_int, builtin_name),9414 .ptr_from_int => return typeCast(gz, scope, ri, node, params[0], .ptr_from_int, builtin_name),
...@@ -9860,6 +9861,26 @@ fn simpleUnOp(...@@ -9860,6 +9861,26 @@ fn simpleUnOp(
9860 return rvalue(gz, ri, result, node);9861 return rvalue(gz, ri, result, node);
9861}9862}
98629863
9864fn floatUnOp(
9865 gz: *GenZir,
9866 scope: *Scope,
9867 ri: ResultInfo,
9868 node: Ast.Node.Index,
9869 operand_node: Ast.Node.Index,
9870 tag: Zir.Inst.Tag,
9871) InnerError!Zir.Inst.Ref {
9872 const result_type = try ri.rl.resultType(gz, node);
9873 const operand_ri: ResultInfo.Loc = if (result_type) |rt| .{
9874 .ty = try gz.addExtendedPayload(.float_op_result_ty, Zir.Inst.UnNode{
9875 .node = gz.nodeIndexToRelative(node),
9876 .operand = rt,
9877 }),
9878 } else .none;
9879 const operand = try expr(gz, scope, .{ .rl = operand_ri }, operand_node);
9880 const result = try gz.addUnNode(tag, operand, node);
9881 return rvalue(gz, ri, result, node);
9882}
9883
9863fn negation(9884fn negation(
9864 gz: *GenZir,9885 gz: *GenZir,
9865 scope: *Scope,9886 scope: *Scope,
lib/std/zig/AstRlAnnotate.zig+13-13
...@@ -889,6 +889,19 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast....@@ -889,6 +889,19 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
889 .frame_address => return true,889 .frame_address => return true,
890 // These builtins take a single argument with a known result type, but do not consume their890 // These builtins take a single argument with a known result type, but do not consume their
891 // result pointer.891 // result pointer.
892 .sqrt,
893 .sin,
894 .cos,
895 .tan,
896 .exp,
897 .exp2,
898 .log,
899 .log2,
900 .log10,
901 .floor,
902 .ceil,
903 .trunc,
904 .round,
892 .size_of,905 .size_of,
893 .bit_size_of,906 .bit_size_of,
894 .align_of,907 .align_of,
...@@ -918,20 +931,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast....@@ -918,20 +931,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
918 // result pointer.931 // result pointer.
919 .int_from_ptr,932 .int_from_ptr,
920 .int_from_enum,933 .int_from_enum,
921 .sqrt,
922 .sin,
923 .cos,
924 .tan,
925 .exp,
926 .exp2,
927 .log,
928 .log2,
929 .log10,
930 .abs,934 .abs,
931 .floor,
932 .ceil,
933 .trunc,
934 .round,
935 .tag_name,935 .tag_name,
936 .type_name,936 .type_name,
937 .Frame,937 .Frame,
lib/std/zig/Zir.zig+6
...@@ -2111,6 +2111,11 @@ pub const Inst = struct {...@@ -2111,6 +2111,11 @@ pub const Inst = struct {
2111 /// This instruction is always `noreturn`, however, it is not considered as such by ZIR-level queries. This allows AstGen to assume that2111 /// This instruction is always `noreturn`, however, it is not considered as such by ZIR-level queries. This allows AstGen to assume that
2112 /// any code may have gone here, avoiding false-positive "unreachable code" errors.2112 /// any code may have gone here, avoiding false-positive "unreachable code" errors.
2113 astgen_error,2113 astgen_error,
2114 /// Given a type, strips away any error unions or optionals stacked
2115 /// on top and returns the base type. That base type must be a float.
2116 /// For example: Provided with error{Foo}!?f64, returns f64.
2117 /// `operand` is `operand: Air.Inst.Ref`.
2118 float_op_result_ty,
21142119
2115 pub const InstData = struct {2120 pub const InstData = struct {
2116 opcode: Extended,2121 opcode: Extended,
...@@ -4436,6 +4441,7 @@ fn findTrackableInner(...@@ -4436,6 +4441,7 @@ fn findTrackableInner(
4436 .tuple_decl,4441 .tuple_decl,
4437 .dbg_empty_stmt,4442 .dbg_empty_stmt,
4438 .astgen_error,4443 .astgen_error,
4444 .float_op_result_ty,
4439 => return,4445 => return,
44404446
4441 // `@TypeOf` has a body.4447 // `@TypeOf` has a body.
src/Air.zig+4
...@@ -1154,6 +1154,10 @@ pub const Inst = struct {...@@ -1154,6 +1154,10 @@ pub const Inst = struct {
1154 pub fn fromValue(v: Value) Ref {1154 pub fn fromValue(v: Value) Ref {
1155 return .fromIntern(v.toIntern());1155 return .fromIntern(v.toIntern());
1156 }1156 }
1157
1158 pub fn fromType(t: Type) Ref {
1159 return .fromIntern(t.toIntern());
1160 }
1157 };1161 };
11581162
1159 /// All instructions have an 8-byte payload, which is contained within1163 /// All instructions have an 8-byte payload, which is contained within
src/Sema.zig+23
...@@ -1468,6 +1468,7 @@ fn analyzeBodyInner(...@@ -1468,6 +1468,7 @@ fn analyzeBodyInner(
1468 continue;1468 continue;
1469 },1469 },
1470 .astgen_error => return error.AnalysisFail,1470 .astgen_error => return error.AnalysisFail,
1471 .float_op_result_ty => try sema.zirFloatOpResultType(block, extended),
1471 };1472 };
1472 },1473 },
14731474
...@@ -25922,6 +25923,28 @@ fn zirBranchHint(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat...@@ -25922,6 +25923,28 @@ fn zirBranchHint(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat
25922 }25923 }
25923}25924}
2592425925
25926fn zirFloatOpResultType(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
25927 const pt = sema.pt;
25928 const zcu = pt.zcu;
25929 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
25930 const operand_src = block.builtinCallArgSrc(extra.node, 0);
25931
25932 const raw_ty = try sema.resolveTypeOrPoison(block, operand_src, extra.operand) orelse return .generic_poison_type;
25933 const float_ty = raw_ty.optEuBaseType(zcu);
25934
25935 switch (float_ty.scalarType(zcu).zigTypeTag(zcu)) {
25936 .float, .comptime_float => {},
25937 else => return sema.fail(
25938 block,
25939 operand_src,
25940 "expected vector of floats or float type, found '{f}'",
25941 .{float_ty.fmt(sema.pt)},
25942 ),
25943 }
25944
25945 return .fromType(float_ty);
25946}
25947
25925fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void {25948fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void {
25926 if (block.isComptime()) {25949 if (block.isComptime()) {
25927 const msg, const fail_block = msg: {25950 const msg, const fail_block = msg: {
src/print_zir.zig+1
...@@ -567,6 +567,7 @@ const Writer = struct {...@@ -567,6 +567,7 @@ const Writer = struct {
567 .work_group_size,567 .work_group_size,
568 .work_group_id,568 .work_group_id,
569 .branch_hint,569 .branch_hint,
570 .float_op_result_ty,
570 => {571 => {
571 const inst_data = self.code.extraData(Zir.Inst.UnNode, extended.operand).data;572 const inst_data = self.code.extraData(Zir.Inst.UnNode, extended.operand).data;
572 try self.writeInstRef(stream, inst_data.operand);573 try self.writeInstRef(stream, inst_data.operand);
test/behavior/floatop.zig+28
...@@ -1737,3 +1737,31 @@ test "comptime calls are only memoized when float arguments are bit-for-bit equa...@@ -1737,3 +1737,31 @@ test "comptime calls are only memoized when float arguments are bit-for-bit equa
1737 try comptime testMemoization();1737 try comptime testMemoization();
1738 try comptime testVectorMemoization(@Vector(4, f32));1738 try comptime testVectorMemoization(@Vector(4, f32));
1739}1739}
1740
1741test "result location forwarded through unary float builtins" {
1742 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1743 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1744 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1745 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
1746 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1747 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1748
1749 const S = struct {
1750 var x: u32 = 10;
1751 };
1752
1753 var y: f64 = 0.0;
1754 y = @sqrt(@floatFromInt(S.x));
1755 y = @sin(@floatFromInt(S.x));
1756 y = @cos(@floatFromInt(S.x));
1757 y = @tan(@floatFromInt(S.x));
1758 y = @exp(@floatFromInt(S.x));
1759 y = @exp2(@floatFromInt(S.x));
1760 y = @log(@floatFromInt(S.x));
1761 y = @log2(@floatFromInt(S.x));
1762 y = @log10(@floatFromInt(S.x));
1763 y = @floor(@floatFromInt(S.x));
1764 y = @ceil(@floatFromInt(S.x));
1765 y = @trunc(@floatFromInt(S.x));
1766 y = @round(@floatFromInt(S.x));
1767}