authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-04-27 13:51:53-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-04-27 14:18:34-07:00
log087aedfa38e0eb2a8f0e3055be18625017060b29
treedaf083d2a0ffccea4b4764ebd94ddb0704eb5333
parent7d6a6ce87fde465ffc3bd6b0a8bb6e039c67268f

stage2: fix recent LLVM backend code

* std.math.snan: fix compilation error. Also make it and nan inline. * LLVM: use a proper enum type for float op instead of enum literal. Also various cleanups. * LLVM: use LLVMBuildVectorSplat for vector splat AIR instruction. - also the bindings had parameter order wrong * LLVM: additionally handle f16 lowering. For now all targets report OK but I think we will need to add some exceptions to this list.

3 files changed, 147 insertions(+), 135 deletions(-)

lib/std/math/nan.zig+5-12
...@@ -1,7 +1,7 @@...@@ -1,7 +1,7 @@
1const math = @import("../math.zig");1const math = @import("../math.zig");
22
3/// Returns the nan representation for type T.3/// Returns the nan representation for type T.
4pub fn nan(comptime T: type) T {4pub inline fn nan(comptime T: type) T {
5 return switch (@typeInfo(T).Float.bits) {5 return switch (@typeInfo(T).Float.bits) {
6 16 => math.nan_f16,6 16 => math.nan_f16,
7 32 => math.nan_f32,7 32 => math.nan_f32,
...@@ -13,15 +13,8 @@ pub fn nan(comptime T: type) T {...@@ -13,15 +13,8 @@ pub fn nan(comptime T: type) T {
13}13}
1414
15/// Returns the signalling nan representation for type T.15/// Returns the signalling nan representation for type T.
16pub fn snan(comptime T: type) T {16/// Note: A signalling nan is identical to a standard right now by may have a different bit
17 // Note: A signalling nan is identical to a standard right now by may have a different bit17/// representation in the future when required.
18 // representation in the future when required.18pub inline fn snan(comptime T: type) T {
19 return switch (@typeInfo(T).Float.bits) {19 return nan(T);
20 16 => math.nan_u16,
21 32 => math.nan_u32,
22 64 => math.nan_u64,
23 80 => math.nan_u80,
24 128 => math.nan_u128,
25 else => @compileError("unreachable"),
26 };
27}20}
src/codegen/llvm.zig+141-119
...@@ -3983,7 +3983,7 @@ pub const FuncGen = struct {...@@ -3983,7 +3983,7 @@ pub const FuncGen = struct {
3983 );3983 );
3984 return phi_node;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 else => unreachable,3987 else => unreachable,
3988 };3988 };
3989 const is_signed = int_ty.isSignedInt();3989 const is_signed = int_ty.isSignedInt();
...@@ -5211,7 +5211,7 @@ pub const FuncGen = struct {...@@ -5211,7 +5211,7 @@ pub const FuncGen = struct {
5211 const inst_ty = self.air.typeOfIndex(inst);5211 const inst_ty = self.air.typeOfIndex(inst);
5212 const scalar_ty = inst_ty.scalarType();5212 const scalar_ty = inst_ty.scalarType();
52135213
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 if (scalar_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, "");5215 if (scalar_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, "");
5216 return self.builder.buildNUWAdd(lhs, rhs, "");5216 return self.builder.buildNUWAdd(lhs, rhs, "");
5217 }5217 }
...@@ -5250,7 +5250,7 @@ pub const FuncGen = struct {...@@ -5250,7 +5250,7 @@ pub const FuncGen = struct {
5250 const inst_ty = self.air.typeOfIndex(inst);5250 const inst_ty = self.air.typeOfIndex(inst);
5251 const scalar_ty = inst_ty.scalarType();5251 const scalar_ty = inst_ty.scalarType();
52525252
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 if (scalar_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, "");5254 if (scalar_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, "");
5255 return self.builder.buildNUWSub(lhs, rhs, "");5255 return self.builder.buildNUWSub(lhs, rhs, "");
5256 }5256 }
...@@ -5288,7 +5288,7 @@ pub const FuncGen = struct {...@@ -5288,7 +5288,7 @@ pub const FuncGen = struct {
5288 const inst_ty = self.air.typeOfIndex(inst);5288 const inst_ty = self.air.typeOfIndex(inst);
5289 const scalar_ty = inst_ty.scalarType();5289 const scalar_ty = inst_ty.scalarType();
52905290
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 if (scalar_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, "");5292 if (scalar_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, "");
5293 return self.builder.buildNUWMul(lhs, rhs, "");5293 return self.builder.buildNUWMul(lhs, rhs, "");
5294 }5294 }
...@@ -5325,7 +5325,7 @@ pub const FuncGen = struct {...@@ -5325,7 +5325,7 @@ pub const FuncGen = struct {
5325 const rhs = try self.resolveInst(bin_op.rhs);5325 const rhs = try self.resolveInst(bin_op.rhs);
5326 const inst_ty = self.air.typeOfIndex(inst);5326 const inst_ty = self.air.typeOfIndex(inst);
53275327
5328 return self.buildFloatOp(.div, inst_ty, &.{ lhs, rhs });5328 return self.buildFloatOp(.div, inst_ty, 2, .{ lhs, rhs });
5329 }5329 }
53305330
5331 fn airDivTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {5331 fn airDivTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
...@@ -5338,8 +5338,8 @@ pub const FuncGen = struct {...@@ -5338,8 +5338,8 @@ pub const FuncGen = struct {
5338 const scalar_ty = inst_ty.scalarType();5338 const scalar_ty = inst_ty.scalarType();
53395339
5340 if (scalar_ty.isRuntimeFloat()) {5340 if (scalar_ty.isRuntimeFloat()) {
5341 const result = try self.buildFloatOp(.div, inst_ty, &.{ lhs, rhs });5341 const result = try self.buildFloatOp(.div, inst_ty, 2, .{ lhs, rhs });
5342 return self.buildFloatOp(.trunc, inst_ty, &.{result});5342 return self.buildFloatOp(.trunc, inst_ty, 1, .{result});
5343 }5343 }
5344 if (scalar_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, "");5344 if (scalar_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, "");
5345 return self.builder.buildUDiv(lhs, rhs, "");5345 return self.builder.buildUDiv(lhs, rhs, "");
...@@ -5355,8 +5355,8 @@ pub const FuncGen = struct {...@@ -5355,8 +5355,8 @@ pub const FuncGen = struct {
5355 const scalar_ty = inst_ty.scalarType();5355 const scalar_ty = inst_ty.scalarType();
53565356
5357 if (scalar_ty.isRuntimeFloat()) {5357 if (scalar_ty.isRuntimeFloat()) {
5358 const result = try self.buildFloatOp(.div, inst_ty, &.{ lhs, rhs });5358 const result = try self.buildFloatOp(.div, inst_ty, 2, .{ lhs, rhs });
5359 return self.buildFloatOp(.floor, inst_ty, &.{result});5359 return self.buildFloatOp(.floor, inst_ty, 1, .{result});
5360 }5360 }
5361 if (scalar_ty.isSignedInt()) {5361 if (scalar_ty.isSignedInt()) {
5362 // const d = @divTrunc(a, b);5362 // const d = @divTrunc(a, b);
...@@ -5386,7 +5386,7 @@ pub const FuncGen = struct {...@@ -5386,7 +5386,7 @@ pub const FuncGen = struct {
5386 const inst_ty = self.air.typeOfIndex(inst);5386 const inst_ty = self.air.typeOfIndex(inst);
5387 const scalar_ty = inst_ty.scalarType();5387 const scalar_ty = inst_ty.scalarType();
53885388
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 if (scalar_ty.isSignedInt()) return self.builder.buildExactSDiv(lhs, rhs, "");5390 if (scalar_ty.isSignedInt()) return self.builder.buildExactSDiv(lhs, rhs, "");
5391 return self.builder.buildExactUDiv(lhs, rhs, "");5391 return self.builder.buildExactUDiv(lhs, rhs, "");
5392 }5392 }
...@@ -5400,7 +5400,7 @@ pub const FuncGen = struct {...@@ -5400,7 +5400,7 @@ pub const FuncGen = struct {
5400 const inst_ty = self.air.typeOfIndex(inst);5400 const inst_ty = self.air.typeOfIndex(inst);
5401 const scalar_ty = inst_ty.scalarType();5401 const scalar_ty = inst_ty.scalarType();
54025402
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 if (scalar_ty.isSignedInt()) return self.builder.buildSRem(lhs, rhs, "");5404 if (scalar_ty.isSignedInt()) return self.builder.buildSRem(lhs, rhs, "");
5405 return self.builder.buildURem(lhs, rhs, "");5405 return self.builder.buildURem(lhs, rhs, "");
5406 }5406 }
...@@ -5416,11 +5416,11 @@ pub const FuncGen = struct {...@@ -5416,11 +5416,11 @@ pub const FuncGen = struct {
5416 const scalar_ty = inst_ty.scalarType();5416 const scalar_ty = inst_ty.scalarType();
54175417
5418 if (scalar_ty.isRuntimeFloat()) {5418 if (scalar_ty.isRuntimeFloat()) {
5419 const a = try self.buildFloatOp(.rem, inst_ty, &.{ lhs, rhs });5419 const a = try self.buildFloatOp(.fmod, inst_ty, 2, .{ lhs, rhs });
5420 const b = try self.buildFloatOp(.add, inst_ty, &.{ a, rhs });5420 const b = try self.buildFloatOp(.add, inst_ty, 2, .{ a, rhs });
5421 const c = try self.buildFloatOp(.rem, inst_ty, &.{ b, rhs });5421 const c = try self.buildFloatOp(.fmod, inst_ty, 2, .{ b, rhs });
5422 const zero = inst_llvm_ty.constNull();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 return self.builder.buildSelect(ltz, c, a, "");5424 return self.builder.buildSelect(ltz, c, a, "");
5425 }5425 }
5426 if (scalar_ty.isSignedInt()) {5426 if (scalar_ty.isSignedInt()) {
...@@ -5508,18 +5508,18 @@ pub const FuncGen = struct {...@@ -5508,18 +5508,18 @@ pub const FuncGen = struct {
5508 ) !*const llvm.Value {5508 ) !*const llvm.Value {
5509 const args_len = @intCast(c_uint, args_vectors.len);5509 const args_len = @intCast(c_uint, args_vectors.len);
5510 const llvm_i32 = self.context.intType(32);5510 const llvm_i32 = self.context.intType(32);
5511 assert(args_len <= 8);5511 assert(args_len <= 3);
55125512
5513 var i: usize = 0;5513 var i: usize = 0;
5514 var result = result_vector;5514 var result = result_vector;
5515 while (i < vector_len) : (i += 1) {5515 while (i < vector_len) : (i += 1) {
5516 const index_i32 = llvm_i32.constInt(i, .False);5516 const index_i32 = llvm_i32.constInt(i, .False);
55175517
5518 var args: [8]*const llvm.Value = undefined;5518 var args: [3]*const llvm.Value = undefined;
5519 for (args_vectors) |arg_vector, k| {5519 for (args_vectors) |arg_vector, k| {
5520 args[k] = self.builder.buildExtractElement(arg_vector, index_i32, "");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 result = self.builder.buildInsertElement(result, result_elem, index_i32, "");5523 result = self.builder.buildInsertElement(result, result_elem, index_i32, "");
5524 }5524 }
5525 return result;5525 return result;
...@@ -5542,20 +5542,27 @@ pub const FuncGen = struct {...@@ -5542,20 +5542,27 @@ pub const FuncGen = struct {
5542 };5542 };
5543 }5543 }
55445544
5545 fn getMathHTypeAbbrev(ty: Type) []const u8 {5545 fn libcFloatPrefix(float_bits: u16) []const u8 {
5546 return switch (ty.tag()) {5546 return switch (float_bits) {
5547 .f16 => "h", // Non-standard5547 16, 80 => "__",
5548 .f32 => "s",5548 32, 64, 128 => "",
5549 .f64 => "",
5550 .f80 => "x", // Non-standard
5551 .c_longdouble => "l",
5552 .f128 => "q", // Non-standard (mimics convention in GCC libquadmath)
5553 else => unreachable,5549 else => unreachable,
5554 };5550 };
5555 }5551 }
55565552
5557 fn getCompilerRtTypeAbbrev(ty: Type, target: std.Target) []const u8 {5553 fn libcFloatSuffix(float_bits: u16) []const u8 {
5558 return switch (ty.floatBits(target)) {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 16 => "h",5566 16 => "h",
5560 32 => "s",5567 32 => "s",
5561 64 => "d",5568 64 => "d",
...@@ -5571,20 +5578,13 @@ pub const FuncGen = struct {...@@ -5571,20 +5578,13 @@ pub const FuncGen = struct {
5571 self: *FuncGen,5578 self: *FuncGen,
5572 pred: math.CompareOperator,5579 pred: math.CompareOperator,
5573 ty: Type,5580 ty: Type,
5574 params: []const *const llvm.Value,5581 params: [2]*const llvm.Value,
5575 ) !*const llvm.Value {5582 ) !*const llvm.Value {
5576 const target = self.dg.module.getTarget();5583 const target = self.dg.module.getTarget();
5577 const scalar_ty = ty.scalarType();5584 const scalar_ty = ty.scalarType();
5578 const scalar_llvm_ty = try self.dg.llvmType(scalar_ty);5585 const scalar_llvm_ty = try self.dg.llvmType(scalar_ty);
55795586
5580 // LLVM does not support all floating point comparisons for all targets, so we5587 if (intrinsicsAllowed(scalar_ty, target)) {
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) {
5588 const llvm_predicate: llvm.RealPredicate = switch (pred) {5588 const llvm_predicate: llvm.RealPredicate = switch (pred) {
5589 .eq => .OEQ,5589 .eq => .OEQ,
5590 .neq => .UNE,5590 .neq => .UNE,
...@@ -5596,7 +5596,8 @@ pub const FuncGen = struct {...@@ -5596,7 +5596,8 @@ pub const FuncGen = struct {
5596 return self.builder.buildFCmp(llvm_predicate, params[0], params[1], "");5596 return self.builder.buildFCmp(llvm_predicate, params[0], params[1], "");
5597 }5597 }
55985598
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 var fn_name_buf: [64]u8 = undefined;5601 var fn_name_buf: [64]u8 = undefined;
5601 const fn_base_name = switch (pred) {5602 const fn_base_name = switch (pred) {
5602 .neq => "ne",5603 .neq => "ne",
...@@ -5606,9 +5607,10 @@ pub const FuncGen = struct {...@@ -5606,9 +5607,10 @@ pub const FuncGen = struct {
5606 .gt => "gt",5607 .gt => "gt",
5607 .gte => "ge",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;
56105613
5611 assert(params.len == 2);
5612 const param_types = [2]*const llvm.Type{ scalar_llvm_ty, scalar_llvm_ty };5614 const param_types = [2]*const llvm.Type{ scalar_llvm_ty, scalar_llvm_ty };
5613 const llvm_i32 = self.context.intType(32);5615 const llvm_i32 = self.context.intType(32);
5614 const libc_fn = self.getLibcFunction(fn_name, param_types[0..], llvm_i32);5616 const libc_fn = self.getLibcFunction(fn_name, param_types[0..], llvm_i32);
...@@ -5628,110 +5630,119 @@ pub const FuncGen = struct {...@@ -5628,110 +5630,119 @@ pub const FuncGen = struct {
5628 const vector_result_ty = llvm_i32.vectorType(vec_len);5630 const vector_result_ty = llvm_i32.vectorType(vec_len);
56295631
5630 var result = vector_result_ty.getUndef();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);
56325634
5633 const zero_vector = self.builder.buildVectorSplat(zero, vec_len, "");5635 const zero_vector = self.builder.buildVectorSplat(vec_len, zero, "");
5634 return self.builder.buildICmp(int_pred, result, zero_vector, "");5636 return self.builder.buildICmp(int_pred, result, zero_vector, "");
5635 }5637 }
56365638
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 return self.builder.buildICmp(int_pred, result, zero, "");5640 return self.builder.buildICmp(int_pred, result, zero, "");
5639 }5641 }
56405642
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 /// Creates a floating point operation (add, sub, fma, sqrt, exp, etc.)5672 /// Creates a floating point operation (add, sub, fma, sqrt, exp, etc.)
5642 /// by lowering to the appropriate hardware instruction or softfloat5673 /// by lowering to the appropriate hardware instruction or softfloat
5643 /// routine for the target5674 /// routine for the target
5644 fn buildFloatOp(5675 fn buildFloatOp(
5645 self: *FuncGen,5676 self: *FuncGen,
5646 comptime op: @TypeOf(.EnumLiteral),5677 comptime op: FloatOp,
5647 ty: Type,5678 ty: Type,
5648 params: []const *const llvm.Value,5679 comptime params_len: usize,
5680 params: [params_len]*const llvm.Value,
5649 ) !*const llvm.Value {5681 ) !*const llvm.Value {
5650 const target = self.dg.module.getTarget();5682 const target = self.dg.module.getTarget();
5651 const scalar_ty = ty.scalarType();5683 const scalar_ty = ty.scalarType();
5652 const llvm_ty = try self.dg.llvmType(ty);5684 const llvm_ty = try self.dg.llvmType(ty);
5653 const scalar_llvm_ty = try self.dg.llvmType(scalar_ty);5685 const scalar_llvm_ty = try self.dg.llvmType(scalar_ty);
56545686
5655 const Strat = union(enum) {5687 const intrinsics_allowed = intrinsicsAllowed(scalar_ty, target);
5656 intrinsic: []const u8,5688 var fn_name_buf: [64]u8 = undefined;
5657 libc: [:0]const u8,5689 const strat: FloatOpStrat = if (intrinsics_allowed) switch (op) {
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: {
5668 // Some operations are dedicated LLVM instructions, not available as intrinsics5690 // Some operations are dedicated LLVM instructions, not available as intrinsics
5669 switch (op) {5691 .add => return self.builder.buildFAdd(params[0], params[1], ""),
5670 .add => return self.builder.buildFAdd(params[0], params[1], ""),5692 .sub => return self.builder.buildFSub(params[0], params[1], ""),
5671 .sub => return self.builder.buildFSub(params[0], params[1], ""),5693 .mul => return self.builder.buildFMul(params[0], params[1], ""),
5672 .mul => return self.builder.buildFMul(params[0], params[1], ""),5694 .div => return self.builder.buildFDiv(params[0], params[1], ""),
5673 .div => return self.builder.buildFDiv(params[0], params[1], ""),5695 .fmod => return self.builder.buildFRem(params[0], params[1], ""),
5674 .rem => return self.builder.buildFRem(params[0], params[1], ""),5696 .fmax => return self.builder.buildMaxNum(params[0], params[1], ""),
5675 else => {},5697 .fmin => return self.builder.buildMinNum(params[0], params[1], ""),
5676 }5698 else => .{ .intrinsic = "llvm." ++ @tagName(op) },
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 };
5687 } else b: {5699 } else b: {
5688 const math_h_type_abbrev = getMathHTypeAbbrev(scalar_ty);5700 const float_bits = scalar_ty.floatBits(target);
5689 const compiler_rt_type_abbrev = getCompilerRtTypeAbbrev(scalar_ty, target);
5690 var fn_name_buf: [64]u8 = undefined;
5691 break :b switch (op) {5701 break :b switch (op) {
5692 .fma => Strat{5702 .add, .sub, .div, .mul => FloatOpStrat{
5693 .libc = switch (scalar_ty.floatBits(target)) {5703 .libc = std.fmt.bufPrintZ(&fn_name_buf, "__{s}{s}f3", .{
5694 80 => "__fmax",5704 @tagName(op), compilerRtFloatAbbrev(float_bits),
5695 else => std.fmt.bufPrintZ(&fn_name_buf, "fma{s}", .{math_h_type_abbrev}) catch unreachable,5705 }) 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,
5706 },5706 },
5707 .sqrt, .sin, .cos, .exp, .exp2, .log, .log2, .log10, .fabs, .floor, .ceil, .round, .trunc => Strat{5707 .ceil,
5708 .libc = std.fmt.bufPrintZ(&fn_name_buf, "{s}{s}", .{ @tagName(op), math_h_type_abbrev }) catch unreachable,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 };
57135731
5714 var llvm_fn: *const llvm.Value = switch (strat) {5732 const llvm_fn: *const llvm.Value = switch (strat) {
5715 .intrinsic => |fn_name| self.getIntrinsic(fn_name, &.{llvm_ty}),5733 .intrinsic => |fn_name| self.getIntrinsic(fn_name, &.{llvm_ty}),
5716 .libc => |fn_name| b: {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 const param_types = [3]*const llvm.Type{ scalar_llvm_ty, scalar_llvm_ty, scalar_llvm_ty };5735 const param_types = [3]*const llvm.Type{ scalar_llvm_ty, scalar_llvm_ty, scalar_llvm_ty };
5724 const libc_fn = self.getLibcFunction(fn_name, param_types[0..params.len], scalar_llvm_ty);5736 const libc_fn = self.getLibcFunction(fn_name, param_types[0..params.len], scalar_llvm_ty);
5725 if (ty.zigTypeTag() == .Vector) {5737 if (ty.zigTypeTag() == .Vector) {
5726 const result = llvm_ty.getUndef();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 }
57295741
5730 break :b libc_fn;5742 break :b libc_fn;
5731 },5743 },
5732 };5744 };
5733 const params_len = @intCast(c_uint, params.len);5745 return self.builder.buildCall(llvm_fn, &params, params_len, .C, .Auto, "");
5734 return self.builder.buildCall(llvm_fn, params.ptr, params_len, .C, .Auto, "");
5735 }5746 }
57365747
5737 fn airMulAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {5748 fn airMulAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
...@@ -5745,7 +5756,7 @@ pub const FuncGen = struct {...@@ -5745,7 +5756,7 @@ pub const FuncGen = struct {
5745 const addend = try self.resolveInst(pl_op.operand);5756 const addend = try self.resolveInst(pl_op.operand);
57465757
5747 const ty = self.air.typeOfIndex(inst);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 }
57505761
5751 fn airShlWithOverflow(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {5762 fn airShlWithOverflow(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
...@@ -6550,15 +6561,14 @@ pub const FuncGen = struct {...@@ -6550,15 +6561,14 @@ pub const FuncGen = struct {
6550 }6561 }
6551 }6562 }
65526563
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 if (self.liveness.isUnused(inst)) return null;6565 if (self.liveness.isUnused(inst)) return null;
65556566
6556 const un_op = self.air.instructions.items(.data)[inst].un_op;6567 const un_op = self.air.instructions.items(.data)[inst].un_op;
6557 const operand = try self.resolveInst(un_op);6568 const operand = try self.resolveInst(un_op);
6558 const operand_ty = self.air.typeOf(un_op);6569 const operand_ty = self.air.typeOf(un_op);
65596570
6560 const params = [_]*const llvm.Value{operand};6571 return self.buildFloatOp(op, operand_ty, 1, .{operand});
6561 return self.buildFloatOp(op, operand_ty, &params);
6562 }6572 }
65636573
6564 fn airClzCtz(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*const llvm.Value {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,17 +6832,9 @@ pub const FuncGen = struct {
68226832
6823 const ty_op = self.air.instructions.items(.data)[inst].ty_op;6833 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
6824 const scalar = try self.resolveInst(ty_op.operand);6834 const scalar = try self.resolveInst(ty_op.operand);
6825 const scalar_ty = self.air.typeOf(ty_op.operand);
6826 const vector_ty = self.air.typeOfIndex(inst);6835 const vector_ty = self.air.typeOfIndex(inst);
6827 const len = vector_ty.vectorLen();6836 const len = vector_ty.vectorLen();
6828 const scalar_llvm_ty = try self.dg.llvmType(scalar_ty);6837 return self.builder.buildVectorSplat(len, scalar, "");
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(), "");
6836 }6838 }
68376839
6838 fn airSelect(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {6840 fn airSelect(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
...@@ -8183,6 +8185,26 @@ fn backendSupportsF80(target: std.Target) bool {...@@ -8183,6 +8185,26 @@ fn backendSupportsF80(target: std.Target) bool {
8183 };8185 };
8184}8186}
81858187
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.
8191fn 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
8199fn 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/// We need to insert extra padding if LLVM's isn't enough.8208/// We need to insert extra padding if LLVM's isn't enough.
8187/// However we don't want to ever call LLVMABIAlignmentOfType or8209/// However we don't want to ever call LLVMABIAlignmentOfType or
8188/// LLVMABISizeOfType because these functions will trip assertions8210/// LLVMABISizeOfType because these functions will trip assertions
src/codegen/llvm/bindings.zig+1-4
...@@ -295,9 +295,6 @@ pub const Type = opaque {...@@ -295,9 +295,6 @@ pub const Type = opaque {
295295
296 pub const countStructElementTypes = LLVMCountStructElementTypes;296 pub const countStructElementTypes = LLVMCountStructElementTypes;
297 extern fn LLVMCountStructElementTypes(StructTy: *const Type) c_uint;297 extern fn LLVMCountStructElementTypes(StructTy: *const Type) c_uint;
298
299 pub const getVectorSize = LLVMGetVectorSize;
300 extern fn LLVMGetVectorSize(VectorTy: *const Type) c_uint;
301};298};
302299
303pub const Module = opaque {300pub const Module = opaque {
...@@ -681,8 +678,8 @@ pub const Builder = opaque {...@@ -681,8 +678,8 @@ pub const Builder = opaque {
681 pub const buildVectorSplat = LLVMBuildVectorSplat;678 pub const buildVectorSplat = LLVMBuildVectorSplat;
682 extern fn LLVMBuildVectorSplat(679 extern fn LLVMBuildVectorSplat(
683 *const Builder,680 *const Builder,
684 EltVal: *const Value,
685 ElementCount: c_uint,681 ElementCount: c_uint,
682 EltVal: *const Value,
686 Name: [*:0]const u8,683 Name: [*:0]const u8,
687 ) *const Value;684 ) *const Value;
688685