xref: /aosp_15_r20/external/XNNPACK/tools/generate-ibilinear-test.py (revision 4bdc94577ba0e567308109d787f7fec7b531ce36)
1#!/usr/bin/env python
2# Copyright 2019 Google LLC
3#
4# This source code is licensed under the BSD-style license found in the
5# LICENSE file in the root directory of this source tree.
6
7import argparse
8import codecs
9import os
10import re
11import sys
12import yaml
13
14sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
15from primes import next_prime
16import xngen
17import xnncommon
18
19
20parser = argparse.ArgumentParser(
21  description='IBILINEAR microkernel test generator')
22parser.add_argument("-s", "--spec", metavar="FILE", required=True,
23                    help="Specification (YAML) file")
24parser.add_argument("-o", "--output", metavar="FILE", required=True,
25                    help='Output (C++ source) file')
26parser.set_defaults(defines=list())
27
28
29def split_ukernel_name(name):
30  match = re.fullmatch(r"xnn_(f16|f32|s8|u8)_ibilinear_ukernel__(.+)_c(\d+)", name)
31  assert match is not None
32  channel_tile = int(match.group(3))
33  pixel_tile = 1
34
35  arch, isa = xnncommon.parse_target_name(target_name=match.group(2))
36  return channel_tile, pixel_tile, arch, isa
37
38
39IBILINEAR_TEST_TEMPLATE = """\
40TEST(${TEST_NAME}, channels_eq_${CHANNEL_TILE}) {
41  $if ISA_CHECK:
42    ${ISA_CHECK};
43  IBilinearMicrokernelTester()
44    .pixels(${PIXEL_TILE})
45    .channels(${CHANNEL_TILE})
46    .Test(${TEST_FUNC});
47}
48
49$if CHANNEL_TILE > 1:
50  TEST(${TEST_NAME}, channels_div_${CHANNEL_TILE}) {
51    $if ISA_CHECK:
52      ${ISA_CHECK};
53    for (size_t channels = ${CHANNEL_TILE*2}; channels < ${CHANNEL_TILE*10}; channels += ${CHANNEL_TILE}) {
54      IBilinearMicrokernelTester()
55        .pixels(${PIXEL_TILE})
56        .channels(channels)
57        .Test(${TEST_FUNC});
58    }
59  }
60
61  TEST(${TEST_NAME}, channels_lt_${CHANNEL_TILE}) {
62    $if ISA_CHECK:
63      ${ISA_CHECK};
64    for (size_t channels = 1; channels < ${CHANNEL_TILE}; channels++) {
65      IBilinearMicrokernelTester()
66        .pixels(${PIXEL_TILE})
67        .channels(channels)
68        .Test(${TEST_FUNC});
69    }
70  }
71
72TEST(${TEST_NAME}, channels_gt_${CHANNEL_TILE}) {
73  $if ISA_CHECK:
74    ${ISA_CHECK};
75  for (size_t channels = ${CHANNEL_TILE+1}; channels < ${10 if CHANNEL_TILE == 1 else CHANNEL_TILE*2}; channels++) {
76    IBilinearMicrokernelTester()
77      .pixels(${PIXEL_TILE})
78      .channels(channels)
79      .Test(${TEST_FUNC});
80  }
81}
82
83$if PIXEL_TILE > 1:
84  TEST(${TEST_NAME}, pixels_div_${PIXEL_TILE}) {
85    $if ISA_CHECK:
86      ${ISA_CHECK};
87    for (size_t pixels = ${PIXEL_TILE*2}; pixels < ${PIXEL_TILE*10}; pixels += ${PIXEL_TILE}) {
88      for (size_t channels = 1; channels <= ${CHANNEL_TILE * 5}; channels += ${max(1, CHANNEL_TILE - 1)}) {
89        IBilinearMicrokernelTester()
90          .pixels(pixels)
91          .channels(channels)
92          .Test(${TEST_FUNC});
93      }
94    }
95  }
96
97  TEST(${TEST_NAME}, pixels_lt_${PIXEL_TILE}) {
98    $if ISA_CHECK:
99      ${ISA_CHECK};
100    for (size_t pixels = 1; pixels < ${PIXEL_TILE}; pixels++) {
101      for (size_t channels = 1; channels <= ${CHANNEL_TILE * 5}; channels += ${max(1, CHANNEL_TILE - 1)}) {
102        IBilinearMicrokernelTester()
103          .pixels(pixels)
104          .channels(channels)
105          .Test(${TEST_FUNC});
106      }
107    }
108  }
109
110TEST(${TEST_NAME}, pixels_gt_${PIXEL_TILE}) {
111  $if ISA_CHECK:
112    ${ISA_CHECK};
113  for (size_t pixels = ${PIXEL_TILE+1}; pixels < ${max(PIXEL_TILE*2, 3)}; pixels++) {
114    for (size_t channels = 1; channels <= ${CHANNEL_TILE * 5}; channels += ${max(1, CHANNEL_TILE - 1)}) {
115      IBilinearMicrokernelTester()
116        .pixels(pixels)
117        .channels(channels)
118        .Test(${TEST_FUNC});
119    }
120  }
121}
122
123TEST(${TEST_NAME}, input_offset) {
124  $if ISA_CHECK:
125    ${ISA_CHECK};
126  for (size_t pixels = 1; pixels < ${PIXEL_TILE * 5}; pixels += ${max(1, PIXEL_TILE - 1)}) {
127    for (size_t channels = 1; channels <= ${CHANNEL_TILE * 5}; channels += ${max(1, CHANNEL_TILE - 1)}) {
128      IBilinearMicrokernelTester()
129        .pixels(pixels)
130        .channels(channels)
131        .input_offset(${next_prime(CHANNEL_TILE * 5 + 1)})
132        .Test(${TEST_FUNC});
133    }
134  }
135}
136
137TEST(${TEST_NAME}, output_stride) {
138  $if ISA_CHECK:
139    ${ISA_CHECK};
140  for (size_t pixels = 1; pixels < ${PIXEL_TILE * 5}; pixels += ${max(1, PIXEL_TILE - 1)}) {
141    for (size_t channels = 1; channels <= ${CHANNEL_TILE * 5}; channels += ${max(1, CHANNEL_TILE - 1)}) {
142      IBilinearMicrokernelTester()
143        .pixels(pixels)
144        .channels(channels)
145        .output_stride(${next_prime(CHANNEL_TILE * 5 + 1)})
146        .Test(${TEST_FUNC});
147    }
148  }
149}
150"""
151
152
153def generate_test_cases(ukernel, channel_tile, pixel_tile, isa):
154  """Generates all tests cases for a BILINEAR micro-kernel.
155
156  Args:
157    ukernel: C name of the micro-kernel function.
158    channel_tile: Number of channels processed per one iteration of the inner
159                  loop of the micro-kernel.
160    pixel_tile: Number of pixels processed per one iteration of the outer loop
161                of the micro-kernel.
162    isa: instruction set required to run the micro-kernel. Generated unit test
163         will skip execution if the host processor doesn't support this ISA.
164
165  Returns:
166    Code for the test case.
167  """
168  _, test_name = ukernel.split("_", 1)
169  _, datatype, ukernel_type, _ = ukernel.split("_", 3)
170  test_args = [ukernel]
171  return xngen.preprocess(IBILINEAR_TEST_TEMPLATE, {
172      "TEST_NAME": test_name.upper().replace("UKERNEL_", ""),
173      "TEST_FUNC": ukernel,
174      "UKERNEL_TYPE": ukernel_type.upper(),
175      "DATATYPE": datatype,
176      "CHANNEL_TILE": channel_tile,
177      "PIXEL_TILE": pixel_tile,
178      "ISA_CHECK": xnncommon.generate_isa_check_macro(isa),
179      "next_prime": next_prime,
180    })
181
182
183def main(args):
184  options = parser.parse_args(args)
185
186  with codecs.open(options.spec, "r", encoding="utf-8") as spec_file:
187    spec_yaml = yaml.safe_load(spec_file)
188    if not isinstance(spec_yaml, list):
189      raise ValueError("expected a list of micro-kernels in the spec")
190
191    tests = """\
192// Copyright 2019 Google LLC
193//
194// This source code is licensed under the BSD-style license found in the
195// LICENSE file in the root directory of this source tree.
196//
197// Auto-generated file. Do not edit!
198//   Specification: {specification}
199//   Generator: {generator}
200
201
202#include <gtest/gtest.h>
203
204#include <xnnpack/common.h>
205#include <xnnpack/isa-checks.h>
206
207#include <xnnpack/ibilinear.h>
208#include "ibilinear-microkernel-tester.h"
209""".format(specification=options.spec, generator=sys.argv[0])
210
211    for ukernel_spec in spec_yaml:
212      name = ukernel_spec["name"]
213      channel_tile, pixel_tile, arch, isa = split_ukernel_name(name)
214
215      # specification can override architecture
216      arch = ukernel_spec.get("arch", arch)
217
218      test_case = generate_test_cases(name, channel_tile, pixel_tile, isa)
219      tests += "\n\n" + xnncommon.postprocess_test_case(test_case, arch, isa)
220
221    txt_changed = True
222    if os.path.exists(options.output):
223      with codecs.open(options.output, "r", encoding="utf-8") as output_file:
224        txt_changed = output_file.read() != tests
225
226    if txt_changed:
227      with codecs.open(options.output, "w", encoding="utf-8") as output_file:
228        output_file.write(tests)
229
230
231if __name__ == "__main__":
232  main(sys.argv[1:])
233