| author | |
| committer | |
| log | 633781e31dedaa27d9692d56f6cf073931ca311a |
| tree | 64f05abeb6225a8c45164a073afe02bb2af47ec0 |
| parent | 4e2fa2d15be248c29051a58995e38caa0b1de0a5 |
10 files changed, 610 insertions(+), 248 deletions(-)
CMakeLists.txt+1| ... | @@ -48,6 +48,7 @@ set(ZIG_SOURCES | ... | @@ -48,6 +48,7 @@ set(ZIG_SOURCES |
| 48 | "${CMAKE_SOURCE_DIR}/src/error.cpp" | 48 | "${CMAKE_SOURCE_DIR}/src/error.cpp" |
| 49 | "${CMAKE_SOURCE_DIR}/src/eval.cpp" | 49 | "${CMAKE_SOURCE_DIR}/src/eval.cpp" |
| 50 | "${CMAKE_SOURCE_DIR}/src/ir.cpp" | 50 | "${CMAKE_SOURCE_DIR}/src/ir.cpp" |
| 51 | "${CMAKE_SOURCE_DIR}/src/ir_print.cpp" | ||
| 51 | "${CMAKE_SOURCE_DIR}/src/link.cpp" | 52 | "${CMAKE_SOURCE_DIR}/src/link.cpp" |
| 52 | "${CMAKE_SOURCE_DIR}/src/main.cpp" | 53 | "${CMAKE_SOURCE_DIR}/src/main.cpp" |
| 53 | "${CMAKE_SOURCE_DIR}/src/os.cpp" | 54 | "${CMAKE_SOURCE_DIR}/src/os.cpp" |
src/all_types.hpp+151| ... | @@ -28,6 +28,14 @@ struct BuiltinFnEntry; | ... | @@ -28,6 +28,14 @@ struct BuiltinFnEntry; |
| 28 | struct TypeStructField; | 28 | struct TypeStructField; |
| 29 | struct CodeGen; | 29 | struct CodeGen; |
| 30 | struct ConstExprValue; | 30 | struct ConstExprValue; |
| 31 | struct IrInstruction; | ||
| 32 | struct IrBasicBlock; | ||
| 33 | |||
| 34 | struct IrExecutable { | ||
| 35 | IrBasicBlock **basic_block_list; | ||
| 36 | size_t basic_block_count; | ||
| 37 | size_t next_debug_id; | ||
| 38 | }; | ||
| 31 | 39 | ||
| 32 | enum OutType { | 40 | enum OutType { |
| 33 | OutTypeUnknown, | 41 | OutTypeUnknown, |
| ... | @@ -1105,6 +1113,7 @@ struct FnTableEntry { | ... | @@ -1105,6 +1113,7 @@ struct FnTableEntry { |
| 1105 | AstNode *want_pure_return_type; | 1113 | AstNode *want_pure_return_type; |
| 1106 | FnInline fn_inline; | 1114 | FnInline fn_inline; |
| 1107 | FnAnalState anal_state; | 1115 | FnAnalState anal_state; |
| 1116 | IrExecutable ir_executable; | ||
| 1108 | 1117 | ||
| 1109 | AstNode *fn_no_inline_set_node; | 1118 | AstNode *fn_no_inline_set_node; |
| 1110 | AstNode *fn_export_set_node; | 1119 | AstNode *fn_export_set_node; |
| ... | @@ -1317,6 +1326,8 @@ struct CodeGen { | ... | @@ -1317,6 +1326,8 @@ struct CodeGen { |
| 1317 | ZigList<AstNode *> error_decls; | 1326 | ZigList<AstNode *> error_decls; |
| 1318 | bool generate_error_name_table; | 1327 | bool generate_error_name_table; |
| 1319 | LLVMValueRef err_name_table; | 1328 | LLVMValueRef err_name_table; |
| 1329 | |||
| 1330 | IrInstruction *invalid_instruction; | ||
| 1320 | }; | 1331 | }; |
| 1321 | 1332 | ||
| 1322 | struct VariableTableEntry { | 1333 | struct VariableTableEntry { |
| ... | @@ -1388,5 +1399,145 @@ enum AtomicOrder { | ... | @@ -1388,5 +1399,145 @@ enum AtomicOrder { |
| 1388 | AtomicOrderSeqCst, | 1399 | AtomicOrderSeqCst, |
| 1389 | }; | 1400 | }; |
| 1390 | 1401 | ||
| 1402 | // A basic block contains no branching. Branches send control flow | ||
| 1403 | // to another basic block. | ||
| 1404 | // Phi instructions must be first in a basic block. | ||
| 1405 | // The last instruction in a basic block must be an expression of type unreachable. | ||
| 1406 | struct IrBasicBlock { | ||
| 1407 | IrInstruction *first; | ||
| 1408 | IrInstruction *last; | ||
| 1409 | }; | ||
| 1410 | |||
| 1411 | enum IrInstructionId { | ||
| 1412 | IrInstructionIdInvalid, | ||
| 1413 | IrInstructionIdCondBr, | ||
| 1414 | IrInstructionIdSwitchBr, | ||
| 1415 | IrInstructionIdPhi, | ||
| 1416 | IrInstructionIdBinOp, | ||
| 1417 | IrInstructionIdLoadVar, | ||
| 1418 | IrInstructionIdStoreVar, | ||
| 1419 | IrInstructionIdCall, | ||
| 1420 | IrInstructionIdBuiltinCall, | ||
| 1421 | IrInstructionIdConst, | ||
| 1422 | IrInstructionIdReturn, | ||
| 1423 | }; | ||
| 1424 | |||
| 1425 | struct IrInstruction { | ||
| 1426 | IrInstruction *prev; | ||
| 1427 | IrInstruction *next; | ||
| 1428 | |||
| 1429 | IrInstructionId id; | ||
| 1430 | AstNode *source_node; | ||
| 1431 | ConstExprValue static_value; | ||
| 1432 | TypeTableEntry *type_entry; | ||
| 1433 | size_t debug_id; | ||
| 1434 | LLVMValueRef llvm_value; | ||
| 1435 | }; | ||
| 1436 | |||
| 1437 | struct IrInstructionCondBr { | ||
| 1438 | IrInstruction base; | ||
| 1439 | |||
| 1440 | // If the condition is null, then this is an unconditional branch. | ||
| 1441 | IrInstruction *cond; | ||
| 1442 | IrBasicBlock *dest; | ||
| 1443 | }; | ||
| 1444 | |||
| 1445 | struct IrInstructionSwitchBrCase { | ||
| 1446 | IrInstruction *value; | ||
| 1447 | IrBasicBlock *block; | ||
| 1448 | }; | ||
| 1449 | |||
| 1450 | struct IrInstructionSwitchBr { | ||
| 1451 | IrInstruction base; | ||
| 1452 | |||
| 1453 | IrInstruction *target_value; | ||
| 1454 | IrBasicBlock *else_block; | ||
| 1455 | size_t case_count; | ||
| 1456 | IrInstructionSwitchBrCase *cases; | ||
| 1457 | }; | ||
| 1458 | |||
| 1459 | struct IrInstructionPhi { | ||
| 1460 | IrInstruction base; | ||
| 1461 | |||
| 1462 | size_t incoming_block_count; | ||
| 1463 | IrBasicBlock **incoming_blocks; | ||
| 1464 | IrInstruction **incoming_values; | ||
| 1465 | }; | ||
| 1466 | |||
| 1467 | enum IrBinOp { | ||
| 1468 | IrBinOpInvalid, | ||
| 1469 | IrBinOpBoolOr, | ||
| 1470 | IrBinOpBoolAnd, | ||
| 1471 | IrBinOpCmpEq, | ||
| 1472 | IrBinOpCmpNotEq, | ||
| 1473 | IrBinOpCmpLessThan, | ||
| 1474 | IrBinOpCmpGreaterThan, | ||
| 1475 | IrBinOpCmpLessOrEq, | ||
| 1476 | IrBinOpCmpGreaterOrEq, | ||
| 1477 | IrBinOpBinOr, | ||
| 1478 | IrBinOpBinXor, | ||
| 1479 | IrBinOpBinAnd, | ||
| 1480 | IrBinOpBitShiftLeft, | ||
| 1481 | IrBinOpBitShiftLeftWrap, | ||
| 1482 | IrBinOpBitShiftRight, | ||
| 1483 | IrBinOpAdd, | ||
| 1484 | IrBinOpAddWrap, | ||
| 1485 | IrBinOpSub, | ||
| 1486 | IrBinOpSubWrap, | ||
| 1487 | IrBinOpMult, | ||
| 1488 | IrBinOpMultWrap, | ||
| 1489 | IrBinOpDiv, | ||
| 1490 | IrBinOpMod, | ||
| 1491 | IrBinOpArrayCat, | ||
| 1492 | IrBinOpArrayMult, | ||
| 1493 | }; | ||
| 1494 | |||
| 1495 | struct IrInstructionBinOp { | ||
| 1496 | IrInstruction base; | ||
| 1497 | |||
| 1498 | IrInstruction *op1; | ||
| 1499 | IrBinOp op_id; | ||
| 1500 | IrInstruction *op2; | ||
| 1501 | }; | ||
| 1502 | |||
| 1503 | struct IrInstructionLoadVar { | ||
| 1504 | IrInstruction base; | ||
| 1505 | |||
| 1506 | VariableTableEntry *var; | ||
| 1507 | }; | ||
| 1508 | |||
| 1509 | struct IrInstructionStoreVar { | ||
| 1510 | IrInstruction base; | ||
| 1511 | |||
| 1512 | IrInstruction *value; | ||
| 1513 | VariableTableEntry *var; | ||
| 1514 | }; | ||
| 1515 | |||
| 1516 | struct IrInstructionCall { | ||
| 1517 | IrInstruction base; | ||
| 1518 | |||
| 1519 | IrInstruction *fn; | ||
| 1520 | size_t arg_count; | ||
| 1521 | IrInstruction **args; | ||
| 1522 | }; | ||
| 1523 | |||
| 1524 | struct IrInstructionBuiltinCall { | ||
| 1525 | IrInstruction base; | ||
| 1526 | |||
| 1527 | BuiltinFnId fn_id; | ||
| 1528 | size_t arg_count; | ||
| 1529 | IrInstruction **args; | ||
| 1530 | }; | ||
| 1531 | |||
| 1532 | struct IrInstructionConst { | ||
| 1533 | IrInstruction base; | ||
| 1534 | }; | ||
| 1535 | |||
| 1536 | struct IrInstructionReturn { | ||
| 1537 | IrInstruction base; | ||
| 1538 | |||
| 1539 | IrInstruction *value; | ||
| 1540 | }; | ||
| 1541 | |||
| 1391 | 1542 | ||
| 1392 | #endif | 1543 | #endif |
src/analyze.cpp+10-16| ... | @@ -11,6 +11,7 @@ | ... | @@ -11,6 +11,7 @@ |
| 11 | #include "error.hpp" | 11 | #include "error.hpp" |
| 12 | #include "eval.hpp" | 12 | #include "eval.hpp" |
| 13 | #include "ir.hpp" | 13 | #include "ir.hpp" |
| 14 | #include "ir_print.hpp" | ||
| 14 | #include "os.hpp" | 15 | #include "os.hpp" |
| 15 | #include "parseh.hpp" | 16 | #include "parseh.hpp" |
| 16 | #include "parser.hpp" | 17 | #include "parser.hpp" |
| ... | @@ -54,7 +55,7 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry); | ... | @@ -54,7 +55,7 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry); |
| 54 | static void resolve_use_decl(CodeGen *g, AstNode *node); | 55 | static void resolve_use_decl(CodeGen *g, AstNode *node); |
| 55 | static void preview_use_decl(CodeGen *g, AstNode *node); | 56 | static void preview_use_decl(CodeGen *g, AstNode *node); |
| 56 | 57 | ||
| 57 | static AstNode *first_executing_node(AstNode *node) { | 58 | AstNode *first_executing_node(AstNode *node) { |
| 58 | switch (node->type) { | 59 | switch (node->type) { |
| 59 | case NodeTypeFnCallExpr: | 60 | case NodeTypeFnCallExpr: |
| 60 | return first_executing_node(node->data.fn_call_expr.fn_ref_expr); | 61 | return first_executing_node(node->data.fn_call_expr.fn_ref_expr); |
| ... | @@ -2381,18 +2382,6 @@ static VariableTableEntry *find_variable(CodeGen *g, BlockContext *orig_context, | ... | @@ -2381,18 +2382,6 @@ static VariableTableEntry *find_variable(CodeGen *g, BlockContext *orig_context, |
| 2381 | return nullptr; | 2382 | return nullptr; |
| 2382 | } | 2383 | } |
| 2383 | 2384 | ||
| 2384 | static LabelTableEntry *find_label(CodeGen *g, BlockContext *orig_context, Buf *name) { | ||
| 2385 | BlockContext *context = orig_context; | ||
| 2386 | while (context && context->fn_entry) { | ||
| 2387 | auto entry = context->label_table.maybe_get(name); | ||
| 2388 | if (entry) { | ||
| 2389 | return entry->value; | ||
| 2390 | } | ||
| 2391 | context = context->parent; | ||
| 2392 | } | ||
| 2393 | return nullptr; | ||
| 2394 | } | ||
| 2395 | |||
| 2396 | static TypeEnumField *find_enum_type_field(TypeTableEntry *enum_type, Buf *name) { | 2385 | static TypeEnumField *find_enum_type_field(TypeTableEntry *enum_type, Buf *name) { |
| 2397 | for (uint32_t i = 0; i < enum_type->data.enumeration.src_field_count; i += 1) { | 2386 | for (uint32_t i = 0; i < enum_type->data.enumeration.src_field_count; i += 1) { |
| 2398 | TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i]; | 2387 | TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i]; |
| ... | @@ -7098,14 +7087,19 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { | ... | @@ -7098,14 +7087,19 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { |
| 7098 | buf_sprintf("byvalue types not yet supported on extern function return values")); | 7087 | buf_sprintf("byvalue types not yet supported on extern function return values")); |
| 7099 | } | 7088 | } |
| 7100 | 7089 | ||
| 7101 | IrBasicBlock *entry_basic_block = ir_gen(g, node, expected_type); | 7090 | IrInstruction *result = ir_gen_fn(g, fn_table_entry); |
| 7102 | if (!entry_basic_block) { | 7091 | if (result == g->invalid_instruction) { |
| 7103 | fn_proto_node->data.fn_proto.skip = true; | 7092 | fn_proto_node->data.fn_proto.skip = true; |
| 7104 | fn_table_entry->anal_state = FnAnalStateSkipped; | 7093 | fn_table_entry->anal_state = FnAnalStateSkipped; |
| 7105 | return; | 7094 | return; |
| 7106 | } | 7095 | } |
| 7107 | TypeTableEntry *block_return_type = ir_analyze(g, node, entry_basic_block, expected_type); | 7096 | if (g->verbose) { |
| 7097 | fprintf(stderr, "fn %s {\n", buf_ptr(&fn_table_entry->symbol_name)); | ||
| 7098 | ir_print(stderr, &fn_table_entry->ir_executable, 4); | ||
| 7099 | fprintf(stderr, "}\n"); | ||
| 7100 | } | ||
| 7108 | 7101 | ||
| 7102 | TypeTableEntry *block_return_type = ir_analyze(g, &fn_table_entry->ir_executable, expected_type); | ||
| 7109 | node->data.fn_def.implicit_return_type = block_return_type; | 7103 | node->data.fn_def.implicit_return_type = block_return_type; |
| 7110 | 7104 | ||
| 7111 | fn_table_entry->anal_state = FnAnalStateComplete; | 7105 | fn_table_entry->anal_state = FnAnalStateComplete; |
src/analyze.hpp+2| ... | @@ -43,4 +43,6 @@ uint64_t get_memcpy_align(CodeGen *g, TypeTableEntry *type_entry); | ... | @@ -43,4 +43,6 @@ uint64_t get_memcpy_align(CodeGen *g, TypeTableEntry *type_entry); |
| 43 | ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, | 43 | ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, |
| 44 | Buf *abs_full_path, Buf *src_dirname, Buf *src_basename, Buf *source_code); | 44 | Buf *abs_full_path, Buf *src_dirname, Buf *src_basename, Buf *source_code); |
| 45 | 45 | ||
| 46 | AstNode *first_executing_node(AstNode *node); | ||
| 47 | |||
| 46 | #endif | 48 | #endif |
src/codegen.cpp+59-13| ... | @@ -5,18 +5,18 @@ | ... | @@ -5,18 +5,18 @@ |
| 5 | * See http://opensource.org/licenses/MIT | 5 | * See http://opensource.org/licenses/MIT |
| 6 | */ | 6 | */ |
| 7 | 7 | ||
| 8 | #include "analyze.hpp" | ||
| 9 | #include "ast_render.hpp" | ||
| 8 | #include "codegen.hpp" | 10 | #include "codegen.hpp" |
| 9 | #include "hash_map.hpp" | ||
| 10 | #include "zig_llvm.hpp" | ||
| 11 | #include "os.hpp" | ||
| 12 | #include "config.h" | 11 | #include "config.h" |
| 13 | #include "error.hpp" | ||
| 14 | #include "analyze.hpp" | ||
| 15 | #include "errmsg.hpp" | 12 | #include "errmsg.hpp" |
| 13 | #include "error.hpp" | ||
| 14 | #include "hash_map.hpp" | ||
| 15 | #include "link.hpp" | ||
| 16 | #include "os.hpp" | ||
| 16 | #include "parseh.hpp" | 17 | #include "parseh.hpp" |
| 17 | #include "ast_render.hpp" | ||
| 18 | #include "target.hpp" | 18 | #include "target.hpp" |
| 19 | #include "link.hpp" | 19 | #include "zig_llvm.hpp" |
| 20 | 20 | ||
| 21 | #include <stdio.h> | 21 | #include <stdio.h> |
| 22 | #include <errno.h> | 22 | #include <errno.h> |
| ... | @@ -65,6 +65,8 @@ CodeGen *codegen_create(Buf *root_source_dir, const ZigTarget *target) { | ... | @@ -65,6 +65,8 @@ CodeGen *codegen_create(Buf *root_source_dir, const ZigTarget *target) { |
| 65 | g->is_test_build = false; | 65 | g->is_test_build = false; |
| 66 | g->want_h_file = true; | 66 | g->want_h_file = true; |
| 67 | 67 | ||
| 68 | g->invalid_instruction = allocate<IrInstruction>(1); | ||
| 69 | |||
| 68 | // the error.Ok value | 70 | // the error.Ok value |
| 69 | g->error_decls.append(nullptr); | 71 | g->error_decls.append(nullptr); |
| 70 | 72 | ||
| ... | @@ -235,6 +237,7 @@ static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node, BinOpType b | ... | @@ -235,6 +237,7 @@ static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node, BinOpType b |
| 235 | static LLVMValueRef gen_unwrap_maybe(CodeGen *g, AstNode *node, LLVMValueRef maybe_struct_ref); | 237 | static LLVMValueRef gen_unwrap_maybe(CodeGen *g, AstNode *node, LLVMValueRef maybe_struct_ref); |
| 236 | static LLVMValueRef gen_div(CodeGen *g, AstNode *source_node, LLVMValueRef val1, LLVMValueRef val2, | 238 | static LLVMValueRef gen_div(CodeGen *g, AstNode *source_node, LLVMValueRef val1, LLVMValueRef val2, |
| 237 | TypeTableEntry *type_entry, bool exact); | 239 | TypeTableEntry *type_entry, bool exact); |
| 240 | static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val); | ||
| 238 | 241 | ||
| 239 | static TypeTableEntry *get_type_for_type_node(AstNode *node) { | 242 | static TypeTableEntry *get_type_for_type_node(AstNode *node) { |
| 240 | Expr *expr = get_resolved_expr(node); | 243 | Expr *expr = get_resolved_expr(node); |
| ... | @@ -249,6 +252,10 @@ static void set_debug_source_node(CodeGen *g, AstNode *node) { | ... | @@ -249,6 +252,10 @@ static void set_debug_source_node(CodeGen *g, AstNode *node) { |
| 249 | ZigLLVMSetCurrentDebugLocation(g->builder, node->line + 1, node->column + 1, node->block_context->di_scope); | 252 | ZigLLVMSetCurrentDebugLocation(g->builder, node->line + 1, node->column + 1, node->block_context->di_scope); |
| 250 | } | 253 | } |
| 251 | 254 | ||
| 255 | static void ir_set_debug(CodeGen *g, IrInstruction *instruction) { | ||
| 256 | set_debug_source_node(g, instruction->source_node); | ||
| 257 | } | ||
| 258 | |||
| 252 | static void clear_debug_source_node(CodeGen *g) { | 259 | static void clear_debug_source_node(CodeGen *g) { |
| 253 | ZigLLVMClearCurrentDebugLocation(g->builder); | 260 | ZigLLVMClearCurrentDebugLocation(g->builder); |
| 254 | } | 261 | } |
| ... | @@ -2792,6 +2799,47 @@ static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) { | ... | @@ -2792,6 +2799,47 @@ static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) { |
| 2792 | return nullptr; | 2799 | return nullptr; |
| 2793 | } | 2800 | } |
| 2794 | 2801 | ||
| 2802 | static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) { | ||
| 2803 | ir_set_debug(g, &return_instruction->base); | ||
| 2804 | LLVMBuildRet(g->builder, return_instruction->value->llvm_value); | ||
| 2805 | return nullptr; | ||
| 2806 | } | ||
| 2807 | |||
| 2808 | static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, IrInstruction *instruction) { | ||
| 2809 | switch (instruction->id) { | ||
| 2810 | case IrInstructionIdInvalid: | ||
| 2811 | zig_unreachable(); | ||
| 2812 | case IrInstructionIdConst: | ||
| 2813 | return gen_const_val(g, instruction->type_entry, &instruction->static_value); | ||
| 2814 | case IrInstructionIdReturn: | ||
| 2815 | return ir_render_return(g, executable, (IrInstructionReturn *)instruction); | ||
| 2816 | case IrInstructionIdCondBr: | ||
| 2817 | case IrInstructionIdSwitchBr: | ||
| 2818 | case IrInstructionIdPhi: | ||
| 2819 | case IrInstructionIdBinOp: | ||
| 2820 | case IrInstructionIdLoadVar: | ||
| 2821 | case IrInstructionIdStoreVar: | ||
| 2822 | case IrInstructionIdCall: | ||
| 2823 | case IrInstructionIdBuiltinCall: | ||
| 2824 | zig_panic("TODO render more IR instructions to LLVM"); | ||
| 2825 | } | ||
| 2826 | zig_unreachable(); | ||
| 2827 | } | ||
| 2828 | |||
| 2829 | static void ir_render(CodeGen *g, FnTableEntry *fn_entry) { | ||
| 2830 | assert(fn_entry); | ||
| 2831 | IrExecutable *executable = &fn_entry->ir_executable; | ||
| 2832 | assert(executable->basic_block_count > 0); | ||
| 2833 | for (size_t i = 0; i < executable->basic_block_count; i += 1) { | ||
| 2834 | IrBasicBlock *current_block = executable->basic_block_list[i]; | ||
| 2835 | for (IrInstruction *instruction = current_block->first; instruction != nullptr; | ||
| 2836 | instruction = instruction->next) | ||
| 2837 | { | ||
| 2838 | instruction->llvm_value = ir_render_instruction(g, executable, instruction); | ||
| 2839 | } | ||
| 2840 | } | ||
| 2841 | } | ||
| 2842 | |||
| 2795 | static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *implicit_return_type) { | 2843 | static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *implicit_return_type) { |
| 2796 | assert(block_node->type == NodeTypeBlock); | 2844 | assert(block_node->type == NodeTypeBlock); |
| 2797 | 2845 | ||
| ... | @@ -3836,6 +3884,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstE | ... | @@ -3836,6 +3884,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstE |
| 3836 | return LLVMConstStruct(fields, 2, false); | 3884 | return LLVMConstStruct(fields, 2, false); |
| 3837 | } | 3885 | } |
| 3838 | } | 3886 | } |
| 3887 | case TypeTableEntryIdVoid: | ||
| 3888 | return nullptr; | ||
| 3839 | case TypeTableEntryIdInvalid: | 3889 | case TypeTableEntryIdInvalid: |
| 3840 | case TypeTableEntryIdMetaType: | 3890 | case TypeTableEntryIdMetaType: |
| 3841 | case TypeTableEntryIdUnreachable: | 3891 | case TypeTableEntryIdUnreachable: |
| ... | @@ -3843,7 +3893,6 @@ static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstE | ... | @@ -3843,7 +3893,6 @@ static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstE |
| 3843 | case TypeTableEntryIdNumLitInt: | 3893 | case TypeTableEntryIdNumLitInt: |
| 3844 | case TypeTableEntryIdUndefLit: | 3894 | case TypeTableEntryIdUndefLit: |
| 3845 | case TypeTableEntryIdNullLit: | 3895 | case TypeTableEntryIdNullLit: |
| 3846 | case TypeTableEntryIdVoid: | ||
| 3847 | case TypeTableEntryIdNamespace: | 3896 | case TypeTableEntryIdNamespace: |
| 3848 | case TypeTableEntryIdBlock: | 3897 | case TypeTableEntryIdBlock: |
| 3849 | case TypeTableEntryIdGenericFn: | 3898 | case TypeTableEntryIdGenericFn: |
| ... | @@ -4199,7 +4248,6 @@ static void do_code_gen(CodeGen *g) { | ... | @@ -4199,7 +4248,6 @@ static void do_code_gen(CodeGen *g) { |
| 4199 | } | 4248 | } |
| 4200 | 4249 | ||
| 4201 | ImportTableEntry *import = fn_table_entry->import_entry; | 4250 | ImportTableEntry *import = fn_table_entry->import_entry; |
| 4202 | AstNode *fn_def_node = fn_table_entry->fn_def_node; | ||
| 4203 | LLVMValueRef fn = fn_table_entry->fn_value; | 4251 | LLVMValueRef fn = fn_table_entry->fn_value; |
| 4204 | g->cur_fn = fn_table_entry; | 4252 | g->cur_fn = fn_table_entry; |
| 4205 | if (handle_is_ptr(fn_table_entry->type_entry->data.fn.fn_type_id.return_type)) { | 4253 | if (handle_is_ptr(fn_table_entry->type_entry->data.fn.fn_type_id.return_type)) { |
| ... | @@ -4307,9 +4355,7 @@ static void do_code_gen(CodeGen *g) { | ... | @@ -4307,9 +4355,7 @@ static void do_code_gen(CodeGen *g) { |
| 4307 | gen_var_debug_decl(g, variable); | 4355 | gen_var_debug_decl(g, variable); |
| 4308 | } | 4356 | } |
| 4309 | 4357 | ||
| 4310 | 4358 | ir_render(g, fn_table_entry); | |
| 4311 | TypeTableEntry *implicit_return_type = fn_def_node->data.fn_def.implicit_return_type; | ||
| 4312 | gen_block(g, fn_def_node->data.fn_def.body, implicit_return_type); | ||
| 4313 | 4359 | ||
| 4314 | } | 4360 | } |
| 4315 | assert(!g->errors.length); | 4361 | assert(!g->errors.length); |
| ... | @@ -4967,7 +5013,6 @@ static void init(CodeGen *g, Buf *source_path) { | ... | @@ -4967,7 +5013,6 @@ static void init(CodeGen *g, Buf *source_path) { |
| 4967 | 5013 | ||
| 4968 | define_builtin_types(g); | 5014 | define_builtin_types(g); |
| 4969 | define_builtin_fns(g); | 5015 | define_builtin_fns(g); |
| 4970 | |||
| 4971 | } | 5016 | } |
| 4972 | 5017 | ||
| 4973 | void codegen_parseh(CodeGen *g, Buf *src_dirname, Buf *src_basename, Buf *source_code) { | 5018 | void codegen_parseh(CodeGen *g, Buf *src_dirname, Buf *src_basename, Buf *source_code) { |
| ... | @@ -5078,6 +5123,7 @@ void codegen_add_root_code(CodeGen *g, Buf *src_dir, Buf *src_basename, Buf *sou | ... | @@ -5078,6 +5123,7 @@ void codegen_add_root_code(CodeGen *g, Buf *src_dir, Buf *src_basename, Buf *sou |
| 5078 | if (g->verbose) { | 5123 | if (g->verbose) { |
| 5079 | fprintf(stderr, "\nCode Generation:\n"); | 5124 | fprintf(stderr, "\nCode Generation:\n"); |
| 5080 | fprintf(stderr, "------------------\n"); | 5125 | fprintf(stderr, "------------------\n"); |
| 5126 | |||
| 5081 | } | 5127 | } |
| 5082 | 5128 | ||
| 5083 | do_code_gen(g); | 5129 | do_code_gen(g); |
src/ir.cpp+262-69| ... | @@ -1,44 +1,117 @@ | ... | @@ -1,44 +1,117 @@ |
| 1 | #include "analyze.hpp" | 1 | #include "analyze.hpp" |
| 2 | #include "ir.hpp" | 2 | #include "ir.hpp" |
| 3 | 3 | #include "error.hpp" | |
| 4 | static IrInstruction *ir_gen_node(IrGen *ir, AstNode *node, BlockContext *block_context); | ||
| 5 | |||
| 6 | static const IrInstruction invalid_instruction_data; | ||
| 7 | static const IrInstruction *invalid_instruction = &invalid_instruction_data; | ||
| 8 | |||
| 9 | static const IrInstruction void_instruction_data; | ||
| 10 | static const IrInstruction *void_instruction = &void_instruction_data; | ||
| 11 | 4 | ||
| 12 | struct IrGen { | 5 | struct IrGen { |
| 13 | CodeGen *codegen; | 6 | CodeGen *codegen; |
| 14 | AstNode *fn_def_node; | 7 | AstNode *node; |
| 15 | IrBasicBlock *current_basic_block; | 8 | IrBasicBlock *current_basic_block; |
| 9 | IrExecutable *exec; | ||
| 16 | }; | 10 | }; |
| 17 | 11 | ||
| 18 | static IrInstruction *ir_build_return(Ir *ir, AstNode *source_node, IrInstruction *return_value) { | 12 | static IrInstruction *ir_gen_node(IrGen *ir, AstNode *node, BlockContext *block_context); |
| 19 | IrInstruction *instructon = allocate<IrInstructionReturn>(1); | ||
| 20 | instruction->base.id = IrInstructionIdReturn; | ||
| 21 | instruction->base.source_node = source_node; | ||
| 22 | instruction->base.type_entry = ir->codegen->builtin_types.entry_unreachable; | ||
| 23 | ir->current_basic_block->instructions->append(instruction); | ||
| 24 | return instructon; | ||
| 25 | } | ||
| 26 | 13 | ||
| 27 | static size_t get_conditional_defer_count(BlockContext *inner_block, BlockContext *outer_block) { | 14 | static void ir_instruction_append(IrBasicBlock *basic_block, IrInstruction *instruction) { |
| 28 | size_t result = 0; | 15 | if (!basic_block->last) { |
| 29 | while (inner_block != outer_block) { | 16 | basic_block->first = instruction; |
| 30 | if (inner_block->node->type == NodeTypeDefer && | 17 | basic_block->last = instruction; |
| 31 | (inner_block->node->data.defer.kind == ReturnKindError || | 18 | instruction->prev = nullptr; |
| 32 | inner_block->node->data.defer.kind == ReturnKindMaybe)) | 19 | instruction->next = nullptr; |
| 33 | { | 20 | } else { |
| 34 | result += 1; | 21 | basic_block->last->next = instruction; |
| 35 | } | 22 | instruction->prev = basic_block->last; |
| 36 | inner_block = inner_block->parent; | 23 | instruction->next = nullptr; |
| 24 | basic_block->last = instruction; | ||
| 37 | } | 25 | } |
| 26 | } | ||
| 27 | |||
| 28 | static size_t exec_next_debug_id(IrGen *ir) { | ||
| 29 | size_t result = ir->exec->next_debug_id; | ||
| 30 | ir->exec->next_debug_id += 1; | ||
| 38 | return result; | 31 | return result; |
| 39 | } | 32 | } |
| 40 | 33 | ||
| 41 | static void ir_gen_defers_for_block(Ir *ir, BlockContext *inner_block, BlockContext *outer_block, | 34 | static constexpr IrInstructionId ir_instruction_id(IrInstructionCondBr *) { |
| 35 | return IrInstructionIdCondBr; | ||
| 36 | } | ||
| 37 | |||
| 38 | static constexpr IrInstructionId ir_instruction_id(IrInstructionSwitchBr *) { | ||
| 39 | return IrInstructionIdSwitchBr; | ||
| 40 | } | ||
| 41 | |||
| 42 | static constexpr IrInstructionId ir_instruction_id(IrInstructionPhi *) { | ||
| 43 | return IrInstructionIdPhi; | ||
| 44 | } | ||
| 45 | |||
| 46 | static constexpr IrInstructionId ir_instruction_id(IrInstructionBinOp *) { | ||
| 47 | return IrInstructionIdBinOp; | ||
| 48 | } | ||
| 49 | |||
| 50 | static constexpr IrInstructionId ir_instruction_id(IrInstructionLoadVar *) { | ||
| 51 | return IrInstructionIdLoadVar; | ||
| 52 | } | ||
| 53 | |||
| 54 | static constexpr IrInstructionId ir_instruction_id(IrInstructionStoreVar *) { | ||
| 55 | return IrInstructionIdStoreVar; | ||
| 56 | } | ||
| 57 | |||
| 58 | static constexpr IrInstructionId ir_instruction_id(IrInstructionCall *) { | ||
| 59 | return IrInstructionIdCall; | ||
| 60 | } | ||
| 61 | |||
| 62 | static constexpr IrInstructionId ir_instruction_id(IrInstructionBuiltinCall *) { | ||
| 63 | return IrInstructionIdBuiltinCall; | ||
| 64 | } | ||
| 65 | |||
| 66 | static constexpr IrInstructionId ir_instruction_id(IrInstructionConst *) { | ||
| 67 | return IrInstructionIdConst; | ||
| 68 | } | ||
| 69 | |||
| 70 | static constexpr IrInstructionId ir_instruction_id(IrInstructionReturn *) { | ||
| 71 | return IrInstructionIdReturn; | ||
| 72 | } | ||
| 73 | |||
| 74 | template<typename T> | ||
| 75 | static T *ir_build_instruction(IrGen *ir, AstNode *source_node) { | ||
| 76 | T *special_instruction = allocate<T>(1); | ||
| 77 | special_instruction->base.id = ir_instruction_id(special_instruction); | ||
| 78 | special_instruction->base.source_node = source_node; | ||
| 79 | special_instruction->base.type_entry = ir->codegen->builtin_types.entry_unreachable; | ||
| 80 | special_instruction->base.debug_id = exec_next_debug_id(ir); | ||
| 81 | ir_instruction_append(ir->current_basic_block, &special_instruction->base); | ||
| 82 | return special_instruction; | ||
| 83 | } | ||
| 84 | |||
| 85 | static IrInstruction *ir_build_return(IrGen *ir, AstNode *source_node, IrInstruction *return_value) { | ||
| 86 | IrInstructionReturn *return_instruction = ir_build_instruction<IrInstructionReturn>(ir, source_node); | ||
| 87 | return_instruction->base.type_entry = ir->codegen->builtin_types.entry_unreachable; | ||
| 88 | return_instruction->base.static_value.ok = true; | ||
| 89 | return_instruction->value = return_value; | ||
| 90 | return &return_instruction->base; | ||
| 91 | } | ||
| 92 | |||
| 93 | static IrInstruction *ir_build_void(IrGen *ir, AstNode *source_node) { | ||
| 94 | IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(ir, source_node); | ||
| 95 | const_instruction->base.type_entry = ir->codegen->builtin_types.entry_void; | ||
| 96 | const_instruction->base.static_value.ok = true; | ||
| 97 | return &const_instruction->base; | ||
| 98 | } | ||
| 99 | |||
| 100 | //static size_t get_conditional_defer_count(BlockContext *inner_block, BlockContext *outer_block) { | ||
| 101 | // size_t result = 0; | ||
| 102 | // while (inner_block != outer_block) { | ||
| 103 | // if (inner_block->node->type == NodeTypeDefer && | ||
| 104 | // (inner_block->node->data.defer.kind == ReturnKindError || | ||
| 105 | // inner_block->node->data.defer.kind == ReturnKindMaybe)) | ||
| 106 | // { | ||
| 107 | // result += 1; | ||
| 108 | // } | ||
| 109 | // inner_block = inner_block->parent; | ||
| 110 | // } | ||
| 111 | // return result; | ||
| 112 | //} | ||
| 113 | |||
| 114 | static void ir_gen_defers_for_block(IrGen *ir, BlockContext *inner_block, BlockContext *outer_block, | ||
| 42 | bool gen_error_defers, bool gen_maybe_defers) | 115 | bool gen_error_defers, bool gen_maybe_defers) |
| 43 | { | 116 | { |
| 44 | while (inner_block != outer_block) { | 117 | while (inner_block != outer_block) { |
| ... | @@ -54,42 +127,44 @@ static void ir_gen_defers_for_block(Ir *ir, BlockContext *inner_block, BlockCont | ... | @@ -54,42 +127,44 @@ static void ir_gen_defers_for_block(Ir *ir, BlockContext *inner_block, BlockCont |
| 54 | } | 127 | } |
| 55 | } | 128 | } |
| 56 | 129 | ||
| 57 | static IrInstruction *ir_gen_return(Ir *ir, AstNode *source_node, IrInstruction *value, ReturnKnowledge rk) { | 130 | //static IrInstruction *ir_gen_return(IrGen *ir, AstNode *source_node, IrInstruction *value, ReturnKnowledge rk) { |
| 58 | BlockContext *defer_inner_block = source_node->block_context; | 131 | // BlockContext *defer_inner_block = source_node->block_context; |
| 59 | BlockContext *defer_outer_block = ir->fn_def_node->block_context; | 132 | // BlockContext *defer_outer_block = ir->node->block_context; |
| 60 | if (rk == ReturnKnowledgeUnknown) { | 133 | // if (rk == ReturnKnowledgeUnknown) { |
| 61 | if (get_conditional_defer_count(defer_inner_block, defer_outer_block) > 0) { | 134 | // if (get_conditional_defer_count(defer_inner_block, defer_outer_block) > 0) { |
| 62 | // generate branching code that checks the return value and generates defers | 135 | // // generate branching code that checks the return value and generates defers |
| 63 | // if the return value is error | 136 | // // if the return value is error |
| 64 | zig_panic("TODO"); | 137 | // zig_panic("TODO"); |
| 65 | } | 138 | // } |
| 66 | } else if (rk != ReturnKnowledgeSkipDefers) { | 139 | // } else if (rk != ReturnKnowledgeSkipDefers) { |
| 67 | ir_gen_defers_for_block(g, defer_inner_block, defer_outer_block, | 140 | // ir_gen_defers_for_block(ir, defer_inner_block, defer_outer_block, |
| 68 | rk == ReturnKnowledgeKnownError, rk == ReturnKnowledgeKnownNull); | 141 | // rk == ReturnKnowledgeKnownError, rk == ReturnKnowledgeKnownNull); |
| 69 | } | 142 | // } |
| 70 | 143 | // | |
| 71 | ir_build_return(ir, source_node, value); | 144 | // return ir_build_return(ir, source_node, value); |
| 72 | return void_instruction; | 145 | //} |
| 73 | } | ||
| 74 | 146 | ||
| 75 | static IrInstruction *ir_gen_block(IrGen *ir, AstNode *block_node, TypeTableEntry *implicit_return_type) { | 147 | static IrInstruction *ir_gen_block(IrGen *ir, AstNode *block_node) { |
| 76 | assert(block_node->type == NodeTypeBlock); | 148 | assert(block_node->type == NodeTypeBlock); |
| 77 | 149 | ||
| 78 | BlockContext *parent_context = block_node->context; | 150 | BlockContext *parent_context = block_node->block_context; |
| 79 | BlockContext *outer_block_context = new_block_context(block_node, parent_context); | 151 | BlockContext *outer_block_context = new_block_context(block_node, parent_context); |
| 80 | BlockContext *child_context = outer_block_context; | 152 | BlockContext *child_context = outer_block_context; |
| 81 | 153 | ||
| 82 | IrInstruction *return_value = nullptr; | 154 | IrInstruction *return_value = nullptr; |
| 83 | for (size_t i = 0; i < block_node->data.block.statements.length; i += 1) { | 155 | for (size_t i = 0; i < block_node->data.block.statements.length; i += 1) { |
| 84 | AstNode *statement_node = block_node->data.block.statements.at(i); | 156 | AstNode *statement_node = block_node->data.block.statements.at(i); |
| 85 | return_value = ir_gen_node(g, statement_node, child_context); | 157 | return_value = ir_gen_node(ir, statement_node, child_context); |
| 86 | if (statement_node->type == NodeTypeDefer && return_value != invalid_instruction) { | 158 | if (statement_node->type == NodeTypeDefer && return_value != ir->codegen->invalid_instruction) { |
| 87 | // defer starts a new block context | 159 | // defer starts a new block context |
| 88 | child_context = statement_node->data.defer.child_block; | 160 | child_context = statement_node->data.defer.child_block; |
| 89 | assert(child_context); | 161 | assert(child_context); |
| 90 | } | 162 | } |
| 91 | } | 163 | } |
| 92 | 164 | ||
| 165 | if (!return_value) | ||
| 166 | return_value = ir_build_void(ir, block_node); | ||
| 167 | |||
| 93 | ir_gen_defers_for_block(ir, child_context, outer_block_context, false, false); | 168 | ir_gen_defers_for_block(ir, child_context, outer_block_context, false, false); |
| 94 | 169 | ||
| 95 | return return_value; | 170 | return return_value; |
| ... | @@ -100,7 +175,7 @@ static IrInstruction *ir_gen_node(IrGen *ir, AstNode *node, BlockContext *block_ | ... | @@ -100,7 +175,7 @@ static IrInstruction *ir_gen_node(IrGen *ir, AstNode *node, BlockContext *block_ |
| 100 | 175 | ||
| 101 | switch (node->type) { | 176 | switch (node->type) { |
| 102 | case NodeTypeBlock: | 177 | case NodeTypeBlock: |
| 103 | return ir_gen_block(ir, node, nullptr); | 178 | return ir_gen_block(ir, node); |
| 104 | case NodeTypeBinOpExpr: | 179 | case NodeTypeBinOpExpr: |
| 105 | case NodeTypeUnwrapErrorExpr: | 180 | case NodeTypeUnwrapErrorExpr: |
| 106 | case NodeTypeReturnExpr: | 181 | case NodeTypeReturnExpr: |
| ... | @@ -153,23 +228,53 @@ static IrInstruction *ir_gen_node(IrGen *ir, AstNode *node, BlockContext *block_ | ... | @@ -153,23 +228,53 @@ static IrInstruction *ir_gen_node(IrGen *ir, AstNode *node, BlockContext *block_ |
| 153 | zig_unreachable(); | 228 | zig_unreachable(); |
| 154 | } | 229 | } |
| 155 | 230 | ||
| 156 | IrBasicBlock *ir_gen(CodeGen *g, AstNode *fn_def_node, TypeTableEntry *return_type) { | 231 | static IrInstruction *ir_gen_add_return(CodeGen *g, AstNode *node, BlockContext *scope, |
| 157 | assert(fn_def_node->type == NodeTypeFnDef); | 232 | IrExecutable *ir_executable, bool add_return) |
| 158 | assert(fn_def_node->data.fn_def.block_context); | 233 | { |
| 159 | assert(fn_def_node->owner); | 234 | assert(node->owner); |
| 160 | assert(return_type); | ||
| 161 | assert(return_type->id != TypeTableEntryIdInvalid); | ||
| 162 | 235 | ||
| 163 | IrGen ir_gen = {0}; | 236 | IrGen ir_gen = {0}; |
| 164 | IrGen *ir = &ir_gen; | 237 | IrGen *ir = &ir_gen; |
| 165 | 238 | ||
| 239 | ir->codegen = g; | ||
| 240 | ir->node = node; | ||
| 241 | ir->exec = ir_executable; | ||
| 242 | |||
| 243 | ir->exec->basic_block_list = allocate<IrBasicBlock*>(1); | ||
| 244 | ir->exec->basic_block_count = 1; | ||
| 245 | |||
| 166 | IrBasicBlock *entry_basic_block = allocate<IrBasicBlock>(1); | 246 | IrBasicBlock *entry_basic_block = allocate<IrBasicBlock>(1); |
| 167 | ir->current_basic_block = entry_basic_block; | 247 | ir->current_basic_block = entry_basic_block; |
| 248 | ir->exec->basic_block_list[0] = entry_basic_block; | ||
| 249 | |||
| 250 | IrInstruction *result = ir_gen_node(ir, node, scope); | ||
| 251 | assert(result); | ||
| 252 | |||
| 253 | if (result == g->invalid_instruction) | ||
| 254 | return result; | ||
| 255 | |||
| 256 | if (add_return) | ||
| 257 | return ir_build_return(ir, result->source_node, result); | ||
| 258 | |||
| 259 | return result; | ||
| 260 | } | ||
| 261 | |||
| 262 | IrInstruction *ir_gen(CodeGen *g, AstNode *node, BlockContext *scope, IrExecutable *ir_executable) { | ||
| 263 | return ir_gen_add_return(g, node, scope, ir_executable, false); | ||
| 264 | } | ||
| 265 | |||
| 266 | IrInstruction *ir_gen_fn(CodeGen *g, FnTableEntry *fn_entry) { | ||
| 267 | assert(fn_entry); | ||
| 268 | |||
| 269 | IrExecutable *ir_executable = &fn_entry->ir_executable; | ||
| 270 | AstNode *fn_def_node = fn_entry->fn_def_node; | ||
| 271 | assert(fn_def_node->type == NodeTypeFnDef); | ||
| 168 | 272 | ||
| 169 | AstNode *body_node = fn_def_node->data.fn_def.body; | 273 | AstNode *body_node = fn_def_node->data.fn_def.body; |
| 170 | body_node->block_context = fn_def_node->data.fn_def.block_context; | 274 | BlockContext *scope = fn_def_node->data.fn_def.block_context; |
| 171 | IrInstruction *instruction = ir_gen_block(ir, body_node, return_type); | 275 | |
| 172 | return (instructon == invalid_instruction) ? nullptr : entry_basic_block; | 276 | bool add_return_yes = true; |
| 277 | return ir_gen_add_return(g, body_node, scope, ir_executable, add_return_yes); | ||
| 173 | } | 278 | } |
| 174 | 279 | ||
| 175 | /* | 280 | /* |
| ... | @@ -205,22 +310,110 @@ static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *no | ... | @@ -205,22 +310,110 @@ static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *no |
| 205 | } | 310 | } |
| 206 | */ | 311 | */ |
| 207 | 312 | ||
| 313 | //static LabelTableEntry *find_label(CodeGen *g, BlockContext *orig_context, Buf *name) { | ||
| 314 | // BlockContext *context = orig_context; | ||
| 315 | // while (context && context->fn_entry) { | ||
| 316 | // auto entry = context->label_table.maybe_get(name); | ||
| 317 | // if (entry) { | ||
| 318 | // return entry->value; | ||
| 319 | // } | ||
| 320 | // context = context->parent; | ||
| 321 | // } | ||
| 322 | // return nullptr; | ||
| 323 | //} | ||
| 324 | |||
| 325 | static IrInstruction *ir_get_casted_instruction(CodeGen *g, IrInstruction *instruction, | ||
| 326 | TypeTableEntry *expected_type) | ||
| 327 | { | ||
| 328 | assert(instruction); | ||
| 329 | assert(instruction != g->invalid_instruction); | ||
| 330 | assert(!expected_type || expected_type->id != TypeTableEntryIdInvalid); | ||
| 331 | assert(instruction->type_entry); | ||
| 332 | assert(instruction->type_entry->id != TypeTableEntryIdInvalid); | ||
| 333 | if (expected_type == nullptr) | ||
| 334 | return instruction; // anything will do | ||
| 335 | if (expected_type == instruction->type_entry) | ||
| 336 | return instruction; // match | ||
| 337 | if (instruction->type_entry->id == TypeTableEntryIdUnreachable) | ||
| 338 | return instruction; | ||
| 339 | |||
| 340 | zig_panic("TODO implicit cast instruction"); | ||
| 341 | } | ||
| 342 | |||
| 343 | static TypeTableEntry *ir_analyze_instruction_return(CodeGen *g, IrInstructionReturn *return_instruction) { | ||
| 344 | AstNode *source_node = return_instruction->base.source_node; | ||
| 345 | BlockContext *scope = source_node->block_context; | ||
| 346 | if (!scope->fn_entry) { | ||
| 347 | add_node_error(g, source_node, buf_sprintf("return expression outside function definition")); | ||
| 348 | return g->builtin_types.entry_invalid; | ||
| 349 | } | ||
| 208 | 350 | ||
| 209 | TypeTableEntry *ir_analyze(CodeGen *g, AstNode *fn_def_node, IrBasicBlock *entry_basic_block, | 351 | TypeTableEntry *expected_return_type = scope->fn_entry->type_entry->data.fn.fn_type_id.return_type; |
| 352 | if (expected_return_type->id == TypeTableEntryIdVoid && !return_instruction->value) { | ||
| 353 | return g->builtin_types.entry_unreachable; | ||
| 354 | } | ||
| 355 | |||
| 356 | return_instruction->value = ir_get_casted_instruction(g, return_instruction->value, expected_return_type); | ||
| 357 | if (return_instruction->value == g->invalid_instruction) { | ||
| 358 | return g->builtin_types.entry_invalid; | ||
| 359 | } | ||
| 360 | return g->builtin_types.entry_unreachable; | ||
| 361 | } | ||
| 362 | |||
| 363 | static TypeTableEntry *ir_analyze_instruction_const(CodeGen *g, IrInstructionConst *const_instruction) { | ||
| 364 | return const_instruction->base.type_entry; | ||
| 365 | } | ||
| 366 | |||
| 367 | static TypeTableEntry *ir_analyze_instruction_nocast(CodeGen *g, IrInstruction *instruction) { | ||
| 368 | switch (instruction->id) { | ||
| 369 | case IrInstructionIdInvalid: | ||
| 370 | zig_unreachable(); | ||
| 371 | case IrInstructionIdReturn: | ||
| 372 | return ir_analyze_instruction_return(g, (IrInstructionReturn *)instruction); | ||
| 373 | case IrInstructionIdConst: | ||
| 374 | return ir_analyze_instruction_const(g, (IrInstructionConst *)instruction); | ||
| 375 | case IrInstructionIdCondBr: | ||
| 376 | case IrInstructionIdSwitchBr: | ||
| 377 | case IrInstructionIdPhi: | ||
| 378 | case IrInstructionIdBinOp: | ||
| 379 | case IrInstructionIdLoadVar: | ||
| 380 | case IrInstructionIdStoreVar: | ||
| 381 | case IrInstructionIdCall: | ||
| 382 | case IrInstructionIdBuiltinCall: | ||
| 383 | zig_panic("TODO analyze more instructions"); | ||
| 384 | } | ||
| 385 | zig_unreachable(); | ||
| 386 | } | ||
| 387 | |||
| 388 | static TypeTableEntry *ir_analyze_instruction(CodeGen *g, IrInstruction *instruction, | ||
| 210 | TypeTableEntry *expected_type) | 389 | TypeTableEntry *expected_type) |
| 211 | { | 390 | { |
| 391 | TypeTableEntry *instruction_type = ir_analyze_instruction_nocast(g, instruction); | ||
| 392 | instruction->type_entry = instruction_type; | ||
| 393 | |||
| 394 | IrInstruction *casted_instruction = ir_get_casted_instruction(g, instruction, expected_type); | ||
| 395 | return casted_instruction->type_entry; | ||
| 396 | } | ||
| 397 | |||
| 398 | TypeTableEntry *ir_analyze(CodeGen *g, IrExecutable *executable, TypeTableEntry *expected_type) { | ||
| 212 | TypeTableEntry *return_type = g->builtin_types.entry_void; | 399 | TypeTableEntry *return_type = g->builtin_types.entry_void; |
| 213 | 400 | ||
| 214 | for (size_t i = 0; i < entry_basic_block->instructions.length; i += 1) { | 401 | for (size_t i = 0; i < executable->basic_block_count; i += 1) { |
| 215 | IrInstruction *instruction = entry_basic_block->instructions.at(i); | 402 | IrBasicBlock *current_block = executable->basic_block_list[i]; |
| 216 | 403 | ||
| 217 | if (return_type->id == TypeTableEntryIdUnreachable) { | 404 | for (IrInstruction *instruction = current_block->first; instruction != nullptr; |
| 218 | add_node_error(g, first_executing_node(instruction->source_node), | 405 | instruction = instruction->next) |
| 219 | buf_sprintf("unreachable code")); | 406 | { |
| 220 | break; | 407 | if (return_type->id == TypeTableEntryIdUnreachable) { |
| 408 | add_node_error(g, first_executing_node(instruction->source_node), | ||
| 409 | buf_sprintf("unreachable code")); | ||
| 410 | break; | ||
| 411 | } | ||
| 412 | bool is_last = (instruction == current_block->last); | ||
| 413 | TypeTableEntry *passed_expected_type = is_last ? expected_type : nullptr; | ||
| 414 | return_type = ir_analyze_instruction(g, instruction, passed_expected_type); | ||
| 221 | } | 415 | } |
| 222 | bool is_last = (i == entry_basic_block->instructions.length - 1); | ||
| 223 | TypeTableEntry *passed_expected_type = is_last ? expected_type : nullptr; | ||
| 224 | return_type = ir_analyze_instruction(g, instruction, passed_expected_type, child); | ||
| 225 | } | 416 | } |
| 417 | |||
| 418 | return return_type; | ||
| 226 | } | 419 | } |
src/ir.hpp+3-138| ... | @@ -10,144 +10,9 @@ | ... | @@ -10,144 +10,9 @@ |
| 10 | 10 | ||
| 11 | #include "all_types.hpp" | 11 | #include "all_types.hpp" |
| 12 | 12 | ||
| 13 | struct IrInstruction; | 13 | IrInstruction *ir_gen(CodeGen *g, AstNode *node, BlockContext *scope, IrExecutable *ir_executable); |
| 14 | IrInstruction *ir_gen_fn(CodeGen *g, FnTableEntry *fn_entry); | ||
| 14 | 15 | ||
| 15 | // A basic block contains no branching. Branches send control flow | 16 | TypeTableEntry *ir_analyze(CodeGen *g, IrExecutable *executable, TypeTableEntry *expected_type); |
| 16 | // to another basic block. | ||
| 17 | // Phi instructions must be first in a basic block. | ||
| 18 | // The last instruction in a basic block must be an expression of type unreachable. | ||
| 19 | struct IrBasicBlock { | ||
| 20 | ZigList<IrInstruction *> instructions; | ||
| 21 | }; | ||
| 22 | |||
| 23 | enum IrInstructionId { | ||
| 24 | IrInstructionIdCondBr, | ||
| 25 | IrInstructionIdSwitchBr, | ||
| 26 | IrInstructionIdPhi, | ||
| 27 | IrInstructionIdAdd, | ||
| 28 | IrInstructionIdBinOp, | ||
| 29 | IrInstructionIdLoadVar, | ||
| 30 | IrInstructionIdStoreVar, | ||
| 31 | IrInstructionIdCall, | ||
| 32 | IrInstructionIdBuiltinCall, | ||
| 33 | IrInstructionIdConst, | ||
| 34 | IrInstructionIdReturn, | ||
| 35 | }; | ||
| 36 | |||
| 37 | struct IrInstruction { | ||
| 38 | IrInstructionId id; | ||
| 39 | AstNode *source_node; | ||
| 40 | ConstExprValue static_value; | ||
| 41 | TypeTableEntry *type_entry; | ||
| 42 | }; | ||
| 43 | |||
| 44 | struct IrInstructionCondBr { | ||
| 45 | IrInstruction base; | ||
| 46 | |||
| 47 | // If the condition is null, then this is an unconditional branch. | ||
| 48 | IrInstruction *cond; | ||
| 49 | IrBasicBlock *dest; | ||
| 50 | }; | ||
| 51 | |||
| 52 | struct IrInstructionSwitchBrCase { | ||
| 53 | IrInstruction *value; | ||
| 54 | IrBasicBlock *block; | ||
| 55 | }; | ||
| 56 | |||
| 57 | struct IrInstructionSwitchBr { | ||
| 58 | IrInstruction base; | ||
| 59 | |||
| 60 | IrInstruction *target_value; | ||
| 61 | IrBasicBlock *else_block; | ||
| 62 | size_t case_count; | ||
| 63 | IrInstructionSwitchBrCase *cases; | ||
| 64 | }; | ||
| 65 | |||
| 66 | struct IrInstructionPhi { | ||
| 67 | IrInstruction base; | ||
| 68 | |||
| 69 | size_t incoming_block_count; | ||
| 70 | IrBasicBlock **incoming_blocks; | ||
| 71 | IrInstruction **incoming_values; | ||
| 72 | }; | ||
| 73 | |||
| 74 | enum IrBinOp { | ||
| 75 | IrBinOpInvalid, | ||
| 76 | IrBinOpBoolOr, | ||
| 77 | IrBinOpBoolAnd, | ||
| 78 | IrBinOpCmpEq, | ||
| 79 | IrBinOpCmpNotEq, | ||
| 80 | IrBinOpCmpLessThan, | ||
| 81 | IrBinOpCmpGreaterThan, | ||
| 82 | IrBinOpCmpLessOrEq, | ||
| 83 | IrBinOpCmpGreaterOrEq, | ||
| 84 | IrBinOpBinOr, | ||
| 85 | IrBinOpBinXor, | ||
| 86 | IrBinOpBinAnd, | ||
| 87 | IrBinOpBitShiftLeft, | ||
| 88 | IrBinOpBitShiftLeftWrap, | ||
| 89 | IrBinOpBitShiftRight, | ||
| 90 | IrBinOpAdd, | ||
| 91 | IrBinOpAddWrap, | ||
| 92 | IrBinOpSub, | ||
| 93 | IrBinOpSubWrap, | ||
| 94 | IrBinOpMult, | ||
| 95 | IrBinOpMultWrap, | ||
| 96 | IrBinOpDiv, | ||
| 97 | IrBinOpMod, | ||
| 98 | IrBinOpArrayCat, | ||
| 99 | IrBinOpArrayMult, | ||
| 100 | }; | ||
| 101 | |||
| 102 | struct IrInstructionBinOp { | ||
| 103 | IrInstruction base; | ||
| 104 | |||
| 105 | IrInstruction *op1; | ||
| 106 | IrBinOp op_id; | ||
| 107 | IrInstruction *op2; | ||
| 108 | }; | ||
| 109 | |||
| 110 | struct IrInstructionLoadVar { | ||
| 111 | IrInstruction base; | ||
| 112 | |||
| 113 | VariableTableEntry *var; | ||
| 114 | }; | ||
| 115 | |||
| 116 | struct IrInstructionStoreVar { | ||
| 117 | IrInstruction base; | ||
| 118 | |||
| 119 | IrInstruction *value; | ||
| 120 | VariableTableEntry *var; | ||
| 121 | }; | ||
| 122 | |||
| 123 | struct IrInstructionCall { | ||
| 124 | IrInstruction base; | ||
| 125 | |||
| 126 | IrInstruction *fn; | ||
| 127 | size_t arg_count; | ||
| 128 | IrInstruction **args; | ||
| 129 | }; | ||
| 130 | |||
| 131 | struct IrInstructionBuiltinCall { | ||
| 132 | IrInstruction base; | ||
| 133 | |||
| 134 | BuiltinFnId fn_id; | ||
| 135 | size_t arg_count; | ||
| 136 | IrInstruction **args; | ||
| 137 | }; | ||
| 138 | |||
| 139 | struct IrInstructionConst { | ||
| 140 | IrInstruction base; | ||
| 141 | }; | ||
| 142 | |||
| 143 | struct IrInstructionReturn { | ||
| 144 | IrInstruction base; | ||
| 145 | |||
| 146 | IrInstruction *value; | ||
| 147 | }; | ||
| 148 | |||
| 149 | IrBasicBlock *ir_gen(CodeGen *g, AstNode *fn_def_node, TypeTableEntry *return_type); | ||
| 150 | TypeTableEntry *ir_analyze(CodeGen *g, AstNode *fn_def_node, IrBasicBlock *entry_basic_block, | ||
| 151 | TypeTableEntry *expected_type); | ||
| 152 | 17 | ||
| 153 | #endif | 18 | #endif |
src/ir_print.cpp created+94| ... | @@ -0,0 +1,94 @@ | ||
| 1 | #include "ir_print.hpp" | ||
| 2 | |||
| 3 | struct IrPrint { | ||
| 4 | FILE *f; | ||
| 5 | int indent; | ||
| 6 | int indent_size; | ||
| 7 | }; | ||
| 8 | |||
| 9 | static void ir_print_indent(IrPrint *irp) { | ||
| 10 | for (int i = 0; i < irp->indent; i += 1) { | ||
| 11 | fprintf(irp->f, " "); | ||
| 12 | } | ||
| 13 | } | ||
| 14 | |||
| 15 | static void ir_print_prefix(IrPrint *irp, IrInstruction *instruction) { | ||
| 16 | ir_print_indent(irp); | ||
| 17 | fprintf(irp->f, "#%-3zu| ", instruction->debug_id); | ||
| 18 | } | ||
| 19 | |||
| 20 | static void ir_print_return(IrPrint *irp, IrInstructionReturn *return_instruction) { | ||
| 21 | ir_print_prefix(irp, &return_instruction->base); | ||
| 22 | assert(return_instruction->value); | ||
| 23 | fprintf(irp->f, "return #%zu;\n", return_instruction->value->debug_id); | ||
| 24 | } | ||
| 25 | |||
| 26 | static void ir_print_const(IrPrint *irp, IrInstructionConst *const_instruction) { | ||
| 27 | ir_print_prefix(irp, &const_instruction->base); | ||
| 28 | switch (const_instruction->base.type_entry->id) { | ||
| 29 | case TypeTableEntryIdInvalid: | ||
| 30 | zig_unreachable(); | ||
| 31 | case TypeTableEntryIdVoid: | ||
| 32 | fprintf(irp->f, "void\n"); | ||
| 33 | break; | ||
| 34 | case TypeTableEntryIdVar: | ||
| 35 | case TypeTableEntryIdMetaType: | ||
| 36 | case TypeTableEntryIdBool: | ||
| 37 | case TypeTableEntryIdUnreachable: | ||
| 38 | case TypeTableEntryIdInt: | ||
| 39 | case TypeTableEntryIdFloat: | ||
| 40 | case TypeTableEntryIdPointer: | ||
| 41 | case TypeTableEntryIdArray: | ||
| 42 | case TypeTableEntryIdStruct: | ||
| 43 | case TypeTableEntryIdNumLitFloat: | ||
| 44 | case TypeTableEntryIdNumLitInt: | ||
| 45 | case TypeTableEntryIdUndefLit: | ||
| 46 | case TypeTableEntryIdNullLit: | ||
| 47 | case TypeTableEntryIdMaybe: | ||
| 48 | case TypeTableEntryIdErrorUnion: | ||
| 49 | case TypeTableEntryIdPureError: | ||
| 50 | case TypeTableEntryIdEnum: | ||
| 51 | case TypeTableEntryIdUnion: | ||
| 52 | case TypeTableEntryIdFn: | ||
| 53 | case TypeTableEntryIdTypeDecl: | ||
| 54 | case TypeTableEntryIdNamespace: | ||
| 55 | case TypeTableEntryIdBlock: | ||
| 56 | case TypeTableEntryIdGenericFn: | ||
| 57 | zig_panic("TODO render more constant types in IR printer"); | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | void ir_print(FILE *f, IrExecutable *executable, int indent_size) { | ||
| 62 | IrPrint ir_print = {}; | ||
| 63 | IrPrint *irp = &ir_print; | ||
| 64 | irp->f = f; | ||
| 65 | irp->indent = indent_size; | ||
| 66 | irp->indent_size = indent_size; | ||
| 67 | |||
| 68 | for (size_t i = 0; i < executable->basic_block_count; i += 1) { | ||
| 69 | IrBasicBlock *current_block = executable->basic_block_list[i]; | ||
| 70 | for (IrInstruction *instruction = current_block->first; instruction != nullptr; | ||
| 71 | instruction = instruction->next) | ||
| 72 | { | ||
| 73 | switch (instruction->id) { | ||
| 74 | case IrInstructionIdInvalid: | ||
| 75 | zig_unreachable(); | ||
| 76 | case IrInstructionIdReturn: | ||
| 77 | ir_print_return(irp, (IrInstructionReturn *)instruction); | ||
| 78 | break; | ||
| 79 | case IrInstructionIdConst: | ||
| 80 | ir_print_const(irp, (IrInstructionConst *)instruction); | ||
| 81 | break; | ||
| 82 | case IrInstructionIdCondBr: | ||
| 83 | case IrInstructionIdSwitchBr: | ||
| 84 | case IrInstructionIdPhi: | ||
| 85 | case IrInstructionIdBinOp: | ||
| 86 | case IrInstructionIdLoadVar: | ||
| 87 | case IrInstructionIdStoreVar: | ||
| 88 | case IrInstructionIdCall: | ||
| 89 | case IrInstructionIdBuiltinCall: | ||
| 90 | zig_panic("TODO print more IR instructions"); | ||
| 91 | } | ||
| 92 | } | ||
| 93 | } | ||
| 94 | } | ||
src/ir_print.hpp created+17| ... | @@ -0,0 +1,17 @@ | ||
| 1 | /* | ||
| 2 | * Copyright (c) 2016 Andrew Kelley | ||
| 3 | * | ||
| 4 | * This file is part of zig, which is MIT licensed. | ||
| 5 | * See http://opensource.org/licenses/MIT | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef ZIG_IR_PRINT_HPP | ||
| 9 | #define ZIG_IR_PRINT_HPP | ||
| 10 | |||
| 11 | #include "all_types.hpp" | ||
| 12 | |||
| 13 | #include <stdio.h> | ||
| 14 | |||
| 15 | void ir_print(FILE *f, IrExecutable *executable, int indent_size); | ||
| 16 | |||
| 17 | #endif | ||
src/parser.cpp+11-12| ... | @@ -1912,14 +1912,14 @@ static AstNode *ast_parse_label(ParseContext *pc, size_t *token_index, bool mand | ... | @@ -1912,14 +1912,14 @@ static AstNode *ast_parse_label(ParseContext *pc, size_t *token_index, bool mand |
| 1912 | return node; | 1912 | return node; |
| 1913 | } | 1913 | } |
| 1914 | 1914 | ||
| 1915 | static AstNode *ast_create_void_expr(ParseContext *pc, Token *token) { | 1915 | //static AstNode *ast_create_void_expr(ParseContext *pc, Token *token) { |
| 1916 | AstNode *node = ast_create_node(pc, NodeTypeContainerInitExpr, token); | 1916 | // AstNode *node = ast_create_node(pc, NodeTypeContainerInitExpr, token); |
| 1917 | node->data.container_init_expr.type = ast_create_node(pc, NodeTypeSymbol, token); | 1917 | // node->data.container_init_expr.type = ast_create_node(pc, NodeTypeSymbol, token); |
| 1918 | node->data.container_init_expr.kind = ContainerInitKindArray; | 1918 | // node->data.container_init_expr.kind = ContainerInitKindArray; |
| 1919 | node->data.container_init_expr.type->data.symbol_expr.symbol = pc->void_buf; | 1919 | // node->data.container_init_expr.type->data.symbol_expr.symbol = pc->void_buf; |
| 1920 | normalize_parent_ptrs(node); | 1920 | // normalize_parent_ptrs(node); |
| 1921 | return node; | 1921 | // return node; |
| 1922 | } | 1922 | //} |
| 1923 | 1923 | ||
| 1924 | /* | 1924 | /* |
| 1925 | Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace) | 1925 | Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace) |
| ... | @@ -1961,13 +1961,12 @@ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mand | ... | @@ -1961,13 +1961,12 @@ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mand |
| 1961 | semicolon_expected = !statement_node; | 1961 | semicolon_expected = !statement_node; |
| 1962 | if (!statement_node) { | 1962 | if (!statement_node) { |
| 1963 | statement_node = ast_parse_non_block_expr(pc, token_index, false); | 1963 | statement_node = ast_parse_non_block_expr(pc, token_index, false); |
| 1964 | if (!statement_node) { | ||
| 1965 | statement_node = ast_create_void_expr(pc, last_token); | ||
| 1966 | } | ||
| 1967 | } | 1964 | } |
| 1968 | } | 1965 | } |
| 1969 | } | 1966 | } |
| 1970 | node->data.block.statements.append(statement_node); | 1967 | if (statement_node) { |
| 1968 | node->data.block.statements.append(statement_node); | ||
| 1969 | } | ||
| 1971 | 1970 | ||
| 1972 | last_token = &pc->tokens->at(*token_index); | 1971 | last_token = &pc->tokens->at(*token_index); |
| 1973 | if (last_token->id == TokenIdRBrace) { | 1972 | if (last_token->id == TokenIdRBrace) { |