minimum/maximum builtins

This commit is contained in:
Robin Voetter 2021-07-25 05:34:11 +02:00 committed by Andrew Kelley
parent 50a29f7c21
commit cdeea3b094
18 changed files with 416 additions and 0 deletions

View file

@ -7988,6 +7988,17 @@ test "@hasDecl" {
</p> </p>
{#header_close#} {#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#} {#header_open|@memcpy#}
<pre>{#syntax#}@memcpy(noalias dest: [*]u8, noalias source: [*]const u8, byte_count: usize){#endsyntax#}</pre> <pre>{#syntax#}@memcpy(noalias dest: [*]u8, noalias source: [*]const u8, byte_count: usize){#endsyntax#}</pre>
<p> <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> mem.set(u8, dest, c);{#endsyntax#}</pre>
{#header_close#} {#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#} {#header_open|@wasmMemorySize#}
<pre>{#syntax#}@wasmMemorySize(index: u32) u32{#endsyntax#}</pre> <pre>{#syntax#}@wasmMemorySize(index: u32) u32{#endsyntax#}</pre>
<p> <p>

View file

@ -2098,8 +2098,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
.builtin_call, .builtin_call,
.field_ptr_type, .field_ptr_type,
.field_parent_ptr, .field_parent_ptr,
.maximum,
.memcpy, .memcpy,
.memset, .memset,
.minimum,
.builtin_async_call, .builtin_async_call,
.c_import, .c_import,
.@"resume", .@"resume",
@ -7227,6 +7229,25 @@ fn builtinCall(
return rvalue(gz, rl, result, node); 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), .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), .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), .mul_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .mul_with_overflow),

View file

@ -57,8 +57,10 @@ pub const Tag = enum {
int_to_error, int_to_error,
int_to_float, int_to_float,
int_to_ptr, int_to_ptr,
maximum,
memcpy, memcpy,
memset, memset,
minimum,
wasm_memory_size, wasm_memory_size,
wasm_memory_grow, wasm_memory_grow,
mod, mod,
@ -518,6 +520,13 @@ pub const list = list: {
.param_count = 2, .param_count = 2,
}, },
}, },
.{
"@maximum",
.{
.tag = .maximum,
.param_count = 2,
},
},
.{ .{
"@memcpy", "@memcpy",
.{ .{
@ -532,6 +541,13 @@ pub const list = list: {
.param_count = 3, .param_count = 3,
}, },
}, },
.{
"@minimum",
.{
.tag = .minimum,
.param_count = 2,
},
},
.{ .{
"@wasmMemorySize", "@wasmMemorySize",
.{ .{

View file

@ -346,8 +346,10 @@ pub fn analyzeBody(
.builtin_call => try sema.zirBuiltinCall(block, inst), .builtin_call => try sema.zirBuiltinCall(block, inst),
.field_ptr_type => try sema.zirFieldPtrType(block, inst), .field_ptr_type => try sema.zirFieldPtrType(block, inst),
.field_parent_ptr => try sema.zirFieldParentPtr(block, inst), .field_parent_ptr => try sema.zirFieldParentPtr(block, inst),
.maximum => try sema.zirMaximum(block, inst),
.memcpy => try sema.zirMemcpy(block, inst), .memcpy => try sema.zirMemcpy(block, inst),
.memset => try sema.zirMemset(block, inst), .memset => try sema.zirMemset(block, inst),
.minimum => try sema.zirMinimum(block, inst),
.builtin_async_call => try sema.zirBuiltinAsyncCall(block, inst), .builtin_async_call => try sema.zirBuiltinAsyncCall(block, inst),
.@"resume" => try sema.zirResume(block, inst), .@"resume" => try sema.zirResume(block, inst),
.@"await" => try sema.zirAwait(block, inst, false), .@"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", .{}); 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 { 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 inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const src = inst_data.src(); 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", .{}); 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 { 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 inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const src = inst_data.src(); const src = inst_data.src();

View file

@ -915,12 +915,18 @@ pub const Inst = struct {
/// Implements the `@fieldParentPtr` builtin. /// Implements the `@fieldParentPtr` builtin.
/// Uses the `pl_node` union field with payload `FieldParentPtr`. /// Uses the `pl_node` union field with payload `FieldParentPtr`.
field_parent_ptr, field_parent_ptr,
/// Implements the `@maximum` builtin.
/// Uses the `pl_node` union field with payload `Bin`
maximum,
/// Implements the `@memcpy` builtin. /// Implements the `@memcpy` builtin.
/// Uses the `pl_node` union field with payload `Memcpy`. /// Uses the `pl_node` union field with payload `Memcpy`.
memcpy, memcpy,
/// Implements the `@memset` builtin. /// Implements the `@memset` builtin.
/// Uses the `pl_node` union field with payload `Memset`. /// Uses the `pl_node` union field with payload `Memset`.
memset, memset,
/// Implements the `@minimum` builtin.
/// Uses the `pl_node` union field with payload `Bin`
minimum,
/// Implements the `@asyncCall` builtin. /// Implements the `@asyncCall` builtin.
/// Uses the `pl_node` union field with payload `AsyncCall`. /// Uses the `pl_node` union field with payload `AsyncCall`.
builtin_async_call, builtin_async_call,
@ -1192,8 +1198,10 @@ pub const Inst = struct {
.builtin_call, .builtin_call,
.field_ptr_type, .field_ptr_type,
.field_parent_ptr, .field_parent_ptr,
.maximum,
.memcpy, .memcpy,
.memset, .memset,
.minimum,
.builtin_async_call, .builtin_async_call,
.c_import, .c_import,
.@"resume", .@"resume",
@ -1463,8 +1471,10 @@ pub const Inst = struct {
.builtin_call = .pl_node, .builtin_call = .pl_node,
.field_ptr_type = .bin, .field_ptr_type = .bin,
.field_parent_ptr = .pl_node, .field_parent_ptr = .pl_node,
.maximum = .pl_node,
.memcpy = .pl_node, .memcpy = .pl_node,
.memset = .pl_node, .memset = .pl_node,
.minimum = .pl_node,
.builtin_async_call = .pl_node, .builtin_async_call = .pl_node,
.c_import = .pl_node, .c_import = .pl_node,
@ -3020,6 +3030,8 @@ const Writer = struct {
.bitcast, .bitcast,
.bitcast_result_ptr, .bitcast_result_ptr,
.vector_type, .vector_type,
.maximum,
.minimum,
=> try self.writePlNodeBin(stream, inst), => try self.writePlNodeBin(stream, inst),
.@"export" => try self.writePlNodeExport(stream, inst), .@"export" => try self.writePlNodeExport(stream, inst),

View file

@ -1796,6 +1796,8 @@ enum BuiltinFnId {
BuiltinFnIdWasmMemoryGrow, BuiltinFnIdWasmMemoryGrow,
BuiltinFnIdSrc, BuiltinFnIdSrc,
BuiltinFnIdReduce, BuiltinFnIdReduce,
BuiltinFnIdMaximum,
BuiltinFnIdMinimum,
}; };
struct BuiltinFnEntry { struct BuiltinFnEntry {
@ -2938,6 +2940,8 @@ enum IrBinOp {
IrBinOpRemMod, IrBinOpRemMod,
IrBinOpArrayCat, IrBinOpArrayCat,
IrBinOpArrayMult, IrBinOpArrayMult,
IrBinOpMaximum,
IrBinOpMinimum,
}; };
struct Stage1ZirInstBinOp { struct Stage1ZirInstBinOp {

View file

@ -4686,6 +4686,21 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast
arg0_value, arg1_value); arg0_value, arg1_value);
return ir_lval_wrap(ag, scope, splat, lval, result_loc); 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: case BuiltinFnIdMemcpy:
{ {
AstNode *arg0_node = node->data.fn_call_expr.params.at(0); 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); 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); 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: case BuiltinFnIdWasmMemorySize:
{ {
AstNode *arg0_node = node->data.fn_call_expr.params.at(0); AstNode *arg0_node = node->data.fn_call_expr.params.at(0);

View file

@ -191,6 +191,30 @@ void bigfloat_sqrt(BigFloat *dest, const BigFloat *op) {
f128M_sqrt(&op->value, &dest->value); 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) { bool bigfloat_is_nan(const BigFloat *op) {
return f128M_isSignalingNaN(&op->value); return f128M_isSignalingNaN(&op->value);
} }

View file

@ -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_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
void bigfloat_mod(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_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); void bigfloat_append_buf(Buf *buf, const BigFloat *op);
Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2); Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2);
bool bigfloat_is_nan(const BigFloat *op); bool bigfloat_is_nan(const BigFloat *op);
// convenience functions // convenience functions

View file

@ -448,6 +448,26 @@ bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) {
} }
#endif #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) { void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2) {
if (op1->digit_count == 0) { if (op1->digit_count == 0) {
return bigint_init_bigint(dest, op2); return bigint_init_bigint(dest, op2);

View file

@ -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_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, void bigint_read_twos_complement(BigInt *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian,
bool is_signed); 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(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_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); void bigint_sub(BigInt *dest, const BigInt *op1, const BigInt *op2);

View file

@ -3248,6 +3248,30 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
case IrBinOpRemMod: case IrBinOpRemMod:
return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
op1_value, op2_value, operand_type, RemKindMod); 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(); zig_unreachable();
} }
@ -8990,6 +9014,8 @@ static void define_builtin_fns(CodeGen *g) {
create_builtin_fn(g, BuiltinFnIdWasmMemoryGrow, "wasmMemoryGrow", 2); create_builtin_fn(g, BuiltinFnIdWasmMemoryGrow, "wasmMemoryGrow", 2);
create_builtin_fn(g, BuiltinFnIdSrc, "src", 0); create_builtin_fn(g, BuiltinFnIdSrc, "src", 0);
create_builtin_fn(g, BuiltinFnIdReduce, "reduce", 2); 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) { static const char *bool_to_str(bool b) {

View file

@ -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) { static void float_negate(ZigValue *out_val, ZigValue *op) {
out_val->type = op->type; out_val->type = op->type;
if (op->type->id == ZigTypeIdComptimeFloat) { 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); float_mod(out_val, op1_val, op2_val);
} }
break; 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) { if (type_entry->id == ZigTypeIdInt) {
@ -9904,6 +10020,8 @@ static bool ok_float_op(IrBinOp op) {
case IrBinOpRemRem: case IrBinOpRemRem:
case IrBinOpRemMod: case IrBinOpRemMod:
case IrBinOpRemUnspecified: case IrBinOpRemUnspecified:
case IrBinOpMaximum:
case IrBinOpMinimum:
return true; return true;
case IrBinOpBoolOr: case IrBinOpBoolOr:
@ -10894,6 +11012,8 @@ static Stage1AirInst *ir_analyze_instruction_bin_op(IrAnalyze *ira, Stage1ZirIns
case IrBinOpRemUnspecified: case IrBinOpRemUnspecified:
case IrBinOpRemRem: case IrBinOpRemRem:
case IrBinOpRemMod: case IrBinOpRemMod:
case IrBinOpMaximum:
case IrBinOpMinimum:
return ir_analyze_bin_op_math(ira, bin_op_instruction); return ir_analyze_bin_op_math(ira, bin_op_instruction);
case IrBinOpArrayCat: case IrBinOpArrayCat:
return ir_analyze_array_cat(ira, bin_op_instruction); return ir_analyze_array_cat(ira, bin_op_instruction);

View file

@ -733,6 +733,10 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) {
return "++"; return "++";
case IrBinOpArrayMult: case IrBinOpArrayMult:
return "**"; return "**";
case IrBinOpMaximum:
return "@maximum";
case IrBinOpMinimum:
return "@minimum";
} }
zig_unreachable(); zig_unreachable();
} }

View file

@ -458,6 +458,36 @@ LLVMValueRef ZigLLVMBuildMemSet(LLVMBuilderRef B, LLVMValueRef Ptr, LLVMValueRef
return wrap(call_inst); 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) { void ZigLLVMFnSetSubprogram(LLVMValueRef fn, ZigLLVMDISubprogram *subprogram) {
assert( isa<Function>(unwrap(fn)) ); assert( isa<Function>(unwrap(fn)) );
Function *unwrapped_function = reinterpret_cast<Function*>(unwrap(fn)); Function *unwrapped_function = reinterpret_cast<Function*>(unwrap(fn));

View file

@ -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, ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildMemSet(LLVMBuilderRef B, LLVMValueRef Ptr, LLVMValueRef Val, LLVMValueRef Size,
unsigned Align, bool isVolatile); 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, ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,
LLVMValueRef new_val, LLVMAtomicOrdering success_ordering, LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,
LLVMAtomicOrdering failure_ordering, bool is_weak); 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, ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildAShrExact(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,
const char *name); const char *name);
ZIG_EXTERN_C struct ZigLLVMDIType *ZigLLVMCreateDebugPointerType(struct ZigLLVMDIBuilder *dibuilder, 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); struct ZigLLVMDIType *pointee_type, uint64_t size_in_bits, uint64_t align_in_bits, const char *name);

View file

@ -105,6 +105,7 @@ test {
_ = @import("behavior/inttoptr.zig"); _ = @import("behavior/inttoptr.zig");
_ = @import("behavior/ir_block_deps.zig"); _ = @import("behavior/ir_block_deps.zig");
_ = @import("behavior/math.zig"); _ = @import("behavior/math.zig");
_ = @import("behavior/maximum_minimum.zig");
_ = @import("behavior/merge_error_sets.zig"); _ = @import("behavior/merge_error_sets.zig");
_ = @import("behavior/misc.zig"); _ = @import("behavior/misc.zig");
_ = @import("behavior/muladd.zig"); _ = @import("behavior/muladd.zig");

View 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();
}