aboutsummaryrefslogtreecommitdiff
path: root/gas/config/tc-sh64.h
blob: 8ce330b5116714b33731a5063adc809cb1a41987 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
/* This file is tc-sh64.h
   Copyright (C) 2000-2016 Free Software Foundation, Inc.

   This file is part of GAS, the GNU Assembler.

   GAS is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3, or (at your option)
   any later version.

   GAS is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with GAS; see the file COPYING.  If not, write to
   the Free Software Foundation, 51 Franklin Street - Fifth Floor,
   Boston, MA 02110-1301, USA.  */

#define TC_SH64
#include "config/tc-sh.h"
#include "elf/sh.h"
#include "elf32-sh64.h"

/* We need to override the tc-sh.h settings of HANDLE_ALIGN and
   MAX_MEM_FOR_RS_ALIGN_CODE; we might need to put in SHmedia NOP:s, not
   SHcompact NOP:s.  */
#undef  HANDLE_ALIGN
#define HANDLE_ALIGN(frag) sh64_handle_align (frag)
extern void sh64_handle_align (fragS *);

#undef  MAX_MEM_FOR_RS_ALIGN_CODE
#define MAX_MEM_FOR_RS_ALIGN_CODE sh64_max_mem_for_rs_align_code ()
extern int sh64_max_mem_for_rs_align_code (void);

#undef  LISTING_HEADER
#define LISTING_HEADER					\
  (target_big_endian ?					\
     "SuperH SHcompact/SHmedia Big Endian GAS"		\
   : "SuperH SHcompact/SHmedia Little Endian GAS")

/* We need to record the new frag position after an .align.  */
extern void sh64_do_align (int, const char *, int, int);
#define md_do_align(n, fill, len, max, l) \
 do { sh64_do_align (n, fill, len, max); goto l; } while (0)

struct sh64_segment_info_type
{
  /* The type of the section is initialized when the range_start_symbol
     member is non-NULL.  */
  symbolS *mode_start_symbol;
  subsegT mode_start_subseg;

  /* A stored symbol indicating location of last call of
     "md_flush_pending_output".  It is NULLed when we actually use it;
     otherwise the contents is just filled in with segment, frag and
     offset within frag.  */
  symbolS *last_contents_mark;

  unsigned int emitted_ranges;
  enum sh64_elf_cr_type contents_type;

  /* This is used by the SH1-4 parts; we set it to 0 for SHmedia code and
     data.  */
  unsigned int in_code : 1;
};

#undef  TC_SEGMENT_INFO_TYPE
#define TC_SEGMENT_INFO_TYPE struct sh64_segment_info_type

#undef  TARGET_FORMAT
#define TARGET_FORMAT sh64_target_format ()
extern const char *sh64_target_format (void);

#define TARGET_MACH sh64_target_mach ()
extern int sh64_target_mach (void);

#undef TC_FORCE_RELOCATION_LOCAL
#define TC_FORCE_RELOCATION_LOCAL(FIX)			\
  (!(FIX)->fx_pcrel					\
   || (FIX)->fx_r_type == BFD_RELOC_32_PLT_PCREL	\
   || (FIX)->fx_r_type == BFD_RELOC_SH_PLT_LOW16	\
   || (FIX)->fx_r_type == BFD_RELOC_SH_PLT_MEDLOW16	\
   || (FIX)->fx_r_type == BFD_RELOC_SH_PLT_MEDHI16	\
   || (FIX)->fx_r_type == BFD_RELOC_SH_PLT_HI16		\
   || (FIX)->fx_r_type == BFD_RELOC_32_GOT_PCREL	\
   || (FIX)->fx_r_type == BFD_RELOC_SH_GOT_LOW16	\
   || (FIX)->fx_r_type == BFD_RELOC_SH_GOT_MEDLOW16	\
   || (FIX)->fx_r_type == BFD_RELOC_SH_GOT_MEDHI16	\
   || (FIX)->fx_r_type == BFD_RELOC_SH_GOT_HI16		\
   || (FIX)->fx_r_type == BFD_RELOC_SH_GOT10BY4		\
   || (FIX)->fx_r_type == BFD_RELOC_SH_GOT10BY8		\
   || (FIX)->fx_r_type == BFD_RELOC_SH_GOTPLT32		\
   || (FIX)->fx_r_type == BFD_RELOC_SH_GOTPLT_LOW16	\
   || (FIX)->fx_r_type == BFD_RELOC_SH_GOTPLT_MEDLOW16	\
   || (FIX)->fx_r_type == BFD_RELOC_SH_GOTPLT_MEDHI16	\
   || (FIX)->fx_r_type == BFD_RELOC_SH_GOTPLT_HI16	\
   || (FIX)->fx_r_type == BFD_RELOC_SH_GOTPLT10BY4	\
   || (FIX)->fx_r_type == BFD_RELOC_SH_GOTPLT10BY8	\
   || (FIX)->fx_r_type == BFD_RELOC_SH_GOTPC		\
   || (FIX)->fx_r_type == BFD_RELOC_SH_GOTPC_LOW16	\
   || (FIX)->fx_r_type == BFD_RELOC_SH_GOTPC_MEDLOW16	\
   || (FIX)->fx_r_type == BFD_RELOC_SH_GOTPC_MEDHI16	\
   || (FIX)->fx_r_type == BFD_RELOC_SH_GOTPC_HI16	\
   || TC_FORCE_RELOCATION (FIX))

#undef TC_FORCE_RELOCATION_SUB_SAME
#define TC_FORCE_RELOCATION_SUB_SAME(FIX, SEC)		\
  (! SEG_NORMAL (SEC)					\
   || TC_FORCE_RELOCATION (FIX)				\
   || (sh_relax && SWITCH_TABLE (FIX))			\
   || *symbol_get_tc ((FIX)->fx_addsy) != NULL)

/* Don't complain when we leave fx_subsy around.  */
#undef TC_VALIDATE_FIX_SUB
#define TC_VALIDATE_FIX_SUB(FIX, SEG)			\
  ((md_register_arithmetic || (SEG) != reg_section)	\
   && ((FIX)->fx_r_type == BFD_RELOC_32_PLT_PCREL	\
       || (sh_relax && SWITCH_TABLE (FIX))		\
       || *symbol_get_tc ((FIX)->fx_addsy) != NULL))

/* Note the kludge: we want to put back C, and we also want to consume the
   expression, since we have handled it ourselves.  FIXME: What we really
   need is a new GAS infrastructure feature: md_qualifier.  */
#undef md_parse_name
#define md_parse_name(NAME, EXP, MODE, CP) \
 sh64_consume_datalabel (NAME, EXP, MODE, CP, operand)
extern int sh64_consume_datalabel (const char *, expressionS *,
				   enum expr_mode, char *,
				   segT (*) (expressionS *, enum expr_mode));

/* Saying "$" is the same as saying ".".  */
#define DOLLAR_DOT

#undef MD_PCREL_FROM_SECTION
#define MD_PCREL_FROM_SECTION(FIX, SEC)		\
  shmedia_md_pcrel_from_section (FIX, SEC)

extern valueT shmedia_md_pcrel_from_section (struct fix *, segT);

/* We need to mark this symbol as a BranchTarget; setting st_other for it
   and adding 1 to its value (temporarily).  */
extern void sh64_frob_label (symbolS *);

#undef  tc_frob_label
#define tc_frob_label(sym) \
  do { sh_frob_label (sym); sh64_frob_label (sym); } while (0)

#define tc_symbol_new_hook(s) sh64_frob_label (s)

/* We use this to mark our "datalabel" symbol copies.  The "mark" is NULL
   for an ordinary symbol, and the pointer to the "ordinary" symbol for a
   datalabel symbol.  */
#define TC_SYMFIELD_TYPE symbolS *

#define tc_frob_symbol(symp, punt)		\
 do						\
   {						\
     punt = sh64_exclude_symbol (symp);		\
   }						\
 while (0)

extern int sh64_exclude_symbol (symbolS *);

extern void sh64_adjust_symtab (void);
#define tc_adjust_symtab sh64_adjust_symtab

#undef  md_flush_pending_output
#define md_flush_pending_output() sh64_flush_pending_output ()
extern void sh64_flush_pending_output (void);

/* Note that tc-sh.c has a sh_frob_section, but it's called from
   tc_frob_file_before_adjust.  */
#define tc_frob_section(sec) shmedia_frob_section_type (sec)
extern void shmedia_frob_section_type (asection *);

/* We need to emit fixups relative to the frag in which the instruction
   resides.  Safest way without calculating max fragment growth or making
   it a fixed number is to provide a pointer to the opcode frag.

   We also need to emit the right NOP pattern in .align frags.  This is
   done after the text-to-bits assembly pass, so we need to mark it with
   the ISA setting at the time the .align was assembled.  */
#define TC_FRAG_TYPE struct sh64_tc_frag_data

enum sh64_isa_values
 {
   sh64_isa_unspecified,
   sh64_isa_shcompact,
   sh64_isa_shmedia,

   /* Special guard value used in contexts when we don't know which ISA it
      is, just that it's specified (not sh64_isa_unspecified).  */
   sh64_isa_sh5_guard
 };

struct sh64_tc_frag_data
{
  fragS *opc_frag;
  enum sh64_isa_values isa;
};

extern enum sh64_isa_values sh64_isa_mode;

#define TC_FRAG_INIT(FRAGP)					\
 do								\
   {								\
     (FRAGP)->tc_frag_data.opc_frag = sh64_last_insn_frag;	\
     (FRAGP)->tc_frag_data.isa = sh64_isa_mode;			\
   }								\
 while (0)

/* This variable is set whenever we generate (or grow) a new opcode frag
   in shmedia_build_Mytes.  */
extern fragS *sh64_last_insn_frag;

#define md_end() shmedia_md_end ()
void shmedia_md_end (void);

/* Because we make .debug_line hold the SHmedia instruction address | 1,
   we have to say we only have minimum byte-size insns.  */
#undef  DWARF2_LINE_MIN_INSN_LENGTH
#define DWARF2_LINE_MIN_INSN_LENGTH 1

#define TC_FAKE_LABEL(NAME) sh64_fake_label(NAME)
extern int sh64_fake_label (const char *);