Skip to content

Commit 674c689

Browse files
committed
analyze_code_size.py: provide option to further categorize specialized functions
The categorization can be viewed by using the `-group-specializations` command line argument. ``` % utils/analyze_code_size.py -arch arm64e -group-specializations -categorize exe ``` Add `-list-group-specializations` to see the individual functions listed under each group. Current groups: Swift.stdlib class Standard library specialization with one class type. Swift.stdlib class(dict) Standard library specialization of a dictionary type with a single class type. Swift.stdlib foundation Standard library specialization involving one foundation type (and possibly a standard library type) or two foundation types. Swift.stdlib foundation, class Standard library specialization where the the first type is from the Foundation library and the second is a class. Swift.stdlib other Standard library specialization with types other than the ones listed here. Swift.stdlib stdlib Standard library specialization involving standard library types only. Swift.stdlib stdlib, class Standard library specialization where the the first type is from the standard library and the second is a class.
1 parent c17966e commit 674c689

File tree

1 file changed

+204
-3
lines changed

1 file changed

+204
-3
lines changed

utils/analyze_code_size.py

Lines changed: 204 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88

99
useCSV = False
10+
groupSpecializations = False
11+
listGroupSpecializations = False
1012

1113

1214
def main(arguments):
@@ -19,6 +21,10 @@ def main(arguments):
1921
default=False)
2022
parser.add_argument('-list-category', type=str,
2123
help='list symbols in category')
24+
parser.add_argument('-group-specializations', action='store_true',
25+
help='group specializations')
26+
parser.add_argument('-list-group-specializations', action='store_true',
27+
help='list group specializations')
2228
parser.add_argument('-csv', dest='use_csv', action='store_true',
2329
help='print results as csv')
2430
parser.add_argument('-uncategorized', action='store_true',
@@ -34,6 +40,14 @@ def main(arguments):
3440
useCSV = True
3541
print("Using csv")
3642

43+
if args.group_specializations:
44+
global groupSpecializations
45+
groupSpecializations = True
46+
47+
if args.list_group_specializations:
48+
global listGroupSpecializations
49+
listGroupSpecializations = True
50+
3751
segments = parse_segments(args.bin, args.arch)
3852

3953
if args.build_categories:
@@ -82,14 +96,49 @@ def add(self, symbol):
8296
self.size += symbol.size
8397

8498

99+
class GenericSpecializationGroupKey(object):
100+
def __init__(self, module_name, type_name, specialization):
101+
self.module_name = module_name
102+
self.type_name = type_name
103+
self.specialization = specialization
104+
105+
def __hash__(self):
106+
return hash((self.module_name, self.type_name, self.specialization))
107+
108+
def __eq__(self, other):
109+
return (self.module_name == other.module_name
110+
and self.type_name == other.type_name
111+
and self.specialization == other.specialization)
112+
113+
114+
class GenericSpecialization(object):
115+
def __init__(self, module_name, type_name, specialization):
116+
self.module_name = module_name
117+
self.type_name = type_name
118+
self.specialization = specialization
119+
self.size = 0
120+
self.symbols = []
121+
122+
def add(self, symbol):
123+
self.symbols.append(symbol)
124+
self.size += symbol.size
125+
126+
def list_symbols(self):
127+
for symbol in self.symbols:
128+
print(" " + symbol.name + " " + str(symbol.size))
129+
130+
85131
class Categories(object):
86132
def __init__(self):
87133
self.category_matching = [
88134
['Objective-C function', re.compile(r'.*[+-]\[')],
89135
['C++', re.compile(r'_+swift')],
90-
['Generic specialization of stdlib',
91-
re.compile(r'.*generic specialization.* of Swift\.')],
92-
['Generic specialization',
136+
['Generic specialization of stdlib',
137+
re.compile(
138+
r'.*generic specialization.* of ' +
139+
r'(static )?(\(extension in Swift\):)?Swift\.'
140+
)],
141+
['Generic specialization',
93142
re.compile(r'.*generic specialization')],
94143
['Merged function', re.compile(r'merged ')],
95144
['Key path', re.compile(r'key path')],
@@ -178,6 +227,44 @@ def __init__(self):
178227
['Swift unknown', re.compile(r'^_\$s.*')],
179228
]
180229
self.categories = {}
230+
self.specializations = {}
231+
self.specialization_matcher = re.compile(
232+
r'.*generic specialization <(?P<spec_list>[^>]*)>.* of' +
233+
r' (static )?(\(extension in Swift\):)?(?P<module_name>[^.]*)\.' +
234+
r'(?:(?P<first_type>[^.^(^<]*)\.){0,1}' +
235+
r'(?:(?P<last_type>[^.^(^<]*)\.)*(?P<function_name>[^(^<]*)'
236+
)
237+
self.single_stdlib_specialized_type_matcher = re.compile(
238+
r'(Swift\.)?[^,^.]*$'
239+
)
240+
self.two_specialized_stdlib_types_matcher = re.compile(
241+
r'(Swift\.)?[^,^.]*, (Swift\.)?[^,^.]*$'
242+
)
243+
self.single_specialized_foundation_type_matcher = re.compile(
244+
r'(Foundation\.)?[^,^.]*$'
245+
)
246+
self.two_specialized_foundation_types_matcher = re.compile(
247+
r'(Swift\.)?[^,^.]*, (Foundation\.)?[^,^.]*$'
248+
)
249+
self.two_specialized_foundation_types_matcher2 = re.compile(
250+
r'(Foundation\.)?[^,^.]*, (Foundation\.)?[^,^.]*$'
251+
)
252+
self.two_specialized_foundation_types_matcher3 = re.compile(
253+
r'(Foundation\.)?[^,^.]*, (Swift\.)?[^,^.]*$'
254+
)
255+
self.array_type_matcher = re.compile(r'Array')
256+
self.dictionary = re.compile(r'Array')
257+
self.single_specialized_types_matcher = re.compile(
258+
r'(?P<module_name>[^,^.]*)\.(?P<type_name>[^,^.]*)$'
259+
)
260+
self.is_class_type_dict = {}
261+
self.stdlib_and_other_type_matcher = re.compile(
262+
r'(Swift\.)?[^,^.]*, (?P<module_name>[^,^.]*)\.(?P<type_name>[^,^.]*)$'
263+
)
264+
self.foundation_and_other_type_matcher = re.compile(
265+
r'(Foundation\.)?[^,^.]*, (?P<module_name>[^,^.]*)\.' +
266+
r'(?P<type_name>[^,^.]*)$'
267+
)
181268

182269
def categorize_by_name(self, symbol):
183270
for c in self.category_matching:
@@ -204,12 +291,121 @@ def add(self, symbol):
204291
category_name = self.categorize_by_name(symbol)
205292
if category_name:
206293
self.add_symbol(category_name, symbol)
294+
if (groupSpecializations and
295+
category_name == 'Generic specialization of stdlib'):
296+
self.add_specialization(symbol)
207297
return
208298
category_name = self.categorize_by_mangled_name(symbol)
209299
if category_name:
210300
self.add_symbol(category_name, symbol)
211301
else:
212302
self.add_symbol('Unknown', symbol)
303+
if (groupSpecializations and
304+
category_name == 'Generic specialization of stdlib'):
305+
self.add_specialization(symbol)
306+
307+
def is_class_type_(self, type_name, mangled_name):
308+
match_class_name = str(len(type_name)) + type_name + 'C'
309+
if match_class_name in mangled_name:
310+
return True
311+
return False
312+
313+
def is_class_type(self, type_name, mangled_name):
314+
existing_categorization = self.is_class_type_dict.get(type_name, 3)
315+
if existing_categorization == 3:
316+
is_class = self.is_class_type_(type_name, mangled_name)
317+
self.is_class_type_dict[type_name] = is_class
318+
return is_class
319+
else:
320+
return existing_categorization
321+
322+
def is_dictionary_like_type(self, type_name):
323+
if 'Dictionary' in type_name:
324+
return True
325+
if 'Set' in type_name:
326+
return True
327+
return False
328+
329+
def group_library_types(self, module, type_name, specialization, mangled_name):
330+
if module != 'Swift':
331+
return module, type_name, specialization
332+
if self.single_stdlib_specialized_type_matcher.match(specialization):
333+
return module, 'stdlib', 'stdlib'
334+
if self.two_specialized_stdlib_types_matcher.match(specialization):
335+
return module, 'stdlib', 'stdlib'
336+
if self.single_specialized_foundation_type_matcher.match(specialization):
337+
return module, 'stdlib', 'foundation'
338+
if self.two_specialized_foundation_types_matcher.match(specialization):
339+
return module, 'stdlib', 'foundation'
340+
if self.two_specialized_foundation_types_matcher2.match(specialization):
341+
return module, 'stdlib', 'foundation'
342+
if self.two_specialized_foundation_types_matcher3.match(specialization):
343+
return module, 'stdlib', 'foundation'
344+
single_spec = self.single_specialized_types_matcher.match(specialization)
345+
if single_spec:
346+
is_class = self.is_class_type(single_spec.group('type_name'), mangled_name)
347+
is_dict = type_name is not None and self.is_dictionary_like_type(type_name)
348+
if not is_dict and is_class:
349+
return module, 'stdlib', 'class'
350+
if is_dict and is_class:
351+
return module, 'stdlib', 'class(dict)'
352+
stdlib_other_spec = self.stdlib_and_other_type_matcher.match(specialization)
353+
if stdlib_other_spec:
354+
is_class = self.is_class_type(stdlib_other_spec.group('type_name'),
355+
mangled_name)
356+
if is_class:
357+
return module, 'stdlib', 'stdlib, class'
358+
foundation_other_spec = self.foundation_and_other_type_matcher.match(
359+
specialization)
360+
if foundation_other_spec:
361+
is_class = self.is_class_type(foundation_other_spec.group('type_name'),
362+
mangled_name)
363+
if is_class:
364+
return module, 'stdlib', 'foundation, class'
365+
return module, 'stdlib', 'other'
366+
367+
def add_specialization(self, symbol):
368+
specialization_match = self.specialization_matcher.match(symbol.name)
369+
if specialization_match:
370+
module = specialization_match.group('module_name')
371+
type_name = specialization_match.group('first_type')
372+
specialization = specialization_match.group('spec_list')
373+
module, type_name, specialization = self.group_library_types(
374+
module, type_name, specialization, symbol.mangled_name)
375+
key = GenericSpecializationGroupKey(module, type_name, specialization)
376+
existing_specialization = self.specializations.get(key)
377+
if existing_specialization:
378+
existing_specialization.add(symbol)
379+
else:
380+
new_specialization = GenericSpecialization(module, type_name,
381+
specialization)
382+
new_specialization.add(symbol)
383+
self.specializations[key] = new_specialization
384+
else:
385+
print(symbol.name)
386+
print('not matched')
387+
return
388+
389+
def print_specializations(self):
390+
values = self.specializations.values()
391+
sorted_specializations = []
392+
for v in values:
393+
sorted_specializations.append(v)
394+
395+
if not sorted_specializations:
396+
return None
397+
else:
398+
sorted_specializations.sort(key=lambda entry: entry.specialization)
399+
sorted_specializations.sort(key=lambda entry: entry.type_name)
400+
sorted_specializations.sort(key=lambda entry: entry.module_name)
401+
print("Specialization info")
402+
for spec in sorted_specializations:
403+
print("%20s.%s %20s %8d" % (spec.module_name, spec.type_name,
404+
spec.specialization, spec.size))
405+
if listGroupSpecializations:
406+
spec.list_symbols()
407+
print("")
408+
return None
213409

214410
def categorize(self, symbols):
215411
for sym in symbols:
@@ -361,6 +557,8 @@ def categorize(segments):
361557
categories.categorize(symbols)
362558
categories.print_summary(section.size)
363559
print('')
560+
if groupSpecializations:
561+
categories.print_specializations()
364562

365563

366564
def uncategorized(segments):
@@ -382,6 +580,9 @@ def list_category(segments, category):
382580
print('Section %22s: %8d' %
383581
(segment.name + ';' + section.name, section.size))
384582
categories.print_category(category)
583+
print('')
584+
if groupSpecializations:
585+
categories.print_specializations()
385586

386587

387588
if __name__ == '__main__':

0 commit comments

Comments
 (0)