From 89565f1b170960d4517a317cad04c17b3498240d Mon Sep 17 00:00:00 2001 From: Richard Sandiford Date: Sun, 14 Jul 2013 13:53:47 +0000 Subject: [PATCH] gas/ * config/tc-mips.c (parse_float_constant): Split out from... (mips_ip): ...here. --- gas/ChangeLog | 5 + gas/config/tc-mips.c | 334 ++++++++++++++++++++++--------------------- 2 files changed, 176 insertions(+), 163 deletions(-) diff --git a/gas/ChangeLog b/gas/ChangeLog index 6dcc1402ea8..4c9bc6b624a 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,8 @@ +2013-07-14 Richard Sandiford + + * config/tc-mips.c (parse_float_constant): Split out from... + (mips_ip): ...here. + 2013-07-14 Richard Sandiford * config/tc-mips.c (INSERT_BITS, INSERT_OPERAND, MIPS16_INSERT_OPERAND): diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c index d16075d268a..9f40684fab2 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 -- 2.34.1