authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-07-25 05:34:11+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-26 20:41:00-04:00
logcdeea3b0943b070d49d8d8d0855f9a38843e3ecc
tree541557cd87bddd0fb76cadd34e2cb9d20495a4bf
parent50a29f7c213d4a906839dfd625b6280663348781

minimum/maximum builtins


18 files changed, 416 insertions(+), 0 deletions(-)

doc/langref.html.in+22
......@@ -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>
80258036mem.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+21
......@@ -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+16
......@@ -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+14
......@@ -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
6153fn 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
61516159fn 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
6171fn 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
61636177fn 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+12
......@@ -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+4
......@@ -1796,6 +1796,8 @@ enum BuiltinFnId {
17961796 BuiltinFnIdWasmMemoryGrow,
17971797 BuiltinFnIdSrc,
17981798 BuiltinFnIdReduce,
1799 BuiltinFnIdMaximum,
1800 BuiltinFnIdMinimum,
17991801};
18001802
18011803struct BuiltinFnEntry {
......@@ -2938,6 +2940,8 @@ enum IrBinOp {
29382940 IrBinOpRemMod,
29392941 IrBinOpArrayCat,
29402942 IrBinOpArrayMult,
2943 IrBinOpMaximum,
2944 IrBinOpMinimum,
29412945};
29422946
29432947struct Stage1ZirInstBinOp {
src/stage1/astgen.cpp+30
......@@ -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+24
......@@ -191,6 +191,30 @@ void bigfloat_sqrt(BigFloat *dest, const BigFloat *op) {
191191 f128M_sqrt(&op->value, &dest->value);
192192}
193193
194void 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
206void 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
194218bool bigfloat_is_nan(const BigFloat *op) {
195219 return f128M_isSignalingNaN(&op->value);
196220}
src/stage1/bigfloat.hpp+3
......@@ -45,9 +45,12 @@ void bigfloat_div_floor(BigFloat *dest, const BigFloat *op1, const BigFloat *op2
4545void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
4646void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
4747void bigfloat_sqrt(BigFloat *dest, const BigFloat *op);
48void bigfloat_min(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
49void bigfloat_max(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
4850void bigfloat_append_buf(Buf *buf, const BigFloat *op);
4951Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2);
5052
53
5154bool bigfloat_is_nan(const BigFloat *op);
5255
5356// convenience functions
src/stage1/bigint.cpp+20
......@@ -448,6 +448,26 @@ bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) {
448448}
449449#endif
450450
451void 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
461void 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
451471void 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+2
......@@ -56,6 +56,8 @@ bool bigint_fits_in_bits(const BigInt *bn, size_t bit_count, bool is_signed);
5656void bigint_write_twos_complement(const BigInt *big_int, uint8_t *buf, size_t bit_count, bool is_big_endian);
5757void bigint_read_twos_complement(BigInt *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian,
5858 bool is_signed);
59void bigint_max(BigInt* dest, const BigInt *op1, const BigInt *op2);
60void bigint_min(BigInt* dest, const BigInt *op1, const BigInt *op2);
5961void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2);
6062void bigint_add_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed);
6163void bigint_sub(BigInt *dest, const BigInt *op1, const BigInt *op2);
src/stage1/codegen.cpp+26
......@@ -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
89959021static const char *bool_to_str(bool b) {
src/stage1/ir.cpp+120
......@@ -3311,6 +3311,108 @@ static void float_mod(ZigValue *out_val, ZigValue *op1, ZigValue *op2) {
33113311 }
33123312}
33133313
3314static void float_max(ZigValue *out_val, ZigValue *op1, ZigValue *op2) {
3315 assert(op1->type == op2->type);
3316 out_val->type = op1->type;
3317 if (op1->type->id == ZigTypeIdComptimeFloat) {
3318 bigfloat_max(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
3319 } else if (op1->type->id == ZigTypeIdFloat) {
3320 switch (op1->type->data.floating.bit_count) {
3321 case 16:
3322 if (zig_f16_isNaN(op1->data.x_f16)) {
3323 out_val->data.x_f16 = op2->data.x_f16;
3324 } else if (zig_f16_isNaN(op2->data.x_f16)) {
3325 out_val->data.x_f16 = op1->data.x_f16;
3326 } else {
3327 out_val->data.x_f16 = f16_lt(op1->data.x_f16, op2->data.x_f16) ? op2->data.x_f16 : op1->data.x_f16;
3328 }
3329 return;
3330 case 32:
3331 if (op1->data.x_f32 != op1->data.x_f32) {
3332 out_val->data.x_f32 = op2->data.x_f32;
3333 } else if (op2->data.x_f32 != op2->data.x_f32) {
3334 out_val->data.x_f32 = op1->data.x_f32;
3335 } else {
3336 out_val->data.x_f32 = op1->data.x_f32 > op2->data.x_f32 ? op1->data.x_f32 : op2->data.x_f32;
3337 }
3338 return;
3339 case 64:
3340 if (op1->data.x_f64 != op1->data.x_f64) {
3341 out_val->data.x_f64 = op2->data.x_f64;
3342 } else if (op2->data.x_f64 != op2->data.x_f64) {
3343 out_val->data.x_f64 = op1->data.x_f64;
3344 } else {
3345 out_val->data.x_f64 = op1->data.x_f64 > op2->data.x_f64 ? op1->data.x_f64 : op2->data.x_f64;
3346 }
3347 return;
3348 case 128:
3349 if (zig_f128_isNaN(&op1->data.x_f128)) {
3350 out_val->data.x_f128 = op2->data.x_f128;
3351 } else if (zig_f128_isNaN(&op2->data.x_f128)) {
3352 out_val->data.x_f128 = op1->data.x_f128;
3353 } else {
3354 out_val->data.x_f128 = f128M_lt(&op1->data.x_f128, &op2->data.x_f128) ? op2->data.x_f128 : op1->data.x_f128;
3355 }
3356 return;
3357 default:
3358 zig_unreachable();
3359 }
3360 } else {
3361 zig_unreachable();
3362 }
3363}
3364
3365static void float_min(ZigValue *out_val, ZigValue *op1, ZigValue *op2) {
3366 assert(op1->type == op2->type);
3367 out_val->type = op1->type;
3368 if (op1->type->id == ZigTypeIdComptimeFloat) {
3369 bigfloat_min(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
3370 } else if (op1->type->id == ZigTypeIdFloat) {
3371 switch (op1->type->data.floating.bit_count) {
3372 case 16:
3373 if (zig_f16_isNaN(op1->data.x_f16)) {
3374 out_val->data.x_f16 = op2->data.x_f16;
3375 } else if (zig_f16_isNaN(op2->data.x_f16)) {
3376 out_val->data.x_f16 = op1->data.x_f16;
3377 } else {
3378 out_val->data.x_f16 = f16_lt(op1->data.x_f16, op2->data.x_f16) ? op1->data.x_f16 : op2->data.x_f16;
3379 }
3380 return;
3381 case 32:
3382 if (op1->data.x_f32 != op1->data.x_f32) {
3383 out_val->data.x_f32 = op2->data.x_f32;
3384 } else if (op2->data.x_f32 != op2->data.x_f32) {
3385 out_val->data.x_f32 = op1->data.x_f32;
3386 } else {
3387 out_val->data.x_f32 = op1->data.x_f32 < op2->data.x_f32 ? op1->data.x_f32 : op2->data.x_f32;
3388 }
3389 return;
3390 case 64:
3391 if (op1->data.x_f64 != op1->data.x_f64) {
3392 out_val->data.x_f64 = op2->data.x_f64;
3393 } else if (op2->data.x_f64 != op2->data.x_f64) {
3394 out_val->data.x_f64 = op1->data.x_f64;
3395 } else {
3396 out_val->data.x_f64 = op1->data.x_f32 < op2->data.x_f64 ? op1->data.x_f64 : op2->data.x_f64;
3397 }
3398 return;
3399 case 128:
3400 if (zig_f128_isNaN(&op1->data.x_f128)) {
3401 out_val->data.x_f128 = op2->data.x_f128;
3402 } else if (zig_f128_isNaN(&op2->data.x_f128)) {
3403 out_val->data.x_f128 = op1->data.x_f128;
3404 } else {
3405 out_val->data.x_f128 = f128M_lt(&op1->data.x_f128, &op2->data.x_f128) ? op1->data.x_f128 : op2->data.x_f128;
3406 }
3407 return;
3408 default:
3409 zig_unreachable();
3410 }
3411 } else {
3412 zig_unreachable();
3413 }
3414}
3415
33143416static void float_negate(ZigValue *out_val, ZigValue *op) {
33153417 out_val->type = op->type;
33163418 if (op->type->id == ZigTypeIdComptimeFloat) {
......@@ -9704,6 +9806,20 @@ static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, Scope *scope, AstNode *s
97049806 float_mod(out_val, op1_val, op2_val);
97059807 }
97069808 break;
9809 case IrBinOpMaximum:
9810 if (is_int) {
9811 bigint_max(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
9812 } else {
9813 float_max(out_val, op1_val, op2_val);
9814 }
9815 break;
9816 case IrBinOpMinimum:
9817 if (is_int) {
9818 bigint_min(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
9819 } else {
9820 float_min(out_val, op1_val, op2_val);
9821 }
9822 break;
97079823 }
97089824
97099825 if (type_entry->id == ZigTypeIdInt) {
......@@ -9904,6 +10020,8 @@ static bool ok_float_op(IrBinOp op) {
990410020 case IrBinOpRemRem:
990510021 case IrBinOpRemMod:
990610022 case IrBinOpRemUnspecified:
10023 case IrBinOpMaximum:
10024 case IrBinOpMinimum:
990710025 return true;
990810026
990910027 case IrBinOpBoolOr:
......@@ -10894,6 +11012,8 @@ static Stage1AirInst *ir_analyze_instruction_bin_op(IrAnalyze *ira, Stage1ZirIns
1089411012 case IrBinOpRemUnspecified:
1089511013 case IrBinOpRemRem:
1089611014 case IrBinOpRemMod:
11015 case IrBinOpMaximum:
11016 case IrBinOpMinimum:
1089711017 return ir_analyze_bin_op_math(ira, bin_op_instruction);
1089811018 case IrBinOpArrayCat:
1089911019 return ir_analyze_array_cat(ira, bin_op_instruction);
src/stage1/ir_print.cpp+4
......@@ -733,6 +733,10 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) {
733733 return "++";
734734 case IrBinOpArrayMult:
735735 return "**";
736 case IrBinOpMaximum:
737 return "@maximum";
738 case IrBinOpMinimum:
739 return "@minimum";
736740 }
737741 zig_unreachable();
738742}
src/zig_llvm.cpp+30
......@@ -458,6 +458,36 @@ LLVMValueRef ZigLLVMBuildMemSet(LLVMBuilderRef B, LLVMValueRef Ptr, LLVMValueRef
458458 return wrap(call_inst);
459459}
460460
461LLVMValueRef ZigLLVMBuildMaxNum(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS, const char *name) {
462 CallInst *call_inst = unwrap(B)->CreateMaxNum(unwrap(LHS), unwrap(RHS), name);
463 return wrap(call_inst);
464}
465
466LLVMValueRef ZigLLVMBuildMinNum(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS, const char *name) {
467 CallInst *call_inst = unwrap(B)->CreateMinNum(unwrap(LHS), unwrap(RHS), name);
468 return wrap(call_inst);
469}
470
471LLVMValueRef ZigLLVMBuildUMax(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS, const char *name) {
472 CallInst *call_inst = unwrap(B)->CreateBinaryIntrinsic(Intrinsic::umax, unwrap(LHS), unwrap(RHS), nullptr, name);
473 return wrap(call_inst);
474}
475
476LLVMValueRef ZigLLVMBuildUMin(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS, const char *name) {
477 CallInst *call_inst = unwrap(B)->CreateBinaryIntrinsic(Intrinsic::umin, unwrap(LHS), unwrap(RHS), nullptr, name);
478 return wrap(call_inst);
479}
480
481LLVMValueRef ZigLLVMBuildSMax(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS, const char *name) {
482 CallInst *call_inst = unwrap(B)->CreateBinaryIntrinsic(Intrinsic::smax, unwrap(LHS), unwrap(RHS), nullptr, name);
483 return wrap(call_inst);
484}
485
486LLVMValueRef ZigLLVMBuildSMin(LLVMBuilderRef B, LLVMValueRef LHS, LLVMValueRef RHS, const char *name) {
487 CallInst *call_inst = unwrap(B)->CreateBinaryIntrinsic(Intrinsic::smin, unwrap(LHS), unwrap(RHS), nullptr, name);
488 return wrap(call_inst);
489}
490
461491void ZigLLVMFnSetSubprogram(LLVMValueRef fn, ZigLLVMDISubprogram *subprogram) {
462492 assert( isa<Function>(unwrap(fn)) );
463493 Function *unwrapped_function = reinterpret_cast<Function*>(unwrap(fn));
src/zig_llvm.h+9
......@@ -129,6 +129,14 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildMemCpy(LLVMBuilderRef B, LLVMValueRef Dst,
129129ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildMemSet(LLVMBuilderRef B, LLVMValueRef Ptr, LLVMValueRef Val, LLVMValueRef Size,
130130 unsigned Align, bool isVolatile);
131131
132ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildMaxNum(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char* name);
133ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildMinNum(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char* name);
134
135ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildUMax(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char* name);
136ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildUMin(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char* name);
137ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildSMax(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char* name);
138ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildSMin(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char* name);
139
132140ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,
133141 LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,
134142 LLVMAtomicOrdering failure_ordering, bool is_weak);
......@@ -142,6 +150,7 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildLShrExact(LLVMBuilderRef builder, LLVMValu
142150ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildAShrExact(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,
143151 const char *name);
144152
153
145154ZIG_EXTERN_C struct ZigLLVMDIType *ZigLLVMCreateDebugPointerType(struct ZigLLVMDIBuilder *dibuilder,
146155 struct ZigLLVMDIType *pointee_type, uint64_t size_in_bits, uint64_t align_in_bits, const char *name);
147156
test/behavior.zig+1
......@@ -105,6 +105,7 @@ test {
105105 _ = @import("behavior/inttoptr.zig");
106106 _ = @import("behavior/ir_block_deps.zig");
107107 _ = @import("behavior/math.zig");
108 _ = @import("behavior/maximum_minimum.zig");
108109 _ = @import("behavior/merge_error_sets.zig");
109110 _ = @import("behavior/misc.zig");
110111 _ = @import("behavior/muladd.zig");
test/behavior/maximum_minimum.zig created+58
......@@ -0,0 +1,58 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const mem = std.mem;
4const expect = std.testing.expect;
5const expectEqual = std.testing.expectEqual;
6const Vector = std.meta.Vector;
7
8test "@maximum" {
9 const S = struct {
10 fn doTheTest() !void {
11 try expectEqual(@as(i32, 10), @maximum(@as(i32, -3), @as(i32, 10)));
12 try expectEqual(@as(f32, 3.2), @maximum(@as(f32, 3.2), @as(f32, 0.68)));
13
14 var a: Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 };
15 var b: Vector(4, i32) = [4]i32{ 1, 2147483647, 3, 4 };
16 var x = @maximum(a, b);
17 try expect(mem.eql(i32, &@as([4]i32, x), &[4]i32{ 2147483647, 2147483647, 30, 40 }));
18
19 var c: Vector(4, f32) = [4]f32{ 0, 0.4, -2.4, 7.8 };
20 var d: Vector(4, f32) = [4]f32{ -0.23, 0.42, -0.64, 0.9 };
21 var y = @maximum(c, d);
22 try expect(mem.eql(f32, &@as([4]f32, y), &[4]f32{ 0, 0.42, -0.64, 7.8 }));
23
24 var e: Vector(2, f32) = [2]f32{ 0, std.math.qnan_f32 };
25 var f: Vector(2, f32) = [2]f32{ std.math.qnan_f32, 0 };
26 var z = @maximum(e, f);
27 try expect(mem.eql(f32, &@as([2]f32, z), &[2]f32{ 0, 0 }));
28 }
29 };
30 try S.doTheTest();
31 comptime try S.doTheTest();
32}
33
34test "@minimum" {
35 const S = struct {
36 fn doTheTest() !void {
37 try expectEqual(@as(i32, -3), @minimum(@as(i32, -3), @as(i32, 10)));
38 try expectEqual(@as(f32, 0.68), @minimum(@as(f32, 3.2), @as(f32, 0.68)));
39
40 var a: Vector(4, i32) = [4]i32{ 2147483647, -2, 30, 40 };
41 var b: Vector(4, i32) = [4]i32{ 1, 2147483647, 3, 4 };
42 var x = @minimum(a, b);
43 try expect(mem.eql(i32, &@as([4]i32, x), &[4]i32{ 1, -2, 3, 4 }));
44
45 var c: Vector(4, f32) = [4]f32{ 0, 0.4, -2.4, 7.8 };
46 var d: Vector(4, f32) = [4]f32{ -0.23, 0.42, -0.64, 0.9 };
47 var y = @minimum(c, d);
48 try expect(mem.eql(f32, &@as([4]f32, y), &[4]f32{ -0.23, 0.4, -2.4, 0.9 }));
49
50 var e: Vector(2, f32) = [2]f32{ 0, std.math.qnan_f32 };
51 var f: Vector(2, f32) = [2]f32{ std.math.qnan_f32, 0 };
52 var z = @maximum(e, f);
53 try expect(mem.eql(f32, &@as([2]f32, z), &[2]f32{ 0, 0 }));
54 }
55 };
56 try S.doTheTest();
57 comptime try S.doTheTest();
58}