diff options
Diffstat (limited to 'gas/config/tc-or1k.c')
-rw-r--r-- | gas/config/tc-or1k.c | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/gas/config/tc-or1k.c b/gas/config/tc-or1k.c new file mode 100644 index 0000000..7b479ca --- /dev/null +++ b/gas/config/tc-or1k.c @@ -0,0 +1,362 @@ +/* tc-or1k.c -- Assembler for the OpenRISC family. + Copyright 2001-2014 Free Software Foundation. + Contributed for OR32 by Johan Rydberg, jrydberg@opencores.org + + 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 this program; if not, see <http://www.gnu.org/licenses/> */ +#include "as.h" +#include "safe-ctype.h" +#include "subsegs.h" +#include "symcat.h" +#include "opcodes/or1k-desc.h" +#include "opcodes/or1k-opc.h" +#include "cgen.h" +#include "elf/or1k.h" +#include "dw2gencfi.h" + +/* Structure to hold all of the different components describing + an individual instruction. */ + +typedef struct +{ + const CGEN_INSN * insn; + const CGEN_INSN * orig_insn; + CGEN_FIELDS fields; +#if CGEN_INT_INSN_P + CGEN_INSN_INT buffer [1]; +#define INSN_VALUE(buf) (*(buf)) +#else + unsigned char buffer [CGEN_MAX_INSN_SIZE]; +#define INSN_VALUE(buf) (buf) +#endif + char * addr; + fragS * frag; + int num_fixups; + fixS * fixups [GAS_CGEN_MAX_FIXUPS]; + int indices [MAX_OPERAND_INSTANCES]; +} +or1k_insn; + +const char comment_chars[] = "#"; +const char line_comment_chars[] = "#"; +const char line_separator_chars[] = ";"; +const char EXP_CHARS[] = "eE"; +const char FLT_CHARS[] = "dD"; + +#define OR1K_SHORTOPTS "m:" +const char * md_shortopts = OR1K_SHORTOPTS; + +struct option md_longopts[] = +{ + {NULL, no_argument, NULL, 0} +}; +size_t md_longopts_size = sizeof (md_longopts); + +unsigned long or1k_machine = 0; /* default */ + +int +md_parse_option (int c ATTRIBUTE_UNUSED, char * arg ATTRIBUTE_UNUSED) +{ + return 0; +} + +void +md_show_usage (FILE * stream ATTRIBUTE_UNUSED) +{ +} + +static void +ignore_pseudo (int val ATTRIBUTE_UNUSED) +{ + discard_rest_of_line (); +} + +static bfd_boolean nodelay = FALSE; +static void +s_nodelay (int val ATTRIBUTE_UNUSED) +{ + nodelay = TRUE; +} + +const char or1k_comment_chars [] = ";#"; + +/* The target specific pseudo-ops which we support. */ +const pseudo_typeS md_pseudo_table[] = +{ + { "align", s_align_bytes, 0 }, + { "word", cons, 4 }, + { "proc", ignore_pseudo, 0 }, + { "endproc", ignore_pseudo, 0 }, + { "nodelay", s_nodelay, 0 }, + { NULL, NULL, 0 } +}; + + +void +md_begin (void) +{ + /* Initialize the `cgen' interface. */ + + /* Set the machine number and endian. */ + gas_cgen_cpu_desc = or1k_cgen_cpu_open (CGEN_CPU_OPEN_MACHS, 0, + CGEN_CPU_OPEN_ENDIAN, + CGEN_ENDIAN_BIG, + CGEN_CPU_OPEN_END); + or1k_cgen_init_asm (gas_cgen_cpu_desc); + + /* This is a callback from cgen to gas to parse operands. */ + cgen_set_parse_operand_fn (gas_cgen_cpu_desc, gas_cgen_parse_operand); +} + +void +md_assemble (char * str) +{ + static int last_insn_had_delay_slot = 0; + or1k_insn insn; + char * errmsg; + + /* Initialize GAS's cgen interface for a new instruction. */ + gas_cgen_init_parse (); + + insn.insn = or1k_cgen_assemble_insn + (gas_cgen_cpu_desc, str, & insn.fields, insn.buffer, & errmsg); + + if (!insn.insn) + { + as_bad ("%s", errmsg); + return; + } + + /* Doesn't really matter what we pass for RELAX_P here. */ + gas_cgen_finish_insn (insn.insn, insn.buffer, + CGEN_FIELDS_BITSIZE (& insn.fields), 1, NULL); + + last_insn_had_delay_slot + = CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_DELAY_SLOT); + (void) last_insn_had_delay_slot; +} + + +/* The syntax in the manual says constants begin with '#'. + We just ignore it. */ + +void +md_operand (expressionS * expressionP) +{ + if (* input_line_pointer == '#') + { + input_line_pointer ++; + expression (expressionP); + } +} + +valueT +md_section_align (segT segment, valueT size) +{ + int align = bfd_get_section_alignment (stdoutput, segment); + return ((size + (1 << align) - 1) & (-1 << align)); +} + +symbolS * +md_undefined_symbol (char * name ATTRIBUTE_UNUSED) +{ + return 0; +} + + +/* Interface to relax_segment. */ + +const relax_typeS md_relax_table[] = +{ +/* The fields are: + 1) most positive reach of this state, + 2) most negative reach of this state, + 3) how many bytes this mode will add to the size of the current frag + 4) which index into the table to try if we can't fit into this one. */ + + /* The first entry must be unused because an `rlx_more' value of zero ends + each list. */ + {1, 1, 0, 0}, + + /* The displacement used by GAS is from the end of the 4 byte insn, + so we subtract 4 from the following. */ + {(((1 << 25) - 1) << 2) - 4, -(1 << 25) - 4, 0, 0}, +}; + +int +md_estimate_size_before_relax (fragS * fragP, segT segment ATTRIBUTE_UNUSED) +{ + return md_relax_table[fragP->fr_subtype].rlx_length; +} + +/* *fragP has been relaxed to its final size, and now needs to have + the bytes inside it modified to conform to the new size. + + Called after relaxation is finished. + fragP->fr_type == rs_machine_dependent. + fragP->fr_subtype is the subtype of what the address relaxed to. */ + +void +md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED, + segT sec ATTRIBUTE_UNUSED, + fragS * fragP ATTRIBUTE_UNUSED) +{ + /* FIXME */ +} + + +/* Functions concerning relocs. */ + +/* The location from which a PC relative jump should be calculated, + given a PC relative reloc. */ + +long +md_pcrel_from_section (fixS * fixP, segT sec) +{ + if (fixP->fx_addsy != (symbolS *) NULL + && (! S_IS_DEFINED (fixP->fx_addsy) + || (S_GET_SEGMENT (fixP->fx_addsy) != sec) + || S_IS_EXTERNAL (fixP->fx_addsy) + || S_IS_WEAK (fixP->fx_addsy))) + { + /* The symbol is undefined (or is defined but not in this section). + Let the linker figure it out. */ + return 0; + } + + return fixP->fx_frag->fr_address + fixP->fx_where; +} + + +/* Return the bfd reloc type for OPERAND of INSN at fixup FIXP. + Returns BFD_RELOC_NONE if no reloc type can be found. + *FIXP may be modified if desired. */ + +bfd_reloc_code_real_type +md_cgen_lookup_reloc (const CGEN_INSN * insn ATTRIBUTE_UNUSED, + const CGEN_OPERAND * operand, + fixS * fixP) +{ + if (fixP->fx_cgen.opinfo) + return fixP->fx_cgen.opinfo; + + switch (operand->type) + { + case OR1K_OPERAND_DISP26: + fixP->fx_pcrel = 1; + return BFD_RELOC_OR1K_REL_26; + + default: /* avoid -Wall warning */ + return BFD_RELOC_NONE; + } +} + +/* Write a value out to the object file, using the appropriate endianness. */ + +void +md_number_to_chars (char * buf, valueT val, int n) +{ + number_to_chars_bigendian (buf, val, n); +} + +/* Turn a string in input_line_pointer into a floating point constant of type + type, and store the appropriate bytes in *litP. The number of LITTLENUMS + emitted is stored in *sizeP . An error message is returned, or NULL on OK. */ + +/* Equal to MAX_PRECISION in atof-ieee.c. */ +#define MAX_LITTLENUMS 6 + +char * +md_atof (int type, char * litP, int * sizeP) +{ + return ieee_md_atof (type, litP, sizeP, TRUE); +} + +bfd_boolean +or1k_fix_adjustable (fixS * fixP) +{ + /* We need the symbol name for the VTABLE entries. */ + if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT + || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY) + return FALSE; + + return TRUE; +} + +#define GOT_NAME "_GLOBAL_OFFSET_TABLE_" + +arelent * +tc_gen_reloc (asection *sec, fixS *fx) +{ + bfd_reloc_code_real_type code = fx->fx_r_type; + + if (fx->fx_addsy != NULL + && strcmp (S_GET_NAME (fx->fx_addsy), GOT_NAME) == 0 + && (code == BFD_RELOC_OR1K_GOTPC_HI16 + || code == BFD_RELOC_OR1K_GOTPC_LO16)) + { + arelent * reloc; + + reloc = xmalloc (sizeof (* reloc)); + reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *)); + *reloc->sym_ptr_ptr = symbol_get_bfdsym (fx->fx_addsy); + reloc->address = fx->fx_frag->fr_address + fx->fx_where; + reloc->howto = bfd_reloc_type_lookup (stdoutput, fx->fx_r_type); + reloc->addend = fx->fx_offset; + return reloc; + } + + return gas_cgen_tc_gen_reloc (sec, fx); +} + +void +or1k_apply_fix (struct fix *f, valueT *t, segT s) +{ + gas_cgen_md_apply_fix (f, t, s); + + switch (f->fx_r_type) + { + case BFD_RELOC_OR1K_TLS_GD_HI16: + case BFD_RELOC_OR1K_TLS_GD_LO16: + case BFD_RELOC_OR1K_TLS_LDM_HI16: + case BFD_RELOC_OR1K_TLS_LDM_LO16: + case BFD_RELOC_OR1K_TLS_LDO_HI16: + case BFD_RELOC_OR1K_TLS_LDO_LO16: + case BFD_RELOC_OR1K_TLS_IE_HI16: + case BFD_RELOC_OR1K_TLS_IE_LO16: + case BFD_RELOC_OR1K_TLS_LE_HI16: + case BFD_RELOC_OR1K_TLS_LE_LO16: + S_SET_THREAD_LOCAL (f->fx_addsy); + break; + default: + break; + } +} + +void +or1k_elf_final_processing (void) +{ + if (nodelay) + elf_elfheader (stdoutput)->e_flags |= EF_OR1K_NODELAY; +} + +/* Standard calling conventions leave the CFA at SP on entry. */ + +void +or1k_cfi_frame_initial_instructions (void) +{ + cfi_add_CFA_def_cfa_register (1); +} + |