Skip to content

Commit cdeea3b

Browse files
Snektronandrewrk
authored andcommitted
minimum/maximum builtins
1 parent 50a29f7 commit cdeea3b

18 files changed

+416
-0
lines changed

doc/langref.html.in

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7988,6 +7988,17 @@ test "@hasDecl" {
79887988
</p>
79897989
{#header_close#}
79907990

7991+
{#header_open|@maximum#}
7992+
<pre>{#syntax#}@maximum(a: T, b: T) T{#endsyntax#}</pre>
7993+
<p>
7994+
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.
7995+
</p>
7996+
<p>
7997+
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.
7998+
</p>
7999+
{#see_also|@minimum|SIMD|Vectors#}
8000+
{#header_close#}
8001+
79918002
{#header_open|@memcpy#}
79928003
<pre>{#syntax#}@memcpy(noalias dest: [*]u8, noalias source: [*]const u8, byte_count: usize){#endsyntax#}</pre>
79938004
<p>
@@ -8025,6 +8036,17 @@ mem.copy(u8, dest[0..byte_count], source[0..byte_count]);{#endsyntax#}</pre>
80258036
mem.set(u8, dest, c);{#endsyntax#}</pre>
80268037
{#header_close#}
80278038

8039+
{#header_open|@minimum#}
8040+
<pre>{#syntax#}@minimum(a: T, b: T) T{#endsyntax#}</pre>
8041+
<p>
8042+
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.
8043+
</p>
8044+
<p>
8045+
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.
8046+
</p>
8047+
{#see_also|@maximum|SIMD|Vectors#}
8048+
{#header_close#}
8049+
80288050
{#header_open|@wasmMemorySize#}
80298051
<pre>{#syntax#}@wasmMemorySize(index: u32) u32{#endsyntax#}</pre>
80308052
<p>

src/AstGen.zig

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2098,8 +2098,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
20982098
.builtin_call,
20992099
.field_ptr_type,
21002100
.field_parent_ptr,
2101+
.maximum,
21012102
.memcpy,
21022103
.memset,
2104+
.minimum,
21032105
.builtin_async_call,
21042106
.c_import,
21052107
.@"resume",
@@ -7227,6 +7229,25 @@ fn builtinCall(
72277229
return rvalue(gz, rl, result, node);
72287230
},
72297231

7232+
.maximum => {
7233+
const a = try expr(gz, scope, .none, params[0]);
7234+
const b = try expr(gz, scope, .none, params[1]);
7235+
const result = try gz.addPlNode(.maximum, node, Zir.Inst.Bin{
7236+
.lhs = a,
7237+
.rhs = b,
7238+
});
7239+
return rvalue(gz, rl, result, node);
7240+
},
7241+
.minimum => {
7242+
const a = try expr(gz, scope, .none, params[0]);
7243+
const b = try expr(gz, scope, .none, params[1]);
7244+
const result = try gz.addPlNode(.minimum, node, Zir.Inst.Bin{
7245+
.lhs = a,
7246+
.rhs = b,
7247+
});
7248+
return rvalue(gz, rl, result, node);
7249+
},
7250+
72307251
.add_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .add_with_overflow),
72317252
.sub_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .sub_with_overflow),
72327253
.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,
@@ -518,6 +520,13 @@ pub const list = list: {
518520
.param_count = 2,
519521
},
520522
},
523+
.{
524+
"@maximum",
525+
.{
526+
.tag = .maximum,
527+
.param_count = 2,
528+
},
529+
},
521530
.{
522531
"@memcpy",
523532
.{
@@ -532,6 +541,13 @@ pub const list = list: {
532541
.param_count = 3,
533542
},
534543
},
544+
.{
545+
"@minimum",
546+
.{
547+
.tag = .minimum,
548+
.param_count = 2,
549+
},
550+
},
535551
.{
536552
"@wasmMemorySize",
537553
.{

src/Sema.zig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,8 +346,10 @@ pub fn analyzeBody(
346346
.builtin_call => try sema.zirBuiltinCall(block, inst),
347347
.field_ptr_type => try sema.zirFieldPtrType(block, inst),
348348
.field_parent_ptr => try sema.zirFieldParentPtr(block, inst),
349+
.maximum => try sema.zirMaximum(block, inst),
349350
.memcpy => try sema.zirMemcpy(block, inst),
350351
.memset => try sema.zirMemset(block, inst),
352+
.minimum => try sema.zirMinimum(block, inst),
351353
.builtin_async_call => try sema.zirBuiltinAsyncCall(block, inst),
352354
.@"resume" => try sema.zirResume(block, inst),
353355
.@"await" => try sema.zirAwait(block, inst, false),
@@ -6148,6 +6150,12 @@ fn zirFieldParentPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Com
61486150
return sema.mod.fail(&block.base, src, "TODO: Sema.zirFieldParentPtr", .{});
61496151
}
61506152

6153+
fn zirMaximum(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
6154+
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6155+
const src = inst_data.src();
6156+
return sema.mod.fail(&block.base, src, "TODO: Sema.zirMaximum", .{});
6157+
}
6158+
61516159
fn zirMemcpy(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
61526160
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
61536161
const src = inst_data.src();
@@ -6160,6 +6168,12 @@ fn zirMemset(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErro
61606168
return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemset", .{});
61616169
}
61626170

6171+
fn zirMinimum(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
6172+
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6173+
const src = inst_data.src();
6174+
return sema.mod.fail(&block.base, src, "TODO: Sema.zirMinimum", .{});
6175+
}
6176+
61636177
fn zirBuiltinAsyncCall(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
61646178
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
61656179
const src = inst_data.src();

src/Zir.zig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -915,12 +915,18 @@ pub const Inst = struct {
915915
/// Implements the `@fieldParentPtr` builtin.
916916
/// Uses the `pl_node` union field with payload `FieldParentPtr`.
917917
field_parent_ptr,
918+
/// Implements the `@maximum` builtin.
919+
/// Uses the `pl_node` union field with payload `Bin`
920+
maximum,
918921
/// Implements the `@memcpy` builtin.
919922
/// Uses the `pl_node` union field with payload `Memcpy`.
920923
memcpy,
921924
/// Implements the `@memset` builtin.
922925
/// Uses the `pl_node` union field with payload `Memset`.
923926
memset,
927+
/// Implements the `@minimum` builtin.
928+
/// Uses the `pl_node` union field with payload `Bin`
929+
minimum,
924930
/// Implements the `@asyncCall` builtin.
925931
/// Uses the `pl_node` union field with payload `AsyncCall`.
926932
builtin_async_call,
@@ -1192,8 +1198,10 @@ pub const Inst = struct {
11921198
.builtin_call,
11931199
.field_ptr_type,
11941200
.field_parent_ptr,
1201+
.maximum,
11951202
.memcpy,
11961203
.memset,
1204+
.minimum,
11971205
.builtin_async_call,
11981206
.c_import,
11991207
.@"resume",
@@ -1463,8 +1471,10 @@ pub const Inst = struct {
14631471
.builtin_call = .pl_node,
14641472
.field_ptr_type = .bin,
14651473
.field_parent_ptr = .pl_node,
1474+
.maximum = .pl_node,
14661475
.memcpy = .pl_node,
14671476
.memset = .pl_node,
1477+
.minimum = .pl_node,
14681478
.builtin_async_call = .pl_node,
14691479
.c_import = .pl_node,
14701480

@@ -3020,6 +3030,8 @@ const Writer = struct {
30203030
.bitcast,
30213031
.bitcast_result_ptr,
30223032
.vector_type,
3033+
.maximum,
3034+
.minimum,
30233035
=> try self.writePlNodeBin(stream, inst),
30243036

30253037
.@"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
@@ -1796,6 +1796,8 @@ enum BuiltinFnId {
17961796
BuiltinFnIdWasmMemoryGrow,
17971797
BuiltinFnIdSrc,
17981798
BuiltinFnIdReduce,
1799+
BuiltinFnIdMaximum,
1800+
BuiltinFnIdMinimum,
17991801
};
18001802

18011803
struct BuiltinFnEntry {
@@ -2938,6 +2940,8 @@ enum IrBinOp {
29382940
IrBinOpRemMod,
29392941
IrBinOpArrayCat,
29402942
IrBinOpArrayMult,
2943+
IrBinOpMaximum,
2944+
IrBinOpMinimum,
29412945
};
29422946

29432947
struct Stage1ZirInstBinOp {

src/stage1/astgen.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4686,6 +4686,21 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast
46864686
arg0_value, arg1_value);
46874687
return ir_lval_wrap(ag, scope, splat, lval, result_loc);
46884688
}
4689+
case BuiltinFnIdMaximum:
4690+
{
4691+
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4692+
Stage1ZirInst *arg0_value = astgen_node(ag, arg0_node, scope);
4693+
if (arg0_value == ag->codegen->invalid_inst_src)
4694+
return arg0_value;
4695+
4696+
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
4697+
Stage1ZirInst *arg1_value = astgen_node(ag, arg1_node, scope);
4698+
if (arg1_value == ag->codegen->invalid_inst_src)
4699+
return arg1_value;
4700+
4701+
Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpMaximum, arg0_value, arg1_value, true);
4702+
return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);
4703+
}
46894704
case BuiltinFnIdMemcpy:
46904705
{
46914706
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
47264741
Stage1ZirInst *ir_memset = ir_build_memset_src(ag, scope, node, arg0_value, arg1_value, arg2_value);
47274742
return ir_lval_wrap(ag, scope, ir_memset, lval, result_loc);
47284743
}
4744+
case BuiltinFnIdMinimum:
4745+
{
4746+
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4747+
Stage1ZirInst *arg0_value = astgen_node(ag, arg0_node, scope);
4748+
if (arg0_value == ag->codegen->invalid_inst_src)
4749+
return arg0_value;
4750+
4751+
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
4752+
Stage1ZirInst *arg1_value = astgen_node(ag, arg1_node, scope);
4753+
if (arg1_value == ag->codegen->invalid_inst_src)
4754+
return arg1_value;
4755+
4756+
Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpMinimum, arg0_value, arg1_value, true);
4757+
return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);
4758+
}
47294759
case BuiltinFnIdWasmMemorySize:
47304760
{
47314761
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
@@ -3248,6 +3248,30 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
32483248
case IrBinOpRemMod:
32493249
return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
32503250
op1_value, op2_value, operand_type, RemKindMod);
3251+
case IrBinOpMaximum:
3252+
if (scalar_type->id == ZigTypeIdFloat) {
3253+
return ZigLLVMBuildMaxNum(g->builder, op1_value, op2_value, "");
3254+
} else if (scalar_type->id == ZigTypeIdInt) {
3255+
if (scalar_type->data.integral.is_signed) {
3256+
return ZigLLVMBuildSMax(g->builder, op1_value, op2_value, "");
3257+
} else {
3258+
return ZigLLVMBuildUMax(g->builder, op1_value, op2_value, "");
3259+
}
3260+
} else {
3261+
zig_unreachable();
3262+
}
3263+
case IrBinOpMinimum:
3264+
if (scalar_type->id == ZigTypeIdFloat) {
3265+
return ZigLLVMBuildMinNum(g->builder, op1_value, op2_value, "");
3266+
} else if (scalar_type->id == ZigTypeIdInt) {
3267+
if (scalar_type->data.integral.is_signed) {
3268+
return ZigLLVMBuildSMin(g->builder, op1_value, op2_value, "");
3269+
} else {
3270+
return ZigLLVMBuildUMin(g->builder, op1_value, op2_value, "");
3271+
}
3272+
} else {
3273+
zig_unreachable();
3274+
}
32513275
}
32523276
zig_unreachable();
32533277
}
@@ -8990,6 +9014,8 @@ static void define_builtin_fns(CodeGen *g) {
89909014
create_builtin_fn(g, BuiltinFnIdWasmMemoryGrow, "wasmMemoryGrow", 2);
89919015
create_builtin_fn(g, BuiltinFnIdSrc, "src", 0);
89929016
create_builtin_fn(g, BuiltinFnIdReduce, "reduce", 2);
9017+
create_builtin_fn(g, BuiltinFnIdMaximum, "maximum", 2);
9018+
create_builtin_fn(g, BuiltinFnIdMinimum, "minimum", 2);
89939019
}
89949020

89959021
static const char *bool_to_str(bool b) {

0 commit comments

Comments
 (0)