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" {...@@ -7988,6 +7988,17 @@ test "@hasDecl" {
7988 </p>7988 </p>
7989 {#header_close#}7989 {#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
7991 {#header_open|@memcpy#}8002 {#header_open|@memcpy#}
7992 <pre>{#syntax#}@memcpy(noalias dest: [*]u8, noalias source: [*]const u8, byte_count: usize){#endsyntax#}</pre>8003 <pre>{#syntax#}@memcpy(noalias dest: [*]u8, noalias source: [*]const u8, byte_count: usize){#endsyntax#}</pre>
7993 <p>8004 <p>
...@@ -8025,6 +8036,17 @@ mem.copy(u8, dest[0..byte_count], source[0..byte_count]);{#endsyntax#}</pre>...@@ -8025,6 +8036,17 @@ mem.copy(u8, dest[0..byte_count], source[0..byte_count]);{#endsyntax#}</pre>
8025mem.set(u8, dest, c);{#endsyntax#}</pre>8036mem.set(u8, dest, c);{#endsyntax#}</pre>
8026 {#header_close#}8037 {#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
8028 {#header_open|@wasmMemorySize#}8050 {#header_open|@wasmMemorySize#}
8029 <pre>{#syntax#}@wasmMemorySize(index: u32) u32{#endsyntax#}</pre>8051 <pre>{#syntax#}@wasmMemorySize(index: u32) u32{#endsyntax#}</pre>
8030 <p>8052 <p>
src/AstGen.zig+21
...@@ -2098,8 +2098,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner...@@ -2098,8 +2098,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
2098 .builtin_call,2098 .builtin_call,
2099 .field_ptr_type,2099 .field_ptr_type,
2100 .field_parent_ptr,2100 .field_parent_ptr,
2101 .maximum,
2101 .memcpy,2102 .memcpy,
2102 .memset,2103 .memset,
2104 .minimum,
2103 .builtin_async_call,2105 .builtin_async_call,
2104 .c_import,2106 .c_import,
2105 .@"resume",2107 .@"resume",
...@@ -7227,6 +7229,25 @@ fn builtinCall(...@@ -7227,6 +7229,25 @@ fn builtinCall(
7227 return rvalue(gz, rl, result, node);7229 return rvalue(gz, rl, result, node);
7228 },7230 },
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
7230 .add_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .add_with_overflow),7251 .add_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .add_with_overflow),
7231 .sub_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .sub_with_overflow),7252 .sub_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .sub_with_overflow),
7232 .mul_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .mul_with_overflow),7253 .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 {...@@ -57,8 +57,10 @@ pub const Tag = enum {
57 int_to_error,57 int_to_error,
58 int_to_float,58 int_to_float,
59 int_to_ptr,59 int_to_ptr,
60 maximum,
60 memcpy,61 memcpy,
61 memset,62 memset,
63 minimum,
62 wasm_memory_size,64 wasm_memory_size,
63 wasm_memory_grow,65 wasm_memory_grow,
64 mod,66 mod,
...@@ -518,6 +520,13 @@ pub const list = list: {...@@ -518,6 +520,13 @@ pub const list = list: {
518 .param_count = 2,520 .param_count = 2,
519 },521 },
520 },522 },
523 .{
524 "@maximum",
525 .{
526 .tag = .maximum,
527 .param_count = 2,
528 },
529 },
521 .{530 .{
522 "@memcpy",531 "@memcpy",
523 .{532 .{
...@@ -532,6 +541,13 @@ pub const list = list: {...@@ -532,6 +541,13 @@ pub const list = list: {
532 .param_count = 3,541 .param_count = 3,
533 },542 },
534 },543 },
544 .{
545 "@minimum",
546 .{
547 .tag = .minimum,
548 .param_count = 2,
549 },
550 },
535 .{551 .{
536 "@wasmMemorySize",552 "@wasmMemorySize",
537 .{553 .{
src/Sema.zig+14
...@@ -346,8 +346,10 @@ pub fn analyzeBody(...@@ -346,8 +346,10 @@ pub fn analyzeBody(
346 .builtin_call => try sema.zirBuiltinCall(block, inst),346 .builtin_call => try sema.zirBuiltinCall(block, inst),
347 .field_ptr_type => try sema.zirFieldPtrType(block, inst),347 .field_ptr_type => try sema.zirFieldPtrType(block, inst),
348 .field_parent_ptr => try sema.zirFieldParentPtr(block, inst),348 .field_parent_ptr => try sema.zirFieldParentPtr(block, inst),
349 .maximum => try sema.zirMaximum(block, inst),
349 .memcpy => try sema.zirMemcpy(block, inst),350 .memcpy => try sema.zirMemcpy(block, inst),
350 .memset => try sema.zirMemset(block, inst),351 .memset => try sema.zirMemset(block, inst),
352 .minimum => try sema.zirMinimum(block, inst),
351 .builtin_async_call => try sema.zirBuiltinAsyncCall(block, inst),353 .builtin_async_call => try sema.zirBuiltinAsyncCall(block, inst),
352 .@"resume" => try sema.zirResume(block, inst),354 .@"resume" => try sema.zirResume(block, inst),
353 .@"await" => try sema.zirAwait(block, inst, false),355 .@"await" => try sema.zirAwait(block, inst, false),
...@@ -6148,6 +6150,12 @@ fn zirFieldParentPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Com...@@ -6148,6 +6150,12 @@ fn zirFieldParentPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Com
6148 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFieldParentPtr", .{});6150 return sema.mod.fail(&block.base, src, "TODO: Sema.zirFieldParentPtr", .{});
6149}6151}
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
6151fn zirMemcpy(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {6159fn zirMemcpy(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
6152 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6160 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6153 const src = inst_data.src();6161 const src = inst_data.src();
...@@ -6160,6 +6168,12 @@ fn zirMemset(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErro...@@ -6160,6 +6168,12 @@ fn zirMemset(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErro
6160 return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemset", .{});6168 return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemset", .{});
6161}6169}
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
6163fn zirBuiltinAsyncCall(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {6177fn zirBuiltinAsyncCall(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
6164 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6178 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6165 const src = inst_data.src();6179 const src = inst_data.src();
src/Zir.zig+12
...@@ -915,12 +915,18 @@ pub const Inst = struct {...@@ -915,12 +915,18 @@ pub const Inst = struct {
915 /// Implements the `@fieldParentPtr` builtin.915 /// Implements the `@fieldParentPtr` builtin.
916 /// Uses the `pl_node` union field with payload `FieldParentPtr`.916 /// Uses the `pl_node` union field with payload `FieldParentPtr`.
917 field_parent_ptr,917 field_parent_ptr,
918 /// Implements the `@maximum` builtin.
919 /// Uses the `pl_node` union field with payload `Bin`
920 maximum,
918 /// Implements the `@memcpy` builtin.921 /// Implements the `@memcpy` builtin.
919 /// Uses the `pl_node` union field with payload `Memcpy`.922 /// Uses the `pl_node` union field with payload `Memcpy`.
920 memcpy,923 memcpy,
921 /// Implements the `@memset` builtin.924 /// Implements the `@memset` builtin.
922 /// Uses the `pl_node` union field with payload `Memset`.925 /// Uses the `pl_node` union field with payload `Memset`.
923 memset,926 memset,
927 /// Implements the `@minimum` builtin.
928 /// Uses the `pl_node` union field with payload `Bin`
929 minimum,
924 /// Implements the `@asyncCall` builtin.930 /// Implements the `@asyncCall` builtin.
925 /// Uses the `pl_node` union field with payload `AsyncCall`.931 /// Uses the `pl_node` union field with payload `AsyncCall`.
926 builtin_async_call,932 builtin_async_call,
...@@ -1192,8 +1198,10 @@ pub const Inst = struct {...@@ -1192,8 +1198,10 @@ pub const Inst = struct {
1192 .builtin_call,1198 .builtin_call,
1193 .field_ptr_type,1199 .field_ptr_type,
1194 .field_parent_ptr,1200 .field_parent_ptr,
1201 .maximum,
1195 .memcpy,1202 .memcpy,
1196 .memset,1203 .memset,
1204 .minimum,
1197 .builtin_async_call,1205 .builtin_async_call,
1198 .c_import,1206 .c_import,
1199 .@"resume",1207 .@"resume",
...@@ -1463,8 +1471,10 @@ pub const Inst = struct {...@@ -1463,8 +1471,10 @@ pub const Inst = struct {
1463 .builtin_call = .pl_node,1471 .builtin_call = .pl_node,
1464 .field_ptr_type = .bin,1472 .field_ptr_type = .bin,
1465 .field_parent_ptr = .pl_node,1473 .field_parent_ptr = .pl_node,
1474 .maximum = .pl_node,
1466 .memcpy = .pl_node,1475 .memcpy = .pl_node,
1467 .memset = .pl_node,1476 .memset = .pl_node,
1477 .minimum = .pl_node,
1468 .builtin_async_call = .pl_node,1478 .builtin_async_call = .pl_node,
1469 .c_import = .pl_node,1479 .c_import = .pl_node,
14701480
...@@ -3020,6 +3030,8 @@ const Writer = struct {...@@ -3020,6 +3030,8 @@ const Writer = struct {
3020 .bitcast,3030 .bitcast,
3021 .bitcast_result_ptr,3031 .bitcast_result_ptr,
3022 .vector_type,3032 .vector_type,
3033 .maximum,
3034 .minimum,
3023 => try self.writePlNodeBin(stream, inst),3035 => try self.writePlNodeBin(stream, inst),
30243036
3025 .@"export" => try self.writePlNodeExport(stream, inst),3037 .@"export" => try self.writePlNodeExport(stream, inst),
src/stage1/all_types.hpp+4
...@@ -1796,6 +1796,8 @@ enum BuiltinFnId {...@@ -1796,6 +1796,8 @@ enum BuiltinFnId {
1796 BuiltinFnIdWasmMemoryGrow,1796 BuiltinFnIdWasmMemoryGrow,
1797 BuiltinFnIdSrc,1797 BuiltinFnIdSrc,
1798 BuiltinFnIdReduce,1798 BuiltinFnIdReduce,
1799 BuiltinFnIdMaximum,
1800 BuiltinFnIdMinimum,
1799};1801};
18001802
1801struct BuiltinFnEntry {1803struct BuiltinFnEntry {
...@@ -2938,6 +2940,8 @@ enum IrBinOp {...@@ -2938,6 +2940,8 @@ enum IrBinOp {
2938 IrBinOpRemMod,2940 IrBinOpRemMod,
2939 IrBinOpArrayCat,2941 IrBinOpArrayCat,
2940 IrBinOpArrayMult,2942 IrBinOpArrayMult,
2943 IrBinOpMaximum,
2944 IrBinOpMinimum,
2941};2945};
29422946
2943struct Stage1ZirInstBinOp {2947struct Stage1ZirInstBinOp {
src/stage1/astgen.cpp+30
...@@ -4686,6 +4686,21 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast...@@ -4686,6 +4686,21 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast
4686 arg0_value, arg1_value);4686 arg0_value, arg1_value);
4687 return ir_lval_wrap(ag, scope, splat, lval, result_loc);4687 return ir_lval_wrap(ag, scope, splat, lval, result_loc);
4688 }4688 }
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 }
4689 case BuiltinFnIdMemcpy:4704 case BuiltinFnIdMemcpy:
4690 {4705 {
4691 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);4706 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...@@ -4726,6 +4741,21 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast
4726 Stage1ZirInst *ir_memset = ir_build_memset_src(ag, scope, node, arg0_value, arg1_value, arg2_value);4741 Stage1ZirInst *ir_memset = ir_build_memset_src(ag, scope, node, arg0_value, arg1_value, arg2_value);
4727 return ir_lval_wrap(ag, scope, ir_memset, lval, result_loc);4742 return ir_lval_wrap(ag, scope, ir_memset, lval, result_loc);
4728 }4743 }
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 }
4729 case BuiltinFnIdWasmMemorySize:4759 case BuiltinFnIdWasmMemorySize:
4730 {4760 {
4731 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);4761 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) {...@@ -191,6 +191,30 @@ void bigfloat_sqrt(BigFloat *dest, const BigFloat *op) {
191 f128M_sqrt(&op->value, &dest->value);191 f128M_sqrt(&op->value, &dest->value);
192}192}
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
194bool bigfloat_is_nan(const BigFloat *op) {218bool bigfloat_is_nan(const BigFloat *op) {
195 return f128M_isSignalingNaN(&op->value);219 return f128M_isSignalingNaN(&op->value);
196}220}
src/stage1/bigfloat.hpp+3
...@@ -45,9 +45,12 @@ void bigfloat_div_floor(BigFloat *dest, const BigFloat *op1, const BigFloat *op2...@@ -45,9 +45,12 @@ void bigfloat_div_floor(BigFloat *dest, const BigFloat *op1, const BigFloat *op2
45void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);45void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
46void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);46void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
47void bigfloat_sqrt(BigFloat *dest, const BigFloat *op);47void 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);
48void bigfloat_append_buf(Buf *buf, const BigFloat *op);50void bigfloat_append_buf(Buf *buf, const BigFloat *op);
49Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2);51Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2);
5052
53
51bool bigfloat_is_nan(const BigFloat *op);54bool bigfloat_is_nan(const BigFloat *op);
5255
53// convenience functions56// convenience functions
src/stage1/bigint.cpp+20
...@@ -448,6 +448,26 @@ bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) {...@@ -448,6 +448,26 @@ bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result) {
448}448}
449#endif449#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
451void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2) {471void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2) {
452 if (op1->digit_count == 0) {472 if (op1->digit_count == 0) {
453 return bigint_init_bigint(dest, op2);473 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);...@@ -56,6 +56,8 @@ bool bigint_fits_in_bits(const BigInt *bn, size_t bit_count, bool is_signed);
56void bigint_write_twos_complement(const BigInt *big_int, uint8_t *buf, size_t bit_count, bool is_big_endian);56void bigint_write_twos_complement(const BigInt *big_int, uint8_t *buf, size_t bit_count, bool is_big_endian);
57void bigint_read_twos_complement(BigInt *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian,57void bigint_read_twos_complement(BigInt *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian,
58 bool is_signed);58 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);
59void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2);61void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2);
60void bigint_add_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed);62void bigint_add_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed);
61void bigint_sub(BigInt *dest, const BigInt *op1, const BigInt *op2);63void 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,...@@ -3248,6 +3248,30 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
3248 case IrBinOpRemMod:3248 case IrBinOpRemMod:
3249 return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),3249 return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
3250 op1_value, op2_value, operand_type, RemKindMod);3250 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 }
3251 }3275 }
3252 zig_unreachable();3276 zig_unreachable();
3253}3277}
...@@ -8990,6 +9014,8 @@ static void define_builtin_fns(CodeGen *g) {...@@ -8990,6 +9014,8 @@ static void define_builtin_fns(CodeGen *g) {
8990 create_builtin_fn(g, BuiltinFnIdWasmMemoryGrow, "wasmMemoryGrow", 2);9014 create_builtin_fn(g, BuiltinFnIdWasmMemoryGrow, "wasmMemoryGrow", 2);
8991 create_builtin_fn(g, BuiltinFnIdSrc, "src", 0);9015 create_builtin_fn(g, BuiltinFnIdSrc, "src", 0);
8992 create_builtin_fn(g, BuiltinFnIdReduce, "reduce", 2);9016 create_builtin_fn(g, BuiltinFnIdReduce, "reduce", 2);
9017 create_builtin_fn(g, BuiltinFnIdMaximum, "maximum", 2);
9018 create_builtin_fn(g, BuiltinFnIdMinimum, "minimum", 2);
8993}9019}
89949020
8995static const char *bool_to_str(bool b) {9021static 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) {...@@ -3311,6 +3311,108 @@ static void float_mod(ZigValue *out_val, ZigValue *op1, ZigValue *op2) {
3311 }3311 }
3312}3312}
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
3314static void float_negate(ZigValue *out_val, ZigValue *op) {3416static void float_negate(ZigValue *out_val, ZigValue *op) {
3315 out_val->type = op->type;3417 out_val->type = op->type;
3316 if (op->type->id == ZigTypeIdComptimeFloat) {3418 if (op->type->id == ZigTypeIdComptimeFloat) {
...@@ -9704,6 +9806,20 @@ static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, Scope *scope, AstNode *s...@@ -9704,6 +9806,20 @@ static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, Scope *scope, AstNode *s
9704 float_mod(out_val, op1_val, op2_val);9806 float_mod(out_val, op1_val, op2_val);
9705 }9807 }
9706 break;9808 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;
9707 }9823 }
97089824
9709 if (type_entry->id == ZigTypeIdInt) {9825 if (type_entry->id == ZigTypeIdInt) {
...@@ -9904,6 +10020,8 @@ static bool ok_float_op(IrBinOp op) {...@@ -9904,6 +10020,8 @@ static bool ok_float_op(IrBinOp op) {
9904 case IrBinOpRemRem:10020 case IrBinOpRemRem:
9905 case IrBinOpRemMod:10021 case IrBinOpRemMod:
9906 case IrBinOpRemUnspecified:10022 case IrBinOpRemUnspecified:
10023 case IrBinOpMaximum:
10024 case IrBinOpMinimum:
9907 return true;10025 return true;
990810026
9909 case IrBinOpBoolOr:10027 case IrBinOpBoolOr:
...@@ -10894,6 +11012,8 @@ static Stage1AirInst *ir_analyze_instruction_bin_op(IrAnalyze *ira, Stage1ZirIns...@@ -10894,6 +11012,8 @@ static Stage1AirInst *ir_analyze_instruction_bin_op(IrAnalyze *ira, Stage1ZirIns
10894 case IrBinOpRemUnspecified:11012 case IrBinOpRemUnspecified:
10895 case IrBinOpRemRem:11013 case IrBinOpRemRem:
10896 case IrBinOpRemMod:11014 case IrBinOpRemMod:
11015 case IrBinOpMaximum:
11016 case IrBinOpMinimum:
10897 return ir_analyze_bin_op_math(ira, bin_op_instruction);11017 return ir_analyze_bin_op_math(ira, bin_op_instruction);
10898 case IrBinOpArrayCat:11018 case IrBinOpArrayCat:
10899 return ir_analyze_array_cat(ira, bin_op_instruction);11019 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) {...@@ -733,6 +733,10 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) {
733 return "++";733 return "++";
734 case IrBinOpArrayMult:734 case IrBinOpArrayMult:
735 return "**";735 return "**";
736 case IrBinOpMaximum:
737 return "@maximum";
738 case IrBinOpMinimum:
739 return "@minimum";
736 }740 }
737 zig_unreachable();741 zig_unreachable();
738}742}
src/zig_llvm.cpp+30
...@@ -458,6 +458,36 @@ LLVMValueRef ZigLLVMBuildMemSet(LLVMBuilderRef B, LLVMValueRef Ptr, LLVMValueRef...@@ -458,6 +458,36 @@ LLVMValueRef ZigLLVMBuildMemSet(LLVMBuilderRef B, LLVMValueRef Ptr, LLVMValueRef
458 return wrap(call_inst);458 return wrap(call_inst);
459}459}
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
461void ZigLLVMFnSetSubprogram(LLVMValueRef fn, ZigLLVMDISubprogram *subprogram) {491void ZigLLVMFnSetSubprogram(LLVMValueRef fn, ZigLLVMDISubprogram *subprogram) {
462 assert( isa<Function>(unwrap(fn)) );492 assert( isa<Function>(unwrap(fn)) );
463 Function *unwrapped_function = reinterpret_cast<Function*>(unwrap(fn));493 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,...@@ -129,6 +129,14 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildMemCpy(LLVMBuilderRef B, LLVMValueRef Dst,
129ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildMemSet(LLVMBuilderRef B, LLVMValueRef Ptr, LLVMValueRef Val, LLVMValueRef Size,129ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildMemSet(LLVMBuilderRef B, LLVMValueRef Ptr, LLVMValueRef Val, LLVMValueRef Size,
130 unsigned Align, bool isVolatile);130 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
132ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,140ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,
133 LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,141 LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,
134 LLVMAtomicOrdering failure_ordering, bool is_weak);142 LLVMAtomicOrdering failure_ordering, bool is_weak);
...@@ -142,6 +150,7 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildLShrExact(LLVMBuilderRef builder, LLVMValu...@@ -142,6 +150,7 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildLShrExact(LLVMBuilderRef builder, LLVMValu
142ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildAShrExact(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,150ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildAShrExact(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,
143 const char *name);151 const char *name);
144152
153
145ZIG_EXTERN_C struct ZigLLVMDIType *ZigLLVMCreateDebugPointerType(struct ZigLLVMDIBuilder *dibuilder,154ZIG_EXTERN_C struct ZigLLVMDIType *ZigLLVMCreateDebugPointerType(struct ZigLLVMDIBuilder *dibuilder,
146 struct ZigLLVMDIType *pointee_type, uint64_t size_in_bits, uint64_t align_in_bits, const char *name);155 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 {...@@ -105,6 +105,7 @@ test {
105 _ = @import("behavior/inttoptr.zig");105 _ = @import("behavior/inttoptr.zig");
106 _ = @import("behavior/ir_block_deps.zig");106 _ = @import("behavior/ir_block_deps.zig");
107 _ = @import("behavior/math.zig");107 _ = @import("behavior/math.zig");
108 _ = @import("behavior/maximum_minimum.zig");
108 _ = @import("behavior/merge_error_sets.zig");109 _ = @import("behavior/merge_error_sets.zig");
109 _ = @import("behavior/misc.zig");110 _ = @import("behavior/misc.zig");
110 _ = @import("behavior/muladd.zig");111 _ = @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}