| ... | ... | @@ -3983,7 +3983,7 @@ pub const FuncGen = struct { |
| 3983 | 3983 | ); |
| 3984 | 3984 | return phi_node; |
| 3985 | 3985 | }, |
| 3986 | | .Float => return self.buildFloatCmp(op, operand_ty, &.{ lhs, rhs }), |
| 3986 | .Float => return self.buildFloatCmp(op, operand_ty, .{ lhs, rhs }), |
| 3987 | 3987 | else => unreachable, |
| 3988 | 3988 | }; |
| 3989 | 3989 | const is_signed = int_ty.isSignedInt(); |
| ... | ... | @@ -5211,7 +5211,7 @@ pub const FuncGen = struct { |
| 5211 | 5211 | const inst_ty = self.air.typeOfIndex(inst); |
| 5212 | 5212 | const scalar_ty = inst_ty.scalarType(); |
| 5213 | 5213 | |
| 5214 | | if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.add, inst_ty, &.{ lhs, rhs }); |
| 5214 | if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.add, inst_ty, 2, .{ lhs, rhs }); |
| 5215 | 5215 | if (scalar_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, ""); |
| 5216 | 5216 | return self.builder.buildNUWAdd(lhs, rhs, ""); |
| 5217 | 5217 | } |
| ... | ... | @@ -5250,7 +5250,7 @@ pub const FuncGen = struct { |
| 5250 | 5250 | const inst_ty = self.air.typeOfIndex(inst); |
| 5251 | 5251 | const scalar_ty = inst_ty.scalarType(); |
| 5252 | 5252 | |
| 5253 | | if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.sub, inst_ty, &.{ lhs, rhs }); |
| 5253 | if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.sub, inst_ty, 2, .{ lhs, rhs }); |
| 5254 | 5254 | if (scalar_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, ""); |
| 5255 | 5255 | return self.builder.buildNUWSub(lhs, rhs, ""); |
| 5256 | 5256 | } |
| ... | ... | @@ -5288,7 +5288,7 @@ pub const FuncGen = struct { |
| 5288 | 5288 | const inst_ty = self.air.typeOfIndex(inst); |
| 5289 | 5289 | const scalar_ty = inst_ty.scalarType(); |
| 5290 | 5290 | |
| 5291 | | if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.mul, inst_ty, &.{ lhs, rhs }); |
| 5291 | if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.mul, inst_ty, 2, .{ lhs, rhs }); |
| 5292 | 5292 | if (scalar_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, ""); |
| 5293 | 5293 | return self.builder.buildNUWMul(lhs, rhs, ""); |
| 5294 | 5294 | } |
| ... | ... | @@ -5325,7 +5325,7 @@ pub const FuncGen = struct { |
| 5325 | 5325 | const rhs = try self.resolveInst(bin_op.rhs); |
| 5326 | 5326 | const inst_ty = self.air.typeOfIndex(inst); |
| 5327 | 5327 | |
| 5328 | | return self.buildFloatOp(.div, inst_ty, &.{ lhs, rhs }); |
| 5328 | return self.buildFloatOp(.div, inst_ty, 2, .{ lhs, rhs }); |
| 5329 | 5329 | } |
| 5330 | 5330 | |
| 5331 | 5331 | fn airDivTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| ... | ... | @@ -5338,8 +5338,8 @@ pub const FuncGen = struct { |
| 5338 | 5338 | const scalar_ty = inst_ty.scalarType(); |
| 5339 | 5339 | |
| 5340 | 5340 | if (scalar_ty.isRuntimeFloat()) { |
| 5341 | | const result = try self.buildFloatOp(.div, inst_ty, &.{ lhs, rhs }); |
| 5342 | | return self.buildFloatOp(.trunc, inst_ty, &.{result}); |
| 5341 | const result = try self.buildFloatOp(.div, inst_ty, 2, .{ lhs, rhs }); |
| 5342 | return self.buildFloatOp(.trunc, inst_ty, 1, .{result}); |
| 5343 | 5343 | } |
| 5344 | 5344 | if (scalar_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, ""); |
| 5345 | 5345 | return self.builder.buildUDiv(lhs, rhs, ""); |
| ... | ... | @@ -5355,8 +5355,8 @@ pub const FuncGen = struct { |
| 5355 | 5355 | const scalar_ty = inst_ty.scalarType(); |
| 5356 | 5356 | |
| 5357 | 5357 | if (scalar_ty.isRuntimeFloat()) { |
| 5358 | | const result = try self.buildFloatOp(.div, inst_ty, &.{ lhs, rhs }); |
| 5359 | | return self.buildFloatOp(.floor, inst_ty, &.{result}); |
| 5358 | const result = try self.buildFloatOp(.div, inst_ty, 2, .{ lhs, rhs }); |
| 5359 | return self.buildFloatOp(.floor, inst_ty, 1, .{result}); |
| 5360 | 5360 | } |
| 5361 | 5361 | if (scalar_ty.isSignedInt()) { |
| 5362 | 5362 | // const d = @divTrunc(a, b); |
| ... | ... | @@ -5386,7 +5386,7 @@ pub const FuncGen = struct { |
| 5386 | 5386 | const inst_ty = self.air.typeOfIndex(inst); |
| 5387 | 5387 | const scalar_ty = inst_ty.scalarType(); |
| 5388 | 5388 | |
| 5389 | | if (scalar_ty.isRuntimeFloat()) return self.buildFloatOp(.div, inst_ty, &.{ lhs, rhs }); |
| 5389 | if (scalar_ty.isRuntimeFloat()) return self.buildFloatOp(.div, inst_ty, 2, .{ lhs, rhs }); |
| 5390 | 5390 | if (scalar_ty.isSignedInt()) return self.builder.buildExactSDiv(lhs, rhs, ""); |
| 5391 | 5391 | return self.builder.buildExactUDiv(lhs, rhs, ""); |
| 5392 | 5392 | } |
| ... | ... | @@ -5400,7 +5400,7 @@ pub const FuncGen = struct { |
| 5400 | 5400 | const inst_ty = self.air.typeOfIndex(inst); |
| 5401 | 5401 | const scalar_ty = inst_ty.scalarType(); |
| 5402 | 5402 | |
| 5403 | | if (scalar_ty.isRuntimeFloat()) return self.buildFloatOp(.rem, inst_ty, &.{ lhs, rhs }); |
| 5403 | if (scalar_ty.isRuntimeFloat()) return self.buildFloatOp(.fmod, inst_ty, 2, .{ lhs, rhs }); |
| 5404 | 5404 | if (scalar_ty.isSignedInt()) return self.builder.buildSRem(lhs, rhs, ""); |
| 5405 | 5405 | return self.builder.buildURem(lhs, rhs, ""); |
| 5406 | 5406 | } |
| ... | ... | @@ -5416,11 +5416,11 @@ pub const FuncGen = struct { |
| 5416 | 5416 | const scalar_ty = inst_ty.scalarType(); |
| 5417 | 5417 | |
| 5418 | 5418 | if (scalar_ty.isRuntimeFloat()) { |
| 5419 | | const a = try self.buildFloatOp(.rem, inst_ty, &.{ lhs, rhs }); |
| 5420 | | const b = try self.buildFloatOp(.add, inst_ty, &.{ a, rhs }); |
| 5421 | | const c = try self.buildFloatOp(.rem, inst_ty, &.{ b, rhs }); |
| 5419 | const a = try self.buildFloatOp(.fmod, inst_ty, 2, .{ lhs, rhs }); |
| 5420 | const b = try self.buildFloatOp(.add, inst_ty, 2, .{ a, rhs }); |
| 5421 | const c = try self.buildFloatOp(.fmod, inst_ty, 2, .{ b, rhs }); |
| 5422 | 5422 | const zero = inst_llvm_ty.constNull(); |
| 5423 | | const ltz = try self.buildFloatCmp(.lt, inst_ty, &.{ lhs, zero }); |
| 5423 | const ltz = try self.buildFloatCmp(.lt, inst_ty, .{ lhs, zero }); |
| 5424 | 5424 | return self.builder.buildSelect(ltz, c, a, ""); |
| 5425 | 5425 | } |
| 5426 | 5426 | if (scalar_ty.isSignedInt()) { |
| ... | ... | @@ -5508,18 +5508,18 @@ pub const FuncGen = struct { |
| 5508 | 5508 | ) !*const llvm.Value { |
| 5509 | 5509 | const args_len = @intCast(c_uint, args_vectors.len); |
| 5510 | 5510 | const llvm_i32 = self.context.intType(32); |
| 5511 | | assert(args_len <= 8); |
| 5511 | assert(args_len <= 3); |
| 5512 | 5512 | |
| 5513 | 5513 | var i: usize = 0; |
| 5514 | 5514 | var result = result_vector; |
| 5515 | 5515 | while (i < vector_len) : (i += 1) { |
| 5516 | 5516 | const index_i32 = llvm_i32.constInt(i, .False); |
| 5517 | 5517 | |
| 5518 | | var args: [8]*const llvm.Value = undefined; |
| 5518 | var args: [3]*const llvm.Value = undefined; |
| 5519 | 5519 | for (args_vectors) |arg_vector, k| { |
| 5520 | 5520 | args[k] = self.builder.buildExtractElement(arg_vector, index_i32, ""); |
| 5521 | 5521 | } |
| 5522 | | const result_elem = self.builder.buildCall(llvm_fn, args[0..], args_len, .C, .Auto, ""); |
| 5522 | const result_elem = self.builder.buildCall(llvm_fn, &args, args_len, .C, .Auto, ""); |
| 5523 | 5523 | result = self.builder.buildInsertElement(result, result_elem, index_i32, ""); |
| 5524 | 5524 | } |
| 5525 | 5525 | return result; |
| ... | ... | @@ -5542,20 +5542,27 @@ pub const FuncGen = struct { |
| 5542 | 5542 | }; |
| 5543 | 5543 | } |
| 5544 | 5544 | |
| 5545 | | fn getMathHTypeAbbrev(ty: Type) []const u8 { |
| 5546 | | return switch (ty.tag()) { |
| 5547 | | .f16 => "h", // Non-standard |
| 5548 | | .f32 => "s", |
| 5549 | | .f64 => "", |
| 5550 | | .f80 => "x", // Non-standard |
| 5551 | | .c_longdouble => "l", |
| 5552 | | .f128 => "q", // Non-standard (mimics convention in GCC libquadmath) |
| 5545 | fn libcFloatPrefix(float_bits: u16) []const u8 { |
| 5546 | return switch (float_bits) { |
| 5547 | 16, 80 => "__", |
| 5548 | 32, 64, 128 => "", |
| 5553 | 5549 | else => unreachable, |
| 5554 | 5550 | }; |
| 5555 | 5551 | } |
| 5556 | 5552 | |
| 5557 | | fn getCompilerRtTypeAbbrev(ty: Type, target: std.Target) []const u8 { |
| 5558 | | return switch (ty.floatBits(target)) { |
| 5553 | fn libcFloatSuffix(float_bits: u16) []const u8 { |
| 5554 | return switch (float_bits) { |
| 5555 | 16 => "h", // Non-standard |
| 5556 | 32 => "s", |
| 5557 | 64 => "", |
| 5558 | 80 => "x", // Non-standard |
| 5559 | 128 => "q", // Non-standard (mimics convention in GCC libquadmath) |
| 5560 | else => unreachable, |
| 5561 | }; |
| 5562 | } |
| 5563 | |
| 5564 | fn compilerRtFloatAbbrev(float_bits: u16) []const u8 { |
| 5565 | return switch (float_bits) { |
| 5559 | 5566 | 16 => "h", |
| 5560 | 5567 | 32 => "s", |
| 5561 | 5568 | 64 => "d", |
| ... | ... | @@ -5571,20 +5578,13 @@ pub const FuncGen = struct { |
| 5571 | 5578 | self: *FuncGen, |
| 5572 | 5579 | pred: math.CompareOperator, |
| 5573 | 5580 | ty: Type, |
| 5574 | | params: []const *const llvm.Value, |
| 5581 | params: [2]*const llvm.Value, |
| 5575 | 5582 | ) !*const llvm.Value { |
| 5576 | 5583 | const target = self.dg.module.getTarget(); |
| 5577 | 5584 | const scalar_ty = ty.scalarType(); |
| 5578 | 5585 | const scalar_llvm_ty = try self.dg.llvmType(scalar_ty); |
| 5579 | 5586 | |
| 5580 | | // LLVM does not support all floating point comparisons for all targets, so we |
| 5581 | | // may need to manually generate a libc call |
| 5582 | | const intrinsics_allowed = switch (scalar_ty.tag()) { |
| 5583 | | .f80 => target.longDoubleIs(f80) and backendSupportsF80(target), |
| 5584 | | .f128 => target.longDoubleIs(f128), |
| 5585 | | else => true, |
| 5586 | | }; |
| 5587 | | if (intrinsics_allowed) { |
| 5587 | if (intrinsicsAllowed(scalar_ty, target)) { |
| 5588 | 5588 | const llvm_predicate: llvm.RealPredicate = switch (pred) { |
| 5589 | 5589 | .eq => .OEQ, |
| 5590 | 5590 | .neq => .UNE, |
| ... | ... | @@ -5596,7 +5596,8 @@ pub const FuncGen = struct { |
| 5596 | 5596 | return self.builder.buildFCmp(llvm_predicate, params[0], params[1], ""); |
| 5597 | 5597 | } |
| 5598 | 5598 | |
| 5599 | | const compiler_rt_type_abbrev = getCompilerRtTypeAbbrev(scalar_ty, target); |
| 5599 | const float_bits = scalar_ty.floatBits(target); |
| 5600 | const compiler_rt_float_abbrev = compilerRtFloatAbbrev(float_bits); |
| 5600 | 5601 | var fn_name_buf: [64]u8 = undefined; |
| 5601 | 5602 | const fn_base_name = switch (pred) { |
| 5602 | 5603 | .neq => "ne", |
| ... | ... | @@ -5606,9 +5607,10 @@ pub const FuncGen = struct { |
| 5606 | 5607 | .gt => "gt", |
| 5607 | 5608 | .gte => "ge", |
| 5608 | 5609 | }; |
| 5609 | | const fn_name = std.fmt.bufPrintZ(&fn_name_buf, "__{s}{s}f2", .{ fn_base_name, compiler_rt_type_abbrev }) catch unreachable; |
| 5610 | const fn_name = std.fmt.bufPrintZ(&fn_name_buf, "__{s}{s}f2", .{ |
| 5611 | fn_base_name, compiler_rt_float_abbrev, |
| 5612 | }) catch unreachable; |
| 5610 | 5613 | |
| 5611 | | assert(params.len == 2); |
| 5612 | 5614 | const param_types = [2]*const llvm.Type{ scalar_llvm_ty, scalar_llvm_ty }; |
| 5613 | 5615 | const llvm_i32 = self.context.intType(32); |
| 5614 | 5616 | const libc_fn = self.getLibcFunction(fn_name, param_types[0..], llvm_i32); |
| ... | ... | @@ -5628,110 +5630,119 @@ pub const FuncGen = struct { |
| 5628 | 5630 | const vector_result_ty = llvm_i32.vectorType(vec_len); |
| 5629 | 5631 | |
| 5630 | 5632 | var result = vector_result_ty.getUndef(); |
| 5631 | | result = try self.buildElementwiseCall(libc_fn, params[0..], result, vec_len); |
| 5633 | result = try self.buildElementwiseCall(libc_fn, &params, result, vec_len); |
| 5632 | 5634 | |
| 5633 | | const zero_vector = self.builder.buildVectorSplat(zero, vec_len, ""); |
| 5635 | const zero_vector = self.builder.buildVectorSplat(vec_len, zero, ""); |
| 5634 | 5636 | return self.builder.buildICmp(int_pred, result, zero_vector, ""); |
| 5635 | 5637 | } |
| 5636 | 5638 | |
| 5637 | | const result = self.builder.buildCall(libc_fn, params.ptr, 2, .C, .Auto, ""); |
| 5639 | const result = self.builder.buildCall(libc_fn, &params, params.len, .C, .Auto, ""); |
| 5638 | 5640 | return self.builder.buildICmp(int_pred, result, zero, ""); |
| 5639 | 5641 | } |
| 5640 | 5642 | |
| 5643 | const FloatOp = enum { |
| 5644 | add, |
| 5645 | ceil, |
| 5646 | cos, |
| 5647 | div, |
| 5648 | exp, |
| 5649 | exp2, |
| 5650 | fabs, |
| 5651 | floor, |
| 5652 | fma, |
| 5653 | log, |
| 5654 | log10, |
| 5655 | log2, |
| 5656 | fmax, |
| 5657 | fmin, |
| 5658 | mul, |
| 5659 | fmod, |
| 5660 | round, |
| 5661 | sin, |
| 5662 | sqrt, |
| 5663 | sub, |
| 5664 | trunc, |
| 5665 | }; |
| 5666 | |
| 5667 | const FloatOpStrat = union(enum) { |
| 5668 | intrinsic: []const u8, |
| 5669 | libc: [:0]const u8, |
| 5670 | }; |
| 5671 | |
| 5641 | 5672 | /// Creates a floating point operation (add, sub, fma, sqrt, exp, etc.) |
| 5642 | 5673 | /// by lowering to the appropriate hardware instruction or softfloat |
| 5643 | 5674 | /// routine for the target |
| 5644 | 5675 | fn buildFloatOp( |
| 5645 | 5676 | self: *FuncGen, |
| 5646 | | comptime op: @TypeOf(.EnumLiteral), |
| 5677 | comptime op: FloatOp, |
| 5647 | 5678 | ty: Type, |
| 5648 | | params: []const *const llvm.Value, |
| 5679 | comptime params_len: usize, |
| 5680 | params: [params_len]*const llvm.Value, |
| 5649 | 5681 | ) !*const llvm.Value { |
| 5650 | 5682 | const target = self.dg.module.getTarget(); |
| 5651 | 5683 | const scalar_ty = ty.scalarType(); |
| 5652 | 5684 | const llvm_ty = try self.dg.llvmType(ty); |
| 5653 | 5685 | const scalar_llvm_ty = try self.dg.llvmType(scalar_ty); |
| 5654 | 5686 | |
| 5655 | | const Strat = union(enum) { |
| 5656 | | intrinsic: []const u8, |
| 5657 | | libc: [:0]const u8, |
| 5658 | | }; |
| 5659 | | |
| 5660 | | // LLVM does not support all relevant intrinsics for all targets, so we |
| 5661 | | // may need to manually generate a libc call |
| 5662 | | const intrinsics_allowed = switch (scalar_ty.tag()) { |
| 5663 | | .f80 => target.longDoubleIs(f80) and backendSupportsF80(target), |
| 5664 | | .f128 => target.longDoubleIs(f128), |
| 5665 | | else => true, |
| 5666 | | }; |
| 5667 | | const strat: Strat = if (intrinsics_allowed) b: { |
| 5687 | const intrinsics_allowed = intrinsicsAllowed(scalar_ty, target); |
| 5688 | var fn_name_buf: [64]u8 = undefined; |
| 5689 | const strat: FloatOpStrat = if (intrinsics_allowed) switch (op) { |
| 5668 | 5690 | // Some operations are dedicated LLVM instructions, not available as intrinsics |
| 5669 | | switch (op) { |
| 5670 | | .add => return self.builder.buildFAdd(params[0], params[1], ""), |
| 5671 | | .sub => return self.builder.buildFSub(params[0], params[1], ""), |
| 5672 | | .mul => return self.builder.buildFMul(params[0], params[1], ""), |
| 5673 | | .div => return self.builder.buildFDiv(params[0], params[1], ""), |
| 5674 | | .rem => return self.builder.buildFRem(params[0], params[1], ""), |
| 5675 | | else => {}, |
| 5676 | | } |
| 5677 | | // All other operations are available as intrinsics |
| 5678 | | break :b .{ |
| 5679 | | .intrinsic = "llvm." ++ switch (op) { |
| 5680 | | .max => "maximum", |
| 5681 | | .min => "minimum", |
| 5682 | | .fma, .sqrt, .sin, .cos, .exp, .exp2, .log, .log2, .log10, .fabs, .floor, .ceil, .round, .trunc => @tagName(op), |
| 5683 | | .add, .sub, .mul, .div, .rem => unreachable, |
| 5684 | | else => unreachable, |
| 5685 | | }, |
| 5686 | | }; |
| 5691 | .add => return self.builder.buildFAdd(params[0], params[1], ""), |
| 5692 | .sub => return self.builder.buildFSub(params[0], params[1], ""), |
| 5693 | .mul => return self.builder.buildFMul(params[0], params[1], ""), |
| 5694 | .div => return self.builder.buildFDiv(params[0], params[1], ""), |
| 5695 | .fmod => return self.builder.buildFRem(params[0], params[1], ""), |
| 5696 | .fmax => return self.builder.buildMaxNum(params[0], params[1], ""), |
| 5697 | .fmin => return self.builder.buildMinNum(params[0], params[1], ""), |
| 5698 | else => .{ .intrinsic = "llvm." ++ @tagName(op) }, |
| 5687 | 5699 | } else b: { |
| 5688 | | const math_h_type_abbrev = getMathHTypeAbbrev(scalar_ty); |
| 5689 | | const compiler_rt_type_abbrev = getCompilerRtTypeAbbrev(scalar_ty, target); |
| 5690 | | var fn_name_buf: [64]u8 = undefined; |
| 5700 | const float_bits = scalar_ty.floatBits(target); |
| 5691 | 5701 | break :b switch (op) { |
| 5692 | | .fma => Strat{ |
| 5693 | | .libc = switch (scalar_ty.floatBits(target)) { |
| 5694 | | 80 => "__fmax", |
| 5695 | | else => std.fmt.bufPrintZ(&fn_name_buf, "fma{s}", .{math_h_type_abbrev}) catch unreachable, |
| 5696 | | }, |
| 5697 | | }, |
| 5698 | | .add, .sub, .div, .mul => Strat{ |
| 5699 | | .libc = std.fmt.bufPrintZ(&fn_name_buf, "__{s}{s}f3", .{ @tagName(op), compiler_rt_type_abbrev }) catch unreachable, |
| 5700 | | }, |
| 5701 | | .rem => Strat{ |
| 5702 | | .libc = std.fmt.bufPrintZ(&fn_name_buf, "fmod{s}", .{math_h_type_abbrev}) catch unreachable, |
| 5703 | | }, |
| 5704 | | .max, .min => Strat{ |
| 5705 | | .libc = std.fmt.bufPrintZ(&fn_name_buf, "f{s}{s}", .{ @tagName(op), math_h_type_abbrev }) catch unreachable, |
| 5702 | .add, .sub, .div, .mul => FloatOpStrat{ |
| 5703 | .libc = std.fmt.bufPrintZ(&fn_name_buf, "__{s}{s}f3", .{ |
| 5704 | @tagName(op), compilerRtFloatAbbrev(float_bits), |
| 5705 | }) catch unreachable, |
| 5706 | 5706 | }, |
| 5707 | | .sqrt, .sin, .cos, .exp, .exp2, .log, .log2, .log10, .fabs, .floor, .ceil, .round, .trunc => Strat{ |
| 5708 | | .libc = std.fmt.bufPrintZ(&fn_name_buf, "{s}{s}", .{ @tagName(op), math_h_type_abbrev }) catch unreachable, |
| 5707 | .ceil, |
| 5708 | .cos, |
| 5709 | .exp, |
| 5710 | .exp2, |
| 5711 | .fabs, |
| 5712 | .floor, |
| 5713 | .fma, |
| 5714 | .fmax, |
| 5715 | .fmin, |
| 5716 | .fmod, |
| 5717 | .log, |
| 5718 | .log10, |
| 5719 | .log2, |
| 5720 | .round, |
| 5721 | .sin, |
| 5722 | .sqrt, |
| 5723 | .trunc, |
| 5724 | => FloatOpStrat{ |
| 5725 | .libc = std.fmt.bufPrintZ(&fn_name_buf, "{s}{s}{s}", .{ |
| 5726 | libcFloatPrefix(float_bits), @tagName(op), libcFloatSuffix(float_bits), |
| 5727 | }) catch unreachable, |
| 5709 | 5728 | }, |
| 5710 | | else => unreachable, |
| 5711 | 5729 | }; |
| 5712 | 5730 | }; |
| 5713 | 5731 | |
| 5714 | | var llvm_fn: *const llvm.Value = switch (strat) { |
| 5732 | const llvm_fn: *const llvm.Value = switch (strat) { |
| 5715 | 5733 | .intrinsic => |fn_name| self.getIntrinsic(fn_name, &.{llvm_ty}), |
| 5716 | 5734 | .libc => |fn_name| b: { |
| 5717 | | assert(params.len == switch (op) { |
| 5718 | | .fma => 3, |
| 5719 | | .add, .sub, .div, .mul, .rem, .max, .min => 2, |
| 5720 | | .sqrt, .sin, .cos, .exp, .exp2, .log, .log2, .log10, .fabs, .floor, .ceil, .round, .trunc => 1, |
| 5721 | | else => unreachable, |
| 5722 | | }); |
| 5723 | 5735 | const param_types = [3]*const llvm.Type{ scalar_llvm_ty, scalar_llvm_ty, scalar_llvm_ty }; |
| 5724 | 5736 | const libc_fn = self.getLibcFunction(fn_name, param_types[0..params.len], scalar_llvm_ty); |
| 5725 | 5737 | if (ty.zigTypeTag() == .Vector) { |
| 5726 | 5738 | const result = llvm_ty.getUndef(); |
| 5727 | | return self.buildElementwiseCall(libc_fn, params[0..], result, ty.vectorLen()); |
| 5739 | return self.buildElementwiseCall(libc_fn, &params, result, ty.vectorLen()); |
| 5728 | 5740 | } |
| 5729 | 5741 | |
| 5730 | 5742 | break :b libc_fn; |
| 5731 | 5743 | }, |
| 5732 | 5744 | }; |
| 5733 | | const params_len = @intCast(c_uint, params.len); |
| 5734 | | return self.builder.buildCall(llvm_fn, params.ptr, params_len, .C, .Auto, ""); |
| 5745 | return self.builder.buildCall(llvm_fn, &params, params_len, .C, .Auto, ""); |
| 5735 | 5746 | } |
| 5736 | 5747 | |
| 5737 | 5748 | fn airMulAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| ... | ... | @@ -5745,7 +5756,7 @@ pub const FuncGen = struct { |
| 5745 | 5756 | const addend = try self.resolveInst(pl_op.operand); |
| 5746 | 5757 | |
| 5747 | 5758 | const ty = self.air.typeOfIndex(inst); |
| 5748 | | return self.buildFloatOp(.fma, ty, &.{ mulend1, mulend2, addend }); |
| 5759 | return self.buildFloatOp(.fma, ty, 3, .{ mulend1, mulend2, addend }); |
| 5749 | 5760 | } |
| 5750 | 5761 | |
| 5751 | 5762 | fn airShlWithOverflow(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| ... | ... | @@ -6550,15 +6561,14 @@ pub const FuncGen = struct { |
| 6550 | 6561 | } |
| 6551 | 6562 | } |
| 6552 | 6563 | |
| 6553 | | fn airUnaryOp(self: *FuncGen, inst: Air.Inst.Index, comptime op: @TypeOf(.EnumLiteral)) !?*const llvm.Value { |
| 6564 | fn airUnaryOp(self: *FuncGen, inst: Air.Inst.Index, comptime op: FloatOp) !?*const llvm.Value { |
| 6554 | 6565 | if (self.liveness.isUnused(inst)) return null; |
| 6555 | 6566 | |
| 6556 | 6567 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 6557 | 6568 | const operand = try self.resolveInst(un_op); |
| 6558 | 6569 | const operand_ty = self.air.typeOf(un_op); |
| 6559 | 6570 | |
| 6560 | | const params = [_]*const llvm.Value{operand}; |
| 6561 | | return self.buildFloatOp(op, operand_ty, &params); |
| 6571 | return self.buildFloatOp(op, operand_ty, 1, .{operand}); |
| 6562 | 6572 | } |
| 6563 | 6573 | |
| 6564 | 6574 | fn airClzCtz(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*const llvm.Value { |
| ... | ... | @@ -6822,17 +6832,9 @@ pub const FuncGen = struct { |
| 6822 | 6832 | |
| 6823 | 6833 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 6824 | 6834 | const scalar = try self.resolveInst(ty_op.operand); |
| 6825 | | const scalar_ty = self.air.typeOf(ty_op.operand); |
| 6826 | 6835 | const vector_ty = self.air.typeOfIndex(inst); |
| 6827 | 6836 | const len = vector_ty.vectorLen(); |
| 6828 | | const scalar_llvm_ty = try self.dg.llvmType(scalar_ty); |
| 6829 | | const op_llvm_ty = scalar_llvm_ty.vectorType(1); |
| 6830 | | const u32_llvm_ty = self.context.intType(32); |
| 6831 | | const mask_llvm_ty = u32_llvm_ty.vectorType(len); |
| 6832 | | const undef_vector = op_llvm_ty.getUndef(); |
| 6833 | | const u32_zero = u32_llvm_ty.constNull(); |
| 6834 | | const op_vector = self.builder.buildInsertElement(undef_vector, scalar, u32_zero, ""); |
| 6835 | | return self.builder.buildShuffleVector(op_vector, undef_vector, mask_llvm_ty.constNull(), ""); |
| 6837 | return self.builder.buildVectorSplat(len, scalar, ""); |
| 6836 | 6838 | } |
| 6837 | 6839 | |
| 6838 | 6840 | fn airSelect(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| ... | ... | @@ -8183,6 +8185,26 @@ fn backendSupportsF80(target: std.Target) bool { |
| 8183 | 8185 | }; |
| 8184 | 8186 | } |
| 8185 | 8187 | |
| 8188 | /// This function returns true if we expect LLVM to lower f16 correctly |
| 8189 | /// and false if we expect LLVM to crash if it counters an f16 type or |
| 8190 | /// if it produces miscompilations. |
| 8191 | fn backendSupportsF16(target: std.Target) bool { |
| 8192 | return switch (target.cpu.arch) { |
| 8193 | else => true, |
| 8194 | }; |
| 8195 | } |
| 8196 | |
| 8197 | /// LLVM does not support all relevant intrinsics for all targets, so we |
| 8198 | /// may need to manually generate a libc call |
| 8199 | fn intrinsicsAllowed(scalar_ty: Type, target: std.Target) bool { |
| 8200 | return switch (scalar_ty.tag()) { |
| 8201 | .f16 => backendSupportsF16(target), |
| 8202 | .f80 => target.longDoubleIs(f80) and backendSupportsF80(target), |
| 8203 | .f128 => target.longDoubleIs(f128), |
| 8204 | else => true, |
| 8205 | }; |
| 8206 | } |
| 8207 | |
| 8186 | 8208 | /// We need to insert extra padding if LLVM's isn't enough. |
| 8187 | 8209 | /// However we don't want to ever call LLVMABIAlignmentOfType or |
| 8188 | 8210 | /// LLVMABISizeOfType because these functions will trip assertions |