| author | |
| committer | |
| log | 79bc5891c1c4cde0592fe1b10b6c9a85914155cf |
| tree | 92999a5e04df045c03d63e4d02e53ad796c70911 |
| parent | 1e805df81d51a338eefaff0535e5f1cca9e22028 |
* AIR: add `mod` instruction for modulus division
- Implement for LLVM backend
* Sema: implement `@mod`, `@rem`, and `%`.
* Sema: fix comptime switch evaluation
* Sema: implement comptime shift left
* Sema: fix the logic inside analyzeArithmetic to handle all the
nuances between the different mathematical operations.
- Implement comptime wrapping operations12 files changed, 1604 insertions(+), 974 deletions(-)
src/Air.zig+9-2| ... | @@ -69,10 +69,16 @@ pub const Inst = struct { | ... | @@ -69,10 +69,16 @@ pub const Inst = struct { |
| 69 | /// is the same as both operands. | 69 | /// is the same as both operands. |
| 70 | /// Uses the `bin_op` field. | 70 | /// Uses the `bin_op` field. |
| 71 | div, | 71 | div, |
| 72 | /// Integer or float remainder. | 72 | /// Integer or float remainder division. |
| 73 | /// Both operands are guaranteed to be the same type, and the result type is the same as both operands. | 73 | /// Both operands are guaranteed to be the same type, and the result type |
| 74 | /// is the same as both operands. | ||
| 74 | /// Uses the `bin_op` field. | 75 | /// Uses the `bin_op` field. |
| 75 | rem, | 76 | rem, |
| 77 | /// Integer or float modulus division. | ||
| 78 | /// Both operands are guaranteed to be the same type, and the result type | ||
| 79 | /// is the same as both operands. | ||
| 80 | /// Uses the `bin_op` field. | ||
| 81 | mod, | ||
| 76 | /// Add an offset to a pointer, returning a new pointer. | 82 | /// Add an offset to a pointer, returning a new pointer. |
| 77 | /// The offset is in element type units, not bytes. | 83 | /// The offset is in element type units, not bytes. |
| 78 | /// Wrapping is undefined behavior. | 84 | /// Wrapping is undefined behavior. |
| ... | @@ -568,6 +574,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { | ... | @@ -568,6 +574,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 568 | .mulwrap, | 574 | .mulwrap, |
| 569 | .div, | 575 | .div, |
| 570 | .rem, | 576 | .rem, |
| 577 | .mod, | ||
| 571 | .bit_and, | 578 | .bit_and, |
| 572 | .bit_or, | 579 | .bit_or, |
| 573 | .xor, | 580 | .xor, |
src/Liveness.zig+1| ... | @@ -232,6 +232,7 @@ fn analyzeInst( | ... | @@ -232,6 +232,7 @@ fn analyzeInst( |
| 232 | .mulwrap, | 232 | .mulwrap, |
| 233 | .div, | 233 | .div, |
| 234 | .rem, | 234 | .rem, |
| 235 | .mod, | ||
| 235 | .ptr_add, | 236 | .ptr_add, |
| 236 | .ptr_sub, | 237 | .ptr_sub, |
| 237 | .bit_and, | 238 | .bit_and, |
src/Sema.zig+547-112| ... | @@ -319,8 +319,6 @@ pub fn analyzeBody( | ... | @@ -319,8 +319,6 @@ pub fn analyzeBody( |
| 319 | .div_exact => try sema.zirDivExact(block, inst), | 319 | .div_exact => try sema.zirDivExact(block, inst), |
| 320 | .div_floor => try sema.zirDivFloor(block, inst), | 320 | .div_floor => try sema.zirDivFloor(block, inst), |
| 321 | .div_trunc => try sema.zirDivTrunc(block, inst), | 321 | .div_trunc => try sema.zirDivTrunc(block, inst), |
| 322 | .mod => try sema.zirMod(block, inst), | ||
| 323 | .rem => try sema.zirRem(block, inst), | ||
| 324 | .shl_exact => try sema.zirShlExact(block, inst), | 322 | .shl_exact => try sema.zirShlExact(block, inst), |
| 325 | .shr_exact => try sema.zirShrExact(block, inst), | 323 | .shr_exact => try sema.zirShrExact(block, inst), |
| 326 | .bit_offset_of => try sema.zirBitOffsetOf(block, inst), | 324 | .bit_offset_of => try sema.zirBitOffsetOf(block, inst), |
| ... | @@ -363,14 +361,16 @@ pub fn analyzeBody( | ... | @@ -363,14 +361,16 @@ pub fn analyzeBody( |
| 363 | .error_set_decl_anon => try sema.zirErrorSetDecl(block, inst, .anon), | 361 | .error_set_decl_anon => try sema.zirErrorSetDecl(block, inst, .anon), |
| 364 | .error_set_decl_func => try sema.zirErrorSetDecl(block, inst, .func), | 362 | .error_set_decl_func => try sema.zirErrorSetDecl(block, inst, .func), |
| 365 | 363 | ||
| 366 | .add => try sema.zirArithmetic(block, inst), | 364 | .add => try sema.zirArithmetic(block, inst, .add), |
| 367 | .addwrap => try sema.zirArithmetic(block, inst), | 365 | .addwrap => try sema.zirArithmetic(block, inst, .addwrap), |
| 368 | .div => try sema.zirArithmetic(block, inst), | 366 | .div => try sema.zirArithmetic(block, inst, .div), |
| 369 | .mod_rem => try sema.zirArithmetic(block, inst), | 367 | .mod_rem => try sema.zirArithmetic(block, inst, .mod_rem), |
| 370 | .mul => try sema.zirArithmetic(block, inst), | 368 | .mod => try sema.zirArithmetic(block, inst, .mod), |
| 371 | .mulwrap => try sema.zirArithmetic(block, inst), | 369 | .rem => try sema.zirArithmetic(block, inst, .rem), |
| 372 | .sub => try sema.zirArithmetic(block, inst), | 370 | .mul => try sema.zirArithmetic(block, inst, .mul), |
| 373 | .subwrap => try sema.zirArithmetic(block, inst), | 371 | .mulwrap => try sema.zirArithmetic(block, inst, .mulwrap), |
| 372 | .sub => try sema.zirArithmetic(block, inst, .sub), | ||
| 373 | .subwrap => try sema.zirArithmetic(block, inst, .subwrap), | ||
| 374 | 374 | ||
| 375 | // Instructions that we know to *always* be noreturn based solely on their tag. | 375 | // Instructions that we know to *always* be noreturn based solely on their tag. |
| 376 | // These functions match the return type of analyzeBody so that we can | 376 | // These functions match the return type of analyzeBody so that we can |
| ... | @@ -886,6 +886,14 @@ fn failWithUseOfUndef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) Compile | ... | @@ -886,6 +886,14 @@ fn failWithUseOfUndef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) Compile |
| 886 | return sema.mod.fail(&block.base, src, "use of undefined value here causes undefined behavior", .{}); | 886 | return sema.mod.fail(&block.base, src, "use of undefined value here causes undefined behavior", .{}); |
| 887 | } | 887 | } |
| 888 | 888 | ||
| 889 | fn failWithDivideByZero(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) CompileError { | ||
| 890 | return sema.mod.fail(&block.base, src, "division by zero here causes undefined behavior", .{}); | ||
| 891 | } | ||
| 892 | |||
| 893 | fn failWithModRemNegative(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, lhs_ty: Type, rhs_ty: Type) CompileError { | ||
| 894 | return sema.mod.fail(&block.base, src, "remainder division with '{}' and '{}': signed integers and floats must use @rem or @mod", .{ lhs_ty, rhs_ty }); | ||
| 895 | } | ||
| 896 | |||
| 889 | /// Appropriate to call when the coercion has already been done by result | 897 | /// Appropriate to call when the coercion has already been done by result |
| 890 | /// location semantics. Asserts the value fits in the provided `Int` type. | 898 | /// location semantics. Asserts the value fits in the provided `Int` type. |
| 891 | /// Only supports `Int` types 64 bits or less. | 899 | /// Only supports `Int` types 64 bits or less. |
| ... | @@ -2366,8 +2374,12 @@ fn resolveBlockBody( | ... | @@ -2366,8 +2374,12 @@ fn resolveBlockBody( |
| 2366 | body: []const Zir.Inst.Index, | 2374 | body: []const Zir.Inst.Index, |
| 2367 | merges: *Scope.Block.Merges, | 2375 | merges: *Scope.Block.Merges, |
| 2368 | ) CompileError!Air.Inst.Ref { | 2376 | ) CompileError!Air.Inst.Ref { |
| 2369 | _ = try sema.analyzeBody(child_block, body); | 2377 | if (child_block.is_comptime) { |
| 2370 | return sema.analyzeBlockBody(parent_block, src, child_block, merges); | 2378 | return sema.resolveBody(child_block, body); |
| 2379 | } else { | ||
| 2380 | _ = try sema.analyzeBody(child_block, body); | ||
| 2381 | return sema.analyzeBlockBody(parent_block, src, child_block, merges); | ||
| 2382 | } | ||
| 2371 | } | 2383 | } |
| 2372 | 2384 | ||
| 2373 | fn analyzeBlockBody( | 2385 | fn analyzeBlockBody( |
| ... | @@ -5867,23 +5879,36 @@ fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A | ... | @@ -5867,23 +5879,36 @@ fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A |
| 5867 | defer tracy.end(); | 5879 | defer tracy.end(); |
| 5868 | 5880 | ||
| 5869 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | 5881 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 5870 | const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node }; | ||
| 5871 | const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; | 5882 | const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; |
| 5872 | const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; | 5883 | const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; |
| 5873 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; | 5884 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 5874 | const lhs = sema.resolveInst(extra.lhs); | 5885 | const lhs = sema.resolveInst(extra.lhs); |
| 5875 | const rhs = sema.resolveInst(extra.rhs); | 5886 | const rhs = sema.resolveInst(extra.rhs); |
| 5876 | 5887 | ||
| 5877 | if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { | 5888 | const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs); |
| 5878 | if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| { | 5889 | const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs); |
| 5879 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | 5890 | |
| 5880 | return sema.addConstUndef(sema.typeOf(lhs)); | 5891 | const runtime_src = if (maybe_lhs_val) |lhs_val| rs: { |
| 5881 | } | 5892 | const lhs_ty = sema.typeOf(lhs); |
| 5882 | return sema.mod.fail(&block.base, src, "TODO implement comptime shl", .{}); | 5893 | |
| 5894 | if (lhs_val.isUndef()) return sema.addConstUndef(lhs_ty); | ||
| 5895 | const rhs_val = maybe_rhs_val orelse break :rs rhs_src; | ||
| 5896 | if (rhs_val.isUndef()) return sema.addConstUndef(lhs_ty); | ||
| 5897 | |||
| 5898 | // If rhs is 0, return lhs without doing any calculations. | ||
| 5899 | if (rhs_val.compareWithZero(.eq)) { | ||
| 5900 | return sema.addConstant(lhs_ty, lhs_val); | ||
| 5883 | } | 5901 | } |
| 5884 | } | 5902 | const val = try lhs_val.shl(rhs_val, sema.arena); |
| 5903 | return sema.addConstant(lhs_ty, val); | ||
| 5904 | } else rs: { | ||
| 5905 | if (maybe_rhs_val) |rhs_val| { | ||
| 5906 | if (rhs_val.isUndef()) return sema.addConstUndef(sema.typeOf(lhs)); | ||
| 5907 | } | ||
| 5908 | break :rs lhs_src; | ||
| 5909 | }; | ||
| 5885 | 5910 | ||
| 5886 | try sema.requireRuntimeBlock(block, src); | 5911 | try sema.requireRuntimeBlock(block, runtime_src); |
| 5887 | return block.addBinOp(.shl, lhs, rhs); | 5912 | return block.addBinOp(.shl, lhs, rhs); |
| 5888 | } | 5913 | } |
| 5889 | 5914 | ||
| ... | @@ -6141,11 +6166,15 @@ fn zirNegate( | ... | @@ -6141,11 +6166,15 @@ fn zirNegate( |
| 6141 | return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src); | 6166 | return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src); |
| 6142 | } | 6167 | } |
| 6143 | 6168 | ||
| 6144 | fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 6169 | fn zirArithmetic( |
| 6170 | sema: *Sema, | ||
| 6171 | block: *Scope.Block, | ||
| 6172 | inst: Zir.Inst.Index, | ||
| 6173 | zir_tag: Zir.Inst.Tag, | ||
| 6174 | ) CompileError!Air.Inst.Ref { | ||
| 6145 | const tracy = trace(@src()); | 6175 | const tracy = trace(@src()); |
| 6146 | defer tracy.end(); | 6176 | defer tracy.end(); |
| 6147 | 6177 | ||
| 6148 | const tag_override = block.sema.code.instructions.items(.tag)[inst]; | ||
| 6149 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | 6178 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 6150 | sema.src = .{ .node_offset_bin_op = inst_data.src_node }; | 6179 | sema.src = .{ .node_offset_bin_op = inst_data.src_node }; |
| 6151 | const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; | 6180 | const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; |
| ... | @@ -6154,7 +6183,7 @@ fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Compile | ... | @@ -6154,7 +6183,7 @@ fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Compile |
| 6154 | const lhs = sema.resolveInst(extra.lhs); | 6183 | const lhs = sema.resolveInst(extra.lhs); |
| 6155 | const rhs = sema.resolveInst(extra.rhs); | 6184 | const rhs = sema.resolveInst(extra.rhs); |
| 6156 | 6185 | ||
| 6157 | return sema.analyzeArithmetic(block, tag_override, lhs, rhs, sema.src, lhs_src, rhs_src); | 6186 | return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src); |
| 6158 | } | 6187 | } |
| 6159 | 6188 | ||
| 6160 | fn zirOverflowArithmetic( | 6189 | fn zirOverflowArithmetic( |
| ... | @@ -6187,6 +6216,7 @@ fn zirSatArithmetic( | ... | @@ -6187,6 +6216,7 @@ fn zirSatArithmetic( |
| 6187 | fn analyzeArithmetic( | 6216 | fn analyzeArithmetic( |
| 6188 | sema: *Sema, | 6217 | sema: *Sema, |
| 6189 | block: *Scope.Block, | 6218 | block: *Scope.Block, |
| 6219 | /// TODO performance investigation: make this comptime? | ||
| 6190 | zir_tag: Zir.Inst.Tag, | 6220 | zir_tag: Zir.Inst.Tag, |
| 6191 | lhs: Air.Inst.Ref, | 6221 | lhs: Air.Inst.Ref, |
| 6192 | rhs: Air.Inst.Ref, | 6222 | rhs: Air.Inst.Ref, |
| ... | @@ -6204,7 +6234,7 @@ fn analyzeArithmetic( | ... | @@ -6204,7 +6234,7 @@ fn analyzeArithmetic( |
| 6204 | lhs_ty.arrayLen(), rhs_ty.arrayLen(), | 6234 | lhs_ty.arrayLen(), rhs_ty.arrayLen(), |
| 6205 | }); | 6235 | }); |
| 6206 | } | 6236 | } |
| 6207 | return sema.mod.fail(&block.base, src, "TODO implement support for vectors in zirBinOp", .{}); | 6237 | return sema.mod.fail(&block.base, src, "TODO implement support for vectors in Sema.analyzeArithmetic", .{}); |
| 6208 | } else if (lhs_zig_ty_tag == .Vector or rhs_zig_ty_tag == .Vector) { | 6238 | } else if (lhs_zig_ty_tag == .Vector or rhs_zig_ty_tag == .Vector) { |
| 6209 | return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{ | 6239 | return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{ |
| 6210 | lhs_ty, rhs_ty, | 6240 | lhs_ty, rhs_ty, |
| ... | @@ -6247,7 +6277,9 @@ fn analyzeArithmetic( | ... | @@ -6247,7 +6277,9 @@ fn analyzeArithmetic( |
| 6247 | }; | 6277 | }; |
| 6248 | 6278 | ||
| 6249 | const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; | 6279 | const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; |
| 6250 | const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]LazySrcLoc{ lhs_src, rhs_src } }); | 6280 | const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ |
| 6281 | .override = &[_]LazySrcLoc{ lhs_src, rhs_src }, | ||
| 6282 | }); | ||
| 6251 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); | 6283 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); |
| 6252 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); | 6284 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); |
| 6253 | 6285 | ||
| ... | @@ -6267,86 +6299,499 @@ fn analyzeArithmetic( | ... | @@ -6267,86 +6299,499 @@ fn analyzeArithmetic( |
| 6267 | }); | 6299 | }); |
| 6268 | } | 6300 | } |
| 6269 | 6301 | ||
| 6270 | if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| { | 6302 | const target = sema.mod.getTarget(); |
| 6271 | if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| { | 6303 | const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs); |
| 6272 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | 6304 | const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs); |
| 6273 | return sema.addConstUndef(resolved_type); | 6305 | const rs: struct { src: LazySrcLoc, air_tag: Air.Inst.Tag } = rs: { |
| 6274 | } | 6306 | switch (zir_tag) { |
| 6275 | // incase rhs is 0, simply return lhs without doing any calculations | 6307 | .add => { |
| 6276 | // TODO Once division is implemented we should throw an error when dividing by 0. | 6308 | // For integers: |
| 6277 | if (rhs_val.compareWithZero(.eq)) { | 6309 | // If either of the operands are zero, then the other operand is |
| 6278 | switch (zir_tag) { | 6310 | // returned, even if it is undefined. |
| 6279 | .add, .addwrap, .sub, .subwrap => { | 6311 | // If either of the operands are undefined, it's a compile error |
| 6280 | return sema.addConstant(scalar_type, lhs_val); | 6312 | // because there is a possible value for which the addition would |
| 6281 | }, | 6313 | // overflow (max_int), causing illegal behavior. |
| 6282 | else => {}, | 6314 | // For floats: either operand being undef makes the result undef. |
| 6315 | if (maybe_lhs_val) |lhs_val| { | ||
| 6316 | if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) { | ||
| 6317 | return casted_rhs; | ||
| 6318 | } | ||
| 6283 | } | 6319 | } |
| 6284 | } | 6320 | if (maybe_rhs_val) |rhs_val| { |
| 6285 | 6321 | if (rhs_val.isUndef()) { | |
| 6286 | const value = switch (zir_tag) { | 6322 | if (is_int) { |
| 6287 | .add => blk: { | 6323 | return sema.failWithUseOfUndef(block, rhs_src); |
| 6288 | const val = if (is_int) | 6324 | } else { |
| 6289 | try lhs_val.intAdd(rhs_val, sema.arena) | 6325 | return sema.addConstUndef(scalar_type); |
| 6290 | else | 6326 | } |
| 6291 | try lhs_val.floatAdd(rhs_val, scalar_type, sema.arena); | 6327 | } |
| 6292 | break :blk val; | 6328 | if (rhs_val.compareWithZero(.eq)) { |
| 6293 | }, | 6329 | return casted_lhs; |
| 6294 | .sub => blk: { | 6330 | } |
| 6295 | const val = if (is_int) | 6331 | } |
| 6296 | try lhs_val.intSub(rhs_val, sema.arena) | 6332 | if (maybe_lhs_val) |lhs_val| { |
| 6297 | else | 6333 | if (lhs_val.isUndef()) { |
| 6298 | try lhs_val.floatSub(rhs_val, scalar_type, sema.arena); | 6334 | if (is_int) { |
| 6299 | break :blk val; | 6335 | return sema.failWithUseOfUndef(block, lhs_src); |
| 6300 | }, | 6336 | } else { |
| 6301 | .div => blk: { | 6337 | return sema.addConstUndef(scalar_type); |
| 6302 | const val = if (is_int) | 6338 | } |
| 6303 | try lhs_val.intDiv(rhs_val, sema.arena) | 6339 | } |
| 6304 | else | 6340 | if (maybe_rhs_val) |rhs_val| { |
| 6305 | try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena); | 6341 | if (is_int) { |
| 6306 | break :blk val; | 6342 | return sema.addConstant( |
| 6307 | }, | 6343 | scalar_type, |
| 6308 | .mul => blk: { | 6344 | try lhs_val.intAdd(rhs_val, sema.arena), |
| 6309 | const val = if (is_int) | 6345 | ); |
| 6310 | try lhs_val.intMul(rhs_val, sema.arena) | 6346 | } else { |
| 6311 | else | 6347 | return sema.addConstant( |
| 6312 | try lhs_val.floatMul(rhs_val, scalar_type, sema.arena); | 6348 | scalar_type, |
| 6313 | break :blk val; | 6349 | try lhs_val.floatAdd(rhs_val, scalar_type, sema.arena), |
| 6314 | }, | 6350 | ); |
| 6315 | else => return sema.mod.fail(&block.base, src, "TODO implement comptime arithmetic for operand '{s}'", .{@tagName(zir_tag)}), | 6351 | } |
| 6316 | }; | 6352 | } else break :rs .{ .src = rhs_src, .air_tag = .add }; |
| 6317 | 6353 | } else break :rs .{ .src = lhs_src, .air_tag = .add }; | |
| 6318 | log.debug("{s}({}, {}) result: {}", .{ @tagName(zir_tag), lhs_val, rhs_val, value }); | 6354 | }, |
| 6319 | 6355 | .addwrap => { | |
| 6320 | return sema.addConstant(scalar_type, value); | 6356 | // Integers only; floats are checked above. |
| 6321 | } else { | 6357 | // If either of the operands are zero, then the other operand is |
| 6322 | try sema.requireRuntimeBlock(block, rhs_src); | 6358 | // returned, even if it is undefined. |
| 6323 | } | 6359 | // If either of the operands are undefined, the result is undefined. |
| 6324 | } else { | 6360 | if (maybe_lhs_val) |lhs_val| { |
| 6325 | try sema.requireRuntimeBlock(block, lhs_src); | 6361 | if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) { |
| 6326 | } | 6362 | return casted_rhs; |
| 6363 | } | ||
| 6364 | } | ||
| 6365 | if (maybe_rhs_val) |rhs_val| { | ||
| 6366 | if (rhs_val.isUndef()) { | ||
| 6367 | return sema.addConstUndef(scalar_type); | ||
| 6368 | } | ||
| 6369 | if (rhs_val.compareWithZero(.eq)) { | ||
| 6370 | return casted_lhs; | ||
| 6371 | } | ||
| 6372 | if (maybe_lhs_val) |lhs_val| { | ||
| 6373 | return sema.addConstant( | ||
| 6374 | scalar_type, | ||
| 6375 | try lhs_val.numberAddWrap(rhs_val, scalar_type, sema.arena, target), | ||
| 6376 | ); | ||
| 6377 | } else break :rs .{ .src = lhs_src, .air_tag = .addwrap }; | ||
| 6378 | } else break :rs .{ .src = rhs_src, .air_tag = .addwrap }; | ||
| 6379 | }, | ||
| 6380 | .sub => { | ||
| 6381 | // For integers: | ||
| 6382 | // If the rhs is zero, then the other operand is | ||
| 6383 | // returned, even if it is undefined. | ||
| 6384 | // If either of the operands are undefined, it's a compile error | ||
| 6385 | // because there is a possible value for which the subtraction would | ||
| 6386 | // overflow, causing illegal behavior. | ||
| 6387 | // For floats: either operand being undef makes the result undef. | ||
| 6388 | if (maybe_rhs_val) |rhs_val| { | ||
| 6389 | if (rhs_val.isUndef()) { | ||
| 6390 | if (is_int) { | ||
| 6391 | return sema.failWithUseOfUndef(block, rhs_src); | ||
| 6392 | } else { | ||
| 6393 | return sema.addConstUndef(scalar_type); | ||
| 6394 | } | ||
| 6395 | } | ||
| 6396 | if (rhs_val.compareWithZero(.eq)) { | ||
| 6397 | return casted_lhs; | ||
| 6398 | } | ||
| 6399 | } | ||
| 6400 | if (maybe_lhs_val) |lhs_val| { | ||
| 6401 | if (lhs_val.isUndef()) { | ||
| 6402 | if (is_int) { | ||
| 6403 | return sema.failWithUseOfUndef(block, lhs_src); | ||
| 6404 | } else { | ||
| 6405 | return sema.addConstUndef(scalar_type); | ||
| 6406 | } | ||
| 6407 | } | ||
| 6408 | if (maybe_rhs_val) |rhs_val| { | ||
| 6409 | if (is_int) { | ||
| 6410 | return sema.addConstant( | ||
| 6411 | scalar_type, | ||
| 6412 | try lhs_val.intSub(rhs_val, sema.arena), | ||
| 6413 | ); | ||
| 6414 | } else { | ||
| 6415 | return sema.addConstant( | ||
| 6416 | scalar_type, | ||
| 6417 | try lhs_val.floatSub(rhs_val, scalar_type, sema.arena), | ||
| 6418 | ); | ||
| 6419 | } | ||
| 6420 | } else break :rs .{ .src = rhs_src, .air_tag = .sub }; | ||
| 6421 | } else break :rs .{ .src = lhs_src, .air_tag = .sub }; | ||
| 6422 | }, | ||
| 6423 | .subwrap => { | ||
| 6424 | // Integers only; floats are checked above. | ||
| 6425 | // If the RHS is zero, then the other operand is returned, even if it is undefined. | ||
| 6426 | // If either of the operands are undefined, the result is undefined. | ||
| 6427 | if (maybe_rhs_val) |rhs_val| { | ||
| 6428 | if (rhs_val.isUndef()) { | ||
| 6429 | return sema.addConstUndef(scalar_type); | ||
| 6430 | } | ||
| 6431 | if (rhs_val.compareWithZero(.eq)) { | ||
| 6432 | return casted_lhs; | ||
| 6433 | } | ||
| 6434 | } | ||
| 6435 | if (maybe_lhs_val) |lhs_val| { | ||
| 6436 | if (lhs_val.isUndef()) { | ||
| 6437 | return sema.addConstUndef(scalar_type); | ||
| 6438 | } | ||
| 6439 | if (maybe_rhs_val) |rhs_val| { | ||
| 6440 | return sema.addConstant( | ||
| 6441 | scalar_type, | ||
| 6442 | try lhs_val.numberSubWrap(rhs_val, scalar_type, sema.arena, target), | ||
| 6443 | ); | ||
| 6444 | } else break :rs .{ .src = rhs_src, .air_tag = .subwrap }; | ||
| 6445 | } else break :rs .{ .src = lhs_src, .air_tag = .subwrap }; | ||
| 6446 | }, | ||
| 6447 | .div => { | ||
| 6448 | // For integers: | ||
| 6449 | // If the lhs is zero, then zero is returned regardless of rhs. | ||
| 6450 | // If the rhs is zero, compile error for division by zero. | ||
| 6451 | // If the rhs is undefined, compile error because there is a possible | ||
| 6452 | // value (zero) for which the division would be illegal behavior. | ||
| 6453 | // If the lhs is undefined: | ||
| 6454 | // * if lhs type is signed: | ||
| 6455 | // * if rhs is comptime-known and not -1, result is undefined | ||
| 6456 | // * if rhs is -1 or runtime-known, compile error because there is a | ||
| 6457 | // possible value (-min_int * -1) for which division would be | ||
| 6458 | // illegal behavior. | ||
| 6459 | // * if lhs type is unsigned, undef is returned regardless of rhs. | ||
| 6460 | // For floats: | ||
| 6461 | // If the rhs is zero, compile error for division by zero. | ||
| 6462 | // If the rhs is undefined, compile error because there is a possible | ||
| 6463 | // value (zero) for which the division would be illegal behavior. | ||
| 6464 | // If the lhs is undefined, result is undefined. | ||
| 6465 | if (maybe_lhs_val) |lhs_val| { | ||
| 6466 | if (!lhs_val.isUndef()) { | ||
| 6467 | if (lhs_val.compareWithZero(.eq)) { | ||
| 6468 | return sema.addConstant(scalar_type, Value.zero); | ||
| 6469 | } | ||
| 6470 | } | ||
| 6471 | } | ||
| 6472 | if (maybe_rhs_val) |rhs_val| { | ||
| 6473 | if (rhs_val.isUndef()) { | ||
| 6474 | return sema.failWithUseOfUndef(block, rhs_src); | ||
| 6475 | } | ||
| 6476 | if (rhs_val.compareWithZero(.eq)) { | ||
| 6477 | return sema.failWithDivideByZero(block, rhs_src); | ||
| 6478 | } | ||
| 6479 | } | ||
| 6480 | if (maybe_lhs_val) |lhs_val| { | ||
| 6481 | if (lhs_val.isUndef()) { | ||
| 6482 | if (lhs_ty.isSignedInt() and rhs_ty.isSignedInt()) { | ||
| 6483 | if (maybe_rhs_val) |rhs_val| { | ||
| 6484 | if (rhs_val.compare(.neq, Value.negative_one, scalar_type)) { | ||
| 6485 | return sema.addConstUndef(scalar_type); | ||
| 6486 | } | ||
| 6487 | } | ||
| 6488 | return sema.failWithUseOfUndef(block, rhs_src); | ||
| 6489 | } | ||
| 6490 | return sema.addConstUndef(scalar_type); | ||
| 6491 | } | ||
| 6327 | 6492 | ||
| 6328 | if (zir_tag == .mod_rem) { | 6493 | if (maybe_rhs_val) |rhs_val| { |
| 6329 | const dirty_lhs = lhs_ty.isSignedInt() or lhs_ty.isRuntimeFloat(); | 6494 | if (is_int) { |
| 6330 | const dirty_rhs = rhs_ty.isSignedInt() or rhs_ty.isRuntimeFloat(); | 6495 | return sema.addConstant( |
| 6331 | if (dirty_lhs or dirty_rhs) { | 6496 | scalar_type, |
| 6332 | return sema.mod.fail(&block.base, src, "remainder division with '{}' and '{}': signed integers and floats must use @rem or @mod", .{ lhs_ty, rhs_ty }); | 6497 | try lhs_val.intDiv(rhs_val, sema.arena), |
| 6498 | ); | ||
| 6499 | } else { | ||
| 6500 | return sema.addConstant( | ||
| 6501 | scalar_type, | ||
| 6502 | try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena), | ||
| 6503 | ); | ||
| 6504 | } | ||
| 6505 | } else break :rs .{ .src = rhs_src, .air_tag = .div }; | ||
| 6506 | } else break :rs .{ .src = lhs_src, .air_tag = .div }; | ||
| 6507 | }, | ||
| 6508 | .mul => { | ||
| 6509 | // For integers: | ||
| 6510 | // If either of the operands are zero, the result is zero. | ||
| 6511 | // If either of the operands are one, the result is the other | ||
| 6512 | // operand, even if it is undefined. | ||
| 6513 | // If either of the operands are undefined, it's a compile error | ||
| 6514 | // because there is a possible value for which the addition would | ||
| 6515 | // overflow (max_int), causing illegal behavior. | ||
| 6516 | // For floats: either operand being undef makes the result undef. | ||
| 6517 | if (maybe_lhs_val) |lhs_val| { | ||
| 6518 | if (!lhs_val.isUndef()) { | ||
| 6519 | if (lhs_val.compareWithZero(.eq)) { | ||
| 6520 | return sema.addConstant(scalar_type, Value.zero); | ||
| 6521 | } | ||
| 6522 | if (lhs_val.compare(.eq, Value.one, scalar_type)) { | ||
| 6523 | return casted_rhs; | ||
| 6524 | } | ||
| 6525 | } | ||
| 6526 | } | ||
| 6527 | if (maybe_rhs_val) |rhs_val| { | ||
| 6528 | if (rhs_val.isUndef()) { | ||
| 6529 | if (is_int) { | ||
| 6530 | return sema.failWithUseOfUndef(block, rhs_src); | ||
| 6531 | } else { | ||
| 6532 | return sema.addConstUndef(scalar_type); | ||
| 6533 | } | ||
| 6534 | } | ||
| 6535 | if (rhs_val.compareWithZero(.eq)) { | ||
| 6536 | return sema.addConstant(scalar_type, Value.zero); | ||
| 6537 | } | ||
| 6538 | if (rhs_val.compare(.eq, Value.one, scalar_type)) { | ||
| 6539 | return casted_lhs; | ||
| 6540 | } | ||
| 6541 | if (maybe_lhs_val) |lhs_val| { | ||
| 6542 | if (lhs_val.isUndef()) { | ||
| 6543 | if (is_int) { | ||
| 6544 | return sema.failWithUseOfUndef(block, lhs_src); | ||
| 6545 | } else { | ||
| 6546 | return sema.addConstUndef(scalar_type); | ||
| 6547 | } | ||
| 6548 | } | ||
| 6549 | if (is_int) { | ||
| 6550 | return sema.addConstant( | ||
| 6551 | scalar_type, | ||
| 6552 | try lhs_val.intMul(rhs_val, sema.arena), | ||
| 6553 | ); | ||
| 6554 | } else { | ||
| 6555 | return sema.addConstant( | ||
| 6556 | scalar_type, | ||
| 6557 | try lhs_val.floatMul(rhs_val, scalar_type, sema.arena), | ||
| 6558 | ); | ||
| 6559 | } | ||
| 6560 | } else break :rs .{ .src = lhs_src, .air_tag = .mul }; | ||
| 6561 | } else break :rs .{ .src = rhs_src, .air_tag = .mul }; | ||
| 6562 | }, | ||
| 6563 | .mulwrap => { | ||
| 6564 | // Integers only; floats are handled above. | ||
| 6565 | // If either of the operands are zero, the result is zero. | ||
| 6566 | // If either of the operands are one, the result is the other | ||
| 6567 | // operand, even if it is undefined. | ||
| 6568 | // If either of the operands are undefined, the result is undefined. | ||
| 6569 | if (maybe_lhs_val) |lhs_val| { | ||
| 6570 | if (!lhs_val.isUndef()) { | ||
| 6571 | if (lhs_val.compareWithZero(.eq)) { | ||
| 6572 | return sema.addConstant(scalar_type, Value.zero); | ||
| 6573 | } | ||
| 6574 | if (lhs_val.compare(.eq, Value.one, scalar_type)) { | ||
| 6575 | return casted_rhs; | ||
| 6576 | } | ||
| 6577 | } | ||
| 6578 | } | ||
| 6579 | if (maybe_rhs_val) |rhs_val| { | ||
| 6580 | if (rhs_val.isUndef()) { | ||
| 6581 | return sema.addConstUndef(scalar_type); | ||
| 6582 | } | ||
| 6583 | if (rhs_val.compareWithZero(.eq)) { | ||
| 6584 | return sema.addConstant(scalar_type, Value.zero); | ||
| 6585 | } | ||
| 6586 | if (rhs_val.compare(.eq, Value.one, scalar_type)) { | ||
| 6587 | return casted_lhs; | ||
| 6588 | } | ||
| 6589 | if (maybe_lhs_val) |lhs_val| { | ||
| 6590 | if (lhs_val.isUndef()) { | ||
| 6591 | return sema.addConstUndef(scalar_type); | ||
| 6592 | } | ||
| 6593 | return sema.addConstant( | ||
| 6594 | scalar_type, | ||
| 6595 | try lhs_val.numberMulWrap(rhs_val, scalar_type, sema.arena, target), | ||
| 6596 | ); | ||
| 6597 | } else break :rs .{ .src = lhs_src, .air_tag = .mulwrap }; | ||
| 6598 | } else break :rs .{ .src = rhs_src, .air_tag = .mulwrap }; | ||
| 6599 | }, | ||
| 6600 | .mod_rem => { | ||
| 6601 | // For integers: | ||
| 6602 | // Either operand being undef is a compile error because there exists | ||
| 6603 | // a possible value (TODO what is it?) that would invoke illegal behavior. | ||
| 6604 | // TODO: can lhs zero be handled better? | ||
| 6605 | // TODO: can lhs undef be handled better? | ||
| 6606 | // | ||
| 6607 | // For floats: | ||
| 6608 | // If the rhs is zero, compile error for division by zero. | ||
| 6609 | // If the rhs is undefined, compile error because there is a possible | ||
| 6610 | // value (zero) for which the division would be illegal behavior. | ||
| 6611 | // If the lhs is undefined, result is undefined. | ||
| 6612 | // | ||
| 6613 | // For either one: if the result would be different between @mod and @rem, | ||
| 6614 | // then emit a compile error saying you have to pick one. | ||
| 6615 | if (is_int) { | ||
| 6616 | if (maybe_lhs_val) |lhs_val| { | ||
| 6617 | if (lhs_val.isUndef()) { | ||
| 6618 | return sema.failWithUseOfUndef(block, lhs_src); | ||
| 6619 | } | ||
| 6620 | if (lhs_val.compareWithZero(.lt)) { | ||
| 6621 | return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty); | ||
| 6622 | } | ||
| 6623 | } else if (lhs_ty.isSignedInt()) { | ||
| 6624 | return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty); | ||
| 6625 | } | ||
| 6626 | if (maybe_rhs_val) |rhs_val| { | ||
| 6627 | if (rhs_val.isUndef()) { | ||
| 6628 | return sema.failWithUseOfUndef(block, rhs_src); | ||
| 6629 | } | ||
| 6630 | if (rhs_val.compareWithZero(.eq)) { | ||
| 6631 | return sema.failWithDivideByZero(block, rhs_src); | ||
| 6632 | } | ||
| 6633 | if (rhs_val.compareWithZero(.lt)) { | ||
| 6634 | return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty); | ||
| 6635 | } | ||
| 6636 | if (maybe_lhs_val) |lhs_val| { | ||
| 6637 | return sema.addConstant( | ||
| 6638 | scalar_type, | ||
| 6639 | try lhs_val.intRem(rhs_val, sema.arena), | ||
| 6640 | ); | ||
| 6641 | } | ||
| 6642 | break :rs .{ .src = lhs_src, .air_tag = .rem }; | ||
| 6643 | } else if (rhs_ty.isSignedInt()) { | ||
| 6644 | return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty); | ||
| 6645 | } else { | ||
| 6646 | break :rs .{ .src = rhs_src, .air_tag = .rem }; | ||
| 6647 | } | ||
| 6648 | } | ||
| 6649 | // float operands | ||
| 6650 | if (maybe_rhs_val) |rhs_val| { | ||
| 6651 | if (rhs_val.isUndef()) { | ||
| 6652 | return sema.failWithUseOfUndef(block, rhs_src); | ||
| 6653 | } | ||
| 6654 | if (rhs_val.compareWithZero(.eq)) { | ||
| 6655 | return sema.failWithDivideByZero(block, rhs_src); | ||
| 6656 | } | ||
| 6657 | if (rhs_val.compareWithZero(.lt)) { | ||
| 6658 | return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty); | ||
| 6659 | } | ||
| 6660 | if (maybe_lhs_val) |lhs_val| { | ||
| 6661 | if (lhs_val.isUndef() or lhs_val.compareWithZero(.lt)) { | ||
| 6662 | return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty); | ||
| 6663 | } | ||
| 6664 | return sema.addConstant( | ||
| 6665 | scalar_type, | ||
| 6666 | try lhs_val.floatRem(rhs_val, sema.arena), | ||
| 6667 | ); | ||
| 6668 | } else { | ||
| 6669 | return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty); | ||
| 6670 | } | ||
| 6671 | } else { | ||
| 6672 | return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty); | ||
| 6673 | } | ||
| 6674 | }, | ||
| 6675 | .rem => { | ||
| 6676 | // For integers: | ||
| 6677 | // Either operand being undef is a compile error because there exists | ||
| 6678 | // a possible value (TODO what is it?) that would invoke illegal behavior. | ||
| 6679 | // TODO: can lhs zero be handled better? | ||
| 6680 | // TODO: can lhs undef be handled better? | ||
| 6681 | // | ||
| 6682 | // For floats: | ||
| 6683 | // If the rhs is zero, compile error for division by zero. | ||
| 6684 | // If the rhs is undefined, compile error because there is a possible | ||
| 6685 | // value (zero) for which the division would be illegal behavior. | ||
| 6686 | // If the lhs is undefined, result is undefined. | ||
| 6687 | if (is_int) { | ||
| 6688 | if (maybe_lhs_val) |lhs_val| { | ||
| 6689 | if (lhs_val.isUndef()) { | ||
| 6690 | return sema.failWithUseOfUndef(block, lhs_src); | ||
| 6691 | } | ||
| 6692 | } | ||
| 6693 | if (maybe_rhs_val) |rhs_val| { | ||
| 6694 | if (rhs_val.isUndef()) { | ||
| 6695 | return sema.failWithUseOfUndef(block, rhs_src); | ||
| 6696 | } | ||
| 6697 | if (rhs_val.compareWithZero(.eq)) { | ||
| 6698 | return sema.failWithDivideByZero(block, rhs_src); | ||
| 6699 | } | ||
| 6700 | if (maybe_lhs_val) |lhs_val| { | ||
| 6701 | return sema.addConstant( | ||
| 6702 | scalar_type, | ||
| 6703 | try lhs_val.intRem(rhs_val, sema.arena), | ||
| 6704 | ); | ||
| 6705 | } | ||
| 6706 | break :rs .{ .src = lhs_src, .air_tag = .rem }; | ||
| 6707 | } else { | ||
| 6708 | break :rs .{ .src = rhs_src, .air_tag = .rem }; | ||
| 6709 | } | ||
| 6710 | } | ||
| 6711 | // float operands | ||
| 6712 | if (maybe_rhs_val) |rhs_val| { | ||
| 6713 | if (rhs_val.isUndef()) { | ||
| 6714 | return sema.failWithUseOfUndef(block, rhs_src); | ||
| 6715 | } | ||
| 6716 | if (rhs_val.compareWithZero(.eq)) { | ||
| 6717 | return sema.failWithDivideByZero(block, rhs_src); | ||
| 6718 | } | ||
| 6719 | } | ||
| 6720 | if (maybe_lhs_val) |lhs_val| { | ||
| 6721 | if (lhs_val.isUndef()) { | ||
| 6722 | return sema.addConstUndef(scalar_type); | ||
| 6723 | } | ||
| 6724 | if (maybe_rhs_val) |rhs_val| { | ||
| 6725 | return sema.addConstant( | ||
| 6726 | scalar_type, | ||
| 6727 | try lhs_val.floatRem(rhs_val, sema.arena), | ||
| 6728 | ); | ||
| 6729 | } else break :rs .{ .src = rhs_src, .air_tag = .rem }; | ||
| 6730 | } else break :rs .{ .src = lhs_src, .air_tag = .rem }; | ||
| 6731 | }, | ||
| 6732 | .mod => { | ||
| 6733 | // For integers: | ||
| 6734 | // Either operand being undef is a compile error because there exists | ||
| 6735 | // a possible value (TODO what is it?) that would invoke illegal behavior. | ||
| 6736 | // TODO: can lhs zero be handled better? | ||
| 6737 | // TODO: can lhs undef be handled better? | ||
| 6738 | // | ||
| 6739 | // For floats: | ||
| 6740 | // If the rhs is zero, compile error for division by zero. | ||
| 6741 | // If the rhs is undefined, compile error because there is a possible | ||
| 6742 | // value (zero) for which the division would be illegal behavior. | ||
| 6743 | // If the lhs is undefined, result is undefined. | ||
| 6744 | if (is_int) { | ||
| 6745 | if (maybe_lhs_val) |lhs_val| { | ||
| 6746 | if (lhs_val.isUndef()) { | ||
| 6747 | return sema.failWithUseOfUndef(block, lhs_src); | ||
| 6748 | } | ||
| 6749 | } | ||
| 6750 | if (maybe_rhs_val) |rhs_val| { | ||
| 6751 | if (rhs_val.isUndef()) { | ||
| 6752 | return sema.failWithUseOfUndef(block, rhs_src); | ||
| 6753 | } | ||
| 6754 | if (rhs_val.compareWithZero(.eq)) { | ||
| 6755 | return sema.failWithDivideByZero(block, rhs_src); | ||
| 6756 | } | ||
| 6757 | if (maybe_lhs_val) |lhs_val| { | ||
| 6758 | return sema.addConstant( | ||
| 6759 | scalar_type, | ||
| 6760 | try lhs_val.intMod(rhs_val, sema.arena), | ||
| 6761 | ); | ||
| 6762 | } | ||
| 6763 | break :rs .{ .src = lhs_src, .air_tag = .mod }; | ||
| 6764 | } else { | ||
| 6765 | break :rs .{ .src = rhs_src, .air_tag = .mod }; | ||
| 6766 | } | ||
| 6767 | } | ||
| 6768 | // float operands | ||
| 6769 | if (maybe_rhs_val) |rhs_val| { | ||
| 6770 | if (rhs_val.isUndef()) { | ||
| 6771 | return sema.failWithUseOfUndef(block, rhs_src); | ||
| 6772 | } | ||
| 6773 | if (rhs_val.compareWithZero(.eq)) { | ||
| 6774 | return sema.failWithDivideByZero(block, rhs_src); | ||
| 6775 | } | ||
| 6776 | } | ||
| 6777 | if (maybe_lhs_val) |lhs_val| { | ||
| 6778 | if (lhs_val.isUndef()) { | ||
| 6779 | return sema.addConstUndef(scalar_type); | ||
| 6780 | } | ||
| 6781 | if (maybe_rhs_val) |rhs_val| { | ||
| 6782 | return sema.addConstant( | ||
| 6783 | scalar_type, | ||
| 6784 | try lhs_val.floatMod(rhs_val, sema.arena), | ||
| 6785 | ); | ||
| 6786 | } else break :rs .{ .src = rhs_src, .air_tag = .mod }; | ||
| 6787 | } else break :rs .{ .src = lhs_src, .air_tag = .mod }; | ||
| 6788 | }, | ||
| 6789 | else => unreachable, | ||
| 6333 | } | 6790 | } |
| 6334 | } | ||
| 6335 | |||
| 6336 | const air_tag: Air.Inst.Tag = switch (zir_tag) { | ||
| 6337 | .add => .add, | ||
| 6338 | .addwrap => .addwrap, | ||
| 6339 | .sub => .sub, | ||
| 6340 | .subwrap => .subwrap, | ||
| 6341 | .mul => .mul, | ||
| 6342 | .mulwrap => .mulwrap, | ||
| 6343 | .div => .div, | ||
| 6344 | .mod_rem => .rem, | ||
| 6345 | .rem => .rem, | ||
| 6346 | else => return sema.mod.fail(&block.base, src, "TODO implement arithmetic for operand '{s}'", .{@tagName(zir_tag)}), | ||
| 6347 | }; | 6791 | }; |
| 6348 | 6792 | ||
| 6349 | return block.addBinOp(air_tag, casted_lhs, casted_rhs); | 6793 | try sema.requireRuntimeBlock(block, rs.src); |
| 6794 | return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs); | ||
| 6350 | } | 6795 | } |
| 6351 | 6796 | ||
| 6352 | fn zirLoad(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 6797 | fn zirLoad(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | @@ -7401,7 +7846,7 @@ fn analyzeRet( | ... | @@ -7401,7 +7846,7 @@ fn analyzeRet( |
| 7401 | fn floatOpAllowed(tag: Zir.Inst.Tag) bool { | 7846 | fn floatOpAllowed(tag: Zir.Inst.Tag) bool { |
| 7402 | // extend this swich as additional operators are implemented | 7847 | // extend this swich as additional operators are implemented |
| 7403 | return switch (tag) { | 7848 | return switch (tag) { |
| 7404 | .add, .sub, .mul, .div => true, | 7849 | .add, .sub, .mul, .div, .mod, .rem, .mod_rem => true, |
| 7405 | else => false, | 7850 | else => false, |
| 7406 | }; | 7851 | }; |
| 7407 | } | 7852 | } |
| ... | @@ -8068,16 +8513,6 @@ fn zirDivTrunc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr | ... | @@ -8068,16 +8513,6 @@ fn zirDivTrunc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr |
| 8068 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirDivTrunc", .{}); | 8513 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirDivTrunc", .{}); |
| 8069 | } | 8514 | } |
| 8070 | 8515 | ||
| 8071 | fn zirMod(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | ||
| 8072 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 8073 | const src = inst_data.src(); | ||
| 8074 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirMod", .{}); | ||
| 8075 | } | ||
| 8076 | |||
| 8077 | fn zirRem(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | ||
| 8078 | return sema.zirArithmetic(block, inst); | ||
| 8079 | } | ||
| 8080 | |||
| 8081 | fn zirShlExact(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 8516 | fn zirShlExact(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 8082 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | 8517 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 8083 | const src = inst_data.src(); | 8518 | const src = inst_data.src(); |
src/Zir.zig+11-11| ... | @@ -395,17 +395,6 @@ pub const Inst = struct { | ... | @@ -395,17 +395,6 @@ pub const Inst = struct { |
| 395 | /// Merge two error sets into one, `E1 || E2`. | 395 | /// Merge two error sets into one, `E1 || E2`. |
| 396 | /// Uses the `pl_node` field with payload `Bin`. | 396 | /// Uses the `pl_node` field with payload `Bin`. |
| 397 | merge_error_sets, | 397 | merge_error_sets, |
| 398 | /// Ambiguously remainder division or modulus. If the computation would possibly have | ||
| 399 | /// a different value depending on whether the operation is remainder division or modulus, | ||
| 400 | /// a compile error is emitted. Otherwise the computation is performed. | ||
| 401 | /// Uses the `pl_node` union field. Payload is `Bin`. | ||
| 402 | mod_rem, | ||
| 403 | /// Arithmetic multiplication. Asserts no integer overflow. | ||
| 404 | /// Uses the `pl_node` union field. Payload is `Bin`. | ||
| 405 | mul, | ||
| 406 | /// Twos complement wrapping integer multiplication. | ||
| 407 | /// Uses the `pl_node` union field. Payload is `Bin`. | ||
| 408 | mulwrap, | ||
| 409 | /// Turns an R-Value into a const L-Value. In other words, it takes a value, | 398 | /// Turns an R-Value into a const L-Value. In other words, it takes a value, |
| 410 | /// stores it in a memory location, and returns a const pointer to it. If the value | 399 | /// stores it in a memory location, and returns a const pointer to it. If the value |
| 411 | /// is `comptime`, the memory location is global static constant data. Otherwise, | 400 | /// is `comptime`, the memory location is global static constant data. Otherwise, |
| ... | @@ -828,6 +817,17 @@ pub const Inst = struct { | ... | @@ -828,6 +817,17 @@ pub const Inst = struct { |
| 828 | /// Implements the `@rem` builtin. | 817 | /// Implements the `@rem` builtin. |
| 829 | /// Uses the `pl_node` union field with payload `Bin`. | 818 | /// Uses the `pl_node` union field with payload `Bin`. |
| 830 | rem, | 819 | rem, |
| 820 | /// Ambiguously remainder division or modulus. If the computation would possibly have | ||
| 821 | /// a different value depending on whether the operation is remainder division or modulus, | ||
| 822 | /// a compile error is emitted. Otherwise the computation is performed. | ||
| 823 | /// Uses the `pl_node` union field. Payload is `Bin`. | ||
| 824 | mod_rem, | ||
| 825 | /// Arithmetic multiplication. Asserts no integer overflow. | ||
| 826 | /// Uses the `pl_node` union field. Payload is `Bin`. | ||
| 827 | mul, | ||
| 828 | /// Twos complement wrapping integer multiplication. | ||
| 829 | /// Uses the `pl_node` union field. Payload is `Bin`. | ||
| 830 | mulwrap, | ||
| 831 | 831 | ||
| 832 | /// Integer shift-left. Zeroes are shifted in from the right hand side. | 832 | /// Integer shift-left. Zeroes are shifted in from the right hand side. |
| 833 | /// Uses the `pl_node` union field. Payload is `Bin`. | 833 | /// Uses the `pl_node` union field. Payload is `Bin`. |
src/codegen.zig+9| ... | @@ -832,6 +832,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -832,6 +832,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 832 | .mulwrap => try self.airMulWrap(inst), | 832 | .mulwrap => try self.airMulWrap(inst), |
| 833 | .div => try self.airDiv(inst), | 833 | .div => try self.airDiv(inst), |
| 834 | .rem => try self.airRem(inst), | 834 | .rem => try self.airRem(inst), |
| 835 | .mod => try self.airMod(inst), | ||
| 835 | 836 | ||
| 836 | .cmp_lt => try self.airCmp(inst, .lt), | 837 | .cmp_lt => try self.airCmp(inst, .lt), |
| 837 | .cmp_lte => try self.airCmp(inst, .lte), | 838 | .cmp_lte => try self.airCmp(inst, .lte), |
| ... | @@ -1353,6 +1354,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1353,6 +1354,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1353 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1354 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1354 | } | 1355 | } |
| 1355 | 1356 | ||
| 1357 | fn airMod(self: *Self, inst: Air.Inst.Index) !void { | ||
| 1358 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | ||
| 1359 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { | ||
| 1360 | else => return self.fail("TODO implement mod for {}", .{self.target.cpu.arch}), | ||
| 1361 | }; | ||
| 1362 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | ||
| 1363 | } | ||
| 1364 | |||
| 1356 | fn airBitAnd(self: *Self, inst: Air.Inst.Index) !void { | 1365 | fn airBitAnd(self: *Self, inst: Air.Inst.Index) !void { |
| 1357 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1366 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1358 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { | 1367 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { |
src/codegen/c.zig+2| ... | @@ -897,6 +897,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO | ... | @@ -897,6 +897,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO |
| 897 | // that wrapping is UB. | 897 | // that wrapping is UB. |
| 898 | .div => try airBinOp( f, inst, " / "), | 898 | .div => try airBinOp( f, inst, " / "), |
| 899 | .rem => try airBinOp( f, inst, " % "), | 899 | .rem => try airBinOp( f, inst, " % "), |
| 900 | // TODO implement modulus division | ||
| 901 | .mod => try airBinOp( f, inst, " mod "), | ||
| 900 | 902 | ||
| 901 | .cmp_eq => try airBinOp(f, inst, " == "), | 903 | .cmp_eq => try airBinOp(f, inst, " == "), |
| 902 | .cmp_gt => try airBinOp(f, inst, " > "), | 904 | .cmp_gt => try airBinOp(f, inst, " > "), |
src/codegen/llvm.zig+29| ... | @@ -1244,6 +1244,7 @@ pub const FuncGen = struct { | ... | @@ -1244,6 +1244,7 @@ pub const FuncGen = struct { |
| 1244 | .mulwrap => try self.airMul(inst, true), | 1244 | .mulwrap => try self.airMul(inst, true), |
| 1245 | .div => try self.airDiv(inst), | 1245 | .div => try self.airDiv(inst), |
| 1246 | .rem => try self.airRem(inst), | 1246 | .rem => try self.airRem(inst), |
| 1247 | .mod => try self.airMod(inst), | ||
| 1247 | .ptr_add => try self.airPtrAdd(inst), | 1248 | .ptr_add => try self.airPtrAdd(inst), |
| 1248 | .ptr_sub => try self.airPtrSub(inst), | 1249 | .ptr_sub => try self.airPtrSub(inst), |
| 1249 | 1250 | ||
| ... | @@ -2095,6 +2096,34 @@ pub const FuncGen = struct { | ... | @@ -2095,6 +2096,34 @@ pub const FuncGen = struct { |
| 2095 | return self.builder.buildURem(lhs, rhs, ""); | 2096 | return self.builder.buildURem(lhs, rhs, ""); |
| 2096 | } | 2097 | } |
| 2097 | 2098 | ||
| 2099 | fn airMod(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | ||
| 2100 | if (self.liveness.isUnused(inst)) return null; | ||
| 2101 | |||
| 2102 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | ||
| 2103 | const lhs = try self.resolveInst(bin_op.lhs); | ||
| 2104 | const rhs = try self.resolveInst(bin_op.rhs); | ||
| 2105 | const inst_ty = self.air.typeOfIndex(inst); | ||
| 2106 | const inst_llvm_ty = try self.dg.llvmType(inst_ty); | ||
| 2107 | |||
| 2108 | if (inst_ty.isRuntimeFloat()) { | ||
| 2109 | const a = self.builder.buildFRem(lhs, rhs, ""); | ||
| 2110 | const b = self.builder.buildFAdd(a, rhs, ""); | ||
| 2111 | const c = self.builder.buildFRem(b, rhs, ""); | ||
| 2112 | const zero = inst_llvm_ty.constNull(); | ||
| 2113 | const ltz = self.builder.buildFCmp(.OLT, lhs, zero, ""); | ||
| 2114 | return self.builder.buildSelect(ltz, c, a, ""); | ||
| 2115 | } | ||
| 2116 | if (inst_ty.isSignedInt()) { | ||
| 2117 | const a = self.builder.buildSRem(lhs, rhs, ""); | ||
| 2118 | const b = self.builder.buildNSWAdd(a, rhs, ""); | ||
| 2119 | const c = self.builder.buildSRem(b, rhs, ""); | ||
| 2120 | const zero = inst_llvm_ty.constNull(); | ||
| 2121 | const ltz = self.builder.buildICmp(.SLT, lhs, zero, ""); | ||
| 2122 | return self.builder.buildSelect(ltz, c, a, ""); | ||
| 2123 | } | ||
| 2124 | return self.builder.buildURem(lhs, rhs, ""); | ||
| 2125 | } | ||
| 2126 | |||
| 2098 | fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | 2127 | fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 2099 | if (self.liveness.isUnused(inst)) | 2128 | if (self.liveness.isUnused(inst)) |
| 2100 | return null; | 2129 | return null; |
src/print_air.zig+1| ... | @@ -110,6 +110,7 @@ const Writer = struct { | ... | @@ -110,6 +110,7 @@ const Writer = struct { |
| 110 | .mulwrap, | 110 | .mulwrap, |
| 111 | .div, | 111 | .div, |
| 112 | .rem, | 112 | .rem, |
| 113 | .mod, | ||
| 113 | .ptr_add, | 114 | .ptr_add, |
| 114 | .ptr_sub, | 115 | .ptr_sub, |
| 115 | .bit_and, | 116 | .bit_and, |
src/value.zig+138| ... | @@ -1616,6 +1616,34 @@ pub const Value = extern union { | ... | @@ -1616,6 +1616,34 @@ pub const Value = extern union { |
| 1616 | return result; | 1616 | return result; |
| 1617 | } | 1617 | } |
| 1618 | 1618 | ||
| 1619 | /// Supports both floats and ints; handles undefined. | ||
| 1620 | pub fn numberMulWrap( | ||
| 1621 | lhs: Value, | ||
| 1622 | rhs: Value, | ||
| 1623 | ty: Type, | ||
| 1624 | arena: *Allocator, | ||
| 1625 | target: Target, | ||
| 1626 | ) !Value { | ||
| 1627 | if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); | ||
| 1628 | |||
| 1629 | if (ty.isAnyFloat()) { | ||
| 1630 | return floatMul(lhs, rhs, ty, arena); | ||
| 1631 | } | ||
| 1632 | const result = try intMul(lhs, rhs, arena); | ||
| 1633 | |||
| 1634 | const max = try ty.maxInt(arena, target); | ||
| 1635 | if (compare(result, .gt, max, ty)) { | ||
| 1636 | @panic("TODO comptime wrapping integer multiplication"); | ||
| 1637 | } | ||
| 1638 | |||
| 1639 | const min = try ty.minInt(arena, target); | ||
| 1640 | if (compare(result, .lt, min, ty)) { | ||
| 1641 | @panic("TODO comptime wrapping integer multiplication"); | ||
| 1642 | } | ||
| 1643 | |||
| 1644 | return result; | ||
| 1645 | } | ||
| 1646 | |||
| 1619 | /// Supports both floats and ints; handles undefined. | 1647 | /// Supports both floats and ints; handles undefined. |
| 1620 | pub fn numberMax(lhs: Value, rhs: Value, arena: *Allocator) !Value { | 1648 | pub fn numberMax(lhs: Value, rhs: Value, arena: *Allocator) !Value { |
| 1621 | if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); | 1649 | if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef); |
| ... | @@ -1840,6 +1868,82 @@ pub const Value = extern union { | ... | @@ -1840,6 +1868,82 @@ pub const Value = extern union { |
| 1840 | } | 1868 | } |
| 1841 | } | 1869 | } |
| 1842 | 1870 | ||
| 1871 | pub fn intRem(lhs: Value, rhs: Value, allocator: *Allocator) !Value { | ||
| 1872 | // TODO is this a performance issue? maybe we should try the operation without | ||
| 1873 | // resorting to BigInt first. | ||
| 1874 | var lhs_space: Value.BigIntSpace = undefined; | ||
| 1875 | var rhs_space: Value.BigIntSpace = undefined; | ||
| 1876 | const lhs_bigint = lhs.toBigInt(&lhs_space); | ||
| 1877 | const rhs_bigint = rhs.toBigInt(&rhs_space); | ||
| 1878 | const limbs_q = try allocator.alloc( | ||
| 1879 | std.math.big.Limb, | ||
| 1880 | lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1, | ||
| 1881 | ); | ||
| 1882 | const limbs_r = try allocator.alloc( | ||
| 1883 | std.math.big.Limb, | ||
| 1884 | lhs_bigint.limbs.len, | ||
| 1885 | ); | ||
| 1886 | const limbs_buffer = try allocator.alloc( | ||
| 1887 | std.math.big.Limb, | ||
| 1888 | std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len), | ||
| 1889 | ); | ||
| 1890 | var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined }; | ||
| 1891 | var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined }; | ||
| 1892 | result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer, null); | ||
| 1893 | const result_limbs = result_r.limbs[0..result_r.len]; | ||
| 1894 | |||
| 1895 | if (result_r.positive) { | ||
| 1896 | return Value.Tag.int_big_positive.create(allocator, result_limbs); | ||
| 1897 | } else { | ||
| 1898 | return Value.Tag.int_big_negative.create(allocator, result_limbs); | ||
| 1899 | } | ||
| 1900 | } | ||
| 1901 | |||
| 1902 | pub fn intMod(lhs: Value, rhs: Value, allocator: *Allocator) !Value { | ||
| 1903 | // TODO is this a performance issue? maybe we should try the operation without | ||
| 1904 | // resorting to BigInt first. | ||
| 1905 | var lhs_space: Value.BigIntSpace = undefined; | ||
| 1906 | var rhs_space: Value.BigIntSpace = undefined; | ||
| 1907 | const lhs_bigint = lhs.toBigInt(&lhs_space); | ||
| 1908 | const rhs_bigint = rhs.toBigInt(&rhs_space); | ||
| 1909 | const limbs_q = try allocator.alloc( | ||
| 1910 | std.math.big.Limb, | ||
| 1911 | lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1, | ||
| 1912 | ); | ||
| 1913 | const limbs_r = try allocator.alloc( | ||
| 1914 | std.math.big.Limb, | ||
| 1915 | lhs_bigint.limbs.len, | ||
| 1916 | ); | ||
| 1917 | const limbs_buffer = try allocator.alloc( | ||
| 1918 | std.math.big.Limb, | ||
| 1919 | std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len), | ||
| 1920 | ); | ||
| 1921 | var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined }; | ||
| 1922 | var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined }; | ||
| 1923 | result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buffer, null); | ||
| 1924 | const result_limbs = result_r.limbs[0..result_r.len]; | ||
| 1925 | |||
| 1926 | if (result_r.positive) { | ||
| 1927 | return Value.Tag.int_big_positive.create(allocator, result_limbs); | ||
| 1928 | } else { | ||
| 1929 | return Value.Tag.int_big_negative.create(allocator, result_limbs); | ||
| 1930 | } | ||
| 1931 | } | ||
| 1932 | |||
| 1933 | pub fn floatRem(lhs: Value, rhs: Value, allocator: *Allocator) !Value { | ||
| 1934 | _ = lhs; | ||
| 1935 | _ = rhs; | ||
| 1936 | _ = allocator; | ||
| 1937 | @panic("TODO implement Value.floatRem"); | ||
| 1938 | } | ||
| 1939 | |||
| 1940 | pub fn floatMod(lhs: Value, rhs: Value, allocator: *Allocator) !Value { | ||
| 1941 | _ = lhs; | ||
| 1942 | _ = rhs; | ||
| 1943 | _ = allocator; | ||
| 1944 | @panic("TODO implement Value.floatMod"); | ||
| 1945 | } | ||
| 1946 | |||
| 1843 | pub fn intMul(lhs: Value, rhs: Value, allocator: *Allocator) !Value { | 1947 | pub fn intMul(lhs: Value, rhs: Value, allocator: *Allocator) !Value { |
| 1844 | // TODO is this a performance issue? maybe we should try the operation without | 1948 | // TODO is this a performance issue? maybe we should try the operation without |
| 1845 | // resorting to BigInt first. | 1949 | // resorting to BigInt first. |
| ... | @@ -1875,6 +1979,31 @@ pub const Value = extern union { | ... | @@ -1875,6 +1979,31 @@ pub const Value = extern union { |
| 1875 | return Tag.int_u64.create(arena, truncated); | 1979 | return Tag.int_u64.create(arena, truncated); |
| 1876 | } | 1980 | } |
| 1877 | 1981 | ||
| 1982 | pub fn shl(lhs: Value, rhs: Value, allocator: *Allocator) !Value { | ||
| 1983 | // TODO is this a performance issue? maybe we should try the operation without | ||
| 1984 | // resorting to BigInt first. | ||
| 1985 | var lhs_space: Value.BigIntSpace = undefined; | ||
| 1986 | const lhs_bigint = lhs.toBigInt(&lhs_space); | ||
| 1987 | const shift = rhs.toUnsignedInt(); | ||
| 1988 | const limbs = try allocator.alloc( | ||
| 1989 | std.math.big.Limb, | ||
| 1990 | lhs_bigint.limbs.len + (shift / (@sizeOf(std.math.big.Limb) * 8)) + 1, | ||
| 1991 | ); | ||
| 1992 | var result_bigint = BigIntMutable{ | ||
| 1993 | .limbs = limbs, | ||
| 1994 | .positive = undefined, | ||
| 1995 | .len = undefined, | ||
| 1996 | }; | ||
| 1997 | result_bigint.shiftLeft(lhs_bigint, shift); | ||
| 1998 | const result_limbs = result_bigint.limbs[0..result_bigint.len]; | ||
| 1999 | |||
| 2000 | if (result_bigint.positive) { | ||
| 2001 | return Value.Tag.int_big_positive.create(allocator, result_limbs); | ||
| 2002 | } else { | ||
| 2003 | return Value.Tag.int_big_negative.create(allocator, result_limbs); | ||
| 2004 | } | ||
| 2005 | } | ||
| 2006 | |||
| 1878 | pub fn shr(lhs: Value, rhs: Value, allocator: *Allocator) !Value { | 2007 | pub fn shr(lhs: Value, rhs: Value, allocator: *Allocator) !Value { |
| 1879 | // TODO is this a performance issue? maybe we should try the operation without | 2008 | // TODO is this a performance issue? maybe we should try the operation without |
| 1880 | // resorting to BigInt first. | 2009 | // resorting to BigInt first. |
| ... | @@ -2227,4 +2356,13 @@ pub const Value = extern union { | ... | @@ -2227,4 +2356,13 @@ pub const Value = extern union { |
| 2227 | /// are possible without using an allocator. | 2356 | /// are possible without using an allocator. |
| 2228 | limbs: [(@sizeOf(u64) / @sizeOf(std.math.big.Limb)) + 1]std.math.big.Limb, | 2357 | limbs: [(@sizeOf(u64) / @sizeOf(std.math.big.Limb)) + 1]std.math.big.Limb, |
| 2229 | }; | 2358 | }; |
| 2359 | |||
| 2360 | pub const zero = initTag(.zero); | ||
| 2361 | pub const one = initTag(.one); | ||
| 2362 | pub const negative_one: Value = .{ .ptr_otherwise = &negative_one_payload.base }; | ||
| 2363 | }; | ||
| 2364 | |||
| 2365 | var negative_one_payload: Value.Payload.I64 = .{ | ||
| 2366 | .base = .{ .tag = .int_i64 }, | ||
| 2367 | .data = -1, | ||
| 2230 | }; | 2368 | }; |
test/behavior.zig+2-1| ... | @@ -10,6 +10,7 @@ test { | ... | @@ -10,6 +10,7 @@ test { |
| 10 | _ = @import("behavior/eval.zig"); | 10 | _ = @import("behavior/eval.zig"); |
| 11 | _ = @import("behavior/generics.zig"); | 11 | _ = @import("behavior/generics.zig"); |
| 12 | _ = @import("behavior/if.zig"); | 12 | _ = @import("behavior/if.zig"); |
| 13 | _ = @import("behavior/math.zig"); | ||
| 13 | _ = @import("behavior/member_func.zig"); | 14 | _ = @import("behavior/member_func.zig"); |
| 14 | _ = @import("behavior/pointers.zig"); | 15 | _ = @import("behavior/pointers.zig"); |
| 15 | _ = @import("behavior/sizeof_and_typeof.zig"); | 16 | _ = @import("behavior/sizeof_and_typeof.zig"); |
| ... | @@ -119,7 +120,7 @@ test { | ... | @@ -119,7 +120,7 @@ test { |
| 119 | _ = @import("behavior/incomplete_struct_param_tld.zig"); | 120 | _ = @import("behavior/incomplete_struct_param_tld.zig"); |
| 120 | _ = @import("behavior/inttoptr.zig"); | 121 | _ = @import("behavior/inttoptr.zig"); |
| 121 | _ = @import("behavior/ir_block_deps.zig"); | 122 | _ = @import("behavior/ir_block_deps.zig"); |
| 122 | _ = @import("behavior/math.zig"); | 123 | _ = @import("behavior/math_stage1.zig"); |
| 123 | _ = @import("behavior/maximum_minimum.zig"); | 124 | _ = @import("behavior/maximum_minimum.zig"); |
| 124 | _ = @import("behavior/merge_error_sets.zig"); | 125 | _ = @import("behavior/merge_error_sets.zig"); |
| 125 | _ = @import("behavior/misc.zig"); | 126 | _ = @import("behavior/misc.zig"); |
test/behavior/math.zig-848| ... | @@ -6,171 +6,6 @@ const maxInt = std.math.maxInt; | ... | @@ -6,171 +6,6 @@ const maxInt = std.math.maxInt; |
| 6 | const minInt = std.math.minInt; | 6 | const minInt = std.math.minInt; |
| 7 | const mem = std.mem; | 7 | const mem = std.mem; |
| 8 | 8 | ||
| 9 | test "division" { | ||
| 10 | try testDivision(); | ||
| 11 | comptime try testDivision(); | ||
| 12 | } | ||
| 13 | fn testDivision() !void { | ||
| 14 | try expect(div(u32, 13, 3) == 4); | ||
| 15 | try expect(div(f16, 1.0, 2.0) == 0.5); | ||
| 16 | try expect(div(f32, 1.0, 2.0) == 0.5); | ||
| 17 | |||
| 18 | try expect(divExact(u32, 55, 11) == 5); | ||
| 19 | try expect(divExact(i32, -55, 11) == -5); | ||
| 20 | try expect(divExact(f16, 55.0, 11.0) == 5.0); | ||
| 21 | try expect(divExact(f16, -55.0, 11.0) == -5.0); | ||
| 22 | try expect(divExact(f32, 55.0, 11.0) == 5.0); | ||
| 23 | try expect(divExact(f32, -55.0, 11.0) == -5.0); | ||
| 24 | |||
| 25 | try expect(divFloor(i32, 5, 3) == 1); | ||
| 26 | try expect(divFloor(i32, -5, 3) == -2); | ||
| 27 | try expect(divFloor(f16, 5.0, 3.0) == 1.0); | ||
| 28 | try expect(divFloor(f16, -5.0, 3.0) == -2.0); | ||
| 29 | try expect(divFloor(f32, 5.0, 3.0) == 1.0); | ||
| 30 | try expect(divFloor(f32, -5.0, 3.0) == -2.0); | ||
| 31 | try expect(divFloor(i32, -0x80000000, -2) == 0x40000000); | ||
| 32 | try expect(divFloor(i32, 0, -0x80000000) == 0); | ||
| 33 | try expect(divFloor(i32, -0x40000001, 0x40000000) == -2); | ||
| 34 | try expect(divFloor(i32, -0x80000000, 1) == -0x80000000); | ||
| 35 | try expect(divFloor(i32, 10, 12) == 0); | ||
| 36 | try expect(divFloor(i32, -14, 12) == -2); | ||
| 37 | try expect(divFloor(i32, -2, 12) == -1); | ||
| 38 | |||
| 39 | try expect(divTrunc(i32, 5, 3) == 1); | ||
| 40 | try expect(divTrunc(i32, -5, 3) == -1); | ||
| 41 | try expect(divTrunc(f16, 5.0, 3.0) == 1.0); | ||
| 42 | try expect(divTrunc(f16, -5.0, 3.0) == -1.0); | ||
| 43 | try expect(divTrunc(f32, 5.0, 3.0) == 1.0); | ||
| 44 | try expect(divTrunc(f32, -5.0, 3.0) == -1.0); | ||
| 45 | try expect(divTrunc(f64, 5.0, 3.0) == 1.0); | ||
| 46 | try expect(divTrunc(f64, -5.0, 3.0) == -1.0); | ||
| 47 | try expect(divTrunc(i32, 10, 12) == 0); | ||
| 48 | try expect(divTrunc(i32, -14, 12) == -1); | ||
| 49 | try expect(divTrunc(i32, -2, 12) == 0); | ||
| 50 | |||
| 51 | try expect(mod(i32, 10, 12) == 10); | ||
| 52 | try expect(mod(i32, -14, 12) == 10); | ||
| 53 | try expect(mod(i32, -2, 12) == 10); | ||
| 54 | |||
| 55 | comptime { | ||
| 56 | try expect( | ||
| 57 | 1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600, | ||
| 58 | ); | ||
| 59 | try expect( | ||
| 60 | @rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600, | ||
| 61 | ); | ||
| 62 | try expect( | ||
| 63 | 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2, | ||
| 64 | ); | ||
| 65 | try expect( | ||
| 66 | @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2, | ||
| 67 | ); | ||
| 68 | try expect( | ||
| 69 | @divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2, | ||
| 70 | ); | ||
| 71 | try expect( | ||
| 72 | @divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2, | ||
| 73 | ); | ||
| 74 | try expect( | ||
| 75 | 4126227191251978491697987544882340798050766755606969681711 % 10 == 1, | ||
| 76 | ); | ||
| 77 | } | ||
| 78 | } | ||
| 79 | fn div(comptime T: type, a: T, b: T) T { | ||
| 80 | return a / b; | ||
| 81 | } | ||
| 82 | fn divExact(comptime T: type, a: T, b: T) T { | ||
| 83 | return @divExact(a, b); | ||
| 84 | } | ||
| 85 | fn divFloor(comptime T: type, a: T, b: T) T { | ||
| 86 | return @divFloor(a, b); | ||
| 87 | } | ||
| 88 | fn divTrunc(comptime T: type, a: T, b: T) T { | ||
| 89 | return @divTrunc(a, b); | ||
| 90 | } | ||
| 91 | fn mod(comptime T: type, a: T, b: T) T { | ||
| 92 | return @mod(a, b); | ||
| 93 | } | ||
| 94 | |||
| 95 | test "@addWithOverflow" { | ||
| 96 | var result: u8 = undefined; | ||
| 97 | try expect(@addWithOverflow(u8, 250, 100, &result)); | ||
| 98 | try expect(!@addWithOverflow(u8, 100, 150, &result)); | ||
| 99 | try expect(result == 250); | ||
| 100 | } | ||
| 101 | |||
| 102 | // TODO test mulWithOverflow | ||
| 103 | // TODO test subWithOverflow | ||
| 104 | |||
| 105 | test "@shlWithOverflow" { | ||
| 106 | var result: u16 = undefined; | ||
| 107 | try expect(@shlWithOverflow(u16, 0b0010111111111111, 3, &result)); | ||
| 108 | try expect(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result)); | ||
| 109 | try expect(result == 0b1011111111111100); | ||
| 110 | } | ||
| 111 | |||
| 112 | test "@*WithOverflow with u0 values" { | ||
| 113 | var result: u0 = undefined; | ||
| 114 | try expect(!@addWithOverflow(u0, 0, 0, &result)); | ||
| 115 | try expect(!@subWithOverflow(u0, 0, 0, &result)); | ||
| 116 | try expect(!@mulWithOverflow(u0, 0, 0, &result)); | ||
| 117 | try expect(!@shlWithOverflow(u0, 0, 0, &result)); | ||
| 118 | } | ||
| 119 | |||
| 120 | test "@clz" { | ||
| 121 | try testClz(); | ||
| 122 | comptime try testClz(); | ||
| 123 | } | ||
| 124 | |||
| 125 | fn testClz() !void { | ||
| 126 | try expect(@clz(u8, 0b10001010) == 0); | ||
| 127 | try expect(@clz(u8, 0b00001010) == 4); | ||
| 128 | try expect(@clz(u8, 0b00011010) == 3); | ||
| 129 | try expect(@clz(u8, 0b00000000) == 8); | ||
| 130 | try expect(@clz(u128, 0xffffffffffffffff) == 64); | ||
| 131 | try expect(@clz(u128, 0x10000000000000000) == 63); | ||
| 132 | } | ||
| 133 | |||
| 134 | test "@clz vectors" { | ||
| 135 | try testClzVectors(); | ||
| 136 | comptime try testClzVectors(); | ||
| 137 | } | ||
| 138 | |||
| 139 | fn testClzVectors() !void { | ||
| 140 | @setEvalBranchQuota(10_000); | ||
| 141 | try expectEqual(@clz(u8, @splat(64, @as(u8, 0b10001010))), @splat(64, @as(u4, 0))); | ||
| 142 | try expectEqual(@clz(u8, @splat(64, @as(u8, 0b00001010))), @splat(64, @as(u4, 4))); | ||
| 143 | try expectEqual(@clz(u8, @splat(64, @as(u8, 0b00011010))), @splat(64, @as(u4, 3))); | ||
| 144 | try expectEqual(@clz(u8, @splat(64, @as(u8, 0b00000000))), @splat(64, @as(u4, 8))); | ||
| 145 | try expectEqual(@clz(u128, @splat(64, @as(u128, 0xffffffffffffffff))), @splat(64, @as(u8, 64))); | ||
| 146 | try expectEqual(@clz(u128, @splat(64, @as(u128, 0x10000000000000000))), @splat(64, @as(u8, 63))); | ||
| 147 | } | ||
| 148 | |||
| 149 | test "@ctz" { | ||
| 150 | try testCtz(); | ||
| 151 | comptime try testCtz(); | ||
| 152 | } | ||
| 153 | |||
| 154 | fn testCtz() !void { | ||
| 155 | try expect(@ctz(u8, 0b10100000) == 5); | ||
| 156 | try expect(@ctz(u8, 0b10001010) == 1); | ||
| 157 | try expect(@ctz(u8, 0b00000000) == 8); | ||
| 158 | try expect(@ctz(u16, 0b00000000) == 16); | ||
| 159 | } | ||
| 160 | |||
| 161 | test "@ctz vectors" { | ||
| 162 | try testClzVectors(); | ||
| 163 | comptime try testClzVectors(); | ||
| 164 | } | ||
| 165 | |||
| 166 | fn testCtzVectors() !void { | ||
| 167 | @setEvalBranchQuota(10_000); | ||
| 168 | try expectEqual(@ctz(u8, @splat(64, @as(u8, 0b10100000))), @splat(64, @as(u4, 5))); | ||
| 169 | try expectEqual(@ctz(u8, @splat(64, @as(u8, 0b10001010))), @splat(64, @as(u4, 1))); | ||
| 170 | try expectEqual(@ctz(u8, @splat(64, @as(u8, 0b00000000))), @splat(64, @as(u4, 8))); | ||
| 171 | try expectEqual(@ctz(u16, @splat(64, @as(u16, 0b00000000))), @splat(64, @as(u5, 16))); | ||
| 172 | } | ||
| 173 | |||
| 174 | test "assignment operators" { | 9 | test "assignment operators" { |
| 175 | var i: u32 = 0; | 10 | var i: u32 = 0; |
| 176 | i += 5; | 11 | i += 5; |
| ... | @@ -218,686 +53,3 @@ fn testThreeExprInARow(f: bool, t: bool) !void { | ... | @@ -218,686 +53,3 @@ fn testThreeExprInARow(f: bool, t: bool) !void { |
| 218 | fn assertFalse(b: bool) !void { | 53 | fn assertFalse(b: bool) !void { |
| 219 | try expect(!b); | 54 | try expect(!b); |
| 220 | } | 55 | } |
| 221 | |||
| 222 | test "const number literal" { | ||
| 223 | const one = 1; | ||
| 224 | const eleven = ten + one; | ||
| 225 | |||
| 226 | try expect(eleven == 11); | ||
| 227 | } | ||
| 228 | const ten = 10; | ||
| 229 | |||
| 230 | test "unsigned wrapping" { | ||
| 231 | try testUnsignedWrappingEval(maxInt(u32)); | ||
| 232 | comptime try testUnsignedWrappingEval(maxInt(u32)); | ||
| 233 | } | ||
| 234 | fn testUnsignedWrappingEval(x: u32) !void { | ||
| 235 | const zero = x +% 1; | ||
| 236 | try expect(zero == 0); | ||
| 237 | const orig = zero -% 1; | ||
| 238 | try expect(orig == maxInt(u32)); | ||
| 239 | } | ||
| 240 | |||
| 241 | test "signed wrapping" { | ||
| 242 | try testSignedWrappingEval(maxInt(i32)); | ||
| 243 | comptime try testSignedWrappingEval(maxInt(i32)); | ||
| 244 | } | ||
| 245 | fn testSignedWrappingEval(x: i32) !void { | ||
| 246 | const min_val = x +% 1; | ||
| 247 | try expect(min_val == minInt(i32)); | ||
| 248 | const max_val = min_val -% 1; | ||
| 249 | try expect(max_val == maxInt(i32)); | ||
| 250 | } | ||
| 251 | |||
| 252 | test "signed negation wrapping" { | ||
| 253 | try testSignedNegationWrappingEval(minInt(i16)); | ||
| 254 | comptime try testSignedNegationWrappingEval(minInt(i16)); | ||
| 255 | } | ||
| 256 | fn testSignedNegationWrappingEval(x: i16) !void { | ||
| 257 | try expect(x == -32768); | ||
| 258 | const neg = -%x; | ||
| 259 | try expect(neg == -32768); | ||
| 260 | } | ||
| 261 | |||
| 262 | test "unsigned negation wrapping" { | ||
| 263 | try testUnsignedNegationWrappingEval(1); | ||
| 264 | comptime try testUnsignedNegationWrappingEval(1); | ||
| 265 | } | ||
| 266 | fn testUnsignedNegationWrappingEval(x: u16) !void { | ||
| 267 | try expect(x == 1); | ||
| 268 | const neg = -%x; | ||
| 269 | try expect(neg == maxInt(u16)); | ||
| 270 | } | ||
| 271 | |||
| 272 | test "unsigned 64-bit division" { | ||
| 273 | try test_u64_div(); | ||
| 274 | comptime try test_u64_div(); | ||
| 275 | } | ||
| 276 | fn test_u64_div() !void { | ||
| 277 | const result = divWithResult(1152921504606846976, 34359738365); | ||
| 278 | try expect(result.quotient == 33554432); | ||
| 279 | try expect(result.remainder == 100663296); | ||
| 280 | } | ||
| 281 | fn divWithResult(a: u64, b: u64) DivResult { | ||
| 282 | return DivResult{ | ||
| 283 | .quotient = a / b, | ||
| 284 | .remainder = a % b, | ||
| 285 | }; | ||
| 286 | } | ||
| 287 | const DivResult = struct { | ||
| 288 | quotient: u64, | ||
| 289 | remainder: u64, | ||
| 290 | }; | ||
| 291 | |||
| 292 | test "binary not" { | ||
| 293 | try expect(comptime x: { | ||
| 294 | break :x ~@as(u16, 0b1010101010101010) == 0b0101010101010101; | ||
| 295 | }); | ||
| 296 | try expect(comptime x: { | ||
| 297 | break :x ~@as(u64, 2147483647) == 18446744071562067968; | ||
| 298 | }); | ||
| 299 | try testBinaryNot(0b1010101010101010); | ||
| 300 | } | ||
| 301 | |||
| 302 | fn testBinaryNot(x: u16) !void { | ||
| 303 | try expect(~x == 0b0101010101010101); | ||
| 304 | } | ||
| 305 | |||
| 306 | test "small int addition" { | ||
| 307 | var x: u2 = 0; | ||
| 308 | try expect(x == 0); | ||
| 309 | |||
| 310 | x += 1; | ||
| 311 | try expect(x == 1); | ||
| 312 | |||
| 313 | x += 1; | ||
| 314 | try expect(x == 2); | ||
| 315 | |||
| 316 | x += 1; | ||
| 317 | try expect(x == 3); | ||
| 318 | |||
| 319 | var result: @TypeOf(x) = 3; | ||
| 320 | try expect(@addWithOverflow(@TypeOf(x), x, 1, &result)); | ||
| 321 | |||
| 322 | try expect(result == 0); | ||
| 323 | } | ||
| 324 | |||
| 325 | test "float equality" { | ||
| 326 | const x: f64 = 0.012; | ||
| 327 | const y: f64 = x + 1.0; | ||
| 328 | |||
| 329 | try testFloatEqualityImpl(x, y); | ||
| 330 | comptime try testFloatEqualityImpl(x, y); | ||
| 331 | } | ||
| 332 | |||
| 333 | fn testFloatEqualityImpl(x: f64, y: f64) !void { | ||
| 334 | const y2 = x + 1.0; | ||
| 335 | try expect(y == y2); | ||
| 336 | } | ||
| 337 | |||
| 338 | test "allow signed integer division/remainder when values are comptime known and positive or exact" { | ||
| 339 | try expect(5 / 3 == 1); | ||
| 340 | try expect(-5 / -3 == 1); | ||
| 341 | try expect(-6 / 3 == -2); | ||
| 342 | |||
| 343 | try expect(5 % 3 == 2); | ||
| 344 | try expect(-6 % 3 == 0); | ||
| 345 | } | ||
| 346 | |||
| 347 | test "hex float literal parsing" { | ||
| 348 | comptime try expect(0x1.0 == 1.0); | ||
| 349 | } | ||
| 350 | |||
| 351 | test "quad hex float literal parsing in range" { | ||
| 352 | const a = 0x1.af23456789bbaaab347645365cdep+5; | ||
| 353 | const b = 0x1.dedafcff354b6ae9758763545432p-9; | ||
| 354 | const c = 0x1.2f34dd5f437e849b4baab754cdefp+4534; | ||
| 355 | const d = 0x1.edcbff8ad76ab5bf46463233214fp-435; | ||
| 356 | if (false) { | ||
| 357 | a; | ||
| 358 | b; | ||
| 359 | c; | ||
| 360 | d; | ||
| 361 | } | ||
| 362 | } | ||
| 363 | |||
| 364 | test "quad hex float literal parsing accurate" { | ||
| 365 | const a: f128 = 0x1.1111222233334444555566667777p+0; | ||
| 366 | |||
| 367 | // implied 1 is dropped, with an exponent of 0 (0x3fff) after biasing. | ||
| 368 | const expected: u128 = 0x3fff1111222233334444555566667777; | ||
| 369 | try expect(@bitCast(u128, a) == expected); | ||
| 370 | |||
| 371 | // non-normalized | ||
| 372 | const b: f128 = 0x11.111222233334444555566667777p-4; | ||
| 373 | try expect(@bitCast(u128, b) == expected); | ||
| 374 | |||
| 375 | const S = struct { | ||
| 376 | fn doTheTest() !void { | ||
| 377 | { | ||
| 378 | var f: f128 = 0x1.2eab345678439abcdefea56782346p+5; | ||
| 379 | try expect(@bitCast(u128, f) == 0x40042eab345678439abcdefea5678234); | ||
| 380 | } | ||
| 381 | { | ||
| 382 | var f: f128 = 0x1.edcb34a235253948765432134674fp-1; | ||
| 383 | try expect(@bitCast(u128, f) == 0x3ffeedcb34a235253948765432134674); | ||
| 384 | } | ||
| 385 | { | ||
| 386 | var f: f128 = 0x1.353e45674d89abacc3a2ebf3ff4ffp-50; | ||
| 387 | try expect(@bitCast(u128, f) == 0x3fcd353e45674d89abacc3a2ebf3ff50); | ||
| 388 | } | ||
| 389 | { | ||
| 390 | var f: f128 = 0x1.ed8764648369535adf4be3214567fp-9; | ||
| 391 | try expect(@bitCast(u128, f) == 0x3ff6ed8764648369535adf4be3214568); | ||
| 392 | } | ||
| 393 | const exp2ft = [_]f64{ | ||
| 394 | 0x1.6a09e667f3bcdp-1, | ||
| 395 | 0x1.7a11473eb0187p-1, | ||
| 396 | 0x1.8ace5422aa0dbp-1, | ||
| 397 | 0x1.9c49182a3f090p-1, | ||
| 398 | 0x1.ae89f995ad3adp-1, | ||
| 399 | 0x1.c199bdd85529cp-1, | ||
| 400 | 0x1.d5818dcfba487p-1, | ||
| 401 | 0x1.ea4afa2a490dap-1, | ||
| 402 | 0x1.0000000000000p+0, | ||
| 403 | 0x1.0b5586cf9890fp+0, | ||
| 404 | 0x1.172b83c7d517bp+0, | ||
| 405 | 0x1.2387a6e756238p+0, | ||
| 406 | 0x1.306fe0a31b715p+0, | ||
| 407 | 0x1.3dea64c123422p+0, | ||
| 408 | 0x1.4bfdad5362a27p+0, | ||
| 409 | 0x1.5ab07dd485429p+0, | ||
| 410 | 0x1.8p23, | ||
| 411 | 0x1.62e430p-1, | ||
| 412 | 0x1.ebfbe0p-3, | ||
| 413 | 0x1.c6b348p-5, | ||
| 414 | 0x1.3b2c9cp-7, | ||
| 415 | 0x1.0p127, | ||
| 416 | -0x1.0p-149, | ||
| 417 | }; | ||
| 418 | |||
| 419 | const answers = [_]u64{ | ||
| 420 | 0x3fe6a09e667f3bcd, | ||
| 421 | 0x3fe7a11473eb0187, | ||
| 422 | 0x3fe8ace5422aa0db, | ||
| 423 | 0x3fe9c49182a3f090, | ||
| 424 | 0x3feae89f995ad3ad, | ||
| 425 | 0x3fec199bdd85529c, | ||
| 426 | 0x3fed5818dcfba487, | ||
| 427 | 0x3feea4afa2a490da, | ||
| 428 | 0x3ff0000000000000, | ||
| 429 | 0x3ff0b5586cf9890f, | ||
| 430 | 0x3ff172b83c7d517b, | ||
| 431 | 0x3ff2387a6e756238, | ||
| 432 | 0x3ff306fe0a31b715, | ||
| 433 | 0x3ff3dea64c123422, | ||
| 434 | 0x3ff4bfdad5362a27, | ||
| 435 | 0x3ff5ab07dd485429, | ||
| 436 | 0x4168000000000000, | ||
| 437 | 0x3fe62e4300000000, | ||
| 438 | 0x3fcebfbe00000000, | ||
| 439 | 0x3fac6b3480000000, | ||
| 440 | 0x3f83b2c9c0000000, | ||
| 441 | 0x47e0000000000000, | ||
| 442 | 0xb6a0000000000000, | ||
| 443 | }; | ||
| 444 | |||
| 445 | for (exp2ft) |x, i| { | ||
| 446 | try expect(@bitCast(u64, x) == answers[i]); | ||
| 447 | } | ||
| 448 | } | ||
| 449 | }; | ||
| 450 | try S.doTheTest(); | ||
| 451 | comptime try S.doTheTest(); | ||
| 452 | } | ||
| 453 | |||
| 454 | test "underscore separator parsing" { | ||
| 455 | try expect(0_0_0_0 == 0); | ||
| 456 | try expect(1_234_567 == 1234567); | ||
| 457 | try expect(001_234_567 == 1234567); | ||
| 458 | try expect(0_0_1_2_3_4_5_6_7 == 1234567); | ||
| 459 | |||
| 460 | try expect(0b0_0_0_0 == 0); | ||
| 461 | try expect(0b1010_1010 == 0b10101010); | ||
| 462 | try expect(0b0000_1010_1010 == 0b10101010); | ||
| 463 | try expect(0b1_0_1_0_1_0_1_0 == 0b10101010); | ||
| 464 | |||
| 465 | try expect(0o0_0_0_0 == 0); | ||
| 466 | try expect(0o1010_1010 == 0o10101010); | ||
| 467 | try expect(0o0000_1010_1010 == 0o10101010); | ||
| 468 | try expect(0o1_0_1_0_1_0_1_0 == 0o10101010); | ||
| 469 | |||
| 470 | try expect(0x0_0_0_0 == 0); | ||
| 471 | try expect(0x1010_1010 == 0x10101010); | ||
| 472 | try expect(0x0000_1010_1010 == 0x10101010); | ||
| 473 | try expect(0x1_0_1_0_1_0_1_0 == 0x10101010); | ||
| 474 | |||
| 475 | try expect(123_456.789_000e1_0 == 123456.789000e10); | ||
| 476 | try expect(0_1_2_3_4_5_6.7_8_9_0_0_0e0_0_1_0 == 123456.789000e10); | ||
| 477 | |||
| 478 | try expect(0x1234_5678.9ABC_DEF0p-1_0 == 0x12345678.9ABCDEF0p-10); | ||
| 479 | try expect(0x1_2_3_4_5_6_7_8.9_A_B_C_D_E_F_0p-0_0_0_1_0 == 0x12345678.9ABCDEF0p-10); | ||
| 480 | } | ||
| 481 | |||
| 482 | test "hex float literal within range" { | ||
| 483 | const a = 0x1.0p16383; | ||
| 484 | const b = 0x0.1p16387; | ||
| 485 | const c = 0x1.0p-16382; | ||
| 486 | if (false) { | ||
| 487 | a; | ||
| 488 | b; | ||
| 489 | c; | ||
| 490 | } | ||
| 491 | } | ||
| 492 | |||
| 493 | test "truncating shift left" { | ||
| 494 | try testShlTrunc(maxInt(u16)); | ||
| 495 | comptime try testShlTrunc(maxInt(u16)); | ||
| 496 | } | ||
| 497 | fn testShlTrunc(x: u16) !void { | ||
| 498 | const shifted = x << 1; | ||
| 499 | try expect(shifted == 65534); | ||
| 500 | } | ||
| 501 | |||
| 502 | test "truncating shift right" { | ||
| 503 | try testShrTrunc(maxInt(u16)); | ||
| 504 | comptime try testShrTrunc(maxInt(u16)); | ||
| 505 | } | ||
| 506 | fn testShrTrunc(x: u16) !void { | ||
| 507 | const shifted = x >> 1; | ||
| 508 | try expect(shifted == 32767); | ||
| 509 | } | ||
| 510 | |||
| 511 | test "exact shift left" { | ||
| 512 | try testShlExact(0b00110101); | ||
| 513 | comptime try testShlExact(0b00110101); | ||
| 514 | } | ||
| 515 | fn testShlExact(x: u8) !void { | ||
| 516 | const shifted = @shlExact(x, 2); | ||
| 517 | try expect(shifted == 0b11010100); | ||
| 518 | } | ||
| 519 | |||
| 520 | test "exact shift right" { | ||
| 521 | try testShrExact(0b10110100); | ||
| 522 | comptime try testShrExact(0b10110100); | ||
| 523 | } | ||
| 524 | fn testShrExact(x: u8) !void { | ||
| 525 | const shifted = @shrExact(x, 2); | ||
| 526 | try expect(shifted == 0b00101101); | ||
| 527 | } | ||
| 528 | |||
| 529 | test "shift left/right on u0 operand" { | ||
| 530 | const S = struct { | ||
| 531 | fn doTheTest() !void { | ||
| 532 | var x: u0 = 0; | ||
| 533 | var y: u0 = 0; | ||
| 534 | try expectEqual(@as(u0, 0), x << 0); | ||
| 535 | try expectEqual(@as(u0, 0), x >> 0); | ||
| 536 | try expectEqual(@as(u0, 0), x << y); | ||
| 537 | try expectEqual(@as(u0, 0), x >> y); | ||
| 538 | try expectEqual(@as(u0, 0), @shlExact(x, 0)); | ||
| 539 | try expectEqual(@as(u0, 0), @shrExact(x, 0)); | ||
| 540 | try expectEqual(@as(u0, 0), @shlExact(x, y)); | ||
| 541 | try expectEqual(@as(u0, 0), @shrExact(x, y)); | ||
| 542 | } | ||
| 543 | }; | ||
| 544 | try S.doTheTest(); | ||
| 545 | comptime try S.doTheTest(); | ||
| 546 | } | ||
| 547 | |||
| 548 | test "comptime_int addition" { | ||
| 549 | comptime { | ||
| 550 | try expect(35361831660712422535336160538497375248 + 101752735581729509668353361206450473702 == 137114567242441932203689521744947848950); | ||
| 551 | try expect(594491908217841670578297176641415611445982232488944558774612 + 390603545391089362063884922208143568023166603618446395589768 == 985095453608931032642182098849559179469148836107390954364380); | ||
| 552 | } | ||
| 553 | } | ||
| 554 | |||
| 555 | test "comptime_int multiplication" { | ||
| 556 | comptime { | ||
| 557 | try expect( | ||
| 558 | 45960427431263824329884196484953148229 * 128339149605334697009938835852565949723 == 5898522172026096622534201617172456926982464453350084962781392314016180490567, | ||
| 559 | ); | ||
| 560 | try expect( | ||
| 561 | 594491908217841670578297176641415611445982232488944558774612 * 390603545391089362063884922208143568023166603618446395589768 == 232210647056203049913662402532976186578842425262306016094292237500303028346593132411865381225871291702600263463125370016, | ||
| 562 | ); | ||
| 563 | } | ||
| 564 | } | ||
| 565 | |||
| 566 | test "comptime_int shifting" { | ||
| 567 | comptime { | ||
| 568 | try expect((@as(u128, 1) << 127) == 0x80000000000000000000000000000000); | ||
| 569 | } | ||
| 570 | } | ||
| 571 | |||
| 572 | test "comptime_int multi-limb shift and mask" { | ||
| 573 | comptime { | ||
| 574 | var a = 0xefffffffa0000001eeeeeeefaaaaaaab; | ||
| 575 | |||
| 576 | try expect(@as(u32, a & 0xffffffff) == 0xaaaaaaab); | ||
| 577 | a >>= 32; | ||
| 578 | try expect(@as(u32, a & 0xffffffff) == 0xeeeeeeef); | ||
| 579 | a >>= 32; | ||
| 580 | try expect(@as(u32, a & 0xffffffff) == 0xa0000001); | ||
| 581 | a >>= 32; | ||
| 582 | try expect(@as(u32, a & 0xffffffff) == 0xefffffff); | ||
| 583 | a >>= 32; | ||
| 584 | |||
| 585 | try expect(a == 0); | ||
| 586 | } | ||
| 587 | } | ||
| 588 | |||
| 589 | test "comptime_int multi-limb partial shift right" { | ||
| 590 | comptime { | ||
| 591 | var a = 0x1ffffffffeeeeeeee; | ||
| 592 | a >>= 16; | ||
| 593 | try expect(a == 0x1ffffffffeeee); | ||
| 594 | } | ||
| 595 | } | ||
| 596 | |||
| 597 | test "xor" { | ||
| 598 | try test_xor(); | ||
| 599 | comptime try test_xor(); | ||
| 600 | } | ||
| 601 | |||
| 602 | fn test_xor() !void { | ||
| 603 | try expect(0xFF ^ 0x00 == 0xFF); | ||
| 604 | try expect(0xF0 ^ 0x0F == 0xFF); | ||
| 605 | try expect(0xFF ^ 0xF0 == 0x0F); | ||
| 606 | try expect(0xFF ^ 0x0F == 0xF0); | ||
| 607 | try expect(0xFF ^ 0xFF == 0x00); | ||
| 608 | } | ||
| 609 | |||
| 610 | test "comptime_int xor" { | ||
| 611 | comptime { | ||
| 612 | try expect(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0x00000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); | ||
| 613 | try expect(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0x0000000000000000FFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); | ||
| 614 | try expect(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x0000000000000000FFFFFFFFFFFFFFFF); | ||
| 615 | try expect(0x0000000000000000FFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFF0000000000000000); | ||
| 616 | try expect(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000000000000000000000000000); | ||
| 617 | try expect(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0x00000000FFFFFFFF00000000FFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); | ||
| 618 | try expect(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000FFFFFFFF00000000FFFFFFFF); | ||
| 619 | try expect(0x00000000FFFFFFFF00000000FFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFF00000000FFFFFFFF00000000); | ||
| 620 | } | ||
| 621 | } | ||
| 622 | |||
| 623 | test "f128" { | ||
| 624 | try test_f128(); | ||
| 625 | comptime try test_f128(); | ||
| 626 | } | ||
| 627 | |||
| 628 | fn make_f128(x: f128) f128 { | ||
| 629 | return x; | ||
| 630 | } | ||
| 631 | |||
| 632 | fn test_f128() !void { | ||
| 633 | try expect(@sizeOf(f128) == 16); | ||
| 634 | try expect(make_f128(1.0) == 1.0); | ||
| 635 | try expect(make_f128(1.0) != 1.1); | ||
| 636 | try expect(make_f128(1.0) > 0.9); | ||
| 637 | try expect(make_f128(1.0) >= 0.9); | ||
| 638 | try expect(make_f128(1.0) >= 1.0); | ||
| 639 | try should_not_be_zero(1.0); | ||
| 640 | } | ||
| 641 | |||
| 642 | fn should_not_be_zero(x: f128) !void { | ||
| 643 | try expect(x != 0.0); | ||
| 644 | } | ||
| 645 | |||
| 646 | test "comptime float rem int" { | ||
| 647 | comptime { | ||
| 648 | var x = @as(f32, 1) % 2; | ||
| 649 | try expect(x == 1.0); | ||
| 650 | } | ||
| 651 | } | ||
| 652 | |||
| 653 | test "remainder division" { | ||
| 654 | comptime try remdiv(f16); | ||
| 655 | comptime try remdiv(f32); | ||
| 656 | comptime try remdiv(f64); | ||
| 657 | comptime try remdiv(f128); | ||
| 658 | try remdiv(f16); | ||
| 659 | try remdiv(f64); | ||
| 660 | try remdiv(f128); | ||
| 661 | } | ||
| 662 | |||
| 663 | fn remdiv(comptime T: type) !void { | ||
| 664 | try expect(@as(T, 1) == @as(T, 1) % @as(T, 2)); | ||
| 665 | try expect(@as(T, 1) == @as(T, 7) % @as(T, 3)); | ||
| 666 | } | ||
| 667 | |||
| 668 | test "@sqrt" { | ||
| 669 | try testSqrt(f64, 12.0); | ||
| 670 | comptime try testSqrt(f64, 12.0); | ||
| 671 | try testSqrt(f32, 13.0); | ||
| 672 | comptime try testSqrt(f32, 13.0); | ||
| 673 | try testSqrt(f16, 13.0); | ||
| 674 | comptime try testSqrt(f16, 13.0); | ||
| 675 | |||
| 676 | const x = 14.0; | ||
| 677 | const y = x * x; | ||
| 678 | const z = @sqrt(y); | ||
| 679 | comptime try expect(z == x); | ||
| 680 | } | ||
| 681 | |||
| 682 | fn testSqrt(comptime T: type, x: T) !void { | ||
| 683 | try expect(@sqrt(x * x) == x); | ||
| 684 | } | ||
| 685 | |||
| 686 | test "@fabs" { | ||
| 687 | try testFabs(f128, 12.0); | ||
| 688 | comptime try testFabs(f128, 12.0); | ||
| 689 | try testFabs(f64, 12.0); | ||
| 690 | comptime try testFabs(f64, 12.0); | ||
| 691 | try testFabs(f32, 12.0); | ||
| 692 | comptime try testFabs(f32, 12.0); | ||
| 693 | try testFabs(f16, 12.0); | ||
| 694 | comptime try testFabs(f16, 12.0); | ||
| 695 | |||
| 696 | const x = 14.0; | ||
| 697 | const y = -x; | ||
| 698 | const z = @fabs(y); | ||
| 699 | comptime try expectEqual(x, z); | ||
| 700 | } | ||
| 701 | |||
| 702 | fn testFabs(comptime T: type, x: T) !void { | ||
| 703 | const y = -x; | ||
| 704 | const z = @fabs(y); | ||
| 705 | try expectEqual(x, z); | ||
| 706 | } | ||
| 707 | |||
| 708 | test "@floor" { | ||
| 709 | // FIXME: Generates a floorl function call | ||
| 710 | // testFloor(f128, 12.0); | ||
| 711 | comptime try testFloor(f128, 12.0); | ||
| 712 | try testFloor(f64, 12.0); | ||
| 713 | comptime try testFloor(f64, 12.0); | ||
| 714 | try testFloor(f32, 12.0); | ||
| 715 | comptime try testFloor(f32, 12.0); | ||
| 716 | try testFloor(f16, 12.0); | ||
| 717 | comptime try testFloor(f16, 12.0); | ||
| 718 | |||
| 719 | const x = 14.0; | ||
| 720 | const y = x + 0.7; | ||
| 721 | const z = @floor(y); | ||
| 722 | comptime try expectEqual(x, z); | ||
| 723 | } | ||
| 724 | |||
| 725 | fn testFloor(comptime T: type, x: T) !void { | ||
| 726 | const y = x + 0.6; | ||
| 727 | const z = @floor(y); | ||
| 728 | try expectEqual(x, z); | ||
| 729 | } | ||
| 730 | |||
| 731 | test "@ceil" { | ||
| 732 | // FIXME: Generates a ceill function call | ||
| 733 | //testCeil(f128, 12.0); | ||
| 734 | comptime try testCeil(f128, 12.0); | ||
| 735 | try testCeil(f64, 12.0); | ||
| 736 | comptime try testCeil(f64, 12.0); | ||
| 737 | try testCeil(f32, 12.0); | ||
| 738 | comptime try testCeil(f32, 12.0); | ||
| 739 | try testCeil(f16, 12.0); | ||
| 740 | comptime try testCeil(f16, 12.0); | ||
| 741 | |||
| 742 | const x = 14.0; | ||
| 743 | const y = x - 0.7; | ||
| 744 | const z = @ceil(y); | ||
| 745 | comptime try expectEqual(x, z); | ||
| 746 | } | ||
| 747 | |||
| 748 | fn testCeil(comptime T: type, x: T) !void { | ||
| 749 | const y = x - 0.8; | ||
| 750 | const z = @ceil(y); | ||
| 751 | try expectEqual(x, z); | ||
| 752 | } | ||
| 753 | |||
| 754 | test "@trunc" { | ||
| 755 | // FIXME: Generates a truncl function call | ||
| 756 | //testTrunc(f128, 12.0); | ||
| 757 | comptime try testTrunc(f128, 12.0); | ||
| 758 | try testTrunc(f64, 12.0); | ||
| 759 | comptime try testTrunc(f64, 12.0); | ||
| 760 | try testTrunc(f32, 12.0); | ||
| 761 | comptime try testTrunc(f32, 12.0); | ||
| 762 | try testTrunc(f16, 12.0); | ||
| 763 | comptime try testTrunc(f16, 12.0); | ||
| 764 | |||
| 765 | const x = 14.0; | ||
| 766 | const y = x + 0.7; | ||
| 767 | const z = @trunc(y); | ||
| 768 | comptime try expectEqual(x, z); | ||
| 769 | } | ||
| 770 | |||
| 771 | fn testTrunc(comptime T: type, x: T) !void { | ||
| 772 | { | ||
| 773 | const y = x + 0.8; | ||
| 774 | const z = @trunc(y); | ||
| 775 | try expectEqual(x, z); | ||
| 776 | } | ||
| 777 | |||
| 778 | { | ||
| 779 | const y = -x - 0.8; | ||
| 780 | const z = @trunc(y); | ||
| 781 | try expectEqual(-x, z); | ||
| 782 | } | ||
| 783 | } | ||
| 784 | |||
| 785 | test "@round" { | ||
| 786 | // FIXME: Generates a roundl function call | ||
| 787 | //testRound(f128, 12.0); | ||
| 788 | comptime try testRound(f128, 12.0); | ||
| 789 | try testRound(f64, 12.0); | ||
| 790 | comptime try testRound(f64, 12.0); | ||
| 791 | try testRound(f32, 12.0); | ||
| 792 | comptime try testRound(f32, 12.0); | ||
| 793 | try testRound(f16, 12.0); | ||
| 794 | comptime try testRound(f16, 12.0); | ||
| 795 | |||
| 796 | const x = 14.0; | ||
| 797 | const y = x + 0.4; | ||
| 798 | const z = @round(y); | ||
| 799 | comptime try expectEqual(x, z); | ||
| 800 | } | ||
| 801 | |||
| 802 | fn testRound(comptime T: type, x: T) !void { | ||
| 803 | const y = x - 0.5; | ||
| 804 | const z = @round(y); | ||
| 805 | try expectEqual(x, z); | ||
| 806 | } | ||
| 807 | |||
| 808 | test "comptime_int param and return" { | ||
| 809 | const a = comptimeAdd(35361831660712422535336160538497375248, 101752735581729509668353361206450473702); | ||
| 810 | try expect(a == 137114567242441932203689521744947848950); | ||
| 811 | |||
| 812 | const b = comptimeAdd(594491908217841670578297176641415611445982232488944558774612, 390603545391089362063884922208143568023166603618446395589768); | ||
| 813 | try expect(b == 985095453608931032642182098849559179469148836107390954364380); | ||
| 814 | } | ||
| 815 | |||
| 816 | fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int { | ||
| 817 | return a + b; | ||
| 818 | } | ||
| 819 | |||
| 820 | test "vector integer addition" { | ||
| 821 | const S = struct { | ||
| 822 | fn doTheTest() !void { | ||
| 823 | var a: std.meta.Vector(4, i32) = [_]i32{ 1, 2, 3, 4 }; | ||
| 824 | var b: std.meta.Vector(4, i32) = [_]i32{ 5, 6, 7, 8 }; | ||
| 825 | var result = a + b; | ||
| 826 | var result_array: [4]i32 = result; | ||
| 827 | const expected = [_]i32{ 6, 8, 10, 12 }; | ||
| 828 | try expectEqualSlices(i32, &expected, &result_array); | ||
| 829 | } | ||
| 830 | }; | ||
| 831 | try S.doTheTest(); | ||
| 832 | comptime try S.doTheTest(); | ||
| 833 | } | ||
| 834 | |||
| 835 | test "NaN comparison" { | ||
| 836 | try testNanEqNan(f16); | ||
| 837 | try testNanEqNan(f32); | ||
| 838 | try testNanEqNan(f64); | ||
| 839 | try testNanEqNan(f128); | ||
| 840 | comptime try testNanEqNan(f16); | ||
| 841 | comptime try testNanEqNan(f32); | ||
| 842 | comptime try testNanEqNan(f64); | ||
| 843 | comptime try testNanEqNan(f128); | ||
| 844 | } | ||
| 845 | |||
| 846 | fn testNanEqNan(comptime F: type) !void { | ||
| 847 | var nan1 = std.math.nan(F); | ||
| 848 | var nan2 = std.math.nan(F); | ||
| 849 | try expect(nan1 != nan2); | ||
| 850 | try expect(!(nan1 == nan2)); | ||
| 851 | try expect(!(nan1 > nan2)); | ||
| 852 | try expect(!(nan1 >= nan2)); | ||
| 853 | try expect(!(nan1 < nan2)); | ||
| 854 | try expect(!(nan1 <= nan2)); | ||
| 855 | } | ||
| 856 | |||
| 857 | test "128-bit multiplication" { | ||
| 858 | var a: i128 = 3; | ||
| 859 | var b: i128 = 2; | ||
| 860 | var c = a * b; | ||
| 861 | try expect(c == 6); | ||
| 862 | } | ||
| 863 | |||
| 864 | test "vector comparison" { | ||
| 865 | const S = struct { | ||
| 866 | fn doTheTest() !void { | ||
| 867 | var a: std.meta.Vector(6, i32) = [_]i32{ 1, 3, -1, 5, 7, 9 }; | ||
| 868 | var b: std.meta.Vector(6, i32) = [_]i32{ -1, 3, 0, 6, 10, -10 }; | ||
| 869 | try expect(mem.eql(bool, &@as([6]bool, a < b), &[_]bool{ false, false, true, true, true, false })); | ||
| 870 | try expect(mem.eql(bool, &@as([6]bool, a <= b), &[_]bool{ false, true, true, true, true, false })); | ||
| 871 | try expect(mem.eql(bool, &@as([6]bool, a == b), &[_]bool{ false, true, false, false, false, false })); | ||
| 872 | try expect(mem.eql(bool, &@as([6]bool, a != b), &[_]bool{ true, false, true, true, true, true })); | ||
| 873 | try expect(mem.eql(bool, &@as([6]bool, a > b), &[_]bool{ true, false, false, false, false, true })); | ||
| 874 | try expect(mem.eql(bool, &@as([6]bool, a >= b), &[_]bool{ true, true, false, false, false, true })); | ||
| 875 | } | ||
| 876 | }; | ||
| 877 | try S.doTheTest(); | ||
| 878 | comptime try S.doTheTest(); | ||
| 879 | } | ||
| 880 | |||
| 881 | test "compare undefined literal with comptime_int" { | ||
| 882 | var x = undefined == 1; | ||
| 883 | // x is now undefined with type bool | ||
| 884 | x = true; | ||
| 885 | try expect(x); | ||
| 886 | } | ||
| 887 | |||
| 888 | test "signed zeros are represented properly" { | ||
| 889 | const S = struct { | ||
| 890 | fn doTheTest() !void { | ||
| 891 | inline for ([_]type{ f16, f32, f64, f128 }) |T| { | ||
| 892 | const ST = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); | ||
| 893 | var as_fp_val = -@as(T, 0.0); | ||
| 894 | var as_uint_val = @bitCast(ST, as_fp_val); | ||
| 895 | // Ensure the sign bit is set. | ||
| 896 | try expect(as_uint_val >> (@typeInfo(T).Float.bits - 1) == 1); | ||
| 897 | } | ||
| 898 | } | ||
| 899 | }; | ||
| 900 | |||
| 901 | try S.doTheTest(); | ||
| 902 | comptime try S.doTheTest(); | ||
| 903 | } |
test/behavior/math_stage1.zig created+855| ... | @@ -0,0 +1,855 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const expect = std.testing.expect; | ||
| 3 | const expectEqual = std.testing.expectEqual; | ||
| 4 | const expectEqualSlices = std.testing.expectEqualSlices; | ||
| 5 | const maxInt = std.math.maxInt; | ||
| 6 | const minInt = std.math.minInt; | ||
| 7 | const mem = std.mem; | ||
| 8 | |||
| 9 | test "division" { | ||
| 10 | try testDivision(); | ||
| 11 | comptime try testDivision(); | ||
| 12 | } | ||
| 13 | fn testDivision() !void { | ||
| 14 | try expect(div(u32, 13, 3) == 4); | ||
| 15 | try expect(div(f16, 1.0, 2.0) == 0.5); | ||
| 16 | try expect(div(f32, 1.0, 2.0) == 0.5); | ||
| 17 | |||
| 18 | try expect(divExact(u32, 55, 11) == 5); | ||
| 19 | try expect(divExact(i32, -55, 11) == -5); | ||
| 20 | try expect(divExact(f16, 55.0, 11.0) == 5.0); | ||
| 21 | try expect(divExact(f16, -55.0, 11.0) == -5.0); | ||
| 22 | try expect(divExact(f32, 55.0, 11.0) == 5.0); | ||
| 23 | try expect(divExact(f32, -55.0, 11.0) == -5.0); | ||
| 24 | |||
| 25 | try expect(divFloor(i32, 5, 3) == 1); | ||
| 26 | try expect(divFloor(i32, -5, 3) == -2); | ||
| 27 | try expect(divFloor(f16, 5.0, 3.0) == 1.0); | ||
| 28 | try expect(divFloor(f16, -5.0, 3.0) == -2.0); | ||
| 29 | try expect(divFloor(f32, 5.0, 3.0) == 1.0); | ||
| 30 | try expect(divFloor(f32, -5.0, 3.0) == -2.0); | ||
| 31 | try expect(divFloor(i32, -0x80000000, -2) == 0x40000000); | ||
| 32 | try expect(divFloor(i32, 0, -0x80000000) == 0); | ||
| 33 | try expect(divFloor(i32, -0x40000001, 0x40000000) == -2); | ||
| 34 | try expect(divFloor(i32, -0x80000000, 1) == -0x80000000); | ||
| 35 | try expect(divFloor(i32, 10, 12) == 0); | ||
| 36 | try expect(divFloor(i32, -14, 12) == -2); | ||
| 37 | try expect(divFloor(i32, -2, 12) == -1); | ||
| 38 | |||
| 39 | try expect(divTrunc(i32, 5, 3) == 1); | ||
| 40 | try expect(divTrunc(i32, -5, 3) == -1); | ||
| 41 | try expect(divTrunc(f16, 5.0, 3.0) == 1.0); | ||
| 42 | try expect(divTrunc(f16, -5.0, 3.0) == -1.0); | ||
| 43 | try expect(divTrunc(f32, 5.0, 3.0) == 1.0); | ||
| 44 | try expect(divTrunc(f32, -5.0, 3.0) == -1.0); | ||
| 45 | try expect(divTrunc(f64, 5.0, 3.0) == 1.0); | ||
| 46 | try expect(divTrunc(f64, -5.0, 3.0) == -1.0); | ||
| 47 | try expect(divTrunc(i32, 10, 12) == 0); | ||
| 48 | try expect(divTrunc(i32, -14, 12) == -1); | ||
| 49 | try expect(divTrunc(i32, -2, 12) == 0); | ||
| 50 | |||
| 51 | try expect(mod(i32, 10, 12) == 10); | ||
| 52 | try expect(mod(i32, -14, 12) == 10); | ||
| 53 | try expect(mod(i32, -2, 12) == 10); | ||
| 54 | |||
| 55 | comptime { | ||
| 56 | try expect( | ||
| 57 | 1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600, | ||
| 58 | ); | ||
| 59 | try expect( | ||
| 60 | @rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600, | ||
| 61 | ); | ||
| 62 | try expect( | ||
| 63 | 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2, | ||
| 64 | ); | ||
| 65 | try expect( | ||
| 66 | @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2, | ||
| 67 | ); | ||
| 68 | try expect( | ||
| 69 | @divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2, | ||
| 70 | ); | ||
| 71 | try expect( | ||
| 72 | @divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2, | ||
| 73 | ); | ||
| 74 | try expect( | ||
| 75 | 4126227191251978491697987544882340798050766755606969681711 % 10 == 1, | ||
| 76 | ); | ||
| 77 | } | ||
| 78 | } | ||
| 79 | fn div(comptime T: type, a: T, b: T) T { | ||
| 80 | return a / b; | ||
| 81 | } | ||
| 82 | fn divExact(comptime T: type, a: T, b: T) T { | ||
| 83 | return @divExact(a, b); | ||
| 84 | } | ||
| 85 | fn divFloor(comptime T: type, a: T, b: T) T { | ||
| 86 | return @divFloor(a, b); | ||
| 87 | } | ||
| 88 | fn divTrunc(comptime T: type, a: T, b: T) T { | ||
| 89 | return @divTrunc(a, b); | ||
| 90 | } | ||
| 91 | fn mod(comptime T: type, a: T, b: T) T { | ||
| 92 | return @mod(a, b); | ||
| 93 | } | ||
| 94 | |||
| 95 | test "@addWithOverflow" { | ||
| 96 | var result: u8 = undefined; | ||
| 97 | try expect(@addWithOverflow(u8, 250, 100, &result)); | ||
| 98 | try expect(!@addWithOverflow(u8, 100, 150, &result)); | ||
| 99 | try expect(result == 250); | ||
| 100 | } | ||
| 101 | |||
| 102 | // TODO test mulWithOverflow | ||
| 103 | // TODO test subWithOverflow | ||
| 104 | |||
| 105 | test "@shlWithOverflow" { | ||
| 106 | var result: u16 = undefined; | ||
| 107 | try expect(@shlWithOverflow(u16, 0b0010111111111111, 3, &result)); | ||
| 108 | try expect(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result)); | ||
| 109 | try expect(result == 0b1011111111111100); | ||
| 110 | } | ||
| 111 | |||
| 112 | test "@*WithOverflow with u0 values" { | ||
| 113 | var result: u0 = undefined; | ||
| 114 | try expect(!@addWithOverflow(u0, 0, 0, &result)); | ||
| 115 | try expect(!@subWithOverflow(u0, 0, 0, &result)); | ||
| 116 | try expect(!@mulWithOverflow(u0, 0, 0, &result)); | ||
| 117 | try expect(!@shlWithOverflow(u0, 0, 0, &result)); | ||
| 118 | } | ||
| 119 | |||
| 120 | test "@clz" { | ||
| 121 | try testClz(); | ||
| 122 | comptime try testClz(); | ||
| 123 | } | ||
| 124 | |||
| 125 | fn testClz() !void { | ||
| 126 | try expect(@clz(u8, 0b10001010) == 0); | ||
| 127 | try expect(@clz(u8, 0b00001010) == 4); | ||
| 128 | try expect(@clz(u8, 0b00011010) == 3); | ||
| 129 | try expect(@clz(u8, 0b00000000) == 8); | ||
| 130 | try expect(@clz(u128, 0xffffffffffffffff) == 64); | ||
| 131 | try expect(@clz(u128, 0x10000000000000000) == 63); | ||
| 132 | } | ||
| 133 | |||
| 134 | test "@clz vectors" { | ||
| 135 | try testClzVectors(); | ||
| 136 | comptime try testClzVectors(); | ||
| 137 | } | ||
| 138 | |||
| 139 | fn testClzVectors() !void { | ||
| 140 | @setEvalBranchQuota(10_000); | ||
| 141 | try expectEqual(@clz(u8, @splat(64, @as(u8, 0b10001010))), @splat(64, @as(u4, 0))); | ||
| 142 | try expectEqual(@clz(u8, @splat(64, @as(u8, 0b00001010))), @splat(64, @as(u4, 4))); | ||
| 143 | try expectEqual(@clz(u8, @splat(64, @as(u8, 0b00011010))), @splat(64, @as(u4, 3))); | ||
| 144 | try expectEqual(@clz(u8, @splat(64, @as(u8, 0b00000000))), @splat(64, @as(u4, 8))); | ||
| 145 | try expectEqual(@clz(u128, @splat(64, @as(u128, 0xffffffffffffffff))), @splat(64, @as(u8, 64))); | ||
| 146 | try expectEqual(@clz(u128, @splat(64, @as(u128, 0x10000000000000000))), @splat(64, @as(u8, 63))); | ||
| 147 | } | ||
| 148 | |||
| 149 | test "@ctz" { | ||
| 150 | try testCtz(); | ||
| 151 | comptime try testCtz(); | ||
| 152 | } | ||
| 153 | |||
| 154 | fn testCtz() !void { | ||
| 155 | try expect(@ctz(u8, 0b10100000) == 5); | ||
| 156 | try expect(@ctz(u8, 0b10001010) == 1); | ||
| 157 | try expect(@ctz(u8, 0b00000000) == 8); | ||
| 158 | try expect(@ctz(u16, 0b00000000) == 16); | ||
| 159 | } | ||
| 160 | |||
| 161 | test "@ctz vectors" { | ||
| 162 | try testClzVectors(); | ||
| 163 | comptime try testClzVectors(); | ||
| 164 | } | ||
| 165 | |||
| 166 | fn testCtzVectors() !void { | ||
| 167 | @setEvalBranchQuota(10_000); | ||
| 168 | try expectEqual(@ctz(u8, @splat(64, @as(u8, 0b10100000))), @splat(64, @as(u4, 5))); | ||
| 169 | try expectEqual(@ctz(u8, @splat(64, @as(u8, 0b10001010))), @splat(64, @as(u4, 1))); | ||
| 170 | try expectEqual(@ctz(u8, @splat(64, @as(u8, 0b00000000))), @splat(64, @as(u4, 8))); | ||
| 171 | try expectEqual(@ctz(u16, @splat(64, @as(u16, 0b00000000))), @splat(64, @as(u5, 16))); | ||
| 172 | } | ||
| 173 | |||
| 174 | test "const number literal" { | ||
| 175 | const one = 1; | ||
| 176 | const eleven = ten + one; | ||
| 177 | |||
| 178 | try expect(eleven == 11); | ||
| 179 | } | ||
| 180 | const ten = 10; | ||
| 181 | |||
| 182 | test "unsigned wrapping" { | ||
| 183 | try testUnsignedWrappingEval(maxInt(u32)); | ||
| 184 | comptime try testUnsignedWrappingEval(maxInt(u32)); | ||
| 185 | } | ||
| 186 | fn testUnsignedWrappingEval(x: u32) !void { | ||
| 187 | const zero = x +% 1; | ||
| 188 | try expect(zero == 0); | ||
| 189 | const orig = zero -% 1; | ||
| 190 | try expect(orig == maxInt(u32)); | ||
| 191 | } | ||
| 192 | |||
| 193 | test "signed wrapping" { | ||
| 194 | try testSignedWrappingEval(maxInt(i32)); | ||
| 195 | comptime try testSignedWrappingEval(maxInt(i32)); | ||
| 196 | } | ||
| 197 | fn testSignedWrappingEval(x: i32) !void { | ||
| 198 | const min_val = x +% 1; | ||
| 199 | try expect(min_val == minInt(i32)); | ||
| 200 | const max_val = min_val -% 1; | ||
| 201 | try expect(max_val == maxInt(i32)); | ||
| 202 | } | ||
| 203 | |||
| 204 | test "signed negation wrapping" { | ||
| 205 | try testSignedNegationWrappingEval(minInt(i16)); | ||
| 206 | comptime try testSignedNegationWrappingEval(minInt(i16)); | ||
| 207 | } | ||
| 208 | fn testSignedNegationWrappingEval(x: i16) !void { | ||
| 209 | try expect(x == -32768); | ||
| 210 | const neg = -%x; | ||
| 211 | try expect(neg == -32768); | ||
| 212 | } | ||
| 213 | |||
| 214 | test "unsigned negation wrapping" { | ||
| 215 | try testUnsignedNegationWrappingEval(1); | ||
| 216 | comptime try testUnsignedNegationWrappingEval(1); | ||
| 217 | } | ||
| 218 | fn testUnsignedNegationWrappingEval(x: u16) !void { | ||
| 219 | try expect(x == 1); | ||
| 220 | const neg = -%x; | ||
| 221 | try expect(neg == maxInt(u16)); | ||
| 222 | } | ||
| 223 | |||
| 224 | test "unsigned 64-bit division" { | ||
| 225 | try test_u64_div(); | ||
| 226 | comptime try test_u64_div(); | ||
| 227 | } | ||
| 228 | fn test_u64_div() !void { | ||
| 229 | const result = divWithResult(1152921504606846976, 34359738365); | ||
| 230 | try expect(result.quotient == 33554432); | ||
| 231 | try expect(result.remainder == 100663296); | ||
| 232 | } | ||
| 233 | fn divWithResult(a: u64, b: u64) DivResult { | ||
| 234 | return DivResult{ | ||
| 235 | .quotient = a / b, | ||
| 236 | .remainder = a % b, | ||
| 237 | }; | ||
| 238 | } | ||
| 239 | const DivResult = struct { | ||
| 240 | quotient: u64, | ||
| 241 | remainder: u64, | ||
| 242 | }; | ||
| 243 | |||
| 244 | test "binary not" { | ||
| 245 | try expect(comptime x: { | ||
| 246 | break :x ~@as(u16, 0b1010101010101010) == 0b0101010101010101; | ||
| 247 | }); | ||
| 248 | try expect(comptime x: { | ||
| 249 | break :x ~@as(u64, 2147483647) == 18446744071562067968; | ||
| 250 | }); | ||
| 251 | try testBinaryNot(0b1010101010101010); | ||
| 252 | } | ||
| 253 | |||
| 254 | fn testBinaryNot(x: u16) !void { | ||
| 255 | try expect(~x == 0b0101010101010101); | ||
| 256 | } | ||
| 257 | |||
| 258 | test "small int addition" { | ||
| 259 | var x: u2 = 0; | ||
| 260 | try expect(x == 0); | ||
| 261 | |||
| 262 | x += 1; | ||
| 263 | try expect(x == 1); | ||
| 264 | |||
| 265 | x += 1; | ||
| 266 | try expect(x == 2); | ||
| 267 | |||
| 268 | x += 1; | ||
| 269 | try expect(x == 3); | ||
| 270 | |||
| 271 | var result: @TypeOf(x) = 3; | ||
| 272 | try expect(@addWithOverflow(@TypeOf(x), x, 1, &result)); | ||
| 273 | |||
| 274 | try expect(result == 0); | ||
| 275 | } | ||
| 276 | |||
| 277 | test "float equality" { | ||
| 278 | const x: f64 = 0.012; | ||
| 279 | const y: f64 = x + 1.0; | ||
| 280 | |||
| 281 | try testFloatEqualityImpl(x, y); | ||
| 282 | comptime try testFloatEqualityImpl(x, y); | ||
| 283 | } | ||
| 284 | |||
| 285 | fn testFloatEqualityImpl(x: f64, y: f64) !void { | ||
| 286 | const y2 = x + 1.0; | ||
| 287 | try expect(y == y2); | ||
| 288 | } | ||
| 289 | |||
| 290 | test "allow signed integer division/remainder when values are comptime known and positive or exact" { | ||
| 291 | try expect(5 / 3 == 1); | ||
| 292 | try expect(-5 / -3 == 1); | ||
| 293 | try expect(-6 / 3 == -2); | ||
| 294 | |||
| 295 | try expect(5 % 3 == 2); | ||
| 296 | try expect(-6 % 3 == 0); | ||
| 297 | } | ||
| 298 | |||
| 299 | test "hex float literal parsing" { | ||
| 300 | comptime try expect(0x1.0 == 1.0); | ||
| 301 | } | ||
| 302 | |||
| 303 | test "quad hex float literal parsing in range" { | ||
| 304 | const a = 0x1.af23456789bbaaab347645365cdep+5; | ||
| 305 | const b = 0x1.dedafcff354b6ae9758763545432p-9; | ||
| 306 | const c = 0x1.2f34dd5f437e849b4baab754cdefp+4534; | ||
| 307 | const d = 0x1.edcbff8ad76ab5bf46463233214fp-435; | ||
| 308 | if (false) { | ||
| 309 | a; | ||
| 310 | b; | ||
| 311 | c; | ||
| 312 | d; | ||
| 313 | } | ||
| 314 | } | ||
| 315 | |||
| 316 | test "quad hex float literal parsing accurate" { | ||
| 317 | const a: f128 = 0x1.1111222233334444555566667777p+0; | ||
| 318 | |||
| 319 | // implied 1 is dropped, with an exponent of 0 (0x3fff) after biasing. | ||
| 320 | const expected: u128 = 0x3fff1111222233334444555566667777; | ||
| 321 | try expect(@bitCast(u128, a) == expected); | ||
| 322 | |||
| 323 | // non-normalized | ||
| 324 | const b: f128 = 0x11.111222233334444555566667777p-4; | ||
| 325 | try expect(@bitCast(u128, b) == expected); | ||
| 326 | |||
| 327 | const S = struct { | ||
| 328 | fn doTheTest() !void { | ||
| 329 | { | ||
| 330 | var f: f128 = 0x1.2eab345678439abcdefea56782346p+5; | ||
| 331 | try expect(@bitCast(u128, f) == 0x40042eab345678439abcdefea5678234); | ||
| 332 | } | ||
| 333 | { | ||
| 334 | var f: f128 = 0x1.edcb34a235253948765432134674fp-1; | ||
| 335 | try expect(@bitCast(u128, f) == 0x3ffeedcb34a235253948765432134674); | ||
| 336 | } | ||
| 337 | { | ||
| 338 | var f: f128 = 0x1.353e45674d89abacc3a2ebf3ff4ffp-50; | ||
| 339 | try expect(@bitCast(u128, f) == 0x3fcd353e45674d89abacc3a2ebf3ff50); | ||
| 340 | } | ||
| 341 | { | ||
| 342 | var f: f128 = 0x1.ed8764648369535adf4be3214567fp-9; | ||
| 343 | try expect(@bitCast(u128, f) == 0x3ff6ed8764648369535adf4be3214568); | ||
| 344 | } | ||
| 345 | const exp2ft = [_]f64{ | ||
| 346 | 0x1.6a09e667f3bcdp-1, | ||
| 347 | 0x1.7a11473eb0187p-1, | ||
| 348 | 0x1.8ace5422aa0dbp-1, | ||
| 349 | 0x1.9c49182a3f090p-1, | ||
| 350 | 0x1.ae89f995ad3adp-1, | ||
| 351 | 0x1.c199bdd85529cp-1, | ||
| 352 | 0x1.d5818dcfba487p-1, | ||
| 353 | 0x1.ea4afa2a490dap-1, | ||
| 354 | 0x1.0000000000000p+0, | ||
| 355 | 0x1.0b5586cf9890fp+0, | ||
| 356 | 0x1.172b83c7d517bp+0, | ||
| 357 | 0x1.2387a6e756238p+0, | ||
| 358 | 0x1.306fe0a31b715p+0, | ||
| 359 | 0x1.3dea64c123422p+0, | ||
| 360 | 0x1.4bfdad5362a27p+0, | ||
| 361 | 0x1.5ab07dd485429p+0, | ||
| 362 | 0x1.8p23, | ||
| 363 | 0x1.62e430p-1, | ||
| 364 | 0x1.ebfbe0p-3, | ||
| 365 | 0x1.c6b348p-5, | ||
| 366 | 0x1.3b2c9cp-7, | ||
| 367 | 0x1.0p127, | ||
| 368 | -0x1.0p-149, | ||
| 369 | }; | ||
| 370 | |||
| 371 | const answers = [_]u64{ | ||
| 372 | 0x3fe6a09e667f3bcd, | ||
| 373 | 0x3fe7a11473eb0187, | ||
| 374 | 0x3fe8ace5422aa0db, | ||
| 375 | 0x3fe9c49182a3f090, | ||
| 376 | 0x3feae89f995ad3ad, | ||
| 377 | 0x3fec199bdd85529c, | ||
| 378 | 0x3fed5818dcfba487, | ||
| 379 | 0x3feea4afa2a490da, | ||
| 380 | 0x3ff0000000000000, | ||
| 381 | 0x3ff0b5586cf9890f, | ||
| 382 | 0x3ff172b83c7d517b, | ||
| 383 | 0x3ff2387a6e756238, | ||
| 384 | 0x3ff306fe0a31b715, | ||
| 385 | 0x3ff3dea64c123422, | ||
| 386 | 0x3ff4bfdad5362a27, | ||
| 387 | 0x3ff5ab07dd485429, | ||
| 388 | 0x4168000000000000, | ||
| 389 | 0x3fe62e4300000000, | ||
| 390 | 0x3fcebfbe00000000, | ||
| 391 | 0x3fac6b3480000000, | ||
| 392 | 0x3f83b2c9c0000000, | ||
| 393 | 0x47e0000000000000, | ||
| 394 | 0xb6a0000000000000, | ||
| 395 | }; | ||
| 396 | |||
| 397 | for (exp2ft) |x, i| { | ||
| 398 | try expect(@bitCast(u64, x) == answers[i]); | ||
| 399 | } | ||
| 400 | } | ||
| 401 | }; | ||
| 402 | try S.doTheTest(); | ||
| 403 | comptime try S.doTheTest(); | ||
| 404 | } | ||
| 405 | |||
| 406 | test "underscore separator parsing" { | ||
| 407 | try expect(0_0_0_0 == 0); | ||
| 408 | try expect(1_234_567 == 1234567); | ||
| 409 | try expect(001_234_567 == 1234567); | ||
| 410 | try expect(0_0_1_2_3_4_5_6_7 == 1234567); | ||
| 411 | |||
| 412 | try expect(0b0_0_0_0 == 0); | ||
| 413 | try expect(0b1010_1010 == 0b10101010); | ||
| 414 | try expect(0b0000_1010_1010 == 0b10101010); | ||
| 415 | try expect(0b1_0_1_0_1_0_1_0 == 0b10101010); | ||
| 416 | |||
| 417 | try expect(0o0_0_0_0 == 0); | ||
| 418 | try expect(0o1010_1010 == 0o10101010); | ||
| 419 | try expect(0o0000_1010_1010 == 0o10101010); | ||
| 420 | try expect(0o1_0_1_0_1_0_1_0 == 0o10101010); | ||
| 421 | |||
| 422 | try expect(0x0_0_0_0 == 0); | ||
| 423 | try expect(0x1010_1010 == 0x10101010); | ||
| 424 | try expect(0x0000_1010_1010 == 0x10101010); | ||
| 425 | try expect(0x1_0_1_0_1_0_1_0 == 0x10101010); | ||
| 426 | |||
| 427 | try expect(123_456.789_000e1_0 == 123456.789000e10); | ||
| 428 | try expect(0_1_2_3_4_5_6.7_8_9_0_0_0e0_0_1_0 == 123456.789000e10); | ||
| 429 | |||
| 430 | try expect(0x1234_5678.9ABC_DEF0p-1_0 == 0x12345678.9ABCDEF0p-10); | ||
| 431 | try expect(0x1_2_3_4_5_6_7_8.9_A_B_C_D_E_F_0p-0_0_0_1_0 == 0x12345678.9ABCDEF0p-10); | ||
| 432 | } | ||
| 433 | |||
| 434 | test "hex float literal within range" { | ||
| 435 | const a = 0x1.0p16383; | ||
| 436 | const b = 0x0.1p16387; | ||
| 437 | const c = 0x1.0p-16382; | ||
| 438 | if (false) { | ||
| 439 | a; | ||
| 440 | b; | ||
| 441 | c; | ||
| 442 | } | ||
| 443 | } | ||
| 444 | |||
| 445 | test "truncating shift left" { | ||
| 446 | try testShlTrunc(maxInt(u16)); | ||
| 447 | comptime try testShlTrunc(maxInt(u16)); | ||
| 448 | } | ||
| 449 | fn testShlTrunc(x: u16) !void { | ||
| 450 | const shifted = x << 1; | ||
| 451 | try expect(shifted == 65534); | ||
| 452 | } | ||
| 453 | |||
| 454 | test "truncating shift right" { | ||
| 455 | try testShrTrunc(maxInt(u16)); | ||
| 456 | comptime try testShrTrunc(maxInt(u16)); | ||
| 457 | } | ||
| 458 | fn testShrTrunc(x: u16) !void { | ||
| 459 | const shifted = x >> 1; | ||
| 460 | try expect(shifted == 32767); | ||
| 461 | } | ||
| 462 | |||
| 463 | test "exact shift left" { | ||
| 464 | try testShlExact(0b00110101); | ||
| 465 | comptime try testShlExact(0b00110101); | ||
| 466 | } | ||
| 467 | fn testShlExact(x: u8) !void { | ||
| 468 | const shifted = @shlExact(x, 2); | ||
| 469 | try expect(shifted == 0b11010100); | ||
| 470 | } | ||
| 471 | |||
| 472 | test "exact shift right" { | ||
| 473 | try testShrExact(0b10110100); | ||
| 474 | comptime try testShrExact(0b10110100); | ||
| 475 | } | ||
| 476 | fn testShrExact(x: u8) !void { | ||
| 477 | const shifted = @shrExact(x, 2); | ||
| 478 | try expect(shifted == 0b00101101); | ||
| 479 | } | ||
| 480 | |||
| 481 | test "shift left/right on u0 operand" { | ||
| 482 | const S = struct { | ||
| 483 | fn doTheTest() !void { | ||
| 484 | var x: u0 = 0; | ||
| 485 | var y: u0 = 0; | ||
| 486 | try expectEqual(@as(u0, 0), x << 0); | ||
| 487 | try expectEqual(@as(u0, 0), x >> 0); | ||
| 488 | try expectEqual(@as(u0, 0), x << y); | ||
| 489 | try expectEqual(@as(u0, 0), x >> y); | ||
| 490 | try expectEqual(@as(u0, 0), @shlExact(x, 0)); | ||
| 491 | try expectEqual(@as(u0, 0), @shrExact(x, 0)); | ||
| 492 | try expectEqual(@as(u0, 0), @shlExact(x, y)); | ||
| 493 | try expectEqual(@as(u0, 0), @shrExact(x, y)); | ||
| 494 | } | ||
| 495 | }; | ||
| 496 | try S.doTheTest(); | ||
| 497 | comptime try S.doTheTest(); | ||
| 498 | } | ||
| 499 | |||
| 500 | test "comptime_int addition" { | ||
| 501 | comptime { | ||
| 502 | try expect(35361831660712422535336160538497375248 + 101752735581729509668353361206450473702 == 137114567242441932203689521744947848950); | ||
| 503 | try expect(594491908217841670578297176641415611445982232488944558774612 + 390603545391089362063884922208143568023166603618446395589768 == 985095453608931032642182098849559179469148836107390954364380); | ||
| 504 | } | ||
| 505 | } | ||
| 506 | |||
| 507 | test "comptime_int multiplication" { | ||
| 508 | comptime { | ||
| 509 | try expect( | ||
| 510 | 45960427431263824329884196484953148229 * 128339149605334697009938835852565949723 == 5898522172026096622534201617172456926982464453350084962781392314016180490567, | ||
| 511 | ); | ||
| 512 | try expect( | ||
| 513 | 594491908217841670578297176641415611445982232488944558774612 * 390603545391089362063884922208143568023166603618446395589768 == 232210647056203049913662402532976186578842425262306016094292237500303028346593132411865381225871291702600263463125370016, | ||
| 514 | ); | ||
| 515 | } | ||
| 516 | } | ||
| 517 | |||
| 518 | test "comptime_int shifting" { | ||
| 519 | comptime { | ||
| 520 | try expect((@as(u128, 1) << 127) == 0x80000000000000000000000000000000); | ||
| 521 | } | ||
| 522 | } | ||
| 523 | |||
| 524 | test "comptime_int multi-limb shift and mask" { | ||
| 525 | comptime { | ||
| 526 | var a = 0xefffffffa0000001eeeeeeefaaaaaaab; | ||
| 527 | |||
| 528 | try expect(@as(u32, a & 0xffffffff) == 0xaaaaaaab); | ||
| 529 | a >>= 32; | ||
| 530 | try expect(@as(u32, a & 0xffffffff) == 0xeeeeeeef); | ||
| 531 | a >>= 32; | ||
| 532 | try expect(@as(u32, a & 0xffffffff) == 0xa0000001); | ||
| 533 | a >>= 32; | ||
| 534 | try expect(@as(u32, a & 0xffffffff) == 0xefffffff); | ||
| 535 | a >>= 32; | ||
| 536 | |||
| 537 | try expect(a == 0); | ||
| 538 | } | ||
| 539 | } | ||
| 540 | |||
| 541 | test "comptime_int multi-limb partial shift right" { | ||
| 542 | comptime { | ||
| 543 | var a = 0x1ffffffffeeeeeeee; | ||
| 544 | a >>= 16; | ||
| 545 | try expect(a == 0x1ffffffffeeee); | ||
| 546 | } | ||
| 547 | } | ||
| 548 | |||
| 549 | test "xor" { | ||
| 550 | try test_xor(); | ||
| 551 | comptime try test_xor(); | ||
| 552 | } | ||
| 553 | |||
| 554 | fn test_xor() !void { | ||
| 555 | try expect(0xFF ^ 0x00 == 0xFF); | ||
| 556 | try expect(0xF0 ^ 0x0F == 0xFF); | ||
| 557 | try expect(0xFF ^ 0xF0 == 0x0F); | ||
| 558 | try expect(0xFF ^ 0x0F == 0xF0); | ||
| 559 | try expect(0xFF ^ 0xFF == 0x00); | ||
| 560 | } | ||
| 561 | |||
| 562 | test "comptime_int xor" { | ||
| 563 | comptime { | ||
| 564 | try expect(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0x00000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); | ||
| 565 | try expect(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0x0000000000000000FFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); | ||
| 566 | try expect(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x0000000000000000FFFFFFFFFFFFFFFF); | ||
| 567 | try expect(0x0000000000000000FFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFF0000000000000000); | ||
| 568 | try expect(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000000000000000000000000000); | ||
| 569 | try expect(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0x00000000FFFFFFFF00000000FFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); | ||
| 570 | try expect(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000FFFFFFFF00000000FFFFFFFF); | ||
| 571 | try expect(0x00000000FFFFFFFF00000000FFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFF00000000FFFFFFFF00000000); | ||
| 572 | } | ||
| 573 | } | ||
| 574 | |||
| 575 | test "f128" { | ||
| 576 | try test_f128(); | ||
| 577 | comptime try test_f128(); | ||
| 578 | } | ||
| 579 | |||
| 580 | fn make_f128(x: f128) f128 { | ||
| 581 | return x; | ||
| 582 | } | ||
| 583 | |||
| 584 | fn test_f128() !void { | ||
| 585 | try expect(@sizeOf(f128) == 16); | ||
| 586 | try expect(make_f128(1.0) == 1.0); | ||
| 587 | try expect(make_f128(1.0) != 1.1); | ||
| 588 | try expect(make_f128(1.0) > 0.9); | ||
| 589 | try expect(make_f128(1.0) >= 0.9); | ||
| 590 | try expect(make_f128(1.0) >= 1.0); | ||
| 591 | try should_not_be_zero(1.0); | ||
| 592 | } | ||
| 593 | |||
| 594 | fn should_not_be_zero(x: f128) !void { | ||
| 595 | try expect(x != 0.0); | ||
| 596 | } | ||
| 597 | |||
| 598 | test "comptime float rem int" { | ||
| 599 | comptime { | ||
| 600 | var x = @as(f32, 1) % 2; | ||
| 601 | try expect(x == 1.0); | ||
| 602 | } | ||
| 603 | } | ||
| 604 | |||
| 605 | test "remainder division" { | ||
| 606 | comptime try remdiv(f16); | ||
| 607 | comptime try remdiv(f32); | ||
| 608 | comptime try remdiv(f64); | ||
| 609 | comptime try remdiv(f128); | ||
| 610 | try remdiv(f16); | ||
| 611 | try remdiv(f64); | ||
| 612 | try remdiv(f128); | ||
| 613 | } | ||
| 614 | |||
| 615 | fn remdiv(comptime T: type) !void { | ||
| 616 | try expect(@as(T, 1) == @as(T, 1) % @as(T, 2)); | ||
| 617 | try expect(@as(T, 1) == @as(T, 7) % @as(T, 3)); | ||
| 618 | } | ||
| 619 | |||
| 620 | test "@sqrt" { | ||
| 621 | try testSqrt(f64, 12.0); | ||
| 622 | comptime try testSqrt(f64, 12.0); | ||
| 623 | try testSqrt(f32, 13.0); | ||
| 624 | comptime try testSqrt(f32, 13.0); | ||
| 625 | try testSqrt(f16, 13.0); | ||
| 626 | comptime try testSqrt(f16, 13.0); | ||
| 627 | |||
| 628 | const x = 14.0; | ||
| 629 | const y = x * x; | ||
| 630 | const z = @sqrt(y); | ||
| 631 | comptime try expect(z == x); | ||
| 632 | } | ||
| 633 | |||
| 634 | fn testSqrt(comptime T: type, x: T) !void { | ||
| 635 | try expect(@sqrt(x * x) == x); | ||
| 636 | } | ||
| 637 | |||
| 638 | test "@fabs" { | ||
| 639 | try testFabs(f128, 12.0); | ||
| 640 | comptime try testFabs(f128, 12.0); | ||
| 641 | try testFabs(f64, 12.0); | ||
| 642 | comptime try testFabs(f64, 12.0); | ||
| 643 | try testFabs(f32, 12.0); | ||
| 644 | comptime try testFabs(f32, 12.0); | ||
| 645 | try testFabs(f16, 12.0); | ||
| 646 | comptime try testFabs(f16, 12.0); | ||
| 647 | |||
| 648 | const x = 14.0; | ||
| 649 | const y = -x; | ||
| 650 | const z = @fabs(y); | ||
| 651 | comptime try expectEqual(x, z); | ||
| 652 | } | ||
| 653 | |||
| 654 | fn testFabs(comptime T: type, x: T) !void { | ||
| 655 | const y = -x; | ||
| 656 | const z = @fabs(y); | ||
| 657 | try expectEqual(x, z); | ||
| 658 | } | ||
| 659 | |||
| 660 | test "@floor" { | ||
| 661 | // FIXME: Generates a floorl function call | ||
| 662 | // testFloor(f128, 12.0); | ||
| 663 | comptime try testFloor(f128, 12.0); | ||
| 664 | try testFloor(f64, 12.0); | ||
| 665 | comptime try testFloor(f64, 12.0); | ||
| 666 | try testFloor(f32, 12.0); | ||
| 667 | comptime try testFloor(f32, 12.0); | ||
| 668 | try testFloor(f16, 12.0); | ||
| 669 | comptime try testFloor(f16, 12.0); | ||
| 670 | |||
| 671 | const x = 14.0; | ||
| 672 | const y = x + 0.7; | ||
| 673 | const z = @floor(y); | ||
| 674 | comptime try expectEqual(x, z); | ||
| 675 | } | ||
| 676 | |||
| 677 | fn testFloor(comptime T: type, x: T) !void { | ||
| 678 | const y = x + 0.6; | ||
| 679 | const z = @floor(y); | ||
| 680 | try expectEqual(x, z); | ||
| 681 | } | ||
| 682 | |||
| 683 | test "@ceil" { | ||
| 684 | // FIXME: Generates a ceill function call | ||
| 685 | //testCeil(f128, 12.0); | ||
| 686 | comptime try testCeil(f128, 12.0); | ||
| 687 | try testCeil(f64, 12.0); | ||
| 688 | comptime try testCeil(f64, 12.0); | ||
| 689 | try testCeil(f32, 12.0); | ||
| 690 | comptime try testCeil(f32, 12.0); | ||
| 691 | try testCeil(f16, 12.0); | ||
| 692 | comptime try testCeil(f16, 12.0); | ||
| 693 | |||
| 694 | const x = 14.0; | ||
| 695 | const y = x - 0.7; | ||
| 696 | const z = @ceil(y); | ||
| 697 | comptime try expectEqual(x, z); | ||
| 698 | } | ||
| 699 | |||
| 700 | fn testCeil(comptime T: type, x: T) !void { | ||
| 701 | const y = x - 0.8; | ||
| 702 | const z = @ceil(y); | ||
| 703 | try expectEqual(x, z); | ||
| 704 | } | ||
| 705 | |||
| 706 | test "@trunc" { | ||
| 707 | // FIXME: Generates a truncl function call | ||
| 708 | //testTrunc(f128, 12.0); | ||
| 709 | comptime try testTrunc(f128, 12.0); | ||
| 710 | try testTrunc(f64, 12.0); | ||
| 711 | comptime try testTrunc(f64, 12.0); | ||
| 712 | try testTrunc(f32, 12.0); | ||
| 713 | comptime try testTrunc(f32, 12.0); | ||
| 714 | try testTrunc(f16, 12.0); | ||
| 715 | comptime try testTrunc(f16, 12.0); | ||
| 716 | |||
| 717 | const x = 14.0; | ||
| 718 | const y = x + 0.7; | ||
| 719 | const z = @trunc(y); | ||
| 720 | comptime try expectEqual(x, z); | ||
| 721 | } | ||
| 722 | |||
| 723 | fn testTrunc(comptime T: type, x: T) !void { | ||
| 724 | { | ||
| 725 | const y = x + 0.8; | ||
| 726 | const z = @trunc(y); | ||
| 727 | try expectEqual(x, z); | ||
| 728 | } | ||
| 729 | |||
| 730 | { | ||
| 731 | const y = -x - 0.8; | ||
| 732 | const z = @trunc(y); | ||
| 733 | try expectEqual(-x, z); | ||
| 734 | } | ||
| 735 | } | ||
| 736 | |||
| 737 | test "@round" { | ||
| 738 | // FIXME: Generates a roundl function call | ||
| 739 | //testRound(f128, 12.0); | ||
| 740 | comptime try testRound(f128, 12.0); | ||
| 741 | try testRound(f64, 12.0); | ||
| 742 | comptime try testRound(f64, 12.0); | ||
| 743 | try testRound(f32, 12.0); | ||
| 744 | comptime try testRound(f32, 12.0); | ||
| 745 | try testRound(f16, 12.0); | ||
| 746 | comptime try testRound(f16, 12.0); | ||
| 747 | |||
| 748 | const x = 14.0; | ||
| 749 | const y = x + 0.4; | ||
| 750 | const z = @round(y); | ||
| 751 | comptime try expectEqual(x, z); | ||
| 752 | } | ||
| 753 | |||
| 754 | fn testRound(comptime T: type, x: T) !void { | ||
| 755 | const y = x - 0.5; | ||
| 756 | const z = @round(y); | ||
| 757 | try expectEqual(x, z); | ||
| 758 | } | ||
| 759 | |||
| 760 | test "comptime_int param and return" { | ||
| 761 | const a = comptimeAdd(35361831660712422535336160538497375248, 101752735581729509668353361206450473702); | ||
| 762 | try expect(a == 137114567242441932203689521744947848950); | ||
| 763 | |||
| 764 | const b = comptimeAdd(594491908217841670578297176641415611445982232488944558774612, 390603545391089362063884922208143568023166603618446395589768); | ||
| 765 | try expect(b == 985095453608931032642182098849559179469148836107390954364380); | ||
| 766 | } | ||
| 767 | |||
| 768 | fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int { | ||
| 769 | return a + b; | ||
| 770 | } | ||
| 771 | |||
| 772 | test "vector integer addition" { | ||
| 773 | const S = struct { | ||
| 774 | fn doTheTest() !void { | ||
| 775 | var a: std.meta.Vector(4, i32) = [_]i32{ 1, 2, 3, 4 }; | ||
| 776 | var b: std.meta.Vector(4, i32) = [_]i32{ 5, 6, 7, 8 }; | ||
| 777 | var result = a + b; | ||
| 778 | var result_array: [4]i32 = result; | ||
| 779 | const expected = [_]i32{ 6, 8, 10, 12 }; | ||
| 780 | try expectEqualSlices(i32, &expected, &result_array); | ||
| 781 | } | ||
| 782 | }; | ||
| 783 | try S.doTheTest(); | ||
| 784 | comptime try S.doTheTest(); | ||
| 785 | } | ||
| 786 | |||
| 787 | test "NaN comparison" { | ||
| 788 | try testNanEqNan(f16); | ||
| 789 | try testNanEqNan(f32); | ||
| 790 | try testNanEqNan(f64); | ||
| 791 | try testNanEqNan(f128); | ||
| 792 | comptime try testNanEqNan(f16); | ||
| 793 | comptime try testNanEqNan(f32); | ||
| 794 | comptime try testNanEqNan(f64); | ||
| 795 | comptime try testNanEqNan(f128); | ||
| 796 | } | ||
| 797 | |||
| 798 | fn testNanEqNan(comptime F: type) !void { | ||
| 799 | var nan1 = std.math.nan(F); | ||
| 800 | var nan2 = std.math.nan(F); | ||
| 801 | try expect(nan1 != nan2); | ||
| 802 | try expect(!(nan1 == nan2)); | ||
| 803 | try expect(!(nan1 > nan2)); | ||
| 804 | try expect(!(nan1 >= nan2)); | ||
| 805 | try expect(!(nan1 < nan2)); | ||
| 806 | try expect(!(nan1 <= nan2)); | ||
| 807 | } | ||
| 808 | |||
| 809 | test "128-bit multiplication" { | ||
| 810 | var a: i128 = 3; | ||
| 811 | var b: i128 = 2; | ||
| 812 | var c = a * b; | ||
| 813 | try expect(c == 6); | ||
| 814 | } | ||
| 815 | |||
| 816 | test "vector comparison" { | ||
| 817 | const S = struct { | ||
| 818 | fn doTheTest() !void { | ||
| 819 | var a: std.meta.Vector(6, i32) = [_]i32{ 1, 3, -1, 5, 7, 9 }; | ||
| 820 | var b: std.meta.Vector(6, i32) = [_]i32{ -1, 3, 0, 6, 10, -10 }; | ||
| 821 | try expect(mem.eql(bool, &@as([6]bool, a < b), &[_]bool{ false, false, true, true, true, false })); | ||
| 822 | try expect(mem.eql(bool, &@as([6]bool, a <= b), &[_]bool{ false, true, true, true, true, false })); | ||
| 823 | try expect(mem.eql(bool, &@as([6]bool, a == b), &[_]bool{ false, true, false, false, false, false })); | ||
| 824 | try expect(mem.eql(bool, &@as([6]bool, a != b), &[_]bool{ true, false, true, true, true, true })); | ||
| 825 | try expect(mem.eql(bool, &@as([6]bool, a > b), &[_]bool{ true, false, false, false, false, true })); | ||
| 826 | try expect(mem.eql(bool, &@as([6]bool, a >= b), &[_]bool{ true, true, false, false, false, true })); | ||
| 827 | } | ||
| 828 | }; | ||
| 829 | try S.doTheTest(); | ||
| 830 | comptime try S.doTheTest(); | ||
| 831 | } | ||
| 832 | |||
| 833 | test "compare undefined literal with comptime_int" { | ||
| 834 | var x = undefined == 1; | ||
| 835 | // x is now undefined with type bool | ||
| 836 | x = true; | ||
| 837 | try expect(x); | ||
| 838 | } | ||
| 839 | |||
| 840 | test "signed zeros are represented properly" { | ||
| 841 | const S = struct { | ||
| 842 | fn doTheTest() !void { | ||
| 843 | inline for ([_]type{ f16, f32, f64, f128 }) |T| { | ||
| 844 | const ST = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); | ||
| 845 | var as_fp_val = -@as(T, 0.0); | ||
| 846 | var as_uint_val = @bitCast(ST, as_fp_val); | ||
| 847 | // Ensure the sign bit is set. | ||
| 848 | try expect(as_uint_val >> (@typeInfo(T).Float.bits - 1) == 1); | ||
| 849 | } | ||
| 850 | } | ||
| 851 | }; | ||
| 852 | |||
| 853 | try S.doTheTest(); | ||
| 854 | comptime try S.doTheTest(); | ||
| 855 | } | ||