summaryrefslogtreecommitdiff
path: root/gas/config
diff options
context:
space:
mode:
authorRichard Sandiford <rsandifo@nildram.co.uk>2013-07-14 13:53:47 +0000
committerRichard Sandiford <rsandifo@nildram.co.uk>2013-07-14 13:53:47 +0000
commit1c3af6e27747c5dc383924f1b95099e535ee6f86 (patch)
tree9b4595126efe65e5c21dc8443871a573f7f26de0 /gas/config
parent2c8c5e615b25bc85a865f16c8aa9b0f3e2abfa69 (diff)
downloadbinutils-redhat-1c3af6e27747c5dc383924f1b95099e535ee6f86.tar.gz
gas/
* config/tc-mips.c (parse_float_constant): Split out from... (mips_ip): ...here.
Diffstat (limited to 'gas/config')
-rw-r--r--gas/config/tc-mips.c334
1 files changed, 171 insertions, 163 deletions
diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c
index d16075d268..9f40684fab 100644
--- a/gas/config/tc-mips.c
+++ b/gas/config/tc-mips.c
@@ -4607,6 +4607,167 @@ match_tied_reg_operand (struct mips_arg_info *arg, char *s,
return s;
}
+/* Read a floating-point constant from S for LI.S or LI.D. LENGTH is
+ the length of the value in bytes (4 for float, 8 for double) and
+ USING_GPRS says whether the destination is a GPR rather than an FPR.
+
+ Return the constant in IMM and OFFSET as follows:
+
+ - If the constant should be loaded via memory, set IMM to O_absent and
+ OFFSET to the memory address.
+
+ - Otherwise, if the constant should be loaded into two 32-bit registers,
+ set IMM to the O_constant to load into the high register and OFFSET
+ to the corresponding value for the low register.
+
+ - Otherwise, set IMM to the full O_constant and set OFFSET to O_absent.
+
+ These constants only appear as the last operand in an instruction,
+ and every instruction that accepts them in any variant accepts them
+ in all variants. This means we don't have to worry about backing out
+ any changes if the instruction does not match. We just match
+ unconditionally and report an error if the constant is invalid. */
+
+static char *
+parse_float_constant (char *s, expressionS *imm, expressionS *offset,
+ int length, bfd_boolean using_gprs)
+{
+ char *save_in, *p, *err;
+ unsigned char data[8];
+ int atof_length;
+ segT seg, new_seg;
+ subsegT subseg;
+ const char *newname;
+
+ /* Where the constant is placed is based on how the MIPS assembler
+ does things:
+
+ length == 4 && using_gprs -- immediate value only
+ length == 8 && using_gprs -- .rdata or immediate value
+ length == 4 && !using_gprs -- .lit4 or immediate value
+ length == 8 && !using_gprs -- .lit8 or immediate value
+
+ The .lit4 and .lit8 sections are only used if permitted by the
+ -G argument. */
+ save_in = input_line_pointer;
+ input_line_pointer = s;
+ err = md_atof (length == 8 ? 'd' : 'f', (char *) data, &atof_length);
+ s = input_line_pointer;
+ input_line_pointer = save_in;
+ if (err && *err)
+ {
+ as_bad (_("Bad floating point constant: %s"), err);
+ memset (data, '\0', sizeof (data));
+ }
+ else
+ gas_assert (atof_length == length);
+
+ /* Handle 32-bit constants for which an immediate value is best. */
+ if (length == 4
+ && (using_gprs
+ || g_switch_value < 4
+ || (data[0] == 0 && data[1] == 0)
+ || (data[2] == 0 && data[3] == 0)))
+ {
+ imm->X_op = O_constant;
+ if (!target_big_endian)
+ imm->X_add_number = bfd_getl32 (data);
+ else
+ imm->X_add_number = bfd_getb32 (data);
+ offset->X_op = O_absent;
+ return s;
+ }
+
+ /* Handle 64-bit constants for which an immediate value is best. */
+ if (length == 8
+ && !mips_disable_float_construction
+ /* Constants can only be constructed in GPRs and copied
+ to FPRs if the GPRs are at least as wide as the FPRs.
+ Force the constant into memory if we are using 64-bit FPRs
+ but the GPRs are only 32 bits wide. */
+ /* ??? No longer true with the addition of MTHC1, but this
+ is legacy code... */
+ && (using_gprs || !(HAVE_64BIT_FPRS && HAVE_32BIT_GPRS))
+ && ((data[0] == 0 && data[1] == 0)
+ || (data[2] == 0 && data[3] == 0))
+ && ((data[4] == 0 && data[5] == 0)
+ || (data[6] == 0 && data[7] == 0)))
+ {
+ /* The value is simple enough to load with a couple of instructions.
+ If using 32-bit registers, set IMM to the high order 32 bits and
+ OFFSET to the low order 32 bits. Otherwise, set IMM to the entire
+ 64 bit constant. */
+ if (using_gprs ? HAVE_32BIT_GPRS : HAVE_32BIT_FPRS)
+ {
+ imm->X_op = O_constant;
+ offset->X_op = O_constant;
+ if (!target_big_endian)
+ {
+ imm->X_add_number = bfd_getl32 (data + 4);
+ offset->X_add_number = bfd_getl32 (data);
+ }
+ else
+ {
+ imm->X_add_number = bfd_getb32 (data);
+ offset->X_add_number = bfd_getb32 (data + 4);
+ }
+ if (offset->X_add_number == 0)
+ offset->X_op = O_absent;
+ }
+ else
+ {
+ imm->X_op = O_constant;
+ if (!target_big_endian)
+ imm->X_add_number = bfd_getl64 (data);
+ else
+ imm->X_add_number = bfd_getb64 (data);
+ offset->X_op = O_absent;
+ }
+ return s;
+ }
+
+ /* Switch to the right section. */
+ seg = now_seg;
+ subseg = now_subseg;
+ if (length == 4)
+ {
+ gas_assert (!using_gprs && g_switch_value >= 4);
+ newname = ".lit4";
+ }
+ else
+ {
+ if (using_gprs || g_switch_value < 8)
+ newname = RDATA_SECTION_NAME;
+ else
+ newname = ".lit8";
+ }
+
+ new_seg = subseg_new (newname, (subsegT) 0);
+ bfd_set_section_flags (stdoutput, new_seg,
+ SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA);
+ frag_align (length == 4 ? 2 : 3, 0, 0);
+ if (strncmp (TARGET_OS, "elf", 3) != 0)
+ record_alignment (new_seg, 4);
+ else
+ record_alignment (new_seg, length == 4 ? 2 : 3);
+ if (seg == now_seg)
+ as_bad (_("Can't use floating point insn in this section"));
+
+ /* Set the argument to the current address in the section. */
+ imm->X_op = O_absent;
+ offset->X_op = O_symbol;
+ offset->X_add_symbol = symbol_temp_new_now ();
+ offset->X_add_number = 0;
+
+ /* Put the floating point number into the section. */
+ p = frag_more (length);
+ memcpy (p, data, length);
+
+ /* Switch back to the original section. */
+ subseg_set (seg, subseg);
+ return s;
+}
+
/* S is the text seen for ARG. Match it against OPERAND. Return the end
of the argument text if the match is successful, otherwise return null. */
@@ -11957,172 +12118,19 @@ mips_ip (char *str, struct mips_cl_insn *ip)
continue;
case 'F':
+ s = parse_float_constant (s, &imm_expr, &offset_expr, 8, TRUE);
+ continue;
+
case 'L':
- case 'f':
- case 'l':
- {
- int f64;
- int using_gprs;
- char *save_in;
- char *err;
- unsigned char temp[8];
- int len;
- unsigned int length;
- segT seg;
- subsegT subseg;
- char *p;
-
- /* These only appear as the last operand in an
- instruction, and every instruction that accepts
- them in any variant accepts them in all variants.
- This means we don't have to worry about backing out
- any changes if the instruction does not match.
-
- The difference between them is the size of the
- floating point constant and where it goes. For 'F'
- and 'L' the constant is 64 bits; for 'f' and 'l' it
- is 32 bits. Where the constant is placed is based
- on how the MIPS assembler does things:
- F -- .rdata
- L -- .lit8
- f -- immediate value
- l -- .lit4
-
- The .lit4 and .lit8 sections are only used if
- permitted by the -G argument.
-
- The code below needs to know whether the target register
- is 32 or 64 bits wide. It relies on the fact 'f' and
- 'F' are used with GPR-based instructions and 'l' and
- 'L' are used with FPR-based instructions. */
-
- f64 = *args == 'F' || *args == 'L';
- using_gprs = *args == 'F' || *args == 'f';
-
- save_in = input_line_pointer;
- input_line_pointer = s;
- err = md_atof (f64 ? 'd' : 'f', (char *) temp, &len);
- length = len;
- s = input_line_pointer;
- input_line_pointer = save_in;
- if (err != NULL && *err != '\0')
- {
- as_bad (_("Bad floating point constant: %s"), err);
- memset (temp, '\0', sizeof temp);
- length = f64 ? 8 : 4;
- }
+ s = parse_float_constant (s, &imm_expr, &offset_expr, 8, FALSE);
+ continue;
- gas_assert (length == (unsigned) (f64 ? 8 : 4));
+ case 'f':
+ s = parse_float_constant (s, &imm_expr, &offset_expr, 4, TRUE);
+ continue;
- if (*args == 'f'
- || (*args == 'l'
- && (g_switch_value < 4
- || (temp[0] == 0 && temp[1] == 0)
- || (temp[2] == 0 && temp[3] == 0))))
- {
- imm_expr.X_op = O_constant;
- if (!target_big_endian)
- imm_expr.X_add_number = bfd_getl32 (temp);
- else
- imm_expr.X_add_number = bfd_getb32 (temp);
- }
- else if (length > 4
- && !mips_disable_float_construction
- /* Constants can only be constructed in GPRs and
- copied to FPRs if the GPRs are at least as wide
- as the FPRs. Force the constant into memory if
- we are using 64-bit FPRs but the GPRs are only
- 32 bits wide. */
- && (using_gprs
- || !(HAVE_64BIT_FPRS && HAVE_32BIT_GPRS))
- && ((temp[0] == 0 && temp[1] == 0)
- || (temp[2] == 0 && temp[3] == 0))
- && ((temp[4] == 0 && temp[5] == 0)
- || (temp[6] == 0 && temp[7] == 0)))
- {
- /* The value is simple enough to load with a couple of
- instructions. If using 32-bit registers, set
- imm_expr to the high order 32 bits and offset_expr to
- the low order 32 bits. Otherwise, set imm_expr to
- the entire 64 bit constant. */
- if (using_gprs ? HAVE_32BIT_GPRS : HAVE_32BIT_FPRS)
- {
- imm_expr.X_op = O_constant;
- offset_expr.X_op = O_constant;
- if (!target_big_endian)
- {
- imm_expr.X_add_number = bfd_getl32 (temp + 4);
- offset_expr.X_add_number = bfd_getl32 (temp);
- }
- else
- {
- imm_expr.X_add_number = bfd_getb32 (temp);
- offset_expr.X_add_number = bfd_getb32 (temp + 4);
- }
- if (offset_expr.X_add_number == 0)
- offset_expr.X_op = O_absent;
- }
- else
- {
- imm_expr.X_op = O_constant;
- if (!target_big_endian)
- imm_expr.X_add_number = bfd_getl64 (temp);
- else
- imm_expr.X_add_number = bfd_getb64 (temp);
- }
- }
- else
- {
- const char *newname;
- segT new_seg;
-
- /* Switch to the right section. */
- seg = now_seg;
- subseg = now_subseg;
- switch (*args)
- {
- default: /* unused default case avoids warnings. */
- case 'L':
- newname = RDATA_SECTION_NAME;
- if (g_switch_value >= 8)
- newname = ".lit8";
- break;
- case 'F':
- newname = RDATA_SECTION_NAME;
- break;
- case 'l':
- gas_assert (g_switch_value >= 4);
- newname = ".lit4";
- break;
- }
- new_seg = subseg_new (newname, (subsegT) 0);
- bfd_set_section_flags (stdoutput, new_seg,
- (SEC_ALLOC
- | SEC_LOAD
- | SEC_READONLY
- | SEC_DATA));
- frag_align (*args == 'l' ? 2 : 3, 0, 0);
- if (strncmp (TARGET_OS, "elf", 3) != 0)
- record_alignment (new_seg, 4);
- else
- record_alignment (new_seg, *args == 'l' ? 2 : 3);
- if (seg == now_seg)
- as_bad (_("Can't use floating point insn in this section"));
-
- /* Set the argument to the current address in the
- section. */
- offset_expr.X_op = O_symbol;
- offset_expr.X_add_symbol = symbol_temp_new_now ();
- offset_expr.X_add_number = 0;
-
- /* Put the floating point number into the section. */
- p = frag_more ((int) length);
- memcpy (p, temp, length);
-
- /* Switch back to the original section. */
- subseg_set (seg, subseg);
- }
- }
+ case 'l':
+ s = parse_float_constant (s, &imm_expr, &offset_expr, 4, FALSE);
continue;
/* ??? This is the traditional behavior, but is flaky if