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