| author | |
| committer | |
| log | c8376af92d63d13574cae7d177b7d314dda44cfe |
| tree | 2695432da4ce275d6b1555855bc05e2ceb4c3bc1 |
| parent | a5aeb7381f22a42a8f11523267541243bab5964d |
8 files changed, 353 insertions(+), 3 deletions(-)
CMakeLists.txt+1| ... | ... | @@ -132,6 +132,7 @@ set(C_HEADERS |
| 132 | 132 | set(ZIG_STD_SRC |
| 133 | 133 | "${CMAKE_SOURCE_DIR}/std/bootstrap.zig" |
| 134 | 134 | "${CMAKE_SOURCE_DIR}/std/builtin.zig" |
| 135 | "${CMAKE_SOURCE_DIR}/std/compiler_rt.zig" | |
| 135 | 136 | "${CMAKE_SOURCE_DIR}/std/test_runner.zig" |
| 136 | 137 | "${CMAKE_SOURCE_DIR}/std/test_runner_libc.zig" |
| 137 | 138 | "${CMAKE_SOURCE_DIR}/std/test_runner_nolibc.zig" |
README.md+1-1| ... | ... | @@ -81,7 +81,7 @@ and `ZIG_LIBC_STATIC_LIB_DIR` should be set to (example below). |
| 81 | 81 | ``` |
| 82 | 82 | mkdir build |
| 83 | 83 | cd build |
| 84 | cmake .. -DCMAKE_INSTALL_PREFIX=$(pwd) -DZIG_LIBC_LIB_DIR=$(dirname $(cc -print-file-name=crt1.o)) -DZIG_LIBC_INCLUDE_DIR=/usr/include -DZIG_LIBC_STATIC_LIB_DIR=$(dirname $(cc -print-file-name=crtbeginT.o)) | |
| 84 | cmake .. -DCMAKE_INSTALL_PREFIX=$(pwd) -DZIG_LIBC_LIB_DIR=$(dirname $(cc -print-file-name=crt1.o)) -DZIG_LIBC_INCLUDE_DIR=/usr/include -DZIG_LIBC_STATIC_LIB_DIR=$(dirname $(cc -print-file-name=crtbegin.o)) | |
| 85 | 85 | make |
| 86 | 86 | make install |
| 87 | 87 | ./run_tests |
src/all_types.hpp+3| ... | ... | @@ -1038,6 +1038,8 @@ enum BuiltinFnId { |
| 1038 | 1038 | BuiltinFnIdCUndef, |
| 1039 | 1039 | BuiltinFnIdCompileVar, |
| 1040 | 1040 | BuiltinFnIdConstEval, |
| 1041 | BuiltinFnIdCtz, | |
| 1042 | BuiltinFnIdClz, | |
| 1041 | 1043 | }; |
| 1042 | 1044 | |
| 1043 | 1045 | struct BuiltinFnEntry { |
| ... | ... | @@ -1161,6 +1163,7 @@ struct CodeGen { |
| 1161 | 1163 | uint32_t error_value_count; |
| 1162 | 1164 | TypeTableEntry *err_tag_type; |
| 1163 | 1165 | LLVMValueRef int_overflow_fns[2][3][4]; // [0-signed,1-unsigned][0-add,1-sub,2-mul][0-8,1-16,2-32,3-64] |
| 1166 | LLVMValueRef int_builtin_fns[2][4]; // [0-ctz,1-clz][0-8,1-16,2-32,3-64] | |
| 1164 | 1167 | |
| 1165 | 1168 | const char **clang_argv; |
| 1166 | 1169 | int clang_argv_len; |
src/analyze.cpp+23| ... | ... | @@ -4304,6 +4304,29 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry |
| 4304 | 4304 | |
| 4305 | 4305 | return resolved_type; |
| 4306 | 4306 | } |
| 4307 | case BuiltinFnIdCtz: | |
| 4308 | case BuiltinFnIdClz: | |
| 4309 | { | |
| 4310 | AstNode *type_node = node->data.fn_call_expr.params.at(0); | |
| 4311 | TypeTableEntry *int_type = analyze_type_expr(g, import, context, type_node); | |
| 4312 | if (int_type->id == TypeTableEntryIdInvalid) { | |
| 4313 | return int_type; | |
| 4314 | } else if (int_type->id == TypeTableEntryIdInt) { | |
| 4315 | AstNode **expr_node = node->data.fn_call_expr.params.at(1)->parent_field; | |
| 4316 | TypeTableEntry *resolved_type = analyze_expression(g, import, context, int_type, *expr_node); | |
| 4317 | if (resolved_type->id == TypeTableEntryIdInvalid) { | |
| 4318 | return resolved_type; | |
| 4319 | } | |
| 4320 | ||
| 4321 | // TODO const expr eval | |
| 4322 | ||
| 4323 | return resolved_type; | |
| 4324 | } else { | |
| 4325 | add_node_error(g, type_node, | |
| 4326 | buf_sprintf("expected integer type, got '%s'", buf_ptr(&int_type->name))); | |
| 4327 | return g->builtin_types.entry_invalid; | |
| 4328 | } | |
| 4329 | } | |
| 4307 | 4330 | |
| 4308 | 4331 | } |
| 4309 | 4332 | zig_unreachable(); |
src/codegen.cpp+39-2| ... | ... | @@ -227,6 +227,24 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, TypeTableEntry *type_entry, |
| 227 | 227 | return *fn; |
| 228 | 228 | } |
| 229 | 229 | |
| 230 | static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, BuiltinFnId fn_id) { | |
| 231 | // [0-ctz,1-clz][0-8,1-16,2-32,3-64] | |
| 232 | int index0 = (fn_id == BuiltinFnIdCtz) ? 0 : 1; | |
| 233 | int index1 = bits_index(int_type->data.integral.bit_count); | |
| 234 | LLVMValueRef *fn = &g->int_builtin_fns[index0][index1]; | |
| 235 | if (!*fn) { | |
| 236 | const char *fn_name = (fn_id == BuiltinFnIdCtz) ? "cttz" : "ctlz"; | |
| 237 | Buf *llvm_name = buf_sprintf("llvm.%s.i%d", fn_name, int_type->data.integral.bit_count); | |
| 238 | LLVMTypeRef param_types[] = { | |
| 239 | int_type->type_ref, | |
| 240 | LLVMInt1Type(), | |
| 241 | }; | |
| 242 | LLVMTypeRef fn_type = LLVMFunctionType(int_type->type_ref, param_types, 2, false); | |
| 243 | *fn = LLVMAddFunction(g->module, buf_ptr(llvm_name), fn_type); | |
| 244 | } | |
| 245 | return *fn; | |
| 246 | } | |
| 247 | ||
| 230 | 248 | static LLVMValueRef get_handle_value(CodeGen *g, AstNode *source_node, LLVMValueRef ptr, TypeTableEntry *type) { |
| 231 | 249 | if (handle_is_ptr(type)) { |
| 232 | 250 | return ptr; |
| ... | ... | @@ -249,6 +267,22 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { |
| 249 | 267 | case BuiltinFnIdCDefine: |
| 250 | 268 | case BuiltinFnIdCUndef: |
| 251 | 269 | zig_unreachable(); |
| 270 | case BuiltinFnIdCtz: | |
| 271 | case BuiltinFnIdClz: | |
| 272 | { | |
| 273 | int fn_call_param_count = node->data.fn_call_expr.params.length; | |
| 274 | assert(fn_call_param_count == 2); | |
| 275 | TypeTableEntry *int_type = get_type_for_type_node(node->data.fn_call_expr.params.at(0)); | |
| 276 | assert(int_type->id == TypeTableEntryIdInt); | |
| 277 | LLVMValueRef fn_val = get_int_builtin_fn(g, int_type, builtin_fn->id); | |
| 278 | LLVMValueRef operand = gen_expr(g, node->data.fn_call_expr.params.at(1)); | |
| 279 | LLVMValueRef params[] { | |
| 280 | operand, | |
| 281 | LLVMConstNull(LLVMInt1Type()), | |
| 282 | }; | |
| 283 | add_debug_source_node(g, node); | |
| 284 | return LLVMBuildCall(g->builder, fn_val, params, 2, ""); | |
| 285 | } | |
| 252 | 286 | case BuiltinFnIdAddWithOverflow: |
| 253 | 287 | case BuiltinFnIdSubWithOverflow: |
| 254 | 288 | case BuiltinFnIdMulWithOverflow: |
| ... | ... | @@ -1025,11 +1059,12 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { |
| 1025 | 1059 | case PrefixOpDereference: |
| 1026 | 1060 | { |
| 1027 | 1061 | LLVMValueRef expr = gen_expr(g, expr_node); |
| 1062 | assert(expr_type->id == TypeTableEntryIdPointer); | |
| 1028 | 1063 | if (!type_has_bits(expr_type)) { |
| 1029 | 1064 | return nullptr; |
| 1030 | 1065 | } else { |
| 1031 | add_debug_source_node(g, node); | |
| 1032 | return LLVMBuildLoad(g->builder, expr, ""); | |
| 1066 | TypeTableEntry *child_type = expr_type->data.pointer.child_type; | |
| 1067 | return get_handle_value(g, node, expr, child_type); | |
| 1033 | 1068 | } |
| 1034 | 1069 | } |
| 1035 | 1070 | case PrefixOpMaybe: |
| ... | ... | @@ -3590,6 +3625,8 @@ static void define_builtin_fns(CodeGen *g) { |
| 3590 | 3625 | create_builtin_fn_with_arg_count(g, BuiltinFnIdCUndef, "c_undef", 1); |
| 3591 | 3626 | create_builtin_fn_with_arg_count(g, BuiltinFnIdCompileVar, "compile_var", 1); |
| 3592 | 3627 | create_builtin_fn_with_arg_count(g, BuiltinFnIdConstEval, "const_eval", 1); |
| 3628 | create_builtin_fn_with_arg_count(g, BuiltinFnIdCtz, "ctz", 2); | |
| 3629 | create_builtin_fn_with_arg_count(g, BuiltinFnIdClz, "clz", 2); | |
| 3593 | 3630 | } |
| 3594 | 3631 | |
| 3595 | 3632 | static void init(CodeGen *g, Buf *source_path) { |
src/link.cpp+3| ... | ... | @@ -205,6 +205,9 @@ static void construct_linker_job_linux(LinkJob *lj) { |
| 205 | 205 | if (!g->link_libc && (g->out_type == OutTypeExe || g->out_type == OutTypeLib)) { |
| 206 | 206 | Buf *builtin_o_path = build_o(g, "builtin"); |
| 207 | 207 | lj->args.append(buf_ptr(builtin_o_path)); |
| 208 | ||
| 209 | Buf *compiler_rt_o_path = build_o(g, "compiler_rt"); | |
| 210 | lj->args.append(buf_ptr(compiler_rt_o_path)); | |
| 208 | 211 | } |
| 209 | 212 | |
| 210 | 213 | auto it = g->link_table.entry_iterator(); |
std/compiler_rt.zig created+269| ... | ... | @@ -0,0 +1,269 @@ |
| 1 | const CHAR_BIT = 8; | |
| 2 | const du_int = c_ulonglong; | |
| 3 | const di_int = c_longlong; | |
| 4 | const si_int = c_int; | |
| 5 | const su_int = c_uint; | |
| 6 | ||
| 7 | const udwords = [2]su_int; | |
| 8 | const low = if (@compile_var("is_big_endian")) 1 else 0; | |
| 9 | const high = 1 - low; | |
| 10 | ||
| 11 | export fn __udivdi3(a: du_int, b: du_int) -> du_int { | |
| 12 | return __udivmoddi4(a, b, null); | |
| 13 | } | |
| 14 | ||
| 15 | fn du_int_to_udwords(x: du_int) -> udwords { | |
| 16 | // TODO ability to take address of params | |
| 17 | const x2 = x; | |
| 18 | return *(&udwords)(&x2); | |
| 19 | } | |
| 20 | ||
| 21 | export fn __udivmoddi4(a: du_int, b: du_int, maybe_rem: ?&du_int) -> du_int { | |
| 22 | const n_uword_bits = @sizeof(su_int) * CHAR_BIT; | |
| 23 | const n_udword_bits = @sizeof(du_int) * CHAR_BIT; | |
| 24 | var n = du_int_to_udwords(a); | |
| 25 | var d = du_int_to_udwords(b); | |
| 26 | var q: udwords = undefined; | |
| 27 | var r: udwords = undefined; | |
| 28 | var sr: c_uint = undefined; | |
| 29 | /* special cases, X is unknown, K != 0 */ | |
| 30 | if (n[high] == 0) { | |
| 31 | if (d[high] == 0) { | |
| 32 | /* 0 X | |
| 33 | * --- | |
| 34 | * 0 X | |
| 35 | */ | |
| 36 | if (const rem ?= maybe_rem) { | |
| 37 | *rem = n[low] % d[low]; | |
| 38 | } | |
| 39 | return n[low] / d[low]; | |
| 40 | } | |
| 41 | /* 0 X | |
| 42 | * --- | |
| 43 | * K X | |
| 44 | */ | |
| 45 | if (const rem ?= maybe_rem) { | |
| 46 | *rem = n[low]; | |
| 47 | } | |
| 48 | return 0; | |
| 49 | } | |
| 50 | /* n[high] != 0 */ | |
| 51 | if (d[low] == 0) { | |
| 52 | if (d[high] == 0) { | |
| 53 | /* K X | |
| 54 | * --- | |
| 55 | * 0 0 | |
| 56 | */ | |
| 57 | if (var rem ?= maybe_rem) { | |
| 58 | *rem = n[high] % d[low]; | |
| 59 | } | |
| 60 | return n[high] / d[low]; | |
| 61 | } | |
| 62 | /* d[high] != 0 */ | |
| 63 | if (n[low] == 0) { | |
| 64 | /* K 0 | |
| 65 | * --- | |
| 66 | * K 0 | |
| 67 | */ | |
| 68 | if (var rem ?= maybe_rem) { | |
| 69 | r[high] = n[high] % d[high]; | |
| 70 | r[low] = 0; | |
| 71 | *rem = *(&du_int)(&r[0]); | |
| 72 | } | |
| 73 | return n[high] / d[high]; | |
| 74 | } | |
| 75 | /* K K | |
| 76 | * --- | |
| 77 | * K 0 | |
| 78 | */ | |
| 79 | // if d is a power of 2 | |
| 80 | if ((d[high] & (d[high] - 1)) == 0) { | |
| 81 | if (var rem ?= maybe_rem) { | |
| 82 | r[low] = n[low]; | |
| 83 | r[high] = n[high] & (d[high] - 1); | |
| 84 | *rem = *(&du_int)(&r[0]); | |
| 85 | } | |
| 86 | return n[high] >> @ctz(@typeof(d[high]), d[high]); | |
| 87 | } | |
| 88 | /* K K | |
| 89 | * --- | |
| 90 | * K 0 | |
| 91 | */ | |
| 92 | sr = @clz(su_int, d[high]) - @clz(su_int, n[high]); | |
| 93 | /* 0 <= sr <= n_uword_bits - 2 or sr large */ | |
| 94 | if (sr > n_uword_bits - 2) { | |
| 95 | if (var rem ?= maybe_rem) { | |
| 96 | *rem = *(&du_int)(&n[0]); | |
| 97 | } | |
| 98 | return 0; | |
| 99 | } | |
| 100 | sr += 1; | |
| 101 | /* 1 <= sr <= n_uword_bits - 1 */ | |
| 102 | /* q.all = n.all << (n_udword_bits - sr); */ | |
| 103 | q[low] = 0; | |
| 104 | q[high] = n[low] << (n_uword_bits - sr); | |
| 105 | /* r.all = n.all >> sr; */ | |
| 106 | r[high] = n[high] >> sr; | |
| 107 | r[low] = (n[high] << (n_uword_bits - sr)) | (n[low] >> sr); | |
| 108 | } else { | |
| 109 | /* d[low] != 0 */ | |
| 110 | if (d[high] == 0) { | |
| 111 | /* K X | |
| 112 | * --- | |
| 113 | * 0 K | |
| 114 | */ | |
| 115 | /* if d is a power of 2 */ | |
| 116 | if ((d[low] & (d[low] - 1)) == 0) { | |
| 117 | if (var rem ?= maybe_rem) { | |
| 118 | *rem = n[low] & (d[low] - 1); | |
| 119 | } | |
| 120 | if (d[low] == 1) { | |
| 121 | return *(&du_int)(&n[0]); | |
| 122 | } | |
| 123 | sr = @ctz(@typeof(d[low]), d[low]); | |
| 124 | q[high] = n[high] >> sr; | |
| 125 | q[low] = (n[high] << (n_uword_bits - sr)) | (n[low] >> sr); | |
| 126 | return *(&du_int)(&q[0]); | |
| 127 | } | |
| 128 | /* K X | |
| 129 | * --- | |
| 130 | * 0 K | |
| 131 | */ | |
| 132 | sr = 1 + n_uword_bits + @clz(su_int, d[low]) - @clz(su_int, n[high]); | |
| 133 | /* 2 <= sr <= n_udword_bits - 1 | |
| 134 | * q.all = n.all << (n_udword_bits - sr); | |
| 135 | * r.all = n.all >> sr; | |
| 136 | */ | |
| 137 | if (sr == n_uword_bits) { | |
| 138 | q[low] = 0; | |
| 139 | q[high] = n[low]; | |
| 140 | r[high] = 0; | |
| 141 | r[low] = n[high]; | |
| 142 | } else if (sr < n_uword_bits) { | |
| 143 | // 2 <= sr <= n_uword_bits - 1 | |
| 144 | q[low] = 0; | |
| 145 | q[high] = n[low] << (n_uword_bits - sr); | |
| 146 | r[high] = n[high] >> sr; | |
| 147 | r[low] = (n[high] << (n_uword_bits - sr)) | (n[low] >> sr); | |
| 148 | } else { | |
| 149 | // n_uword_bits + 1 <= sr <= n_udword_bits - 1 | |
| 150 | q[low] = n[low] << (n_udword_bits - sr); | |
| 151 | q[high] = (n[high] << (n_udword_bits - sr)) | | |
| 152 | (n[low] >> (sr - n_uword_bits)); | |
| 153 | r[high] = 0; | |
| 154 | r[low] = n[high] >> (sr - n_uword_bits); | |
| 155 | } | |
| 156 | } else { | |
| 157 | /* K X | |
| 158 | * --- | |
| 159 | * K K | |
| 160 | */ | |
| 161 | sr = @clz(su_int, d[high]) - @clz(su_int, n[high]); | |
| 162 | /* 0 <= sr <= n_uword_bits - 1 or sr large */ | |
| 163 | if (sr > n_uword_bits - 1) { | |
| 164 | if (var rem ?= maybe_rem) { | |
| 165 | *rem = *(&du_int)(&n[0]); | |
| 166 | } | |
| 167 | return 0; | |
| 168 | } | |
| 169 | sr += 1; | |
| 170 | /* 1 <= sr <= n_uword_bits */ | |
| 171 | /* q.all = n.all << (n_udword_bits - sr); */ | |
| 172 | q[low] = 0; | |
| 173 | if (sr == n_uword_bits) { | |
| 174 | q[high] = n[low]; | |
| 175 | r[high] = 0; | |
| 176 | r[low] = n[high]; | |
| 177 | } else { | |
| 178 | q[high] = n[low] << (n_uword_bits - sr); | |
| 179 | r[high] = n[high] >> sr; | |
| 180 | r[low] = (n[high] << (n_uword_bits - sr)) | (n[low] >> sr); | |
| 181 | } | |
| 182 | } | |
| 183 | } | |
| 184 | /* Not a special case | |
| 185 | * q and r are initialized with: | |
| 186 | * q.all = n.all << (n_udword_bits - sr); | |
| 187 | * r.all = n.all >> sr; | |
| 188 | * 1 <= sr <= n_udword_bits - 1 | |
| 189 | */ | |
| 190 | var carry: su_int = 0; | |
| 191 | while (sr > 0) { | |
| 192 | /* r:q = ((r:q) << 1) | carry */ | |
| 193 | r[high] = (r[high] << 1) | (r[low] >> (n_uword_bits - 1)); | |
| 194 | r[low] = (r[low] << 1) | (q[high] >> (n_uword_bits - 1)); | |
| 195 | q[high] = (q[high] << 1) | (q[low] >> (n_uword_bits - 1)); | |
| 196 | q[low] = (q[low] << 1) | carry; | |
| 197 | /* carry = 0; | |
| 198 | * if (r.all >= d.all) | |
| 199 | * { | |
| 200 | * r.all -= d.all; | |
| 201 | * carry = 1; | |
| 202 | * } | |
| 203 | */ | |
| 204 | const s: di_int = (di_int)(*(&du_int)(&d[0]) - *(&du_int)(&r[0]) - 1) >> (n_udword_bits - 1); | |
| 205 | carry = su_int(s & 1); | |
| 206 | *(&du_int)(&r[0]) -= *(&du_int)(&d[0]) & c_ulonglong(s); | |
| 207 | ||
| 208 | sr -= 1; | |
| 209 | } | |
| 210 | *(&du_int)(&q[0]) = (*(&du_int)(&q[0]) << 1) | carry; | |
| 211 | if (var rem ?= maybe_rem) { | |
| 212 | *rem = *(&du_int)(&r[0]); | |
| 213 | } | |
| 214 | return *(&du_int)(&q[0]); | |
| 215 | } | |
| 216 | ||
| 217 | export fn __umoddi3(a: du_int, b: du_int) -> du_int { | |
| 218 | var r: du_int = undefined; | |
| 219 | __udivmoddi4(a, b, &r); | |
| 220 | return r; | |
| 221 | } | |
| 222 | ||
| 223 | ||
| 224 | #attribute("test") | |
| 225 | fn test_umoddi3() { | |
| 226 | test_one_umoddi3(0, 1, 0); | |
| 227 | test_one_umoddi3(2, 1, 0); | |
| 228 | test_one_umoddi3(0x8000000000000000, 1, 0x0); | |
| 229 | test_one_umoddi3(0x8000000000000000, 2, 0x0); | |
| 230 | test_one_umoddi3(0xFFFFFFFFFFFFFFFF, 2, 0x1); | |
| 231 | } | |
| 232 | ||
| 233 | fn test_one_umoddi3(a: du_int, b: du_int, expected_r: du_int) { | |
| 234 | const r = __umoddi3(a, b); | |
| 235 | assert(r == expected_r); | |
| 236 | } | |
| 237 | ||
| 238 | #attribute("test") | |
| 239 | fn test_udivmoddi4() { | |
| 240 | const cases = [][4]du_int { | |
| 241 | []du_int{0x0000000000000000, 0x0000000000000001, 0x0000000000000000, 0x0000000000000000}, | |
| 242 | []du_int{0x0000000080000000, 0x0000000100000001, 0x0000000000000000, 0x0000000080000000}, | |
| 243 | []du_int{0x7FFFFFFF00000001, 0x0000000000000001, 0x7FFFFFFF00000001, 0x0000000000000000}, | |
| 244 | []du_int{0x7FFFFFFF7FFFFFFF, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000, 0x7FFFFFFF7FFFFFFF}, | |
| 245 | []du_int{0x8000000000000002, 0xFFFFFFFFFFFFFFFE, 0x0000000000000000, 0x8000000000000002}, | |
| 246 | []du_int{0x80000000FFFFFFFD, 0xFFFFFFFFFFFFFFFD, 0x0000000000000000, 0x80000000FFFFFFFD}, | |
| 247 | []du_int{0xFFFFFFFD00000010, 0xFFFFFFFF80000000, 0x0000000000000000, 0xFFFFFFFD00000010}, | |
| 248 | []du_int{0xFFFFFFFDFFFFFFFF, 0xFFFFFFFF7FFFFFFF, 0x0000000000000000, 0xFFFFFFFDFFFFFFFF}, | |
| 249 | []du_int{0xFFFFFFFE0747AE14, 0xFFFFFFFF0747AE14, 0x0000000000000000, 0xFFFFFFFE0747AE14}, | |
| 250 | []du_int{0xFFFFFFFF00000001, 0xFFFFFFFF078644FA, 0x0000000000000000, 0xFFFFFFFF00000001}, | |
| 251 | []du_int{0xFFFFFFFF80000000, 0xFFFFFFFF00000010, 0x0000000000000001, 0x000000007FFFFFF0}, | |
| 252 | []du_int{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0x0000000000000001, 0x0000000000000000}, | |
| 253 | }; | |
| 254 | ||
| 255 | for (cases) |case| { | |
| 256 | test_one_udivmoddi4(case[0], case[1], case[2], case[3]); | |
| 257 | } | |
| 258 | } | |
| 259 | ||
| 260 | fn test_one_udivmoddi4(a: du_int, b: du_int, expected_q: du_int, expected_r: du_int) { | |
| 261 | var r: du_int = undefined; | |
| 262 | const q = __udivmoddi4(a, b, &r); | |
| 263 | assert(q == expected_q); | |
| 264 | assert(r == expected_r); | |
| 265 | } | |
| 266 | ||
| 267 | fn assert(b: bool) { | |
| 268 | if (!b) unreachable{}; | |
| 269 | } |
test/self_hosted.zig+14| ... | ... | @@ -479,6 +479,20 @@ struct ArrayDotLenConstExpr { |
| 479 | 479 | const some_array = []u8 {0, 1, 2, 3}; |
| 480 | 480 | |
| 481 | 481 | |
| 482 | #attribute("test") | |
| 483 | fn count_leading_zeroes() { | |
| 484 | assert(@clz(u8, 0b00001010) == 4); | |
| 485 | assert(@clz(u8, 0b10001010) == 0); | |
| 486 | assert(@clz(u8, 0b00000000) == 8); | |
| 487 | } | |
| 488 | ||
| 489 | #attribute("test") | |
| 490 | fn count_trailing_zeroes() { | |
| 491 | assert(@ctz(u8, 0b10100000) == 5); | |
| 492 | assert(@ctz(u8, 0b10001010) == 1); | |
| 493 | assert(@ctz(u8, 0b00000000) == 8); | |
| 494 | } | |
| 495 | ||
| 482 | 496 | |
| 483 | 497 | |
| 484 | 498 | fn assert(b: bool) { |