1#!/usr/bin/python3 2# 3# Copyright (C) 2018 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17 18"""Generate intrinsics code.""" 19 20from collections import OrderedDict 21 22import asm_defs 23import json 24import os 25import re 26import sys 27 28# C-level intrinsic calling convention: 29# 1. All arguments are passed using the natural data types: 30# - int8_t passed as one byte argument (on the stack in IA32 mode, in GP register in x86-64 mode) 31# - int32_t passed as 4 bytes argument (on the stack in IA32 mode, in GP register in x86-64 mode) 32# - int64_t is passed as 8 byte argument (on the stack in IA32 mode, in GP register in x86-64 mode) 33# - float is passed as float (on the stack in IA32 mode, in XMM register in x86-64 mode) 34# - double is passed as double (on the stack in IA32 mode, in XMM register in x86-64 mode) 35# - vector formats are passed as pointers to 128bit data structure 36# 2. Return values. 37# - Values are returned as std::tuple. This means that on IA32 it's always returned on stack. 38 39INDENT = ' ' 40AUTOGEN = """\ 41// This file automatically generated by gen_intrinsics.py 42// DO NOT EDIT! 43""" 44 45 46class VecFormat(object): 47 48 def __init__(self, num_elements, element_size, is_unsigned, is_float, index, 49 c_type): 50 self.num_elements = num_elements 51 self.element_size = element_size 52 self.is_unsigned = is_unsigned 53 self.is_float = is_float 54 self.index = index 55 self.c_type = c_type 56 57 58# Vector format defined as: 59# vector_size, element_size, is_unsigned, is_float, index, ir_format, c_type 60# TODO(olonho): make flat numbering after removing legacy macro compat. 61_VECTOR_FORMATS = { 62 'U8x8': VecFormat(8, 1, True, False, 1, 'uint8_t'), 63 'U16x4': VecFormat(4, 2, True, False, 2, 'uint16_t'), 64 'U32x2': VecFormat(2, 4, True, False, 3, 'uint32_t'), 65 'U64x1': VecFormat(1, 8, True, False, 4, 'uint64_t'), 66 'U8x16': VecFormat(16, 1, True, False, 5, 'uint8_t'), 67 'U16x8': VecFormat(8, 2, True, False, 6, 'uint16_t'), 68 'U32x4': VecFormat(4, 4, True, False, 7, 'uint32_t'), 69 'U64x2': VecFormat(2, 8, True, False, 8, 'uint64_t'), 70 'I8x8': VecFormat(8, 1, False, False, 9, 'int8_t'), 71 'I16x4': VecFormat(4, 2, False, False, 10, 'int16_t'), 72 'I32x2': VecFormat(2, 4, False, False, 11, 'int32_t'), 73 'I64x1': VecFormat(1, 8, False, False, 12, 'int64_t'), 74 'I8x16': VecFormat(16, 1, False, False, 13, 'int8_t'), 75 'I16x8': VecFormat(8, 2, False, False, 14, 'int16_t'), 76 'I32x4': VecFormat(4, 4, False, False, 15, 'int32_t'), 77 'I64x2': VecFormat(2, 8, False, False, 16, 'int64_t'), 78 'U8x1': VecFormat(1, 1, True, False, 17, 'uint8_t'), 79 'I8x1': VecFormat(1, 1, False, False, 18, 'int8_t'), 80 'U16x1': VecFormat(1, 2, True, False, 19, 'uint16_t'), 81 'I16x1': VecFormat(1, 2, False, False, 20, 'int16_t'), 82 'U32x1': VecFormat(1, 4, True, False, 21, 'uint32_t'), 83 'I32x1': VecFormat(1, 4, False, False, 22, 'int32_t'), 84 # These vector formats can never intersect with above, so can reuse index. 85 'F32x1': VecFormat(1, 4, False, True, 1, 'Float32'), 86 'F32x2': VecFormat(2, 4, False, True, 2, 'Float32'), 87 'F32x4': VecFormat(4, 4, False, True, 3, 'Float32'), 88 'F64x1': VecFormat(1, 8, False, True, 4, 'Float64'), 89 'F64x2': VecFormat(2, 8, False, True, 5, 'Float64'), 90 # Those vector formats can never intersect with above, so can reuse index. 91 'U8x4': VecFormat(4, 1, True, False, 1, 'uint8_t'), 92 'U16x2': VecFormat(2, 2, True, False, 2, 'uint16_t'), 93 'I8x4': VecFormat(4, 1, False, False, 3, 'int8_t'), 94 'I16x2': VecFormat(2, 2, False, False, 4, 'int16_t'), 95} 96 97 98class VecSize(object): 99 100 def __init__(self, num_elements, index): 101 self.num_elements = num_elements 102 self.index = index 103 104 105_VECTOR_SIZES = {'X64': VecSize(64, 1), 'X128': VecSize(128, 2)} 106 107_ROUNDING_MODES = ['FE_TONEAREST', 'FE_DOWNWARD', 'FE_UPWARD', 'FE_TOWARDZERO', 'FE_TIESAWAY'] 108 109 110def _is_imm_type(arg_type): 111 return 'imm' in arg_type 112 113 114def _is_template_type(arg_type): 115 if not arg_type.startswith('Type'): 116 return False 117 assert isinstance(int(arg_type[4:]), int) 118 return True 119 120 121def _get_imm_c_type(arg_type): 122 return { 123 'imm8' : 'int8_t', 124 'uimm8' : 'uint8_t', 125 'uimm16' : 'uint16_t', 126 'uimm32' : 'uint32_t', 127 }[arg_type] 128 129 130def _get_c_type(arg_type): 131 if (arg_type in ('Float16', 'Float32', 'Float64', 'int8_t', 'uint8_t', 'int16_t', 132 'uint16_t', 'int32_t', 'uint32_t', 'int64_t', 'uint64_t', 133 'volatile uint8_t*', 'volatile uint32_t*') or 134 _is_template_type(arg_type)): 135 return arg_type 136 if arg_type in ('fp_flags', 'fp_control', 'int', 'flag', 'flags', 'vec32'): 137 return 'uint32_t' 138 if _is_imm_type(arg_type): 139 return _get_imm_c_type(arg_type) 140 if arg_type == 'vec': 141 return 'SIMD128Register' 142 if arg_type in _ROUNDING_MODES: 143 return 'int' 144 raise Exception('Type %s not supported' % (arg_type)) 145 146 147def _get_semantic_player_type(arg_type, type_map): 148 if type_map is not None and arg_type in type_map: 149 return type_map[arg_type] 150 if arg_type in ('Float16', 'Float32', 'Float64', 'vec'): 151 return 'SimdRegister' 152 if _is_imm_type(arg_type): 153 return _get_imm_c_type(arg_type) 154 return 'Register' 155 156 157def _gen_scalar_intr_decl(f, name, intr): 158 ins = intr.get('in') 159 outs = intr.get('out') 160 params = [_get_c_type(op) for op in ins] 161 if len(outs) > 0: 162 retval = 'std::tuple<' + ', '.join(_get_c_type(out) for out in outs) + '>' 163 else: 164 retval = 'void' 165 comment = intr.get('comment') 166 if comment: 167 print('// %s.' % (comment), file=f) 168 if intr.get('precise_nans', False): 169 print('template <bool precise_nan_operations_handling, ' 170 'enum PreferredIntrinsicsImplementation = kUseAssemblerImplementationIfPossible>', 171 file=f) 172 print('%s %s(%s);' % (retval, name, ', '.join(params)), file=f) 173 174 175def _gen_template_intr_decl(f, name, intr): 176 ins = intr.get('in') 177 outs = intr.get('out') 178 params = [_get_c_type(op) for op in ins] 179 if len(outs) > 0: 180 retval = 'std::tuple<' + ', '.join(_get_c_type(out) for out in outs) + '>' 181 else: 182 retval = 'void' 183 comment = intr.get('comment') 184 if comment: 185 print('// %s.' % (comment), file=f) 186 print('template <%s>' % _get_template_arguments( 187 intr.get('variants'), intr.get('precise_nans', False)), file=f) 188 print('%s %s(%s);' % (retval, name, ', '.join(params)), file=f) 189 190 191def _get_template_arguments( 192 variants, 193 precise_nans = False, 194 extra = ['enum PreferredIntrinsicsImplementation = kUseAssemblerImplementationIfPossible']): 195 template = None 196 for variant in variants: 197 counter = -1 198 def get_counter(): 199 nonlocal counter 200 counter += 1 201 return counter 202 new_template = ', '.join( 203 (["bool kPreciseNaNOperationsHandling"] if precise_nans else []) + 204 ['bool kBool%s' % get_counter() if param.strip() in ('true', 'false') else 205 'uint32_t kInt%s' % get_counter() if param.strip() in _ROUNDING_MODES else 206 'typename Type%d' % get_counter() if re.search('[_a-zA-Z]', param) else 207 'int kInt%s' % get_counter() 208 for param in variant.split(',')] + extra) 209 assert template is None or template == new_template 210 template = new_template 211 return template 212 213 214def _is_vector_class(intr): 215 return intr.get('class') in ('vector_4', 'vector_8', 'vector_16', 216 'vector_8/16', 'vector_8/16/single', 217 'vector_8/single', 'vector_16/single') 218 219 220def _is_simd128_conversion_required(t, type_map=None): 221 return (_get_semantic_player_type(t, type_map) == 'SimdRegister' and 222 _get_c_type(t) != 'SIMD128Register') 223 224 225def _get_semantics_player_hook_result(intr): 226 outs = intr['out'] 227 if len(outs) == 0: 228 return 'void' 229 elif len(outs) == 1: 230 # No tuple for single result. 231 return _get_semantic_player_type(outs[0], intr.get('sem-player-types')) 232 return 'std::tuple<' + ', '.join( 233 _get_semantic_player_type(out, intr.get('sem-player-types')) 234 for out in outs) + '>' 235 236 237def _get_semantics_player_hook_proto_components(name, intr): 238 ins = intr['in'] 239 240 args = [] 241 if _is_vector_class(intr): 242 if 'raw' in intr['variants']: 243 assert len(intr['variants']) == 1, "Unexpected length of variants" 244 args = ["uint8_t size"] 245 else: 246 args = ["uint8_t elem_size", "uint8_t elem_num"] 247 if (_is_signed(intr) and _is_unsigned(intr)): 248 args += ['bool is_signed'] 249 250 args += [ 251 '%s arg%d' % ( 252 _get_semantic_player_type(op, intr.get('sem-player-types')), num) 253 for num, op in enumerate(ins) 254 ] 255 256 result = _get_semantics_player_hook_result(intr) 257 258 return result, name, ', '.join(args) 259 260 261def _get_semantics_player_hook_proto(name, intr): 262 result, name, args = _get_semantics_player_hook_proto_components(name, intr) 263 if intr.get('class') == 'template': 264 return 'template<%s>\n%s %s(%s)' % ( 265 _get_template_arguments(intr.get('variants'), False, []), result, name, args) 266 return '%s %s(%s)' % (result, name, args) 267 268 269def _get_interpreter_hook_call_expr(name, intr, desc=None): 270 ins = intr['in'] 271 outs = intr['out'] 272 273 call_params = [] 274 for num, op in enumerate(ins): 275 arg = 'arg%d' % (num) 276 semantic_player_type = _get_semantic_player_type( 277 op, intr.get('sem-player-types')) 278 if semantic_player_type == 'FpRegister': 279 call_params.append('FPRegToFloat<%s>(%s)' % (op, arg)) 280 elif semantic_player_type == 'SimdRegister': 281 call_params.append(_get_cast_from_simd128(arg, op, ptr_bits=64)) 282 elif '*' in _get_c_type(op): 283 call_params.append('berberis::bit_cast<%s>(%s)' % (_get_c_type(op), arg)) 284 else: 285 call_params.append('GPRRegToInteger<%s>(%s)' % (_get_c_type(op), arg)) 286 287 call_expr = 'intrinsics::%s%s(%s)' % ( 288 name, _get_desc_specializations(intr, desc).replace( 289 'Float', 'intrinsics::Float'), ', '.join(call_params)) 290 291 if len(outs) == 1: 292 # Unwrap tuple for single result. 293 call_expr = 'std::get<0>(%s)' % call_expr 294 if 'sem-player-types' in intr: 295 out_type = _get_semantic_player_type(outs[0], intr.get('sem-player-types')) 296 if out_type == "FpRegister": 297 call_expr = 'FloatToFPReg(%s)' % call_expr 298 elif out_type != "SimdRegister": 299 assert out_type == "Register" 300 assert not _is_simd128_conversion_required( 301 outs[0], intr.get('sem-player-types')) 302 call_expr = 'IntegerToGPRReg(%s)' % call_expr 303 else: 304 # Currently this kind of mismatch can only happen for single result, so we 305 # can keep simple code here for now. 306 if _is_simd128_conversion_required(outs[0]): 307 out_type = _get_c_type(outs[0]) 308 if out_type in ('Float16', 'Float32', 'Float64'): 309 call_expr = 'FloatToFPReg(%s)' % call_expr 310 else: 311 raise Exception('Type %s is not supported' % (out_type)) 312 else: 313 if any(_is_simd128_conversion_required(out) for out in outs): 314 raise Exception( 315 'Unsupported SIMD128Register conversion with multiple results') 316 317 return call_expr 318 319 320def _get_interpreter_hook_return_stmt(name, intr, desc=None): 321 return 'return ' + _get_interpreter_hook_call_expr(name, intr, desc) + ';' 322 323def _get_unused(intr): 324 call_expr = 'UNUSED(%s);' % ', '.join('arg%d' % (num) for num, _ in enumerate(intr['in'])) 325 return call_expr 326 327def _get_placeholder_return_stmt(intr, f): 328 print(INDENT + _get_unused(intr), file=f) 329 outs = intr['out'] 330 if outs: 331 print(INDENT + 'return {};', file=f) 332 333def _get_semantics_player_hook_raw_vector_body(name, intr, get_return_stmt): 334 outs = intr['out'] 335 if (len(outs) == 0): 336 raise Exception('No result raw vector intrinsic is not supported') 337 reg_class = intr.get('class') 338 yield 'switch (size) {' 339 for fmt, desc in _VECTOR_SIZES.items(): 340 if _check_reg_class_size(reg_class, desc.num_elements / 8): 341 yield INDENT + 'case %s:' % desc.num_elements 342 yield 2 * INDENT + get_return_stmt(name, intr, desc) 343 yield INDENT + 'default:' 344 yield 2 * INDENT + 'LOG_ALWAYS_FATAL("Unsupported size");' 345 yield '}' 346 347 348def _is_signed(intr): 349 return any(v.startswith("signed") for v in intr['variants']) 350 351 352def _is_unsigned(intr): 353 return any(v.startswith("unsigned") for v in intr['variants']) 354 355 356def _get_vector_format_init_expr(intr): 357 variants = intr.get('variants') 358 359 if ('Float16' in variants or 'Float32' in variants or 'Float64' in variants): 360 return 'intrinsics::GetVectorFormatFP(elem_size, elem_num)' 361 362 assert _is_signed(intr) or _is_unsigned(intr), "Unexpected intrinsic class" 363 if _is_signed(intr) and _is_unsigned(intr): 364 signed_arg = ', is_signed' 365 else: 366 signed_arg = ', true' if _is_signed(intr) else ', false' 367 return 'intrinsics::GetVectorFormatInt(elem_size, elem_num%s)' % signed_arg 368 369 370def _get_semantics_player_hook_vector_body(name, intr, get_return_stmt): 371 outs = intr['out'] 372 if (len(outs) == 0): 373 raise Exception('No result vector intrinsic is not supported') 374 reg_class = intr.get('class') 375 yield 'auto format = %s;' % _get_vector_format_init_expr(intr) 376 yield 'switch (format) {' 377 for variant in intr.get('variants'): 378 for fmt, desc in _VECTOR_FORMATS.items(): 379 if (_check_reg_class_size(reg_class, 380 desc.element_size * desc.num_elements) and 381 _check_typed_variant(variant, desc)): 382 yield INDENT + 'case intrinsics::kVector%s:' % fmt 383 yield 2 * INDENT + get_return_stmt(name, intr, desc) 384 elif (reg_class in ('vector_8/single', 'vector_8/16/single', 'vector_16/single') and 385 desc.num_elements == 1 and 386 _check_typed_variant(variant, desc)): 387 assert desc.element_size <= 8, "Unexpected element size" 388 yield INDENT + 'case intrinsics::kVector%s:' % fmt 389 yield 2 * INDENT + get_return_stmt(name, intr, desc) 390 yield INDENT + 'default:' 391 yield 2 * INDENT + 'LOG_ALWAYS_FATAL("Unsupported format");' 392 yield '}' 393 394 395# Syntax sugar heavily used in tests. 396def _get_interpreter_hook_vector_body(name, intr): 397 return _get_semantics_player_hook_vector_body( 398 name, intr, _get_interpreter_hook_return_stmt) 399 400 401def _gen_interpreter_hook(f, name, intr, option): 402 print('%s const {' % (_get_semantics_player_hook_proto(name, intr)), file=f) 403 404 if _is_vector_class(intr): 405 if 'raw' in intr['variants']: 406 assert len(intr['variants']) == 1, "Unexpected length of variants" 407 lines = _get_semantics_player_hook_raw_vector_body( 408 name, 409 intr, 410 _get_interpreter_hook_return_stmt) 411 else: 412 lines = _get_interpreter_hook_vector_body(name, intr) 413 414 lines = [INDENT + l for l in lines] 415 print('\n'.join(lines), file=f) 416 else: 417 # TODO(b/363057506): Add float support and clean up the logic here. 418 arm64_allowlist = ['AmoAdd', 'AmoAnd', 'AmoMax', 'AmoMin', 'AmoOr', 'AmoSwap', 'AmoXor', 'Bclr', 419 'Bclri', 'Bext', 'Bexti', 'Binv', 'Binvi', 'Bset', 'Bseti', 'Div', 'Max', 420 'Min', 'Rem', 'Rev8', 'Rol', 'Ror', 'Sext', 'Sh1add', 'Sh1adduw', 'Sh2add', 421 'Sh2adduw', 'Sh3add', 'Sh3adduw', 'Zext', 'UnboxNan'] 422 if (option == 'arm64') and (name not in arm64_allowlist): 423 _get_placeholder_return_stmt(intr, f) 424 else: 425 print(INDENT + _get_interpreter_hook_return_stmt(name, intr), file=f) 426 427 print('}\n', file=f) 428 429 430def _get_translator_hook_call_expr(name, intr, desc = None): 431 desc_spec = _get_desc_specializations(intr, desc).replace( 432 'Float', 'intrinsics::Float') 433 args = [('arg%d' % n) for n, _ in enumerate(intr['in'])] 434 template_params = ['&intrinsics::' + name + desc_spec] 435 template_params += [_get_semantics_player_hook_result(intr)] 436 return 'CallIntrinsic<%s>(%s)' % (', '.join(template_params), ', '.join(args)) 437 438 439def _get_translator_hook_return_stmt(name, intr, desc=None): 440 return 'return ' + _get_translator_hook_call_expr(name, intr, desc) + ';' 441 442 443def _gen_translator_hook(f, name, intr): 444 print('%s {' % (_get_semantics_player_hook_proto(name, intr)), file=f) 445 446 if _is_vector_class(intr): 447 if 'raw' in intr['variants']: 448 assert len(intr['variants']) == 1, "Unexpected length of variants" 449 lines = _get_semantics_player_hook_raw_vector_body( 450 name, 451 intr, 452 _get_translator_hook_return_stmt) 453 else: 454 lines = _get_semantics_player_hook_vector_body( 455 name, 456 intr, 457 _get_translator_hook_return_stmt) 458 lines = [INDENT + l for l in lines] 459 print('\n'.join(lines), file=f) 460 else: 461 print(INDENT + _get_translator_hook_return_stmt(name, intr), file=f) 462 463 print('}\n', file=f) 464 465 466def _gen_mock_semantics_listener_hook(f, name, intr): 467 result, name, args = _get_semantics_player_hook_proto_components(name, intr) 468 if intr.get('class') == 'template': 469 print('template<%s>\n%s %s(%s) {\n return %s(%s);\n}' % ( 470 _get_template_arguments(intr.get('variants'), False, []), 471 result, 472 name, 473 args, 474 name, 475 ', '.join([ 476 'intrinsics::kEnumFromTemplateType<%s>' % arg if arg.startswith('Type') else arg 477 for arg in _get_template_spec_arguments(intr.get('variants'))] + 478 [('arg%d' % n) for n, _ in enumerate(intr['in'])])), file=f) 479 args = ', '.join([ 480 '%s %s' % ( 481 { 482 'kBoo': 'bool', 483 'kInt': 'int', 484 'Type': 'intrinsics::EnumFromTemplateType' 485 }[argument[0:4]], 486 argument) 487 for argument in _get_template_spec_arguments(intr.get('variants'))] + [args]) 488 print('MOCK_METHOD((%s), %s, (%s));' % (result, name, args), file=f) 489 490 491def _check_signed_variant(variant, desc): 492 if variant == 'signed': 493 return True 494 if variant == 'signed_32': 495 return desc.element_size == 4 496 if variant == 'signed_64': 497 return desc.element_size == 8 498 if variant == 'signed_16/32': 499 return desc.element_size in (2, 4) 500 if variant == 'signed_8/16/32': 501 return desc.element_size in (1, 2, 4) 502 if variant == 'signed_16/32/64': 503 return desc.element_size in (2, 4, 8) 504 if variant == 'signed_8/16/32/64': 505 return desc.element_size in (1, 2, 4, 8) 506 if variant == 'signed_32/64': 507 return desc.element_size in (4, 8) 508 return False 509 510 511def _check_unsigned_variant(variant, desc): 512 if variant == 'unsigned': 513 return True 514 if variant == 'unsigned_8': 515 return desc.element_size == 1 516 if variant == 'unsigned_16': 517 return desc.element_size == 2 518 if variant == 'unsigned_32': 519 return desc.element_size == 4 520 if variant == 'unsigned_64': 521 return desc.element_size == 8 522 if variant == 'unsigned_8/16': 523 return desc.element_size in (1, 2) 524 if variant == 'unsigned_8/16/32': 525 return desc.element_size in (1, 2, 4) 526 if variant == 'unsigned_16/32/64': 527 return desc.element_size in (2, 4, 8) 528 if variant == 'unsigned_8/16/32/64': 529 return desc.element_size in (1, 2, 4, 8) 530 if variant == 'unsigned_32/64': 531 return desc.element_size in (4, 8) 532 return False 533 534 535def _check_reg_class_size(reg_class, size): 536 # Small vectors are separate namespace. 537 if size == 4 and reg_class == 'vector_4': 538 return True 539 if size == 8 and reg_class in ('vector_8', 'vector_8/16', 'vector_8/16/single', 540 'vector_8/single'): 541 return True 542 if size == 16 and reg_class in ('vector_16', 'vector_8/16', 'vector_8/16/single', 543 'vector_16/single'): 544 return True 545 return False 546 547 548def _check_typed_variant(variant, desc): 549 if desc.is_unsigned and not desc.is_float: 550 return _check_unsigned_variant(variant, desc) 551 if not desc.is_unsigned and not desc.is_float: 552 return _check_signed_variant(variant, desc) 553 if desc.is_float: 554 if desc.element_size == 2: 555 return variant == 'Float16' 556 if desc.element_size == 4: 557 return variant == 'Float32' 558 if desc.element_size == 8: 559 return variant == 'Float64' 560 return False 561 562 563def _get_formats_with_descriptions(intr): 564 reg_class = intr.get('class') 565 for variant in intr.get('variants'): 566 found_fmt = False 567 for fmt, desc in _VECTOR_FORMATS.items(): 568 if (_check_reg_class_size(reg_class, 569 desc.element_size * desc.num_elements) and 570 _check_typed_variant(variant, desc) and 571 (reg_class != 'vector_4' or desc.element_size < 4)): 572 found_fmt = True 573 yield fmt, desc 574 575 if variant == 'raw': 576 for fmt, desc in _VECTOR_SIZES.items(): 577 if _check_reg_class_size(reg_class, desc.num_elements / 8): 578 found_fmt = True 579 yield fmt, desc 580 581 assert found_fmt, 'Couldn\'t expand %s' % reg_class 582 583 584def _get_result_type(outs): 585 result_type = 'void' 586 return_stmt = '' 587 if len(outs) >= 1: 588 result_type = ('std::tuple<' + 589 ', '.join(_get_c_type(out) for out in outs) + '>') 590 return_stmt = 'return ' 591 return result_type, return_stmt 592 593 594def _get_in_params(params): 595 for param_index, param in enumerate(params): 596 yield _get_c_type(param), 'in%d' % (param_index) 597 598 599def _get_out_params(params): 600 for param_index, param in enumerate(params): 601 yield _get_c_type(param), 'out%d' % (param_index) 602 603 604def _get_cast_from_simd128(var, target_type, ptr_bits): 605 if ('*' in target_type): 606 return 'berberis::bit_cast<%s>(%s.Get<uint%d_t>(0))' % (_get_c_type(target_type), var, 607 ptr_bits) 608 609 c_type = _get_c_type(target_type) 610 if c_type in ('Float16', 'Float32', 'Float64'): 611 return 'FPRegToFloat<intrinsics::%s>(%s)' % (c_type, var) 612 613 cast_map = { 614 'int8_t': '.Get<int8_t>(0)', 615 'uint8_t': '.Get<uint8_t>(0)', 616 'int16_t': '.Get<int16_t>(0)', 617 'uint16_t': '.Get<uint16_t>(0)', 618 'int32_t': '.Get<int32_t>(0)', 619 'uint32_t': '.Get<uint32_t>(0)', 620 'int64_t': '.Get<int64_t>(0)', 621 'uint64_t': '.Get<uint64_t>(0)', 622 'SIMD128Register': '' 623 } 624 return '%s%s' % (var, cast_map[c_type]) 625 626 627def _get_desc_specializations(intr, desc=None): 628 if intr.get('class') == 'template': 629 spec = _get_template_spec_arguments(intr.get('variants')) 630 elif hasattr(desc, 'c_type'): 631 spec = [desc.c_type, str(desc.num_elements)] 632 elif hasattr(desc, 'num_elements'): 633 spec = [str(desc.num_elements)] 634 else: 635 spec = [] 636 if intr.get('precise_nans', False): 637 spec = ['config::kPreciseNaNOperationsHandling'] + spec 638 if not len(spec): 639 return '' 640 return '<%s>' % ', '.join(spec) 641 642 643def _get_template_spec_arguments(variants): 644 spec = None 645 for variant in variants: 646 counter = -1 647 def get_counter(): 648 nonlocal counter 649 counter += 1 650 return counter 651 new_spec = [ 652 'kBool%s' % get_counter() if param.strip() in ('true', 'false') else 653 'kInt%s' % get_counter() if param.strip() in _ROUNDING_MODES else 654 'Type%d' % get_counter() if re.search('[_a-zA-Z]', param) else 655 'kInt%s' % get_counter() 656 for param in variant.split(',')] 657 assert spec is None or spec == new_spec 658 spec = new_spec 659 return spec 660 661 662def _intr_has_side_effects(intr, fmt=None): 663 ins = intr.get('in') 664 outs = intr.get('out') 665 # If we have 'has_side_effects' mark in JSON file then we use it "as is". 666 if 'has_side_effects' in intr: 667 return intr.get('has_side_effects') 668 # Otherwise we mark all floating-point related intrinsics as "volatile". 669 # TODO(b/68857496): move that information in HIR/LIR and stop doing that. 670 if 'Float16' in ins or 'Float32' in ins or 'Float64' in ins: 671 return True 672 if 'Float16' in outs or 'Float32' in outs or 'Float64' in outs: 673 return True 674 if fmt is not None and fmt.startswith('F'): 675 return True 676 return False 677 678 679def _gen_intrinsics_inl_h(f, intrs): 680 print(AUTOGEN, file=f) 681 for name, intr in intrs: 682 if intr.get('class') == 'scalar': 683 _gen_scalar_intr_decl(f, name, intr) 684 elif intr.get('class') == 'template': 685 _gen_template_intr_decl(f, name, intr) 686 687 688def _gen_semantic_player_types(intrs): 689 for name, intr in intrs: 690 if intr.get('class') == 'template': 691 map = None 692 for variant in intr.get('variants'): 693 counter = -1 694 def get_counter(): 695 nonlocal counter 696 counter += 1 697 return counter 698 new_map = { 699 'Float16': 'FpRegister', 700 'Float32': 'FpRegister', 701 'Float64': 'FpRegister', 702 } 703 for type in filter( 704 lambda param: param.strip() not in ('true', 'false') and 705 re.search('[_a-zA-Z]', param), 706 variant.split(',')): 707 new_map['Type%d' % get_counter()] = ( 708 'FpRegister' if type.strip() in ('Float16', 'Float32', 'Float64') else 709 _get_semantic_player_type(type, None)) 710 assert map is None or map == new_map 711 map = new_map 712 intr['sem-player-types'] = map 713 714 715def _gen_interpreter_intrinsics_hooks_impl_inl_h(f, intrs, option): 716 print(AUTOGEN, file=f) 717 for name, intr in intrs: 718 _gen_interpreter_hook(f, name, intr, option) 719 720 721def _gen_translator_intrinsics_hooks_impl_inl_h(f, intrs): 722 print(AUTOGEN, file=f) 723 for name, intr in intrs: 724 _gen_translator_hook(f, name, intr) 725 726 727def _gen_mock_semantics_listener_intrinsics_hooks_impl_inl_h(f, intrs): 728 print(AUTOGEN, file=f) 729 for name, intr in intrs: 730 _gen_mock_semantics_listener_hook(f, name, intr) 731 732 733def _get_reg_operand_info(arg, info_prefix=None): 734 need_tmp = arg['class'] in ('EAX', 'EDX', 'CL', 'ECX') 735 if info_prefix is None: 736 class_info = 'void' 737 else: 738 class_info = '%s::%s' % (info_prefix, arg['class']) 739 if arg['class'] == 'Imm8': 740 return 'ImmArg<%d, int8_t, %s>' % (arg['ir_arg'], class_info) 741 if info_prefix is None: 742 using_info = 'void' 743 else: 744 using_info = '%s::%s' % (info_prefix, { 745 'def': 'Def', 746 'def_early_clobber': 'DefEarlyClobber', 747 'use': 'Use', 748 'use_def': 'UseDef' 749 }[arg['usage']]) 750 if arg['usage'] == 'use': 751 if need_tmp: 752 return 'InTmpArg<%d, %s, %s>' % (arg['ir_arg'], class_info, using_info) 753 return 'InArg<%d, %s, %s>' % (arg['ir_arg'], class_info, using_info) 754 if arg['usage'] in ('def', 'def_early_clobber'): 755 assert 'ir_arg' not in arg 756 if 'ir_res' in arg: 757 if need_tmp: 758 return 'OutTmpArg<%d, %s, %s>' % (arg['ir_res'], class_info, using_info) 759 return 'OutArg<%d, %s, %s>' % (arg['ir_res'], class_info, using_info) 760 return 'TmpArg<%s, %s>' % (class_info, using_info) 761 if arg['usage'] == 'use_def': 762 if 'ir_res' in arg: 763 if need_tmp: 764 return 'InOutTmpArg<%s, %s, %s, %s>' % (arg['ir_arg'], arg['ir_res'], 765 class_info, using_info) 766 return 'InOutArg<%s, %s, %s, %s>' % (arg['ir_arg'], arg['ir_res'], 767 class_info, using_info) 768 return 'InTmpArg<%s, %s, %s>' % (arg['ir_arg'], class_info, using_info) 769 assert False, 'unknown operand usage %s' % (arg['usage']) 770 771 772def _gen_make_intrinsics(f, intrs, archs): 773 print("""%s 774template <typename MacroAssembler, 775 typename Callback, 776 typename... Args> 777void ProcessAllBindings([[maybe_unused]] Callback callback, 778 [[maybe_unused]] Args&&... args) {""" % AUTOGEN, 779 file=f) 780 for line in _gen_c_intrinsics_generator( 781 intrs, _is_interpreter_compatible_assembler, False): # False for gen_builder 782 print(line, file=f) 783 print('}', file=f) 784 785def _gen_opcode_generators_f(f, intrs): 786 for line in _gen_opcode_generators(intrs): 787 print(line, file=f) 788 789def _gen_opcode_generators(intrs): 790 opcode_generators = {} 791 for name, intr in intrs: 792 if 'asm' not in intr: 793 continue 794 if 'variants' in intr: 795 variants = _get_formats_with_descriptions(intr) 796 variants = sorted(variants, key=lambda variant: variant[1].index) 797 # Collect intr_asms for all variants of intrinsic. 798 # Note: not all variants are guaranteed to have an asm variant! 799 # If that happens the list of intr_asms for that variant will be empty. 800 variants = [[ 801 intr_asm for intr_asm in _gen_sorted_asms(intr) 802 if fmt in intr_asm['variants'] 803 ] for fmt, _ in variants] 804 # Print intrinsic generator 805 for intr_asms in variants: 806 if len(intr_asms) > 0: 807 for intr_asm in intr_asms: 808 if not _is_translator_compatible_assembler(intr_asm): 809 continue 810 for line in _gen_opcode_generator(intr_asm, opcode_generators): 811 yield line 812 else: 813 for intr_asm in _gen_sorted_asms(intr): 814 if not _is_translator_compatible_assembler(intr_asm): 815 continue 816 for line in _gen_opcode_generator(intr_asm, opcode_generators): 817 yield line 818 819def _gen_opcode_generator(asm, opcode_generators): 820 name = asm['name'] 821 num_mem_args = sum(1 for arg in asm['args'] if arg.get('class').startswith("Mem") and arg.get('usage') == 'def_early_clobber') 822 opcode = 'Undefined' if num_mem_args > 2 else (asm_defs.get_mem_macro_name(asm, '').replace("Mem", "MemBaseDisp")) if num_mem_args > 0 else name 823 824 if name not in opcode_generators: 825 opcode_generators[name] = True 826 yield """ 827// TODO(b/260725458): Pass lambda as template argument after C++20 becomes available. 828class GetOpcode%s { 829 public: 830 template <typename Opcode> 831 constexpr auto operator()() { 832 return Opcode::kMachineOp%s; 833 } 834};""" % (name, opcode) 835 836def _gen_process_bindings(f, intrs, archs): 837 print('%s' % AUTOGEN, file=f) 838 _gen_opcode_generators_f(f, intrs) 839 print(""" 840template <auto kFunc, 841 typename MacroAssembler, 842 typename Result, 843 typename Callback, 844 typename... Args> 845Result ProcessBindings(Callback callback, Result def_result, Args&&... args) {""", 846 file=f) 847 for line in _gen_c_intrinsics_generator( 848 intrs, _is_translator_compatible_assembler, True): # True for gen_builder 849 print(line, file=f) 850 print(""" } 851 return std::forward<Result>(def_result); 852}""", file=f) 853 854 855def _gen_c_intrinsics_generator(intrs, check_compatible_assembler, gen_builder): 856 string_labels = {} 857 mnemo_idx = [0] 858 for name, intr in intrs: 859 ins = intr.get('in') 860 outs = intr.get('out') 861 params = _get_in_params(ins) 862 formal_args = ', '.join('%s %s' % (type, param) for type, param in params) 863 result_type, _ = _get_result_type(outs) 864 if 'asm' not in intr: 865 continue 866 if 'variants' in intr: 867 variants = _get_formats_with_descriptions(intr) 868 # Sort by index, to keep order close to what _gen_intrs_enum produces. 869 variants = sorted(variants, key=lambda variant: variant[1].index) 870 # Collect intr_asms for all versions of intrinsic. 871 # Note: not all variants are guaranteed to have asm version! 872 # If that happens list of intr_asms for that variant would be empty. 873 variants = [(desc, [ 874 intr_asm for intr_asm in _gen_sorted_asms(intr) 875 if fmt in intr_asm['variants'] 876 ]) for fmt, desc in variants] 877 # Print intrinsic generator 878 for desc, intr_asms in variants: 879 if len(intr_asms) > 0: 880 if 'raw' in intr['variants']: 881 spec = '%d' % (desc.num_elements) 882 else: 883 spec = '%s, %d' % (desc.c_type, desc.num_elements) 884 for intr_asm in intr_asms: 885 for line in _gen_c_intrinsic('%s<%s>' % (name, spec), 886 intr, 887 intr_asm, 888 string_labels, 889 mnemo_idx, 890 check_compatible_assembler, 891 gen_builder): 892 yield line 893 else: 894 for intr_asm in _gen_sorted_asms(intr): 895 for line in _gen_c_intrinsic(name, 896 intr, 897 intr_asm, 898 string_labels, 899 mnemo_idx, 900 check_compatible_assembler, 901 gen_builder): 902 yield line 903 904 905def _gen_sorted_asms(intr): 906 return sorted(intr['asm'], 907 key = lambda intr: 908 intr.get('nan', '') + 909 _KNOWN_FEATURES_KEYS.get( 910 intr.get('feature', ''), intr.get('feature', '')), reverse = True) 911 912_KNOWN_FEATURES_KEYS = { 913 'LZCNT': '001', 914 'BMI': '002', 915 'BMI2': '003', 916 'SSE': '010', 917 'SSE2': '011', 918 'SSE3': '012', 919 'SSSE3': '013', 920 'SSE4a': '014', 921 'SSE4_1': '015', 922 'SSE4_2': '016', 923 'AVX': '017', 924 'AVX2': '018', 925 'FMA': '019', 926 'FMA4': '020', 927 'CustomCapability': '021' 928} 929 930 931def _gen_c_intrinsic(name, 932 intr, 933 asm, 934 string_labels, 935 mnemo_idx, 936 check_compatible_assembler, 937 gen_builder): 938 if not check_compatible_assembler(asm): 939 return 940 941 cpuid_restriction = 'intrinsics::bindings::NoCPUIDRestriction' 942 if 'feature' in asm: 943 if asm['feature'] == 'AuthenticAMD': 944 cpuid_restriction = 'intrinsics::bindings::IsAuthenticAMD' 945 else: 946 cpuid_restriction = 'intrinsics::bindings::Has%s' % asm['feature'] 947 948 nan_restriction = 'intrinsics::bindings::NoNansOperation' 949 if 'nan' in asm: 950 nan_restriction = 'intrinsics::bindings::%sNanOperationsHandling' % asm['nan'] 951 template_arg = 'true' if asm['nan'] == "Precise" else "false" 952 if '<' in name: 953 template_pos = name.index('<') 954 name = name[0:template_pos+1] + template_arg + ", " + name[template_pos+1:] 955 else: 956 name += '<' + template_arg + '>' 957 958 if name not in string_labels: 959 name_label = 'kName%d' % len(string_labels) 960 string_labels[name] = name_label 961 if check_compatible_assembler == _is_translator_compatible_assembler: 962 yield ' %s if constexpr (std::is_same_v<FunctionCompareTag<kFunc>,' % ( 963 '' if name_label == 'kName0' else ' } else' 964 ) 965 yield ' FunctionCompareTag<%s>>) {' % name 966 yield ' static constexpr const char %s[] = "%s";' % ( 967 name_label, name) 968 else: 969 name_label = string_labels[name] 970 971 mnemo = asm['mnemo'] 972 mnemo_label = 'kMnemo%d' % mnemo_idx[0] 973 mnemo_idx[0] += 1 974 yield ' static constexpr const char %s[] = "%s";' % ( 975 mnemo_label, mnemo) 976 977 restriction = [cpuid_restriction, nan_restriction] 978 979 if check_compatible_assembler == _is_translator_compatible_assembler: 980 yield ' if (auto result = callback(' 981 else: 982 yield ' callback(' 983 yield ' intrinsics::bindings::AsmCallInfo<' 984 yield ' %s>(),' % ( 985 ',\n '.join( 986 [name_label, 987 _get_asm_reference(asm), 988 mnemo_label, 989 _get_builder_reference(intr, asm) if gen_builder else 'void', 990 cpuid_restriction, 991 nan_restriction, 992 'true' if _intr_has_side_effects(intr) else 'false', 993 _get_c_type_tuple(intr['in']), 994 _get_c_type_tuple(intr['out'])] + 995 [_get_reg_operand_info(arg, 'intrinsics::bindings') 996 for arg in asm['args']])) 997 if check_compatible_assembler == _is_translator_compatible_assembler: 998 yield ' std::forward<Args>(args)...); result.has_value()) {' 999 yield ' return *std::move(result);' 1000 yield ' }' 1001 else: 1002 yield ' std::forward<Args>(args)...);' 1003 1004 1005def _get_c_type_tuple(arguments): 1006 return 'std::tuple<%s>' % ', '.join( 1007 _get_c_type(argument) for argument in arguments).replace( 1008 'Float', 'intrinsics::Float') 1009 1010 1011def _get_asm_type(asm, prefix=''): 1012 args = filter( 1013 lambda arg: not asm_defs.is_implicit_reg(arg['class']), asm['args']) 1014 return ', '.join(_get_asm_operand_type(arg, prefix) for arg in args) 1015 1016 1017def _get_asm_operand_type(arg, prefix=''): 1018 cls = arg.get('class') 1019 if asm_defs.is_x87reg(cls): 1020 return prefix + 'X87Register' 1021 if asm_defs.is_greg(cls): 1022 return prefix + 'Register' 1023 if asm_defs.is_xreg(cls): 1024 return prefix + 'XMMRegister' 1025 if asm_defs.is_mem_op(cls): 1026 return 'const ' + prefix + 'Operand&' 1027 if asm_defs.is_imm(cls): 1028 if cls == 'Imm2': 1029 return 'int8_t' 1030 return 'int' + cls[3:] + '_t' 1031 assert False 1032 1033 1034def _get_asm_reference(asm): 1035 # Because of misfeature of Itanium C++ ABI we couldn't just use MacroAssembler 1036 # to static cast these references if we want to use them as template argument: 1037 # https://ibob.bg/blog/2018/08/18/a-bug-in-the-cpp-standard/ 1038 1039 # Thankfully there are usually no need to use the same trick for MacroInstructions 1040 # since we may always rename these, except when immediates are involved. 1041 1042 # But for assembler we need to use actual type from where these 1043 # instructions come from! 1044 # 1045 # E.g. LZCNT have to be processed like this: 1046 # static_cast<void (Assembler_common_x86::*)( 1047 # typename Assembler_common_x86::Register, 1048 # typename Assembler_common_x86::Register)>( 1049 # &Assembler_common_x86::Lzcntl) 1050 assembler = 'std::tuple_element_t<%s, MacroAssembler>' % asm['macroassembler'] 1051 return 'static_cast<void (%s::*)(%s)>(%s&%s::%s%s)' % ( 1052 assembler, 1053 _get_asm_type(asm, 'typename %s::' % assembler), 1054 '\n ', 1055 assembler, 1056 'template ' if '<' in asm['asm'] else '', 1057 asm['asm']) 1058 1059def _get_builder_reference(intr, asm): 1060 return 'GetOpcode%s' % (asm['name']) 1061 1062def _load_intrs_def_files(intrs_def_files): 1063 result = {} 1064 for intrs_def in intrs_def_files: 1065 with open(intrs_def) as intrs: 1066 result.update(json.load(intrs)) 1067 result.pop('License', None) 1068 return result 1069 1070 1071def _load_intrs_arch_def(intrs_defs): 1072 json_data = [] 1073 for intrs_def in intrs_defs: 1074 with open(intrs_def) as intrs: 1075 json_array = json.load(intrs) 1076 while isinstance(len(json_array) > 0 and json_array[0], str): 1077 json_array.pop(0) 1078 json_data.extend(json_array) 1079 return json_data 1080 1081 1082def _load_macro_def(intrs, arch_intrs, insns_def, macroassembler): 1083 arch, insns = asm_defs.load_asm_defs(insns_def) 1084 for insn in insns: 1085 insn['macroassembler'] = macroassembler 1086 insns_map = dict((insn['name'], insn) for insn in insns) 1087 unprocessed_intrs = [] 1088 for arch_intr in arch_intrs: 1089 if arch_intr['insn'] in insns_map: 1090 insn = insns_map[arch_intr['insn']] 1091 _add_asm_insn(intrs, arch_intr, insn) 1092 else: 1093 unprocessed_intrs.append(arch_intr) 1094 return arch, unprocessed_intrs 1095 1096 1097def _is_interpreter_compatible_assembler(intr_asm): 1098 if intr_asm.get('usage', '') == 'inline-only': 1099 return False 1100 return True 1101 1102 1103def _is_translator_compatible_assembler(intr_asm): 1104 if intr_asm.get('usage', '') == 'no-inline': 1105 return False 1106 return True 1107 1108 1109 1110def _add_asm_insn(intrs, arch_intr, insn): 1111 name = ','.join(name_part.strip() for name_part in arch_intr['name'].split(',')) 1112 # Sanity checks: MacroInstruction could implement few different intrinsics but 1113 # number of arguments in arch intrinsic and arch-independent intrinsic 1114 # should match. 1115 # 1116 # Note: we allow combining intrinsics with variants and intrinsics without 1117 # variants (e.g. AbsF32 is combined with VectorAbsoluteFP for F32x2 and F32x4), 1118 # but don't allow macroinstructions which would handle different set of 1119 # variants for different intrinsics. 1120 1121 assert 'variants' not in insn or insn['variants'] == arch_intr['variants'] 1122 assert 'feature' not in insn or insn['feature'] == arch_intr['feature'] 1123 assert 'nan' not in insn or insn['nan'] == arch_intr['nan'] 1124 assert 'usage' not in insn or insn['usage'] == arch_intr['usage'] 1125 # Some intrinsics have extra inputs which can be ignored. e,g fpcr could be 1126 # ignored when not needed for precise emulation of NaNs. 1127 # Therefore we check that number inputs to (macro) instruction is less than 1128 # or equal to number of inputs to number of inputs to intrinsic. 1129 assert len(intrs[name]['in']) >= len(arch_intr['in']) 1130 assert len(intrs[name]['out']) == len(arch_intr['out']) 1131 1132 if 'variants' in arch_intr: 1133 insn['variants'] = arch_intr['variants'] 1134 if 'feature' in arch_intr: 1135 insn['feature'] = arch_intr['feature'] 1136 if 'nan' in arch_intr: 1137 insn['nan'] = arch_intr['nan'] 1138 if 'usage' in arch_intr: 1139 insn['usage'] = arch_intr['usage'] 1140 1141 for count, in_arg in enumerate(arch_intr['in']): 1142 # Sanity check: each in argument should only be used once - but if two 1143 # different intrinsics use them same macroinstruction it could be already 1144 # defined... yet it must be defined identically. 1145 assert ('ir_arg' not in insn['args'][in_arg] or 1146 insn['args'][in_arg]['ir_arg'] == count) 1147 insn['args'][in_arg]['ir_arg'] = count 1148 1149 for count, out_arg in enumerate(arch_intr['out']): 1150 # Sanity check: each out argument should only be used once, too. 1151 assert ('ir_res' not in insn['args'][out_arg] or 1152 insn['args'][out_arg]['ir_res'] == count) 1153 insn['args'][out_arg]['ir_res'] = count 1154 1155 # Note: one intrinsic could have more than one implementation (e.g. 1156 # SSE2 vs SSE4.2). 1157 if 'asm' not in intrs[name]: 1158 intrs[name]['asm'] = [] 1159 intrs[name]['asm'].append(insn) 1160 1161 1162def _open_asm_def_files(def_files, arch_def_files, asm_def_files, need_archs=True): 1163 intrs = _load_intrs_def_files(def_files) 1164 expanded_intrs = _expand_template_intrinsics(intrs) 1165 arch_intrs = _load_intrs_arch_def(arch_def_files) 1166 archs = [] 1167 macro_assemblers = 0 1168 for macro_def in asm_def_files: 1169 arch, arch_intrs = _load_macro_def(expanded_intrs, arch_intrs, macro_def, macro_assemblers) 1170 macro_assemblers += 1 1171 # Make sure that all intrinsics were found during processing of arch_intrs. 1172 assert arch_intrs == [] 1173 if need_archs: 1174 return archs, sorted(intrs.items()), sorted(expanded_intrs.items()) 1175 else: 1176 return sorted(intrs.items()) 1177 1178 1179def _expand_template_intrinsics(intrs): 1180 expanded_intrs = {} 1181 for name, intr in intrs.items(): 1182 if intr.get('class') != 'template': 1183 expanded_intrs[name] = intr 1184 else: 1185 for variant in intr.get('variants'): 1186 types = {} 1187 params = [param.strip() for param in variant.split(',')] 1188 for param in params: 1189 if param in ('true', 'false'): 1190 continue 1191 if re.search('[_a-zA-Z]', param): 1192 types['Type'+str(len(types))] = param 1193 new_intr = intr.copy() 1194 del new_intr['variants'] 1195 new_intr['in'] = [types.get(param, param) for param in new_intr.get('in')] 1196 new_intr['out'] = [types.get(param, param) for param in new_intr.get('out')] 1197 expanded_intrs[name+'<'+','.join(params)+'>'] = new_intr 1198 return expanded_intrs 1199 1200 1201def main(argv): 1202 # Usage: 1203 # gen_intrinsics.py --public_headers <intrinsics-inl.h> 1204 # <intrinsics_process_bindings-inl.h> 1205 # <interpreter_intrinsics_hooks-inl.h> 1206 # <translator_intrinsics_hooks-inl.h> 1207 # <mock_semantics_listener_intrinsics_hooks-inl.h> 1208 # <riscv64_to_x86_64/intrinsic_def.json", 1209 # ... 1210 # <riscv64_to_x86_64/machine_ir_intrinsic_binding.json>, 1211 # ... 1212 # <riscv64_to_x86_64/macro_def.json>, 1213 # ... 1214 # gen_intrinsics.py --text_asm_intrinsics_bindings <make_intrinsics-inl.h> 1215 # <riscv64_to_x86_64/intrinsic_def.json", 1216 # ... 1217 # <riscv64_to_x86_64/machine_ir_intrinsic_binding.json>, 1218 # ... 1219 # <riscv64_to_x86_64/macro_def.json>, 1220 # ... 1221 1222 def open_out_file(name): 1223 try: 1224 os.makedirs(os.path.dirname(name)) 1225 except: 1226 pass 1227 return open(name, 'w') 1228 1229 # Temporary special case for riscv64 to arm64. 1230 # TODO(b/362520361): generalize and combine with the below. 1231 option = argv[1] 1232 if option == 'arm64': 1233 mode = argv[2] 1234 out_files_end = 5 1235 def_files_end = out_files_end 1236 while argv[def_files_end].endswith('intrinsic_def.json'): 1237 def_files_end += 1 1238 if (def_files_end == len(argv)): 1239 break 1240 intrs = sorted(_load_intrs_def_files(argv[out_files_end:def_files_end]).items()) 1241 _gen_intrinsics_inl_h(open_out_file(argv[3]), intrs) 1242 _gen_semantic_player_types(intrs) 1243 _gen_interpreter_intrinsics_hooks_impl_inl_h(open_out_file(argv[4]), intrs, option) 1244 return 0 1245 1246 mode = argv[1] 1247 if mode in ('--text_asm_intrinsics_bindings', '--public_headers'): 1248 out_files_end = 3 if mode == '--text_asm_intrinsics_bindings' else 7 1249 def_files_end = out_files_end 1250 while argv[def_files_end].endswith('intrinsic_def.json'): 1251 def_files_end += 1 1252 arch_def_files_end = def_files_end 1253 while argv[arch_def_files_end].endswith('machine_ir_intrinsic_binding.json'): 1254 arch_def_files_end += 1 1255 archs, intrs, expanded_intrs = _open_asm_def_files( 1256 argv[out_files_end:def_files_end], 1257 argv[def_files_end:arch_def_files_end], 1258 argv[arch_def_files_end:], 1259 True) 1260 if mode == '--text_asm_intrinsics_bindings': 1261 _gen_make_intrinsics(open_out_file(argv[2]), expanded_intrs, archs) 1262 else: 1263 _gen_intrinsics_inl_h(open_out_file(argv[2]), intrs) 1264 _gen_process_bindings(open_out_file(argv[3]), expanded_intrs, archs) 1265 _gen_semantic_player_types(intrs) 1266 _gen_interpreter_intrinsics_hooks_impl_inl_h(open_out_file(argv[4]), intrs, '') 1267 _gen_translator_intrinsics_hooks_impl_inl_h( 1268 open_out_file(argv[5]), intrs) 1269 _gen_mock_semantics_listener_intrinsics_hooks_impl_inl_h( 1270 open_out_file(argv[6]), intrs) 1271 else: 1272 assert False, 'unknown option %s' % (mode) 1273 1274 return 0 1275 1276 1277if __name__ == '__main__': 1278 sys.exit(main(sys.argv)) 1279