1/* tc-mt.c -- Assembler for the Morpho Technologies mt .
2   Copyright (C) 2005-2022 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 this program; if not, write to the Free Software
18   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
19   MA 02110-1301, 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  literal_prefix_dollar_hex = true;
183}
184
185void
186md_assemble (char * str)
187{
188  static long delayed_load_register = 0;
189  static long prev_delayed_load_register = 0;
190  static int last_insn_had_delay_slot = 0;
191  static int last_insn_in_noncond_delay_slot = 0;
192  static int last_insn_has_load_delay = 0;
193  static int last_insn_was_memory_access = 0;
194  static int last_insn_was_io_insn = 0;
195  static int last_insn_was_arithmetic_or_logic = 0;
196  static int last_insn_was_branch_insn = 0;
197  static int last_insn_was_conditional_branch_insn = 0;
198
199  mt_insn insn;
200  char * errmsg;
201
202  /* Initialize GAS's cgen interface for a new instruction.  */
203  gas_cgen_init_parse ();
204
205  insn.insn = mt_cgen_assemble_insn
206      (gas_cgen_cpu_desc, str, & insn.fields, insn.buffer, & errmsg);
207
208  if (!insn.insn)
209    {
210      as_bad ("%s", errmsg);
211      return;
212    }
213
214  /* Doesn't really matter what we pass for RELAX_P here.  */
215  gas_cgen_finish_insn (insn.insn, insn.buffer,
216			CGEN_FIELDS_BITSIZE (& insn.fields), 1, NULL);
217
218
219  /* Handle Scheduling Restrictions.  */
220  if (!no_scheduling_restrictions)
221    {
222      /* Detect consecutive Memory Accesses.  */
223      if (last_insn_was_memory_access
224	  && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_MEMORY_ACCESS)
225	  && mt_mach == ms1_64_001)
226	as_warn (_("instruction %s may not follow another memory access instruction."),
227		 CGEN_INSN_NAME (insn.insn));
228
229      /* Detect consecutive I/O Instructions.  */
230      else if (last_insn_was_io_insn
231	       && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_IO_INSN))
232	as_warn (_("instruction %s may not follow another I/O instruction."),
233		 CGEN_INSN_NAME (insn.insn));
234
235      /* Detect consecutive branch instructions.  */
236      else if (last_insn_was_branch_insn
237	       && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_BR_INSN))
238	as_warn (_("%s may not occupy the delay slot of another branch insn."),
239		 CGEN_INSN_NAME (insn.insn));
240
241      /* Detect data dependencies on delayed loads: memory and input insns.  */
242      if (last_insn_has_load_delay && delayed_load_register)
243	{
244	  if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR1)
245	      && insn.fields.f_sr1 == delayed_load_register)
246	    as_warn (_("operand references R%ld of previous load."),
247		     insn.fields.f_sr1);
248
249	  if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR2)
250	      && insn.fields.f_sr2 == delayed_load_register)
251	    as_warn (_("operand references R%ld of previous load."),
252		     insn.fields.f_sr2);
253	}
254
255      /* Detect JAL/RETI hazard */
256      if (mt_mach == ms2
257	  && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_JAL_HAZARD))
258	{
259	  if ((CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR1)
260	       && insn.fields.f_sr1 == delayed_load_register)
261	      || (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR2)
262		  && insn.fields.f_sr2 == delayed_load_register))
263	    as_warn (_("operand references R%ld of previous instruction."),
264		     delayed_load_register);
265	  else if ((CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR1)
266		    && insn.fields.f_sr1 == prev_delayed_load_register)
267		   || (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR2)
268		       && insn.fields.f_sr2 == prev_delayed_load_register))
269	    as_warn (_("operand references R%ld of instruction before previous."),
270		     prev_delayed_load_register);
271	}
272
273      /* Detect data dependency between conditional branch instruction
274         and an immediately preceding arithmetic or logical instruction.  */
275      if (last_insn_was_arithmetic_or_logic
276	  && !last_insn_in_noncond_delay_slot
277	  && (delayed_load_register != 0)
278	  && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_BR_INSN)
279	  && mt_arch == ms1_64_001)
280	{
281	  if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR1)
282	      && insn.fields.f_sr1 == delayed_load_register)
283	    as_warn (_("conditional branch or jal insn's operand references R%ld of previous arithmetic or logic insn."),
284		     insn.fields.f_sr1);
285
286	  if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR2)
287	      && insn.fields.f_sr2 == delayed_load_register)
288	    as_warn (_("conditional branch or jal insn's operand references R%ld of previous arithmetic or logic insn."),
289		     insn.fields.f_sr2);
290	}
291    }
292
293  /* Keep track of details of this insn for processing next insn.  */
294  last_insn_in_noncond_delay_slot = last_insn_was_branch_insn
295    && !last_insn_was_conditional_branch_insn;
296
297  last_insn_had_delay_slot =
298    CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_DELAY_SLOT);
299  (void) last_insn_had_delay_slot;
300
301  last_insn_has_load_delay =
302    CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_LOAD_DELAY);
303
304  last_insn_was_memory_access =
305    CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_MEMORY_ACCESS);
306
307  last_insn_was_io_insn =
308    CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_IO_INSN);
309
310  last_insn_was_arithmetic_or_logic =
311    CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_AL_INSN);
312
313  last_insn_was_branch_insn =
314    CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_BR_INSN);
315
316  last_insn_was_conditional_branch_insn =
317  CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_BR_INSN)
318    && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR2);
319
320  prev_delayed_load_register = delayed_load_register;
321
322  if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRDR))
323     delayed_load_register = insn.fields.f_dr;
324  else if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRDRRR))
325     delayed_load_register = insn.fields.f_drrr;
326  else  /* Insns has no destination register.  */
327     delayed_load_register = 0;
328
329  /* Generate dwarf2 line numbers.  */
330  dwarf2_emit_insn (4);
331}
332
333valueT
334md_section_align (segT segment, valueT size)
335{
336  int align = bfd_section_alignment (segment);
337
338  return ((size + (1 << align) - 1) & -(1 << align));
339}
340
341symbolS *
342md_undefined_symbol (char * name ATTRIBUTE_UNUSED)
343{
344    return NULL;
345}
346
347int
348md_estimate_size_before_relax (fragS * fragP ATTRIBUTE_UNUSED,
349			       segT    segment ATTRIBUTE_UNUSED)
350{
351  as_fatal (_("md_estimate_size_before_relax\n"));
352  return 1;
353}
354
355/* *fragP has been relaxed to its final size, and now needs to have
356   the bytes inside it modified to conform to the new size.
357
358   Called after relaxation is finished.
359   fragP->fr_type == rs_machine_dependent.
360   fragP->fr_subtype is the subtype of what the address relaxed to.  */
361
362void
363md_convert_frag (bfd   * abfd  ATTRIBUTE_UNUSED,
364		 segT    sec   ATTRIBUTE_UNUSED,
365		 fragS * fragP ATTRIBUTE_UNUSED)
366{
367}
368
369
370/* Functions concerning relocs.  */
371
372long
373md_pcrel_from_section (fixS *fixP, segT sec)
374{
375  if (fixP->fx_addsy != (symbolS *) NULL
376      && (!S_IS_DEFINED (fixP->fx_addsy)
377	  || S_GET_SEGMENT (fixP->fx_addsy) != sec))
378    /* The symbol is undefined (or is defined but not in this section).
379       Let the linker figure it out.  */
380    return 0;
381
382  /* Return the address of the opcode - cgen adjusts for opcode size
383     itself, to be consistent with the disassembler, which must do
384     so.  */
385  return fixP->fx_where + fixP->fx_frag->fr_address;
386}
387
388
389/* Return the bfd reloc type for OPERAND of INSN at fixup FIXP.
390   Returns BFD_RELOC_NONE if no reloc type can be found.
391   *FIXP may be modified if desired.  */
392
393bfd_reloc_code_real_type
394md_cgen_lookup_reloc (const CGEN_INSN *    insn     ATTRIBUTE_UNUSED,
395		      const CGEN_OPERAND * operand,
396		      fixS *               fixP     ATTRIBUTE_UNUSED)
397{
398  bfd_reloc_code_real_type result;
399
400  result = BFD_RELOC_NONE;
401
402  switch (operand->type)
403    {
404    case MT_OPERAND_IMM16O:
405      result = BFD_RELOC_16_PCREL;
406      fixP->fx_pcrel = 1;
407      /* fixP->fx_no_overflow = 1; */
408      break;
409    case MT_OPERAND_IMM16:
410    case MT_OPERAND_IMM16Z:
411      /* These may have been processed at parse time.  */
412      if (fixP->fx_cgen.opinfo != 0)
413        result = fixP->fx_cgen.opinfo;
414      fixP->fx_no_overflow = 1;
415      break;
416    case MT_OPERAND_LOOPSIZE:
417      result = BFD_RELOC_MT_PCINSN8;
418      fixP->fx_pcrel = 1;
419      /* Adjust for the delay slot, which is not part of the loop  */
420      fixP->fx_offset -= 8;
421      break;
422    default:
423      result = BFD_RELOC_NONE;
424      break;
425    }
426
427  return result;
428}
429
430/* Write a value out to the object file, using the appropriate endianness.  */
431
432void
433md_number_to_chars (char * buf, valueT val, int n)
434{
435  number_to_chars_bigendian (buf, val, n);
436}
437
438const char *
439md_atof (int type, char * litP, int * sizeP)
440{
441  return ieee_md_atof (type, litP, sizeP, false);
442}
443
444/* See whether we need to force a relocation into the output file.  */
445
446int
447mt_force_relocation (fixS * fixp ATTRIBUTE_UNUSED)
448{
449  return 0;
450}
451
452void
453mt_apply_fix (fixS *fixP, valueT *valueP, segT seg)
454{
455  if ((fixP->fx_pcrel != 0) && (fixP->fx_r_type == BFD_RELOC_32))
456    fixP->fx_r_type = BFD_RELOC_32_PCREL;
457
458  gas_cgen_md_apply_fix (fixP, valueP, seg);
459}
460
461bool
462mt_fix_adjustable (fixS * fixP)
463{
464  if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
465    {
466      const CGEN_INSN *insn = NULL;
467      int opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
468      const CGEN_OPERAND *operand;
469
470      operand = cgen_operand_lookup_by_num(gas_cgen_cpu_desc, opindex);
471      md_cgen_lookup_reloc (insn, operand, fixP);
472    }
473
474  if (fixP->fx_addsy == NULL)
475    return true;
476
477  /* Prevent all adjustments to global symbols.  */
478  if (S_IS_EXTERNAL (fixP->fx_addsy))
479    return false;
480
481  if (S_IS_WEAK (fixP->fx_addsy))
482    return false;
483
484  return true;
485}
486