Commit fa7c64ccd5

Andrew Kelley <superjoe30@gmail.com>
2017-03-18 16:24:58
lazy analysis of top level declarations
previously, we had lazy analysis of top level declarations, but if a declaration was referenced within a compile-time if or switch statement, that would still add the top level declaration to the resolution queue. now we have a declref ir instruction, which is only resolved if we analyze the instruction. this takes into account comptime branching. closes #270
1 parent af536ac
src/all_types.hpp
@@ -1610,6 +1610,12 @@ struct IrBasicBlock {
     IrInstruction *must_be_comptime_source_instr;
 };
 
+struct LVal {
+    bool is_ptr;
+    bool is_const;
+    bool is_volatile;
+};
+
 enum IrInstructionId {
     IrInstructionIdInvalid,
     IrInstructionIdBr,
@@ -1701,6 +1707,7 @@ enum IrInstructionId {
     IrInstructionIdCanImplicitCast,
     IrInstructionIdSetGlobalAlign,
     IrInstructionIdSetGlobalSection,
+    IrInstructionIdDeclRef,
 };
 
 struct IrInstruction {
@@ -2421,6 +2428,13 @@ struct IrInstructionSetGlobalSection {
     IrInstruction *value;
 };
 
+struct IrInstructionDeclRef {
+    IrInstruction base;
+
+    Tld *tld;
+    LVal lval;
+};
+
 static const size_t slice_ptr_index = 0;
 static const size_t slice_len_index = 1;
 
src/codegen.cpp
@@ -2519,6 +2519,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
         case IrInstructionIdCanImplicitCast:
         case IrInstructionIdSetGlobalAlign:
         case IrInstructionIdSetGlobalSection:
+        case IrInstructionIdDeclRef:
             zig_unreachable();
         case IrInstructionIdReturn:
             return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
src/ir.cpp
@@ -44,12 +44,6 @@ struct IrAnalyze {
     IrBasicBlock *const_predecessor_bb;
 };
 
-struct LVal {
-    bool is_ptr;
-    bool is_const;
-    bool is_volatile;
-};
-
 static const LVal LVAL_NONE = { false, false, false };
 static const LVal LVAL_PTR = { true, false, false };
 
@@ -534,6 +528,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSetGlobalSection
     return IrInstructionIdSetGlobalSection;
 }
 
+static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclRef *) {
+    return IrInstructionIdDeclRef;
+}
+
 template<typename T>
 static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
     T *special_instruction = allocate<T>(1);
@@ -686,14 +684,20 @@ static IrInstruction *ir_build_const_type(IrBuilder *irb, Scope *scope, AstNode
     return instruction;
 }
 
-static IrInstruction *ir_build_const_fn(IrBuilder *irb, Scope *scope, AstNode *source_node, FnTableEntry *fn_entry) {
-    IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
+static IrInstruction *ir_create_const_fn(IrBuilder *irb, Scope *scope, AstNode *source_node, FnTableEntry *fn_entry) {
+    IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(irb, scope, source_node);
     const_instruction->base.value.type = fn_entry->type_entry;
     const_instruction->base.value.special = ConstValSpecialStatic;
     const_instruction->base.value.data.x_fn = fn_entry;
     return &const_instruction->base;
 }
 
+static IrInstruction *ir_build_const_fn(IrBuilder *irb, Scope *scope, AstNode *source_node, FnTableEntry *fn_entry) {
+    IrInstruction *instruction = ir_create_const_fn(irb, scope, source_node, fn_entry);
+    ir_instruction_append(irb->current_basic_block, instruction);
+    return instruction;
+}
+
 static IrInstruction *ir_build_const_import(IrBuilder *irb, Scope *scope, AstNode *source_node, ImportTableEntry *import) {
     IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
     const_instruction->base.value.type = irb->codegen->builtin_types.entry_namespace;
@@ -2088,6 +2092,17 @@ static IrInstruction *ir_build_set_global_section(IrBuilder *irb, Scope *scope,
     return &instruction->base;
 }
 
+static IrInstruction *ir_build_decl_ref(IrBuilder *irb, Scope *scope, AstNode *source_node,
+        Tld *tld, LVal lval)
+{
+    IrInstructionDeclRef *instruction = ir_build_instruction<IrInstructionDeclRef>(
+            irb, scope, source_node);
+    instruction->tld = tld;
+    instruction->lval = lval;
+
+    return &instruction->base;
+}
+
 static IrInstruction *ir_instruction_br_get_dep(IrInstructionBr *instruction, size_t index) {
     return nullptr;
 }
@@ -2727,6 +2742,10 @@ static IrInstruction *ir_instruction_setglobalsection_get_dep(IrInstructionSetGl
     }
 }
 
+static IrInstruction *ir_instruction_declref_get_dep(IrInstructionDeclRef *instruction, size_t index) {
+    return nullptr;
+}
+
 static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t index) {
     switch (instruction->id) {
         case IrInstructionIdInvalid:
@@ -2909,6 +2928,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
             return ir_instruction_setglobalalign_get_dep((IrInstructionSetGlobalAlign *) instruction, index);
         case IrInstructionIdSetGlobalSection:
             return ir_instruction_setglobalsection_get_dep((IrInstructionSetGlobalSection *) instruction, index);
+        case IrInstructionIdDeclRef:
+            return ir_instruction_declref_get_dep((IrInstructionDeclRef *) instruction, index);
     }
     zig_unreachable();
 }
@@ -3574,52 +3595,6 @@ static IrInstruction *ir_gen_var_literal(IrBuilder *irb, Scope *scope, AstNode *
     return ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_var);
 }
 
-static IrInstruction *ir_gen_decl_ref(IrBuilder *irb, AstNode *source_node, Tld *tld,
-        LVal lval, Scope *scope)
-{
-    resolve_top_level_decl(irb->codegen, tld, lval.is_ptr);
-    if (tld->resolution == TldResolutionInvalid)
-        return irb->codegen->invalid_instruction;
-
-    switch (tld->id) {
-        case TldIdContainer:
-            zig_unreachable();
-        case TldIdVar:
-        {
-            TldVar *tld_var = (TldVar *)tld;
-            VariableTableEntry *var = tld_var->var;
-            IrInstruction *var_ptr = ir_build_var_ptr(irb, scope, source_node, var,
-                    !lval.is_ptr || lval.is_const, lval.is_ptr && lval.is_volatile);
-            if (lval.is_ptr)
-                return var_ptr;
-            else
-                return ir_build_load_ptr(irb, scope, source_node, var_ptr);
-        }
-        case TldIdFn:
-        {
-            TldFn *tld_fn = (TldFn *)tld;
-            FnTableEntry *fn_entry = tld_fn->fn_entry;
-            assert(fn_entry->type_entry);
-            IrInstruction *ref_instruction = ir_build_const_fn(irb, scope, source_node, fn_entry);
-            if (lval.is_ptr)
-                return ir_build_ref(irb, scope, source_node, ref_instruction, true, false);
-            else
-                return ref_instruction;
-        }
-        case TldIdTypeDef:
-        {
-            TldTypeDef *tld_typedef = (TldTypeDef *)tld;
-            TypeTableEntry *typedef_type = tld_typedef->type_entry;
-            IrInstruction *ref_instruction = ir_build_const_type(irb, scope, source_node, typedef_type);
-            if (lval.is_ptr)
-                return ir_build_ref(irb, scope, source_node, ref_instruction, true, false);
-            else
-                return ref_instruction;
-        }
-    }
-    zig_unreachable();
-}
-
 static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {
     assert(node->type == NodeTypeSymbol);
 
@@ -3656,7 +3631,7 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node,
 
     Tld *tld = find_decl(irb->codegen, scope, variable_name);
     if (tld)
-        return ir_gen_decl_ref(irb, node, tld, lval, scope);
+        return ir_build_decl_ref(irb, scope, node, tld, lval);
 
     if (node->owner->any_imports_failed) {
         // skip the error message since we had a failing import in this file
@@ -12152,6 +12127,75 @@ static TypeTableEntry *ir_analyze_instruction_can_implicit_cast(IrAnalyze *ira,
     return ira->codegen->builtin_types.entry_bool;
 }
 
+static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira,
+        IrInstructionDeclRef *instruction)
+{
+    Tld *tld = instruction->tld;
+    LVal lval = instruction->lval;
+
+    resolve_top_level_decl(ira->codegen, tld, lval.is_ptr);
+    if (tld->resolution == TldResolutionInvalid)
+        return ira->codegen->builtin_types.entry_invalid;
+
+    switch (tld->id) {
+        case TldIdContainer:
+            zig_unreachable();
+        case TldIdVar:
+        {
+            TldVar *tld_var = (TldVar *)tld;
+            VariableTableEntry *var = tld_var->var;
+
+            IrInstruction *var_ptr = ir_get_var_ptr(ira, &instruction->base, var,
+                    !lval.is_ptr || lval.is_const, lval.is_ptr && lval.is_volatile);
+            if (type_is_invalid(var_ptr->value.type))
+                return ira->codegen->builtin_types.entry_invalid;
+
+            if (lval.is_ptr) {
+                ir_link_new_instruction(var_ptr, &instruction->base);
+                return var_ptr->value.type;
+            } else {
+                IrInstruction *loaded_instr = ir_get_deref(ira, &instruction->base, var_ptr);
+                ir_link_new_instruction(loaded_instr, &instruction->base);
+                return loaded_instr->value.type;
+            }
+        }
+        case TldIdFn:
+        {
+            TldFn *tld_fn = (TldFn *)tld;
+            FnTableEntry *fn_entry = tld_fn->fn_entry;
+            assert(fn_entry->type_entry);
+
+            IrInstruction *ref_instruction = ir_create_const_fn(&ira->new_irb, instruction->base.scope,
+                    instruction->base.source_node, fn_entry);
+            if (lval.is_ptr) {
+                IrInstruction *ptr_instr = ir_get_ref(ira, &instruction->base, ref_instruction, true, false);
+                ir_link_new_instruction(ptr_instr, &instruction->base);
+                return ptr_instr->value.type;
+            } else {
+                ir_link_new_instruction(ref_instruction, &instruction->base);
+                return ref_instruction->value.type;
+            }
+        }
+        case TldIdTypeDef:
+        {
+            TldTypeDef *tld_typedef = (TldTypeDef *)tld;
+            TypeTableEntry *typedef_type = tld_typedef->type_entry;
+
+            IrInstruction *ref_instruction = ir_create_const_type(&ira->new_irb, instruction->base.scope,
+                    instruction->base.source_node, typedef_type);
+            if (lval.is_ptr) {
+                IrInstruction *ptr_inst = ir_get_ref(ira, &instruction->base, ref_instruction, true, false);
+                ir_link_new_instruction(ptr_inst, &instruction->base);
+                return ptr_inst->value.type;
+            } else {
+                ir_link_new_instruction(ref_instruction, &instruction->base);
+                return ref_instruction->value.type;
+            }
+        }
+    }
+    zig_unreachable();
+}
+
 static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
     switch (instruction->id) {
         case IrInstructionIdInvalid:
@@ -12317,6 +12361,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
             return ir_analyze_instruction_test_type(ira, (IrInstructionTestType *)instruction);
         case IrInstructionIdCanImplicitCast:
             return ir_analyze_instruction_can_implicit_cast(ira, (IrInstructionCanImplicitCast *)instruction);
+        case IrInstructionIdDeclRef:
+            return ir_analyze_instruction_decl_ref(ira, (IrInstructionDeclRef *)instruction);
         case IrInstructionIdMaybeWrap:
         case IrInstructionIdErrWrapCode:
         case IrInstructionIdErrWrapPayload:
@@ -12495,6 +12541,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdTestType:
         case IrInstructionIdTypeName:
         case IrInstructionIdCanImplicitCast:
+        case IrInstructionIdDeclRef:
             return false;
         case IrInstructionIdAsm:
             {
src/ir_print.cpp
@@ -849,6 +849,13 @@ static void ir_print_set_global_section(IrPrint *irp, IrInstructionSetGlobalSect
     fprintf(irp->f, ")");
 }
 
+static void ir_print_decl_ref(IrPrint *irp, IrInstructionDeclRef *instruction) {
+    const char *ptr_str = instruction->lval.is_ptr ? "ptr " : "";
+    const char *const_str = instruction->lval.is_const ? "const " : "";
+    const char *volatile_str = instruction->lval.is_volatile ? "volatile " : "";
+    fprintf(irp->f, "declref %s%s%s%s", const_str, volatile_str, ptr_str, buf_ptr(instruction->tld->name));
+}
+
 static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
     ir_print_prefix(irp, instruction);
     switch (instruction->id) {
@@ -1121,6 +1128,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdSetGlobalSection:
             ir_print_set_global_section(irp, (IrInstructionSetGlobalSection *)instruction);
             break;
+        case IrInstructionIdDeclRef:
+            ir_print_decl_ref(irp, (IrInstructionDeclRef *)instruction);
+            break;
     }
     fprintf(irp->f, "\n");
 }