| author | |
| committer | |
| log | cdeea3b0943b070d49d8d8d0855f9a38843e3ecc |
| tree | 541557cd87bddd0fb76cadd34e2cb9d20495a4bf |
| parent | 50a29f7c213d4a906839dfd625b6280663348781 |
18 files changed, 416 insertions(+), 0 deletions(-)
doc/langref.html.in+22| ... | ... | @@ -7988,6 +7988,17 @@ test "@hasDecl" { |
| 7988 | 7988 | </p> |
| 7989 | 7989 | {#header_close#} |
| 7990 | 7990 | |
| 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 | 8002 | {#header_open|@memcpy#} |
| 7992 | 8003 | <pre>{#syntax#}@memcpy(noalias dest: [*]u8, noalias source: [*]const u8, byte_count: usize){#endsyntax#}</pre> |
| 7993 | 8004 | <p> |
| ... | ... | @@ -8025,6 +8036,17 @@ mem.copy(u8, dest[0..byte_count], source[0..byte_count]);{#endsyntax#}</pre> |
| 8025 | 8036 | mem.set(u8, dest, c);{#endsyntax#}</pre> |
| 8026 | 8037 | {#header_close#} |
| 8027 | 8038 | |
| 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 | 8050 | {#header_open|@wasmMemorySize#} |
| 8029 | 8051 | <pre>{#syntax#}@wasmMemorySize(index: u32) u32{#endsyntax#}</pre> |
| 8030 | 8052 | <p> |
src/AstGen.zig+21| ... | ... | @@ -2098,8 +2098,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner |
| 2098 | 2098 | .builtin_call, |
| 2099 | 2099 | .field_ptr_type, |
| 2100 | 2100 | .field_parent_ptr, |
| 2101 | .maximum, | |
| 2101 | 2102 | .memcpy, |
| 2102 | 2103 | .memset, |
| 2104 | .minimum, | |
| 2103 | 2105 | .builtin_async_call, |
| 2104 | 2106 | .c_import, |
| 2105 | 2107 | .@"resume", |
| ... | ... | @@ -7227,6 +7229,25 @@ fn builtinCall( |
| 7227 | 7229 | return rvalue(gz, rl, result, node); |
| 7228 | 7230 | }, |
| 7229 | 7231 | |
| 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 | 7251 | .add_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .add_with_overflow), |
| 7231 | 7252 | .sub_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .sub_with_overflow), |
| 7232 | 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 | 57 | int_to_error, |
| 58 | 58 | int_to_float, |
| 59 | 59 | int_to_ptr, |
| 60 | maximum, | |
| 60 | 61 | memcpy, |
| 61 | 62 | memset, |
| 63 | minimum, | |
| 62 | 64 | wasm_memory_size, |
| 63 | 65 | wasm_memory_grow, |
| 64 | 66 | mod, |
| ... | ... | @@ -518,6 +520,13 @@ pub const list = list: { |
| 518 | 520 | .param_count = 2, |
| 519 | 521 | }, |
| 520 | 522 | }, |
| 523 | .{ | |
| 524 | "@maximum", | |
| 525 | .{ | |
| 526 | .tag = .maximum, | |
| 527 | .param_count = 2, | |
| 528 | }, | |
| 529 | }, | |
| 521 | 530 | .{ |
| 522 | 531 | "@memcpy", |
| 523 | 532 | .{ |
| ... | ... | @@ -532,6 +541,13 @@ pub const list = list: { |
| 532 | 541 | .param_count = 3, |
| 533 | 542 | }, |
| 534 | 543 | }, |
| 544 | .{ | |
| 545 | "@minimum", | |
| 546 | .{ | |
| 547 | .tag = .minimum, | |
| 548 | .param_count = 2, | |
| 549 | }, | |
| 550 | }, | |
| 535 | 551 | .{ |
| 536 | 552 | "@wasmMemorySize", |
| 537 | 553 | .{ |
src/Sema.zig+14| ... | ... | @@ -346,8 +346,10 @@ pub fn analyzeBody( |
| 346 | 346 | .builtin_call => try sema.zirBuiltinCall(block, inst), |
| 347 | 347 | .field_ptr_type => try sema.zirFieldPtrType(block, inst), |
| 348 | 348 | .field_parent_ptr => try sema.zirFieldParentPtr(block, inst), |
| 349 | .maximum => try sema.zirMaximum(block, inst), | |
| 349 | 350 | .memcpy => try sema.zirMemcpy(block, inst), |
| 350 | 351 | .memset => try sema.zirMemset(block, inst), |
| 352 | .minimum => try sema.zirMinimum(block, inst), | |
| 351 | 353 | .builtin_async_call => try sema.zirBuiltinAsyncCall(block, inst), |
| 352 | 354 | .@"resume" => try sema.zirResume(block, inst), |
| 353 | 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 | 6150 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirFieldParentPtr", .{}); |
| 6149 | 6151 | } |
| 6150 | 6152 | |
| 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 | ||
| 6151 | 6159 | fn zirMemcpy(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 6152 | 6160 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 6153 | 6161 | const src = inst_data.src(); |
| ... | ... | @@ -6160,6 +6168,12 @@ fn zirMemset(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErro |
| 6160 | 6168 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemset", .{}); |
| 6161 | 6169 | } |
| 6162 | 6170 | |
| 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 | ||
| 6163 | 6177 | fn zirBuiltinAsyncCall(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 6164 | 6178 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 6165 | 6179 | const src = inst_data.src(); |
src/Zir.zig+12| ... | ... | @@ -915,12 +915,18 @@ pub const Inst = struct { |
| 915 | 915 | /// Implements the `@fieldParentPtr` builtin. |
| 916 | 916 | /// Uses the `pl_node` union field with payload `FieldParentPtr`. |
| 917 | 917 | field_parent_ptr, |
| 918 | /// Implements the `@maximum` builtin. | |
| 919 | /// Uses the `pl_node` union field with payload `Bin` | |
| 920 | maximum, | |
| 918 | 921 | /// Implements the `@memcpy` builtin. |
| 919 | 922 | /// Uses the `pl_node` union field with payload `Memcpy`. |
| 920 | 923 | memcpy, |
| 921 | 924 | /// Implements the `@memset` builtin. |
| 922 | 925 | /// Uses the `pl_node` union field with payload `Memset`. |
| 923 | 926 | memset, |
| 927 | /// Implements the `@minimum` builtin. | |
| 928 | /// Uses the `pl_node` union field with payload `Bin` | |
| 929 | minimum, | |
| 924 | 930 | /// Implements the `@asyncCall` builtin. |
| 925 | 931 | /// Uses the `pl_node` union field with payload `AsyncCall`. |
| 926 | 932 | builtin_async_call, |
| ... | ... | @@ -1192,8 +1198,10 @@ pub const Inst = struct { |
| 1192 | 1198 | .builtin_call, |
| 1193 | 1199 | .field_ptr_type, |
| 1194 | 1200 | .field_parent_ptr, |
| 1201 | .maximum, | |
| 1195 | 1202 | .memcpy, |
| 1196 | 1203 | .memset, |
| 1204 | .minimum, | |
| 1197 | 1205 | .builtin_async_call, |
| 1198 | 1206 | .c_import, |
| 1199 | 1207 | .@"resume", |
| ... | ... | @@ -1463,8 +1471,10 @@ pub const Inst = struct { |
| 1463 | 1471 | .builtin_call = .pl_node, |
| 1464 | 1472 | .field_ptr_type = .bin, |
| 1465 | 1473 | .field_parent_ptr = .pl_node, |
| 1474 | .maximum = .pl_node, | |
| 1466 | 1475 | .memcpy = .pl_node, |
| 1467 | 1476 | .memset = .pl_node, |
| 1477 | .minimum = .pl_node, | |
| 1468 | 1478 | .builtin_async_call = .pl_node, |
| 1469 | 1479 | .c_import = .pl_node, |
| 1470 | 1480 | |
| ... | ... | @@ -3020,6 +3030,8 @@ const Writer = struct { |
| 3020 | 3030 | .bitcast, |
| 3021 | 3031 | .bitcast_result_ptr, |
| 3022 | 3032 | .vector_type, |
| 3033 | .maximum, | |
| 3034 | .minimum, | |
| 3023 | 3035 | => try self.writePlNodeBin(stream, inst), |
| 3024 | 3036 | |
| 3025 | 3037 | .@"export" => try self.writePlNodeExport(stream, inst), |
src/stage1/all_types.hpp+4| ... | ... | @@ -1796,6 +1796,8 @@ enum BuiltinFnId { |
| 1796 | 1796 | BuiltinFnIdWasmMemoryGrow, |
| 1797 | 1797 | BuiltinFnIdSrc, |
| 1798 | 1798 | BuiltinFnIdReduce, |
| 1799 | BuiltinFnIdMaximum, | |
| 1800 | BuiltinFnIdMinimum, | |
| 1799 | 1801 | }; |
| 1800 | 1802 | |
| 1801 | 1803 | struct BuiltinFnEntry { |
| ... | ... | @@ -2938,6 +2940,8 @@ enum IrBinOp { |
| 2938 | 2940 | IrBinOpRemMod, |
| 2939 | 2941 | IrBinOpArrayCat, |
| 2940 | 2942 | IrBinOpArrayMult, |
| 2943 | IrBinOpMaximum, | |
| 2944 | IrBinOpMinimum, | |
| 2941 | 2945 | }; |
| 2942 | 2946 | |
| 2943 | 2947 | struct Stage1ZirInstBinOp { |
src/stage1/astgen.cpp+30| ... | ... | @@ -4686,6 +4686,21 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast |
| 4686 | 4686 | arg0_value, arg1_value); |
| 4687 | 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 | 4704 | case BuiltinFnIdMemcpy: |
| 4690 | 4705 | { |
| 4691 | 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 | 4741 | Stage1ZirInst *ir_memset = ir_build_memset_src(ag, scope, node, arg0_value, arg1_value, arg2_value); |
| 4727 | 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 | 4759 | case BuiltinFnIdWasmMemorySize: |
| 4730 | 4760 | { |
| 4731 | 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 | 191 | f128M_sqrt(&op->value, &dest->value); |
| 192 | 192 | } |
| 193 | 193 | |
| 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 | ||
| 194 | 218 | bool bigfloat_is_nan(const BigFloat *op) { |
| 195 | 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 | 45 | void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2); |
| 46 | 46 | void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2); |
| 47 | 47 | 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); | |
| 48 | 50 | void bigfloat_append_buf(Buf *buf, const BigFloat *op); |
| 49 | 51 | Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2); |
| 50 | 52 | |
| 53 | ||
| 51 | 54 | bool bigfloat_is_nan(const BigFloat *op); |
| 52 | 55 | |
| 53 | 56 | // 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 | 448 | } |
| 449 | 449 | #endif |
| 450 | 450 | |
| 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 | ||
| 451 | 471 | void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2) { |
| 452 | 472 | if (op1->digit_count == 0) { |
| 453 | 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 | 56 | void bigint_write_twos_complement(const BigInt *big_int, uint8_t *buf, size_t bit_count, bool is_big_endian); |
| 57 | 57 | void bigint_read_twos_complement(BigInt *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian, |
| 58 | 58 | 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); | |
| 59 | 61 | void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2); |
| 60 | 62 | void bigint_add_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); |
| 61 | 63 | void 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 | 3248 | case IrBinOpRemMod: |
| 3249 | 3249 | return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), |
| 3250 | 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 | 3276 | zig_unreachable(); |
| 3253 | 3277 | } |
| ... | ... | @@ -8990,6 +9014,8 @@ static void define_builtin_fns(CodeGen *g) { |
| 8990 | 9014 | create_builtin_fn(g, BuiltinFnIdWasmMemoryGrow, "wasmMemoryGrow", 2); |
| 8991 | 9015 | create_builtin_fn(g, BuiltinFnIdSrc, "src", 0); |
| 8992 | 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 | } |
| 8994 | 9020 | |
| 8995 | 9021 | static 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 | 3311 | } |
| 3312 | 3312 | } |
| 3313 | 3313 | |
| 3314 | static 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 | ||
| 3365 | static 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 | ||
| 3314 | 3416 | static void float_negate(ZigValue *out_val, ZigValue *op) { |
| 3315 | 3417 | out_val->type = op->type; |
| 3316 | 3418 | if (op->type->id == ZigTypeIdComptimeFloat) { |
| ... | ... | @@ -9704,6 +9806,20 @@ static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, Scope *scope, AstNode *s |
| 9704 | 9806 | float_mod(out_val, op1_val, op2_val); |
| 9705 | 9807 | } |
| 9706 | 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 | } |
| 9708 | 9824 | |
| 9709 | 9825 | if (type_entry->id == ZigTypeIdInt) { |
| ... | ... | @@ -9904,6 +10020,8 @@ static bool ok_float_op(IrBinOp op) { |
| 9904 | 10020 | case IrBinOpRemRem: |
| 9905 | 10021 | case IrBinOpRemMod: |
| 9906 | 10022 | case IrBinOpRemUnspecified: |
| 10023 | case IrBinOpMaximum: | |
| 10024 | case IrBinOpMinimum: | |
| 9907 | 10025 | return true; |
| 9908 | 10026 | |
| 9909 | 10027 | case IrBinOpBoolOr: |
| ... | ... | @@ -10894,6 +11012,8 @@ static Stage1AirInst *ir_analyze_instruction_bin_op(IrAnalyze *ira, Stage1ZirIns |
| 10894 | 11012 | case IrBinOpRemUnspecified: |
| 10895 | 11013 | case IrBinOpRemRem: |
| 10896 | 11014 | case IrBinOpRemMod: |
| 11015 | case IrBinOpMaximum: | |
| 11016 | case IrBinOpMinimum: | |
| 10897 | 11017 | return ir_analyze_bin_op_math(ira, bin_op_instruction); |
| 10898 | 11018 | case IrBinOpArrayCat: |
| 10899 | 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 | 733 | return "++"; |
| 734 | 734 | case IrBinOpArrayMult: |
| 735 | 735 | return "**"; |
| 736 | case IrBinOpMaximum: | |
| 737 | return "@maximum"; | |
| 738 | case IrBinOpMinimum: | |
| 739 | return "@minimum"; | |
| 736 | 740 | } |
| 737 | 741 | zig_unreachable(); |
| 738 | 742 | } |
src/zig_llvm.cpp+30| ... | ... | @@ -458,6 +458,36 @@ LLVMValueRef ZigLLVMBuildMemSet(LLVMBuilderRef B, LLVMValueRef Ptr, LLVMValueRef |
| 458 | 458 | return wrap(call_inst); |
| 459 | 459 | } |
| 460 | 460 | |
| 461 | LLVMValueRef 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 | ||
| 466 | LLVMValueRef 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 | ||
| 471 | LLVMValueRef 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 | ||
| 476 | LLVMValueRef 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 | ||
| 481 | LLVMValueRef 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 | ||
| 486 | LLVMValueRef 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 | ||
| 461 | 491 | void ZigLLVMFnSetSubprogram(LLVMValueRef fn, ZigLLVMDISubprogram *subprogram) { |
| 462 | 492 | assert( isa<Function>(unwrap(fn)) ); |
| 463 | 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 | 129 | ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildMemSet(LLVMBuilderRef B, LLVMValueRef Ptr, LLVMValueRef Val, LLVMValueRef Size, |
| 130 | 130 | unsigned Align, bool isVolatile); |
| 131 | 131 | |
| 132 | ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildMaxNum(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char* name); | |
| 133 | ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildMinNum(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char* name); | |
| 134 | ||
| 135 | ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildUMax(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char* name); | |
| 136 | ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildUMin(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char* name); | |
| 137 | ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildSMax(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char* name); | |
| 138 | ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildSMin(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char* name); | |
| 139 | ||
| 132 | 140 | ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp, |
| 133 | 141 | LLVMValueRef new_val, LLVMAtomicOrdering success_ordering, |
| 134 | 142 | LLVMAtomicOrdering failure_ordering, bool is_weak); |
| ... | ... | @@ -142,6 +150,7 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildLShrExact(LLVMBuilderRef builder, LLVMValu |
| 142 | 150 | ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildAShrExact(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, |
| 143 | 151 | const char *name); |
| 144 | 152 | |
| 153 | ||
| 145 | 154 | ZIG_EXTERN_C struct ZigLLVMDIType *ZigLLVMCreateDebugPointerType(struct ZigLLVMDIBuilder *dibuilder, |
| 146 | 155 | struct ZigLLVMDIType *pointee_type, uint64_t size_in_bits, uint64_t align_in_bits, const char *name); |
| 147 | 156 |
test/behavior.zig+1| ... | ... | @@ -105,6 +105,7 @@ test { |
| 105 | 105 | _ = @import("behavior/inttoptr.zig"); |
| 106 | 106 | _ = @import("behavior/ir_block_deps.zig"); |
| 107 | 107 | _ = @import("behavior/math.zig"); |
| 108 | _ = @import("behavior/maximum_minimum.zig"); | |
| 108 | 109 | _ = @import("behavior/merge_error_sets.zig"); |
| 109 | 110 | _ = @import("behavior/misc.zig"); |
| 110 | 111 | _ = @import("behavior/muladd.zig"); |
test/behavior/maximum_minimum.zig created+58| ... | ... | @@ -0,0 +1,58 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const mem = std.mem; | |
| 4 | const expect = std.testing.expect; | |
| 5 | const expectEqual = std.testing.expectEqual; | |
| 6 | const Vector = std.meta.Vector; | |
| 7 | ||
| 8 | test "@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 | ||
| 34 | test "@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 | } |