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