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 {...@@ -764,15 +764,25 @@ pub const Object = struct {
764 arg_ptr.setAlignment(param_ty.abiAlignment(target));764 arg_ptr.setAlignment(param_ty.abiAlignment(target));
765765
766 var field_i: u32 = 0;766 var field_i: u32 = 0;
767 var field_offset: u32 = 0;
767 for (llvm_ints) |int_bits| {768 for (llvm_ints) |int_bits| {
768 const param = llvm_func.getParam(llvm_arg_i);769 const param = llvm_func.getParam(llvm_arg_i);
769 llvm_arg_i += 1;770 llvm_arg_i += 1;
770771
771 const big_int_ty = dg.context.intType(int_bits);772 const big_int_ty = dg.context.intType(int_bits);
772 var bits_used: u16 = 0;773 var bits_used: u32 = 0;
773 while (bits_used < int_bits) {774 while (bits_used < int_bits) {
774 const field = fields[field_i];775 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;
776 const field_int_ty = dg.context.intType(field_abi_bits);786 const field_int_ty = dg.context.intType(field_abi_bits);
777 const shifted = if (bits_used == 0) param else s: {787 const shifted = if (bits_used == 0) param else s: {
778 const shift_amt = big_int_ty.constInt(bits_used, .False);788 const shift_amt = big_int_ty.constInt(bits_used, .False);
...@@ -782,14 +792,15 @@ pub const Object = struct {...@@ -782,14 +792,15 @@ pub const Object = struct {
782 var ty_buf: Type.Payload.Pointer = undefined;792 var ty_buf: Type.Payload.Pointer = undefined;
783 const llvm_i = llvmFieldIndex(param_ty, field_i, target, &ty_buf).?;793 const llvm_i = llvmFieldIndex(param_ty, field_i, target, &ty_buf).?;
784 const field_ptr = builder.buildStructGEP(arg_ptr, llvm_i, "");794 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), "");795 const casted_ptr = builder.buildBitCast(field_ptr, field_int_ty.pointerType(0), "");
787 const store_inst = builder.buildStore(field_as_int, casted_ptr);796 const store_inst = builder.buildStore(field_as_int, casted_ptr);
788 store_inst.setAlignment(field_alignment);797 store_inst.setAlignment(field_alignment);
789798
790 bits_used += field_abi_bits;
791 field_i += 1;799 field_i += 1;
792 if (field_i >= fields.len) break;800 if (field_i >= fields.len) break;
801
802 bits_used += field_abi_bits;
803 field_offset += field_size;
793 }804 }
794 if (field_i >= fields.len) break;805 if (field_i >= fields.len) break;
795 }806 }
...@@ -2689,9 +2700,65 @@ pub const DeclGen = struct {...@@ -2689,9 +2700,65 @@ pub const DeclGen = struct {
2689 try llvm_params.append(dg.context.intType(abi_size * 8));2700 try llvm_params.append(dg.context.intType(abi_size * 8));
2690 },2701 },
2691 .multiple_llvm_ints => {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];
2692 try llvm_params.ensureUnusedCapacity(it.llvm_types_len);2705 try llvm_params.ensureUnusedCapacity(it.llvm_types_len);
2693 for (it.llvm_types_buffer[0..it.llvm_types_len]) |int_bits| {2706
2694 llvm_params.appendAssumeCapacity(dg.context.intType(int_bits));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 },
2695 }2762 }
2696 },2763 },
2697 };2764 };
...@@ -4070,22 +4137,46 @@ pub const FuncGen = struct {...@@ -4070,22 +4137,46 @@ pub const FuncGen = struct {
4070 .Struct => {4137 .Struct => {
4071 const fields = param_ty.structFields().values();4138 const fields = param_ty.structFields().values();
4072 var field_i: u32 = 0;4139 var field_i: u32 = 0;
4140 var field_offset: u32 = 0;
4073 for (llvm_ints) |int_bits| {4141 for (llvm_ints) |int_bits| {
4074 const big_int_ty = self.dg.context.intType(int_bits);4142 const big_int_ty = self.dg.context.intType(int_bits);
4075 var int_arg: *const llvm.Value = undefined;4143 var int_arg: *const llvm.Value = undefined;
4076 var bits_used: u16 = 0;4144 var bits_used: u32 = 0;
4077 while (bits_used < int_bits) {4145 while (bits_used < int_bits) {
4078 const field = fields[field_i];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 }
4079 var ty_buf: Type.Payload.Pointer = undefined;4155 var ty_buf: Type.Payload.Pointer = undefined;
4080 const llvm_i = llvmFieldIndex(param_ty, field_i, target, &ty_buf).?;4156 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
4082 const field_int_ty = self.dg.context.intType(field_abi_bits);4174 const field_int_ty = self.dg.context.intType(field_abi_bits);
4083 const llvm_field = if (is_by_ref) f: {4175 const llvm_field = if (is_by_ref) f: {
4084 const field_ptr = self.builder.buildStructGEP(llvm_arg, llvm_i, "");4176 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), "");4177 const casted_ptr = self.builder.buildBitCast(field_ptr, field_int_ty.pointerType(0), "");
4087 const load_inst = self.builder.buildLoad(casted_ptr, "");4178 const load_inst = self.builder.buildLoad(casted_ptr, "");
4088 load_inst.setAlignment(alignment);4179 load_inst.setAlignment(field_alignment);
4089 break :f load_inst;4180 break :f load_inst;
4090 } else f: {4181 } else f: {
4091 const llvm_field = self.builder.buildExtractValue(llvm_arg, llvm_i, "");4182 const llvm_field = self.builder.buildExtractValue(llvm_arg, llvm_i, "");
...@@ -4101,9 +4192,11 @@ pub const FuncGen = struct {...@@ -4101,9 +4192,11 @@ pub const FuncGen = struct {
4101 int_arg = self.builder.buildOr(int_arg, shifted, "");4192 int_arg = self.builder.buildOr(int_arg, shifted, "");
4102 }4193 }
41034194
4104 bits_used += field_abi_bits;
4105 field_i += 1;4195 field_i += 1;
4106 if (field_i >= fields.len) break;4196 if (field_i >= fields.len) break;
4197
4198 bits_used += field_abi_bits;
4199 field_offset += field_size;
4107 }4200 }
4108 llvm_args.appendAssumeCapacity(int_arg);4201 llvm_args.appendAssumeCapacity(int_arg);
4109 if (field_i >= fields.len) break;4202 if (field_i >= fields.len) break;