|
|
|
|
|
|
|
|
|
|
|
|
|
import argparse |
|
import os |
|
import re |
|
import sys |
|
import io |
|
|
|
|
|
TOOLS_DIR = os.path.dirname(os.path.abspath(__file__)) |
|
|
|
ISA_LIST = frozenset({ |
|
'armsimd32', |
|
'avx', |
|
'avx2', |
|
'avx512f', |
|
'avx512skx', |
|
'avx512vbmi', |
|
'f16c', |
|
'fma', |
|
'fma3', |
|
'fp16arith', |
|
'hexagon', |
|
'neon', |
|
'neonbf16', |
|
'neondot', |
|
'neonfma', |
|
'neonfp16', |
|
'neonfp16arith', |
|
'neonv8', |
|
'rvv', |
|
'scalar', |
|
'sse', |
|
'sse2', |
|
'sse41', |
|
'ssse3', |
|
'wasm', |
|
'wasmblendvps', |
|
'wasmrelaxedsimd', |
|
'wasmpshufb', |
|
'wasmsdot', |
|
'wasmsimd', |
|
'xop', |
|
}) |
|
|
|
ISA_MAP = { |
|
'wasmblendvps': 'wasmrelaxedsimd', |
|
'wasmpshufb': 'wasmrelaxedsimd', |
|
'wasmsdot': 'wasmrelaxedsimd', |
|
} |
|
|
|
ARCH_LIST = frozenset({ |
|
'aarch32', |
|
'aarch64', |
|
'wasm32', |
|
'wasmsimd32', |
|
'wasmrelaxedsimd32', |
|
}) |
|
|
|
parser = argparse.ArgumentParser( |
|
description='Utility for re-generating microkernel lists') |
|
|
|
|
|
import re |
|
|
|
NUMBERS_REGEX = re.compile('([0-9]+)') |
|
|
|
def human_sort_key(text): |
|
return [int(token) if token.isdigit() else token.lower() for token in NUMBERS_REGEX.split(text)] |
|
|
|
|
|
def overwrite_if_changed(filepath, mem_file): |
|
file_changed = True |
|
if os.path.exists(filepath): |
|
with open(filepath, 'r', encoding='utf-8') as f: |
|
mem_file.seek(0) |
|
file_changed = f.read() != mem_file.read() |
|
if file_changed: |
|
with open(filepath, 'w', encoding='utf-8') as f: |
|
mem_file.seek(0) |
|
f.write(mem_file.read()) |
|
|
|
def make_variable_name(prefix, key, suffix): |
|
return '_'.join(token for token in [prefix, key.upper(), suffix] if token) |
|
|
|
def write_grouped_microkernels_bzl(file, microkernels, prefix, suffix): |
|
for key in sorted(microkernels): |
|
variable_name = make_variable_name(prefix, key, suffix) |
|
file.write(f'\n{variable_name} = [\n') |
|
for microkernel in sorted(microkernels[key], key=human_sort_key): |
|
file.write(f' "{microkernel}",\n') |
|
file.write(']\n') |
|
|
|
def write_grouped_microkernels_cmake(file, microkernels, prefix, suffix): |
|
for key in sorted(microkernels): |
|
variable_name = make_variable_name(prefix, key, suffix) |
|
file.write(f'\nSET({variable_name}') |
|
for microkernel in sorted(microkernels[key], key=human_sort_key): |
|
file.write(f'\n {microkernel}') |
|
file.write(')\n') |
|
|
|
def main(args): |
|
options = parser.parse_args(args) |
|
root_dir = os.path.normpath(os.path.join(TOOLS_DIR, '..')) |
|
src_dir = os.path.join(root_dir, 'src') |
|
ignore_roots = { |
|
src_dir, |
|
os.path.join(src_dir, 'amalgam', 'gen'), |
|
os.path.join(src_dir, 'configs'), |
|
os.path.join(src_dir, 'enums'), |
|
os.path.join(src_dir, 'jit'), |
|
os.path.join(src_dir, 'operators'), |
|
os.path.join(src_dir, 'subgraph'), |
|
os.path.join(src_dir, 'tables'), |
|
os.path.join(src_dir, 'xnnpack'), |
|
} |
|
c_microkernels_per_isa = {isa: [] for isa in ISA_LIST if isa not in ISA_MAP} |
|
c_microkernels_per_isa['neon_aarch64'] = list() |
|
c_microkernels_per_isa['neonfma_aarch64'] = list() |
|
c_microkernels_per_isa['neonfp16arith_aarch64'] = list() |
|
c_microkernels_per_isa['neonbf16_aarch64'] = list() |
|
asm_microkernels_per_arch = {arch: [] for arch in ARCH_LIST} |
|
jit_microkernels_per_arch = {arch: [] for arch in ARCH_LIST} |
|
for root, dirs, files in os.walk(src_dir, topdown=False): |
|
if root in ignore_roots: |
|
continue |
|
for name in files: |
|
if name.endswith('.in'): |
|
continue |
|
basename, ext = os.path.splitext(name) |
|
if ext == '.sollya': |
|
continue |
|
filepath = os.path.join(os.path.relpath(root, root_dir), name) |
|
|
|
if ext == '.c': |
|
arch = None |
|
for component in basename.split('-'): |
|
if component in ARCH_LIST: |
|
arch = component |
|
elif component in ISA_LIST: |
|
isa = ISA_MAP.get(component, component) |
|
key = isa if arch is None else f'{isa}_{arch}' |
|
c_microkernels_per_isa[key].append(filepath) |
|
break |
|
else: |
|
print('Unknown ISA for C microkernel %s' % filepath) |
|
elif ext == '.S': |
|
for component in basename.split('-'): |
|
if component in ARCH_LIST: |
|
asm_microkernels_per_arch[component].append(filepath) |
|
break |
|
else: |
|
print('Unknown architecture for assembly microkernel %s' % filepath) |
|
elif ext == '.cc': |
|
for component in basename.split('-'): |
|
if component in ARCH_LIST: |
|
jit_microkernels_per_arch[component].append(filepath) |
|
break |
|
else: |
|
print('Unknown architecture for JIT microkernel %s' % filepath) |
|
|
|
with io.StringIO() as microkernels_bzl: |
|
microkernels_bzl.write('''\ |
|
""" |
|
Microkernel filenames lists. |
|
|
|
Auto-generated file. Do not edit! |
|
Generator: tools/update-microkernels.py |
|
""" |
|
''') |
|
write_grouped_microkernels_bzl(microkernels_bzl, c_microkernels_per_isa, "ALL", "MICROKERNEL_SRCS") |
|
write_grouped_microkernels_bzl(microkernels_bzl, asm_microkernels_per_arch, "", "ASM_MICROKERNEL_SRCS") |
|
write_grouped_microkernels_bzl(microkernels_bzl, jit_microkernels_per_arch, "", "JIT_MICROKERNEL_SRCS") |
|
overwrite_if_changed(os.path.join(root_dir, 'microkernels.bzl'), microkernels_bzl) |
|
|
|
with io.StringIO() as microkernels_cmake: |
|
microkernels_cmake.write("""\ |
|
# Copyright 2022 Google LLC |
|
# |
|
# This source code is licensed under the BSD-style license found in the |
|
# LICENSE file in the root directory of this source tree. |
|
# |
|
# Description: microkernel filename lists. |
|
# |
|
# Auto-generated file. Do not edit! |
|
# Generator: tools/update-microkernels.py |
|
|
|
""") |
|
write_grouped_microkernels_cmake(microkernels_cmake, c_microkernels_per_isa, "ALL", "MICROKERNEL_SRCS") |
|
write_grouped_microkernels_cmake(microkernels_cmake, asm_microkernels_per_arch, "", "ASM_MICROKERNEL_SRCS") |
|
write_grouped_microkernels_cmake(microkernels_cmake, jit_microkernels_per_arch, "", "JIT_MICROKERNEL_SRCS") |
|
overwrite_if_changed(os.path.join(root_dir, "cmake", 'microkernels.cmake'), microkernels_cmake) |
|
|
|
if __name__ == '__main__': |
|
main(sys.argv[1:]) |
|
|