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