authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-01-24 18:34:50-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-01-24 18:34:50-07:00
log419652ee8ff709deae988603ecca2c335599d969
tree7678349497a842e64073df781fe241d3dae9c84d
parentca7b85b32e26acfd716c02047966254a34cd2237

ability to return structs byvalue from functions

closes #57

4 files changed, 191 insertions(+), 71 deletions(-)

src/all_types.hpp+6-1
......@@ -214,6 +214,9 @@ struct AstNodeParamDecl {
214214
215215 // populated by semantic analyzer
216216 VariableTableEntry *variable;
217 bool is_byval;
218 int src_index;
219 int gen_index;
217220};
218221
219222struct AstNodeBlock {
......@@ -794,7 +797,8 @@ struct TypeTableEntryEnum {
794797};
795798
796799struct TypeTableEntryFn {
797 TypeTableEntry *return_type;
800 TypeTableEntry *src_return_type;
801 TypeTableEntry *gen_return_type;
798802 TypeTableEntry **param_types;
799803 int src_param_count;
800804 LLVMTypeRef raw_type_ref;
......@@ -989,6 +993,7 @@ struct CodeGen {
989993
990994 OutType out_type;
991995 FnTableEntry *cur_fn;
996 LLVMValueRef cur_ret_ptr;
992997 // TODO remove this in favor of get_resolved_expr(expr_node)->context
993998 BlockContext *cur_block_context;
994999 ZigList<LLVMBasicBlockRef> break_block_stack;
src/analyze.cpp+82-40
......@@ -424,6 +424,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
424424 AstNodeFnProto *fn_proto = &node->data.fn_proto;
425425
426426 TypeTableEntry *fn_type = new_type_table_entry(TypeTableEntryIdFn);
427 fn_table_entry->type_entry = fn_type;
427428 fn_type->data.fn.calling_convention = fn_table_entry->internal_linkage ? LLVMFastCallConv : LLVMCCallConv;
428429
429430 for (int i = 0; i < fn_proto->directives->length; i += 1) {
......@@ -452,22 +453,18 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
452453 }
453454
454455 int src_param_count = node->data.fn_proto.params.length;
455
456456 fn_type->size_in_bits = g->pointer_size_bytes * 8;
457457 fn_type->align_in_bits = g->pointer_size_bytes * 8;
458458 fn_type->data.fn.src_param_count = src_param_count;
459459 fn_type->data.fn.param_types = allocate<TypeTableEntry*>(src_param_count);
460460
461 fn_table_entry->type_entry = fn_type;
462
461 // first, analyze the parameters and return type in order they appear in
462 // source code in order for error messages to be in the best order.
463463 buf_resize(&fn_type->name, 0);
464464 const char *export_str = fn_table_entry->internal_linkage ? "" : "export ";
465465 const char *inline_str = fn_table_entry->is_inline ? "inline " : "";
466466 const char *naked_str = fn_type->data.fn.is_naked ? "naked " : "";
467467 buf_appendf(&fn_type->name, "%s%s%sfn(", export_str, inline_str, naked_str);
468 int gen_param_count = 0;
469 LLVMTypeRef *gen_param_types = allocate<LLVMTypeRef>(src_param_count);
470 LLVMZigDIType **param_di_types = allocate<LLVMZigDIType*>(1 + src_param_count);
471468 for (int i = 0; i < src_param_count; i += 1) {
472469 AstNode *child = node->data.fn_proto.params.at(i);
473470 assert(child->type == NodeTypeParamDecl);
......@@ -475,6 +472,54 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
475472 child->data.param_decl.type);
476473 fn_type->data.fn.param_types[i] = type_entry;
477474
475 const char *comma = (i == 0) ? "" : ", ";
476 buf_appendf(&fn_type->name, "%s%s", comma, buf_ptr(&type_entry->name));
477 }
478
479 TypeTableEntry *return_type = analyze_type_expr(g, import, import->block_context,
480 node->data.fn_proto.return_type);
481 fn_type->data.fn.src_return_type = return_type;
482 if (return_type->id == TypeTableEntryIdInvalid) {
483 fn_proto->skip = true;
484 }
485 fn_type->data.fn.is_var_args = fn_proto->is_var_args;
486 if (fn_proto->is_var_args) {
487 const char *comma = (src_param_count == 0) ? "" : ", ";
488 buf_appendf(&fn_type->name, "%s...", comma);
489 }
490
491 buf_appendf(&fn_type->name, ")");
492 if (return_type->id != TypeTableEntryIdVoid) {
493 buf_appendf(&fn_type->name, " %s", buf_ptr(&return_type->name));
494 }
495
496
497 // next, loop over the parameters again and compute debug information
498 // and codegen information
499 bool first_arg_return = handle_is_ptr(return_type);
500 // +1 for maybe making the first argument the return value
501 LLVMTypeRef *gen_param_types = allocate<LLVMTypeRef>(1 + src_param_count);
502 // +1 because 0 is the return type and +1 for maybe making first arg ret val
503 LLVMZigDIType **param_di_types = allocate<LLVMZigDIType*>(2 + src_param_count);
504 param_di_types[0] = return_type->di_type;
505 int gen_param_index = 0;
506 TypeTableEntry *gen_return_type;
507 if (first_arg_return) {
508 TypeTableEntry *gen_type = get_pointer_to_type(g, return_type, false);
509 gen_param_types[gen_param_index] = gen_type->type_ref;
510 gen_param_index += 1;
511 // after the gen_param_index += 1 because 0 is the return type
512 param_di_types[gen_param_index] = gen_type->di_type;
513 gen_return_type = g->builtin_types.entry_void;
514 } else {
515 gen_return_type = return_type;
516 }
517 fn_type->data.fn.gen_return_type = gen_return_type;
518 for (int i = 0; i < src_param_count; i += 1) {
519 AstNode *child = node->data.fn_proto.params.at(i);
520 assert(child->type == NodeTypeParamDecl);
521 TypeTableEntry *type_entry = fn_type->data.fn.param_types[i];
522
478523 if (type_entry->id == TypeTableEntryIdUnreachable) {
479524 add_node_error(g, child->data.param_decl.type,
480525 buf_sprintf("parameter of type 'unreachable' not allowed"));
......@@ -483,39 +528,29 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
483528 fn_proto->skip = true;
484529 }
485530
531 child->data.param_decl.src_index = i;
532 child->data.param_decl.gen_index = -1;
533
486534 if (!fn_proto->skip && type_entry->size_in_bits > 0) {
487 const char *comma = (gen_param_count == 0) ? "" : ", ";
488 buf_appendf(&fn_type->name, "%s%s", comma, buf_ptr(&type_entry->name));
489535
490 TypeTableEntry *gen_type = handle_is_ptr(type_entry) ?
491 get_pointer_to_type(g, type_entry, true) : type_entry;
492 gen_param_types[gen_param_count] = gen_type->type_ref;
536 TypeTableEntry *gen_type;
537 if (handle_is_ptr(type_entry)) {
538 gen_type = get_pointer_to_type(g, type_entry, true);
539 child->data.param_decl.is_byval = true;
540 } else {
541 gen_type = type_entry;
542 }
543 gen_param_types[gen_param_index] = gen_type->type_ref;
544 child->data.param_decl.gen_index = gen_param_index;
493545
494 gen_param_count += 1;
546 gen_param_index += 1;
495547
496 // after the gen_param_count += 1 because 0 is the return type
497 param_di_types[gen_param_count] = gen_type->di_type;
548 // after the gen_param_index += 1 because 0 is the return type
549 param_di_types[gen_param_index] = gen_type->di_type;
498550 }
499551 }
500552
501 fn_type->data.fn.gen_param_count = gen_param_count;
502 fn_type->data.fn.is_var_args = fn_proto->is_var_args;
503 if (fn_proto->is_var_args) {
504 const char *comma = (gen_param_count == 0) ? "" : ", ";
505 buf_appendf(&fn_type->name, "%s...", comma);
506 }
507
508 TypeTableEntry *return_type = analyze_type_expr(g, import, import->block_context,
509 node->data.fn_proto.return_type);
510 fn_type->data.fn.return_type = return_type;
511 if (return_type->id == TypeTableEntryIdInvalid) {
512 fn_proto->skip = true;
513 }
514
515 buf_appendf(&fn_type->name, ")");
516 if (return_type->id != TypeTableEntryIdVoid) {
517 buf_appendf(&fn_type->name, " %s", buf_ptr(&return_type->name));
518 }
553 fn_type->data.fn.gen_param_count = gen_param_index;
519554
520555 if (fn_proto->skip) {
521556 return;
......@@ -526,12 +561,11 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
526561 fn_type = table_entry->value;
527562 fn_table_entry->type_entry = fn_type;
528563 } else {
529 fn_type->data.fn.raw_type_ref = LLVMFunctionType(return_type->type_ref, gen_param_types, gen_param_count,
530 fn_type->data.fn.is_var_args);
564 fn_type->data.fn.raw_type_ref = LLVMFunctionType(gen_return_type->type_ref,
565 gen_param_types, gen_param_index, fn_type->data.fn.is_var_args);
531566 fn_type->type_ref = LLVMPointerType(fn_type->data.fn.raw_type_ref, 0);
532 param_di_types[0] = return_type->di_type;
533567 fn_type->di_type = LLVMZigCreateSubroutineType(g->dbuilder, import->di_file,
534 param_di_types, gen_param_count + 1, 0);
568 param_di_types, gen_param_index + 1, 0);
535569
536570 import->fn_type_table.put(&fn_type->name, fn_type);
537571 }
......@@ -550,7 +584,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
550584 LLVMSetLinkage(fn_table_entry->fn_value, fn_table_entry->internal_linkage ?
551585 LLVMInternalLinkage : LLVMExternalLinkage);
552586
553 if (return_type->id == TypeTableEntryIdUnreachable) {
587 if (gen_return_type->id == TypeTableEntryIdUnreachable) {
554588 LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNoReturnAttribute);
555589 }
556590 LLVMSetFunctionCallConv(fn_table_entry->fn_value, fn_type->data.fn.calling_convention);
......@@ -3320,7 +3354,13 @@ static TypeTableEntry *analyze_fn_call_raw(CodeGen *g, ImportTableEntry *import,
33203354 analyze_expression(g, import, context, expected_param_type, child);
33213355 }
33223356
3323 return unwrapped_node_type(fn_proto->return_type);
3357 TypeTableEntry *return_type = unwrapped_node_type(fn_proto->return_type);
3358
3359 if (handle_is_ptr(return_type)) {
3360 context->cast_alloca_list.append(node);
3361 }
3362
3363 return return_type;
33243364}
33253365
33263366static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
......@@ -3417,7 +3457,7 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import
34173457
34183458 // function pointer
34193459 if (invoke_type_entry->id == TypeTableEntryIdFn) {
3420 return invoke_type_entry->data.fn.return_type;
3460 return invoke_type_entry->data.fn.src_return_type;
34213461 } else {
34223462 add_node_error(g, fn_ref_expr,
34233463 buf_sprintf("type '%s' not a function", buf_ptr(&invoke_type_entry->name)));
......@@ -3681,6 +3721,7 @@ static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import,
36813721 } else {
36823722 add_node_error(g, node->data.return_expr.expr,
36833723 buf_sprintf("expected error type, got '%s'", buf_ptr(&resolved_type->name)));
3724 return g->builtin_types.entry_invalid;
36843725 }
36853726 }
36863727 case ReturnKindMaybe:
......@@ -4711,6 +4752,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
47114752 return type_entry->id == TypeTableEntryIdStruct ||
47124753 (type_entry->id == TypeTableEntryIdEnum && type_entry->data.enumeration.gen_field_count != 0) ||
47134754 type_entry->id == TypeTableEntryIdMaybe ||
4714 type_entry->id == TypeTableEntryIdArray;
4755 type_entry->id == TypeTableEntryIdArray ||
4756 (type_entry->id == TypeTableEntryIdErrorUnion && type_entry->data.error.child_type->size_in_bits > 0);
47154757}
47164758
src/codegen.cpp+82-30
......@@ -81,11 +81,6 @@ static TypeTableEntry *get_type_for_type_node(AstNode *node) {
8181 return const_val->data.x_type;
8282}
8383
84static bool is_param_decl_type_void(CodeGen *g, AstNode *param_decl_node) {
85 assert(param_decl_node->type == NodeTypeParamDecl);
86 return get_type_for_type_node(param_decl_node->data.param_decl.type)->size_in_bits == 0;
87}
88
8984static void add_debug_source_node(CodeGen *g, AstNode *node) {
9085 if (!g->cur_block_context)
9186 return;
......@@ -424,17 +419,22 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
424419 fn_type = get_expr_type(fn_ref_expr);
425420 }
426421
427 int expected_param_count = fn_type->data.fn.src_param_count;
422 TypeTableEntry *src_return_type = fn_type->data.fn.src_return_type;
423 TypeTableEntry *gen_return_type = fn_type->data.fn.gen_return_type;
424
428425 int fn_call_param_count = node->data.fn_call_expr.params.length;
429 int actual_param_count = fn_call_param_count + (struct_type ? 1 : 0);
426 bool first_arg_ret = handle_is_ptr(src_return_type);
427 int actual_param_count = fn_call_param_count + (struct_type ? 1 : 0) + (first_arg_ret ? 1 : 0);
430428 bool is_var_args = fn_type->data.fn.is_var_args;
431 assert((is_var_args && actual_param_count >= expected_param_count) ||
432 actual_param_count == expected_param_count);
433429
434430 // don't really include void values
435431 LLVMValueRef *gen_param_values = allocate<LLVMValueRef>(actual_param_count);
436432
437433 int gen_param_index = 0;
434 if (first_arg_ret) {
435 gen_param_values[gen_param_index] = node->data.fn_call_expr.tmp_ptr;
436 gen_param_index += 1;
437 }
438438 if (struct_type) {
439439 gen_param_values[gen_param_index] = gen_expr(g, first_param_expr);
440440 gen_param_index += 1;
......@@ -454,8 +454,10 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
454454 LLVMValueRef result = LLVMZigBuildCall(g->builder, fn_val,
455455 gen_param_values, gen_param_index, fn_type->data.fn.calling_convention, "");
456456
457 if (fn_type->data.fn.return_type->id == TypeTableEntryIdUnreachable) {
457 if (gen_return_type->id == TypeTableEntryIdUnreachable) {
458458 return LLVMBuildUnreachable(g->builder);
459 } else if (first_arg_ret) {
460 return node->data.fn_call_expr.tmp_ptr;
459461 } else {
460462 return result;
461463 }
......@@ -1236,21 +1238,60 @@ static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) {
12361238 zig_unreachable();
12371239}
12381240
1241static LLVMValueRef gen_return(CodeGen *g, AstNode *source_node, LLVMValueRef value) {
1242 TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.src_return_type;
1243 if (handle_is_ptr(return_type)) {
1244 assert(g->cur_ret_ptr);
1245 gen_assign_raw(g, source_node, BinOpTypeAssign, g->cur_ret_ptr, value, return_type, return_type);
1246 add_debug_source_node(g, source_node);
1247 return LLVMBuildRetVoid(g->builder);
1248 } else {
1249 add_debug_source_node(g, source_node);
1250 return LLVMBuildRet(g->builder, value);
1251 }
1252}
1253
12391254static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
12401255 assert(node->type == NodeTypeReturnExpr);
12411256 AstNode *param_node = node->data.return_expr.expr;
12421257 assert(param_node);
1258 LLVMValueRef value = gen_expr(g, param_node);
1259 TypeTableEntry *value_type = get_expr_type(param_node);
12431260
12441261 switch (node->data.return_expr.kind) {
12451262 case ReturnKindUnconditional:
1263 return gen_return(g, node, value);
1264 case ReturnKindError:
12461265 {
1247 LLVMValueRef value = gen_expr(g, param_node);
1266 assert(value_type->id == TypeTableEntryIdErrorUnion);
1267 TypeTableEntry *child_type = value_type->data.error.child_type;
12481268
1249 add_debug_source_node(g, node);
1250 return LLVMBuildRet(g->builder, value);
1269 LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ErrReturnYes");
1270 LLVMBasicBlockRef continue_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ErrReturnNo");
1271
1272 LLVMPositionBuilderAtEnd(g->builder, return_block);
1273 if (child_type->size_in_bits > 0) {
1274 zig_panic("TODO write the error tag value to sret");
1275 add_debug_source_node(g, node);
1276 LLVMBuildRetVoid(g->builder);
1277 } else {
1278 add_debug_source_node(g, node);
1279 LLVMBuildRet(g->builder, value);
1280 }
1281
1282 LLVMPositionBuilderAtEnd(g->builder, continue_block);
1283 if (child_type->size_in_bits > 0) {
1284 add_debug_source_node(g, node);
1285 LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, value, 1, "");
1286 if (handle_is_ptr(child_type)) {
1287 return val_ptr;
1288 } else {
1289 return LLVMBuildLoad(g->builder, val_ptr, "");
1290 }
1291 } else {
1292 return nullptr;
1293 }
12511294 }
1252 case ReturnKindError:
1253 zig_panic("TODO");
12541295 case ReturnKindMaybe:
12551296 zig_panic("TODO");
12561297 }
......@@ -1386,13 +1427,8 @@ static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *i
13861427 return_value = gen_expr(g, statement_node);
13871428 }
13881429
1389 if (implicit_return_type) {
1390 add_debug_source_node(g, block_node);
1391 if (implicit_return_type->id == TypeTableEntryIdVoid) {
1392 LLVMBuildRetVoid(g->builder);
1393 } else if (implicit_return_type->id != TypeTableEntryIdUnreachable) {
1394 LLVMBuildRet(g->builder, return_value);
1395 }
1430 if (implicit_return_type && implicit_return_type->id != TypeTableEntryIdUnreachable) {
1431 gen_return(g, block_node, return_value);
13961432 }
13971433
13981434 g->cur_block_context = old_block_context;
......@@ -2257,25 +2293,35 @@ static void do_code_gen(CodeGen *g) {
22572293 assert(proto_node->type == NodeTypeFnProto);
22582294 AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
22592295
2296 if (handle_is_ptr(fn_table_entry->type_entry->data.fn.src_return_type)) {
2297 LLVMValueRef first_arg = LLVMGetParam(fn_table_entry->fn_value, 0);
2298 LLVMAddAttribute(first_arg, LLVMStructRetAttribute);
2299 }
2300
22602301 // set parameter attributes
2261 int gen_param_index = 0;
22622302 for (int param_decl_i = 0; param_decl_i < fn_proto->params.length; param_decl_i += 1) {
22632303 AstNode *param_node = fn_proto->params.at(param_decl_i);
22642304 assert(param_node->type == NodeTypeParamDecl);
2265 if (is_param_decl_type_void(g, param_node))
2305
2306 int gen_index = param_node->data.param_decl.gen_index;
2307
2308 if (gen_index < 0) {
22662309 continue;
2310 }
2311
22672312 AstNode *type_node = param_node->data.param_decl.type;
22682313 TypeTableEntry *param_type = fn_proto_type_from_type_node(g, type_node);
2269 LLVMValueRef argument_val = LLVMGetParam(fn_table_entry->fn_value, gen_param_index);
2314 LLVMValueRef argument_val = LLVMGetParam(fn_table_entry->fn_value, gen_index);
22702315 bool param_is_noalias = param_node->data.param_decl.is_noalias;
22712316 if (param_type->id == TypeTableEntryIdPointer && param_is_noalias) {
22722317 LLVMAddAttribute(argument_val, LLVMNoAliasAttribute);
2273 } else if (param_type->id == TypeTableEntryIdPointer &&
2274 param_type->data.pointer.is_const)
2275 {
2318 }
2319 if (param_type->id == TypeTableEntryIdPointer && param_type->data.pointer.is_const) {
22762320 LLVMAddAttribute(argument_val, LLVMReadOnlyAttribute);
22772321 }
2278 gen_param_index += 1;
2322 if (param_node->data.param_decl.is_byval) {
2323 LLVMAddAttribute(argument_val, LLVMByValAttribute);
2324 }
22792325 }
22802326
22812327 }
......@@ -2287,6 +2333,11 @@ static void do_code_gen(CodeGen *g) {
22872333 AstNode *fn_def_node = fn_table_entry->fn_def_node;
22882334 LLVMValueRef fn = fn_table_entry->fn_value;
22892335 g->cur_fn = fn_table_entry;
2336 if (handle_is_ptr(fn_table_entry->type_entry->data.fn.src_return_type)) {
2337 g->cur_ret_ptr = LLVMGetParam(fn, 0);
2338 } else {
2339 g->cur_ret_ptr = nullptr;
2340 }
22902341
22912342 AstNode *proto_node = fn_table_entry->proto_node;
22922343 assert(proto_node->type == NodeTypeFnProto);
......@@ -2368,8 +2419,9 @@ static void do_code_gen(CodeGen *g) {
23682419 AstNode *param_decl = fn_proto->params.at(param_i);
23692420 assert(param_decl->type == NodeTypeParamDecl);
23702421
2371 if (is_param_decl_type_void(g, param_decl))
2422 if (param_decl->data.param_decl.gen_index < 0) {
23722423 continue;
2424 }
23732425
23742426 VariableTableEntry *variable = param_decl->data.param_decl.variable;
23752427
test/run_tests.cpp+21
......@@ -1233,6 +1233,27 @@ pub fn main(args: [][]u8) %void => {
12331233 print_str("OK\n");
12341234 return;
12351235 }
1236}
1237 )SOURCE", "OK\n");
1238
1239 add_simple_case("return struct byval from function", R"SOURCE(
1240import "std.zig";
1241struct Foo {
1242 x: i32,
1243 y: i32,
1244}
1245fn make_foo() Foo => {
1246 Foo {
1247 .x = 1234,
1248 .y = 5678,
1249 }
1250}
1251pub fn main(args: [][]u8) %void => {
1252 const foo = make_foo();
1253 if (foo.y != 5678) {
1254 print_str("BAD\n");
1255 }
1256 print_str("OK\n");
12361257}
12371258 )SOURCE", "OK\n");
12381259}