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 bisect 9import codecs 10import math 11import os 12import sys 13import yaml 14 15sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) 16from primes import next_prime 17import xngen 18import xnncommon 19 20 21parser = argparse.ArgumentParser(description='XNNPACK generator') 22parser.add_argument("-s", "--spec", metavar="FILE", required=True, 23 help="Spec (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 common_name, target_name = name.split("__", 1) 31 common_parts = common_name.split("_") 32 param_spec = common_parts[-1] 33 assert param_spec.startswith("up") 34 cr, kr = map(int, param_spec[2:].split("x")) 35 arch, isa = xnncommon.parse_target_name(target_name) 36 return cr, kr, arch, isa 37 38 39DWCONV_TEST_CODE = """\ 40TEST(${TEST_NAME}, c_eq_${CBLOCK}) { 41 $if ISA_CHECK: 42 ${ISA_CHECK}; 43 DWConvMicrokernelTester() 44 .cr(${CR}) 45 .kr(${KR}) 46 .channels(${CBLOCK}) 47 .Test(${", ".join(TEST_ARGS)}); 48} 49 50$if IS_PIPELINED: 51 TEST(${TEST_NAME}, c_eq_${CBLOCK * 2}) { 52 $if ISA_CHECK: 53 ${ISA_CHECK}; 54 DWConvMicrokernelTester() 55 .cr(${CR}) 56 .kr(${KR}) 57 .channels(${CBLOCK * 2}) 58 .Test(${", ".join(TEST_ARGS)}); 59 } 60 61$if CBLOCK > 1: 62 TEST(${TEST_NAME}, c_div_${CBLOCK}) { 63 $if ISA_CHECK: 64 ${ISA_CHECK}; 65 for (uint32_t channels = ${ADJCBLOCK + CBLOCK}; channels < ${CR * 16}; channels += ${CR * 3}) { 66 DWConvMicrokernelTester() 67 .cr(${CR}) 68 .kr(${KR}) 69 .channels(channels) 70 .Test(${", ".join(TEST_ARGS)}); 71 } 72 } 73 74 TEST(${TEST_NAME}, c_div_${CBLOCK}_with_qmin) { 75 $if ISA_CHECK: 76 ${ISA_CHECK}; 77 for (uint32_t channels = ${ADJCBLOCK + CBLOCK}; channels < ${CR * 16}; channels += ${CR * 3}) { 78 DWConvMicrokernelTester() 79 .cr(${CR}) 80 .kr(${KR}) 81 .channels(channels) 82 .qmin(128) 83 .Test(${", ".join(TEST_ARGS)}); 84 } 85 } 86 87 TEST(${TEST_NAME}, c_div_${CBLOCK}_with_qmax) { 88 $if ISA_CHECK: 89 ${ISA_CHECK}; 90 for (uint32_t channels = ${ADJCBLOCK + CBLOCK}; channels < ${CR * 16}; channels += ${CR * 3}) { 91 DWConvMicrokernelTester() 92 .cr(${CR}) 93 .kr(${KR}) 94 .channels(channels) 95 .qmax(128) 96 .Test(${", ".join(TEST_ARGS)}); 97 } 98 } 99 100 TEST(${TEST_NAME}, c_lt_${ADJCBLOCK}) { 101 $if ISA_CHECK: 102 ${ISA_CHECK}; 103 for (uint32_t channels = 1; channels < ${ADJCBLOCK}; channels++) { 104 DWConvMicrokernelTester() 105 .cr(${CR}) 106 .kr(${KR}) 107 .channels(channels) 108 .Test(${", ".join(TEST_ARGS)}); 109 } 110 } 111 112TEST(${TEST_NAME}, c_gt_${ADJCBLOCK}) { 113 $if ISA_CHECK: 114 ${ISA_CHECK}; 115 for (uint32_t channels = ${ADJCBLOCK + 1}; channels < ${10 if CBLOCK == 1 else ADJCBLOCK + CBLOCK}; channels++) { 116 DWConvMicrokernelTester() 117 .cr(${CR}) 118 .kr(${KR}) 119 .channels(channels) 120 .Test(${", ".join(TEST_ARGS)}); 121 } 122} 123 124TEST(${TEST_NAME}, c_gt_${ADJCBLOCK}_with_qmin) { 125 $if ISA_CHECK: 126 ${ISA_CHECK}; 127 for (uint32_t channels = ${ADJCBLOCK + 1}; channels < ${10 if CBLOCK == 1 else ADJCBLOCK + CBLOCK}; channels++) { 128 DWConvMicrokernelTester() 129 .cr(${CR}) 130 .kr(${KR}) 131 .channels(channels) 132 .qmin(128) 133 .Test(${", ".join(TEST_ARGS)}); 134 } 135} 136 137TEST(${TEST_NAME}, c_gt_${ADJCBLOCK}_with_qmax) { 138 $if ISA_CHECK: 139 ${ISA_CHECK}; 140 for (uint32_t channels = ${ADJCBLOCK + 1}; channels < ${10 if CBLOCK == 1 else ADJCBLOCK + CBLOCK}; channels++) { 141 DWConvMicrokernelTester() 142 .cr(${CR}) 143 .kr(${KR}) 144 .channels(channels) 145 .qmax(128) 146 .Test(${", ".join(TEST_ARGS)}); 147 } 148} 149 150TEST(${TEST_NAME}, multipixel) { 151 $if ISA_CHECK: 152 ${ISA_CHECK}; 153 for (size_t channels = 1; channels <= ${CBLOCK * 5}; channels += ${max(1, CBLOCK - 1)}) { 154 DWConvMicrokernelTester() 155 .cr(${CR}) 156 .kr(${KR}) 157 .channels(channels) 158 .width(3) 159 .Test(${", ".join(TEST_ARGS)}); 160 } 161} 162 163TEST(${TEST_NAME}, multipixel_with_step) { 164 $if ISA_CHECK: 165 ${ISA_CHECK}; 166 for (size_t channels = 1; channels <= ${CBLOCK * 5}; channels += ${max(1, CBLOCK - 1)}) { 167 for (size_t step = 2; step <= ${KR}; step++) { 168 DWConvMicrokernelTester() 169 .cr(${CR}) 170 .kr(${KR}) 171 .channels(channels) 172 .width(3) 173 .step(step) 174 .Test(${", ".join(TEST_ARGS)}); 175 } 176 } 177} 178 179TEST(${TEST_NAME}, multipixel_with_output_stride) { 180 $if ISA_CHECK: 181 ${ISA_CHECK}; 182 for (size_t channels = 1; channels <= ${CBLOCK * 5}; channels += ${max(1, CBLOCK - 1)}) { 183 DWConvMicrokernelTester() 184 .cr(${CR}) 185 .kr(${KR}) 186 .channels(${CR}) 187 .width(5) 188 .output_stride(${next_prime(CR * 5 + 1)}) 189 .Test(${", ".join(TEST_ARGS)}); 190 } 191} 192 193TEST(${TEST_NAME}, multipixel_with_qmin) { 194 $if ISA_CHECK: 195 ${ISA_CHECK}; 196 for (size_t channels = 1; channels <= ${CBLOCK * 5}; channels += ${max(1, CBLOCK - 1)}) { 197 DWConvMicrokernelTester() 198 .cr(${CR}) 199 .kr(${KR}) 200 .channels(channels) 201 .width(3) 202 .qmin(128) 203 .Test(${", ".join(TEST_ARGS)}); 204 } 205} 206 207TEST(${TEST_NAME}, multipixel_with_qmax) { 208 $if ISA_CHECK: 209 ${ISA_CHECK}; 210 for (size_t channels = 1; channels <= ${CBLOCK * 5}; channels += ${max(1, CBLOCK - 1)}) { 211 DWConvMicrokernelTester() 212 .cr(${CR}) 213 .kr(${KR}) 214 .channels(channels) 215 .width(3) 216 .qmax(128) 217 .Test(${", ".join(TEST_ARGS)}); 218 } 219} 220 221$if DATATYPE == "q8": 222 TEST(${TEST_NAME}, input_zero_point_only) { 223 $if ISA_CHECK: 224 ${ISA_CHECK}; 225 for (size_t channels = 1; channels <= ${CBLOCK * 5}; channels += ${max(1, CBLOCK - 1)}) { 226 DWConvMicrokernelTester() 227 .cr(${CR}) 228 .kr(${KR}) 229 .channels(channels) 230 .width(3) 231 .input_zero_point(255) 232 .kernel_zero_point(0) 233 .Test(${", ".join(TEST_ARGS)}); 234 } 235 } 236 237 TEST(${TEST_NAME}, kernel_zero_point_only) { 238 $if ISA_CHECK: 239 ${ISA_CHECK}; 240 for (size_t channels = 1; channels <= ${CBLOCK * 5}; channels += ${max(1, CBLOCK - 1)}) { 241 DWConvMicrokernelTester() 242 .cr(${CR}) 243 .kr(${KR}) 244 .channels(channels) 245 .width(3) 246 .input_zero_point(0) 247 .kernel_zero_point(255) 248 .Test(${", ".join(TEST_ARGS)}); 249 } 250 } 251""" 252 253 254def generate_test_cases(ukernel, cr, kr, c_block, is_pipelined, isa): 255 """Generates all tests cases for a DWCONV micro-kernel. 256 257 Args: 258 ukernel: C name of the micro-kernel function. 259 cr: CR parameter of the DWCONV micro-kernel. 260 kr: KR parameter of the DWCONV micro-kernel. 261 k_block: Number of C values processed per one iteration of the main loop of 262 the micro-kernel. 263 is_pipelined: Indicates if the micro-kernel is implemented with software 264 pipelining. Additional test cases are generated for software 265 pipelined micro-kernels to separately test prologue + epiloque 266 of the pipelined loop and iteration of the pipelined loop. 267 isa: instruction set required to run the micro-kernel. Generated unit test 268 will skip execution if the host processor doesn't support this ISA. 269 270 Returns: 271 Code for the test case. 272 """ 273 _, test_name = ukernel.split("_", 1) 274 _, datatype, ukernel_type, _ = ukernel.split("_", 3) 275 test_args = [ukernel] 276 if not isa or isa == "psimd": 277 test_args.append("DWConvMicrokernelTester::Variant::Scalar") 278 return xngen.preprocess(DWCONV_TEST_CODE, { 279 "TEST_NAME": test_name.upper().replace("UKERNEL_", ""), 280 "TEST_ARGS": test_args, 281 "UKERNEL_TYPE": ukernel_type.upper(), 282 "DATATYPE": datatype, 283 "CR": cr, 284 "KR": kr, 285 "CBLOCK": c_block, 286 "ADJCBLOCK": 2 * c_block if is_pipelined else c_block, 287 "IS_PIPELINED": is_pipelined, 288 "ISA_CHECK": xnncommon.generate_isa_check_macro(isa), 289 "next_prime": next_prime, 290 "sqrt": math.sqrt, 291 }) 292 293 294def main(args): 295 options = parser.parse_args(args) 296 297 with codecs.open(options.spec, "r", encoding="utf-8") as spec_file: 298 spec_yaml = yaml.safe_load(spec_file) 299 if not isinstance(spec_yaml, list): 300 raise ValueError("expected a list of micro-kernels in the spec") 301 302 tests = """\ 303// Copyright (c) Facebook, Inc. and its affiliates. 304// All rights reserved. 305// 306// Copyright 2019 Google LLC 307// 308// This source code is licensed under the BSD-style license found in the 309// LICENSE file in the root directory of this source tree. 310// 311// Auto-generated file. Do not edit! 312// Specification: {specification} 313// Generator: {generator} 314 315 316#include <gtest/gtest.h> 317 318#include <xnnpack/common.h> 319#include <xnnpack/isa-checks.h> 320 321#include <xnnpack/dwconv.h> 322#include "dwconv-microkernel-tester.h" 323""".format(specification=options.spec, generator=sys.argv[0]) 324 325 for ukernel_spec in spec_yaml: 326 name = ukernel_spec["name"] 327 pipelined = bool(ukernel_spec.get("pipelined", False)) 328 assembly = bool(ukernel_spec.get("assembly", False)) 329 cr, kr, arch, isa = split_ukernel_name(name) 330 331 # specification can override architecture 332 arch = ukernel_spec.get("arch", arch) 333 334 test_case = generate_test_cases(name, cr, kr, cr, pipelined, isa) 335 tests += "\n\n" + xnncommon.postprocess_test_case(test_case, arch, isa, assembly) 336 337 with codecs.open(options.output, "w", encoding="utf-8") as output_file: 338 output_file.write(tests) 339 340 341if __name__ == "__main__": 342 main(sys.argv[1:]) 343