authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-03-09 14:20:44-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-03-09 14:20:44-05:00
log6db9be8900bf43632c8a98d91c6a92f30b33500a
treee5a9dde7f5cc3a44439e22c16695edac010b55ab
parentaaf2230ae89d74497042b7fada8c8023bf274dbd

don't memoize comptime functions if they can mutate state via parameters

closes #639

6 files changed, 77 insertions(+), 9 deletions(-)

src/all_types.hpp+8-1
......@@ -1168,10 +1168,17 @@ struct TypeTableEntry {
11681168 LLVMTypeRef type_ref;
11691169 ZigLLVMDIType *di_type;
11701170
1171 bool zero_bits;
1171 bool zero_bits; // this is denormalized data
11721172 bool is_copyable;
11731173 bool gen_h_loop_flag;
11741174
1175 // This is denormalized data. The simplest type that has this
1176 // flag set to true is a mutable pointer. A const pointer has
1177 // the same value for this flag as the child type.
1178 // If a struct has any fields that have this flag true, then
1179 // the flag is true for the struct.
1180 bool can_mutate_state_through_it;
1181
11751182 union {
11761183 TypeTableEntryPointer pointer;
11771184 TypeTableEntryInt integral;
src/analyze.cpp+28
......@@ -398,6 +398,7 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type
398398
399399 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPointer);
400400 entry->is_copyable = true;
401 entry->can_mutate_state_through_it = is_const ? child_type->can_mutate_state_through_it : true;
401402
402403 const char *const_str = is_const ? "const " : "";
403404 const char *volatile_str = is_volatile ? "volatile " : "";
......@@ -482,6 +483,7 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) {
482483 assert(child_type->type_ref || child_type->zero_bits);
483484 assert(child_type->di_type);
484485 entry->is_copyable = type_is_copyable(g, child_type);
486 entry->can_mutate_state_through_it = child_type->can_mutate_state_through_it;
485487
486488 buf_resize(&entry->name, 0);
487489 buf_appendf(&entry->name, "?%s", buf_ptr(&child_type->name));
......@@ -572,6 +574,7 @@ TypeTableEntry *get_error_union_type(CodeGen *g, TypeTableEntry *err_set_type, T
572574 entry->is_copyable = true;
573575 assert(payload_type->di_type);
574576 ensure_complete_type(g, payload_type);
577 entry->can_mutate_state_through_it = payload_type->can_mutate_state_through_it;
575578
576579 buf_resize(&entry->name, 0);
577580 buf_appendf(&entry->name, "%s!%s", buf_ptr(&err_set_type->name), buf_ptr(&payload_type->name));
......@@ -730,6 +733,7 @@ TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *ptr_type) {
730733
731734 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdStruct);
732735 entry->is_copyable = true;
736 entry->can_mutate_state_through_it = ptr_type->can_mutate_state_through_it;
733737
734738 // replace the & with [] to go from a ptr type name to a slice type name
735739 buf_resize(&entry->name, 0);
......@@ -1735,6 +1739,8 @@ TypeTableEntry *get_struct_type(CodeGen *g, const char *type_name, const char *f
17351739 struct_type->data.structure.gen_field_count += 1;
17361740 } else {
17371741 field->gen_index = SIZE_MAX;
1742 struct_type->can_mutate_state_through_it = struct_type->can_mutate_state_through_it ||
1743 field->type_entry->can_mutate_state_through_it;
17381744 }
17391745
17401746 auto prev_entry = struct_type->data.structure.fields_by_name.put_unique(field->name, field);
......@@ -2475,6 +2481,9 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
24752481 if (!type_has_bits(field_type))
24762482 continue;
24772483
2484 struct_type->can_mutate_state_through_it = struct_type->can_mutate_state_through_it ||
2485 field_type->can_mutate_state_through_it;
2486
24782487 if (gen_field_index == 0) {
24792488 if (struct_type->data.structure.layout == ContainerLayoutPacked) {
24802489 struct_type->data.structure.abi_alignment = 1;
......@@ -2662,6 +2671,8 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
26622671 }
26632672 }
26642673 union_field->type_entry = field_type;
2674 union_type->can_mutate_state_through_it = union_type->can_mutate_state_through_it ||
2675 field_type->can_mutate_state_through_it;
26652676
26662677 if (field_node->data.struct_field.value != nullptr && !decl_node->data.container_decl.auto_enum) {
26672678 ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.value,
......@@ -4565,6 +4576,23 @@ bool generic_fn_type_id_eql(GenericFnTypeId *a, GenericFnTypeId *b) {
45654576 return true;
45664577}
45674578
4579bool fn_eval_cacheable(Scope *scope) {
4580 while (scope) {
4581 if (scope->id == ScopeIdVarDecl) {
4582 ScopeVarDecl *var_scope = (ScopeVarDecl *)scope;
4583 if (var_scope->var->value->type->can_mutate_state_through_it)
4584 return false;
4585 } else if (scope->id == ScopeIdFnDef) {
4586 return true;
4587 } else {
4588 zig_unreachable();
4589 }
4590
4591 scope = scope->parent;
4592 }
4593 zig_unreachable();
4594}
4595
45684596uint32_t fn_eval_hash(Scope* scope) {
45694597 uint32_t result = 0;
45704598 while (scope) {
src/analyze.hpp+1
......@@ -195,5 +195,6 @@ TypeTableEntry *get_auto_err_set_type(CodeGen *g, FnTableEntry *fn_entry);
195195
196196uint32_t get_coro_frame_align_bytes(CodeGen *g);
197197bool fn_type_can_fail(FnTypeId *fn_type_id);
198bool fn_eval_cacheable(Scope *scope);
198199
199200#endif
src/ir.cpp+11-6
......@@ -11830,12 +11830,15 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
1183011830 return_type = specified_return_type;
1183111831 }
1183211832
11833 IrInstruction *result;
11833 bool cacheable = fn_eval_cacheable(exec_scope);
11834 IrInstruction *result = nullptr;
11835 if (cacheable) {
11836 auto entry = ira->codegen->memoized_fn_eval_table.maybe_get(exec_scope);
11837 if (entry)
11838 result = entry->value;
11839 }
1183411840
11835 auto entry = ira->codegen->memoized_fn_eval_table.maybe_get(exec_scope);
11836 if (entry) {
11837 result = entry->value;
11838 } else {
11841 if (result == nullptr) {
1183911842 // Analyze the fn body block like any other constant expression.
1184011843 AstNode *body_node = fn_entry->body_node;
1184111844 result = ir_eval_const_value(ira->codegen, exec_scope, body_node, return_type,
......@@ -11859,7 +11862,9 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
1185911862 }
1186011863 }
1186111864
11862 ira->codegen->memoized_fn_eval_table.put(exec_scope, result);
11865 if (cacheable) {
11866 ira->codegen->memoized_fn_eval_table.put(exec_scope, result);
11867 }
1186311868
1186411869 if (type_is_invalid(result->value.type))
1186511870 return ira->codegen->builtin_types.entry_invalid;
std/sort.zig+1-2
......@@ -964,8 +964,7 @@ fn u8desc(lhs: &const u8, rhs: &const u8) bool {
964964
965965test "stable sort" {
966966 testStableSort();
967 // TODO: uncomment this after https://github.com/zig-lang/zig/issues/639
968 //comptime testStableSort();
967 comptime testStableSort();
969968}
970969fn testStableSort() void {
971970 var expected = []IdAndValue {
test/cases/eval.zig+28
......@@ -420,3 +420,31 @@ test "binary math operator in partially inlined function" {
420420 assert(s[2] == 0x90a0b0c);
421421 assert(s[3] == 0xd0e0f10);
422422}
423
424
425test "comptime function with the same args is memoized" {
426 comptime {
427 assert(MakeType(i32) == MakeType(i32));
428 assert(MakeType(i32) != MakeType(f64));
429 }
430}
431
432fn MakeType(comptime T: type) type {
433 return struct {
434 field: T,
435 };
436}
437
438test "comptime function with mutable pointer is not memoized" {
439 comptime {
440 var x: i32 = 1;
441 const ptr = &x;
442 increment(ptr);
443 increment(ptr);
444 assert(x == 3);
445 }
446}
447
448fn increment(value: &i32) void {
449 *value += 1;
450}