Commit 33e77f127d

Andrew Kelley <andrew@ziglang.org>
2021-09-29 07:38:51
stage2: implement `@clz` and `@ctz`
Also improve the LLVM backend to support lowering bigints to LLVM values. Moves over a bunch of math.zig test cases to the "passing for stage2" section.
1 parent 7efc2a0
src/codegen/llvm/bindings.zig
@@ -172,6 +172,9 @@ pub const Type = opaque {
     pub const constInt = LLVMConstInt;
     extern fn LLVMConstInt(IntTy: *const Type, N: c_ulonglong, SignExtend: Bool) *const Value;
 
+    pub const constIntOfArbitraryPrecision = LLVMConstIntOfArbitraryPrecision;
+    extern fn LLVMConstIntOfArbitraryPrecision(IntTy: *const Type, NumWords: c_uint, Words: [*]const u64) *const Value;
+
     pub const constReal = LLVMConstReal;
     extern fn LLVMConstReal(RealTy: *const Type, N: f64) *const Value;
 
@@ -300,7 +303,7 @@ extern fn LLVMGetInlineAsm(
 pub const functionType = LLVMFunctionType;
 extern fn LLVMFunctionType(
     ReturnType: *const Type,
-    ParamTypes: [*]*const Type,
+    ParamTypes: [*]const *const Type,
     ParamCount: c_uint,
     IsVarArg: Bool,
 ) *const Type;
@@ -346,7 +349,7 @@ pub const Builder = opaque {
     extern fn LLVMBuildCall(
         *const Builder,
         Fn: *const Value,
-        Args: [*]*const Value,
+        Args: [*]const *const Value,
         NumArgs: c_uint,
         Name: [*:0]const u8,
     ) *const Value;
src/codegen/c.zig
@@ -962,6 +962,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
             .memcpy           => try airMemcpy(f, inst),
             .set_union_tag    => try airSetUnionTag(f, inst),
             .get_union_tag    => try airGetUnionTag(f, inst),
+            .clz              => try airBuiltinCall(f, inst, "clz"),
+            .ctz              => try airBuiltinCall(f, inst, "ctz"),
 
             .int_to_float,
             .float_to_int,
@@ -2075,6 +2077,23 @@ fn airSimpleCast(f: *Function, inst: Air.Inst.Index) !CValue {
     return local;
 }
 
+fn airBuiltinCall(f: *Function, inst: Air.Inst.Index, fn_name: [*:0]const u8) !CValue {
+    if (f.liveness.isUnused(inst)) return CValue.none;
+
+    const inst_ty = f.air.typeOfIndex(inst);
+    const local = try f.allocLocal(inst_ty, .Const);
+    const ty_op = f.air.instructions.items(.data)[inst].ty_op;
+    const writer = f.object.writer();
+    const operand = try f.resolveInst(ty_op.operand);
+
+    // TODO implement the function in zig.h and call it here
+
+    try writer.print(" = {s}(", .{fn_name});
+    try f.writeCValue(writer, operand);
+    try writer.writeAll(");\n");
+    return local;
+}
+
 fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue {
     const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
     const extra = f.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
src/codegen/llvm.zig
@@ -889,14 +889,14 @@ pub const DeclGen = struct {
                 const llvm_type = try self.llvmType(tv.ty);
                 if (bigint.eqZero()) return llvm_type.constNull();
 
-                if (bigint.limbs.len != 1) {
-                    return self.todo("implement bigger bigint", .{});
-                }
-                const llvm_int = llvm_type.constInt(bigint.limbs[0], .False);
+                const unsigned_val = if (bigint.limbs.len == 1)
+                    llvm_type.constInt(bigint.limbs[0], .False)
+                else
+                    llvm_type.constIntOfArbitraryPrecision(@intCast(c_uint, bigint.limbs.len), bigint.limbs.ptr);
                 if (!bigint.positive) {
-                    return llvm.constNeg(llvm_int);
+                    return llvm.constNeg(unsigned_val);
                 }
-                return llvm_int;
+                return unsigned_val;
             },
             .Enum => {
                 const llvm_type = try self.llvmType(tv.ty);
@@ -1310,6 +1310,8 @@ pub const FuncGen = struct {
                 .memcpy         => try self.airMemcpy(inst),
                 .set_union_tag  => try self.airSetUnionTag(inst),
                 .get_union_tag  => try self.airGetUnionTag(inst),
+                .clz            => try self.airClzCtz(inst, "ctlz"),
+                .ctz            => try self.airClzCtz(inst, "cttz"),
 
                 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
                 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
@@ -2699,6 +2701,41 @@ pub const FuncGen = struct {
         return self.builder.buildExtractValue(un, 1, "");
     }
 
+    fn airClzCtz(self: *FuncGen, inst: Air.Inst.Index, prefix: [*:0]const u8) !?*const llvm.Value {
+        if (self.liveness.isUnused(inst)) return null;
+
+        const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+        const operand_ty = self.air.typeOf(ty_op.operand);
+        const operand = try self.resolveInst(ty_op.operand);
+        const target = self.dg.module.getTarget();
+        const bits = operand_ty.intInfo(target).bits;
+
+        var fn_name_buf: [100]u8 = undefined;
+        const llvm_fn_name = std.fmt.bufPrintZ(&fn_name_buf, "llvm.{s}.i{d}", .{
+            prefix, bits,
+        }) catch unreachable;
+        const llvm_i1 = self.context.intType(1);
+        const fn_val = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: {
+            const operand_llvm_ty = try self.dg.llvmType(operand_ty);
+            const param_types = [_]*const llvm.Type{ operand_llvm_ty, llvm_i1 };
+            const fn_type = llvm.functionType(operand_llvm_ty, &param_types, param_types.len, .False);
+            break :blk self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type);
+        };
+
+        const params = [_]*const llvm.Value{ operand, llvm_i1.constNull() };
+        const wrong_size_result = self.builder.buildCall(fn_val, &params, params.len, "");
+        const result_ty = self.air.typeOfIndex(inst);
+        const result_llvm_ty = try self.dg.llvmType(result_ty);
+        const result_bits = result_ty.intInfo(target).bits;
+        if (bits > result_bits) {
+            return self.builder.buildTrunc(wrong_size_result, result_llvm_ty, "");
+        } else if (bits < result_bits) {
+            return self.builder.buildZExt(wrong_size_result, result_llvm_ty, "");
+        } else {
+            return wrong_size_result;
+        }
+    }
+
     fn fieldPtr(
         self: *FuncGen,
         inst: Air.Inst.Index,
src/Air.zig
@@ -160,6 +160,14 @@ pub const Inst = struct {
         /// Result type is the return type of the function being called.
         /// Uses the `pl_op` field with the `Call` payload. operand is the callee.
         call,
+        /// Count leading zeroes of an integer according to its representation in twos complement.
+        /// Result type will always be an unsigned integer big enough to fit the answer.
+        /// Uses the `ty_op` field.
+        clz,
+        /// Count trailing zeroes of an integer according to its representation in twos complement.
+        /// Result type will always be an unsigned integer big enough to fit the answer.
+        /// Uses the `ty_op` field.
+        ctz,
 
         /// `<`. Result type is always bool.
         /// Uses the `bin_op` field.
@@ -669,6 +677,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
         .float_to_int,
         .int_to_float,
         .get_union_tag,
+        .clz,
+        .ctz,
         => return air.getRefType(datas[inst].ty_op.ty),
 
         .loop,
src/codegen.zig
@@ -896,6 +896,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
                     .memset          => try self.airMemset(inst),
                     .set_union_tag   => try self.airSetUnionTag(inst),
                     .get_union_tag   => try self.airGetUnionTag(inst),
+                    .clz             => try self.airClz(inst),
+                    .ctz             => try self.airCtz(inst),
 
                     .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
                     .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
@@ -1606,6 +1608,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
             return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
         }
 
+        fn airClz(self: *Self, inst: Air.Inst.Index) !void {
+            const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+            const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
+                else => return self.fail("TODO implement airClz for {}", .{self.target.cpu.arch}),
+            };
+            return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
+        }
+
+        fn airCtz(self: *Self, inst: Air.Inst.Index) !void {
+            const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+            const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
+                else => return self.fail("TODO implement airCtz for {}", .{self.target.cpu.arch}),
+            };
+            return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
+        }
+
         fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
             if (!self.liveness.operandDies(inst, op_index))
                 return false;
src/Liveness.zig
@@ -304,6 +304,8 @@ fn analyzeInst(
         .float_to_int,
         .int_to_float,
         .get_union_tag,
+        .clz,
+        .ctz,
         => {
             const o = inst_datas[inst].ty_op;
             return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none });
src/print_air.zig
@@ -186,6 +186,8 @@ const Writer = struct {
             .int_to_float,
             .float_to_int,
             .get_union_tag,
+            .clz,
+            .ctz,
             => try w.writeTyOp(s, inst),
 
             .block,
src/Sema.zig
@@ -4611,8 +4611,8 @@ fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
     const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);
     const operand = sema.resolveInst(extra.rhs);
 
-    const dest_is_comptime_int = try sema.requireIntegerType(block, dest_ty_src, dest_type);
-    _ = try sema.requireIntegerType(block, operand_src, sema.typeOf(operand));
+    const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_type);
+    _ = try sema.checkIntType(block, operand_src, sema.typeOf(operand));
 
     if (try sema.isComptimeKnown(block, operand_src, operand)) {
         return sema.coerce(block, dest_type, operand, operand_src);
@@ -8384,7 +8384,7 @@ fn zirIntToFloat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Compile
     const operand = sema.resolveInst(extra.rhs);
     const operand_ty = sema.typeOf(operand);
 
-    try sema.checkIntType(block, ty_src, dest_ty);
+    _ = try sema.checkIntType(block, ty_src, dest_ty);
     try sema.checkFloatType(block, operand_src, operand_ty);
 
     if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
@@ -8493,8 +8493,8 @@ fn zirTruncate(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
     const operand = sema.resolveInst(extra.rhs);
     const operand_ty = sema.typeOf(operand);
     const mod = sema.mod;
-    const dest_is_comptime_int = try sema.requireIntegerType(block, dest_ty_src, dest_ty);
-    const src_is_comptime_int = try sema.requireIntegerType(block, operand_src, operand_ty);
+    const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_ty);
+    const src_is_comptime_int = try sema.checkIntType(block, operand_src, operand_ty);
 
     if (dest_is_comptime_int) {
         return sema.coerce(block, dest_ty, operand, operand_src);
@@ -8552,14 +8552,56 @@ fn zirAlignCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE
 
 fn zirClz(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
-    const src = inst_data.src();
-    return sema.mod.fail(&block.base, src, "TODO: Sema.zirClz", .{});
+    const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+    const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
+    const operand = sema.resolveInst(inst_data.operand);
+    const operand_ty = sema.typeOf(operand);
+    // TODO implement support for vectors
+    if (operand_ty.zigTypeTag() != .Int) {
+        return sema.mod.fail(&block.base, ty_src, "expected integer type, found '{}'", .{
+            operand_ty,
+        });
+    }
+    const target = sema.mod.getTarget();
+    const bits = operand_ty.intInfo(target).bits;
+    if (bits == 0) return Air.Inst.Ref.zero;
+
+    const result_ty = try Type.smallestUnsignedInt(sema.arena, bits);
+
+    const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
+        if (val.isUndef()) return sema.addConstUndef(result_ty);
+        return sema.addIntUnsigned(result_ty, val.clz(operand_ty, target));
+    } else operand_src;
+
+    try sema.requireRuntimeBlock(block, runtime_src);
+    return block.addTyOp(.clz, result_ty, operand);
 }
 
 fn zirCtz(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
-    const src = inst_data.src();
-    return sema.mod.fail(&block.base, src, "TODO: Sema.zirCtz", .{});
+    const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+    const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
+    const operand = sema.resolveInst(inst_data.operand);
+    const operand_ty = sema.typeOf(operand);
+    // TODO implement support for vectors
+    if (operand_ty.zigTypeTag() != .Int) {
+        return sema.mod.fail(&block.base, ty_src, "expected integer type, found '{}'", .{
+            operand_ty,
+        });
+    }
+    const target = sema.mod.getTarget();
+    const bits = operand_ty.intInfo(target).bits;
+    if (bits == 0) return Air.Inst.Ref.zero;
+
+    const result_ty = try Type.smallestUnsignedInt(sema.arena, bits);
+
+    const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
+        if (val.isUndef()) return sema.addConstUndef(result_ty);
+        return sema.mod.fail(&block.base, operand_src, "TODO: implement comptime @ctz", .{});
+    } else operand_src;
+
+    try sema.requireRuntimeBlock(block, runtime_src);
+    return block.addTyOp(.ctz, result_ty, operand);
 }
 
 fn zirPopCount(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -8616,17 +8658,12 @@ fn zirOffsetOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
     return sema.mod.fail(&block.base, src, "TODO: Sema.zirOffsetOf", .{});
 }
 
-fn checkIntType(
-    sema: *Sema,
-    block: *Scope.Block,
-    ty_src: LazySrcLoc,
-    ty: Type,
-) CompileError!void {
+/// Returns `true` if the type was a comptime_int.
+fn checkIntType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) CompileError!bool {
     switch (ty.zigTypeTag()) {
-        .ComptimeInt, .Int => {},
-        else => return sema.mod.fail(&block.base, ty_src, "expected integer type, found '{}'", .{
-            ty,
-        }),
+        .ComptimeInt => return true,
+        .Int => return false,
+        else => return sema.mod.fail(&block.base, src, "expected integer type, found '{}'", .{ty}),
     }
 }
 
@@ -9416,14 +9453,6 @@ fn requireRuntimeBlock(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void
     try sema.requireFunctionBlock(block, src);
 }
 
-fn requireIntegerType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) !bool {
-    switch (ty.zigTypeTag()) {
-        .ComptimeInt => return true,
-        .Int => return false,
-        else => return sema.mod.fail(&block.base, src, "expected integer type, found '{}'", .{ty}),
-    }
-}
-
 /// Emit a compile error if type cannot be used for a runtime variable.
 fn validateVarType(
     sema: *Sema,
src/type.zig
@@ -3902,16 +3902,16 @@ pub const Type = extern union {
         const bits = bits: {
             if (max == 0) break :bits 0;
             const base = std.math.log2(max);
-            const upper = (@as(u64, 1) << base) - 1;
+            const upper = (@as(u64, 1) << @intCast(u6, base)) - 1;
             break :bits base + @boolToInt(upper < max);
         };
-        return switch (bits) {
+        return switch (@intCast(u16, bits)) {
             1 => initTag(.u1),
             8 => initTag(.u8),
             16 => initTag(.u16),
             32 => initTag(.u32),
             64 => initTag(.u64),
-            else => return Tag.int_unsigned.create(arena, bits),
+            else => |b| return Tag.int_unsigned.create(arena, b),
         };
     }
 };
src/value.zig
@@ -962,6 +962,45 @@ pub const Value = extern union {
         };
     }
 
+    pub fn clz(val: Value, ty: Type, target: Target) u64 {
+        const ty_bits = ty.intInfo(target).bits;
+        switch (val.tag()) {
+            .zero, .bool_false => return ty_bits,
+            .one, .bool_true => return ty_bits - 1,
+
+            .int_u64 => {
+                const big = @clz(u64, val.castTag(.int_u64).?.data);
+                return big + ty_bits - 64;
+            },
+            .int_i64 => {
+                @panic("TODO implement i64 Value clz");
+            },
+            .int_big_positive => {
+                // TODO: move this code into std lib big ints
+                const bigint = val.castTag(.int_big_positive).?.asBigInt();
+                // Limbs are stored in little-endian order but we need
+                // to iterate big-endian.
+                var total_limb_lz: u64 = 0;
+                var i: usize = bigint.limbs.len;
+                const bits_per_limb = @sizeOf(std.math.big.Limb) * 8;
+                while (i != 0) {
+                    i -= 1;
+                    const limb = bigint.limbs[i];
+                    const this_limb_lz = @clz(std.math.big.Limb, limb);
+                    total_limb_lz += this_limb_lz;
+                    if (this_limb_lz != bits_per_limb) break;
+                }
+                const total_limb_bits = bigint.limbs.len * bits_per_limb;
+                return total_limb_lz + ty_bits - total_limb_bits;
+            },
+            .int_big_negative => {
+                @panic("TODO implement int_big_negative Value clz");
+            },
+
+            else => unreachable,
+        }
+    }
+
     /// Asserts the value is an integer and not undefined.
     /// Returns the number of bits the value requires to represent stored in twos complement form.
     pub fn intBitCountTwosComp(self: Value) usize {
test/behavior/math.zig
@@ -53,3 +53,185 @@ fn testThreeExprInARow(f: bool, t: bool) !void {
 fn assertFalse(b: bool) !void {
     try expect(!b);
 }
+
+test "@clz" {
+    try testClz();
+    comptime try testClz();
+}
+
+fn testClz() !void {
+    try expect(testOneClz(u8, 0b10001010) == 0);
+    try expect(testOneClz(u8, 0b00001010) == 4);
+    try expect(testOneClz(u8, 0b00011010) == 3);
+    try expect(testOneClz(u8, 0b00000000) == 8);
+    try expect(testOneClz(u128, 0xffffffffffffffff) == 64);
+    try expect(testOneClz(u128, 0x10000000000000000) == 63);
+}
+
+fn testOneClz(comptime T: type, x: T) u32 {
+    return @clz(T, x);
+}
+
+test "const number literal" {
+    const one = 1;
+    const eleven = ten + one;
+
+    try expect(eleven == 11);
+}
+const ten = 10;
+
+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 "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;
+    _ = a;
+    _ = b;
+    _ = c;
+    _ = d;
+}
+
+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;
+    _ = a;
+    _ = b;
+    _ = c;
+}
+
+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 testOneXor(0xFF, 0x00, 0xFF);
+    try testOneXor(0xF0, 0x0F, 0xFF);
+    try testOneXor(0xFF, 0xF0, 0x0F);
+    try testOneXor(0xFF, 0x0F, 0xF0);
+    try testOneXor(0xFF, 0xFF, 0x00);
+}
+
+fn testOneXor(a: u8, b: u8, c: u8) !void {
+    try expect(a ^ b == c);
+}
+
+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 "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/behavior/math_stage1.zig
@@ -117,20 +117,6 @@ test "@*WithOverflow with u0 values" {
     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();
@@ -171,14 +157,6 @@ fn testCtzVectors() !void {
     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));
@@ -274,19 +252,6 @@ test "small int addition" {
     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);
@@ -296,23 +261,6 @@ test "allow signed integer division/remainder when values are comptime known and
     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;
 
@@ -403,45 +351,6 @@ test "quad hex float literal parsing accurate" {
     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));
@@ -497,81 +406,6 @@ test "shift left/right on u0 operand" {
     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();
@@ -757,18 +591,6 @@ fn testRound(comptime T: type, x: T) !void {
     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 {