authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-07 04:00:45-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-03-07 04:00:45-05:00
log8c32d989c995f8675f1824fb084245b833b26223
tree81f80b17835931b5fa0a877e9c554225f75ea7a9
parent6547da8f97b94453fb08f582c2c7ce4eb1782a80
parent3c1ebf95567db0c844c2618c5b8971d62c27352f
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11054 from schmee/mul-add

Implement `@mulAdd` for scalar floats

21 files changed, 541 insertions(+), 114 deletions(-)

lib/std/math/fma.zig+2
...@@ -19,6 +19,8 @@ pub fn fma(comptime T: type, x: T, y: T, z: T) T {...@@ -19,6 +19,8 @@ pub fn fma(comptime T: type, x: T, y: T, z: T) T {
19 // TODO this is not correct for some targets19 // TODO this is not correct for some targets
20 c_longdouble => @floatCast(c_longdouble, fma128(x, y, z)),20 c_longdouble => @floatCast(c_longdouble, fma128(x, y, z)),
2121
22 f80 => @floatCast(f80, fma128(x, y, z)),
23
22 else => @compileError("fma not implemented for " ++ @typeName(T)),24 else => @compileError("fma not implemented for " ++ @typeName(T)),
23 };25 };
24}26}
lib/std/special/c.zig+1-19
...@@ -12,7 +12,7 @@ const maxInt = std.math.maxInt;...@@ -12,7 +12,7 @@ const maxInt = std.math.maxInt;
12const native_os = builtin.os.tag;12const native_os = builtin.os.tag;
13const native_arch = builtin.cpu.arch;13const native_arch = builtin.cpu.arch;
14const native_abi = builtin.abi;14const native_abi = builtin.abi;
15const long_double_is_f128 = builtin.target.longDoubleIsF128();15const long_double_is_f128 = builtin.target.longDoubleIs(f128);
1616
17const is_wasm = switch (native_arch) {17const is_wasm = switch (native_arch) {
18 .wasm32, .wasm64 => true,18 .wasm32, .wasm64 => true,
...@@ -90,10 +90,6 @@ comptime {...@@ -90,10 +90,6 @@ comptime {
90 @export(fmod, .{ .name = "fmod", .linkage = .Strong });90 @export(fmod, .{ .name = "fmod", .linkage = .Strong });
91 @export(fmodf, .{ .name = "fmodf", .linkage = .Strong });91 @export(fmodf, .{ .name = "fmodf", .linkage = .Strong });
9292
93 @export(fma, .{ .name = "fma", .linkage = .Strong });
94 @export(fmaf, .{ .name = "fmaf", .linkage = .Strong });
95 @export(fmal, .{ .name = "fmal", .linkage = .Strong });
96
97 @export(sincos, .{ .name = "sincos", .linkage = .Strong });93 @export(sincos, .{ .name = "sincos", .linkage = .Strong });
98 @export(sincosf, .{ .name = "sincosf", .linkage = .Strong });94 @export(sincosf, .{ .name = "sincosf", .linkage = .Strong });
9995
...@@ -561,20 +557,6 @@ test "fmod, fmodf" {...@@ -561,20 +557,6 @@ test "fmod, fmodf" {
561 }557 }
562}558}
563559
564fn fmaf(a: f32, b: f32, c: f32) callconv(.C) f32 {
565 return math.fma(f32, a, b, c);
566}
567
568fn fma(a: f64, b: f64, c: f64) callconv(.C) f64 {
569 return math.fma(f64, a, b, c);
570}
571fn fmal(a: c_longdouble, b: c_longdouble, c: c_longdouble) callconv(.C) c_longdouble {
572 if (!long_double_is_f128) {
573 @panic("TODO implement this");
574 }
575 return math.fma(c_longdouble, a, b, c);
576}
577
578fn sincos(a: f64, r_sin: *f64, r_cos: *f64) callconv(.C) void {560fn sincos(a: f64, r_sin: *f64, r_cos: *f64) callconv(.C) void {
579 r_sin.* = math.sin(a);561 r_sin.* = math.sin(a);
580 r_cos.* = math.cos(a);562 r_cos.* = math.cos(a);
lib/std/special/compiler_rt.zig+41-10
...@@ -19,7 +19,8 @@ const strong_linkage = if (is_test)...@@ -19,7 +19,8 @@ const strong_linkage = if (is_test)
19else19else
20 std.builtin.GlobalLinkage.Strong;20 std.builtin.GlobalLinkage.Strong;
2121
22const long_double_is_f128 = builtin.target.longDoubleIsF128();22const long_double_is_f80 = builtin.target.longDoubleIs(f80);
23const long_double_is_f128 = builtin.target.longDoubleIs(f128);
2324
24comptime {25comptime {
25 // These files do their own comptime exporting logic.26 // These files do their own comptime exporting logic.
...@@ -673,6 +674,29 @@ comptime {...@@ -673,6 +674,29 @@ comptime {
673 @export(_aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage });674 @export(_aullrem, .{ .name = "\x01__aullrem", .linkage = strong_linkage });
674 }675 }
675676
677 const fmodl = @import("compiler_rt/floatfmodl.zig").fmodl;
678 if (!is_test) {
679 @export(fmodl, .{ .name = "fmodl", .linkage = linkage });
680
681 @export(floorf, .{ .name = "floorf", .linkage = linkage });
682 @export(floor, .{ .name = "floor", .linkage = linkage });
683 @export(floorl, .{ .name = "floorl", .linkage = linkage });
684
685 @export(fma, .{ .name = "fma", .linkage = linkage });
686 @export(fmaf, .{ .name = "fmaf", .linkage = linkage });
687 @export(fmal, .{ .name = "fmal", .linkage = linkage });
688 if (long_double_is_f80) {
689 @export(fmal, .{ .name = "__fmax", .linkage = linkage });
690 } else {
691 @export(__fmax, .{ .name = "__fmax", .linkage = linkage });
692 }
693 if (long_double_is_f128) {
694 @export(fmal, .{ .name = "fmaq", .linkage = linkage });
695 } else {
696 @export(fmaq, .{ .name = "fmaq", .linkage = linkage });
697 }
698 }
699
676 if (arch.isSPARC()) {700 if (arch.isSPARC()) {
677 // SPARC systems use a different naming scheme701 // SPARC systems use a different naming scheme
678 const _Qp_add = @import("compiler_rt/sparc.zig")._Qp_add;702 const _Qp_add = @import("compiler_rt/sparc.zig")._Qp_add;
...@@ -725,7 +749,7 @@ comptime {...@@ -725,7 +749,7 @@ comptime {
725 @export(_Qp_qtod, .{ .name = "_Qp_qtod", .linkage = linkage });749 @export(_Qp_qtod, .{ .name = "_Qp_qtod", .linkage = linkage });
726 }750 }
727751
728 if ((arch == .powerpc or arch.isPPC64()) and !is_test) {752 if ((arch.isPPC() or arch.isPPC64()) and !is_test) {
729 @export(__addtf3, .{ .name = "__addkf3", .linkage = linkage });753 @export(__addtf3, .{ .name = "__addkf3", .linkage = linkage });
730 @export(__subtf3, .{ .name = "__subkf3", .linkage = linkage });754 @export(__subtf3, .{ .name = "__subkf3", .linkage = linkage });
731 @export(__multf3, .{ .name = "__mulkf3", .linkage = linkage });755 @export(__multf3, .{ .name = "__mulkf3", .linkage = linkage });
...@@ -750,22 +774,29 @@ comptime {...@@ -750,22 +774,29 @@ comptime {
750 @export(__letf2, .{ .name = "__lekf2", .linkage = linkage });774 @export(__letf2, .{ .name = "__lekf2", .linkage = linkage });
751 @export(__getf2, .{ .name = "__gtkf2", .linkage = linkage });775 @export(__getf2, .{ .name = "__gtkf2", .linkage = linkage });
752 @export(__unordtf2, .{ .name = "__unordkf2", .linkage = linkage });776 @export(__unordtf2, .{ .name = "__unordkf2", .linkage = linkage });
753 }
754777
755 const fmodl = @import("compiler_rt/floatfmodl.zig").fmodl;778 // LLVM PPC backend lowers f128 fma to `fmaf128`.
756 @export(fmodl, .{ .name = "fmodl", .linkage = linkage });779 @export(fmal, .{ .name = "fmaf128", .linkage = linkage });
757780 }
758 @export(floorf, .{ .name = "floorf", .linkage = linkage });
759 @export(floor, .{ .name = "floor", .linkage = linkage });
760 @export(floorl, .{ .name = "floorl", .linkage = linkage });
761 @export(fmaq, .{ .name = "fmaq", .linkage = linkage });
762}781}
763782
764const math = std.math;783const math = std.math;
765784
785fn fmaf(a: f32, b: f32, c: f32) callconv(.C) f32 {
786 return math.fma(f32, a, b, c);
787}
788fn fma(a: f64, b: f64, c: f64) callconv(.C) f64 {
789 return math.fma(f64, a, b, c);
790}
791fn __fmax(a: f80, b: f80, c: f80) callconv(.C) f80 {
792 return math.fma(f80, a, b, c);
793}
766fn fmaq(a: f128, b: f128, c: f128) callconv(.C) f128 {794fn fmaq(a: f128, b: f128, c: f128) callconv(.C) f128 {
767 return math.fma(f128, a, b, c);795 return math.fma(f128, a, b, c);
768}796}
797fn fmal(a: c_longdouble, b: c_longdouble, c: c_longdouble) callconv(.C) c_longdouble {
798 return math.fma(c_longdouble, a, b, c);
799}
769800
770// TODO add intrinsics for these (and probably the double version too)801// TODO add intrinsics for these (and probably the double version too)
771// and have the math stuff use the intrinsic. same as @mod and @rem802// and have the math stuff use the intrinsic. same as @mod and @rem
lib/std/target.zig+49-3
...@@ -1714,9 +1714,55 @@ pub const Target = struct {...@@ -1714,9 +1714,55 @@ pub const Target = struct {
1714 };1714 };
1715 }1715 }
17161716
1717 pub inline fn longDoubleIsF128(target: Target) bool {1717 pub inline fn longDoubleIs(target: Target, comptime F: type) bool {
1718 return switch (target.cpu.arch) {1718 if (target.abi == .msvc) {
1719 .riscv64, .aarch64, .aarch64_be, .aarch64_32, .s390x, .mips64, .mips64el => true,1719 return F == f64;
1720 }
1721 return switch (F) {
1722 f128 => switch (target.cpu.arch) {
1723 .riscv64,
1724 .aarch64,
1725 .aarch64_be,
1726 .aarch64_32,
1727 .s390x,
1728 .mips64,
1729 .mips64el,
1730 .sparc,
1731 .sparcv9,
1732 .sparcel,
1733 .powerpc,
1734 .powerpcle,
1735 .powerpc64,
1736 .powerpc64le,
1737 => true,
1738
1739 else => false,
1740 },
1741 f80 => switch (target.cpu.arch) {
1742 .x86_64, .i386 => true,
1743 else => false,
1744 },
1745 f64 => switch (target.cpu.arch) {
1746 .x86_64,
1747 .i386,
1748 .riscv64,
1749 .aarch64,
1750 .aarch64_be,
1751 .aarch64_32,
1752 .s390x,
1753 .mips64,
1754 .mips64el,
1755 .sparc,
1756 .sparcv9,
1757 .sparcel,
1758 .powerpc,
1759 .powerpcle,
1760 .powerpc64,
1761 .powerpc64le,
1762 => false,
1763
1764 else => true,
1765 },
1720 else => false,1766 else => false,
1721 };1767 };
1722 }1768 }
src/Air.zig+7
...@@ -579,6 +579,11 @@ pub const Inst = struct {...@@ -579,6 +579,11 @@ 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 `pl_op` field with payload `Bin`.
584 /// The operand is the addend. The mulends are lhs and rhs.
585 mul_add,
586
582 /// Implements @fieldParentPtr builtin.587 /// Implements @fieldParentPtr builtin.
583 /// Uses the `ty_pl` field.588 /// Uses the `ty_pl` field.
584 field_parent_ptr,589 field_parent_ptr,
...@@ -986,6 +991,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -986,6 +991,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
986 return ptr_ty.elemType();991 return ptr_ty.elemType();
987 },992 },
988993
994 .mul_add => return air.typeOf(datas[inst].pl_op.operand),
995
989 .add_with_overflow,996 .add_with_overflow,
990 .sub_with_overflow,997 .sub_with_overflow,
991 .mul_with_overflow,998 .mul_with_overflow,
src/AstGen.zig+2-2
...@@ -7309,8 +7309,8 @@ fn builtinCall(...@@ -7309,8 +7309,8 @@ fn builtinCall(
7309 },7309 },
7310 .mul_add => {7310 .mul_add => {
7311 const float_type = try typeExpr(gz, scope, params[0]);7311 const float_type = try typeExpr(gz, scope, params[0]);
7312 const mulend1 = try expr(gz, scope, .{ .ty = float_type }, params[1]);7312 const mulend1 = try expr(gz, scope, .{ .coerced_ty = float_type }, params[1]);
7313 const mulend2 = try expr(gz, scope, .{ .ty = float_type }, params[2]);7313 const mulend2 = try expr(gz, scope, .{ .coerced_ty = float_type }, params[2]);
7314 const addend = try expr(gz, scope, .{ .ty = float_type }, params[3]);7314 const addend = try expr(gz, scope, .{ .ty = float_type }, params[3]);
7315 const result = try gz.addPlNode(.mul_add, node, Zir.Inst.MulAdd{7315 const result = try gz.addPlNode(.mul_add, node, Zir.Inst.MulAdd{
7316 .mulend1 = mulend1,7316 .mulend1 = mulend1,
src/Liveness.zig+5
...@@ -464,6 +464,11 @@ fn analyzeInst(...@@ -464,6 +464,11 @@ 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 pl_op = inst_datas[inst].pl_op;
469 const extra = a.air.extraData(Air.Bin, pl_op.payload).data;
470 return trackOperands(a, new_set, inst, main_tomb, .{ extra.lhs, extra.rhs, pl_op.operand });
471 },
467 .atomic_load => {472 .atomic_load => {
468 const ptr = inst_datas[inst].atomic_load.ptr;473 const ptr = inst_datas[inst].atomic_load.ptr;
469 return trackOperands(a, new_set, inst, main_tomb, .{ ptr, .none, .none });474 return trackOperands(a, new_set, inst, main_tomb, .{ ptr, .none, .none });
src/Sema.zig+76-1
...@@ -13518,8 +13518,83 @@ fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -13518,8 +13518,83 @@ 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 addend = sema.resolveInst(extra.addend);
13529 const ty = sema.typeOf(addend);
13530 const mulend1 = try sema.coerce(block, ty, sema.resolveInst(extra.mulend1), mulend1_src);
13531 const mulend2 = try sema.coerce(block, ty, sema.resolveInst(extra.mulend2), mulend2_src);
13532
13533 const target = sema.mod.getTarget();
13534
13535 switch (ty.zigTypeTag()) {
13536 .ComptimeFloat, .Float => {
13537 const maybe_mulend1 = try sema.resolveMaybeUndefVal(block, mulend1_src, mulend1);
13538 const maybe_mulend2 = try sema.resolveMaybeUndefVal(block, mulend2_src, mulend2);
13539 const maybe_addend = try sema.resolveMaybeUndefVal(block, addend_src, addend);
13540
13541 const runtime_src = if (maybe_mulend1) |mulend1_val| rs: {
13542 if (maybe_mulend2) |mulend2_val| {
13543 if (mulend2_val.isUndef()) return sema.addConstUndef(ty);
13544
13545 if (maybe_addend) |addend_val| {
13546 if (addend_val.isUndef()) return sema.addConstUndef(ty);
13547
13548 const result_val = try Value.mulAdd(
13549 ty,
13550 mulend1_val,
13551 mulend2_val,
13552 addend_val,
13553 sema.arena,
13554 target,
13555 );
13556 return sema.addConstant(ty, result_val);
13557 } else {
13558 break :rs addend_src;
13559 }
13560 } else {
13561 if (maybe_addend) |addend_val| {
13562 if (addend_val.isUndef()) return sema.addConstUndef(ty);
13563 }
13564 break :rs mulend2_src;
13565 }
13566 } else rs: {
13567 if (maybe_mulend2) |mulend2_val| {
13568 if (mulend2_val.isUndef()) return sema.addConstUndef(ty);
13569 }
13570 if (maybe_addend) |addend_val| {
13571 if (addend_val.isUndef()) return sema.addConstUndef(ty);
13572 }
13573 break :rs mulend1_src;
13574 };
13575
13576 try sema.requireRuntimeBlock(block, runtime_src);
13577 return block.addInst(.{
13578 .tag = .mul_add,
13579 .data = .{ .pl_op = .{
13580 .operand = addend,
13581 .payload = try sema.addExtra(Air.Bin{
13582 .lhs = mulend1,
13583 .rhs = mulend2,
13584 }),
13585 } },
13586 });
13587 },
13588 .Vector => {
13589 const scalar_ty = ty.scalarType();
13590 switch (scalar_ty.zigTypeTag()) {
13591 .ComptimeFloat, .Float => {},
13592 else => return sema.fail(block, src, "expected vector of floats or float type, found '{}'", .{scalar_ty}),
13593 }
13594 return sema.fail(block, src, "TODO: implement @mulAdd for vectors", .{});
13595 },
13596 else => return sema.fail(block, src, "expected vector of floats or float type, found '{}'", .{ty}),
13597 }
13523}13598}
1352413599
13525fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {13600fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
src/Zir.zig+2
...@@ -891,6 +891,8 @@ pub const Inst = struct {...@@ -891,6 +891,8 @@ pub const Inst = struct {
891 atomic_store,891 atomic_store,
892 /// Implements the `@mulAdd` builtin.892 /// Implements the `@mulAdd` builtin.
893 /// Uses the `pl_node` union field with payload `MulAdd`.893 /// Uses the `pl_node` union field with payload `MulAdd`.
894 /// The addend communicates the type of the builtin.
895 /// The mulends need to be coerced to the same type.
894 mul_add,896 mul_add,
895 /// Implements the `@call` builtin.897 /// Implements the `@call` builtin.
896 /// Uses the `pl_node` union field with payload `BuiltinCall`.898 /// Uses the `pl_node` union field with payload `BuiltinCall`.
src/arch/aarch64/CodeGen.zig+10
...@@ -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,15 @@ fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {...@@ -3652,6 +3653,15 @@ 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 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
3658 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
3659 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
3660 return self.fail("TODO implement airMulAdd for aarch64", .{});
3661 };
3662 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });
3663}
3664
3655fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {3665fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
3656 // First section of indexes correspond to a set number of constant values.3666 // First section of indexes correspond to a set number of constant values.
3657 const ref_int = @enumToInt(inst);3667 const ref_int = @enumToInt(inst);
src/arch/arm/CodeGen.zig+10
...@@ -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,15 @@ fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {...@@ -4086,6 +4087,15 @@ 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 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
4092 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
4093 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
4094 return self.fail("TODO implement airMulAdd for arm", .{});
4095 };
4096 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });
4097}
4098
4089fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {4099fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
4090 // First section of indexes correspond to a set number of constant values.4100 // First section of indexes correspond to a set number of constant values.
4091 const ref_int = @enumToInt(inst);4101 const ref_int = @enumToInt(inst);
src/arch/riscv64/CodeGen.zig+10
...@@ -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,15 @@ fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {...@@ -2203,6 +2204,15 @@ 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 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
2209 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
2210 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
2211 return self.fail("TODO implement airMulAdd for riscv64", .{});
2212 };
2213 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });
2214}
2215
2206fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {2216fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
2207 // First section of indexes correspond to a set number of constant values.2217 // First section of indexes correspond to a set number of constant values.
2208 const ref_int = @enumToInt(inst);2218 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+10
...@@ -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,15 @@ fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {...@@ -5559,6 +5560,15 @@ 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 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
5565 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
5566 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
5567 return self.fail("TODO implement airMulAdd for x86_64", .{});
5568 };
5569 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });
5570}
5571
5562fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {5572fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
5563 // First section of indexes correspond to a set number of constant values.5573 // First section of indexes correspond to a set number of constant values.
5564 const ref_int = @enumToInt(inst);5574 const ref_int = @enumToInt(inst);
src/codegen/c.zig+32
...@@ -16,6 +16,7 @@ const trace = @import("../tracy.zig").trace;...@@ -16,6 +16,7 @@ const trace = @import("../tracy.zig").trace;
16const LazySrcLoc = Module.LazySrcLoc;16const LazySrcLoc = Module.LazySrcLoc;
17const Air = @import("../Air.zig");17const Air = @import("../Air.zig");
18const Liveness = @import("../Liveness.zig");18const Liveness = @import("../Liveness.zig");
19const CType = @import("../type.zig").CType;
1920
20const Mutability = enum { Const, Mut };21const Mutability = enum { Const, Mut };
21const BigIntConst = std.math.big.int.Const;22const BigIntConst = std.math.big.int.Const;
...@@ -1635,6 +1636,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -1635,6 +1636,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
1635 .trunc_float,1636 .trunc_float,
1636 => |tag| return f.fail("TODO: C backend: implement unary op for tag '{s}'", .{@tagName(tag)}),1637 => |tag| return f.fail("TODO: C backend: implement unary op for tag '{s}'", .{@tagName(tag)}),
16371638
1639 .mul_add => try airMulAdd(f, inst),
1640
1638 .add_with_overflow => try airAddWithOverflow(f, inst),1641 .add_with_overflow => try airAddWithOverflow(f, inst),
1639 .sub_with_overflow => try airSubWithOverflow(f, inst),1642 .sub_with_overflow => try airSubWithOverflow(f, inst),
1640 .mul_with_overflow => try airMulWithOverflow(f, inst),1643 .mul_with_overflow => try airMulWithOverflow(f, inst),
...@@ -3621,6 +3624,35 @@ fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3621,6 +3624,35 @@ fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue {
3621 return local;3624 return local;
3622}3625}
36233626
3627fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {
3628 if (f.liveness.isUnused(inst)) return CValue.none;
3629 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
3630 const extra = f.air.extraData(Air.Bin, pl_op.payload).data;
3631 const inst_ty = f.air.typeOfIndex(inst);
3632 const mulend1 = try f.resolveInst(extra.lhs);
3633 const mulend2 = try f.resolveInst(extra.rhs);
3634 const addend = try f.resolveInst(pl_op.operand);
3635 const writer = f.object.writer();
3636 const target = f.object.dg.module.getTarget();
3637 const fn_name = switch (inst_ty.floatBits(target)) {
3638 16, 32 => "fmaf",
3639 64 => "fma",
3640 80 => if (CType.longdouble.sizeInBits(target) == 80) "fmal" else "__fmax",
3641 128 => if (CType.longdouble.sizeInBits(target) == 128) "fmal" else "fmaq",
3642 else => unreachable,
3643 };
3644 const local = try f.allocLocal(inst_ty, .Const);
3645 try writer.writeAll(" = ");
3646 try writer.print("{s}(", .{fn_name});
3647 try f.writeCValue(writer, mulend1);
3648 try writer.writeAll(", ");
3649 try f.writeCValue(writer, mulend2);
3650 try writer.writeAll(", ");
3651 try f.writeCValue(writer, addend);
3652 try writer.writeAll(");\n");
3653 return local;
3654}
3655
3624fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 {3656fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 {
3625 return switch (order) {3657 return switch (order) {
3626 .Unordered => "memory_order_relaxed",3658 .Unordered => "memory_order_relaxed",
src/codegen/llvm.zig+140-3
...@@ -18,6 +18,7 @@ const target_util = @import("../target.zig");...@@ -18,6 +18,7 @@ const target_util = @import("../target.zig");
18const Value = @import("../value.zig").Value;18const Value = @import("../value.zig").Value;
19const Type = @import("../type.zig").Type;19const Type = @import("../type.zig").Type;
20const LazySrcLoc = Module.LazySrcLoc;20const LazySrcLoc = Module.LazySrcLoc;
21const CType = @import("../type.zig").CType;
2122
22const Error = error{ OutOfMemory, CodegenFail };23const Error = error{ OutOfMemory, CodegenFail };
2324
...@@ -2189,6 +2190,7 @@ pub const FuncGen = struct {...@@ -2189,6 +2190,7 @@ pub const FuncGen = struct {
2189 .min => try self.airMin(inst),2190 .min => try self.airMin(inst),
2190 .max => try self.airMax(inst),2191 .max => try self.airMax(inst),
2191 .slice => try self.airSlice(inst),2192 .slice => try self.airSlice(inst),
2193 .mul_add => try self.airMulAdd(inst),
21922194
2193 .add_with_overflow => try self.airOverflow(inst, "llvm.sadd.with.overflow", "llvm.uadd.with.overflow"),2195 .add_with_overflow => try self.airOverflow(inst, "llvm.sadd.with.overflow", "llvm.uadd.with.overflow"),
2194 .sub_with_overflow => try self.airOverflow(inst, "llvm.ssub.with.overflow", "llvm.usub.with.overflow"),2196 .sub_with_overflow => try self.airOverflow(inst, "llvm.ssub.with.overflow", "llvm.usub.with.overflow"),
...@@ -3842,6 +3844,46 @@ pub const FuncGen = struct {...@@ -3842,6 +3844,46 @@ pub const FuncGen = struct {
3842 return overflow_bit;3844 return overflow_bit;
3843 }3845 }
38443846
3847 fn airMulAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
3848 if (self.liveness.isUnused(inst)) return null;
3849
3850 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
3851 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
3852
3853 const mulend1 = try self.resolveInst(extra.lhs);
3854 const mulend2 = try self.resolveInst(extra.rhs);
3855 const addend = try self.resolveInst(pl_op.operand);
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 Strat = union(enum) {
3862 intrinsic,
3863 libc: [*:0]const u8,
3864 };
3865 const strat: Strat = switch (ty.floatBits(target)) {
3866 16, 32, 64 => Strat.intrinsic,
3867 80 => if (CType.longdouble.sizeInBits(target) == 80) Strat{ .intrinsic = {} } else Strat{ .libc = "__fmax" },
3868 // LLVM always lowers the fma builtin for f128 to fmal, which is for `long double`.
3869 // On some targets this will be correct; on others it will be incorrect.
3870 128 => if (CType.longdouble.sizeInBits(target) == 128) Strat{ .intrinsic = {} } else Strat{ .libc = "fmaq" },
3871 else => unreachable,
3872 };
3873
3874 const llvm_fn = switch (strat) {
3875 .intrinsic => self.getIntrinsic("llvm.fma", &.{llvm_ty}),
3876 .libc => |fn_name| self.dg.object.llvm_module.getNamedFunction(fn_name) orelse b: {
3877 const param_types = [_]*const llvm.Type{ llvm_ty, llvm_ty, llvm_ty };
3878 const fn_type = llvm.functionType(llvm_ty, &param_types, param_types.len, .False);
3879 break :b self.dg.object.llvm_module.addFunction(fn_name, fn_type);
3880 },
3881 };
3882
3883 const params = [_]*const llvm.Value{ mulend1, mulend2, addend };
3884 return self.builder.buildCall(llvm_fn, &params, params.len, .C, .Auto, "");
3885 }
3886
3845 fn airShlWithOverflow(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {3887 fn airShlWithOverflow(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
3846 if (self.liveness.isUnused(inst))3888 if (self.liveness.isUnused(inst))
3847 return null;3889 return null;
...@@ -4020,8 +4062,15 @@ pub const FuncGen = struct {...@@ -4020,8 +4062,15 @@ pub const FuncGen = struct {
40204062
4021 const ty_op = self.air.instructions.items(.data)[inst].ty_op;4063 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4022 const operand = try self.resolveInst(ty_op.operand);4064 const operand = try self.resolveInst(ty_op.operand);
4023 const dest_llvm_ty = try self.dg.llvmType(self.air.typeOfIndex(inst));4065 const operand_ty = self.air.typeOf(ty_op.operand);
40244066 const dest_ty = self.air.typeOfIndex(inst);
4067 const target = self.dg.module.getTarget();
4068 const dest_bits = dest_ty.floatBits(target);
4069 const src_bits = operand_ty.floatBits(target);
4070 if (!backendSupportsF80(target) and (src_bits == 80 or dest_bits == 80)) {
4071 return softF80TruncOrExt(self, operand, src_bits, dest_bits);
4072 }
4073 const dest_llvm_ty = try self.dg.llvmType(dest_ty);
4025 return self.builder.buildFPTrunc(operand, dest_llvm_ty, "");4074 return self.builder.buildFPTrunc(operand, dest_llvm_ty, "");
4026 }4075 }
40274076
...@@ -4031,8 +4080,15 @@ pub const FuncGen = struct {...@@ -4031,8 +4080,15 @@ pub const FuncGen = struct {
40314080
4032 const ty_op = self.air.instructions.items(.data)[inst].ty_op;4081 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
4033 const operand = try self.resolveInst(ty_op.operand);4082 const operand = try self.resolveInst(ty_op.operand);
4083 const operand_ty = self.air.typeOf(ty_op.operand);
4084 const dest_ty = self.air.typeOfIndex(inst);
4085 const target = self.dg.module.getTarget();
4086 const dest_bits = dest_ty.floatBits(target);
4087 const src_bits = operand_ty.floatBits(target);
4088 if (!backendSupportsF80(target) and (src_bits == 80 or dest_bits == 80)) {
4089 return softF80TruncOrExt(self, operand, src_bits, dest_bits);
4090 }
4034 const dest_llvm_ty = try self.dg.llvmType(self.air.typeOfIndex(inst));4091 const dest_llvm_ty = try self.dg.llvmType(self.air.typeOfIndex(inst));
4035
4036 return self.builder.buildFPExt(operand, dest_llvm_ty, "");4092 return self.builder.buildFPExt(operand, dest_llvm_ty, "");
4037 }4093 }
40384094
...@@ -5064,6 +5120,87 @@ pub const FuncGen = struct {...@@ -5064,6 +5120,87 @@ pub const FuncGen = struct {
5064 return null;5120 return null;
5065 }5121 }
50665122
5123 fn softF80TruncOrExt(
5124 self: *FuncGen,
5125 operand: *const llvm.Value,
5126 src_bits: u16,
5127 dest_bits: u16,
5128 ) !?*const llvm.Value {
5129 const target = self.dg.module.getTarget();
5130
5131 var param_llvm_ty: *const llvm.Type = self.context.intType(80);
5132 var ret_llvm_ty: *const llvm.Type = param_llvm_ty;
5133 var fn_name: [*:0]const u8 = undefined;
5134 var arg = operand;
5135 var final_cast: ?*const llvm.Type = null;
5136
5137 assert(src_bits == 80 or dest_bits == 80);
5138
5139 if (src_bits == 80) switch (dest_bits) {
5140 16 => {
5141 // See corresponding condition at definition of
5142 // __truncxfhf2 in compiler-rt.
5143 if (target.cpu.arch.isAARCH64()) {
5144 ret_llvm_ty = self.context.halfType();
5145 } else {
5146 ret_llvm_ty = self.context.intType(16);
5147 final_cast = self.context.halfType();
5148 }
5149 fn_name = "__truncxfhf2";
5150 },
5151 32 => {
5152 ret_llvm_ty = self.context.floatType();
5153 fn_name = "__truncxfsf2";
5154 },
5155 64 => {
5156 ret_llvm_ty = self.context.doubleType();
5157 fn_name = "__truncxfdf2";
5158 },
5159 80 => return operand,
5160 128 => {
5161 ret_llvm_ty = self.context.fp128Type();
5162 fn_name = "__extendxftf2";
5163 },
5164 else => unreachable,
5165 } else switch (src_bits) {
5166 16 => {
5167 // See corresponding condition at definition of
5168 // __extendhfxf2 in compiler-rt.
5169 param_llvm_ty = if (target.cpu.arch.isAARCH64())
5170 self.context.halfType()
5171 else
5172 self.context.intType(16);
5173 arg = self.builder.buildBitCast(arg, param_llvm_ty, "");
5174 fn_name = "__extendhfxf2";
5175 },
5176 32 => {
5177 param_llvm_ty = self.context.floatType();
5178 fn_name = "__extendsfxf2";
5179 },
5180 64 => {
5181 param_llvm_ty = self.context.doubleType();
5182 fn_name = "__extenddfxf2";
5183 },
5184 80 => return operand,
5185 128 => {
5186 param_llvm_ty = self.context.fp128Type();
5187 fn_name = "__trunctfxf2";
5188 },
5189 else => unreachable,
5190 }
5191
5192 const llvm_fn = self.dg.object.llvm_module.getNamedFunction(fn_name) orelse f: {
5193 const param_types = [_]*const llvm.Type{param_llvm_ty};
5194 const fn_type = llvm.functionType(ret_llvm_ty, &param_types, param_types.len, .False);
5195 break :f self.dg.object.llvm_module.addFunction(fn_name, fn_type);
5196 };
5197
5198 var args: [1]*const llvm.Value = .{arg};
5199 const result = self.builder.buildCall(llvm_fn, &args, args.len, .C, .Auto, "");
5200 const final_cast_llvm_ty = final_cast orelse return result;
5201 return self.builder.buildBitCast(result, final_cast_llvm_ty, "");
5202 }
5203
5067 fn getErrorNameTable(self: *FuncGen) !*const llvm.Value {5204 fn getErrorNameTable(self: *FuncGen) !*const llvm.Value {
5068 if (self.dg.object.error_name_table) |table| {5205 if (self.dg.object.error_name_table) |table| {
5069 return table;5206 return table;
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 pl_op = w.air.instructions.items(.data)[inst].pl_op;
364 const extra = w.air.extraData(Air.Bin, pl_op.payload).data;
365
366 try w.writeOperand(s, inst, 0, extra.lhs);
367 try s.writeAll(", ");
368 try w.writeOperand(s, inst, 1, extra.rhs);
369 try s.writeAll(", ");
370 try w.writeOperand(s, inst, 2, pl_op.operand);
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/stage1/target.cpp+10
...@@ -1004,6 +1004,9 @@ bool target_has_debug_info(const ZigTarget *target) {...@@ -1004,6 +1004,9 @@ bool target_has_debug_info(const ZigTarget *target) {
1004}1004}
10051005
1006bool target_long_double_is_f128(const ZigTarget *target) {1006bool target_long_double_is_f128(const ZigTarget *target) {
1007 if (target->abi == ZigLLVM_MSVC) {
1008 return false;
1009 }
1007 switch (target->arch) {1010 switch (target->arch) {
1008 case ZigLLVM_riscv64:1011 case ZigLLVM_riscv64:
1009 case ZigLLVM_aarch64:1012 case ZigLLVM_aarch64:
...@@ -1012,6 +1015,13 @@ bool target_long_double_is_f128(const ZigTarget *target) {...@@ -1012,6 +1015,13 @@ bool target_long_double_is_f128(const ZigTarget *target) {
1012 case ZigLLVM_systemz:1015 case ZigLLVM_systemz:
1013 case ZigLLVM_mips64:1016 case ZigLLVM_mips64:
1014 case ZigLLVM_mips64el:1017 case ZigLLVM_mips64el:
1018 case ZigLLVM_sparc:
1019 case ZigLLVM_sparcv9:
1020 case ZigLLVM_sparcel:
1021 case ZigLLVM_ppc:
1022 case ZigLLVM_ppcle:
1023 case ZigLLVM_ppc64:
1024 case ZigLLVM_ppc64le:
1015 return true;1025 return true;
10161026
1017 default:1027 default:
src/type.zig+55-67
...@@ -5436,33 +5436,36 @@ pub const CType = enum {...@@ -5436,33 +5436,36 @@ pub const CType = enum {
5436 switch (target.os.tag) {5436 switch (target.os.tag) {
5437 .freestanding, .other => switch (target.cpu.arch) {5437 .freestanding, .other => switch (target.cpu.arch) {
5438 .msp430 => switch (self) {5438 .msp430 => switch (self) {
5439 .short,5439 .short, .ushort, .int, .uint => return 16,
5440 .ushort,5440 .long, .ulong => return 32,
5441 .int,5441 .longlong, .ulonglong, .longdouble => return 64,
5442 .uint,
5443 => return 16,
5444 .long,
5445 .ulong,
5446 => return 32,
5447 .longlong,
5448 .ulonglong,
5449 => return 64,
5450 .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
5451 },5442 },
5452 else => switch (self) {5443 else => switch (self) {
5453 .short,5444 .short, .ushort => return 16,
5454 .ushort,5445 .int, .uint => return 32,
5455 => return 16,5446 .long, .ulong => return target.cpu.arch.ptrBitWidth(),
5456 .int,5447 .longlong, .ulonglong => return 64,
5457 .uint,5448 .longdouble => switch (target.cpu.arch) {
5458 => return 32,5449 .i386, .x86_64 => return 80,
5459 .long,5450
5460 .ulong,5451 .riscv64,
5461 => return target.cpu.arch.ptrBitWidth(),5452 .aarch64,
5462 .longlong,5453 .aarch64_be,
5463 .ulonglong,5454 .aarch64_32,
5464 => return 64,5455 .s390x,
5465 .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),5456 .mips64,
5457 .mips64el,
5458 .sparc,
5459 .sparcv9,
5460 .sparcel,
5461 .powerpc,
5462 .powerpcle,
5463 .powerpc64,
5464 .powerpc64le,
5465 => return 128,
5466
5467 else => return 64,
5468 },
5466 },5469 },
5467 },5470 },
54685471
...@@ -5477,19 +5480,13 @@ pub const CType = enum {...@@ -5477,19 +5480,13 @@ pub const CType = enum {
5477 .plan9,5480 .plan9,
5478 .solaris,5481 .solaris,
5479 => switch (self) {5482 => switch (self) {
5480 .short,5483 .short, .ushort => return 16,
5481 .ushort,5484 .int, .uint => return 32,
5482 => return 16,5485 .long, .ulong => return target.cpu.arch.ptrBitWidth(),
5483 .int,5486 .longlong, .ulonglong => return 64,
5484 .uint,
5485 => return 32,
5486 .long,
5487 .ulong,
5488 => return target.cpu.arch.ptrBitWidth(),
5489 .longlong,
5490 .ulonglong,
5491 => return 64,
5492 .longdouble => switch (target.cpu.arch) {5487 .longdouble => switch (target.cpu.arch) {
5488 .i386, .x86_64 => return 80,
5489
5493 .riscv64,5490 .riscv64,
5494 .aarch64,5491 .aarch64,
5495 .aarch64_be,5492 .aarch64_be,
...@@ -5497,40 +5494,33 @@ pub const CType = enum {...@@ -5497,40 +5494,33 @@ pub const CType = enum {
5497 .s390x,5494 .s390x,
5498 .mips64,5495 .mips64,
5499 .mips64el,5496 .mips64el,
5497 .sparc,
5498 .sparcv9,
5499 .sparcel,
5500 .powerpc,
5501 .powerpcle,
5502 .powerpc64,
5503 .powerpc64le,
5500 => return 128,5504 => return 128,
55015505
5502 else => return 80,5506 else => return 64,
5503 },5507 },
5504 },5508 },
55055509
5506 .windows, .uefi => switch (self) {5510 .windows, .uefi => switch (self) {
5507 .short,5511 .short, .ushort => return 16,
5508 .ushort,5512 .int, .uint, .long, .ulong => return 32,
5509 => return 16,5513 .longlong, .ulonglong, .longdouble => return 64,
5510 .int,5514 },
5511 .uint,5515
5512 .long,5516 .ios, .tvos, .watchos => switch (self) {
5513 .ulong,5517 .short, .ushort => return 16,
5514 => return 32,5518 .int, .uint => return 32,
5515 .longlong,5519 .long, .ulong, .longlong, .ulonglong => return 64,
5516 .ulonglong,5520 .longdouble => switch (target.cpu.arch) {
5517 => return 64,5521 .i386, .x86_64 => return 80,
5518 .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),5522 else => return 64,
5519 },5523 },
5520
5521 .ios => switch (self) {
5522 .short,
5523 .ushort,
5524 => return 16,
5525 .int,
5526 .uint,
5527 => return 32,
5528 .long,
5529 .ulong,
5530 .longlong,
5531 .ulonglong,
5532 => return 64,
5533 .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"),
5534 },5524 },
55355525
5536 .ananas,5526 .ananas,
...@@ -5549,8 +5539,6 @@ pub const CType = enum {...@@ -5549,8 +5539,6 @@ pub const CType = enum {
5549 .amdhsa,5539 .amdhsa,
5550 .ps4,5540 .ps4,
5551 .elfiamcu,5541 .elfiamcu,
5552 .tvos,
5553 .watchos,
5554 .mesa3d,5542 .mesa3d,
5555 .contiki,5543 .contiki,
5556 .amdpal,5544 .amdpal,
src/value.zig+47-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,49 @@ pub const Value = extern union {...@@ -4020,6 +4020,49 @@ pub const Value = extern union {
4020 }4020 }
4021 }4021 }
40224022
4023 pub fn mulAdd(
4024 float_type: Type,
4025 mulend1: Value,
4026 mulend2: Value,
4027 addend: Value,
4028 arena: Allocator,
4029 target: Target,
4030 ) Allocator.Error!Value {
4031 switch (float_type.floatBits(target)) {
4032 16 => {
4033 const m1 = mulend1.toFloat(f16);
4034 const m2 = mulend2.toFloat(f16);
4035 const a = addend.toFloat(f16);
4036 return Value.Tag.float_16.create(arena, @mulAdd(f16, m1, m2, a));
4037 },
4038 32 => {
4039 const m1 = mulend1.toFloat(f32);
4040 const m2 = mulend2.toFloat(f32);
4041 const a = addend.toFloat(f32);
4042 return Value.Tag.float_32.create(arena, @mulAdd(f32, m1, m2, a));
4043 },
4044 64 => {
4045 const m1 = mulend1.toFloat(f64);
4046 const m2 = mulend2.toFloat(f64);
4047 const a = addend.toFloat(f64);
4048 return Value.Tag.float_64.create(arena, @mulAdd(f64, m1, m2, a));
4049 },
4050 80 => {
4051 const m1 = mulend1.toFloat(f80);
4052 const m2 = mulend2.toFloat(f80);
4053 const a = addend.toFloat(f80);
4054 return Value.Tag.float_80.create(arena, @mulAdd(f80, m1, m2, a));
4055 },
4056 128 => {
4057 const m1 = mulend1.toFloat(f128);
4058 const m2 = mulend2.toFloat(f128);
4059 const a = addend.toFloat(f128);
4060 return Value.Tag.float_128.create(arena, @mulAdd(f128, m1, m2, a));
4061 },
4062 else => unreachable,
4063 }
4064 }
4065
4023 /// This type is not copyable since it may contain pointers to its inner data.4066 /// This type is not copyable since it may contain pointers to its inner data.
4024 pub const Payload = struct {4067 pub const Payload = struct {
4025 tag: Tag,4068 tag: Tag,
test/behavior/muladd.zig+19-5
...@@ -2,7 +2,11 @@ const builtin = @import("builtin");...@@ -2,7 +2,11 @@ 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_c) return error.SkipZigTest; // TODO
6 if (builtin.zig_backend == .stage2_wasm) 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();
...@@ -47,18 +51,28 @@ fn testMulAdd80() !void {...@@ -47,18 +51,28 @@ fn testMulAdd80() !void {
47}51}
4852
49test "@mulAdd f128" {53test "@mulAdd f128" {
50 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO54 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
55 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
56 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
57 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
58 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
5159
52 if (builtin.os.tag == .macos and builtin.cpu.arch == .aarch64) {60 if (builtin.os.tag == .macos and builtin.cpu.arch == .aarch64) {
53 // https://github.com/ziglang/zig/issues/990061 // https://github.com/ziglang/zig/issues/9900
54 return error.SkipZigTest;62 return error.SkipZigTest;
55 }63 }
5664
57 comptime try testMullAdd128();65 if (builtin.zig_backend == .stage1 and
58 try testMullAdd128();66 builtin.cpu.arch == .i386 and builtin.os.tag == .linux)
67 {
68 return error.SkipZigTest;
69 }
70
71 comptime try testMulAdd128();
72 try testMulAdd128();
59}73}
6074
61fn testMullAdd128() !void {75fn testMulAdd128() !void {
62 var a: f16 = 5.5;76 var a: f16 = 5.5;
63 var b: f128 = 2.5;77 var b: f128 = 2.5;
64 var c: f128 = 6.25;78 var c: f128 = 6.25;