| ... | ... | @@ -2718,73 +2718,60 @@ pub const Object = struct { |
| 2718 | 2718 | @panic("TODO: LLVM backend lower async function"); |
| 2719 | 2719 | } |
| 2720 | 2720 | |
| 2721 | | { |
| 2722 | | const cc_info = toLlvmCallConv(fn_info.cc, target).?; |
| 2721 | const cc_info = toLlvmCallConv(fn_info.cc, target).?; |
| 2723 | 2722 | |
| 2724 | | function_index.setCallConv(cc_info.llvm_cc, &o.builder); |
| 2723 | function_index.setCallConv(cc_info.llvm_cc, &o.builder); |
| 2725 | 2724 | |
| 2726 | | if (cc_info.align_stack) { |
| 2727 | | try attributes.addFnAttr(.{ .alignstack = .wrap(.fromByteUnits(target.stackAlignment())) }, &o.builder); |
| 2728 | | } else { |
| 2729 | | _ = try attributes.removeFnAttr(.alignstack); |
| 2730 | | } |
| 2731 | | |
| 2732 | | if (cc_info.naked) { |
| 2733 | | try attributes.addFnAttr(.naked, &o.builder); |
| 2734 | | } else { |
| 2735 | | _ = try attributes.removeFnAttr(.naked); |
| 2736 | | } |
| 2725 | if (cc_info.align_stack) { |
| 2726 | try attributes.addFnAttr(.{ .alignstack = .wrap(.fromByteUnits(target.stackAlignment())) }, &o.builder); |
| 2727 | } |
| 2737 | 2728 | |
| 2738 | | for (0..cc_info.inreg_param_count) |param_idx| { |
| 2739 | | try attributes.addParamAttr(param_idx, .inreg, &o.builder); |
| 2740 | | } |
| 2741 | | for (cc_info.inreg_param_count..std.math.maxInt(u2)) |param_idx| { |
| 2742 | | _ = try attributes.removeParamAttr(param_idx, .inreg); |
| 2743 | | } |
| 2729 | if (cc_info.naked) { |
| 2730 | try attributes.addFnAttr(.naked, &o.builder); |
| 2731 | } |
| 2744 | 2732 | |
| 2745 | | switch (fn_info.cc) { |
| 2746 | | inline .riscv64_interrupt, |
| 2747 | | .riscv32_interrupt, |
| 2748 | | .mips_interrupt, |
| 2749 | | .mips64_interrupt, |
| 2750 | | => |info| { |
| 2751 | | try attributes.addFnAttr(.{ .string = .{ |
| 2752 | | .kind = try o.builder.string("interrupt"), |
| 2753 | | .value = try o.builder.string(@tagName(info.mode)), |
| 2754 | | } }, &o.builder); |
| 2755 | | }, |
| 2756 | | .arm_interrupt, |
| 2757 | | => |info| { |
| 2758 | | try attributes.addFnAttr(.{ .string = .{ |
| 2759 | | .kind = try o.builder.string("interrupt"), |
| 2760 | | .value = try o.builder.string(switch (info.type) { |
| 2761 | | .generic => "", |
| 2762 | | .irq => "IRQ", |
| 2763 | | .fiq => "FIQ", |
| 2764 | | .swi => "SWI", |
| 2765 | | .abort => "ABORT", |
| 2766 | | .undef => "UNDEF", |
| 2767 | | }), |
| 2768 | | } }, &o.builder); |
| 2769 | | }, |
| 2770 | | // these function attributes serve as a backup against any mistakes LLVM makes. |
| 2771 | | // clang sets both the function's calling convention and the function attributes |
| 2772 | | // in its backend, so future patches to the AVR backend could end up checking only one, |
| 2773 | | // possibly breaking our support. it's safer to just emit both. |
| 2774 | | .avr_interrupt, .avr_signal, .csky_interrupt => { |
| 2775 | | try attributes.addFnAttr(.{ .string = .{ |
| 2776 | | .kind = try o.builder.string(switch (fn_info.cc) { |
| 2777 | | .avr_interrupt, |
| 2778 | | .csky_interrupt, |
| 2779 | | => "interrupt", |
| 2780 | | .avr_signal => "signal", |
| 2781 | | else => unreachable, |
| 2782 | | }), |
| 2783 | | .value = .empty, |
| 2784 | | } }, &o.builder); |
| 2785 | | }, |
| 2786 | | else => {}, |
| 2787 | | } |
| 2733 | switch (fn_info.cc) { |
| 2734 | inline .riscv64_interrupt, |
| 2735 | .riscv32_interrupt, |
| 2736 | .mips_interrupt, |
| 2737 | .mips64_interrupt, |
| 2738 | => |info| { |
| 2739 | try attributes.addFnAttr(.{ .string = .{ |
| 2740 | .kind = try o.builder.string("interrupt"), |
| 2741 | .value = try o.builder.string(@tagName(info.mode)), |
| 2742 | } }, &o.builder); |
| 2743 | }, |
| 2744 | .arm_interrupt, |
| 2745 | => |info| { |
| 2746 | try attributes.addFnAttr(.{ .string = .{ |
| 2747 | .kind = try o.builder.string("interrupt"), |
| 2748 | .value = try o.builder.string(switch (info.type) { |
| 2749 | .generic => "", |
| 2750 | .irq => "IRQ", |
| 2751 | .fiq => "FIQ", |
| 2752 | .swi => "SWI", |
| 2753 | .abort => "ABORT", |
| 2754 | .undef => "UNDEF", |
| 2755 | }), |
| 2756 | } }, &o.builder); |
| 2757 | }, |
| 2758 | // these function attributes serve as a backup against any mistakes LLVM makes. |
| 2759 | // clang sets both the function's calling convention and the function attributes |
| 2760 | // in its backend, so future patches to the AVR backend could end up checking only one, |
| 2761 | // possibly breaking our support. it's safer to just emit both. |
| 2762 | .avr_interrupt, .avr_signal, .csky_interrupt => { |
| 2763 | try attributes.addFnAttr(.{ .string = .{ |
| 2764 | .kind = try o.builder.string(switch (fn_info.cc) { |
| 2765 | .avr_interrupt, |
| 2766 | .csky_interrupt, |
| 2767 | => "interrupt", |
| 2768 | .avr_signal => "signal", |
| 2769 | else => unreachable, |
| 2770 | }), |
| 2771 | .value = .empty, |
| 2772 | } }, &o.builder); |
| 2773 | }, |
| 2774 | else => {}, |
| 2788 | 2775 | } |
| 2789 | 2776 | |
| 2790 | 2777 | // Function attributes that are independent of analysis results of the function body. |
| ... | ... | @@ -2821,6 +2808,10 @@ pub const Object = struct { |
| 2821 | 2808 | try attributes.addParamAttr(it.llvm_index, .nonnull, &o.builder); |
| 2822 | 2809 | it.llvm_index += 1; |
| 2823 | 2810 | } |
| 2811 | |
| 2812 | var remaining_inreg_int = cc_info.inreg_int_params; |
| 2813 | var remaining_inreg_float = cc_info.inreg_float_params; |
| 2814 | |
| 2824 | 2815 | while (try it.next()) |lowering| switch (lowering) { |
| 2825 | 2816 | .byval => { |
| 2826 | 2817 | const param_index = it.zig_index - 1; |
| ... | ... | @@ -2828,6 +2819,21 @@ pub const Object = struct { |
| 2828 | 2819 | if (!isByRef(param_ty, zcu)) { |
| 2829 | 2820 | try o.addByValParamAttrs(pt, &attributes, param_ty, param_index, fn_info, it.llvm_index - 1); |
| 2830 | 2821 | } |
| 2822 | |
| 2823 | if (remaining_inreg_int > 0 and |
| 2824 | (param_ty.isPtrAtRuntime(zcu) or |
| 2825 | (param_ty.isAbiInt(zcu) and param_ty.abiSize(zcu) <= Type.usize.abiSize(zcu)))) |
| 2826 | { |
| 2827 | try attributes.addParamAttr(it.llvm_index - 1, .inreg, &o.builder); |
| 2828 | remaining_inreg_int -= 1; |
| 2829 | } |
| 2830 | |
| 2831 | if (remaining_inreg_float > 0 and |
| 2832 | param_ty.zigTypeTag(zcu) == .float) |
| 2833 | { |
| 2834 | try attributes.addParamAttr(it.llvm_index - 1, .inreg, &o.builder); |
| 2835 | remaining_inreg_float -= 1; |
| 2836 | } |
| 2831 | 2837 | }, |
| 2832 | 2838 | .byref => { |
| 2833 | 2839 | const param_ty: Type = .fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); |
| ... | ... | @@ -4379,15 +4385,19 @@ const CallingConventionInfo = struct { |
| 4379 | 4385 | align_stack: bool, |
| 4380 | 4386 | /// Whether the function needs a `naked` attribute. |
| 4381 | 4387 | naked: bool, |
| 4382 | | /// How many leading parameters to apply the `inreg` attribute to. |
| 4383 | | inreg_param_count: u2 = 0, |
| 4388 | /// How many leading register-sized integer parameters to apply the `inreg` attribute to. |
| 4389 | inreg_int_params: u2 = 0, |
| 4390 | /// How many leading floating-point parameters to apply the `inreg` attribute to. |
| 4391 | inreg_float_params: u3 = 0, |
| 4384 | 4392 | }; |
| 4385 | 4393 | |
| 4386 | 4394 | pub fn toLlvmCallConv(cc: std.lang.CallingConvention, target: *const std.Target) ?CallingConventionInfo { |
| 4387 | 4395 | const llvm_cc = toLlvmCallConvTag(cc, target) orelse return null; |
| 4388 | | const incoming_stack_alignment: ?u64, const register_params: u2 = switch (cc) { |
| 4396 | const incoming_stack_alignment: ?u64, const inreg_int_params: u2, const inreg_float_params: u3 = switch (cc) { |
| 4397 | .x86_fastcall => |opts| .{ opts.incoming_stack_alignment, 2, 0 }, |
| 4398 | .x86_vectorcall => |opts| .{ opts.incoming_stack_alignment, 2, 6 }, |
| 4389 | 4399 | inline else => |pl| switch (@TypeOf(pl)) { |
| 4390 | | void => .{ null, 0 }, |
| 4400 | void => .{ null, 0, 0 }, |
| 4391 | 4401 | std.lang.CallingConvention.ArcInterruptOptions, |
| 4392 | 4402 | std.lang.CallingConvention.ArmInterruptOptions, |
| 4393 | 4403 | std.lang.CallingConvention.RiscvInterruptOptions, |
| ... | ... | @@ -4395,8 +4405,8 @@ pub fn toLlvmCallConv(cc: std.lang.CallingConvention, target: *const std.Target) |
| 4395 | 4405 | std.lang.CallingConvention.MicroblazeInterruptOptions, |
| 4396 | 4406 | std.lang.CallingConvention.MipsInterruptOptions, |
| 4397 | 4407 | std.lang.CallingConvention.CommonOptions, |
| 4398 | | => .{ pl.incoming_stack_alignment, 0 }, |
| 4399 | | std.lang.CallingConvention.X86RegparmOptions => .{ pl.incoming_stack_alignment, pl.register_params }, |
| 4408 | => .{ pl.incoming_stack_alignment, 0, 0 }, |
| 4409 | std.lang.CallingConvention.X86RegparmOptions => .{ pl.incoming_stack_alignment, pl.register_params, 0 }, |
| 4400 | 4410 | else => @compileError("TODO: toLlvmCallConv" ++ @tagName(pl)), |
| 4401 | 4411 | }, |
| 4402 | 4412 | }; |
| ... | ... | @@ -4407,7 +4417,8 @@ pub fn toLlvmCallConv(cc: std.lang.CallingConvention, target: *const std.Target) |
| 4407 | 4417 | break :need_align a < normal_stack_align; |
| 4408 | 4418 | } else false, |
| 4409 | 4419 | .naked = cc == .naked, |
| 4410 | | .inreg_param_count = register_params, |
| 4420 | .inreg_int_params = inreg_int_params, |
| 4421 | .inreg_float_params = inreg_float_params, |
| 4411 | 4422 | }; |
| 4412 | 4423 | } |
| 4413 | 4424 | pub fn toLlvmCallConvTag(cc_tag: std.lang.CallingConvention.Tag, target: *const std.Target) ?Builder.CallConv { |