authorgravatar for john.schmidt.h@gmail.comJohn Schmidt <john.schmidt.h@gmail.com> 2022-03-03 21:54:01+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-06 15:36:56-07:00
log6637335981f7179b449fced78cfd4052b1618051
treebb5bd8eaaf3b1534f1087630d5c30dbee42600ed
parentc7e4c711fc5795e66f974316611922a0b962eb99

stage2: implement `@mulAdd` for scalar floats


13 files changed, 242 insertions(+), 26 deletions(-)

src/Air.zig+11
...@@ -579,6 +579,10 @@ pub const Inst = struct {...@@ -579,6 +579,10 @@ pub const Inst = struct {
579 /// Uses the `prefetch` field.579 /// Uses the `prefetch` field.
580 prefetch,580 prefetch,
581581
582 /// Computes `(a * b) + c`, but only rounds once.
583 /// Uses the `ty_pl` field.
584 mul_add,
585
582 /// Implements @fieldParentPtr builtin.586 /// Implements @fieldParentPtr builtin.
583 /// Uses the `ty_pl` field.587 /// Uses the `ty_pl` field.
584 field_parent_ptr,588 field_parent_ptr,
...@@ -724,6 +728,12 @@ pub const Bin = struct {...@@ -724,6 +728,12 @@ pub const Bin = struct {
724 rhs: Inst.Ref,728 rhs: Inst.Ref,
725};729};
726730
731pub const MulAdd = struct {
732 mulend1: Inst.Ref,
733 mulend2: Inst.Ref,
734 addend: Inst.Ref,
735};
736
727pub const FieldParentPtr = struct {737pub const FieldParentPtr = struct {
728 field_ptr: Inst.Ref,738 field_ptr: Inst.Ref,
729 field_index: u32,739 field_index: u32,
...@@ -889,6 +899,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -889,6 +899,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
889 .aggregate_init,899 .aggregate_init,
890 .union_init,900 .union_init,
891 .field_parent_ptr,901 .field_parent_ptr,
902 .mul_add,
892 => return air.getRefType(datas[inst].ty_pl.ty),903 => return air.getRefType(datas[inst].ty_pl.ty),
893904
894 .not,905 .not,
src/Liveness.zig+4
...@@ -464,6 +464,10 @@ fn analyzeInst(...@@ -464,6 +464,10 @@ fn analyzeInst(
464 const extra = a.air.extraData(Air.Cmpxchg, inst_datas[inst].ty_pl.payload).data;464 const extra = a.air.extraData(Air.Cmpxchg, inst_datas[inst].ty_pl.payload).data;
465 return trackOperands(a, new_set, inst, main_tomb, .{ extra.ptr, extra.expected_value, extra.new_value });465 return trackOperands(a, new_set, inst, main_tomb, .{ extra.ptr, extra.expected_value, extra.new_value });
466 },466 },
467 .mul_add => {
468 const extra = a.air.extraData(Air.MulAdd, inst_datas[inst].ty_pl.payload).data;
469 return trackOperands(a, new_set, inst, main_tomb, .{ extra.mulend1, extra.mulend2, extra.addend });
470 },
467 .atomic_load => {471 .atomic_load => {
468 const ptr = inst_datas[inst].atomic_load.ptr;472 const ptr = inst_datas[inst].atomic_load.ptr;
469 return trackOperands(a, new_set, inst, main_tomb, .{ ptr, .none, .none });473 return trackOperands(a, new_set, inst, main_tomb, .{ ptr, .none, .none });
src/Sema.zig+77-1
...@@ -13518,8 +13518,84 @@ fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -13518,8 +13518,84 @@ fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
1351813518
13519fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {13519fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
13520 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;13520 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
13521 const extra = sema.code.extraData(Zir.Inst.MulAdd, inst_data.payload_index).data;
13521 const src = inst_data.src();13522 const src = inst_data.src();
13522 return sema.fail(block, src, "TODO: Sema.zirMulAdd", .{});13523
13524 const mulend1_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
13525 const mulend2_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
13526 const addend_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node };
13527
13528 const mulend1 = sema.resolveInst(extra.mulend1);
13529 const mulend2 = sema.resolveInst(extra.mulend2);
13530 const addend = sema.resolveInst(extra.addend);
13531 // All args have the same type
13532 const ty = sema.typeOf(mulend1);
13533 switch (ty.zigTypeTag()) {
13534 .ComptimeFloat, .Float => {},
13535 .Vector => {
13536 const scalar_ty = ty.scalarType();
13537 switch (scalar_ty.zigTypeTag()) {
13538 .ComptimeFloat, .Float => {},
13539 else => return sema.fail(block, src, "expected vector of floats or float type, found '{}'", .{scalar_ty}),
13540 }
13541 },
13542 else => return sema.fail(block, src, "expected vector of floats or float type, found '{}'", .{ty}),
13543 }
13544
13545 const target = sema.mod.getTarget();
13546 switch (ty.zigTypeTag()) {
13547 .ComptimeFloat, .Float => {
13548 const maybe_mulend1 = try sema.resolveMaybeUndefVal(block, mulend1_src, mulend1);
13549 const maybe_mulend2 = try sema.resolveMaybeUndefVal(block, mulend2_src, mulend2);
13550 const maybe_addend = try sema.resolveMaybeUndefVal(block, addend_src, addend);
13551
13552 if (maybe_mulend1) |mulend1_val| {
13553 if (mulend1_val.isUndef())
13554 return sema.addConstUndef(ty);
13555 }
13556
13557 if (maybe_mulend2) |mulend2_val| {
13558 if (mulend2_val.isUndef())
13559 return sema.addConstUndef(ty);
13560 }
13561
13562 if (maybe_addend) |addend_val| {
13563 if (addend_val.isUndef())
13564 return sema.addConstUndef(ty);
13565 }
13566
13567 if (maybe_mulend1) |mulend1_val| {
13568 if (maybe_mulend2) |mulend2_val| {
13569 if (maybe_addend) |addend_val| {
13570 const result_val = try Value.mulAdd(
13571 ty,
13572 mulend1_val,
13573 mulend2_val,
13574 addend_val,
13575 sema.arena,
13576 target,
13577 );
13578 return sema.addConstant(ty, result_val);
13579 }
13580 }
13581 }
13582
13583 try sema.requireRuntimeBlock(block, src);
13584 return block.addInst(.{
13585 .tag = .mul_add,
13586 .data = .{ .ty_pl = .{
13587 .ty = try sema.addType(ty),
13588 .payload = try sema.addExtra(Air.MulAdd{
13589 .mulend1 = mulend1,
13590 .mulend2 = mulend2,
13591 .addend = addend,
13592 }),
13593 } },
13594 });
13595 },
13596 .Vector => return sema.fail(block, src, "TODO: implement @mulAdd for vectors", .{}),
13597 else => unreachable,
13598 }
13523}13599}
1352413600
13525fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {13601fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
src/arch/aarch64/CodeGen.zig+6
...@@ -632,6 +632,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -632,6 +632,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
632 .aggregate_init => try self.airAggregateInit(inst),632 .aggregate_init => try self.airAggregateInit(inst),
633 .union_init => try self.airUnionInit(inst),633 .union_init => try self.airUnionInit(inst),
634 .prefetch => try self.airPrefetch(inst),634 .prefetch => try self.airPrefetch(inst),
635 .mul_add => try self.airMulAdd(inst),
635636
636 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),637 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
637 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),638 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
...@@ -3652,6 +3653,11 @@ fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {...@@ -3652,6 +3653,11 @@ fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {
3652 return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });3653 return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });
3653}3654}
36543655
3656fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {
3657 _ = inst;
3658 return self.fail("TODO implement airMulAdd for aarch64", .{});
3659}
3660
3655fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {3661fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
3656 // First section of indexes correspond to a set number of constant values.3662 // First section of indexes correspond to a set number of constant values.
3657 const ref_int = @enumToInt(inst);3663 const ref_int = @enumToInt(inst);
src/arch/arm/CodeGen.zig+6
...@@ -628,6 +628,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -628,6 +628,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
628 .aggregate_init => try self.airAggregateInit(inst),628 .aggregate_init => try self.airAggregateInit(inst),
629 .union_init => try self.airUnionInit(inst),629 .union_init => try self.airUnionInit(inst),
630 .prefetch => try self.airPrefetch(inst),630 .prefetch => try self.airPrefetch(inst),
631 .mul_add => try self.airMulAdd(inst),
631632
632 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),633 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
633 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),634 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
...@@ -4086,6 +4087,11 @@ fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {...@@ -4086,6 +4087,11 @@ fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {
4086 return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });4087 return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });
4087}4088}
40884089
4090fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {
4091 _ = inst;
4092 return self.fail("TODO implement airMulAdd for arm", .{});
4093}
4094
4089fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {4095fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
4090 // First section of indexes correspond to a set number of constant values.4096 // First section of indexes correspond to a set number of constant values.
4091 const ref_int = @enumToInt(inst);4097 const ref_int = @enumToInt(inst);
src/arch/riscv64/CodeGen.zig+6
...@@ -600,6 +600,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -600,6 +600,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
600 .aggregate_init => try self.airAggregateInit(inst),600 .aggregate_init => try self.airAggregateInit(inst),
601 .union_init => try self.airUnionInit(inst),601 .union_init => try self.airUnionInit(inst),
602 .prefetch => try self.airPrefetch(inst),602 .prefetch => try self.airPrefetch(inst),
603 .mul_add => try self.airMulAdd(inst),
603604
604 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),605 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
605 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),606 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
...@@ -2203,6 +2204,11 @@ fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {...@@ -2203,6 +2204,11 @@ fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {
2203 return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });2204 return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });
2204}2205}
22052206
2207fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {
2208 _ = inst;
2209 return self.fail("TODO implement airMulAdd for riscv64", .{});
2210}
2211
2206fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {2212fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
2207 // First section of indexes correspond to a set number of constant values.2213 // First section of indexes correspond to a set number of constant values.
2208 const ref_int = @enumToInt(inst);2214 const ref_int = @enumToInt(inst);
src/arch/wasm/CodeGen.zig+1
...@@ -1333,6 +1333,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {...@@ -1333,6 +1333,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
1333 .error_name,1333 .error_name,
1334 .errunion_payload_ptr_set,1334 .errunion_payload_ptr_set,
1335 .field_parent_ptr,1335 .field_parent_ptr,
1336 .mul_add,
13361337
1337 // For these 4, probably best to wait until https://github.com/ziglang/zig/issues/102481338 // For these 4, probably best to wait until https://github.com/ziglang/zig/issues/10248
1338 // is implemented in the frontend before implementing them here in the wasm backend.1339 // is implemented in the frontend before implementing them here in the wasm backend.
src/arch/x86_64/CodeGen.zig+6
...@@ -717,6 +717,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -717,6 +717,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
717 .aggregate_init => try self.airAggregateInit(inst),717 .aggregate_init => try self.airAggregateInit(inst),
718 .union_init => try self.airUnionInit(inst),718 .union_init => try self.airUnionInit(inst),
719 .prefetch => try self.airPrefetch(inst),719 .prefetch => try self.airPrefetch(inst),
720 .mul_add => try self.airMulAdd(inst),
720721
721 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),722 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
722 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),723 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
...@@ -5559,6 +5560,11 @@ fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {...@@ -5559,6 +5560,11 @@ fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {
5559 return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });5560 return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });
5560}5561}
55615562
5563fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {
5564 _ = inst;
5565 return self.fail("TODO implement airMulAdd for x86_64", .{});
5566}
5567
5562fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {5568fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
5563 // First section of indexes correspond to a set number of constant values.5569 // First section of indexes correspond to a set number of constant values.
5564 const ref_int = @enumToInt(inst);5570 const ref_int = @enumToInt(inst);
src/codegen/c.zig+2
...@@ -1635,6 +1635,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -1635,6 +1635,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
1635 .trunc_float,1635 .trunc_float,
1636 => |tag| return f.fail("TODO: C backend: implement unary op for tag '{s}'", .{@tagName(tag)}),1636 => |tag| return f.fail("TODO: C backend: implement unary op for tag '{s}'", .{@tagName(tag)}),
16371637
1638 .mul_add => return f.fail("TODO: C backend: implement @mulAdd", .{}),
1639
1638 .add_with_overflow => try airAddWithOverflow(f, inst),1640 .add_with_overflow => try airAddWithOverflow(f, inst),
1639 .sub_with_overflow => try airSubWithOverflow(f, inst),1641 .sub_with_overflow => try airSubWithOverflow(f, inst),
1640 .mul_with_overflow => try airMulWithOverflow(f, inst),1642 .mul_with_overflow => try airMulWithOverflow(f, inst),
src/codegen/llvm.zig+41
...@@ -2194,6 +2194,7 @@ pub const FuncGen = struct {...@@ -2194,6 +2194,7 @@ pub const FuncGen = struct {
2194 .sub_with_overflow => try self.airOverflow(inst, "llvm.ssub.with.overflow", "llvm.usub.with.overflow"),2194 .sub_with_overflow => try self.airOverflow(inst, "llvm.ssub.with.overflow", "llvm.usub.with.overflow"),
2195 .mul_with_overflow => try self.airOverflow(inst, "llvm.smul.with.overflow", "llvm.umul.with.overflow"),2195 .mul_with_overflow => try self.airOverflow(inst, "llvm.smul.with.overflow", "llvm.umul.with.overflow"),
2196 .shl_with_overflow => try self.airShlWithOverflow(inst),2196 .shl_with_overflow => try self.airShlWithOverflow(inst),
2197 .mul_add => try self.airMulAdd(inst),
21972198
2198 .bit_and, .bool_and => try self.airAnd(inst),2199 .bit_and, .bool_and => try self.airAnd(inst),
2199 .bit_or, .bool_or => try self.airOr(inst),2200 .bit_or, .bool_or => try self.airOr(inst),
...@@ -3842,6 +3843,46 @@ pub const FuncGen = struct {...@@ -3842,6 +3843,46 @@ pub const FuncGen = struct {
3842 return overflow_bit;3843 return overflow_bit;
3843 }3844 }
38443845
3846 fn airMulAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
3847 if (self.liveness.isUnused(inst))
3848 return null;
3849
3850 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3851 const extra = self.air.extraData(Air.MulAdd, ty_pl.payload).data;
3852
3853 const mulend1 = try self.resolveInst(extra.mulend1);
3854 const mulend2 = try self.resolveInst(extra.mulend2);
3855 const addend = try self.resolveInst(extra.addend);
3856
3857 const ty = self.air.typeOfIndex(inst);
3858 const llvm_ty = try self.dg.llvmType(ty);
3859 const target = self.dg.module.getTarget();
3860
3861 const fn_val = switch (ty.floatBits(target)) {
3862 16, 32, 64 => blk: {
3863 break :blk self.getIntrinsic("llvm.fma", &.{llvm_ty});
3864 },
3865 // TODO: using `llvm.fma` for f80 does not seem to work for all targets, needs further investigation.
3866 80 => return self.dg.todo("Implement mulAdd for f80", .{}),
3867 128 => blk: {
3868 // LLVM incorrectly lowers the fma builtin for f128 to fmal, which is for
3869 // `long double`. On some targets this will be correct; on others it will be incorrect.
3870 if (target.longDoubleIsF128()) {
3871 break :blk self.getIntrinsic("llvm.fma", &.{llvm_ty});
3872 } else {
3873 break :blk self.dg.object.llvm_module.getNamedFunction("fmaq") orelse fn_blk: {
3874 const param_types = [_]*const llvm.Type{ llvm_ty, llvm_ty, llvm_ty };
3875 const fn_type = llvm.functionType(llvm_ty, &param_types, param_types.len, .False);
3876 break :fn_blk self.dg.object.llvm_module.addFunction("fmaq", fn_type);
3877 };
3878 }
3879 },
3880 else => unreachable,
3881 };
3882 const params = [_]*const llvm.Value{ mulend1, mulend2, addend };
3883 return self.builder.buildCall(fn_val, &params, params.len, .C, .Auto, "");
3884 }
3885
3845 fn airShlWithOverflow(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {3886 fn airShlWithOverflow(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
3846 if (self.liveness.isUnused(inst))3887 if (self.liveness.isUnused(inst))
3847 return null;3888 return null;
src/print_air.zig+12
...@@ -252,6 +252,7 @@ const Writer = struct {...@@ -252,6 +252,7 @@ const Writer = struct {
252 .field_parent_ptr => try w.writeFieldParentPtr(s, inst),252 .field_parent_ptr => try w.writeFieldParentPtr(s, inst),
253 .wasm_memory_size => try w.writeWasmMemorySize(s, inst),253 .wasm_memory_size => try w.writeWasmMemorySize(s, inst),
254 .wasm_memory_grow => try w.writeWasmMemoryGrow(s, inst),254 .wasm_memory_grow => try w.writeWasmMemoryGrow(s, inst),
255 .mul_add => try w.writeMulAdd(s, inst),
255256
256 .add_with_overflow,257 .add_with_overflow,
257 .sub_with_overflow,258 .sub_with_overflow,
...@@ -358,6 +359,17 @@ const Writer = struct {...@@ -358,6 +359,17 @@ const Writer = struct {
358 });359 });
359 }360 }
360361
362 fn writeMulAdd(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
363 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
364 const extra = w.air.extraData(Air.MulAdd, ty_pl.payload).data;
365
366 try w.writeOperand(s, inst, 0, extra.mulend1);
367 try s.writeAll(", ");
368 try w.writeOperand(s, inst, 1, extra.mulend2);
369 try s.writeAll(", ");
370 try w.writeOperand(s, inst, 2, extra.addend);
371 }
372
361 fn writeFence(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {373 fn writeFence(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
362 const atomic_order = w.air.instructions.items(.data)[inst].fence;374 const atomic_order = w.air.instructions.items(.data)[inst].fence;
363375
src/value.zig+40-4
...@@ -2931,7 +2931,7 @@ pub const Value = extern union {...@@ -2931,7 +2931,7 @@ pub const Value = extern union {
2931 return fromBigInt(arena, result_bigint.toConst());2931 return fromBigInt(arena, result_bigint.toConst());
2932 }2932 }
29332933
2934 /// operands must be integers; handles undefined. 2934 /// operands must be integers; handles undefined.
2935 pub fn bitwiseAnd(lhs: Value, rhs: Value, arena: Allocator) !Value {2935 pub fn bitwiseAnd(lhs: Value, rhs: Value, arena: Allocator) !Value {
2936 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);2936 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
29372937
...@@ -2951,7 +2951,7 @@ pub const Value = extern union {...@@ -2951,7 +2951,7 @@ pub const Value = extern union {
2951 return fromBigInt(arena, result_bigint.toConst());2951 return fromBigInt(arena, result_bigint.toConst());
2952 }2952 }
29532953
2954 /// operands must be integers; handles undefined. 2954 /// operands must be integers; handles undefined.
2955 pub fn bitwiseNand(lhs: Value, rhs: Value, ty: Type, arena: Allocator, target: Target) !Value {2955 pub fn bitwiseNand(lhs: Value, rhs: Value, ty: Type, arena: Allocator, target: Target) !Value {
2956 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);2956 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
29572957
...@@ -2965,7 +2965,7 @@ pub const Value = extern union {...@@ -2965,7 +2965,7 @@ pub const Value = extern union {
2965 return bitwiseXor(anded, all_ones, arena);2965 return bitwiseXor(anded, all_ones, arena);
2966 }2966 }
29672967
2968 /// operands must be integers; handles undefined. 2968 /// operands must be integers; handles undefined.
2969 pub fn bitwiseOr(lhs: Value, rhs: Value, arena: Allocator) !Value {2969 pub fn bitwiseOr(lhs: Value, rhs: Value, arena: Allocator) !Value {
2970 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);2970 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
29712971
...@@ -2984,7 +2984,7 @@ pub const Value = extern union {...@@ -2984,7 +2984,7 @@ pub const Value = extern union {
2984 return fromBigInt(arena, result_bigint.toConst());2984 return fromBigInt(arena, result_bigint.toConst());
2985 }2985 }
29862986
2987 /// operands must be integers; handles undefined. 2987 /// operands must be integers; handles undefined.
2988 pub fn bitwiseXor(lhs: Value, rhs: Value, arena: Allocator) !Value {2988 pub fn bitwiseXor(lhs: Value, rhs: Value, arena: Allocator) !Value {
2989 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);2989 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
29902990
...@@ -4020,6 +4020,42 @@ pub const Value = extern union {...@@ -4020,6 +4020,42 @@ pub const Value = extern union {
4020 }4020 }
4021 }4021 }
40224022
4023 pub fn mulAdd(float_type: Type, mulend1: Value, mulend2: Value, addend: Value, arena: Allocator, target: Target) Allocator.Error!Value {
4024 switch (float_type.floatBits(target)) {
4025 16 => {
4026 if (true) {
4027 // TODO: missing f16 implementation of FMA in `std.math.fma` or compiler-rt
4028 @panic("TODO implement mulAdd for f16");
4029 }
4030 },
4031 32 => {
4032 const m1 = mulend1.toFloat(f32);
4033 const m2 = mulend2.toFloat(f32);
4034 const a = addend.toFloat(f32);
4035 return Value.Tag.float_32.create(arena, std.math.fma(f32, m1, m2, a));
4036 },
4037 64 => {
4038 const m1 = mulend1.toFloat(f64);
4039 const m2 = mulend2.toFloat(f64);
4040 const a = addend.toFloat(f64);
4041 return Value.Tag.float_64.create(arena, std.math.fma(f64, m1, m2, a));
4042 },
4043 80 => {
4044 if (true) {
4045 // TODO: missing f80 implementation of FMA in `std.math.fma` or compiler-rt
4046 @panic("TODO implement mulAdd for f80");
4047 }
4048 },
4049 128 => {
4050 const m1 = mulend1.toFloat(f128);
4051 const m2 = mulend2.toFloat(f128);
4052 const a = addend.toFloat(f128);
4053 return Value.Tag.float_128.create(arena, std.math.fma(f128, m1, m2, a));
4054 },
4055 else => unreachable,
4056 }
4057 }
4058
4023 /// This type is not copyable since it may contain pointers to its inner data.4059 /// This type is not copyable since it may contain pointers to its inner data.
4024 pub const Payload = struct {4060 pub const Payload = struct {
4025 tag: Tag,4061 tag: Tag,
test/behavior/muladd.zig+30-21
...@@ -2,29 +2,33 @@ const builtin = @import("builtin");...@@ -2,29 +2,33 @@ const builtin = @import("builtin");
2const expect = @import("std").testing.expect;2const expect = @import("std").testing.expect;
33
4test "@mulAdd" {4test "@mulAdd" {
5 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO5 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
6 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
7 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
8 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
610
7 comptime try testMulAdd();11 comptime try testMulAdd();
8 try testMulAdd();12 try testMulAdd();
9}13}
1014
11fn testMulAdd() !void {15fn testMulAdd() !void {
12 {16 if (builtin.zig_backend == .stage1) {
13 var a: f16 = 5.5;17 const a: f16 = 5.5;
14 var b: f16 = 2.5;18 const b: f16 = 2.5;
15 var c: f16 = 6.25;19 const c: f16 = 6.25;
16 try expect(@mulAdd(f16, a, b, c) == 20);20 try expect(@mulAdd(f16, a, b, c) == 20);
17 }21 }
18 {22 {
19 var a: f32 = 5.5;23 const a: f32 = 5.5;
20 var b: f32 = 2.5;24 const b: f32 = 2.5;
21 var c: f32 = 6.25;25 const c: f32 = 6.25;
22 try expect(@mulAdd(f32, a, b, c) == 20);26 try expect(@mulAdd(f32, a, b, c) == 20);
23 }27 }
24 {28 {
25 var a: f64 = 5.5;29 const a: f64 = 5.5;
26 var b: f64 = 2.5;30 const b: f64 = 2.5;
27 var c: f64 = 6.25;31 const c: f64 = 6.25;
28 try expect(@mulAdd(f64, a, b, c) == 20);32 try expect(@mulAdd(f64, a, b, c) == 20);
29 }33 }
30}34}
...@@ -35,7 +39,9 @@ test "@mulAdd f80" {...@@ -35,7 +39,9 @@ test "@mulAdd f80" {
35 return error.SkipZigTest;39 return error.SkipZigTest;
36 }40 }
3741
38 comptime try testMulAdd80();42 // TODO: missing f80 implementation of FMA in `std.math.fma` or compiler-rt
43 // comptime try testMulAdd80();
44
39 try testMulAdd80();45 try testMulAdd80();
40}46}
4147
...@@ -43,24 +49,27 @@ fn testMulAdd80() !void {...@@ -43,24 +49,27 @@ fn testMulAdd80() !void {
43 var a: f16 = 5.5;49 var a: f16 = 5.5;
44 var b: f80 = 2.5;50 var b: f80 = 2.5;
45 var c: f80 = 6.25;51 var c: f80 = 6.25;
46 try expect(@mulAdd(f80, a, b, c) == 20);52 try expect(@mulAdd(f80, a, b, c) == 20.0);
47}53}
4854
49test "@mulAdd f128" {55test "@mulAdd f128" {
50 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO56 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
5157 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
58 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
59 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
60 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
52 if (builtin.os.tag == .macos and builtin.cpu.arch == .aarch64) {61 if (builtin.os.tag == .macos and builtin.cpu.arch == .aarch64) {
53 // https://github.com/ziglang/zig/issues/990062 // https://github.com/ziglang/zig/issues/9900
54 return error.SkipZigTest;63 return error.SkipZigTest;
55 }64 }
5665
57 comptime try testMullAdd128();66 comptime try testMulAdd128();
58 try testMullAdd128();67 try testMulAdd128();
59}68}
6069
61fn testMullAdd128() !void {70fn testMulAdd128() !void {
62 var a: f16 = 5.5;71 const a: f16 = 5.5;
63 var b: f128 = 2.5;72 const b: f128 = 2.5;
64 var c: f128 = 6.25;73 const c: f128 = 6.25;
65 try expect(@mulAdd(f128, a, b, c) == 20);74 try expect(@mulAdd(f128, a, b, c) == 20);
66}75}