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,
19441944 if (resolved_type->id != TypeTableEntryIdInvalid) {
19451945 assert(resolved_type->id == TypeTableEntryIdBool);
19461946 bool constant_cond_value = number_literal.data.x_uint;
1947 if (constant_cond_value && !node->codegen_node->data.while_node.contains_break) {
1948 expr_return_type = g->builtin_types.entry_unreachable;
1947 if (constant_cond_value) {
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 }
19491952 }
19501953 }
19511954 }
......@@ -2085,13 +2088,74 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
20852088 builtin_fn->param_count, actual_param_count));
20862089 }
20872090
2088 for (int i = 0; i < actual_param_count; i += 1) {
2089 AstNode *child = node->data.fn_call_expr.params.at(i);
2090 TypeTableEntry *expected_param_type = builtin_fn->param_types[i];
2091 analyze_expression(g, import, context, expected_param_type, child);
2092 }
2091 switch (builtin_fn->id) {
2092 case BuiltinFnIdInvalid:
2093 zig_unreachable();
2094 case BuiltinFnIdArithmeticWithOverflow:
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();
20952159 } else {
20962160 add_node_error(g, node,
20972161 buf_sprintf("invalid builtin function: '%s'", buf_ptr(name)));
src/analyze.hpp+3
......@@ -151,6 +151,8 @@ struct FnTableEntry {
151151enum BuiltinFnId {
152152 BuiltinFnIdInvalid,
153153 BuiltinFnIdArithmeticWithOverflow,
154 BuiltinFnIdMemcpy,
155 BuiltinFnIdMemset,
154156};
155157
156158struct BuiltinFnEntry {
......@@ -354,6 +356,7 @@ struct ImportNode {
354356};
355357
356358struct WhileNode {
359 bool condition_always_true;
357360 bool contains_break;
358361};
359362
src/codegen.cpp+126-16
......@@ -171,6 +171,67 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
171171
172172 return overflow_bit;
173173 }
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 }
174235 }
175236 zig_unreachable();
176237}
......@@ -1376,23 +1437,35 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) {
13761437 assert(node->data.while_expr.condition);
13771438 assert(node->data.while_expr.body);
13781439
1379 if (get_expr_type(node)->id == TypeTableEntryIdUnreachable) {
1380 // generate a forever loop. guarantees no break statements
1440 bool condition_always_true = node->codegen_node->data.while_node.condition_always_true;
1441 bool contains_break = node->codegen_node->data.while_node.contains_break;
1442 if (condition_always_true) {
1443 // generate a forever loop
13811444
13821445 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
13841451 add_debug_source_node(g, node);
13851452 LLVMBuildBr(g->builder, body_block);
13861453
13871454 LLVMPositionBuilderAtEnd(g->builder, body_block);
1455 g->break_block_stack.append(end_block);
13881456 g->continue_block_stack.append(body_block);
13891457 gen_expr(g, node->data.while_expr.body);
1458 g->break_block_stack.pop();
13901459 g->continue_block_stack.pop();
13911460
13921461 if (get_expr_type(node->data.while_expr.body)->id != TypeTableEntryIdUnreachable) {
13931462 add_debug_source_node(g, node);
13941463 LLVMBuildBr(g->builder, body_block);
13951464 }
1465
1466 if (contains_break) {
1467 LLVMPositionBuilderAtEnd(g->builder, end_block);
1468 }
13961469 } else {
13971470 // generate a normal while loop
13981471
......@@ -1755,20 +1828,6 @@ static LLVMAttribute to_llvm_fn_attr(FnAttrId attr_id) {
17551828static void do_code_gen(CodeGen *g) {
17561829 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
17721831 // Generate module level variables
17731832 for (int i = 0; i < g->global_vars.length; i += 1) {
17741833 VariableTableEntry *var = g->global_vars.at(i);
......@@ -2267,6 +2326,57 @@ static void define_builtin_fns(CodeGen *g) {
22672326 define_builtin_fns_int(g, g->builtin_types.entry_i16);
22682327 define_builtin_fns_int(g, g->builtin_types.entry_i32);
22692328 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 }
22702380}
22712381
22722382
std/std.zig+1-7
......@@ -118,13 +118,7 @@ fn buf_print_u64(out_buf: []u8, x: u64) -> usize {
118118
119119 const len = buf.len - index;
120120
121 // TODO memcpy intrinsic
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 }
121 @memcpy(out_buf.ptr, &buf[index], len);
128122
129123 return len;
130124}
test/run_tests.cpp+18
......@@ -973,6 +973,24 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
973973 return 0;
974974}
975975 )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");
976994}
977995
978996