authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-09-30 02:21:21-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-09-30 02:21:21-04:00
log4e2fa2d15be248c29051a58995e38caa0b1de0a5
treeb8f27f8f3157b1897cf796c6b0241dafacbfd7b0
parent0562111b0208d94852928f4bc7a7f2b8e335366f

*WIP*


4 files changed, 402 insertions(+), 46 deletions(-)

CMakeLists.txt+10-9
...@@ -38,24 +38,25 @@ include_directories(...@@ -38,24 +38,25 @@ include_directories(
38)38)
3939
40set(ZIG_SOURCES40set(ZIG_SOURCES
41 "${CMAKE_SOURCE_DIR}/src/link.cpp"41 "${CMAKE_SOURCE_DIR}/src/analyze.cpp"
42 "${CMAKE_SOURCE_DIR}/src/target.cpp"
43 "${CMAKE_SOURCE_DIR}/src/ast_render.cpp"42 "${CMAKE_SOURCE_DIR}/src/ast_render.cpp"
44 "${CMAKE_SOURCE_DIR}/src/bignum.cpp"43 "${CMAKE_SOURCE_DIR}/src/bignum.cpp"
45 "${CMAKE_SOURCE_DIR}/src/tokenizer.cpp"44 "${CMAKE_SOURCE_DIR}/src/buffer.cpp"
46 "${CMAKE_SOURCE_DIR}/src/c_tokenizer.cpp"45 "${CMAKE_SOURCE_DIR}/src/c_tokenizer.cpp"
47 "${CMAKE_SOURCE_DIR}/src/parser.cpp"
48 "${CMAKE_SOURCE_DIR}/src/eval.cpp"
49 "${CMAKE_SOURCE_DIR}/src/analyze.cpp"
50 "${CMAKE_SOURCE_DIR}/src/codegen.cpp"46 "${CMAKE_SOURCE_DIR}/src/codegen.cpp"
51 "${CMAKE_SOURCE_DIR}/src/buffer.cpp"47 "${CMAKE_SOURCE_DIR}/src/errmsg.cpp"
52 "${CMAKE_SOURCE_DIR}/src/error.cpp"48 "${CMAKE_SOURCE_DIR}/src/error.cpp"
49 "${CMAKE_SOURCE_DIR}/src/eval.cpp"
50 "${CMAKE_SOURCE_DIR}/src/ir.cpp"
51 "${CMAKE_SOURCE_DIR}/src/link.cpp"
53 "${CMAKE_SOURCE_DIR}/src/main.cpp"52 "${CMAKE_SOURCE_DIR}/src/main.cpp"
54 "${CMAKE_SOURCE_DIR}/src/os.cpp"53 "${CMAKE_SOURCE_DIR}/src/os.cpp"
54 "${CMAKE_SOURCE_DIR}/src/parseh.cpp"
55 "${CMAKE_SOURCE_DIR}/src/parser.cpp"
56 "${CMAKE_SOURCE_DIR}/src/target.cpp"
57 "${CMAKE_SOURCE_DIR}/src/tokenizer.cpp"
55 "${CMAKE_SOURCE_DIR}/src/util.cpp"58 "${CMAKE_SOURCE_DIR}/src/util.cpp"
56 "${CMAKE_SOURCE_DIR}/src/errmsg.cpp"
57 "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp"59 "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp"
58 "${CMAKE_SOURCE_DIR}/src/parseh.cpp"
59)60)
6061
61set(TEST_SOURCES62set(TEST_SOURCES
src/analyze.cpp+13-37
...@@ -6,14 +6,15 @@...@@ -6,14 +6,15 @@
6 */6 */
77
8#include "analyze.hpp"8#include "analyze.hpp"
9#include "parser.hpp"9#include "ast_render.hpp"
10#include "config.h"
10#include "error.hpp"11#include "error.hpp"
11#include "zig_llvm.hpp"12#include "eval.hpp"
13#include "ir.hpp"
12#include "os.hpp"14#include "os.hpp"
13#include "parseh.hpp"15#include "parseh.hpp"
14#include "config.h"16#include "parser.hpp"
15#include "ast_render.hpp"17#include "zig_llvm.hpp"
16#include "eval.hpp"
1718
18static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,19static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
19 TypeTableEntry *expected_type, AstNode *node);20 TypeTableEntry *expected_type, AstNode *node);
...@@ -6880,22 +6881,6 @@ static TypeTableEntry *analyze_goto_pass1(CodeGen *g, ImportTableEntry *import,...@@ -6880,22 +6881,6 @@ static TypeTableEntry *analyze_goto_pass1(CodeGen *g, ImportTableEntry *import,
6880 return g->builtin_types.entry_unreachable;6881 return g->builtin_types.entry_unreachable;
6881}6882}
68826883
6883static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *node) {
6884 assert(node->type == NodeTypeGoto);
6885 Buf *label_name = node->data.goto_expr.name;
6886 BlockContext *context = node->block_context;
6887 assert(context);
6888 LabelTableEntry *label = find_label(g, context, label_name);
6889
6890 if (!label) {
6891 add_node_error(g, node, buf_sprintf("no label in scope named '%s'", buf_ptr(label_name)));
6892 return;
6893 }
6894
6895 label->used = true;
6896 node->data.goto_expr.label_entry = label;
6897}
6898
6899static TypeTableEntry *analyze_expression_pointer_only(CodeGen *g, ImportTableEntry *import,6884static TypeTableEntry *analyze_expression_pointer_only(CodeGen *g, ImportTableEntry *import,
6900 BlockContext *context, TypeTableEntry *expected_type, AstNode *node, bool pointer_only)6885 BlockContext *context, TypeTableEntry *expected_type, AstNode *node, bool pointer_only)
6901{6886{
...@@ -7113,24 +7098,15 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {...@@ -7113,24 +7098,15 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
7113 buf_sprintf("byvalue types not yet supported on extern function return values"));7098 buf_sprintf("byvalue types not yet supported on extern function return values"));
7114 }7099 }
71157100
7116 TypeTableEntry *block_return_type = analyze_expression(g, import, context, expected_type, node->data.fn_def.body);7101 IrBasicBlock *entry_basic_block = ir_gen(g, node, expected_type);
71177102 if (!entry_basic_block) {
7118 node->data.fn_def.implicit_return_type = block_return_type;7103 fn_proto_node->data.fn_proto.skip = true;
71197104 fn_table_entry->anal_state = FnAnalStateSkipped;
7120 for (size_t i = 0; i < fn_table_entry->goto_list.length; i += 1) {7105 return;
7121 AstNode *goto_node = fn_table_entry->goto_list.at(i);
7122 assert(goto_node->type == NodeTypeGoto);
7123 analyze_goto_pass2(g, import, goto_node);
7124 }7106 }
7107 TypeTableEntry *block_return_type = ir_analyze(g, node, entry_basic_block, expected_type);
71257108
7126 for (size_t i = 0; i < fn_table_entry->all_labels.length; i += 1) {7109 node->data.fn_def.implicit_return_type = block_return_type;
7127 LabelTableEntry *label = fn_table_entry->all_labels.at(i);
7128 if (!label->used) {
7129 add_node_error(g, label->decl_node,
7130 buf_sprintf("label '%s' defined but not used",
7131 buf_ptr(label->decl_node->data.label.name)));
7132 }
7133 }
71347110
7135 fn_table_entry->anal_state = FnAnalStateComplete;7111 fn_table_entry->anal_state = FnAnalStateComplete;
7136}7112}
src/ir.cpp created+226
...@@ -0,0 +1,226 @@
1#include "analyze.hpp"
2#include "ir.hpp"
3
4static IrInstruction *ir_gen_node(IrGen *ir, AstNode *node, BlockContext *block_context);
5
6static const IrInstruction invalid_instruction_data;
7static const IrInstruction *invalid_instruction = &invalid_instruction_data;
8
9static const IrInstruction void_instruction_data;
10static const IrInstruction *void_instruction = &void_instruction_data;
11
12struct IrGen {
13 CodeGen *codegen;
14 AstNode *fn_def_node;
15 IrBasicBlock *current_basic_block;
16};
17
18static IrInstruction *ir_build_return(Ir *ir, AstNode *source_node, IrInstruction *return_value) {
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
27static size_t get_conditional_defer_count(BlockContext *inner_block, BlockContext *outer_block) {
28 size_t result = 0;
29 while (inner_block != outer_block) {
30 if (inner_block->node->type == NodeTypeDefer &&
31 (inner_block->node->data.defer.kind == ReturnKindError ||
32 inner_block->node->data.defer.kind == ReturnKindMaybe))
33 {
34 result += 1;
35 }
36 inner_block = inner_block->parent;
37 }
38 return result;
39}
40
41static void ir_gen_defers_for_block(Ir *ir, BlockContext *inner_block, BlockContext *outer_block,
42 bool gen_error_defers, bool gen_maybe_defers)
43{
44 while (inner_block != outer_block) {
45 if (inner_block->node->type == NodeTypeDefer &&
46 ((inner_block->node->data.defer.kind == ReturnKindUnconditional) ||
47 (gen_error_defers && inner_block->node->data.defer.kind == ReturnKindError) ||
48 (gen_maybe_defers && inner_block->node->data.defer.kind == ReturnKindMaybe)))
49 {
50 AstNode *defer_expr_node = inner_block->node->data.defer.expr;
51 ir_gen_node(ir, defer_expr_node, defer_expr_node->block_context);
52 }
53 inner_block = inner_block->parent;
54 }
55}
56
57static IrInstruction *ir_gen_return(Ir *ir, AstNode *source_node, IrInstruction *value, ReturnKnowledge rk) {
58 BlockContext *defer_inner_block = source_node->block_context;
59 BlockContext *defer_outer_block = ir->fn_def_node->block_context;
60 if (rk == ReturnKnowledgeUnknown) {
61 if (get_conditional_defer_count(defer_inner_block, defer_outer_block) > 0) {
62 // generate branching code that checks the return value and generates defers
63 // if the return value is error
64 zig_panic("TODO");
65 }
66 } else if (rk != ReturnKnowledgeSkipDefers) {
67 ir_gen_defers_for_block(g, defer_inner_block, defer_outer_block,
68 rk == ReturnKnowledgeKnownError, rk == ReturnKnowledgeKnownNull);
69 }
70
71 ir_build_return(ir, source_node, value);
72 return void_instruction;
73}
74
75static IrInstruction *ir_gen_block(IrGen *ir, AstNode *block_node, TypeTableEntry *implicit_return_type) {
76 assert(block_node->type == NodeTypeBlock);
77
78 BlockContext *parent_context = block_node->context;
79 BlockContext *outer_block_context = new_block_context(block_node, parent_context);
80 BlockContext *child_context = outer_block_context;
81
82 IrInstruction *return_value = nullptr;
83 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);
85 return_value = ir_gen_node(g, statement_node, child_context);
86 if (statement_node->type == NodeTypeDefer && return_value != invalid_instruction) {
87 // defer starts a new block context
88 child_context = statement_node->data.defer.child_block;
89 assert(child_context);
90 }
91 }
92
93 ir_gen_defers_for_block(ir, child_context, outer_block_context, false, false);
94
95 return return_value;
96}
97
98static IrInstruction *ir_gen_node(IrGen *ir, AstNode *node, BlockContext *block_context) {
99 node->block_context = block_context;
100
101 switch (node->type) {
102 case NodeTypeBlock:
103 return ir_gen_block(ir, node, nullptr);
104 case NodeTypeBinOpExpr:
105 case NodeTypeUnwrapErrorExpr:
106 case NodeTypeReturnExpr:
107 case NodeTypeDefer:
108 case NodeTypeVariableDeclaration:
109 case NodeTypePrefixOpExpr:
110 case NodeTypeFnCallExpr:
111 case NodeTypeArrayAccessExpr:
112 case NodeTypeSliceExpr:
113 case NodeTypeFieldAccessExpr:
114 case NodeTypeIfBoolExpr:
115 case NodeTypeIfVarExpr:
116 case NodeTypeWhileExpr:
117 case NodeTypeForExpr:
118 case NodeTypeAsmExpr:
119 case NodeTypeSymbol:
120 case NodeTypeGoto:
121 case NodeTypeBreak:
122 case NodeTypeContinue:
123 case NodeTypeLabel:
124 case NodeTypeContainerInitExpr:
125 case NodeTypeSwitchExpr:
126 case NodeTypeNumberLiteral:
127 case NodeTypeBoolLiteral:
128 case NodeTypeStringLiteral:
129 case NodeTypeCharLiteral:
130 case NodeTypeNullLiteral:
131 case NodeTypeUndefinedLiteral:
132 case NodeTypeZeroesLiteral:
133 case NodeTypeThisLiteral:
134 case NodeTypeErrorType:
135 case NodeTypeTypeLiteral:
136 case NodeTypeArrayType:
137 case NodeTypeVarLiteral:
138 case NodeTypeRoot:
139 case NodeTypeFnProto:
140 case NodeTypeFnDef:
141 case NodeTypeFnDecl:
142 case NodeTypeParamDecl:
143 case NodeTypeUse:
144 case NodeTypeContainerDecl:
145 case NodeTypeStructField:
146 case NodeTypeStructValueField:
147 case NodeTypeSwitchProng:
148 case NodeTypeSwitchRange:
149 case NodeTypeErrorValueDecl:
150 case NodeTypeTypeDecl:
151 zig_panic("TODO more IR gen");
152 }
153 zig_unreachable();
154}
155
156IrBasicBlock *ir_gen(CodeGen *g, AstNode *fn_def_node, TypeTableEntry *return_type) {
157 assert(fn_def_node->type == NodeTypeFnDef);
158 assert(fn_def_node->data.fn_def.block_context);
159 assert(fn_def_node->owner);
160 assert(return_type);
161 assert(return_type->id != TypeTableEntryIdInvalid);
162
163 IrGen ir_gen = {0};
164 IrGen *ir = &ir_gen;
165
166 IrBasicBlock *entry_basic_block = allocate<IrBasicBlock>(1);
167 ir->current_basic_block = entry_basic_block;
168
169 AstNode *body_node = fn_def_node->data.fn_def.body;
170 body_node->block_context = fn_def_node->data.fn_def.block_context;
171 IrInstruction *instruction = ir_gen_block(ir, body_node, return_type);
172 return (instructon == invalid_instruction) ? nullptr : entry_basic_block;
173}
174
175/*
176static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *node) {
177 assert(node->type == NodeTypeGoto);
178 Buf *label_name = node->data.goto_expr.name;
179 BlockContext *context = node->block_context;
180 assert(context);
181 LabelTableEntry *label = find_label(g, context, label_name);
182
183 if (!label) {
184 add_node_error(g, node, buf_sprintf("no label in scope named '%s'", buf_ptr(label_name)));
185 return;
186 }
187
188 label->used = true;
189 node->data.goto_expr.label_entry = label;
190}
191
192 for (size_t i = 0; i < fn_table_entry->goto_list.length; i += 1) {
193 AstNode *goto_node = fn_table_entry->goto_list.at(i);
194 assert(goto_node->type == NodeTypeGoto);
195 analyze_goto_pass2(g, import, goto_node);
196 }
197
198 for (size_t i = 0; i < fn_table_entry->all_labels.length; i += 1) {
199 LabelTableEntry *label = fn_table_entry->all_labels.at(i);
200 if (!label->used) {
201 add_node_error(g, label->decl_node,
202 buf_sprintf("label '%s' defined but not used",
203 buf_ptr(label->decl_node->data.label.name)));
204 }
205 }
206*/
207
208
209TypeTableEntry *ir_analyze(CodeGen *g, AstNode *fn_def_node, IrBasicBlock *entry_basic_block,
210 TypeTableEntry *expected_type)
211{
212 TypeTableEntry *return_type = g->builtin_types.entry_void;
213
214 for (size_t i = 0; i < entry_basic_block->instructions.length; i += 1) {
215 IrInstruction *instruction = entry_basic_block->instructions.at(i);
216
217 if (return_type->id == TypeTableEntryIdUnreachable) {
218 add_node_error(g, first_executing_node(instruction->source_node),
219 buf_sprintf("unreachable code"));
220 break;
221 }
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 }
226}
src/ir.hpp created+153
...@@ -0,0 +1,153 @@
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_HPP
9#define ZIG_IR_HPP
10
11#include "all_types.hpp"
12
13struct IrInstruction;
14
15// A basic block contains no branching. Branches send control flow
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.
19struct IrBasicBlock {
20 ZigList<IrInstruction *> instructions;
21};
22
23enum 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
37struct IrInstruction {
38 IrInstructionId id;
39 AstNode *source_node;
40 ConstExprValue static_value;
41 TypeTableEntry *type_entry;
42};
43
44struct 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
52struct IrInstructionSwitchBrCase {
53 IrInstruction *value;
54 IrBasicBlock *block;
55};
56
57struct IrInstructionSwitchBr {
58 IrInstruction base;
59
60 IrInstruction *target_value;
61 IrBasicBlock *else_block;
62 size_t case_count;
63 IrInstructionSwitchBrCase *cases;
64};
65
66struct IrInstructionPhi {
67 IrInstruction base;
68
69 size_t incoming_block_count;
70 IrBasicBlock **incoming_blocks;
71 IrInstruction **incoming_values;
72};
73
74enum 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
102struct IrInstructionBinOp {
103 IrInstruction base;
104
105 IrInstruction *op1;
106 IrBinOp op_id;
107 IrInstruction *op2;
108};
109
110struct IrInstructionLoadVar {
111 IrInstruction base;
112
113 VariableTableEntry *var;
114};
115
116struct IrInstructionStoreVar {
117 IrInstruction base;
118
119 IrInstruction *value;
120 VariableTableEntry *var;
121};
122
123struct IrInstructionCall {
124 IrInstruction base;
125
126 IrInstruction *fn;
127 size_t arg_count;
128 IrInstruction **args;
129};
130
131struct IrInstructionBuiltinCall {
132 IrInstruction base;
133
134 BuiltinFnId fn_id;
135 size_t arg_count;
136 IrInstruction **args;
137};
138
139struct IrInstructionConst {
140 IrInstruction base;
141};
142
143struct IrInstructionReturn {
144 IrInstruction base;
145
146 IrInstruction *value;
147};
148
149IrBasicBlock *ir_gen(CodeGen *g, AstNode *fn_def_node, TypeTableEntry *return_type);
150TypeTableEntry *ir_analyze(CodeGen *g, AstNode *fn_def_node, IrBasicBlock *entry_basic_block,
151 TypeTableEntry *expected_type);
152
153#endif