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