/* tc-hppa.c -- Assemble for the PA
- Copyright 1989, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
- 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+ Copyright 1989, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+ 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
As in 0f12.456 or 0d1.2345e12.
Be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
- changed in read.c. Ideally it shouldn't hae to know abou it at
- all, but nothing is ideal around here. */
+ changed in read.c. Ideally it shouldn't have to know about it
+ at all, but nothing is ideal around here. */
const char FLT_CHARS[] = "rRsSfFdDxXpP";
static struct pa_it the_insn;
#ifdef OBJ_SOM
/* default space and subspace dictionaries */
-#define GDB_SYMBOLS GDB_SYMBOLS_SUBSPACE_NAME
-#define GDB_STRINGS GDB_STRINGS_SUBSPACE_NAME
+#define GDB_SYMBOLS GDB_SYMBOLS_SUBSPACE_NAME
+#define GDB_STRINGS GDB_STRINGS_SUBSPACE_NAME
/* pre-defined subsegments (subspaces) for the HPPA. */
#define SUBSEG_CODE 0
#define IS_R_SELECT(S) (*(S) == 'R' || *(S) == 'r')
#define IS_L_SELECT(S) (*(S) == 'L' || *(S) == 'l')
+/* Store immediate values of shift/deposit/extract functions. */
+
+#define SAVE_IMMEDIATE(VALUE) \
+ { \
+ if (immediate_check) \
+ { \
+ if (pos == -1) \
+ pos = (VALUE); \
+ else if (len == -1) \
+ len = (VALUE); \
+ } \
+ }
+
/* Insert FIELD into OPCODE starting at bit START. Continue pa_ip
main loop after insertion. */
if ((FIELD) > (HIGH) || (FIELD) < (LOW)) \
{ \
if (! IGNORE) \
- as_bad (_("Field out of range [%d..%d] (%d)."), (LOW), (HIGH), \
+ as_bad (_("Field out of range [%d..%d] (%d)."), (LOW), (HIGH), \
(int) (FIELD));\
- break; \
+ break; \
} \
}
{ \
if ((FIELD) > (HIGH) || (FIELD) < (LOW)) \
{ \
- as_bad_where ((FILENAME), (LINE), \
+ as_bad_where ((FILENAME), (LINE), \
_("Field out of range [%d..%d] (%d)."), (LOW), (HIGH), \
(int) (FIELD));\
- break; \
+ break; \
} \
}
if ((FIELD) & ((ALIGN) - 1)) \
{ \
if (! IGNORE) \
- as_bad (_("Field not properly aligned [%d] (%d)."), (ALIGN), \
+ as_bad (_("Field not properly aligned [%d] (%d)."), (ALIGN), \
(int) (FIELD));\
- break; \
+ break; \
} \
}
((exp).X_op == O_subtract \
&& strcmp (S_GET_NAME ((exp).X_op_symbol), "$global$") == 0)
+#define is_SB_relative(exp) \
+ ((exp).X_op == O_subtract \
+ && strcmp (S_GET_NAME ((exp).X_op_symbol), "$segrel$") == 0)
+
#define is_PC_relative(exp) \
((exp).X_op == O_subtract \
&& strcmp (S_GET_NAME ((exp).X_op_symbol), "$PIC_pcrel$0") == 0)
it now so as not to confuse write.c. Ditto for $PIC_pcrel$0. */
if (new_fix->fx_subsy
&& (strcmp (S_GET_NAME (new_fix->fx_subsy), "$global$") == 0
+ || strcmp (S_GET_NAME (new_fix->fx_subsy), "$segrel$") == 0
|| strcmp (S_GET_NAME (new_fix->fx_subsy), "$PIC_pcrel$0") == 0
|| strcmp (S_GET_NAME (new_fix->fx_subsy), "$tls_gdidx$") == 0
|| strcmp (S_GET_NAME (new_fix->fx_subsy), "$tls_ldidx$") == 0
else if (is_PC_relative (*exp))
rel_type = R_HPPA_PCREL_CALL;
#ifdef OBJ_ELF
+ else if (is_SB_relative (*exp))
+ rel_type = R_PARISC_SEGREL32;
else if (is_tls_gdidx (*exp))
rel_type = R_PARISC_TLS_GD21L;
else if (is_tls_ldidx (*exp))
return nullif;
}
-/* 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 or NULL is returned. */
-
-#define MAX_LITTLENUMS 6
-
char *
md_atof (int type, char *litP, int *sizeP)
{
- int prec;
- LITTLENUM_TYPE words[MAX_LITTLENUMS];
- LITTLENUM_TYPE *wordP;
- char *t;
-
- switch (type)
- {
-
- case 'f':
- case 'F':
- case 's':
- case 'S':
- prec = 2;
- break;
-
- case 'd':
- case 'D':
- case 'r':
- case 'R':
- prec = 4;
- break;
-
- case 'x':
- case 'X':
- prec = 6;
- break;
-
- case 'p':
- case 'P':
- prec = 6;
- break;
-
- default:
- *sizeP = 0;
- return _("Bad call to MD_ATOF()");
- }
- t = atof_ieee (input_line_pointer, type, words);
- if (t)
- input_line_pointer = t;
- *sizeP = prec * sizeof (LITTLENUM_TYPE);
- for (wordP = words; prec--;)
- {
- md_number_to_chars (litP, (valueT) (*wordP++), sizeof (LITTLENUM_TYPE));
- litP += sizeof (LITTLENUM_TYPE);
- }
- return NULL;
+ return ieee_md_atof (type, litP, sizeP, TRUE);
}
/* Write out big-endian. */
if (fixp->fx_addsy == 0)
return &no_relocs;
- assert (hppa_fixp != 0);
- assert (section != 0);
+ gas_assert (hppa_fixp != 0);
+ gas_assert (section != 0);
reloc = xmalloc (sizeof (arelent));
reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
*reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+
+ /* Allow fixup_segment to recognize hand-written pc-relative relocations.
+ When we went through cons_fix_new_hppa, we classified them as complex. */
+ /* ??? It might be better to hide this +8 stuff in tc_cfi_emit_pcrel_expr,
+ undefine DIFF_EXPR_OK, and let these sorts of complex expressions fail
+ when R_HPPA_COMPLEX == R_PARISC_UNIMPLEMENTED. */
+ if (fixp->fx_r_type == R_HPPA_COMPLEX && fixp->fx_pcrel)
+ {
+ fixp->fx_r_type = R_HPPA_PCREL_CALL;
+ fixp->fx_offset += 8;
+ }
+
codes = hppa_gen_reloc_type (stdoutput,
fixp->fx_r_type,
hppa_fixp->fx_r_format,
switch (fixp->fx_r_type)
{
default:
- assert (n_relocs == 1);
+ gas_assert (n_relocs == 1);
code = *codes[0];
(bfd_reloc_code_real_type) code);
reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
- assert (reloc->howto && (unsigned int) code == reloc->howto->type);
+ gas_assert (reloc->howto && (unsigned int) code == reloc->howto->type);
break;
}
#else /* OBJ_SOM */
/* The only time we ever use a R_COMP2 fixup is for the difference
of two symbols. With that in mind we fill in all four
relocs now and break out of the loop. */
- assert (i == 1);
+ gas_assert (i == 1);
relocs[0]->sym_ptr_ptr
= (asymbol **) bfd_abs_section_ptr->symbol_ptr_ptr;
relocs[0]->howto
case R_PARISC_TLS_LE14R:
case R_PARISC_TLS_IE21L:
case R_PARISC_TLS_IE14R:
- if (fixP->fx_addsy)
+ if (fixP->fx_addsy)
S_SET_THREAD_LOCAL (fixP->fx_addsy);
break;
default:
p++;
c = *p;
/* Tege hack: Special case for general registers as the general
- code makes a binary search with case translation, and is VERY
- slow. */
+ code makes a binary search with case translation, and is VERY
+ slow. */
if (c == 'r')
{
p++;
else
{
/* And finally, it could be a symbol in the absolute section which
- is effectively a constant, or a register alias symbol. */
+ is effectively a constant, or a register alias symbol. */
name = p;
c = *p;
while (is_part_of_name (c))
cmpltr = 7;
}
/* If we have something like addb,n then there is no condition
- completer. */
+ completer. */
else if (strcasecmp (name, "n") == 0)
{
cmpltr = 0;
cmpltr = 7;
}
/* If we have something like addb,n then there is no condition
- completer. */
+ completer. */
else if (strcasecmp (name, "n") == 0)
{
cmpltr = 0;
cmpltr = 7;
}
/* If we have something like addb,n then there is no condition
- completer. */
+ completer. */
else if (strcasecmp (name, "n") == 0)
{
cmpltr = 0;
cmpltr = 7;
}
/* If we have something like addb,n then there is no condition
- completer. */
+ completer. */
else if (strcasecmp (name, "n") == 0)
{
cmpltr = 0;
cmpltr = 15;
}
/* If we have something like addb,n then there is no condition
- completer. */
+ completer. */
else if (strcasecmp (name, "n") == 0)
{
cmpltr = 0;
int match = FALSE;
int comma = 0;
int cmpltr, nullif, flag, cond, num;
+ int immediate_check = 0, pos = -1, len = -1;
unsigned long opcode;
struct pa_opcode *insn;
return;
}
- /* Look up the opcode in the has table. */
+ /* Look up the opcode in the hash table. */
if ((insn = (struct pa_opcode *) hash_find (op_hash, str)) == NULL)
{
- as_bad ("Unknown opcode: `%s'", str);
+ as_bad (_("Unknown opcode: `%s'"), str);
return;
}
goto failed;
/* Build the opcode, checking as we go to make
- sure that the operands match. */
+ sure that the operands match. */
for (args = insn->args;; ++args)
{
/* Absorb white space in instruction. */
s = s + 1;
if (!strncasecmp (s, "%sar", 4))
- {
+ {
s += 4;
continue;
}
else if (!strncasecmp (s, "%cr11", 5))
- {
+ {
s += 5;
continue;
}
break;
s = expr_end;
CHECK_FIELD (num, 32, 1, 0);
+ SAVE_IMMEDIATE(num);
INSERT_FIELD_AND_CONTINUE (opcode, 32 - num, 0);
/* Handle a 5 bit immediate at 15. */
}
else if (*args == 'J')
{
- /* M bit is explicit in the major opcode. */
+ /* M bit is explicit in the major opcode. */
INSERT_FIELD_AND_CONTINUE (opcode, a, 2);
}
else if (*args == 'e')
permloc[2] = 8;
permloc[3] = 6;
for (; i < 4; i++)
- {
+ {
switch (*s++)
{
case '0':
case 'x':
case 'y':
cmpltr = 0;
+ /* Check immediate values in shift/extract/deposit
+ * instructions if they will give undefined behaviour. */
+ immediate_check = 1;
if (*s == ',')
{
save_s = s++;
continue;
}
else
- break;
+ break;
/* Handle '%sr0,%r31' implicit operand of be,l instruction. */
case 'Y':
break;
s = expr_end;
CHECK_FIELD (num, 31, 0, strict);
+ SAVE_IMMEDIATE(num);
INSERT_FIELD_AND_CONTINUE (opcode, 31 - num, 5);
/* Handle a 6 bit shift count at 20,22:26. */
break;
s = expr_end;
CHECK_FIELD (num, 63, 0, strict);
+ SAVE_IMMEDIATE(num);
num = 63 - num;
opcode |= (num & 0x20) << 6;
INSERT_FIELD_AND_CONTINUE (opcode, num & 0x1f, 5);
break;
s = expr_end;
CHECK_FIELD (num, 64, 1, strict);
+ SAVE_IMMEDIATE(num);
num--;
opcode |= (num & 0x20) << 3;
num = 31 - (num & 0x1f);
break;
s = expr_end;
CHECK_FIELD (num, 64, 1, strict);
+ SAVE_IMMEDIATE(num);
num--;
opcode |= (num & 0x20) << 7;
num = 31 - (num & 0x1f);
break;
s = expr_end;
CHECK_FIELD (num, 31, 0, strict);
+ SAVE_IMMEDIATE(num);
INSERT_FIELD_AND_CONTINUE (opcode, num, 5);
/* Handle a 6 bit bit position at 20,22:26. */
break;
s = expr_end;
CHECK_FIELD (num, 63, 0, strict);
+ SAVE_IMMEDIATE(num);
opcode |= (num & 0x20) << 6;
INSERT_FIELD_AND_CONTINUE (opcode, num & 0x1f, 5);
/* Handle all floating point registers. */
case 'f':
switch (*++args)
- {
+ {
/* Float target register. */
case 't':
if (!pa_parse_number (&s, 3))
CHECK_FIELD (num, 31, 0, 0);
if (the_insn.fpof1 == SGL)
{
- if (num < 16)
- {
+ if (num < 16)
+ {
as_bad (_("Invalid register for single precision fmpyadd or fmpysub"));
break;
- }
- num &= 0xF;
- num |= (pa_number & FP_REG_RSEL ? 1 << 4 : 0);
+ }
+ num &= 0xF;
+ num |= (pa_number & FP_REG_RSEL ? 1 << 4 : 0);
}
INSERT_FIELD_AND_CONTINUE (opcode, num, 16);
}
CHECK_FIELD (num, 31, 0, 0);
if (the_insn.fpof1 == SGL)
{
- if (num < 16)
- {
+ if (num < 16)
+ {
as_bad (_("Invalid register for single precision fmpyadd or fmpysub"));
break;
- }
- num &= 0xF;
- num |= (pa_number & FP_REG_RSEL ? 1 << 4 : 0);
+ }
+ num &= 0xF;
+ num |= (pa_number & FP_REG_RSEL ? 1 << 4 : 0);
}
INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
}
CHECK_FIELD (num, 31, 0, 0);
if (the_insn.fpof1 == SGL)
{
- if (num < 16)
- {
+ if (num < 16)
+ {
as_bad (_("Invalid register for single precision fmpyadd or fmpysub"));
break;
- }
- num &= 0xF;
- num |= (pa_number & FP_REG_RSEL ? 1 << 4 : 0);
+ }
+ num &= 0xF;
+ num |= (pa_number & FP_REG_RSEL ? 1 << 4 : 0);
}
INSERT_FIELD_AND_CONTINUE (opcode, num, 6);
}
CHECK_FIELD (num, 31, 0, 0);
if (the_insn.fpof1 == SGL)
{
- if (num < 16)
- {
+ if (num < 16)
+ {
as_bad (_("Invalid register for single precision fmpyadd or fmpysub"));
break;
- }
- num &= 0xF;
- num |= (pa_number & FP_REG_RSEL ? 1 << 4 : 0);
+ }
+ num &= 0xF;
+ num |= (pa_number & FP_REG_RSEL ? 1 << 4 : 0);
}
INSERT_FIELD_AND_CONTINUE (opcode, num, 11);
}
break;
}
+ if (immediate_check)
+ {
+ if (pos != -1 && len != -1 && pos < len - 1)
+ as_warn (_("Immediates %d and %d will give undefined behavior."),
+ pos, len);
+ }
+
the_insn.opcode = opcode;
}
char *to;
/* The had better be something to assemble. */
- assert (str);
+ gas_assert (str);
/* If we are within a procedure definition, make sure we've
defined a label for the procedure; handle case where the
demand_empty_rest_of_line ();
}
-/* Return TRUE if FRAG1 and FRAG2 are the same. */
-
-static bfd_boolean
-is_same_frag (fragS *frag1, fragS *frag2)
-{
-
- if (frag1 == NULL)
- return FALSE;
- else if (frag2 == NULL)
- return FALSE;
- else if (frag1 == frag2)
- return TRUE;
- else if (frag2->fr_type == rs_fill && frag2->fr_fix == 0)
- return (is_same_frag (frag1, frag2->fr_next));
- else
- return FALSE;
-}
-
#ifdef OBJ_ELF
/* Build an entry in the UNWIND subspace from the given function
attributes in CALL_INFO. This is not needed for SOM as using
last_call_info->ci_unwind.descriptor.save_sp = 1;
}
/* Is this an unwindable procedure. If so mark it so
- in the unwind descriptor. */
+ in the unwind descriptor. */
else if ((strncasecmp (name, "no_unwind", 9) == 0))
{
p = input_line_pointer;
last_call_info->ci_unwind.descriptor.cannot_unwind = 1;
}
/* Is this an interrupt routine. If so mark it in the
- unwind descriptor. */
+ unwind descriptor. */
else if ((strncasecmp (name, "hpux_int", 7) == 0))
{
p = input_line_pointer;
}
/* This is different than the standard GAS s_comm(). On HP9000/800 machines,
- the .comm pseudo-op has the following symtax:
+ the .comm pseudo-op has the following syntax:
<label> .comm <length>
S_SET_EXTERNAL (symbol);
/* colon() has already set the frag to the current location in the
- current subspace; we need to reset the fragment to the zero address
- fragment. We also need to reset the segment pointer. */
+ current subspace; we need to reset the fragment to the zero address
+ fragment. We also need to reset the segment pointer. */
symbol_set_frag (symbol, &zero_address_frag);
}
demand_empty_rest_of_line ();
symbolP = symbol_new (name, now_seg, (valueT) (frag_now_fix () - 4),
frag_now);
- assert (symbolP);
+ gas_assert (symbolP);
S_CLEAR_EXTERNAL (symbolP);
symbol_table_insert (symbolP);
}
{
input_line_pointer += 4;
/* IMPORTing/EXPORTing CODE types for functions is meaningless for SOM,
- instead one should be IMPORTing/EXPORTing ENTRY types.
+ instead one should be IMPORTing/EXPORTing ENTRY types.
- Complain if one tries to EXPORT a CODE type since that's never
- done. Both GCC and HP C still try to IMPORT CODE types, so
- silently fix them to be ENTRY types. */
+ Complain if one tries to EXPORT a CODE type since that's never
+ done. Both GCC and HP C still try to IMPORT CODE types, so
+ silently fix them to be ENTRY types. */
if (S_IS_FUNCTION (symbolP))
{
if (is_export)
else
{
/* OK. Set the external bits and process argument relocations.
- For the HP, weak and global are not mutually exclusive.
- S_SET_EXTERNAL will not set BSF_GLOBAL if WEAK is set.
- Call S_SET_EXTERNAL to get the other processing. Manually
- set BSF_GLOBAL when we get back. */
+ For the HP, weak and global are not mutually exclusive.
+ S_SET_EXTERNAL will not set BSF_GLOBAL if WEAK is set.
+ Call S_SET_EXTERNAL to get the other processing. Manually
+ set BSF_GLOBAL when we get back. */
S_SET_EXTERNAL (symbol);
symbol_get_bfdsym (symbol)->flags |= BSF_GLOBAL;
p = input_line_pointer;
print_errors = FALSE;
ptemp = input_line_pointer + 1;
/* First see if the space was specified as a number rather than
- as a name. According to the PA assembly manual the rest of
- the line should be ignored. */
+ as a name. According to the PA assembly manual the rest of
+ the line should be ignored. */
strict = 0;
pa_parse_number (&ptemp, 0);
if (pa_number >= 0)
else
{
/* Check for some of the predefined spaces. FIXME: most of the code
- below is repeated several times, can we extract the common parts
- and place them into a subroutine or something similar? */
+ below is repeated several times, can we extract the common parts
+ and place them into a subroutine or something similar? */
/* FIXME Is this (and the next IF stmt) really right?
What if INPUT_LINE_POINTER points to "$TEXT$FOO"? */
if (strncmp (input_line_pointer, "$TEXT$", 6) == 0)
else
ssd = is_defined_subspace (ss_name);
/* Allow user to override the builtin attributes of subspaces. But
- only allow the attributes to be changed once! */
+ only allow the attributes to be changed once! */
if (ssd && SUBSPACE_DEFINED (ssd))
{
subseg_set (ssd->ssd_seg, ssd->ssd_subseg);
}
/* We should be working with a new subspace now. Fill in
- any information as specified by the user. */
+ any information as specified by the user. */
if (!is_end_of_statement ())
{
input_line_pointer++;
}
/* Compute a reasonable set of BFD flags based on the information
- in the .subspace directive. */
+ in the .subspace directive. */
applicable = bfd_applicable_section_flags (stdoutput);
flags = 0;
if (loadable)
applicable &= flags;
/* If this is an existing subspace, then we want to use the
- segment already associated with the subspace.
+ segment already associated with the subspace.
- FIXME NOW! ELF BFD doesn't appear to be ready to deal with
- lots of sections. It might be a problem in the PA ELF
- code, I do not know yet. For now avoid creating anything
- but the "standard" sections for ELF. */
+ FIXME NOW! ELF BFD doesn't appear to be ready to deal with
+ lots of sections. It might be a problem in the PA ELF
+ code, I do not know yet. For now avoid creating anything
+ but the "standard" sections for ELF. */
if (create_new)
section = subseg_force_new (ss_name, 0);
else if (ssd)
pa_subspace_start (space, quadrant));
/* Now that all the flags are set, update an existing subspace,
- or create a new one. */
+ or create a new one. */
if (ssd)
current_subspace = update_subspace (space, ss_name, loadable,
sd_chain_struct *space;
/* Pick the right name for the new section and pick the right
- subsegment number. */
+ subsegment number. */
name = pa_def_subspaces[i].name;
subsegment = 0;
segment = subseg_new (name, subsegment);
/* For SOM we want to replace the standard .text, .data, and .bss
- sections with our own. We also want to set BFD flags for
+ sections with our own. We also want to set BFD flags for
all the built-in subspaces. */
if (!strcmp (pa_def_subspaces[i].name, "$CODE$"))
{
}
/* At this point we've found the correct place to add the new
- entry. So add it and update the linked lists as appropriate. */
+ entry. So add it and update the linked lists as appropriate. */
if (prev_chain_pointer)
{
chain_entry->sd_next = chain_pointer;
}
/* Now we have somewhere to put the new entry. Insert it and update
- the links. */
+ the links. */
if (prev_chain_pointer)
{
chain_entry->ssd_next = chain_pointer;
}
}
}
- stringer (append_zero);
+ stringer (8 + append_zero);
pa_undefine_label ();
}
dummy_symbol = symbol_find_or_make ("L$dummy");
S_SET_SEGMENT (dummy_symbol, text_section);
/* Force the symbol to be converted to a real symbol. */
- (void) symbol_get_bfdsym (dummy_symbol);
+ symbol_get_bfdsym (dummy_symbol)->flags |= BSF_KEEP;
#endif
}
return 1;
#endif
- assert (fixp->fx_addsy != NULL);
+ gas_assert (fixp->fx_addsy != NULL);
/* Ensure we emit a relocation for global symbols so that dynamic
linking works. */
{
p = regname + 2;
regnum = strtoul (p, &q, 10);
+#if TARGET_ARCH_SIZE == 64
if (p == q || *q || regnum <= 4 || regnum >= 32)
return -1;
regnum += 32 - 4;
+#else
+ if (p == q
+ || (*q && ((*q != 'L' && *q != 'R') || *(q + 1)))
+ || regnum <= 4 || regnum >= 32)
+ return -1;
+ regnum = (regnum - 4) * 2 + 32;
+ if (*q == 'R')
+ regnum++;
+#endif
}
return regnum;
}