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 {...@@ -682,25 +682,141 @@ pub const Object = struct {
682 else682 else
683 null;683 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.
685 var args = std.ArrayList(*const llvm.Value).init(gpa);688 var args = std.ArrayList(*const llvm.Value).init(gpa);
686 defer args.deinit();689 defer args.deinit();
687690
688 const param_offset = @as(c_uint, @boolToInt(ret_ptr != null)) + @boolToInt(err_return_tracing);691 {
689 for (fn_info.param_types) |param_ty| {692 var llvm_arg_i = @as(c_uint, @boolToInt(ret_ptr != null)) + @boolToInt(err_return_tracing);
690 if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;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;718 if (isByRef(param_ty)) {
693 const param = llvm_func.getParam(llvm_arg_i);719 try args.append(param);
694 // It is possible for the calling convention to make the argument's by-reference nature720 } else {
695 // disagree with our canonical value for it, in which case we must dereference here.721 const alignment = param_ty.abiAlignment(target);
696 const need_deref = !param_ty.isPtrAtRuntime() and !isByRef(param_ty) and722 const load_inst = builder.buildLoad(param, "");
697 (param.typeOf().getTypeKind() == .Pointer);723 load_inst.setAlignment(alignment);
698 const loaded_param = if (!need_deref) param else l: {724 try args.append(load_inst);
699 const load_inst = builder.buildLoad(param, "");725 }
700 load_inst.setAlignment(param_ty.abiAlignment(target));726 },
701 break :l load_inst;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 },
702 };819 };
703 try args.append(loaded_param);
704 }820 }
705821
706 var di_file: ?*llvm.DIFile = null;822 var di_file: ?*llvm.DIFile = null;
...@@ -2072,22 +2188,31 @@ pub const DeclGen = struct {...@@ -2072,22 +2188,31 @@ pub const DeclGen = struct {
2072 }2188 }
20732189
2074 // Set parameter attributes.2190 // 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
2086 // TODO: more attributes. see codegen.cpp `make_fn_llvm_value`.2191 // TODO: more attributes. see codegen.cpp `make_fn_llvm_value`.
2087 if (fn_info.cc == .Naked) {2192 switch (fn_info.cc) {
2088 dg.addFnAttr(llvm_fn, "naked");2193 .Unspecified, .Inline => {
2089 } else {2194 var llvm_param_i: c_uint = @as(c_uint, @boolToInt(sret)) + @boolToInt(err_return_tracing);
2090 llvm_fn.setFunctionCallConv(toLlvmCallConv(fn_info.cc, target));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 },
2091 }2216 }
20922217
2093 if (fn_info.alignment != 0) {2218 if (fn_info.alignment != 0) {
...@@ -2518,42 +2643,7 @@ pub const DeclGen = struct {...@@ -2518,42 +2643,7 @@ pub const DeclGen = struct {
2518 llvm_union_ty.structSetBody(&llvm_fields, llvm_fields_len, .False);2643 llvm_union_ty.structSetBody(&llvm_fields, llvm_fields_len, .False);
2519 return llvm_union_ty;2644 return llvm_union_ty;
2520 },2645 },
2521 .Fn => {2646 .Fn => return llvmTypeFn(dg, t),
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 },
2557 .ComptimeInt => unreachable,2647 .ComptimeInt => unreachable,
2558 .ComptimeFloat => unreachable,2648 .ComptimeFloat => unreachable,
2559 .Type => unreachable,2649 .Type => unreachable,
...@@ -2568,6 +2658,119 @@ pub const DeclGen = struct {...@@ -2568,6 +2658,119 @@ pub const DeclGen = struct {
2568 }2658 }
2569 }2659 }
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
2571 fn genTypedValue(dg: *DeclGen, tv: TypedValue) Error!*const llvm.Value {2774 fn genTypedValue(dg: *DeclGen, tv: TypedValue) Error!*const llvm.Value {
2572 if (tv.val.isUndef()) {2775 if (tv.val.isUndef()) {
2573 const llvm_type = try dg.llvmType(tv.ty);2776 const llvm_type = try dg.llvmType(tv.ty);
...@@ -3848,52 +4051,164 @@ pub const FuncGen = struct {...@@ -3848,52 +4051,164 @@ pub const FuncGen = struct {
3848 try llvm_args.append(self.err_ret_trace.?);4051 try llvm_args.append(self.err_ret_trace.?);
3849 }4052 }
38504053
3851 for (args) |arg| {4054 var it = iterateParamTypes(self.dg, fn_info);
3852 const param_ty = self.air.typeOf(arg);4055 while (it.next()) |lowering| switch (lowering) {
3853 if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;4056 .no_bits => continue,
38544057 .byval => {
3855 const llvm_arg = try self.resolveInst(arg);4058 const arg = args[it.zig_index - 1];
3856 const abi_llvm_ty = try lowerFnParamTy(self.dg, fn_info.cc, param_ty);4059 const param_ty = self.air.typeOf(arg);
3857 const param_llvm_ty = llvm_arg.typeOf();4060 const llvm_arg = try self.resolveInst(arg);
3858 if (abi_llvm_ty == param_llvm_ty) {4061 if (isByRef(param_ty)) {
3859 try llvm_args.append(llvm_arg);4062 const alignment = param_ty.abiAlignment(target);
3860 continue;4063 const load_inst = self.builder.buildLoad(llvm_arg, "");
3861 }4064 load_inst.setAlignment(alignment);
38624065 try llvm_args.append(load_inst);
3863 // In this case the function param type is honoring the calling convention4066 } else {
3864 // by having a different LLVM type than the usual one. We solve this here4067 if (param_ty.zigTypeTag() == .Pointer) {
3865 // at the callsite by bitcasting a pointer to our canonical type, then4068 // We need a bitcast in case of two possibilities:
3866 // loading it if necessary.4069 // 1. The parameter type is a pointer to zero-sized type,
3867 const alignment = param_ty.abiAlignment(target);4070 // which is always lowered to an LLVM type of `*i8`.
3868 const ptr_abi_ty = abi_llvm_ty.pointerType(0);4071 // 2. The argument is a global which does act as a pointer, however
38694072 // a bitcast is needed in order for the LLVM types to match.
3870 const casted_ptr = if (isByRef(param_ty))4073 const llvm_param_ty = try self.dg.llvmType(param_ty);
3871 self.builder.buildBitCast(llvm_arg, ptr_abi_ty, "")4074 const casted_ptr = self.builder.buildBitCast(llvm_arg, llvm_param_ty, "");
3872 else p: {4075 try llvm_args.append(casted_ptr);
3873 const arg_ptr = self.buildAlloca(param_llvm_ty);4076 } else {
3874 arg_ptr.setAlignment(alignment);4077 try llvm_args.append(llvm_arg);
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;
3884 }4078 }
3885 const casted_ptr = self.builder.buildBitCast(arg_ptr, abi_llvm_ty, "");
3886 try llvm_args.append(casted_ptr);
3887 continue;
3888 }4079 }
38894080 },
3890 break :p self.builder.buildBitCast(arg_ptr, ptr_abi_ty, "");4081 .byref => {
3891 };4082 const arg = args[it.zig_index - 1];
38924083 const param_ty = self.air.typeOf(arg);
3893 const load_inst = self.builder.buildLoad(casted_ptr, "");4084 const llvm_arg = try self.resolveInst(arg);
3894 load_inst.setAlignment(alignment);4085 if (isByRef(param_ty)) {
3895 try llvm_args.append(load_inst);4086 try llvm_args.append(llvm_arg);
3896 }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
3898 const call = self.builder.buildCall(4213 const call = self.builder.buildCall(
3899 llvm_fn,4214 llvm_fn,
...@@ -6489,24 +6804,7 @@ pub const FuncGen = struct {...@@ -6489,24 +6804,7 @@ pub const FuncGen = struct {
6489 /// Use this instead of builder.buildAlloca, because this function makes sure to6804 /// Use this instead of builder.buildAlloca, because this function makes sure to
6490 /// put the alloca instruction at the top of the function!6805 /// put the alloca instruction at the top of the function!
6491 fn buildAlloca(self: *FuncGen, llvm_ty: *const llvm.Type) *const llvm.Value {6806 fn buildAlloca(self: *FuncGen, llvm_ty: *const llvm.Type) *const llvm.Value {
6492 const prev_block = self.builder.getInsertBlock();6807 return buildAllocaInner(self.builder, self.llvm_func, self.di_scope != null, llvm_ty);
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, "");
6510 }6808 }
65116809
6512 fn airStore(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {6810 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....@@ -8296,99 +8594,171 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm.
8296 }8594 }
8297}8595}
82988596
8299fn lowerFnParamTy(dg: *DeclGen, cc: std.builtin.CallingConvention, ty: Type) !*const llvm.Type {8597const ParamTypeIterator = struct {
8300 assert(ty.hasRuntimeBitsIgnoreComptime());8598 dg: *DeclGen,
8301 const target = dg.module.getTarget();8599 fn_info: Type.Payload.Function.Data,
8302 switch (cc) {8600 zig_index: u32,
8303 .Unspecified, .Inline => {8601 llvm_index: u32,
8304 const raw_llvm_ty = try dg.llvmType(ty);8602 target: std.Target,
8305 if (isByRef(ty)) {8603 llvm_types_len: u32,
8306 return raw_llvm_ty.pointerType(0);8604 llvm_types_buffer: [8]u16,
8307 } else {8605
8308 return raw_llvm_ty;8606 const Lowering = enum {
8309 }8607 no_bits,
8310 },8608 byval,
8311 .C => {8609 byref,
8312 const is_scalar = switch (ty.zigTypeTag()) {8610 abi_sized_int,
8313 .Void,8611 multiple_llvm_ints,
8314 .Bool,8612 };
8315 .NoReturn,
8316 .Int,
8317 .Float,
8318 .Pointer,
8319 .Optional,
8320 .ErrorSet,
8321 .Enum,
8322 .AnyFrame,
8323 .Vector,
8324 => true,
83258613
8326 else => false,8614 fn next(it: *ParamTypeIterator) ?Lowering {
8327 };8615 if (it.zig_index >= it.fn_info.param_types.len) return null;
8328 switch (target.cpu.arch) {8616
8329 .mips, .mipsel => return dg.llvmType(ty),8617 const ty = it.fn_info.param_types[it.zig_index];
8330 .x86_64 => switch (target.os.tag) {8618 if (!ty.hasRuntimeBitsIgnoreComptime()) {
8331 .windows => switch (x86_64_abi.classifyWindows(ty, target)) {8619 it.zig_index += 1;
8332 .integer => {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 => {
8333 if (is_scalar) {8685 if (is_scalar) {
8334 return dg.llvmType(ty);8686 it.zig_index += 1;
8335 } else {8687 it.llvm_index += 1;
8336 const abi_size = ty.abiSize(target);8688 return .byval;
8337 return dg.context.intType(@intCast(c_uint, abi_size * 8));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 }
8338 }8722 }
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;
8339 },8733 },
8340 .memory => return (try dg.llvmType(ty)).pointerType(0),
8341 .sse => return dg.llvmType(ty),
8342 else => unreachable,
8343 },8734 },
8735 // TODO investigate C ABI for other architectures
8344 else => {8736 else => {
8345 if (is_scalar) {8737 it.zig_index += 1;
8346 return dg.llvmType(ty);8738 it.llvm_index += 1;
8347 }8739 return .byval;
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);
8384 },8740 },
8385 },8741 }
8386 // TODO investigate C ABI for other architectures8742 },
8387 else => return dg.llvmType(ty),8743 else => {
8388 }8744 it.zig_index += 1;
8389 },8745 it.llvm_index += 1;
8390 else => return dg.llvmType(ty),8746 return .byval;
8747 },
8748 }
8391 }8749 }
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 };
8392}8762}
83938763
8394fn isByRef(ty: Type) bool {8764fn isByRef(ty: Type) bool {
...@@ -8549,3 +8919,29 @@ fn compilerRtIntBits(bits: u16) u16 {...@@ -8549,3 +8919,29 @@ fn compilerRtIntBits(bits: u16) u16 {
8549 }8919 }
8550 return bits;8920 return bits;
8551}8921}
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 {...@@ -1019,6 +1019,9 @@ pub const TargetMachine = opaque {
1019pub const TargetData = opaque {1019pub const TargetData = opaque {
1020 pub const dispose = LLVMDisposeTargetData;1020 pub const dispose = LLVMDisposeTargetData;
1021 extern fn LLVMDisposeTargetData(*const TargetData) void;1021 extern fn LLVMDisposeTargetData(*const TargetData) void;
1022
1023 pub const abiAlignmentOfType = LLVMABIAlignmentOfType;
1024 extern fn LLVMABIAlignmentOfType(TD: *const TargetData, Ty: *const Type) c_uint;
1022};1025};
10231026
1024pub const CodeModel = enum(c_int) {1027pub const CodeModel = enum(c_int) {