Home | History | Annotate | Line # | Download | only in opcodes
      1 /* Instruction building/extraction support for @arch@. -*- C -*-
      2 
      3    THIS FILE IS MACHINE GENERATED WITH CGEN: Cpu tools GENerator.
      4    - the resultant file is machine generated, cgen-ibld.in isn't
      5 
      6    Copyright (C) 1996-2026 Free Software Foundation, Inc.
      7 
      8    This file is part of libopcodes.
      9 
     10    This library is free software; you can redistribute it and/or modify
     11    it under the terms of the GNU General Public License as published by
     12    the Free Software Foundation; either version 3, or (at your option)
     13    any later version.
     14 
     15    It is distributed in the hope that it will be useful, but WITHOUT
     16    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     17    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
     18    License for more details.
     19 
     20    You should have received a copy of the GNU General Public License
     21    along with this program; if not, write to the Free Software Foundation, Inc.,
     22    51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
     23 
     24 /* ??? Eventually more and more of this stuff can go to cpu-independent files.
     25    Keep that in mind.  */
     26 
     27 #include "sysdep.h"
     28 #include <stdio.h>
     29 #include "ansidecl.h"
     30 #include "dis-asm.h"
     31 #include "bfd.h"
     32 #include "symcat.h"
     33 #include "@prefix (at) -desc.h"
     34 #include "@prefix (at) -opc.h"
     35 #include "cgen/basic-modes.h"
     36 #include "opintl.h"
     37 #include "safe-ctype.h"
     38 
     39 #undef  min
     40 #define min(a,b) ((a) < (b) ? (a) : (b))
     41 #undef  max
     42 #define max(a,b) ((a) > (b) ? (a) : (b))
     43 
     44 /* Used by the ifield rtx function.  */
     45 #define FLD(f) (fields->f)
     46 
     47 static const char * insert_normal
     48   (CGEN_CPU_DESC, long, unsigned int, unsigned int, unsigned int,
     49    unsigned int, unsigned int, unsigned int, CGEN_INSN_BYTES_PTR);
     50 static const char * insert_insn_normal
     51   (CGEN_CPU_DESC, const CGEN_INSN *,
     52    CGEN_FIELDS *, CGEN_INSN_BYTES_PTR, bfd_vma);
     53 static int extract_normal
     54   (CGEN_CPU_DESC, CGEN_EXTRACT_INFO *, CGEN_INSN_INT,
     55    unsigned int, unsigned int, unsigned int, unsigned int,
     56    unsigned int, unsigned int, bfd_vma, long *);
     57 static int extract_insn_normal
     58   (CGEN_CPU_DESC, const CGEN_INSN *, CGEN_EXTRACT_INFO *,
     59    CGEN_INSN_INT, CGEN_FIELDS *, bfd_vma);
     60 #if CGEN_INT_INSN_P
     61 static void put_insn_int_value
     62   (CGEN_CPU_DESC, CGEN_INSN_BYTES_PTR, int, int, CGEN_INSN_INT);
     63 #endif
     64 #if ! CGEN_INT_INSN_P
     65 static CGEN_INLINE void insert_1
     66   (CGEN_CPU_DESC, unsigned long, int, int, int, unsigned char *);
     67 static CGEN_INLINE int fill_cache
     68   (CGEN_CPU_DESC, CGEN_EXTRACT_INFO *,  int, int, bfd_vma);
     69 static CGEN_INLINE long extract_1
     70   (CGEN_CPU_DESC, CGEN_EXTRACT_INFO *, int, int, int, unsigned char *, bfd_vma);
     71 #endif
     72 
     73 /* Operand insertion.  */
     75 
     76 #if ! CGEN_INT_INSN_P
     77 
     78 /* Subroutine of insert_normal.  */
     79 
     80 static CGEN_INLINE void
     81 insert_1 (CGEN_CPU_DESC cd,
     82 	  unsigned long value,
     83 	  int start,
     84 	  int length,
     85 	  int word_length,
     86 	  unsigned char *bufp)
     87 {
     88   unsigned long x, mask;
     89   int shift;
     90 
     91   x = cgen_get_insn_value (cd, bufp, word_length, cd->endian);
     92 
     93   /* Written this way to avoid undefined behaviour.  */
     94   mask = (1UL << (length - 1) << 1) - 1;
     95   if (CGEN_INSN_LSB0_P)
     96     shift = (start + 1) - length;
     97   else
     98     shift = (word_length - (start + length));
     99   x = (x & ~(mask << shift)) | ((value & mask) << shift);
    100 
    101   cgen_put_insn_value (cd, bufp, word_length, (bfd_vma) x, cd->endian);
    102 }
    103 
    104 #endif /* ! CGEN_INT_INSN_P */
    105 
    106 /* Default insertion routine.
    107 
    108    ATTRS is a mask of the boolean attributes.
    109    WORD_OFFSET is the offset in bits from the start of the insn of the value.
    110    WORD_LENGTH is the length of the word in bits in which the value resides.
    111    START is the starting bit number in the word, architecture origin.
    112    LENGTH is the length of VALUE in bits.
    113    TOTAL_LENGTH is the total length of the insn in bits.
    114 
    115    The result is an error message or NULL if success.  */
    116 
    117 /* ??? This duplicates functionality with bfd's howto table and
    118    bfd_install_relocation.  */
    119 /* ??? This doesn't handle bfd_vma's.  Create another function when
    120    necessary.  */
    121 
    122 static const char *
    123 insert_normal (CGEN_CPU_DESC cd,
    124 	       long value,
    125 	       unsigned int attrs,
    126 	       unsigned int word_offset,
    127 	       unsigned int start,
    128 	       unsigned int length,
    129 	       unsigned int word_length,
    130 	       unsigned int total_length,
    131 	       CGEN_INSN_BYTES_PTR buffer)
    132 {
    133   static char errbuf[100];
    134   unsigned long mask;
    135 
    136   /* If LENGTH is zero, this operand doesn't contribute to the value.  */
    137   if (length == 0)
    138     return NULL;
    139 
    140   /* Written this way to avoid undefined behaviour.  */
    141   mask = (1UL << (length - 1) << 1) - 1;
    142 
    143   if (word_length > 8 * sizeof (CGEN_INSN_INT))
    144     abort ();
    145 
    146   /* For architectures with insns smaller than the base-insn-bitsize,
    147      word_length may be too big.  */
    148   if (cd->min_insn_bitsize < cd->base_insn_bitsize)
    149     {
    150       if (word_offset == 0
    151 	  && word_length > total_length)
    152 	word_length = total_length;
    153     }
    154 
    155   /* Ensure VALUE will fit.  */
    156   if (CGEN_BOOL_ATTR (attrs, CGEN_IFLD_SIGN_OPT))
    157     {
    158       long minval = - (1UL << (length - 1));
    159       unsigned long maxval = mask;
    160 
    161       if ((value > 0 && (unsigned long) value > maxval)
    162 	  || value < minval)
    163 	{
    164 	  /* xgettext:c-format */
    165 	  sprintf (errbuf,
    166 		   _("operand out of range (%ld not between %ld and %lu)"),
    167 		   value, minval, maxval);
    168 	  return errbuf;
    169 	}
    170     }
    171   else if (! CGEN_BOOL_ATTR (attrs, CGEN_IFLD_SIGNED))
    172     {
    173       unsigned long maxval = mask;
    174       unsigned long val = (unsigned long) value;
    175 
    176       /* For hosts with a word size > 32 check to see if value has been sign
    177 	 extended beyond 32 bits.  If so then ignore these higher sign bits
    178 	 as the user is attempting to store a 32-bit signed value into an
    179 	 unsigned 32-bit field which is allowed.  */
    180       if (sizeof (unsigned long) > 4 && ((value >> 32) == -1))
    181 	val &= 0xFFFFFFFF;
    182 
    183       if (val > maxval)
    184 	{
    185 	  /* xgettext:c-format */
    186 	  sprintf (errbuf,
    187 		   _("operand out of range (0x%lx not between 0 and 0x%lx)"),
    188 		   val, maxval);
    189 	  return errbuf;
    190 	}
    191     }
    192   else
    193     {
    194       if (! cgen_signed_overflow_ok_p (cd))
    195 	{
    196 	  long minval = - (1UL << (length - 1));
    197 	  long maxval =   (1UL << (length - 1)) - 1;
    198 
    199 	  if (value < minval || value > maxval)
    200 	    {
    201 	      sprintf
    202 		/* xgettext:c-format */
    203 		(errbuf, _("operand out of range (%ld not between %ld and %ld)"),
    204 		 value, minval, maxval);
    205 	      return errbuf;
    206 	    }
    207 	}
    208     }
    209 
    210 #if CGEN_INT_INSN_P
    211 
    212   {
    213     int shift_within_word, shift_to_word, shift;
    214 
    215     /* How to shift the value to BIT0 of the word.  */
    216     shift_to_word = total_length - (word_offset + word_length);
    217 
    218     /* How to shift the value to the field within the word.  */
    219     if (CGEN_INSN_LSB0_P)
    220       shift_within_word = start + 1 - length;
    221     else
    222       shift_within_word = word_length - start - length;
    223 
    224     /* The total SHIFT, then mask in the value.  */
    225     shift = shift_to_word + shift_within_word;
    226     *buffer = (*buffer & ~(mask << shift)) | ((value & mask) << shift);
    227   }
    228 
    229 #else /* ! CGEN_INT_INSN_P */
    230 
    231   {
    232     unsigned char *bufp = (unsigned char *) buffer + word_offset / 8;
    233 
    234     insert_1 (cd, value, start, length, word_length, bufp);
    235   }
    236 
    237 #endif /* ! CGEN_INT_INSN_P */
    238 
    239   return NULL;
    240 }
    241 
    242 /* Default insn builder (insert handler).
    243    The instruction is recorded in CGEN_INT_INSN_P byte order (meaning
    244    that if CGEN_INSN_BYTES_PTR is an int * and thus, the value is
    245    recorded in host byte order, otherwise BUFFER is an array of bytes
    246    and the value is recorded in target byte order).
    247    The result is an error message or NULL if success.  */
    248 
    249 static const char *
    250 insert_insn_normal (CGEN_CPU_DESC cd,
    251 		    const CGEN_INSN * insn,
    252 		    CGEN_FIELDS * fields,
    253 		    CGEN_INSN_BYTES_PTR buffer,
    254 		    bfd_vma pc)
    255 {
    256   const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn);
    257   unsigned long value;
    258   const CGEN_SYNTAX_CHAR_TYPE * syn;
    259 
    260   CGEN_INIT_INSERT (cd);
    261   value = CGEN_INSN_BASE_VALUE (insn);
    262 
    263   /* If we're recording insns as numbers (rather than a string of bytes),
    264      target byte order handling is deferred until later.  */
    265 
    266 #if CGEN_INT_INSN_P
    267 
    268   put_insn_int_value (cd, buffer, cd->base_insn_bitsize,
    269 		      CGEN_FIELDS_BITSIZE (fields), value);
    270 
    271 #else
    272 
    273   cgen_put_insn_value (cd, buffer, min ((unsigned) cd->base_insn_bitsize,
    274                                         (unsigned) CGEN_FIELDS_BITSIZE (fields)),
    275 		       value, cd->insn_endian);
    276 
    277 #endif /* ! CGEN_INT_INSN_P */
    278 
    279   /* ??? It would be better to scan the format's fields.
    280      Still need to be able to insert a value based on the operand though;
    281      e.g. storing a branch displacement that got resolved later.
    282      Needs more thought first.  */
    283 
    284   for (syn = CGEN_SYNTAX_STRING (syntax); * syn; ++ syn)
    285     {
    286       const char *errmsg;
    287 
    288       if (CGEN_SYNTAX_CHAR_P (* syn))
    289 	continue;
    290 
    291       errmsg = (* cd->insert_operand) (cd, CGEN_SYNTAX_FIELD (*syn),
    292 				       fields, buffer, pc);
    293       if (errmsg)
    294 	return errmsg;
    295     }
    296 
    297   return NULL;
    298 }
    299 
    300 #if CGEN_INT_INSN_P
    301 /* Cover function to store an insn value into an integral insn.  Must go here
    302    because it needs <prefix>-desc.h for CGEN_INT_INSN_P.  */
    303 
    304 static void
    305 put_insn_int_value (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
    306 		    CGEN_INSN_BYTES_PTR buf,
    307 		    int length,
    308 		    int insn_length,
    309 		    CGEN_INSN_INT value)
    310 {
    311   /* For architectures with insns smaller than the base-insn-bitsize,
    312      length may be too big.  */
    313   if (length > insn_length)
    314     *buf = value;
    315   else
    316     {
    317       int shift = insn_length - length;
    318       /* Written this way to avoid undefined behaviour.  */
    319       CGEN_INSN_INT mask = length == 0 ? 0 : (1UL << (length - 1) << 1) - 1;
    320 
    321       *buf = (*buf & ~(mask << shift)) | ((value & mask) << shift);
    322     }
    323 }
    324 #endif
    325 
    326 /* Operand extraction.  */
    328 
    329 #if ! CGEN_INT_INSN_P
    330 
    331 /* Subroutine of extract_normal.
    332    Ensure sufficient bytes are cached in EX_INFO.
    333    OFFSET is the offset in bytes from the start of the insn of the value.
    334    BYTES is the length of the needed value.
    335    Returns 1 for success, 0 for failure.  */
    336 
    337 static CGEN_INLINE int
    338 fill_cache (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
    339 	    CGEN_EXTRACT_INFO *ex_info,
    340 	    int offset,
    341 	    int bytes,
    342 	    bfd_vma pc)
    343 {
    344   /* It's doubtful that the middle part has already been fetched so
    345      we don't optimize that case.  kiss.  */
    346   unsigned int mask;
    347   disassemble_info *info = (disassemble_info *) ex_info->dis_info;
    348 
    349   /* First do a quick check.  */
    350   mask = (1 << bytes) - 1;
    351   if (((ex_info->valid >> offset) & mask) == mask)
    352     return 1;
    353 
    354   /* Search for the first byte we need to read.  */
    355   for (mask = 1 << offset; bytes > 0; --bytes, ++offset, mask <<= 1)
    356     if (! (mask & ex_info->valid))
    357       break;
    358 
    359   if (bytes)
    360     {
    361       int status;
    362 
    363       pc += offset;
    364       status = (*info->read_memory_func)
    365 	(pc, ex_info->insn_bytes + offset, bytes, info);
    366 
    367       if (status != 0)
    368 	{
    369 	  (*info->memory_error_func) (status, pc, info);
    370 	  return 0;
    371 	}
    372 
    373       ex_info->valid |= ((1 << bytes) - 1) << offset;
    374     }
    375 
    376   return 1;
    377 }
    378 
    379 /* Subroutine of extract_normal.  */
    380 
    381 static CGEN_INLINE long
    382 extract_1 (CGEN_CPU_DESC cd,
    383 	   CGEN_EXTRACT_INFO *ex_info ATTRIBUTE_UNUSED,
    384 	   int start,
    385 	   int length,
    386 	   int word_length,
    387 	   unsigned char *bufp,
    388 	   bfd_vma pc ATTRIBUTE_UNUSED)
    389 {
    390   unsigned long x;
    391   int shift;
    392 
    393   x = cgen_get_insn_value (cd, bufp, word_length, cd->endian);
    394 
    395   if (CGEN_INSN_LSB0_P)
    396     shift = (start + 1) - length;
    397   else
    398     shift = (word_length - (start + length));
    399   return x >> shift;
    400 }
    401 
    402 #endif /* ! CGEN_INT_INSN_P */
    403 
    404 /* Default extraction routine.
    405 
    406    INSN_VALUE is the first base_insn_bitsize bits of the insn in host order,
    407    or sometimes less for cases like the m32r where the base insn size is 32
    408    but some insns are 16 bits.
    409    ATTRS is a mask of the boolean attributes.  We only need `SIGNED',
    410    but for generality we take a bitmask of all of them.
    411    WORD_OFFSET is the offset in bits from the start of the insn of the value.
    412    WORD_LENGTH is the length of the word in bits in which the value resides.
    413    START is the starting bit number in the word, architecture origin.
    414    LENGTH is the length of VALUE in bits.
    415    TOTAL_LENGTH is the total length of the insn in bits.
    416 
    417    Returns 1 for success, 0 for failure.  */
    418 
    419 /* ??? The return code isn't properly used.  wip.  */
    420 
    421 /* ??? This doesn't handle bfd_vma's.  Create another function when
    422    necessary.  */
    423 
    424 static int
    425 extract_normal (CGEN_CPU_DESC cd,
    426 #if ! CGEN_INT_INSN_P
    427 		CGEN_EXTRACT_INFO *ex_info,
    428 #else
    429 		CGEN_EXTRACT_INFO *ex_info ATTRIBUTE_UNUSED,
    430 #endif
    431 		CGEN_INSN_INT insn_value,
    432 		unsigned int attrs,
    433 		unsigned int word_offset,
    434 		unsigned int start,
    435 		unsigned int length,
    436 		unsigned int word_length,
    437 		unsigned int total_length,
    438 #if ! CGEN_INT_INSN_P
    439 		bfd_vma pc,
    440 #else
    441 		bfd_vma pc ATTRIBUTE_UNUSED,
    442 #endif
    443 		long *valuep)
    444 {
    445   long value, mask;
    446 
    447   /* If LENGTH is zero, this operand doesn't contribute to the value
    448      so give it a standard value of zero.  */
    449   if (length == 0)
    450     {
    451       *valuep = 0;
    452       return 1;
    453     }
    454 
    455   if (word_length > 8 * sizeof (CGEN_INSN_INT))
    456     abort ();
    457 
    458   /* For architectures with insns smaller than the insn-base-bitsize,
    459      word_length may be too big.  */
    460   if (cd->min_insn_bitsize < cd->base_insn_bitsize)
    461     {
    462       if (word_offset + word_length > total_length)
    463 	word_length = total_length - word_offset;
    464     }
    465 
    466   /* Does the value reside in INSN_VALUE, and at the right alignment?  */
    467 
    468   if (CGEN_INT_INSN_P || (word_offset == 0 && word_length == total_length))
    469     {
    470       if (CGEN_INSN_LSB0_P)
    471 	value = insn_value >> ((word_offset + start + 1) - length);
    472       else
    473 	value = insn_value >> (total_length - ( word_offset + start + length));
    474     }
    475 
    476 #if ! CGEN_INT_INSN_P
    477 
    478   else
    479     {
    480       unsigned char *bufp = ex_info->insn_bytes + word_offset / 8;
    481 
    482       if (word_length > 8 * sizeof (CGEN_INSN_INT))
    483 	abort ();
    484 
    485       if (fill_cache (cd, ex_info, word_offset / 8, word_length / 8, pc) == 0)
    486 	{
    487 	  *valuep = 0;
    488 	  return 0;
    489 	}
    490 
    491       value = extract_1 (cd, ex_info, start, length, word_length, bufp, pc);
    492     }
    493 
    494 #endif /* ! CGEN_INT_INSN_P */
    495 
    496   /* Written this way to avoid undefined behaviour.  */
    497   mask = (1UL << (length - 1) << 1) - 1;
    498 
    499   value &= mask;
    500   /* sign extend? */
    501   if (CGEN_BOOL_ATTR (attrs, CGEN_IFLD_SIGNED)
    502       && (value & (1UL << (length - 1))))
    503     value |= ~mask;
    504 
    505   *valuep = value;
    506 
    507   return 1;
    508 }
    509 
    510 /* Default insn extractor.
    511 
    512    INSN_VALUE is the first base_insn_bitsize bits, translated to host order.
    513    The extracted fields are stored in FIELDS.
    514    EX_INFO is used to handle reading variable length insns.
    515    Return the length of the insn in bits, or 0 if no match,
    516    or -1 if an error occurs fetching data (memory_error_func will have
    517    been called).  */
    518 
    519 static int
    520 extract_insn_normal (CGEN_CPU_DESC cd,
    521 		     const CGEN_INSN *insn,
    522 		     CGEN_EXTRACT_INFO *ex_info,
    523 		     CGEN_INSN_INT insn_value,
    524 		     CGEN_FIELDS *fields,
    525 		     bfd_vma pc)
    526 {
    527   const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn);
    528   const CGEN_SYNTAX_CHAR_TYPE *syn;
    529 
    530   CGEN_FIELDS_BITSIZE (fields) = CGEN_INSN_BITSIZE (insn);
    531 
    532   CGEN_INIT_EXTRACT (cd);
    533 
    534   for (syn = CGEN_SYNTAX_STRING (syntax); *syn; ++syn)
    535     {
    536       int length;
    537 
    538       if (CGEN_SYNTAX_CHAR_P (*syn))
    539 	continue;
    540 
    541       length = (* cd->extract_operand) (cd, CGEN_SYNTAX_FIELD (*syn),
    542 					ex_info, insn_value, fields, pc);
    543       if (length <= 0)
    544 	return length;
    545     }
    546 
    547   /* We recognized and successfully extracted this insn.  */
    548   return CGEN_INSN_BITSIZE (insn);
    549 }
    550 
    551 /* Machine generated code added here.  */
    553