Commit a8f4ac2b94

Jacob Young <jacobly0@users.noreply.github.com>
2023-03-03 06:18:35
CBE: implement big integer and vector comparisons
1 parent 874ae81
Changed files (7)
lib/zig.h
@@ -37,6 +37,14 @@ typedef char bool;
 #define zig_has_attribute(attribute) 0
 #endif
 
+#if __LITTLE_ENDIAN__ || _MSC_VER
+#define zig_little_endian 1
+#define zig_big_endian 0
+#else
+#define zig_little_endian 0
+#define zig_big_endian 1
+#endif
+
 #if __STDC_VERSION__ >= 201112L
 #define zig_threadlocal _Thread_local
 #elif defined(__GNUC__)
@@ -1379,7 +1387,7 @@ typedef   signed __int128 zig_i128;
 
 #else /* zig_has_int128 */
 
-#if __LITTLE_ENDIAN__ || _MSC_VER
+#if zig_little_endian
 typedef struct { zig_align(16) uint64_t lo; uint64_t hi; } zig_u128;
 typedef struct { zig_align(16) uint64_t lo; int64_t hi; } zig_i128;
 #else
@@ -1909,6 +1917,177 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, uint8_t bits) {
     return zig_bitcast_i128(zig_bit_reverse_u128(zig_bitcast_u128(val), bits));
 }
 
+/* ========================== Big Integer Support =========================== */
+
+static inline uint16_t zig_big_bytes(uint16_t bits) {
+    uint16_t bytes = (bits + CHAR_BIT - 1) / CHAR_BIT;
+    uint16_t alignment = 16;
+    while (alignment / 2 >= bytes) alignment /= 2;
+    return (bytes + alignment - 1) / alignment * alignment;
+}
+
+static inline int32_t zig_cmp_big(const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
+    const uint8_t *lhs_bytes = lhs;
+    const uint8_t *rhs_bytes = rhs;
+    uint16_t byte_offset = 0;
+    bool do_signed = is_signed;
+    uint16_t remaining_bytes = zig_big_bytes(bits);
+
+#if zig_little_endian
+    byte_offset = remaining_bytes;
+#endif
+
+    while (remaining_bytes >= 128 / CHAR_BIT) {
+        int32_t limb_cmp;
+
+#if zig_little_endian
+        byte_offset -= 128 / CHAR_BIT;
+#endif
+
+        if (do_signed) {
+            zig_i128 lhs_limb;
+            zig_i128 rhs_limb;
+
+            memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
+            memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
+            limb_cmp = zig_cmp_i128(lhs_limb, rhs_limb);
+            do_signed = false;
+        } else {
+            zig_u128 lhs_limb;
+            zig_u128 rhs_limb;
+
+            memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
+            memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
+            limb_cmp = zig_cmp_u128(lhs_limb, rhs_limb);
+        }
+
+        if (limb_cmp != 0) return limb_cmp;
+        remaining_bytes -= 128 / CHAR_BIT;
+
+#if zig_big_endian
+        byte_offset -= 128 / CHAR_BIT;
+#endif
+    }
+
+    while (remaining_bytes >= 64 / CHAR_BIT) {
+#if zig_little_endian
+        byte_offset -= 64 / CHAR_BIT;
+#endif
+
+        if (do_signed) {
+            int64_t lhs_limb;
+            int64_t rhs_limb;
+
+            memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
+            memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
+            if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb);
+            do_signed = false;
+        } else {
+            uint64_t lhs_limb;
+            uint64_t rhs_limb;
+
+            memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
+            memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
+            if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb);
+        }
+
+        remaining_bytes -= 64 / CHAR_BIT;
+
+#if zig_big_endian
+        byte_offset -= 64 / CHAR_BIT;
+#endif
+    }
+
+    while (remaining_bytes >= 32 / CHAR_BIT) {
+#if zig_little_endian
+        byte_offset -= 32 / CHAR_BIT;
+#endif
+
+        if (do_signed) {
+            int32_t lhs_limb;
+            int32_t rhs_limb;
+
+            memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
+            memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
+            if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb);
+            do_signed = false;
+        } else {
+            uint32_t lhs_limb;
+            uint32_t rhs_limb;
+
+            memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
+            memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
+            if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb);
+        }
+
+        remaining_bytes -= 32 / CHAR_BIT;
+
+#if zig_big_endian
+        byte_offset -= 32 / CHAR_BIT;
+#endif
+    }
+
+    while (remaining_bytes >= 16 / CHAR_BIT) {
+#if zig_little_endian
+        byte_offset -= 16 / CHAR_BIT;
+#endif
+
+        if (do_signed) {
+            int16_t lhs_limb;
+            int16_t rhs_limb;
+
+            memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
+            memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
+            if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb);
+            do_signed = false;
+        } else {
+            uint16_t lhs_limb;
+            uint16_t rhs_limb;
+
+            memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
+            memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
+            if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb);
+        }
+
+        remaining_bytes -= 16 / CHAR_BIT;
+
+#if zig_big_endian
+        byte_offset -= 16 / CHAR_BIT;
+#endif
+    }
+
+    while (remaining_bytes >= 8 / CHAR_BIT) {
+#if zig_little_endian
+        byte_offset -= 8 / CHAR_BIT;
+#endif
+
+        if (do_signed) {
+            int8_t lhs_limb;
+            int8_t rhs_limb;
+
+            memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
+            memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
+            if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb);
+            do_signed = false;
+        } else {
+            uint8_t lhs_limb;
+            uint8_t rhs_limb;
+
+            memcpy(&lhs_limb, &lhs_bytes[byte_offset], sizeof(lhs_limb));
+            memcpy(&rhs_limb, &rhs_bytes[byte_offset], sizeof(rhs_limb));
+            if (lhs_limb != rhs_limb) return (lhs_limb > rhs_limb) - (lhs_limb < rhs_limb);
+        }
+
+        remaining_bytes -= 8 / CHAR_BIT;
+
+#if zig_big_endian
+        byte_offset -= 8 / CHAR_BIT;
+#endif
+    }
+
+    return 0;
+}
+
 /* ========================= Floating Point Support ========================= */
 
 #if _MSC_VER
@@ -1933,7 +2112,6 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, uint8_t bits) {
 #define zig_make_special_f64(sign, name, arg, repr) sign zig_make_f64(__builtin_##name, )(arg)
 #define zig_make_special_f80(sign, name, arg, repr) sign zig_make_f80(__builtin_##name, )(arg)
 #define zig_make_special_f128(sign, name, arg, repr) sign zig_make_f128(__builtin_##name, )(arg)
-#define zig_make_special_c_longdouble(sign, name, arg, repr) sign zig_make_c_longdouble(__builtin_##name, )(arg)
 #else
 #define zig_has_float_builtins 0
 #define zig_make_special_f16(sign, name, arg, repr) zig_float_from_repr_f16(repr)
@@ -1941,13 +2119,13 @@ static inline zig_i128 zig_bit_reverse_i128(zig_i128 val, uint8_t bits) {
 #define zig_make_special_f64(sign, name, arg, repr) zig_float_from_repr_f64(repr)
 #define zig_make_special_f80(sign, name, arg, repr) zig_float_from_repr_f80(repr)
 #define zig_make_special_f128(sign, name, arg, repr)  zig_float_from_repr_f128(repr)
-#define zig_make_special_c_longdouble(sign, name, arg, repr) zig_float_from_repr_c_longdouble(repr)
 #endif
 
 #define zig_has_f16 1
 #define zig_bitSizeOf_f16 16
+typedef int16_t zig_repr_f16;
 #define zig_libc_name_f16(name) __##name##h
-#define zig_make_special_constant_f16(sign, name, arg, repr) zig_make_special_f16(sign, name, arg, repr)
+#define zig_init_special_f16(sign, name, arg, repr) zig_make_special_f16(sign, name, arg, repr)
 #if FLT_MANT_DIG == 11
 typedef float zig_f16;
 #define zig_make_f16(fp, repr) fp##f
@@ -1956,7 +2134,9 @@ typedef double zig_f16;
 #define zig_make_f16(fp, repr) fp
 #elif LDBL_MANT_DIG == 11
 #define zig_bitSizeOf_c_longdouble 16
-typedef uint16_t zig_repr_c_longdouble;
+#ifndef ZIG_TARGET_ABI_MSVC
+typedef zig_repr_f16 zig_repr_c_longdouble;
+#endif
 typedef long double zig_f16;
 #define zig_make_f16(fp, repr) fp##l
 #elif FLT16_MANT_DIG == 11 && (zig_has_builtin(inff16) || defined(zig_gnuc))
@@ -1973,17 +2153,18 @@ typedef int16_t zig_f16;
 #define zig_make_f16(fp, repr) repr
 #undef zig_make_special_f16
 #define zig_make_special_f16(sign, name, arg, repr) repr
-#undef zig_make_special_constant_f16
-#define zig_make_special_constant_f16(sign, name, arg, repr) repr
+#undef zig_init_special_f16
+#define zig_init_special_f16(sign, name, arg, repr) repr
 #endif
 
 #define zig_has_f32 1
 #define zig_bitSizeOf_f32 32
+typedef int32_t zig_repr_f32;
 #define zig_libc_name_f32(name) name##f
 #if _MSC_VER
-#define zig_make_special_constant_f32(sign, name, arg, repr) sign zig_make_f32(zig_msvc_flt_##name, )
+#define zig_init_special_f32(sign, name, arg, repr) sign zig_make_f32(zig_msvc_flt_##name, )
 #else
-#define zig_make_special_constant_f32(sign, name, arg, repr) zig_make_special_f32(sign, name, arg, repr)
+#define zig_init_special_f32(sign, name, arg, repr) zig_make_special_f32(sign, name, arg, repr)
 #endif
 #if FLT_MANT_DIG == 24
 typedef float zig_f32;
@@ -1993,7 +2174,9 @@ typedef double zig_f32;
 #define zig_make_f32(fp, repr) fp
 #elif LDBL_MANT_DIG == 24
 #define zig_bitSizeOf_c_longdouble 32
-typedef uint32_t zig_repr_c_longdouble;
+#ifndef ZIG_TARGET_ABI_MSVC
+typedef zig_repr_f32 zig_repr_c_longdouble;
+#endif
 typedef long double zig_f32;
 #define zig_make_f32(fp, repr) fp##l
 #elif FLT32_MANT_DIG == 24
@@ -2007,21 +2190,24 @@ typedef int32_t zig_f32;
 #define zig_make_f32(fp, repr) repr
 #undef zig_make_special_f32
 #define zig_make_special_f32(sign, name, arg, repr) repr
-#undef zig_make_special_constant_f32
-#define zig_make_special_constant_f32(sign, name, arg, repr) repr
+#undef zig_init_special_f32
+#define zig_init_special_f32(sign, name, arg, repr) repr
 #endif
 
 #define zig_has_f64 1
 #define zig_bitSizeOf_f64 64
+typedef int64_t zig_repr_f64;
 #define zig_libc_name_f64(name) name
 #if _MSC_VER
 #ifdef ZIG_TARGET_ABI_MSVC
 #define zig_bitSizeOf_c_longdouble 64
-typedef uint64_t zig_repr_c_longdouble;
+#ifndef ZIG_TARGET_ABI_MSVC
+typedef zig_repr_f64 zig_repr_c_longdouble;
+#endif
 #endif
-#define zig_make_special_constant_f64(sign, name, arg, repr) sign zig_make_f64(zig_msvc_flt_##name, )
+#define zig_init_special_f64(sign, name, arg, repr) sign zig_make_f64(zig_msvc_flt_##name, )
 #else /* _MSC_VER */
-#define zig_make_special_constant_f64(sign, name, arg, repr) zig_make_special_f64(sign, name, arg, repr)
+#define zig_init_special_f64(sign, name, arg, repr) zig_make_special_f64(sign, name, arg, repr)
 #endif /* _MSC_VER */
 #if FLT_MANT_DIG == 53
 typedef float zig_f64;
@@ -2031,7 +2217,9 @@ typedef double zig_f64;
 #define zig_make_f64(fp, repr) fp
 #elif LDBL_MANT_DIG == 53
 #define zig_bitSizeOf_c_longdouble 64
-typedef uint64_t zig_repr_c_longdouble;
+#ifndef ZIG_TARGET_ABI_MSVC
+typedef zig_repr_f64 zig_repr_c_longdouble;
+#endif
 typedef long double zig_f64;
 #define zig_make_f64(fp, repr) fp##l
 #elif FLT64_MANT_DIG == 53
@@ -2048,14 +2236,15 @@ typedef int64_t zig_f64;
 #define zig_make_f64(fp, repr) repr
 #undef zig_make_special_f64
 #define zig_make_special_f64(sign, name, arg, repr) repr
-#undef zig_make_special_constant_f64
-#define zig_make_special_constant_f64(sign, name, arg, repr) repr
+#undef zig_init_special_f64
+#define zig_init_special_f64(sign, name, arg, repr) repr
 #endif
 
 #define zig_has_f80 1
 #define zig_bitSizeOf_f80 80
+typedef zig_i128 zig_repr_f80;
 #define zig_libc_name_f80(name) __##name##x
-#define zig_make_special_constant_f80(sign, name, arg, repr) zig_make_special_f80(sign, name, arg, repr)
+#define zig_init_special_f80(sign, name, arg, repr) zig_make_special_f80(sign, name, arg, repr)
 #if FLT_MANT_DIG == 64
 typedef float zig_f80;
 #define zig_make_f80(fp, repr) fp##f
@@ -2064,7 +2253,9 @@ typedef double zig_f80;
 #define zig_make_f80(fp, repr) fp
 #elif LDBL_MANT_DIG == 64
 #define zig_bitSizeOf_c_longdouble 80
-typedef zig_u128 zig_repr_c_longdouble;
+#ifndef ZIG_TARGET_ABI_MSVC
+typedef zig_repr_f80 zig_repr_c_longdouble;
+#endif
 typedef long double zig_f80;
 #define zig_make_f80(fp, repr) fp##l
 #elif FLT80_MANT_DIG == 64
@@ -2084,14 +2275,15 @@ typedef zig_i128 zig_f80;
 #define zig_make_f80(fp, repr) repr
 #undef zig_make_special_f80
 #define zig_make_special_f80(sign, name, arg, repr) repr
-#undef zig_make_special_constant_f80
-#define zig_make_special_constant_f80(sign, name, arg, repr) repr
+#undef zig_init_special_f80
+#define zig_init_special_f80(sign, name, arg, repr) repr
 #endif
 
 #define zig_has_f128 1
 #define zig_bitSizeOf_f128 128
+typedef zig_i128 zig_repr_f128;
 #define zig_libc_name_f128(name) name##q
-#define zig_make_special_constant_f128(sign, name, arg, repr) zig_make_special_f128(sign, name, arg, repr)
+#define zig_init_special_f128(sign, name, arg, repr) zig_make_special_f128(sign, name, arg, repr)
 #if FLT_MANT_DIG == 113
 typedef float zig_f128;
 #define zig_make_f128(fp, repr) fp##f
@@ -2100,7 +2292,9 @@ typedef double zig_f128;
 #define zig_make_f128(fp, repr) fp
 #elif LDBL_MANT_DIG == 113
 #define zig_bitSizeOf_c_longdouble 128
-typedef zig_u128 zig_repr_c_longdouble;
+#ifndef ZIG_TARGET_ABI_MSVC
+typedef zig_repr_f128 zig_repr_c_longdouble;
+#endif
 typedef long double zig_f128;
 #define zig_make_f128(fp, repr) fp##l
 #elif FLT128_MANT_DIG == 113
@@ -2122,63 +2316,44 @@ typedef zig_i128 zig_f128;
 #define zig_make_f128(fp, repr) repr
 #undef zig_make_special_f128
 #define zig_make_special_f128(sign, name, arg, repr) repr
-#undef zig_make_special_constant_f128
-#define zig_make_special_constant_f128(sign, name, arg, repr) repr
+#undef zig_init_special_f128
+#define zig_init_special_f128(sign, name, arg, repr) repr
 #endif
 
-#define zig_has_c_longdouble 1
-
-#ifdef ZIG_TARGET_ABI_MSVC
-#define zig_libc_name_c_longdouble(name) name
-#else
-#define zig_libc_name_c_longdouble(name) name##l
-#endif
-
-#define zig_make_special_constant_c_longdouble(sign, name, arg, repr) zig_make_special_c_longdouble(sign, name, arg, repr)
 #ifdef zig_bitSizeOf_c_longdouble
 
+#define zig_has_c_longdouble 1
 #ifdef ZIG_TARGET_ABI_MSVC
 #undef zig_bitSizeOf_c_longdouble
 #define zig_bitSizeOf_c_longdouble 64
-typedef uint64_t zig_repr_c_longdouble;
 typedef zig_f64 zig_c_longdouble;
-#define zig_make_c_longdouble(fp, repr) fp
+typedef zig_repr_f64 zig_repr_c_longdouble;
 #else
 typedef long double zig_c_longdouble;
-#define zig_make_c_longdouble(fp, repr) fp##l
 #endif
 
 #else /* zig_bitSizeOf_c_longdouble */
 
-#undef zig_has_c_longdouble
 #define zig_has_c_longdouble 0
-#define zig_bitSizeOf_c_longdouble 80
-typedef zig_u128 zig_repr_c_longdouble;
-#define zig_compiler_rt_abbrev_c_longdouble zig_compiler_rt_abbrev_f80
 #define zig_bitSizeOf_repr_c_longdouble 128
-typedef zig_i128 zig_c_longdouble;
-#define zig_make_c_longdouble(fp, repr) repr
-#undef zig_make_special_c_longdouble
-#define zig_make_special_c_longdouble(sign, name, arg, repr) repr
-#undef zig_make_special_constant_c_longdouble
-#define zig_make_special_constant_c_longdouble(sign, name, arg, repr) repr
+typedef zig_f128 zig_c_longdouble;
+typedef zig_repr_f128 zig_repr_c_longdouble;
 
 #endif /* zig_bitSizeOf_c_longdouble */
 
 #if !zig_has_float_builtins
-#define zig_float_from_repr(Type, ReprType) \
-    static inline zig_##Type zig_float_from_repr_##Type(ReprType repr) { \
+#define zig_float_from_repr(Type) \
+    static inline zig_##Type zig_float_from_repr_##Type(zig_repr_##Type repr) { \
         zig_##Type result; \
         memcpy(&result, &repr, sizeof(result)); \
         return result; \
     }
 
-zig_float_from_repr(f16, uint16_t)
-zig_float_from_repr(f32, uint32_t)
-zig_float_from_repr(f64, uint64_t)
-zig_float_from_repr(f80, zig_u128)
-zig_float_from_repr(f128, zig_u128)
-zig_float_from_repr(c_longdouble, zig_repr_c_longdouble)
+zig_float_from_repr(f16)
+zig_float_from_repr(f32)
+zig_float_from_repr(f64)
+zig_float_from_repr(f80)
+zig_float_from_repr(f128)
 #endif
 
 #define zig_cast_f16 (zig_f16)
@@ -2187,11 +2362,9 @@ zig_float_from_repr(c_longdouble, zig_repr_c_longdouble)
 
 #if _MSC_VER && !zig_has_f128
 #define zig_cast_f80
-#define zig_cast_c_longdouble
 #define zig_cast_f128
 #else
 #define zig_cast_f80 (zig_f80)
-#define zig_cast_c_longdouble (zig_c_longdouble)
 #define zig_cast_f128 (zig_f128)
 #endif
 
@@ -2320,7 +2493,6 @@ zig_float_builtins(f32)
 zig_float_builtins(f64)
 zig_float_builtins(f80)
 zig_float_builtins(f128)
-zig_float_builtins(c_longdouble)
 
 #if _MSC_VER && (_M_IX86 || _M_X64)
 
@@ -2563,6 +2735,29 @@ zig_msvc_atomics_128op(u128, max)
 
 #endif /* _MSC_VER && (_M_IX86 || _M_X64) */
 
+/* ============================= Vector Support ============================= */
+
+#define zig_cmp_vec(operation, operator) \
+    static inline void zig_##operation##_vec(bool *result, const void *lhs, const void *rhs, uint32_t len, bool is_signed, uint16_t elem_bits) { \
+        uint32_t index = 0; \
+        const uint8_t *lhs_ptr = lhs; \
+        const uint8_t *rhs_ptr = rhs; \
+        uint16_t elem_bytes = zig_big_bytes(elem_bits); \
+ \
+        while (index < len) { \
+            result[index] = zig_cmp_big(lhs_ptr, rhs_ptr, is_signed, elem_bits) operator 0; \
+            lhs_ptr += elem_bytes; \
+            rhs_ptr += elem_bytes; \
+            index += 1; \
+        } \
+    }
+zig_cmp_vec(eq, ==)
+zig_cmp_vec(ne, !=)
+zig_cmp_vec(lt, < )
+zig_cmp_vec(le, <=)
+zig_cmp_vec(gt, > )
+zig_cmp_vec(ge, >=)
+
 /* ======================== Special Case Intrinsics ========================= */
 
 #if (_MSC_VER && _M_X64) || defined(__x86_64__)
src/codegen/c/type.zig
@@ -496,6 +496,116 @@ pub const CType = extern union {
         }
     };
 
+    pub fn isBool(self: CType) bool {
+        return switch (self.tag()) {
+            ._Bool,
+            .bool,
+            => true,
+            else => false,
+        };
+    }
+
+    pub fn isInteger(self: CType) bool {
+        return switch (self.tag()) {
+            .char,
+            .@"signed char",
+            .short,
+            .int,
+            .long,
+            .@"long long",
+            .@"unsigned char",
+            .@"unsigned short",
+            .@"unsigned int",
+            .@"unsigned long",
+            .@"unsigned long long",
+            .size_t,
+            .ptrdiff_t,
+            .uint8_t,
+            .int8_t,
+            .uint16_t,
+            .int16_t,
+            .uint32_t,
+            .int32_t,
+            .uint64_t,
+            .int64_t,
+            .uintptr_t,
+            .intptr_t,
+            .zig_u128,
+            .zig_i128,
+            => true,
+            else => false,
+        };
+    }
+
+    pub fn signedness(self: CType) ?std.builtin.Signedness {
+        return switch (self.tag()) {
+            .char => null, // unknown signedness
+            .@"signed char",
+            .short,
+            .int,
+            .long,
+            .@"long long",
+            .ptrdiff_t,
+            .int8_t,
+            .int16_t,
+            .int32_t,
+            .int64_t,
+            .intptr_t,
+            .zig_i128,
+            => .signed,
+            .@"unsigned char",
+            .@"unsigned short",
+            .@"unsigned int",
+            .@"unsigned long",
+            .@"unsigned long long",
+            .size_t,
+            .uint8_t,
+            .uint16_t,
+            .uint32_t,
+            .uint64_t,
+            .uintptr_t,
+            .zig_u128,
+            => .unsigned,
+            else => unreachable,
+        };
+    }
+
+    pub fn isFloat(self: CType) bool {
+        return switch (self.tag()) {
+            .float,
+            .double,
+            .@"long double",
+            .zig_f16,
+            .zig_f32,
+            .zig_f64,
+            .zig_f80,
+            .zig_f128,
+            .zig_c_longdouble,
+            => true,
+            else => false,
+        };
+    }
+
+    pub fn isPointer(self: CType) bool {
+        return switch (self.tag()) {
+            .pointer,
+            .pointer_const,
+            .pointer_volatile,
+            .pointer_const_volatile,
+            => true,
+            else => false,
+        };
+    }
+
+    pub fn isFunction(self: CType) bool {
+        return switch (self.tag()) {
+            .function,
+            .varargs_function,
+            => true,
+            else => false,
+        };
+    }
+
     pub fn toSigned(self: CType) CType {
         return CType.initTag(switch (self.tag()) {
             .char, .@"signed char", .@"unsigned char" => .@"signed char",
@@ -725,6 +835,20 @@ pub const CType = extern union {
         }
     }
 
+    pub fn floatActiveBits(self: CType, target: Target) u16 {
+        return switch (self.tag()) {
+            .float => target.c_type_bit_size(.float),
+            .double => target.c_type_bit_size(.double),
+            .@"long double", .zig_c_longdouble => target.c_type_bit_size(.longdouble),
+            .zig_f16 => 16,
+            .zig_f32 => 32,
+            .zig_f64 => 64,
+            .zig_f80 => 80,
+            .zig_f128 => 128,
+            else => unreachable,
+        };
+    }
+
     pub fn byteSize(self: CType, store: Store.Set, target: Target) u64 {
         return switch (self.tag()) {
             .void => 0,
src/codegen/c.zig
@@ -112,11 +112,7 @@ const ValueRenderLocation = enum {
     }
 };
 
-const BuiltinInfo = enum {
-    None,
-    Range,
-    Bits,
-};
+const BuiltinInfo = enum { none, bits };
 
 const reserved_idents = std.ComptimeStringMap(void, .{
     // C language
@@ -440,6 +436,10 @@ pub const Function = struct {
         return f.object.dg.typeToCType(ty, kind);
     }
 
+    fn byteSize(f: *Function, cty: CType) u64 {
+        return f.object.dg.byteSize(cty);
+    }
+
     fn renderType(f: *Function, w: anytype, t: Type) !void {
         return f.object.dg.renderType(w, t);
     }
@@ -1003,8 +1003,9 @@ pub const DeclGen = struct {
                         //     return dg.fail("Only quiet nans are supported in global variable initializers", .{});
                     }
 
-                    try writer.writeAll("zig_make_special_");
-                    if (location == .StaticInitializer) try writer.writeAll("constant_");
+                    try writer.writeAll("zig_");
+                    try writer.writeAll(if (location == .StaticInitializer) "init" else "make");
+                    try writer.writeAll("_special_");
                     try dg.renderTypeForBuiltinFnName(writer, ty);
                     try writer.writeByte('(');
                     if (std.math.signbit(f128_val)) try writer.writeByte('-');
@@ -1565,6 +1566,10 @@ pub const DeclGen = struct {
         return dg.ctypes.typeToCType(dg.gpa, ty, dg.module, kind);
     }
 
+    fn byteSize(dg: *DeclGen, cty: CType) u64 {
+        return cty.byteSize(dg.ctypes.set, dg.module.getTarget());
+    }
+
     /// Renders a type as a single identifier, generating intermediate typedefs
     /// if necessary.
     ///
@@ -1861,51 +1866,64 @@ pub const DeclGen = struct {
     }
 
     fn renderTypeForBuiltinFnName(dg: *DeclGen, writer: anytype, ty: Type) !void {
-        const target = dg.module.getTarget();
-        if (ty.isAbiInt()) {
-            const int_info = ty.intInfo(target);
-            const c_bits = toCIntBits(int_info.bits) orelse
-                return dg.fail("TODO: C backend: implement integer types larger than 128 bits", .{});
-            try writer.print("{c}{d}", .{ signAbbrev(int_info.signedness), c_bits });
-        } else if (ty.isRuntimeFloat()) {
-            try ty.print(writer, dg.module);
-        } else if (ty.isPtrAtRuntime()) {
-            try writer.print("p{d}", .{ty.bitSize(target)});
-        } else if (ty.zigTypeTag() == .Bool) {
-            try writer.print("u8", .{});
-        } else return dg.fail("TODO: CBE: implement renderTypeForBuiltinFnName for type {}", .{
-            ty.fmt(dg.module),
-        });
+        try dg.renderCTypeForBuiltinFnName(writer, try dg.typeToCType(ty, .complete));
+    }
+
+    fn renderCTypeForBuiltinFnName(dg: *DeclGen, writer: anytype, cty: CType) !void {
+        switch (cty.tag()) {
+            else => try writer.print("{c}{d}", .{
+                if (cty.isBool())
+                    signAbbrev(.unsigned)
+                else if (cty.isInteger())
+                    signAbbrev(cty.signedness() orelse .unsigned)
+                else if (cty.isFloat())
+                    @as(u8, 'f')
+                else if (cty.isPointer())
+                    @as(u8, 'p')
+                else
+                    return dg.fail("TODO: CBE: implement renderTypeForBuiltinFnName for type {}", .{
+                        cty.tag(),
+                    }),
+                if (cty.isFloat()) cty.floatActiveBits(dg.module.getTarget()) else dg.byteSize(cty) * 8,
+            }),
+            .array => try writer.writeAll("big"),
+            .vector => try writer.writeAll("vec"),
+        }
     }
 
     fn renderBuiltinInfo(dg: *DeclGen, writer: anytype, ty: Type, info: BuiltinInfo) !void {
-        const target = dg.module.getTarget();
         switch (info) {
-            .None => {},
-            .Range => {
-                var arena = std.heap.ArenaAllocator.init(dg.gpa);
-                defer arena.deinit();
-
-                const ExpectedContents = union { u: Value.Payload.U64, i: Value.Payload.I64 };
-                var stack align(@alignOf(ExpectedContents)) =
-                    std.heap.stackFallback(@sizeOf(ExpectedContents), arena.allocator());
-
-                const int_info = ty.intInfo(target);
-                if (int_info.signedness == .signed) {
-                    const min_val = try ty.minInt(stack.get(), target);
-                    try writer.print(", {x}", .{try dg.fmtIntLiteral(ty, min_val, .Other)});
+            .none => {},
+            .bits => {
+                const cty = try dg.typeToCType(ty, .complete);
+                if (cty.castTag(.vector)) |pl| {
+                    var len_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = pl.data.len };
+                    try writer.print(", {}", .{try dg.fmtIntLiteral(
+                        Type.u32,
+                        Value.initPayload(&len_pl.base),
+                        .FunctionArgument,
+                    )});
                 }
 
-                const max_val = try ty.maxInt(stack.get(), target);
-                try writer.print(", {x}", .{try dg.fmtIntLiteral(ty, max_val, .Other)});
-            },
-            .Bits => {
-                var bits_pl = Value.Payload.U64{
-                    .base = .{ .tag = .int_u64 },
-                    .data = ty.bitSize(target),
-                };
-                const bits_val = Value.initPayload(&bits_pl.base);
-                try writer.print(", {}", .{try dg.fmtIntLiteral(Type.u8, bits_val, .Other)});
+                const target = dg.module.getTarget();
+                const elem_ty = ty.shallowElemType();
+                const elem_info = if (elem_ty.isAbiInt())
+                    elem_ty.intInfo(target)
+                else
+                    std.builtin.Type.Int{
+                        .signedness = .unsigned,
+                        .bits = @intCast(u16, elem_ty.bitSize(target)),
+                    };
+                switch (cty.tag()) {
+                    else => {},
+                    .array, .vector => try writer.print(", {}", .{elem_info.signedness == .signed}),
+                }
+
+                var bits_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = elem_info.bits };
+                try writer.print(", {}", .{try dg.fmtIntLiteral(switch (cty.tag()) {
+                    else => Type.u8,
+                    .array, .vector => Type.u16,
+                }, Value.initPayload(&bits_pl.base), .FunctionArgument)});
             },
         }
     }
@@ -2758,35 +2776,35 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
 
             // TODO use a different strategy for add, sub, mul, div
             // that communicates to the optimizer that wrapping is UB.
-            .add => try airBinOp(f, inst, "+", "add", .None),
-            .sub => try airBinOp(f, inst, "-", "sub", .None),
-            .mul => try airBinOp(f, inst, "*", "mul", .None),
+            .add => try airBinOp(f, inst, "+", "add", .none),
+            .sub => try airBinOp(f, inst, "-", "sub", .none),
+            .mul => try airBinOp(f, inst, "*", "mul", .none),
 
             .neg => try airFloatNeg(f, inst),
-            .div_float => try airBinBuiltinCall(f, inst, "div", .None),
+            .div_float => try airBinBuiltinCall(f, inst, "div", .none),
 
-            .div_trunc, .div_exact => try airBinOp(f, inst, "/", "div_trunc", .None),
+            .div_trunc, .div_exact => try airBinOp(f, inst, "/", "div_trunc", .none),
             .rem => blk: {
                 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
                 const lhs_ty = f.air.typeOf(bin_op.lhs);
                 // For binary operations @TypeOf(lhs)==@TypeOf(rhs),
                 // so we only check one.
                 break :blk if (lhs_ty.isInt())
-                    try airBinOp(f, inst, "%", "rem", .None)
+                    try airBinOp(f, inst, "%", "rem", .none)
                 else
                     try airBinFloatOp(f, inst, "fmod");
             },
-            .div_floor => try airBinBuiltinCall(f, inst, "div_floor", .None),
-            .mod       => try airBinBuiltinCall(f, inst, "mod", .None),
+            .div_floor => try airBinBuiltinCall(f, inst, "div_floor", .none),
+            .mod       => try airBinBuiltinCall(f, inst, "mod", .none),
 
-            .addwrap => try airBinBuiltinCall(f, inst, "addw", .Bits),
-            .subwrap => try airBinBuiltinCall(f, inst, "subw", .Bits),
-            .mulwrap => try airBinBuiltinCall(f, inst, "mulw", .Bits),
+            .addwrap => try airBinBuiltinCall(f, inst, "addw", .bits),
+            .subwrap => try airBinBuiltinCall(f, inst, "subw", .bits),
+            .mulwrap => try airBinBuiltinCall(f, inst, "mulw", .bits),
 
-            .add_sat => try airBinBuiltinCall(f, inst, "adds", .Bits),
-            .sub_sat => try airBinBuiltinCall(f, inst, "subs", .Bits),
-            .mul_sat => try airBinBuiltinCall(f, inst, "muls", .Bits),
-            .shl_sat => try airBinBuiltinCall(f, inst, "shls", .Bits),
+            .add_sat => try airBinBuiltinCall(f, inst, "adds", .bits),
+            .sub_sat => try airBinBuiltinCall(f, inst, "subs", .bits),
+            .mul_sat => try airBinBuiltinCall(f, inst, "muls", .bits),
+            .shl_sat => try airBinBuiltinCall(f, inst, "shls", .bits),
 
             .sqrt        => try airUnFloatOp(f, inst, "sqrt"),
             .sin         => try airUnFloatOp(f, inst, "sin"),
@@ -2805,34 +2823,38 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
 
             .mul_add => try airMulAdd(f, inst),
 
-            .add_with_overflow => try airOverflow(f, inst, "add", .Bits),
-            .sub_with_overflow => try airOverflow(f, inst, "sub", .Bits),
-            .mul_with_overflow => try airOverflow(f, inst, "mul", .Bits),
-            .shl_with_overflow => try airOverflow(f, inst, "shl", .Bits),
+            .add_with_overflow => try airOverflow(f, inst, "add", .bits),
+            .sub_with_overflow => try airOverflow(f, inst, "sub", .bits),
+            .mul_with_overflow => try airOverflow(f, inst, "mul", .bits),
+            .shl_with_overflow => try airOverflow(f, inst, "shl", .bits),
 
             .min => try airMinMax(f, inst, '<', "fmin"),
             .max => try airMinMax(f, inst, '>', "fmax"),
 
             .slice => try airSlice(f, inst),
 
-            .cmp_gt  => try airCmpOp(f, inst, ">",  "gt"),
-            .cmp_gte => try airCmpOp(f, inst, ">=", "ge"),
-            .cmp_lt  => try airCmpOp(f, inst, "<",  "lt"),
-            .cmp_lte => try airCmpOp(f, inst, "<=", "le"),
+            .cmp_gt  => try airCmpOp(f, inst, .gt),
+            .cmp_gte => try airCmpOp(f, inst, .gte),
+            .cmp_lt  => try airCmpOp(f, inst, .lt),
+            .cmp_lte => try airCmpOp(f, inst, .lte),
 
-            .cmp_eq  => try airEquality(f, inst,  "((", "==", "eq"),
-            .cmp_neq => try airEquality(f, inst, "!((", "!=", "ne"),
+            .cmp_eq  => try airEquality(f, inst, .eq),
+            .cmp_neq => try airEquality(f, inst, .neq),
 
-            .cmp_vector => return f.fail("TODO: C backend: implement cmp_vector", .{}),
+            .cmp_vector => blk: {
+                const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
+                const extra = f.air.extraData(Air.VectorCmp, ty_pl.payload).data;
+                break :blk try cmpBuiltinCall(f, inst, extra, extra.compareOperator(), .operator, .bits);
+            },
             .cmp_lt_errors_len => try airCmpLtErrorsLen(f, inst),
 
             // bool_and and bool_or are non-short-circuit operations
-            .bool_and, .bit_and => try airBinOp(f, inst, "&",  "and", .None),
-            .bool_or,  .bit_or  => try airBinOp(f, inst, "|",  "or",  .None),
-            .xor                => try airBinOp(f, inst, "^",  "xor", .None),
-            .shr, .shr_exact    => try airBinBuiltinCall(f, inst, "shr", .None),
-            .shl,               => try airBinBuiltinCall(f, inst, "shlw", .Bits),
-            .shl_exact          => try airBinOp(f, inst, "<<", "shl", .None),
+            .bool_and, .bit_and => try airBinOp(f, inst, "&",  "and", .none),
+            .bool_or,  .bit_or  => try airBinOp(f, inst, "|",  "or",  .none),
+            .xor                => try airBinOp(f, inst, "^",  "xor", .none),
+            .shr, .shr_exact    => try airBinBuiltinCall(f, inst, "shr", .none),
+            .shl,               => try airBinBuiltinCall(f, inst, "shlw", .bits),
+            .shl_exact          => try airBinOp(f, inst, "<<", "shl", .none),
             .not                => try airNot  (f, inst),
 
             .optional_payload         => try airOptionalPayload(f, inst),
@@ -2877,11 +2899,11 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
             .memcpy           => try airMemcpy(f, inst),
             .set_union_tag    => try airSetUnionTag(f, inst),
             .get_union_tag    => try airGetUnionTag(f, inst),
-            .clz              => try airUnBuiltinCall(f, inst, "clz", .Bits),
-            .ctz              => try airUnBuiltinCall(f, inst, "ctz", .Bits),
-            .popcount         => try airUnBuiltinCall(f, inst, "popcount", .Bits),
-            .byte_swap        => try airUnBuiltinCall(f, inst, "byte_swap", .Bits),
-            .bit_reverse      => try airUnBuiltinCall(f, inst, "bit_reverse", .Bits),
+            .clz              => try airUnBuiltinCall(f, inst, "clz", .bits),
+            .ctz              => try airUnBuiltinCall(f, inst, "ctz", .bits),
+            .popcount         => try airUnBuiltinCall(f, inst, "popcount", .bits),
+            .byte_swap        => try airUnBuiltinCall(f, inst, "byte_swap", .bits),
+            .bit_reverse      => try airUnBuiltinCall(f, inst, "bit_reverse", .bits),
             .tag_name         => try airTagName(f, inst),
             .error_name       => try airErrorName(f, inst),
             .splat            => try airSplat(f, inst),
@@ -3349,7 +3371,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
         try f.writeCValueDeref(writer, operand);
         try writer.print(", {})", .{try f.fmtIntLiteral(bit_offset_ty, bit_offset_val)});
         if (cant_cast) try writer.writeByte(')');
-        try f.object.dg.renderBuiltinInfo(writer, field_ty, .Bits);
+        try f.object.dg.renderBuiltinInfo(writer, field_ty, .bits);
         try writer.writeByte(')');
     } else {
         try f.writeCValue(writer, local, .Other);
@@ -3744,7 +3766,7 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info:
 fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {
     const inst_ty = f.air.typeOfIndex(inst);
     if (inst_ty.tag() != .bool)
-        return try airUnBuiltinCall(f, inst, "not", .Bits);
+        return try airUnBuiltinCall(f, inst, "not", .bits);
 
     const ty_op = f.air.instructions.items(.data)[inst].ty_op;
 
@@ -3803,7 +3825,7 @@ fn airBinOp(
     return local;
 }
 
-fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: []const u8, operation: []const u8) !CValue {
+fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: std.math.CompareOperator) !CValue {
     const bin_op = f.air.instructions.items(.data)[inst].bin_op;
 
     if (f.liveness.isUnused(inst)) {
@@ -3813,10 +3835,11 @@ fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: []const u8, operation:
 
     const operand_ty = f.air.typeOf(bin_op.lhs);
     const target = f.object.dg.module.getTarget();
-    if (operand_ty.isInt() and operand_ty.bitSize(target) > 64)
-        return try cmpBuiltinCall(f, inst, operator, "cmp");
+    const operand_bits = operand_ty.bitSize(target);
+    if (operand_ty.isInt() and operand_bits > 64)
+        return cmpBuiltinCall(f, inst, bin_op, operator, .cmp, if (operand_bits > 128) .bits else .none);
     if (operand_ty.isRuntimeFloat())
-        return try cmpBuiltinCall(f, inst, operator, operation);
+        return cmpBuiltinCall(f, inst, bin_op, operator, .operator, .none);
 
     const inst_ty = f.air.typeOfIndex(inst);
     const lhs = try f.resolveInst(bin_op.lhs);
@@ -3829,7 +3852,7 @@ fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: []const u8, operation:
     try writer.writeAll(" = ");
     try f.writeCValue(writer, lhs, .Other);
     try writer.writeByte(' ');
-    try writer.writeAll(operator);
+    try writer.writeAll(compareOperatorC(operator));
     try writer.writeByte(' ');
     try f.writeCValue(writer, rhs, .Other);
     try writer.writeAll(";\n");
@@ -3840,9 +3863,7 @@ fn airCmpOp(f: *Function, inst: Air.Inst.Index, operator: []const u8, operation:
 fn airEquality(
     f: *Function,
     inst: Air.Inst.Index,
-    negate_prefix: []const u8,
-    operator: []const u8,
-    operation: []const u8,
+    operator: std.math.CompareOperator,
 ) !CValue {
     const bin_op = f.air.instructions.items(.data)[inst].bin_op;
 
@@ -3853,10 +3874,11 @@ fn airEquality(
 
     const operand_ty = f.air.typeOf(bin_op.lhs);
     const target = f.object.dg.module.getTarget();
-    if (operand_ty.isInt() and operand_ty.bitSize(target) > 64)
-        return try cmpBuiltinCall(f, inst, operator, "cmp");
+    const operand_bits = operand_ty.bitSize(target);
+    if (operand_ty.isInt() and operand_bits > 64)
+        return cmpBuiltinCall(f, inst, bin_op, operator, .cmp, if (operand_bits > 128) .bits else .none);
     if (operand_ty.isRuntimeFloat())
-        return try cmpBuiltinCall(f, inst, operator, operation);
+        return cmpBuiltinCall(f, inst, bin_op, operator, .operator, .none);
 
     const lhs = try f.resolveInst(bin_op.lhs);
     const rhs = try f.resolveInst(bin_op.rhs);
@@ -3872,7 +3894,12 @@ fn airEquality(
         // (A && B)  || (C && (A == B))
         // A = lhs.is_null  ;  B = rhs.is_null  ;  C = rhs.payload == lhs.payload
 
-        try writer.writeAll(negate_prefix);
+        switch (operator) {
+            .eq => {},
+            .neq => try writer.writeByte('!'),
+            else => unreachable,
+        }
+        try writer.writeAll("((");
         try f.writeCValue(writer, lhs, .Other);
         try writer.writeAll(".is_null && ");
         try f.writeCValue(writer, rhs, .Other);
@@ -3891,7 +3918,7 @@ fn airEquality(
 
     try f.writeCValue(writer, lhs, .Other);
     try writer.writeByte(' ');
-    try writer.writeAll(operator);
+    try writer.writeAll(compareOperatorC(operator));
     try writer.writeByte(' ');
     try f.writeCValue(writer, rhs, .Other);
     try writer.writeAll(";\n");
@@ -3972,7 +3999,7 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons
     const inst_ty = f.air.typeOfIndex(inst);
     const target = f.object.dg.module.getTarget();
     if (inst_ty.isInt() and inst_ty.bitSize(target) > 64)
-        return try airBinBuiltinCall(f, inst, operation[1..], .None);
+        return try airBinBuiltinCall(f, inst, operation[1..], .none);
     if (inst_ty.isRuntimeFloat())
         return try airBinFloatOp(f, inst, operation);
 
@@ -4418,12 +4445,35 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
 
     // Ensure padding bits have the expected value.
     if (dest_ty.isAbiInt()) {
+        const dest_cty = try f.typeToCType(dest_ty, .complete);
+        const dest_info = dest_ty.intInfo(target);
+        var wrap_ty_pl = Type.Payload.Bits{ .base = .{ .tag = switch (dest_info.signedness) {
+            .unsigned => .int_unsigned,
+            .signed => .int_signed,
+        } }, .data = dest_info.bits };
+
         try f.writeCValue(writer, local, .Other);
+        if (dest_cty.castTag(.array)) |pl| {
+            try writer.print("[{d}]", .{switch (target.cpu.arch.endian()) {
+                .Little => pl.data.len - 1,
+                .Big => 0,
+            }});
+            wrap_ty_pl.data -= 1;
+            wrap_ty_pl.data %= @intCast(u16, f.byteSize(f.indexToCType(pl.data.elem_type)) * 8);
+            wrap_ty_pl.data += 1;
+        }
+        const wrap_ty = Type.initPayload(&wrap_ty_pl.base);
         try writer.writeAll(" = zig_wrap_");
-        try f.object.dg.renderTypeForBuiltinFnName(writer, dest_ty);
+        try f.object.dg.renderTypeForBuiltinFnName(writer, wrap_ty);
         try writer.writeByte('(');
         try f.writeCValue(writer, local, .Other);
-        try f.object.dg.renderBuiltinInfo(writer, dest_ty, .Bits);
+        if (dest_cty.castTag(.array)) |pl| {
+            try writer.print("[{d}]", .{switch (target.cpu.arch.endian()) {
+                .Little => pl.data.len - 1,
+                .Big => 0,
+            }});
+        }
+        try f.object.dg.renderBuiltinInfo(writer, wrap_ty, .bits);
         try writer.writeAll(");\n");
     }
 
@@ -5438,7 +5488,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
                 try f.object.dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument);
                 try writer.writeByte(')');
                 if (cant_cast) try writer.writeByte(')');
-                try f.object.dg.renderBuiltinInfo(writer, field_int_ty, .Bits);
+                try f.object.dg.renderBuiltinInfo(writer, field_int_ty, .bits);
                 try writer.writeAll(");\n");
                 if (inst_ty.eql(field_int_ty, f.object.dg.module)) return temp_local;
 
@@ -5871,7 +5921,7 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {
     try f.writeCValue(writer, operand, .FunctionArgument);
     try writer.writeByte(')');
     if (inst_ty.isInt() and operand_ty.isRuntimeFloat()) {
-        try f.object.dg.renderBuiltinInfo(writer, inst_ty, .Bits);
+        try f.object.dg.renderBuiltinInfo(writer, inst_ty, .bits);
         try writer.writeByte(')');
     }
     try writer.writeAll(";\n");
@@ -5972,29 +6022,46 @@ fn airBinBuiltinCall(
 fn cmpBuiltinCall(
     f: *Function,
     inst: Air.Inst.Index,
-    operator: []const u8,
-    operation: []const u8,
+    data: anytype,
+    operator: std.math.CompareOperator,
+    operation: enum { cmp, operator },
+    info: BuiltinInfo,
 ) !CValue {
     const inst_ty = f.air.typeOfIndex(inst);
-    const bin_op = f.air.instructions.items(.data)[inst].bin_op;
-    const operand_ty = f.air.typeOf(bin_op.lhs);
+    const operand_ty = f.air.typeOf(data.lhs);
 
-    const lhs = try f.resolveInst(bin_op.lhs);
-    const rhs = try f.resolveInst(bin_op.rhs);
-    try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
+    const lhs = try f.resolveInst(data.lhs);
+    const rhs = try f.resolveInst(data.rhs);
+    try reap(f, inst, &.{ data.lhs, data.rhs });
+
+    const ref_ret = inst_ty.tag() != .bool;
 
     const writer = f.object.writer();
     const local = try f.allocLocal(inst, inst_ty);
-    try f.writeCValue(writer, local, .Other);
-    try writer.writeAll(" = zig_");
-    try writer.writeAll(operation);
-    try writer.writeByte('_');
+    if (!ref_ret) {
+        try f.writeCValue(writer, local, .Other);
+        try writer.writeAll(" = ");
+    }
+    try writer.print("zig_{s}_", .{switch (operation) {
+        else => @tagName(operation),
+        .operator => compareOperatorAbbrev(operator),
+    }});
     try f.object.dg.renderTypeForBuiltinFnName(writer, operand_ty);
     try writer.writeByte('(');
+    if (ref_ret) {
+        try f.writeCValue(writer, local, .FunctionArgument);
+        try writer.writeAll(", ");
+    }
     try f.writeCValue(writer, lhs, .FunctionArgument);
     try writer.writeAll(", ");
     try f.writeCValue(writer, rhs, .FunctionArgument);
-    try writer.print(") {s} {};\n", .{ operator, try f.fmtIntLiteral(Type.initTag(.i32), Value.zero) });
+    try f.object.dg.renderBuiltinInfo(writer, operand_ty, info);
+    try writer.writeByte(')');
+    if (!ref_ret) try writer.print(" {s} {}", .{
+        compareOperatorC(operator),
+        try f.fmtIntLiteral(Type.initTag(.i32), Value.zero),
+    });
+    try writer.writeAll(";\n");
     return local;
 }
 
@@ -6675,7 +6742,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
 
                     try writer.writeAll(", ");
                     try f.object.dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument);
-                    try f.object.dg.renderBuiltinInfo(writer, inst_ty, .Bits);
+                    try f.object.dg.renderBuiltinInfo(writer, inst_ty, .bits);
                     try writer.writeByte(')');
                     if (!empty) try writer.writeByte(')');
 
@@ -7094,6 +7161,28 @@ fn compilerRtAbbrev(ty: Type, target: std.Target) []const u8 {
     } else unreachable;
 }
 
+fn compareOperatorAbbrev(operator: std.math.CompareOperator) []const u8 {
+    return switch (operator) {
+        .lt => "lt",
+        .lte => "le",
+        .eq => "eq",
+        .gte => "ge",
+        .gt => "gt",
+        .neq => "ne",
+    };
+}
+
+fn compareOperatorC(operator: std.math.CompareOperator) []const u8 {
+    return switch (operator) {
+        .lt => "<",
+        .lte => "<=",
+        .eq => "==",
+        .gte => ">=",
+        .gt => ">",
+        .neq => "!=",
+    };
+}
+
 fn StringLiteral(comptime WriterType: type) type {
     // MSVC has a length limit of 16380 per string literal (before concatenation)
     const max_char_len = 4;
@@ -7239,14 +7328,6 @@ fn formatIntLiteral(
         .positive = undefined,
     };
     defer allocator.free(wrap.limbs);
-    if (wrap.addWrap(int, one, data.int_info.signedness, c_bits) or
-        data.int_info.signedness == .signed and wrap.subWrap(int, one, data.int_info.signedness, c_bits))
-        return writer.print("{s}_{s}", .{
-            data.cty.getStandardDefineAbbrev() orelse return writer.print("zig_{s}Int_{c}{d}", .{
-                if (int.positive) "max" else "min", signAbbrev(data.int_info.signedness), c_bits,
-            }),
-            if (int.positive) "MAX" else "MIN",
-        });
 
     const c_limb_info: struct {
         cty: CType,
@@ -7277,6 +7358,15 @@ fn formatIntLiteral(
         },
     };
     if (c_limb_info.count == 1) {
+        if (wrap.addWrap(int, one, data.int_info.signedness, c_bits) or
+            data.int_info.signedness == .signed and wrap.subWrap(int, one, data.int_info.signedness, c_bits))
+            return writer.print("{s}_{s}", .{
+                data.cty.getStandardDefineAbbrev() orelse return writer.print("zig_{s}Int_{c}{d}", .{
+                    if (int.positive) "max" else "min", signAbbrev(data.int_info.signedness), c_bits,
+                }),
+                if (int.positive) "MAX" else "MIN",
+            });
+
         if (!int.positive) try writer.writeByte('-');
         try data.cty.renderLiteralPrefix(writer, data.kind);
 
@@ -7310,7 +7400,7 @@ fn formatIntLiteral(
         try writer.writeAll(string);
     } else {
         try data.cty.renderLiteralPrefix(writer, data.kind);
-        wrap.convertToTwosComplement(int, .unsigned, data.int_info.bits);
+        wrap.convertToTwosComplement(int, data.int_info.signedness, c_bits);
         std.mem.set(BigIntLimb, wrap.limbs[wrap.len..], 0);
         wrap.len = wrap.limbs.len;
         const limbs_per_c_limb = @divExact(wrap.len, c_limb_info.count);
@@ -7343,7 +7433,7 @@ fn formatIntLiteral(
                 c_limb_cty = c_limb_info.cty.toSigned();
 
                 c_limb_mut.positive = wrap.positive;
-                c_limb_mut.convertToTwosComplement(
+                c_limb_mut.truncate(
                     c_limb_mut.toConst(),
                     .signed,
                     data.int_info.bits - limb_i * @bitSizeOf(BigIntLimb),
src/type.zig
@@ -4213,7 +4213,7 @@ pub const Type = extern union {
         };
     }
 
-    fn shallowElemType(child_ty: Type) Type {
+    pub fn shallowElemType(child_ty: Type) Type {
         return switch (child_ty.zigTypeTag()) {
             .Array, .Vector => child_ty.childType(),
             else => child_ty,
test/behavior/bitcast.zig
@@ -34,7 +34,6 @@ test "@bitCast iX -> uX (8, 16, 128)" {
 
 test "@bitCast iX -> uX exotic integers" {
     if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
@@ -81,7 +80,6 @@ fn conv_uN(comptime N: usize, x: std.meta.Int(.unsigned, N)) std.meta.Int(.signe
 
 test "bitcast uX to bytes" {
     if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
test/behavior/math.zig
@@ -1526,7 +1526,6 @@ fn testNanEqNan(comptime F: type) !void {
 }
 
 test "vector comparison" {
-    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
test/behavior/vector.zig
@@ -48,7 +48,6 @@ test "vector wrap operators" {
 
 test "vector bin compares with mem.eql" {
     if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
-    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
@@ -403,7 +402,6 @@ test "initialize vector which is a struct field" {
 
 test "vector comparison operators" {
     if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
-    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO