authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-01-09 02:16:54-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-01-09 02:16:54-07:00
log6d9119fcd91cd01d658180d7fa5e4c8c203ba3db
tree95343f47aa29a3660e9ba1afee30ec27d1e192db
parentbdca82ea66259de136ce9374e172f567ee93ef89

add memcpy and memset intrinsics


5 files changed, 220 insertions(+), 31 deletions(-)

src/analyze.cpp+72-8
...@@ -1944,8 +1944,11 @@ static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import,...@@ -1944,8 +1944,11 @@ static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import,
1944 if (resolved_type->id != TypeTableEntryIdInvalid) {1944 if (resolved_type->id != TypeTableEntryIdInvalid) {
1945 assert(resolved_type->id == TypeTableEntryIdBool);1945 assert(resolved_type->id == TypeTableEntryIdBool);
1946 bool constant_cond_value = number_literal.data.x_uint;1946 bool constant_cond_value = number_literal.data.x_uint;
1947 if (constant_cond_value && !node->codegen_node->data.while_node.contains_break) {1947 if (constant_cond_value) {
1948 expr_return_type = g->builtin_types.entry_unreachable;1948 node->codegen_node->data.while_node.condition_always_true = true;
1949 if (!node->codegen_node->data.while_node.contains_break) {
1950 expr_return_type = g->builtin_types.entry_unreachable;
1951 }
1949 }1952 }
1950 }1953 }
1951 }1954 }
...@@ -2085,13 +2088,74 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry...@@ -2085,13 +2088,74 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
2085 builtin_fn->param_count, actual_param_count));2088 builtin_fn->param_count, actual_param_count));
2086 }2089 }
20872090
2088 for (int i = 0; i < actual_param_count; i += 1) {2091 switch (builtin_fn->id) {
2089 AstNode *child = node->data.fn_call_expr.params.at(i);2092 case BuiltinFnIdInvalid:
2090 TypeTableEntry *expected_param_type = builtin_fn->param_types[i];2093 zig_unreachable();
2091 analyze_expression(g, import, context, expected_param_type, child);2094 case BuiltinFnIdArithmeticWithOverflow:
2092 }2095 for (int i = 0; i < actual_param_count; i += 1) {
2096 AstNode *child = node->data.fn_call_expr.params.at(i);
2097 TypeTableEntry *expected_param_type = builtin_fn->param_types[i];
2098 analyze_expression(g, import, context, expected_param_type, child);
2099 }
2100 return builtin_fn->return_type;
2101 case BuiltinFnIdMemcpy:
2102 {
2103 AstNode *dest_node = node->data.fn_call_expr.params.at(0);
2104 AstNode *src_node = node->data.fn_call_expr.params.at(1);
2105 AstNode *len_node = node->data.fn_call_expr.params.at(2);
2106 TypeTableEntry *dest_type = analyze_expression(g, import, context, nullptr, dest_node);
2107 TypeTableEntry *src_type = analyze_expression(g, import, context, nullptr, src_node);
2108 analyze_expression(g, import, context, builtin_fn->param_types[2], len_node);
2109
2110 if (dest_type->id != TypeTableEntryIdInvalid &&
2111 dest_type->id != TypeTableEntryIdPointer)
2112 {
2113 add_node_error(g, dest_node,
2114 buf_sprintf("expected pointer argument, got '%s'", buf_ptr(&dest_type->name)));
2115 }
2116
2117 if (src_type->id != TypeTableEntryIdInvalid &&
2118 src_type->id != TypeTableEntryIdPointer)
2119 {
2120 add_node_error(g, src_node,
2121 buf_sprintf("expected pointer argument, got '%s'", buf_ptr(&src_type->name)));
2122 }
20932123
2094 return builtin_fn->return_type;2124 if (dest_type->id == TypeTableEntryIdPointer &&
2125 src_type->id == TypeTableEntryIdPointer)
2126 {
2127 uint64_t dest_align_bits = dest_type->data.pointer.child_type->align_in_bits;
2128 uint64_t src_align_bits = src_type->data.pointer.child_type->align_in_bits;
2129 if (dest_align_bits != src_align_bits) {
2130 add_node_error(g, dest_node, buf_sprintf(
2131 "misaligned memcpy, '%s' has alignment '%" PRIu64 ", '%s' has alignment %" PRIu64,
2132 buf_ptr(&dest_type->name), dest_align_bits / 8,
2133 buf_ptr(&src_type->name), src_align_bits / 8));
2134 }
2135 }
2136
2137 return builtin_fn->return_type;
2138 }
2139 case BuiltinFnIdMemset:
2140 {
2141 AstNode *dest_node = node->data.fn_call_expr.params.at(0);
2142 AstNode *char_node = node->data.fn_call_expr.params.at(1);
2143 AstNode *len_node = node->data.fn_call_expr.params.at(2);
2144 TypeTableEntry *dest_type = analyze_expression(g, import, context, nullptr, dest_node);
2145 analyze_expression(g, import, context, builtin_fn->param_types[1], char_node);
2146 analyze_expression(g, import, context, builtin_fn->param_types[2], len_node);
2147
2148 if (dest_type->id != TypeTableEntryIdInvalid &&
2149 dest_type->id != TypeTableEntryIdPointer)
2150 {
2151 add_node_error(g, dest_node,
2152 buf_sprintf("expected pointer argument, got '%s'", buf_ptr(&dest_type->name)));
2153 }
2154
2155 return builtin_fn->return_type;
2156 }
2157 }
2158 zig_unreachable();
2095 } else {2159 } else {
2096 add_node_error(g, node,2160 add_node_error(g, node,
2097 buf_sprintf("invalid builtin function: '%s'", buf_ptr(name)));2161 buf_sprintf("invalid builtin function: '%s'", buf_ptr(name)));
src/analyze.hpp+3
...@@ -151,6 +151,8 @@ struct FnTableEntry {...@@ -151,6 +151,8 @@ struct FnTableEntry {
151enum BuiltinFnId {151enum BuiltinFnId {
152 BuiltinFnIdInvalid,152 BuiltinFnIdInvalid,
153 BuiltinFnIdArithmeticWithOverflow,153 BuiltinFnIdArithmeticWithOverflow,
154 BuiltinFnIdMemcpy,
155 BuiltinFnIdMemset,
154};156};
155157
156struct BuiltinFnEntry {158struct BuiltinFnEntry {
...@@ -354,6 +356,7 @@ struct ImportNode {...@@ -354,6 +356,7 @@ struct ImportNode {
354};356};
355357
356struct WhileNode {358struct WhileNode {
359 bool condition_always_true;
357 bool contains_break;360 bool contains_break;
358};361};
359362
src/codegen.cpp+126-16
...@@ -171,6 +171,67 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {...@@ -171,6 +171,67 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
171171
172 return overflow_bit;172 return overflow_bit;
173 }173 }
174 case BuiltinFnIdMemcpy:
175 {
176 int fn_call_param_count = node->data.fn_call_expr.params.length;
177 assert(fn_call_param_count == 3);
178
179 AstNode *dest_node = node->data.fn_call_expr.params.at(0);
180 TypeTableEntry *dest_type = get_expr_type(dest_node);
181
182 LLVMValueRef dest_ptr = gen_expr(g, dest_node);
183 LLVMValueRef src_ptr = gen_expr(g, node->data.fn_call_expr.params.at(1));
184 LLVMValueRef len_val = gen_expr(g, node->data.fn_call_expr.params.at(2));
185
186 LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
187
188 add_debug_source_node(g, node);
189 LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, dest_ptr, ptr_u8, "");
190 LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, src_ptr, ptr_u8, "");
191
192 uint64_t align_in_bytes = dest_type->data.pointer.child_type->align_in_bits / 8;
193
194 LLVMValueRef params[] = {
195 dest_ptr_casted, // dest pointer
196 src_ptr_casted, // source pointer
197 len_val, // byte count
198 LLVMConstInt(LLVMInt32Type(), align_in_bytes, false), // align in bytes
199 LLVMConstNull(LLVMInt1Type()), // is volatile
200 };
201
202 LLVMBuildCall(g->builder, builtin_fn->fn_val, params, 5, "");
203 return nullptr;
204 }
205 case BuiltinFnIdMemset:
206 {
207 int fn_call_param_count = node->data.fn_call_expr.params.length;
208 assert(fn_call_param_count == 3);
209
210 AstNode *dest_node = node->data.fn_call_expr.params.at(0);
211 TypeTableEntry *dest_type = get_expr_type(dest_node);
212
213 LLVMValueRef dest_ptr = gen_expr(g, dest_node);
214 LLVMValueRef char_val = gen_expr(g, node->data.fn_call_expr.params.at(1));
215 LLVMValueRef len_val = gen_expr(g, node->data.fn_call_expr.params.at(2));
216
217 LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0);
218
219 add_debug_source_node(g, node);
220 LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, dest_ptr, ptr_u8, "");
221
222 uint64_t align_in_bytes = dest_type->data.pointer.child_type->align_in_bits / 8;
223
224 LLVMValueRef params[] = {
225 dest_ptr_casted, // dest pointer
226 char_val, // source pointer
227 len_val, // byte count
228 LLVMConstInt(LLVMInt32Type(), align_in_bytes, false), // align in bytes
229 LLVMConstNull(LLVMInt1Type()), // is volatile
230 };
231
232 LLVMBuildCall(g->builder, builtin_fn->fn_val, params, 5, "");
233 return nullptr;
234 }
174 }235 }
175 zig_unreachable();236 zig_unreachable();
176}237}
...@@ -1376,23 +1437,35 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) {...@@ -1376,23 +1437,35 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) {
1376 assert(node->data.while_expr.condition);1437 assert(node->data.while_expr.condition);
1377 assert(node->data.while_expr.body);1438 assert(node->data.while_expr.body);
13781439
1379 if (get_expr_type(node)->id == TypeTableEntryIdUnreachable) {1440 bool condition_always_true = node->codegen_node->data.while_node.condition_always_true;
1380 // generate a forever loop. guarantees no break statements1441 bool contains_break = node->codegen_node->data.while_node.contains_break;
1442 if (condition_always_true) {
1443 // generate a forever loop
13811444
1382 LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileBody");1445 LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileBody");
1446 LLVMBasicBlockRef end_block = nullptr;
1447 if (contains_break) {
1448 end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileEnd");
1449 }
13831450
1384 add_debug_source_node(g, node);1451 add_debug_source_node(g, node);
1385 LLVMBuildBr(g->builder, body_block);1452 LLVMBuildBr(g->builder, body_block);
13861453
1387 LLVMPositionBuilderAtEnd(g->builder, body_block);1454 LLVMPositionBuilderAtEnd(g->builder, body_block);
1455 g->break_block_stack.append(end_block);
1388 g->continue_block_stack.append(body_block);1456 g->continue_block_stack.append(body_block);
1389 gen_expr(g, node->data.while_expr.body);1457 gen_expr(g, node->data.while_expr.body);
1458 g->break_block_stack.pop();
1390 g->continue_block_stack.pop();1459 g->continue_block_stack.pop();
13911460
1392 if (get_expr_type(node->data.while_expr.body)->id != TypeTableEntryIdUnreachable) {1461 if (get_expr_type(node->data.while_expr.body)->id != TypeTableEntryIdUnreachable) {
1393 add_debug_source_node(g, node);1462 add_debug_source_node(g, node);
1394 LLVMBuildBr(g->builder, body_block);1463 LLVMBuildBr(g->builder, body_block);
1395 }1464 }
1465
1466 if (contains_break) {
1467 LLVMPositionBuilderAtEnd(g->builder, end_block);
1468 }
1396 } else {1469 } else {
1397 // generate a normal while loop1470 // generate a normal while loop
13981471
...@@ -1755,20 +1828,6 @@ static LLVMAttribute to_llvm_fn_attr(FnAttrId attr_id) {...@@ -1755,20 +1828,6 @@ static LLVMAttribute to_llvm_fn_attr(FnAttrId attr_id) {
1755static void do_code_gen(CodeGen *g) {1828static void do_code_gen(CodeGen *g) {
1756 assert(!g->errors.length);1829 assert(!g->errors.length);
17571830
1758 {
1759 LLVMTypeRef param_types[] = {
1760 LLVMPointerType(LLVMInt8Type(), 0),
1761 LLVMPointerType(LLVMInt8Type(), 0),
1762 LLVMIntType(g->pointer_size_bytes * 8),
1763 LLVMInt32Type(),
1764 LLVMInt1Type(),
1765 };
1766 LLVMTypeRef fn_type = LLVMFunctionType(LLVMVoidType(), param_types, 5, false);
1767 Buf *name = buf_sprintf("llvm.memcpy.p0i8.p0i8.i%d", g->pointer_size_bytes * 8);
1768 g->memcpy_fn_val = LLVMAddFunction(g->module, buf_ptr(name), fn_type);
1769 assert(LLVMGetIntrinsicID(g->memcpy_fn_val));
1770 }
1771
1772 // Generate module level variables1831 // Generate module level variables
1773 for (int i = 0; i < g->global_vars.length; i += 1) {1832 for (int i = 0; i < g->global_vars.length; i += 1) {
1774 VariableTableEntry *var = g->global_vars.at(i);1833 VariableTableEntry *var = g->global_vars.at(i);
...@@ -2267,6 +2326,57 @@ static void define_builtin_fns(CodeGen *g) {...@@ -2267,6 +2326,57 @@ static void define_builtin_fns(CodeGen *g) {
2267 define_builtin_fns_int(g, g->builtin_types.entry_i16);2326 define_builtin_fns_int(g, g->builtin_types.entry_i16);
2268 define_builtin_fns_int(g, g->builtin_types.entry_i32);2327 define_builtin_fns_int(g, g->builtin_types.entry_i32);
2269 define_builtin_fns_int(g, g->builtin_types.entry_i64);2328 define_builtin_fns_int(g, g->builtin_types.entry_i64);
2329 {
2330 BuiltinFnEntry *builtin_fn = allocate<BuiltinFnEntry>(1);
2331 buf_init_from_str(&builtin_fn->name, "memcpy");
2332 builtin_fn->id = BuiltinFnIdMemcpy;
2333 builtin_fn->return_type = g->builtin_types.entry_void;
2334 builtin_fn->param_count = 3;
2335 builtin_fn->param_types = allocate<TypeTableEntry *>(builtin_fn->param_count);
2336 builtin_fn->param_types[0] = nullptr; // manually checked later
2337 builtin_fn->param_types[1] = nullptr; // manually checked later
2338 builtin_fn->param_types[2] = g->builtin_types.entry_usize;
2339
2340 LLVMTypeRef param_types[] = {
2341 LLVMPointerType(LLVMInt8Type(), 0),
2342 LLVMPointerType(LLVMInt8Type(), 0),
2343 LLVMIntType(g->pointer_size_bytes * 8),
2344 LLVMInt32Type(),
2345 LLVMInt1Type(),
2346 };
2347 LLVMTypeRef fn_type = LLVMFunctionType(LLVMVoidType(), param_types, 5, false);
2348 Buf *name = buf_sprintf("llvm.memcpy.p0i8.p0i8.i%d", g->pointer_size_bytes * 8);
2349 g->memcpy_fn_val = LLVMAddFunction(g->module, buf_ptr(name), fn_type);
2350 builtin_fn->fn_val = g->memcpy_fn_val;
2351 assert(LLVMGetIntrinsicID(g->memcpy_fn_val));
2352
2353 g->builtin_fn_table.put(&builtin_fn->name, builtin_fn);
2354 }
2355 {
2356 BuiltinFnEntry *builtin_fn = allocate<BuiltinFnEntry>(1);
2357 buf_init_from_str(&builtin_fn->name, "memset");
2358 builtin_fn->id = BuiltinFnIdMemset;
2359 builtin_fn->return_type = g->builtin_types.entry_void;
2360 builtin_fn->param_count = 3;
2361 builtin_fn->param_types = allocate<TypeTableEntry *>(builtin_fn->param_count);
2362 builtin_fn->param_types[0] = nullptr; // manually checked later
2363 builtin_fn->param_types[1] = g->builtin_types.entry_u8;
2364 builtin_fn->param_types[2] = g->builtin_types.entry_usize;
2365
2366 LLVMTypeRef param_types[] = {
2367 LLVMPointerType(LLVMInt8Type(), 0),
2368 LLVMInt8Type(),
2369 LLVMIntType(g->pointer_size_bytes * 8),
2370 LLVMInt32Type(),
2371 LLVMInt1Type(),
2372 };
2373 LLVMTypeRef fn_type = LLVMFunctionType(LLVMVoidType(), param_types, 5, false);
2374 Buf *name = buf_sprintf("llvm.memset.p0i8.i%d", g->pointer_size_bytes * 8);
2375 builtin_fn->fn_val = LLVMAddFunction(g->module, buf_ptr(name), fn_type);
2376 assert(LLVMGetIntrinsicID(builtin_fn->fn_val));
2377
2378 g->builtin_fn_table.put(&builtin_fn->name, builtin_fn);
2379 }
2270}2380}
22712381
22722382
std/std.zig+1-7
...@@ -118,13 +118,7 @@ fn buf_print_u64(out_buf: []u8, x: u64) -> usize {...@@ -118,13 +118,7 @@ fn buf_print_u64(out_buf: []u8, x: u64) -> usize {
118118
119 const len = buf.len - index;119 const len = buf.len - index;
120120
121 // TODO memcpy intrinsic121 @memcpy(out_buf.ptr, &buf[index], len);
122 // @memcpy(out_buf, buf, len);
123 var i: usize = 0;
124 while (i < len) {
125 out_buf[i] = buf[index + i];
126 i += 1;
127 }
128122
129 return len;123 return len;
130}124}
test/run_tests.cpp+18
...@@ -973,6 +973,24 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {...@@ -973,6 +973,24 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
973 return 0;973 return 0;
974}974}
975 )SOURCE", "OK\n");975 )SOURCE", "OK\n");
976
977 add_simple_case("memcpy and memset intrinsics", R"SOURCE(
978use "std.zig";
979pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
980 var foo : [20]u8;
981 var bar : [20]u8;
982
983 @memset(foo.ptr, 'A', foo.len);
984 @memcpy(bar.ptr, foo.ptr, bar.len);
985
986 if (bar[11] != 'A') {
987 print_str("BAD\n");
988 }
989
990 print_str("OK\n");
991 return 0;
992}
993 )SOURCE", "OK\n");
976}994}
977995
978996