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