| ... | ... | @@ -682,25 +682,141 @@ 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 | var field_offset: u32 = 0; |
| 768 | for (llvm_ints) |int_bits| { |
| 769 | const param = llvm_func.getParam(llvm_arg_i); |
| 770 | llvm_arg_i += 1; |
| 771 | |
| 772 | const big_int_ty = dg.context.intType(int_bits); |
| 773 | var bits_used: u32 = 0; |
| 774 | while (bits_used < int_bits) { |
| 775 | const field = fields[field_i]; |
| 776 | const field_alignment = field.normalAlignment(target); |
| 777 | const prev_offset = field_offset; |
| 778 | field_offset = std.mem.alignForwardGeneric(u32, field_offset, field_alignment); |
| 779 | if (field_offset > prev_offset) { |
| 780 | // Padding counts as bits used. |
| 781 | bits_used += (field_offset - prev_offset) * 8; |
| 782 | if (bits_used >= int_bits) break; |
| 783 | } |
| 784 | const field_size = @intCast(u16, field.ty.abiSize(target)); |
| 785 | const field_abi_bits = field_size * 8; |
| 786 | const field_int_ty = dg.context.intType(field_abi_bits); |
| 787 | const shifted = if (bits_used == 0) param else s: { |
| 788 | const shift_amt = big_int_ty.constInt(bits_used, .False); |
| 789 | break :s builder.buildLShr(param, shift_amt, ""); |
| 790 | }; |
| 791 | const field_as_int = builder.buildTrunc(shifted, field_int_ty, ""); |
| 792 | var ty_buf: Type.Payload.Pointer = undefined; |
| 793 | const llvm_i = llvmFieldIndex(param_ty, field_i, target, &ty_buf).?; |
| 794 | const field_ptr = builder.buildStructGEP(arg_ptr, llvm_i, ""); |
| 795 | const casted_ptr = builder.buildBitCast(field_ptr, field_int_ty.pointerType(0), ""); |
| 796 | const store_inst = builder.buildStore(field_as_int, casted_ptr); |
| 797 | store_inst.setAlignment(field_alignment); |
| 798 | |
| 799 | field_i += 1; |
| 800 | if (field_i >= fields.len) break; |
| 801 | |
| 802 | bits_used += field_abi_bits; |
| 803 | field_offset += field_size; |
| 804 | } |
| 805 | if (field_i >= fields.len) break; |
| 806 | } |
| 807 | |
| 808 | try args.append(arg_ptr); |
| 809 | } else { |
| 810 | @panic("TODO: LLVM backend: implement C calling convention on x86_64 with byval struct parameter"); |
| 811 | } |
| 812 | }, |
| 813 | .Union => { |
| 814 | @panic("TODO: LLVM backend: implement C calling convention on x86_64 with union parameter"); |
| 815 | }, |
| 816 | else => unreachable, |
| 817 | } |
| 818 | }, |
| 702 | 819 | }; |
| 703 | | try args.append(loaded_param); |
| 704 | 820 | } |
| 705 | 821 | |
| 706 | 822 | var di_file: ?*llvm.DIFile = null; |
| ... | ... | @@ -2072,22 +2188,31 @@ pub const DeclGen = struct { |
| 2072 | 2188 | } |
| 2073 | 2189 | |
| 2074 | 2190 | // Set parameter attributes. |
| 2075 | | var llvm_param_i: c_uint = @as(c_uint, @boolToInt(sret)) + @boolToInt(err_return_tracing); |
| 2076 | | for (fn_info.param_types) |param_ty| { |
| 2077 | | if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; |
| 2078 | | |
| 2079 | | if (isByRef(param_ty)) { |
| 2080 | | dg.addArgAttr(llvm_fn, llvm_param_i, "nonnull"); |
| 2081 | | // TODO readonly, noalias, align |
| 2082 | | } |
| 2083 | | llvm_param_i += 1; |
| 2084 | | } |
| 2085 | | |
| 2086 | 2191 | // TODO: more attributes. see codegen.cpp `make_fn_llvm_value`. |
| 2087 | | if (fn_info.cc == .Naked) { |
| 2088 | | dg.addFnAttr(llvm_fn, "naked"); |
| 2089 | | } else { |
| 2090 | | llvm_fn.setFunctionCallConv(toLlvmCallConv(fn_info.cc, target)); |
| 2192 | switch (fn_info.cc) { |
| 2193 | .Unspecified, .Inline => { |
| 2194 | var llvm_param_i: c_uint = @as(c_uint, @boolToInt(sret)) + @boolToInt(err_return_tracing); |
| 2195 | for (fn_info.param_types) |param_ty| { |
| 2196 | if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; |
| 2197 | |
| 2198 | if (isByRef(param_ty)) { |
| 2199 | dg.addArgAttr(llvm_fn, llvm_param_i, "nonnull"); |
| 2200 | // TODO readonly, noalias, align |
| 2201 | } |
| 2202 | llvm_param_i += 1; |
| 2203 | } |
| 2204 | llvm_fn.setFunctionCallConv(.Fast); |
| 2205 | }, |
| 2206 | .Naked => { |
| 2207 | dg.addFnAttr(llvm_fn, "naked"); |
| 2208 | }, |
| 2209 | .Async => { |
| 2210 | llvm_fn.setFunctionCallConv(.Fast); |
| 2211 | @panic("TODO: LLVM backend lower async function"); |
| 2212 | }, |
| 2213 | else => { |
| 2214 | llvm_fn.setFunctionCallConv(toLlvmCallConv(fn_info.cc, target)); |
| 2215 | }, |
| 2091 | 2216 | } |
| 2092 | 2217 | |
| 2093 | 2218 | if (fn_info.alignment != 0) { |
| ... | ... | @@ -2518,42 +2643,7 @@ pub const DeclGen = struct { |
| 2518 | 2643 | llvm_union_ty.structSetBody(&llvm_fields, llvm_fields_len, .False); |
| 2519 | 2644 | return llvm_union_ty; |
| 2520 | 2645 | }, |
| 2521 | | .Fn => { |
| 2522 | | const fn_info = t.fnInfo(); |
| 2523 | | const llvm_ret_ty = try lowerFnRetTy(dg, fn_info); |
| 2524 | | |
| 2525 | | var llvm_params = std.ArrayList(*const llvm.Type).init(dg.gpa); |
| 2526 | | defer llvm_params.deinit(); |
| 2527 | | |
| 2528 | | if (firstParamSRet(fn_info, target)) { |
| 2529 | | const llvm_sret_ty = try dg.llvmType(fn_info.return_type); |
| 2530 | | try llvm_params.append(llvm_sret_ty.pointerType(0)); |
| 2531 | | } |
| 2532 | | |
| 2533 | | if (fn_info.return_type.isError() and |
| 2534 | | dg.module.comp.bin_file.options.error_return_tracing) |
| 2535 | | { |
| 2536 | | var ptr_ty_payload: Type.Payload.ElemType = .{ |
| 2537 | | .base = .{ .tag = .single_mut_pointer }, |
| 2538 | | .data = dg.object.getStackTraceType(), |
| 2539 | | }; |
| 2540 | | const ptr_ty = Type.initPayload(&ptr_ty_payload.base); |
| 2541 | | try llvm_params.append(try lowerFnParamTy(dg, fn_info.cc, ptr_ty)); |
| 2542 | | } |
| 2543 | | |
| 2544 | | for (fn_info.param_types) |param_ty| { |
| 2545 | | if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; |
| 2546 | | |
| 2547 | | try llvm_params.append(try lowerFnParamTy(dg, fn_info.cc, param_ty)); |
| 2548 | | } |
| 2549 | | |
| 2550 | | return llvm.functionType( |
| 2551 | | llvm_ret_ty, |
| 2552 | | llvm_params.items.ptr, |
| 2553 | | @intCast(c_uint, llvm_params.items.len), |
| 2554 | | llvm.Bool.fromBool(fn_info.is_var_args), |
| 2555 | | ); |
| 2556 | | }, |
| 2646 | .Fn => return llvmTypeFn(dg, t), |
| 2557 | 2647 | .ComptimeInt => unreachable, |
| 2558 | 2648 | .ComptimeFloat => unreachable, |
| 2559 | 2649 | .Type => unreachable, |
| ... | ... | @@ -2568,6 +2658,119 @@ pub const DeclGen = struct { |
| 2568 | 2658 | } |
| 2569 | 2659 | } |
| 2570 | 2660 | |
| 2661 | fn llvmTypeFn(dg: *DeclGen, fn_ty: Type) Allocator.Error!*const llvm.Type { |
| 2662 | const target = dg.module.getTarget(); |
| 2663 | const fn_info = fn_ty.fnInfo(); |
| 2664 | const llvm_ret_ty = try lowerFnRetTy(dg, fn_info); |
| 2665 | |
| 2666 | var llvm_params = std.ArrayList(*const llvm.Type).init(dg.gpa); |
| 2667 | defer llvm_params.deinit(); |
| 2668 | |
| 2669 | if (firstParamSRet(fn_info, target)) { |
| 2670 | const llvm_sret_ty = try dg.llvmType(fn_info.return_type); |
| 2671 | try llvm_params.append(llvm_sret_ty.pointerType(0)); |
| 2672 | } |
| 2673 | |
| 2674 | if (fn_info.return_type.isError() and |
| 2675 | dg.module.comp.bin_file.options.error_return_tracing) |
| 2676 | { |
| 2677 | var ptr_ty_payload: Type.Payload.ElemType = .{ |
| 2678 | .base = .{ .tag = .single_mut_pointer }, |
| 2679 | .data = dg.object.getStackTraceType(), |
| 2680 | }; |
| 2681 | const ptr_ty = Type.initPayload(&ptr_ty_payload.base); |
| 2682 | try llvm_params.append(try dg.llvmType(ptr_ty)); |
| 2683 | } |
| 2684 | |
| 2685 | var it = iterateParamTypes(dg, fn_info); |
| 2686 | while (it.next()) |lowering| switch (lowering) { |
| 2687 | .no_bits => continue, |
| 2688 | .byval => { |
| 2689 | const param_ty = fn_info.param_types[it.zig_index - 1]; |
| 2690 | try llvm_params.append(try dg.llvmType(param_ty)); |
| 2691 | }, |
| 2692 | .byref => { |
| 2693 | const param_ty = fn_info.param_types[it.zig_index - 1]; |
| 2694 | const raw_llvm_ty = try dg.llvmType(param_ty); |
| 2695 | try llvm_params.append(raw_llvm_ty.pointerType(0)); |
| 2696 | }, |
| 2697 | .abi_sized_int => { |
| 2698 | const param_ty = fn_info.param_types[it.zig_index - 1]; |
| 2699 | const abi_size = @intCast(c_uint, param_ty.abiSize(target)); |
| 2700 | try llvm_params.append(dg.context.intType(abi_size * 8)); |
| 2701 | }, |
| 2702 | .multiple_llvm_ints => { |
| 2703 | const param_ty = fn_info.param_types[it.zig_index - 1]; |
| 2704 | const llvm_ints = it.llvm_types_buffer[0..it.llvm_types_len]; |
| 2705 | try llvm_params.ensureUnusedCapacity(it.llvm_types_len); |
| 2706 | |
| 2707 | // The reason we have all this logic instead of simply appending |
| 2708 | // big_int_ty is for the special case of a pointer type; |
| 2709 | // we want to use a pointer type instead of inttoptr at the callsites, |
| 2710 | // which may prevent optimization. |
| 2711 | switch (param_ty.zigTypeTag()) { |
| 2712 | .Struct => { |
| 2713 | const fields = param_ty.structFields().values(); |
| 2714 | var field_i: u32 = 0; |
| 2715 | var field_offset: u32 = 0; |
| 2716 | llvm_arg: for (llvm_ints) |int_bits| { |
| 2717 | const big_int_ty = dg.context.intType(int_bits); |
| 2718 | var bits_used: u32 = 0; |
| 2719 | while (bits_used < int_bits) { |
| 2720 | const field = fields[field_i]; |
| 2721 | const field_alignment = field.normalAlignment(target); |
| 2722 | const prev_offset = field_offset; |
| 2723 | field_offset = std.mem.alignForwardGeneric(u32, field_offset, field_alignment); |
| 2724 | if (field_offset > prev_offset) { |
| 2725 | // Padding counts as bits used. |
| 2726 | bits_used += (field_offset - prev_offset) * 8; |
| 2727 | if (bits_used >= int_bits) break; |
| 2728 | } |
| 2729 | const field_size = @intCast(u16, field.ty.abiSize(target)); |
| 2730 | const field_abi_bits = field_size * 8; |
| 2731 | |
| 2732 | // Special case for when the entire LLVM integer represents |
| 2733 | // one field; in this case keep the type information |
| 2734 | // to avoid the potentially costly ptrtoint/bitcast. |
| 2735 | if (bits_used == 0 and field_abi_bits == int_bits) { |
| 2736 | const llvm_field_ty = try dg.llvmType(field.ty); |
| 2737 | llvm_params.appendAssumeCapacity(llvm_field_ty); |
| 2738 | field_i += 1; |
| 2739 | if (field_i >= fields.len) { |
| 2740 | break :llvm_arg; |
| 2741 | } else { |
| 2742 | continue :llvm_arg; |
| 2743 | } |
| 2744 | } |
| 2745 | |
| 2746 | field_i += 1; |
| 2747 | if (field_i >= fields.len) break; |
| 2748 | |
| 2749 | bits_used += field_abi_bits; |
| 2750 | field_offset += field_size; |
| 2751 | } |
| 2752 | llvm_params.appendAssumeCapacity(big_int_ty); |
| 2753 | if (field_i >= fields.len) break; |
| 2754 | } |
| 2755 | }, |
| 2756 | else => { |
| 2757 | for (llvm_ints) |int_bits| { |
| 2758 | const big_int_ty = dg.context.intType(int_bits); |
| 2759 | llvm_params.appendAssumeCapacity(big_int_ty); |
| 2760 | } |
| 2761 | }, |
| 2762 | } |
| 2763 | }, |
| 2764 | }; |
| 2765 | |
| 2766 | return llvm.functionType( |
| 2767 | llvm_ret_ty, |
| 2768 | llvm_params.items.ptr, |
| 2769 | @intCast(c_uint, llvm_params.items.len), |
| 2770 | llvm.Bool.fromBool(fn_info.is_var_args), |
| 2771 | ); |
| 2772 | } |
| 2773 | |
| 2571 | 2774 | fn genTypedValue(dg: *DeclGen, tv: TypedValue) Error!*const llvm.Value { |
| 2572 | 2775 | if (tv.val.isUndef()) { |
| 2573 | 2776 | const llvm_type = try dg.llvmType(tv.ty); |
| ... | ... | @@ -3848,52 +4051,164 @@ pub const FuncGen = struct { |
| 3848 | 4051 | try llvm_args.append(self.err_ret_trace.?); |
| 3849 | 4052 | } |
| 3850 | 4053 | |
| 3851 | | for (args) |arg| { |
| 3852 | | const param_ty = self.air.typeOf(arg); |
| 3853 | | if (!param_ty.hasRuntimeBitsIgnoreComptime()) continue; |
| 3854 | | |
| 3855 | | const llvm_arg = try self.resolveInst(arg); |
| 3856 | | const abi_llvm_ty = try lowerFnParamTy(self.dg, fn_info.cc, param_ty); |
| 3857 | | const param_llvm_ty = llvm_arg.typeOf(); |
| 3858 | | if (abi_llvm_ty == param_llvm_ty) { |
| 3859 | | try llvm_args.append(llvm_arg); |
| 3860 | | continue; |
| 3861 | | } |
| 3862 | | |
| 3863 | | // In this case the function param type is honoring the calling convention |
| 3864 | | // by having a different LLVM type than the usual one. We solve this here |
| 3865 | | // at the callsite by bitcasting a pointer to our canonical type, then |
| 3866 | | // loading it if necessary. |
| 3867 | | const alignment = param_ty.abiAlignment(target); |
| 3868 | | const ptr_abi_ty = abi_llvm_ty.pointerType(0); |
| 3869 | | |
| 3870 | | const casted_ptr = if (isByRef(param_ty)) |
| 3871 | | self.builder.buildBitCast(llvm_arg, ptr_abi_ty, "") |
| 3872 | | else p: { |
| 3873 | | const arg_ptr = self.buildAlloca(param_llvm_ty); |
| 3874 | | arg_ptr.setAlignment(alignment); |
| 3875 | | const store_inst = self.builder.buildStore(llvm_arg, arg_ptr); |
| 3876 | | store_inst.setAlignment(alignment); |
| 3877 | | |
| 3878 | | if (abi_llvm_ty.getTypeKind() == .Pointer) { |
| 3879 | | // In this case, the calling convention wants a pointer, but |
| 3880 | | // we have a value. |
| 3881 | | if (arg_ptr.typeOf() == abi_llvm_ty) { |
| 3882 | | try llvm_args.append(arg_ptr); |
| 3883 | | continue; |
| 4054 | var it = iterateParamTypes(self.dg, fn_info); |
| 4055 | while (it.next()) |lowering| switch (lowering) { |
| 4056 | .no_bits => continue, |
| 4057 | .byval => { |
| 4058 | const arg = args[it.zig_index - 1]; |
| 4059 | const param_ty = self.air.typeOf(arg); |
| 4060 | const llvm_arg = try self.resolveInst(arg); |
| 4061 | if (isByRef(param_ty)) { |
| 4062 | const alignment = param_ty.abiAlignment(target); |
| 4063 | const load_inst = self.builder.buildLoad(llvm_arg, ""); |
| 4064 | load_inst.setAlignment(alignment); |
| 4065 | try llvm_args.append(load_inst); |
| 4066 | } else { |
| 4067 | if (param_ty.zigTypeTag() == .Pointer) { |
| 4068 | // We need a bitcast in case of two possibilities: |
| 4069 | // 1. The parameter type is a pointer to zero-sized type, |
| 4070 | // which is always lowered to an LLVM type of `*i8`. |
| 4071 | // 2. The argument is a global which does act as a pointer, however |
| 4072 | // a bitcast is needed in order for the LLVM types to match. |
| 4073 | const llvm_param_ty = try self.dg.llvmType(param_ty); |
| 4074 | const casted_ptr = self.builder.buildBitCast(llvm_arg, llvm_param_ty, ""); |
| 4075 | try llvm_args.append(casted_ptr); |
| 4076 | } else { |
| 4077 | try llvm_args.append(llvm_arg); |
| 3884 | 4078 | } |
| 3885 | | const casted_ptr = self.builder.buildBitCast(arg_ptr, abi_llvm_ty, ""); |
| 3886 | | try llvm_args.append(casted_ptr); |
| 3887 | | continue; |
| 3888 | 4079 | } |
| 3889 | | |
| 3890 | | break :p self.builder.buildBitCast(arg_ptr, ptr_abi_ty, ""); |
| 3891 | | }; |
| 3892 | | |
| 3893 | | const load_inst = self.builder.buildLoad(casted_ptr, ""); |
| 3894 | | load_inst.setAlignment(alignment); |
| 3895 | | try llvm_args.append(load_inst); |
| 3896 | | } |
| 4080 | }, |
| 4081 | .byref => { |
| 4082 | const arg = args[it.zig_index - 1]; |
| 4083 | const param_ty = self.air.typeOf(arg); |
| 4084 | const llvm_arg = try self.resolveInst(arg); |
| 4085 | if (isByRef(param_ty)) { |
| 4086 | try llvm_args.append(llvm_arg); |
| 4087 | } else { |
| 4088 | const alignment = param_ty.abiAlignment(target); |
| 4089 | const param_llvm_ty = llvm_arg.typeOf(); |
| 4090 | const arg_ptr = self.buildAlloca(param_llvm_ty); |
| 4091 | arg_ptr.setAlignment(alignment); |
| 4092 | const store_inst = self.builder.buildStore(llvm_arg, arg_ptr); |
| 4093 | store_inst.setAlignment(alignment); |
| 4094 | try llvm_args.append(arg_ptr); |
| 4095 | } |
| 4096 | }, |
| 4097 | .abi_sized_int => { |
| 4098 | const arg = args[it.zig_index - 1]; |
| 4099 | const param_ty = self.air.typeOf(arg); |
| 4100 | const llvm_arg = try self.resolveInst(arg); |
| 4101 | const abi_size = @intCast(c_uint, param_ty.abiSize(target)); |
| 4102 | const int_llvm_ty = self.dg.context.intType(abi_size * 8); |
| 4103 | const int_ptr_llvm_ty = int_llvm_ty.pointerType(0); |
| 4104 | |
| 4105 | if (isByRef(param_ty)) { |
| 4106 | const alignment = param_ty.abiAlignment(target); |
| 4107 | const casted_ptr = self.builder.buildBitCast(llvm_arg, int_ptr_llvm_ty, ""); |
| 4108 | const load_inst = self.builder.buildLoad(casted_ptr, ""); |
| 4109 | load_inst.setAlignment(alignment); |
| 4110 | try llvm_args.append(load_inst); |
| 4111 | } else { |
| 4112 | // LLVM does not allow bitcasting structs so we must allocate |
| 4113 | // a local, bitcast its pointer, store, and then load. |
| 4114 | const alignment = @maximum( |
| 4115 | param_ty.abiAlignment(target), |
| 4116 | self.dg.object.target_data.abiAlignmentOfType(int_llvm_ty), |
| 4117 | ); |
| 4118 | const int_ptr = self.buildAlloca(int_llvm_ty); |
| 4119 | int_ptr.setAlignment(alignment); |
| 4120 | const param_llvm_ty = try self.dg.llvmType(param_ty); |
| 4121 | const casted_ptr = self.builder.buildBitCast(int_ptr, param_llvm_ty.pointerType(0), ""); |
| 4122 | const store_inst = self.builder.buildStore(llvm_arg, casted_ptr); |
| 4123 | store_inst.setAlignment(alignment); |
| 4124 | const load_inst = self.builder.buildLoad(int_ptr, ""); |
| 4125 | load_inst.setAlignment(alignment); |
| 4126 | try llvm_args.append(load_inst); |
| 4127 | } |
| 4128 | }, |
| 4129 | .multiple_llvm_ints => { |
| 4130 | const arg = args[it.zig_index - 1]; |
| 4131 | const param_ty = self.air.typeOf(arg); |
| 4132 | const llvm_ints = it.llvm_types_buffer[0..it.llvm_types_len]; |
| 4133 | const llvm_arg = try self.resolveInst(arg); |
| 4134 | const is_by_ref = isByRef(param_ty); |
| 4135 | try llvm_args.ensureUnusedCapacity(it.llvm_types_len); |
| 4136 | switch (param_ty.zigTypeTag()) { |
| 4137 | .Struct => { |
| 4138 | const fields = param_ty.structFields().values(); |
| 4139 | var field_i: u32 = 0; |
| 4140 | var field_offset: u32 = 0; |
| 4141 | for (llvm_ints) |int_bits| { |
| 4142 | const big_int_ty = self.dg.context.intType(int_bits); |
| 4143 | var int_arg: *const llvm.Value = undefined; |
| 4144 | var bits_used: u32 = 0; |
| 4145 | while (bits_used < int_bits) { |
| 4146 | const field = fields[field_i]; |
| 4147 | const field_alignment = field.normalAlignment(target); |
| 4148 | const prev_offset = field_offset; |
| 4149 | field_offset = std.mem.alignForwardGeneric(u32, field_offset, field_alignment); |
| 4150 | if (field_offset > prev_offset) { |
| 4151 | // Padding counts as bits used. |
| 4152 | bits_used += (field_offset - prev_offset) * 8; |
| 4153 | if (bits_used >= int_bits) break; |
| 4154 | } |
| 4155 | var ty_buf: Type.Payload.Pointer = undefined; |
| 4156 | const llvm_i = llvmFieldIndex(param_ty, field_i, target, &ty_buf).?; |
| 4157 | const field_size = @intCast(u16, field.ty.abiSize(target)); |
| 4158 | const field_abi_bits = field_size * 8; |
| 4159 | |
| 4160 | // Special case for when the entire LLVM integer represents |
| 4161 | // one field; in this case keep the type information |
| 4162 | // to avoid the potentially costly ptrtoint/bitcast. |
| 4163 | if (bits_used == 0 and field_abi_bits == int_bits) { |
| 4164 | int_arg = if (is_by_ref) f: { |
| 4165 | const field_ptr = self.builder.buildStructGEP(llvm_arg, llvm_i, ""); |
| 4166 | const load_inst = self.builder.buildLoad(field_ptr, ""); |
| 4167 | load_inst.setAlignment(field_alignment); |
| 4168 | break :f load_inst; |
| 4169 | } else self.builder.buildExtractValue(llvm_arg, llvm_i, ""); |
| 4170 | field_i += 1; |
| 4171 | break; |
| 4172 | } |
| 4173 | |
| 4174 | const field_int_ty = self.dg.context.intType(field_abi_bits); |
| 4175 | const llvm_field = if (is_by_ref) f: { |
| 4176 | const field_ptr = self.builder.buildStructGEP(llvm_arg, llvm_i, ""); |
| 4177 | const casted_ptr = self.builder.buildBitCast(field_ptr, field_int_ty.pointerType(0), ""); |
| 4178 | const load_inst = self.builder.buildLoad(casted_ptr, ""); |
| 4179 | load_inst.setAlignment(field_alignment); |
| 4180 | break :f load_inst; |
| 4181 | } else f: { |
| 4182 | const llvm_field = self.builder.buildExtractValue(llvm_arg, llvm_i, ""); |
| 4183 | break :f self.builder.buildBitCast(llvm_field, field_int_ty, ""); |
| 4184 | }; |
| 4185 | |
| 4186 | const extended = self.builder.buildZExt(llvm_field, big_int_ty, ""); |
| 4187 | if (bits_used == 0) { |
| 4188 | int_arg = extended; |
| 4189 | } else { |
| 4190 | const shift_amt = big_int_ty.constInt(bits_used, .False); |
| 4191 | const shifted = self.builder.buildShl(extended, shift_amt, ""); |
| 4192 | int_arg = self.builder.buildOr(int_arg, shifted, ""); |
| 4193 | } |
| 4194 | |
| 4195 | field_i += 1; |
| 4196 | if (field_i >= fields.len) break; |
| 4197 | |
| 4198 | bits_used += field_abi_bits; |
| 4199 | field_offset += field_size; |
| 4200 | } |
| 4201 | llvm_args.appendAssumeCapacity(int_arg); |
| 4202 | if (field_i >= fields.len) break; |
| 4203 | } |
| 4204 | }, |
| 4205 | .Union => { |
| 4206 | return self.todo("airCall C calling convention on x86_64 with union argument ", .{}); |
| 4207 | }, |
| 4208 | else => unreachable, |
| 4209 | } |
| 4210 | }, |
| 4211 | }; |
| 3897 | 4212 | |
| 3898 | 4213 | const call = self.builder.buildCall( |
| 3899 | 4214 | llvm_fn, |
| ... | ... | @@ -6489,24 +6804,7 @@ pub const FuncGen = struct { |
| 6489 | 6804 | /// Use this instead of builder.buildAlloca, because this function makes sure to |
| 6490 | 6805 | /// put the alloca instruction at the top of the function! |
| 6491 | 6806 | fn buildAlloca(self: *FuncGen, llvm_ty: *const llvm.Type) *const llvm.Value { |
| 6492 | | const prev_block = self.builder.getInsertBlock(); |
| 6493 | | const prev_debug_location = self.builder.getCurrentDebugLocation2(); |
| 6494 | | defer { |
| 6495 | | self.builder.positionBuilderAtEnd(prev_block); |
| 6496 | | if (self.di_scope != null) { |
| 6497 | | self.builder.setCurrentDebugLocation2(prev_debug_location); |
| 6498 | | } |
| 6499 | | } |
| 6500 | | |
| 6501 | | const entry_block = self.llvm_func.getFirstBasicBlock().?; |
| 6502 | | if (entry_block.getFirstInstruction()) |first_inst| { |
| 6503 | | self.builder.positionBuilder(entry_block, first_inst); |
| 6504 | | } else { |
| 6505 | | self.builder.positionBuilderAtEnd(entry_block); |
| 6506 | | } |
| 6507 | | self.builder.clearCurrentDebugLocation(); |
| 6508 | | |
| 6509 | | return self.builder.buildAlloca(llvm_ty, ""); |
| 6807 | return buildAllocaInner(self.builder, self.llvm_func, self.di_scope != null, llvm_ty); |
| 6510 | 6808 | } |
| 6511 | 6809 | |
| 6512 | 6810 | fn airStore(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| ... | ... | @@ -8296,99 +8594,171 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm. |
| 8296 | 8594 | } |
| 8297 | 8595 | } |
| 8298 | 8596 | |
| 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, |
| 8597 | const ParamTypeIterator = struct { |
| 8598 | dg: *DeclGen, |
| 8599 | fn_info: Type.Payload.Function.Data, |
| 8600 | zig_index: u32, |
| 8601 | llvm_index: u32, |
| 8602 | target: std.Target, |
| 8603 | llvm_types_len: u32, |
| 8604 | llvm_types_buffer: [8]u16, |
| 8605 | |
| 8606 | const Lowering = enum { |
| 8607 | no_bits, |
| 8608 | byval, |
| 8609 | byref, |
| 8610 | abi_sized_int, |
| 8611 | multiple_llvm_ints, |
| 8612 | }; |
| 8325 | 8613 | |
| 8326 | | else => false, |
| 8327 | | }; |
| 8328 | | switch (target.cpu.arch) { |
| 8329 | | .mips, .mipsel => return dg.llvmType(ty), |
| 8330 | | .x86_64 => switch (target.os.tag) { |
| 8331 | | .windows => switch (x86_64_abi.classifyWindows(ty, target)) { |
| 8332 | | .integer => { |
| 8614 | fn next(it: *ParamTypeIterator) ?Lowering { |
| 8615 | if (it.zig_index >= it.fn_info.param_types.len) return null; |
| 8616 | |
| 8617 | const ty = it.fn_info.param_types[it.zig_index]; |
| 8618 | if (!ty.hasRuntimeBitsIgnoreComptime()) { |
| 8619 | it.zig_index += 1; |
| 8620 | return .no_bits; |
| 8621 | } |
| 8622 | |
| 8623 | switch (it.fn_info.cc) { |
| 8624 | .Unspecified, .Inline => { |
| 8625 | it.zig_index += 1; |
| 8626 | it.llvm_index += 1; |
| 8627 | if (isByRef(ty)) { |
| 8628 | return .byref; |
| 8629 | } else { |
| 8630 | return .byval; |
| 8631 | } |
| 8632 | }, |
| 8633 | .Async => { |
| 8634 | @panic("TODO implement async function lowering in the LLVM backend"); |
| 8635 | }, |
| 8636 | .C => { |
| 8637 | const is_scalar = switch (ty.zigTypeTag()) { |
| 8638 | .Void, |
| 8639 | .Bool, |
| 8640 | .NoReturn, |
| 8641 | .Int, |
| 8642 | .Float, |
| 8643 | .Pointer, |
| 8644 | .Optional, |
| 8645 | .ErrorSet, |
| 8646 | .Enum, |
| 8647 | .AnyFrame, |
| 8648 | .Vector, |
| 8649 | => true, |
| 8650 | |
| 8651 | else => false, |
| 8652 | }; |
| 8653 | switch (it.target.cpu.arch) { |
| 8654 | .mips, .mipsel => { |
| 8655 | it.zig_index += 1; |
| 8656 | it.llvm_index += 1; |
| 8657 | return .byval; |
| 8658 | }, |
| 8659 | .x86_64 => switch (it.target.os.tag) { |
| 8660 | .windows => switch (x86_64_abi.classifyWindows(ty, it.target)) { |
| 8661 | .integer => { |
| 8662 | if (is_scalar) { |
| 8663 | it.zig_index += 1; |
| 8664 | it.llvm_index += 1; |
| 8665 | return .byval; |
| 8666 | } else { |
| 8667 | it.zig_index += 1; |
| 8668 | it.llvm_index += 1; |
| 8669 | return .abi_sized_int; |
| 8670 | } |
| 8671 | }, |
| 8672 | .memory => { |
| 8673 | it.zig_index += 1; |
| 8674 | it.llvm_index += 1; |
| 8675 | return .byref; |
| 8676 | }, |
| 8677 | .sse => { |
| 8678 | it.zig_index += 1; |
| 8679 | it.llvm_index += 1; |
| 8680 | return .byval; |
| 8681 | }, |
| 8682 | else => unreachable, |
| 8683 | }, |
| 8684 | else => { |
| 8333 | 8685 | if (is_scalar) { |
| 8334 | | return dg.llvmType(ty); |
| 8335 | | } else { |
| 8336 | | const abi_size = ty.abiSize(target); |
| 8337 | | return dg.context.intType(@intCast(c_uint, abi_size * 8)); |
| 8686 | it.zig_index += 1; |
| 8687 | it.llvm_index += 1; |
| 8688 | return .byval; |
| 8689 | } |
| 8690 | const classes = x86_64_abi.classifySystemV(ty, it.target); |
| 8691 | if (classes[0] == .memory) { |
| 8692 | it.zig_index += 1; |
| 8693 | it.llvm_index += 1; |
| 8694 | return .byref; |
| 8695 | } |
| 8696 | var llvm_types_buffer: [8]u16 = undefined; |
| 8697 | var llvm_types_index: u32 = 0; |
| 8698 | for (classes) |class| { |
| 8699 | switch (class) { |
| 8700 | .integer => { |
| 8701 | llvm_types_buffer[llvm_types_index] = 64; |
| 8702 | llvm_types_index += 1; |
| 8703 | }, |
| 8704 | .sse => { |
| 8705 | @panic("TODO"); |
| 8706 | }, |
| 8707 | .sseup => { |
| 8708 | @panic("TODO"); |
| 8709 | }, |
| 8710 | .x87 => { |
| 8711 | @panic("TODO"); |
| 8712 | }, |
| 8713 | .x87up => { |
| 8714 | @panic("TODO"); |
| 8715 | }, |
| 8716 | .complex_x87 => { |
| 8717 | @panic("TODO"); |
| 8718 | }, |
| 8719 | .memory => unreachable, // handled above |
| 8720 | .none => break, |
| 8721 | } |
| 8338 | 8722 | } |
| 8723 | if (classes[0] == .integer and classes[1] == .none) { |
| 8724 | it.zig_index += 1; |
| 8725 | it.llvm_index += 1; |
| 8726 | return .abi_sized_int; |
| 8727 | } |
| 8728 | it.llvm_types_buffer = llvm_types_buffer; |
| 8729 | it.llvm_types_len = llvm_types_index; |
| 8730 | it.llvm_index += llvm_types_index; |
| 8731 | it.zig_index += 1; |
| 8732 | return .multiple_llvm_ints; |
| 8339 | 8733 | }, |
| 8340 | | .memory => return (try dg.llvmType(ty)).pointerType(0), |
| 8341 | | .sse => return dg.llvmType(ty), |
| 8342 | | else => unreachable, |
| 8343 | 8734 | }, |
| 8735 | // TODO investigate C ABI for other architectures |
| 8344 | 8736 | else => { |
| 8345 | | if (is_scalar) { |
| 8346 | | return dg.llvmType(ty); |
| 8347 | | } |
| 8348 | | const classes = x86_64_abi.classifySystemV(ty, target); |
| 8349 | | if (classes[0] == .memory) { |
| 8350 | | return (try dg.llvmType(ty)).pointerType(0); |
| 8351 | | } |
| 8352 | | var llvm_types_buffer: [8]*const llvm.Type = undefined; |
| 8353 | | var llvm_types_index: u32 = 0; |
| 8354 | | for (classes) |class| { |
| 8355 | | switch (class) { |
| 8356 | | .integer => { |
| 8357 | | llvm_types_buffer[llvm_types_index] = dg.context.intType(64); |
| 8358 | | llvm_types_index += 1; |
| 8359 | | }, |
| 8360 | | .sse => { |
| 8361 | | @panic("TODO"); |
| 8362 | | }, |
| 8363 | | .sseup => { |
| 8364 | | @panic("TODO"); |
| 8365 | | }, |
| 8366 | | .x87 => { |
| 8367 | | @panic("TODO"); |
| 8368 | | }, |
| 8369 | | .x87up => { |
| 8370 | | @panic("TODO"); |
| 8371 | | }, |
| 8372 | | .complex_x87 => { |
| 8373 | | @panic("TODO"); |
| 8374 | | }, |
| 8375 | | .memory => unreachable, // handled above |
| 8376 | | .none => break, |
| 8377 | | } |
| 8378 | | } |
| 8379 | | if (classes[0] == .integer and classes[1] == .none) { |
| 8380 | | const abi_size = ty.abiSize(target); |
| 8381 | | return dg.context.intType(@intCast(c_uint, abi_size * 8)); |
| 8382 | | } |
| 8383 | | return dg.context.structType(&llvm_types_buffer, llvm_types_index, .False); |
| 8737 | it.zig_index += 1; |
| 8738 | it.llvm_index += 1; |
| 8739 | return .byval; |
| 8384 | 8740 | }, |
| 8385 | | }, |
| 8386 | | // TODO investigate C ABI for other architectures |
| 8387 | | else => return dg.llvmType(ty), |
| 8388 | | } |
| 8389 | | }, |
| 8390 | | else => return dg.llvmType(ty), |
| 8741 | } |
| 8742 | }, |
| 8743 | else => { |
| 8744 | it.zig_index += 1; |
| 8745 | it.llvm_index += 1; |
| 8746 | return .byval; |
| 8747 | }, |
| 8748 | } |
| 8391 | 8749 | } |
| 8750 | }; |
| 8751 | |
| 8752 | fn iterateParamTypes(dg: *DeclGen, fn_info: Type.Payload.Function.Data) ParamTypeIterator { |
| 8753 | return .{ |
| 8754 | .dg = dg, |
| 8755 | .fn_info = fn_info, |
| 8756 | .zig_index = 0, |
| 8757 | .llvm_index = 0, |
| 8758 | .target = dg.module.getTarget(), |
| 8759 | .llvm_types_buffer = undefined, |
| 8760 | .llvm_types_len = 0, |
| 8761 | }; |
| 8392 | 8762 | } |
| 8393 | 8763 | |
| 8394 | 8764 | fn isByRef(ty: Type) bool { |
| ... | ... | @@ -8549,3 +8919,29 @@ fn compilerRtIntBits(bits: u16) u16 { |
| 8549 | 8919 | } |
| 8550 | 8920 | return bits; |
| 8551 | 8921 | } |
| 8922 | |
| 8923 | fn buildAllocaInner( |
| 8924 | builder: *const llvm.Builder, |
| 8925 | llvm_func: *const llvm.Value, |
| 8926 | di_scope_non_null: bool, |
| 8927 | llvm_ty: *const llvm.Type, |
| 8928 | ) *const llvm.Value { |
| 8929 | const prev_block = builder.getInsertBlock(); |
| 8930 | const prev_debug_location = builder.getCurrentDebugLocation2(); |
| 8931 | defer { |
| 8932 | builder.positionBuilderAtEnd(prev_block); |
| 8933 | if (di_scope_non_null) { |
| 8934 | builder.setCurrentDebugLocation2(prev_debug_location); |
| 8935 | } |
| 8936 | } |
| 8937 | |
| 8938 | const entry_block = llvm_func.getFirstBasicBlock().?; |
| 8939 | if (entry_block.getFirstInstruction()) |first_inst| { |
| 8940 | builder.positionBuilder(entry_block, first_inst); |
| 8941 | } else { |
| 8942 | builder.positionBuilderAtEnd(entry_block); |
| 8943 | } |
| 8944 | builder.clearCurrentDebugLocation(); |
| 8945 | |
| 8946 | return builder.buildAlloca(llvm_ty, ""); |
| 8947 | } |