xref: /netbsd-src/external/gpl3/binutils/dist/cpu/or1k.opc (revision 03f5171a7b36cbd3eb2581be737439f912b7121f)
1/* OpenRISC 1000 opcode support.  -*- C -*-
2   Copyright 2000-2014 Free Software Foundation, Inc.
3
4   Originally ontributed for OR32 by Red Hat Inc;
5
6   This file is part of the GNU Binutils.
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 3 of the License, or
11   (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program; if not, see <http://www.gnu.org/licenses/>. */
20
21/* This file is an addendum to or1k.cpu.  Heavy use of C code isn't
22   appropriate in .cpu files, so it resides here.  This especially applies
23   to assembly/disassembly where parsing/printing can be quite involved.
24   Such things aren't really part of the specification of the cpu, per se,
25   so .cpu files provide the general framework and .opc files handle the
26   nitty-gritty details as necessary.
27
28   Each section is delimited with start and end markers.
29
30   <arch>-opc.h additions use: "-- opc.h"
31   <arch>-opc.c additions use: "-- opc.c"
32   <arch>-asm.c additions use: "-- asm.c"
33   <arch>-dis.c additions use: "-- dis.c"
34   <arch>-ibd.h additions use: "-- ibd.h"  */
35
36/* -- opc.h */
37
38#undef  CGEN_DIS_HASH_SIZE
39#define CGEN_DIS_HASH_SIZE 256
40#undef  CGEN_DIS_HASH
41#define CGEN_DIS_HASH(buffer, value) (((unsigned char *) (buffer))[0] >> 2)
42
43/* Check applicability of instructions against machines.  */
44#define CGEN_VALIDATE_INSN_SUPPORTED
45
46extern int or1k_cgen_insn_supported (CGEN_CPU_DESC, const CGEN_INSN *);
47
48/* -- */
49
50/* -- opc.c */
51
52/* Special check to ensure that instruction exists for given machine.  */
53
54int
55or1k_cgen_insn_supported (CGEN_CPU_DESC cd, const CGEN_INSN *insn)
56{
57  int machs = CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_MACH);
58
59  /* No mach attribute?  Assume it's supported for all machs.  */
60  if (machs == 0)
61    return 1;
62
63  return ((machs & cd->machs) != 0);
64}
65
66/* -- */
67
68/* -- asm.c */
69
70static const char * MISSING_CLOSING_PARENTHESIS = N_("missing `)'");
71static const char * INVALID_STORE_RELOC = N_("relocation invalid for store");
72static const char * INVALID_RELOC_TYPE = N_("internal relocation type invalid");
73
74#define CGEN_VERBOSE_ASSEMBLER_ERRORS
75
76static const char *
77parse_disp26 (CGEN_CPU_DESC cd,
78	      const char ** strp,
79	      int opindex,
80	      int opinfo ATTRIBUTE_UNUSED,
81	      enum cgen_parse_operand_result * resultp,
82	      bfd_vma * valuep)
83{
84  const char *str = *strp;
85  const char *errmsg = NULL;
86  bfd_reloc_code_real_type reloc = BFD_RELOC_OR1K_REL_26;
87
88  if (strncasecmp (str, "plta(", 5) == 0)
89    {
90      *strp = str + 5;
91      reloc = BFD_RELOC_OR1K_PLTA26;
92    }
93  else if (strncasecmp (str, "plt(", 4) == 0)
94    {
95      *strp = str + 4;
96      reloc = BFD_RELOC_OR1K_PLT26;
97    }
98
99  errmsg = cgen_parse_address (cd, strp, opindex, reloc, resultp, valuep);
100
101  if (reloc != BFD_RELOC_OR1K_REL_26)
102    {
103      if (**strp != ')')
104	errmsg = MISSING_CLOSING_PARENTHESIS;
105      else
106	++*strp;
107    }
108
109  return errmsg;
110}
111
112static const char *
113parse_disp21 (CGEN_CPU_DESC cd,
114	      const char ** strp,
115	      int opindex,
116	      int opinfo ATTRIBUTE_UNUSED,
117	      enum cgen_parse_operand_result * resultp,
118	      bfd_vma * valuep)
119{
120  const char *str = *strp;
121  const char *errmsg = NULL;
122  bfd_reloc_code_real_type reloc = BFD_RELOC_OR1K_PCREL_PG21;
123
124  if (strncasecmp (str, "got(", 4) == 0)
125    {
126      *strp = str + 4;
127      reloc = BFD_RELOC_OR1K_GOT_PG21;
128    }
129  else if (strncasecmp (str, "tlsgd(", 6) == 0)
130    {
131      *strp = str + 6;
132      reloc = BFD_RELOC_OR1K_TLS_GD_PG21;
133    }
134  else if (strncasecmp (str, "tlsldm(", 7) == 0)
135    {
136      *strp = str + 7;
137      reloc = BFD_RELOC_OR1K_TLS_LDM_PG21;
138    }
139  else if (strncasecmp (str, "gottp(", 6) == 0)
140    {
141      *strp = str + 6;
142      reloc = BFD_RELOC_OR1K_TLS_IE_PG21;
143    }
144
145  errmsg = cgen_parse_address (cd, strp, opindex, reloc, resultp, valuep);
146
147  if (reloc != BFD_RELOC_OR1K_PCREL_PG21)
148    {
149      if (**strp != ')')
150	errmsg = MISSING_CLOSING_PARENTHESIS;
151      else
152	++*strp;
153    }
154
155  return errmsg;
156}
157
158enum or1k_rclass
159{
160  RCLASS_DIRECT   = 0,
161  RCLASS_GOT      = 1,
162  RCLASS_GOTPC    = 2,
163  RCLASS_GOTOFF   = 3,
164  RCLASS_TLSGD    = 4,
165  RCLASS_TLSLDM   = 5,
166  RCLASS_DTPOFF   = 6,
167  RCLASS_GOTTPOFF = 7,
168  RCLASS_TPOFF    = 8,
169};
170
171enum or1k_rtype
172{
173  RTYPE_LO = 0,
174  RTYPE_SLO = 1,
175  RTYPE_PO = 2,
176  RTYPE_SPO = 3,
177  RTYPE_HI = 4,
178  RTYPE_AHI = 5,
179};
180
181#define RCLASS_SHIFT 3
182#define RTYPE_MASK   7
183
184static const bfd_reloc_code_real_type or1k_imm16_relocs[][6] = {
185  { BFD_RELOC_LO16,
186    BFD_RELOC_OR1K_SLO16,
187    BFD_RELOC_OR1K_LO13,
188    BFD_RELOC_OR1K_SLO13,
189    BFD_RELOC_HI16,
190    BFD_RELOC_HI16_S, },
191  { BFD_RELOC_OR1K_GOT16,
192    BFD_RELOC_UNUSED,
193    BFD_RELOC_OR1K_GOT_LO13,
194    BFD_RELOC_UNUSED,
195    BFD_RELOC_UNUSED,
196    BFD_RELOC_OR1K_GOT_AHI16 },
197  { BFD_RELOC_OR1K_GOTPC_LO16,
198    BFD_RELOC_UNUSED,
199    BFD_RELOC_UNUSED,
200    BFD_RELOC_UNUSED,
201    BFD_RELOC_OR1K_GOTPC_HI16,
202    BFD_RELOC_UNUSED },
203  { BFD_RELOC_LO16_GOTOFF,
204    BFD_RELOC_OR1K_GOTOFF_SLO16,
205    BFD_RELOC_UNUSED,
206    BFD_RELOC_UNUSED,
207    BFD_RELOC_HI16_GOTOFF,
208    BFD_RELOC_HI16_S_GOTOFF },
209  { BFD_RELOC_OR1K_TLS_GD_LO16,
210    BFD_RELOC_UNUSED,
211    BFD_RELOC_OR1K_TLS_GD_LO13,
212    BFD_RELOC_UNUSED,
213    BFD_RELOC_OR1K_TLS_GD_HI16,
214    BFD_RELOC_UNUSED },
215  { BFD_RELOC_OR1K_TLS_LDM_LO16,
216    BFD_RELOC_UNUSED,
217    BFD_RELOC_OR1K_TLS_LDM_LO13,
218    BFD_RELOC_UNUSED,
219    BFD_RELOC_OR1K_TLS_LDM_HI16,
220    BFD_RELOC_UNUSED },
221  { BFD_RELOC_OR1K_TLS_LDO_LO16,
222    BFD_RELOC_UNUSED,
223    BFD_RELOC_UNUSED,
224    BFD_RELOC_UNUSED,
225    BFD_RELOC_OR1K_TLS_LDO_HI16,
226    BFD_RELOC_UNUSED },
227  { BFD_RELOC_OR1K_TLS_IE_LO16,
228    BFD_RELOC_UNUSED,
229    BFD_RELOC_OR1K_TLS_IE_LO13,
230    BFD_RELOC_UNUSED,
231    BFD_RELOC_OR1K_TLS_IE_HI16,
232    BFD_RELOC_OR1K_TLS_IE_AHI16 },
233  { BFD_RELOC_OR1K_TLS_LE_LO16,
234    BFD_RELOC_OR1K_TLS_LE_SLO16,
235    BFD_RELOC_UNUSED,
236    BFD_RELOC_UNUSED,
237    BFD_RELOC_OR1K_TLS_LE_HI16,
238    BFD_RELOC_OR1K_TLS_LE_AHI16 },
239};
240
241static int
242parse_reloc (const char **strp)
243{
244    const char *str = *strp;
245    enum or1k_rclass cls = RCLASS_DIRECT;
246    enum or1k_rtype typ;
247
248    if (strncasecmp (str, "got(", 4) == 0)
249      {
250	*strp = str + 4;
251	return (RCLASS_GOT << RCLASS_SHIFT) | RTYPE_LO;
252      }
253    if (strncasecmp (str, "gotpo(", 6) == 0)
254      {
255	*strp = str + 6;
256	return (RCLASS_GOT << RCLASS_SHIFT) | RTYPE_PO;
257      }
258    if (strncasecmp (str, "gottppo(", 8) == 0)
259      {
260	*strp = str + 8;
261	return (RCLASS_GOTTPOFF << RCLASS_SHIFT) | RTYPE_PO;
262      }
263
264    if (strncasecmp (str, "gotpc", 5) == 0)
265      {
266	str += 5;
267	cls = RCLASS_GOTPC;
268      }
269    else if (strncasecmp (str, "gotoff", 6) == 0)
270      {
271	str += 6;
272	cls = RCLASS_GOTOFF;
273      }
274    else if (strncasecmp (str, "tlsgd", 5) == 0)
275      {
276	str += 5;
277	cls = RCLASS_TLSGD;
278      }
279    else if (strncasecmp (str, "tlsldm", 6) == 0)
280      {
281	str += 6;
282	cls = RCLASS_TLSLDM;
283      }
284    else if (strncasecmp (str, "dtpoff", 6) == 0)
285      {
286	str += 6;
287	cls = RCLASS_DTPOFF;
288      }
289    else if (strncasecmp (str, "gottpoff", 8) == 0)
290      {
291	str += 8;
292	cls = RCLASS_GOTTPOFF;
293      }
294    else if (strncasecmp (str, "tpoff", 5) == 0)
295      {
296	str += 5;
297	cls = RCLASS_TPOFF;
298      }
299    else if (strncasecmp (str, "got", 3) == 0)
300      {
301	str += 3;
302	cls = RCLASS_GOT;
303      }
304
305    if (strncasecmp (str, "hi(", 3) == 0)
306      {
307	str += 3;
308	typ = RTYPE_HI;
309      }
310    else if (strncasecmp (str, "lo(", 3) == 0)
311      {
312	str += 3;
313	typ = RTYPE_LO;
314      }
315    else if (strncasecmp (str, "ha(", 3) == 0)
316      {
317	str += 3;
318	typ = RTYPE_AHI;
319      }
320    else if (strncasecmp (str, "po(", 3) == 0 && cls != RCLASS_GOTTPOFF)
321      {
322	str += 3;
323	typ = RTYPE_PO;
324      }
325    else
326      return -1;
327
328    *strp = str;
329    return (cls << RCLASS_SHIFT) | typ;
330}
331
332static const char *
333parse_imm16 (CGEN_CPU_DESC cd, const char **strp, int opindex,
334	     long *valuep, int splitp)
335{
336  const char *errmsg;
337  enum cgen_parse_operand_result result_type;
338  bfd_reloc_code_real_type reloc = BFD_RELOC_UNUSED;
339  enum or1k_rtype reloc_type;
340  int reloc_code;
341  bfd_vma ret;
342
343  if (**strp == '#')
344    ++*strp;
345
346  reloc_code = parse_reloc (strp);
347  reloc_type = reloc_code & RTYPE_MASK;
348  if (reloc_code >= 0)
349    {
350      enum or1k_rclass reloc_class = reloc_code >> RCLASS_SHIFT;
351      if (splitp)
352	{
353	  if ((reloc_type == RTYPE_LO || reloc_type == RTYPE_PO)
354	      && reloc_class != RCLASS_GOT)
355	    /* If split we or up the type to RTYPE_SLO or RTYPE_SPO.  */
356	    reloc_type |= 1;
357	  else
358	    return INVALID_STORE_RELOC;
359	}
360      reloc = or1k_imm16_relocs[reloc_class][reloc_type];
361    }
362
363  if (reloc != BFD_RELOC_UNUSED)
364    {
365      bfd_vma value;
366
367      errmsg = cgen_parse_address (cd, strp, opindex, reloc,
368				   &result_type, &value);
369      if (**strp != ')')
370	errmsg = MISSING_CLOSING_PARENTHESIS;
371      ++*strp;
372
373      ret = value;
374
375      if (errmsg == NULL && result_type == CGEN_PARSE_OPERAND_RESULT_NUMBER)
376	switch (reloc_type)
377	  {
378	  case RTYPE_AHI:
379	    ret += 0x8000;
380	    /* FALLTHRU */
381	  case RTYPE_HI:
382	    ret >>= 16;
383	    /* FALLTHRU */
384	  case RTYPE_LO:
385	  case RTYPE_SLO:
386	    ret &= 0xffff;
387	    ret = (ret ^ 0x8000) - 0x8000;
388	    break;
389	  case RTYPE_PO:
390	  case RTYPE_SPO:
391	    ret &= 0x1fff;
392	    break;
393	  default:
394	    errmsg = INVALID_RELOC_TYPE;
395	  }
396    }
397  else
398    {
399      long value;
400      errmsg = cgen_parse_signed_integer (cd, strp, opindex, &value);
401      ret = value;
402    }
403
404  if (errmsg == NULL)
405    *valuep = ret;
406
407  return errmsg;
408}
409
410static const char *
411parse_simm16 (CGEN_CPU_DESC cd, const char **strp, int opindex, long *valuep)
412{
413  return parse_imm16(cd, strp, opindex, (long *) valuep, 0);
414}
415
416static const char *
417parse_simm16_split (CGEN_CPU_DESC cd, const char **strp, int opindex,
418		    long *valuep)
419{
420  return parse_imm16(cd, strp, opindex, (long *) valuep, 1);
421}
422
423static const char *
424parse_uimm16 (CGEN_CPU_DESC cd, const char **strp, int opindex,
425	      unsigned long *valuep)
426{
427  const char *errmsg = parse_imm16(cd, strp, opindex, (long *) valuep, 0);
428  if (errmsg == NULL)
429    *valuep &= 0xffff;
430  return errmsg;
431}
432
433static const char *
434parse_uimm16_split (CGEN_CPU_DESC cd, const char **strp, int opindex,
435		    unsigned long *valuep)
436{
437  const char *errmsg = parse_imm16(cd, strp, opindex, (long *) valuep, 1);
438  if (errmsg == NULL)
439    *valuep &= 0xffff;
440  return errmsg;
441}
442
443/* Parse register pairs with syntax rA,rB to a flag + rA value.  */
444
445static const char *
446parse_regpair (CGEN_CPU_DESC cd, const char **strp,
447	       int opindex ATTRIBUTE_UNUSED, unsigned long *valuep)
448{
449  long reg1_index;
450  long reg2_index;
451  const char *errmsg;
452
453  /* The first part should just be a register.  */
454  errmsg = cgen_parse_keyword (cd, strp, &or1k_cgen_opval_h_gpr,
455			       &reg1_index);
456
457  /* If that worked skip the comma separator.  */
458  if (errmsg == NULL)
459    {
460      if (**strp == ',')
461	++*strp;
462      else
463	errmsg = "Unexpected character, expected ','";
464    }
465
466  /* If that worked the next part is just another register.  */
467  if (errmsg == NULL)
468    errmsg = cgen_parse_keyword (cd, strp, &or1k_cgen_opval_h_gpr,
469				 &reg2_index);
470
471  /* Validate the register pair is valid and create the output value.  */
472  if (errmsg == NULL)
473    {
474      int regoffset = reg2_index - reg1_index;
475
476      if (regoffset == 1 || regoffset == 2)
477	{
478	  unsigned short offsetmask;
479	  unsigned short value;
480
481	  offsetmask = ((regoffset == 2 ? 1 : 0) << 5);
482	  value = offsetmask | reg1_index;
483
484	  *valuep = value;
485	}
486      else
487	errmsg = "Invalid register pair, offset not 1 or 2.";
488    }
489
490  return errmsg;
491}
492
493/* -- */
494
495/* -- dis.c */
496
497static void
498print_regpair (CGEN_CPU_DESC cd ATTRIBUTE_UNUSED,
499	       void * dis_info,
500	       long value,
501	       unsigned int attrs ATTRIBUTE_UNUSED,
502	       bfd_vma pc ATTRIBUTE_UNUSED,
503	       int length ATTRIBUTE_UNUSED)
504{
505  disassemble_info *info = dis_info;
506  char reg1_index;
507  char reg2_index;
508
509  reg1_index = value & 0x1f;
510  reg2_index = reg1_index + ((value & (1 << 5)) ? 2 : 1);
511
512  (*info->fprintf_func) (info->stream, "r%d,r%d", reg1_index, reg2_index);
513}
514
515/* -- */
516
517/* -- ibd.h */
518
519/* -- */
520