Skip to content

Commit 2c95d81

Browse files
committed
minimum/maximum builtins
1 parent b87105c commit 2c95d81

18 files changed

+417
-1
lines changed

doc/langref.html.in

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7983,6 +7983,17 @@ test "@hasDecl" {
79837983
</p>
79847984
{#header_close#}
79857985

7986+
{#header_open|@maximum#}
7987+
<pre>{#syntax#}@maximum(a: T, b: T) T{#endsyntax#}</pre>
7988+
<p>
7989+
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.
7990+
</p>
7991+
<p>
7992+
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.
7993+
</p>
7994+
{#see_also|@minimum|SIMD|Vectors#}
7995+
{#header_close#}
7996+
79867997
{#header_open|@memcpy#}
79877998
<pre>{#syntax#}@memcpy(noalias dest: [*]u8, noalias source: [*]const u8, byte_count: usize){#endsyntax#}</pre>
79887999
<p>
@@ -8020,6 +8031,17 @@ mem.copy(u8, dest[0..byte_count], source[0..byte_count]);{#endsyntax#}</pre>
80208031
mem.set(u8, dest, c);{#endsyntax#}</pre>
80218032
{#header_close#}
80228033

8034+
{#header_open|@minimum#}
8035+
<pre>{#syntax#}@minimum(a: T, b: T) T{#endsyntax#}</pre>
8036+
<p>
8037+
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.
8038+
</p>
8039+
<p>
8040+
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.
8041+
</p>
8042+
{#see_also|@maximum|SIMD|Vectors#}
8043+
{#header_close#}
8044+
80238045
{#header_open|@wasmMemorySize#}
80248046
<pre>{#syntax#}@wasmMemorySize(index: u32) u32{#endsyntax#}</pre>
80258047
<p>

src/AstGen.zig

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2097,8 +2097,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
20972097
.builtin_call,
20982098
.field_ptr_type,
20992099
.field_parent_ptr,
2100+
.maximum,
21002101
.memcpy,
21012102
.memset,
2103+
.minimum,
21022104
.builtin_async_call,
21032105
.c_import,
21042106
.@"resume",
@@ -7226,6 +7228,25 @@ fn builtinCall(
72267228
return rvalue(gz, rl, result, node);
72277229
},
72287230

7231+
.maximum => {
7232+
const a = try expr(gz, scope, .none, params[0]);
7233+
const b = try expr(gz, scope, .none, params[1]);
7234+
const result = try gz.addPlNode(.maximum, node, Zir.Inst.Bin{
7235+
.lhs = a,
7236+
.rhs = b,
7237+
});
7238+
return rvalue(gz, rl, result, node);
7239+
},
7240+
.minimum => {
7241+
const a = try expr(gz, scope, .none, params[0]);
7242+
const b = try expr(gz, scope, .none, params[1]);
7243+
const result = try gz.addPlNode(.minimum, node, Zir.Inst.Bin{
7244+
.lhs = a,
7245+
.rhs = b,
7246+
});
7247+
return rvalue(gz, rl, result, node);
7248+
},
7249+
72297250
.add_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .add_with_overflow),
72307251
.sub_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .sub_with_overflow),
72317252
.mul_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .mul_with_overflow),

src/BuiltinFn.zig

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,10 @@ pub const Tag = enum {
5757
int_to_error,
5858
int_to_float,
5959
int_to_ptr,
60+
maximum,
6061
memcpy,
6162
memset,
63+
minimum,
6264
wasm_memory_size,
6365
wasm_memory_grow,
6466
mod,
@@ -517,6 +519,13 @@ pub const list = list: {
517519
.param_count = 2,
518520
},
519521
},
522+
.{
523+
"@maximum",
524+
.{
525+
.tag = .maximum,
526+
.param_count = 2,
527+
},
528+
},
520529
.{
521530
"@memcpy",
522531
.{
@@ -531,6 +540,13 @@ pub const list = list: {
531540
.param_count = 3,
532541
},
533542
},
543+
.{
544+
"@minimum",
545+
.{
546+
.tag = .minimum,
547+
.param_count = 2,
548+
},
549+
},
534550
.{
535551
"@wasmMemorySize",
536552
.{

src/Sema.zig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,8 +345,10 @@ pub fn analyzeBody(
345345
.builtin_call => try sema.zirBuiltinCall(block, inst),
346346
.field_ptr_type => try sema.zirFieldPtrType(block, inst),
347347
.field_parent_ptr => try sema.zirFieldParentPtr(block, inst),
348+
.maximum => try sema.zirMaximum(block, inst),
348349
.memcpy => try sema.zirMemcpy(block, inst),
349350
.memset => try sema.zirMemset(block, inst),
351+
.minimum => try sema.zirMinimum(block, inst),
350352
.builtin_async_call => try sema.zirBuiltinAsyncCall(block, inst),
351353
.@"resume" => try sema.zirResume(block, inst),
352354
.@"await" => try sema.zirAwait(block, inst, false),
@@ -6141,6 +6143,12 @@ fn zirFieldParentPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Com
61416143
return sema.mod.fail(&block.base, src, "TODO: Sema.zirFieldParentPtr", .{});
61426144
}
61436145

6146+
fn zirMaximum(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
6147+
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6148+
const src = inst_data.src();
6149+
return sema.mod.fail(&block.base, src, "TODO: Sema.zirMaximum", .{});
6150+
}
6151+
61446152
fn zirMemcpy(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
61456153
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
61466154
const src = inst_data.src();
@@ -6153,6 +6161,12 @@ fn zirMemset(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErro
61536161
return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemset", .{});
61546162
}
61556163

6164+
fn zirMinimum(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
6165+
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6166+
const src = inst_data.src();
6167+
return sema.mod.fail(&block.base, src, "TODO: Sema.zirMinimum", .{});
6168+
}
6169+
61566170
fn zirBuiltinAsyncCall(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
61576171
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
61586172
const src = inst_data.src();

src/Zir.zig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -912,12 +912,18 @@ pub const Inst = struct {
912912
/// Implements the `@fieldParentPtr` builtin.
913913
/// Uses the `pl_node` union field with payload `FieldParentPtr`.
914914
field_parent_ptr,
915+
/// Implements the `@maximum` builtin.
916+
/// Uses the `pl_node` union field with payload `Bin`
917+
maximum,
915918
/// Implements the `@memcpy` builtin.
916919
/// Uses the `pl_node` union field with payload `Memcpy`.
917920
memcpy,
918921
/// Implements the `@memset` builtin.
919922
/// Uses the `pl_node` union field with payload `Memset`.
920923
memset,
924+
/// Implements the `@minimum` builtin.
925+
/// Uses the `pl_node` union field with payload `Bin`
926+
minimum,
921927
/// Implements the `@asyncCall` builtin.
922928
/// Uses the `pl_node` union field with payload `AsyncCall`.
923929
builtin_async_call,
@@ -1188,8 +1194,10 @@ pub const Inst = struct {
11881194
.builtin_call,
11891195
.field_ptr_type,
11901196
.field_parent_ptr,
1197+
.maximum,
11911198
.memcpy,
11921199
.memset,
1200+
.minimum,
11931201
.builtin_async_call,
11941202
.c_import,
11951203
.@"resume",
@@ -1458,8 +1466,10 @@ pub const Inst = struct {
14581466
.builtin_call = .pl_node,
14591467
.field_ptr_type = .bin,
14601468
.field_parent_ptr = .pl_node,
1469+
.maximum = .pl_node,
14611470
.memcpy = .pl_node,
14621471
.memset = .pl_node,
1472+
.minimum = .pl_node,
14631473
.builtin_async_call = .pl_node,
14641474
.c_import = .pl_node,
14651475

@@ -3007,6 +3017,8 @@ const Writer = struct {
30073017
.bitcast,
30083018
.bitcast_result_ptr,
30093019
.vector_type,
3020+
.maximum,
3021+
.minimum,
30103022
=> try self.writePlNodeBin(stream, inst),
30113023

30123024
.@"export" => try self.writePlNodeExport(stream, inst),

src/stage1/all_types.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1795,6 +1795,8 @@ enum BuiltinFnId {
17951795
BuiltinFnIdWasmMemoryGrow,
17961796
BuiltinFnIdSrc,
17971797
BuiltinFnIdReduce,
1798+
BuiltinFnIdMaximum,
1799+
BuiltinFnIdMinimum,
17981800
};
17991801

18001802
struct BuiltinFnEntry {
@@ -2932,6 +2934,8 @@ enum IrBinOp {
29322934
IrBinOpRemMod,
29332935
IrBinOpArrayCat,
29342936
IrBinOpArrayMult,
2937+
IrBinOpMaximum,
2938+
IrBinOpMinimum,
29352939
};
29362940

29372941
struct Stage1ZirInstBinOp {

src/stage1/astgen.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4635,6 +4635,21 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast
46354635
arg0_value, arg1_value);
46364636
return ir_lval_wrap(ag, scope, splat, lval, result_loc);
46374637
}
4638+
case BuiltinFnIdMaximum:
4639+
{
4640+
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4641+
Stage1ZirInst *arg0_value = astgen_node(ag, arg0_node, scope);
4642+
if (arg0_value == ag->codegen->invalid_inst_src)
4643+
return arg0_value;
4644+
4645+
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
4646+
Stage1ZirInst *arg1_value = astgen_node(ag, arg1_node, scope);
4647+
if (arg1_value == ag->codegen->invalid_inst_src)
4648+
return arg1_value;
4649+
4650+
Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpMaximum, arg0_value, arg1_value, true);
4651+
return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);
4652+
}
46384653
case BuiltinFnIdMemcpy:
46394654
{
46404655
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
@@ -4675,6 +4690,21 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast
46754690
Stage1ZirInst *ir_memset = ir_build_memset_src(ag, scope, node, arg0_value, arg1_value, arg2_value);
46764691
return ir_lval_wrap(ag, scope, ir_memset, lval, result_loc);
46774692
}
4693+
case BuiltinFnIdMinimum:
4694+
{
4695+
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4696+
Stage1ZirInst *arg0_value = astgen_node(ag, arg0_node, scope);
4697+
if (arg0_value == ag->codegen->invalid_inst_src)
4698+
return arg0_value;
4699+
4700+
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
4701+
Stage1ZirInst *arg1_value = astgen_node(ag, arg1_node, scope);
4702+
if (arg1_value == ag->codegen->invalid_inst_src)
4703+
return arg1_value;
4704+
4705+
Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpMinimum, arg0_value, arg1_value, true);
4706+
return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);
4707+
}
46784708
case BuiltinFnIdWasmMemorySize:
46794709
{
46804710
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);

src/stage1/bigfloat.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,30 @@ void bigfloat_sqrt(BigFloat *dest, const BigFloat *op) {
191191
f128M_sqrt(&op->value, &dest->value);
192192
}
193193

194+
void bigfloat_min(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
195+
if (bigfloat_is_nan(op1)) {
196+
bigfloat_init_bigfloat(dest, op2);
197+
} else if (bigfloat_is_nan(op2)) {
198+
bigfloat_init_bigfloat(dest, op1);
199+
} else if (f128M_lt(&op1->value, &op2->value)) {
200+
bigfloat_init_bigfloat(dest, op1);
201+
} else {
202+
bigfloat_init_bigfloat(dest, op2);
203+
}
204+
}
205+
206+
void bigfloat_max(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) {
207+
if (bigfloat_is_nan(op1)) {
208+
bigfloat_init_bigfloat(dest, op2);
209+
} else if (bigfloat_is_nan(op2)) {
210+
bigfloat_init_bigfloat(dest, op1);
211+
} else if (f128M_lt(&op1->value, &op2->value)) {
212+
bigfloat_init_bigfloat(dest, op2);
213+
} else {
214+
bigfloat_init_bigfloat(dest, op1);
215+
}
216+
}
217+
194218
bool bigfloat_is_nan(const BigFloat *op) {
195219
return f128M_isSignalingNaN(&op->value);
196220
}

src/stage1/bigfloat.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,12 @@ void bigfloat_div_floor(BigFloat *dest, const BigFloat *op1, const BigFloat *op2
4545
void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
4646
void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
4747
void bigfloat_sqrt(BigFloat *dest, const BigFloat *op);
48+
void bigfloat_min(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
49+
void bigfloat_max(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
4850
void bigfloat_append_buf(Buf *buf, const BigFloat *op);
4951
Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2);
5052

53+
5154
bool bigfloat_is_nan(const BigFloat *op);
5255

5356
// convenience functions

src/stage1/bigint.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,26 @@ bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) {
448448
}
449449
#endif
450450

451+
void bigint_max(BigInt* dest, const BigInt *op1, const BigInt *op2) {
452+
switch (bigint_cmp(op1, op2)) {
453+
case CmpEQ:
454+
case CmpLT:
455+
return bigint_init_bigint(dest, op2);
456+
case CmpGT:
457+
return bigint_init_bigint(dest, op1);
458+
}
459+
}
460+
461+
void bigint_min(BigInt* dest, const BigInt *op1, const BigInt *op2) {
462+
switch (bigint_cmp(op1, op2)) {
463+
case CmpEQ:
464+
case CmpLT:
465+
return bigint_init_bigint(dest, op1);
466+
case CmpGT:
467+
return bigint_init_bigint(dest, op2);
468+
}
469+
}
470+
451471
void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2) {
452472
if (op1->digit_count == 0) {
453473
return bigint_init_bigint(dest, op2);

src/stage1/bigint.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ bool bigint_fits_in_bits(const BigInt *bn, size_t bit_count, bool is_signed);
5656
void bigint_write_twos_complement(const BigInt *big_int, uint8_t *buf, size_t bit_count, bool is_big_endian);
5757
void bigint_read_twos_complement(BigInt *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian,
5858
bool is_signed);
59+
void bigint_max(BigInt* dest, const BigInt *op1, const BigInt *op2);
60+
void bigint_min(BigInt* dest, const BigInt *op1, const BigInt *op2);
5961
void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2);
6062
void bigint_add_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed);
6163
void bigint_sub(BigInt *dest, const BigInt *op1, const BigInt *op2);

src/stage1/codegen.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3231,6 +3231,30 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
32313231
case IrBinOpRemMod:
32323232
return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
32333233
op1_value, op2_value, operand_type, RemKindMod);
3234+
case IrBinOpMaximum:
3235+
if (scalar_type->id == ZigTypeIdFloat) {
3236+
return ZigLLVMBuildMaxNum(g->builder, op1_value, op2_value, "");
3237+
} else if (scalar_type->id == ZigTypeIdInt) {
3238+
if (scalar_type->data.integral.is_signed) {
3239+
return ZigLLVMBuildSMax(g->builder, op1_value, op2_value, "");
3240+
} else {
3241+
return ZigLLVMBuildUMax(g->builder, op1_value, op2_value, "");
3242+
}
3243+
} else {
3244+
zig_unreachable();
3245+
}
3246+
case IrBinOpMinimum:
3247+
if (scalar_type->id == ZigTypeIdFloat) {
3248+
return ZigLLVMBuildMinNum(g->builder, op1_value, op2_value, "");
3249+
} else if (scalar_type->id == ZigTypeIdInt) {
3250+
if (scalar_type->data.integral.is_signed) {
3251+
return ZigLLVMBuildSMin(g->builder, op1_value, op2_value, "");
3252+
} else {
3253+
return ZigLLVMBuildUMin(g->builder, op1_value, op2_value, "");
3254+
}
3255+
} else {
3256+
zig_unreachable();
3257+
}
32343258
}
32353259
zig_unreachable();
32363260
}
@@ -8962,6 +8986,8 @@ static void define_builtin_fns(CodeGen *g) {
89628986
create_builtin_fn(g, BuiltinFnIdWasmMemoryGrow, "wasmMemoryGrow", 2);
89638987
create_builtin_fn(g, BuiltinFnIdSrc, "src", 0);
89648988
create_builtin_fn(g, BuiltinFnIdReduce, "reduce", 2);
8989+
create_builtin_fn(g, BuiltinFnIdMaximum, "maximum", 2);
8990+
create_builtin_fn(g, BuiltinFnIdMinimum, "minimum", 2);
89658991
}
89668992

89678993
static const char *bool_to_str(bool b) {

0 commit comments

Comments
 (0)