| author | |
| committer | |
| log | 6637335981f7179b449fced78cfd4052b1618051 |
| tree | bb5bd8eaaf3b1534f1087630d5c30dbee42600ed |
| parent | c7e4c711fc5795e66f974316611922a0b962eb99 |
13 files changed, 242 insertions(+), 26 deletions(-)
src/Air.zig+11| ... | ... | @@ -579,6 +579,10 @@ pub const Inst = struct { |
| 579 | 579 | /// Uses the `prefetch` field. |
| 580 | 580 | prefetch, |
| 581 | 581 | |
| 582 | /// Computes `(a * b) + c`, but only rounds once. | |
| 583 | /// Uses the `ty_pl` field. | |
| 584 | mul_add, | |
| 585 | ||
| 582 | 586 | /// Implements @fieldParentPtr builtin. |
| 583 | 587 | /// Uses the `ty_pl` field. |
| 584 | 588 | field_parent_ptr, |
| ... | ... | @@ -724,6 +728,12 @@ pub const Bin = struct { |
| 724 | 728 | rhs: Inst.Ref, |
| 725 | 729 | }; |
| 726 | 730 | |
| 731 | pub const MulAdd = struct { | |
| 732 | mulend1: Inst.Ref, | |
| 733 | mulend2: Inst.Ref, | |
| 734 | addend: Inst.Ref, | |
| 735 | }; | |
| 736 | ||
| 727 | 737 | pub const FieldParentPtr = struct { |
| 728 | 738 | field_ptr: Inst.Ref, |
| 729 | 739 | field_index: u32, |
| ... | ... | @@ -889,6 +899,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 889 | 899 | .aggregate_init, |
| 890 | 900 | .union_init, |
| 891 | 901 | .field_parent_ptr, |
| 902 | .mul_add, | |
| 892 | 903 | => return air.getRefType(datas[inst].ty_pl.ty), |
| 893 | 904 | |
| 894 | 905 | .not, |
src/Liveness.zig+4| ... | ... | @@ -464,6 +464,10 @@ fn analyzeInst( |
| 464 | 464 | const extra = a.air.extraData(Air.Cmpxchg, inst_datas[inst].ty_pl.payload).data; |
| 465 | 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 | 471 | .atomic_load => { |
| 468 | 472 | const ptr = inst_datas[inst].atomic_load.ptr; |
| 469 | 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 | 13518 | |
| 13519 | 13519 | fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 13520 | 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 | 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 | } |
| 13524 | 13600 | |
| 13525 | 13601 | fn 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 | 632 | .aggregate_init => try self.airAggregateInit(inst), |
| 633 | 633 | .union_init => try self.airUnionInit(inst), |
| 634 | 634 | .prefetch => try self.airPrefetch(inst), |
| 635 | .mul_add => try self.airMulAdd(inst), | |
| 635 | 636 | |
| 636 | 637 | .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), |
| 637 | 638 | .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), |
| ... | ... | @@ -3652,6 +3653,11 @@ fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void { |
| 3652 | 3653 | return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none }); |
| 3653 | 3654 | } |
| 3654 | 3655 | |
| 3656 | fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { | |
| 3657 | _ = inst; | |
| 3658 | return self.fail("TODO implement airMulAdd for aarch64", .{}); | |
| 3659 | } | |
| 3660 | ||
| 3655 | 3661 | fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { |
| 3656 | 3662 | // First section of indexes correspond to a set number of constant values. |
| 3657 | 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 | 628 | .aggregate_init => try self.airAggregateInit(inst), |
| 629 | 629 | .union_init => try self.airUnionInit(inst), |
| 630 | 630 | .prefetch => try self.airPrefetch(inst), |
| 631 | .mul_add => try self.airMulAdd(inst), | |
| 631 | 632 | |
| 632 | 633 | .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), |
| 633 | 634 | .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), |
| ... | ... | @@ -4086,6 +4087,11 @@ fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void { |
| 4086 | 4087 | return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none }); |
| 4087 | 4088 | } |
| 4088 | 4089 | |
| 4090 | fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { | |
| 4091 | _ = inst; | |
| 4092 | return self.fail("TODO implement airMulAdd for arm", .{}); | |
| 4093 | } | |
| 4094 | ||
| 4089 | 4095 | fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { |
| 4090 | 4096 | // First section of indexes correspond to a set number of constant values. |
| 4091 | 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 | 600 | .aggregate_init => try self.airAggregateInit(inst), |
| 601 | 601 | .union_init => try self.airUnionInit(inst), |
| 602 | 602 | .prefetch => try self.airPrefetch(inst), |
| 603 | .mul_add => try self.airMulAdd(inst), | |
| 603 | 604 | |
| 604 | 605 | .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), |
| 605 | 606 | .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), |
| ... | ... | @@ -2203,6 +2204,11 @@ fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void { |
| 2203 | 2204 | return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none }); |
| 2204 | 2205 | } |
| 2205 | 2206 | |
| 2207 | fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { | |
| 2208 | _ = inst; | |
| 2209 | return self.fail("TODO implement airMulAdd for riscv64", .{}); | |
| 2210 | } | |
| 2211 | ||
| 2206 | 2212 | fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { |
| 2207 | 2213 | // First section of indexes correspond to a set number of constant values. |
| 2208 | 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 | 1333 | .error_name, |
| 1334 | 1334 | .errunion_payload_ptr_set, |
| 1335 | 1335 | .field_parent_ptr, |
| 1336 | .mul_add, | |
| 1336 | 1337 | |
| 1337 | 1338 | // For these 4, probably best to wait until https://github.com/ziglang/zig/issues/10248 |
| 1338 | 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 | 717 | .aggregate_init => try self.airAggregateInit(inst), |
| 718 | 718 | .union_init => try self.airUnionInit(inst), |
| 719 | 719 | .prefetch => try self.airPrefetch(inst), |
| 720 | .mul_add => try self.airMulAdd(inst), | |
| 720 | 721 | |
| 721 | 722 | .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), |
| 722 | 723 | .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), |
| ... | ... | @@ -5559,6 +5560,11 @@ fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void { |
| 5559 | 5560 | return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none }); |
| 5560 | 5561 | } |
| 5561 | 5562 | |
| 5563 | fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { | |
| 5564 | _ = inst; | |
| 5565 | return self.fail("TODO implement airMulAdd for x86_64", .{}); | |
| 5566 | } | |
| 5567 | ||
| 5562 | 5568 | fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { |
| 5563 | 5569 | // First section of indexes correspond to a set number of constant values. |
| 5564 | 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 | 1635 | .trunc_float, |
| 1636 | 1636 | => |tag| return f.fail("TODO: C backend: implement unary op for tag '{s}'", .{@tagName(tag)}), |
| 1637 | 1637 | |
| 1638 | .mul_add => return f.fail("TODO: C backend: implement @mulAdd", .{}), | |
| 1639 | ||
| 1638 | 1640 | .add_with_overflow => try airAddWithOverflow(f, inst), |
| 1639 | 1641 | .sub_with_overflow => try airSubWithOverflow(f, inst), |
| 1640 | 1642 | .mul_with_overflow => try airMulWithOverflow(f, inst), |
src/codegen/llvm.zig+41| ... | ... | @@ -2194,6 +2194,7 @@ pub const FuncGen = struct { |
| 2194 | 2194 | .sub_with_overflow => try self.airOverflow(inst, "llvm.ssub.with.overflow", "llvm.usub.with.overflow"), |
| 2195 | 2195 | .mul_with_overflow => try self.airOverflow(inst, "llvm.smul.with.overflow", "llvm.umul.with.overflow"), |
| 2196 | 2196 | .shl_with_overflow => try self.airShlWithOverflow(inst), |
| 2197 | .mul_add => try self.airMulAdd(inst), | |
| 2197 | 2198 | |
| 2198 | 2199 | .bit_and, .bool_and => try self.airAnd(inst), |
| 2199 | 2200 | .bit_or, .bool_or => try self.airOr(inst), |
| ... | ... | @@ -3842,6 +3843,46 @@ pub const FuncGen = struct { |
| 3842 | 3843 | return overflow_bit; |
| 3843 | 3844 | } |
| 3844 | 3845 | |
| 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 | 3886 | fn airShlWithOverflow(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 3846 | 3887 | if (self.liveness.isUnused(inst)) |
| 3847 | 3888 | return null; |
src/print_air.zig+12| ... | ... | @@ -252,6 +252,7 @@ const Writer = struct { |
| 252 | 252 | .field_parent_ptr => try w.writeFieldParentPtr(s, inst), |
| 253 | 253 | .wasm_memory_size => try w.writeWasmMemorySize(s, inst), |
| 254 | 254 | .wasm_memory_grow => try w.writeWasmMemoryGrow(s, inst), |
| 255 | .mul_add => try w.writeMulAdd(s, inst), | |
| 255 | 256 | |
| 256 | 257 | .add_with_overflow, |
| 257 | 258 | .sub_with_overflow, |
| ... | ... | @@ -358,6 +359,17 @@ const Writer = struct { |
| 358 | 359 | }); |
| 359 | 360 | } |
| 360 | 361 | |
| 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 | 373 | fn writeFence(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { |
| 362 | 374 | const atomic_order = w.air.instructions.items(.data)[inst].fence; |
| 363 | 375 |
src/value.zig+40-4| ... | ... | @@ -2931,7 +2931,7 @@ pub const Value = extern union { |
| 2931 | 2931 | return fromBigInt(arena, result_bigint.toConst()); |
| 2932 | 2932 | } |
| 2933 | 2933 | |
| 2934 | /// operands must be integers; handles undefined. | |
| 2934 | /// operands must be integers; handles undefined. | |
| 2935 | 2935 | pub fn bitwiseAnd(lhs: Value, rhs: Value, arena: Allocator) !Value { |
| 2936 | 2936 | if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); |
| 2937 | 2937 | |
| ... | ... | @@ -2951,7 +2951,7 @@ pub const Value = extern union { |
| 2951 | 2951 | return fromBigInt(arena, result_bigint.toConst()); |
| 2952 | 2952 | } |
| 2953 | 2953 | |
| 2954 | /// operands must be integers; handles undefined. | |
| 2954 | /// operands must be integers; handles undefined. | |
| 2955 | 2955 | pub fn bitwiseNand(lhs: Value, rhs: Value, ty: Type, arena: Allocator, target: Target) !Value { |
| 2956 | 2956 | if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); |
| 2957 | 2957 | |
| ... | ... | @@ -2965,7 +2965,7 @@ pub const Value = extern union { |
| 2965 | 2965 | return bitwiseXor(anded, all_ones, arena); |
| 2966 | 2966 | } |
| 2967 | 2967 | |
| 2968 | /// operands must be integers; handles undefined. | |
| 2968 | /// operands must be integers; handles undefined. | |
| 2969 | 2969 | pub fn bitwiseOr(lhs: Value, rhs: Value, arena: Allocator) !Value { |
| 2970 | 2970 | if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); |
| 2971 | 2971 | |
| ... | ... | @@ -2984,7 +2984,7 @@ pub const Value = extern union { |
| 2984 | 2984 | return fromBigInt(arena, result_bigint.toConst()); |
| 2985 | 2985 | } |
| 2986 | 2986 | |
| 2987 | /// operands must be integers; handles undefined. | |
| 2987 | /// operands must be integers; handles undefined. | |
| 2988 | 2988 | pub fn bitwiseXor(lhs: Value, rhs: Value, arena: Allocator) !Value { |
| 2989 | 2989 | if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); |
| 2990 | 2990 | |
| ... | ... | @@ -4020,6 +4020,42 @@ pub const Value = extern union { |
| 4020 | 4020 | } |
| 4021 | 4021 | } |
| 4022 | 4022 | |
| 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 | 4059 | /// This type is not copyable since it may contain pointers to its inner data. |
| 4024 | 4060 | pub const Payload = struct { |
| 4025 | 4061 | tag: Tag, |
test/behavior/muladd.zig+30-21| ... | ... | @@ -2,29 +2,33 @@ const builtin = @import("builtin"); |
| 2 | 2 | const expect = @import("std").testing.expect; |
| 3 | 3 | |
| 4 | 4 | test "@mulAdd" { |
| 5 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | |
| 5 | 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 | |
| 6 | 10 | |
| 7 | 11 | comptime try testMulAdd(); |
| 8 | 12 | try testMulAdd(); |
| 9 | 13 | } |
| 10 | 14 | |
| 11 | 15 | fn testMulAdd() !void { |
| 12 | { | |
| 13 | var a: f16 = 5.5; | |
| 14 | var b: f16 = 2.5; | |
| 15 | var c: f16 = 6.25; | |
| 16 | if (builtin.zig_backend == .stage1) { | |
| 17 | const a: f16 = 5.5; | |
| 18 | const b: f16 = 2.5; | |
| 19 | const c: f16 = 6.25; | |
| 16 | 20 | try expect(@mulAdd(f16, a, b, c) == 20); |
| 17 | 21 | } |
| 18 | 22 | { |
| 19 | var a: f32 = 5.5; | |
| 20 | var b: f32 = 2.5; | |
| 21 | var c: f32 = 6.25; | |
| 23 | const a: f32 = 5.5; | |
| 24 | const b: f32 = 2.5; | |
| 25 | const c: f32 = 6.25; | |
| 22 | 26 | try expect(@mulAdd(f32, a, b, c) == 20); |
| 23 | 27 | } |
| 24 | 28 | { |
| 25 | var a: f64 = 5.5; | |
| 26 | var b: f64 = 2.5; | |
| 27 | var c: f64 = 6.25; | |
| 29 | const a: f64 = 5.5; | |
| 30 | const b: f64 = 2.5; | |
| 31 | const c: f64 = 6.25; | |
| 28 | 32 | try expect(@mulAdd(f64, a, b, c) == 20); |
| 29 | 33 | } |
| 30 | 34 | } |
| ... | ... | @@ -35,7 +39,9 @@ test "@mulAdd f80" { |
| 35 | 39 | return error.SkipZigTest; |
| 36 | 40 | } |
| 37 | 41 | |
| 38 | comptime try testMulAdd80(); | |
| 42 | // TODO: missing f80 implementation of FMA in `std.math.fma` or compiler-rt | |
| 43 | // comptime try testMulAdd80(); | |
| 44 | ||
| 39 | 45 | try testMulAdd80(); |
| 40 | 46 | } |
| 41 | 47 | |
| ... | ... | @@ -43,24 +49,27 @@ fn testMulAdd80() !void { |
| 43 | 49 | var a: f16 = 5.5; |
| 44 | 50 | var b: f80 = 2.5; |
| 45 | 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 | } |
| 48 | 54 | |
| 49 | 55 | test "@mulAdd f128" { |
| 50 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | |
| 51 | ||
| 56 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | |
| 57 | 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 | 61 | if (builtin.os.tag == .macos and builtin.cpu.arch == .aarch64) { |
| 53 | 62 | // https://github.com/ziglang/zig/issues/9900 |
| 54 | 63 | return error.SkipZigTest; |
| 55 | 64 | } |
| 56 | 65 | |
| 57 | comptime try testMullAdd128(); | |
| 58 | try testMullAdd128(); | |
| 66 | comptime try testMulAdd128(); | |
| 67 | try testMulAdd128(); | |
| 59 | 68 | } |
| 60 | 69 | |
| 61 | fn testMullAdd128() !void { | |
| 62 | var a: f16 = 5.5; | |
| 63 | var b: f128 = 2.5; | |
| 64 | var c: f128 = 6.25; | |
| 70 | fn testMulAdd128() !void { | |
| 71 | const a: f16 = 5.5; | |
| 72 | const b: f128 = 2.5; | |
| 73 | const c: f128 = 6.25; | |
| 65 | 74 | try expect(@mulAdd(f128, a, b, c) == 20); |
| 66 | 75 | } |