mirror of
https://github.com/ziglang/zig.git
synced 2026-01-30 03:03:46 +00:00
stage1: add @sin @cos @exp @exp2 @ln @log2 @log10 @fabs @floor @ceil @trunc @round
and expand @sqrt
This revealed that the accuracy of ln is not as good as the current algorithm in
musl and glibc, and should be ported again.
v2: actually include tests
v3: fix reversal of in and out arguments on f128M_sqrt()
add test for @sqrt on comptime_float
do not include @nearbyInt() until it works on all targets.
This commit is contained in:
parent
ebde2ff899
commit
71e014caec
@ -7354,10 +7354,91 @@ test "@setRuntimeSafety" {
|
||||
<pre>{#syntax#}@sqrt(comptime T: type, value: T) T{#endsyntax#}</pre>
|
||||
<p>
|
||||
Performs the square root of a floating point number. Uses a dedicated hardware instruction
|
||||
when available. Currently only supports f32 and f64 at runtime. f128 at runtime is TODO.
|
||||
when available. Supports f16, f32, f64, and f128, as well as vectors.
|
||||
</p>
|
||||
{#header_close#}
|
||||
{#header_open|@sin#}
|
||||
<pre>{#syntax#}@sin(comptime T: type, value: T) T{#endsyntax#}</pre>
|
||||
<p>
|
||||
This is a low-level intrinsic. Most code can use {#syntax#}std.math.sqrt{#endsyntax#} instead.
|
||||
Sine trigometric function on a floating point number. Uses a dedicated hardware instruction
|
||||
when available. Currently supports f32 and f64.
|
||||
</p>
|
||||
{#header_close#}
|
||||
{#header_open|@cos#}
|
||||
<pre>{#syntax#}@cos(comptime T: type, value: T) T{#endsyntax#}</pre>
|
||||
<p>
|
||||
Cosine trigometric function on a floating point number. Uses a dedicated hardware instruction
|
||||
when available. Currently supports f32 and f64.
|
||||
</p>
|
||||
{#header_close#}
|
||||
{#header_open|@exp#}
|
||||
<pre>{#syntax#}@exp(comptime T: type, value: T) T{#endsyntax#}</pre>
|
||||
<p>
|
||||
Base-e exponential function on a floating point number. Uses a dedicated hardware instruction
|
||||
when available. Currently supports f32 and f64.
|
||||
</p>
|
||||
{#header_close#}
|
||||
{#header_open|@exp2#}
|
||||
<pre>{#syntax#}@exp2(comptime T: type, value: T) T{#endsyntax#}</pre>
|
||||
<p>
|
||||
Base-2 exponential function on a floating point number. Uses a dedicated hardware instruction
|
||||
when available. Currently supports f32 and f64.
|
||||
</p>
|
||||
{#header_close#}
|
||||
{#header_open|@ln#}
|
||||
<pre>{#syntax#}@ln(comptime T: type, value: T) T{#endsyntax#}</pre>
|
||||
<p>
|
||||
Returns the natural logarithm of a floating point number. Uses a dedicated hardware instruction
|
||||
when available. Currently supports f32 and f64.
|
||||
</p>
|
||||
{#header_close#}
|
||||
{#header_open|@log2#}
|
||||
<pre>{#syntax#}@log2(comptime T: type, value: T) T{#endsyntax#}</pre>
|
||||
<p>
|
||||
Returns the logarithm to the base 2 of a floating point number. Uses a dedicated hardware instruction
|
||||
when available. Currently supports f32 and f64.
|
||||
</p>
|
||||
{#header_close#}
|
||||
{#header_open|@log10#}
|
||||
<pre>{#syntax#}@log10(comptime T: type, value: T) T{#endsyntax#}</pre>
|
||||
<p>
|
||||
Returns the logarithm to the base 10 of a floating point number. Uses a dedicated hardware instruction
|
||||
when available. Currently supports f32 and f64.
|
||||
</p>
|
||||
{#header_close#}
|
||||
{#header_open|@fabs#}
|
||||
<pre>{#syntax#}@fabs(comptime T: type, value: T) T{#endsyntax#}</pre>
|
||||
<p>
|
||||
Returns the absolute value of a floating point number. Uses a dedicated hardware instruction
|
||||
when available. Currently supports f32 and f64.
|
||||
</p>
|
||||
{#header_close#}
|
||||
{#header_open|@floor#}
|
||||
<pre>{#syntax#}@floor(comptime T: type, value: T) T{#endsyntax#}</pre>
|
||||
<p>
|
||||
Returns the largest integral value not greater than the given floating point number. Uses a dedicated hardware instruction
|
||||
when available. Currently supports f32 and f64.
|
||||
</p>
|
||||
{#header_close#}
|
||||
{#header_open|@ceil#}
|
||||
<pre>{#syntax#}@ceil(comptime T: type, value: T) T{#endsyntax#}</pre>
|
||||
<p>
|
||||
Returns the largest integral value not less than the given floating point number. Uses a dedicated hardware instruction
|
||||
when available. Currently supports f32 and f64.
|
||||
</p>
|
||||
{#header_close#}
|
||||
{#header_open|@trunc#}
|
||||
<pre>{#syntax#}@trunc(comptime T: type, value: T) T{#endsyntax#}</pre>
|
||||
<p>
|
||||
Rounds the given floating point number to an integer, towards zero. Uses a dedicated hardware instruction
|
||||
when available. Currently supports f32 and f64.
|
||||
</p>
|
||||
{#header_close#}
|
||||
{#header_open|@round#}
|
||||
<pre>{#syntax#}@round(comptime T: type, value: T) T{#endsyntax#}</pre>
|
||||
<p>
|
||||
Rounds the given floating point number to an integer, away from zero. Uses a dedicated hardware instruction
|
||||
when available. Currently supports f32 and f64.
|
||||
</p>
|
||||
{#header_close#}
|
||||
|
||||
|
||||
@ -1434,6 +1434,19 @@ enum BuiltinFnId {
|
||||
BuiltinFnIdRem,
|
||||
BuiltinFnIdMod,
|
||||
BuiltinFnIdSqrt,
|
||||
BuiltinFnIdSin,
|
||||
BuiltinFnIdCos,
|
||||
BuiltinFnIdExp,
|
||||
BuiltinFnIdExp2,
|
||||
BuiltinFnIdLn,
|
||||
BuiltinFnIdLog2,
|
||||
BuiltinFnIdLog10,
|
||||
BuiltinFnIdFabs,
|
||||
BuiltinFnIdFloor,
|
||||
BuiltinFnIdCeil,
|
||||
BuiltinFnIdTrunc,
|
||||
BuiltinFnIdNearbyInt,
|
||||
BuiltinFnIdRound,
|
||||
BuiltinFnIdTruncate,
|
||||
BuiltinFnIdIntCast,
|
||||
BuiltinFnIdFloatCast,
|
||||
@ -1556,9 +1569,7 @@ enum ZigLLVMFnId {
|
||||
ZigLLVMFnIdPopCount,
|
||||
ZigLLVMFnIdOverflowArithmetic,
|
||||
ZigLLVMFnIdFMA,
|
||||
ZigLLVMFnIdFloor,
|
||||
ZigLLVMFnIdCeil,
|
||||
ZigLLVMFnIdSqrt,
|
||||
ZigLLVMFnIdFloatOp,
|
||||
ZigLLVMFnIdBswap,
|
||||
ZigLLVMFnIdBitReverse,
|
||||
};
|
||||
@ -1585,6 +1596,7 @@ struct ZigLLVMFnKey {
|
||||
uint32_t bit_count;
|
||||
} pop_count;
|
||||
struct {
|
||||
BuiltinFnId op;
|
||||
uint32_t bit_count;
|
||||
uint32_t vector_len; // 0 means not a vector
|
||||
} floating;
|
||||
@ -2239,6 +2251,7 @@ enum IrInstructionId {
|
||||
IrInstructionIdAlignOf,
|
||||
IrInstructionIdOverflowOp,
|
||||
IrInstructionIdMulAdd,
|
||||
IrInstructionIdFloatOp,
|
||||
IrInstructionIdTestErr,
|
||||
IrInstructionIdUnwrapErrCode,
|
||||
IrInstructionIdUnwrapErrPayload,
|
||||
@ -2300,7 +2313,6 @@ enum IrInstructionId {
|
||||
IrInstructionIdAddImplicitReturnType,
|
||||
IrInstructionIdMergeErrRetTraces,
|
||||
IrInstructionIdMarkErrRetTracePtr,
|
||||
IrInstructionIdSqrt,
|
||||
IrInstructionIdErrSetCast,
|
||||
IrInstructionIdToBytes,
|
||||
IrInstructionIdFromBytes,
|
||||
@ -3474,11 +3486,13 @@ struct IrInstructionMarkErrRetTracePtr {
|
||||
IrInstruction *err_ret_trace_ptr;
|
||||
};
|
||||
|
||||
struct IrInstructionSqrt {
|
||||
// For float ops which take a single argument
|
||||
struct IrInstructionFloatOp {
|
||||
IrInstruction base;
|
||||
|
||||
BuiltinFnId op;
|
||||
IrInstruction *type;
|
||||
IrInstruction *op;
|
||||
IrInstruction *op1;
|
||||
};
|
||||
|
||||
struct IrInstructionCheckRuntimeScope {
|
||||
|
||||
@ -5736,9 +5736,10 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) {
|
||||
return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817;
|
||||
case ZigLLVMFnIdPopCount:
|
||||
return (uint32_t)(x.data.clz.bit_count) * (uint32_t)101195049;
|
||||
case ZigLLVMFnIdFloor:
|
||||
case ZigLLVMFnIdCeil:
|
||||
case ZigLLVMFnIdSqrt:
|
||||
case ZigLLVMFnIdFloatOp:
|
||||
return (uint32_t)(x.data.floating.bit_count) * ((uint32_t)x.id + 1025) +
|
||||
(uint32_t)(x.data.floating.vector_len) * (((uint32_t)x.id << 5) + 1025) +
|
||||
(uint32_t)(x.data.floating.op) * (uint32_t)43789879;
|
||||
case ZigLLVMFnIdFMA:
|
||||
return (uint32_t)(x.data.floating.bit_count) * ((uint32_t)x.id + 1025) +
|
||||
(uint32_t)(x.data.floating.vector_len) * (((uint32_t)x.id << 5) + 1025);
|
||||
@ -5769,10 +5770,10 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
|
||||
return a.data.bswap.bit_count == b.data.bswap.bit_count;
|
||||
case ZigLLVMFnIdBitReverse:
|
||||
return a.data.bit_reverse.bit_count == b.data.bit_reverse.bit_count;
|
||||
case ZigLLVMFnIdFloor:
|
||||
case ZigLLVMFnIdCeil:
|
||||
case ZigLLVMFnIdSqrt:
|
||||
return a.data.floating.bit_count == b.data.floating.bit_count;
|
||||
case ZigLLVMFnIdFloatOp:
|
||||
return a.data.floating.bit_count == b.data.floating.bit_count &&
|
||||
a.data.floating.vector_len == b.data.floating.vector_len &&
|
||||
a.data.floating.op == b.data.floating.op;
|
||||
case ZigLLVMFnIdFMA:
|
||||
return a.data.floating.bit_count == b.data.floating.bit_count &&
|
||||
a.data.floating.vector_len == b.data.floating.vector_len;
|
||||
|
||||
@ -806,7 +806,7 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, ZigType *operand_type, AddSu
|
||||
return fn_val;
|
||||
}
|
||||
|
||||
static LLVMValueRef get_float_fn(CodeGen *g, ZigType *type_entry, ZigLLVMFnId fn_id) {
|
||||
static LLVMValueRef get_float_fn(CodeGen *g, ZigType *type_entry, ZigLLVMFnId fn_id, BuiltinFnId op) {
|
||||
assert(type_entry->id == ZigTypeIdFloat ||
|
||||
type_entry->id == ZigTypeIdVector);
|
||||
|
||||
@ -817,6 +817,7 @@ static LLVMValueRef get_float_fn(CodeGen *g, ZigType *type_entry, ZigLLVMFnId fn
|
||||
key.id = fn_id;
|
||||
key.data.floating.bit_count = (uint32_t)float_type->data.floating.bit_count;
|
||||
key.data.floating.vector_len = is_vector ? (uint32_t)type_entry->data.vector.len : 0;
|
||||
key.data.floating.op = op;
|
||||
|
||||
auto existing_entry = g->llvm_fn_table.maybe_get(key);
|
||||
if (existing_entry)
|
||||
@ -824,18 +825,12 @@ static LLVMValueRef get_float_fn(CodeGen *g, ZigType *type_entry, ZigLLVMFnId fn
|
||||
|
||||
const char *name;
|
||||
uint32_t num_args;
|
||||
if (fn_id == ZigLLVMFnIdFloor) {
|
||||
name = "floor";
|
||||
num_args = 1;
|
||||
} else if (fn_id == ZigLLVMFnIdCeil) {
|
||||
name = "ceil";
|
||||
num_args = 1;
|
||||
} else if (fn_id == ZigLLVMFnIdSqrt) {
|
||||
name = "sqrt";
|
||||
num_args = 1;
|
||||
} else if (fn_id == ZigLLVMFnIdFMA) {
|
||||
if (fn_id == ZigLLVMFnIdFMA) {
|
||||
name = "fma";
|
||||
num_args = 3;
|
||||
} else if (fn_id == ZigLLVMFnIdFloatOp) {
|
||||
name = float_op_to_name(op, true);
|
||||
num_args = 1;
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -2480,22 +2475,17 @@ static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *type_entry,
|
||||
return result;
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_floor(CodeGen *g, LLVMValueRef val, ZigType *type_entry) {
|
||||
if (type_entry->id == ZigTypeIdInt)
|
||||
static LLVMValueRef gen_float_op(CodeGen *g, LLVMValueRef val, ZigType *type_entry, BuiltinFnId op) {
|
||||
if ((op == BuiltinFnIdCeil ||
|
||||
op == BuiltinFnIdFloor) &&
|
||||
type_entry->id == ZigTypeIdInt)
|
||||
return val;
|
||||
assert(type_entry->id == ZigTypeIdFloat);
|
||||
|
||||
LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloor);
|
||||
LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloatOp, op);
|
||||
return LLVMBuildCall(g->builder, floor_fn, &val, 1, "");
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_ceil(CodeGen *g, LLVMValueRef val, ZigType *type_entry) {
|
||||
if (type_entry->id == ZigTypeIdInt)
|
||||
return val;
|
||||
|
||||
LLVMValueRef ceil_fn = get_float_fn(g, type_entry, ZigLLVMFnIdCeil);
|
||||
return LLVMBuildCall(g->builder, ceil_fn, &val, 1, "");
|
||||
}
|
||||
|
||||
enum DivKind {
|
||||
DivKindFloat,
|
||||
DivKindTrunc,
|
||||
@ -2571,7 +2561,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast
|
||||
return result;
|
||||
case DivKindExact:
|
||||
if (want_runtime_safety) {
|
||||
LLVMValueRef floored = gen_floor(g, result, type_entry);
|
||||
LLVMValueRef floored = gen_float_op(g, result, type_entry, BuiltinFnIdFloor);
|
||||
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk");
|
||||
LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail");
|
||||
LLVMValueRef ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, floored, result, "");
|
||||
@ -2593,12 +2583,12 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast
|
||||
LLVMBuildCondBr(g->builder, ltz, ltz_block, gez_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, ltz_block);
|
||||
LLVMValueRef ceiled = gen_ceil(g, result, type_entry);
|
||||
LLVMValueRef ceiled = gen_float_op(g, result, type_entry, BuiltinFnIdCeil);
|
||||
LLVMBasicBlockRef ceiled_end_block = LLVMGetInsertBlock(g->builder);
|
||||
LLVMBuildBr(g->builder, end_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, gez_block);
|
||||
LLVMValueRef floored = gen_floor(g, result, type_entry);
|
||||
LLVMValueRef floored = gen_float_op(g, result, type_entry, BuiltinFnIdFloor);
|
||||
LLVMBasicBlockRef floored_end_block = LLVMGetInsertBlock(g->builder);
|
||||
LLVMBuildBr(g->builder, end_block);
|
||||
|
||||
@ -2610,7 +2600,7 @@ static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast
|
||||
return phi;
|
||||
}
|
||||
case DivKindFloor:
|
||||
return gen_floor(g, result, type_entry);
|
||||
return gen_float_op(g, result, type_entry, BuiltinFnIdFloor);
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -5450,10 +5440,10 @@ static LLVMValueRef ir_render_mark_err_ret_trace_ptr(CodeGen *g, IrExecutable *e
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_sqrt(CodeGen *g, IrExecutable *executable, IrInstructionSqrt *instruction) {
|
||||
LLVMValueRef op = ir_llvm_value(g, instruction->op);
|
||||
static LLVMValueRef ir_render_float_op(CodeGen *g, IrExecutable *executable, IrInstructionFloatOp *instruction) {
|
||||
LLVMValueRef op = ir_llvm_value(g, instruction->op1);
|
||||
assert(instruction->base.value.type->id == ZigTypeIdFloat);
|
||||
LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdSqrt);
|
||||
LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdFloatOp, instruction->op);
|
||||
return LLVMBuildCall(g->builder, fn_val, &op, 1, "");
|
||||
}
|
||||
|
||||
@ -5463,7 +5453,7 @@ static LLVMValueRef ir_render_mul_add(CodeGen *g, IrExecutable *executable, IrIn
|
||||
LLVMValueRef op3 = ir_llvm_value(g, instruction->op3);
|
||||
assert(instruction->base.value.type->id == ZigTypeIdFloat ||
|
||||
instruction->base.value.type->id == ZigTypeIdVector);
|
||||
LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdFMA);
|
||||
LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdFMA, BuiltinFnIdMulAdd);
|
||||
LLVMValueRef args[3] = {
|
||||
op1,
|
||||
op2,
|
||||
@ -5814,8 +5804,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
||||
return ir_render_merge_err_ret_traces(g, executable, (IrInstructionMergeErrRetTraces *)instruction);
|
||||
case IrInstructionIdMarkErrRetTracePtr:
|
||||
return ir_render_mark_err_ret_trace_ptr(g, executable, (IrInstructionMarkErrRetTracePtr *)instruction);
|
||||
case IrInstructionIdSqrt:
|
||||
return ir_render_sqrt(g, executable, (IrInstructionSqrt *)instruction);
|
||||
case IrInstructionIdFloatOp:
|
||||
return ir_render_float_op(g, executable, (IrInstructionFloatOp *)instruction);
|
||||
case IrInstructionIdMulAdd:
|
||||
return ir_render_mul_add(g, executable, (IrInstructionMulAdd *)instruction);
|
||||
case IrInstructionIdArrayToVector:
|
||||
@ -7435,6 +7425,20 @@ static void define_builtin_fns(CodeGen *g) {
|
||||
create_builtin_fn(g, BuiltinFnIdRem, "rem", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdMod, "mod", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdSqrt, "sqrt", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdSin, "sin", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdCos, "cos", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdExp, "exp", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdExp2, "exp2", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdLn, "ln", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdLog2, "log2", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdLog10, "log10", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdFabs, "fabs", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdFloor, "floor", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdCeil, "ceil", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdTrunc, "trunc", 2);
|
||||
//Needs library support on Windows
|
||||
//create_builtin_fn(g, BuiltinFnIdNearbyInt, "nearbyInt", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdRound, "round", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdMulAdd, "mulAdd", 4);
|
||||
create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX);
|
||||
create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX);
|
||||
|
||||
365
src/ir.cpp
365
src/ir.cpp
@ -991,8 +991,8 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionMarkErrRetTraceP
|
||||
return IrInstructionIdMarkErrRetTracePtr;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionSqrt *) {
|
||||
return IrInstructionIdSqrt;
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionFloatOp *) {
|
||||
return IrInstructionIdFloatOp;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckRuntimeScope *) {
|
||||
@ -2312,6 +2312,59 @@ static IrInstruction *ir_build_overflow_op(IrBuilder *irb, Scope *scope, AstNode
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
|
||||
//TODO Powi, Pow, minnum, maxnum, maximum, minimum, copysign,
|
||||
// lround, llround, lrint, llrint
|
||||
// So far this is only non-complicated type functions.
|
||||
const char *float_op_to_name(BuiltinFnId op, bool llvm_name) {
|
||||
const bool b = llvm_name;
|
||||
|
||||
switch (op) {
|
||||
case BuiltinFnIdSqrt:
|
||||
return "sqrt";
|
||||
case BuiltinFnIdSin:
|
||||
return "sin";
|
||||
case BuiltinFnIdCos:
|
||||
return "cos";
|
||||
case BuiltinFnIdExp:
|
||||
return "exp";
|
||||
case BuiltinFnIdExp2:
|
||||
return "exp2";
|
||||
case BuiltinFnIdLn:
|
||||
return b ? "log" : "ln";
|
||||
case BuiltinFnIdLog10:
|
||||
return "log10";
|
||||
case BuiltinFnIdLog2:
|
||||
return "log2";
|
||||
case BuiltinFnIdFabs:
|
||||
return "fabs";
|
||||
case BuiltinFnIdFloor:
|
||||
return "floor";
|
||||
case BuiltinFnIdCeil:
|
||||
return "ceil";
|
||||
case BuiltinFnIdTrunc:
|
||||
return "trunc";
|
||||
case BuiltinFnIdNearbyInt:
|
||||
return b ? "nearbyint" : "nearbyInt";
|
||||
case BuiltinFnIdRound:
|
||||
return "round";
|
||||
default:
|
||||
zig_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_float_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op1, BuiltinFnId op) {
|
||||
IrInstructionFloatOp *instruction = ir_build_instruction<IrInstructionFloatOp>(irb, scope, source_node);
|
||||
instruction->type = type;
|
||||
instruction->op1 = op1;
|
||||
instruction->op = op;
|
||||
|
||||
if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block);
|
||||
ir_ref_instruction(op1, irb->current_basic_block);
|
||||
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_mul_add(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||
IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2, IrInstruction *op3) {
|
||||
IrInstructionMulAdd *instruction = ir_build_instruction<IrInstructionMulAdd>(irb, scope, source_node);
|
||||
@ -3033,17 +3086,6 @@ static IrInstruction *ir_build_mark_err_ret_trace_ptr(IrBuilder *irb, Scope *sco
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_sqrt(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op) {
|
||||
IrInstructionSqrt *instruction = ir_build_instruction<IrInstructionSqrt>(irb, scope, source_node);
|
||||
instruction->type = type;
|
||||
instruction->op = op;
|
||||
|
||||
if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block);
|
||||
ir_ref_instruction(op, irb->current_basic_block);
|
||||
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_has_decl(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||
IrInstruction *container, IrInstruction *name)
|
||||
{
|
||||
@ -4400,6 +4442,19 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
|
||||
return ir_lval_wrap(irb, scope, bin_op, lval);
|
||||
}
|
||||
case BuiltinFnIdSqrt:
|
||||
case BuiltinFnIdSin:
|
||||
case BuiltinFnIdCos:
|
||||
case BuiltinFnIdExp:
|
||||
case BuiltinFnIdExp2:
|
||||
case BuiltinFnIdLn:
|
||||
case BuiltinFnIdLog2:
|
||||
case BuiltinFnIdLog10:
|
||||
case BuiltinFnIdFabs:
|
||||
case BuiltinFnIdFloor:
|
||||
case BuiltinFnIdCeil:
|
||||
case BuiltinFnIdTrunc:
|
||||
case BuiltinFnIdNearbyInt:
|
||||
case BuiltinFnIdRound:
|
||||
{
|
||||
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
||||
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
||||
@ -4411,7 +4466,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
|
||||
if (arg1_value == irb->codegen->invalid_instruction)
|
||||
return arg1_value;
|
||||
|
||||
IrInstruction *ir_sqrt = ir_build_sqrt(irb, scope, node, arg0_value, arg1_value);
|
||||
IrInstruction *ir_sqrt = ir_build_float_op(irb, scope, node, arg0_value, arg1_value, builtin_fn->id);
|
||||
return ir_lval_wrap(irb, scope, ir_sqrt, lval);
|
||||
}
|
||||
case BuiltinFnIdTruncate:
|
||||
@ -23214,70 +23269,248 @@ static IrInstruction *ir_analyze_instruction_mark_err_ret_trace_ptr(IrAnalyze *i
|
||||
return result;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstructionSqrt *instruction) {
|
||||
ZigType *float_type = ir_resolve_type(ira, instruction->type->child);
|
||||
if (type_is_invalid(float_type))
|
||||
static void ir_eval_float_op(IrAnalyze *ira, IrInstructionFloatOp *source_instr, ZigType *float_type,
|
||||
ConstExprValue *op, ConstExprValue *out_val) {
|
||||
assert(ira && source_instr && float_type && out_val && op);
|
||||
assert(float_type->id == ZigTypeIdFloat ||
|
||||
float_type->id == ZigTypeIdComptimeFloat);
|
||||
|
||||
BuiltinFnId fop = source_instr->op;
|
||||
unsigned bits;
|
||||
|
||||
if (float_type->id == ZigTypeIdComptimeFloat) {
|
||||
bits = 128;
|
||||
} else if (float_type->id == ZigTypeIdFloat)
|
||||
bits = float_type->data.floating.bit_count;
|
||||
|
||||
switch (bits) {
|
||||
case 16: {
|
||||
switch (fop) {
|
||||
case BuiltinFnIdSqrt:
|
||||
out_val->data.x_f16 = f16_sqrt(op->data.x_f16);
|
||||
break;
|
||||
case BuiltinFnIdSin:
|
||||
case BuiltinFnIdCos:
|
||||
case BuiltinFnIdExp:
|
||||
case BuiltinFnIdExp2:
|
||||
case BuiltinFnIdLn:
|
||||
case BuiltinFnIdLog10:
|
||||
case BuiltinFnIdLog2:
|
||||
case BuiltinFnIdFabs:
|
||||
case BuiltinFnIdFloor:
|
||||
case BuiltinFnIdCeil:
|
||||
case BuiltinFnIdTrunc:
|
||||
case BuiltinFnIdNearbyInt:
|
||||
case BuiltinFnIdRound:
|
||||
zig_panic("unimplemented f16 builtin");
|
||||
default:
|
||||
zig_unreachable();
|
||||
};
|
||||
break;
|
||||
};
|
||||
case 32: {
|
||||
switch (fop) {
|
||||
case BuiltinFnIdSqrt:
|
||||
out_val->data.x_f32 = sqrtf(op->data.x_f32);
|
||||
break;
|
||||
case BuiltinFnIdSin:
|
||||
out_val->data.x_f32 = sinf(op->data.x_f32);
|
||||
break;
|
||||
case BuiltinFnIdCos:
|
||||
out_val->data.x_f32 = cosf(op->data.x_f32);
|
||||
break;
|
||||
case BuiltinFnIdExp:
|
||||
out_val->data.x_f32 = expf(op->data.x_f32);
|
||||
break;
|
||||
case BuiltinFnIdExp2:
|
||||
out_val->data.x_f32 = exp2f(op->data.x_f32);
|
||||
break;
|
||||
case BuiltinFnIdLn:
|
||||
out_val->data.x_f32 = logf(op->data.x_f32);
|
||||
break;
|
||||
case BuiltinFnIdLog10:
|
||||
out_val->data.x_f32 = log10f(op->data.x_f32);
|
||||
break;
|
||||
case BuiltinFnIdLog2:
|
||||
out_val->data.x_f32 = log2f(op->data.x_f32);
|
||||
break;
|
||||
case BuiltinFnIdFabs:
|
||||
out_val->data.x_f32 = fabsf(op->data.x_f32);
|
||||
break;
|
||||
case BuiltinFnIdFloor:
|
||||
out_val->data.x_f32 = floorf(op->data.x_f32);
|
||||
break;
|
||||
case BuiltinFnIdCeil:
|
||||
out_val->data.x_f32 = ceilf(op->data.x_f32);
|
||||
break;
|
||||
case BuiltinFnIdTrunc:
|
||||
out_val->data.x_f32 = truncf(op->data.x_f32);
|
||||
break;
|
||||
case BuiltinFnIdNearbyInt:
|
||||
out_val->data.x_f32 = nearbyintf(op->data.x_f32);
|
||||
break;
|
||||
case BuiltinFnIdRound:
|
||||
out_val->data.x_f32 = roundf(op->data.x_f32);
|
||||
break;
|
||||
default:
|
||||
zig_unreachable();
|
||||
};
|
||||
break;
|
||||
};
|
||||
case 64: {
|
||||
switch (fop) {
|
||||
case BuiltinFnIdSqrt:
|
||||
out_val->data.x_f64 = sqrt(op->data.x_f64);
|
||||
break;
|
||||
case BuiltinFnIdSin:
|
||||
out_val->data.x_f64 = sin(op->data.x_f64);
|
||||
break;
|
||||
case BuiltinFnIdCos:
|
||||
out_val->data.x_f64 = cos(op->data.x_f64);
|
||||
break;
|
||||
case BuiltinFnIdExp:
|
||||
out_val->data.x_f64 = exp(op->data.x_f64);
|
||||
break;
|
||||
case BuiltinFnIdExp2:
|
||||
out_val->data.x_f64 = exp2(op->data.x_f64);
|
||||
break;
|
||||
case BuiltinFnIdLn:
|
||||
out_val->data.x_f64 = log(op->data.x_f64);
|
||||
break;
|
||||
case BuiltinFnIdLog10:
|
||||
out_val->data.x_f64 = log10(op->data.x_f64);
|
||||
break;
|
||||
case BuiltinFnIdLog2:
|
||||
out_val->data.x_f64 = log2(op->data.x_f64);
|
||||
break;
|
||||
case BuiltinFnIdFabs:
|
||||
out_val->data.x_f64 = fabs(op->data.x_f64);
|
||||
break;
|
||||
case BuiltinFnIdFloor:
|
||||
out_val->data.x_f64 = floor(op->data.x_f64);
|
||||
break;
|
||||
case BuiltinFnIdCeil:
|
||||
out_val->data.x_f64 = ceil(op->data.x_f64);
|
||||
break;
|
||||
case BuiltinFnIdTrunc:
|
||||
out_val->data.x_f64 = trunc(op->data.x_f64);
|
||||
break;
|
||||
case BuiltinFnIdNearbyInt:
|
||||
out_val->data.x_f64 = nearbyint(op->data.x_f64);
|
||||
break;
|
||||
case BuiltinFnIdRound:
|
||||
out_val->data.x_f64 = round(op->data.x_f64);
|
||||
break;
|
||||
default:
|
||||
zig_unreachable();
|
||||
}
|
||||
break;
|
||||
};
|
||||
case 128: {
|
||||
float128_t *out, *in;
|
||||
if (float_type->id == ZigTypeIdComptimeFloat) {
|
||||
out = &out_val->data.x_bigfloat.value;
|
||||
in = &op->data.x_bigfloat.value;
|
||||
} else {
|
||||
out = &out_val->data.x_f128;
|
||||
in = &op->data.x_f128;
|
||||
}
|
||||
switch (fop) {
|
||||
case BuiltinFnIdSqrt:
|
||||
f128M_sqrt(in, out);
|
||||
break;
|
||||
case BuiltinFnIdNearbyInt:
|
||||
case BuiltinFnIdSin:
|
||||
case BuiltinFnIdCos:
|
||||
case BuiltinFnIdExp:
|
||||
case BuiltinFnIdExp2:
|
||||
case BuiltinFnIdLn:
|
||||
case BuiltinFnIdLog10:
|
||||
case BuiltinFnIdLog2:
|
||||
case BuiltinFnIdFabs:
|
||||
case BuiltinFnIdFloor:
|
||||
case BuiltinFnIdCeil:
|
||||
case BuiltinFnIdTrunc:
|
||||
case BuiltinFnIdRound:
|
||||
zig_panic("unimplemented f128 builtin");
|
||||
default:
|
||||
zig_unreachable();
|
||||
}
|
||||
break;
|
||||
};
|
||||
default:
|
||||
zig_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
static IrInstruction *ir_analyze_instruction_float_op(IrAnalyze *ira, IrInstructionFloatOp *instruction) {
|
||||
IrInstruction *type = instruction->type->child;
|
||||
if (type_is_invalid(type->value.type))
|
||||
return ira->codegen->invalid_instruction;
|
||||
|
||||
ZigType *expr_type = ir_resolve_type(ira, type);
|
||||
if (type_is_invalid(expr_type))
|
||||
return ira->codegen->invalid_instruction;
|
||||
|
||||
IrInstruction *op = instruction->op->child;
|
||||
if (type_is_invalid(op->value.type))
|
||||
return ira->codegen->invalid_instruction;
|
||||
|
||||
bool ok_type = float_type->id == ZigTypeIdComptimeFloat || float_type->id == ZigTypeIdFloat;
|
||||
if (!ok_type) {
|
||||
ir_add_error(ira, instruction->type, buf_sprintf("@sqrt does not support type '%s'", buf_ptr(&float_type->name)));
|
||||
// Only allow float types, and vectors of floats.
|
||||
ZigType *float_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type;
|
||||
if (float_type->id != ZigTypeIdFloat && float_type->id != ZigTypeIdComptimeFloat) {
|
||||
ir_add_error(ira, instruction->type, buf_sprintf("@%s does not support type '%s'", float_op_to_name(instruction->op, false), buf_ptr(&float_type->name)));
|
||||
return ira->codegen->invalid_instruction;
|
||||
}
|
||||
|
||||
IrInstruction *casted_op = ir_implicit_cast(ira, op, float_type);
|
||||
if (type_is_invalid(casted_op->value.type))
|
||||
IrInstruction *op1 = instruction->op1->child;
|
||||
if (type_is_invalid(op1->value.type))
|
||||
return ira->codegen->invalid_instruction;
|
||||
|
||||
if (instr_is_comptime(casted_op)) {
|
||||
ConstExprValue *val = ir_resolve_const(ira, casted_op, UndefBad);
|
||||
if (!val)
|
||||
IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, float_type);
|
||||
if (type_is_invalid(casted_op1->value.type))
|
||||
return ira->codegen->invalid_instruction;
|
||||
|
||||
if (instr_is_comptime(casted_op1)) {
|
||||
// Our comptime 16-bit and 128-bit support is quite limited.
|
||||
if ((float_type->id == ZigTypeIdComptimeFloat ||
|
||||
float_type->data.floating.bit_count == 16 ||
|
||||
float_type->data.floating.bit_count == 128) &&
|
||||
instruction->op != BuiltinFnIdSqrt) {
|
||||
ir_add_error(ira, instruction->type, buf_sprintf("@%s does not support type '%s'", float_op_to_name(instruction->op, false), buf_ptr(&float_type->name)));
|
||||
return ira->codegen->invalid_instruction;
|
||||
|
||||
IrInstruction *result = ir_const(ira, &instruction->base, float_type);
|
||||
ConstExprValue *out_val = &result->value;
|
||||
|
||||
if (float_type->id == ZigTypeIdComptimeFloat) {
|
||||
bigfloat_sqrt(&out_val->data.x_bigfloat, &val->data.x_bigfloat);
|
||||
} else if (float_type->id == ZigTypeIdFloat) {
|
||||
switch (float_type->data.floating.bit_count) {
|
||||
case 16:
|
||||
out_val->data.x_f16 = f16_sqrt(val->data.x_f16);
|
||||
break;
|
||||
case 32:
|
||||
out_val->data.x_f32 = sqrtf(val->data.x_f32);
|
||||
break;
|
||||
case 64:
|
||||
out_val->data.x_f64 = sqrt(val->data.x_f64);
|
||||
break;
|
||||
case 128:
|
||||
f128M_sqrt(&val->data.x_f128, &out_val->data.x_f128);
|
||||
break;
|
||||
default:
|
||||
zig_unreachable();
|
||||
}
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
ConstExprValue *op1_const = ir_resolve_const(ira, casted_op1, UndefBad);
|
||||
if (!op1_const)
|
||||
return ira->codegen->invalid_instruction;
|
||||
|
||||
IrInstruction *result = ir_const(ira, &instruction->base, expr_type);
|
||||
ConstExprValue *out_val = &result->value;
|
||||
|
||||
if (expr_type->id == ZigTypeIdVector) {
|
||||
expand_undef_array(ira->codegen, op1_const);
|
||||
out_val->special = ConstValSpecialUndef;
|
||||
expand_undef_array(ira->codegen, out_val);
|
||||
size_t len = expr_type->data.vector.len;
|
||||
for (size_t i = 0; i < len; i += 1) {
|
||||
ConstExprValue *float_operand_op1 = &op1_const->data.x_array.data.s_none.elements[i];
|
||||
ConstExprValue *float_out_val = &out_val->data.x_array.data.s_none.elements[i];
|
||||
assert(float_operand_op1->type == float_type);
|
||||
assert(float_out_val->type == float_type);
|
||||
ir_eval_float_op(ira, instruction, float_type,
|
||||
op1_const, float_out_val);
|
||||
float_out_val->type = float_type;
|
||||
}
|
||||
out_val->type = expr_type;
|
||||
out_val->special = ConstValSpecialStatic;
|
||||
} else {
|
||||
ir_eval_float_op(ira, instruction, float_type, op1_const, out_val);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ir_assert(float_type->id == ZigTypeIdFloat, &instruction->base);
|
||||
if (float_type->data.floating.bit_count != 16 &&
|
||||
float_type->data.floating.bit_count != 32 &&
|
||||
float_type->data.floating.bit_count != 64) {
|
||||
ir_add_error(ira, instruction->type, buf_sprintf("compiler TODO: add implementation of sqrt for '%s'", buf_ptr(&float_type->name)));
|
||||
return ira->codegen->invalid_instruction;
|
||||
}
|
||||
|
||||
IrInstruction *result = ir_build_sqrt(&ira->new_irb, instruction->base.scope,
|
||||
instruction->base.source_node, nullptr, casted_op);
|
||||
result->value.type = float_type;
|
||||
IrInstruction *result = ir_build_float_op(&ira->new_irb, instruction->base.scope,
|
||||
instruction->base.source_node, nullptr, casted_op1, instruction->op);
|
||||
result->value.type = expr_type;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -23762,8 +23995,8 @@ static IrInstruction *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructio
|
||||
return ir_analyze_instruction_merge_err_ret_traces(ira, (IrInstructionMergeErrRetTraces *)instruction);
|
||||
case IrInstructionIdMarkErrRetTracePtr:
|
||||
return ir_analyze_instruction_mark_err_ret_trace_ptr(ira, (IrInstructionMarkErrRetTracePtr *)instruction);
|
||||
case IrInstructionIdSqrt:
|
||||
return ir_analyze_instruction_sqrt(ira, (IrInstructionSqrt *)instruction);
|
||||
case IrInstructionIdFloatOp:
|
||||
return ir_analyze_instruction_float_op(ira, (IrInstructionFloatOp *)instruction);
|
||||
case IrInstructionIdMulAdd:
|
||||
return ir_analyze_instruction_mul_add(ira, (IrInstructionMulAdd *)instruction);
|
||||
case IrInstructionIdIntToErr:
|
||||
@ -24004,7 +24237,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
||||
case IrInstructionIdCoroFree:
|
||||
case IrInstructionIdCoroPromise:
|
||||
case IrInstructionIdPromiseResultType:
|
||||
case IrInstructionIdSqrt:
|
||||
case IrInstructionIdFloatOp:
|
||||
case IrInstructionIdMulAdd:
|
||||
case IrInstructionIdAtomicLoad:
|
||||
case IrInstructionIdIntCast:
|
||||
|
||||
@ -26,5 +26,6 @@ bool ir_has_side_effects(IrInstruction *instruction);
|
||||
struct IrAnalyze;
|
||||
ConstExprValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ConstExprValue *const_val,
|
||||
AstNode *source_node);
|
||||
const char *float_op_to_name(BuiltinFnId op, bool llvm_name);
|
||||
|
||||
#endif
|
||||
|
||||
@ -1427,15 +1427,16 @@ static void ir_print_mark_err_ret_trace_ptr(IrPrint *irp, IrInstructionMarkErrRe
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_sqrt(IrPrint *irp, IrInstructionSqrt *instruction) {
|
||||
fprintf(irp->f, "@sqrt(");
|
||||
static void ir_print_float_op(IrPrint *irp, IrInstructionFloatOp *instruction) {
|
||||
|
||||
fprintf(irp->f, "@%s(", float_op_to_name(instruction->op, false));
|
||||
if (instruction->type != nullptr) {
|
||||
ir_print_other_instruction(irp, instruction->type);
|
||||
} else {
|
||||
fprintf(irp->f, "null");
|
||||
}
|
||||
fprintf(irp->f, ",");
|
||||
ir_print_other_instruction(irp, instruction->op);
|
||||
ir_print_other_instruction(irp, instruction->op1);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
@ -1918,8 +1919,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
case IrInstructionIdMarkErrRetTracePtr:
|
||||
ir_print_mark_err_ret_trace_ptr(irp, (IrInstructionMarkErrRetTracePtr *)instruction);
|
||||
break;
|
||||
case IrInstructionIdSqrt:
|
||||
ir_print_sqrt(irp, (IrInstructionSqrt *)instruction);
|
||||
case IrInstructionIdFloatOp:
|
||||
ir_print_float_op(irp, (IrInstructionFloatOp *)instruction);
|
||||
break;
|
||||
case IrInstructionIdMulAdd:
|
||||
ir_print_mul_add(irp, (IrInstructionMulAdd *)instruction);
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
#include "userland.h"
|
||||
|
||||
void zig_panic(const char *format, ...) {
|
||||
abort();
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
vfprintf(stderr, format, ap);
|
||||
|
||||
@ -254,24 +254,32 @@ export fn fmod(x: f64, y: f64) f64 {
|
||||
|
||||
// TODO add intrinsics for these (and probably the double version too)
|
||||
// and have the math stuff use the intrinsic. same as @mod and @rem
|
||||
export fn floorf(x: f32) f32 {
|
||||
return math.floor(x);
|
||||
}
|
||||
export fn ceilf(x: f32) f32 {
|
||||
return math.ceil(x);
|
||||
}
|
||||
export fn floor(x: f64) f64 {
|
||||
return math.floor(x);
|
||||
}
|
||||
export fn ceil(x: f64) f64 {
|
||||
return math.ceil(x);
|
||||
}
|
||||
export fn fma(a: f64, b: f64, c: f64) f64 {
|
||||
return math.fma(f64, a, b, c);
|
||||
}
|
||||
export fn fmaf(a: f32, b: f32, c: f32) f32 {
|
||||
return math.fma(f32, a, b, c);
|
||||
}
|
||||
export fn floorf(x: f32) f32 {return math.floor(x);}
|
||||
export fn ceilf(x: f32) f32 {return math.ceil(x);}
|
||||
export fn floor(x: f64) f64 {return math.floor(x);}
|
||||
export fn ceil(x: f64) f64 {return math.ceil(x);}
|
||||
export fn fma(a: f64, b: f64, c: f64) f64 {return math.fma(f64, a, b, c);}
|
||||
export fn fmaf(a: f32, b: f32, c: f32) f32 {return math.fma(f32, a, b, c);}
|
||||
export fn sin(a: f64) f64 {return math.sin(a);}
|
||||
export fn sinf(a: f32) f32 {return math.sin(a);}
|
||||
export fn cos(a: f64) f64 {return math.cos(a);}
|
||||
export fn cosf(a: f32) f32 {return math.cos(a);}
|
||||
export fn exp(a: f64) f64 {return math.exp(a);}
|
||||
export fn expf(a: f32) f32 {return math.exp(a);}
|
||||
export fn exp2(a: f64) f64 {return math.exp2(a);}
|
||||
export fn exp2f(a: f32) f32 {return math.exp2(a);}
|
||||
export fn log(a: f64) f64 {return math.ln(a);}
|
||||
export fn logf(a: f32) f32 {return math.ln(a);}
|
||||
export fn log2(a: f64) f64 {return math.log2(a);}
|
||||
export fn log2f(a: f32) f32 {return math.log2(a);}
|
||||
export fn log10(a: f64) f64 {return math.log10(a);}
|
||||
export fn log10f(a: f32) f32 {return math.log10(a);}
|
||||
export fn fabs(a: f64) f64 {return math.fabs(a);}
|
||||
export fn fabsf(a: f32) f32 {return math.fabs(a);}
|
||||
export fn trunc(a: f64) f64 {return math.trunc(a);}
|
||||
export fn truncf(a: f32) f32 {return math.trunc(a);}
|
||||
export fn round(a: f64) f64 {return math.round(a);}
|
||||
export fn roundf(a: f32) f32 {return math.round(a);}
|
||||
fn generic_fmod(comptime T: type, x: T, y: T) T {
|
||||
@setRuntimeSafety(false);
|
||||
|
||||
|
||||
@ -71,6 +71,7 @@ comptime {
|
||||
_ = @import("behavior/pointers.zig");
|
||||
_ = @import("behavior/popcount.zig");
|
||||
_ = @import("behavior/muladd.zig");
|
||||
_ = @import("behavior/floatop.zig");
|
||||
_ = @import("behavior/ptrcast.zig");
|
||||
_ = @import("behavior/pub_enum.zig");
|
||||
_ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig");
|
||||
|
||||
243
test/stage1/behavior/floatop.zig
Normal file
243
test/stage1/behavior/floatop.zig
Normal file
@ -0,0 +1,243 @@
|
||||
const expect = @import("std").testing.expect;
|
||||
const pi = @import("std").math.pi;
|
||||
const e = @import("std").math.e;
|
||||
|
||||
test "@sqrt" {
|
||||
comptime testSqrt();
|
||||
testSqrt();
|
||||
}
|
||||
|
||||
fn testSqrt() void {
|
||||
{
|
||||
var a: f16 = 4;
|
||||
expect(@sqrt(f16, a) == 2);
|
||||
}
|
||||
{
|
||||
var a: f32 = 9;
|
||||
expect(@sqrt(f32, a) == 3);
|
||||
}
|
||||
{
|
||||
var a: f64 = 25;
|
||||
expect(@sqrt(f64, a) == 5);
|
||||
}
|
||||
{
|
||||
const a: comptime_float = 25.0;
|
||||
expect(@sqrt(comptime_float, a) == 5.0);
|
||||
}
|
||||
// Waiting on a c.zig implementation
|
||||
//{
|
||||
// var a: f128 = 49;
|
||||
// expect(@sqrt(f128, a) == 7);
|
||||
//}
|
||||
}
|
||||
|
||||
test "@sin" {
|
||||
comptime testSin();
|
||||
testSin();
|
||||
}
|
||||
|
||||
fn testSin() void {
|
||||
// TODO - this is actually useful and should be implemented
|
||||
// (all the trig functions for f16)
|
||||
// but will probably wait till self-hosted
|
||||
//{
|
||||
// var a: f16 = pi;
|
||||
// expect(@sin(f16, a/2) == 1);
|
||||
//}
|
||||
{
|
||||
var a: f32 = 0;
|
||||
expect(@sin(f32, a) == 0);
|
||||
}
|
||||
{
|
||||
var a: f64 = 0;
|
||||
expect(@sin(f64, a) == 0);
|
||||
}
|
||||
// TODO
|
||||
//{
|
||||
// var a: f16 = pi;
|
||||
// expect(@sqrt(f128, a/2) == 1);
|
||||
//}
|
||||
}
|
||||
|
||||
test "@cos" {
|
||||
comptime testCos();
|
||||
testCos();
|
||||
}
|
||||
|
||||
fn testCos() void {
|
||||
{
|
||||
var a: f32 = 0;
|
||||
expect(@cos(f32, a) == 1);
|
||||
}
|
||||
{
|
||||
var a: f64 = 0;
|
||||
expect(@cos(f64, a) == 1);
|
||||
}
|
||||
}
|
||||
|
||||
test "@exp" {
|
||||
comptime testExp();
|
||||
testExp();
|
||||
}
|
||||
|
||||
fn testExp() void {
|
||||
{
|
||||
var a: f32 = 0;
|
||||
expect(@exp(f32, a) == 1);
|
||||
}
|
||||
{
|
||||
var a: f64 = 0;
|
||||
expect(@exp(f64, a) == 1);
|
||||
}
|
||||
}
|
||||
|
||||
test "@exp2" {
|
||||
comptime testExp2();
|
||||
testExp2();
|
||||
}
|
||||
|
||||
fn testExp2() void {
|
||||
{
|
||||
var a: f32 = 2;
|
||||
expect(@exp2(f32, a) == 4);
|
||||
}
|
||||
{
|
||||
var a: f64 = 2;
|
||||
expect(@exp2(f64, a) == 4);
|
||||
}
|
||||
}
|
||||
|
||||
test "@ln" {
|
||||
// Old musl (and glibc?), and our current math.ln implementation do not return 1
|
||||
// so also accept those values.
|
||||
comptime testLn();
|
||||
testLn();
|
||||
}
|
||||
|
||||
fn testLn() void {
|
||||
{
|
||||
var a: f32 = e;
|
||||
expect(@ln(f32, a) == 1 or @ln(f32, a) == @bitCast(f32, u32(0x3f7fffff)));
|
||||
}
|
||||
{
|
||||
var a: f64 = e;
|
||||
expect(@ln(f64, a) == 1 or @ln(f64, a) == @bitCast(f64, u64(0x3ff0000000000000)));
|
||||
}
|
||||
}
|
||||
|
||||
test "@log2" {
|
||||
comptime testLog2();
|
||||
testLog2();
|
||||
}
|
||||
|
||||
fn testLog2() void {
|
||||
{
|
||||
var a: f32 = 4;
|
||||
expect(@log2(f32, a) == 2);
|
||||
}
|
||||
{
|
||||
var a: f64 = 4;
|
||||
expect(@log2(f64, a) == 2);
|
||||
}
|
||||
}
|
||||
|
||||
test "@log10" {
|
||||
comptime testLog10();
|
||||
testLog10();
|
||||
}
|
||||
|
||||
fn testLog10() void {
|
||||
{
|
||||
var a: f32 = 100;
|
||||
expect(@log10(f32, a) == 2);
|
||||
}
|
||||
{
|
||||
var a: f64 = 1000;
|
||||
expect(@log10(f64, a) == 3);
|
||||
}
|
||||
}
|
||||
|
||||
test "@fabs" {
|
||||
comptime testFabs();
|
||||
testFabs();
|
||||
}
|
||||
|
||||
fn testFabs() void {
|
||||
{
|
||||
var a: f32 = -2.5;
|
||||
var b: f32 = 2.5;
|
||||
expect(@fabs(f32, a) == 2.5);
|
||||
expect(@fabs(f32, b) == 2.5);
|
||||
}
|
||||
{
|
||||
var a: f64 = -2.5;
|
||||
var b: f64 = 2.5;
|
||||
expect(@fabs(f64, a) == 2.5);
|
||||
expect(@fabs(f64, b) == 2.5);
|
||||
}
|
||||
}
|
||||
|
||||
test "@floor" {
|
||||
comptime testFloor();
|
||||
testFloor();
|
||||
}
|
||||
|
||||
fn testFloor() void {
|
||||
{
|
||||
var a: f32 = 2.1;
|
||||
expect(@floor(f32, a) == 2);
|
||||
}
|
||||
{
|
||||
var a: f64 = 3.5;
|
||||
expect(@floor(f64, a) == 3);
|
||||
}
|
||||
}
|
||||
|
||||
test "@ceil" {
|
||||
comptime testCeil();
|
||||
testCeil();
|
||||
}
|
||||
|
||||
fn testCeil() void {
|
||||
{
|
||||
var a: f32 = 2.1;
|
||||
expect(@ceil(f32, a) == 3);
|
||||
}
|
||||
{
|
||||
var a: f64 = 3.5;
|
||||
expect(@ceil(f64, a) == 4);
|
||||
}
|
||||
}
|
||||
|
||||
test "@trunc" {
|
||||
comptime testTrunc();
|
||||
testTrunc();
|
||||
}
|
||||
|
||||
fn testTrunc() void {
|
||||
{
|
||||
var a: f32 = 2.1;
|
||||
expect(@trunc(f32, a) == 2);
|
||||
}
|
||||
{
|
||||
var a: f64 = -3.5;
|
||||
expect(@trunc(f64, a) == -3);
|
||||
}
|
||||
}
|
||||
|
||||
// This is waiting on library support for the Windows build (not sure why the other's don't need it)
|
||||
//test "@nearbyInt" {
|
||||
// comptime testNearbyInt();
|
||||
// testNearbyInt();
|
||||
//}
|
||||
|
||||
//fn testNearbyInt() void {
|
||||
// {
|
||||
// var a: f32 = 2.1;
|
||||
// expect(@nearbyInt(f32, a) == 2);
|
||||
// }
|
||||
// {
|
||||
// var a: f64 = -3.75;
|
||||
// expect(@nearbyInt(f64, a) == -4);
|
||||
// }
|
||||
//}
|
||||
Loading…
x
Reference in New Issue
Block a user