Commit a09b505558

Andrew Kelley <superjoe30@gmail.com>
2016-01-28 22:04:22
null pointer optimization for ?&T
this is necessary for the parseh change where all pointers from .h files are maybe pointers.
1 parent 2fc4b36
src/analyze.cpp
@@ -199,43 +199,54 @@ static TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) {
         return entry;
     } else {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdMaybe);
-        // create a struct with a boolean whether this is the null value
         assert(child_type->type_ref);
-        LLVMTypeRef elem_types[] = {
-            child_type->type_ref,
-            LLVMInt1Type(),
-        };
-        entry->type_ref = LLVMStructType(elem_types, 2, false);
+        assert(child_type->di_type);
+
         buf_resize(&entry->name, 0);
         buf_appendf(&entry->name, "?%s", buf_ptr(&child_type->name));
-        entry->size_in_bits = child_type->size_in_bits + 8;
-        entry->align_in_bits = child_type->align_in_bits;
-        assert(child_type->di_type);
 
+        if (child_type->id == TypeTableEntryIdPointer) {
+            // this is an optimization but also is necessary for calling C
+            // functions where all pointers are maybe pointers
+            entry->size_in_bits = child_type->size_in_bits;
+            entry->align_in_bits = child_type->align_in_bits;
+            entry->type_ref = child_type->type_ref;
+            entry->di_type = child_type->di_type;
+        } else {
+            // create a struct with a boolean whether this is the null value
+            LLVMTypeRef elem_types[] = {
+                child_type->type_ref,
+                LLVMInt1Type(),
+            };
+            entry->type_ref = LLVMStructType(elem_types, 2, false);
+            entry->size_in_bits = child_type->size_in_bits + 8;
+            entry->align_in_bits = child_type->align_in_bits;
 
-        LLVMZigDIScope *compile_unit_scope = LLVMZigCompileUnitToScope(g->compile_unit);
-        LLVMZigDIFile *di_file = nullptr;
-        unsigned line = 0;
-        entry->di_type = LLVMZigCreateReplaceableCompositeType(g->dbuilder,
-            LLVMZigTag_DW_structure_type(), buf_ptr(&entry->name),
-            compile_unit_scope, di_file, line);
 
-        LLVMZigDIType *di_element_types[] = {
-            LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(entry->di_type),
-                    "val", di_file, line, child_type->size_in_bits, child_type->align_in_bits, 0, 0,
-                    child_type->di_type),
-            LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(entry->di_type),
-                    "maybe", di_file, line, 8, 8, child_type->size_in_bits, 0,
-                    child_type->di_type),
-        };
-        LLVMZigDIType *replacement_di_type = LLVMZigCreateDebugStructType(g->dbuilder,
-                compile_unit_scope,
-                buf_ptr(&entry->name),
-                di_file, line, entry->size_in_bits, entry->align_in_bits, 0,
-                nullptr, di_element_types, 2, 0, nullptr, "");
+            LLVMZigDIScope *compile_unit_scope = LLVMZigCompileUnitToScope(g->compile_unit);
+            LLVMZigDIFile *di_file = nullptr;
+            unsigned line = 0;
+            entry->di_type = LLVMZigCreateReplaceableCompositeType(g->dbuilder,
+                LLVMZigTag_DW_structure_type(), buf_ptr(&entry->name),
+                compile_unit_scope, di_file, line);
 
-        LLVMZigReplaceTemporary(g->dbuilder, entry->di_type, replacement_di_type);
-        entry->di_type = replacement_di_type;
+            LLVMZigDIType *di_element_types[] = {
+                LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(entry->di_type),
+                        "val", di_file, line, child_type->size_in_bits, child_type->align_in_bits, 0, 0,
+                        child_type->di_type),
+                LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(entry->di_type),
+                        "maybe", di_file, line, 8, 8, child_type->size_in_bits, 0,
+                        child_type->di_type),
+            };
+            LLVMZigDIType *replacement_di_type = LLVMZigCreateDebugStructType(g->dbuilder,
+                    compile_unit_scope,
+                    buf_ptr(&entry->name),
+                    di_file, line, entry->size_in_bits, entry->align_in_bits, 0,
+                    nullptr, di_element_types, 2, 0, nullptr, "");
+
+            LLVMZigReplaceTemporary(g->dbuilder, entry->di_type, replacement_di_type);
+            entry->di_type = replacement_di_type;
+        }
 
         entry->data.maybe.child_type = child_type;
 
@@ -5078,12 +5089,13 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
              return false;
         case TypeTableEntryIdArray:
         case TypeTableEntryIdStruct:
-        case TypeTableEntryIdMaybe:
              return true;
         case TypeTableEntryIdErrorUnion:
              return type_entry->data.error.child_type->size_in_bits > 0;
         case TypeTableEntryIdEnum:
              return type_entry->data.enumeration.gen_field_count != 0;
+        case TypeTableEntryIdMaybe:
+             return type_entry->data.maybe.child_type->id != TypeTableEntryIdPointer;
     }
     zig_unreachable();
 }
src/codegen.cpp
@@ -74,7 +74,7 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *expr_node);
 static LLVMValueRef gen_lvalue(CodeGen *g, AstNode *expr_node, AstNode *node, TypeTableEntry **out_type_entry);
 static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node, bool is_lvalue);
 static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVariableDeclaration *var_decl,
-        bool unwrap_maybe, LLVMValueRef *init_val);
+        bool unwrap_maybe, LLVMValueRef *init_val, TypeTableEntry **init_val_type);
 static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node, BinOpType bin_op,
         LLVMValueRef target_ref, LLVMValueRef value,
         TypeTableEntry *op1_type, TypeTableEntry *op2_type);
@@ -389,14 +389,20 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) {
                 assert(wanted_type->id == TypeTableEntryIdMaybe);
                 assert(actual_type);
 
-                add_debug_source_node(g, node);
-                LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 0, "");
-                gen_assign_raw(g, node, BinOpTypeAssign,
-                        val_ptr, expr_val, wanted_type->data.maybe.child_type, actual_type);
+                TypeTableEntry *child_type = wanted_type->data.maybe.child_type;
 
-                add_debug_source_node(g, node);
-                LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 1, "");
-                LLVMBuildStore(g->builder, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr);
+                if (child_type->id == TypeTableEntryIdPointer) {
+                    return expr_val;
+                } else {
+                    add_debug_source_node(g, node);
+                    LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 0, "");
+                    gen_assign_raw(g, node, BinOpTypeAssign,
+                            val_ptr, expr_val, child_type, actual_type);
+
+                    add_debug_source_node(g, node);
+                    LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 1, "");
+                    LLVMBuildStore(g->builder, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr);
+                }
 
                 return cast_expr->tmp_ptr;
             }
@@ -1245,10 +1251,20 @@ static LLVMValueRef gen_assign_expr(CodeGen *g, AstNode *node) {
 }
 
 static LLVMValueRef gen_unwrap_maybe(CodeGen *g, AstNode *node, LLVMValueRef maybe_struct_ref) {
-    add_debug_source_node(g, node);
-    LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 0, "");
-    // TODO if it's a struct we might not want to load the pointer
-    return LLVMBuildLoad(g->builder, maybe_field_ptr, "");
+    TypeTableEntry *type_entry = get_expr_type(node);
+    assert(type_entry->id == TypeTableEntryIdMaybe);
+    TypeTableEntry *child_type = type_entry->data.maybe.child_type;
+    if (child_type->id == TypeTableEntryIdPointer) {
+        return maybe_struct_ref;
+    } else {
+        add_debug_source_node(g, node);
+        LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 0, "");
+        if (handle_is_ptr(child_type)) {
+            return maybe_field_ptr;
+        } else {
+            return LLVMBuildLoad(g->builder, maybe_field_ptr, "");
+        }
+    }
 }
 
 static LLVMValueRef gen_unwrap_maybe_expr(CodeGen *g, AstNode *node) {
@@ -1260,29 +1276,32 @@ static LLVMValueRef gen_unwrap_maybe_expr(CodeGen *g, AstNode *node) {
 
     LLVMValueRef maybe_struct_ref = gen_expr(g, op1_node);
 
-    add_debug_source_node(g, node);
-    LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 1, "");
-    LLVMValueRef cond_value = LLVMBuildLoad(g->builder, maybe_field_ptr, "");
+    TypeTableEntry *maybe_type = get_expr_type(op1_node);
+    assert(maybe_type->id == TypeTableEntryIdMaybe);
+    TypeTableEntry *child_type = maybe_type->data.maybe.child_type;
+
+    LLVMValueRef cond_value;
+    if (child_type->id == TypeTableEntryIdPointer) {
+        cond_value = LLVMBuildICmp(g->builder, LLVMIntNE, maybe_struct_ref,
+                LLVMConstNull(child_type->type_ref), "");
+    } else {
+        add_debug_source_node(g, node);
+        LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 1, "");
+        cond_value = LLVMBuildLoad(g->builder, maybe_field_ptr, "");
+    }
 
     LLVMBasicBlockRef non_null_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeNonNull");
     LLVMBasicBlockRef null_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeNull");
-    LLVMBasicBlockRef end_block;
+    LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeEnd");
 
-    bool non_null_reachable = get_expr_type(op1_node)->id != TypeTableEntryIdUnreachable;
     bool null_reachable = get_expr_type(op2_node)->id != TypeTableEntryIdUnreachable;
-    bool end_reachable = non_null_reachable || null_reachable;
-    if (end_reachable) {
-        end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeEnd");
-    }
 
     LLVMBuildCondBr(g->builder, cond_value, non_null_block, null_block);
 
     LLVMPositionBuilderAtEnd(g->builder, non_null_block);
     LLVMValueRef non_null_result = gen_unwrap_maybe(g, op1_node, maybe_struct_ref);
-    if (non_null_reachable) {
-        add_debug_source_node(g, node);
-        LLVMBuildBr(g->builder, end_block);
-    }
+    add_debug_source_node(g, node);
+    LLVMBuildBr(g->builder, end_block);
     LLVMBasicBlockRef post_non_null_result_block = LLVMGetInsertBlock(g->builder);
 
     LLVMPositionBuilderAtEnd(g->builder, null_block);
@@ -1293,18 +1312,16 @@ static LLVMValueRef gen_unwrap_maybe_expr(CodeGen *g, AstNode *node) {
     }
     LLVMBasicBlockRef post_null_result_block = LLVMGetInsertBlock(g->builder);
 
-    if (end_reachable) {
-        LLVMPositionBuilderAtEnd(g->builder, end_block);
-        if (null_reachable) {
-            add_debug_source_node(g, node);
-            LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(non_null_result), "");
-            LLVMValueRef incoming_values[2] = {non_null_result, null_result};
-            LLVMBasicBlockRef incoming_blocks[2] = {post_non_null_result_block, post_null_result_block};
-            LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
-            return phi;
-        } else {
-            return non_null_result;
-        }
+    LLVMPositionBuilderAtEnd(g->builder, end_block);
+    if (null_reachable) {
+        add_debug_source_node(g, node);
+        LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(non_null_result), "");
+        LLVMValueRef incoming_values[2] = {non_null_result, null_result};
+        LLVMBasicBlockRef incoming_blocks[2] = {post_non_null_result_block, post_null_result_block};
+        LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2);
+        return phi;
+    } else {
+        return non_null_result;
     }
 
     return nullptr;
@@ -1607,12 +1624,20 @@ static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) {
     assert(node->data.if_var_expr.var_decl.expr);
 
     LLVMValueRef init_val;
-    gen_var_decl_raw(g, node, &node->data.if_var_expr.var_decl, true, &init_val);
+    TypeTableEntry *expr_type;
+    gen_var_decl_raw(g, node, &node->data.if_var_expr.var_decl, true, &init_val, &expr_type);
 
     // test if value is the maybe state
-    add_debug_source_node(g, node);
-    LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, init_val, 1, "");
-    LLVMValueRef cond_value = LLVMBuildLoad(g->builder, maybe_field_ptr, "");
+    assert(expr_type->id == TypeTableEntryIdMaybe);
+    TypeTableEntry *child_type = expr_type->data.maybe.child_type;
+    LLVMValueRef cond_value;
+    if (child_type->id == TypeTableEntryIdPointer) {
+        cond_value = LLVMBuildICmp(g->builder, LLVMIntNE, init_val, LLVMConstNull(child_type->type_ref), "");
+    } else {
+        add_debug_source_node(g, node);
+        LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, init_val, 1, "");
+        cond_value = LLVMBuildLoad(g->builder, maybe_field_ptr, "");
+    }
 
     LLVMValueRef return_value = gen_if_bool_expr_raw(g, node, cond_value,
             node->data.if_var_expr.then_block,
@@ -1978,7 +2003,7 @@ static LLVMValueRef gen_continue(CodeGen *g, AstNode *node) {
 }
 
 static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVariableDeclaration *var_decl,
-        bool unwrap_maybe, LLVMValueRef *init_value)
+        bool unwrap_maybe, LLVMValueRef *init_value, TypeTableEntry **expr_type)
 {
     VariableTableEntry *variable = var_decl->variable;
 
@@ -1987,6 +2012,7 @@ static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVa
 
     if (var_decl->expr) {
         *init_value = gen_expr(g, var_decl->expr);
+        *expr_type = get_expr_type(var_decl->expr);
     }
     if (variable->type->size_in_bits == 0) {
         return nullptr;
@@ -2005,7 +2031,7 @@ static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVa
         if (unwrap_maybe) {
             assert(var_decl->expr);
             assert(expr_type->id == TypeTableEntryIdMaybe);
-            value = gen_unwrap_maybe(g, source_node, *init_value);
+            value = gen_unwrap_maybe(g, var_decl->expr, *init_value);
             expr_type = expr_type->data.maybe.child_type;
         } else {
             value = *init_value;
@@ -2089,7 +2115,8 @@ static LLVMValueRef gen_var_decl_expr(CodeGen *g, AstNode *node) {
     }
 
     LLVMValueRef init_val;
-    return gen_var_decl_raw(g, node, &node->data.variable_declaration, false, &init_val);
+    TypeTableEntry *init_val_type;
+    return gen_var_decl_raw(g, node, &node->data.variable_declaration, false, &init_val, &init_val_type);
 }
 
 static LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) {
@@ -2100,11 +2127,7 @@ static LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) {
             return nullptr;
         } else if (variable->is_ptr) {
             assert(variable->value_ref);
-            if (variable->type->id == TypeTableEntryIdArray) {
-                return variable->value_ref;
-            } else if (variable->type->id == TypeTableEntryIdStruct ||
-                        variable->type->id == TypeTableEntryIdMaybe)
-            {
+            if (handle_is_ptr(variable->type)) {
                 return variable->value_ref;
             } else {
                 add_debug_source_node(g, node);
@@ -2330,20 +2353,28 @@ static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstE
         case TypeTableEntryIdMaybe:
             {
                 TypeTableEntry *child_type = type_entry->data.maybe.child_type;
-                LLVMValueRef child_val;
-                LLVMValueRef maybe_val;
-                if (const_val->data.x_maybe) {
-                    child_val = gen_const_val(g, child_type, const_val->data.x_maybe);
-                    maybe_val = LLVMConstAllOnes(LLVMInt1Type());
+                if (child_type->id == TypeTableEntryIdPointer) {
+                    if (const_val->data.x_maybe) {
+                        return gen_const_val(g, child_type, const_val->data.x_maybe);
+                    } else {
+                        return LLVMConstNull(child_type->type_ref);
+                    }
                 } else {
-                    child_val = LLVMConstNull(child_type->type_ref);
-                    maybe_val = LLVMConstNull(LLVMInt1Type());
+                    LLVMValueRef child_val;
+                    LLVMValueRef maybe_val;
+                    if (const_val->data.x_maybe) {
+                        child_val = gen_const_val(g, child_type, const_val->data.x_maybe);
+                        maybe_val = LLVMConstAllOnes(LLVMInt1Type());
+                    } else {
+                        child_val = LLVMConstNull(child_type->type_ref);
+                        maybe_val = LLVMConstNull(LLVMInt1Type());
+                    }
+                    LLVMValueRef fields[] = {
+                        child_val,
+                        maybe_val,
+                    };
+                    return LLVMConstStruct(fields, 2, false);
                 }
-                LLVMValueRef fields[] = {
-                    child_val,
-                    maybe_val,
-                };
-                return LLVMConstStruct(fields, 2, false);
             }
         case TypeTableEntryIdStruct:
             {
src/parseh.cpp
@@ -97,6 +97,14 @@ static AstNode *create_var_decl_node(Context *c, const char *var_name, AstNode *
     return node;
 }
 
+static AstNode *create_prefix_node(Context *c, PrefixOp op, AstNode *child_node) {
+    AstNode *node = create_node(c, NodeTypePrefixOpExpr);
+    node->data.prefix_op_expr.prefix_op = op;
+    node->data.prefix_op_expr.primary_expr = child_node;
+    normalize_parent_ptrs(node);
+    return node;
+}
+
 static const char *decl_name(const Decl *decl) {
     const NamedDecl *named_decl = static_cast<const NamedDecl *>(decl);
     return (const char *)named_decl->getName().bytes_begin();
@@ -132,11 +140,9 @@ static AstNode *pointer_to_type(Context *c, AstNode *type_node, bool is_const) {
     if (!type_node) {
         return nullptr;
     }
-    AstNode *node = create_node(c, NodeTypePrefixOpExpr);
-    node->data.prefix_op_expr.prefix_op = is_const ? PrefixOpConstAddressOf : PrefixOpAddressOf;
-    node->data.prefix_op_expr.primary_expr = convert_to_c_void(c, type_node);
-    normalize_parent_ptrs(node);
-    return node;
+    PrefixOp op = is_const ? PrefixOpConstAddressOf : PrefixOpAddressOf;
+    AstNode *child_node = create_prefix_node(c, op, convert_to_c_void(c, type_node));
+    return create_prefix_node(c, PrefixOpMaybe, child_node);
 }
 
 static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl) {
test/run_tests.cpp
@@ -1827,7 +1827,7 @@ pub const Foo = enum_Foo;)OUTPUT");
     add_parseh_case("restrict -> noalias", R"SOURCE(
 void foo(void *restrict bar, void *restrict);
     )SOURCE", R"OUTPUT(pub const c_void = u8;
-pub extern fn foo(noalias bar: &c_void, noalias arg1: &c_void);)OUTPUT");
+pub extern fn foo(noalias bar: ?&c_void, noalias arg1: ?&c_void);)OUTPUT");
 }
 
 static void print_compiler_invocation(TestCase *test_case) {
README.md
@@ -112,3 +112,8 @@ To fix this, you have 2 options:
 
  * Compile Zig with the same compiler that LLVM was compiled with.
  * Add `-DZIG_LLVM_OLD_CXX_ABI=yes` to the cmake configure line.
+
+## Community
+
+Zig is in its infancy. However one place you can gather to chat is the `#zig`
+IRC channel on Freenode.