-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
aarch64.cr
160 lines (142 loc) · 4.14 KB
/
aarch64.cr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
require "../abi"
# Based on
# https://github.com/rust-lang/rust/blob/master/src/librustc_trans/cabi_aarch64.rs
class LLVM::ABI::AArch64 < LLVM::ABI
def abi_info(atys : Array(Type), rty : Type, ret_def : Bool, context : Context)
ret_ty = compute_return_type(rty, ret_def, context)
arg_tys = atys.map { |aty| compute_arg_type(aty, context) }
FunctionType.new(arg_tys, ret_ty)
end
def align(type : Type)
align(type, 8)
end
def size(type : Type)
size(type, 8)
end
def homogeneous_aggregate?(type)
homog_agg = case type
when Type::Kind::Float
return {type, 1}
when Type::Kind::Double
return {type, 1}
when Type::Kind::Array
check_array(type)
when Type::Kind::Struct
check_struct(type)
else
# go on
end
# Ensure we have at most four uniquely addressable members
if homog_agg
if 0 < homog_agg[1] <= 4
return homog_agg
end
end
end
private def check_array(type)
len = type.array_size.to_u64
return if len == 0
element = type.element_type
# if our element is an HFA/HVA, so are we; multiply members by our len
if homog_agg = homogeneous_aggregate?(element)
base_type, members = homog_agg
{base_type, len * members}
end
end
private def check_struct(type)
elements = type.struct_element_types
return if elements.empty?
base_type = nil
members = 0
elements.each do |element|
opt_homog_agg = homogeneous_aggregate?(element)
# field isn't itself oan HFA, so we aren't either
return unless opt_homog_agg
field_type, field_members = opt_homog_agg
if !base_type
# first field - store its type and number of members
base_type = field_type
members = field_members
else
# 2nd or later field - give up if it's a different type; otherwise incr. members
return unless base_type == field_type
members += field_members
end
end
return unless base_type
if size(type) == size(base_type) * members
{base_type, members}
end
end
private def compute_return_type(rty, ret_def, context)
if !ret_def
ArgType.direct(context.void)
elsif register?(rty)
non_struct(rty, context)
elsif homog_agg = homogeneous_aggregate?(rty)
base_type, members = homog_agg
ArgType.direct(rty, base_type.array(members))
else
size = size(rty)
if size <= 16
cast = if size <= 1
context.int8
elsif size <= 2
context.int16
elsif size <= 4
context.int32
elsif size <= 8
context.int64
else
context.int64.array(((size + 7) // 8).to_u64)
end
ArgType.direct(rty, cast)
else
ArgType.indirect(rty, LLVM::Attribute::StructRet)
end
end
end
private def compute_arg_type(aty, context)
if register?(aty)
non_struct(aty, context)
elsif homog_agg = homogeneous_aggregate?(aty)
base_type, members = homog_agg
ArgType.direct(aty, base_type.array(members))
else
size = size(aty)
if size <= 16
cast = if size == 0
context.int64.array(0)
elsif size <= 1
context.int8
elsif size <= 2
context.int16
elsif size <= 4
context.int32
elsif size <= 8
context.int64
else
context.int64.array(((size + 7) // 8).to_u64)
end
ArgType.direct(aty, cast)
else
ArgType.indirect(aty, nil)
end
end
end
def register?(type)
case type.kind
when Type::Kind::Integer,
Type::Kind::Float,
Type::Kind::Double,
Type::Kind::Pointer
true
else
false
end
end
private def non_struct(type, context)
attr = type == context.int1 ? LLVM::Attribute::ZExt : nil
ArgType.direct(type, attr: attr)
end
end