github.com/cloudwego/iasm@v0.2.0/mkasm_x86_64.py (about)

     1  #!/usr/bin/env python3
     2  # -*- coding: utf-8 -*-
     3  
     4  import os
     5  import copy
     6  import json
     7  
     8  from typing import List
     9  from typing import Tuple
    10  from typing import Iterable
    11  from opcodes import x86_64
    12  
    13  def instruction_set():
    14      for ins in x86_64.read_instruction_set():
    15          fv = []
    16          for form in ins.forms:
    17              if any(op.type in ['{sae}', '{er}'] for op in form.operands):
    18                  new = copy.deepcopy(form)
    19                  new.operands = [op for op in new.operands if op.type not in ['{sae}', '{er}']]
    20                  new_evex = next(v for v in new.encodings[0].components if isinstance(v, x86_64.EVEX))
    21                  new_evex.b = 0
    22                  new_evex.LL = 0b10
    23                  fv.append(new)
    24                  old = next(v for v in form.encodings[0].components if isinstance(v, x86_64.EVEX))
    25                  if not isinstance(old.LL, x86_64.Operand):
    26                      old.LL = 0
    27                  old.b = 1
    28          ins.forms.extend(fv)
    29          yield ins
    30  
    31  def instruction_domains():
    32      with open(os.path.join(os.path.dirname(__file__), 'domains_x86_64.json')) as fp:
    33          domains = json.load(fp)
    34          return { ins: dom for dom, insv in domains.items() for ins in insv }
    35  
    36  instrs = {}
    37  domains = instruction_domains()
    38  
    39  for instr in instruction_set():
    40      for form in instr.forms:
    41          if all([v.type not in ('r8l', 'r16l', 'r32l', 'moffs32', 'moffs64') for v in form.operands]):
    42              name = form.gas_name.upper()
    43              if name not in instrs:
    44                  instrs[name] = (instr.name, instr.summary, [])
    45              instrs[name][2].append(form)
    46  
    47  for _, _, forms in instrs.values():
    48      forms.sort(key = lambda f: max([x.score for x in f.isa_extensions], default = 0))
    49  
    50  OPCHECKS = {
    51      '1'             : 'isConst1(%s)',
    52      '3'             : 'isConst3(%s)',
    53      'al'            : '%s == AL',
    54      'ax'            : '%s == AX',
    55      'eax'           : '%s == EAX',
    56      'rax'           : '%s == RAX',
    57      'cl'            : '%s == CL',
    58      'xmm0'          : '%s == XMM0',
    59      'rel8'          : 'isRel8(%s)',
    60      'rel32'         : 'isRel32(%s)',
    61      'imm4'          : 'isImm4(%s)',
    62      'imm8'          : 'isImm8(%s)',
    63      'imm16'         : 'isImm16(%s)',
    64      'imm32'         : 'isImm32(%s)',
    65      'imm64'         : 'isImm64(%s)',
    66      'r8'            : 'isReg8(%s)',
    67      'r16'           : 'isReg16(%s)',
    68      'r32'           : 'isReg32(%s)',
    69      'r64'           : 'isReg64(%s)',
    70      'mm'            : 'isMM(%s)',
    71      'xmm'           : 'isXMM(%s)',
    72      'xmm{k}'        : 'isXMMk(%s)',
    73      'xmm{k}{z}'     : 'isXMMkz(%s)',
    74      'ymm'           : 'isYMM(%s)',
    75      'ymm{k}'        : 'isYMMk(%s)',
    76      'ymm{k}{z}'     : 'isYMMkz(%s)',
    77      'zmm'           : 'isZMM(%s)',
    78      'zmm{k}'        : 'isZMMk(%s)',
    79      'zmm{k}{z}'     : 'isZMMkz(%s)',
    80      'k'             : 'isK(%s)',
    81      'k{k}'          : 'isKk(%s)',
    82      'm'             : 'isM(%s)',
    83      'm8'            : 'isM8(%s)',
    84      'm16'           : 'isM16(%s)',
    85      'm16{k}{z}'     : 'isM16kz(%s)',
    86      'm32'           : 'isM32(%s)',
    87      'm32{k}'        : 'isM32k(%s)',
    88      'm32{k}{z}'     : 'isM32kz(%s)',
    89      'm64'           : 'isM64(%s)',
    90      'm64{k}'        : 'isM64k(%s)',
    91      'm64{k}{z}'     : 'isM64kz(%s)',
    92      'm80'           : 'isM80(%s)',
    93      'm128'          : 'isM128(%s)',
    94      'm128{k}{z}'    : 'isM128kz(%s)',
    95      'm256'          : 'isM256(%s)',
    96      'm256{k}{z}'    : 'isM256kz(%s)',
    97      'm512'          : 'isM512(%s)',
    98      'm512{k}{z}'    : 'isM512kz(%s)',
    99      'm64/m32bcst'   : 'isM64M32bcst(%s)',
   100      'm128/m32bcst'  : 'isM128M32bcst(%s)',
   101      'm256/m32bcst'  : 'isM256M32bcst(%s)',
   102      'm512/m32bcst'  : 'isM512M32bcst(%s)',
   103      'm128/m64bcst'  : 'isM128M64bcst(%s)',
   104      'm256/m64bcst'  : 'isM256M64bcst(%s)',
   105      'm512/m64bcst'  : 'isM512M64bcst(%s)',
   106      'vm32x'         : 'isVMX(%s)',
   107      'vm32x{k}'      : 'isVMXk(%s)',
   108      'vm32y'         : 'isVMY(%s)',
   109      'vm32y{k}'      : 'isVMYk(%s)',
   110      'vm32z'         : 'isVMZ(%s)',
   111      'vm32z{k}'      : 'isVMZk(%s)',
   112      'vm64x'         : 'isVMX(%s)',
   113      'vm64x{k}'      : 'isVMXk(%s)',
   114      'vm64y'         : 'isVMY(%s)',
   115      'vm64y{k}'      : 'isVMYk(%s)',
   116      'vm64z'         : 'isVMZ(%s)',
   117      'vm64z{k}'      : 'isVMZk(%s)',
   118      '{sae}'         : 'isSAE(%s)',
   119      '{er}'          : 'isER(%s)',
   120  }
   121  
   122  IMMCHECKS = {
   123      'imm8'  : 'isImm8Ext(%s, %d)',
   124      'imm32' : 'isImm32Ext(%s, %d)',
   125  }
   126  
   127  EVEXCHECKS = {
   128      'xmm'   : 'isEVEXXMM(%s)',
   129      'ymm'   : 'isEVEXYMM(%s)',
   130      'vm32x' : 'isEVEXVMX(%s)',
   131      'vm64x' : 'isEVEXVMX(%s)',
   132      'vm32y' : 'isEVEXVMY(%s)',
   133      'vm64y' : 'isEVEXVMY(%s)',
   134  }
   135  
   136  VEXBYTES = {
   137      'VEX': '0xc4',
   138      'XOP': '0x8f',
   139  }
   140  
   141  ISAMAPPING = {
   142      'CPUID'           : 'ISA_CPUID',
   143      'RDTSC'           : 'ISA_RDTSC',
   144      'RDTSCP'          : 'ISA_RDTSCP',
   145      'CMOV'            : 'ISA_CMOV',
   146      'MOVBE'           : 'ISA_MOVBE',
   147      'POPCNT'          : 'ISA_POPCNT',
   148      'LZCNT'           : 'ISA_LZCNT',
   149      'TBM'             : 'ISA_TBM',
   150      'BMI'             : 'ISA_BMI',
   151      'BMI2'            : 'ISA_BMI2',
   152      'ADX'             : 'ISA_ADX',
   153      'MMX'             : 'ISA_MMX',
   154      'MMX+'            : 'ISA_MMX_PLUS',
   155      'FEMMS'           : 'ISA_FEMMS',
   156      '3dnow!'          : 'ISA_3DNOW',
   157      '3dnow!+'         : 'ISA_3DNOW_PLUS',
   158      'SSE'             : 'ISA_SSE',
   159      'SSE2'            : 'ISA_SSE2',
   160      'SSE3'            : 'ISA_SSE3',
   161      'SSSE3'           : 'ISA_SSSE3',
   162      'SSE4A'           : 'ISA_SSE4A',
   163      'SSE4.1'          : 'ISA_SSE4_1',
   164      'SSE4.2'          : 'ISA_SSE4_2',
   165      'FMA3'            : 'ISA_FMA3',
   166      'FMA4'            : 'ISA_FMA4',
   167      'XOP'             : 'ISA_XOP',
   168      'F16C'            : 'ISA_F16C',
   169      'AVX'             : 'ISA_AVX',
   170      'AVX2'            : 'ISA_AVX2',
   171      'AVX512F'         : 'ISA_AVX512F',
   172      'AVX512BW'        : 'ISA_AVX512BW',
   173      'AVX512DQ'        : 'ISA_AVX512DQ',
   174      'AVX512VL'        : 'ISA_AVX512VL',
   175      'AVX512PF'        : 'ISA_AVX512PF',
   176      'AVX512ER'        : 'ISA_AVX512ER',
   177      'AVX512CD'        : 'ISA_AVX512CD',
   178      'AVX512VBMI'      : 'ISA_AVX512VBMI',
   179      'AVX512IFMA'      : 'ISA_AVX512IFMA',
   180      'AVX512VPOPCNTDQ' : 'ISA_AVX512VPOPCNTDQ',
   181      'AVX512_4VNNIW'   : 'ISA_AVX512_4VNNIW',
   182      'AVX512_4FMAPS'   : 'ISA_AVX512_4FMAPS',
   183      'PREFETCH'        : 'ISA_PREFETCH',
   184      'PREFETCHW'       : 'ISA_PREFETCHW',
   185      'PREFETCHWT1'     : 'ISA_PREFETCHWT1',
   186      'CLFLUSH'         : 'ISA_CLFLUSH',
   187      'CLFLUSHOPT'      : 'ISA_CLFLUSHOPT',
   188      'CLWB'            : 'ISA_CLWB',
   189      'CLZERO'          : 'ISA_CLZERO',
   190      'RDRAND'          : 'ISA_RDRAND',
   191      'RDSEED'          : 'ISA_RDSEED',
   192      'PCLMULQDQ'       : 'ISA_PCLMULQDQ',
   193      'AES'             : 'ISA_AES',
   194      'SHA'             : 'ISA_SHA',
   195      'MONITOR'         : 'ISA_MONITOR',
   196      'MONITORX'        : 'ISA_MONITORX',
   197  }
   198  
   199  DOMAIN_MAP = {
   200      'generic' : 'DomainGeneric',
   201      'mmxsse'  : 'DomainMMXSSE',
   202      'avx'     : 'DomainAVX',
   203      'fma'     : 'DomainFMA',
   204      'crypto'  : 'DomainCrypto',
   205      'mask'    : 'DomainMask',
   206      'amd'     : 'DomainAMDSpecific',
   207      'misc'    : 'DomainMisc',
   208  }
   209  
   210  BRANCH_INSTRUCTIONS = {
   211      'JA'    , 'JNA',
   212      'JAE'   , 'JNAE',
   213      'JB'    , 'JNB',
   214      'JBE'   , 'JNBE',
   215      'JC'    , 'JNC',
   216      'JE'    , 'JNE',
   217      'JG'    , 'JNG',
   218      'JGE'   , 'JNGE',
   219      'JL'    , 'JNL',
   220      'JLE'   , 'JNLE',
   221      'JO'    , 'JNO',
   222      'JP'    , 'JNP',
   223      'JS'    , 'JNS',
   224      'JZ'    , 'JNZ',
   225      'JPE'   , 'JPO',
   226      'JECXZ' , 'JRCXZ',
   227      'JMP'
   228  }
   229  
   230  def is_avx512(form: x86_64.InstructionForm) -> bool:
   231      return any(v.name.startswith('AVX512') for v in form.isa_extensions)
   232  
   233  def dump_form(form: x86_64.InstructionForm) -> str:
   234      if not form.operands:
   235          return form.gas_name.upper()
   236      else:
   237          return form.gas_name.upper() + ' ' + ', '.join(v.type for v in reversed(form.operands))
   238  
   239  def require_isa(isa: List[x86_64.ISAExtension]) -> str:
   240      flags = []
   241      for v in isa:
   242          if v.name not in ISAMAPPING:
   243              raise RuntimeError('invalid ISA: ' + v.name)
   244          flags.append(ISAMAPPING[v.name])
   245      return ' | '.join(flags)
   246  
   247  def operand_match(ops: List[x86_64.Operand], argc: int, avx512: bool) -> Iterable[str]:
   248      for i, op in enumerate(ops):
   249          if i < argc:
   250              argv = 'v%d' % i
   251          else:
   252              argv = 'vv[%d]' % (i - argc)
   253          if op.extended_size is not None and op.type in IMMCHECKS:
   254              yield IMMCHECKS[op.type] % (argv, op.extended_size)
   255          elif avx512 and op.type in EVEXCHECKS:
   256              yield EVEXCHECKS[op.type] % argv
   257          else:
   258              yield OPCHECKS[op.type] % argv
   259  
   260  def generate_encoding(enc: x86_64.Encoding, ops: List[x86_64.Operand], gen_branch: bool = False) -> Tuple[str, List[str]]:
   261      buf = []
   262      flags = []
   263      disp8v = None
   264      for item in enc.components:
   265          if isinstance(item, x86_64.Prefix):
   266              buf.append('m.emit(0x%02x)' % item.byte)
   267          elif isinstance(item, x86_64.REX):
   268              item.set_ignored()
   269              if item.is_mandatory:
   270                  if isinstance(item.X, x86_64.Operand):
   271                      args = [str(item.W)]
   272                      if isinstance(item.R, x86_64.Operand):
   273                          args.append('hcode(v[%d])' % ops.index(item.R))
   274                      else:
   275                          args.append(str(item.R))
   276                      args.append('addr(v[%d])' % ops.index(item.X))
   277                      buf.append('m.rexm(%s)' % ', '.join(args))
   278                  else:
   279                      rex = 0x40 | (item.W << 3)
   280                      args = []
   281                      if isinstance(item.R, x86_64.Operand):
   282                          args.append('hcode(v[%d]) << 2' % ops.index(item.R))
   283                      else:
   284                          rex |= item.R << 2
   285                      if isinstance(item.B, x86_64.Operand):
   286                          args.append('hcode(v[%d])' % ops.index(item.B))
   287                      else:
   288                          rex |= item.B
   289                      rex |= item.X << 1
   290                      buf.append('m.emit(%s)' % ' | '.join(['0x%02x' % rex] + args))
   291              else:
   292                  args = []
   293                  if isinstance(item.R, x86_64.Operand):
   294                      args.append('hcode(v[%d])' % ops.index(item.R))
   295                  else:
   296                      args.append(str(item.R))
   297                  if isinstance(item.X, x86_64.Operand):
   298                      args.append('addr(v[%d])' % ops.index(item.X))
   299                  else:
   300                      args.append('v[%d]' % ops.index(item.B))
   301                  rexv = []
   302                  for i, op in enumerate(ops):
   303                      if op.type == 'r8':
   304                          rexv.append('isReg8REX(v[%d])' % i)
   305                  if not rexv:
   306                      args.append('false')
   307                  else:
   308                      args.append(' || '.join(rexv))
   309                  buf.append('m.rexo(%s)' % ', '.join(args))
   310          elif isinstance(item, x86_64.VEX):
   311              item.set_ignored()
   312              if item.type == 'VEX' and item.mmmmm == 0b00001 and item.W == 0:
   313                  if item.R == 1 and item.X == 1 and item.B == 1:
   314                      buf.append('m.emit(0xc5)')
   315                      buf.append('m.emit(0x%02x)' % (0xf8 | (item.L << 2) | int(item.pp)))
   316                  else:
   317                      args = [str(int(item.L << 2) | item.pp)]
   318                      if isinstance(item.R, x86_64.Operand):
   319                          args.append('hcode(v[%d])' % ops.index(item.R))
   320                      else:
   321                          args.append('0')
   322                      if isinstance(item.X, x86_64.Operand):
   323                          args.append('addr(v[%d])' % ops.index(item.X))
   324                      elif isinstance(item.B, x86_64.Operand):
   325                          args.append('v[%d]' % ops.index(item.B))
   326                      else:
   327                          args.append('nil')
   328                      if isinstance(item.vvvv, x86_64.Operand):
   329                          args.append('hlcode(v[%d])' % ops.index(item.vvvv))
   330                      else:
   331                          args.append('0')
   332                      buf.append('m.vex2(%s)' % ', '.join(args))
   333              else:
   334                  if isinstance(item.X, x86_64.Operand):
   335                      args = [
   336                          VEXBYTES[item.type],
   337                          bin(item.mmmmm),
   338                          '0x%02x' % ((item.W << 7) | (item.L << 2) | int(item.pp)),
   339                      ]
   340                      if isinstance(item.R, x86_64.Operand):
   341                          args.append('hcode(v[%d])' % ops.index(item.R))
   342                      else:
   343                          args.append('0')
   344                      args.append('addr(v[%d])' % ops.index(item.X))
   345                      if isinstance(item.vvvv, x86_64.Operand):
   346                          args.append('hlcode(v[%d])' % ops.index(item.vvvv))
   347                      else:
   348                          args.append('0')
   349                      buf.append('m.vex3(%s)' % ', '.join(args))
   350                  else:
   351                      buf.append('m.emit(%s)' % VEXBYTES[item.type])
   352                      v0 = '0x%02x' % (0xe0 | item.mmmmm)
   353                      if isinstance(item.R, x86_64.Operand):
   354                          v0 += ' ^ (hcode(v[%d]) << 7)' % ops.index(item.R)
   355                      if isinstance(item.B, x86_64.Operand):
   356                          v0 += ' ^ (hcode(v[%d]) << 5)' % ops.index(item.B)
   357                      buf.append('m.emit(%s)' % v0)
   358                      vex = 0x78 | (item.W << 7) | (item.L << 2) | int(item.pp)
   359                      if isinstance(item.vvvv, x86_64.Operand):
   360                          buf.append('m.emit(0x%02x ^ (hlcode(v[%d]) << 3))' % (vex, ops.index(item.vvvv)))
   361                      else:
   362                          buf.append('m.emit(0x%02x)' % vex)
   363          elif isinstance(item, x86_64.EVEX):
   364              disp8v = item.disp8xN
   365              item.set_ignored()
   366              if item.X.is_memory:
   367                  args = ['0b' + format(item.mm, '02b'), '0x%02x' % (item.W << 7 | int(item.pp) | 0b100)]
   368                  if isinstance(item.LL, x86_64.Operand):
   369                      args.append('vcode(v[%d])' % ops.index(item.LL))
   370                  else:
   371                      args.append('0b' + format(item.LL, '02b'))
   372                  if isinstance(item.RR, x86_64.Operand):
   373                      args.append('ehcode(v[%d])' % ops.index(item.RR))
   374                  else:
   375                      args.append(str(item.RR))
   376                  args.append('addr(v[%d])' % ops.index(item.X))
   377                  if item.vvvv != 0:
   378                      args.append('vcode(v[%d])' % ops.index(item.vvvv))
   379                  else:
   380                      args.append('0')
   381                  if item.aaa != 0:
   382                      args.append('kcode(v[%d])' % ops.index(item.aaa))
   383                  else:
   384                      args.append('0')
   385                  if item.z != 0:
   386                      args.append('zcode(v[%d])' % ops.index(item.z))
   387                  else:
   388                      args.append('0')
   389                  if isinstance(item.b, x86_64.Operand):
   390                      args.append('bcode(v[%d])' % ops.index(item.b))
   391                  elif item.b != 0:
   392                      args.append(str(item.b))
   393                  else:
   394                      args.append('0')
   395                  buf.append('m.evex(%s)' % ', '.join(args))
   396              else:
   397                  buf.append('m.emit(0x62)')
   398                  if isinstance(item.RR, x86_64.Operand):
   399                      v0, v1, v2, v3 = 0xf0 | item.mm, ops.index(item.RR), ops.index(item.B), ops.index(item.RR)
   400                      buf.append('m.emit(0x%02x ^ ((hcode(v[%d]) << 7) | (ehcode(v[%d]) << 5) | (ecode(v[%d]) << 4)))' % (v0, v1, v2, v3))
   401                  else:
   402                      r0 = item.RR & 1
   403                      r1 = (item.RR >> 1) & 1
   404                      byte = (item.mm | (r0 << 7) | (r1 << 4)) ^ 0xf0
   405                      if byte == 0:
   406                          buf.append('m.emit(ehcode(v[%d]) << 5)' % ops.index(item.B))
   407                      else:
   408                          buf.append('m.emit(0x%02x ^ (ehcode(v[%d]) << 5))' % (byte, ops.index(item.B)))
   409                  vvvv = item.W << 7 | int(item.pp) | 0b01111100
   410                  if isinstance(item.vvvv, x86_64.Operand):
   411                      buf.append('m.emit(0x%02x ^ (hlcode(v[%d]) << 3))' % (vvvv, ops.index(item.vvvv)))
   412                  else:
   413                      buf.append('m.emit(0x%02x)' % vvvv)
   414                  byte = item.b << 4
   415                  parts = []
   416                  if isinstance(item.z, x86_64.Operand):
   417                      parts.append('(zcode(v[%d]) << 7)' % ops.index(item.z))
   418                  else:
   419                      byte |= item.z << 7
   420                  if isinstance(item.LL, x86_64.Operand):
   421                      parts.append('(vcode(v[%d]) << 5)' % ops.index(item.LL))
   422                  else:
   423                      byte |= item.LL << 5
   424                  if isinstance(item.V, x86_64.Operand):
   425                      parts.append('(0x08 ^ (ecode(v[%d]) << 3))' % ops.index(item.V))
   426                  else:
   427                      byte |= (item.V ^ 1) << 3
   428                  if isinstance(item.aaa, x86_64.Operand):
   429                      parts.append('kcode(v[%d])' % ops.index(item.aaa))
   430                  parts.append('0x%02x' % byte)
   431                  buf.append('m.emit(%s)' % ' | '.join(parts))
   432          elif isinstance(item, x86_64.Opcode):
   433              if not item.addend:
   434                  buf.append('m.emit(0x%02x)' % item.byte)
   435              else:
   436                  buf.append('m.emit(0x%02x | lcode(v[%d]))' % (item.byte, ops.index(item.addend)))
   437          elif isinstance(item, x86_64.ModRM):
   438              if isinstance(item.mode, x86_64.Operand):
   439                  if isinstance(item.reg, x86_64.Operand):
   440                      reg = 'lcode(v[%d])' % ops.index(item.reg)
   441                  else:
   442                      reg = str(item.reg)
   443                  if disp8v is None:
   444                      disp = 1
   445                  else:
   446                      disp = disp8v
   447                  buf.append('m.mrsd(%s, addr(v[%d]), %d)' % (reg, ops.index(item.rm), disp))
   448              else:
   449                  mod = item.mode << 6
   450                  parts = []
   451                  if isinstance(item.reg, x86_64.Operand):
   452                      parts.append('lcode(v[%d]) << 3' % ops.index(item.reg))
   453                  elif item.reg:
   454                      mod |= item.reg << 3
   455                  parts.append('lcode(v[%d])' % ops.index(item.rm))
   456                  buf.append('m.emit(%s)' % ' | '.join(['0x%02x' % mod] + parts))
   457          elif isinstance(item, x86_64.Immediate):
   458              if isinstance(item.value, x86_64.Operand):
   459                  if item.size == 1:
   460                      buf.append('m.imm1(toImmAny(v[%d]))' % ops.index(item.value))
   461                  elif item.size == 2:
   462                      buf.append('m.imm2(toImmAny(v[%d]))' % ops.index(item.value))
   463                  elif item.size == 4:
   464                      buf.append('m.imm4(toImmAny(v[%d]))' % ops.index(item.value))
   465                  elif item.size == 8:
   466                      buf.append('m.imm8(toImmAny(v[%d]))' % ops.index(item.value))
   467                  else:
   468                      raise RuntimeError('invalid imm size: ' + str(item.size))
   469              else:
   470                  if item.size == 1:
   471                      buf.append('m.imm1(0x%02x)' % item.value)
   472                  elif item.size == 2:
   473                      buf.append('m.imm2(0x%04x)' % item.value)
   474                  elif item.size == 4:
   475                      buf.append('m.imm4(0x%08x)' % item.value)
   476                  elif item.size == 8:
   477                      buf.append('m.imm8(0x%016x)' % item.value)
   478                  else:
   479                      raise RuntimeError('invalid imm size: ' + str(item.size))
   480          elif isinstance(item, x86_64.RegisterByte):
   481              ibr = 'hlcode(v[%d]) << 4' % ops.index(item.register)
   482              if item.payload is not None:
   483                  ibr = '(%s) | imml(v[%d])' % (ibr, ops.index(item.payload))
   484              buf.append('m.emit(%s)' % ibr)
   485          elif isinstance(item, x86_64.CodeOffset):
   486              if item.size == 1:
   487                  buf.append('m.imm1(relv(v[%d]))' % ops.index(item.value))
   488                  if gen_branch:
   489                      flags.append('_F_rel1')
   490              elif item.size == 4:
   491                  buf.append('m.imm4(relv(v[%d]))' % ops.index(item.value))
   492                  if gen_branch:
   493                      flags.append('_F_rel4')
   494              else:
   495                  raise RuntimeError('invalid code offset size: ' + repr(item.size))
   496          else:
   497              raise RuntimeError('unknown encoding component: ' + repr(item))
   498      if not flags:
   499          return '0', buf
   500      else:
   501          return ' | '.join(flags), buf
   502  
   503  class CodeGen:
   504      def __init__(self):
   505          self.buf = []
   506          self.level = 0
   507  
   508      @property
   509      def src(self) -> str:
   510          return '\n'.join(self.buf)
   511  
   512      def line(self, src: str = ''):
   513          self.buf.append(' ' * (self.level * 4) + src)
   514  
   515      def dedent(self):
   516          self.level -= 1
   517  
   518      def indent(self):
   519          self.level += 1
   520  
   521  class CodeBlock:
   522      def __init__(self, gen: CodeGen):
   523          self.gen = gen
   524  
   525      def __exit__(self, *_):
   526          self.gen.dedent()
   527  
   528      def __enter__(self):
   529          self.gen.indent()
   530          return self
   531  
   532  cc = CodeGen()
   533  cc.line('// Code generated by "mkasm_amd64.py", DO NOT EDIT.')
   534  cc.line()
   535  cc.line('package x86_64')
   536  cc.line()
   537  
   538  nargs = 0
   539  nforms = 0
   540  argsmap = {}
   541  for name, (_, _, forms) in instrs.items():
   542      fcnt = 0
   543      for form in forms:
   544          acnt = len(form.operands)
   545          fcnt += len(form.encodings)
   546          argsmap.setdefault(name, set()).add(acnt)
   547          if nargs < acnt:
   548              nargs = acnt
   549      if nforms < fcnt:
   550          nforms = fcnt
   551  
   552  cc.line('const (')
   553  with CodeBlock(cc):
   554      cc.line('_N_args  = %d' % nargs)
   555      cc.line('_N_forms = %d' % nforms)
   556  cc.line(')')
   557  cc.line()
   558  cc.line('// Instructions maps all the instruction name to it\'s encoder function.')
   559  cc.line('var Instructions = map[string]_InstructionEncoder {')
   560  
   561  width = max(
   562      len(x)
   563      for x in instrs
   564  )
   565  
   566  with CodeBlock(cc):
   567      for name in sorted(instrs):
   568          key = '"%s"' % name.lower()
   569          cc.line('%s: __asm_proxy_%s__,' % (key.ljust(width + 3), name))
   570  
   571  cc.line('}')
   572  cc.line()
   573  
   574  for name in sorted(instrs):
   575      cc.line('func __asm_proxy_%s__(p *Program, v ...interface{}) *Instruction {' % name)
   576      with CodeBlock(cc):
   577          args = argsmap[name]
   578          if len(args) == 1:
   579              argc = next(iter(args))
   580              cc.line('if len(v) == %d {' % argc)
   581              with CodeBlock(cc):
   582                  argv = ['v[%d]' % i for i in range(argc)]
   583                  cc.line('return p.%s(%s)' % (name, ', '.join(argv)))
   584              cc.line('} else {')
   585              with CodeBlock(cc):
   586                  if argc == 0:
   587                      cc.line('panic("instruction %s takes no operands")' % name)
   588                  elif argc == 1:
   589                      cc.line('panic("instruction %s takes exactly 1 operand")' % name)
   590                  else:
   591                      cc.line('panic("instruction %s takes exactly %d operands")' % (name, argc))
   592              cc.line('}')
   593          else:
   594              cc.line('switch len(v) {')
   595              with CodeBlock(cc):
   596                  for argc in sorted(args):
   597                      argv = ['v[%d]' % i for i in range(argc)]
   598                      cc.line('case %d  : return p.%s(%s)' % (argc, name, ', '.join(argv)))
   599                  cc.line('default : panic("instruction %s takes %s operands")' % (name, ' or '.join(map(str, sorted(args)))))
   600              cc.line('}')
   601      cc.line('}')
   602      cc.line()
   603  
   604  with open('x86_64/instructions_table.go', 'w') as fp:
   605      fp.write(cc.src)
   606  
   607  cc = CodeGen()
   608  cc.line('// Code generated by "mkasm_amd64.py", DO NOT EDIT.')
   609  cc.line()
   610  cc.line('package x86_64')
   611  cc.line()
   612  
   613  for name, (ins, desc, forms) in sorted(instrs.items()):
   614      cc.line('// %s performs "%s".' % (name, desc))
   615      cc.line('//')
   616      cc.line('// Mnemonic        : ' + ins)
   617      cc.line('// Supported forms : (%d form%s)' % (len(forms), '' if len(forms) == 1 else 's'))
   618      cc.line('//')
   619      nops = set()
   620      fwidth = max(map(len, map(dump_form, forms)))
   621      for form in forms:
   622          nops.add(len(form.operands))
   623          if not form.isa_extensions:
   624              cc.line('//    * ' + dump_form(form))
   625          else:
   626              cc.line('//    * %-*s    [%s]' % (fwidth, dump_form(form), ','.join(sorted(v.name for v in form.isa_extensions))))
   627      nfix = min(nops)
   628      args = ['v%d interface{}' % i for i in range(nfix)]
   629      if len(nops) != 1:
   630          args.append('vv ...interface{}')
   631      cc.line('//')
   632      cc.line('func (self *Program) %s(%s) *Instruction {' % (name, ', '.join(args)))
   633      with CodeBlock(cc):
   634          base = ['v%d' % i for i in range(nfix)]
   635          if len(nops) == 1:
   636              cc.line('p := self.alloc("%s", %d, Operands { %s })' % (name, nfix, ', '.join(base)))
   637          else:
   638              cc.line('var p *Instruction')
   639              cc.line('switch len(vv) {')
   640              with CodeBlock(cc):
   641                  for argc in sorted(nops):
   642                      args = base[:] + ['vv[%d]' % i for i in range(argc - nfix)]
   643                      cc.line('case %d  : p = self.alloc("%s", %d, Operands { %s })' % (argc - nfix, name, argc, ', '.join(args)))
   644                  cc.line('default : panic("instruction %s takes %s operands")' % (name, ' or '.join(map(str, sorted(nops)))))
   645              cc.line('}')
   646          if name == 'JMP':
   647              cc.line('p.branch = _B_unconditional')
   648          elif name in BRANCH_INSTRUCTIONS:
   649              cc.line('p.branch = _B_conditional')
   650          is_labeled = False
   651          must_success = False
   652          for form in forms:
   653              ops = list(reversed(form.operands))
   654              if len(ops) == 1 and ops[0].type in ('rel8', 'rel32'):
   655                  is_labeled = True
   656              conds = []
   657              cc.line('// ' + dump_form(form))
   658              if len(nops) != 1:
   659                  conds.append('len(vv) == %d' % (len(ops) - nfix))
   660              conds.extend(operand_match(ops, nfix, is_avx512(form)))
   661              if conds:
   662                  cc.line('if %s {' % ' && '.join(conds))
   663                  cc.indent()
   664              else:
   665                  must_success = True
   666              if form.isa_extensions:
   667                  cc.line('self.require(%s)' % require_isa(form.isa_extensions))
   668              cc.line('p.domain = ' + DOMAIN_MAP[domains.get(form.name, 'misc')])
   669              for enc in form.encodings:
   670                  flags, instr = generate_encoding(enc, ops, gen_branch = False)
   671                  cc.line('p.add(%s, func(m *_Encoding, v []interface{}) {' % flags)
   672                  with CodeBlock(cc):
   673                      for line in instr:
   674                          cc.line(line)
   675                  cc.line('})')
   676              if conds:
   677                  cc.dedent()
   678                  cc.line('}')
   679          if is_labeled:
   680              cc.line('// %s label' % name)
   681              cc.line('if isLabel(v0) {')
   682              with CodeBlock(cc):
   683                  for form in forms:
   684                      ops = list(reversed(form.operands))
   685                      for enc in form.encodings:
   686                          flags, instr = generate_encoding(enc, ops, gen_branch = True)
   687                          cc.line('p.add(%s, func(m *_Encoding, v []interface{}) {' % flags)
   688                          with CodeBlock(cc):
   689                              for line in instr:
   690                                  cc.line(line)
   691                          cc.line('})')
   692              cc.line('}')
   693          if not must_success:
   694              cc.line('if p.len == 0 {')
   695              with CodeBlock(cc):
   696                  cc.line('panic("invalid operands for %s")' % name)
   697              cc.line('}')
   698          cc.line('return p')
   699      cc.line('}')
   700      cc.line()
   701  
   702  with open('x86_64/instructions.go', 'w') as fp:
   703      fp.write(cc.src)