authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-20 21:14:53-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-20 21:50:23-07:00
log1697a6f0443ca8896081f98bd8648bc7bdb2cc58
treef0704b505b18292c2603271e256189a5075422dd
parent1a92264b3d3f3e14f9a3b724fccefb5f5a38346f

LLVM: rework calling convention lowering

The previous implementation of calling conventions was hacky and broken. This commit reworks lowerFnParamTy into iterateParamTypes which returns enum tags indicating how to handle each parameter. This is then used in the three places that matter: * lowering a function type to llvm type * converting function parameters to the canonical type representation (with respect to isByRef). * converting canonical type representation to function arguments at callsites (again with respect to isByRef). As a result, we are one step closer to the C ABI tests passing. Before this commit, attempting to build them crashed the compiler. I isolated the broken function and verified that it now is lowered correctly. I will keep working on this one piece at a time until all the C ABI tests pass, and then I will enable all of them in the CI.

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

src/codegen/llvm.zig+515-212
......@@ -682,25 +682,130 @@ 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 for (llvm_ints) |int_bits| {
768 const param = llvm_func.getParam(llvm_arg_i);
769 llvm_arg_i += 1;
770
771 const big_int_ty = dg.context.intType(int_bits);
772 var bits_used: u16 = 0;
773 while (bits_used < int_bits) {
774 const field = fields[field_i];
775 const field_abi_bits = @intCast(u16, field.ty.abiSize(target)) * 8;
776 const field_int_ty = dg.context.intType(field_abi_bits);
777 const shifted = if (bits_used == 0) param else s: {
778 const shift_amt = big_int_ty.constInt(bits_used, .False);
779 break :s builder.buildLShr(param, shift_amt, "");
780 };
781 const field_as_int = builder.buildTrunc(shifted, field_int_ty, "");
782 var ty_buf: Type.Payload.Pointer = undefined;
783 const llvm_i = llvmFieldIndex(param_ty, field_i, target, &ty_buf).?;
784 const field_ptr = builder.buildStructGEP(arg_ptr, llvm_i, "");
785 const field_alignment = field.normalAlignment(target);
786 const casted_ptr = builder.buildBitCast(field_ptr, field_int_ty.pointerType(0), "");
787 const store_inst = builder.buildStore(field_as_int, casted_ptr);
788 store_inst.setAlignment(field_alignment);
789
790 bits_used += field_abi_bits;
791 field_i += 1;
792 if (field_i >= fields.len) break;
793 }
794 if (field_i >= fields.len) break;
795 }
796
797 try args.append(arg_ptr);
798 } else {
799 @panic("TODO: LLVM backend: implement C calling convention on x86_64 with byval struct parameter");
800 }
801 },
802 .Union => {
803 @panic("TODO: LLVM backend: implement C calling convention on x86_64 with union parameter");
804 },
805 else => unreachable,
806 }
807 },
702808 };
703 try args.append(loaded_param);
704809 }
705810
706811 var di_file: ?*llvm.DIFile = null;
......@@ -2072,22 +2177,31 @@ pub const DeclGen = struct {
20722177 }
20732178
20742179 // 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
20862180 // 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));
2181 switch (fn_info.cc) {
2182 .Unspecified, .Inline => {
2183 var llvm_param_i: c_uint = @as(c_uint, @boolToInt(sret)) + @boolToInt(err_return_tracing);
2184 for (fn_info.param_types) |param_ty| {
2185 if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue;
2186
2187 if (isByRef(param_ty)) {
2188 dg.addArgAttr(llvm_fn, llvm_param_i, "nonnull");
2189 // TODO readonly, noalias, align
2190 }
2191 llvm_param_i += 1;
2192 }
2193 llvm_fn.setFunctionCallConv(.Fast);
2194 },
2195 .Naked => {
2196 dg.addFnAttr(llvm_fn, "naked");
2197 },
2198 .Async => {
2199 llvm_fn.setFunctionCallConv(.Fast);
2200 @panic("TODO: LLVM backend lower async function");
2201 },
2202 else => {
2203 llvm_fn.setFunctionCallConv(toLlvmCallConv(fn_info.cc, target));
2204 },
20912205 }
20922206
20932207 if (fn_info.alignment != 0) {
......@@ -2518,42 +2632,7 @@ pub const DeclGen = struct {
25182632 llvm_union_ty.structSetBody(&llvm_fields, llvm_fields_len, .False);
25192633 return llvm_union_ty;
25202634 },
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 },
2635 .Fn => return llvmTypeFn(dg, t),
25572636 .ComptimeInt => unreachable,
25582637 .ComptimeFloat => unreachable,
25592638 .Type => unreachable,
......@@ -2568,6 +2647,63 @@ pub const DeclGen = struct {
25682647 }
25692648 }
25702649
2650 fn llvmTypeFn(dg: *DeclGen, fn_ty: Type) Allocator.Error!*const llvm.Type {
2651 const target = dg.module.getTarget();
2652 const fn_info = fn_ty.fnInfo();
2653 const llvm_ret_ty = try lowerFnRetTy(dg, fn_info);
2654
2655 var llvm_params = std.ArrayList(*const llvm.Type).init(dg.gpa);
2656 defer llvm_params.deinit();
2657
2658 if (firstParamSRet(fn_info, target)) {
2659 const llvm_sret_ty = try dg.llvmType(fn_info.return_type);
2660 try llvm_params.append(llvm_sret_ty.pointerType(0));
2661 }
2662
2663 if (fn_info.return_type.isError() and
2664 dg.module.comp.bin_file.options.error_return_tracing)
2665 {
2666 var ptr_ty_payload: Type.Payload.ElemType = .{
2667 .base = .{ .tag = .single_mut_pointer },
2668 .data = dg.object.getStackTraceType(),
2669 };
2670 const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
2671 try llvm_params.append(try dg.llvmType(ptr_ty));
2672 }
2673
2674 var it = iterateParamTypes(dg, fn_info);
2675 while (it.next()) |lowering| switch (lowering) {
2676 .no_bits => continue,
2677 .byval => {
2678 const param_ty = fn_info.param_types[it.zig_index - 1];
2679 try llvm_params.append(try dg.llvmType(param_ty));
2680 },
2681 .byref => {
2682 const param_ty = fn_info.param_types[it.zig_index - 1];
2683 const raw_llvm_ty = try dg.llvmType(param_ty);
2684 try llvm_params.append(raw_llvm_ty.pointerType(0));
2685 },
2686 .abi_sized_int => {
2687 const param_ty = fn_info.param_types[it.zig_index - 1];
2688 const abi_size = @intCast(c_uint, param_ty.abiSize(target));
2689 try llvm_params.append(dg.context.intType(abi_size * 8));
2690 },
2691 .multiple_llvm_ints => {
2692 try llvm_params.ensureUnusedCapacity(it.llvm_types_len);
2693 for (it.llvm_types_buffer[0..it.llvm_types_len]) |int_bits| {
2694 llvm_params.appendAssumeCapacity(dg.context.intType(int_bits));
2695 }
2696 },
2697 };
2698
2699 return llvm.functionType(
2700 llvm_ret_ty,
2701 llvm_params.items.ptr,
2702 @intCast(c_uint, llvm_params.items.len),
2703 llvm.Bool.fromBool(fn_info.is_var_args),
2704 );
2705 }
2706
25712707 fn genTypedValue(dg: *DeclGen, tv: TypedValue) Error!*const llvm.Value {
25722708 if (tv.val.isUndef()) {
25732709 const llvm_type = try dg.llvmType(tv.ty);
......@@ -3848,52 +3984,138 @@ pub const FuncGen = struct {
38483984 try llvm_args.append(self.err_ret_trace.?);
38493985 }
38503986
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;
3987 var it = iterateParamTypes(self.dg, fn_info);
3988 while (it.next()) |lowering| switch (lowering) {
3989 .no_bits => continue,
3990 .byval => {
3991 const arg = args[it.zig_index - 1];
3992 const param_ty = self.air.typeOf(arg);
3993 const llvm_arg = try self.resolveInst(arg);
3994 if (isByRef(param_ty)) {
3995 const alignment = param_ty.abiAlignment(target);
3996 const load_inst = self.builder.buildLoad(llvm_arg, "");
3997 load_inst.setAlignment(alignment);
3998 try llvm_args.append(load_inst);
3999 } else {
4000 if (param_ty.zigTypeTag() == .Pointer) {
4001 // We need a bitcast in case of two possibilities:
4002 // 1. The parameter type is a pointer to zero-sized type,
4003 // which is always lowered to an LLVM type of `*i8`.
4004 // 2. The argument is a global which does act as a pointer, however
4005 // a bitcast is needed in order for the LLVM types to match.
4006 const llvm_param_ty = try self.dg.llvmType(param_ty);
4007 const casted_ptr = self.builder.buildBitCast(llvm_arg, llvm_param_ty, "");
4008 try llvm_args.append(casted_ptr);
4009 } else {
4010 try llvm_args.append(llvm_arg);
38844011 }
3885 const casted_ptr = self.builder.buildBitCast(arg_ptr, abi_llvm_ty, "");
3886 try llvm_args.append(casted_ptr);
3887 continue;
38884012 }
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 }
4013 },
4014 .byref => {
4015 const arg = args[it.zig_index - 1];
4016 const param_ty = self.air.typeOf(arg);
4017 const llvm_arg = try self.resolveInst(arg);
4018 if (isByRef(param_ty)) {
4019 try llvm_args.append(llvm_arg);
4020 } else {
4021 const alignment = param_ty.abiAlignment(target);
4022 const param_llvm_ty = llvm_arg.typeOf();
4023 const arg_ptr = self.buildAlloca(param_llvm_ty);
4024 arg_ptr.setAlignment(alignment);
4025 const store_inst = self.builder.buildStore(llvm_arg, arg_ptr);
4026 store_inst.setAlignment(alignment);
4027 try llvm_args.append(arg_ptr);
4028 }
4029 },
4030 .abi_sized_int => {
4031 const arg = args[it.zig_index - 1];
4032 const param_ty = self.air.typeOf(arg);
4033 const llvm_arg = try self.resolveInst(arg);
4034 const abi_size = @intCast(c_uint, param_ty.abiSize(target));
4035 const int_llvm_ty = self.dg.context.intType(abi_size * 8);
4036 const int_ptr_llvm_ty = int_llvm_ty.pointerType(0);
4037
4038 if (isByRef(param_ty)) {
4039 const alignment = param_ty.abiAlignment(target);
4040 const casted_ptr = self.builder.buildBitCast(llvm_arg, int_ptr_llvm_ty, "");
4041 const load_inst = self.builder.buildLoad(casted_ptr, "");
4042 load_inst.setAlignment(alignment);
4043 try llvm_args.append(load_inst);
4044 } else {
4045 // LLVM does not allow bitcasting structs so we must allocate
4046 // a local, bitcast its pointer, store, and then load.
4047 const alignment = @maximum(
4048 param_ty.abiAlignment(target),
4049 self.dg.object.target_data.abiAlignmentOfType(int_llvm_ty),
4050 );
4051 const int_ptr = self.buildAlloca(int_llvm_ty);
4052 int_ptr.setAlignment(alignment);
4053 const param_llvm_ty = try self.dg.llvmType(param_ty);
4054 const casted_ptr = self.builder.buildBitCast(int_ptr, param_llvm_ty.pointerType(0), "");
4055 const store_inst = self.builder.buildStore(llvm_arg, casted_ptr);
4056 store_inst.setAlignment(alignment);
4057 const load_inst = self.builder.buildLoad(int_ptr, "");
4058 load_inst.setAlignment(alignment);
4059 try llvm_args.append(load_inst);
4060 }
4061 },
4062 .multiple_llvm_ints => {
4063 const arg = args[it.zig_index - 1];
4064 const param_ty = self.air.typeOf(arg);
4065 const llvm_ints = it.llvm_types_buffer[0..it.llvm_types_len];
4066 const llvm_arg = try self.resolveInst(arg);
4067 const is_by_ref = isByRef(param_ty);
4068 try llvm_args.ensureUnusedCapacity(it.llvm_types_len);
4069 switch (param_ty.zigTypeTag()) {
4070 .Struct => {
4071 const fields = param_ty.structFields().values();
4072 var field_i: u32 = 0;
4073 for (llvm_ints) |int_bits| {
4074 const big_int_ty = self.dg.context.intType(int_bits);
4075 var int_arg: *const llvm.Value = undefined;
4076 var bits_used: u16 = 0;
4077 while (bits_used < int_bits) {
4078 const field = fields[field_i];
4079 var ty_buf: Type.Payload.Pointer = undefined;
4080 const llvm_i = llvmFieldIndex(param_ty, field_i, target, &ty_buf).?;
4081 const field_abi_bits = @intCast(u16, field.ty.abiSize(target)) * 8;
4082 const field_int_ty = self.dg.context.intType(field_abi_bits);
4083 const llvm_field = if (is_by_ref) f: {
4084 const field_ptr = self.builder.buildStructGEP(llvm_arg, llvm_i, "");
4085 const alignment = field.normalAlignment(target);
4086 const casted_ptr = self.builder.buildBitCast(field_ptr, field_int_ty.pointerType(0), "");
4087 const load_inst = self.builder.buildLoad(casted_ptr, "");
4088 load_inst.setAlignment(alignment);
4089 break :f load_inst;
4090 } else f: {
4091 const llvm_field = self.builder.buildExtractValue(llvm_arg, llvm_i, "");
4092 break :f self.builder.buildBitCast(llvm_field, field_int_ty, "");
4093 };
4094
4095 const extended = self.builder.buildZExt(llvm_field, big_int_ty, "");
4096 if (bits_used == 0) {
4097 int_arg = extended;
4098 } else {
4099 const shift_amt = big_int_ty.constInt(bits_used, .False);
4100 const shifted = self.builder.buildShl(extended, shift_amt, "");
4101 int_arg = self.builder.buildOr(int_arg, shifted, "");
4102 }
4103
4104 bits_used += field_abi_bits;
4105 field_i += 1;
4106 if (field_i >= fields.len) break;
4107 }
4108 llvm_args.appendAssumeCapacity(int_arg);
4109 if (field_i >= fields.len) break;
4110 }
4111 },
4112 .Union => {
4113 return self.todo("airCall C calling convention on x86_64 with union argument ", .{});
4114 },
4115 else => unreachable,
4116 }
4117 },
4118 };
38974119
38984120 const call = self.builder.buildCall(
38994121 llvm_fn,
......@@ -6489,24 +6711,7 @@ pub const FuncGen = struct {
64896711 /// Use this instead of builder.buildAlloca, because this function makes sure to
64906712 /// put the alloca instruction at the top of the function!
64916713 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, "");
6714 return buildAllocaInner(self.builder, self.llvm_func, self.di_scope != null, llvm_ty);
65106715 }
65116716
65126717 fn airStore(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
......@@ -8296,99 +8501,171 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm.
82968501 }
82978502}
82988503
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,
8504const ParamTypeIterator = struct {
8505 dg: *DeclGen,
8506 fn_info: Type.Payload.Function.Data,
8507 zig_index: u32,
8508 llvm_index: u32,
8509 target: std.Target,
8510 llvm_types_len: u32,
8511 llvm_types_buffer: [8]u16,
8512
8513 const Lowering = enum {
8514 no_bits,
8515 byval,
8516 byref,
8517 abi_sized_int,
8518 multiple_llvm_ints,
8519 };
83258520
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 => {
8521 fn next(it: *ParamTypeIterator) ?Lowering {
8522 if (it.zig_index >= it.fn_info.param_types.len) return null;
8523
8524 const ty = it.fn_info.param_types[it.zig_index];
8525 if (!ty.hasRuntimeBitsIgnoreComptime()) {
8526 it.zig_index += 1;
8527 return .no_bits;
8528 }
8529
8530 switch (it.fn_info.cc) {
8531 .Unspecified, .Inline => {
8532 it.zig_index += 1;
8533 it.llvm_index += 1;
8534 if (isByRef(ty)) {
8535 return .byref;
8536 } else {
8537 return .byval;
8538 }
8539 },
8540 .Async => {
8541 @panic("TODO implement async function lowering in the LLVM backend");
8542 },
8543 .C => {
8544 const is_scalar = switch (ty.zigTypeTag()) {
8545 .Void,
8546 .Bool,
8547 .NoReturn,
8548 .Int,
8549 .Float,
8550 .Pointer,
8551 .Optional,
8552 .ErrorSet,
8553 .Enum,
8554 .AnyFrame,
8555 .Vector,
8556 => true,
8557
8558 else => false,
8559 };
8560 switch (it.target.cpu.arch) {
8561 .mips, .mipsel => {
8562 it.zig_index += 1;
8563 it.llvm_index += 1;
8564 return .byval;
8565 },
8566 .x86_64 => switch (it.target.os.tag) {
8567 .windows => switch (x86_64_abi.classifyWindows(ty, it.target)) {
8568 .integer => {
8569 if (is_scalar) {
8570 it.zig_index += 1;
8571 it.llvm_index += 1;
8572 return .byval;
8573 } else {
8574 it.zig_index += 1;
8575 it.llvm_index += 1;
8576 return .abi_sized_int;
8577 }
8578 },
8579 .memory => {
8580 it.zig_index += 1;
8581 it.llvm_index += 1;
8582 return .byref;
8583 },
8584 .sse => {
8585 it.zig_index += 1;
8586 it.llvm_index += 1;
8587 return .byval;
8588 },
8589 else => unreachable,
8590 },
8591 else => {
83338592 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));
8593 it.zig_index += 1;
8594 it.llvm_index += 1;
8595 return .byval;
8596 }
8597 const classes = x86_64_abi.classifySystemV(ty, it.target);
8598 if (classes[0] == .memory) {
8599 it.zig_index += 1;
8600 it.llvm_index += 1;
8601 return .byref;
83388602 }
8603 var llvm_types_buffer: [8]u16 = undefined;
8604 var llvm_types_index: u32 = 0;
8605 for (classes) |class| {
8606 switch (class) {
8607 .integer => {
8608 llvm_types_buffer[llvm_types_index] = 64;
8609 llvm_types_index += 1;
8610 },
8611 .sse => {
8612 @panic("TODO");
8613 },
8614 .sseup => {
8615 @panic("TODO");
8616 },
8617 .x87 => {
8618 @panic("TODO");
8619 },
8620 .x87up => {
8621 @panic("TODO");
8622 },
8623 .complex_x87 => {
8624 @panic("TODO");
8625 },
8626 .memory => unreachable, // handled above
8627 .none => break,
8628 }
8629 }
8630 if (classes[0] == .integer and classes[1] == .none) {
8631 it.zig_index += 1;
8632 it.llvm_index += 1;
8633 return .abi_sized_int;
8634 }
8635 it.llvm_types_buffer = llvm_types_buffer;
8636 it.llvm_types_len = llvm_types_index;
8637 it.llvm_index += llvm_types_index;
8638 it.zig_index += 1;
8639 return .multiple_llvm_ints;
83398640 },
8340 .memory => return (try dg.llvmType(ty)).pointerType(0),
8341 .sse => return dg.llvmType(ty),
8342 else => unreachable,
83438641 },
8642 // TODO investigate C ABI for other architectures
83448643 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);
8644 it.zig_index += 1;
8645 it.llvm_index += 1;
8646 return .byval;
83848647 },
8385 },
8386 // TODO investigate C ABI for other architectures
8387 else => return dg.llvmType(ty),
8388 }
8389 },
8390 else => return dg.llvmType(ty),
8648 }
8649 },
8650 else => {
8651 it.zig_index += 1;
8652 it.llvm_index += 1;
8653 return .byval;
8654 },
8655 }
83918656 }
8657};
8658
8659fn iterateParamTypes(dg: *DeclGen, fn_info: Type.Payload.Function.Data) ParamTypeIterator {
8660 return .{
8661 .dg = dg,
8662 .fn_info = fn_info,
8663 .zig_index = 0,
8664 .llvm_index = 0,
8665 .target = dg.module.getTarget(),
8666 .llvm_types_buffer = undefined,
8667 .llvm_types_len = 0,
8668 };
83928669}
83938670
83948671fn isByRef(ty: Type) bool {
......@@ -8549,3 +8826,29 @@ fn compilerRtIntBits(bits: u16) u16 {
85498826 }
85508827 return bits;
85518828}
8829
8830fn buildAllocaInner(
8831 builder: *const llvm.Builder,
8832 llvm_func: *const llvm.Value,
8833 di_scope_non_null: bool,
8834 llvm_ty: *const llvm.Type,
8835) *const llvm.Value {
8836 const prev_block = builder.getInsertBlock();
8837 const prev_debug_location = builder.getCurrentDebugLocation2();
8838 defer {
8839 builder.positionBuilderAtEnd(prev_block);
8840 if (di_scope_non_null) {
8841 builder.setCurrentDebugLocation2(prev_debug_location);
8842 }
8843 }
8844
8845 const entry_block = llvm_func.getFirstBasicBlock().?;
8846 if (entry_block.getFirstInstruction()) |first_inst| {
8847 builder.positionBuilder(entry_block, first_inst);
8848 } else {
8849 builder.positionBuilderAtEnd(entry_block);
8850 }
8851 builder.clearCurrentDebugLocation();
8852
8853 return builder.buildAlloca(llvm_ty, "");
8854}
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) {