Commit 1c15091bc8

Tadeo Kondrak <me@tadeo.ca>
2021-01-11 15:59:11
stage1: switch from inline fn to callconv(.Inline)
1 parent 5dfe0e7
lib/std/builtin.zig
@@ -155,6 +155,7 @@ pub const CallingConvention = enum {
     C,
     Naked,
     Async,
+    Inline,
     Interrupt,
     Signal,
     Stdcall,
@@ -404,21 +405,13 @@ pub const TypeInfo = union(enum) {
             /// therefore must be kept in sync with the compiler implementation.
             pub const FnDecl = struct {
                 fn_type: type,
-                inline_type: Inline,
+                is_noinline: bool,
                 is_var_args: bool,
                 is_extern: bool,
                 is_export: bool,
                 lib_name: ?[]const u8,
                 return_type: type,
                 arg_names: []const []const u8,
-
-                /// This data structure is used by the Zig language code generation and
-                /// therefore must be kept in sync with the compiler implementation.
-                pub const Inline = enum {
-                    Auto,
-                    Always,
-                    Never,
-                };
             };
         };
     };
src/stage1/all_types.hpp
@@ -74,6 +74,7 @@ enum CallingConvention {
     CallingConventionC,
     CallingConventionNaked,
     CallingConventionAsync,
+    CallingConventionInline,
     CallingConventionInterrupt,
     CallingConventionSignal,
     CallingConventionStdcall,
@@ -703,12 +704,6 @@ enum NodeType {
     NodeTypeAnyTypeField,
 };
 
-enum FnInline {
-    FnInlineAuto,
-    FnInlineAlways,
-    FnInlineNever,
-};
-
 struct AstNodeFnProto {
     Buf *name;
     ZigList<AstNode *> params;
@@ -725,13 +720,12 @@ struct AstNodeFnProto {
     AstNode *callconv_expr;
     Buf doc_comments;
 
-    FnInline fn_inline;
-
     VisibMod visib_mod;
     bool auto_err_set;
     bool is_var_args;
     bool is_extern;
     bool is_export;
+    bool is_noinline;
 };
 
 struct AstNodeFnDef {
@@ -1719,7 +1713,6 @@ struct ZigFn {
 
     LLVMValueRef valgrind_client_request_array;
 
-    FnInline fn_inline;
     FnAnalState anal_state;
 
     uint32_t align_bytes;
@@ -1728,6 +1721,7 @@ struct ZigFn {
     bool calls_or_awaits_errorable_fn;
     bool is_cold;
     bool is_test;
+    bool is_noinline;
 };
 
 uint32_t fn_table_entry_hash(ZigFn*);
src/stage1/analyze.cpp
@@ -973,6 +973,7 @@ const char *calling_convention_name(CallingConvention cc) {
         case CallingConventionAPCS: return "APCS";
         case CallingConventionAAPCS: return "AAPCS";
         case CallingConventionAAPCSVFP: return "AAPCSVFP";
+        case CallingConventionInline: return "Inline";
     }
     zig_unreachable();
 }
@@ -981,6 +982,7 @@ bool calling_convention_allows_zig_types(CallingConvention cc) {
     switch (cc) {
         case CallingConventionUnspecified:
         case CallingConventionAsync:
+        case CallingConventionInline:
             return true;
         case CallingConventionC:
         case CallingConventionNaked:
@@ -1007,7 +1009,8 @@ ZigType *get_stack_trace_type(CodeGen *g) {
 }
 
 bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id) {
-    if (fn_type_id->cc == CallingConventionUnspecified) {
+    if (fn_type_id->cc == CallingConventionUnspecified
+        || fn_type_id->cc == CallingConventionInline) {
         return handle_is_ptr(g, fn_type_id->return_type);
     }
     if (fn_type_id->cc != CallingConventionC) {
@@ -1888,6 +1891,7 @@ Error emit_error_unless_callconv_allowed_for_target(CodeGen *g, AstNode *source_
         case CallingConventionC:
         case CallingConventionNaked:
         case CallingConventionAsync:
+        case CallingConventionInline:
             break;
         case CallingConventionInterrupt:
             if (g->zig_target->arch != ZigLLVM_x86
@@ -3587,7 +3591,7 @@ static void get_fully_qualified_decl_name(CodeGen *g, Buf *buf, Tld *tld, bool i
     }
 }
 
-static ZigFn *create_fn_raw(CodeGen *g, FnInline inline_value) {
+static ZigFn *create_fn_raw(CodeGen *g, bool is_noinline) {
     ZigFn *fn_entry = heap::c_allocator.create<ZigFn>();
     fn_entry->ir_executable = heap::c_allocator.create<IrExecutableSrc>();
 
@@ -3597,7 +3601,7 @@ static ZigFn *create_fn_raw(CodeGen *g, FnInline inline_value) {
     fn_entry->analyzed_executable.backward_branch_quota = &fn_entry->prealloc_backward_branch_quota;
     fn_entry->analyzed_executable.fn_entry = fn_entry;
     fn_entry->ir_executable->fn_entry = fn_entry;
-    fn_entry->fn_inline = inline_value;
+    fn_entry->is_noinline = is_noinline;
 
     return fn_entry;
 }
@@ -3606,7 +3610,7 @@ ZigFn *create_fn(CodeGen *g, AstNode *proto_node) {
     assert(proto_node->type == NodeTypeFnProto);
     AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
 
-    ZigFn *fn_entry = create_fn_raw(g, fn_proto->fn_inline);
+    ZigFn *fn_entry = create_fn_raw(g, fn_proto->is_noinline);
 
     fn_entry->proto_node = proto_node;
     fn_entry->body_node = (proto_node->data.fn_proto.fn_def_node == nullptr) ? nullptr :
@@ -3739,6 +3743,12 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
                     fn_table_entry->type_entry = g->builtin_types.entry_invalid;
                     tld_fn->base.resolution = TldResolutionInvalid;
                     return;
+                case CallingConventionInline:
+                    add_node_error(g, fn_def_node,
+                        buf_sprintf("exported function cannot be inline"));
+                    fn_table_entry->type_entry = g->builtin_types.entry_invalid;
+                    tld_fn->base.resolution = TldResolutionInvalid;
+                    return;
                 case CallingConventionC:
                 case CallingConventionNaked:
                 case CallingConventionInterrupt:
@@ -3774,7 +3784,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
             fn_table_entry->inferred_async_node = fn_table_entry->proto_node;
         }
     } else if (source_node->type == NodeTypeTestDecl) {
-        ZigFn *fn_table_entry = create_fn_raw(g, FnInlineAuto);
+        ZigFn *fn_table_entry = create_fn_raw(g, false);
 
         get_fully_qualified_decl_name(g, &fn_table_entry->symbol_name, &tld_fn->base, true);
 
src/stage1/ast_render.cpp
@@ -123,13 +123,8 @@ static const char *export_string(bool is_export) {
 //    zig_unreachable();
 //}
 
-static const char *inline_string(FnInline fn_inline) {
-    switch (fn_inline) {
-        case FnInlineAlways: return "inline ";
-        case FnInlineNever:  return "noinline ";
-        case FnInlineAuto:   return "";
-    }
-    zig_unreachable();
+static const char *inline_string(bool is_inline) {
+    return is_inline ? "inline" : "";
 }
 
 static const char *const_or_var_string(bool is_const) {
@@ -446,7 +441,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
                 const char *pub_str = visib_mod_string(node->data.fn_proto.visib_mod);
                 const char *extern_str = extern_string(node->data.fn_proto.is_extern);
                 const char *export_str = export_string(node->data.fn_proto.is_export);
-                const char *inline_str = inline_string(node->data.fn_proto.fn_inline);
+                const char *inline_str = inline_string(node->data.fn_proto.is_noinline);
                 fprintf(ar->f, "%s%s%s%sfn ", pub_str, inline_str, export_str, extern_str);
                 if (node->data.fn_proto.name != nullptr) {
                     print_symbol(ar, node->data.fn_proto.name);
src/stage1/codegen.cpp
@@ -159,6 +159,7 @@ static const char *get_mangled_name(CodeGen *g, const char *original_name) {
 static ZigLLVM_CallingConv get_llvm_cc(CodeGen *g, CallingConvention cc) {
     switch (cc) {
         case CallingConventionUnspecified:
+        case CallingConventionInline:
             return ZigLLVM_Fast;
         case CallingConventionC:
             return ZigLLVM_C;
@@ -350,6 +351,7 @@ static bool cc_want_sret_attr(CallingConvention cc) {
             return true;
         case CallingConventionAsync:
         case CallingConventionUnspecified:
+        case CallingConventionInline:
             return false;
     }
     zig_unreachable();
@@ -452,20 +454,11 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) {
         }
     }
 
-    switch (fn->fn_inline) {
-        case FnInlineAlways:
-            addLLVMFnAttr(llvm_fn, "alwaysinline");
-            g->inline_fns.append(fn);
-            break;
-        case FnInlineNever:
-            addLLVMFnAttr(llvm_fn, "noinline");
-            break;
-        case FnInlineAuto:
-            if (fn->alignstack_value != 0) {
-                addLLVMFnAttr(llvm_fn, "noinline");
-            }
-            break;
-    }
+    if (cc == CallingConventionInline)
+        addLLVMFnAttr(llvm_fn, "alwaysinline");
+
+    if (fn->is_noinline || (cc != CallingConventionInline && fn->alignstack_value != 0))
+        addLLVMFnAttr(llvm_fn, "noinline");
 
     if (cc == CallingConventionNaked) {
         addLLVMFnAttr(llvm_fn, "naked");
@@ -532,7 +525,7 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) {
     addLLVMFnAttr(llvm_fn, "nounwind");
     add_uwtable_attr(g, llvm_fn);
     addLLVMFnAttr(llvm_fn, "nobuiltin");
-    if (codegen_have_frame_pointer(g) && fn->fn_inline != FnInlineAlways) {
+    if (codegen_have_frame_pointer(g) && cc != CallingConventionInline) {
         ZigLLVMAddFunctionAttr(llvm_fn, "frame-pointer", "all");
     }
     if (fn->section_name) {
@@ -9043,19 +9036,16 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
     static_assert(CallingConventionC == 1, "");
     static_assert(CallingConventionNaked == 2, "");
     static_assert(CallingConventionAsync == 3, "");
-    static_assert(CallingConventionInterrupt == 4, "");
-    static_assert(CallingConventionSignal == 5, "");
-    static_assert(CallingConventionStdcall == 6, "");
-    static_assert(CallingConventionFastcall == 7, "");
-    static_assert(CallingConventionVectorcall == 8, "");
-    static_assert(CallingConventionThiscall == 9, "");
-    static_assert(CallingConventionAPCS == 10, "");
-    static_assert(CallingConventionAAPCS == 11, "");
-    static_assert(CallingConventionAAPCSVFP == 12, "");
-
-    static_assert(FnInlineAuto == 0, "");
-    static_assert(FnInlineAlways == 1, "");
-    static_assert(FnInlineNever == 2, "");
+    static_assert(CallingConventionInline == 4, "");
+    static_assert(CallingConventionInterrupt == 5, "");
+    static_assert(CallingConventionSignal == 6, "");
+    static_assert(CallingConventionStdcall == 7, "");
+    static_assert(CallingConventionFastcall == 8, "");
+    static_assert(CallingConventionVectorcall == 9, "");
+    static_assert(CallingConventionThiscall == 10, "");
+    static_assert(CallingConventionAPCS == 11, "");
+    static_assert(CallingConventionAAPCS == 12, "");
+    static_assert(CallingConventionAAPCSVFP == 13, "");
 
     static_assert(BuiltinPtrSizeOne == 0, "");
     static_assert(BuiltinPtrSizeMany == 1, "");
src/stage1/ir.cpp
@@ -19000,7 +19000,7 @@ static IrInstGen *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstSrcDeclV
             } else if (init_val->type->id == ZigTypeIdFn &&
                 init_val->special != ConstValSpecialUndef &&
                 init_val->data.x_ptr.special == ConstPtrSpecialFunction &&
-                init_val->data.x_ptr.data.fn.fn_entry->fn_inline == FnInlineAlways)
+                init_val->data.x_ptr.data.fn.fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionInline)
             {
                 var_class_requires_const = true;
                 if (!var->src_is_const && !is_comptime_var) {
@@ -19182,6 +19182,11 @@ static IrInstGen *ir_analyze_instruction_export(IrAnalyze *ira, IrInstSrcExport
                         buf_sprintf("exported function cannot be async"));
                     add_error_note(ira->codegen, msg, fn_entry->proto_node, buf_sprintf("declared here"));
                 } break;
+                case CallingConventionInline: {
+                    ErrorMsg *msg = ir_add_error(ira, &target->base,
+                        buf_sprintf("exported function cannot be inline"));
+                    add_error_note(ira->codegen, msg, fn_entry->proto_node, buf_sprintf("declared here"));
+                } break;
                 case CallingConventionC:
                 case CallingConventionNaked:
                 case CallingConventionInterrupt:
@@ -21120,7 +21125,7 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
     if (type_is_invalid(return_type))
         return ira->codegen->invalid_inst_gen;
 
-    if (fn_entry != nullptr && fn_entry->fn_inline == FnInlineAlways && modifier == CallModifierNeverInline) {
+    if (fn_entry != nullptr && fn_type_id->cc == CallingConventionInline && modifier == CallModifierNeverInline) {
         ir_add_error(ira, source_instr,
             buf_sprintf("no-inline call of inline function"));
         return ira->codegen->invalid_inst_gen;
@@ -25219,10 +25224,6 @@ static Error ir_make_type_info_decls(IrAnalyze *ira, IrInst* source_instr, ZigVa
     if ((err = type_resolve(ira->codegen, type_info_fn_decl_type, ResolveStatusSizeKnown)))
         return err;
 
-    ZigType *type_info_fn_decl_inline_type = ir_type_info_get_type(ira, "Inline", type_info_fn_decl_type);
-    if ((err = type_resolve(ira->codegen, type_info_fn_decl_inline_type, ResolveStatusSizeKnown)))
-        return err;
-
     resolve_container_usingnamespace_decls(ira->codegen, decls_scope);
 
     // The unresolved declarations are collected in a separate queue to avoid
@@ -25365,11 +25366,11 @@ static Error ir_make_type_info_decls(IrAnalyze *ira, IrInst* source_instr, ZigVa
                     fn_decl_fields[0]->special = ConstValSpecialStatic;
                     fn_decl_fields[0]->type = ira->codegen->builtin_types.entry_type;
                     fn_decl_fields[0]->data.x_type = fn_entry->type_entry;
-                    // inline_type: Data.FnDecl.Inline
-                    ensure_field_index(fn_decl_val->type, "inline_type", 1);
+                    // is_noinline: bool
+                    ensure_field_index(fn_decl_val->type, "is_noinline", 1);
                     fn_decl_fields[1]->special = ConstValSpecialStatic;
-                    fn_decl_fields[1]->type = type_info_fn_decl_inline_type;
-                    bigint_init_unsigned(&fn_decl_fields[1]->data.x_enum_tag, fn_entry->fn_inline);
+                    fn_decl_fields[1]->type = ira->codegen->builtin_types.entry_bool;
+                    fn_decl_fields[1]->data.x_bool = fn_entry->is_noinline;
                     // is_var_args: bool
                     ensure_field_index(fn_decl_val->type, "is_var_args", 2);
                     bool is_varargs = fn_node->is_var_args;
@@ -30957,7 +30958,7 @@ static IrInstGen *ir_analyze_instruction_set_align_stack(IrAnalyze *ira, IrInstS
         return ira->codegen->invalid_inst_gen;
     }
 
-    if (fn_entry->fn_inline == FnInlineAlways) {
+    if (fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionInline) {
         ir_add_error(ira, &instruction->base.base, buf_sprintf("@setAlignStack in inline function"));
         return ira->codegen->invalid_inst_gen;
     }
src/stage1/parser.cpp
@@ -693,8 +693,6 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, B
     Token *first = eat_token_if(pc, TokenIdKeywordExport);
     if (first == nullptr)
         first = eat_token_if(pc, TokenIdKeywordExtern);
-    if (first == nullptr)
-        first = eat_token_if(pc, TokenIdKeywordInline);
     if (first == nullptr)
         first = eat_token_if(pc, TokenIdKeywordNoInline);
     if (first != nullptr) {
@@ -702,7 +700,7 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, B
         if (first->id == TokenIdKeywordExtern)
             lib_name = eat_token_if(pc, TokenIdStringLiteral);
 
-        if (first->id != TokenIdKeywordInline && first->id != TokenIdKeywordNoInline) {
+        if (first->id != TokenIdKeywordNoInline) {
             Token *thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal);
             AstNode *var_decl = ast_parse_var_decl(pc);
             if (var_decl != nullptr) {
@@ -739,17 +737,8 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, B
             if (!fn_proto->data.fn_proto.is_extern)
                 fn_proto->data.fn_proto.is_extern = first->id == TokenIdKeywordExtern;
             fn_proto->data.fn_proto.is_export = first->id == TokenIdKeywordExport;
-            switch (first->id) {
-                case TokenIdKeywordInline:
-                    fn_proto->data.fn_proto.fn_inline = FnInlineAlways;
-                    break;
-                case TokenIdKeywordNoInline:
-                    fn_proto->data.fn_proto.fn_inline = FnInlineNever;
-                    break;
-                default:
-                    fn_proto->data.fn_proto.fn_inline = FnInlineAuto;
-                    break;
-            }
+            if (first->id == TokenIdKeywordNoInline)
+                fn_proto->data.fn_proto.is_noinline = true;
             fn_proto->data.fn_proto.lib_name = token_buf(lib_name);
 
             AstNode *res = fn_proto;