Commit 79bc5891c1
Changed files (12)
src/codegen/c.zig
@@ -897,6 +897,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
// that wrapping is UB.
.div => try airBinOp( f, inst, " / "),
.rem => try airBinOp( f, inst, " % "),
+ // TODO implement modulus division
+ .mod => try airBinOp( f, inst, " mod "),
.cmp_eq => try airBinOp(f, inst, " == "),
.cmp_gt => try airBinOp(f, inst, " > "),
src/codegen/llvm.zig
@@ -1244,6 +1244,7 @@ pub const FuncGen = struct {
.mulwrap => try self.airMul(inst, true),
.div => try self.airDiv(inst),
.rem => try self.airRem(inst),
+ .mod => try self.airMod(inst),
.ptr_add => try self.airPtrAdd(inst),
.ptr_sub => try self.airPtrSub(inst),
@@ -2095,6 +2096,34 @@ pub const FuncGen = struct {
return self.builder.buildURem(lhs, rhs, "");
}
+ fn airMod(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+ if (self.liveness.isUnused(inst)) return null;
+
+ const bin_op = self.air.instructions.items(.data)[inst].bin_op;
+ const lhs = try self.resolveInst(bin_op.lhs);
+ const rhs = try self.resolveInst(bin_op.rhs);
+ const inst_ty = self.air.typeOfIndex(inst);
+ const inst_llvm_ty = try self.dg.llvmType(inst_ty);
+
+ if (inst_ty.isRuntimeFloat()) {
+ const a = self.builder.buildFRem(lhs, rhs, "");
+ const b = self.builder.buildFAdd(a, rhs, "");
+ const c = self.builder.buildFRem(b, rhs, "");
+ const zero = inst_llvm_ty.constNull();
+ const ltz = self.builder.buildFCmp(.OLT, lhs, zero, "");
+ return self.builder.buildSelect(ltz, c, a, "");
+ }
+ if (inst_ty.isSignedInt()) {
+ const a = self.builder.buildSRem(lhs, rhs, "");
+ const b = self.builder.buildNSWAdd(a, rhs, "");
+ const c = self.builder.buildSRem(b, rhs, "");
+ const zero = inst_llvm_ty.constNull();
+ const ltz = self.builder.buildICmp(.SLT, lhs, zero, "");
+ return self.builder.buildSelect(ltz, c, a, "");
+ }
+ return self.builder.buildURem(lhs, rhs, "");
+ }
+
fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
if (self.liveness.isUnused(inst))
return null;
src/Air.zig
@@ -69,10 +69,16 @@ pub const Inst = struct {
/// is the same as both operands.
/// Uses the `bin_op` field.
div,
- /// Integer or float remainder.
- /// Both operands are guaranteed to be the same type, and the result type is the same as both operands.
+ /// Integer or float remainder division.
+ /// Both operands are guaranteed to be the same type, and the result type
+ /// is the same as both operands.
/// Uses the `bin_op` field.
rem,
+ /// Integer or float modulus division.
+ /// Both operands are guaranteed to be the same type, and the result type
+ /// is the same as both operands.
+ /// Uses the `bin_op` field.
+ mod,
/// Add an offset to a pointer, returning a new pointer.
/// The offset is in element type units, not bytes.
/// Wrapping is undefined behavior.
@@ -568,6 +574,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
.mulwrap,
.div,
.rem,
+ .mod,
.bit_and,
.bit_or,
.xor,
src/codegen.zig
@@ -832,6 +832,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.mulwrap => try self.airMulWrap(inst),
.div => try self.airDiv(inst),
.rem => try self.airRem(inst),
+ .mod => try self.airMod(inst),
.cmp_lt => try self.airCmp(inst, .lt),
.cmp_lte => try self.airCmp(inst, .lte),
@@ -1353,6 +1354,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
}
+ fn airMod(self: *Self, inst: Air.Inst.Index) !void {
+ const bin_op = self.air.instructions.items(.data)[inst].bin_op;
+ const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
+ else => return self.fail("TODO implement mod for {}", .{self.target.cpu.arch}),
+ };
+ return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
+ }
+
fn airBitAnd(self: *Self, inst: Air.Inst.Index) !void {
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
src/Liveness.zig
@@ -232,6 +232,7 @@ fn analyzeInst(
.mulwrap,
.div,
.rem,
+ .mod,
.ptr_add,
.ptr_sub,
.bit_and,
src/print_air.zig
@@ -110,6 +110,7 @@ const Writer = struct {
.mulwrap,
.div,
.rem,
+ .mod,
.ptr_add,
.ptr_sub,
.bit_and,
src/Sema.zig
@@ -319,8 +319,6 @@ pub fn analyzeBody(
.div_exact => try sema.zirDivExact(block, inst),
.div_floor => try sema.zirDivFloor(block, inst),
.div_trunc => try sema.zirDivTrunc(block, inst),
- .mod => try sema.zirMod(block, inst),
- .rem => try sema.zirRem(block, inst),
.shl_exact => try sema.zirShlExact(block, inst),
.shr_exact => try sema.zirShrExact(block, inst),
.bit_offset_of => try sema.zirBitOffsetOf(block, inst),
@@ -363,14 +361,16 @@ pub fn analyzeBody(
.error_set_decl_anon => try sema.zirErrorSetDecl(block, inst, .anon),
.error_set_decl_func => try sema.zirErrorSetDecl(block, inst, .func),
- .add => try sema.zirArithmetic(block, inst),
- .addwrap => try sema.zirArithmetic(block, inst),
- .div => try sema.zirArithmetic(block, inst),
- .mod_rem => try sema.zirArithmetic(block, inst),
- .mul => try sema.zirArithmetic(block, inst),
- .mulwrap => try sema.zirArithmetic(block, inst),
- .sub => try sema.zirArithmetic(block, inst),
- .subwrap => try sema.zirArithmetic(block, inst),
+ .add => try sema.zirArithmetic(block, inst, .add),
+ .addwrap => try sema.zirArithmetic(block, inst, .addwrap),
+ .div => try sema.zirArithmetic(block, inst, .div),
+ .mod_rem => try sema.zirArithmetic(block, inst, .mod_rem),
+ .mod => try sema.zirArithmetic(block, inst, .mod),
+ .rem => try sema.zirArithmetic(block, inst, .rem),
+ .mul => try sema.zirArithmetic(block, inst, .mul),
+ .mulwrap => try sema.zirArithmetic(block, inst, .mulwrap),
+ .sub => try sema.zirArithmetic(block, inst, .sub),
+ .subwrap => try sema.zirArithmetic(block, inst, .subwrap),
// Instructions that we know to *always* be noreturn based solely on their tag.
// 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
return sema.mod.fail(&block.base, src, "use of undefined value here causes undefined behavior", .{});
}
+fn failWithDivideByZero(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) CompileError {
+ return sema.mod.fail(&block.base, src, "division by zero here causes undefined behavior", .{});
+}
+
+fn failWithModRemNegative(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, lhs_ty: Type, rhs_ty: Type) CompileError {
+ return sema.mod.fail(&block.base, src, "remainder division with '{}' and '{}': signed integers and floats must use @rem or @mod", .{ lhs_ty, rhs_ty });
+}
+
/// Appropriate to call when the coercion has already been done by result
/// location semantics. Asserts the value fits in the provided `Int` type.
/// Only supports `Int` types 64 bits or less.
@@ -2366,8 +2374,12 @@ fn resolveBlockBody(
body: []const Zir.Inst.Index,
merges: *Scope.Block.Merges,
) CompileError!Air.Inst.Ref {
- _ = try sema.analyzeBody(child_block, body);
- return sema.analyzeBlockBody(parent_block, src, child_block, merges);
+ if (child_block.is_comptime) {
+ return sema.resolveBody(child_block, body);
+ } else {
+ _ = try sema.analyzeBody(child_block, body);
+ return sema.analyzeBlockBody(parent_block, src, child_block, merges);
+ }
}
fn analyzeBlockBody(
@@ -5867,23 +5879,36 @@ fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A
defer tracy.end();
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
- const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
const lhs = sema.resolveInst(extra.lhs);
const rhs = sema.resolveInst(extra.rhs);
- if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| {
- if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| {
- if (lhs_val.isUndef() or rhs_val.isUndef()) {
- return sema.addConstUndef(sema.typeOf(lhs));
- }
- return sema.mod.fail(&block.base, src, "TODO implement comptime shl", .{});
+ const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs);
+ const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs);
+
+ const runtime_src = if (maybe_lhs_val) |lhs_val| rs: {
+ const lhs_ty = sema.typeOf(lhs);
+
+ if (lhs_val.isUndef()) return sema.addConstUndef(lhs_ty);
+ const rhs_val = maybe_rhs_val orelse break :rs rhs_src;
+ if (rhs_val.isUndef()) return sema.addConstUndef(lhs_ty);
+
+ // If rhs is 0, return lhs without doing any calculations.
+ if (rhs_val.compareWithZero(.eq)) {
+ return sema.addConstant(lhs_ty, lhs_val);
}
- }
+ const val = try lhs_val.shl(rhs_val, sema.arena);
+ return sema.addConstant(lhs_ty, val);
+ } else rs: {
+ if (maybe_rhs_val) |rhs_val| {
+ if (rhs_val.isUndef()) return sema.addConstUndef(sema.typeOf(lhs));
+ }
+ break :rs lhs_src;
+ };
- try sema.requireRuntimeBlock(block, src);
+ try sema.requireRuntimeBlock(block, runtime_src);
return block.addBinOp(.shl, lhs, rhs);
}
@@ -6141,11 +6166,15 @@ fn zirNegate(
return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src);
}
-fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+fn zirArithmetic(
+ sema: *Sema,
+ block: *Scope.Block,
+ inst: Zir.Inst.Index,
+ zir_tag: Zir.Inst.Tag,
+) CompileError!Air.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
- const tag_override = block.sema.code.instructions.items(.tag)[inst];
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
sema.src = .{ .node_offset_bin_op = inst_data.src_node };
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
const lhs = sema.resolveInst(extra.lhs);
const rhs = sema.resolveInst(extra.rhs);
- return sema.analyzeArithmetic(block, tag_override, lhs, rhs, sema.src, lhs_src, rhs_src);
+ return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src);
}
fn zirOverflowArithmetic(
@@ -6187,6 +6216,7 @@ fn zirSatArithmetic(
fn analyzeArithmetic(
sema: *Sema,
block: *Scope.Block,
+ /// TODO performance investigation: make this comptime?
zir_tag: Zir.Inst.Tag,
lhs: Air.Inst.Ref,
rhs: Air.Inst.Ref,
@@ -6204,7 +6234,7 @@ fn analyzeArithmetic(
lhs_ty.arrayLen(), rhs_ty.arrayLen(),
});
}
- return sema.mod.fail(&block.base, src, "TODO implement support for vectors in zirBinOp", .{});
+ return sema.mod.fail(&block.base, src, "TODO implement support for vectors in Sema.analyzeArithmetic", .{});
} else if (lhs_zig_ty_tag == .Vector or rhs_zig_ty_tag == .Vector) {
return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{
lhs_ty, rhs_ty,
@@ -6247,7 +6277,9 @@ fn analyzeArithmetic(
};
const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
- const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]LazySrcLoc{ lhs_src, rhs_src } });
+ const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
+ .override = &[_]LazySrcLoc{ lhs_src, rhs_src },
+ });
const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
@@ -6267,86 +6299,499 @@ fn analyzeArithmetic(
});
}
- if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| {
- if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| {
- if (lhs_val.isUndef() or rhs_val.isUndef()) {
- return sema.addConstUndef(resolved_type);
- }
- // incase rhs is 0, simply return lhs without doing any calculations
- // TODO Once division is implemented we should throw an error when dividing by 0.
- if (rhs_val.compareWithZero(.eq)) {
- switch (zir_tag) {
- .add, .addwrap, .sub, .subwrap => {
- return sema.addConstant(scalar_type, lhs_val);
- },
- else => {},
+ const target = sema.mod.getTarget();
+ const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs);
+ const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs);
+ const rs: struct { src: LazySrcLoc, air_tag: Air.Inst.Tag } = rs: {
+ switch (zir_tag) {
+ .add => {
+ // For integers:
+ // If either of the operands are zero, then the other operand is
+ // returned, even if it is undefined.
+ // If either of the operands are undefined, it's a compile error
+ // because there is a possible value for which the addition would
+ // overflow (max_int), causing illegal behavior.
+ // For floats: either operand being undef makes the result undef.
+ if (maybe_lhs_val) |lhs_val| {
+ if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) {
+ return casted_rhs;
+ }
}
- }
-
- const value = switch (zir_tag) {
- .add => blk: {
- const val = if (is_int)
- try lhs_val.intAdd(rhs_val, sema.arena)
- else
- try lhs_val.floatAdd(rhs_val, scalar_type, sema.arena);
- break :blk val;
- },
- .sub => blk: {
- const val = if (is_int)
- try lhs_val.intSub(rhs_val, sema.arena)
- else
- try lhs_val.floatSub(rhs_val, scalar_type, sema.arena);
- break :blk val;
- },
- .div => blk: {
- const val = if (is_int)
- try lhs_val.intDiv(rhs_val, sema.arena)
- else
- try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena);
- break :blk val;
- },
- .mul => blk: {
- const val = if (is_int)
- try lhs_val.intMul(rhs_val, sema.arena)
- else
- try lhs_val.floatMul(rhs_val, scalar_type, sema.arena);
- break :blk val;
- },
- else => return sema.mod.fail(&block.base, src, "TODO implement comptime arithmetic for operand '{s}'", .{@tagName(zir_tag)}),
- };
-
- log.debug("{s}({}, {}) result: {}", .{ @tagName(zir_tag), lhs_val, rhs_val, value });
-
- return sema.addConstant(scalar_type, value);
- } else {
- try sema.requireRuntimeBlock(block, rhs_src);
- }
- } else {
- try sema.requireRuntimeBlock(block, lhs_src);
- }
+ if (maybe_rhs_val) |rhs_val| {
+ if (rhs_val.isUndef()) {
+ if (is_int) {
+ return sema.failWithUseOfUndef(block, rhs_src);
+ } else {
+ return sema.addConstUndef(scalar_type);
+ }
+ }
+ if (rhs_val.compareWithZero(.eq)) {
+ return casted_lhs;
+ }
+ }
+ if (maybe_lhs_val) |lhs_val| {
+ if (lhs_val.isUndef()) {
+ if (is_int) {
+ return sema.failWithUseOfUndef(block, lhs_src);
+ } else {
+ return sema.addConstUndef(scalar_type);
+ }
+ }
+ if (maybe_rhs_val) |rhs_val| {
+ if (is_int) {
+ return sema.addConstant(
+ scalar_type,
+ try lhs_val.intAdd(rhs_val, sema.arena),
+ );
+ } else {
+ return sema.addConstant(
+ scalar_type,
+ try lhs_val.floatAdd(rhs_val, scalar_type, sema.arena),
+ );
+ }
+ } else break :rs .{ .src = rhs_src, .air_tag = .add };
+ } else break :rs .{ .src = lhs_src, .air_tag = .add };
+ },
+ .addwrap => {
+ // Integers only; floats are checked above.
+ // If either of the operands are zero, then the other operand is
+ // returned, even if it is undefined.
+ // If either of the operands are undefined, the result is undefined.
+ if (maybe_lhs_val) |lhs_val| {
+ if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) {
+ return casted_rhs;
+ }
+ }
+ if (maybe_rhs_val) |rhs_val| {
+ if (rhs_val.isUndef()) {
+ return sema.addConstUndef(scalar_type);
+ }
+ if (rhs_val.compareWithZero(.eq)) {
+ return casted_lhs;
+ }
+ if (maybe_lhs_val) |lhs_val| {
+ return sema.addConstant(
+ scalar_type,
+ try lhs_val.numberAddWrap(rhs_val, scalar_type, sema.arena, target),
+ );
+ } else break :rs .{ .src = lhs_src, .air_tag = .addwrap };
+ } else break :rs .{ .src = rhs_src, .air_tag = .addwrap };
+ },
+ .sub => {
+ // For integers:
+ // If the rhs is zero, then the other operand is
+ // returned, even if it is undefined.
+ // If either of the operands are undefined, it's a compile error
+ // because there is a possible value for which the subtraction would
+ // overflow, causing illegal behavior.
+ // For floats: either operand being undef makes the result undef.
+ if (maybe_rhs_val) |rhs_val| {
+ if (rhs_val.isUndef()) {
+ if (is_int) {
+ return sema.failWithUseOfUndef(block, rhs_src);
+ } else {
+ return sema.addConstUndef(scalar_type);
+ }
+ }
+ if (rhs_val.compareWithZero(.eq)) {
+ return casted_lhs;
+ }
+ }
+ if (maybe_lhs_val) |lhs_val| {
+ if (lhs_val.isUndef()) {
+ if (is_int) {
+ return sema.failWithUseOfUndef(block, lhs_src);
+ } else {
+ return sema.addConstUndef(scalar_type);
+ }
+ }
+ if (maybe_rhs_val) |rhs_val| {
+ if (is_int) {
+ return sema.addConstant(
+ scalar_type,
+ try lhs_val.intSub(rhs_val, sema.arena),
+ );
+ } else {
+ return sema.addConstant(
+ scalar_type,
+ try lhs_val.floatSub(rhs_val, scalar_type, sema.arena),
+ );
+ }
+ } else break :rs .{ .src = rhs_src, .air_tag = .sub };
+ } else break :rs .{ .src = lhs_src, .air_tag = .sub };
+ },
+ .subwrap => {
+ // Integers only; floats are checked above.
+ // If the RHS is zero, then the other operand is returned, even if it is undefined.
+ // If either of the operands are undefined, the result is undefined.
+ if (maybe_rhs_val) |rhs_val| {
+ if (rhs_val.isUndef()) {
+ return sema.addConstUndef(scalar_type);
+ }
+ if (rhs_val.compareWithZero(.eq)) {
+ return casted_lhs;
+ }
+ }
+ if (maybe_lhs_val) |lhs_val| {
+ if (lhs_val.isUndef()) {
+ return sema.addConstUndef(scalar_type);
+ }
+ if (maybe_rhs_val) |rhs_val| {
+ return sema.addConstant(
+ scalar_type,
+ try lhs_val.numberSubWrap(rhs_val, scalar_type, sema.arena, target),
+ );
+ } else break :rs .{ .src = rhs_src, .air_tag = .subwrap };
+ } else break :rs .{ .src = lhs_src, .air_tag = .subwrap };
+ },
+ .div => {
+ // For integers:
+ // If the lhs is zero, then zero is returned regardless of rhs.
+ // If the rhs is zero, compile error for division by zero.
+ // If the rhs is undefined, compile error because there is a possible
+ // value (zero) for which the division would be illegal behavior.
+ // If the lhs is undefined:
+ // * if lhs type is signed:
+ // * if rhs is comptime-known and not -1, result is undefined
+ // * if rhs is -1 or runtime-known, compile error because there is a
+ // possible value (-min_int * -1) for which division would be
+ // illegal behavior.
+ // * if lhs type is unsigned, undef is returned regardless of rhs.
+ // For floats:
+ // If the rhs is zero, compile error for division by zero.
+ // If the rhs is undefined, compile error because there is a possible
+ // value (zero) for which the division would be illegal behavior.
+ // If the lhs is undefined, result is undefined.
+ if (maybe_lhs_val) |lhs_val| {
+ if (!lhs_val.isUndef()) {
+ if (lhs_val.compareWithZero(.eq)) {
+ return sema.addConstant(scalar_type, Value.zero);
+ }
+ }
+ }
+ if (maybe_rhs_val) |rhs_val| {
+ if (rhs_val.isUndef()) {
+ return sema.failWithUseOfUndef(block, rhs_src);
+ }
+ if (rhs_val.compareWithZero(.eq)) {
+ return sema.failWithDivideByZero(block, rhs_src);
+ }
+ }
+ if (maybe_lhs_val) |lhs_val| {
+ if (lhs_val.isUndef()) {
+ if (lhs_ty.isSignedInt() and rhs_ty.isSignedInt()) {
+ if (maybe_rhs_val) |rhs_val| {
+ if (rhs_val.compare(.neq, Value.negative_one, scalar_type)) {
+ return sema.addConstUndef(scalar_type);
+ }
+ }
+ return sema.failWithUseOfUndef(block, rhs_src);
+ }
+ return sema.addConstUndef(scalar_type);
+ }
- if (zir_tag == .mod_rem) {
- const dirty_lhs = lhs_ty.isSignedInt() or lhs_ty.isRuntimeFloat();
- const dirty_rhs = rhs_ty.isSignedInt() or rhs_ty.isRuntimeFloat();
- if (dirty_lhs or dirty_rhs) {
- return sema.mod.fail(&block.base, src, "remainder division with '{}' and '{}': signed integers and floats must use @rem or @mod", .{ lhs_ty, rhs_ty });
+ if (maybe_rhs_val) |rhs_val| {
+ if (is_int) {
+ return sema.addConstant(
+ scalar_type,
+ try lhs_val.intDiv(rhs_val, sema.arena),
+ );
+ } else {
+ return sema.addConstant(
+ scalar_type,
+ try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena),
+ );
+ }
+ } else break :rs .{ .src = rhs_src, .air_tag = .div };
+ } else break :rs .{ .src = lhs_src, .air_tag = .div };
+ },
+ .mul => {
+ // For integers:
+ // If either of the operands are zero, the result is zero.
+ // If either of the operands are one, the result is the other
+ // operand, even if it is undefined.
+ // If either of the operands are undefined, it's a compile error
+ // because there is a possible value for which the addition would
+ // overflow (max_int), causing illegal behavior.
+ // For floats: either operand being undef makes the result undef.
+ if (maybe_lhs_val) |lhs_val| {
+ if (!lhs_val.isUndef()) {
+ if (lhs_val.compareWithZero(.eq)) {
+ return sema.addConstant(scalar_type, Value.zero);
+ }
+ if (lhs_val.compare(.eq, Value.one, scalar_type)) {
+ return casted_rhs;
+ }
+ }
+ }
+ if (maybe_rhs_val) |rhs_val| {
+ if (rhs_val.isUndef()) {
+ if (is_int) {
+ return sema.failWithUseOfUndef(block, rhs_src);
+ } else {
+ return sema.addConstUndef(scalar_type);
+ }
+ }
+ if (rhs_val.compareWithZero(.eq)) {
+ return sema.addConstant(scalar_type, Value.zero);
+ }
+ if (rhs_val.compare(.eq, Value.one, scalar_type)) {
+ return casted_lhs;
+ }
+ if (maybe_lhs_val) |lhs_val| {
+ if (lhs_val.isUndef()) {
+ if (is_int) {
+ return sema.failWithUseOfUndef(block, lhs_src);
+ } else {
+ return sema.addConstUndef(scalar_type);
+ }
+ }
+ if (is_int) {
+ return sema.addConstant(
+ scalar_type,
+ try lhs_val.intMul(rhs_val, sema.arena),
+ );
+ } else {
+ return sema.addConstant(
+ scalar_type,
+ try lhs_val.floatMul(rhs_val, scalar_type, sema.arena),
+ );
+ }
+ } else break :rs .{ .src = lhs_src, .air_tag = .mul };
+ } else break :rs .{ .src = rhs_src, .air_tag = .mul };
+ },
+ .mulwrap => {
+ // Integers only; floats are handled above.
+ // If either of the operands are zero, the result is zero.
+ // If either of the operands are one, the result is the other
+ // operand, even if it is undefined.
+ // If either of the operands are undefined, the result is undefined.
+ if (maybe_lhs_val) |lhs_val| {
+ if (!lhs_val.isUndef()) {
+ if (lhs_val.compareWithZero(.eq)) {
+ return sema.addConstant(scalar_type, Value.zero);
+ }
+ if (lhs_val.compare(.eq, Value.one, scalar_type)) {
+ return casted_rhs;
+ }
+ }
+ }
+ if (maybe_rhs_val) |rhs_val| {
+ if (rhs_val.isUndef()) {
+ return sema.addConstUndef(scalar_type);
+ }
+ if (rhs_val.compareWithZero(.eq)) {
+ return sema.addConstant(scalar_type, Value.zero);
+ }
+ if (rhs_val.compare(.eq, Value.one, scalar_type)) {
+ return casted_lhs;
+ }
+ if (maybe_lhs_val) |lhs_val| {
+ if (lhs_val.isUndef()) {
+ return sema.addConstUndef(scalar_type);
+ }
+ return sema.addConstant(
+ scalar_type,
+ try lhs_val.numberMulWrap(rhs_val, scalar_type, sema.arena, target),
+ );
+ } else break :rs .{ .src = lhs_src, .air_tag = .mulwrap };
+ } else break :rs .{ .src = rhs_src, .air_tag = .mulwrap };
+ },
+ .mod_rem => {
+ // For integers:
+ // Either operand being undef is a compile error because there exists
+ // a possible value (TODO what is it?) that would invoke illegal behavior.
+ // TODO: can lhs zero be handled better?
+ // TODO: can lhs undef be handled better?
+ //
+ // For floats:
+ // If the rhs is zero, compile error for division by zero.
+ // If the rhs is undefined, compile error because there is a possible
+ // value (zero) for which the division would be illegal behavior.
+ // If the lhs is undefined, result is undefined.
+ //
+ // For either one: if the result would be different between @mod and @rem,
+ // then emit a compile error saying you have to pick one.
+ if (is_int) {
+ if (maybe_lhs_val) |lhs_val| {
+ if (lhs_val.isUndef()) {
+ return sema.failWithUseOfUndef(block, lhs_src);
+ }
+ if (lhs_val.compareWithZero(.lt)) {
+ return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty);
+ }
+ } else if (lhs_ty.isSignedInt()) {
+ return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty);
+ }
+ if (maybe_rhs_val) |rhs_val| {
+ if (rhs_val.isUndef()) {
+ return sema.failWithUseOfUndef(block, rhs_src);
+ }
+ if (rhs_val.compareWithZero(.eq)) {
+ return sema.failWithDivideByZero(block, rhs_src);
+ }
+ if (rhs_val.compareWithZero(.lt)) {
+ return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty);
+ }
+ if (maybe_lhs_val) |lhs_val| {
+ return sema.addConstant(
+ scalar_type,
+ try lhs_val.intRem(rhs_val, sema.arena),
+ );
+ }
+ break :rs .{ .src = lhs_src, .air_tag = .rem };
+ } else if (rhs_ty.isSignedInt()) {
+ return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty);
+ } else {
+ break :rs .{ .src = rhs_src, .air_tag = .rem };
+ }
+ }
+ // float operands
+ if (maybe_rhs_val) |rhs_val| {
+ if (rhs_val.isUndef()) {
+ return sema.failWithUseOfUndef(block, rhs_src);
+ }
+ if (rhs_val.compareWithZero(.eq)) {
+ return sema.failWithDivideByZero(block, rhs_src);
+ }
+ if (rhs_val.compareWithZero(.lt)) {
+ return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty);
+ }
+ if (maybe_lhs_val) |lhs_val| {
+ if (lhs_val.isUndef() or lhs_val.compareWithZero(.lt)) {
+ return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty);
+ }
+ return sema.addConstant(
+ scalar_type,
+ try lhs_val.floatRem(rhs_val, sema.arena),
+ );
+ } else {
+ return sema.failWithModRemNegative(block, lhs_src, lhs_ty, rhs_ty);
+ }
+ } else {
+ return sema.failWithModRemNegative(block, rhs_src, lhs_ty, rhs_ty);
+ }
+ },
+ .rem => {
+ // For integers:
+ // Either operand being undef is a compile error because there exists
+ // a possible value (TODO what is it?) that would invoke illegal behavior.
+ // TODO: can lhs zero be handled better?
+ // TODO: can lhs undef be handled better?
+ //
+ // For floats:
+ // If the rhs is zero, compile error for division by zero.
+ // If the rhs is undefined, compile error because there is a possible
+ // value (zero) for which the division would be illegal behavior.
+ // If the lhs is undefined, result is undefined.
+ if (is_int) {
+ if (maybe_lhs_val) |lhs_val| {
+ if (lhs_val.isUndef()) {
+ return sema.failWithUseOfUndef(block, lhs_src);
+ }
+ }
+ if (maybe_rhs_val) |rhs_val| {
+ if (rhs_val.isUndef()) {
+ return sema.failWithUseOfUndef(block, rhs_src);
+ }
+ if (rhs_val.compareWithZero(.eq)) {
+ return sema.failWithDivideByZero(block, rhs_src);
+ }
+ if (maybe_lhs_val) |lhs_val| {
+ return sema.addConstant(
+ scalar_type,
+ try lhs_val.intRem(rhs_val, sema.arena),
+ );
+ }
+ break :rs .{ .src = lhs_src, .air_tag = .rem };
+ } else {
+ break :rs .{ .src = rhs_src, .air_tag = .rem };
+ }
+ }
+ // float operands
+ if (maybe_rhs_val) |rhs_val| {
+ if (rhs_val.isUndef()) {
+ return sema.failWithUseOfUndef(block, rhs_src);
+ }
+ if (rhs_val.compareWithZero(.eq)) {
+ return sema.failWithDivideByZero(block, rhs_src);
+ }
+ }
+ if (maybe_lhs_val) |lhs_val| {
+ if (lhs_val.isUndef()) {
+ return sema.addConstUndef(scalar_type);
+ }
+ if (maybe_rhs_val) |rhs_val| {
+ return sema.addConstant(
+ scalar_type,
+ try lhs_val.floatRem(rhs_val, sema.arena),
+ );
+ } else break :rs .{ .src = rhs_src, .air_tag = .rem };
+ } else break :rs .{ .src = lhs_src, .air_tag = .rem };
+ },
+ .mod => {
+ // For integers:
+ // Either operand being undef is a compile error because there exists
+ // a possible value (TODO what is it?) that would invoke illegal behavior.
+ // TODO: can lhs zero be handled better?
+ // TODO: can lhs undef be handled better?
+ //
+ // For floats:
+ // If the rhs is zero, compile error for division by zero.
+ // If the rhs is undefined, compile error because there is a possible
+ // value (zero) for which the division would be illegal behavior.
+ // If the lhs is undefined, result is undefined.
+ if (is_int) {
+ if (maybe_lhs_val) |lhs_val| {
+ if (lhs_val.isUndef()) {
+ return sema.failWithUseOfUndef(block, lhs_src);
+ }
+ }
+ if (maybe_rhs_val) |rhs_val| {
+ if (rhs_val.isUndef()) {
+ return sema.failWithUseOfUndef(block, rhs_src);
+ }
+ if (rhs_val.compareWithZero(.eq)) {
+ return sema.failWithDivideByZero(block, rhs_src);
+ }
+ if (maybe_lhs_val) |lhs_val| {
+ return sema.addConstant(
+ scalar_type,
+ try lhs_val.intMod(rhs_val, sema.arena),
+ );
+ }
+ break :rs .{ .src = lhs_src, .air_tag = .mod };
+ } else {
+ break :rs .{ .src = rhs_src, .air_tag = .mod };
+ }
+ }
+ // float operands
+ if (maybe_rhs_val) |rhs_val| {
+ if (rhs_val.isUndef()) {
+ return sema.failWithUseOfUndef(block, rhs_src);
+ }
+ if (rhs_val.compareWithZero(.eq)) {
+ return sema.failWithDivideByZero(block, rhs_src);
+ }
+ }
+ if (maybe_lhs_val) |lhs_val| {
+ if (lhs_val.isUndef()) {
+ return sema.addConstUndef(scalar_type);
+ }
+ if (maybe_rhs_val) |rhs_val| {
+ return sema.addConstant(
+ scalar_type,
+ try lhs_val.floatMod(rhs_val, sema.arena),
+ );
+ } else break :rs .{ .src = rhs_src, .air_tag = .mod };
+ } else break :rs .{ .src = lhs_src, .air_tag = .mod };
+ },
+ else => unreachable,
}
- }
-
- const air_tag: Air.Inst.Tag = switch (zir_tag) {
- .add => .add,
- .addwrap => .addwrap,
- .sub => .sub,
- .subwrap => .subwrap,
- .mul => .mul,
- .mulwrap => .mulwrap,
- .div => .div,
- .mod_rem => .rem,
- .rem => .rem,
- else => return sema.mod.fail(&block.base, src, "TODO implement arithmetic for operand '{s}'", .{@tagName(zir_tag)}),
};
- return block.addBinOp(air_tag, casted_lhs, casted_rhs);
+ try sema.requireRuntimeBlock(block, rs.src);
+ return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs);
}
fn zirLoad(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -7401,7 +7846,7 @@ fn analyzeRet(
fn floatOpAllowed(tag: Zir.Inst.Tag) bool {
// extend this swich as additional operators are implemented
return switch (tag) {
- .add, .sub, .mul, .div => true,
+ .add, .sub, .mul, .div, .mod, .rem, .mod_rem => true,
else => false,
};
}
@@ -8068,16 +8513,6 @@ fn zirDivTrunc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
return sema.mod.fail(&block.base, src, "TODO: Sema.zirDivTrunc", .{});
}
-fn zirMod(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
- const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
- const src = inst_data.src();
- return sema.mod.fail(&block.base, src, "TODO: Sema.zirMod", .{});
-}
-
-fn zirRem(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
- return sema.zirArithmetic(block, inst);
-}
-
fn zirShlExact(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const src = inst_data.src();
src/value.zig
@@ -1616,6 +1616,34 @@ pub const Value = extern union {
return result;
}
+ /// Supports both floats and ints; handles undefined.
+ pub fn numberMulWrap(
+ lhs: Value,
+ rhs: Value,
+ ty: Type,
+ arena: *Allocator,
+ target: Target,
+ ) !Value {
+ if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
+
+ if (ty.isAnyFloat()) {
+ return floatMul(lhs, rhs, ty, arena);
+ }
+ const result = try intMul(lhs, rhs, arena);
+
+ const max = try ty.maxInt(arena, target);
+ if (compare(result, .gt, max, ty)) {
+ @panic("TODO comptime wrapping integer multiplication");
+ }
+
+ const min = try ty.minInt(arena, target);
+ if (compare(result, .lt, min, ty)) {
+ @panic("TODO comptime wrapping integer multiplication");
+ }
+
+ return result;
+ }
+
/// Supports both floats and ints; handles undefined.
pub fn numberMax(lhs: Value, rhs: Value, arena: *Allocator) !Value {
if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
@@ -1840,6 +1868,82 @@ pub const Value = extern union {
}
}
+ pub fn intRem(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
+ // TODO is this a performance issue? maybe we should try the operation without
+ // resorting to BigInt first.
+ var lhs_space: Value.BigIntSpace = undefined;
+ var rhs_space: Value.BigIntSpace = undefined;
+ const lhs_bigint = lhs.toBigInt(&lhs_space);
+ const rhs_bigint = rhs.toBigInt(&rhs_space);
+ const limbs_q = try allocator.alloc(
+ std.math.big.Limb,
+ lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1,
+ );
+ const limbs_r = try allocator.alloc(
+ std.math.big.Limb,
+ lhs_bigint.limbs.len,
+ );
+ const limbs_buffer = try allocator.alloc(
+ std.math.big.Limb,
+ std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
+ );
+ var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
+ var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
+ result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer, null);
+ const result_limbs = result_r.limbs[0..result_r.len];
+
+ if (result_r.positive) {
+ return Value.Tag.int_big_positive.create(allocator, result_limbs);
+ } else {
+ return Value.Tag.int_big_negative.create(allocator, result_limbs);
+ }
+ }
+
+ pub fn intMod(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
+ // TODO is this a performance issue? maybe we should try the operation without
+ // resorting to BigInt first.
+ var lhs_space: Value.BigIntSpace = undefined;
+ var rhs_space: Value.BigIntSpace = undefined;
+ const lhs_bigint = lhs.toBigInt(&lhs_space);
+ const rhs_bigint = rhs.toBigInt(&rhs_space);
+ const limbs_q = try allocator.alloc(
+ std.math.big.Limb,
+ lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1,
+ );
+ const limbs_r = try allocator.alloc(
+ std.math.big.Limb,
+ lhs_bigint.limbs.len,
+ );
+ const limbs_buffer = try allocator.alloc(
+ std.math.big.Limb,
+ std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
+ );
+ var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
+ var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
+ result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buffer, null);
+ const result_limbs = result_r.limbs[0..result_r.len];
+
+ if (result_r.positive) {
+ return Value.Tag.int_big_positive.create(allocator, result_limbs);
+ } else {
+ return Value.Tag.int_big_negative.create(allocator, result_limbs);
+ }
+ }
+
+ pub fn floatRem(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
+ _ = lhs;
+ _ = rhs;
+ _ = allocator;
+ @panic("TODO implement Value.floatRem");
+ }
+
+ pub fn floatMod(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
+ _ = lhs;
+ _ = rhs;
+ _ = allocator;
+ @panic("TODO implement Value.floatMod");
+ }
+
pub fn intMul(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
// TODO is this a performance issue? maybe we should try the operation without
// resorting to BigInt first.
@@ -1875,6 +1979,31 @@ pub const Value = extern union {
return Tag.int_u64.create(arena, truncated);
}
+ pub fn shl(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
+ // TODO is this a performance issue? maybe we should try the operation without
+ // resorting to BigInt first.
+ var lhs_space: Value.BigIntSpace = undefined;
+ const lhs_bigint = lhs.toBigInt(&lhs_space);
+ const shift = rhs.toUnsignedInt();
+ const limbs = try allocator.alloc(
+ std.math.big.Limb,
+ lhs_bigint.limbs.len + (shift / (@sizeOf(std.math.big.Limb) * 8)) + 1,
+ );
+ var result_bigint = BigIntMutable{
+ .limbs = limbs,
+ .positive = undefined,
+ .len = undefined,
+ };
+ result_bigint.shiftLeft(lhs_bigint, shift);
+ const result_limbs = result_bigint.limbs[0..result_bigint.len];
+
+ if (result_bigint.positive) {
+ return Value.Tag.int_big_positive.create(allocator, result_limbs);
+ } else {
+ return Value.Tag.int_big_negative.create(allocator, result_limbs);
+ }
+ }
+
pub fn shr(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
// TODO is this a performance issue? maybe we should try the operation without
// resorting to BigInt first.
@@ -2227,4 +2356,13 @@ pub const Value = extern union {
/// are possible without using an allocator.
limbs: [(@sizeOf(u64) / @sizeOf(std.math.big.Limb)) + 1]std.math.big.Limb,
};
+
+ pub const zero = initTag(.zero);
+ pub const one = initTag(.one);
+ pub const negative_one: Value = .{ .ptr_otherwise = &negative_one_payload.base };
+};
+
+var negative_one_payload: Value.Payload.I64 = .{
+ .base = .{ .tag = .int_i64 },
+ .data = -1,
};
src/Zir.zig
@@ -395,17 +395,6 @@ pub const Inst = struct {
/// Merge two error sets into one, `E1 || E2`.
/// Uses the `pl_node` field with payload `Bin`.
merge_error_sets,
- /// Ambiguously remainder division or modulus. If the computation would possibly have
- /// a different value depending on whether the operation is remainder division or modulus,
- /// a compile error is emitted. Otherwise the computation is performed.
- /// Uses the `pl_node` union field. Payload is `Bin`.
- mod_rem,
- /// Arithmetic multiplication. Asserts no integer overflow.
- /// Uses the `pl_node` union field. Payload is `Bin`.
- mul,
- /// Twos complement wrapping integer multiplication.
- /// Uses the `pl_node` union field. Payload is `Bin`.
- mulwrap,
/// Turns an R-Value into a const L-Value. In other words, it takes a value,
/// stores it in a memory location, and returns a const pointer to it. If the value
/// is `comptime`, the memory location is global static constant data. Otherwise,
@@ -828,6 +817,17 @@ pub const Inst = struct {
/// Implements the `@rem` builtin.
/// Uses the `pl_node` union field with payload `Bin`.
rem,
+ /// Ambiguously remainder division or modulus. If the computation would possibly have
+ /// a different value depending on whether the operation is remainder division or modulus,
+ /// a compile error is emitted. Otherwise the computation is performed.
+ /// Uses the `pl_node` union field. Payload is `Bin`.
+ mod_rem,
+ /// Arithmetic multiplication. Asserts no integer overflow.
+ /// Uses the `pl_node` union field. Payload is `Bin`.
+ mul,
+ /// Twos complement wrapping integer multiplication.
+ /// Uses the `pl_node` union field. Payload is `Bin`.
+ mulwrap,
/// Integer shift-left. Zeroes are shifted in from the right hand side.
/// Uses the `pl_node` union field. Payload is `Bin`.
test/behavior/math.zig
@@ -6,171 +6,6 @@ const maxInt = std.math.maxInt;
const minInt = std.math.minInt;
const mem = std.mem;
-test "division" {
- try testDivision();
- comptime try testDivision();
-}
-fn testDivision() !void {
- try expect(div(u32, 13, 3) == 4);
- try expect(div(f16, 1.0, 2.0) == 0.5);
- try expect(div(f32, 1.0, 2.0) == 0.5);
-
- try expect(divExact(u32, 55, 11) == 5);
- try expect(divExact(i32, -55, 11) == -5);
- try expect(divExact(f16, 55.0, 11.0) == 5.0);
- try expect(divExact(f16, -55.0, 11.0) == -5.0);
- try expect(divExact(f32, 55.0, 11.0) == 5.0);
- try expect(divExact(f32, -55.0, 11.0) == -5.0);
-
- try expect(divFloor(i32, 5, 3) == 1);
- try expect(divFloor(i32, -5, 3) == -2);
- try expect(divFloor(f16, 5.0, 3.0) == 1.0);
- try expect(divFloor(f16, -5.0, 3.0) == -2.0);
- try expect(divFloor(f32, 5.0, 3.0) == 1.0);
- try expect(divFloor(f32, -5.0, 3.0) == -2.0);
- try expect(divFloor(i32, -0x80000000, -2) == 0x40000000);
- try expect(divFloor(i32, 0, -0x80000000) == 0);
- try expect(divFloor(i32, -0x40000001, 0x40000000) == -2);
- try expect(divFloor(i32, -0x80000000, 1) == -0x80000000);
- try expect(divFloor(i32, 10, 12) == 0);
- try expect(divFloor(i32, -14, 12) == -2);
- try expect(divFloor(i32, -2, 12) == -1);
-
- try expect(divTrunc(i32, 5, 3) == 1);
- try expect(divTrunc(i32, -5, 3) == -1);
- try expect(divTrunc(f16, 5.0, 3.0) == 1.0);
- try expect(divTrunc(f16, -5.0, 3.0) == -1.0);
- try expect(divTrunc(f32, 5.0, 3.0) == 1.0);
- try expect(divTrunc(f32, -5.0, 3.0) == -1.0);
- try expect(divTrunc(f64, 5.0, 3.0) == 1.0);
- try expect(divTrunc(f64, -5.0, 3.0) == -1.0);
- try expect(divTrunc(i32, 10, 12) == 0);
- try expect(divTrunc(i32, -14, 12) == -1);
- try expect(divTrunc(i32, -2, 12) == 0);
-
- try expect(mod(i32, 10, 12) == 10);
- try expect(mod(i32, -14, 12) == 10);
- try expect(mod(i32, -2, 12) == 10);
-
- comptime {
- try expect(
- 1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600,
- );
- try expect(
- @rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600,
- );
- try expect(
- 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2,
- );
- try expect(
- @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2,
- );
- try expect(
- @divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2,
- );
- try expect(
- @divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2,
- );
- try expect(
- 4126227191251978491697987544882340798050766755606969681711 % 10 == 1,
- );
- }
-}
-fn div(comptime T: type, a: T, b: T) T {
- return a / b;
-}
-fn divExact(comptime T: type, a: T, b: T) T {
- return @divExact(a, b);
-}
-fn divFloor(comptime T: type, a: T, b: T) T {
- return @divFloor(a, b);
-}
-fn divTrunc(comptime T: type, a: T, b: T) T {
- return @divTrunc(a, b);
-}
-fn mod(comptime T: type, a: T, b: T) T {
- return @mod(a, b);
-}
-
-test "@addWithOverflow" {
- var result: u8 = undefined;
- try expect(@addWithOverflow(u8, 250, 100, &result));
- try expect(!@addWithOverflow(u8, 100, 150, &result));
- try expect(result == 250);
-}
-
-// TODO test mulWithOverflow
-// TODO test subWithOverflow
-
-test "@shlWithOverflow" {
- var result: u16 = undefined;
- try expect(@shlWithOverflow(u16, 0b0010111111111111, 3, &result));
- try expect(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result));
- try expect(result == 0b1011111111111100);
-}
-
-test "@*WithOverflow with u0 values" {
- var result: u0 = undefined;
- try expect(!@addWithOverflow(u0, 0, 0, &result));
- try expect(!@subWithOverflow(u0, 0, 0, &result));
- try expect(!@mulWithOverflow(u0, 0, 0, &result));
- try expect(!@shlWithOverflow(u0, 0, 0, &result));
-}
-
-test "@clz" {
- try testClz();
- comptime try testClz();
-}
-
-fn testClz() !void {
- try expect(@clz(u8, 0b10001010) == 0);
- try expect(@clz(u8, 0b00001010) == 4);
- try expect(@clz(u8, 0b00011010) == 3);
- try expect(@clz(u8, 0b00000000) == 8);
- try expect(@clz(u128, 0xffffffffffffffff) == 64);
- try expect(@clz(u128, 0x10000000000000000) == 63);
-}
-
-test "@clz vectors" {
- try testClzVectors();
- comptime try testClzVectors();
-}
-
-fn testClzVectors() !void {
- @setEvalBranchQuota(10_000);
- try expectEqual(@clz(u8, @splat(64, @as(u8, 0b10001010))), @splat(64, @as(u4, 0)));
- try expectEqual(@clz(u8, @splat(64, @as(u8, 0b00001010))), @splat(64, @as(u4, 4)));
- try expectEqual(@clz(u8, @splat(64, @as(u8, 0b00011010))), @splat(64, @as(u4, 3)));
- try expectEqual(@clz(u8, @splat(64, @as(u8, 0b00000000))), @splat(64, @as(u4, 8)));
- try expectEqual(@clz(u128, @splat(64, @as(u128, 0xffffffffffffffff))), @splat(64, @as(u8, 64)));
- try expectEqual(@clz(u128, @splat(64, @as(u128, 0x10000000000000000))), @splat(64, @as(u8, 63)));
-}
-
-test "@ctz" {
- try testCtz();
- comptime try testCtz();
-}
-
-fn testCtz() !void {
- try expect(@ctz(u8, 0b10100000) == 5);
- try expect(@ctz(u8, 0b10001010) == 1);
- try expect(@ctz(u8, 0b00000000) == 8);
- try expect(@ctz(u16, 0b00000000) == 16);
-}
-
-test "@ctz vectors" {
- try testClzVectors();
- comptime try testClzVectors();
-}
-
-fn testCtzVectors() !void {
- @setEvalBranchQuota(10_000);
- try expectEqual(@ctz(u8, @splat(64, @as(u8, 0b10100000))), @splat(64, @as(u4, 5)));
- try expectEqual(@ctz(u8, @splat(64, @as(u8, 0b10001010))), @splat(64, @as(u4, 1)));
- try expectEqual(@ctz(u8, @splat(64, @as(u8, 0b00000000))), @splat(64, @as(u4, 8)));
- try expectEqual(@ctz(u16, @splat(64, @as(u16, 0b00000000))), @splat(64, @as(u5, 16)));
-}
-
test "assignment operators" {
var i: u32 = 0;
i += 5;
@@ -218,686 +53,3 @@ fn testThreeExprInARow(f: bool, t: bool) !void {
fn assertFalse(b: bool) !void {
try expect(!b);
}
-
-test "const number literal" {
- const one = 1;
- const eleven = ten + one;
-
- try expect(eleven == 11);
-}
-const ten = 10;
-
-test "unsigned wrapping" {
- try testUnsignedWrappingEval(maxInt(u32));
- comptime try testUnsignedWrappingEval(maxInt(u32));
-}
-fn testUnsignedWrappingEval(x: u32) !void {
- const zero = x +% 1;
- try expect(zero == 0);
- const orig = zero -% 1;
- try expect(orig == maxInt(u32));
-}
-
-test "signed wrapping" {
- try testSignedWrappingEval(maxInt(i32));
- comptime try testSignedWrappingEval(maxInt(i32));
-}
-fn testSignedWrappingEval(x: i32) !void {
- const min_val = x +% 1;
- try expect(min_val == minInt(i32));
- const max_val = min_val -% 1;
- try expect(max_val == maxInt(i32));
-}
-
-test "signed negation wrapping" {
- try testSignedNegationWrappingEval(minInt(i16));
- comptime try testSignedNegationWrappingEval(minInt(i16));
-}
-fn testSignedNegationWrappingEval(x: i16) !void {
- try expect(x == -32768);
- const neg = -%x;
- try expect(neg == -32768);
-}
-
-test "unsigned negation wrapping" {
- try testUnsignedNegationWrappingEval(1);
- comptime try testUnsignedNegationWrappingEval(1);
-}
-fn testUnsignedNegationWrappingEval(x: u16) !void {
- try expect(x == 1);
- const neg = -%x;
- try expect(neg == maxInt(u16));
-}
-
-test "unsigned 64-bit division" {
- try test_u64_div();
- comptime try test_u64_div();
-}
-fn test_u64_div() !void {
- const result = divWithResult(1152921504606846976, 34359738365);
- try expect(result.quotient == 33554432);
- try expect(result.remainder == 100663296);
-}
-fn divWithResult(a: u64, b: u64) DivResult {
- return DivResult{
- .quotient = a / b,
- .remainder = a % b,
- };
-}
-const DivResult = struct {
- quotient: u64,
- remainder: u64,
-};
-
-test "binary not" {
- try expect(comptime x: {
- break :x ~@as(u16, 0b1010101010101010) == 0b0101010101010101;
- });
- try expect(comptime x: {
- break :x ~@as(u64, 2147483647) == 18446744071562067968;
- });
- try testBinaryNot(0b1010101010101010);
-}
-
-fn testBinaryNot(x: u16) !void {
- try expect(~x == 0b0101010101010101);
-}
-
-test "small int addition" {
- var x: u2 = 0;
- try expect(x == 0);
-
- x += 1;
- try expect(x == 1);
-
- x += 1;
- try expect(x == 2);
-
- x += 1;
- try expect(x == 3);
-
- var result: @TypeOf(x) = 3;
- try expect(@addWithOverflow(@TypeOf(x), x, 1, &result));
-
- try expect(result == 0);
-}
-
-test "float equality" {
- const x: f64 = 0.012;
- const y: f64 = x + 1.0;
-
- try testFloatEqualityImpl(x, y);
- comptime try testFloatEqualityImpl(x, y);
-}
-
-fn testFloatEqualityImpl(x: f64, y: f64) !void {
- const y2 = x + 1.0;
- try expect(y == y2);
-}
-
-test "allow signed integer division/remainder when values are comptime known and positive or exact" {
- try expect(5 / 3 == 1);
- try expect(-5 / -3 == 1);
- try expect(-6 / 3 == -2);
-
- try expect(5 % 3 == 2);
- try expect(-6 % 3 == 0);
-}
-
-test "hex float literal parsing" {
- comptime try expect(0x1.0 == 1.0);
-}
-
-test "quad hex float literal parsing in range" {
- const a = 0x1.af23456789bbaaab347645365cdep+5;
- const b = 0x1.dedafcff354b6ae9758763545432p-9;
- const c = 0x1.2f34dd5f437e849b4baab754cdefp+4534;
- const d = 0x1.edcbff8ad76ab5bf46463233214fp-435;
- if (false) {
- a;
- b;
- c;
- d;
- }
-}
-
-test "quad hex float literal parsing accurate" {
- const a: f128 = 0x1.1111222233334444555566667777p+0;
-
- // implied 1 is dropped, with an exponent of 0 (0x3fff) after biasing.
- const expected: u128 = 0x3fff1111222233334444555566667777;
- try expect(@bitCast(u128, a) == expected);
-
- // non-normalized
- const b: f128 = 0x11.111222233334444555566667777p-4;
- try expect(@bitCast(u128, b) == expected);
-
- const S = struct {
- fn doTheTest() !void {
- {
- var f: f128 = 0x1.2eab345678439abcdefea56782346p+5;
- try expect(@bitCast(u128, f) == 0x40042eab345678439abcdefea5678234);
- }
- {
- var f: f128 = 0x1.edcb34a235253948765432134674fp-1;
- try expect(@bitCast(u128, f) == 0x3ffeedcb34a235253948765432134674);
- }
- {
- var f: f128 = 0x1.353e45674d89abacc3a2ebf3ff4ffp-50;
- try expect(@bitCast(u128, f) == 0x3fcd353e45674d89abacc3a2ebf3ff50);
- }
- {
- var f: f128 = 0x1.ed8764648369535adf4be3214567fp-9;
- try expect(@bitCast(u128, f) == 0x3ff6ed8764648369535adf4be3214568);
- }
- const exp2ft = [_]f64{
- 0x1.6a09e667f3bcdp-1,
- 0x1.7a11473eb0187p-1,
- 0x1.8ace5422aa0dbp-1,
- 0x1.9c49182a3f090p-1,
- 0x1.ae89f995ad3adp-1,
- 0x1.c199bdd85529cp-1,
- 0x1.d5818dcfba487p-1,
- 0x1.ea4afa2a490dap-1,
- 0x1.0000000000000p+0,
- 0x1.0b5586cf9890fp+0,
- 0x1.172b83c7d517bp+0,
- 0x1.2387a6e756238p+0,
- 0x1.306fe0a31b715p+0,
- 0x1.3dea64c123422p+0,
- 0x1.4bfdad5362a27p+0,
- 0x1.5ab07dd485429p+0,
- 0x1.8p23,
- 0x1.62e430p-1,
- 0x1.ebfbe0p-3,
- 0x1.c6b348p-5,
- 0x1.3b2c9cp-7,
- 0x1.0p127,
- -0x1.0p-149,
- };
-
- const answers = [_]u64{
- 0x3fe6a09e667f3bcd,
- 0x3fe7a11473eb0187,
- 0x3fe8ace5422aa0db,
- 0x3fe9c49182a3f090,
- 0x3feae89f995ad3ad,
- 0x3fec199bdd85529c,
- 0x3fed5818dcfba487,
- 0x3feea4afa2a490da,
- 0x3ff0000000000000,
- 0x3ff0b5586cf9890f,
- 0x3ff172b83c7d517b,
- 0x3ff2387a6e756238,
- 0x3ff306fe0a31b715,
- 0x3ff3dea64c123422,
- 0x3ff4bfdad5362a27,
- 0x3ff5ab07dd485429,
- 0x4168000000000000,
- 0x3fe62e4300000000,
- 0x3fcebfbe00000000,
- 0x3fac6b3480000000,
- 0x3f83b2c9c0000000,
- 0x47e0000000000000,
- 0xb6a0000000000000,
- };
-
- for (exp2ft) |x, i| {
- try expect(@bitCast(u64, x) == answers[i]);
- }
- }
- };
- try S.doTheTest();
- comptime try S.doTheTest();
-}
-
-test "underscore separator parsing" {
- try expect(0_0_0_0 == 0);
- try expect(1_234_567 == 1234567);
- try expect(001_234_567 == 1234567);
- try expect(0_0_1_2_3_4_5_6_7 == 1234567);
-
- try expect(0b0_0_0_0 == 0);
- try expect(0b1010_1010 == 0b10101010);
- try expect(0b0000_1010_1010 == 0b10101010);
- try expect(0b1_0_1_0_1_0_1_0 == 0b10101010);
-
- try expect(0o0_0_0_0 == 0);
- try expect(0o1010_1010 == 0o10101010);
- try expect(0o0000_1010_1010 == 0o10101010);
- try expect(0o1_0_1_0_1_0_1_0 == 0o10101010);
-
- try expect(0x0_0_0_0 == 0);
- try expect(0x1010_1010 == 0x10101010);
- try expect(0x0000_1010_1010 == 0x10101010);
- try expect(0x1_0_1_0_1_0_1_0 == 0x10101010);
-
- try expect(123_456.789_000e1_0 == 123456.789000e10);
- try expect(0_1_2_3_4_5_6.7_8_9_0_0_0e0_0_1_0 == 123456.789000e10);
-
- try expect(0x1234_5678.9ABC_DEF0p-1_0 == 0x12345678.9ABCDEF0p-10);
- 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);
-}
-
-test "hex float literal within range" {
- const a = 0x1.0p16383;
- const b = 0x0.1p16387;
- const c = 0x1.0p-16382;
- if (false) {
- a;
- b;
- c;
- }
-}
-
-test "truncating shift left" {
- try testShlTrunc(maxInt(u16));
- comptime try testShlTrunc(maxInt(u16));
-}
-fn testShlTrunc(x: u16) !void {
- const shifted = x << 1;
- try expect(shifted == 65534);
-}
-
-test "truncating shift right" {
- try testShrTrunc(maxInt(u16));
- comptime try testShrTrunc(maxInt(u16));
-}
-fn testShrTrunc(x: u16) !void {
- const shifted = x >> 1;
- try expect(shifted == 32767);
-}
-
-test "exact shift left" {
- try testShlExact(0b00110101);
- comptime try testShlExact(0b00110101);
-}
-fn testShlExact(x: u8) !void {
- const shifted = @shlExact(x, 2);
- try expect(shifted == 0b11010100);
-}
-
-test "exact shift right" {
- try testShrExact(0b10110100);
- comptime try testShrExact(0b10110100);
-}
-fn testShrExact(x: u8) !void {
- const shifted = @shrExact(x, 2);
- try expect(shifted == 0b00101101);
-}
-
-test "shift left/right on u0 operand" {
- const S = struct {
- fn doTheTest() !void {
- var x: u0 = 0;
- var y: u0 = 0;
- try expectEqual(@as(u0, 0), x << 0);
- try expectEqual(@as(u0, 0), x >> 0);
- try expectEqual(@as(u0, 0), x << y);
- try expectEqual(@as(u0, 0), x >> y);
- try expectEqual(@as(u0, 0), @shlExact(x, 0));
- try expectEqual(@as(u0, 0), @shrExact(x, 0));
- try expectEqual(@as(u0, 0), @shlExact(x, y));
- try expectEqual(@as(u0, 0), @shrExact(x, y));
- }
- };
- try S.doTheTest();
- comptime try S.doTheTest();
-}
-
-test "comptime_int addition" {
- comptime {
- try expect(35361831660712422535336160538497375248 + 101752735581729509668353361206450473702 == 137114567242441932203689521744947848950);
- try expect(594491908217841670578297176641415611445982232488944558774612 + 390603545391089362063884922208143568023166603618446395589768 == 985095453608931032642182098849559179469148836107390954364380);
- }
-}
-
-test "comptime_int multiplication" {
- comptime {
- try expect(
- 45960427431263824329884196484953148229 * 128339149605334697009938835852565949723 == 5898522172026096622534201617172456926982464453350084962781392314016180490567,
- );
- try expect(
- 594491908217841670578297176641415611445982232488944558774612 * 390603545391089362063884922208143568023166603618446395589768 == 232210647056203049913662402532976186578842425262306016094292237500303028346593132411865381225871291702600263463125370016,
- );
- }
-}
-
-test "comptime_int shifting" {
- comptime {
- try expect((@as(u128, 1) << 127) == 0x80000000000000000000000000000000);
- }
-}
-
-test "comptime_int multi-limb shift and mask" {
- comptime {
- var a = 0xefffffffa0000001eeeeeeefaaaaaaab;
-
- try expect(@as(u32, a & 0xffffffff) == 0xaaaaaaab);
- a >>= 32;
- try expect(@as(u32, a & 0xffffffff) == 0xeeeeeeef);
- a >>= 32;
- try expect(@as(u32, a & 0xffffffff) == 0xa0000001);
- a >>= 32;
- try expect(@as(u32, a & 0xffffffff) == 0xefffffff);
- a >>= 32;
-
- try expect(a == 0);
- }
-}
-
-test "comptime_int multi-limb partial shift right" {
- comptime {
- var a = 0x1ffffffffeeeeeeee;
- a >>= 16;
- try expect(a == 0x1ffffffffeeee);
- }
-}
-
-test "xor" {
- try test_xor();
- comptime try test_xor();
-}
-
-fn test_xor() !void {
- try expect(0xFF ^ 0x00 == 0xFF);
- try expect(0xF0 ^ 0x0F == 0xFF);
- try expect(0xFF ^ 0xF0 == 0x0F);
- try expect(0xFF ^ 0x0F == 0xF0);
- try expect(0xFF ^ 0xFF == 0x00);
-}
-
-test "comptime_int xor" {
- comptime {
- try expect(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0x00000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
- try expect(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0x0000000000000000FFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
- try expect(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x0000000000000000FFFFFFFFFFFFFFFF);
- try expect(0x0000000000000000FFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFF0000000000000000);
- try expect(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000000000000000000000000000);
- try expect(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0x00000000FFFFFFFF00000000FFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
- try expect(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000FFFFFFFF00000000FFFFFFFF);
- try expect(0x00000000FFFFFFFF00000000FFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFF00000000FFFFFFFF00000000);
- }
-}
-
-test "f128" {
- try test_f128();
- comptime try test_f128();
-}
-
-fn make_f128(x: f128) f128 {
- return x;
-}
-
-fn test_f128() !void {
- try expect(@sizeOf(f128) == 16);
- try expect(make_f128(1.0) == 1.0);
- try expect(make_f128(1.0) != 1.1);
- try expect(make_f128(1.0) > 0.9);
- try expect(make_f128(1.0) >= 0.9);
- try expect(make_f128(1.0) >= 1.0);
- try should_not_be_zero(1.0);
-}
-
-fn should_not_be_zero(x: f128) !void {
- try expect(x != 0.0);
-}
-
-test "comptime float rem int" {
- comptime {
- var x = @as(f32, 1) % 2;
- try expect(x == 1.0);
- }
-}
-
-test "remainder division" {
- comptime try remdiv(f16);
- comptime try remdiv(f32);
- comptime try remdiv(f64);
- comptime try remdiv(f128);
- try remdiv(f16);
- try remdiv(f64);
- try remdiv(f128);
-}
-
-fn remdiv(comptime T: type) !void {
- try expect(@as(T, 1) == @as(T, 1) % @as(T, 2));
- try expect(@as(T, 1) == @as(T, 7) % @as(T, 3));
-}
-
-test "@sqrt" {
- try testSqrt(f64, 12.0);
- comptime try testSqrt(f64, 12.0);
- try testSqrt(f32, 13.0);
- comptime try testSqrt(f32, 13.0);
- try testSqrt(f16, 13.0);
- comptime try testSqrt(f16, 13.0);
-
- const x = 14.0;
- const y = x * x;
- const z = @sqrt(y);
- comptime try expect(z == x);
-}
-
-fn testSqrt(comptime T: type, x: T) !void {
- try expect(@sqrt(x * x) == x);
-}
-
-test "@fabs" {
- try testFabs(f128, 12.0);
- comptime try testFabs(f128, 12.0);
- try testFabs(f64, 12.0);
- comptime try testFabs(f64, 12.0);
- try testFabs(f32, 12.0);
- comptime try testFabs(f32, 12.0);
- try testFabs(f16, 12.0);
- comptime try testFabs(f16, 12.0);
-
- const x = 14.0;
- const y = -x;
- const z = @fabs(y);
- comptime try expectEqual(x, z);
-}
-
-fn testFabs(comptime T: type, x: T) !void {
- const y = -x;
- const z = @fabs(y);
- try expectEqual(x, z);
-}
-
-test "@floor" {
- // FIXME: Generates a floorl function call
- // testFloor(f128, 12.0);
- comptime try testFloor(f128, 12.0);
- try testFloor(f64, 12.0);
- comptime try testFloor(f64, 12.0);
- try testFloor(f32, 12.0);
- comptime try testFloor(f32, 12.0);
- try testFloor(f16, 12.0);
- comptime try testFloor(f16, 12.0);
-
- const x = 14.0;
- const y = x + 0.7;
- const z = @floor(y);
- comptime try expectEqual(x, z);
-}
-
-fn testFloor(comptime T: type, x: T) !void {
- const y = x + 0.6;
- const z = @floor(y);
- try expectEqual(x, z);
-}
-
-test "@ceil" {
- // FIXME: Generates a ceill function call
- //testCeil(f128, 12.0);
- comptime try testCeil(f128, 12.0);
- try testCeil(f64, 12.0);
- comptime try testCeil(f64, 12.0);
- try testCeil(f32, 12.0);
- comptime try testCeil(f32, 12.0);
- try testCeil(f16, 12.0);
- comptime try testCeil(f16, 12.0);
-
- const x = 14.0;
- const y = x - 0.7;
- const z = @ceil(y);
- comptime try expectEqual(x, z);
-}
-
-fn testCeil(comptime T: type, x: T) !void {
- const y = x - 0.8;
- const z = @ceil(y);
- try expectEqual(x, z);
-}
-
-test "@trunc" {
- // FIXME: Generates a truncl function call
- //testTrunc(f128, 12.0);
- comptime try testTrunc(f128, 12.0);
- try testTrunc(f64, 12.0);
- comptime try testTrunc(f64, 12.0);
- try testTrunc(f32, 12.0);
- comptime try testTrunc(f32, 12.0);
- try testTrunc(f16, 12.0);
- comptime try testTrunc(f16, 12.0);
-
- const x = 14.0;
- const y = x + 0.7;
- const z = @trunc(y);
- comptime try expectEqual(x, z);
-}
-
-fn testTrunc(comptime T: type, x: T) !void {
- {
- const y = x + 0.8;
- const z = @trunc(y);
- try expectEqual(x, z);
- }
-
- {
- const y = -x - 0.8;
- const z = @trunc(y);
- try expectEqual(-x, z);
- }
-}
-
-test "@round" {
- // FIXME: Generates a roundl function call
- //testRound(f128, 12.0);
- comptime try testRound(f128, 12.0);
- try testRound(f64, 12.0);
- comptime try testRound(f64, 12.0);
- try testRound(f32, 12.0);
- comptime try testRound(f32, 12.0);
- try testRound(f16, 12.0);
- comptime try testRound(f16, 12.0);
-
- const x = 14.0;
- const y = x + 0.4;
- const z = @round(y);
- comptime try expectEqual(x, z);
-}
-
-fn testRound(comptime T: type, x: T) !void {
- const y = x - 0.5;
- const z = @round(y);
- try expectEqual(x, z);
-}
-
-test "comptime_int param and return" {
- const a = comptimeAdd(35361831660712422535336160538497375248, 101752735581729509668353361206450473702);
- try expect(a == 137114567242441932203689521744947848950);
-
- const b = comptimeAdd(594491908217841670578297176641415611445982232488944558774612, 390603545391089362063884922208143568023166603618446395589768);
- try expect(b == 985095453608931032642182098849559179469148836107390954364380);
-}
-
-fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int {
- return a + b;
-}
-
-test "vector integer addition" {
- const S = struct {
- fn doTheTest() !void {
- var a: std.meta.Vector(4, i32) = [_]i32{ 1, 2, 3, 4 };
- var b: std.meta.Vector(4, i32) = [_]i32{ 5, 6, 7, 8 };
- var result = a + b;
- var result_array: [4]i32 = result;
- const expected = [_]i32{ 6, 8, 10, 12 };
- try expectEqualSlices(i32, &expected, &result_array);
- }
- };
- try S.doTheTest();
- comptime try S.doTheTest();
-}
-
-test "NaN comparison" {
- try testNanEqNan(f16);
- try testNanEqNan(f32);
- try testNanEqNan(f64);
- try testNanEqNan(f128);
- comptime try testNanEqNan(f16);
- comptime try testNanEqNan(f32);
- comptime try testNanEqNan(f64);
- comptime try testNanEqNan(f128);
-}
-
-fn testNanEqNan(comptime F: type) !void {
- var nan1 = std.math.nan(F);
- var nan2 = std.math.nan(F);
- try expect(nan1 != nan2);
- try expect(!(nan1 == nan2));
- try expect(!(nan1 > nan2));
- try expect(!(nan1 >= nan2));
- try expect(!(nan1 < nan2));
- try expect(!(nan1 <= nan2));
-}
-
-test "128-bit multiplication" {
- var a: i128 = 3;
- var b: i128 = 2;
- var c = a * b;
- try expect(c == 6);
-}
-
-test "vector comparison" {
- const S = struct {
- fn doTheTest() !void {
- var a: std.meta.Vector(6, i32) = [_]i32{ 1, 3, -1, 5, 7, 9 };
- var b: std.meta.Vector(6, i32) = [_]i32{ -1, 3, 0, 6, 10, -10 };
- try expect(mem.eql(bool, &@as([6]bool, a < b), &[_]bool{ false, false, true, true, true, false }));
- try expect(mem.eql(bool, &@as([6]bool, a <= b), &[_]bool{ false, true, true, true, true, false }));
- try expect(mem.eql(bool, &@as([6]bool, a == b), &[_]bool{ false, true, false, false, false, false }));
- try expect(mem.eql(bool, &@as([6]bool, a != b), &[_]bool{ true, false, true, true, true, true }));
- try expect(mem.eql(bool, &@as([6]bool, a > b), &[_]bool{ true, false, false, false, false, true }));
- try expect(mem.eql(bool, &@as([6]bool, a >= b), &[_]bool{ true, true, false, false, false, true }));
- }
- };
- try S.doTheTest();
- comptime try S.doTheTest();
-}
-
-test "compare undefined literal with comptime_int" {
- var x = undefined == 1;
- // x is now undefined with type bool
- x = true;
- try expect(x);
-}
-
-test "signed zeros are represented properly" {
- const S = struct {
- fn doTheTest() !void {
- inline for ([_]type{ f16, f32, f64, f128 }) |T| {
- const ST = std.meta.Int(.unsigned, @typeInfo(T).Float.bits);
- var as_fp_val = -@as(T, 0.0);
- var as_uint_val = @bitCast(ST, as_fp_val);
- // Ensure the sign bit is set.
- try expect(as_uint_val >> (@typeInfo(T).Float.bits - 1) == 1);
- }
- }
- };
-
- try S.doTheTest();
- comptime try S.doTheTest();
-}
test/behavior/math_stage1.zig
@@ -0,0 +1,855 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const expectEqual = std.testing.expectEqual;
+const expectEqualSlices = std.testing.expectEqualSlices;
+const maxInt = std.math.maxInt;
+const minInt = std.math.minInt;
+const mem = std.mem;
+
+test "division" {
+ try testDivision();
+ comptime try testDivision();
+}
+fn testDivision() !void {
+ try expect(div(u32, 13, 3) == 4);
+ try expect(div(f16, 1.0, 2.0) == 0.5);
+ try expect(div(f32, 1.0, 2.0) == 0.5);
+
+ try expect(divExact(u32, 55, 11) == 5);
+ try expect(divExact(i32, -55, 11) == -5);
+ try expect(divExact(f16, 55.0, 11.0) == 5.0);
+ try expect(divExact(f16, -55.0, 11.0) == -5.0);
+ try expect(divExact(f32, 55.0, 11.0) == 5.0);
+ try expect(divExact(f32, -55.0, 11.0) == -5.0);
+
+ try expect(divFloor(i32, 5, 3) == 1);
+ try expect(divFloor(i32, -5, 3) == -2);
+ try expect(divFloor(f16, 5.0, 3.0) == 1.0);
+ try expect(divFloor(f16, -5.0, 3.0) == -2.0);
+ try expect(divFloor(f32, 5.0, 3.0) == 1.0);
+ try expect(divFloor(f32, -5.0, 3.0) == -2.0);
+ try expect(divFloor(i32, -0x80000000, -2) == 0x40000000);
+ try expect(divFloor(i32, 0, -0x80000000) == 0);
+ try expect(divFloor(i32, -0x40000001, 0x40000000) == -2);
+ try expect(divFloor(i32, -0x80000000, 1) == -0x80000000);
+ try expect(divFloor(i32, 10, 12) == 0);
+ try expect(divFloor(i32, -14, 12) == -2);
+ try expect(divFloor(i32, -2, 12) == -1);
+
+ try expect(divTrunc(i32, 5, 3) == 1);
+ try expect(divTrunc(i32, -5, 3) == -1);
+ try expect(divTrunc(f16, 5.0, 3.0) == 1.0);
+ try expect(divTrunc(f16, -5.0, 3.0) == -1.0);
+ try expect(divTrunc(f32, 5.0, 3.0) == 1.0);
+ try expect(divTrunc(f32, -5.0, 3.0) == -1.0);
+ try expect(divTrunc(f64, 5.0, 3.0) == 1.0);
+ try expect(divTrunc(f64, -5.0, 3.0) == -1.0);
+ try expect(divTrunc(i32, 10, 12) == 0);
+ try expect(divTrunc(i32, -14, 12) == -1);
+ try expect(divTrunc(i32, -2, 12) == 0);
+
+ try expect(mod(i32, 10, 12) == 10);
+ try expect(mod(i32, -14, 12) == 10);
+ try expect(mod(i32, -2, 12) == 10);
+
+ comptime {
+ try expect(
+ 1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600,
+ );
+ try expect(
+ @rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600,
+ );
+ try expect(
+ 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2,
+ );
+ try expect(
+ @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2,
+ );
+ try expect(
+ @divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2,
+ );
+ try expect(
+ @divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2,
+ );
+ try expect(
+ 4126227191251978491697987544882340798050766755606969681711 % 10 == 1,
+ );
+ }
+}
+fn div(comptime T: type, a: T, b: T) T {
+ return a / b;
+}
+fn divExact(comptime T: type, a: T, b: T) T {
+ return @divExact(a, b);
+}
+fn divFloor(comptime T: type, a: T, b: T) T {
+ return @divFloor(a, b);
+}
+fn divTrunc(comptime T: type, a: T, b: T) T {
+ return @divTrunc(a, b);
+}
+fn mod(comptime T: type, a: T, b: T) T {
+ return @mod(a, b);
+}
+
+test "@addWithOverflow" {
+ var result: u8 = undefined;
+ try expect(@addWithOverflow(u8, 250, 100, &result));
+ try expect(!@addWithOverflow(u8, 100, 150, &result));
+ try expect(result == 250);
+}
+
+// TODO test mulWithOverflow
+// TODO test subWithOverflow
+
+test "@shlWithOverflow" {
+ var result: u16 = undefined;
+ try expect(@shlWithOverflow(u16, 0b0010111111111111, 3, &result));
+ try expect(!@shlWithOverflow(u16, 0b0010111111111111, 2, &result));
+ try expect(result == 0b1011111111111100);
+}
+
+test "@*WithOverflow with u0 values" {
+ var result: u0 = undefined;
+ try expect(!@addWithOverflow(u0, 0, 0, &result));
+ try expect(!@subWithOverflow(u0, 0, 0, &result));
+ try expect(!@mulWithOverflow(u0, 0, 0, &result));
+ try expect(!@shlWithOverflow(u0, 0, 0, &result));
+}
+
+test "@clz" {
+ try testClz();
+ comptime try testClz();
+}
+
+fn testClz() !void {
+ try expect(@clz(u8, 0b10001010) == 0);
+ try expect(@clz(u8, 0b00001010) == 4);
+ try expect(@clz(u8, 0b00011010) == 3);
+ try expect(@clz(u8, 0b00000000) == 8);
+ try expect(@clz(u128, 0xffffffffffffffff) == 64);
+ try expect(@clz(u128, 0x10000000000000000) == 63);
+}
+
+test "@clz vectors" {
+ try testClzVectors();
+ comptime try testClzVectors();
+}
+
+fn testClzVectors() !void {
+ @setEvalBranchQuota(10_000);
+ try expectEqual(@clz(u8, @splat(64, @as(u8, 0b10001010))), @splat(64, @as(u4, 0)));
+ try expectEqual(@clz(u8, @splat(64, @as(u8, 0b00001010))), @splat(64, @as(u4, 4)));
+ try expectEqual(@clz(u8, @splat(64, @as(u8, 0b00011010))), @splat(64, @as(u4, 3)));
+ try expectEqual(@clz(u8, @splat(64, @as(u8, 0b00000000))), @splat(64, @as(u4, 8)));
+ try expectEqual(@clz(u128, @splat(64, @as(u128, 0xffffffffffffffff))), @splat(64, @as(u8, 64)));
+ try expectEqual(@clz(u128, @splat(64, @as(u128, 0x10000000000000000))), @splat(64, @as(u8, 63)));
+}
+
+test "@ctz" {
+ try testCtz();
+ comptime try testCtz();
+}
+
+fn testCtz() !void {
+ try expect(@ctz(u8, 0b10100000) == 5);
+ try expect(@ctz(u8, 0b10001010) == 1);
+ try expect(@ctz(u8, 0b00000000) == 8);
+ try expect(@ctz(u16, 0b00000000) == 16);
+}
+
+test "@ctz vectors" {
+ try testClzVectors();
+ comptime try testClzVectors();
+}
+
+fn testCtzVectors() !void {
+ @setEvalBranchQuota(10_000);
+ try expectEqual(@ctz(u8, @splat(64, @as(u8, 0b10100000))), @splat(64, @as(u4, 5)));
+ try expectEqual(@ctz(u8, @splat(64, @as(u8, 0b10001010))), @splat(64, @as(u4, 1)));
+ try expectEqual(@ctz(u8, @splat(64, @as(u8, 0b00000000))), @splat(64, @as(u4, 8)));
+ try expectEqual(@ctz(u16, @splat(64, @as(u16, 0b00000000))), @splat(64, @as(u5, 16)));
+}
+
+test "const number literal" {
+ const one = 1;
+ const eleven = ten + one;
+
+ try expect(eleven == 11);
+}
+const ten = 10;
+
+test "unsigned wrapping" {
+ try testUnsignedWrappingEval(maxInt(u32));
+ comptime try testUnsignedWrappingEval(maxInt(u32));
+}
+fn testUnsignedWrappingEval(x: u32) !void {
+ const zero = x +% 1;
+ try expect(zero == 0);
+ const orig = zero -% 1;
+ try expect(orig == maxInt(u32));
+}
+
+test "signed wrapping" {
+ try testSignedWrappingEval(maxInt(i32));
+ comptime try testSignedWrappingEval(maxInt(i32));
+}
+fn testSignedWrappingEval(x: i32) !void {
+ const min_val = x +% 1;
+ try expect(min_val == minInt(i32));
+ const max_val = min_val -% 1;
+ try expect(max_val == maxInt(i32));
+}
+
+test "signed negation wrapping" {
+ try testSignedNegationWrappingEval(minInt(i16));
+ comptime try testSignedNegationWrappingEval(minInt(i16));
+}
+fn testSignedNegationWrappingEval(x: i16) !void {
+ try expect(x == -32768);
+ const neg = -%x;
+ try expect(neg == -32768);
+}
+
+test "unsigned negation wrapping" {
+ try testUnsignedNegationWrappingEval(1);
+ comptime try testUnsignedNegationWrappingEval(1);
+}
+fn testUnsignedNegationWrappingEval(x: u16) !void {
+ try expect(x == 1);
+ const neg = -%x;
+ try expect(neg == maxInt(u16));
+}
+
+test "unsigned 64-bit division" {
+ try test_u64_div();
+ comptime try test_u64_div();
+}
+fn test_u64_div() !void {
+ const result = divWithResult(1152921504606846976, 34359738365);
+ try expect(result.quotient == 33554432);
+ try expect(result.remainder == 100663296);
+}
+fn divWithResult(a: u64, b: u64) DivResult {
+ return DivResult{
+ .quotient = a / b,
+ .remainder = a % b,
+ };
+}
+const DivResult = struct {
+ quotient: u64,
+ remainder: u64,
+};
+
+test "binary not" {
+ try expect(comptime x: {
+ break :x ~@as(u16, 0b1010101010101010) == 0b0101010101010101;
+ });
+ try expect(comptime x: {
+ break :x ~@as(u64, 2147483647) == 18446744071562067968;
+ });
+ try testBinaryNot(0b1010101010101010);
+}
+
+fn testBinaryNot(x: u16) !void {
+ try expect(~x == 0b0101010101010101);
+}
+
+test "small int addition" {
+ var x: u2 = 0;
+ try expect(x == 0);
+
+ x += 1;
+ try expect(x == 1);
+
+ x += 1;
+ try expect(x == 2);
+
+ x += 1;
+ try expect(x == 3);
+
+ var result: @TypeOf(x) = 3;
+ try expect(@addWithOverflow(@TypeOf(x), x, 1, &result));
+
+ try expect(result == 0);
+}
+
+test "float equality" {
+ const x: f64 = 0.012;
+ const y: f64 = x + 1.0;
+
+ try testFloatEqualityImpl(x, y);
+ comptime try testFloatEqualityImpl(x, y);
+}
+
+fn testFloatEqualityImpl(x: f64, y: f64) !void {
+ const y2 = x + 1.0;
+ try expect(y == y2);
+}
+
+test "allow signed integer division/remainder when values are comptime known and positive or exact" {
+ try expect(5 / 3 == 1);
+ try expect(-5 / -3 == 1);
+ try expect(-6 / 3 == -2);
+
+ try expect(5 % 3 == 2);
+ try expect(-6 % 3 == 0);
+}
+
+test "hex float literal parsing" {
+ comptime try expect(0x1.0 == 1.0);
+}
+
+test "quad hex float literal parsing in range" {
+ const a = 0x1.af23456789bbaaab347645365cdep+5;
+ const b = 0x1.dedafcff354b6ae9758763545432p-9;
+ const c = 0x1.2f34dd5f437e849b4baab754cdefp+4534;
+ const d = 0x1.edcbff8ad76ab5bf46463233214fp-435;
+ if (false) {
+ a;
+ b;
+ c;
+ d;
+ }
+}
+
+test "quad hex float literal parsing accurate" {
+ const a: f128 = 0x1.1111222233334444555566667777p+0;
+
+ // implied 1 is dropped, with an exponent of 0 (0x3fff) after biasing.
+ const expected: u128 = 0x3fff1111222233334444555566667777;
+ try expect(@bitCast(u128, a) == expected);
+
+ // non-normalized
+ const b: f128 = 0x11.111222233334444555566667777p-4;
+ try expect(@bitCast(u128, b) == expected);
+
+ const S = struct {
+ fn doTheTest() !void {
+ {
+ var f: f128 = 0x1.2eab345678439abcdefea56782346p+5;
+ try expect(@bitCast(u128, f) == 0x40042eab345678439abcdefea5678234);
+ }
+ {
+ var f: f128 = 0x1.edcb34a235253948765432134674fp-1;
+ try expect(@bitCast(u128, f) == 0x3ffeedcb34a235253948765432134674);
+ }
+ {
+ var f: f128 = 0x1.353e45674d89abacc3a2ebf3ff4ffp-50;
+ try expect(@bitCast(u128, f) == 0x3fcd353e45674d89abacc3a2ebf3ff50);
+ }
+ {
+ var f: f128 = 0x1.ed8764648369535adf4be3214567fp-9;
+ try expect(@bitCast(u128, f) == 0x3ff6ed8764648369535adf4be3214568);
+ }
+ const exp2ft = [_]f64{
+ 0x1.6a09e667f3bcdp-1,
+ 0x1.7a11473eb0187p-1,
+ 0x1.8ace5422aa0dbp-1,
+ 0x1.9c49182a3f090p-1,
+ 0x1.ae89f995ad3adp-1,
+ 0x1.c199bdd85529cp-1,
+ 0x1.d5818dcfba487p-1,
+ 0x1.ea4afa2a490dap-1,
+ 0x1.0000000000000p+0,
+ 0x1.0b5586cf9890fp+0,
+ 0x1.172b83c7d517bp+0,
+ 0x1.2387a6e756238p+0,
+ 0x1.306fe0a31b715p+0,
+ 0x1.3dea64c123422p+0,
+ 0x1.4bfdad5362a27p+0,
+ 0x1.5ab07dd485429p+0,
+ 0x1.8p23,
+ 0x1.62e430p-1,
+ 0x1.ebfbe0p-3,
+ 0x1.c6b348p-5,
+ 0x1.3b2c9cp-7,
+ 0x1.0p127,
+ -0x1.0p-149,
+ };
+
+ const answers = [_]u64{
+ 0x3fe6a09e667f3bcd,
+ 0x3fe7a11473eb0187,
+ 0x3fe8ace5422aa0db,
+ 0x3fe9c49182a3f090,
+ 0x3feae89f995ad3ad,
+ 0x3fec199bdd85529c,
+ 0x3fed5818dcfba487,
+ 0x3feea4afa2a490da,
+ 0x3ff0000000000000,
+ 0x3ff0b5586cf9890f,
+ 0x3ff172b83c7d517b,
+ 0x3ff2387a6e756238,
+ 0x3ff306fe0a31b715,
+ 0x3ff3dea64c123422,
+ 0x3ff4bfdad5362a27,
+ 0x3ff5ab07dd485429,
+ 0x4168000000000000,
+ 0x3fe62e4300000000,
+ 0x3fcebfbe00000000,
+ 0x3fac6b3480000000,
+ 0x3f83b2c9c0000000,
+ 0x47e0000000000000,
+ 0xb6a0000000000000,
+ };
+
+ for (exp2ft) |x, i| {
+ try expect(@bitCast(u64, x) == answers[i]);
+ }
+ }
+ };
+ try S.doTheTest();
+ comptime try S.doTheTest();
+}
+
+test "underscore separator parsing" {
+ try expect(0_0_0_0 == 0);
+ try expect(1_234_567 == 1234567);
+ try expect(001_234_567 == 1234567);
+ try expect(0_0_1_2_3_4_5_6_7 == 1234567);
+
+ try expect(0b0_0_0_0 == 0);
+ try expect(0b1010_1010 == 0b10101010);
+ try expect(0b0000_1010_1010 == 0b10101010);
+ try expect(0b1_0_1_0_1_0_1_0 == 0b10101010);
+
+ try expect(0o0_0_0_0 == 0);
+ try expect(0o1010_1010 == 0o10101010);
+ try expect(0o0000_1010_1010 == 0o10101010);
+ try expect(0o1_0_1_0_1_0_1_0 == 0o10101010);
+
+ try expect(0x0_0_0_0 == 0);
+ try expect(0x1010_1010 == 0x10101010);
+ try expect(0x0000_1010_1010 == 0x10101010);
+ try expect(0x1_0_1_0_1_0_1_0 == 0x10101010);
+
+ try expect(123_456.789_000e1_0 == 123456.789000e10);
+ try expect(0_1_2_3_4_5_6.7_8_9_0_0_0e0_0_1_0 == 123456.789000e10);
+
+ try expect(0x1234_5678.9ABC_DEF0p-1_0 == 0x12345678.9ABCDEF0p-10);
+ 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);
+}
+
+test "hex float literal within range" {
+ const a = 0x1.0p16383;
+ const b = 0x0.1p16387;
+ const c = 0x1.0p-16382;
+ if (false) {
+ a;
+ b;
+ c;
+ }
+}
+
+test "truncating shift left" {
+ try testShlTrunc(maxInt(u16));
+ comptime try testShlTrunc(maxInt(u16));
+}
+fn testShlTrunc(x: u16) !void {
+ const shifted = x << 1;
+ try expect(shifted == 65534);
+}
+
+test "truncating shift right" {
+ try testShrTrunc(maxInt(u16));
+ comptime try testShrTrunc(maxInt(u16));
+}
+fn testShrTrunc(x: u16) !void {
+ const shifted = x >> 1;
+ try expect(shifted == 32767);
+}
+
+test "exact shift left" {
+ try testShlExact(0b00110101);
+ comptime try testShlExact(0b00110101);
+}
+fn testShlExact(x: u8) !void {
+ const shifted = @shlExact(x, 2);
+ try expect(shifted == 0b11010100);
+}
+
+test "exact shift right" {
+ try testShrExact(0b10110100);
+ comptime try testShrExact(0b10110100);
+}
+fn testShrExact(x: u8) !void {
+ const shifted = @shrExact(x, 2);
+ try expect(shifted == 0b00101101);
+}
+
+test "shift left/right on u0 operand" {
+ const S = struct {
+ fn doTheTest() !void {
+ var x: u0 = 0;
+ var y: u0 = 0;
+ try expectEqual(@as(u0, 0), x << 0);
+ try expectEqual(@as(u0, 0), x >> 0);
+ try expectEqual(@as(u0, 0), x << y);
+ try expectEqual(@as(u0, 0), x >> y);
+ try expectEqual(@as(u0, 0), @shlExact(x, 0));
+ try expectEqual(@as(u0, 0), @shrExact(x, 0));
+ try expectEqual(@as(u0, 0), @shlExact(x, y));
+ try expectEqual(@as(u0, 0), @shrExact(x, y));
+ }
+ };
+ try S.doTheTest();
+ comptime try S.doTheTest();
+}
+
+test "comptime_int addition" {
+ comptime {
+ try expect(35361831660712422535336160538497375248 + 101752735581729509668353361206450473702 == 137114567242441932203689521744947848950);
+ try expect(594491908217841670578297176641415611445982232488944558774612 + 390603545391089362063884922208143568023166603618446395589768 == 985095453608931032642182098849559179469148836107390954364380);
+ }
+}
+
+test "comptime_int multiplication" {
+ comptime {
+ try expect(
+ 45960427431263824329884196484953148229 * 128339149605334697009938835852565949723 == 5898522172026096622534201617172456926982464453350084962781392314016180490567,
+ );
+ try expect(
+ 594491908217841670578297176641415611445982232488944558774612 * 390603545391089362063884922208143568023166603618446395589768 == 232210647056203049913662402532976186578842425262306016094292237500303028346593132411865381225871291702600263463125370016,
+ );
+ }
+}
+
+test "comptime_int shifting" {
+ comptime {
+ try expect((@as(u128, 1) << 127) == 0x80000000000000000000000000000000);
+ }
+}
+
+test "comptime_int multi-limb shift and mask" {
+ comptime {
+ var a = 0xefffffffa0000001eeeeeeefaaaaaaab;
+
+ try expect(@as(u32, a & 0xffffffff) == 0xaaaaaaab);
+ a >>= 32;
+ try expect(@as(u32, a & 0xffffffff) == 0xeeeeeeef);
+ a >>= 32;
+ try expect(@as(u32, a & 0xffffffff) == 0xa0000001);
+ a >>= 32;
+ try expect(@as(u32, a & 0xffffffff) == 0xefffffff);
+ a >>= 32;
+
+ try expect(a == 0);
+ }
+}
+
+test "comptime_int multi-limb partial shift right" {
+ comptime {
+ var a = 0x1ffffffffeeeeeeee;
+ a >>= 16;
+ try expect(a == 0x1ffffffffeeee);
+ }
+}
+
+test "xor" {
+ try test_xor();
+ comptime try test_xor();
+}
+
+fn test_xor() !void {
+ try expect(0xFF ^ 0x00 == 0xFF);
+ try expect(0xF0 ^ 0x0F == 0xFF);
+ try expect(0xFF ^ 0xF0 == 0x0F);
+ try expect(0xFF ^ 0x0F == 0xF0);
+ try expect(0xFF ^ 0xFF == 0x00);
+}
+
+test "comptime_int xor" {
+ comptime {
+ try expect(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0x00000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
+ try expect(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0x0000000000000000FFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
+ try expect(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x0000000000000000FFFFFFFFFFFFFFFF);
+ try expect(0x0000000000000000FFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFF0000000000000000);
+ try expect(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000000000000000000000000000);
+ try expect(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0x00000000FFFFFFFF00000000FFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
+ try expect(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000FFFFFFFF00000000FFFFFFFF);
+ try expect(0x00000000FFFFFFFF00000000FFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFF00000000FFFFFFFF00000000);
+ }
+}
+
+test "f128" {
+ try test_f128();
+ comptime try test_f128();
+}
+
+fn make_f128(x: f128) f128 {
+ return x;
+}
+
+fn test_f128() !void {
+ try expect(@sizeOf(f128) == 16);
+ try expect(make_f128(1.0) == 1.0);
+ try expect(make_f128(1.0) != 1.1);
+ try expect(make_f128(1.0) > 0.9);
+ try expect(make_f128(1.0) >= 0.9);
+ try expect(make_f128(1.0) >= 1.0);
+ try should_not_be_zero(1.0);
+}
+
+fn should_not_be_zero(x: f128) !void {
+ try expect(x != 0.0);
+}
+
+test "comptime float rem int" {
+ comptime {
+ var x = @as(f32, 1) % 2;
+ try expect(x == 1.0);
+ }
+}
+
+test "remainder division" {
+ comptime try remdiv(f16);
+ comptime try remdiv(f32);
+ comptime try remdiv(f64);
+ comptime try remdiv(f128);
+ try remdiv(f16);
+ try remdiv(f64);
+ try remdiv(f128);
+}
+
+fn remdiv(comptime T: type) !void {
+ try expect(@as(T, 1) == @as(T, 1) % @as(T, 2));
+ try expect(@as(T, 1) == @as(T, 7) % @as(T, 3));
+}
+
+test "@sqrt" {
+ try testSqrt(f64, 12.0);
+ comptime try testSqrt(f64, 12.0);
+ try testSqrt(f32, 13.0);
+ comptime try testSqrt(f32, 13.0);
+ try testSqrt(f16, 13.0);
+ comptime try testSqrt(f16, 13.0);
+
+ const x = 14.0;
+ const y = x * x;
+ const z = @sqrt(y);
+ comptime try expect(z == x);
+}
+
+fn testSqrt(comptime T: type, x: T) !void {
+ try expect(@sqrt(x * x) == x);
+}
+
+test "@fabs" {
+ try testFabs(f128, 12.0);
+ comptime try testFabs(f128, 12.0);
+ try testFabs(f64, 12.0);
+ comptime try testFabs(f64, 12.0);
+ try testFabs(f32, 12.0);
+ comptime try testFabs(f32, 12.0);
+ try testFabs(f16, 12.0);
+ comptime try testFabs(f16, 12.0);
+
+ const x = 14.0;
+ const y = -x;
+ const z = @fabs(y);
+ comptime try expectEqual(x, z);
+}
+
+fn testFabs(comptime T: type, x: T) !void {
+ const y = -x;
+ const z = @fabs(y);
+ try expectEqual(x, z);
+}
+
+test "@floor" {
+ // FIXME: Generates a floorl function call
+ // testFloor(f128, 12.0);
+ comptime try testFloor(f128, 12.0);
+ try testFloor(f64, 12.0);
+ comptime try testFloor(f64, 12.0);
+ try testFloor(f32, 12.0);
+ comptime try testFloor(f32, 12.0);
+ try testFloor(f16, 12.0);
+ comptime try testFloor(f16, 12.0);
+
+ const x = 14.0;
+ const y = x + 0.7;
+ const z = @floor(y);
+ comptime try expectEqual(x, z);
+}
+
+fn testFloor(comptime T: type, x: T) !void {
+ const y = x + 0.6;
+ const z = @floor(y);
+ try expectEqual(x, z);
+}
+
+test "@ceil" {
+ // FIXME: Generates a ceill function call
+ //testCeil(f128, 12.0);
+ comptime try testCeil(f128, 12.0);
+ try testCeil(f64, 12.0);
+ comptime try testCeil(f64, 12.0);
+ try testCeil(f32, 12.0);
+ comptime try testCeil(f32, 12.0);
+ try testCeil(f16, 12.0);
+ comptime try testCeil(f16, 12.0);
+
+ const x = 14.0;
+ const y = x - 0.7;
+ const z = @ceil(y);
+ comptime try expectEqual(x, z);
+}
+
+fn testCeil(comptime T: type, x: T) !void {
+ const y = x - 0.8;
+ const z = @ceil(y);
+ try expectEqual(x, z);
+}
+
+test "@trunc" {
+ // FIXME: Generates a truncl function call
+ //testTrunc(f128, 12.0);
+ comptime try testTrunc(f128, 12.0);
+ try testTrunc(f64, 12.0);
+ comptime try testTrunc(f64, 12.0);
+ try testTrunc(f32, 12.0);
+ comptime try testTrunc(f32, 12.0);
+ try testTrunc(f16, 12.0);
+ comptime try testTrunc(f16, 12.0);
+
+ const x = 14.0;
+ const y = x + 0.7;
+ const z = @trunc(y);
+ comptime try expectEqual(x, z);
+}
+
+fn testTrunc(comptime T: type, x: T) !void {
+ {
+ const y = x + 0.8;
+ const z = @trunc(y);
+ try expectEqual(x, z);
+ }
+
+ {
+ const y = -x - 0.8;
+ const z = @trunc(y);
+ try expectEqual(-x, z);
+ }
+}
+
+test "@round" {
+ // FIXME: Generates a roundl function call
+ //testRound(f128, 12.0);
+ comptime try testRound(f128, 12.0);
+ try testRound(f64, 12.0);
+ comptime try testRound(f64, 12.0);
+ try testRound(f32, 12.0);
+ comptime try testRound(f32, 12.0);
+ try testRound(f16, 12.0);
+ comptime try testRound(f16, 12.0);
+
+ const x = 14.0;
+ const y = x + 0.4;
+ const z = @round(y);
+ comptime try expectEqual(x, z);
+}
+
+fn testRound(comptime T: type, x: T) !void {
+ const y = x - 0.5;
+ const z = @round(y);
+ try expectEqual(x, z);
+}
+
+test "comptime_int param and return" {
+ const a = comptimeAdd(35361831660712422535336160538497375248, 101752735581729509668353361206450473702);
+ try expect(a == 137114567242441932203689521744947848950);
+
+ const b = comptimeAdd(594491908217841670578297176641415611445982232488944558774612, 390603545391089362063884922208143568023166603618446395589768);
+ try expect(b == 985095453608931032642182098849559179469148836107390954364380);
+}
+
+fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int {
+ return a + b;
+}
+
+test "vector integer addition" {
+ const S = struct {
+ fn doTheTest() !void {
+ var a: std.meta.Vector(4, i32) = [_]i32{ 1, 2, 3, 4 };
+ var b: std.meta.Vector(4, i32) = [_]i32{ 5, 6, 7, 8 };
+ var result = a + b;
+ var result_array: [4]i32 = result;
+ const expected = [_]i32{ 6, 8, 10, 12 };
+ try expectEqualSlices(i32, &expected, &result_array);
+ }
+ };
+ try S.doTheTest();
+ comptime try S.doTheTest();
+}
+
+test "NaN comparison" {
+ try testNanEqNan(f16);
+ try testNanEqNan(f32);
+ try testNanEqNan(f64);
+ try testNanEqNan(f128);
+ comptime try testNanEqNan(f16);
+ comptime try testNanEqNan(f32);
+ comptime try testNanEqNan(f64);
+ comptime try testNanEqNan(f128);
+}
+
+fn testNanEqNan(comptime F: type) !void {
+ var nan1 = std.math.nan(F);
+ var nan2 = std.math.nan(F);
+ try expect(nan1 != nan2);
+ try expect(!(nan1 == nan2));
+ try expect(!(nan1 > nan2));
+ try expect(!(nan1 >= nan2));
+ try expect(!(nan1 < nan2));
+ try expect(!(nan1 <= nan2));
+}
+
+test "128-bit multiplication" {
+ var a: i128 = 3;
+ var b: i128 = 2;
+ var c = a * b;
+ try expect(c == 6);
+}
+
+test "vector comparison" {
+ const S = struct {
+ fn doTheTest() !void {
+ var a: std.meta.Vector(6, i32) = [_]i32{ 1, 3, -1, 5, 7, 9 };
+ var b: std.meta.Vector(6, i32) = [_]i32{ -1, 3, 0, 6, 10, -10 };
+ try expect(mem.eql(bool, &@as([6]bool, a < b), &[_]bool{ false, false, true, true, true, false }));
+ try expect(mem.eql(bool, &@as([6]bool, a <= b), &[_]bool{ false, true, true, true, true, false }));
+ try expect(mem.eql(bool, &@as([6]bool, a == b), &[_]bool{ false, true, false, false, false, false }));
+ try expect(mem.eql(bool, &@as([6]bool, a != b), &[_]bool{ true, false, true, true, true, true }));
+ try expect(mem.eql(bool, &@as([6]bool, a > b), &[_]bool{ true, false, false, false, false, true }));
+ try expect(mem.eql(bool, &@as([6]bool, a >= b), &[_]bool{ true, true, false, false, false, true }));
+ }
+ };
+ try S.doTheTest();
+ comptime try S.doTheTest();
+}
+
+test "compare undefined literal with comptime_int" {
+ var x = undefined == 1;
+ // x is now undefined with type bool
+ x = true;
+ try expect(x);
+}
+
+test "signed zeros are represented properly" {
+ const S = struct {
+ fn doTheTest() !void {
+ inline for ([_]type{ f16, f32, f64, f128 }) |T| {
+ const ST = std.meta.Int(.unsigned, @typeInfo(T).Float.bits);
+ var as_fp_val = -@as(T, 0.0);
+ var as_uint_val = @bitCast(ST, as_fp_val);
+ // Ensure the sign bit is set.
+ try expect(as_uint_val >> (@typeInfo(T).Float.bits - 1) == 1);
+ }
+ }
+ };
+
+ try S.doTheTest();
+ comptime try S.doTheTest();
+}
test/behavior.zig
@@ -10,6 +10,7 @@ test {
_ = @import("behavior/eval.zig");
_ = @import("behavior/generics.zig");
_ = @import("behavior/if.zig");
+ _ = @import("behavior/math.zig");
_ = @import("behavior/member_func.zig");
_ = @import("behavior/pointers.zig");
_ = @import("behavior/sizeof_and_typeof.zig");
@@ -119,7 +120,7 @@ test {
_ = @import("behavior/incomplete_struct_param_tld.zig");
_ = @import("behavior/inttoptr.zig");
_ = @import("behavior/ir_block_deps.zig");
- _ = @import("behavior/math.zig");
+ _ = @import("behavior/math_stage1.zig");
_ = @import("behavior/maximum_minimum.zig");
_ = @import("behavior/merge_error_sets.zig");
_ = @import("behavior/misc.zig");