authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-10-20 13:29:58+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-10-20 20:11:12+03:00
log646d927c792dbdd6db4a5bbee3cf5847283fe861
treef285b620485ff0b8e01035ce08b42fd66551a41b
parent07b6173cb8877ce22fe6cb754cfc17367989b11e

stage2: fix handling of aarch64 C ABI float array like structs

Closes #11702 Closes #13125

4 files changed, 142 insertions(+), 27 deletions(-)

src/arch/aarch64/abi.zig+73-17
......@@ -5,29 +5,21 @@ const Register = bits.Register;
55const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
66const Type = @import("../../type.zig").Type;
77
8pub const Class = enum { memory, integer, none, float_array };
8pub const Class = enum(u8) { memory, integer, none, float_array, _ };
99
10/// For `float_array` the second element will be the amount of floats.
1011pub fn classifyType(ty: Type, target: std.Target) [2]Class {
12 var maybe_float_bits: ?u16 = null;
13 const float_count = countFloats(ty, target, &maybe_float_bits);
14 if (float_count <= sret_float_count) return .{ .float_array, @intToEnum(Class, float_count) };
15 return classifyTypeInner(ty, target);
16}
17
18fn classifyTypeInner(ty: Type, target: std.Target) [2]Class {
1119 if (!ty.hasRuntimeBitsIgnoreComptime()) return .{ .none, .none };
1220 switch (ty.zigTypeTag()) {
1321 .Struct => {
1422 if (ty.containerLayout() == .Packed) return .{ .integer, .none };
15
16 if (ty.structFieldCount() <= 4) {
17 const fields = ty.structFields();
18 var float_size: ?u64 = null;
19 for (fields.values()) |field| {
20 if (field.ty.zigTypeTag() != .Float) break;
21 const field_size = field.ty.bitSize(target);
22 const prev_size = float_size orelse {
23 float_size = field_size;
24 continue;
25 };
26 if (field_size != prev_size) break;
27 } else {
28 return .{ .float_array, .none };
29 }
30 }
3123 const bit_size = ty.bitSize(target);
3224 if (bit_size > 128) return .{ .memory, .none };
3325 if (bit_size > 64) return .{ .integer, .integer };
......@@ -67,6 +59,70 @@ pub fn classifyType(ty: Type, target: std.Target) [2]Class {
6759 }
6860}
6961
62const sret_float_count = 4;
63fn countFloats(ty: Type, target: std.Target, maybe_float_bits: *?u16) u32 {
64 const invalid = std.math.maxInt(u32);
65 switch (ty.zigTypeTag()) {
66 .Union => {
67 const fields = ty.unionFields();
68 var max_count: u32 = 0;
69 for (fields.values()) |field| {
70 const field_count = countFloats(field.ty, target, maybe_float_bits);
71 if (field_count == invalid) return invalid;
72 if (field_count > max_count) max_count = field_count;
73 if (max_count > sret_float_count) return invalid;
74 }
75 return max_count;
76 },
77 .Struct => {
78 const fields_len = ty.structFieldCount();
79 var count: u32 = 0;
80 var i: u32 = 0;
81 while (i < fields_len) : (i += 1) {
82 const field_ty = ty.structFieldType(i);
83 const field_count = countFloats(field_ty, target, maybe_float_bits);
84 if (field_count == invalid) return invalid;
85 count += field_count;
86 if (count > sret_float_count) return invalid;
87 }
88 return count;
89 },
90 .Float => {
91 const float_bits = maybe_float_bits.* orelse {
92 maybe_float_bits.* = ty.floatBits(target);
93 return 1;
94 };
95 if (ty.floatBits(target) == float_bits) return 1;
96 return invalid;
97 },
98 .Void => return 0,
99 else => return invalid,
100 }
101}
102
103pub fn getFloatArrayType(ty: Type) ?Type {
104 switch (ty.zigTypeTag()) {
105 .Union => {
106 const fields = ty.unionFields();
107 for (fields.values()) |field| {
108 if (getFloatArrayType(field.ty)) |some| return some;
109 }
110 return null;
111 },
112 .Struct => {
113 const fields_len = ty.structFieldCount();
114 var i: u32 = 0;
115 while (i < fields_len) : (i += 1) {
116 const field_ty = ty.structFieldType(i);
117 if (getFloatArrayType(field_ty)) |some| return some;
118 }
119 return null;
120 },
121 .Float => return ty,
122 else => return null,
123 }
124}
125
70126const callee_preserved_regs_impl = if (builtin.os.tag.isDarwin()) struct {
71127 pub const callee_preserved_regs = [_]Register{
72128 .x20, .x21, .x22, .x23,
src/codegen/llvm.zig+9-10
......@@ -3125,10 +3125,10 @@ pub const DeclGen = struct {
31253125 .as_u16 => {
31263126 try llvm_params.append(dg.context.intType(16));
31273127 },
3128 .float_array => {
3128 .float_array => |count| {
31293129 const param_ty = fn_info.param_types[it.zig_index - 1];
3130 const float_ty = try dg.lowerType(param_ty.structFieldType(0));
3131 const field_count = @intCast(c_uint, param_ty.structFieldCount());
3130 const float_ty = try dg.lowerType(aarch64_c_abi.getFloatArrayType(param_ty).?);
3131 const field_count = @intCast(c_uint, count);
31323132 const arr_ty = float_ty.arrayType(field_count);
31333133 try llvm_params.append(arr_ty);
31343134 },
......@@ -4801,7 +4801,7 @@ pub const FuncGen = struct {
48014801 const casted = self.builder.buildBitCast(llvm_arg, self.dg.context.intType(16), "");
48024802 try llvm_args.append(casted);
48034803 },
4804 .float_array => {
4804 .float_array => |count| {
48054805 const arg = args[it.zig_index - 1];
48064806 const arg_ty = self.air.typeOf(arg);
48074807 var llvm_arg = try self.resolveInst(arg);
......@@ -4812,9 +4812,8 @@ pub const FuncGen = struct {
48124812 llvm_arg = store_inst;
48134813 }
48144814
4815 const float_ty = try self.dg.lowerType(arg_ty.structFieldType(0));
4816 const field_count = @intCast(u32, arg_ty.structFieldCount());
4817 const array_llvm_ty = float_ty.arrayType(field_count);
4815 const float_ty = try self.dg.lowerType(aarch64_c_abi.getFloatArrayType(arg_ty).?);
4816 const array_llvm_ty = float_ty.arrayType(count);
48184817
48194818 const casted = self.builder.buildBitCast(llvm_arg, array_llvm_ty.pointerType(0), "");
48204819 const alignment = arg_ty.abiAlignment(target);
......@@ -10214,7 +10213,7 @@ const ParamTypeIterator = struct {
1021410213 llvm_types_buffer: [8]u16,
1021510214 byval_attr: bool,
1021610215
10217 const Lowering = enum {
10216 const Lowering = union(enum) {
1021810217 no_bits,
1021910218 byval,
1022010219 byref,
......@@ -10223,7 +10222,7 @@ const ParamTypeIterator = struct {
1022310222 multiple_llvm_float,
1022410223 slice,
1022510224 as_u16,
10226 float_array,
10225 float_array: u8,
1022710226 };
1022810227
1022910228 pub fn next(it: *ParamTypeIterator) ?Lowering {
......@@ -10400,7 +10399,7 @@ const ParamTypeIterator = struct {
1040010399 return .byref;
1040110400 }
1040210401 if (classes[0] == .float_array) {
10403 return .float_array;
10402 return Lowering{ .float_array = @enumToInt(classes[1]) };
1040410403 }
1040510404 if (classes[1] == .none) {
1040610405 it.llvm_types_len = 1;
test/c_abi/cfuncs.c+27
......@@ -650,3 +650,30 @@ void c_struct_with_array(StructWithArray x) {
650650StructWithArray c_ret_struct_with_array() {
651651 return (StructWithArray) { 4, {}, 155 };
652652}
653
654typedef struct {
655 struct Point {
656 double x;
657 double y;
658 } origin;
659 struct Size {
660 double width;
661 double height;
662 } size;
663} FloatArrayStruct;
664
665void c_float_array_struct(FloatArrayStruct x) {
666 assert_or_panic(x.origin.x == 5);
667 assert_or_panic(x.origin.y == 6);
668 assert_or_panic(x.size.width == 7);
669 assert_or_panic(x.size.height == 8);
670}
671
672FloatArrayStruct c_ret_float_array_struct() {
673 FloatArrayStruct x;
674 x.origin.x = 1;
675 x.origin.y = 2;
676 x.size.width = 3;
677 x.size.height = 4;
678 return x;
679}
test/c_abi/main.zig+33
......@@ -700,3 +700,36 @@ test "Struct with array as padding." {
700700 try std.testing.expect(x.a == 4);
701701 try std.testing.expect(x.b == 155);
702702}
703
704const FloatArrayStruct = extern struct {
705 origin: extern struct {
706 x: f64,
707 y: f64,
708 },
709 size: extern struct {
710 width: f64,
711 height: f64,
712 },
713};
714
715extern fn c_float_array_struct(FloatArrayStruct) void;
716extern fn c_ret_float_array_struct() FloatArrayStruct;
717
718test "Float array like struct" {
719 c_float_array_struct(.{
720 .origin = .{
721 .x = 5,
722 .y = 6,
723 },
724 .size = .{
725 .width = 7,
726 .height = 8,
727 },
728 });
729
730 var x = c_ret_float_array_struct();
731 try std.testing.expect(x.origin.x == 1);
732 try std.testing.expect(x.origin.y == 2);
733 try std.testing.expect(x.size.width == 3);
734 try std.testing.expect(x.size.height == 4);
735}