authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-28 15:45:58-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-28 16:12:24-07:00
log79bc5891c1c4cde0592fe1b10b6c9a85914155cf
tree92999a5e04df045c03d63e4d02e53ad796c70911
parent1e805df81d51a338eefaff0535e5f1cca9e22028

stage2: more arithmetic support

* 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 operations

12 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),
365363
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),
374374
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 can376 // 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}
888888
889fn 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
893fn 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 result897/// 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}
23722384
2373fn analyzeBlockBody(2385fn 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();
58685880
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);
58765887
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 };
58855910
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}
58895914
...@@ -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}
61436168
6144fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {6169fn 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();
61476177
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);
61566185
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}
61596188
6160fn zirOverflowArithmetic(6189fn zirOverflowArithmetic(
...@@ -6187,6 +6216,7 @@ fn zirSatArithmetic(...@@ -6187,6 +6216,7 @@ fn zirSatArithmetic(
6187fn analyzeArithmetic(6216fn 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 };
62486278
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);
62536285
...@@ -6267,86 +6299,499 @@ fn analyzeArithmetic(...@@ -6267,86 +6299,499 @@ fn analyzeArithmetic(
6267 });6299 });
6268 }6300 }
62696301
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 calculations6307 .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| {
62856321 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 else6326 }
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 else6333 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 else6340 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 else6347 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 };
63176353 } else break :rs .{ .src = lhs_src, .air_tag = .add };
6318 log.debug("{s}({}, {}) result: {}", .{ @tagName(zir_tag), lhs_val, rhs_val, value });6354 },
63196355 .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 }
63276492
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 };
63486792
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}
63516796
6352fn zirLoad(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {6797fn 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(
7401fn floatOpAllowed(tag: Zir.Inst.Tag) bool {7846fn floatOpAllowed(tag: Zir.Inst.Tag) bool {
7402 // extend this swich as additional operators are implemented7847 // 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}
80708515
8071fn 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
8077fn zirRem(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
8078 return sema.zirArithmetic(block, inst);
8079}
8080
8081fn zirShlExact(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {8516fn 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 value399 /// 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,
831831
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),
835836
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 }
13551356
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 "),
900902
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),
12491250
...@@ -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 }
20972098
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 }
16181618
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 }
18421870
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 without1948 // 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 }
18771981
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 without2008 // 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
2365var 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;
6const minInt = std.math.minInt;6const minInt = std.math.minInt;
7const mem = std.mem;7const mem = std.mem;
88
9test "division" {
10 try testDivision();
11 comptime try testDivision();
12}
13fn 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}
79fn div(comptime T: type, a: T, b: T) T {
80 return a / b;
81}
82fn divExact(comptime T: type, a: T, b: T) T {
83 return @divExact(a, b);
84}
85fn divFloor(comptime T: type, a: T, b: T) T {
86 return @divFloor(a, b);
87}
88fn divTrunc(comptime T: type, a: T, b: T) T {
89 return @divTrunc(a, b);
90}
91fn mod(comptime T: type, a: T, b: T) T {
92 return @mod(a, b);
93}
94
95test "@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
105test "@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
112test "@*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
120test "@clz" {
121 try testClz();
122 comptime try testClz();
123}
124
125fn 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
134test "@clz vectors" {
135 try testClzVectors();
136 comptime try testClzVectors();
137}
138
139fn 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
149test "@ctz" {
150 try testCtz();
151 comptime try testCtz();
152}
153
154fn 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
161test "@ctz vectors" {
162 try testClzVectors();
163 comptime try testClzVectors();
164}
165
166fn 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
174test "assignment operators" {9test "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 {
218fn assertFalse(b: bool) !void {53fn assertFalse(b: bool) !void {
219 try expect(!b);54 try expect(!b);
220}55}
221
222test "const number literal" {
223 const one = 1;
224 const eleven = ten + one;
225
226 try expect(eleven == 11);
227}
228const ten = 10;
229
230test "unsigned wrapping" {
231 try testUnsignedWrappingEval(maxInt(u32));
232 comptime try testUnsignedWrappingEval(maxInt(u32));
233}
234fn 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
241test "signed wrapping" {
242 try testSignedWrappingEval(maxInt(i32));
243 comptime try testSignedWrappingEval(maxInt(i32));
244}
245fn 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
252test "signed negation wrapping" {
253 try testSignedNegationWrappingEval(minInt(i16));
254 comptime try testSignedNegationWrappingEval(minInt(i16));
255}
256fn testSignedNegationWrappingEval(x: i16) !void {
257 try expect(x == -32768);
258 const neg = -%x;
259 try expect(neg == -32768);
260}
261
262test "unsigned negation wrapping" {
263 try testUnsignedNegationWrappingEval(1);
264 comptime try testUnsignedNegationWrappingEval(1);
265}
266fn testUnsignedNegationWrappingEval(x: u16) !void {
267 try expect(x == 1);
268 const neg = -%x;
269 try expect(neg == maxInt(u16));
270}
271
272test "unsigned 64-bit division" {
273 try test_u64_div();
274 comptime try test_u64_div();
275}
276fn test_u64_div() !void {
277 const result = divWithResult(1152921504606846976, 34359738365);
278 try expect(result.quotient == 33554432);
279 try expect(result.remainder == 100663296);
280}
281fn divWithResult(a: u64, b: u64) DivResult {
282 return DivResult{
283 .quotient = a / b,
284 .remainder = a % b,
285 };
286}
287const DivResult = struct {
288 quotient: u64,
289 remainder: u64,
290};
291
292test "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
302fn testBinaryNot(x: u16) !void {
303 try expect(~x == 0b0101010101010101);
304}
305
306test "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
325test "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
333fn testFloatEqualityImpl(x: f64, y: f64) !void {
334 const y2 = x + 1.0;
335 try expect(y == y2);
336}
337
338test "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
347test "hex float literal parsing" {
348 comptime try expect(0x1.0 == 1.0);
349}
350
351test "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
364test "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
454test "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
482test "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
493test "truncating shift left" {
494 try testShlTrunc(maxInt(u16));
495 comptime try testShlTrunc(maxInt(u16));
496}
497fn testShlTrunc(x: u16) !void {
498 const shifted = x << 1;
499 try expect(shifted == 65534);
500}
501
502test "truncating shift right" {
503 try testShrTrunc(maxInt(u16));
504 comptime try testShrTrunc(maxInt(u16));
505}
506fn testShrTrunc(x: u16) !void {
507 const shifted = x >> 1;
508 try expect(shifted == 32767);
509}
510
511test "exact shift left" {
512 try testShlExact(0b00110101);
513 comptime try testShlExact(0b00110101);
514}
515fn testShlExact(x: u8) !void {
516 const shifted = @shlExact(x, 2);
517 try expect(shifted == 0b11010100);
518}
519
520test "exact shift right" {
521 try testShrExact(0b10110100);
522 comptime try testShrExact(0b10110100);
523}
524fn testShrExact(x: u8) !void {
525 const shifted = @shrExact(x, 2);
526 try expect(shifted == 0b00101101);
527}
528
529test "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
548test "comptime_int addition" {
549 comptime {
550 try expect(35361831660712422535336160538497375248 + 101752735581729509668353361206450473702 == 137114567242441932203689521744947848950);
551 try expect(594491908217841670578297176641415611445982232488944558774612 + 390603545391089362063884922208143568023166603618446395589768 == 985095453608931032642182098849559179469148836107390954364380);
552 }
553}
554
555test "comptime_int multiplication" {
556 comptime {
557 try expect(
558 45960427431263824329884196484953148229 * 128339149605334697009938835852565949723 == 5898522172026096622534201617172456926982464453350084962781392314016180490567,
559 );
560 try expect(
561 594491908217841670578297176641415611445982232488944558774612 * 390603545391089362063884922208143568023166603618446395589768 == 232210647056203049913662402532976186578842425262306016094292237500303028346593132411865381225871291702600263463125370016,
562 );
563 }
564}
565
566test "comptime_int shifting" {
567 comptime {
568 try expect((@as(u128, 1) << 127) == 0x80000000000000000000000000000000);
569 }
570}
571
572test "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
589test "comptime_int multi-limb partial shift right" {
590 comptime {
591 var a = 0x1ffffffffeeeeeeee;
592 a >>= 16;
593 try expect(a == 0x1ffffffffeeee);
594 }
595}
596
597test "xor" {
598 try test_xor();
599 comptime try test_xor();
600}
601
602fn 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
610test "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
623test "f128" {
624 try test_f128();
625 comptime try test_f128();
626}
627
628fn make_f128(x: f128) f128 {
629 return x;
630}
631
632fn 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
642fn should_not_be_zero(x: f128) !void {
643 try expect(x != 0.0);
644}
645
646test "comptime float rem int" {
647 comptime {
648 var x = @as(f32, 1) % 2;
649 try expect(x == 1.0);
650 }
651}
652
653test "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
663fn 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
668test "@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
682fn testSqrt(comptime T: type, x: T) !void {
683 try expect(@sqrt(x * x) == x);
684}
685
686test "@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
702fn testFabs(comptime T: type, x: T) !void {
703 const y = -x;
704 const z = @fabs(y);
705 try expectEqual(x, z);
706}
707
708test "@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
725fn 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
731test "@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
748fn 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
754test "@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
771fn 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
785test "@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
802fn 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
808test "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
816fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int {
817 return a + b;
818}
819
820test "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
835test "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
846fn 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
857test "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
864test "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
881test "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
888test "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 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const expectEqual = std.testing.expectEqual;
4const expectEqualSlices = std.testing.expectEqualSlices;
5const maxInt = std.math.maxInt;
6const minInt = std.math.minInt;
7const mem = std.mem;
8
9test "division" {
10 try testDivision();
11 comptime try testDivision();
12}
13fn 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}
79fn div(comptime T: type, a: T, b: T) T {
80 return a / b;
81}
82fn divExact(comptime T: type, a: T, b: T) T {
83 return @divExact(a, b);
84}
85fn divFloor(comptime T: type, a: T, b: T) T {
86 return @divFloor(a, b);
87}
88fn divTrunc(comptime T: type, a: T, b: T) T {
89 return @divTrunc(a, b);
90}
91fn mod(comptime T: type, a: T, b: T) T {
92 return @mod(a, b);
93}
94
95test "@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
105test "@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
112test "@*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
120test "@clz" {
121 try testClz();
122 comptime try testClz();
123}
124
125fn 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
134test "@clz vectors" {
135 try testClzVectors();
136 comptime try testClzVectors();
137}
138
139fn 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
149test "@ctz" {
150 try testCtz();
151 comptime try testCtz();
152}
153
154fn 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
161test "@ctz vectors" {
162 try testClzVectors();
163 comptime try testClzVectors();
164}
165
166fn 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
174test "const number literal" {
175 const one = 1;
176 const eleven = ten + one;
177
178 try expect(eleven == 11);
179}
180const ten = 10;
181
182test "unsigned wrapping" {
183 try testUnsignedWrappingEval(maxInt(u32));
184 comptime try testUnsignedWrappingEval(maxInt(u32));
185}
186fn 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
193test "signed wrapping" {
194 try testSignedWrappingEval(maxInt(i32));
195 comptime try testSignedWrappingEval(maxInt(i32));
196}
197fn 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
204test "signed negation wrapping" {
205 try testSignedNegationWrappingEval(minInt(i16));
206 comptime try testSignedNegationWrappingEval(minInt(i16));
207}
208fn testSignedNegationWrappingEval(x: i16) !void {
209 try expect(x == -32768);
210 const neg = -%x;
211 try expect(neg == -32768);
212}
213
214test "unsigned negation wrapping" {
215 try testUnsignedNegationWrappingEval(1);
216 comptime try testUnsignedNegationWrappingEval(1);
217}
218fn testUnsignedNegationWrappingEval(x: u16) !void {
219 try expect(x == 1);
220 const neg = -%x;
221 try expect(neg == maxInt(u16));
222}
223
224test "unsigned 64-bit division" {
225 try test_u64_div();
226 comptime try test_u64_div();
227}
228fn test_u64_div() !void {
229 const result = divWithResult(1152921504606846976, 34359738365);
230 try expect(result.quotient == 33554432);
231 try expect(result.remainder == 100663296);
232}
233fn divWithResult(a: u64, b: u64) DivResult {
234 return DivResult{
235 .quotient = a / b,
236 .remainder = a % b,
237 };
238}
239const DivResult = struct {
240 quotient: u64,
241 remainder: u64,
242};
243
244test "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
254fn testBinaryNot(x: u16) !void {
255 try expect(~x == 0b0101010101010101);
256}
257
258test "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
277test "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
285fn testFloatEqualityImpl(x: f64, y: f64) !void {
286 const y2 = x + 1.0;
287 try expect(y == y2);
288}
289
290test "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
299test "hex float literal parsing" {
300 comptime try expect(0x1.0 == 1.0);
301}
302
303test "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
316test "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
406test "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
434test "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
445test "truncating shift left" {
446 try testShlTrunc(maxInt(u16));
447 comptime try testShlTrunc(maxInt(u16));
448}
449fn testShlTrunc(x: u16) !void {
450 const shifted = x << 1;
451 try expect(shifted == 65534);
452}
453
454test "truncating shift right" {
455 try testShrTrunc(maxInt(u16));
456 comptime try testShrTrunc(maxInt(u16));
457}
458fn testShrTrunc(x: u16) !void {
459 const shifted = x >> 1;
460 try expect(shifted == 32767);
461}
462
463test "exact shift left" {
464 try testShlExact(0b00110101);
465 comptime try testShlExact(0b00110101);
466}
467fn testShlExact(x: u8) !void {
468 const shifted = @shlExact(x, 2);
469 try expect(shifted == 0b11010100);
470}
471
472test "exact shift right" {
473 try testShrExact(0b10110100);
474 comptime try testShrExact(0b10110100);
475}
476fn testShrExact(x: u8) !void {
477 const shifted = @shrExact(x, 2);
478 try expect(shifted == 0b00101101);
479}
480
481test "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
500test "comptime_int addition" {
501 comptime {
502 try expect(35361831660712422535336160538497375248 + 101752735581729509668353361206450473702 == 137114567242441932203689521744947848950);
503 try expect(594491908217841670578297176641415611445982232488944558774612 + 390603545391089362063884922208143568023166603618446395589768 == 985095453608931032642182098849559179469148836107390954364380);
504 }
505}
506
507test "comptime_int multiplication" {
508 comptime {
509 try expect(
510 45960427431263824329884196484953148229 * 128339149605334697009938835852565949723 == 5898522172026096622534201617172456926982464453350084962781392314016180490567,
511 );
512 try expect(
513 594491908217841670578297176641415611445982232488944558774612 * 390603545391089362063884922208143568023166603618446395589768 == 232210647056203049913662402532976186578842425262306016094292237500303028346593132411865381225871291702600263463125370016,
514 );
515 }
516}
517
518test "comptime_int shifting" {
519 comptime {
520 try expect((@as(u128, 1) << 127) == 0x80000000000000000000000000000000);
521 }
522}
523
524test "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
541test "comptime_int multi-limb partial shift right" {
542 comptime {
543 var a = 0x1ffffffffeeeeeeee;
544 a >>= 16;
545 try expect(a == 0x1ffffffffeeee);
546 }
547}
548
549test "xor" {
550 try test_xor();
551 comptime try test_xor();
552}
553
554fn 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
562test "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
575test "f128" {
576 try test_f128();
577 comptime try test_f128();
578}
579
580fn make_f128(x: f128) f128 {
581 return x;
582}
583
584fn 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
594fn should_not_be_zero(x: f128) !void {
595 try expect(x != 0.0);
596}
597
598test "comptime float rem int" {
599 comptime {
600 var x = @as(f32, 1) % 2;
601 try expect(x == 1.0);
602 }
603}
604
605test "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
615fn 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
620test "@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
634fn testSqrt(comptime T: type, x: T) !void {
635 try expect(@sqrt(x * x) == x);
636}
637
638test "@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
654fn testFabs(comptime T: type, x: T) !void {
655 const y = -x;
656 const z = @fabs(y);
657 try expectEqual(x, z);
658}
659
660test "@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
677fn 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
683test "@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
700fn 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
706test "@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
723fn 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
737test "@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
754fn 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
760test "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
768fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int {
769 return a + b;
770}
771
772test "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
787test "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
798fn 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
809test "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
816test "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
833test "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
840test "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}