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