1#!/usr/bin/python
2#
3# Copyright (C) 2014 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 assembler files out of the definition file."""
19
20import asm_defs
21import os
22import re
23import sys
24
25
26INDENT = '  '
27
28ROUNDING_MODES = ['FE_TONEAREST', 'FE_DOWNWARD', 'FE_UPWARD', 'FE_TOWARDZERO', 'FE_TIESAWAY']
29
30_imm_types = {
31    # x86 immediates
32    'Imm2': 'int8_t',
33    'Imm8': 'int8_t',
34    'Imm16': 'int16_t',
35    'Imm32': 'int32_t',
36    'Imm64': 'int64_t',
37    # Official RISC-V immediates
38    'B-Imm': 'BImmediate',
39    'I-Imm': 'IImmediate',
40    'J-Imm': 'JImmediate',
41    'P-Imm': 'PImmediate',
42    'S-Imm': 'SImmediate',
43    'U-Imm': 'UImmediate',
44    # Extra RISC-V immediates
45    'Csr-Imm' : 'CsrImmediate',
46    'Shift32-Imm': 'Shift32Immediate',
47    'Shift64-Imm': 'Shift64Immediate'
48}
49
50
51def _get_arg_type_name(arg, insn_type):
52  cls = arg.get('class')
53  if asm_defs.is_x87reg(cls):
54    return 'X87Register'
55  if asm_defs.is_greg(cls):
56    return 'Register'
57  if asm_defs.is_freg(cls):
58    return 'FpRegister'
59  if asm_defs.is_xreg(cls):
60    return 'XMMRegister'
61  if asm_defs.is_yreg(cls):
62    return 'YMMRegister'
63  if asm_defs.is_imm(cls):
64    return _imm_types[cls]
65  if asm_defs.is_disp(cls):
66    return 'int32_t'
67  if asm_defs.is_label(cls):
68    return 'const Label&'
69  if asm_defs.is_cond(cls):
70    return 'Condition'
71  if asm_defs.is_csr(cls):
72    return 'Csr'
73  if asm_defs.is_rm(cls):
74    return 'Rounding'
75  if asm_defs.is_mem_op(cls):
76    if insn_type is not None and insn_type.endswith('-type'):
77      return 'const Operand<Register, %sImmediate>&' % insn_type[:-5]
78    return 'const Operand&'
79  raise Exception('class %s is not supported' % (cls))
80
81
82def _get_immediate_type(insn):
83  imm_type = None
84  for arg in insn.get('args'):
85    cls = arg.get('class')
86    if asm_defs.is_imm(cls):
87      assert imm_type is None
88      imm_type = _imm_types[cls]
89  return imm_type
90
91
92def _get_params(insn, filter=None):
93  result = []
94  arg_count = 0
95  for arg in insn.get('args'):
96    if asm_defs.is_implicit_reg(arg.get('class')):
97      continue
98    if filter is not None and filter(arg):
99      continue
100    result.append("%s arg%d" % (
101      _get_arg_type_name(arg, insn.get('type', None)), arg_count))
102    arg_count += 1
103  return ', '.join(result)
104
105
106def _contains_mem(insn):
107  return any(asm_defs.is_mem_op(arg['class']) for arg in insn.get('args'))
108
109
110def _get_template_name(insn):
111  name = insn.get('asm')
112  if '<' not in name:
113    return None, name
114  return 'template <%s>' % ', '.join(
115      'int' if param.strip() in ROUNDING_MODES else
116      'bool' if param.strip() in ('true', 'false') else
117      'typename' if re.search('[_a-zA-Z]', param) else 'int'
118      for param in name.split('<',1)[1][:-1].split(',')), name.split('<')[0]
119
120
121def _gen_generic_functions_h(f, insns, binary_assembler, arch):
122  template_names = set()
123  for insn in insns:
124    template, name = _get_template_name(insn)
125    params = _get_params(insn)
126    imm_type = _get_immediate_type(insn)
127    if template:
128      # We could only describe each template function once, or that would be
129      # compilation error.  Yet functions with the same name but different
130      # arguments are different (e.g. MacroVTbl<15> and MacroVTbl<23>).
131      # But different types of arguments could map to the same C++ type.
132      # For example MacroCmpFloat<Float32> and MacroCmpFloat<Float64> have
133      # different IR arguments (FpReg32 vs FpReg64), but both map to the
134      # same C++ type: XMMRegister.
135      #
136      # Use function name + parameters (as described by _get_params) to get
137      # full description of template function.
138      template_name = str({
139          'name': name,
140          'params': params
141      })
142      if template_name in template_names:
143        continue
144      template_names.add(template_name)
145      print(template, file=f)
146    # If this is binary assembler then we only generate header and then actual
147    # implementation is written manually.
148    #
149    # Text assembled passes "real" work down to GNU as, this works fine with
150    # just a simple generic implementation.
151    if binary_assembler:
152      if 'opcode' in insn:
153        assert '' not in insn
154        insn['opcodes'] = [insn['opcode']]
155      if 'opcodes' in insn:
156        opcodes = []
157        for opcode in insn['opcodes']:
158          if re.match('^[0-9a-fA-F]{2}$', opcode):
159            opcodes.append('uint8_t{0x%s}' % opcode)
160          elif re.match('^[0-9a-fA-F]{4}$', opcode):
161            opcodes.append('uint16_t{0x%s}' % opcode)
162          elif re.match('^[0-9a-fA-F]{8}$', opcode):
163            opcodes.append('uint32_t{0x%s}' % opcode)
164          elif re.match('^[0-9a-fA-F]{4}_[0-9a-fA-F]{4}$', opcode):
165            opcodes.append('uint32_t{0x%s}' % re.sub('_', '\'', opcode))
166          elif re.match('^[0-7]$', opcode):
167            opcodes.append('uint8_t{%s}' % opcode)
168          else:
169            assert False
170        insn['processed_opcodes'] = opcodes
171        print('void %s(%s) {' % (name, params), file=f)
172        if 'x86' in arch:
173          _gen_emit_shortcut(f, insn, insns)
174        _gen_emit_instruction(f, insn, arch)
175        print('}', file=f)
176        # If we have a memory operand (there may be at most one) then we also
177        # have a special x86-64 exclusive form which accepts Label (it can be
178        # emulated on x86-32, too, if needed).
179        if 'const Operand&' in params and 'x86' in arch:
180          print("", file=f)
181          print('void %s(%s) {' % (
182              name, params.replace('const Operand&', 'const LabelOperand')), file=f)
183          _gen_emit_shortcut(f, insn, insns)
184          _gen_emit_instruction(f, insn, arch, rip_operand=True)
185          print('}\n', file=f)
186        if 'Rounding' in params:
187          print("", file=f)
188          print('void %s(%s) {' % (
189              name, _get_params(insn, lambda arg: arg.get('class', '') == 'Rm')), file=f)
190          _gen_emit_instruction(f, insn, arch, dyn_rm=True)
191          print('}\n', file=f)
192      else:
193        print('void %s(%s);' % (name, params), file=f)
194      # If immediate type is integer then we want to prevent automatic
195      # conversions from integers of larger sizes.
196      if imm_type is not None and "int" in imm_type:
197        if template:
198          print(template[:-1] + ", typename ImmType>", file=f)
199        else:
200          print('template<typename ImmType>', file=f)
201        print(('auto %s(%s) -> '
202                    'std::enable_if_t<std::is_integral_v<ImmType> && '
203                    'sizeof(%s) < sizeof(ImmType)> = delete;') % (
204                        name, params.replace(imm_type, 'ImmType'), imm_type), file=f)
205    else:
206      print('void %s(%s) {' % (name, params), file=f);
207      if 'feature' in insn:
208        print('  SetRequiredFeature%s();' % insn['feature'], file=f)
209      print('  Instruction(%s);' % ', '.join(
210          ['"%s"' % insn.get('native-asm', name)] +
211          list(_gen_instruction_args(insn, arch))), file=f)
212      print('}', file=f)
213
214
215def _gen_instruction_args(insn, arch):
216  arg_count = 0
217  for arg in insn.get('args'):
218    if asm_defs.is_implicit_reg(arg.get('class')):
219      continue
220    if (_get_arg_type_name(arg, insn.get('type', None)) == 'Register'
221        and 'x86' in arch):
222      yield 'typename DerivedAssemblerType::%s(arg%d)' % (
223          _ARGUMENT_FORMATS_TO_SIZES[arg['class']], arg_count)
224    else:
225      yield 'arg%d' % arg_count
226    arg_count += 1
227
228
229def _gen_emit_shortcut(f, insn, insns):
230  # If we have one 'Imm8' argument then it could be shift, try too see if
231  # ShiftByOne with the same arguments exist.
232  if asm_defs.exactly_one_of(arg['class'] == 'Imm8' for arg in insn['args']):
233    _gen_emit_shortcut_shift(f, insn, insns)
234  if asm_defs.exactly_one_of(arg['class'] in ('Imm16', 'Imm32') for arg in insn['args']):
235    if insn['asm'].endswith('Accumulator'):
236      _gen_emit_shortcut_accumulator_imm8(f, insn, insns)
237    else:
238      _gen_emit_shortcut_generic_imm8(f, insn, insns)
239  if len(insn['args']) > 1 and insn['args'][0]['class'].startswith('GeneralReg'):
240    _gen_emit_shortcut_accumulator(f, insn, insns)
241
242
243def _gen_emit_shortcut_shift(f, insn, insns):
244  # Replace Imm8 argument with '1' argument.
245  non_imm_args = [arg for arg in insn['args'] if arg['class'] != 'Imm8']
246  imm_arg_index = insn['args'].index({'class': 'Imm8'})
247  for maybe_shift_by_1_insn in insns:
248    if not _is_insn_match(maybe_shift_by_1_insn,
249                          insn['asm'] + 'ByOne',
250                          non_imm_args):
251      continue
252    # Now call that version if immediate is 1.
253    args = []
254    arg_count = 0
255    for arg in non_imm_args:
256      if asm_defs.is_implicit_reg(arg['class']):
257        continue
258      args.append('arg%d' % arg_count)
259      arg_count += 1
260    print('  if (arg%d == 1) return %sByOne(%s);' % (
261        imm_arg_index, insn['asm'], ', '.join(args)), file=f)
262
263
264def _gen_emit_shortcut_accumulator_imm8(f, insn, insns):
265  insn_name = insn['asm'][:-11]
266  args = insn['args']
267  assert len(args) == 3 and args[2]['class'] == 'FLAGS'
268  acc_class = args[0]['class']
269  # Note: AL is accumulator, too, but but imm is always 8-bit for it which means
270  # it shouldn't be encountered here and if it *does* appear here - it's an error
271  # and we should fail.
272  assert acc_class in ('AX', 'EAX', 'RAX')
273  greg_class = {
274      'AX': 'GeneralReg16',
275      'EAX': 'GeneralReg32',
276      'RAX': 'GeneralReg64'
277  }[acc_class]
278  maybe_8bit_imm_args = [
279    { 'class': greg_class, 'usage': args[0]['usage'] },
280    { 'class': 'Imm8' },
281    { 'class': 'FLAGS', 'usage': insn['args'][2]['usage'] }
282  ]
283  for maybe_imm8_insn in insns:
284    if not _is_insn_match(maybe_imm8_insn,
285                          insn_name + 'Imm8',
286                          maybe_8bit_imm_args):
287      continue
288    print('  if (IsInRange<int8_t>(arg0)) {', file=f)
289    print(('    return %s(DerivedAssemblerType::Accumulator(), '
290                 'static_cast<int8_t>(arg0));') % (
291                     maybe_imm8_insn['asm'],), file=f)
292    print('  }', file=f)
293
294def _gen_emit_shortcut_generic_imm8(f, insn, insns):
295  maybe_8bit_imm_args = [{ 'class': 'Imm8' } if arg['class'].startswith('Imm') else arg
296                         for arg in insn['args']]
297  imm_arg_index = maybe_8bit_imm_args.index({'class': 'Imm8'})
298  for maybe_imm8_insn in insns:
299    if not _is_insn_match(maybe_imm8_insn,
300                          insn['asm'] + 'Imm8',
301                          maybe_8bit_imm_args):
302      continue
303    # Now call that version if immediate fits into 8-bit.
304    arg_count = len(_get_params(insn).split(','))
305    print('  if (IsInRange<int8_t>(arg%d)) {' % (arg_count - 1), file=f)
306    print('   return %s(%s);' % (maybe_imm8_insn['asm'], ', '.join(
307        ('static_cast<int8_t>(arg%d)' if n == arg_count - 1 else 'arg%d') % n
308        for n in range(arg_count))), file=f)
309    print('  }', file=f)
310
311
312def _gen_emit_shortcut_accumulator(f, insn, insns):
313  accumulator_name = {
314      'GeneralReg8': 'AL',
315      'GeneralReg16': 'AX',
316      'GeneralReg32': 'EAX',
317      'GeneralReg64': 'RAX'
318  }[insn['args'][0]['class']]
319  maybe_accumulator_args = [
320      { 'class': accumulator_name, 'usage': insn['args'][0]['usage']}
321  ] + insn['args'][1:]
322  for maybe_accumulator_insn in insns:
323    if not _is_insn_match(maybe_accumulator_insn,
324                          insn['asm'] + 'Accumulator',
325                          maybe_accumulator_args):
326      continue
327    # Now call that version if register is an Accumulator.
328    arg_count = len(_get_params(insn).split(','))
329    print('  if (DerivedAssemblerType::IsAccumulator(arg0)) {', file=f)
330    print('  return %s(%s);' % (
331      maybe_accumulator_insn['asm'],
332      ', '.join('arg%d' % n for n in range(1, arg_count))), file=f)
333    print('}', file=f)
334
335
336def _is_insn_match(insn, expected_name, expected_args):
337  # Note: usually there are more than one instruction with the same name
338  # but different arguments because they could accept either GeneralReg
339  # or Memory or Immediate argument.
340  #   Instructions:
341  #     Addl %eax, $1
342  #     Addl (%eax), $1
343  #     Addl %eax, %eax
344  #     Addl (%eax), %eax
345  #   are all valid.
346  #
347  # Yet not all instruction have all kinds of optimizations: TEST only have
348  # version with accumulator (which is shorter than usual) - but does not
349  # have while version with short immediate. Imul have version with short
350  # immediate - but not version with accumulator.
351  #
352  # We want to ensure that we have the exact match - expected name plus
353  # expected arguments.
354
355  return insn['asm'] == expected_name and insn['args'] == expected_args
356
357
358_ARGUMENT_FORMATS_TO_SIZES = {
359  'X87Reg' : 'RegisterDefaultBit',
360  'Cond': '',
361  'FpReg32' : 'VectorRegister128Bit',
362  'FpReg64' : 'VectorRegister128Bit',
363  'GeneralReg' : 'RegisterDefaultBit',
364  'GeneralReg8' : 'Register8Bit',
365  'GeneralReg16' : 'Register16Bit',
366  'GeneralReg32' : 'Register32Bit',
367  'GeneralReg64' : 'Register64Bit',
368  'Imm2': '',
369  'Imm8': '',
370  'Imm16': '',
371  'Imm32': '',
372  'Imm64': '',
373  'Mem': 'MemoryDefaultBit',
374  'Mem8' : 'Memory8Bit',
375  'Mem16' : 'Memory16Bit',
376  'Mem32' : 'Memory32Bit',
377  'Mem64' : 'Memory64Bit',
378  'Mem128' : 'Memory128Bit',
379  'MemX87': 'MemoryX87',
380  'MemX8716': 'MemoryX8716Bit',
381  'MemX8732': 'MemoryX8732Bit',
382  'MemX8764': 'MemoryX8764Bit',
383  'MemX8780': 'MemoryX8780Bit',
384  'RegX87': 'X87Register',
385  'XmmReg' : 'VectorRegister128Bit',
386  'VecMem32': 'VectorMemory32Bit',
387  'VecMem64': 'VectorMemory64Bit',
388  'VecMem128': 'VectorMemory128Bit',
389  'VecMem256': 'VectorMemory256Bit',
390  'VecReg128' : 'VectorRegister128Bit',
391  'VecReg256' : 'VectorRegister256Bit'
392}
393
394
395# On x86-64 each instruction which accepts explicit memory operant (there may at most be one)
396# can also accept $rip-relative addressing (where distance to operand is specified by 32-bit
397# difference between end of instruction and operand address).
398#
399# We use it to support Label operands - only name of class is changed from MemoryXXX to LabelXXX,
400# e.g. VectorMemory32Bit becomes VectorLabel32Bit.
401#
402# Note: on x86-32 that mode can also be emulated using regular instruction form, if needed.
403def _gen_emit_instruction(f, insn, arch, rip_operand=False, dyn_rm=False):
404  result = []
405  arg_count = 0
406  for arg in insn['args']:
407    if asm_defs.is_implicit_reg(arg['class']):
408      continue
409    # Note: in RISC-V there is never any ambiguity about whether full register or its part is used.
410    # Instead size of operand is always encoded in the name, e.g. addw vs add or fadd.s vs fadd.d
411    if arch in ['common_riscv', 'rv32', 'rv64']:
412      if dyn_rm and arg['class'] == 'Rm':
413        result.append('Rounding::kDyn')
414      else:
415        result.append('arg%d' % arg_count)
416    else:
417      result.append('%s(arg%d)' % (_ARGUMENT_FORMATS_TO_SIZES[arg['class']], arg_count))
418    arg_count += 1
419  # If we want %rip--operand then we need to replace 'Memory' with 'Labal'
420  if rip_operand:
421    result = [arg.replace('Memory', 'Label') for arg in result]
422  print('  Emit%sInstruction<%s>(%s);' % (
423      asm_defs._get_cxx_name(insn.get('type', '')),
424      ', '.join(insn['processed_opcodes']),
425      ', '.join(result)), file=f)
426
427
428def _gen_memory_function_specializations_h(f, insns, arch):
429  for insn in insns:
430    # Only build additional definitions needed for memory access in LIR if there
431    # are memory arguments and instruction is intended for use in LIR
432    if not _contains_mem(insn) or insn.get('skip_lir'):
433      continue
434    template, _ = _get_template_name(insn)
435    params = _get_params(insn)
436    for addr_mode in ('Absolute', 'BaseDisp', 'IndexDisp', 'BaseIndexDisp'):
437      # Generate a function to expand a macro and emit a corresponding
438      # assembly instruction with a memory operand.
439      macro_name = asm_defs.get_mem_macro_name(insn, addr_mode)
440      incoming_args = []
441      outgoing_args = []
442      for i, arg in enumerate(insn.get('args')):
443        if asm_defs.is_implicit_reg(arg.get('class')):
444          continue
445        arg_name = 'arg%d' % (i)
446        if asm_defs.is_mem_op(arg.get('class')):
447          if addr_mode == 'Absolute':
448            incoming_args.append('int32_t %s' % (arg_name))
449            outgoing_args.append('{.disp = %s}' % (arg_name))
450            continue
451          mem_args = []
452          if addr_mode in ('BaseDisp', 'BaseIndexDisp'):
453            mem_args.append(['Register', 'base', arg_name + '_base'])
454          if addr_mode in ('IndexDisp', 'BaseIndexDisp'):
455            mem_args.append(['Register', 'index', arg_name + '_index'])
456            mem_args.append(['ScaleFactor', 'scale', arg_name + '_scale'])
457          mem_args.append(['int32_t', 'disp', arg_name + '_disp'])
458          incoming_args.extend(['%s %s' % (pair[0], pair[2]) for pair in mem_args])
459          outgoing_args.append('{%s}' % (
460              ', '.join(['.%s = %s' % (pair[1], pair[2]) for pair in mem_args])))
461        else:
462          incoming_args.append('%s %s' % (_get_arg_type_name(arg, None), arg_name))
463          outgoing_args.append(arg_name)
464      if template:
465        print(template, file=f)
466      print('void %s(%s) {' % (macro_name, ', '.join(incoming_args)), file=f)
467      print('  %s(%s);' % (insn.get('asm'), ', '.join(outgoing_args)), file=f)
468      print('}', file=f)
469
470
471def _is_for_asm(insn):
472  if insn.get('skip_asm'):
473    return False
474  return True
475
476
477def _load_asm_defs(asm_def):
478  arch, insns = asm_defs.load_asm_defs(asm_def)
479  # Filter out explicitly disabled instructions.
480  return arch, [i for i in insns if _is_for_asm(i)]
481
482
483def main(argv):
484  # Usage: gen_asm.py --binary-assembler|--text_assembler
485  #                   <assembler_common-inl.h>
486  #                   <assembler_<arch>-inl.h>
487  #                   ...
488  #                   <def_common>
489  #                   <def_arch>
490  #                   ...
491
492  mode = argv[1]
493  assert len(argv) % 2 == 0
494  filenames = argv[2:]
495  filename_pairs = ((filenames[i], filenames[len(filenames)//2 + i])
496                    for i in range(0, len(filenames)//2))
497
498  if mode == '--binary-assembler':
499    binary_assembler = True
500  elif mode == '--text-assembler':
501    binary_assembler = False
502  else:
503    assert False, 'unknown option %s' % (mode)
504
505  for out_filename, input_filename in filename_pairs:
506    arch, loaded_defs = _load_asm_defs(input_filename)
507    with open(out_filename, 'w') as out_file:
508      _gen_generic_functions_h(out_file, loaded_defs, binary_assembler, arch)
509      if binary_assembler and arch is not None and 'x86' in arch:
510        _gen_memory_function_specializations_h(out_file, loaded_defs, arch)
511
512if __name__ == '__main__':
513  sys.exit(main(sys.argv))
514