authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-02-13 12:50:13-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-02-13 12:50:13-07:00
logc8376af92d63d13574cae7d177b7d314dda44cfe
tree2695432da4ce275d6b1555855bc05e2ceb4c3bc1
parenta5aeb7381f22a42a8f11523267541243bab5964d

add @ctz, @clz and compiler_rt implementation


8 files changed, 353 insertions(+), 3 deletions(-)

CMakeLists.txt+1
......@@ -132,6 +132,7 @@ set(C_HEADERS
132132set(ZIG_STD_SRC
133133 "${CMAKE_SOURCE_DIR}/std/bootstrap.zig"
134134 "${CMAKE_SOURCE_DIR}/std/builtin.zig"
135 "${CMAKE_SOURCE_DIR}/std/compiler_rt.zig"
135136 "${CMAKE_SOURCE_DIR}/std/test_runner.zig"
136137 "${CMAKE_SOURCE_DIR}/std/test_runner_libc.zig"
137138 "${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).
8181```
8282mkdir build
8383cd build
84cmake .. -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))
84cmake .. -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))
8585make
8686make install
8787./run_tests
src/all_types.hpp+3
......@@ -1038,6 +1038,8 @@ enum BuiltinFnId {
10381038 BuiltinFnIdCUndef,
10391039 BuiltinFnIdCompileVar,
10401040 BuiltinFnIdConstEval,
1041 BuiltinFnIdCtz,
1042 BuiltinFnIdClz,
10411043};
10421044
10431045struct BuiltinFnEntry {
......@@ -1161,6 +1163,7 @@ struct CodeGen {
11611163 uint32_t error_value_count;
11621164 TypeTableEntry *err_tag_type;
11631165 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]
11641167
11651168 const char **clang_argv;
11661169 int clang_argv_len;
src/analyze.cpp+23
......@@ -4304,6 +4304,29 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
43044304
43054305 return resolved_type;
43064306 }
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 }
43074330
43084331 }
43094332 zig_unreachable();
src/codegen.cpp+39-2
......@@ -227,6 +227,24 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, TypeTableEntry *type_entry,
227227 return *fn;
228228}
229229
230static 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
230248static LLVMValueRef get_handle_value(CodeGen *g, AstNode *source_node, LLVMValueRef ptr, TypeTableEntry *type) {
231249 if (handle_is_ptr(type)) {
232250 return ptr;
......@@ -249,6 +267,22 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
249267 case BuiltinFnIdCDefine:
250268 case BuiltinFnIdCUndef:
251269 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 }
252286 case BuiltinFnIdAddWithOverflow:
253287 case BuiltinFnIdSubWithOverflow:
254288 case BuiltinFnIdMulWithOverflow:
......@@ -1025,11 +1059,12 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
10251059 case PrefixOpDereference:
10261060 {
10271061 LLVMValueRef expr = gen_expr(g, expr_node);
1062 assert(expr_type->id == TypeTableEntryIdPointer);
10281063 if (!type_has_bits(expr_type)) {
10291064 return nullptr;
10301065 } 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);
10331068 }
10341069 }
10351070 case PrefixOpMaybe:
......@@ -3590,6 +3625,8 @@ static void define_builtin_fns(CodeGen *g) {
35903625 create_builtin_fn_with_arg_count(g, BuiltinFnIdCUndef, "c_undef", 1);
35913626 create_builtin_fn_with_arg_count(g, BuiltinFnIdCompileVar, "compile_var", 1);
35923627 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);
35933630}
35943631
35953632static void init(CodeGen *g, Buf *source_path) {
src/link.cpp+3
......@@ -205,6 +205,9 @@ static void construct_linker_job_linux(LinkJob *lj) {
205205 if (!g->link_libc && (g->out_type == OutTypeExe || g->out_type == OutTypeLib)) {
206206 Buf *builtin_o_path = build_o(g, "builtin");
207207 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));
208211 }
209212
210213 auto it = g->link_table.entry_iterator();
std/compiler_rt.zig created+269
......@@ -0,0 +1,269 @@
1const CHAR_BIT = 8;
2const du_int = c_ulonglong;
3const di_int = c_longlong;
4const si_int = c_int;
5const su_int = c_uint;
6
7const udwords = [2]su_int;
8const low = if (@compile_var("is_big_endian")) 1 else 0;
9const high = 1 - low;
10
11export fn __udivdi3(a: du_int, b: du_int) -> du_int {
12 return __udivmoddi4(a, b, null);
13}
14
15fn 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
21export 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
217export 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")
225fn 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
233fn 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")
239fn 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
260fn 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
267fn assert(b: bool) {
268 if (!b) unreachable{};
269}
test/self_hosted.zig+14
......@@ -479,6 +479,20 @@ struct ArrayDotLenConstExpr {
479479const some_array = []u8 {0, 1, 2, 3};
480480
481481
482#attribute("test")
483fn 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")
490fn count_trailing_zeroes() {
491 assert(@ctz(u8, 0b10100000) == 5);
492 assert(@ctz(u8, 0b10001010) == 1);
493 assert(@ctz(u8, 0b00000000) == 8);
494}
495
482496
483497
484498fn assert(b: bool) {