diff --git a/src/AstGen.zig b/src/AstGen.zig index 252610aeeb1a..747321d365b7 100644 --- a/src/AstGen.zig +++ b/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, }); @@ -3404,7 +3405,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; @@ -3418,6 +3418,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()); @@ -3625,6 +3629,7 @@ fn fnDecl( .is_inferred_error = false, .is_test = false, .is_extern = true, + .is_noinline = is_noinline, .noalias_bits = noalias_bits, }); } else func: { @@ -3673,6 +3678,7 @@ fn fnDecl( .is_inferred_error = is_inferred_error, .is_test = false, .is_extern = false, + .is_noinline = is_noinline, .noalias_bits = noalias_bits, }); }; @@ -4108,6 +4114,7 @@ fn testDecl( .is_inferred_error = true, .is_test = true, .is_extern = false, + .is_noinline = false, .noalias_bits = 0, }); @@ -10191,6 +10198,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; @@ -10232,10 +10240,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 = &.{}; @@ -10268,6 +10275,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, diff --git a/src/Module.zig b/src/Module.zig index 4a5257a4f376..d711679a7102 100644 --- a/src/Module.zig +++ b/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 diff --git a/src/Sema.zig b/src/Sema.zig index 702991bf5e69..7ef787874d66 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7075,6 +7075,7 @@ fn zirFunc( src_locs, null, 0, + false, ); } @@ -7204,6 +7205,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 ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset }; const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = src_node_offset }; @@ -7350,6 +7352,10 @@ fn funcCommon( }); } + if (cc_workaround == .Inline and is_noinline) { + return sema.fail(block, cc_src, "callconv(.Inline) and noinline are incompatible together", .{}); + } + break :fn_ty try Type.Tag.function.create(sema.arena, .{ .param_types = param_types, .comptime_params = comptime_params.ptr, @@ -7430,6 +7436,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); @@ -17858,6 +17865,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, @@ -17875,6 +17883,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A src_locs, lib_name, noalias_bits, + is_noinline, ); } diff --git a/src/Zir.zig b/src/Zir.zig index b3126ea2f305..6751f2517689 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -2739,6 +2739,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, @@ -2751,7 +2752,7 @@ pub const Inst = struct { has_ret_ty_body: bool, has_lib_name: bool, has_any_noalias: bool, - _: u16 = undefined, + _: u15 = undefined, }; }; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 9dc20755ebf1..4d07e50777d4 100644 --- a/src/codegen/llvm.zig +++ b/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| { diff --git a/src/print_zir.zig b/src/print_zir.zig index 878fa0c14c18..d35bae14dab5 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -2000,6 +2000,7 @@ const Writer = struct { inferred_error_set, false, false, + false, .none, &.{}, @@ -2107,6 +2108,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, @@ -2266,6 +2268,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, @@ -2289,6 +2292,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});