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(
93909390 .embed_file => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .embed_file),
93919391 .error_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .anyerror_type } }, params[0], .error_name),
93929392 .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),
94029393 .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),
94079394 .tag_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .tag_name),
94089395 .type_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .type_name),
94099396 .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
94119412 .int_from_float => return typeCast(gz, scope, ri, node, params[0], .int_from_float, builtin_name),
94129413 .float_from_int => return typeCast(gz, scope, ri, node, params[0], .float_from_int, builtin_name),
94139414 .ptr_from_int => return typeCast(gz, scope, ri, node, params[0], .ptr_from_int, builtin_name),
......@@ -9860,6 +9861,26 @@ fn simpleUnOp(
98609861 return rvalue(gz, ri, result, node);
98619862}
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
98639884fn negation(
98649885 gz: *GenZir,
98659886 scope: *Scope,
lib/std/zig/AstRlAnnotate.zig+13-13
......@@ -889,6 +889,19 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
889889 .frame_address => return true,
890890 // These builtins take a single argument with a known result type, but do not consume their
891891 // 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,
892905 .size_of,
893906 .bit_size_of,
894907 .align_of,
......@@ -918,20 +931,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
918931 // result pointer.
919932 .int_from_ptr,
920933 .int_from_enum,
921 .sqrt,
922 .sin,
923 .cos,
924 .tan,
925 .exp,
926 .exp2,
927 .log,
928 .log2,
929 .log10,
930934 .abs,
931 .floor,
932 .ceil,
933 .trunc,
934 .round,
935935 .tag_name,
936936 .type_name,
937937 .Frame,
lib/std/zig/Zir.zig+6
......@@ -2111,6 +2111,11 @@ pub const Inst = struct {
21112111 /// This instruction is always `noreturn`, however, it is not considered as such by ZIR-level queries. This allows AstGen to assume that
21122112 /// any code may have gone here, avoiding false-positive "unreachable code" errors.
21132113 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
21152120 pub const InstData = struct {
21162121 opcode: Extended,
......@@ -4436,6 +4441,7 @@ fn findTrackableInner(
44364441 .tuple_decl,
44374442 .dbg_empty_stmt,
44384443 .astgen_error,
4444 .float_op_result_ty,
44394445 => return,
44404446
44414447 // `@TypeOf` has a body.
src/Air.zig+4
......@@ -1154,6 +1154,10 @@ pub const Inst = struct {
11541154 pub fn fromValue(v: Value) Ref {
11551155 return .fromIntern(v.toIntern());
11561156 }
1157
1158 pub fn fromType(t: Type) Ref {
1159 return .fromIntern(t.toIntern());
1160 }
11571161 };
11581162
11591163 /// All instructions have an 8-byte payload, which is contained within
src/Sema.zig+23
......@@ -1468,6 +1468,7 @@ fn analyzeBodyInner(
14681468 continue;
14691469 },
14701470 .astgen_error => return error.AnalysisFail,
1471 .float_op_result_ty => try sema.zirFloatOpResultType(block, extended),
14711472 };
14721473 },
14731474
......@@ -25922,6 +25923,28 @@ fn zirBranchHint(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat
2592225923 }
2592325924}
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
2592525948fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void {
2592625949 if (block.isComptime()) {
2592725950 const msg, const fail_block = msg: {
src/print_zir.zig+1
......@@ -567,6 +567,7 @@ const Writer = struct {
567567 .work_group_size,
568568 .work_group_id,
569569 .branch_hint,
570 .float_op_result_ty,
570571 => {
571572 const inst_data = self.code.extraData(Zir.Inst.UnNode, extended.operand).data;
572573 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
17371737 try comptime testMemoization();
17381738 try comptime testVectorMemoization(@Vector(4, f32));
17391739}
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}