Fix seg fault in linker when performing garbage collection on COFF based targets.
[external/binutils.git] / opcodes / ip2k-dis.c
1 /* Disassembler interface for targets using CGEN. -*- C -*-
2    CGEN: Cpu tools GENerator
3
4    THIS FILE IS MACHINE GENERATED WITH CGEN.
5    - the resultant file is machine generated, cgen-dis.in isn't
6
7    Copyright (C) 1996-2016 Free Software Foundation, Inc.
8
9    This file is part of libopcodes.
10
11    This library is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 3, or (at your option)
14    any later version.
15
16    It is distributed in the hope that it will be useful, but WITHOUT
17    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
19    License for more details.
20
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software Foundation, Inc.,
23    51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
24
25 /* ??? Eventually more and more of this stuff can go to cpu-independent files.
26    Keep that in mind.  */
27
28 #include "sysdep.h"
29 #include <stdio.h>
30 #include "ansidecl.h"
31 #include "dis-asm.h"
32 #include "bfd.h"
33 #include "symcat.h"
34 #include "libiberty.h"
35 #include "ip2k-desc.h"
36 #include "ip2k-opc.h"
37 #include "opintl.h"
38
39 /* Default text to print if an instruction isn't recognized.  */
40 #define UNKNOWN_INSN_MSG _("*unknown*")
41
42 static void print_normal
43   (CGEN_CPU_DESC, void *, long, unsigned int, bfd_vma, int);
44 static void print_address
45   (CGEN_CPU_DESC, void *, bfd_vma, unsigned int, bfd_vma, int) ATTRIBUTE_UNUSED;
46 static void print_keyword
47   (CGEN_CPU_DESC, void *, CGEN_KEYWORD *, long, unsigned int) ATTRIBUTE_UNUSED;
48 static void print_insn_normal
49   (CGEN_CPU_DESC, void *, const CGEN_INSN *, CGEN_FIELDS *, bfd_vma, int);
50 static int print_insn
51   (CGEN_CPU_DESC, bfd_vma,  disassemble_info *, bfd_byte *, unsigned);
52 static int default_print_insn
53   (CGEN_CPU_DESC, bfd_vma, disassemble_info *) ATTRIBUTE_UNUSED;
54 static int read_insn
55   (CGEN_CPU_DESC, bfd_vma, disassemble_info *, bfd_byte *, int, CGEN_EXTRACT_INFO *,
56    unsigned long *);
57 \f
58 /* -- disassembler routines inserted here.  */
59
60 /* -- dis.c */
61
62 static void
63 print_fr (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
64           void * dis_info,
65           long value,
66           unsigned int attrs ATTRIBUTE_UNUSED,
67           bfd_vma pc ATTRIBUTE_UNUSED,
68           int length ATTRIBUTE_UNUSED)
69 {
70   disassemble_info *info = (disassemble_info *) dis_info;
71   const CGEN_KEYWORD_ENTRY *ke;
72   extern CGEN_KEYWORD ip2k_cgen_opval_register_names;
73   long offsettest;
74   long offsetvalue;
75
76   if (value == 0) /* This is (IP).  */
77     {
78       (*info->fprintf_func) (info->stream, "%s", "(IP)");
79       return;
80     }
81
82   offsettest = value >> 7;
83   offsetvalue = value & 0x7F;
84
85   /* Check to see if first two bits are 10 -> (DP).  */
86   if (offsettest == 2)
87     {
88       if (offsetvalue == 0)
89         (*info->fprintf_func) (info->stream, "%s","(DP)");
90       else
91         (*info->fprintf_func) (info->stream, "$%lx%s", offsetvalue, "(DP)");
92       return;
93     }
94
95   /* Check to see if first two bits are 11 -> (SP).  */
96   if (offsettest == 3)
97     {
98       if (offsetvalue == 0)
99         (*info->fprintf_func) (info->stream, "%s", "(SP)");
100       else
101         (*info->fprintf_func) (info->stream, "$%lx%s", offsetvalue,"(SP)");
102       return;
103     }
104
105   /* Attempt to print as a register keyword.  */
106   ke = cgen_keyword_lookup_value (& ip2k_cgen_opval_register_names, value);
107
108   if (ke != NULL)
109     (*info->fprintf_func) (info->stream, "%s", ke->name);
110   else
111     /* Print as an address literal.  */
112     (*info->fprintf_func) (info->stream, "$%02lx", value);
113 }
114
115 static void
116 print_dollarhex (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
117                  void * dis_info,
118                  long value,
119                  unsigned int attrs ATTRIBUTE_UNUSED,
120                  bfd_vma pc ATTRIBUTE_UNUSED,
121                  int length ATTRIBUTE_UNUSED)
122 {
123   disassemble_info *info = (disassemble_info *) dis_info;
124
125   (*info->fprintf_func) (info->stream, "$%lx", value);
126 }
127
128 static void
129 print_dollarhex8 (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
130                   void * dis_info,
131                   long value,
132                   unsigned int attrs ATTRIBUTE_UNUSED,
133                   bfd_vma pc ATTRIBUTE_UNUSED,
134                   int length ATTRIBUTE_UNUSED)
135 {
136   disassemble_info *info = (disassemble_info *) dis_info;
137
138   (*info->fprintf_func) (info->stream, "$%02lx", value);
139 }
140
141 static void
142 print_dollarhex_addr16h (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
143                          void * dis_info,
144                          long value,
145                          unsigned int attrs ATTRIBUTE_UNUSED,
146                          bfd_vma pc ATTRIBUTE_UNUSED,
147                          int length ATTRIBUTE_UNUSED)
148 {
149   disassemble_info *info = (disassemble_info *) dis_info;
150
151   /* This is a loadh instruction. Shift the value to the left
152      by 8 bits so that disassembled code will reassemble properly.  */
153   value = ((value << 8) & 0xFF00);
154
155   (*info->fprintf_func) (info->stream, "$%04lx", value);
156 }
157
158 static void
159 print_dollarhex_addr16l (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
160                          void * dis_info,
161                          long value,
162                          unsigned int attrs ATTRIBUTE_UNUSED,
163                          bfd_vma pc ATTRIBUTE_UNUSED,
164                          int length ATTRIBUTE_UNUSED)
165 {
166   disassemble_info *info = (disassemble_info *) dis_info;
167
168   (*info->fprintf_func) (info->stream, "$%04lx", value);
169 }
170
171 static void
172 print_dollarhex_p (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
173                    void * dis_info,
174                    long value,
175                    unsigned int attrs ATTRIBUTE_UNUSED,
176                    bfd_vma pc ATTRIBUTE_UNUSED,
177                    int length ATTRIBUTE_UNUSED)
178 {
179   disassemble_info *info = (disassemble_info *) dis_info;
180
181   value = ((value << 14) & 0x1C000);
182   ;value = (value  & 0x1FFFF);
183   (*info->fprintf_func) (info->stream, "$%05lx", value);
184 }
185
186 static void
187 print_dollarhex_cj (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
188                     void * dis_info,
189                     long value,
190                     unsigned int attrs ATTRIBUTE_UNUSED,
191                     bfd_vma pc ATTRIBUTE_UNUSED,
192                     int length ATTRIBUTE_UNUSED)
193 {
194   disassemble_info *info = (disassemble_info *) dis_info;
195
196   value = ((value << 1) & 0x1FFFF);
197   (*info->fprintf_func) (info->stream, "$%05lx", value);
198 }
199
200 static void
201 print_decimal (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
202                void * dis_info,
203                long value,
204                unsigned int attrs ATTRIBUTE_UNUSED,
205                bfd_vma pc ATTRIBUTE_UNUSED,
206                int length ATTRIBUTE_UNUSED)
207 {
208   disassemble_info *info = (disassemble_info *) dis_info;
209
210   (*info->fprintf_func) (info->stream, "%ld", value);
211 }
212
213
214
215 /* -- */
216
217 void ip2k_cgen_print_operand
218   (CGEN_CPU_DESC, int, PTR, CGEN_FIELDS *, void const *, bfd_vma, int);
219
220 /* Main entry point for printing operands.
221    XINFO is a `void *' and not a `disassemble_info *' to not put a requirement
222    of dis-asm.h on cgen.h.
223
224    This function is basically just a big switch statement.  Earlier versions
225    used tables to look up the function to use, but
226    - if the table contains both assembler and disassembler functions then
227      the disassembler contains much of the assembler and vice-versa,
228    - there's a lot of inlining possibilities as things grow,
229    - using a switch statement avoids the function call overhead.
230
231    This function could be moved into `print_insn_normal', but keeping it
232    separate makes clear the interface between `print_insn_normal' and each of
233    the handlers.  */
234
235 void
236 ip2k_cgen_print_operand (CGEN_CPU_DESC cd,
237                            int opindex,
238                            void * xinfo,
239                            CGEN_FIELDS *fields,
240                            void const *attrs ATTRIBUTE_UNUSED,
241                            bfd_vma pc,
242                            int length)
243 {
244   disassemble_info *info = (disassemble_info *) xinfo;
245
246   switch (opindex)
247     {
248     case IP2K_OPERAND_ADDR16CJP :
249       print_dollarhex_cj (cd, info, fields->f_addr16cjp, 0|(1<<CGEN_OPERAND_ABS_ADDR), pc, length);
250       break;
251     case IP2K_OPERAND_ADDR16H :
252       print_dollarhex_addr16h (cd, info, fields->f_imm8, 0, pc, length);
253       break;
254     case IP2K_OPERAND_ADDR16L :
255       print_dollarhex_addr16l (cd, info, fields->f_imm8, 0, pc, length);
256       break;
257     case IP2K_OPERAND_ADDR16P :
258       print_dollarhex_p (cd, info, fields->f_page3, 0, pc, length);
259       break;
260     case IP2K_OPERAND_BITNO :
261       print_decimal (cd, info, fields->f_bitno, 0, pc, length);
262       break;
263     case IP2K_OPERAND_CBIT :
264       print_normal (cd, info, 0, 0, pc, length);
265       break;
266     case IP2K_OPERAND_DCBIT :
267       print_normal (cd, info, 0, 0, pc, length);
268       break;
269     case IP2K_OPERAND_FR :
270       print_fr (cd, info, fields->f_reg, 0|(1<<CGEN_OPERAND_ABS_ADDR), pc, length);
271       break;
272     case IP2K_OPERAND_LIT8 :
273       print_dollarhex8 (cd, info, fields->f_imm8, 0|(1<<CGEN_OPERAND_SIGNED), pc, length);
274       break;
275     case IP2K_OPERAND_PABITS :
276       print_normal (cd, info, 0, 0, pc, length);
277       break;
278     case IP2K_OPERAND_RETI3 :
279       print_dollarhex (cd, info, fields->f_reti3, 0, pc, length);
280       break;
281     case IP2K_OPERAND_ZBIT :
282       print_normal (cd, info, 0, 0, pc, length);
283       break;
284
285     default :
286       /* xgettext:c-format */
287       fprintf (stderr, _("Unrecognized field %d while printing insn.\n"),
288                opindex);
289     abort ();
290   }
291 }
292
293 cgen_print_fn * const ip2k_cgen_print_handlers[] =
294 {
295   print_insn_normal,
296 };
297
298
299 void
300 ip2k_cgen_init_dis (CGEN_CPU_DESC cd)
301 {
302   ip2k_cgen_init_opcode_table (cd);
303   ip2k_cgen_init_ibld_table (cd);
304   cd->print_handlers = & ip2k_cgen_print_handlers[0];
305   cd->print_operand = ip2k_cgen_print_operand;
306 }
307
308 \f
309 /* Default print handler.  */
310
311 static void
312 print_normal (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
313               void *dis_info,
314               long value,
315               unsigned int attrs,
316               bfd_vma pc ATTRIBUTE_UNUSED,
317               int length ATTRIBUTE_UNUSED)
318 {
319   disassemble_info *info = (disassemble_info *) dis_info;
320
321   /* Print the operand as directed by the attributes.  */
322   if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY))
323     ; /* nothing to do */
324   else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SIGNED))
325     (*info->fprintf_func) (info->stream, "%ld", value);
326   else
327     (*info->fprintf_func) (info->stream, "0x%lx", value);
328 }
329
330 /* Default address handler.  */
331
332 static void
333 print_address (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
334                void *dis_info,
335                bfd_vma value,
336                unsigned int attrs,
337                bfd_vma pc ATTRIBUTE_UNUSED,
338                int length ATTRIBUTE_UNUSED)
339 {
340   disassemble_info *info = (disassemble_info *) dis_info;
341
342   /* Print the operand as directed by the attributes.  */
343   if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SEM_ONLY))
344     ; /* Nothing to do.  */
345   else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_PCREL_ADDR))
346     (*info->print_address_func) (value, info);
347   else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_ABS_ADDR))
348     (*info->print_address_func) (value, info);
349   else if (CGEN_BOOL_ATTR (attrs, CGEN_OPERAND_SIGNED))
350     (*info->fprintf_func) (info->stream, "%ld", (long) value);
351   else
352     (*info->fprintf_func) (info->stream, "0x%lx", (long) value);
353 }
354
355 /* Keyword print handler.  */
356
357 static void
358 print_keyword (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
359                void *dis_info,
360                CGEN_KEYWORD *keyword_table,
361                long value,
362                unsigned int attrs ATTRIBUTE_UNUSED)
363 {
364   disassemble_info *info = (disassemble_info *) dis_info;
365   const CGEN_KEYWORD_ENTRY *ke;
366
367   ke = cgen_keyword_lookup_value (keyword_table, value);
368   if (ke != NULL)
369     (*info->fprintf_func) (info->stream, "%s", ke->name);
370   else
371     (*info->fprintf_func) (info->stream, "???");
372 }
373 \f
374 /* Default insn printer.
375
376    DIS_INFO is defined as `void *' so the disassembler needn't know anything
377    about disassemble_info.  */
378
379 static void
380 print_insn_normal (CGEN_CPU_DESC cd,
381                    void *dis_info,
382                    const CGEN_INSN *insn,
383                    CGEN_FIELDS *fields,
384                    bfd_vma pc,
385                    int length)
386 {
387   const CGEN_SYNTAX *syntax = CGEN_INSN_SYNTAX (insn);
388   disassemble_info *info = (disassemble_info *) dis_info;
389   const CGEN_SYNTAX_CHAR_TYPE *syn;
390
391   CGEN_INIT_PRINT (cd);
392
393   for (syn = CGEN_SYNTAX_STRING (syntax); *syn; ++syn)
394     {
395       if (CGEN_SYNTAX_MNEMONIC_P (*syn))
396         {
397           (*info->fprintf_func) (info->stream, "%s", CGEN_INSN_MNEMONIC (insn));
398           continue;
399         }
400       if (CGEN_SYNTAX_CHAR_P (*syn))
401         {
402           (*info->fprintf_func) (info->stream, "%c", CGEN_SYNTAX_CHAR (*syn));
403           continue;
404         }
405
406       /* We have an operand.  */
407       ip2k_cgen_print_operand (cd, CGEN_SYNTAX_FIELD (*syn), info,
408                                  fields, CGEN_INSN_ATTRS (insn), pc, length);
409     }
410 }
411 \f
412 /* Subroutine of print_insn. Reads an insn into the given buffers and updates
413    the extract info.
414    Returns 0 if all is well, non-zero otherwise.  */
415
416 static int
417 read_insn (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
418            bfd_vma pc,
419            disassemble_info *info,
420            bfd_byte *buf,
421            int buflen,
422            CGEN_EXTRACT_INFO *ex_info,
423            unsigned long *insn_value)
424 {
425   int status = (*info->read_memory_func) (pc, buf, buflen, info);
426
427   if (status != 0)
428     {
429       (*info->memory_error_func) (status, pc, info);
430       return -1;
431     }
432
433   ex_info->dis_info = info;
434   ex_info->valid = (1 << buflen) - 1;
435   ex_info->insn_bytes = buf;
436
437   *insn_value = bfd_get_bits (buf, buflen * 8, info->endian == BFD_ENDIAN_BIG);
438   return 0;
439 }
440
441 /* Utility to print an insn.
442    BUF is the base part of the insn, target byte order, BUFLEN bytes long.
443    The result is the size of the insn in bytes or zero for an unknown insn
444    or -1 if an error occurs fetching data (memory_error_func will have
445    been called).  */
446
447 static int
448 print_insn (CGEN_CPU_DESC cd,
449             bfd_vma pc,
450             disassemble_info *info,
451             bfd_byte *buf,
452             unsigned int buflen)
453 {
454   CGEN_INSN_INT insn_value;
455   const CGEN_INSN_LIST *insn_list;
456   CGEN_EXTRACT_INFO ex_info;
457   int basesize;
458
459   /* Extract base part of instruction, just in case CGEN_DIS_* uses it. */
460   basesize = cd->base_insn_bitsize < buflen * 8 ?
461                                      cd->base_insn_bitsize : buflen * 8;
462   insn_value = cgen_get_insn_value (cd, buf, basesize);
463
464
465   /* Fill in ex_info fields like read_insn would.  Don't actually call
466      read_insn, since the incoming buffer is already read (and possibly
467      modified a la m32r).  */
468   ex_info.valid = (1 << buflen) - 1;
469   ex_info.dis_info = info;
470   ex_info.insn_bytes = buf;
471
472   /* The instructions are stored in hash lists.
473      Pick the first one and keep trying until we find the right one.  */
474
475   insn_list = CGEN_DIS_LOOKUP_INSN (cd, (char *) buf, insn_value);
476   while (insn_list != NULL)
477     {
478       const CGEN_INSN *insn = insn_list->insn;
479       CGEN_FIELDS fields;
480       int length;
481       unsigned long insn_value_cropped;
482
483 #ifdef CGEN_VALIDATE_INSN_SUPPORTED
484       /* Not needed as insn shouldn't be in hash lists if not supported.  */
485       /* Supported by this cpu?  */
486       if (! ip2k_cgen_insn_supported (cd, insn))
487         {
488           insn_list = CGEN_DIS_NEXT_INSN (insn_list);
489           continue;
490         }
491 #endif
492
493       /* Basic bit mask must be correct.  */
494       /* ??? May wish to allow target to defer this check until the extract
495          handler.  */
496
497       /* Base size may exceed this instruction's size.  Extract the
498          relevant part from the buffer. */
499       if ((unsigned) (CGEN_INSN_BITSIZE (insn) / 8) < buflen &&
500           (unsigned) (CGEN_INSN_BITSIZE (insn) / 8) <= sizeof (unsigned long))
501         insn_value_cropped = bfd_get_bits (buf, CGEN_INSN_BITSIZE (insn),
502                                            info->endian == BFD_ENDIAN_BIG);
503       else
504         insn_value_cropped = insn_value;
505
506       if ((insn_value_cropped & CGEN_INSN_BASE_MASK (insn))
507           == CGEN_INSN_BASE_VALUE (insn))
508         {
509           /* Printing is handled in two passes.  The first pass parses the
510              machine insn and extracts the fields.  The second pass prints
511              them.  */
512
513           /* Make sure the entire insn is loaded into insn_value, if it
514              can fit.  */
515           if (((unsigned) CGEN_INSN_BITSIZE (insn) > cd->base_insn_bitsize) &&
516               (unsigned) (CGEN_INSN_BITSIZE (insn) / 8) <= sizeof (unsigned long))
517             {
518               unsigned long full_insn_value;
519               int rc = read_insn (cd, pc, info, buf,
520                                   CGEN_INSN_BITSIZE (insn) / 8,
521                                   & ex_info, & full_insn_value);
522               if (rc != 0)
523                 return rc;
524               length = CGEN_EXTRACT_FN (cd, insn)
525                 (cd, insn, &ex_info, full_insn_value, &fields, pc);
526             }
527           else
528             length = CGEN_EXTRACT_FN (cd, insn)
529               (cd, insn, &ex_info, insn_value_cropped, &fields, pc);
530
531           /* Length < 0 -> error.  */
532           if (length < 0)
533             return length;
534           if (length > 0)
535             {
536               CGEN_PRINT_FN (cd, insn) (cd, info, insn, &fields, pc, length);
537               /* Length is in bits, result is in bytes.  */
538               return length / 8;
539             }
540         }
541
542       insn_list = CGEN_DIS_NEXT_INSN (insn_list);
543     }
544
545   return 0;
546 }
547
548 /* Default value for CGEN_PRINT_INSN.
549    The result is the size of the insn in bytes or zero for an unknown insn
550    or -1 if an error occured fetching bytes.  */
551
552 #ifndef CGEN_PRINT_INSN
553 #define CGEN_PRINT_INSN default_print_insn
554 #endif
555
556 static int
557 default_print_insn (CGEN_CPU_DESC cd, bfd_vma pc, disassemble_info *info)
558 {
559   bfd_byte buf[CGEN_MAX_INSN_SIZE];
560   int buflen;
561   int status;
562
563   /* Attempt to read the base part of the insn.  */
564   buflen = cd->base_insn_bitsize / 8;
565   status = (*info->read_memory_func) (pc, buf, buflen, info);
566
567   /* Try again with the minimum part, if min < base.  */
568   if (status != 0 && (cd->min_insn_bitsize < cd->base_insn_bitsize))
569     {
570       buflen = cd->min_insn_bitsize / 8;
571       status = (*info->read_memory_func) (pc, buf, buflen, info);
572     }
573
574   if (status != 0)
575     {
576       (*info->memory_error_func) (status, pc, info);
577       return -1;
578     }
579
580   return print_insn (cd, pc, info, buf, buflen);
581 }
582
583 /* Main entry point.
584    Print one instruction from PC on INFO->STREAM.
585    Return the size of the instruction (in bytes).  */
586
587 typedef struct cpu_desc_list
588 {
589   struct cpu_desc_list *next;
590   CGEN_BITSET *isa;
591   int mach;
592   int endian;
593   CGEN_CPU_DESC cd;
594 } cpu_desc_list;
595
596 int
597 print_insn_ip2k (bfd_vma pc, disassemble_info *info)
598 {
599   static cpu_desc_list *cd_list = 0;
600   cpu_desc_list *cl = 0;
601   static CGEN_CPU_DESC cd = 0;
602   static CGEN_BITSET *prev_isa;
603   static int prev_mach;
604   static int prev_endian;
605   int length;
606   CGEN_BITSET *isa;
607   int mach;
608   int endian = (info->endian == BFD_ENDIAN_BIG
609                 ? CGEN_ENDIAN_BIG
610                 : CGEN_ENDIAN_LITTLE);
611   enum bfd_architecture arch;
612
613   /* ??? gdb will set mach but leave the architecture as "unknown" */
614 #ifndef CGEN_BFD_ARCH
615 #define CGEN_BFD_ARCH bfd_arch_ip2k
616 #endif
617   arch = info->arch;
618   if (arch == bfd_arch_unknown)
619     arch = CGEN_BFD_ARCH;
620
621   /* There's no standard way to compute the machine or isa number
622      so we leave it to the target.  */
623 #ifdef CGEN_COMPUTE_MACH
624   mach = CGEN_COMPUTE_MACH (info);
625 #else
626   mach = info->mach;
627 #endif
628
629 #ifdef CGEN_COMPUTE_ISA
630   {
631     static CGEN_BITSET *permanent_isa;
632
633     if (!permanent_isa)
634       permanent_isa = cgen_bitset_create (MAX_ISAS);
635     isa = permanent_isa;
636     cgen_bitset_clear (isa);
637     cgen_bitset_add (isa, CGEN_COMPUTE_ISA (info));
638   }
639 #else
640   isa = info->insn_sets;
641 #endif
642
643   /* If we've switched cpu's, try to find a handle we've used before */
644   if (cd
645       && (cgen_bitset_compare (isa, prev_isa) != 0
646           || mach != prev_mach
647           || endian != prev_endian))
648     {
649       cd = 0;
650       for (cl = cd_list; cl; cl = cl->next)
651         {
652           if (cgen_bitset_compare (cl->isa, isa) == 0 &&
653               cl->mach == mach &&
654               cl->endian == endian)
655             {
656               cd = cl->cd;
657               prev_isa = cd->isas;
658               break;
659             }
660         }
661     }
662
663   /* If we haven't initialized yet, initialize the opcode table.  */
664   if (! cd)
665     {
666       const bfd_arch_info_type *arch_type = bfd_lookup_arch (arch, mach);
667       const char *mach_name;
668
669       if (!arch_type)
670         abort ();
671       mach_name = arch_type->printable_name;
672
673       prev_isa = cgen_bitset_copy (isa);
674       prev_mach = mach;
675       prev_endian = endian;
676       cd = ip2k_cgen_cpu_open (CGEN_CPU_OPEN_ISAS, prev_isa,
677                                  CGEN_CPU_OPEN_BFDMACH, mach_name,
678                                  CGEN_CPU_OPEN_ENDIAN, prev_endian,
679                                  CGEN_CPU_OPEN_END);
680       if (!cd)
681         abort ();
682
683       /* Save this away for future reference.  */
684       cl = xmalloc (sizeof (struct cpu_desc_list));
685       cl->cd = cd;
686       cl->isa = prev_isa;
687       cl->mach = mach;
688       cl->endian = endian;
689       cl->next = cd_list;
690       cd_list = cl;
691
692       ip2k_cgen_init_dis (cd);
693     }
694
695   /* We try to have as much common code as possible.
696      But at this point some targets need to take over.  */
697   /* ??? Some targets may need a hook elsewhere.  Try to avoid this,
698      but if not possible try to move this hook elsewhere rather than
699      have two hooks.  */
700   length = CGEN_PRINT_INSN (cd, pc, info);
701   if (length > 0)
702     return length;
703   if (length < 0)
704     return -1;
705
706   (*info->fprintf_func) (info->stream, UNKNOWN_INSN_MSG);
707   return cd->default_insn_bitsize / 8;
708 }