• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python
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"""Parse assembler definition file.
19
20Definition JSON file for this script have following form:
21{
22  "arch": "XXX",
23  "insns": [
24    {
25      "name": "ShlbRegReg",
26      "args": [
27         {"class": "GeneralReg8", "usage": "use_def"},
28         {"class": "RCX", "usage": "use"},
29         {"class": "FLAGS", "usage": "def"}
30       ],
31       "asm": "ShlbByCl",
32       "mnemo": "SHLB"
33    },
34    ...
35  ]
36'arch' is primarily used for C++ namespace in LIR generator, and is ignored by
37this script.
38
39'insn' is array of objects, each describing single instruction variant.
40Each instruction is an object with following fields:
41  'name' - instruction unique name, used in LIR generator, typical name is
42           InsnOp1Op2, where 'Insn' is instruction name, 'Op1', 'Op2' -
43           operand types, such as Imm, Reg, Mem(Op), Base, Disp.
44  'args' - described ordered list of instruction arguments.
45           for each argument 'class' (any GP register, fixed GP register,
46           any XMM register, immediate, memory operand, flags register)
47           and how it is treated by an instruction (used, defined,
48           both used and defined)
49  'asm' - which internal assembler's mnemonic is used
50  'opcodes' - optional flag for autogeneration: if opcode bytes are specified
51              then implementation would be automatically generated
52  'reg_to_rm' - optional flag to make RM field in ModRegRM byte destination
53                (most instructions with two registers use reg as destination)
54  'mnemo' - how instruction shall be named in LIR dumps (ignored here)
55
56Memory operand for assembler instructions can be described as either opaque
57Operand class, which provides full power of x86 addressing modes, or as
58explicit BaseDisp format, which translates to reg+disp form.
59
60For some instructions (such as pop, push, jmp reg) exact register width is not
61specified, and 'GeneralReg' class is used, as same encoding is used for 32 and
6264 bit operands, depending on current CPU mode.
63
64This script produces inline file for internal assembler's header, such as for
65above example it would yield single line
66
67  void ShlbByCl(Register);
68
69Fixed arguments (such as 'RCX') and flags ('FLAGS') are ignored when generating
70assembler's header, while for others emitted an argument of type depending on
71argument's class.
72"""
73
74import copy
75import json
76
77
78def is_imm(arg_type):
79  return arg_type in ('Imm2', 'Imm8', 'Imm16', 'Imm32', 'Imm64')
80
81
82def is_disp(arg_type):
83  return arg_type == 'Disp'
84
85
86def is_mem_op(arg_type):
87  return arg_type in ('Mem8', 'Mem16', 'Mem32', 'Mem64', 'Mem128',
88                      'VecMem32', 'VecMem64', 'VecMem128')
89
90
91def is_cond(arg_type):
92  return arg_type == 'Cond'
93
94
95def is_label(arg_type):
96  return arg_type == 'Label'
97
98
99def is_greg(arg_type):
100  return arg_type in ('GeneralReg',
101                      'GeneralReg8', 'GeneralReg16',
102                      'GeneralReg32', 'GeneralReg64')
103
104
105def is_xreg(arg_type):
106  return arg_type in ('XmmReg',
107                      'VecReg64', 'VecReg128',
108                      'FpReg32', 'FpReg64')
109
110
111# Operands of this type are NOT passed to assembler
112def is_implicit_reg(arg_type):
113  return arg_type in ('RAX', 'EAX', 'AX', 'AL',
114                      'RCX', 'ECX', 'CL',
115                      'RDX', 'EDX', 'DX',
116                      'RBX', 'EBX', 'BX',
117                      'RDI', 'RSI', 'RSP', 'FLAGS')
118
119
120def exactly_one_of(iterable):
121  return sum(1 for elem in iterable if elem) == 1
122
123
124def get_mem_macro_name(insn, addr_mode = None):
125  macro_name = insn.get('asm')
126  if macro_name.endswith('ByCl'):
127    macro_name = macro_name[:-4]
128  for arg in insn['args']:
129    clazz = arg['class']
130    # Don't reflect FLAGS or Conditions or Labels in the name - we don't ever
131    # have two different instructions where these cause the difference.
132    if clazz == 'FLAGS' or is_cond(clazz) or is_label(clazz):
133      pass
134    elif is_greg(clazz) or is_implicit_reg(clazz):
135      macro_name += 'Reg'
136    elif is_xreg(clazz):
137      macro_name += 'XReg'
138    elif is_imm(clazz):
139      macro_name += 'Imm'
140    elif is_mem_op(clazz):
141      if addr_mode is not None:
142        macro_name += 'Mem' + addr_mode
143      else:
144        macro_name += 'Op'
145    else:
146      raise Exception('arg type %s is not supported' % clazz)
147  return macro_name
148
149
150def _expand_name(insn, stem, encoding = {}):
151  # Make deep copy of the instruction to make sure consumers could treat them
152  # as independent entities and add/remove marks freely.
153  #
154  # JSON never have "merged" objects thus having them in result violates
155  # expectations.
156  expanded_insn = copy.deepcopy(insn)
157  expanded_insn['asm'] = stem
158  expanded_insn['name'] = get_mem_macro_name(expanded_insn)
159  expanded_insn['mnemo'] = stem.upper()
160  expanded_insn.update(encoding)
161  return expanded_insn
162
163
164def _expand_insn_by_encodings(insns):
165  expanded_insns = []
166  for insn in insns:
167    if insn.get('encodings'):
168      assert all((f not in insn) for f in ['stems', 'name', 'asm', 'mnemo'])
169      # If we have encoding then we must have at least opcodes
170      assert all('opcodes' in encoding for _, encoding in insn['encodings'].items())
171      expanded_insns.extend([_expand_name(insn, stem, encoding)
172                            for stem, encoding in insn['encodings'].items()])
173    elif insn.get('stems'):
174      assert all((f not in insn) for f in ['encoding', 'name', 'asm', 'mnemo'])
175      expanded_insns.extend([_expand_name(insn, stem)
176                            for stem in insn['stems']])
177    else:
178      assert all((f in insn) for f in ['name', 'asm', 'mnemo'])
179      expanded_insns.append(insn)
180  return expanded_insns
181
182
183def _expand_insns_by_operands(insns):
184  expanded_insns = []
185  for insn in insns:
186    split_done = False
187    for arg in insn['args']:
188      if '/' in arg['class']:
189        assert not split_done
190        operand_classes = arg['class'].split('/')
191        for subclass in operand_classes:
192          arg['class'] = subclass
193          expanded_insn = copy.deepcopy(insn)
194          expanded_insns.append(expanded_insn)
195        split_done = True
196    if not split_done:
197      expanded_insns.append(insn)
198  return expanded_insns
199
200
201def load_asm_defs(asm_def):
202  result = []
203  with open(asm_def) as asm:
204    obj = json.load(asm)
205    insns = obj.get('insns')
206    insns = _expand_insns_by_operands(insns)
207    insns = _expand_insn_by_encodings(insns)
208    insns = sorted(insns, key=lambda i: i.get('asm'))
209    result.extend(insns)
210  return obj.get('arch'), result
211