mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-09 15:19:07 +00:00
minimum/maximum builtins
This commit is contained in:
parent
50a29f7c21
commit
cdeea3b094
18 changed files with 416 additions and 0 deletions
|
|
@ -7988,6 +7988,17 @@ test "@hasDecl" {
|
|||
</p>
|
||||
{#header_close#}
|
||||
|
||||
{#header_open|@maximum#}
|
||||
<pre>{#syntax#}@maximum(a: T, b: T) T{#endsyntax#}</pre>
|
||||
<p>
|
||||
Returns the maximum value of {#syntax#}a{#endsyntax#} and {#syntax#}b{#endsyntax#}. This builtin accepts integers, floats, and vectors of either. In the latter case, the operation is performed element wise.
|
||||
</p>
|
||||
<p>
|
||||
NaNs are handled as follows: if one of the operands of a (pairwise) operation is NaN, the other operand is returned. If both operands are NaN, NaN is returned.
|
||||
</p>
|
||||
{#see_also|@minimum|SIMD|Vectors#}
|
||||
{#header_close#}
|
||||
|
||||
{#header_open|@memcpy#}
|
||||
<pre>{#syntax#}@memcpy(noalias dest: [*]u8, noalias source: [*]const u8, byte_count: usize){#endsyntax#}</pre>
|
||||
<p>
|
||||
|
|
@ -8025,6 +8036,17 @@ mem.copy(u8, dest[0..byte_count], source[0..byte_count]);{#endsyntax#}</pre>
|
|||
mem.set(u8, dest, c);{#endsyntax#}</pre>
|
||||
{#header_close#}
|
||||
|
||||
{#header_open|@minimum#}
|
||||
<pre>{#syntax#}@minimum(a: T, b: T) T{#endsyntax#}</pre>
|
||||
<p>
|
||||
Returns the minimum value of {#syntax#}a{#endsyntax#} and {#syntax#}b{#endsyntax#}. This builtin accepts integers, floats, and vectors of either. In the latter case, the operation is performed element wise.
|
||||
</p>
|
||||
<p>
|
||||
NaNs are handled as follows: if one of the operands of a (pairwise) operation is NaN, the other operand is returned. If both operands are NaN, NaN is returned.
|
||||
</p>
|
||||
{#see_also|@maximum|SIMD|Vectors#}
|
||||
{#header_close#}
|
||||
|
||||
{#header_open|@wasmMemorySize#}
|
||||
<pre>{#syntax#}@wasmMemorySize(index: u32) u32{#endsyntax#}</pre>
|
||||
<p>
|
||||
|
|
|
|||
|
|
@ -2098,8 +2098,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
|
|||
.builtin_call,
|
||||
.field_ptr_type,
|
||||
.field_parent_ptr,
|
||||
.maximum,
|
||||
.memcpy,
|
||||
.memset,
|
||||
.minimum,
|
||||
.builtin_async_call,
|
||||
.c_import,
|
||||
.@"resume",
|
||||
|
|
@ -7227,6 +7229,25 @@ fn builtinCall(
|
|||
return rvalue(gz, rl, result, node);
|
||||
},
|
||||
|
||||
.maximum => {
|
||||
const a = try expr(gz, scope, .none, params[0]);
|
||||
const b = try expr(gz, scope, .none, params[1]);
|
||||
const result = try gz.addPlNode(.maximum, node, Zir.Inst.Bin{
|
||||
.lhs = a,
|
||||
.rhs = b,
|
||||
});
|
||||
return rvalue(gz, rl, result, node);
|
||||
},
|
||||
.minimum => {
|
||||
const a = try expr(gz, scope, .none, params[0]);
|
||||
const b = try expr(gz, scope, .none, params[1]);
|
||||
const result = try gz.addPlNode(.minimum, node, Zir.Inst.Bin{
|
||||
.lhs = a,
|
||||
.rhs = b,
|
||||
});
|
||||
return rvalue(gz, rl, result, node);
|
||||
},
|
||||
|
||||
.add_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .add_with_overflow),
|
||||
.sub_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .sub_with_overflow),
|
||||
.mul_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .mul_with_overflow),
|
||||
|
|
|
|||
|
|
@ -57,8 +57,10 @@ pub const Tag = enum {
|
|||
int_to_error,
|
||||
int_to_float,
|
||||
int_to_ptr,
|
||||
maximum,
|
||||
memcpy,
|
||||
memset,
|
||||
minimum,
|
||||
wasm_memory_size,
|
||||
wasm_memory_grow,
|
||||
mod,
|
||||
|
|
@ -518,6 +520,13 @@ pub const list = list: {
|
|||
.param_count = 2,
|
||||
},
|
||||
},
|
||||
.{
|
||||
"@maximum",
|
||||
.{
|
||||
.tag = .maximum,
|
||||
.param_count = 2,
|
||||
},
|
||||
},
|
||||
.{
|
||||
"@memcpy",
|
||||
.{
|
||||
|
|
@ -532,6 +541,13 @@ pub const list = list: {
|
|||
.param_count = 3,
|
||||
},
|
||||
},
|
||||
.{
|
||||
"@minimum",
|
||||
.{
|
||||
.tag = .minimum,
|
||||
.param_count = 2,
|
||||
},
|
||||
},
|
||||
.{
|
||||
"@wasmMemorySize",
|
||||
.{
|
||||
|
|
|
|||
14
src/Sema.zig
14
src/Sema.zig
|
|
@ -346,8 +346,10 @@ pub fn analyzeBody(
|
|||
.builtin_call => try sema.zirBuiltinCall(block, inst),
|
||||
.field_ptr_type => try sema.zirFieldPtrType(block, inst),
|
||||
.field_parent_ptr => try sema.zirFieldParentPtr(block, inst),
|
||||
.maximum => try sema.zirMaximum(block, inst),
|
||||
.memcpy => try sema.zirMemcpy(block, inst),
|
||||
.memset => try sema.zirMemset(block, inst),
|
||||
.minimum => try sema.zirMinimum(block, inst),
|
||||
.builtin_async_call => try sema.zirBuiltinAsyncCall(block, inst),
|
||||
.@"resume" => try sema.zirResume(block, inst),
|
||||
.@"await" => try sema.zirAwait(block, inst, false),
|
||||
|
|
@ -6148,6 +6150,12 @@ fn zirFieldParentPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Com
|
|||
return sema.mod.fail(&block.base, src, "TODO: Sema.zirFieldParentPtr", .{});
|
||||
}
|
||||
|
||||
fn zirMaximum(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src = inst_data.src();
|
||||
return sema.mod.fail(&block.base, src, "TODO: Sema.zirMaximum", .{});
|
||||
}
|
||||
|
||||
fn zirMemcpy(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src = inst_data.src();
|
||||
|
|
@ -6160,6 +6168,12 @@ fn zirMemset(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErro
|
|||
return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemset", .{});
|
||||
}
|
||||
|
||||
fn zirMinimum(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src = inst_data.src();
|
||||
return sema.mod.fail(&block.base, src, "TODO: Sema.zirMinimum", .{});
|
||||
}
|
||||
|
||||
fn zirBuiltinAsyncCall(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src = inst_data.src();
|
||||
|
|
|
|||
12
src/Zir.zig
12
src/Zir.zig
|
|
@ -915,12 +915,18 @@ pub const Inst = struct {
|
|||
/// Implements the `@fieldParentPtr` builtin.
|
||||
/// Uses the `pl_node` union field with payload `FieldParentPtr`.
|
||||
field_parent_ptr,
|
||||
/// Implements the `@maximum` builtin.
|
||||
/// Uses the `pl_node` union field with payload `Bin`
|
||||
maximum,
|
||||
/// Implements the `@memcpy` builtin.
|
||||
/// Uses the `pl_node` union field with payload `Memcpy`.
|
||||
memcpy,
|
||||
/// Implements the `@memset` builtin.
|
||||
/// Uses the `pl_node` union field with payload `Memset`.
|
||||
memset,
|
||||
/// Implements the `@minimum` builtin.
|
||||
/// Uses the `pl_node` union field with payload `Bin`
|
||||
minimum,
|
||||
/// Implements the `@asyncCall` builtin.
|
||||
/// Uses the `pl_node` union field with payload `AsyncCall`.
|
||||
builtin_async_call,
|
||||
|
|
@ -1192,8 +1198,10 @@ pub const Inst = struct {
|
|||
.builtin_call,
|
||||
.field_ptr_type,
|
||||
.field_parent_ptr,
|
||||
.maximum,
|
||||
.memcpy,
|
||||
.memset,
|
||||
.minimum,
|
||||
.builtin_async_call,
|
||||
.c_import,
|
||||
.@"resume",
|
||||
|
|
@ -1463,8 +1471,10 @@ pub const Inst = struct {
|
|||
.builtin_call = .pl_node,
|
||||
.field_ptr_type = .bin,
|
||||
.field_parent_ptr = .pl_node,
|
||||
.maximum = .pl_node,
|
||||
.memcpy = .pl_node,
|
||||
.memset = .pl_node,
|
||||
.minimum = .pl_node,
|
||||
.builtin_async_call = .pl_node,
|
||||
.c_import = .pl_node,
|
||||
|
||||
|
|
@ -3020,6 +3030,8 @@ const Writer = struct {
|
|||
.bitcast,
|
||||
.bitcast_result_ptr,
|
||||
.vector_type,
|
||||
.maximum,
|
||||
.minimum,
|
||||
=> try self.writePlNodeBin(stream, inst),
|
||||
|
||||
.@"export" => try self.writePlNodeExport(stream, inst),
|
||||
|
|
|
|||
|
|
@ -1796,6 +1796,8 @@ enum BuiltinFnId {
|
|||
BuiltinFnIdWasmMemoryGrow,
|
||||
BuiltinFnIdSrc,
|
||||
BuiltinFnIdReduce,
|
||||
BuiltinFnIdMaximum,
|
||||
BuiltinFnIdMinimum,
|
||||
};
|
||||
|
||||
struct BuiltinFnEntry {
|
||||
|
|
@ -2938,6 +2940,8 @@ enum IrBinOp {
|
|||
IrBinOpRemMod,
|
||||
IrBinOpArrayCat,
|
||||
IrBinOpArrayMult,
|
||||
IrBinOpMaximum,
|
||||
IrBinOpMinimum,
|
||||
};
|
||||
|
||||
struct Stage1ZirInstBinOp {
|
||||
|
|
|
|||
|
|
@ -4686,6 +4686,21 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast
|
|||
arg0_value, arg1_value);
|
||||
return ir_lval_wrap(ag, scope, splat, lval, result_loc);
|
||||
}
|
||||
case BuiltinFnIdMaximum:
|
||||
{
|
||||
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
||||
Stage1ZirInst *arg0_value = astgen_node(ag, arg0_node, scope);
|
||||
if (arg0_value == ag->codegen->invalid_inst_src)
|
||||
return arg0_value;
|
||||
|
||||
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
||||
Stage1ZirInst *arg1_value = astgen_node(ag, arg1_node, scope);
|
||||
if (arg1_value == ag->codegen->invalid_inst_src)
|
||||
return arg1_value;
|
||||
|
||||
Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpMaximum, arg0_value, arg1_value, true);
|
||||
return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);
|
||||
}
|
||||
case BuiltinFnIdMemcpy:
|
||||
{
|
||||
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
||||
|
|
@ -4726,6 +4741,21 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast
|
|||
Stage1ZirInst *ir_memset = ir_build_memset_src(ag, scope, node, arg0_value, arg1_value, arg2_value);
|
||||
return ir_lval_wrap(ag, scope, ir_memset, lval, result_loc);
|
||||
}
|
||||
case BuiltinFnIdMinimum:
|
||||
{
|
||||
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
||||
Stage1ZirInst *arg0_value = astgen_node(ag, arg0_node, scope);
|
||||
if (arg0_value == ag->codegen->invalid_inst_src)
|
||||
return arg0_value;
|
||||
|
||||
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
||||
Stage1ZirInst *arg1_value = astgen_node(ag, arg1_node, scope);
|
||||
if (arg1_value == ag->codegen->invalid_inst_src)
|
||||
return arg1_value;
|
||||
|
||||
Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpMinimum, arg0_value, arg1_value, true);
|
||||
return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);
|
||||
}
|
||||
case BuiltinFnIdWasmMemorySize:
|
||||
{
|
||||
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
||||
|
|
|
|||
|
|
@ -191,6 +191,30 @@ void bigfloat_sqrt(BigFloat *dest, const BigFloat *op) {
|
|||
f128M_sqrt(&op->value, &dest->value);
|
||||
}
|
||||
|
||||
void bigfloat_min(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
|
||||
if (bigfloat_is_nan(op1)) {
|
||||
bigfloat_init_bigfloat(dest, op2);
|
||||
} else if (bigfloat_is_nan(op2)) {
|
||||
bigfloat_init_bigfloat(dest, op1);
|
||||
} else if (f128M_lt(&op1->value, &op2->value)) {
|
||||
bigfloat_init_bigfloat(dest, op1);
|
||||
} else {
|
||||
bigfloat_init_bigfloat(dest, op2);
|
||||
}
|
||||
}
|
||||
|
||||
void bigfloat_max(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
|
||||
if (bigfloat_is_nan(op1)) {
|
||||
bigfloat_init_bigfloat(dest, op2);
|
||||
} else if (bigfloat_is_nan(op2)) {
|
||||
bigfloat_init_bigfloat(dest, op1);
|
||||
} else if (f128M_lt(&op1->value, &op2->value)) {
|
||||
bigfloat_init_bigfloat(dest, op2);
|
||||
} else {
|
||||
bigfloat_init_bigfloat(dest, op1);
|
||||
}
|
||||
}
|
||||
|
||||
bool bigfloat_is_nan(const BigFloat *op) {
|
||||
return f128M_isSignalingNaN(&op->value);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,9 +45,12 @@ void bigfloat_div_floor(BigFloat *dest, const BigFloat *op1, const BigFloat *op2
|
|||
void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
|
||||
void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
|
||||
void bigfloat_sqrt(BigFloat *dest, const BigFloat *op);
|
||||
void bigfloat_min(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
|
||||
void bigfloat_max(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
|
||||
void bigfloat_append_buf(Buf *buf, const BigFloat *op);
|
||||
Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2);
|
||||
|
||||
|
||||
bool bigfloat_is_nan(const BigFloat *op);
|
||||
|
||||
// convenience functions
|
||||
|
|
|
|||
|
|
@ -448,6 +448,26 @@ bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) {
|
|||
}
|
||||
#endif
|
||||
|
||||
void bigint_max(BigInt* dest, const BigInt *op1, const BigInt *op2) {
|
||||
switch (bigint_cmp(op1, op2)) {
|
||||
case CmpEQ:
|
||||
case CmpLT:
|
||||
return bigint_init_bigint(dest, op2);
|
||||
case CmpGT:
|
||||
return bigint_init_bigint(dest, op1);
|
||||
}
|
||||
}
|
||||
|
||||
void bigint_min(BigInt* dest, const BigInt *op1, const BigInt *op2) {
|
||||
switch (bigint_cmp(op1, op2)) {
|
||||
case CmpEQ:
|
||||
case CmpLT:
|
||||
return bigint_init_bigint(dest, op1);
|
||||
case CmpGT:
|
||||
return bigint_init_bigint(dest, op2);
|
||||
}
|
||||
}
|
||||
|
||||
void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2) {
|
||||
if (op1->digit_count == 0) {
|
||||
return bigint_init_bigint(dest, op2);
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ bool bigint_fits_in_bits(const BigInt *bn, size_t bit_count, bool is_signed);
|
|||
void bigint_write_twos_complement(const BigInt *big_int, uint8_t *buf, size_t bit_count, bool is_big_endian);
|
||||
void bigint_read_twos_complement(BigInt *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian,
|
||||
bool is_signed);
|
||||
void bigint_max(BigInt* dest, const BigInt *op1, const BigInt *op2);
|
||||
void bigint_min(BigInt* dest, const BigInt *op1, const BigInt *op2);
|
||||
void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2);
|
||||
void bigint_add_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed);
|
||||
void bigint_sub(BigInt *dest, const BigInt *op1, const BigInt *op2);
|
||||
|
|
|
|||
|
|
@ -3248,6 +3248,30 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
|
|||
case IrBinOpRemMod:
|
||||
return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
|
||||
op1_value, op2_value, operand_type, RemKindMod);
|
||||
case IrBinOpMaximum:
|
||||
if (scalar_type->id == ZigTypeIdFloat) {
|
||||
return ZigLLVMBuildMaxNum(g->builder, op1_value, op2_value, "");
|
||||
} else if (scalar_type->id == ZigTypeIdInt) {
|
||||
if (scalar_type->data.integral.is_signed) {
|
||||
return ZigLLVMBuildSMax(g->builder, op1_value, op2_value, "");
|
||||
} else {
|
||||
return ZigLLVMBuildUMax(g->builder, op1_value, op2_value, "");
|
||||
}
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
case IrBinOpMinimum:
|
||||
if (scalar_type->id == ZigTypeIdFloat) {
|
||||
return ZigLLVMBuildMinNum(g->builder, op1_value, op2_value, "");
|
||||
} else if (scalar_type->id == ZigTypeIdInt) {
|
||||
if (scalar_type->data.integral.is_signed) {
|
||||
return ZigLLVMBuildSMin(g->builder, op1_value, op2_value, "");
|
||||
} else {
|
||||
return ZigLLVMBuildUMin(g->builder, op1_value, op2_value, "");
|
||||
}
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
|
@ -8990,6 +9014,8 @@ static void define_builtin_fns(CodeGen *g) {
|
|||
create_builtin_fn(g, BuiltinFnIdWasmMemoryGrow, "wasmMemoryGrow", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdSrc, "src", 0);
|
||||
create_builtin_fn(g, BuiltinFnIdReduce, "reduce", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdMaximum, "maximum", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdMinimum, "minimum", 2);
|
||||
}
|
||||
|
||||
static const char *bool_to_str(bool b) {
|
||||
|
|
|
|||
|
|
@ -3311,6 +3311,108 @@ static void float_mod(ZigValue *out_val, ZigValue *op1, ZigValue *op2) {
|
|||
}
|
||||
}
|
||||
|
||||
static void float_max(ZigValue *out_val, ZigValue *op1, ZigValue *op2) {
|
||||
assert(op1->type == op2->type);
|
||||
out_val->type = op1->type;
|
||||
if (op1->type->id == ZigTypeIdComptimeFloat) {
|
||||
bigfloat_max(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
|
||||
} else if (op1->type->id == ZigTypeIdFloat) {
|
||||
switch (op1->type->data.floating.bit_count) {
|
||||
case 16:
|
||||
if (zig_f16_isNaN(op1->data.x_f16)) {
|
||||
out_val->data.x_f16 = op2->data.x_f16;
|
||||
} else if (zig_f16_isNaN(op2->data.x_f16)) {
|
||||
out_val->data.x_f16 = op1->data.x_f16;
|
||||
} else {
|
||||
out_val->data.x_f16 = f16_lt(op1->data.x_f16, op2->data.x_f16) ? op2->data.x_f16 : op1->data.x_f16;
|
||||
}
|
||||
return;
|
||||
case 32:
|
||||
if (op1->data.x_f32 != op1->data.x_f32) {
|
||||
out_val->data.x_f32 = op2->data.x_f32;
|
||||
} else if (op2->data.x_f32 != op2->data.x_f32) {
|
||||
out_val->data.x_f32 = op1->data.x_f32;
|
||||
} else {
|
||||
out_val->data.x_f32 = op1->data.x_f32 > op2->data.x_f32 ? op1->data.x_f32 : op2->data.x_f32;
|
||||
}
|
||||
return;
|
||||
case 64:
|
||||
if (op1->data.x_f64 != op1->data.x_f64) {
|
||||
out_val->data.x_f64 = op2->data.x_f64;
|
||||
} else if (op2->data.x_f64 != op2->data.x_f64) {
|
||||
out_val->data.x_f64 = op1->data.x_f64;
|
||||
} else {
|
||||
out_val->data.x_f64 = op1->data.x_f64 > op2->data.x_f64 ? op1->data.x_f64 : op2->data.x_f64;
|
||||
}
|
||||
return;
|
||||
case 128:
|
||||
if (zig_f128_isNaN(&op1->data.x_f128)) {
|
||||
out_val->data.x_f128 = op2->data.x_f128;
|
||||
} else if (zig_f128_isNaN(&op2->data.x_f128)) {
|
||||
out_val->data.x_f128 = op1->data.x_f128;
|
||||
} else {
|
||||
out_val->data.x_f128 = f128M_lt(&op1->data.x_f128, &op2->data.x_f128) ? op2->data.x_f128 : op1->data.x_f128;
|
||||
}
|
||||
return;
|
||||
default:
|
||||
zig_unreachable();
|
||||
}
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
static void float_min(ZigValue *out_val, ZigValue *op1, ZigValue *op2) {
|
||||
assert(op1->type == op2->type);
|
||||
out_val->type = op1->type;
|
||||
if (op1->type->id == ZigTypeIdComptimeFloat) {
|
||||
bigfloat_min(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
|
||||
} else if (op1->type->id == ZigTypeIdFloat) {
|
||||
switch (op1->type->data.floating.bit_count) {
|
||||
case 16:
|
||||
if (zig_f16_isNaN(op1->data.x_f16)) {
|
||||
out_val->data.x_f16 = op2->data.x_f16;
|
||||
} else if (zig_f16_isNaN(op2->data.x_f16)) {
|
||||
out_val->data.x_f16 = op1->data.x_f16;
|
||||
} else {
|
||||
out_val->data.x_f16 = f16_lt(op1->data.x_f16, op2->data.x_f16) ? op1->data.x_f16 : op2->data.x_f16;
|
||||
}
|
||||
return;
|
||||
case 32:
|
||||
if (op1->data.x_f32 != op1->data.x_f32) {
|
||||
out_val->data.x_f32 = op2->data.x_f32;
|
||||
} else if (op2->data.x_f32 != op2->data.x_f32) {
|
||||
out_val->data.x_f32 = op1->data.x_f32;
|
||||
} else {
|
||||
out_val->data.x_f32 = op1->data.x_f32 < op2->data.x_f32 ? op1->data.x_f32 : op2->data.x_f32;
|
||||
}
|
||||
return;
|
||||
case 64:
|
||||
if (op1->data.x_f64 != op1->data.x_f64) {
|
||||
out_val->data.x_f64 = op2->data.x_f64;
|
||||
} else if (op2->data.x_f64 != op2->data.x_f64) {
|
||||
out_val->data.x_f64 = op1->data.x_f64;
|
||||
} else {
|
||||
out_val->data.x_f64 = op1->data.x_f32 < op2->data.x_f64 ? op1->data.x_f64 : op2->data.x_f64;
|
||||
}
|
||||
return;
|
||||
case 128:
|
||||
if (zig_f128_isNaN(&op1->data.x_f128)) {
|
||||
out_val->data.x_f128 = op2->data.x_f128;
|
||||
} else if (zig_f128_isNaN(&op2->data.x_f128)) {
|
||||
out_val->data.x_f128 = op1->data.x_f128;
|
||||
} else {
|
||||
out_val->data.x_f128 = f128M_lt(&op1->data.x_f128, &op2->data.x_f128) ? op1->data.x_f128 : op2->data.x_f128;
|
||||
}
|
||||
return;
|
||||
default:
|
||||
zig_unreachable();
|
||||
}
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
static void float_negate(ZigValue *out_val, ZigValue *op) {
|
||||
out_val->type = op->type;
|
||||
if (op->type->id == ZigTypeIdComptimeFloat) {
|
||||
|
|
@ -9704,6 +9806,20 @@ static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, Scope *scope, AstNode *s
|
|||
float_mod(out_val, op1_val, op2_val);
|
||||
}
|
||||
break;
|
||||
case IrBinOpMaximum:
|
||||
if (is_int) {
|
||||
bigint_max(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
|
||||
} else {
|
||||
float_max(out_val, op1_val, op2_val);
|
||||
}
|
||||
break;
|
||||
case IrBinOpMinimum:
|
||||
if (is_int) {
|
||||
bigint_min(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
|
||||
} else {
|
||||
float_min(out_val, op1_val, op2_val);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (type_entry->id == ZigTypeIdInt) {
|
||||
|
|
@ -9904,6 +10020,8 @@ static bool ok_float_op(IrBinOp op) {
|
|||
case IrBinOpRemRem:
|
||||
case IrBinOpRemMod:
|
||||
case IrBinOpRemUnspecified:
|
||||
case IrBinOpMaximum:
|
||||
case IrBinOpMinimum:
|
||||
return true;
|
||||
|
||||
case IrBinOpBoolOr:
|
||||
|
|
@ -10894,6 +11012,8 @@ static Stage1AirInst *ir_analyze_instruction_bin_op(IrAnalyze *ira, Stage1ZirIns
|
|||
case IrBinOpRemUnspecified:
|
||||
case IrBinOpRemRem:
|
||||
case IrBinOpRemMod:
|
||||
case IrBinOpMaximum:
|
||||
case IrBinOpMinimum:
|
||||
return ir_analyze_bin_op_math(ira, bin_op_instruction);
|
||||
case IrBinOpArrayCat:
|
||||
return ir_analyze_array_cat(ira, bin_op_instruction);
|
||||
|
|
|
|||
|
|
@ -733,6 +733,10 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) {
|
|||
return "++";
|
||||
case IrBinOpArrayMult:
|
||||
return "**";
|
||||
case IrBinOpMaximum:
|
||||
return "@maximum";
|
||||
case IrBinOpMinimum:
|
||||
return "@minimum";
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -458,6 +458,36 @@ LLVMValueRef ZigLLVMBuildMemSet(LLVMBuilderRef B, LLVMValueRef Ptr, LLVMValueRef
|
|||
return wrap(call_inst);
|
||||
}
|
||||
|
||||
LLVMValueRef ZigLLVMBuildMaxNum(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS, const char *name) {
|
||||
CallInst *call_inst = unwrap(B)->CreateMaxNum(unwrap(LHS), unwrap(RHS), name);
|
||||
return wrap(call_inst);
|
||||
}
|
||||
|
||||
LLVMValueRef ZigLLVMBuildMinNum(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS, const char *name) {
|
||||
CallInst *call_inst = unwrap(B)->CreateMinNum(unwrap(LHS), unwrap(RHS), name);
|
||||
return wrap(call_inst);
|
||||
}
|
||||
|
||||
LLVMValueRef ZigLLVMBuildUMax(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS, const char *name) {
|
||||
CallInst *call_inst = unwrap(B)->CreateBinaryIntrinsic(Intrinsic::umax, unwrap(LHS), unwrap(RHS), nullptr, name);
|
||||
return wrap(call_inst);
|
||||
}
|
||||
|
||||
LLVMValueRef ZigLLVMBuildUMin(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS, const char *name) {
|
||||
CallInst *call_inst = unwrap(B)->CreateBinaryIntrinsic(Intrinsic::umin, unwrap(LHS), unwrap(RHS), nullptr, name);
|
||||
return wrap(call_inst);
|
||||
}
|
||||
|
||||
LLVMValueRef ZigLLVMBuildSMax(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS, const char *name) {
|
||||
CallInst *call_inst = unwrap(B)->CreateBinaryIntrinsic(Intrinsic::smax, unwrap(LHS), unwrap(RHS), nullptr, name);
|
||||
return wrap(call_inst);
|
||||
}
|
||||
|
||||
LLVMValueRef ZigLLVMBuildSMin(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS, const char *name) {
|
||||
CallInst *call_inst = unwrap(B)->CreateBinaryIntrinsic(Intrinsic::smin, unwrap(LHS), unwrap(RHS), nullptr, name);
|
||||
return wrap(call_inst);
|
||||
}
|
||||
|
||||
void ZigLLVMFnSetSubprogram(LLVMValueRef fn, ZigLLVMDISubprogram *subprogram) {
|
||||
assert( isa<Function>(unwrap(fn)) );
|
||||
Function *unwrapped_function = reinterpret_cast<Function*>(unwrap(fn));
|
||||
|
|
|
|||
|
|
@ -129,6 +129,14 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildMemCpy(LLVMBuilderRef B, LLVMValueRef Dst,
|
|||
ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildMemSet(LLVMBuilderRef B, LLVMValueRef Ptr, LLVMValueRef Val, LLVMValueRef Size,
|
||||
unsigned Align, bool isVolatile);
|
||||
|
||||
ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildMaxNum(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char* name);
|
||||
ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildMinNum(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char* name);
|
||||
|
||||
ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildUMax(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char* name);
|
||||
ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildUMin(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char* name);
|
||||
ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildSMax(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char* name);
|
||||
ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildSMin(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char* name);
|
||||
|
||||
ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,
|
||||
LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,
|
||||
LLVMAtomicOrdering failure_ordering, bool is_weak);
|
||||
|
|
@ -142,6 +150,7 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildLShrExact(LLVMBuilderRef builder, LLVMValu
|
|||
ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildAShrExact(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,
|
||||
const char *name);
|
||||
|
||||
|
||||
ZIG_EXTERN_C struct ZigLLVMDIType *ZigLLVMCreateDebugPointerType(struct ZigLLVMDIBuilder *dibuilder,
|
||||
struct ZigLLVMDIType *pointee_type, uint64_t size_in_bits, uint64_t align_in_bits, const char *name);
|
||||
|
||||
|
|
|
|||
|
|
@ -105,6 +105,7 @@ test {
|
|||
_ = @import("behavior/inttoptr.zig");
|
||||
_ = @import("behavior/ir_block_deps.zig");
|
||||
_ = @import("behavior/math.zig");
|
||||
_ = @import("behavior/maximum_minimum.zig");
|
||||
_ = @import("behavior/merge_error_sets.zig");
|
||||
_ = @import("behavior/misc.zig");
|
||||
_ = @import("behavior/muladd.zig");
|
||||
|
|
|
|||
58
test/behavior/maximum_minimum.zig
Normal file
58
test/behavior/maximum_minimum.zig
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const mem = std.mem;
|
||||
const expect = std.testing.expect;
|
||||
const expectEqual = std.testing.expectEqual;
|
||||
const Vector = std.meta.Vector;
|
||||
|
||||
test "@maximum" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
try expectEqual(@as(i32, 10), @maximum(@as(i32, -3), @as(i32, 10)));
|
||||
try expectEqual(@as(f32, 3.2), @maximum(@as(f32, 3.2), @as(f32, 0.68)));
|
||||
|
||||
var a: Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 };
|
||||
var b: Vector(4, i32) = [4]i32{ 1, 2147483647, 3, 4 };
|
||||
var x = @maximum(a, b);
|
||||
try expect(mem.eql(i32, &@as([4]i32, x), &[4]i32{ 2147483647, 2147483647, 30, 40 }));
|
||||
|
||||
var c: Vector(4, f32) = [4]f32{ 0, 0.4, -2.4, 7.8 };
|
||||
var d: Vector(4, f32) = [4]f32{ -0.23, 0.42, -0.64, 0.9 };
|
||||
var y = @maximum(c, d);
|
||||
try expect(mem.eql(f32, &@as([4]f32, y), &[4]f32{ 0, 0.42, -0.64, 7.8 }));
|
||||
|
||||
var e: Vector(2, f32) = [2]f32{ 0, std.math.qnan_f32 };
|
||||
var f: Vector(2, f32) = [2]f32{ std.math.qnan_f32, 0 };
|
||||
var z = @maximum(e, f);
|
||||
try expect(mem.eql(f32, &@as([2]f32, z), &[2]f32{ 0, 0 }));
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "@minimum" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
try expectEqual(@as(i32, -3), @minimum(@as(i32, -3), @as(i32, 10)));
|
||||
try expectEqual(@as(f32, 0.68), @minimum(@as(f32, 3.2), @as(f32, 0.68)));
|
||||
|
||||
var a: Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 };
|
||||
var b: Vector(4, i32) = [4]i32{ 1, 2147483647, 3, 4 };
|
||||
var x = @minimum(a, b);
|
||||
try expect(mem.eql(i32, &@as([4]i32, x), &[4]i32{ 1, -2, 3, 4 }));
|
||||
|
||||
var c: Vector(4, f32) = [4]f32{ 0, 0.4, -2.4, 7.8 };
|
||||
var d: Vector(4, f32) = [4]f32{ -0.23, 0.42, -0.64, 0.9 };
|
||||
var y = @minimum(c, d);
|
||||
try expect(mem.eql(f32, &@as([4]f32, y), &[4]f32{ -0.23, 0.4, -2.4, 0.9 }));
|
||||
|
||||
var e: Vector(2, f32) = [2]f32{ 0, std.math.qnan_f32 };
|
||||
var f: Vector(2, f32) = [2]f32{ std.math.qnan_f32, 0 };
|
||||
var z = @maximum(e, f);
|
||||
try expect(mem.eql(f32, &@as([2]f32, z), &[2]f32{ 0, 0 }));
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue