authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-21 15:22:52-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-05-21 15:22:52-04:00
log1f5b0c11563d9ee0a284b708c7ca092da0a0b9ef
tree284bf8063e09513e67ee87252d569a15f7c225d1
parent800edb03b5dca7998941eaba016d57a5512bf337
parentf31f86a86a482267a524cdca59fb2b940ecadf25
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11689 from ziglang/llvm-cc

LLVM: rework calling convention lowering

2 files changed, 611 insertions(+), 212 deletions(-)

src/codegen/llvm.zig+608-212
......@@ -682,25 +682,141 @@ pub const Object = struct {
682682 else
683683 null;
684684
685 // This is the list of args we will use that correspond directly to the AIR arg
686 // instructions. Depending on the calling convention, this list is not necessarily
687 // a bijection with the actual LLVM parameters of the function.
685688 var args = std.ArrayList(*const llvm.Value).init(gpa);
686689 defer args.deinit();
687690
688 const param_offset = @as(c_uint, @boolToInt(ret_ptr != null)) + @boolToInt(err_return_tracing);
689 for (fn_info.param_types) |param_ty| {
690 if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
691 {
692 var llvm_arg_i = @as(c_uint, @boolToInt(ret_ptr != null)) + @boolToInt(err_return_tracing);
693 var it = iterateParamTypes(&dg, fn_info);
694 while (it.next()) |lowering| switch (lowering) {
695 .no_bits => continue,
696 .byval => {
697 const param_ty = fn_info.param_types[it.zig_index - 1];
698 const param = llvm_func.getParam(llvm_arg_i);
699 llvm_arg_i += 1;
700
701 if (isByRef(param_ty)) {
702 const alignment = param_ty.abiAlignment(target);
703 const param_llvm_ty = param.typeOf();
704 const arg_ptr = buildAllocaInner(builder, llvm_func, false, param_llvm_ty);
705 arg_ptr.setAlignment(alignment);
706 const store_inst = builder.buildStore(param, arg_ptr);
707 store_inst.setAlignment(alignment);
708 try args.append(arg_ptr);
709 } else {
710 try args.append(param);
711 }
712 },
713 .byref => {
714 const param_ty = fn_info.param_types[it.zig_index - 1];
715 const param = llvm_func.getParam(llvm_arg_i);
716 llvm_arg_i += 1;
691717
692 const llvm_arg_i = @intCast(c_uint, args.items.len) + param_offset;
693 const param = llvm_func.getParam(llvm_arg_i);
694 // It is possible for the calling convention to make the argument's by-reference nature
695 // disagree with our canonical value for it, in which case we must dereference here.
696 const need_deref = !param_ty.isPtrAtRuntime() and !isByRef(param_ty) and
697 (param.typeOf().getTypeKind() == .Pointer);
698 const loaded_param = if (!need_deref) param else l: {
699 const load_inst = builder.buildLoad(param, "");
700 load_inst.setAlignment(param_ty.abiAlignment(target));
701 break :l load_inst;
718 if (isByRef(param_ty)) {
719 try args.append(param);
720 } else {
721 const alignment = param_ty.abiAlignment(target);
722 const load_inst = builder.buildLoad(param, "");
723 load_inst.setAlignment(alignment);
724 try args.append(load_inst);
725 }
726 },
727 .abi_sized_int => {
728 const param_ty = fn_info.param_types[it.zig_index - 1];
729 const param = llvm_func.getParam(llvm_arg_i);
730 llvm_arg_i += 1;
731
732 const param_llvm_ty = try dg.llvmType(param_ty);
733 const abi_size = @intCast(c_uint, param_ty.abiSize(target));
734 const int_llvm_ty = dg.context.intType(abi_size * 8);
735 const int_ptr_llvm_ty = int_llvm_ty.pointerType(0);
736 const alignment = @maximum(
737 param_ty.abiAlignment(target),
738 dg.object.target_data.abiAlignmentOfType(int_llvm_ty),
739 );
740 const arg_ptr = buildAllocaInner(builder, llvm_func, false, param_llvm_ty);
741 arg_ptr.setAlignment(alignment);
742 const casted_ptr = builder.buildBitCast(arg_ptr, int_ptr_llvm_ty, "");
743 const store_inst = builder.buildStore(param, casted_ptr);
744 store_inst.setAlignment(alignment);
745
746 if (isByRef(param_ty)) {
747 try args.append(arg_ptr);
748 } else {
749 const load_inst = builder.buildLoad(arg_ptr, "");
750 load_inst.setAlignment(alignment);
751 try args.append(load_inst);
752 }
753 },
754 .multiple_llvm_ints => {
755 const param_ty = fn_info.param_types[it.zig_index - 1];
756 const llvm_ints = it.llvm_types_buffer[0..it.llvm_types_len];
757 const is_by_ref = isByRef(param_ty);
758 switch (param_ty.zigTypeTag()) {
759 .Struct => {
760 const fields = param_ty.structFields().values();
761 if (is_by_ref) {
762 const param_llvm_ty = try dg.llvmType(param_ty);
763 const arg_ptr = buildAllocaInner(builder, llvm_func, false, param_llvm_ty);
764 arg_ptr.setAlignment(param_ty.abiAlignment(target));
765
766 var field_i: u32 = 0;
767 var field_offset: u32 = 0;
768 for (llvm_ints) |int_bits| {
769 const param = llvm_func.getParam(llvm_arg_i);
770 llvm_arg_i += 1;
771
772 const big_int_ty = dg.context.intType(int_bits);
773 var bits_used: u32 = 0;
774 while (bits_used < int_bits) {
775 const field = fields[field_i];
776 const field_alignment = field.normalAlignment(target);
777 const prev_offset = field_offset;
778 field_offset = std.mem.alignForwardGeneric(u32, field_offset, field_alignment);
779 if (field_offset > prev_offset) {
780 // Padding counts as bits used.
781 bits_used += (field_offset - prev_offset) * 8;
782 if (bits_used >= int_bits) break;
783 }
784 const field_size = @intCast(u16, field.ty.abiSize(target));
785 const field_abi_bits = field_size * 8;
786 const field_int_ty = dg.context.intType(field_abi_bits);
787 const shifted = if (bits_used == 0) param else s: {
788 const shift_amt = big_int_ty.constInt(bits_used, .False);
789 break :s builder.buildLShr(param, shift_amt, "");
790 };
791 const field_as_int = builder.buildTrunc(shifted, field_int_ty, "");
792 var ty_buf: Type.Payload.Pointer = undefined;
793 const llvm_i = llvmFieldIndex(param_ty, field_i, target, &ty_buf).?;
794 const field_ptr = builder.buildStructGEP(arg_ptr, llvm_i, "");
795 const casted_ptr = builder.buildBitCast(field_ptr, field_int_ty.pointerType(0), "");
796 const store_inst = builder.buildStore(field_as_int, casted_ptr);
797 store_inst.setAlignment(field_alignment);
798
799 field_i += 1;
800 if (field_i >= fields.len) break;
801
802 bits_used += field_abi_bits;
803 field_offset += field_size;
804 }
805 if (field_i >= fields.len) break;
806 }
807
808 try args.append(arg_ptr);
809 } else {
810 @panic("TODO: LLVM backend: implement C calling convention on x86_64 with byval struct parameter");
811 }
812 },
813 .Union => {
814 @panic("TODO: LLVM backend: implement C calling convention on x86_64 with union parameter");
815 },
816 else => unreachable,
817 }
818 },
702819 };
703 try args.append(loaded_param);
704820 }
705821
706822 var di_file: ?*llvm.DIFile = null;
......@@ -2072,22 +2188,31 @@ pub const DeclGen = struct {
20722188 }
20732189
20742190 // Set parameter attributes.
2075 var llvm_param_i: c_uint = @as(c_uint, @boolToInt(sret)) + @boolToInt(err_return_tracing);
2076 for (fn_info.param_types) |param_ty| {
2077 if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
2078
2079 if (isByRef(param_ty)) {
2080 dg.addArgAttr(llvm_fn, llvm_param_i, "nonnull");
2081 // TODO readonly, noalias, align
2082 }
2083 llvm_param_i += 1;
2084 }
2085
20862191 // TODO: more attributes. see codegen.cpp `make_fn_llvm_value`.
2087 if (fn_info.cc == .Naked) {
2088 dg.addFnAttr(llvm_fn, "naked");
2089 } else {
2090 llvm_fn.setFunctionCallConv(toLlvmCallConv(fn_info.cc, target));
2192 switch (fn_info.cc) {
2193 .Unspecified, .Inline => {
2194 var llvm_param_i: c_uint = @as(c_uint, @boolToInt(sret)) + @boolToInt(err_return_tracing);
2195 for (fn_info.param_types) |param_ty| {
2196 if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
2197
2198 if (isByRef(param_ty)) {
2199 dg.addArgAttr(llvm_fn, llvm_param_i, "nonnull");
2200 // TODO readonly, noalias, align
2201 }
2202 llvm_param_i += 1;
2203 }
2204 llvm_fn.setFunctionCallConv(.Fast);
2205 },
2206 .Naked => {
2207 dg.addFnAttr(llvm_fn, "naked");
2208 },
2209 .Async => {
2210 llvm_fn.setFunctionCallConv(.Fast);
2211 @panic("TODO: LLVM backend lower async function");
2212 },
2213 else => {
2214 llvm_fn.setFunctionCallConv(toLlvmCallConv(fn_info.cc, target));
2215 },
20912216 }
20922217
20932218 if (fn_info.alignment != 0) {
......@@ -2518,42 +2643,7 @@ pub const DeclGen = struct {
25182643 llvm_union_ty.structSetBody(&llvm_fields, llvm_fields_len, .False);
25192644 return llvm_union_ty;
25202645 },
2521 .Fn => {
2522 const fn_info = t.fnInfo();
2523 const llvm_ret_ty = try lowerFnRetTy(dg, fn_info);
2524
2525 var llvm_params = std.ArrayList(*const llvm.Type).init(dg.gpa);
2526 defer llvm_params.deinit();
2527
2528 if (firstParamSRet(fn_info, target)) {
2529 const llvm_sret_ty = try dg.llvmType(fn_info.return_type);
2530 try llvm_params.append(llvm_sret_ty.pointerType(0));
2531 }
2532
2533 if (fn_info.return_type.isError() and
2534 dg.module.comp.bin_file.options.error_return_tracing)
2535 {
2536 var ptr_ty_payload: Type.Payload.ElemType = .{
2537 .base = .{ .tag = .single_mut_pointer },
2538 .data = dg.object.getStackTraceType(),
2539 };
2540 const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
2541 try llvm_params.append(try lowerFnParamTy(dg, fn_info.cc, ptr_ty));
2542 }
2543
2544 for (fn_info.param_types) |param_ty| {
2545 if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
2546
2547 try llvm_params.append(try lowerFnParamTy(dg, fn_info.cc, param_ty));
2548 }
2549
2550 return llvm.functionType(
2551 llvm_ret_ty,
2552 llvm_params.items.ptr,
2553 @intCast(c_uint, llvm_params.items.len),
2554 llvm.Bool.fromBool(fn_info.is_var_args),
2555 );
2556 },
2646 .Fn => return llvmTypeFn(dg, t),
25572647 .ComptimeInt => unreachable,
25582648 .ComptimeFloat => unreachable,
25592649 .Type => unreachable,
......@@ -2568,6 +2658,119 @@ pub const DeclGen = struct {
25682658 }
25692659 }
25702660
2661 fn llvmTypeFn(dg: *DeclGen, fn_ty: Type) Allocator.Error!*const llvm.Type {
2662 const target = dg.module.getTarget();
2663 const fn_info = fn_ty.fnInfo();
2664 const llvm_ret_ty = try lowerFnRetTy(dg, fn_info);
2665
2666 var llvm_params = std.ArrayList(*const llvm.Type).init(dg.gpa);
2667 defer llvm_params.deinit();
2668
2669 if (firstParamSRet(fn_info, target)) {
2670 const llvm_sret_ty = try dg.llvmType(fn_info.return_type);
2671 try llvm_params.append(llvm_sret_ty.pointerType(0));
2672 }
2673
2674 if (fn_info.return_type.isError() and
2675 dg.module.comp.bin_file.options.error_return_tracing)
2676 {
2677 var ptr_ty_payload: Type.Payload.ElemType = .{
2678 .base = .{ .tag = .single_mut_pointer },
2679 .data = dg.object.getStackTraceType(),
2680 };
2681 const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
2682 try llvm_params.append(try dg.llvmType(ptr_ty));
2683 }
2684
2685 var it = iterateParamTypes(dg, fn_info);
2686 while (it.next()) |lowering| switch (lowering) {
2687 .no_bits => continue,
2688 .byval => {
2689 const param_ty = fn_info.param_types[it.zig_index - 1];
2690 try llvm_params.append(try dg.llvmType(param_ty));
2691 },
2692 .byref => {
2693 const param_ty = fn_info.param_types[it.zig_index - 1];
2694 const raw_llvm_ty = try dg.llvmType(param_ty);
2695 try llvm_params.append(raw_llvm_ty.pointerType(0));
2696 },
2697 .abi_sized_int => {
2698 const param_ty = fn_info.param_types[it.zig_index - 1];
2699 const abi_size = @intCast(c_uint, param_ty.abiSize(target));
2700 try llvm_params.append(dg.context.intType(abi_size * 8));
2701 },
2702 .multiple_llvm_ints => {
2703 const param_ty = fn_info.param_types[it.zig_index - 1];
2704 const llvm_ints = it.llvm_types_buffer[0..it.llvm_types_len];
2705 try llvm_params.ensureUnusedCapacity(it.llvm_types_len);
2706
2707 // The reason we have all this logic instead of simply appending
2708 // big_int_ty is for the special case of a pointer type;
2709 // we want to use a pointer type instead of inttoptr at the callsites,
2710 // which may prevent optimization.
2711 switch (param_ty.zigTypeTag()) {
2712 .Struct => {
2713 const fields = param_ty.structFields().values();
2714 var field_i: u32 = 0;
2715 var field_offset: u32 = 0;
2716 llvm_arg: for (llvm_ints) |int_bits| {
2717 const big_int_ty = dg.context.intType(int_bits);
2718 var bits_used: u32 = 0;
2719 while (bits_used < int_bits) {
2720 const field = fields[field_i];
2721 const field_alignment = field.normalAlignment(target);
2722 const prev_offset = field_offset;
2723 field_offset = std.mem.alignForwardGeneric(u32, field_offset, field_alignment);
2724 if (field_offset > prev_offset) {
2725 // Padding counts as bits used.
2726 bits_used += (field_offset - prev_offset) * 8;
2727 if (bits_used >= int_bits) break;
2728 }
2729 const field_size = @intCast(u16, field.ty.abiSize(target));
2730 const field_abi_bits = field_size * 8;
2731
2732 // Special case for when the entire LLVM integer represents
2733 // one field; in this case keep the type information
2734 // to avoid the potentially costly ptrtoint/bitcast.
2735 if (bits_used == 0 and field_abi_bits == int_bits) {
2736 const llvm_field_ty = try dg.llvmType(field.ty);
2737 llvm_params.appendAssumeCapacity(llvm_field_ty);
2738 field_i += 1;
2739 if (field_i >= fields.len) {
2740 break :llvm_arg;
2741 } else {
2742 continue :llvm_arg;
2743 }
2744 }
2745
2746 field_i += 1;
2747 if (field_i >= fields.len) break;
2748
2749 bits_used += field_abi_bits;
2750 field_offset += field_size;
2751 }
2752 llvm_params.appendAssumeCapacity(big_int_ty);
2753 if (field_i >= fields.len) break;
2754 }
2755 },
2756 else => {
2757 for (llvm_ints) |int_bits| {
2758 const big_int_ty = dg.context.intType(int_bits);
2759 llvm_params.appendAssumeCapacity(big_int_ty);
2760 }
2761 },
2762 }
2763 },
2764 };
2765
2766 return llvm.functionType(
2767 llvm_ret_ty,
2768 llvm_params.items.ptr,
2769 @intCast(c_uint, llvm_params.items.len),
2770 llvm.Bool.fromBool(fn_info.is_var_args),
2771 );
2772 }
2773
25712774 fn genTypedValue(dg: *DeclGen, tv: TypedValue) Error!*const llvm.Value {
25722775 if (tv.val.isUndef()) {
25732776 const llvm_type = try dg.llvmType(tv.ty);
......@@ -3848,52 +4051,164 @@ pub const FuncGen = struct {
38484051 try llvm_args.append(self.err_ret_trace.?);
38494052 }
38504053
3851 for (args) |arg| {
3852 const param_ty = self.air.typeOf(arg);
3853 if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
3854
3855 const llvm_arg = try self.resolveInst(arg);
3856 const abi_llvm_ty = try lowerFnParamTy(self.dg, fn_info.cc, param_ty);
3857 const param_llvm_ty = llvm_arg.typeOf();
3858 if (abi_llvm_ty == param_llvm_ty) {
3859 try llvm_args.append(llvm_arg);
3860 continue;
3861 }
3862
3863 // In this case the function param type is honoring the calling convention
3864 // by having a different LLVM type than the usual one. We solve this here
3865 // at the callsite by bitcasting a pointer to our canonical type, then
3866 // loading it if necessary.
3867 const alignment = param_ty.abiAlignment(target);
3868 const ptr_abi_ty = abi_llvm_ty.pointerType(0);
3869
3870 const casted_ptr = if (isByRef(param_ty))
3871 self.builder.buildBitCast(llvm_arg, ptr_abi_ty, "")
3872 else p: {
3873 const arg_ptr = self.buildAlloca(param_llvm_ty);
3874 arg_ptr.setAlignment(alignment);
3875 const store_inst = self.builder.buildStore(llvm_arg, arg_ptr);
3876 store_inst.setAlignment(alignment);
3877
3878 if (abi_llvm_ty.getTypeKind() == .Pointer) {
3879 // In this case, the calling convention wants a pointer, but
3880 // we have a value.
3881 if (arg_ptr.typeOf() == abi_llvm_ty) {
3882 try llvm_args.append(arg_ptr);
3883 continue;
4054 var it = iterateParamTypes(self.dg, fn_info);
4055 while (it.next()) |lowering| switch (lowering) {
4056 .no_bits => continue,
4057 .byval => {
4058 const arg = args[it.zig_index - 1];
4059 const param_ty = self.air.typeOf(arg);
4060 const llvm_arg = try self.resolveInst(arg);
4061 if (isByRef(param_ty)) {
4062 const alignment = param_ty.abiAlignment(target);
4063 const load_inst = self.builder.buildLoad(llvm_arg, "");
4064 load_inst.setAlignment(alignment);
4065 try llvm_args.append(load_inst);
4066 } else {
4067 if (param_ty.zigTypeTag() == .Pointer) {
4068 // We need a bitcast in case of two possibilities:
4069 // 1. The parameter type is a pointer to zero-sized type,
4070 // which is always lowered to an LLVM type of `*i8`.
4071 // 2. The argument is a global which does act as a pointer, however
4072 // a bitcast is needed in order for the LLVM types to match.
4073 const llvm_param_ty = try self.dg.llvmType(param_ty);
4074 const casted_ptr = self.builder.buildBitCast(llvm_arg, llvm_param_ty, "");
4075 try llvm_args.append(casted_ptr);
4076 } else {
4077 try llvm_args.append(llvm_arg);
38844078 }
3885 const casted_ptr = self.builder.buildBitCast(arg_ptr, abi_llvm_ty, "");
3886 try llvm_args.append(casted_ptr);
3887 continue;
38884079 }
3889
3890 break :p self.builder.buildBitCast(arg_ptr, ptr_abi_ty, "");
3891 };
3892
3893 const load_inst = self.builder.buildLoad(casted_ptr, "");
3894 load_inst.setAlignment(alignment);
3895 try llvm_args.append(load_inst);
3896 }
4080 },
4081 .byref => {
4082 const arg = args[it.zig_index - 1];
4083 const param_ty = self.air.typeOf(arg);
4084 const llvm_arg = try self.resolveInst(arg);
4085 if (isByRef(param_ty)) {
4086 try llvm_args.append(llvm_arg);
4087 } else {
4088 const alignment = param_ty.abiAlignment(target);
4089 const param_llvm_ty = llvm_arg.typeOf();
4090 const arg_ptr = self.buildAlloca(param_llvm_ty);
4091 arg_ptr.setAlignment(alignment);
4092 const store_inst = self.builder.buildStore(llvm_arg, arg_ptr);
4093 store_inst.setAlignment(alignment);
4094 try llvm_args.append(arg_ptr);
4095 }
4096 },
4097 .abi_sized_int => {
4098 const arg = args[it.zig_index - 1];
4099 const param_ty = self.air.typeOf(arg);
4100 const llvm_arg = try self.resolveInst(arg);
4101 const abi_size = @intCast(c_uint, param_ty.abiSize(target));
4102 const int_llvm_ty = self.dg.context.intType(abi_size * 8);
4103 const int_ptr_llvm_ty = int_llvm_ty.pointerType(0);
4104
4105 if (isByRef(param_ty)) {
4106 const alignment = param_ty.abiAlignment(target);
4107 const casted_ptr = self.builder.buildBitCast(llvm_arg, int_ptr_llvm_ty, "");
4108 const load_inst = self.builder.buildLoad(casted_ptr, "");
4109 load_inst.setAlignment(alignment);
4110 try llvm_args.append(load_inst);
4111 } else {
4112 // LLVM does not allow bitcasting structs so we must allocate
4113 // a local, bitcast its pointer, store, and then load.
4114 const alignment = @maximum(
4115 param_ty.abiAlignment(target),
4116 self.dg.object.target_data.abiAlignmentOfType(int_llvm_ty),
4117 );
4118 const int_ptr = self.buildAlloca(int_llvm_ty);
4119 int_ptr.setAlignment(alignment);
4120 const param_llvm_ty = try self.dg.llvmType(param_ty);
4121 const casted_ptr = self.builder.buildBitCast(int_ptr, param_llvm_ty.pointerType(0), "");
4122 const store_inst = self.builder.buildStore(llvm_arg, casted_ptr);
4123 store_inst.setAlignment(alignment);
4124 const load_inst = self.builder.buildLoad(int_ptr, "");
4125 load_inst.setAlignment(alignment);
4126 try llvm_args.append(load_inst);
4127 }
4128 },
4129 .multiple_llvm_ints => {
4130 const arg = args[it.zig_index - 1];
4131 const param_ty = self.air.typeOf(arg);
4132 const llvm_ints = it.llvm_types_buffer[0..it.llvm_types_len];
4133 const llvm_arg = try self.resolveInst(arg);
4134 const is_by_ref = isByRef(param_ty);
4135 try llvm_args.ensureUnusedCapacity(it.llvm_types_len);
4136 switch (param_ty.zigTypeTag()) {
4137 .Struct => {
4138 const fields = param_ty.structFields().values();
4139 var field_i: u32 = 0;
4140 var field_offset: u32 = 0;
4141 for (llvm_ints) |int_bits| {
4142 const big_int_ty = self.dg.context.intType(int_bits);
4143 var int_arg: *const llvm.Value = undefined;
4144 var bits_used: u32 = 0;
4145 while (bits_used < int_bits) {
4146 const field = fields[field_i];
4147 const field_alignment = field.normalAlignment(target);
4148 const prev_offset = field_offset;
4149 field_offset = std.mem.alignForwardGeneric(u32, field_offset, field_alignment);
4150 if (field_offset > prev_offset) {
4151 // Padding counts as bits used.
4152 bits_used += (field_offset - prev_offset) * 8;
4153 if (bits_used >= int_bits) break;
4154 }
4155 var ty_buf: Type.Payload.Pointer = undefined;
4156 const llvm_i = llvmFieldIndex(param_ty, field_i, target, &ty_buf).?;
4157 const field_size = @intCast(u16, field.ty.abiSize(target));
4158 const field_abi_bits = field_size * 8;
4159
4160 // Special case for when the entire LLVM integer represents
4161 // one field; in this case keep the type information
4162 // to avoid the potentially costly ptrtoint/bitcast.
4163 if (bits_used == 0 and field_abi_bits == int_bits) {
4164 int_arg = if (is_by_ref) f: {
4165 const field_ptr = self.builder.buildStructGEP(llvm_arg, llvm_i, "");
4166 const load_inst = self.builder.buildLoad(field_ptr, "");
4167 load_inst.setAlignment(field_alignment);
4168 break :f load_inst;
4169 } else self.builder.buildExtractValue(llvm_arg, llvm_i, "");
4170 field_i += 1;
4171 break;
4172 }
4173
4174 const field_int_ty = self.dg.context.intType(field_abi_bits);
4175 const llvm_field = if (is_by_ref) f: {
4176 const field_ptr = self.builder.buildStructGEP(llvm_arg, llvm_i, "");
4177 const casted_ptr = self.builder.buildBitCast(field_ptr, field_int_ty.pointerType(0), "");
4178 const load_inst = self.builder.buildLoad(casted_ptr, "");
4179 load_inst.setAlignment(field_alignment);
4180 break :f load_inst;
4181 } else f: {
4182 const llvm_field = self.builder.buildExtractValue(llvm_arg, llvm_i, "");
4183 break :f self.builder.buildBitCast(llvm_field, field_int_ty, "");
4184 };
4185
4186 const extended = self.builder.buildZExt(llvm_field, big_int_ty, "");
4187 if (bits_used == 0) {
4188 int_arg = extended;
4189 } else {
4190 const shift_amt = big_int_ty.constInt(bits_used, .False);
4191 const shifted = self.builder.buildShl(extended, shift_amt, "");
4192 int_arg = self.builder.buildOr(int_arg, shifted, "");
4193 }
4194
4195 field_i += 1;
4196 if (field_i >= fields.len) break;
4197
4198 bits_used += field_abi_bits;
4199 field_offset += field_size;
4200 }
4201 llvm_args.appendAssumeCapacity(int_arg);
4202 if (field_i >= fields.len) break;
4203 }
4204 },
4205 .Union => {
4206 return self.todo("airCall C calling convention on x86_64 with union argument ", .{});
4207 },
4208 else => unreachable,
4209 }
4210 },
4211 };
38974212
38984213 const call = self.builder.buildCall(
38994214 llvm_fn,
......@@ -6489,24 +6804,7 @@ pub const FuncGen = struct {
64896804 /// Use this instead of builder.buildAlloca, because this function makes sure to
64906805 /// put the alloca instruction at the top of the function!
64916806 fn buildAlloca(self: *FuncGen, llvm_ty: *const llvm.Type) *const llvm.Value {
6492 const prev_block = self.builder.getInsertBlock();
6493 const prev_debug_location = self.builder.getCurrentDebugLocation2();
6494 defer {
6495 self.builder.positionBuilderAtEnd(prev_block);
6496 if (self.di_scope != null) {
6497 self.builder.setCurrentDebugLocation2(prev_debug_location);
6498 }
6499 }
6500
6501 const entry_block = self.llvm_func.getFirstBasicBlock().?;
6502 if (entry_block.getFirstInstruction()) |first_inst| {
6503 self.builder.positionBuilder(entry_block, first_inst);
6504 } else {
6505 self.builder.positionBuilderAtEnd(entry_block);
6506 }
6507 self.builder.clearCurrentDebugLocation();
6508
6509 return self.builder.buildAlloca(llvm_ty, "");
6807 return buildAllocaInner(self.builder, self.llvm_func, self.di_scope != null, llvm_ty);
65106808 }
65116809
65126810 fn airStore(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
......@@ -8296,99 +8594,171 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm.
82968594 }
82978595}
82988596
8299fn lowerFnParamTy(dg: *DeclGen, cc: std.builtin.CallingConvention, ty: Type) !*const llvm.Type {
8300 assert(ty.hasRuntimeBitsIgnoreComptime());
8301 const target = dg.module.getTarget();
8302 switch (cc) {
8303 .Unspecified, .Inline => {
8304 const raw_llvm_ty = try dg.llvmType(ty);
8305 if (isByRef(ty)) {
8306 return raw_llvm_ty.pointerType(0);
8307 } else {
8308 return raw_llvm_ty;
8309 }
8310 },
8311 .C => {
8312 const is_scalar = switch (ty.zigTypeTag()) {
8313 .Void,
8314 .Bool,
8315 .NoReturn,
8316 .Int,
8317 .Float,
8318 .Pointer,
8319 .Optional,
8320 .ErrorSet,
8321 .Enum,
8322 .AnyFrame,
8323 .Vector,
8324 => true,
8597const ParamTypeIterator = struct {
8598 dg: *DeclGen,
8599 fn_info: Type.Payload.Function.Data,
8600 zig_index: u32,
8601 llvm_index: u32,
8602 target: std.Target,
8603 llvm_types_len: u32,
8604 llvm_types_buffer: [8]u16,
8605
8606 const Lowering = enum {
8607 no_bits,
8608 byval,
8609 byref,
8610 abi_sized_int,
8611 multiple_llvm_ints,
8612 };
83258613
8326 else => false,
8327 };
8328 switch (target.cpu.arch) {
8329 .mips, .mipsel => return dg.llvmType(ty),
8330 .x86_64 => switch (target.os.tag) {
8331 .windows => switch (x86_64_abi.classifyWindows(ty, target)) {
8332 .integer => {
8614 fn next(it: *ParamTypeIterator) ?Lowering {
8615 if (it.zig_index >= it.fn_info.param_types.len) return null;
8616
8617 const ty = it.fn_info.param_types[it.zig_index];
8618 if (!ty.hasRuntimeBitsIgnoreComptime()) {
8619 it.zig_index += 1;
8620 return .no_bits;
8621 }
8622
8623 switch (it.fn_info.cc) {
8624 .Unspecified, .Inline => {
8625 it.zig_index += 1;
8626 it.llvm_index += 1;
8627 if (isByRef(ty)) {
8628 return .byref;
8629 } else {
8630 return .byval;
8631 }
8632 },
8633 .Async => {
8634 @panic("TODO implement async function lowering in the LLVM backend");
8635 },
8636 .C => {
8637 const is_scalar = switch (ty.zigTypeTag()) {
8638 .Void,
8639 .Bool,
8640 .NoReturn,
8641 .Int,
8642 .Float,
8643 .Pointer,
8644 .Optional,
8645 .ErrorSet,
8646 .Enum,
8647 .AnyFrame,
8648 .Vector,
8649 => true,
8650
8651 else => false,
8652 };
8653 switch (it.target.cpu.arch) {
8654 .mips, .mipsel => {
8655 it.zig_index += 1;
8656 it.llvm_index += 1;
8657 return .byval;
8658 },
8659 .x86_64 => switch (it.target.os.tag) {
8660 .windows => switch (x86_64_abi.classifyWindows(ty, it.target)) {
8661 .integer => {
8662 if (is_scalar) {
8663 it.zig_index += 1;
8664 it.llvm_index += 1;
8665 return .byval;
8666 } else {
8667 it.zig_index += 1;
8668 it.llvm_index += 1;
8669 return .abi_sized_int;
8670 }
8671 },
8672 .memory => {
8673 it.zig_index += 1;
8674 it.llvm_index += 1;
8675 return .byref;
8676 },
8677 .sse => {
8678 it.zig_index += 1;
8679 it.llvm_index += 1;
8680 return .byval;
8681 },
8682 else => unreachable,
8683 },
8684 else => {
83338685 if (is_scalar) {
8334 return dg.llvmType(ty);
8335 } else {
8336 const abi_size = ty.abiSize(target);
8337 return dg.context.intType(@intCast(c_uint, abi_size * 8));
8686 it.zig_index += 1;
8687 it.llvm_index += 1;
8688 return .byval;
8689 }
8690 const classes = x86_64_abi.classifySystemV(ty, it.target);
8691 if (classes[0] == .memory) {
8692 it.zig_index += 1;
8693 it.llvm_index += 1;
8694 return .byref;
8695 }
8696 var llvm_types_buffer: [8]u16 = undefined;
8697 var llvm_types_index: u32 = 0;
8698 for (classes) |class| {
8699 switch (class) {
8700 .integer => {
8701 llvm_types_buffer[llvm_types_index] = 64;
8702 llvm_types_index += 1;
8703 },
8704 .sse => {
8705 @panic("TODO");
8706 },
8707 .sseup => {
8708 @panic("TODO");
8709 },
8710 .x87 => {
8711 @panic("TODO");
8712 },
8713 .x87up => {
8714 @panic("TODO");
8715 },
8716 .complex_x87 => {
8717 @panic("TODO");
8718 },
8719 .memory => unreachable, // handled above
8720 .none => break,
8721 }
83388722 }
8723 if (classes[0] == .integer and classes[1] == .none) {
8724 it.zig_index += 1;
8725 it.llvm_index += 1;
8726 return .abi_sized_int;
8727 }
8728 it.llvm_types_buffer = llvm_types_buffer;
8729 it.llvm_types_len = llvm_types_index;
8730 it.llvm_index += llvm_types_index;
8731 it.zig_index += 1;
8732 return .multiple_llvm_ints;
83398733 },
8340 .memory => return (try dg.llvmType(ty)).pointerType(0),
8341 .sse => return dg.llvmType(ty),
8342 else => unreachable,
83438734 },
8735 // TODO investigate C ABI for other architectures
83448736 else => {
8345 if (is_scalar) {
8346 return dg.llvmType(ty);
8347 }
8348 const classes = x86_64_abi.classifySystemV(ty, target);
8349 if (classes[0] == .memory) {
8350 return (try dg.llvmType(ty)).pointerType(0);
8351 }
8352 var llvm_types_buffer: [8]*const llvm.Type = undefined;
8353 var llvm_types_index: u32 = 0;
8354 for (classes) |class| {
8355 switch (class) {
8356 .integer => {
8357 llvm_types_buffer[llvm_types_index] = dg.context.intType(64);
8358 llvm_types_index += 1;
8359 },
8360 .sse => {
8361 @panic("TODO");
8362 },
8363 .sseup => {
8364 @panic("TODO");
8365 },
8366 .x87 => {
8367 @panic("TODO");
8368 },
8369 .x87up => {
8370 @panic("TODO");
8371 },
8372 .complex_x87 => {
8373 @panic("TODO");
8374 },
8375 .memory => unreachable, // handled above
8376 .none => break,
8377 }
8378 }
8379 if (classes[0] == .integer and classes[1] == .none) {
8380 const abi_size = ty.abiSize(target);
8381 return dg.context.intType(@intCast(c_uint, abi_size * 8));
8382 }
8383 return dg.context.structType(&llvm_types_buffer, llvm_types_index, .False);
8737 it.zig_index += 1;
8738 it.llvm_index += 1;
8739 return .byval;
83848740 },
8385 },
8386 // TODO investigate C ABI for other architectures
8387 else => return dg.llvmType(ty),
8388 }
8389 },
8390 else => return dg.llvmType(ty),
8741 }
8742 },
8743 else => {
8744 it.zig_index += 1;
8745 it.llvm_index += 1;
8746 return .byval;
8747 },
8748 }
83918749 }
8750};
8751
8752fn iterateParamTypes(dg: *DeclGen, fn_info: Type.Payload.Function.Data) ParamTypeIterator {
8753 return .{
8754 .dg = dg,
8755 .fn_info = fn_info,
8756 .zig_index = 0,
8757 .llvm_index = 0,
8758 .target = dg.module.getTarget(),
8759 .llvm_types_buffer = undefined,
8760 .llvm_types_len = 0,
8761 };
83928762}
83938763
83948764fn isByRef(ty: Type) bool {
......@@ -8549,3 +8919,29 @@ fn compilerRtIntBits(bits: u16) u16 {
85498919 }
85508920 return bits;
85518921}
8922
8923fn buildAllocaInner(
8924 builder: *const llvm.Builder,
8925 llvm_func: *const llvm.Value,
8926 di_scope_non_null: bool,
8927 llvm_ty: *const llvm.Type,
8928) *const llvm.Value {
8929 const prev_block = builder.getInsertBlock();
8930 const prev_debug_location = builder.getCurrentDebugLocation2();
8931 defer {
8932 builder.positionBuilderAtEnd(prev_block);
8933 if (di_scope_non_null) {
8934 builder.setCurrentDebugLocation2(prev_debug_location);
8935 }
8936 }
8937
8938 const entry_block = llvm_func.getFirstBasicBlock().?;
8939 if (entry_block.getFirstInstruction()) |first_inst| {
8940 builder.positionBuilder(entry_block, first_inst);
8941 } else {
8942 builder.positionBuilderAtEnd(entry_block);
8943 }
8944 builder.clearCurrentDebugLocation();
8945
8946 return builder.buildAlloca(llvm_ty, "");
8947}
src/codegen/llvm/bindings.zig+3
......@@ -1019,6 +1019,9 @@ pub const TargetMachine = opaque {
10191019pub const TargetData = opaque {
10201020 pub const dispose = LLVMDisposeTargetData;
10211021 extern fn LLVMDisposeTargetData(*const TargetData) void;
1022
1023 pub const abiAlignmentOfType = LLVMABIAlignmentOfType;
1024 extern fn LLVMABIAlignmentOfType(TD: *const TargetData, Ty: *const Type) c_uint;
10221025};
10231026
10241027pub const CodeModel = enum(c_int) {