authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-07-07 18:23:07-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-07-07 18:24:34-07:00
log3a03872af76652515e467c1f33d918ead2c0a6b0
tree121d0c50ccd1342fccc13a9304880a92e3b7b7b5
parent8d6011361fb088e3e4d1ad649ac196bec101ee78

LLVM: more robust implementation of C ABI for multiple_llvm_ints

The previous code here was potentially more optimal for some cases, however, I never tested the perf, so it might not actually matter. This code handles more cases. We can go back and re-evaluate that other implementation if it seems worthwhile in the future.

1 files changed, 49 insertions(+), 193 deletions(-)

src/codegen/llvm.zig+49-193
......@@ -858,71 +858,34 @@ pub const Object = struct {
858858 try args.append(aggregate);
859859 },
860860 .multiple_llvm_ints => {
861 const param_ty = fn_info.param_types[it.zig_index - 1];
862861 const llvm_ints = it.llvm_types_buffer[0..it.llvm_types_len];
863 switch (param_ty.zigTypeTag()) {
864 .Struct => {
865 const fields = param_ty.structFields().values();
866 const param_llvm_ty = try dg.lowerType(param_ty);
867 const param_alignment = param_ty.abiAlignment(target);
868 const arg_ptr = buildAllocaInner(builder, llvm_func, false, param_llvm_ty);
869 arg_ptr.setAlignment(param_alignment);
870
871 var field_i: u32 = 0;
872 var field_offset: u32 = 0;
873 for (llvm_ints) |int_bits| {
874 const param = llvm_func.getParam(llvm_arg_i);
875 llvm_arg_i += 1;
876
877 const big_int_ty = dg.context.intType(int_bits);
878 var bits_used: u32 = 0;
879 while (bits_used < int_bits) {
880 const field = fields[field_i];
881 const field_alignment = field.normalAlignment(target);
882 const prev_offset = field_offset;
883 field_offset = std.mem.alignForwardGeneric(u32, field_offset, field_alignment);
884 if (field_offset > prev_offset) {
885 // Padding counts as bits used.
886 bits_used += (field_offset - prev_offset) * 8;
887 if (bits_used >= int_bits) break;
888 }
889 const field_size = @intCast(u16, field.ty.abiSize(target));
890 const field_abi_bits = field_size * 8;
891 const field_int_ty = dg.context.intType(field_abi_bits);
892 const shifted = if (bits_used == 0) param else s: {
893 const shift_amt = big_int_ty.constInt(bits_used, .False);
894 break :s builder.buildLShr(param, shift_amt, "");
895 };
896 const field_as_int = builder.buildTrunc(shifted, field_int_ty, "");
897 var ty_buf: Type.Payload.Pointer = undefined;
898 const llvm_i = llvmFieldIndex(param_ty, field_i, target, &ty_buf).?;
899 const field_ptr = builder.buildStructGEP(arg_ptr, llvm_i, "");
900 const casted_ptr = builder.buildBitCast(field_ptr, field_int_ty.pointerType(0), "");
901 const store_inst = builder.buildStore(field_as_int, casted_ptr);
902 store_inst.setAlignment(field_alignment);
903
904 field_i += 1;
905 if (field_i >= fields.len) break;
906
907 bits_used += field_abi_bits;
908 field_offset += field_size;
909 }
910 if (field_i >= fields.len) break;
911 }
912
913 const is_by_ref = isByRef(param_ty);
914 const loaded = if (is_by_ref) arg_ptr else l: {
915 const load_inst = builder.buildLoad(arg_ptr, "");
916 load_inst.setAlignment(param_alignment);
917 break :l load_inst;
918 };
919 try args.append(loaded);
920 },
921 .Union => {
922 @panic("TODO: LLVM backend: implement C calling convention on x86_64 with union parameter");
923 },
924 else => unreachable,
862 const param_ty = fn_info.param_types[it.zig_index - 1];
863 const param_llvm_ty = try dg.lowerType(param_ty);
864 const param_alignment = param_ty.abiAlignment(target);
865 const arg_ptr = buildAllocaInner(builder, llvm_func, false, param_llvm_ty);
866 arg_ptr.setAlignment(param_alignment);
867 var field_types_buf: [8]*const llvm.Type = undefined;
868 const field_types = field_types_buf[0..llvm_ints.len];
869 for (llvm_ints) |int_bits, i| {
870 field_types[i] = dg.context.intType(int_bits);
871 }
872 const ints_llvm_ty = dg.context.structType(field_types.ptr, @intCast(c_uint, field_types.len), .False);
873 const casted_ptr = builder.buildBitCast(arg_ptr, ints_llvm_ty.pointerType(0), "");
874 for (llvm_ints) |_, i_usize| {
875 const i = @intCast(c_uint, i_usize);
876 const param = llvm_func.getParam(i);
877 const field_ptr = builder.buildStructGEP(casted_ptr, i, "");
878 const store_inst = builder.buildStore(param, field_ptr);
879 store_inst.setAlignment(target.cpu.arch.ptrBitWidth() / 8);
925880 }
881
882 const is_by_ref = isByRef(param_ty);
883 const loaded = if (is_by_ref) arg_ptr else l: {
884 const load_inst = builder.buildLoad(arg_ptr, "");
885 load_inst.setAlignment(param_alignment);
886 break :l load_inst;
887 };
888 try args.append(loaded);
926889 },
927890 };
928891 }
......@@ -2821,65 +2784,11 @@ pub const DeclGen = struct {
28212784 llvm_params.appendAssumeCapacity(len_llvm_ty);
28222785 },
28232786 .multiple_llvm_ints => {
2824 const param_ty = fn_info.param_types[it.zig_index - 1];
28252787 const llvm_ints = it.llvm_types_buffer[0..it.llvm_types_len];
28262788 try llvm_params.ensureUnusedCapacity(it.llvm_types_len);
2827
2828 // The reason we have all this logic instead of simply appending
2829 // big_int_ty is for the special case of a pointer type;
2830 // we want to use a pointer type instead of inttoptr at the callsites,
2831 // which may prevent optimization.
2832 switch (param_ty.zigTypeTag()) {
2833 .Struct => {
2834 const fields = param_ty.structFields().values();
2835 var field_i: u32 = 0;
2836 var field_offset: u32 = 0;
2837 llvm_arg: for (llvm_ints) |int_bits| {
2838 const big_int_ty = dg.context.intType(int_bits);
2839 var bits_used: u32 = 0;
2840 while (bits_used < int_bits) {
2841 const field = fields[field_i];
2842 const field_alignment = field.normalAlignment(target);
2843 const prev_offset = field_offset;
2844 field_offset = std.mem.alignForwardGeneric(u32, field_offset, field_alignment);
2845 if (field_offset > prev_offset) {
2846 // Padding counts as bits used.
2847 bits_used += (field_offset - prev_offset) * 8;
2848 if (bits_used >= int_bits) break;
2849 }
2850 const field_size = @intCast(u16, field.ty.abiSize(target));
2851 const field_abi_bits = field_size * 8;
2852
2853 // Special case for when the entire LLVM integer represents
2854 // one field; in this case keep the type information
2855 // to avoid the potentially costly ptrtoint/bitcast.
2856 if (bits_used == 0 and field_abi_bits == int_bits) {
2857 const llvm_field_ty = try dg.lowerType(field.ty);
2858 llvm_params.appendAssumeCapacity(llvm_field_ty);
2859 field_i += 1;
2860 if (field_i >= fields.len) {
2861 break :llvm_arg;
2862 } else {
2863 continue :llvm_arg;
2864 }
2865 }
2866
2867 field_i += 1;
2868 if (field_i >= fields.len) break;
2869
2870 bits_used += field_abi_bits;
2871 field_offset += field_size;
2872 }
2873 llvm_params.appendAssumeCapacity(big_int_ty);
2874 if (field_i >= fields.len) break;
2875 }
2876 },
2877 else => {
2878 for (llvm_ints) |int_bits| {
2879 const big_int_ty = dg.context.intType(int_bits);
2880 llvm_params.appendAssumeCapacity(big_int_ty);
2881 }
2882 },
2789 for (llvm_ints) |int_bits| {
2790 const big_int_ty = dg.context.intType(int_bits);
2791 llvm_params.appendAssumeCapacity(big_int_ty);
28832792 }
28842793 },
28852794 };
......@@ -4299,80 +4208,27 @@ pub const FuncGen = struct {
42994208 const llvm_ints = it.llvm_types_buffer[0..it.llvm_types_len];
43004209 const llvm_arg = try self.resolveInst(arg);
43014210 const is_by_ref = isByRef(param_ty);
4302 try llvm_args.ensureUnusedCapacity(it.llvm_types_len);
4303 switch (param_ty.zigTypeTag()) {
4304 .Struct => {
4305 const fields = param_ty.structFields().values();
4306 var field_i: u32 = 0;
4307 var field_offset: u32 = 0;
4308 for (llvm_ints) |int_bits| {
4309 const big_int_ty = self.dg.context.intType(int_bits);
4310 var int_arg: *const llvm.Value = undefined;
4311 var bits_used: u32 = 0;
4312 while (bits_used < int_bits) {
4313 const field = fields[field_i];
4314 const field_alignment = field.normalAlignment(target);
4315 const prev_offset = field_offset;
4316 field_offset = std.mem.alignForwardGeneric(u32, field_offset, field_alignment);
4317 if (field_offset > prev_offset) {
4318 // Padding counts as bits used.
4319 bits_used += (field_offset - prev_offset) * 8;
4320 if (bits_used >= int_bits) break;
4321 }
4322 var ty_buf: Type.Payload.Pointer = undefined;
4323 const llvm_i = llvmFieldIndex(param_ty, field_i, target, &ty_buf).?;
4324 const field_size = @intCast(u16, field.ty.abiSize(target));
4325 const field_abi_bits = field_size * 8;
4326
4327 // Special case for when the entire LLVM integer represents
4328 // one field; in this case keep the type information
4329 // to avoid the potentially costly ptrtoint/bitcast.
4330 if (bits_used == 0 and field_abi_bits == int_bits) {
4331 int_arg = if (is_by_ref) f: {
4332 const field_ptr = self.builder.buildStructGEP(llvm_arg, llvm_i, "");
4333 const load_inst = self.builder.buildLoad(field_ptr, "");
4334 load_inst.setAlignment(field_alignment);
4335 break :f load_inst;
4336 } else self.builder.buildExtractValue(llvm_arg, llvm_i, "");
4337 field_i += 1;
4338 break;
4339 }
4340
4341 const field_int_ty = self.dg.context.intType(field_abi_bits);
4342 const llvm_field = if (is_by_ref) f: {
4343 const field_ptr = self.builder.buildStructGEP(llvm_arg, llvm_i, "");
4344 const casted_ptr = self.builder.buildBitCast(field_ptr, field_int_ty.pointerType(0), "");
4345 const load_inst = self.builder.buildLoad(casted_ptr, "");
4346 load_inst.setAlignment(field_alignment);
4347 break :f load_inst;
4348 } else f: {
4349 const llvm_field = self.builder.buildExtractValue(llvm_arg, llvm_i, "");
4350 break :f self.builder.buildBitCast(llvm_field, field_int_ty, "");
4351 };
4352
4353 const extended = self.builder.buildZExt(llvm_field, big_int_ty, "");
4354 if (bits_used == 0) {
4355 int_arg = extended;
4356 } else {
4357 const shift_amt = big_int_ty.constInt(bits_used, .False);
4358 const shifted = self.builder.buildShl(extended, shift_amt, "");
4359 int_arg = self.builder.buildOr(int_arg, shifted, "");
4360 }
4361
4362 field_i += 1;
4363 if (field_i >= fields.len) break;
4211 const arg_ptr = if (is_by_ref) llvm_arg else p: {
4212 const p = self.buildAlloca(llvm_arg.typeOf());
4213 const store_inst = self.builder.buildStore(llvm_arg, p);
4214 store_inst.setAlignment(param_ty.abiAlignment(target));
4215 break :p p;
4216 };
43644217
4365 bits_used += field_abi_bits;
4366 field_offset += field_size;
4367 }
4368 llvm_args.appendAssumeCapacity(int_arg);
4369 if (field_i >= fields.len) break;
4370 }
4371 },
4372 .Union => {
4373 return self.todo("airCall C calling convention on x86_64 with union argument ", .{});
4374 },
4375 else => unreachable,
4218 var field_types_buf: [8]*const llvm.Type = undefined;
4219 const field_types = field_types_buf[0..llvm_ints.len];
4220 for (llvm_ints) |int_bits, i| {
4221 field_types[i] = self.dg.context.intType(int_bits);
4222 }
4223 const ints_llvm_ty = self.dg.context.structType(field_types.ptr, @intCast(c_uint, field_types.len), .False);
4224 const casted_ptr = self.builder.buildBitCast(arg_ptr, ints_llvm_ty.pointerType(0), "");
4225 try llvm_args.ensureUnusedCapacity(it.llvm_types_len);
4226 for (llvm_ints) |_, i_usize| {
4227 const i = @intCast(c_uint, i_usize);
4228 const field_ptr = self.builder.buildStructGEP(casted_ptr, i, "");
4229 const load_inst = self.builder.buildLoad(field_ptr, "");
4230 load_inst.setAlignment(target.cpu.arch.ptrBitWidth() / 8);
4231 llvm_args.appendAssumeCapacity(load_inst);
43764232 }
43774233 },
43784234 };