| ... | @@ -682,25 +682,130 @@ pub const Object = struct { | ... | @@ -682,25 +682,130 @@ pub const Object = struct { |
| 682 | else | 682 | else |
| 683 | null; | 683 | null; |
| 684 | | 684 | |
| | 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(); |
| 687 | | 690 | |
| 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; |
| 691 | | 717 | |
| 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 nature | 720 | } 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) and | 722 | 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 | 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 | }, |
| 702 | }; | 808 | }; |
| 703 | try args.append(loaded_param); | | |
| 704 | } | 809 | } |
| 705 | | 810 | |
| 706 | var di_file: ?*llvm.DIFile = null; | 811 | var di_file: ?*llvm.DIFile = null; |
| ... | @@ -2072,22 +2177,31 @@ pub const DeclGen = struct { | ... | @@ -2072,22 +2177,31 @@ pub const DeclGen = struct { |
| 2072 | } | 2177 | } |
| 2073 | | 2178 | |
| 2074 | // Set parameter attributes. | 2179 | // 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`. | 2180 | // TODO: more attributes. see codegen.cpp `make_fn_llvm_value`. |
| 2087 | if (fn_info.cc == .Naked) { | 2181 | switch (fn_info.cc) { |
| 2088 | dg.addFnAttr(llvm_fn, "naked"); | 2182 | .Unspecified, .Inline => { |
| 2089 | } else { | 2183 | var llvm_param_i: c_uint = @as(c_uint, @boolToInt(sret)) + @boolToInt(err_return_tracing); |
| 2090 | llvm_fn.setFunctionCallConv(toLlvmCallConv(fn_info.cc, target)); | 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 | }, |
| 2091 | } | 2205 | } |
| 2092 | | 2206 | |
| 2093 | if (fn_info.alignment != 0) { | 2207 | if (fn_info.alignment != 0) { |
| ... | @@ -2518,42 +2632,7 @@ pub const DeclGen = struct { | ... | @@ -2518,42 +2632,7 @@ pub const DeclGen = struct { |
| 2518 | llvm_union_ty.structSetBody(&llvm_fields, llvm_fields_len, .False); | 2632 | llvm_union_ty.structSetBody(&llvm_fields, llvm_fields_len, .False); |
| 2519 | return llvm_union_ty; | 2633 | return llvm_union_ty; |
| 2520 | }, | 2634 | }, |
| 2521 | .Fn => { | 2635 | .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, | 2636 | .ComptimeInt => unreachable, |
| 2558 | .ComptimeFloat => unreachable, | 2637 | .ComptimeFloat => unreachable, |
| 2559 | .Type => unreachable, | 2638 | .Type => unreachable, |
| ... | @@ -2568,6 +2647,63 @@ pub const DeclGen = struct { | ... | @@ -2568,6 +2647,63 @@ pub const DeclGen = struct { |
| 2568 | } | 2647 | } |
| 2569 | } | 2648 | } |
| 2570 | | 2649 | |
| | 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 | |
| 2571 | fn genTypedValue(dg: *DeclGen, tv: TypedValue) Error!*const llvm.Value { | 2707 | fn genTypedValue(dg: *DeclGen, tv: TypedValue) Error!*const llvm.Value { |
| 2572 | if (tv.val.isUndef()) { | 2708 | if (tv.val.isUndef()) { |
| 2573 | const llvm_type = try dg.llvmType(tv.ty); | 2709 | const llvm_type = try dg.llvmType(tv.ty); |
| ... | @@ -3848,52 +3984,138 @@ pub const FuncGen = struct { | ... | @@ -3848,52 +3984,138 @@ pub const FuncGen = struct { |
| 3848 | try llvm_args.append(self.err_ret_trace.?); | 3984 | try llvm_args.append(self.err_ret_trace.?); |
| 3849 | } | 3985 | } |
| 3850 | | 3986 | |
| 3851 | for (args) |arg| { | 3987 | var it = iterateParamTypes(self.dg, fn_info); |
| 3852 | const param_ty = self.air.typeOf(arg); | 3988 | while (it.next()) |lowering| switch (lowering) { |
| 3853 | if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; | 3989 | .no_bits => continue, |
| 3854 | | 3990 | .byval => { |
| 3855 | const llvm_arg = try self.resolveInst(arg); | 3991 | const arg = args[it.zig_index - 1]; |
| 3856 | const abi_llvm_ty = try lowerFnParamTy(self.dg, fn_info.cc, param_ty); | 3992 | const param_ty = self.air.typeOf(arg); |
| 3857 | const param_llvm_ty = llvm_arg.typeOf(); | 3993 | const llvm_arg = try self.resolveInst(arg); |
| 3858 | if (abi_llvm_ty == param_llvm_ty) { | 3994 | if (isByRef(param_ty)) { |
| 3859 | try llvm_args.append(llvm_arg); | 3995 | const alignment = param_ty.abiAlignment(target); |
| 3860 | continue; | 3996 | const load_inst = self.builder.buildLoad(llvm_arg, ""); |
| 3861 | } | 3997 | load_inst.setAlignment(alignment); |
| 3862 | | 3998 | try llvm_args.append(load_inst); |
| 3863 | // In this case the function param type is honoring the calling convention | 3999 | } else { |
| 3864 | // by having a different LLVM type than the usual one. We solve this here | 4000 | if (param_ty.zigTypeTag() == .Pointer) { |
| 3865 | // at the callsite by bitcasting a pointer to our canonical type, then | 4001 | // We need a bitcast in case of two possibilities: |
| 3866 | // loading it if necessary. | 4002 | // 1. The parameter type is a pointer to zero-sized type, |
| 3867 | const alignment = param_ty.abiAlignment(target); | 4003 | // which is always lowered to an LLVM type of `*i8`. |
| 3868 | const ptr_abi_ty = abi_llvm_ty.pointerType(0); | 4004 | // 2. The argument is a global which does act as a pointer, however |
| 3869 | | 4005 | // a bitcast is needed in order for the LLVM types to match. |
| 3870 | const casted_ptr = if (isByRef(param_ty)) | 4006 | const llvm_param_ty = try self.dg.llvmType(param_ty); |
| 3871 | self.builder.buildBitCast(llvm_arg, ptr_abi_ty, "") | 4007 | const casted_ptr = self.builder.buildBitCast(llvm_arg, llvm_param_ty, ""); |
| 3872 | else p: { | 4008 | try llvm_args.append(casted_ptr); |
| 3873 | const arg_ptr = self.buildAlloca(param_llvm_ty); | 4009 | } else { |
| 3874 | arg_ptr.setAlignment(alignment); | 4010 | 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 | } | 4011 | } |
| 3885 | const casted_ptr = self.builder.buildBitCast(arg_ptr, abi_llvm_ty, ""); | | |
| 3886 | try llvm_args.append(casted_ptr); | | |
| 3887 | continue; | | |
| 3888 | } | 4012 | } |
| 3889 | | 4013 | }, |
| 3890 | break :p self.builder.buildBitCast(arg_ptr, ptr_abi_ty, ""); | 4014 | .byref => { |
| 3891 | }; | 4015 | const arg = args[it.zig_index - 1]; |
| 3892 | | 4016 | const param_ty = self.air.typeOf(arg); |
| 3893 | const load_inst = self.builder.buildLoad(casted_ptr, ""); | 4017 | const llvm_arg = try self.resolveInst(arg); |
| 3894 | load_inst.setAlignment(alignment); | 4018 | if (isByRef(param_ty)) { |
| 3895 | try llvm_args.append(load_inst); | 4019 | try llvm_args.append(llvm_arg); |
| 3896 | } | 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 | }; |
| 3897 | | 4119 | |
| 3898 | const call = self.builder.buildCall( | 4120 | const call = self.builder.buildCall( |
| 3899 | llvm_fn, | 4121 | llvm_fn, |
| ... | @@ -6489,24 +6711,7 @@ pub const FuncGen = struct { | ... | @@ -6489,24 +6711,7 @@ pub const FuncGen = struct { |
| 6489 | /// Use this instead of builder.buildAlloca, because this function makes sure to | 6711 | /// Use this instead of builder.buildAlloca, because this function makes sure to |
| 6490 | /// put the alloca instruction at the top of the function! | 6712 | /// put the alloca instruction at the top of the function! |
| 6491 | fn buildAlloca(self: *FuncGen, llvm_ty: *const llvm.Type) *const llvm.Value { | 6713 | fn buildAlloca(self: *FuncGen, llvm_ty: *const llvm.Type) *const llvm.Value { |
| 6492 | const prev_block = self.builder.getInsertBlock(); | 6714 | 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 | } | 6715 | } |
| 6511 | | 6716 | |
| 6512 | fn airStore(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | 6717 | 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. | ... | @@ -8296,99 +8501,171 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm. |
| 8296 | } | 8501 | } |
| 8297 | } | 8502 | } |
| 8298 | | 8503 | |
| 8299 | fn lowerFnParamTy(dg: *DeclGen, cc: std.builtin.CallingConvention, ty: Type) !*const llvm.Type { | 8504 | const ParamTypeIterator = struct { |
| 8300 | assert(ty.hasRuntimeBitsIgnoreComptime()); | 8505 | dg: *DeclGen, |
| 8301 | const target = dg.module.getTarget(); | 8506 | fn_info: Type.Payload.Function.Data, |
| 8302 | switch (cc) { | 8507 | zig_index: u32, |
| 8303 | .Unspecified, .Inline => { | 8508 | llvm_index: u32, |
| 8304 | const raw_llvm_ty = try dg.llvmType(ty); | 8509 | target: std.Target, |
| 8305 | if (isByRef(ty)) { | 8510 | llvm_types_len: u32, |
| 8306 | return raw_llvm_ty.pointerType(0); | 8511 | llvm_types_buffer: [8]u16, |
| 8307 | } else { | 8512 | |
| 8308 | return raw_llvm_ty; | 8513 | const Lowering = enum { |
| 8309 | } | 8514 | no_bits, |
| 8310 | }, | 8515 | byval, |
| 8311 | .C => { | 8516 | byref, |
| 8312 | const is_scalar = switch (ty.zigTypeTag()) { | 8517 | abi_sized_int, |
| 8313 | .Void, | 8518 | multiple_llvm_ints, |
| 8314 | .Bool, | 8519 | }; |
| 8315 | .NoReturn, | | |
| 8316 | .Int, | | |
| 8317 | .Float, | | |
| 8318 | .Pointer, | | |
| 8319 | .Optional, | | |
| 8320 | .ErrorSet, | | |
| 8321 | .Enum, | | |
| 8322 | .AnyFrame, | | |
| 8323 | .Vector, | | |
| 8324 | => true, | | |
| 8325 | | 8520 | |
| 8326 | else => false, | 8521 | fn next(it: *ParamTypeIterator) ?Lowering { |
| 8327 | }; | 8522 | if (it.zig_index >= it.fn_info.param_types.len) return null; |
| 8328 | switch (target.cpu.arch) { | 8523 | |
| 8329 | .mips, .mipsel => return dg.llvmType(ty), | 8524 | const ty = it.fn_info.param_types[it.zig_index]; |
| 8330 | .x86_64 => switch (target.os.tag) { | 8525 | if (!ty.hasRuntimeBitsIgnoreComptime()) { |
| 8331 | .windows => switch (x86_64_abi.classifyWindows(ty, target)) { | 8526 | it.zig_index += 1; |
| 8332 | .integer => { | 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 => { |
| 8333 | if (is_scalar) { | 8592 | if (is_scalar) { |
| 8334 | return dg.llvmType(ty); | 8593 | it.zig_index += 1; |
| 8335 | } else { | 8594 | it.llvm_index += 1; |
| 8336 | const abi_size = ty.abiSize(target); | 8595 | return .byval; |
| 8337 | return dg.context.intType(@intCast(c_uint, abi_size * 8)); | 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; |
| 8338 | } | 8602 | } |
| | 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; |
| 8339 | }, | 8640 | }, |
| 8340 | .memory => return (try dg.llvmType(ty)).pointerType(0), | | |
| 8341 | .sse => return dg.llvmType(ty), | | |
| 8342 | else => unreachable, | | |
| 8343 | }, | 8641 | }, |
| | 8642 | // TODO investigate C ABI for other architectures |
| 8344 | else => { | 8643 | else => { |
| 8345 | if (is_scalar) { | 8644 | it.zig_index += 1; |
| 8346 | return dg.llvmType(ty); | 8645 | it.llvm_index += 1; |
| 8347 | } | 8646 | 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 | }, | 8647 | }, |
| 8385 | }, | 8648 | } |
| 8386 | // TODO investigate C ABI for other architectures | 8649 | }, |
| 8387 | else => return dg.llvmType(ty), | 8650 | else => { |
| 8388 | } | 8651 | it.zig_index += 1; |
| 8389 | }, | 8652 | it.llvm_index += 1; |
| 8390 | else => return dg.llvmType(ty), | 8653 | return .byval; |
| | 8654 | }, |
| | 8655 | } |
| 8391 | } | 8656 | } |
| | 8657 | }; |
| | 8658 | |
| | 8659 | fn 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 | }; |
| 8392 | } | 8669 | } |
| 8393 | | 8670 | |
| 8394 | fn isByRef(ty: Type) bool { | 8671 | fn isByRef(ty: Type) bool { |
| ... | @@ -8549,3 +8826,29 @@ fn compilerRtIntBits(bits: u16) u16 { | ... | @@ -8549,3 +8826,29 @@ fn compilerRtIntBits(bits: u16) u16 { |
| 8549 | } | 8826 | } |
| 8550 | return bits; | 8827 | return bits; |
| 8551 | } | 8828 | } |
| | 8829 | |
| | 8830 | fn 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 | } |