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;...@@ -5,29 +5,21 @@ const Register = bits.Register;
5const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;5const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
6const Type = @import("../../type.zig").Type;6const 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.
10pub fn classifyType(ty: Type, target: std.Target) [2]Class {11pub 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 {
11 if (!ty.hasRuntimeBitsIgnoreComptime()) return .{ .none, .none };19 if (!ty.hasRuntimeBitsIgnoreComptime()) return .{ .none, .none };
12 switch (ty.zigTypeTag()) {20 switch (ty.zigTypeTag()) {
13 .Struct => {21 .Struct => {
14 if (ty.containerLayout() == .Packed) return .{ .integer, .none };22 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 }
31 const bit_size = ty.bitSize(target);23 const bit_size = ty.bitSize(target);
32 if (bit_size > 128) return .{ .memory, .none };24 if (bit_size > 128) return .{ .memory, .none };
33 if (bit_size > 64) return .{ .integer, .integer };25 if (bit_size > 64) return .{ .integer, .integer };
...@@ -67,6 +59,70 @@ pub fn classifyType(ty: Type, target: std.Target) [2]Class {...@@ -67,6 +59,70 @@ pub fn classifyType(ty: Type, target: std.Target) [2]Class {
67 }59 }
68}60}
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
70const callee_preserved_regs_impl = if (builtin.os.tag.isDarwin()) struct {126const callee_preserved_regs_impl = if (builtin.os.tag.isDarwin()) struct {
71 pub const callee_preserved_regs = [_]Register{127 pub const callee_preserved_regs = [_]Register{
72 .x20, .x21, .x22, .x23,128 .x20, .x21, .x22, .x23,
src/codegen/llvm.zig+9-10
...@@ -3125,10 +3125,10 @@ pub const DeclGen = struct {...@@ -3125,10 +3125,10 @@ pub const DeclGen = struct {
3125 .as_u16 => {3125 .as_u16 => {
3126 try llvm_params.append(dg.context.intType(16));3126 try llvm_params.append(dg.context.intType(16));
3127 },3127 },
3128 .float_array => {3128 .float_array => |count| {
3129 const param_ty = fn_info.param_types[it.zig_index - 1];3129 const param_ty = fn_info.param_types[it.zig_index - 1];
3130 const float_ty = try dg.lowerType(param_ty.structFieldType(0));3130 const float_ty = try dg.lowerType(aarch64_c_abi.getFloatArrayType(param_ty).?);
3131 const field_count = @intCast(c_uint, param_ty.structFieldCount());3131 const field_count = @intCast(c_uint, count);
3132 const arr_ty = float_ty.arrayType(field_count);3132 const arr_ty = float_ty.arrayType(field_count);
3133 try llvm_params.append(arr_ty);3133 try llvm_params.append(arr_ty);
3134 },3134 },
...@@ -4801,7 +4801,7 @@ pub const FuncGen = struct {...@@ -4801,7 +4801,7 @@ pub const FuncGen = struct {
4801 const casted = self.builder.buildBitCast(llvm_arg, self.dg.context.intType(16), "");4801 const casted = self.builder.buildBitCast(llvm_arg, self.dg.context.intType(16), "");
4802 try llvm_args.append(casted);4802 try llvm_args.append(casted);
4803 },4803 },
4804 .float_array => {4804 .float_array => |count| {
4805 const arg = args[it.zig_index - 1];4805 const arg = args[it.zig_index - 1];
4806 const arg_ty = self.air.typeOf(arg);4806 const arg_ty = self.air.typeOf(arg);
4807 var llvm_arg = try self.resolveInst(arg);4807 var llvm_arg = try self.resolveInst(arg);
...@@ -4812,9 +4812,8 @@ pub const FuncGen = struct {...@@ -4812,9 +4812,8 @@ pub const FuncGen = struct {
4812 llvm_arg = store_inst;4812 llvm_arg = store_inst;
4813 }4813 }
48144814
4815 const float_ty = try self.dg.lowerType(arg_ty.structFieldType(0));4815 const float_ty = try self.dg.lowerType(aarch64_c_abi.getFloatArrayType(arg_ty).?);
4816 const field_count = @intCast(u32, arg_ty.structFieldCount());4816 const array_llvm_ty = float_ty.arrayType(count);
4817 const array_llvm_ty = float_ty.arrayType(field_count);
48184817
4819 const casted = self.builder.buildBitCast(llvm_arg, array_llvm_ty.pointerType(0), "");4818 const casted = self.builder.buildBitCast(llvm_arg, array_llvm_ty.pointerType(0), "");
4820 const alignment = arg_ty.abiAlignment(target);4819 const alignment = arg_ty.abiAlignment(target);
...@@ -10214,7 +10213,7 @@ const ParamTypeIterator = struct {...@@ -10214,7 +10213,7 @@ const ParamTypeIterator = struct {
10214 llvm_types_buffer: [8]u16,10213 llvm_types_buffer: [8]u16,
10215 byval_attr: bool,10214 byval_attr: bool,
1021610215
10217 const Lowering = enum {10216 const Lowering = union(enum) {
10218 no_bits,10217 no_bits,
10219 byval,10218 byval,
10220 byref,10219 byref,
...@@ -10223,7 +10222,7 @@ const ParamTypeIterator = struct {...@@ -10223,7 +10222,7 @@ const ParamTypeIterator = struct {
10223 multiple_llvm_float,10222 multiple_llvm_float,
10224 slice,10223 slice,
10225 as_u16,10224 as_u16,
10226 float_array,10225 float_array: u8,
10227 };10226 };
1022810227
10229 pub fn next(it: *ParamTypeIterator) ?Lowering {10228 pub fn next(it: *ParamTypeIterator) ?Lowering {
...@@ -10400,7 +10399,7 @@ const ParamTypeIterator = struct {...@@ -10400,7 +10399,7 @@ const ParamTypeIterator = struct {
10400 return .byref;10399 return .byref;
10401 }10400 }
10402 if (classes[0] == .float_array) {10401 if (classes[0] == .float_array) {
10403 return .float_array;10402 return Lowering{ .float_array = @enumToInt(classes[1]) };
10404 }10403 }
10405 if (classes[1] == .none) {10404 if (classes[1] == .none) {
10406 it.llvm_types_len = 1;10405 it.llvm_types_len = 1;
test/c_abi/cfuncs.c+27
...@@ -650,3 +650,30 @@ void c_struct_with_array(StructWithArray x) {...@@ -650,3 +650,30 @@ void c_struct_with_array(StructWithArray x) {
650StructWithArray c_ret_struct_with_array() {650StructWithArray c_ret_struct_with_array() {
651 return (StructWithArray) { 4, {}, 155 };651 return (StructWithArray) { 4, {}, 155 };
652}652}
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." {...@@ -700,3 +700,36 @@ test "Struct with array as padding." {
700 try std.testing.expect(x.a == 4);700 try std.testing.expect(x.a == 4);
701 try std.testing.expect(x.b == 155);701 try std.testing.expect(x.b == 155);
702}702}
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}