Commit dea437edfb

Meghan <hello@nektro.net>
2022-07-24 10:56:33
stage2: implement `noinline fn`
1 parent 903bed9
Changed files (7)
src/codegen/llvm.zig
@@ -699,6 +699,12 @@ pub const Object = struct {
             DeclGen.removeFnAttr(llvm_func, "cold");
         }
 
+        if (func.is_noinline) {
+            dg.addFnAttr(llvm_func, "noinline");
+        } else {
+            DeclGen.removeFnAttr(llvm_func, "noinline");
+        }
+
         // Remove all the basic blocks of a function in order to start over, generating
         // LLVM IR from an empty function body.
         while (llvm_func.getFirstBasicBlock()) |bb| {
src/AstGen.zig
@@ -1274,6 +1274,7 @@ fn fnProtoExpr(
         .is_inferred_error = false,
         .is_test = false,
         .is_extern = false,
+        .is_noinline = false,
         .noalias_bits = noalias_bits,
     });
 
@@ -3389,7 +3390,6 @@ fn fnDecl(
     };
     defer fn_gz.unstack();
 
-    // TODO: support noinline
     const is_pub = fn_proto.visib_token != null;
     const is_export = blk: {
         const maybe_export_token = fn_proto.extern_export_inline_token orelse break :blk false;
@@ -3403,6 +3403,10 @@ fn fnDecl(
         const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false;
         break :blk token_tags[maybe_inline_token] == .keyword_inline;
     };
+    const is_noinline = blk: {
+        const maybe_noinline_token = fn_proto.extern_export_inline_token orelse break :blk false;
+        break :blk token_tags[maybe_noinline_token] == .keyword_noinline;
+    };
 
     const doc_comment_index = try astgen.docCommentAsString(fn_proto.firstToken());
 
@@ -3610,6 +3614,7 @@ fn fnDecl(
             .is_inferred_error = false,
             .is_test = false,
             .is_extern = true,
+            .is_noinline = is_noinline,
             .noalias_bits = noalias_bits,
         });
     } else func: {
@@ -3658,6 +3663,7 @@ fn fnDecl(
             .is_inferred_error = is_inferred_error,
             .is_test = false,
             .is_extern = false,
+            .is_noinline = is_noinline,
             .noalias_bits = noalias_bits,
         });
     };
@@ -4093,6 +4099,7 @@ fn testDecl(
         .is_inferred_error = true,
         .is_test = true,
         .is_extern = false,
+        .is_noinline = false,
         .noalias_bits = 0,
     });
 
@@ -10175,6 +10182,7 @@ const GenZir = struct {
         is_inferred_error: bool,
         is_test: bool,
         is_extern: bool,
+        is_noinline: bool,
     }) !Zir.Inst.Ref {
         assert(args.src_node != 0);
         const astgen = gz.astgen;
@@ -10216,10 +10224,9 @@ const GenZir = struct {
         }
         const body_len = astgen.countBodyLenAfterFixups(body);
 
-        if (args.cc_ref != .none or args.lib_name != 0 or
-            args.is_var_args or args.is_test or args.is_extern or
-            args.align_ref != .none or args.section_ref != .none or
-            args.addrspace_ref != .none or args.noalias_bits != 0)
+        if (args.cc_ref != .none or args.lib_name != 0 or args.is_var_args or args.is_test or
+            args.is_extern or args.align_ref != .none or args.section_ref != .none or
+            args.addrspace_ref != .none or args.noalias_bits != 0 or args.is_noinline)
         {
             var align_body: []Zir.Inst.Index = &.{};
             var addrspace_body: []Zir.Inst.Index = &.{};
@@ -10252,6 +10259,7 @@ const GenZir = struct {
                     .is_inferred_error = args.is_inferred_error,
                     .is_test = args.is_test,
                     .is_extern = args.is_extern,
+                    .is_noinline = args.is_noinline,
                     .has_lib_name = args.lib_name != 0,
                     .has_any_noalias = args.noalias_bits != 0,
 
src/Module.zig
@@ -1488,7 +1488,7 @@ pub const Fn = struct {
     branch_quota: u32,
     state: Analysis,
     is_cold: bool = false,
-    is_noinline: bool = false,
+    is_noinline: bool,
     calls_or_awaits_errorable_fn: bool = false,
 
     /// Any inferred error sets that this function owns, both its own inferred error set and
src/print_zir.zig
@@ -1984,6 +1984,7 @@ const Writer = struct {
             inferred_error_set,
             false,
             false,
+            false,
 
             .none,
             &.{},
@@ -2091,6 +2092,7 @@ const Writer = struct {
             extra.data.bits.is_inferred_error,
             extra.data.bits.is_var_args,
             extra.data.bits.is_extern,
+            extra.data.bits.is_noinline,
             align_ref,
             align_body,
             addrspace_ref,
@@ -2250,6 +2252,7 @@ const Writer = struct {
         inferred_error_set: bool,
         var_args: bool,
         is_extern: bool,
+        is_noinline: bool,
         align_ref: Zir.Inst.Ref,
         align_body: []const Zir.Inst.Index,
         addrspace_ref: Zir.Inst.Ref,
@@ -2273,6 +2276,7 @@ const Writer = struct {
         try self.writeFlag(stream, "vargs, ", var_args);
         try self.writeFlag(stream, "extern, ", is_extern);
         try self.writeFlag(stream, "inferror, ", inferred_error_set);
+        try self.writeFlag(stream, "noinline, ", is_noinline);
 
         if (noalias_bits != 0) {
             try stream.print("noalias=0b{b}, ", .{noalias_bits});
src/Sema.zig
@@ -7252,6 +7252,7 @@ fn zirFunc(
         src_locs,
         null,
         0,
+        false,
     );
 }
 
@@ -7382,6 +7383,7 @@ fn funcCommon(
     src_locs: Zir.Inst.Func.SrcLocs,
     opt_lib_name: ?[]const u8,
     noalias_bits: u32,
+    is_noinline: bool,
 ) CompileError!Air.Inst.Ref {
     const fn_src = LazySrcLoc.nodeOffset(src_node_offset);
     const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset };
@@ -7577,6 +7579,10 @@ fn funcCommon(
             });
         }
 
+        if (cc_workaround == .Inline and is_noinline) {
+            return sema.fail(block, cc_src, "'noinline' function cannot have callconv 'Inline'", .{});
+        }
+
         break :fn_ty try Type.Tag.function.create(sema.arena, .{
             .param_types = param_types,
             .comptime_params = comptime_params.ptr,
@@ -7657,6 +7663,7 @@ fn funcCommon(
         .rbrace_column = @truncate(u16, src_locs.columns >> 16),
         .param_names = param_names,
         .branch_quota = default_branch_quota,
+        .is_noinline = is_noinline,
     };
     if (maybe_inferred_error_set_node) |node| {
         new_func.inferred_error_sets.prepend(node);
@@ -18443,6 +18450,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
     const is_var_args = extra.data.bits.is_var_args;
     const is_inferred_error = extra.data.bits.is_inferred_error;
     const is_extern = extra.data.bits.is_extern;
+    const is_noinline = extra.data.bits.is_noinline;
 
     return sema.funcCommon(
         block,
@@ -18460,6 +18468,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
         src_locs,
         lib_name,
         noalias_bits,
+        is_noinline,
     );
 }
 
src/Zir.zig
@@ -2736,6 +2736,7 @@ pub const Inst = struct {
             is_inferred_error: bool,
             is_test: bool,
             is_extern: bool,
+            is_noinline: bool,
             has_align_ref: bool,
             has_align_body: bool,
             has_addrspace_ref: bool,
@@ -2748,7 +2749,7 @@ pub const Inst = struct {
             has_ret_ty_body: bool,
             has_lib_name: bool,
             has_any_noalias: bool,
-            _: u16 = undefined,
+            _: u15 = undefined,
         };
     };
 
test/cases/compile_errors/noinline_fn_cc_inline.zig
@@ -0,0 +1,12 @@
+const cc = .Inline;
+noinline fn foo() callconv(cc) void {}
+
+comptime {
+    _ = foo;
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :2:28: error: 'noinline' function cannot have callconv 'Inline'