1/* tc-mt.c -- Assembler for the Morpho Technologies mt .
2   Copyright (C) 2005-2017 Free Software Foundation, Inc.
3
4   This file is part of GAS, the GNU Assembler.
5
6   GAS is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 3, or (at your option)
9   any later version.
10
11   GAS is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with GAS; see the file COPYING.  If not, write to
18   the Free Software Foundation, 59 Temple Place - Suite 330,
19   Boston, MA 02111-1307, USA.  */
20
21#include "as.h"
22#include "dwarf2dbg.h"
23#include "subsegs.h"
24#include "symcat.h"
25#include "opcodes/mt-desc.h"
26#include "opcodes/mt-opc.h"
27#include "cgen.h"
28#include "elf/common.h"
29#include "elf/mt.h"
30
31/* Structure to hold all of the different components
32   describing an individual instruction.  */
33typedef struct
34{
35  const CGEN_INSN *	insn;
36  const CGEN_INSN *	orig_insn;
37  CGEN_FIELDS		fields;
38#if CGEN_INT_INSN_P
39  CGEN_INSN_INT         buffer [1];
40#define INSN_VALUE(buf) (*(buf))
41#else
42  unsigned char         buffer [CGEN_MAX_INSN_SIZE];
43#define INSN_VALUE(buf) (buf)
44#endif
45  char *		addr;
46  fragS *		frag;
47  int                   num_fixups;
48  fixS *                fixups [GAS_CGEN_MAX_FIXUPS];
49  int                   indices [MAX_OPERAND_INSTANCES];
50}
51mt_insn;
52
53
54const char comment_chars[]        = ";";
55const char line_comment_chars[]   = "#";
56const char line_separator_chars[] = "";
57const char EXP_CHARS[]            = "eE";
58const char FLT_CHARS[]            = "dD";
59
60/* The target specific pseudo-ops which we support.  */
61const pseudo_typeS md_pseudo_table[] =
62{
63    { "word",   cons,                   4 },
64    { NULL, 	NULL,			0 }
65};
66
67
68
69static int no_scheduling_restrictions = 0;
70
71struct option md_longopts[] =
72{
73#define OPTION_NO_SCHED_REST	(OPTION_MD_BASE)
74  { "nosched",	   no_argument, NULL, OPTION_NO_SCHED_REST },
75#define OPTION_MARCH		(OPTION_MD_BASE + 1)
76  { "march", required_argument, NULL, OPTION_MARCH},
77  { NULL,	   no_argument, NULL, 0 },
78};
79size_t md_longopts_size = sizeof (md_longopts);
80
81const char * md_shortopts = "";
82
83/* Mach selected from command line.  */
84static int mt_mach = bfd_mach_ms1;
85static unsigned mt_mach_bitmask = 1 << MACH_MS1;
86
87/* Flags to set in the elf header */
88static flagword mt_flags = EF_MT_CPU_MRISC;
89
90/* The architecture to use.  */
91enum mt_architectures
92  {
93    ms1_64_001,
94    ms1_16_002,
95    ms1_16_003,
96    ms2
97  };
98
99/* MT architecture we are using for this output file.  */
100static enum mt_architectures mt_arch = ms1_16_002;
101
102int
103md_parse_option (int c ATTRIBUTE_UNUSED, const char * arg)
104{
105  switch (c)
106    {
107    case OPTION_MARCH:
108      if (strcmp (arg, "ms1-64-001") == 0)
109 	{
110 	  mt_flags = (mt_flags & ~EF_MT_CPU_MASK) | EF_MT_CPU_MRISC;
111 	  mt_mach = bfd_mach_ms1;
112 	  mt_mach_bitmask = 1 << MACH_MS1;
113 	  mt_arch = ms1_64_001;
114 	}
115      else if (strcmp (arg, "ms1-16-002") == 0)
116 	{
117 	  mt_flags = (mt_flags & ~EF_MT_CPU_MASK) | EF_MT_CPU_MRISC;
118 	  mt_mach = bfd_mach_ms1;
119 	  mt_mach_bitmask = 1 << MACH_MS1;
120 	  mt_arch = ms1_16_002;
121 	}
122      else if (strcmp (arg, "ms1-16-003") == 0)
123 	{
124 	  mt_flags = (mt_flags & ~EF_MT_CPU_MASK) | EF_MT_CPU_MRISC2;
125 	  mt_mach = bfd_mach_mrisc2;
126 	  mt_mach_bitmask = 1 << MACH_MS1_003;
127 	  mt_arch = ms1_16_003;
128 	}
129      else if (strcmp (arg, "ms2") == 0)
130 	{
131 	  mt_flags = (mt_flags & ~EF_MT_CPU_MASK) | EF_MT_CPU_MS2;
132 	  mt_mach = bfd_mach_mrisc2;
133 	  mt_mach_bitmask = 1 << MACH_MS2;
134 	  mt_arch = ms2;
135 	}
136      break;
137    case OPTION_NO_SCHED_REST:
138      no_scheduling_restrictions = 1;
139      break;
140    default:
141      return 0;
142    }
143
144  return 1;
145}
146
147
148void
149md_show_usage (FILE * stream)
150{
151  fprintf (stream, _("MT specific command line options:\n"));
152  fprintf (stream, _("  -march=ms1-64-001         allow ms1-64-001 instructions\n"));
153  fprintf (stream, _("  -march=ms1-16-002         allow ms1-16-002 instructions (default)\n"));
154  fprintf (stream, _("  -march=ms1-16-003         allow ms1-16-003 instructions\n"));
155  fprintf (stream, _("  -march=ms2                allow ms2 instructions \n"));
156  fprintf (stream, _("  -nosched                  disable scheduling restrictions\n"));
157}
158
159
160void
161md_begin (void)
162{
163  /* Initialize the `cgen' interface.  */
164
165  /* Set the machine number and endian.  */
166  gas_cgen_cpu_desc = mt_cgen_cpu_open (CGEN_CPU_OPEN_MACHS, mt_mach_bitmask,
167					CGEN_CPU_OPEN_ENDIAN,
168					CGEN_ENDIAN_BIG,
169					CGEN_CPU_OPEN_END);
170  mt_cgen_init_asm (gas_cgen_cpu_desc);
171
172  /* This is a callback from cgen to gas to parse operands.  */
173  cgen_set_parse_operand_fn (gas_cgen_cpu_desc, gas_cgen_parse_operand);
174
175  /* Set the ELF flags if desired. */
176  if (mt_flags)
177    bfd_set_private_flags (stdoutput, mt_flags);
178
179  /* Set the machine type.  */
180  bfd_default_set_arch_mach (stdoutput, bfd_arch_mt, mt_mach);
181}
182
183void
184md_assemble (char * str)
185{
186  static long delayed_load_register = 0;
187  static long prev_delayed_load_register = 0;
188  static int last_insn_had_delay_slot = 0;
189  static int last_insn_in_noncond_delay_slot = 0;
190  static int last_insn_has_load_delay = 0;
191  static int last_insn_was_memory_access = 0;
192  static int last_insn_was_io_insn = 0;
193  static int last_insn_was_arithmetic_or_logic = 0;
194  static int last_insn_was_branch_insn = 0;
195  static int last_insn_was_conditional_branch_insn = 0;
196
197  mt_insn insn;
198  char * errmsg;
199
200  /* Initialize GAS's cgen interface for a new instruction.  */
201  gas_cgen_init_parse ();
202
203  insn.insn = mt_cgen_assemble_insn
204      (gas_cgen_cpu_desc, str, & insn.fields, insn.buffer, & errmsg);
205
206  if (!insn.insn)
207    {
208      as_bad ("%s", errmsg);
209      return;
210    }
211
212  /* Doesn't really matter what we pass for RELAX_P here.  */
213  gas_cgen_finish_insn (insn.insn, insn.buffer,
214			CGEN_FIELDS_BITSIZE (& insn.fields), 1, NULL);
215
216
217  /* Handle Scheduling Restrictions.  */
218  if (!no_scheduling_restrictions)
219    {
220      /* Detect consecutive Memory Accesses.  */
221      if (last_insn_was_memory_access
222	  && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_MEMORY_ACCESS)
223	  && mt_mach == ms1_64_001)
224	as_warn (_("instruction %s may not follow another memory access instruction."),
225		 CGEN_INSN_NAME (insn.insn));
226
227      /* Detect consecutive I/O Instructions.  */
228      else if (last_insn_was_io_insn
229	       && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_IO_INSN))
230	as_warn (_("instruction %s may not follow another I/O instruction."),
231		 CGEN_INSN_NAME (insn.insn));
232
233      /* Detect consecutive branch instructions.  */
234      else if (last_insn_was_branch_insn
235	       && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_BR_INSN))
236	as_warn (_("%s may not occupy the delay slot of another branch insn."),
237		 CGEN_INSN_NAME (insn.insn));
238
239      /* Detect data dependencies on delayed loads: memory and input insns.  */
240      if (last_insn_has_load_delay && delayed_load_register)
241	{
242	  if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR1)
243	      && insn.fields.f_sr1 == delayed_load_register)
244	    as_warn (_("operand references R%ld of previous load."),
245		     insn.fields.f_sr1);
246
247	  if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR2)
248	      && insn.fields.f_sr2 == delayed_load_register)
249	    as_warn (_("operand references R%ld of previous load."),
250		     insn.fields.f_sr2);
251	}
252
253      /* Detect JAL/RETI hazard */
254      if (mt_mach == ms2
255	  && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_JAL_HAZARD))
256	{
257	  if ((CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR1)
258	       && insn.fields.f_sr1 == delayed_load_register)
259	      || (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR2)
260		  && insn.fields.f_sr2 == delayed_load_register))
261	    as_warn (_("operand references R%ld of previous instruction."),
262		     delayed_load_register);
263	  else if ((CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR1)
264		    && insn.fields.f_sr1 == prev_delayed_load_register)
265		   || (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR2)
266		       && insn.fields.f_sr2 == prev_delayed_load_register))
267	    as_warn (_("operand references R%ld of instruction before previous."),
268		     prev_delayed_load_register);
269	}
270
271      /* Detect data dependency between conditional branch instruction
272         and an immediately preceding arithmetic or logical instruction.  */
273      if (last_insn_was_arithmetic_or_logic
274	  && !last_insn_in_noncond_delay_slot
275	  && (delayed_load_register != 0)
276	  && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_BR_INSN)
277	  && mt_arch == ms1_64_001)
278	{
279	  if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR1)
280	      && insn.fields.f_sr1 == delayed_load_register)
281	    as_warn (_("conditional branch or jal insn's operand references R%ld of previous arithmetic or logic insn."),
282		     insn.fields.f_sr1);
283
284	  if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR2)
285	      && insn.fields.f_sr2 == delayed_load_register)
286	    as_warn (_("conditional branch or jal insn's operand references R%ld of previous arithmetic or logic insn."),
287		     insn.fields.f_sr2);
288	}
289    }
290
291  /* Keep track of details of this insn for processing next insn.  */
292  last_insn_in_noncond_delay_slot = last_insn_was_branch_insn
293    && !last_insn_was_conditional_branch_insn;
294
295  last_insn_had_delay_slot =
296    CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_DELAY_SLOT);
297  (void) last_insn_had_delay_slot;
298
299  last_insn_has_load_delay =
300    CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_LOAD_DELAY);
301
302  last_insn_was_memory_access =
303    CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_MEMORY_ACCESS);
304
305  last_insn_was_io_insn =
306    CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_IO_INSN);
307
308  last_insn_was_arithmetic_or_logic =
309    CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_AL_INSN);
310
311  last_insn_was_branch_insn =
312    CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_BR_INSN);
313
314  last_insn_was_conditional_branch_insn =
315  CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_BR_INSN)
316    && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR2);
317
318  prev_delayed_load_register = delayed_load_register;
319
320  if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRDR))
321     delayed_load_register = insn.fields.f_dr;
322  else if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRDRRR))
323     delayed_load_register = insn.fields.f_drrr;
324  else  /* Insns has no destination register.  */
325     delayed_load_register = 0;
326
327  /* Generate dwarf2 line numbers.  */
328  dwarf2_emit_insn (4);
329}
330
331valueT
332md_section_align (segT segment, valueT size)
333{
334  int align = bfd_get_section_alignment (stdoutput, segment);
335
336  return ((size + (1 << align) - 1) & -(1 << align));
337}
338
339symbolS *
340md_undefined_symbol (char * name ATTRIBUTE_UNUSED)
341{
342    return NULL;
343}
344
345int
346md_estimate_size_before_relax (fragS * fragP ATTRIBUTE_UNUSED,
347			       segT    segment ATTRIBUTE_UNUSED)
348{
349  as_fatal (_("md_estimate_size_before_relax\n"));
350  return 1;
351}
352
353/* *fragP has been relaxed to its final size, and now needs to have
354   the bytes inside it modified to conform to the new size.
355
356   Called after relaxation is finished.
357   fragP->fr_type == rs_machine_dependent.
358   fragP->fr_subtype is the subtype of what the address relaxed to.  */
359
360void
361md_convert_frag (bfd   * abfd  ATTRIBUTE_UNUSED,
362		 segT    sec   ATTRIBUTE_UNUSED,
363		 fragS * fragP ATTRIBUTE_UNUSED)
364{
365}
366
367
368/* Functions concerning relocs.  */
369
370long
371md_pcrel_from_section (fixS *fixP, segT sec)
372{
373  if (fixP->fx_addsy != (symbolS *) NULL
374      && (!S_IS_DEFINED (fixP->fx_addsy)
375	  || S_GET_SEGMENT (fixP->fx_addsy) != sec))
376    /* The symbol is undefined (or is defined but not in this section).
377       Let the linker figure it out.  */
378    return 0;
379
380  /* Return the address of the opcode - cgen adjusts for opcode size
381     itself, to be consistent with the disassembler, which must do
382     so.  */
383  return fixP->fx_where + fixP->fx_frag->fr_address;
384}
385
386
387/* Return the bfd reloc type for OPERAND of INSN at fixup FIXP.
388   Returns BFD_RELOC_NONE if no reloc type can be found.
389   *FIXP may be modified if desired.  */
390
391bfd_reloc_code_real_type
392md_cgen_lookup_reloc (const CGEN_INSN *    insn     ATTRIBUTE_UNUSED,
393		      const CGEN_OPERAND * operand,
394		      fixS *               fixP     ATTRIBUTE_UNUSED)
395{
396  bfd_reloc_code_real_type result;
397
398  result = BFD_RELOC_NONE;
399
400  switch (operand->type)
401    {
402    case MT_OPERAND_IMM16O:
403      result = BFD_RELOC_16_PCREL;
404      fixP->fx_pcrel = 1;
405      /* fixP->fx_no_overflow = 1; */
406      break;
407    case MT_OPERAND_IMM16:
408    case MT_OPERAND_IMM16Z:
409      /* These may have been processed at parse time.  */
410      if (fixP->fx_cgen.opinfo != 0)
411        result = fixP->fx_cgen.opinfo;
412      fixP->fx_no_overflow = 1;
413      break;
414    case MT_OPERAND_LOOPSIZE:
415      result = BFD_RELOC_MT_PCINSN8;
416      fixP->fx_pcrel = 1;
417      /* Adjust for the delay slot, which is not part of the loop  */
418      fixP->fx_offset -= 8;
419      break;
420    default:
421      result = BFD_RELOC_NONE;
422      break;
423    }
424
425  return result;
426}
427
428/* Write a value out to the object file, using the appropriate endianness.  */
429
430void
431md_number_to_chars (char * buf, valueT val, int n)
432{
433  number_to_chars_bigendian (buf, val, n);
434}
435
436const char *
437md_atof (int type, char * litP, int * sizeP)
438{
439  return ieee_md_atof (type, litP, sizeP, FALSE);
440}
441
442/* See whether we need to force a relocation into the output file.  */
443
444int
445mt_force_relocation (fixS * fixp ATTRIBUTE_UNUSED)
446{
447  return 0;
448}
449
450void
451mt_apply_fix (fixS *fixP, valueT *valueP, segT seg)
452{
453  if ((fixP->fx_pcrel != 0) && (fixP->fx_r_type == BFD_RELOC_32))
454    fixP->fx_r_type = BFD_RELOC_32_PCREL;
455
456  gas_cgen_md_apply_fix (fixP, valueP, seg);
457}
458
459bfd_boolean
460mt_fix_adjustable (fixS * fixP)
461{
462  if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
463    {
464      const CGEN_INSN *insn = NULL;
465      int opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
466      const CGEN_OPERAND *operand;
467
468      operand = cgen_operand_lookup_by_num(gas_cgen_cpu_desc, opindex);
469      md_cgen_lookup_reloc (insn, operand, fixP);
470    }
471
472  if (fixP->fx_addsy == NULL)
473    return TRUE;
474
475  /* Prevent all adjustments to global symbols.  */
476  if (S_IS_EXTERNAL (fixP->fx_addsy))
477    return FALSE;
478
479  if (S_IS_WEAK (fixP->fx_addsy))
480    return FALSE;
481
482  return 1;
483}
484