aboutsummaryrefslogtreecommitdiff
path: root/src/codegen/llvm.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/codegen/llvm.zig')
-rw-r--r--src/codegen/llvm.zig443
1 files changed, 298 insertions, 145 deletions
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index b2abc536ef..c9ea5bebac 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -3518,19 +3518,20 @@ pub const FuncGen = struct {
.shr => try self.airShr(inst, false),
.shr_exact => try self.airShr(inst, true),
- .sqrt => try self.airUnaryOp(inst, "sqrt"),
- .sin => try self.airUnaryOp(inst, "sin"),
- .cos => try self.airUnaryOp(inst, "cos"),
- .exp => try self.airUnaryOp(inst, "exp"),
- .exp2 => try self.airUnaryOp(inst, "exp2"),
- .log => try self.airUnaryOp(inst, "log"),
- .log2 => try self.airUnaryOp(inst, "log2"),
- .log10 => try self.airUnaryOp(inst, "log10"),
- .fabs => try self.airUnaryOp(inst, "fabs"),
- .floor => try self.airUnaryOp(inst, "floor"),
- .ceil => try self.airUnaryOp(inst, "ceil"),
- .round => try self.airUnaryOp(inst, "round"),
- .trunc_float => try self.airUnaryOp(inst, "trunc"),
+ .sqrt => try self.airUnaryOp(inst, .sqrt),
+ .sin => try self.airUnaryOp(inst, .sin),
+ .cos => try self.airUnaryOp(inst, .cos),
+ .tan => try self.airUnaryOp(inst, .tan),
+ .exp => try self.airUnaryOp(inst, .exp),
+ .exp2 => try self.airUnaryOp(inst, .exp2),
+ .log => try self.airUnaryOp(inst, .log),
+ .log2 => try self.airUnaryOp(inst, .log2),
+ .log10 => try self.airUnaryOp(inst, .log10),
+ .fabs => try self.airUnaryOp(inst, .fabs),
+ .floor => try self.airUnaryOp(inst, .floor),
+ .ceil => try self.airUnaryOp(inst, .ceil),
+ .round => try self.airUnaryOp(inst, .round),
+ .trunc_float => try self.airUnaryOp(inst, .trunc),
.cmp_eq => try self.airCmp(inst, .eq),
.cmp_gt => try self.airCmp(inst, .gt),
@@ -3905,7 +3906,7 @@ pub const FuncGen = struct {
rhs: *const llvm.Value,
operand_ty: Type,
op: math.CompareOperator,
- ) *const llvm.Value {
+ ) Allocator.Error!*const llvm.Value {
var int_buffer: Type.Payload.Bits = undefined;
var opt_buffer: Type.Payload.ElemType = undefined;
@@ -3947,7 +3948,7 @@ pub const FuncGen = struct {
self.builder.positionBuilderAtEnd(both_pl_block);
const lhs_payload = self.optPayloadHandle(lhs, is_by_ref);
const rhs_payload = self.optPayloadHandle(rhs, is_by_ref);
- const payload_cmp = self.cmp(lhs_payload, rhs_payload, payload_ty, op);
+ const payload_cmp = try self.cmp(lhs_payload, rhs_payload, payload_ty, op);
_ = self.builder.buildBr(end_block);
const both_pl_block_end = self.builder.getInsertBlock();
@@ -3983,17 +3984,7 @@ pub const FuncGen = struct {
);
return phi_node;
},
- .Float => {
- const operation: llvm.RealPredicate = switch (op) {
- .eq => .OEQ,
- .neq => .UNE,
- .lt => .OLT,
- .lte => .OLE,
- .gt => .OGT,
- .gte => .OGE,
- };
- return self.builder.buildFCmp(operation, lhs, rhs, "");
- },
+ .Float => return self.buildFloatCmp(op, operand_ty, .{ lhs, rhs }),
else => unreachable,
};
const is_signed = int_ty.isSignedInt();
@@ -5221,7 +5212,7 @@ pub const FuncGen = struct {
const inst_ty = self.air.typeOfIndex(inst);
const scalar_ty = inst_ty.scalarType();
- if (scalar_ty.isAnyFloat()) return self.builder.buildFAdd(lhs, rhs, "");
+ if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.add, inst_ty, 2, .{ lhs, rhs });
if (scalar_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, "");
return self.builder.buildNUWAdd(lhs, rhs, "");
}
@@ -5260,7 +5251,7 @@ pub const FuncGen = struct {
const inst_ty = self.air.typeOfIndex(inst);
const scalar_ty = inst_ty.scalarType();
- if (scalar_ty.isAnyFloat()) return self.builder.buildFSub(lhs, rhs, "");
+ if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.sub, inst_ty, 2, .{ lhs, rhs });
if (scalar_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, "");
return self.builder.buildNUWSub(lhs, rhs, "");
}
@@ -5298,7 +5289,7 @@ pub const FuncGen = struct {
const inst_ty = self.air.typeOfIndex(inst);
const scalar_ty = inst_ty.scalarType();
- if (scalar_ty.isAnyFloat()) return self.builder.buildFMul(lhs, rhs, "");
+ if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.mul, inst_ty, 2, .{ lhs, rhs });
if (scalar_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, "");
return self.builder.buildNUWMul(lhs, rhs, "");
}
@@ -5333,8 +5324,9 @@ pub const FuncGen = struct {
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const lhs = try self.resolveInst(bin_op.lhs);
const rhs = try self.resolveInst(bin_op.rhs);
+ const inst_ty = self.air.typeOfIndex(inst);
- return self.builder.buildFDiv(lhs, rhs, "");
+ return self.buildFloatOp(.div, inst_ty, 2, .{ lhs, rhs });
}
fn airDivTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
@@ -5347,8 +5339,8 @@ pub const FuncGen = struct {
const scalar_ty = inst_ty.scalarType();
if (scalar_ty.isRuntimeFloat()) {
- const result = self.builder.buildFDiv(lhs, rhs, "");
- return self.callTrunc(result, inst_ty);
+ const result = try self.buildFloatOp(.div, inst_ty, 2, .{ lhs, rhs });
+ return self.buildFloatOp(.trunc, inst_ty, 1, .{result});
}
if (scalar_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, "");
return self.builder.buildUDiv(lhs, rhs, "");
@@ -5364,8 +5356,8 @@ pub const FuncGen = struct {
const scalar_ty = inst_ty.scalarType();
if (scalar_ty.isRuntimeFloat()) {
- const result = self.builder.buildFDiv(lhs, rhs, "");
- return try self.callFloor(result, inst_ty);
+ const result = try self.buildFloatOp(.div, inst_ty, 2, .{ lhs, rhs });
+ return self.buildFloatOp(.floor, inst_ty, 1, .{result});
}
if (scalar_ty.isSignedInt()) {
// const d = @divTrunc(a, b);
@@ -5395,7 +5387,7 @@ pub const FuncGen = struct {
const inst_ty = self.air.typeOfIndex(inst);
const scalar_ty = inst_ty.scalarType();
- if (scalar_ty.isRuntimeFloat()) return self.builder.buildFDiv(lhs, rhs, "");
+ if (scalar_ty.isRuntimeFloat()) return self.buildFloatOp(.div, inst_ty, 2, .{ lhs, rhs });
if (scalar_ty.isSignedInt()) return self.builder.buildExactSDiv(lhs, rhs, "");
return self.builder.buildExactUDiv(lhs, rhs, "");
}
@@ -5409,7 +5401,7 @@ pub const FuncGen = struct {
const inst_ty = self.air.typeOfIndex(inst);
const scalar_ty = inst_ty.scalarType();
- if (scalar_ty.isRuntimeFloat()) return self.builder.buildFRem(lhs, rhs, "");
+ if (scalar_ty.isRuntimeFloat()) return self.buildFloatOp(.fmod, inst_ty, 2, .{ lhs, rhs });
if (scalar_ty.isSignedInt()) return self.builder.buildSRem(lhs, rhs, "");
return self.builder.buildURem(lhs, rhs, "");
}
@@ -5425,11 +5417,11 @@ pub const FuncGen = struct {
const scalar_ty = inst_ty.scalarType();
if (scalar_ty.isRuntimeFloat()) {
- const a = self.builder.buildFRem(lhs, rhs, "");
- const b = self.builder.buildFAdd(a, rhs, "");
- const c = self.builder.buildFRem(b, rhs, "");
+ const a = try self.buildFloatOp(.fmod, inst_ty, 2, .{ lhs, rhs });
+ const b = try self.buildFloatOp(.add, inst_ty, 2, .{ a, rhs });
+ const c = try self.buildFloatOp(.fmod, inst_ty, 2, .{ b, rhs });
const zero = inst_llvm_ty.constNull();
- const ltz = self.builder.buildFCmp(.OLT, lhs, zero, "");
+ const ltz = try self.buildFloatCmp(.lt, inst_ty, .{ lhs, zero });
return self.builder.buildSelect(ltz, c, a, "");
}
if (scalar_ty.isSignedInt()) {
@@ -5508,75 +5500,266 @@ pub const FuncGen = struct {
return result_struct;
}
- fn airMulAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
- if (self.liveness.isUnused(inst)) return null;
+ fn buildElementwiseCall(
+ self: *FuncGen,
+ llvm_fn: *const llvm.Value,
+ args_vectors: []const *const llvm.Value,
+ result_vector: *const llvm.Value,
+ vector_len: usize,
+ ) !*const llvm.Value {
+ const args_len = @intCast(c_uint, args_vectors.len);
+ const llvm_i32 = self.context.intType(32);
+ assert(args_len <= 3);
- const pl_op = self.air.instructions.items(.data)[inst].pl_op;
- const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
+ var i: usize = 0;
+ var result = result_vector;
+ while (i < vector_len) : (i += 1) {
+ const index_i32 = llvm_i32.constInt(i, .False);
- const mulend1 = try self.resolveInst(extra.lhs);
- const mulend2 = try self.resolveInst(extra.rhs);
- const addend = try self.resolveInst(pl_op.operand);
+ var args: [3]*const llvm.Value = undefined;
+ for (args_vectors) |arg_vector, k| {
+ args[k] = self.builder.buildExtractElement(arg_vector, index_i32, "");
+ }
+ const result_elem = self.builder.buildCall(llvm_fn, &args, args_len, .C, .Auto, "");
+ result = self.builder.buildInsertElement(result, result_elem, index_i32, "");
+ }
+ return result;
+ }
- const ty = self.air.typeOfIndex(inst);
- const llvm_ty = try self.dg.llvmType(ty);
- const scalar_ty = ty.scalarType();
- const target = self.dg.module.getTarget();
+ fn getLibcFunction(
+ self: *FuncGen,
+ fn_name: [:0]const u8,
+ param_types: []const *const llvm.Type,
+ return_type: *const llvm.Type,
+ ) *const llvm.Value {
+ return self.dg.object.llvm_module.getNamedFunction(fn_name.ptr) orelse b: {
+ const alias = self.dg.object.llvm_module.getNamedGlobalAlias(fn_name.ptr, fn_name.len);
+ break :b if (alias) |a| a.getAliasee() else null;
+ } orelse b: {
+ const params_len = @intCast(c_uint, param_types.len);
+ const fn_type = llvm.functionType(return_type, param_types.ptr, params_len, .False);
+ const f = self.dg.object.llvm_module.addFunction(fn_name, fn_type);
+ break :b f;
+ };
+ }
+
+ fn libcFloatPrefix(float_bits: u16) []const u8 {
+ return switch (float_bits) {
+ 16, 80 => "__",
+ 32, 64, 128 => "",
+ else => unreachable,
+ };
+ }
- const Strat = union(enum) {
- intrinsic,
- libc: [*:0]const u8,
+ fn libcFloatSuffix(float_bits: u16) []const u8 {
+ return switch (float_bits) {
+ 16 => "h", // Non-standard
+ 32 => "f",
+ 64 => "",
+ 80 => "x", // Non-standard
+ 128 => "q", // Non-standard (mimics convention in GCC libquadmath)
+ else => unreachable,
};
+ }
- const strat: Strat = switch (scalar_ty.floatBits(target)) {
- 16, 32, 64 => Strat.intrinsic,
- 80 => if (CType.longdouble.sizeInBits(target) == 80) Strat{ .intrinsic = {} } else Strat{ .libc = "__fmax" },
- // LLVM always lowers the fma builtin for f128 to fmal, which is for `long double`.
- // On some targets this will be correct; on others it will be incorrect.
- 128 => if (CType.longdouble.sizeInBits(target) == 128) Strat{ .intrinsic = {} } else Strat{ .libc = "fmaq" },
+ fn compilerRtFloatAbbrev(float_bits: u16) []const u8 {
+ return switch (float_bits) {
+ 16 => "h",
+ 32 => "s",
+ 64 => "d",
+ 80 => "x",
+ 128 => "t",
else => unreachable,
};
+ }
- switch (strat) {
- .intrinsic => {
- const llvm_fn = self.getIntrinsic("llvm.fma", &.{llvm_ty});
- const params = [_]*const llvm.Value{ mulend1, mulend2, addend };
- return self.builder.buildCall(llvm_fn, &params, params.len, .C, .Auto, "");
- },
- .libc => |fn_name| {
- const scalar_llvm_ty = try self.dg.llvmType(scalar_ty);
- const llvm_fn = self.dg.object.llvm_module.getNamedFunction(fn_name) orelse b: {
- const param_types = [_]*const llvm.Type{ scalar_llvm_ty, scalar_llvm_ty, scalar_llvm_ty };
- const fn_type = llvm.functionType(scalar_llvm_ty, &param_types, param_types.len, .False);
- break :b self.dg.object.llvm_module.addFunction(fn_name, fn_type);
- };
+ /// Creates a floating point comparison by lowering to the appropriate
+ /// hardware instruction or softfloat routine for the target
+ fn buildFloatCmp(
+ self: *FuncGen,
+ pred: math.CompareOperator,
+ ty: Type,
+ params: [2]*const llvm.Value,
+ ) !*const llvm.Value {
+ const target = self.dg.module.getTarget();
+ const scalar_ty = ty.scalarType();
+ const scalar_llvm_ty = try self.dg.llvmType(scalar_ty);
- if (ty.zigTypeTag() == .Vector) {
- const llvm_i32 = self.context.intType(32);
- const vector_llvm_ty = try self.dg.llvmType(ty);
+ if (intrinsicsAllowed(scalar_ty, target)) {
+ const llvm_predicate: llvm.RealPredicate = switch (pred) {
+ .eq => .OEQ,
+ .neq => .UNE,
+ .lt => .OLT,
+ .lte => .OLE,
+ .gt => .OGT,
+ .gte => .OGE,
+ };
+ return self.builder.buildFCmp(llvm_predicate, params[0], params[1], "");
+ }
+
+ const float_bits = scalar_ty.floatBits(target);
+ const compiler_rt_float_abbrev = compilerRtFloatAbbrev(float_bits);
+ var fn_name_buf: [64]u8 = undefined;
+ const fn_base_name = switch (pred) {
+ .neq => "ne",
+ .eq => "eq",
+ .lt => "lt",
+ .lte => "le",
+ .gt => "gt",
+ .gte => "ge",
+ };
+ const fn_name = std.fmt.bufPrintZ(&fn_name_buf, "__{s}{s}f2", .{
+ fn_base_name, compiler_rt_float_abbrev,
+ }) catch unreachable;
- var i: usize = 0;
- var vector = vector_llvm_ty.getUndef();
- while (i < ty.vectorLen()) : (i += 1) {
- const index_i32 = llvm_i32.constInt(i, .False);
+ const param_types = [2]*const llvm.Type{ scalar_llvm_ty, scalar_llvm_ty };
+ const llvm_i32 = self.context.intType(32);
+ const libc_fn = self.getLibcFunction(fn_name, param_types[0..], llvm_i32);
- const mulend1_elem = self.builder.buildExtractElement(mulend1, index_i32, "");
- const mulend2_elem = self.builder.buildExtractElement(mulend2, index_i32, "");
- const addend_elem = self.builder.buildExtractElement(addend, index_i32, "");
+ const zero = llvm_i32.constInt(0, .False);
+ const int_pred: llvm.IntPredicate = switch (pred) {
+ .eq => .EQ,
+ .neq => .NE,
+ .lt => .SLT,
+ .lte => .SLE,
+ .gt => .SGT,
+ .gte => .SGE,
+ };
- const params = [_]*const llvm.Value{ mulend1_elem, mulend2_elem, addend_elem };
- const mul_add = self.builder.buildCall(llvm_fn, &params, params.len, .C, .Auto, "");
+ if (ty.zigTypeTag() == .Vector) {
+ const vec_len = ty.vectorLen();
+ const vector_result_ty = llvm_i32.vectorType(vec_len);
+
+ var result = vector_result_ty.getUndef();
+ result = try self.buildElementwiseCall(libc_fn, &params, result, vec_len);
+
+ const zero_vector = self.builder.buildVectorSplat(vec_len, zero, "");
+ return self.builder.buildICmp(int_pred, result, zero_vector, "");
+ }
+
+ const result = self.builder.buildCall(libc_fn, &params, params.len, .C, .Auto, "");
+ return self.builder.buildICmp(int_pred, result, zero, "");
+ }
+
+ const FloatOp = enum {
+ add,
+ ceil,
+ cos,
+ div,
+ exp,
+ exp2,
+ fabs,
+ floor,
+ fma,
+ log,
+ log10,
+ log2,
+ fmax,
+ fmin,
+ mul,
+ fmod,
+ round,
+ sin,
+ sqrt,
+ sub,
+ tan,
+ trunc,
+ };
- vector = self.builder.buildInsertElement(vector, mul_add, index_i32, "");
- }
+ const FloatOpStrat = union(enum) {
+ intrinsic: []const u8,
+ libc: [:0]const u8,
+ };
- return vector;
- } else {
- const params = [_]*const llvm.Value{ mulend1, mulend2, addend };
- return self.builder.buildCall(llvm_fn, &params, params.len, .C, .Auto, "");
+ /// Creates a floating point operation (add, sub, fma, sqrt, exp, etc.)
+ /// by lowering to the appropriate hardware instruction or softfloat
+ /// routine for the target
+ fn buildFloatOp(
+ self: *FuncGen,
+ comptime op: FloatOp,
+ ty: Type,
+ comptime params_len: usize,
+ params: [params_len]*const llvm.Value,
+ ) !*const llvm.Value {
+ const target = self.dg.module.getTarget();
+ const scalar_ty = ty.scalarType();
+ const llvm_ty = try self.dg.llvmType(ty);
+ const scalar_llvm_ty = try self.dg.llvmType(scalar_ty);
+
+ const intrinsics_allowed = op != .tan and intrinsicsAllowed(scalar_ty, target);
+ var fn_name_buf: [64]u8 = undefined;
+ const strat: FloatOpStrat = if (intrinsics_allowed) switch (op) {
+ // Some operations are dedicated LLVM instructions, not available as intrinsics
+ .add => return self.builder.buildFAdd(params[0], params[1], ""),
+ .sub => return self.builder.buildFSub(params[0], params[1], ""),
+ .mul => return self.builder.buildFMul(params[0], params[1], ""),
+ .div => return self.builder.buildFDiv(params[0], params[1], ""),
+ .fmod => return self.builder.buildFRem(params[0], params[1], ""),
+ .fmax => return self.builder.buildMaxNum(params[0], params[1], ""),
+ .fmin => return self.builder.buildMinNum(params[0], params[1], ""),
+ else => .{ .intrinsic = "llvm." ++ @tagName(op) },
+ } else b: {
+ const float_bits = scalar_ty.floatBits(target);
+ break :b switch (op) {
+ .add, .sub, .div, .mul => FloatOpStrat{
+ .libc = std.fmt.bufPrintZ(&fn_name_buf, "__{s}{s}f3", .{
+ @tagName(op), compilerRtFloatAbbrev(float_bits),
+ }) catch unreachable,
+ },
+ .ceil,
+ .cos,
+ .exp,
+ .exp2,
+ .fabs,
+ .floor,
+ .fma,
+ .fmax,
+ .fmin,
+ .fmod,
+ .log,
+ .log10,
+ .log2,
+ .round,
+ .sin,
+ .sqrt,
+ .tan,
+ .trunc,
+ => FloatOpStrat{
+ .libc = std.fmt.bufPrintZ(&fn_name_buf, "{s}{s}{s}", .{
+ libcFloatPrefix(float_bits), @tagName(op), libcFloatSuffix(float_bits),
+ }) catch unreachable,
+ },
+ };
+ };
+
+ const llvm_fn: *const llvm.Value = switch (strat) {
+ .intrinsic => |fn_name| self.getIntrinsic(fn_name, &.{llvm_ty}),
+ .libc => |fn_name| b: {
+ const param_types = [3]*const llvm.Type{ scalar_llvm_ty, scalar_llvm_ty, scalar_llvm_ty };
+ const libc_fn = self.getLibcFunction(fn_name, param_types[0..params.len], scalar_llvm_ty);
+ if (ty.zigTypeTag() == .Vector) {
+ const result = llvm_ty.getUndef();
+ return self.buildElementwiseCall(libc_fn, &params, result, ty.vectorLen());
}
+
+ break :b libc_fn;
},
- }
+ };
+ return self.builder.buildCall(llvm_fn, &params, params_len, .C, .Auto, "");
+ }
+
+ fn airMulAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+ if (self.liveness.isUnused(inst)) return null;
+
+ const pl_op = self.air.instructions.items(.data)[inst].pl_op;
+ const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
+
+ const mulend1 = try self.resolveInst(extra.lhs);
+ const mulend2 = try self.resolveInst(extra.rhs);
+ const addend = try self.resolveInst(pl_op.operand);
+
+ const ty = self.air.typeOfIndex(inst);
+ return self.buildFloatOp(.fma, ty, 3, .{ mulend1, mulend2, addend });
}
fn airShlWithOverflow(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
@@ -6381,14 +6564,14 @@ pub const FuncGen = struct {
}
}
- fn airUnaryOp(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*const llvm.Value {
+ fn airUnaryOp(self: *FuncGen, inst: Air.Inst.Index, comptime op: FloatOp) !?*const llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const un_op = self.air.instructions.items(.data)[inst].un_op;
const operand = try self.resolveInst(un_op);
const operand_ty = self.air.typeOf(un_op);
- return self.callFloatUnary(operand, operand_ty, llvm_fn_name);
+ return self.buildFloatOp(op, operand_ty, 1, .{operand});
}
fn airClzCtz(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*const llvm.Value {
@@ -6652,17 +6835,9 @@ pub const FuncGen = struct {
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const scalar = try self.resolveInst(ty_op.operand);
- const scalar_ty = self.air.typeOf(ty_op.operand);
const vector_ty = self.air.typeOfIndex(inst);
const len = vector_ty.vectorLen();
- const scalar_llvm_ty = try self.dg.llvmType(scalar_ty);
- const op_llvm_ty = scalar_llvm_ty.vectorType(1);
- const u32_llvm_ty = self.context.intType(32);
- const mask_llvm_ty = u32_llvm_ty.vectorType(len);
- const undef_vector = op_llvm_ty.getUndef();
- const u32_zero = u32_llvm_ty.constNull();
- const op_vector = self.builder.buildInsertElement(undef_vector, scalar, u32_zero, "");
- return self.builder.buildShuffleVector(op_vector, undef_vector, mask_llvm_ty.constNull(), "");
+ return self.builder.buildVectorSplat(len, scalar, "");
}
fn airSelect(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
@@ -7191,48 +7366,6 @@ pub const FuncGen = struct {
return self.builder.buildExtractValue(opt_handle, 0, "");
}
- fn callFloor(self: *FuncGen, arg: *const llvm.Value, ty: Type) !*const llvm.Value {
- return self.callFloatUnary(arg, ty, "floor");
- }
-
- fn callCeil(self: *FuncGen, arg: *const llvm.Value, ty: Type) !*const llvm.Value {
- return self.callFloatUnary(arg, ty, "ceil");
- }
-
- fn callTrunc(self: *FuncGen, arg: *const llvm.Value, ty: Type) !*const llvm.Value {
- return self.callFloatUnary(arg, ty, "trunc");
- }
-
- fn callFloatUnary(
- self: *FuncGen,
- arg: *const llvm.Value,
- ty: Type,
- name: []const u8,
- ) !*const llvm.Value {
- const target = self.dg.module.getTarget();
-
- var fn_name_buf: [100]u8 = undefined;
- const llvm_fn_name = switch (ty.zigTypeTag()) {
- .Vector => std.fmt.bufPrintZ(&fn_name_buf, "llvm.{s}.v{d}f{d}", .{
- name, ty.vectorLen(), ty.childType().floatBits(target),
- }) catch unreachable,
- .Float => std.fmt.bufPrintZ(&fn_name_buf, "llvm.{s}.f{d}", .{
- name, ty.floatBits(target),
- }) catch unreachable,
- else => unreachable,
- };
-
- const llvm_fn = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: {
- const operand_llvm_ty = try self.dg.llvmType(ty);
- const param_types = [_]*const llvm.Type{operand_llvm_ty};
- const fn_type = llvm.functionType(operand_llvm_ty, &param_types, param_types.len, .False);
- break :blk self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type);
- };
-
- const args: [1]*const llvm.Value = .{arg};
- return self.builder.buildCall(llvm_fn, &args, args.len, .C, .Auto, "");
- }
-
fn fieldPtr(
self: *FuncGen,
inst: Air.Inst.Index,
@@ -8055,6 +8188,26 @@ fn backendSupportsF80(target: std.Target) bool {
};
}
+/// This function returns true if we expect LLVM to lower f16 correctly
+/// and false if we expect LLVM to crash if it counters an f16 type or
+/// if it produces miscompilations.
+fn backendSupportsF16(target: std.Target) bool {
+ return switch (target.cpu.arch) {
+ else => true,
+ };
+}
+
+/// LLVM does not support all relevant intrinsics for all targets, so we
+/// may need to manually generate a libc call
+fn intrinsicsAllowed(scalar_ty: Type, target: std.Target) bool {
+ return switch (scalar_ty.tag()) {
+ .f16 => backendSupportsF16(target),
+ .f80 => target.longDoubleIs(f80) and backendSupportsF80(target),
+ .f128 => target.longDoubleIs(f128),
+ else => true,
+ };
+}
+
/// We need to insert extra padding if LLVM's isn't enough.
/// However we don't want to ever call LLVMABIAlignmentOfType or
/// LLVMABISizeOfType because these functions will trip assertions