Skip to content

minimum/maximum builtins #9448

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions doc/langref.html.in
Original file line number Diff line number Diff line change
Expand Up @@ -7983,6 +7983,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>
Expand Down Expand Up @@ -8020,6 +8031,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>
Expand Down
21 changes: 21 additions & 0 deletions src/AstGen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2097,8 +2097,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",
Expand Down Expand Up @@ -7226,6 +7228,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),
Expand Down
16 changes: 16 additions & 0 deletions src/BuiltinFn.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -517,6 +519,13 @@ pub const list = list: {
.param_count = 2,
},
},
.{
"@maximum",
.{
.tag = .maximum,
.param_count = 2,
},
},
.{
"@memcpy",
.{
Expand All @@ -531,6 +540,13 @@ pub const list = list: {
.param_count = 3,
},
},
.{
"@minimum",
.{
.tag = .minimum,
.param_count = 2,
},
},
.{
"@wasmMemorySize",
.{
Expand Down
14 changes: 14 additions & 0 deletions src/Sema.zig
Original file line number Diff line number Diff line change
Expand Up @@ -345,8 +345,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),
Expand Down Expand Up @@ -6141,6 +6143,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();
Expand All @@ -6153,6 +6161,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();
Expand Down
12 changes: 12 additions & 0 deletions src/Zir.zig
Original file line number Diff line number Diff line change
Expand Up @@ -912,12 +912,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,
Expand Down Expand Up @@ -1188,8 +1194,10 @@ pub const Inst = struct {
.builtin_call,
.field_ptr_type,
.field_parent_ptr,
.maximum,
.memcpy,
.memset,
.minimum,
.builtin_async_call,
.c_import,
.@"resume",
Expand Down Expand Up @@ -1458,8 +1466,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,

Expand Down Expand Up @@ -3007,6 +3017,8 @@ const Writer = struct {
.bitcast,
.bitcast_result_ptr,
.vector_type,
.maximum,
.minimum,
=> try self.writePlNodeBin(stream, inst),

.@"export" => try self.writePlNodeExport(stream, inst),
Expand Down
4 changes: 4 additions & 0 deletions src/stage1/all_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1795,6 +1795,8 @@ enum BuiltinFnId {
BuiltinFnIdWasmMemoryGrow,
BuiltinFnIdSrc,
BuiltinFnIdReduce,
BuiltinFnIdMaximum,
BuiltinFnIdMinimum,
};

struct BuiltinFnEntry {
Expand Down Expand Up @@ -2932,6 +2934,8 @@ enum IrBinOp {
IrBinOpRemMod,
IrBinOpArrayCat,
IrBinOpArrayMult,
IrBinOpMaximum,
IrBinOpMinimum,
};

struct Stage1ZirInstBinOp {
Expand Down
30 changes: 30 additions & 0 deletions src/stage1/astgen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4635,6 +4635,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);
Expand Down Expand Up @@ -4675,6 +4690,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);
Expand Down
24 changes: 24 additions & 0 deletions src/stage1/bigfloat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
3 changes: 3 additions & 0 deletions src/stage1/bigfloat.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 20 additions & 0 deletions src/stage1/bigint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions src/stage1/bigint.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
26 changes: 26 additions & 0 deletions src/stage1/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3231,6 +3231,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();
}
Expand Down Expand Up @@ -8962,6 +8986,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) {
Expand Down
Loading