| ... | ... | @@ -682,25 +682,130 @@ pub const Object = struct { |
| 682 | 682 | else |
| 683 | 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 | 688 | var args = std.ArrayList(*const llvm.Value).init(gpa); |
| 686 | 689 | defer args.deinit(); |
| 687 | 690 | |
| 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; |
| 691 | 717 | |
| 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 | }, |
| 702 | 808 | }; |
| 703 | | try args.append(loaded_param); |
| 704 | 809 | } |
| 705 | 810 | |
| 706 | 811 | var di_file: ?*llvm.DIFile = null; |
| ... | ... | @@ -2072,22 +2177,31 @@ pub const DeclGen = struct { |
| 2072 | 2177 | } |
| 2073 | 2178 | |
| 2074 | 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 | 2180 | // 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 | }, |
| 2091 | 2205 | } |
| 2092 | 2206 | |
| 2093 | 2207 | if (fn_info.alignment != 0) { |
| ... | ... | @@ -2518,42 +2632,7 @@ pub const DeclGen = struct { |
| 2518 | 2632 | llvm_union_ty.structSetBody(&llvm_fields, llvm_fields_len, .False); |
| 2519 | 2633 | return llvm_union_ty; |
| 2520 | 2634 | }, |
| 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), |
| 2557 | 2636 | .ComptimeInt => unreachable, |
| 2558 | 2637 | .ComptimeFloat => unreachable, |
| 2559 | 2638 | .Type => unreachable, |
| ... | ... | @@ -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 | 2707 | fn genTypedValue(dg: *DeclGen, tv: TypedValue) Error!*const llvm.Value { |
| 2572 | 2708 | if (tv.val.isUndef()) { |
| 2573 | 2709 | const llvm_type = try dg.llvmType(tv.ty); |
| ... | ... | @@ -3848,52 +3984,138 @@ pub const FuncGen = struct { |
| 3848 | 3984 | try llvm_args.append(self.err_ret_trace.?); |
| 3849 | 3985 | } |
| 3850 | 3986 | |
| 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); |
| 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 | | |
| 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 | }; |
| 3897 | 4119 | |
| 3898 | 4120 | const call = self.builder.buildCall( |
| 3899 | 4121 | llvm_fn, |
| ... | ... | @@ -6489,24 +6711,7 @@ pub const FuncGen = struct { |
| 6489 | 6711 | /// Use this instead of builder.buildAlloca, because this function makes sure to |
| 6490 | 6712 | /// put the alloca instruction at the top of the function! |
| 6491 | 6713 | 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); |
| 6510 | 6715 | } |
| 6511 | 6716 | |
| 6512 | 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 | 8501 | } |
| 8297 | 8502 | } |
| 8298 | 8503 | |
| 8299 | | fn 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, |
| 8504 | const 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 | }; |
| 8325 | 8520 | |
| 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 => { |
| 8333 | 8592 | 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; |
| 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 | 8643 | 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; |
| 8384 | 8647 | }, |
| 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 | } |
| 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 | 8671 | fn isByRef(ty: Type) bool { |
| ... | ... | @@ -8549,3 +8826,29 @@ fn compilerRtIntBits(bits: u16) u16 { |
| 8549 | 8826 | } |
| 8550 | 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 | } |