authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-12-04 23:52:43-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-12-04 23:52:43-05:00
log25a89e7a362ab4876139fad7427a2193665cb042
treef84e4d5374c20dd9aa649192384bb9d06f35970d
parent9f23475b172dbe09861c5680ebec2eb96f307cb0

IR: compile time function evaluation


6 files changed, 156 insertions(+), 82 deletions(-)

src/all_types.hpp+3-1
......@@ -46,7 +46,7 @@ struct IrExecutable {
4646 ZigList<IrBasicBlock *> basic_block_list;
4747 size_t mem_slot_count;
4848 size_t next_debug_id;
49 size_t backward_branch_count;
49 size_t *backward_branch_count;
5050 size_t backward_branch_quota;
5151 bool invalid;
5252 ZigList<LabelTableEntry *> all_labels;
......@@ -968,6 +968,7 @@ struct FnTableEntry {
968968 FnAnalState anal_state;
969969 IrExecutable ir_executable;
970970 IrExecutable analyzed_executable;
971 size_t prealloc_bbc;
971972
972973 AstNode *fn_no_inline_set_node;
973974 AstNode *fn_export_set_node;
......@@ -1311,6 +1312,7 @@ struct IrBasicBlock {
13111312 size_t debug_id;
13121313 size_t ref_count;
13131314 LLVMBasicBlockRef llvm_block;
1315 LLVMBasicBlockRef llvm_exit_block;
13141316};
13151317
13161318enum IrInstructionId {
src/analyze.cpp+5-38
......@@ -873,43 +873,9 @@ TypeTableEntry *get_underlying_type(TypeTableEntry *type_entry) {
873873 }
874874}
875875
876static IrInstruction *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node,
877 TypeTableEntry *expected_type)
878{
879 IrExecutable ir_executable = {0};
880 ir_executable.is_inline = true;
881 ir_gen(g, node, scope, &ir_executable);
882
883 if (ir_executable.invalid)
884 return g->invalid_instruction;
885
886 if (g->verbose) {
887 fprintf(stderr, "\nSource: ");
888 ast_render(stderr, node, 4);
889 fprintf(stderr, "\n{ // (IR)\n");
890 ir_print(stderr, &ir_executable, 4);
891 fprintf(stderr, "}\n");
892 }
893 IrExecutable analyzed_executable = {0};
894 analyzed_executable.is_inline = true;
895 analyzed_executable.backward_branch_quota = default_backward_branch_quota;
896 TypeTableEntry *result_type = ir_analyze(g, &ir_executable, &analyzed_executable, expected_type, node);
897 if (result_type->id == TypeTableEntryIdInvalid)
898 return g->invalid_instruction;
899
900 if (g->verbose) {
901 fprintf(stderr, "{ // (analyzed)\n");
902 ir_print(stderr, &analyzed_executable, 4);
903 fprintf(stderr, "}\n");
904 }
905
906 IrInstruction *result = ir_exec_const_result(&analyzed_executable);
907 if (!result) {
908 add_node_error(g, node, buf_sprintf("unable to evaluate constant expression"));
909 return g->invalid_instruction;
910 }
911
912 return result;
876static IrInstruction *analyze_const_value(CodeGen *g, Scope *scope, AstNode *node, TypeTableEntry *type_entry) {
877 size_t backward_branch_count = 0;
878 return ir_eval_const_value(g, scope, node, type_entry, &backward_branch_count, default_backward_branch_quota);
913879}
914880
915881static TypeTableEntry *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node) {
......@@ -1403,9 +1369,10 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
14031369 }
14041370
14051371 FnTableEntry *fn_table_entry = allocate<FnTableEntry>(1);
1372 fn_table_entry->analyzed_executable.backward_branch_count = &fn_table_entry->prealloc_bbc;
14061373 fn_table_entry->analyzed_executable.backward_branch_quota = default_backward_branch_quota;
1407 fn_table_entry->ir_executable.fn_entry = fn_table_entry;
14081374 fn_table_entry->analyzed_executable.fn_entry = fn_table_entry;
1375 fn_table_entry->ir_executable.fn_entry = fn_table_entry;
14091376 fn_table_entry->import_entry = import;
14101377 fn_table_entry->proto_node = proto_node;
14111378 fn_table_entry->fn_def_node = fn_def_node;
src/codegen.cpp+2-1
......@@ -1758,7 +1758,7 @@ static LLVMValueRef ir_render_phi(CodeGen *g, IrExecutable *executable, IrInstru
17581758 LLVMBasicBlockRef *incoming_blocks = allocate<LLVMBasicBlockRef>(instruction->incoming_count);
17591759 for (size_t i = 0; i < instruction->incoming_count; i += 1) {
17601760 incoming_values[i] = ir_llvm_value(g, instruction->incoming_values[i]);
1761 incoming_blocks[i] = instruction->incoming_blocks[i]->llvm_block;
1761 incoming_blocks[i] = instruction->incoming_blocks[i]->llvm_exit_block;
17621762 }
17631763 LLVMAddIncoming(phi, incoming_values, incoming_blocks, instruction->incoming_count);
17641764 return phi;
......@@ -1877,6 +1877,7 @@ static void ir_render(CodeGen *g, FnTableEntry *fn_entry) {
18771877 continue;
18781878 instruction->llvm_value = ir_render_instruction(g, executable, instruction);
18791879 }
1880 current_block->llvm_exit_block = LLVMGetInsertBlock(g->builder);
18801881 }
18811882}
18821883
src/ir.cpp+125-40
......@@ -6,6 +6,7 @@
66 */
77
88#include "analyze.hpp"
9#include "ast_render.hpp"
910#include "error.hpp"
1011#include "eval.hpp"
1112#include "ir.hpp"
......@@ -2814,7 +2815,7 @@ IrInstruction *ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutabl
28142815 return return_instruction;
28152816}
28162817
2817IrInstruction *ir_gen_fn(CodeGen *codegn, FnTableEntry *fn_entry) {
2818IrInstruction *ir_gen_fn(CodeGen *codegen, FnTableEntry *fn_entry) {
28182819 assert(fn_entry);
28192820
28202821 IrExecutable *ir_executable = &fn_entry->ir_executable;
......@@ -2826,18 +2827,30 @@ IrInstruction *ir_gen_fn(CodeGen *codegn, FnTableEntry *fn_entry) {
28262827 assert(fn_entry->child_scope);
28272828 Scope *child_scope = fn_entry->child_scope;
28282829
2829 return ir_gen(codegn, body_node, child_scope, ir_executable);
2830 return ir_gen(codegen, body_node, child_scope, ir_executable);
28302831}
28312832
28322833static ErrorMsg *ir_add_error(IrAnalyze *ira, IrInstruction *source_instruction, Buf *msg) {
2834 ira->new_irb.exec->invalid = true;
28332835 return add_node_error(ira->codegen, source_instruction->source_node, msg);
28342836}
28352837
2836static IrInstruction *ir_eval_fn(IrAnalyze *ira, IrInstruction *source_instruction,
2837 size_t arg_count, IrInstruction **args)
2838{
2839 // TODO count this as part of the backward branch quota
2840 zig_panic("TODO ir_eval_fn");
2838static IrInstruction *ir_exec_const_result(IrExecutable *exec) {
2839 if (exec->basic_block_list.length != 1)
2840 return nullptr;
2841
2842 IrBasicBlock *bb = exec->basic_block_list.at(0);
2843 if (bb->instruction_list.length != 1)
2844 return nullptr;
2845
2846 IrInstruction *only_inst = bb->instruction_list.at(0);
2847 if (only_inst->id != IrInstructionIdReturn)
2848 return nullptr;
2849
2850 IrInstructionReturn *ret_inst = (IrInstructionReturn *)only_inst;
2851 IrInstruction *value = ret_inst->value;
2852 assert(value->static_value.special != ConstValSpecialRuntime);
2853 return value;
28412854}
28422855
28432856static bool ir_emit_global_runtime_side_effect(IrAnalyze *ira, IrInstruction *source_instruction) {
......@@ -3140,14 +3153,26 @@ static TypeTableEntry *ir_unreach_error(IrAnalyze *ira) {
31403153 return ira->codegen->builtin_types.entry_unreachable;
31413154}
31423155
3156static bool ir_emit_backward_branch(IrAnalyze *ira, IrInstruction *source_instruction) {
3157 size_t *bbc = ira->new_irb.exec->backward_branch_count;
3158 size_t quota = ira->new_irb.exec->backward_branch_quota;
3159
3160 // If we're already over quota, we've already given an error message for this.
3161 if (*bbc > quota)
3162 return false;
3163
3164 *bbc += 1;
3165 if (*bbc > quota) {
3166 ir_add_error(ira, source_instruction, buf_sprintf("evaluation exceeded %zu backwards branches", quota));
3167 return false;
3168 }
3169 return true;
3170}
3171
31433172static TypeTableEntry *ir_inline_bb(IrAnalyze *ira, IrInstruction *source_instruction, IrBasicBlock *old_bb) {
31443173 if (old_bb->debug_id <= ira->old_irb.current_basic_block->debug_id) {
3145 ira->new_irb.exec->backward_branch_count += 1;
3146 if (ira->new_irb.exec->backward_branch_count > ira->new_irb.exec->backward_branch_quota) {
3147 add_node_error(ira->codegen, source_instruction->source_node,
3148 buf_sprintf("evaluation exceeded %zu backwards branches", ira->new_irb.exec->backward_branch_quota));
3174 if (!ir_emit_backward_branch(ira, source_instruction))
31493175 return ir_unreach_error(ira);
3150 }
31513176 }
31523177
31533178 ir_start_bb(ira, old_bb, ira->old_irb.current_basic_block);
......@@ -3216,13 +3241,90 @@ static TypeTableEntry *ir_analyze_const_usize(IrAnalyze *ira, IrInstruction *ins
32163241
32173242static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value) {
32183243 if (value->static_value.special != ConstValSpecialStatic) {
3219 add_node_error(ira->codegen, value->source_node,
3220 buf_sprintf("unable to evaluate constant expression"));
3244 ir_add_error(ira, value, buf_sprintf("unable to evaluate constant expression"));
32213245 return nullptr;
32223246 }
32233247 return &value->static_value;
32243248}
32253249
3250IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node,
3251 TypeTableEntry *expected_type, size_t *backward_branch_count, size_t backward_branch_quota)
3252{
3253 IrExecutable ir_executable = {0};
3254 ir_executable.is_inline = true;
3255 ir_gen(codegen, node, scope, &ir_executable);
3256
3257 if (ir_executable.invalid)
3258 return codegen->invalid_instruction;
3259
3260 if (codegen->verbose) {
3261 fprintf(stderr, "\nSource: ");
3262 ast_render(stderr, node, 4);
3263 fprintf(stderr, "\n{ // (IR)\n");
3264 ir_print(stderr, &ir_executable, 4);
3265 fprintf(stderr, "}\n");
3266 }
3267 IrExecutable analyzed_executable = {0};
3268 analyzed_executable.is_inline = true;
3269 analyzed_executable.backward_branch_count = backward_branch_count;
3270 analyzed_executable.backward_branch_quota = backward_branch_quota;
3271 TypeTableEntry *result_type = ir_analyze(codegen, &ir_executable, &analyzed_executable, expected_type, node);
3272 if (result_type->id == TypeTableEntryIdInvalid)
3273 return codegen->invalid_instruction;
3274
3275 if (codegen->verbose) {
3276 fprintf(stderr, "{ // (analyzed)\n");
3277 ir_print(stderr, &analyzed_executable, 4);
3278 fprintf(stderr, "}\n");
3279 }
3280
3281 IrInstruction *result = ir_exec_const_result(&analyzed_executable);
3282 if (!result) {
3283 add_node_error(codegen, node, buf_sprintf("unable to evaluate constant expression"));
3284 return codegen->invalid_instruction;
3285 }
3286
3287 return result;
3288}
3289
3290static IrInstruction *ir_eval_fn(IrAnalyze *ira, IrInstruction *source_instruction,
3291 FnTableEntry *fn_entry, IrInstruction **args)
3292{
3293 if (!fn_entry) {
3294 ir_add_error(ira, source_instruction,
3295 buf_sprintf("unable to evaluate constant expression"));
3296 return ira->codegen->invalid_instruction;
3297 }
3298
3299 if (!ir_emit_backward_branch(ira, source_instruction))
3300 return ira->codegen->invalid_instruction;
3301
3302 TypeTableEntry *fn_type = fn_entry->type_entry;
3303 FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
3304
3305 // Fork a scope of the function with known values for the parameters.
3306
3307 Scope *exec_scope = &fn_entry->fndef_scope->base;
3308 for (size_t i = 0; i < fn_type_id->param_count; i += 1) {
3309 AstNode *param_decl_node = fn_entry->proto_node->data.fn_proto.params.at(i);
3310 Buf *param_name = param_decl_node->data.param_decl.name;
3311 IrInstruction *arg = args[i];
3312 ConstExprValue *arg_val = ir_resolve_const(ira, arg);
3313 if (!arg_val)
3314 return ira->codegen->invalid_instruction;
3315
3316 VariableTableEntry *var = add_variable(ira->codegen, param_decl_node, exec_scope, param_name,
3317 arg->type_entry, true, arg_val);
3318 exec_scope = var->child_scope;
3319 }
3320
3321 // Analyze the fn body block like any other constant expression.
3322
3323 AstNode *body_node = fn_entry->fn_def_node->data.fn_def.body;
3324 return ir_eval_const_value(ira->codegen, exec_scope, body_node, fn_type_id->return_type,
3325 ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota);
3326}
3327
32263328static TypeTableEntry *ir_resolve_type_lval(IrAnalyze *ira, IrInstruction *type_value, LValPurpose lval) {
32273329 if (lval != LValPurposeNone)
32283330 zig_panic("TODO");
......@@ -4238,7 +4340,8 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
42384340 return ira->codegen->builtin_types.entry_invalid;
42394341
42404342 if (is_inline) {
4241 IrInstruction *result = ir_eval_fn(ira, &call_instruction->base, call_param_count, casted_args);
4343 assert(call_param_count == fn_type_id->param_count);
4344 IrInstruction *result = ir_eval_fn(ira, &call_instruction->base, fn_entry, casted_args);
42424345 if (result->type_entry->id == TypeTableEntryIdInvalid)
42434346 return ira->codegen->builtin_types.entry_invalid;
42444347
......@@ -4252,9 +4355,9 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
42524355 fn_entry, fn_ref, call_param_count, casted_args);
42534356
42544357 if (type_has_bits(return_type) && handle_is_ptr(return_type)) {
4255 FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec);
4256 assert(fn_entry);
4257 fn_entry->alloca_list.append(new_call_instruction);
4358 FnTableEntry *owner_fn = exec_fn_entry(ira->new_irb.exec);
4359 assert(owner_fn);
4360 owner_fn->alloca_list.append(new_call_instruction);
42584361 }
42594362
42604363 return ir_finish_anal(ira, return_type);
......@@ -4782,13 +4885,13 @@ static TypeTableEntry *ir_analyze_var_ptr(IrAnalyze *ira, IrInstruction *instruc
47824885
47834886 ConstExprValue *mem_slot = nullptr;
47844887 FnTableEntry *fn_entry = scope_fn_entry(var->parent_scope);
4785 if (fn_entry) {
4888 if (var->src_is_const && var->value) {
4889 mem_slot = var->value;
4890 assert(mem_slot->special != ConstValSpecialRuntime);
4891 } else if (fn_entry) {
47864892 // TODO once the analyze code is fully ported over to IR we won't need this SIZE_MAX thing.
47874893 if (var->mem_slot_index != SIZE_MAX)
47884894 mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index];
4789 } else if (var->src_is_const) {
4790 mem_slot = var->value;
4791 assert(mem_slot->special != ConstValSpecialRuntime);
47924895 }
47934896
47944897 if (mem_slot && mem_slot->special != ConstValSpecialRuntime) {
......@@ -6481,24 +6584,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
64816584 zig_unreachable();
64826585}
64836586
6484IrInstruction *ir_exec_const_result(IrExecutable *exec) {
6485 if (exec->basic_block_list.length != 1)
6486 return nullptr;
6487
6488 IrBasicBlock *bb = exec->basic_block_list.at(0);
6489 if (bb->instruction_list.length != 1)
6490 return nullptr;
6491
6492 IrInstruction *only_inst = bb->instruction_list.at(0);
6493 if (only_inst->id != IrInstructionIdReturn)
6494 return nullptr;
6495
6496 IrInstructionReturn *ret_inst = (IrInstructionReturn *)only_inst;
6497 IrInstruction *value = ret_inst->value;
6498 assert(value->static_value.special != ConstValSpecialRuntime);
6499 return value;
6500}
6501
65026587// TODO port over all this commented out code into new IR way of doing things
65036588
65046589//static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *import, BlockContext *context,
src/ir.hpp+3-2
......@@ -13,11 +13,12 @@
1313IrInstruction *ir_gen(CodeGen *g, AstNode *node, Scope *scope, IrExecutable *ir_executable);
1414IrInstruction *ir_gen_fn(CodeGen *g, FnTableEntry *fn_entry);
1515
16IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node,
17 TypeTableEntry *expected_type, size_t *backward_branch_count, size_t backward_branch_quota);
18
1619TypeTableEntry *ir_analyze(CodeGen *g, IrExecutable *old_executable, IrExecutable *new_executable,
1720 TypeTableEntry *expected_type, AstNode *expected_type_source_node);
1821
19IrInstruction *ir_exec_const_result(IrExecutable *exec);
20
2122bool ir_has_side_effects(IrInstruction *instruction);
2223ConstExprValue *const_ptr_pointee(ConstExprValue *const_val);
2324
test/self_hosted2.zig+18
......@@ -96,6 +96,22 @@ fn testStructStatic() {
9696 assert(result == 7);
9797}
9898
99const should_be_11 = FooA.add(5, 6);
100fn testStaticFnEval() {
101 assert(should_be_11 == 11);
102}
103
104fn fib(x: i32) -> i32 {
105 if (x < 2) x else fib(x - 1) + fib(x - 2)
106}
107
108const fib_7 = fib(7);
109
110fn testCompileTimeFib() {
111 assert(fib_7 == 13);
112}
113
114
99115fn assert(ok: bool) {
100116 if (!ok)
101117 @unreachable();
......@@ -111,6 +127,8 @@ fn runAllTests() {
111127 testNamespaceFnCall();
112128 gotoAndLabels();
113129 testStructStatic();
130 testStaticFnEval();
131 testCompileTimeFib();
114132}
115133
116134export nakedcc fn _start() -> unreachable {