authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-20 23:52:01-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-21 00:09:30-07:00
logf31f86a86a482267a524cdca59fb2b940ecadf25
tree8b23b571343ad7c89d5731f37a981c0ad2b91380
parent1697a6f0443ca8896081f98bd8648bc7bdb2cc58

LLVM: fix calling convention lowering involving pointers

The previous commit caused LLVM module verification failure because we attemped to bitcast LLVM pointers to i64 parameters. This is exactly what we want, however it's technically not allowed according to LLVM's type system. It could have been fixed trivially by using ptrtoint instead of bitcast in the case of pointers, however, out of concern for inttoptr being problematic for the optimizer, I put in special code to detect when a given parameter can be treated as its actual type rather than an integer type. This makes Zig's output LLVM IR closer to what Clang outputs.

1 files changed, 104 insertions(+), 11 deletions(-)

src/codegen/llvm.zig+104-11
......@@ -764,15 +764,25 @@ pub const Object = struct {
764764 arg_ptr.setAlignment(param_ty.abiAlignment(target));
765765
766766 var field_i: u32 = 0;
767 var field_offset: u32 = 0;
767768 for (llvm_ints) |int_bits| {
768769 const param = llvm_func.getParam(llvm_arg_i);
769770 llvm_arg_i += 1;
770771
771772 const big_int_ty = dg.context.intType(int_bits);
772 var bits_used: u16 = 0;
773 var bits_used: u32 = 0;
773774 while (bits_used < int_bits) {
774775 const field = fields[field_i];
775 const field_abi_bits = @intCast(u16, field.ty.abiSize(target)) * 8;
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;
776786 const field_int_ty = dg.context.intType(field_abi_bits);
777787 const shifted = if (bits_used == 0) param else s: {
778788 const shift_amt = big_int_ty.constInt(bits_used, .False);
......@@ -782,14 +792,15 @@ pub const Object = struct {
782792 var ty_buf: Type.Payload.Pointer = undefined;
783793 const llvm_i = llvmFieldIndex(param_ty, field_i, target, &ty_buf).?;
784794 const field_ptr = builder.buildStructGEP(arg_ptr, llvm_i, "");
785 const field_alignment = field.normalAlignment(target);
786795 const casted_ptr = builder.buildBitCast(field_ptr, field_int_ty.pointerType(0), "");
787796 const store_inst = builder.buildStore(field_as_int, casted_ptr);
788797 store_inst.setAlignment(field_alignment);
789798
790 bits_used += field_abi_bits;
791799 field_i += 1;
792800 if (field_i >= fields.len) break;
801
802 bits_used += field_abi_bits;
803 field_offset += field_size;
793804 }
794805 if (field_i >= fields.len) break;
795806 }
......@@ -2689,9 +2700,65 @@ pub const DeclGen = struct {
26892700 try llvm_params.append(dg.context.intType(abi_size * 8));
26902701 },
26912702 .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];
26922705 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));
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 },
26952762 }
26962763 },
26972764 };
......@@ -4070,22 +4137,46 @@ pub const FuncGen = struct {
40704137 .Struct => {
40714138 const fields = param_ty.structFields().values();
40724139 var field_i: u32 = 0;
4140 var field_offset: u32 = 0;
40734141 for (llvm_ints) |int_bits| {
40744142 const big_int_ty = self.dg.context.intType(int_bits);
40754143 var int_arg: *const llvm.Value = undefined;
4076 var bits_used: u16 = 0;
4144 var bits_used: u32 = 0;
40774145 while (bits_used < int_bits) {
40784146 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 }
40794155 var ty_buf: Type.Payload.Pointer = undefined;
40804156 const llvm_i = llvmFieldIndex(param_ty, field_i, target, &ty_buf).?;
4081 const field_abi_bits = @intCast(u16, field.ty.abiSize(target)) * 8;
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
40824174 const field_int_ty = self.dg.context.intType(field_abi_bits);
40834175 const llvm_field = if (is_by_ref) f: {
40844176 const field_ptr = self.builder.buildStructGEP(llvm_arg, llvm_i, "");
4085 const alignment = field.normalAlignment(target);
40864177 const casted_ptr = self.builder.buildBitCast(field_ptr, field_int_ty.pointerType(0), "");
40874178 const load_inst = self.builder.buildLoad(casted_ptr, "");
4088 load_inst.setAlignment(alignment);
4179 load_inst.setAlignment(field_alignment);
40894180 break :f load_inst;
40904181 } else f: {
40914182 const llvm_field = self.builder.buildExtractValue(llvm_arg, llvm_i, "");
......@@ -4101,9 +4192,11 @@ pub const FuncGen = struct {
41014192 int_arg = self.builder.buildOr(int_arg, shifted, "");
41024193 }
41034194
4104 bits_used += field_abi_bits;
41054195 field_i += 1;
41064196 if (field_i >= fields.len) break;
4197
4198 bits_used += field_abi_bits;
4199 field_offset += field_size;
41074200 }
41084201 llvm_args.appendAssumeCapacity(int_arg);
41094202 if (field_i >= fields.len) break;