diff options
Diffstat (limited to 'gcc/expmed.c')
-rw-r--r-- | gcc/expmed.c | 4315 |
1 files changed, 0 insertions, 4315 deletions
diff --git a/gcc/expmed.c b/gcc/expmed.c deleted file mode 100644 index 65fb007da57..00000000000 --- a/gcc/expmed.c +++ /dev/null @@ -1,4315 +0,0 @@ -/* Medium-level subroutines: convert bit-field store and extract - and shifts, multiplies and divides to rtl instructions. - Copyright (C) 1987, 88, 89, 92-6, 1997 Free Software Foundation, Inc. - -This file is part of GNU CC. - -GNU CC 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 2, or (at your option) -any later version. - -GNU CC 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 GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ - - -#include "config.h" -#include "rtl.h" -#include "tree.h" -#include "flags.h" -#include "insn-flags.h" -#include "insn-codes.h" -#include "insn-config.h" -#include "expr.h" -#include "real.h" -#include "recog.h" - -static void store_fixed_bit_field PROTO((rtx, int, int, int, rtx, int)); -static void store_split_bit_field PROTO((rtx, int, int, rtx, int)); -static rtx extract_fixed_bit_field PROTO((enum machine_mode, rtx, int, - int, int, rtx, int, int)); -static rtx mask_rtx PROTO((enum machine_mode, int, - int, int)); -static rtx lshift_value PROTO((enum machine_mode, rtx, - int, int)); -static rtx extract_split_bit_field PROTO((rtx, int, int, int, int)); - -#define CEIL(x,y) (((x) + (y) - 1) / (y)) - -/* Non-zero means divides or modulus operations are relatively cheap for - powers of two, so don't use branches; emit the operation instead. - Usually, this will mean that the MD file will emit non-branch - sequences. */ - -static int sdiv_pow2_cheap, smod_pow2_cheap; - -#ifndef SLOW_UNALIGNED_ACCESS -#define SLOW_UNALIGNED_ACCESS STRICT_ALIGNMENT -#endif - -/* For compilers that support multiple targets with different word sizes, - MAX_BITS_PER_WORD contains the biggest value of BITS_PER_WORD. An example - is the H8/300(H) compiler. */ - -#ifndef MAX_BITS_PER_WORD -#define MAX_BITS_PER_WORD BITS_PER_WORD -#endif - -/* Cost of various pieces of RTL. Note that some of these are indexed by shift count, - and some by mode. */ -static int add_cost, negate_cost, zero_cost; -static int shift_cost[MAX_BITS_PER_WORD]; -static int shiftadd_cost[MAX_BITS_PER_WORD]; -static int shiftsub_cost[MAX_BITS_PER_WORD]; -static int mul_cost[NUM_MACHINE_MODES]; -static int div_cost[NUM_MACHINE_MODES]; -static int mul_widen_cost[NUM_MACHINE_MODES]; -static int mul_highpart_cost[NUM_MACHINE_MODES]; - -void -init_expmed () -{ - char *free_point; - /* This is "some random pseudo register" for purposes of calling recog - to see what insns exist. */ - rtx reg = gen_rtx (REG, word_mode, 10000); - rtx shift_insn, shiftadd_insn, shiftsub_insn; - int dummy; - int m; - enum machine_mode mode, wider_mode; - - start_sequence (); - - /* Since we are on the permanent obstack, we must be sure we save this - spot AFTER we call start_sequence, since it will reuse the rtl it - makes. */ - - free_point = (char *) oballoc (0); - - zero_cost = rtx_cost (const0_rtx, 0); - add_cost = rtx_cost (gen_rtx (PLUS, word_mode, reg, reg), SET); - - shift_insn = emit_insn (gen_rtx (SET, VOIDmode, reg, - gen_rtx (ASHIFT, word_mode, reg, - const0_rtx))); - - shiftadd_insn = emit_insn (gen_rtx (SET, VOIDmode, reg, - gen_rtx (PLUS, word_mode, - gen_rtx (MULT, word_mode, - reg, const0_rtx), - reg))); - - shiftsub_insn = emit_insn (gen_rtx (SET, VOIDmode, reg, - gen_rtx (MINUS, word_mode, - gen_rtx (MULT, word_mode, - reg, const0_rtx), - reg))); - - init_recog (); - - shift_cost[0] = 0; - shiftadd_cost[0] = shiftsub_cost[0] = add_cost; - - for (m = 1; m < BITS_PER_WORD; m++) - { - shift_cost[m] = shiftadd_cost[m] = shiftsub_cost[m] = 32000; - - XEXP (SET_SRC (PATTERN (shift_insn)), 1) = GEN_INT (m); - if (recog (PATTERN (shift_insn), shift_insn, &dummy) >= 0) - shift_cost[m] = rtx_cost (SET_SRC (PATTERN (shift_insn)), SET); - - XEXP (XEXP (SET_SRC (PATTERN (shiftadd_insn)), 0), 1) - = GEN_INT ((HOST_WIDE_INT) 1 << m); - if (recog (PATTERN (shiftadd_insn), shiftadd_insn, &dummy) >= 0) - shiftadd_cost[m] = rtx_cost (SET_SRC (PATTERN (shiftadd_insn)), SET); - - XEXP (XEXP (SET_SRC (PATTERN (shiftsub_insn)), 0), 1) - = GEN_INT ((HOST_WIDE_INT) 1 << m); - if (recog (PATTERN (shiftsub_insn), shiftsub_insn, &dummy) >= 0) - shiftsub_cost[m] = rtx_cost (SET_SRC (PATTERN (shiftsub_insn)), SET); - } - - negate_cost = rtx_cost (gen_rtx (NEG, word_mode, reg), SET); - - sdiv_pow2_cheap - = (rtx_cost (gen_rtx (DIV, word_mode, reg, GEN_INT (32)), SET) - <= 2 * add_cost); - smod_pow2_cheap - = (rtx_cost (gen_rtx (MOD, word_mode, reg, GEN_INT (32)), SET) - <= 2 * add_cost); - - for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); - mode != VOIDmode; - mode = GET_MODE_WIDER_MODE (mode)) - { - reg = gen_rtx (REG, mode, 10000); - div_cost[(int) mode] = rtx_cost (gen_rtx (UDIV, mode, reg, reg), SET); - mul_cost[(int) mode] = rtx_cost (gen_rtx (MULT, mode, reg, reg), SET); - wider_mode = GET_MODE_WIDER_MODE (mode); - if (wider_mode != VOIDmode) - { - mul_widen_cost[(int) wider_mode] - = rtx_cost (gen_rtx (MULT, wider_mode, - gen_rtx (ZERO_EXTEND, wider_mode, reg), - gen_rtx (ZERO_EXTEND, wider_mode, reg)), - SET); - mul_highpart_cost[(int) mode] - = rtx_cost (gen_rtx (TRUNCATE, mode, - gen_rtx (LSHIFTRT, wider_mode, - gen_rtx (MULT, wider_mode, - gen_rtx (ZERO_EXTEND, wider_mode, reg), - gen_rtx (ZERO_EXTEND, wider_mode, reg)), - GEN_INT (GET_MODE_BITSIZE (mode)))), - SET); - } - } - - /* Free the objects we just allocated. */ - end_sequence (); - obfree (free_point); -} - -/* Return an rtx representing minus the value of X. - MODE is the intended mode of the result, - useful if X is a CONST_INT. */ - -rtx -negate_rtx (mode, x) - enum machine_mode mode; - rtx x; -{ - rtx result = simplify_unary_operation (NEG, mode, x, mode); - - if (result == 0) - result = expand_unop (mode, neg_optab, x, NULL_RTX, 0); - - return result; -} - -/* Generate code to store value from rtx VALUE - into a bit-field within structure STR_RTX - containing BITSIZE bits starting at bit BITNUM. - FIELDMODE is the machine-mode of the FIELD_DECL node for this field. - ALIGN is the alignment that STR_RTX is known to have, measured in bytes. - TOTAL_SIZE is the size of the structure in bytes, or -1 if varying. */ - -/* ??? Note that there are two different ideas here for how - to determine the size to count bits within, for a register. - One is BITS_PER_WORD, and the other is the size of operand 3 - of the insv pattern. (The latter assumes that an n-bit machine - will be able to insert bit fields up to n bits wide.) - It isn't certain that either of these is right. - extract_bit_field has the same quandary. */ - -rtx -store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size) - rtx str_rtx; - register int bitsize; - int bitnum; - enum machine_mode fieldmode; - rtx value; - int align; - int total_size; -{ - int unit = (GET_CODE (str_rtx) == MEM) ? BITS_PER_UNIT : BITS_PER_WORD; - register int offset = bitnum / unit; - register int bitpos = bitnum % unit; - register rtx op0 = str_rtx; - - if (GET_CODE (str_rtx) == MEM && ! MEM_IN_STRUCT_P (str_rtx)) - abort (); - - /* Discount the part of the structure before the desired byte. - We need to know how many bytes are safe to reference after it. */ - if (total_size >= 0) - total_size -= (bitpos / BIGGEST_ALIGNMENT - * (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); - - while (GET_CODE (op0) == SUBREG) - { - /* The following line once was done only if WORDS_BIG_ENDIAN, - but I think that is a mistake. WORDS_BIG_ENDIAN is - meaningful at a much higher level; when structures are copied - between memory and regs, the higher-numbered regs - always get higher addresses. */ - offset += SUBREG_WORD (op0); - /* We used to adjust BITPOS here, but now we do the whole adjustment - right after the loop. */ - op0 = SUBREG_REG (op0); - } - - /* If OP0 is a register, BITPOS must count within a word. - But as we have it, it counts within whatever size OP0 now has. - On a bigendian machine, these are not the same, so convert. */ - if (BYTES_BIG_ENDIAN - && GET_CODE (op0) != MEM - && unit > GET_MODE_BITSIZE (GET_MODE (op0))) - bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0)); - - value = protect_from_queue (value, 0); - - if (flag_force_mem) - value = force_not_mem (value); - - /* Note that the adjustment of BITPOS above has no effect on whether - BITPOS is 0 in a REG bigger than a word. */ - if (GET_MODE_SIZE (fieldmode) >= UNITS_PER_WORD - && (GET_CODE (op0) != MEM - || ! SLOW_UNALIGNED_ACCESS - || (offset * BITS_PER_UNIT % bitsize == 0 - && align % GET_MODE_SIZE (fieldmode) == 0)) - && bitpos == 0 && bitsize == GET_MODE_BITSIZE (fieldmode)) - { - /* Storing in a full-word or multi-word field in a register - can be done with just SUBREG. */ - if (GET_MODE (op0) != fieldmode) - { - if (GET_CODE (op0) == REG) - op0 = gen_rtx (SUBREG, fieldmode, op0, offset); - else - op0 = change_address (op0, fieldmode, - plus_constant (XEXP (op0, 0), offset)); - } - emit_move_insn (op0, value); - return value; - } - - /* Storing an lsb-aligned field in a register - can be done with a movestrict instruction. */ - - if (GET_CODE (op0) != MEM - && (BYTES_BIG_ENDIAN ? bitpos + bitsize == unit : bitpos == 0) - && bitsize == GET_MODE_BITSIZE (fieldmode) - && (GET_MODE (op0) == fieldmode - || (movstrict_optab->handlers[(int) fieldmode].insn_code - != CODE_FOR_nothing))) - { - /* Get appropriate low part of the value being stored. */ - if (GET_CODE (value) == CONST_INT || GET_CODE (value) == REG) - value = gen_lowpart (fieldmode, value); - else if (!(GET_CODE (value) == SYMBOL_REF - || GET_CODE (value) == LABEL_REF - || GET_CODE (value) == CONST)) - value = convert_to_mode (fieldmode, value, 0); - - if (GET_MODE (op0) == fieldmode) - emit_move_insn (op0, value); - else - { - int icode = movstrict_optab->handlers[(int) fieldmode].insn_code; - if(! (*insn_operand_predicate[icode][1]) (value, fieldmode)) - value = copy_to_mode_reg (fieldmode, value); - emit_insn (GEN_FCN (icode) - (gen_rtx (SUBREG, fieldmode, op0, offset), value)); - } - return value; - } - - /* Handle fields bigger than a word. */ - - if (bitsize > BITS_PER_WORD) - { - /* Here we transfer the words of the field - in the order least significant first. - This is because the most significant word is the one which may - be less than full. - However, only do that if the value is not BLKmode. */ - - int backwards = WORDS_BIG_ENDIAN && fieldmode != BLKmode; - - int nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD; - int i; - - /* This is the mode we must force value to, so that there will be enough - subwords to extract. Note that fieldmode will often (always?) be - VOIDmode, because that is what store_field uses to indicate that this - is a bit field, but passing VOIDmode to operand_subword_force will - result in an abort. */ - fieldmode = mode_for_size (nwords * BITS_PER_WORD, MODE_INT, 0); - - for (i = 0; i < nwords; i++) - { - /* If I is 0, use the low-order word in both field and target; - if I is 1, use the next to lowest word; and so on. */ - int wordnum = (backwards ? nwords - i - 1 : i); - int bit_offset = (backwards - ? MAX (bitsize - (i + 1) * BITS_PER_WORD, 0) - : i * BITS_PER_WORD); - store_bit_field (op0, MIN (BITS_PER_WORD, - bitsize - i * BITS_PER_WORD), - bitnum + bit_offset, word_mode, - operand_subword_force (value, wordnum, - (GET_MODE (value) == VOIDmode - ? fieldmode - : GET_MODE (value))), - align, total_size); - } - return value; - } - - /* From here on we can assume that the field to be stored in is - a full-word (whatever type that is), since it is shorter than a word. */ - - /* OFFSET is the number of words or bytes (UNIT says which) - from STR_RTX to the first word or byte containing part of the field. */ - - if (GET_CODE (op0) == REG) - { - if (offset != 0 - || GET_MODE_SIZE (GET_MODE (op0)) > UNITS_PER_WORD) - op0 = gen_rtx (SUBREG, TYPE_MODE (type_for_size (BITS_PER_WORD, 0)), - op0, offset); - offset = 0; - } - else - { - op0 = protect_from_queue (op0, 1); - } - - /* If VALUE is a floating-point mode, access it as an integer of the - corresponding size. This can occur on a machine with 64 bit registers - that uses SFmode for float. This can also occur for unaligned float - structure fields. */ - if (GET_MODE_CLASS (GET_MODE (value)) == MODE_FLOAT) - { - if (GET_CODE (value) != REG) - value = copy_to_reg (value); - value = gen_rtx (SUBREG, word_mode, value, 0); - } - - /* Now OFFSET is nonzero only if OP0 is memory - and is therefore always measured in bytes. */ - -#ifdef HAVE_insv - if (HAVE_insv - && GET_MODE (value) != BLKmode - && !(bitsize == 1 && GET_CODE (value) == CONST_INT) - /* Ensure insv's size is wide enough for this field. */ - && (GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_insv][3]) - >= bitsize) - && ! ((GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG) - && (bitsize + bitpos - > GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_insv][3])))) - { - int xbitpos = bitpos; - rtx value1; - rtx xop0 = op0; - rtx last = get_last_insn (); - rtx pat; - enum machine_mode maxmode - = insn_operand_mode[(int) CODE_FOR_insv][3]; - - int save_volatile_ok = volatile_ok; - volatile_ok = 1; - - /* If this machine's insv can only insert into a register, copy OP0 - into a register and save it back later. */ - /* This used to check flag_force_mem, but that was a serious - de-optimization now that flag_force_mem is enabled by -O2. */ - if (GET_CODE (op0) == MEM - && ! ((*insn_operand_predicate[(int) CODE_FOR_insv][0]) - (op0, VOIDmode))) - { - rtx tempreg; - enum machine_mode bestmode; - - /* Get the mode to use for inserting into this field. If OP0 is - BLKmode, get the smallest mode consistent with the alignment. If - OP0 is a non-BLKmode object that is no wider than MAXMODE, use its - mode. Otherwise, use the smallest mode containing the field. */ - - if (GET_MODE (op0) == BLKmode - || GET_MODE_SIZE (GET_MODE (op0)) > GET_MODE_SIZE (maxmode)) - bestmode - = get_best_mode (bitsize, bitnum, align * BITS_PER_UNIT, maxmode, - MEM_VOLATILE_P (op0)); - else - bestmode = GET_MODE (op0); - - if (bestmode == VOIDmode - || (SLOW_UNALIGNED_ACCESS && GET_MODE_SIZE (bestmode) > align)) - goto insv_loses; - - /* Adjust address to point to the containing unit of that mode. */ - unit = GET_MODE_BITSIZE (bestmode); - /* Compute offset as multiple of this unit, counting in bytes. */ - offset = (bitnum / unit) * GET_MODE_SIZE (bestmode); - bitpos = bitnum % unit; - op0 = change_address (op0, bestmode, - plus_constant (XEXP (op0, 0), offset)); - - /* Fetch that unit, store the bitfield in it, then store the unit. */ - tempreg = copy_to_reg (op0); - store_bit_field (tempreg, bitsize, bitpos, fieldmode, value, - align, total_size); - emit_move_insn (op0, tempreg); - return value; - } - volatile_ok = save_volatile_ok; - - /* Add OFFSET into OP0's address. */ - if (GET_CODE (xop0) == MEM) - xop0 = change_address (xop0, byte_mode, - plus_constant (XEXP (xop0, 0), offset)); - - /* If xop0 is a register, we need it in MAXMODE - to make it acceptable to the format of insv. */ - if (GET_CODE (xop0) == SUBREG) - /* We can't just change the mode, because this might clobber op0, - and we will need the original value of op0 if insv fails. */ - xop0 = gen_rtx (SUBREG, maxmode, SUBREG_REG (xop0), SUBREG_WORD (xop0)); - if (GET_CODE (xop0) == REG && GET_MODE (xop0) != maxmode) - xop0 = gen_rtx (SUBREG, maxmode, xop0, 0); - - /* On big-endian machines, we count bits from the most significant. - If the bit field insn does not, we must invert. */ - - if (BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN) - xbitpos = unit - bitsize - xbitpos; - - /* We have been counting XBITPOS within UNIT. - Count instead within the size of the register. */ - if (BITS_BIG_ENDIAN && GET_CODE (xop0) != MEM) - xbitpos += GET_MODE_BITSIZE (maxmode) - unit; - - unit = GET_MODE_BITSIZE (maxmode); - - /* Convert VALUE to maxmode (which insv insn wants) in VALUE1. */ - value1 = value; - if (GET_MODE (value) != maxmode) - { - if (GET_MODE_BITSIZE (GET_MODE (value)) >= bitsize) - { - /* Optimization: Don't bother really extending VALUE - if it has all the bits we will actually use. However, - if we must narrow it, be sure we do it correctly. */ - - if (GET_MODE_SIZE (GET_MODE (value)) < GET_MODE_SIZE (maxmode)) - { - /* Avoid making subreg of a subreg, or of a mem. */ - if (GET_CODE (value1) != REG) - value1 = copy_to_reg (value1); - value1 = gen_rtx (SUBREG, maxmode, value1, 0); - } - else - value1 = gen_lowpart (maxmode, value1); - } - else if (!CONSTANT_P (value)) - /* Parse phase is supposed to make VALUE's data type - match that of the component reference, which is a type - at least as wide as the field; so VALUE should have - a mode that corresponds to that type. */ - abort (); - } - - /* If this machine's insv insists on a register, - get VALUE1 into a register. */ - if (! ((*insn_operand_predicate[(int) CODE_FOR_insv][3]) - (value1, maxmode))) - value1 = force_reg (maxmode, value1); - - pat = gen_insv (xop0, GEN_INT (bitsize), GEN_INT (xbitpos), value1); - if (pat) - emit_insn (pat); - else - { - delete_insns_since (last); - store_fixed_bit_field (op0, offset, bitsize, bitpos, value, align); - } - } - else - insv_loses: -#endif - /* Insv is not available; store using shifts and boolean ops. */ - store_fixed_bit_field (op0, offset, bitsize, bitpos, value, align); - return value; -} - -/* Use shifts and boolean operations to store VALUE - into a bit field of width BITSIZE - in a memory location specified by OP0 except offset by OFFSET bytes. - (OFFSET must be 0 if OP0 is a register.) - The field starts at position BITPOS within the byte. - (If OP0 is a register, it may be a full word or a narrower mode, - but BITPOS still counts within a full word, - which is significant on bigendian machines.) - STRUCT_ALIGN is the alignment the structure is known to have (in bytes). - - Note that protect_from_queue has already been done on OP0 and VALUE. */ - -static void -store_fixed_bit_field (op0, offset, bitsize, bitpos, value, struct_align) - register rtx op0; - register int offset, bitsize, bitpos; - register rtx value; - int struct_align; -{ - register enum machine_mode mode; - int total_bits = BITS_PER_WORD; - rtx subtarget, temp; - int all_zero = 0; - int all_one = 0; - - if (! SLOW_UNALIGNED_ACCESS) - struct_align = BIGGEST_ALIGNMENT / BITS_PER_UNIT; - - /* There is a case not handled here: - a structure with a known alignment of just a halfword - and a field split across two aligned halfwords within the structure. - Or likewise a structure with a known alignment of just a byte - and a field split across two bytes. - Such cases are not supposed to be able to occur. */ - - if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG) - { - if (offset != 0) - abort (); - /* Special treatment for a bit field split across two registers. */ - if (bitsize + bitpos > BITS_PER_WORD) - { - store_split_bit_field (op0, bitsize, bitpos, - value, BITS_PER_WORD); - return; - } - } - else - { - /* Get the proper mode to use for this field. We want a mode that - includes the entire field. If such a mode would be larger than - a word, we won't be doing the extraction the normal way. */ - - mode = get_best_mode (bitsize, bitpos + offset * BITS_PER_UNIT, - struct_align * BITS_PER_UNIT, word_mode, - GET_CODE (op0) == MEM && MEM_VOLATILE_P (op0)); - - if (mode == VOIDmode) - { - /* The only way this should occur is if the field spans word - boundaries. */ - store_split_bit_field (op0, - bitsize, bitpos + offset * BITS_PER_UNIT, - value, struct_align); - return; - } - - total_bits = GET_MODE_BITSIZE (mode); - - /* Make sure bitpos is valid for the chosen mode. Adjust BITPOS to - be be in the range 0 to total_bits-1, and put any excess bytes in - OFFSET. */ - if (bitpos >= total_bits) - { - offset += (bitpos / total_bits) * (total_bits / BITS_PER_UNIT); - bitpos -= ((bitpos / total_bits) * (total_bits / BITS_PER_UNIT) - * BITS_PER_UNIT); - } - - /* Get ref to an aligned byte, halfword, or word containing the field. - Adjust BITPOS to be position within a word, - and OFFSET to be the offset of that word. - Then alter OP0 to refer to that word. */ - bitpos += (offset % (total_bits / BITS_PER_UNIT)) * BITS_PER_UNIT; - offset -= (offset % (total_bits / BITS_PER_UNIT)); - op0 = change_address (op0, mode, - plus_constant (XEXP (op0, 0), offset)); - } - - mode = GET_MODE (op0); - - /* Now MODE is either some integral mode for a MEM as OP0, - or is a full-word for a REG as OP0. TOTAL_BITS corresponds. - The bit field is contained entirely within OP0. - BITPOS is the starting bit number within OP0. - (OP0's mode may actually be narrower than MODE.) */ - - if (BYTES_BIG_ENDIAN) - /* BITPOS is the distance between our msb - and that of the containing datum. - Convert it to the distance from the lsb. */ - bitpos = total_bits - bitsize - bitpos; - - /* Now BITPOS is always the distance between our lsb - and that of OP0. */ - - /* Shift VALUE left by BITPOS bits. If VALUE is not constant, - we must first convert its mode to MODE. */ - - if (GET_CODE (value) == CONST_INT) - { - register HOST_WIDE_INT v = INTVAL (value); - - if (bitsize < HOST_BITS_PER_WIDE_INT) - v &= ((HOST_WIDE_INT) 1 << bitsize) - 1; - - if (v == 0) - all_zero = 1; - else if ((bitsize < HOST_BITS_PER_WIDE_INT - && v == ((HOST_WIDE_INT) 1 << bitsize) - 1) - || (bitsize == HOST_BITS_PER_WIDE_INT && v == -1)) - all_one = 1; - - value = lshift_value (mode, value, bitpos, bitsize); - } - else - { - int must_and = (GET_MODE_BITSIZE (GET_MODE (value)) != bitsize - && bitpos + bitsize != GET_MODE_BITSIZE (mode)); - - if (GET_MODE (value) != mode) - { - if ((GET_CODE (value) == REG || GET_CODE (value) == SUBREG) - && GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (value))) - value = gen_lowpart (mode, value); - else - value = convert_to_mode (mode, value, 1); - } - - if (must_and) - value = expand_binop (mode, and_optab, value, - mask_rtx (mode, 0, bitsize, 0), - NULL_RTX, 1, OPTAB_LIB_WIDEN); - if (bitpos > 0) - value = expand_shift (LSHIFT_EXPR, mode, value, - build_int_2 (bitpos, 0), NULL_RTX, 1); - } - - /* Now clear the chosen bits in OP0, - except that if VALUE is -1 we need not bother. */ - - subtarget = (GET_CODE (op0) == REG || ! flag_force_mem) ? op0 : 0; - - if (! all_one) - { - temp = expand_binop (mode, and_optab, op0, - mask_rtx (mode, bitpos, bitsize, 1), - subtarget, 1, OPTAB_LIB_WIDEN); - subtarget = temp; - } - else - temp = op0; - - /* Now logical-or VALUE into OP0, unless it is zero. */ - - if (! all_zero) - temp = expand_binop (mode, ior_optab, temp, value, - subtarget, 1, OPTAB_LIB_WIDEN); - if (op0 != temp) - emit_move_insn (op0, temp); -} - -/* Store a bit field that is split across multiple accessible memory objects. - - OP0 is the REG, SUBREG or MEM rtx for the first of the objects. - BITSIZE is the field width; BITPOS the position of its first bit - (within the word). - VALUE is the value to store. - ALIGN is the known alignment of OP0, measured in bytes. - This is also the size of the memory objects to be used. - - This does not yet handle fields wider than BITS_PER_WORD. */ - -static void -store_split_bit_field (op0, bitsize, bitpos, value, align) - rtx op0; - int bitsize, bitpos; - rtx value; - int align; -{ - int unit; - int bitsdone = 0; - - /* Make sure UNIT isn't larger than BITS_PER_WORD, we can only handle that - much at a time. */ - if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG) - unit = BITS_PER_WORD; - else - unit = MIN (align * BITS_PER_UNIT, BITS_PER_WORD); - - /* If VALUE is a constant other than a CONST_INT, get it into a register in - WORD_MODE. If we can do this using gen_lowpart_common, do so. Note - that VALUE might be a floating-point constant. */ - if (CONSTANT_P (value) && GET_CODE (value) != CONST_INT) - { - rtx word = gen_lowpart_common (word_mode, value); - - if (word && (value != word)) - value = word; - else - value = gen_lowpart_common (word_mode, - force_reg (GET_MODE (value) != VOIDmode - ? GET_MODE (value) - : word_mode, value)); - } - - while (bitsdone < bitsize) - { - int thissize; - rtx part, word; - int thispos; - int offset; - - offset = (bitpos + bitsdone) / unit; - thispos = (bitpos + bitsdone) % unit; - - /* THISSIZE must not overrun a word boundary. Otherwise, - store_fixed_bit_field will call us again, and we will mutually - recurse forever. */ - thissize = MIN (bitsize - bitsdone, BITS_PER_WORD); - thissize = MIN (thissize, unit - thispos); - - if (BYTES_BIG_ENDIAN) - { - int total_bits; - - /* We must do an endian conversion exactly the same way as it is - done in extract_bit_field, so that the two calls to - extract_fixed_bit_field will have comparable arguments. */ - if (GET_CODE (value) != MEM || GET_MODE (value) == BLKmode) - total_bits = BITS_PER_WORD; - else - total_bits = GET_MODE_BITSIZE (GET_MODE (value)); - - /* Fetch successively less significant portions. */ - if (GET_CODE (value) == CONST_INT) - part = GEN_INT (((unsigned HOST_WIDE_INT) (INTVAL (value)) - >> (bitsize - bitsdone - thissize)) - & (((HOST_WIDE_INT) 1 << thissize) - 1)); - else - /* The args are chosen so that the last part includes the - lsb. Give extract_bit_field the value it needs (with - endianness compensation) to fetch the piece we want. - - ??? We have no idea what the alignment of VALUE is, so - we have to use a guess. */ - part - = extract_fixed_bit_field - (word_mode, value, 0, thissize, - total_bits - bitsize + bitsdone, NULL_RTX, 1, - GET_MODE (value) == VOIDmode - ? UNITS_PER_WORD - : (GET_MODE (value) == BLKmode - ? 1 - : GET_MODE_ALIGNMENT (GET_MODE (value)) / BITS_PER_UNIT)); - } - else - { - /* Fetch successively more significant portions. */ - if (GET_CODE (value) == CONST_INT) - part = GEN_INT (((unsigned HOST_WIDE_INT) (INTVAL (value)) - >> bitsdone) - & (((HOST_WIDE_INT) 1 << thissize) - 1)); - else - part - = extract_fixed_bit_field - (word_mode, value, 0, thissize, bitsdone, NULL_RTX, 1, - GET_MODE (value) == VOIDmode - ? UNITS_PER_WORD - : (GET_MODE (value) == BLKmode - ? 1 - : GET_MODE_ALIGNMENT (GET_MODE (value)) / BITS_PER_UNIT)); - } - - /* If OP0 is a register, then handle OFFSET here. - - When handling multiword bitfields, extract_bit_field may pass - down a word_mode SUBREG of a larger REG for a bitfield that actually - crosses a word boundary. Thus, for a SUBREG, we must find - the current word starting from the base register. */ - if (GET_CODE (op0) == SUBREG) - { - word = operand_subword_force (SUBREG_REG (op0), - SUBREG_WORD (op0) + offset, - GET_MODE (SUBREG_REG (op0))); - offset = 0; - } - else if (GET_CODE (op0) == REG) - { - word = operand_subword_force (op0, offset, GET_MODE (op0)); - offset = 0; - } - else - word = op0; - - /* OFFSET is in UNITs, and UNIT is in bits. - store_fixed_bit_field wants offset in bytes. */ - store_fixed_bit_field (word, offset * unit / BITS_PER_UNIT, - thissize, thispos, part, align); - bitsdone += thissize; - } -} - -/* Generate code to extract a byte-field from STR_RTX - containing BITSIZE bits, starting at BITNUM, - and put it in TARGET if possible (if TARGET is nonzero). - Regardless of TARGET, we return the rtx for where the value is placed. - It may be a QUEUED. - - STR_RTX is the structure containing the byte (a REG or MEM). - UNSIGNEDP is nonzero if this is an unsigned bit field. - MODE is the natural mode of the field value once extracted. - TMODE is the mode the caller would like the value to have; - but the value may be returned with type MODE instead. - - ALIGN is the alignment that STR_RTX is known to have, measured in bytes. - TOTAL_SIZE is the size in bytes of the containing structure, - or -1 if varying. - - If a TARGET is specified and we can store in it at no extra cost, - we do so, and return TARGET. - Otherwise, we return a REG of mode TMODE or MODE, with TMODE preferred - if they are equally easy. */ - -rtx -extract_bit_field (str_rtx, bitsize, bitnum, unsignedp, - target, mode, tmode, align, total_size) - rtx str_rtx; - register int bitsize; - int bitnum; - int unsignedp; - rtx target; - enum machine_mode mode, tmode; - int align; - int total_size; -{ - int unit = (GET_CODE (str_rtx) == MEM) ? BITS_PER_UNIT : BITS_PER_WORD; - register int offset = bitnum / unit; - register int bitpos = bitnum % unit; - register rtx op0 = str_rtx; - rtx spec_target = target; - rtx spec_target_subreg = 0; - - /* Discount the part of the structure before the desired byte. - We need to know how many bytes are safe to reference after it. */ - if (total_size >= 0) - total_size -= (bitpos / BIGGEST_ALIGNMENT - * (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); - - if (tmode == VOIDmode) - tmode = mode; - while (GET_CODE (op0) == SUBREG) - { - int outer_size = GET_MODE_BITSIZE (GET_MODE (op0)); - int inner_size = GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0))); - - offset += SUBREG_WORD (op0); - - if (BYTES_BIG_ENDIAN && (outer_size < inner_size)) - { - bitpos += inner_size - outer_size; - if (bitpos > unit) - { - offset += (bitpos / unit); - bitpos %= unit; - } - } - - op0 = SUBREG_REG (op0); - } - - /* ??? We currently assume TARGET is at least as big as BITSIZE. - If that's wrong, the solution is to test for it and set TARGET to 0 - if needed. */ - - /* If OP0 is a register, BITPOS must count within a word. - But as we have it, it counts within whatever size OP0 now has. - On a bigendian machine, these are not the same, so convert. */ - if (BYTES_BIG_ENDIAN && - GET_CODE (op0) != MEM - && unit > GET_MODE_BITSIZE (GET_MODE (op0))) - bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0)); - - /* Extracting a full-word or multi-word value - from a structure in a register or aligned memory. - This can be done with just SUBREG. - So too extracting a subword value in - the least significant part of the register. */ - - if (((GET_CODE (op0) == REG - && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode), - GET_MODE_BITSIZE (GET_MODE (op0)))) - || (GET_CODE (op0) == MEM - && (! SLOW_UNALIGNED_ACCESS - || (offset * BITS_PER_UNIT % bitsize == 0 - && align * BITS_PER_UNIT % bitsize == 0)))) - && ((bitsize >= BITS_PER_WORD && bitsize == GET_MODE_BITSIZE (mode) - && bitpos % BITS_PER_WORD == 0) - || (mode_for_size (bitsize, GET_MODE_CLASS (tmode), 0) != BLKmode - && (BYTES_BIG_ENDIAN - ? bitpos + bitsize == BITS_PER_WORD - : bitpos == 0)))) - { - enum machine_mode mode1 - = mode_for_size (bitsize, GET_MODE_CLASS (tmode), 0); - - if (mode1 != GET_MODE (op0)) - { - if (GET_CODE (op0) == REG) - op0 = gen_rtx (SUBREG, mode1, op0, offset); - else - op0 = change_address (op0, mode1, - plus_constant (XEXP (op0, 0), offset)); - } - if (mode1 != mode) - return convert_to_mode (tmode, op0, unsignedp); - return op0; - } - - /* Handle fields bigger than a word. */ - - if (bitsize > BITS_PER_WORD) - { - /* Here we transfer the words of the field - in the order least significant first. - This is because the most significant word is the one which may - be less than full. */ - - int nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD; - int i; - - if (target == 0 || GET_CODE (target) != REG) - target = gen_reg_rtx (mode); - - /* Indicate for flow that the entire target reg is being set. */ - emit_insn (gen_rtx (CLOBBER, VOIDmode, target)); - - for (i = 0; i < nwords; i++) - { - /* If I is 0, use the low-order word in both field and target; - if I is 1, use the next to lowest word; and so on. */ - /* Word number in TARGET to use. */ - int wordnum = (WORDS_BIG_ENDIAN - ? GET_MODE_SIZE (GET_MODE (target)) / UNITS_PER_WORD - i - 1 - : i); - /* Offset from start of field in OP0. */ - int bit_offset = (WORDS_BIG_ENDIAN - ? MAX (0, bitsize - (i + 1) * BITS_PER_WORD) - : i * BITS_PER_WORD); - rtx target_part = operand_subword (target, wordnum, 1, VOIDmode); - rtx result_part - = extract_bit_field (op0, MIN (BITS_PER_WORD, - bitsize - i * BITS_PER_WORD), - bitnum + bit_offset, - 1, target_part, mode, word_mode, - align, total_size); - - if (target_part == 0) - abort (); - - if (result_part != target_part) - emit_move_insn (target_part, result_part); - } - - if (unsignedp) - { - /* Unless we've filled TARGET, the upper regs in a multi-reg value - need to be zero'd out. */ - if (GET_MODE_SIZE (GET_MODE (target)) > nwords * UNITS_PER_WORD) - { - int i,total_words; - - total_words = GET_MODE_SIZE (GET_MODE (target)) / UNITS_PER_WORD; - for (i = nwords; i < total_words; i++) - { - int wordnum = WORDS_BIG_ENDIAN ? total_words - i - 1 : i; - rtx target_part = operand_subword (target, wordnum, 1, VOIDmode); - emit_move_insn (target_part, const0_rtx); - } - } - return target; - } - - /* Signed bit field: sign-extend with two arithmetic shifts. */ - target = expand_shift (LSHIFT_EXPR, mode, target, - build_int_2 (GET_MODE_BITSIZE (mode) - bitsize, 0), - NULL_RTX, 0); - return expand_shift (RSHIFT_EXPR, mode, target, - build_int_2 (GET_MODE_BITSIZE (mode) - bitsize, 0), - NULL_RTX, 0); - } - - /* From here on we know the desired field is smaller than a word - so we can assume it is an integer. So we can safely extract it as one - size of integer, if necessary, and then truncate or extend - to the size that is wanted. */ - - /* OFFSET is the number of words or bytes (UNIT says which) - from STR_RTX to the first word or byte containing part of the field. */ - - if (GET_CODE (op0) == REG) - { - if (offset != 0 - || GET_MODE_SIZE (GET_MODE (op0)) > UNITS_PER_WORD) - op0 = gen_rtx (SUBREG, TYPE_MODE (type_for_size (BITS_PER_WORD, 0)), - op0, offset); - offset = 0; - } - else - { - op0 = protect_from_queue (str_rtx, 1); - } - - /* Now OFFSET is nonzero only for memory operands. */ - - if (unsignedp) - { -#ifdef HAVE_extzv - if (HAVE_extzv - && (GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_extzv][0]) - >= bitsize) - && ! ((GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG) - && (bitsize + bitpos - > GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_extzv][0])))) - { - int xbitpos = bitpos, xoffset = offset; - rtx bitsize_rtx, bitpos_rtx; - rtx last = get_last_insn(); - rtx xop0 = op0; - rtx xtarget = target; - rtx xspec_target = spec_target; - rtx xspec_target_subreg = spec_target_subreg; - rtx pat; - enum machine_mode maxmode - = insn_operand_mode[(int) CODE_FOR_extzv][0]; - - if (GET_CODE (xop0) == MEM) - { - int save_volatile_ok = volatile_ok; - volatile_ok = 1; - - /* Is the memory operand acceptable? */ - if (flag_force_mem - || ! ((*insn_operand_predicate[(int) CODE_FOR_extzv][1]) - (xop0, GET_MODE (xop0)))) - { - /* No, load into a reg and extract from there. */ - enum machine_mode bestmode; - - /* Get the mode to use for inserting into this field. If - OP0 is BLKmode, get the smallest mode consistent with the - alignment. If OP0 is a non-BLKmode object that is no - wider than MAXMODE, use its mode. Otherwise, use the - smallest mode containing the field. */ - - if (GET_MODE (xop0) == BLKmode - || (GET_MODE_SIZE (GET_MODE (op0)) - > GET_MODE_SIZE (maxmode))) - bestmode = get_best_mode (bitsize, bitnum, - align * BITS_PER_UNIT, maxmode, - MEM_VOLATILE_P (xop0)); - else - bestmode = GET_MODE (xop0); - - if (bestmode == VOIDmode - || (SLOW_UNALIGNED_ACCESS && GET_MODE_SIZE (bestmode) > align)) - goto extzv_loses; - - /* Compute offset as multiple of this unit, - counting in bytes. */ - unit = GET_MODE_BITSIZE (bestmode); - xoffset = (bitnum / unit) * GET_MODE_SIZE (bestmode); - xbitpos = bitnum % unit; - xop0 = change_address (xop0, bestmode, - plus_constant (XEXP (xop0, 0), - xoffset)); - /* Fetch it to a register in that size. */ - xop0 = force_reg (bestmode, xop0); - - /* XBITPOS counts within UNIT, which is what is expected. */ - } - else - /* Get ref to first byte containing part of the field. */ - xop0 = change_address (xop0, byte_mode, - plus_constant (XEXP (xop0, 0), xoffset)); - - volatile_ok = save_volatile_ok; - } - - /* If op0 is a register, we need it in MAXMODE (which is usually - SImode). to make it acceptable to the format of extzv. */ - if (GET_CODE (xop0) == SUBREG && GET_MODE (xop0) != maxmode) - abort (); - if (GET_CODE (xop0) == REG && GET_MODE (xop0) != maxmode) - xop0 = gen_rtx (SUBREG, maxmode, xop0, 0); - - /* On big-endian machines, we count bits from the most significant. - If the bit field insn does not, we must invert. */ - if (BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN) - xbitpos = unit - bitsize - xbitpos; - - /* Now convert from counting within UNIT to counting in MAXMODE. */ - if (BITS_BIG_ENDIAN && GET_CODE (xop0) != MEM) - xbitpos += GET_MODE_BITSIZE (maxmode) - unit; - - unit = GET_MODE_BITSIZE (maxmode); - - if (xtarget == 0 - || (flag_force_mem && GET_CODE (xtarget) == MEM)) - xtarget = xspec_target = gen_reg_rtx (tmode); - - if (GET_MODE (xtarget) != maxmode) - { - if (GET_CODE (xtarget) == REG) - { - int wider = (GET_MODE_SIZE (maxmode) - > GET_MODE_SIZE (GET_MODE (xtarget))); - xtarget = gen_lowpart (maxmode, xtarget); - if (wider) - xspec_target_subreg = xtarget; - } - else - xtarget = gen_reg_rtx (maxmode); - } - - /* If this machine's extzv insists on a register target, - make sure we have one. */ - if (! ((*insn_operand_predicate[(int) CODE_FOR_extzv][0]) - (xtarget, maxmode))) - xtarget = gen_reg_rtx (maxmode); - - bitsize_rtx = GEN_INT (bitsize); - bitpos_rtx = GEN_INT (xbitpos); - - pat = gen_extzv (protect_from_queue (xtarget, 1), - xop0, bitsize_rtx, bitpos_rtx); - if (pat) - { - emit_insn (pat); - target = xtarget; - spec_target = xspec_target; - spec_target_subreg = xspec_target_subreg; - } - else - { - delete_insns_since (last); - target = extract_fixed_bit_field (tmode, op0, offset, bitsize, - bitpos, target, 1, align); - } - } - else - extzv_loses: -#endif - target = extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos, - target, 1, align); - } - else - { -#ifdef HAVE_extv - if (HAVE_extv - && (GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_extv][0]) - >= bitsize) - && ! ((GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG) - && (bitsize + bitpos - > GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_extv][0])))) - { - int xbitpos = bitpos, xoffset = offset; - rtx bitsize_rtx, bitpos_rtx; - rtx last = get_last_insn(); - rtx xop0 = op0, xtarget = target; - rtx xspec_target = spec_target; - rtx xspec_target_subreg = spec_target_subreg; - rtx pat; - enum machine_mode maxmode - = insn_operand_mode[(int) CODE_FOR_extv][0]; - - if (GET_CODE (xop0) == MEM) - { - /* Is the memory operand acceptable? */ - if (! ((*insn_operand_predicate[(int) CODE_FOR_extv][1]) - (xop0, GET_MODE (xop0)))) - { - /* No, load into a reg and extract from there. */ - enum machine_mode bestmode; - - /* Get the mode to use for inserting into this field. If - OP0 is BLKmode, get the smallest mode consistent with the - alignment. If OP0 is a non-BLKmode object that is no - wider than MAXMODE, use its mode. Otherwise, use the - smallest mode containing the field. */ - - if (GET_MODE (xop0) == BLKmode - || (GET_MODE_SIZE (GET_MODE (op0)) - > GET_MODE_SIZE (maxmode))) - bestmode = get_best_mode (bitsize, bitnum, - align * BITS_PER_UNIT, maxmode, - MEM_VOLATILE_P (xop0)); - else - bestmode = GET_MODE (xop0); - - if (bestmode == VOIDmode - || (SLOW_UNALIGNED_ACCESS && GET_MODE_SIZE (bestmode) > align)) - goto extv_loses; - - /* Compute offset as multiple of this unit, - counting in bytes. */ - unit = GET_MODE_BITSIZE (bestmode); - xoffset = (bitnum / unit) * GET_MODE_SIZE (bestmode); - xbitpos = bitnum % unit; - xop0 = change_address (xop0, bestmode, - plus_constant (XEXP (xop0, 0), - xoffset)); - /* Fetch it to a register in that size. */ - xop0 = force_reg (bestmode, xop0); - - /* XBITPOS counts within UNIT, which is what is expected. */ - } - else - /* Get ref to first byte containing part of the field. */ - xop0 = change_address (xop0, byte_mode, - plus_constant (XEXP (xop0, 0), xoffset)); - } - - /* If op0 is a register, we need it in MAXMODE (which is usually - SImode) to make it acceptable to the format of extv. */ - if (GET_CODE (xop0) == SUBREG && GET_MODE (xop0) != maxmode) - abort (); - if (GET_CODE (xop0) == REG && GET_MODE (xop0) != maxmode) - xop0 = gen_rtx (SUBREG, maxmode, xop0, 0); - - /* On big-endian machines, we count bits from the most significant. - If the bit field insn does not, we must invert. */ - if (BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN) - xbitpos = unit - bitsize - xbitpos; - - /* XBITPOS counts within a size of UNIT. - Adjust to count within a size of MAXMODE. */ - if (BITS_BIG_ENDIAN && GET_CODE (xop0) != MEM) - xbitpos += (GET_MODE_BITSIZE (maxmode) - unit); - - unit = GET_MODE_BITSIZE (maxmode); - - if (xtarget == 0 - || (flag_force_mem && GET_CODE (xtarget) == MEM)) - xtarget = xspec_target = gen_reg_rtx (tmode); - - if (GET_MODE (xtarget) != maxmode) - { - if (GET_CODE (xtarget) == REG) - { - int wider = (GET_MODE_SIZE (maxmode) - > GET_MODE_SIZE (GET_MODE (xtarget))); - xtarget = gen_lowpart (maxmode, xtarget); - if (wider) - xspec_target_subreg = xtarget; - } - else - xtarget = gen_reg_rtx (maxmode); - } - - /* If this machine's extv insists on a register target, - make sure we have one. */ - if (! ((*insn_operand_predicate[(int) CODE_FOR_extv][0]) - (xtarget, maxmode))) - xtarget = gen_reg_rtx (maxmode); - - bitsize_rtx = GEN_INT (bitsize); - bitpos_rtx = GEN_INT (xbitpos); - - pat = gen_extv (protect_from_queue (xtarget, 1), - xop0, bitsize_rtx, bitpos_rtx); - if (pat) - { - emit_insn (pat); - target = xtarget; - spec_target = xspec_target; - spec_target_subreg = xspec_target_subreg; - } - else - { - delete_insns_since (last); - target = extract_fixed_bit_field (tmode, op0, offset, bitsize, - bitpos, target, 0, align); - } - } - else - extv_loses: -#endif - target = extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos, - target, 0, align); - } - if (target == spec_target) - return target; - if (target == spec_target_subreg) - return spec_target; - if (GET_MODE (target) != tmode && GET_MODE (target) != mode) - { - /* If the target mode is floating-point, first convert to the - integer mode of that size and then access it as a floating-point - value via a SUBREG. */ - if (GET_MODE_CLASS (tmode) == MODE_FLOAT) - { - target = convert_to_mode (mode_for_size (GET_MODE_BITSIZE (tmode), - MODE_INT, 0), - target, unsignedp); - if (GET_CODE (target) != REG) - target = copy_to_reg (target); - return gen_rtx (SUBREG, tmode, target, 0); - } - else - return convert_to_mode (tmode, target, unsignedp); - } - return target; -} - -/* Extract a bit field using shifts and boolean operations - Returns an rtx to represent the value. - OP0 addresses a register (word) or memory (byte). - BITPOS says which bit within the word or byte the bit field starts in. - OFFSET says how many bytes farther the bit field starts; - it is 0 if OP0 is a register. - BITSIZE says how many bits long the bit field is. - (If OP0 is a register, it may be narrower than a full word, - but BITPOS still counts within a full word, - which is significant on bigendian machines.) - - UNSIGNEDP is nonzero for an unsigned bit field (don't sign-extend value). - If TARGET is nonzero, attempts to store the value there - and return TARGET, but this is not guaranteed. - If TARGET is not used, create a pseudo-reg of mode TMODE for the value. - - ALIGN is the alignment that STR_RTX is known to have, measured in bytes. */ - -static rtx -extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos, - target, unsignedp, align) - enum machine_mode tmode; - register rtx op0, target; - register int offset, bitsize, bitpos; - int unsignedp; - int align; -{ - int total_bits = BITS_PER_WORD; - enum machine_mode mode; - - if (GET_CODE (op0) == SUBREG || GET_CODE (op0) == REG) - { - /* Special treatment for a bit field split across two registers. */ - if (bitsize + bitpos > BITS_PER_WORD) - return extract_split_bit_field (op0, bitsize, bitpos, - unsignedp, align); - } - else - { - /* Get the proper mode to use for this field. We want a mode that - includes the entire field. If such a mode would be larger than - a word, we won't be doing the extraction the normal way. */ - - mode = get_best_mode (bitsize, bitpos + offset * BITS_PER_UNIT, - align * BITS_PER_UNIT, word_mode, - GET_CODE (op0) == MEM && MEM_VOLATILE_P (op0)); - - if (mode == VOIDmode) - /* The only way this should occur is if the field spans word - boundaries. */ - return extract_split_bit_field (op0, bitsize, - bitpos + offset * BITS_PER_UNIT, - unsignedp, align); - - total_bits = GET_MODE_BITSIZE (mode); - - /* Make sure bitpos is valid for the chosen mode. Adjust BITPOS to - be be in the range 0 to total_bits-1, and put any excess bytes in - OFFSET. */ - if (bitpos >= total_bits) - { - offset += (bitpos / total_bits) * (total_bits / BITS_PER_UNIT); - bitpos -= ((bitpos / total_bits) * (total_bits / BITS_PER_UNIT) - * BITS_PER_UNIT); - } - - /* Get ref to an aligned byte, halfword, or word containing the field. - Adjust BITPOS to be position within a word, - and OFFSET to be the offset of that word. - Then alter OP0 to refer to that word. */ - bitpos += (offset % (total_bits / BITS_PER_UNIT)) * BITS_PER_UNIT; - offset -= (offset % (total_bits / BITS_PER_UNIT)); - op0 = change_address (op0, mode, - plus_constant (XEXP (op0, 0), offset)); - } - - mode = GET_MODE (op0); - - if (BYTES_BIG_ENDIAN) - { - /* BITPOS is the distance between our msb and that of OP0. - Convert it to the distance from the lsb. */ - - bitpos = total_bits - bitsize - bitpos; - } - - /* Now BITPOS is always the distance between the field's lsb and that of OP0. - We have reduced the big-endian case to the little-endian case. */ - - if (unsignedp) - { - if (bitpos) - { - /* If the field does not already start at the lsb, - shift it so it does. */ - tree amount = build_int_2 (bitpos, 0); - /* Maybe propagate the target for the shift. */ - /* But not if we will return it--could confuse integrate.c. */ - rtx subtarget = (target != 0 && GET_CODE (target) == REG - && !REG_FUNCTION_VALUE_P (target) - ? target : 0); - if (tmode != mode) subtarget = 0; - op0 = expand_shift (RSHIFT_EXPR, mode, op0, amount, subtarget, 1); - } - /* Convert the value to the desired mode. */ - if (mode != tmode) - op0 = convert_to_mode (tmode, op0, 1); - - /* Unless the msb of the field used to be the msb when we shifted, - mask out the upper bits. */ - - if (GET_MODE_BITSIZE (mode) != bitpos + bitsize -#if 0 -#ifdef SLOW_ZERO_EXTEND - /* Always generate an `and' if - we just zero-extended op0 and SLOW_ZERO_EXTEND, since it - will combine fruitfully with the zero-extend. */ - || tmode != mode -#endif -#endif - ) - return expand_binop (GET_MODE (op0), and_optab, op0, - mask_rtx (GET_MODE (op0), 0, bitsize, 0), - target, 1, OPTAB_LIB_WIDEN); - return op0; - } - - /* To extract a signed bit-field, first shift its msb to the msb of the word, - then arithmetic-shift its lsb to the lsb of the word. */ - op0 = force_reg (mode, op0); - if (mode != tmode) - target = 0; - - /* Find the narrowest integer mode that contains the field. */ - - for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT); mode != VOIDmode; - mode = GET_MODE_WIDER_MODE (mode)) - if (GET_MODE_BITSIZE (mode) >= bitsize + bitpos) - { - op0 = convert_to_mode (mode, op0, 0); - break; - } - - if (GET_MODE_BITSIZE (mode) != (bitsize + bitpos)) - { - tree amount = build_int_2 (GET_MODE_BITSIZE (mode) - (bitsize + bitpos), 0); - /* Maybe propagate the target for the shift. */ - /* But not if we will return the result--could confuse integrate.c. */ - rtx subtarget = (target != 0 && GET_CODE (target) == REG - && ! REG_FUNCTION_VALUE_P (target) - ? target : 0); - op0 = expand_shift (LSHIFT_EXPR, mode, op0, amount, subtarget, 1); - } - - return expand_shift (RSHIFT_EXPR, mode, op0, - build_int_2 (GET_MODE_BITSIZE (mode) - bitsize, 0), - target, 0); -} - -/* Return a constant integer (CONST_INT or CONST_DOUBLE) mask value - of mode MODE with BITSIZE ones followed by BITPOS zeros, or the - complement of that if COMPLEMENT. The mask is truncated if - necessary to the width of mode MODE. The mask is zero-extended if - BITSIZE+BITPOS is too small for MODE. */ - -static rtx -mask_rtx (mode, bitpos, bitsize, complement) - enum machine_mode mode; - int bitpos, bitsize, complement; -{ - HOST_WIDE_INT masklow, maskhigh; - - if (bitpos < HOST_BITS_PER_WIDE_INT) - masklow = (HOST_WIDE_INT) -1 << bitpos; - else - masklow = 0; - - if (bitpos + bitsize < HOST_BITS_PER_WIDE_INT) - masklow &= ((unsigned HOST_WIDE_INT) -1 - >> (HOST_BITS_PER_WIDE_INT - bitpos - bitsize)); - - if (bitpos <= HOST_BITS_PER_WIDE_INT) - maskhigh = -1; - else - maskhigh = (HOST_WIDE_INT) -1 << (bitpos - HOST_BITS_PER_WIDE_INT); - - if (bitpos + bitsize > HOST_BITS_PER_WIDE_INT) - maskhigh &= ((unsigned HOST_WIDE_INT) -1 - >> (2 * HOST_BITS_PER_WIDE_INT - bitpos - bitsize)); - else - maskhigh = 0; - - if (complement) - { - maskhigh = ~maskhigh; - masklow = ~masklow; - } - - return immed_double_const (masklow, maskhigh, mode); -} - -/* Return a constant integer (CONST_INT or CONST_DOUBLE) rtx with the value - VALUE truncated to BITSIZE bits and then shifted left BITPOS bits. */ - -static rtx -lshift_value (mode, value, bitpos, bitsize) - enum machine_mode mode; - rtx value; - int bitpos, bitsize; -{ - unsigned HOST_WIDE_INT v = INTVAL (value); - HOST_WIDE_INT low, high; - - if (bitsize < HOST_BITS_PER_WIDE_INT) - v &= ~((HOST_WIDE_INT) -1 << bitsize); - - if (bitpos < HOST_BITS_PER_WIDE_INT) - { - low = v << bitpos; - high = (bitpos > 0 ? (v >> (HOST_BITS_PER_WIDE_INT - bitpos)) : 0); - } - else - { - low = 0; - high = v << (bitpos - HOST_BITS_PER_WIDE_INT); - } - - return immed_double_const (low, high, mode); -} - -/* Extract a bit field that is split across two words - and return an RTX for the result. - - OP0 is the REG, SUBREG or MEM rtx for the first of the two words. - BITSIZE is the field width; BITPOS, position of its first bit, in the word. - UNSIGNEDP is 1 if should zero-extend the contents; else sign-extend. - - ALIGN is the known alignment of OP0, measured in bytes. - This is also the size of the memory objects to be used. */ - -static rtx -extract_split_bit_field (op0, bitsize, bitpos, unsignedp, align) - rtx op0; - int bitsize, bitpos, unsignedp, align; -{ - int unit; - int bitsdone = 0; - rtx result; - int first = 1; - - /* Make sure UNIT isn't larger than BITS_PER_WORD, we can only handle that - much at a time. */ - if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG) - unit = BITS_PER_WORD; - else - unit = MIN (align * BITS_PER_UNIT, BITS_PER_WORD); - - while (bitsdone < bitsize) - { - int thissize; - rtx part, word; - int thispos; - int offset; - - offset = (bitpos + bitsdone) / unit; - thispos = (bitpos + bitsdone) % unit; - - /* THISSIZE must not overrun a word boundary. Otherwise, - extract_fixed_bit_field will call us again, and we will mutually - recurse forever. */ - thissize = MIN (bitsize - bitsdone, BITS_PER_WORD); - thissize = MIN (thissize, unit - thispos); - - /* If OP0 is a register, then handle OFFSET here. - - When handling multiword bitfields, extract_bit_field may pass - down a word_mode SUBREG of a larger REG for a bitfield that actually - crosses a word boundary. Thus, for a SUBREG, we must find - the current word starting from the base register. */ - if (GET_CODE (op0) == SUBREG) - { - word = operand_subword_force (SUBREG_REG (op0), - SUBREG_WORD (op0) + offset, - GET_MODE (SUBREG_REG (op0))); - offset = 0; - } - else if (GET_CODE (op0) == REG) - { - word = operand_subword_force (op0, offset, GET_MODE (op0)); - offset = 0; - } - else - word = op0; - - /* Extract the parts in bit-counting order, - whose meaning is determined by BYTES_PER_UNIT. - OFFSET is in UNITs, and UNIT is in bits. - extract_fixed_bit_field wants offset in bytes. */ - part = extract_fixed_bit_field (word_mode, word, - offset * unit / BITS_PER_UNIT, - thissize, thispos, 0, 1, align); - bitsdone += thissize; - - /* Shift this part into place for the result. */ - if (BYTES_BIG_ENDIAN) - { - if (bitsize != bitsdone) - part = expand_shift (LSHIFT_EXPR, word_mode, part, - build_int_2 (bitsize - bitsdone, 0), 0, 1); - } - else - { - if (bitsdone != thissize) - part = expand_shift (LSHIFT_EXPR, word_mode, part, - build_int_2 (bitsdone - thissize, 0), 0, 1); - } - - if (first) - result = part; - else - /* Combine the parts with bitwise or. This works - because we extracted each part as an unsigned bit field. */ - result = expand_binop (word_mode, ior_optab, part, result, NULL_RTX, 1, - OPTAB_LIB_WIDEN); - - first = 0; - } - - /* Unsigned bit field: we are done. */ - if (unsignedp) - return result; - /* Signed bit field: sign-extend with two arithmetic shifts. */ - result = expand_shift (LSHIFT_EXPR, word_mode, result, - build_int_2 (BITS_PER_WORD - bitsize, 0), - NULL_RTX, 0); - return expand_shift (RSHIFT_EXPR, word_mode, result, - build_int_2 (BITS_PER_WORD - bitsize, 0), NULL_RTX, 0); -} - -/* Add INC into TARGET. */ - -void -expand_inc (target, inc) - rtx target, inc; -{ - rtx value = expand_binop (GET_MODE (target), add_optab, - target, inc, - target, 0, OPTAB_LIB_WIDEN); - if (value != target) - emit_move_insn (target, value); -} - -/* Subtract DEC from TARGET. */ - -void -expand_dec (target, dec) - rtx target, dec; -{ - rtx value = expand_binop (GET_MODE (target), sub_optab, - target, dec, - target, 0, OPTAB_LIB_WIDEN); - if (value != target) - emit_move_insn (target, value); -} - -/* Output a shift instruction for expression code CODE, - with SHIFTED being the rtx for the value to shift, - and AMOUNT the tree for the amount to shift by. - Store the result in the rtx TARGET, if that is convenient. - If UNSIGNEDP is nonzero, do a logical shift; otherwise, arithmetic. - Return the rtx for where the value is. */ - -rtx -expand_shift (code, mode, shifted, amount, target, unsignedp) - enum tree_code code; - register enum machine_mode mode; - rtx shifted; - tree amount; - register rtx target; - int unsignedp; -{ - register rtx op1, temp = 0; - register int left = (code == LSHIFT_EXPR || code == LROTATE_EXPR); - register int rotate = (code == LROTATE_EXPR || code == RROTATE_EXPR); - int try; - - /* Previously detected shift-counts computed by NEGATE_EXPR - and shifted in the other direction; but that does not work - on all machines. */ - - op1 = expand_expr (amount, NULL_RTX, VOIDmode, 0); - -#ifdef SHIFT_COUNT_TRUNCATED - if (SHIFT_COUNT_TRUNCATED - && GET_CODE (op1) == CONST_INT - && (unsigned HOST_WIDE_INT) INTVAL (op1) >= GET_MODE_BITSIZE (mode)) - op1 = GEN_INT ((unsigned HOST_WIDE_INT) INTVAL (op1) - % GET_MODE_BITSIZE (mode)); -#endif - - if (op1 == const0_rtx) - return shifted; - - for (try = 0; temp == 0 && try < 3; try++) - { - enum optab_methods methods; - - if (try == 0) - methods = OPTAB_DIRECT; - else if (try == 1) - methods = OPTAB_WIDEN; - else - methods = OPTAB_LIB_WIDEN; - - if (rotate) - { - /* Widening does not work for rotation. */ - if (methods == OPTAB_WIDEN) - continue; - else if (methods == OPTAB_LIB_WIDEN) - { - /* If we have been unable to open-code this by a rotation, - do it as the IOR of two shifts. I.e., to rotate A - by N bits, compute (A << N) | ((unsigned) A >> (C - N)) - where C is the bitsize of A. - - It is theoretically possible that the target machine might - not be able to perform either shift and hence we would - be making two libcalls rather than just the one for the - shift (similarly if IOR could not be done). We will allow - this extremely unlikely lossage to avoid complicating the - code below. */ - - rtx subtarget = target == shifted ? 0 : target; - rtx temp1; - tree type = TREE_TYPE (amount); - tree new_amount = make_tree (type, op1); - tree other_amount - = fold (build (MINUS_EXPR, type, - convert (type, - build_int_2 (GET_MODE_BITSIZE (mode), - 0)), - amount)); - - shifted = force_reg (mode, shifted); - - temp = expand_shift (left ? LSHIFT_EXPR : RSHIFT_EXPR, - mode, shifted, new_amount, subtarget, 1); - temp1 = expand_shift (left ? RSHIFT_EXPR : LSHIFT_EXPR, - mode, shifted, other_amount, 0, 1); - return expand_binop (mode, ior_optab, temp, temp1, target, - unsignedp, methods); - } - - temp = expand_binop (mode, - left ? rotl_optab : rotr_optab, - shifted, op1, target, unsignedp, methods); - - /* If we don't have the rotate, but we are rotating by a constant - that is in range, try a rotate in the opposite direction. */ - - if (temp == 0 && GET_CODE (op1) == CONST_INT - && INTVAL (op1) > 0 && INTVAL (op1) < GET_MODE_BITSIZE (mode)) - temp = expand_binop (mode, - left ? rotr_optab : rotl_optab, - shifted, - GEN_INT (GET_MODE_BITSIZE (mode) - - INTVAL (op1)), - target, unsignedp, methods); - } - else if (unsignedp) - temp = expand_binop (mode, - left ? ashl_optab : lshr_optab, - shifted, op1, target, unsignedp, methods); - - /* Do arithmetic shifts. - Also, if we are going to widen the operand, we can just as well - use an arithmetic right-shift instead of a logical one. */ - if (temp == 0 && ! rotate - && (! unsignedp || (! left && methods == OPTAB_WIDEN))) - { - enum optab_methods methods1 = methods; - - /* If trying to widen a log shift to an arithmetic shift, - don't accept an arithmetic shift of the same size. */ - if (unsignedp) - methods1 = OPTAB_MUST_WIDEN; - - /* Arithmetic shift */ - - temp = expand_binop (mode, - left ? ashl_optab : ashr_optab, - shifted, op1, target, unsignedp, methods1); - } - - /* We used to try extzv here for logical right shifts, but that was - only useful for one machine, the VAX, and caused poor code - generation there for lshrdi3, so the code was deleted and a - define_expand for lshrsi3 was added to vax.md. */ - } - - if (temp == 0) - abort (); - return temp; -} - -enum alg_code { alg_zero, alg_m, alg_shift, - alg_add_t_m2, alg_sub_t_m2, - alg_add_factor, alg_sub_factor, - alg_add_t2_m, alg_sub_t2_m, - alg_add, alg_subtract, alg_factor, alg_shiftop }; - -/* This structure records a sequence of operations. - `ops' is the number of operations recorded. - `cost' is their total cost. - The operations are stored in `op' and the corresponding - logarithms of the integer coefficients in `log'. - - These are the operations: - alg_zero total := 0; - alg_m total := multiplicand; - alg_shift total := total * coeff - alg_add_t_m2 total := total + multiplicand * coeff; - alg_sub_t_m2 total := total - multiplicand * coeff; - alg_add_factor total := total * coeff + total; - alg_sub_factor total := total * coeff - total; - alg_add_t2_m total := total * coeff + multiplicand; - alg_sub_t2_m total := total * coeff - multiplicand; - - The first operand must be either alg_zero or alg_m. */ - -struct algorithm -{ - short cost; - short ops; - /* The size of the OP and LOG fields are not directly related to the - word size, but the worst-case algorithms will be if we have few - consecutive ones or zeros, i.e., a multiplicand like 10101010101... - In that case we will generate shift-by-2, add, shift-by-2, add,..., - in total wordsize operations. */ - enum alg_code op[MAX_BITS_PER_WORD]; - char log[MAX_BITS_PER_WORD]; -}; - -/* Compute and return the best algorithm for multiplying by T. - The algorithm must cost less than cost_limit - If retval.cost >= COST_LIMIT, no algorithm was found and all - other field of the returned struct are undefined. */ - -static void -synth_mult (alg_out, t, cost_limit) - struct algorithm *alg_out; - unsigned HOST_WIDE_INT t; - int cost_limit; -{ - int m; - struct algorithm *alg_in, *best_alg; - unsigned int cost; - unsigned HOST_WIDE_INT q; - - /* Indicate that no algorithm is yet found. If no algorithm - is found, this value will be returned and indicate failure. */ - alg_out->cost = cost_limit; - - if (cost_limit <= 0) - return; - - /* t == 1 can be done in zero cost. */ - if (t == 1) - { - alg_out->ops = 1; - alg_out->cost = 0; - alg_out->op[0] = alg_m; - return; - } - - /* t == 0 sometimes has a cost. If it does and it exceeds our limit, - fail now. */ - if (t == 0) - { - if (zero_cost >= cost_limit) - return; - else - { - alg_out->ops = 1; - alg_out->cost = zero_cost; - alg_out->op[0] = alg_zero; - return; - } - } - - /* We'll be needing a couple extra algorithm structures now. */ - - alg_in = (struct algorithm *)alloca (sizeof (struct algorithm)); - best_alg = (struct algorithm *)alloca (sizeof (struct algorithm)); - - /* If we have a group of zero bits at the low-order part of T, try - multiplying by the remaining bits and then doing a shift. */ - - if ((t & 1) == 0) - { - m = floor_log2 (t & -t); /* m = number of low zero bits */ - q = t >> m; - cost = shift_cost[m]; - synth_mult (alg_in, q, cost_limit - cost); - - cost += alg_in->cost; - if (cost < cost_limit) - { - struct algorithm *x; - x = alg_in, alg_in = best_alg, best_alg = x; - best_alg->log[best_alg->ops] = m; - best_alg->op[best_alg->ops] = alg_shift; - cost_limit = cost; - } - } - - /* If we have an odd number, add or subtract one. */ - if ((t & 1) != 0) - { - unsigned HOST_WIDE_INT w; - - for (w = 1; (w & t) != 0; w <<= 1) - ; - if (w > 2 - /* Reject the case where t is 3. - Thus we prefer addition in that case. */ - && t != 3) - { - /* T ends with ...111. Multiply by (T + 1) and subtract 1. */ - - cost = add_cost; - synth_mult (alg_in, t + 1, cost_limit - cost); - - cost += alg_in->cost; - if (cost < cost_limit) - { - struct algorithm *x; - x = alg_in, alg_in = best_alg, best_alg = x; - best_alg->log[best_alg->ops] = 0; - best_alg->op[best_alg->ops] = alg_sub_t_m2; - cost_limit = cost; - } - } - else - { - /* T ends with ...01 or ...011. Multiply by (T - 1) and add 1. */ - - cost = add_cost; - synth_mult (alg_in, t - 1, cost_limit - cost); - - cost += alg_in->cost; - if (cost < cost_limit) - { - struct algorithm *x; - x = alg_in, alg_in = best_alg, best_alg = x; - best_alg->log[best_alg->ops] = 0; - best_alg->op[best_alg->ops] = alg_add_t_m2; - cost_limit = cost; - } - } - } - - /* Look for factors of t of the form - t = q(2**m +- 1), 2 <= m <= floor(log2(t - 1)). - If we find such a factor, we can multiply by t using an algorithm that - multiplies by q, shift the result by m and add/subtract it to itself. - - We search for large factors first and loop down, even if large factors - are less probable than small; if we find a large factor we will find a - good sequence quickly, and therefore be able to prune (by decreasing - COST_LIMIT) the search. */ - - for (m = floor_log2 (t - 1); m >= 2; m--) - { - unsigned HOST_WIDE_INT d; - - d = ((unsigned HOST_WIDE_INT) 1 << m) + 1; - if (t % d == 0 && t > d) - { - cost = MIN (shiftadd_cost[m], add_cost + shift_cost[m]); - synth_mult (alg_in, t / d, cost_limit - cost); - - cost += alg_in->cost; - if (cost < cost_limit) - { - struct algorithm *x; - x = alg_in, alg_in = best_alg, best_alg = x; - best_alg->log[best_alg->ops] = m; - best_alg->op[best_alg->ops] = alg_add_factor; - cost_limit = cost; - } - /* Other factors will have been taken care of in the recursion. */ - break; - } - - d = ((unsigned HOST_WIDE_INT) 1 << m) - 1; - if (t % d == 0 && t > d) - { - cost = MIN (shiftsub_cost[m], add_cost + shift_cost[m]); - synth_mult (alg_in, t / d, cost_limit - cost); - - cost += alg_in->cost; - if (cost < cost_limit) - { - struct algorithm *x; - x = alg_in, alg_in = best_alg, best_alg = x; - best_alg->log[best_alg->ops] = m; - best_alg->op[best_alg->ops] = alg_sub_factor; - cost_limit = cost; - } - break; - } - } - - /* Try shift-and-add (load effective address) instructions, - i.e. do a*3, a*5, a*9. */ - if ((t & 1) != 0) - { - q = t - 1; - q = q & -q; - m = exact_log2 (q); - if (m >= 0) - { - cost = shiftadd_cost[m]; - synth_mult (alg_in, (t - 1) >> m, cost_limit - cost); - - cost += alg_in->cost; - if (cost < cost_limit) - { - struct algorithm *x; - x = alg_in, alg_in = best_alg, best_alg = x; - best_alg->log[best_alg->ops] = m; - best_alg->op[best_alg->ops] = alg_add_t2_m; - cost_limit = cost; - } - } - - q = t + 1; - q = q & -q; - m = exact_log2 (q); - if (m >= 0) - { - cost = shiftsub_cost[m]; - synth_mult (alg_in, (t + 1) >> m, cost_limit - cost); - - cost += alg_in->cost; - if (cost < cost_limit) - { - struct algorithm *x; - x = alg_in, alg_in = best_alg, best_alg = x; - best_alg->log[best_alg->ops] = m; - best_alg->op[best_alg->ops] = alg_sub_t2_m; - cost_limit = cost; - } - } - } - - /* If cost_limit has not decreased since we stored it in alg_out->cost, - we have not found any algorithm. */ - if (cost_limit == alg_out->cost) - return; - - /* If we are getting a too long sequence for `struct algorithm' - to record, make this search fail. */ - if (best_alg->ops == MAX_BITS_PER_WORD) - return; - - /* Copy the algorithm from temporary space to the space at alg_out. - We avoid using structure assignment because the majority of - best_alg is normally undefined, and this is a critical function. */ - alg_out->ops = best_alg->ops + 1; - alg_out->cost = cost_limit; - bcopy ((char *) best_alg->op, (char *) alg_out->op, - alg_out->ops * sizeof *alg_out->op); - bcopy ((char *) best_alg->log, (char *) alg_out->log, - alg_out->ops * sizeof *alg_out->log); -} - -/* Perform a multiplication and return an rtx for the result. - MODE is mode of value; OP0 and OP1 are what to multiply (rtx's); - TARGET is a suggestion for where to store the result (an rtx). - - We check specially for a constant integer as OP1. - If you want this check for OP0 as well, then before calling - you should swap the two operands if OP0 would be constant. */ - -rtx -expand_mult (mode, op0, op1, target, unsignedp) - enum machine_mode mode; - register rtx op0, op1, target; - int unsignedp; -{ - rtx const_op1 = op1; - - /* synth_mult does an `unsigned int' multiply. As long as the mode is - less than or equal in size to `unsigned int' this doesn't matter. - If the mode is larger than `unsigned int', then synth_mult works only - if the constant value exactly fits in an `unsigned int' without any - truncation. This means that multiplying by negative values does - not work; results are off by 2^32 on a 32 bit machine. */ - - /* If we are multiplying in DImode, it may still be a win - to try to work with shifts and adds. */ - if (GET_CODE (op1) == CONST_DOUBLE - && GET_MODE_CLASS (GET_MODE (op1)) == MODE_INT - && HOST_BITS_PER_INT >= BITS_PER_WORD - && CONST_DOUBLE_HIGH (op1) == 0) - const_op1 = GEN_INT (CONST_DOUBLE_LOW (op1)); - else if (HOST_BITS_PER_INT < GET_MODE_BITSIZE (mode) - && GET_CODE (op1) == CONST_INT - && INTVAL (op1) < 0) - const_op1 = 0; - - /* We used to test optimize here, on the grounds that it's better to - produce a smaller program when -O is not used. - But this causes such a terrible slowdown sometimes - that it seems better to use synth_mult always. */ - - if (const_op1 && GET_CODE (const_op1) == CONST_INT) - { - struct algorithm alg; - struct algorithm alg2; - HOST_WIDE_INT val = INTVAL (op1); - HOST_WIDE_INT val_so_far; - rtx insn; - int mult_cost; - enum {basic_variant, negate_variant, add_variant} variant = basic_variant; - - /* Try to do the computation three ways: multiply by the negative of OP1 - and then negate, do the multiplication directly, or do multiplication - by OP1 - 1. */ - - mult_cost = rtx_cost (gen_rtx (MULT, mode, op0, op1), SET); - mult_cost = MIN (12 * add_cost, mult_cost); - - synth_mult (&alg, val, mult_cost); - - /* This works only if the inverted value actually fits in an - `unsigned int' */ - if (HOST_BITS_PER_INT >= GET_MODE_BITSIZE (mode)) - { - synth_mult (&alg2, - val, - (alg.cost < mult_cost ? alg.cost : mult_cost) - negate_cost); - if (alg2.cost + negate_cost < alg.cost) - alg = alg2, variant = negate_variant; - } - - /* This proves very useful for division-by-constant. */ - synth_mult (&alg2, val - 1, - (alg.cost < mult_cost ? alg.cost : mult_cost) - add_cost); - if (alg2.cost + add_cost < alg.cost) - alg = alg2, variant = add_variant; - - if (alg.cost < mult_cost) - { - /* We found something cheaper than a multiply insn. */ - int opno; - rtx accum, tem; - - op0 = protect_from_queue (op0, 0); - - /* Avoid referencing memory over and over. - For speed, but also for correctness when mem is volatile. */ - if (GET_CODE (op0) == MEM) - op0 = force_reg (mode, op0); - - /* ACCUM starts out either as OP0 or as a zero, depending on - the first operation. */ - - if (alg.op[0] == alg_zero) - { - accum = copy_to_mode_reg (mode, const0_rtx); - val_so_far = 0; - } - else if (alg.op[0] == alg_m) - { - accum = copy_to_mode_reg (mode, op0); - val_so_far = 1; - } - else - abort (); - - for (opno = 1; opno < alg.ops; opno++) - { - int log = alg.log[opno]; - int preserve = preserve_subexpressions_p (); - rtx shift_subtarget = preserve ? 0 : accum; - rtx add_target - = (opno == alg.ops - 1 && target != 0 && variant != add_variant - ? target : 0); - rtx accum_target = preserve ? 0 : accum; - - switch (alg.op[opno]) - { - case alg_shift: - accum = expand_shift (LSHIFT_EXPR, mode, accum, - build_int_2 (log, 0), NULL_RTX, 0); - val_so_far <<= log; - break; - - case alg_add_t_m2: - tem = expand_shift (LSHIFT_EXPR, mode, op0, - build_int_2 (log, 0), NULL_RTX, 0); - accum = force_operand (gen_rtx (PLUS, mode, accum, tem), - add_target ? add_target : accum_target); - val_so_far += (HOST_WIDE_INT) 1 << log; - break; - - case alg_sub_t_m2: - tem = expand_shift (LSHIFT_EXPR, mode, op0, - build_int_2 (log, 0), NULL_RTX, 0); - accum = force_operand (gen_rtx (MINUS, mode, accum, tem), - add_target ? add_target : accum_target); - val_so_far -= (HOST_WIDE_INT) 1 << log; - break; - - case alg_add_t2_m: - accum = expand_shift (LSHIFT_EXPR, mode, accum, - build_int_2 (log, 0), shift_subtarget, - 0); - accum = force_operand (gen_rtx (PLUS, mode, accum, op0), - add_target ? add_target : accum_target); - val_so_far = (val_so_far << log) + 1; - break; - - case alg_sub_t2_m: - accum = expand_shift (LSHIFT_EXPR, mode, accum, - build_int_2 (log, 0), shift_subtarget, - 0); - accum = force_operand (gen_rtx (MINUS, mode, accum, op0), - add_target ? add_target : accum_target); - val_so_far = (val_so_far << log) - 1; - break; - - case alg_add_factor: - tem = expand_shift (LSHIFT_EXPR, mode, accum, - build_int_2 (log, 0), NULL_RTX, 0); - accum = force_operand (gen_rtx (PLUS, mode, accum, tem), - add_target ? add_target : accum_target); - val_so_far += val_so_far << log; - break; - - case alg_sub_factor: - tem = expand_shift (LSHIFT_EXPR, mode, accum, - build_int_2 (log, 0), NULL_RTX, 0); - accum = force_operand (gen_rtx (MINUS, mode, tem, accum), - (add_target ? add_target - : preserve ? 0 : tem)); - val_so_far = (val_so_far << log) - val_so_far; - break; - - default: - abort ();; - } - - /* Write a REG_EQUAL note on the last insn so that we can cse - multiplication sequences. */ - - insn = get_last_insn (); - REG_NOTES (insn) - = gen_rtx (EXPR_LIST, REG_EQUAL, - gen_rtx (MULT, mode, op0, GEN_INT (val_so_far)), - REG_NOTES (insn)); - } - - if (variant == negate_variant) - { - val_so_far = - val_so_far; - accum = expand_unop (mode, neg_optab, accum, target, 0); - } - else if (variant == add_variant) - { - val_so_far = val_so_far + 1; - accum = force_operand (gen_rtx (PLUS, mode, accum, op0), target); - } - - if (val != val_so_far) - abort (); - - return accum; - } - } - - /* This used to use umul_optab if unsigned, but for non-widening multiply - there is no difference between signed and unsigned. */ - op0 = expand_binop (mode, smul_optab, - op0, op1, target, unsignedp, OPTAB_LIB_WIDEN); - if (op0 == 0) - abort (); - return op0; -} - -/* Return the smallest n such that 2**n >= X. */ - -int -ceil_log2 (x) - unsigned HOST_WIDE_INT x; -{ - return floor_log2 (x - 1) + 1; -} - -/* Choose a minimal N + 1 bit approximation to 1/D that can be used to - replace division by D, and put the least significant N bits of the result - in *MULTIPLIER_PTR and return the most significant bit. - - The width of operations is N (should be <= HOST_BITS_PER_WIDE_INT), the - needed precision is in PRECISION (should be <= N). - - PRECISION should be as small as possible so this function can choose - multiplier more freely. - - The rounded-up logarithm of D is placed in *lgup_ptr. A shift count that - is to be used for a final right shift is placed in *POST_SHIFT_PTR. - - Using this function, x/D will be equal to (x * m) >> (*POST_SHIFT_PTR), - where m is the full HOST_BITS_PER_WIDE_INT + 1 bit multiplier. */ - -static -unsigned HOST_WIDE_INT -choose_multiplier (d, n, precision, multiplier_ptr, post_shift_ptr, lgup_ptr) - unsigned HOST_WIDE_INT d; - int n; - int precision; - unsigned HOST_WIDE_INT *multiplier_ptr; - int *post_shift_ptr; - int *lgup_ptr; -{ - unsigned HOST_WIDE_INT mhigh_hi, mhigh_lo; - unsigned HOST_WIDE_INT mlow_hi, mlow_lo; - int lgup, post_shift; - int pow, pow2; - unsigned HOST_WIDE_INT nh, nl, dummy1, dummy2; - - /* lgup = ceil(log2(divisor)); */ - lgup = ceil_log2 (d); - - if (lgup > n) - abort (); - - pow = n + lgup; - pow2 = n + lgup - precision; - - if (pow == 2 * HOST_BITS_PER_WIDE_INT) - { - /* We could handle this with some effort, but this case is much better - handled directly with a scc insn, so rely on caller using that. */ - abort (); - } - - /* mlow = 2^(N + lgup)/d */ - if (pow >= HOST_BITS_PER_WIDE_INT) - { - nh = (unsigned HOST_WIDE_INT) 1 << (pow - HOST_BITS_PER_WIDE_INT); - nl = 0; - } - else - { - nh = 0; - nl = (unsigned HOST_WIDE_INT) 1 << pow; - } - div_and_round_double (TRUNC_DIV_EXPR, 1, nl, nh, d, (HOST_WIDE_INT) 0, - &mlow_lo, &mlow_hi, &dummy1, &dummy2); - - /* mhigh = (2^(N + lgup) + 2^N + lgup - precision)/d */ - if (pow2 >= HOST_BITS_PER_WIDE_INT) - nh |= (unsigned HOST_WIDE_INT) 1 << (pow2 - HOST_BITS_PER_WIDE_INT); - else - nl |= (unsigned HOST_WIDE_INT) 1 << pow2; - div_and_round_double (TRUNC_DIV_EXPR, 1, nl, nh, d, (HOST_WIDE_INT) 0, - &mhigh_lo, &mhigh_hi, &dummy1, &dummy2); - - if (mhigh_hi && nh - d >= d) - abort (); - if (mhigh_hi > 1 || mlow_hi > 1) - abort (); - /* assert that mlow < mhigh. */ - if (! (mlow_hi < mhigh_hi || (mlow_hi == mhigh_hi && mlow_lo < mhigh_lo))) - abort(); - - /* If precision == N, then mlow, mhigh exceed 2^N - (but they do not exceed 2^(N+1)). */ - - /* Reduce to lowest terms */ - for (post_shift = lgup; post_shift > 0; post_shift--) - { - unsigned HOST_WIDE_INT ml_lo = (mlow_hi << (HOST_BITS_PER_WIDE_INT - 1)) | (mlow_lo >> 1); - unsigned HOST_WIDE_INT mh_lo = (mhigh_hi << (HOST_BITS_PER_WIDE_INT - 1)) | (mhigh_lo >> 1); - if (ml_lo >= mh_lo) - break; - - mlow_hi = 0; - mlow_lo = ml_lo; - mhigh_hi = 0; - mhigh_lo = mh_lo; - } - - *post_shift_ptr = post_shift; - *lgup_ptr = lgup; - if (n < HOST_BITS_PER_WIDE_INT) - { - unsigned HOST_WIDE_INT mask = ((unsigned HOST_WIDE_INT) 1 << n) - 1; - *multiplier_ptr = mhigh_lo & mask; - return mhigh_lo >= mask; - } - else - { - *multiplier_ptr = mhigh_lo; - return mhigh_hi; - } -} - -/* Compute the inverse of X mod 2**n, i.e., find Y such that X * Y is - congruent to 1 (mod 2**N). */ - -static unsigned HOST_WIDE_INT -invert_mod2n (x, n) - unsigned HOST_WIDE_INT x; - int n; -{ - /* Solve x*y == 1 (mod 2^n), where x is odd. Return y. */ - - /* The algorithm notes that the choice y = x satisfies - x*y == 1 mod 2^3, since x is assumed odd. - Each iteration doubles the number of bits of significance in y. */ - - unsigned HOST_WIDE_INT mask; - unsigned HOST_WIDE_INT y = x; - int nbit = 3; - - mask = (n == HOST_BITS_PER_WIDE_INT - ? ~(unsigned HOST_WIDE_INT) 0 - : ((unsigned HOST_WIDE_INT) 1 << n) - 1); - - while (nbit < n) - { - y = y * (2 - x*y) & mask; /* Modulo 2^N */ - nbit *= 2; - } - return y; -} - -/* Emit code to adjust ADJ_OPERAND after multiplication of wrong signedness - flavor of OP0 and OP1. ADJ_OPERAND is already the high half of the - product OP0 x OP1. If UNSIGNEDP is nonzero, adjust the signed product - to become unsigned, if UNSIGNEDP is zero, adjust the unsigned product to - become signed. - - The result is put in TARGET if that is convenient. - - MODE is the mode of operation. */ - -rtx -expand_mult_highpart_adjust (mode, adj_operand, op0, op1, target, unsignedp) - enum machine_mode mode; - register rtx adj_operand, op0, op1, target; - int unsignedp; -{ - rtx tem; - enum rtx_code adj_code = unsignedp ? PLUS : MINUS; - - tem = expand_shift (RSHIFT_EXPR, mode, op0, - build_int_2 (GET_MODE_BITSIZE (mode) - 1, 0), - NULL_RTX, 0); - tem = expand_and (tem, op1, NULL_RTX); - adj_operand = force_operand (gen_rtx (adj_code, mode, adj_operand, tem), - adj_operand); - - tem = expand_shift (RSHIFT_EXPR, mode, op1, - build_int_2 (GET_MODE_BITSIZE (mode) - 1, 0), - NULL_RTX, 0); - tem = expand_and (tem, op0, NULL_RTX); - target = force_operand (gen_rtx (adj_code, mode, adj_operand, tem), target); - - return target; -} - -/* Emit code to multiply OP0 and CNST1, putting the high half of the result - in TARGET if that is convenient, and return where the result is. If the - operation can not be performed, 0 is returned. - - MODE is the mode of operation and result. - - UNSIGNEDP nonzero means unsigned multiply. - - MAX_COST is the total allowed cost for the expanded RTL. */ - -rtx -expand_mult_highpart (mode, op0, cnst1, target, unsignedp, max_cost) - enum machine_mode mode; - register rtx op0, target; - unsigned HOST_WIDE_INT cnst1; - int unsignedp; - int max_cost; -{ - enum machine_mode wider_mode = GET_MODE_WIDER_MODE (mode); - optab mul_highpart_optab; - optab moptab; - rtx tem; - int size = GET_MODE_BITSIZE (mode); - rtx op1, wide_op1; - - /* We can't support modes wider than HOST_BITS_PER_INT. */ - if (size > HOST_BITS_PER_WIDE_INT) - abort (); - - op1 = GEN_INT (cnst1); - - if (GET_MODE_BITSIZE (wider_mode) <= HOST_BITS_PER_INT) - wide_op1 = op1; - else - wide_op1 - = immed_double_const (cnst1, - (unsignedp - ? (HOST_WIDE_INT) 0 - : -(cnst1 >> (HOST_BITS_PER_WIDE_INT - 1))), - wider_mode); - - /* expand_mult handles constant multiplication of word_mode - or narrower. It does a poor job for large modes. */ - if (size < BITS_PER_WORD - && mul_cost[(int) wider_mode] + shift_cost[size-1] < max_cost) - { - /* We have to do this, since expand_binop doesn't do conversion for - multiply. Maybe change expand_binop to handle widening multiply? */ - op0 = convert_to_mode (wider_mode, op0, unsignedp); - - tem = expand_mult (wider_mode, op0, wide_op1, NULL_RTX, unsignedp); - tem = expand_shift (RSHIFT_EXPR, wider_mode, tem, - build_int_2 (size, 0), NULL_RTX, 1); - return convert_modes (mode, wider_mode, tem, unsignedp); - } - - if (target == 0) - target = gen_reg_rtx (mode); - - /* Firstly, try using a multiplication insn that only generates the needed - high part of the product, and in the sign flavor of unsignedp. */ - if (mul_highpart_cost[(int) mode] < max_cost) - { - mul_highpart_optab = unsignedp ? umul_highpart_optab : smul_highpart_optab; - target = expand_binop (mode, mul_highpart_optab, - op0, wide_op1, target, unsignedp, OPTAB_DIRECT); - if (target) - return target; - } - - /* Secondly, same as above, but use sign flavor opposite of unsignedp. - Need to adjust the result after the multiplication. */ - if (mul_highpart_cost[(int) mode] + 2 * shift_cost[size-1] + 4 * add_cost < max_cost) - { - mul_highpart_optab = unsignedp ? smul_highpart_optab : umul_highpart_optab; - target = expand_binop (mode, mul_highpart_optab, - op0, wide_op1, target, unsignedp, OPTAB_DIRECT); - if (target) - /* We used the wrong signedness. Adjust the result. */ - return expand_mult_highpart_adjust (mode, target, op0, - op1, target, unsignedp); - } - - /* Try widening multiplication. */ - moptab = unsignedp ? umul_widen_optab : smul_widen_optab; - if (moptab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing - && mul_widen_cost[(int) wider_mode] < max_cost) - { - op1 = force_reg (mode, op1); - goto try; - } - - /* Try widening the mode and perform a non-widening multiplication. */ - moptab = smul_optab; - if (smul_optab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing - && mul_cost[(int) wider_mode] + shift_cost[size-1] < max_cost) - { - op1 = wide_op1; - goto try; - } - - /* Try widening multiplication of opposite signedness, and adjust. */ - moptab = unsignedp ? smul_widen_optab : umul_widen_optab; - if (moptab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing - && (mul_widen_cost[(int) wider_mode] - + 2 * shift_cost[size-1] + 4 * add_cost < max_cost)) - { - rtx regop1 = force_reg (mode, op1); - tem = expand_binop (wider_mode, moptab, op0, regop1, - NULL_RTX, ! unsignedp, OPTAB_WIDEN); - if (tem != 0) - { - /* Extract the high half of the just generated product. */ - tem = expand_shift (RSHIFT_EXPR, wider_mode, tem, - build_int_2 (size, 0), NULL_RTX, 1); - tem = convert_modes (mode, wider_mode, tem, unsignedp); - /* We used the wrong signedness. Adjust the result. */ - return expand_mult_highpart_adjust (mode, tem, op0, op1, - target, unsignedp); - } - } - - return 0; - - try: - /* Pass NULL_RTX as target since TARGET has wrong mode. */ - tem = expand_binop (wider_mode, moptab, op0, op1, - NULL_RTX, unsignedp, OPTAB_WIDEN); - if (tem == 0) - return 0; - - /* Extract the high half of the just generated product. */ - if (mode == word_mode) - { - return gen_highpart (mode, tem); - } - else - { - tem = expand_shift (RSHIFT_EXPR, wider_mode, tem, - build_int_2 (size, 0), NULL_RTX, 1); - return convert_modes (mode, wider_mode, tem, unsignedp); - } -} - -/* Emit the code to divide OP0 by OP1, putting the result in TARGET - if that is convenient, and returning where the result is. - You may request either the quotient or the remainder as the result; - specify REM_FLAG nonzero to get the remainder. - - CODE is the expression code for which kind of division this is; - it controls how rounding is done. MODE is the machine mode to use. - UNSIGNEDP nonzero means do unsigned division. */ - -/* ??? For CEIL_MOD_EXPR, can compute incorrect remainder with ANDI - and then correct it by or'ing in missing high bits - if result of ANDI is nonzero. - For ROUND_MOD_EXPR, can use ANDI and then sign-extend the result. - This could optimize to a bfexts instruction. - But C doesn't use these operations, so their optimizations are - left for later. */ - -#define EXACT_POWER_OF_2_OR_ZERO_P(x) (((x) & ((x) - 1)) == 0) - -rtx -expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp) - int rem_flag; - enum tree_code code; - enum machine_mode mode; - register rtx op0, op1, target; - int unsignedp; -{ - enum machine_mode compute_mode; - register rtx tquotient; - rtx quotient = 0, remainder = 0; - rtx last; - int size; - rtx insn, set; - optab optab1, optab2; - int op1_is_constant, op1_is_pow2; - int max_cost, extra_cost; - - op1_is_constant = GET_CODE (op1) == CONST_INT; - op1_is_pow2 = (op1_is_constant - && ((EXACT_POWER_OF_2_OR_ZERO_P (INTVAL (op1)) - || EXACT_POWER_OF_2_OR_ZERO_P (-INTVAL (op1))))); - - /* - This is the structure of expand_divmod: - - First comes code to fix up the operands so we can perform the operations - correctly and efficiently. - - Second comes a switch statement with code specific for each rounding mode. - For some special operands this code emits all RTL for the desired - operation, for other cases, it generates only a quotient and stores it in - QUOTIENT. The case for trunc division/remainder might leave quotient = 0, - to indicate that it has not done anything. - - Last comes code that finishes the operation. If QUOTIENT is set and - REM_FLAG is set, the remainder is computed as OP0 - QUOTIENT * OP1. If - QUOTIENT is not set, it is computed using trunc rounding. - - We try to generate special code for division and remainder when OP1 is a - constant. If |OP1| = 2**n we can use shifts and some other fast - operations. For other values of OP1, we compute a carefully selected - fixed-point approximation m = 1/OP1, and generate code that multiplies OP0 - by m. - - In all cases but EXACT_DIV_EXPR, this multiplication requires the upper - half of the product. Different strategies for generating the product are - implemented in expand_mult_highpart. - - If what we actually want is the remainder, we generate that by another - by-constant multiplication and a subtraction. */ - - /* We shouldn't be called with OP1 == const1_rtx, but some of the - code below will malfunction if we are, so check here and handle - the special case if so. */ - if (op1 == const1_rtx) - return rem_flag ? const0_rtx : op0; - - if (target - /* Don't use the function value register as a target - since we have to read it as well as write it, - and function-inlining gets confused by this. */ - && ((REG_P (target) && REG_FUNCTION_VALUE_P (target)) - /* Don't clobber an operand while doing a multi-step calculation. */ - || ((rem_flag || op1_is_constant) - && (reg_mentioned_p (target, op0) - || (GET_CODE (op0) == MEM && GET_CODE (target) == MEM))) - || reg_mentioned_p (target, op1) - || (GET_CODE (op1) == MEM && GET_CODE (target) == MEM))) - target = 0; - - /* Get the mode in which to perform this computation. Normally it will - be MODE, but sometimes we can't do the desired operation in MODE. - If so, pick a wider mode in which we can do the operation. Convert - to that mode at the start to avoid repeated conversions. - - First see what operations we need. These depend on the expression - we are evaluating. (We assume that divxx3 insns exist under the - same conditions that modxx3 insns and that these insns don't normally - fail. If these assumptions are not correct, we may generate less - efficient code in some cases.) - - Then see if we find a mode in which we can open-code that operation - (either a division, modulus, or shift). Finally, check for the smallest - mode for which we can do the operation with a library call. */ - - /* We might want to refine this now that we have division-by-constant - optimization. Since expand_mult_highpart tries so many variants, it is - not straightforward to generalize this. Maybe we should make an array - of possible modes in init_expmed? Save this for GCC 2.7. */ - - optab1 = (op1_is_pow2 ? (unsignedp ? lshr_optab : ashr_optab) - : (unsignedp ? udiv_optab : sdiv_optab)); - optab2 = (op1_is_pow2 ? optab1 : (unsignedp ? udivmod_optab : sdivmod_optab)); - - for (compute_mode = mode; compute_mode != VOIDmode; - compute_mode = GET_MODE_WIDER_MODE (compute_mode)) - if (optab1->handlers[(int) compute_mode].insn_code != CODE_FOR_nothing - || optab2->handlers[(int) compute_mode].insn_code != CODE_FOR_nothing) - break; - - if (compute_mode == VOIDmode) - for (compute_mode = mode; compute_mode != VOIDmode; - compute_mode = GET_MODE_WIDER_MODE (compute_mode)) - if (optab1->handlers[(int) compute_mode].libfunc - || optab2->handlers[(int) compute_mode].libfunc) - break; - - /* If we still couldn't find a mode, use MODE, but we'll probably abort - in expand_binop. */ - if (compute_mode == VOIDmode) - compute_mode = mode; - - if (target && GET_MODE (target) == compute_mode) - tquotient = target; - else - tquotient = gen_reg_rtx (compute_mode); - - size = GET_MODE_BITSIZE (compute_mode); -#if 0 - /* It should be possible to restrict the precision to GET_MODE_BITSIZE - (mode), and thereby get better code when OP1 is a constant. Do that - later. It will require going over all usages of SIZE below. */ - size = GET_MODE_BITSIZE (mode); -#endif - - max_cost = div_cost[(int) compute_mode] - - (rem_flag ? mul_cost[(int) compute_mode] + add_cost : 0); - - /* Now convert to the best mode to use. */ - if (compute_mode != mode) - { - op0 = convert_modes (compute_mode, mode, op0, unsignedp); - op1 = convert_modes (compute_mode, mode, op1, unsignedp); - } - - /* If one of the operands is a volatile MEM, copy it into a register. */ - - if (GET_CODE (op0) == MEM && MEM_VOLATILE_P (op0)) - op0 = force_reg (compute_mode, op0); - if (GET_CODE (op1) == MEM && MEM_VOLATILE_P (op1)) - op1 = force_reg (compute_mode, op1); - - /* If we need the remainder or if OP1 is constant, we need to - put OP0 in a register in case it has any queued subexpressions. */ - if (rem_flag || op1_is_constant) - op0 = force_reg (compute_mode, op0); - - last = get_last_insn (); - - /* Promote floor rounding to trunc rounding for unsigned operations. */ - if (unsignedp) - { - if (code == FLOOR_DIV_EXPR) - code = TRUNC_DIV_EXPR; - if (code == FLOOR_MOD_EXPR) - code = TRUNC_MOD_EXPR; - } - - if (op1 != const0_rtx) - switch (code) - { - case TRUNC_MOD_EXPR: - case TRUNC_DIV_EXPR: - if (op1_is_constant) - { - if (unsignedp) - { - unsigned HOST_WIDE_INT mh, ml; - int pre_shift, post_shift; - int dummy; - unsigned HOST_WIDE_INT d = INTVAL (op1); - - if (EXACT_POWER_OF_2_OR_ZERO_P (d)) - { - pre_shift = floor_log2 (d); - if (rem_flag) - { - remainder = - expand_binop (compute_mode, and_optab, op0, - GEN_INT (((HOST_WIDE_INT) 1 << pre_shift) - 1), - remainder, 1, - OPTAB_LIB_WIDEN); - if (remainder) - return gen_lowpart (mode, remainder); - } - quotient = expand_shift (RSHIFT_EXPR, compute_mode, op0, - build_int_2 (pre_shift, 0), - tquotient, 1); - } - else if (size <= HOST_BITS_PER_WIDE_INT) - { - if (d >= ((unsigned HOST_WIDE_INT) 1 << (size - 1))) - { - /* Most significant bit of divisor is set; emit an scc - insn. */ - quotient = emit_store_flag (tquotient, GEU, op0, op1, - compute_mode, 1, 1); - if (quotient == 0) - goto fail1; - } - else - { - /* Find a suitable multiplier and right shift count - instead of multiplying with D. */ - - mh = choose_multiplier (d, size, size, - &ml, &post_shift, &dummy); - - /* If the suggested multiplier is more than SIZE bits, - we can do better for even divisors, using an - initial right shift. */ - if (mh != 0 && (d & 1) == 0) - { - pre_shift = floor_log2 (d & -d); - mh = choose_multiplier (d >> pre_shift, size, - size - pre_shift, - &ml, &post_shift, &dummy); - if (mh) - abort (); - } - else - pre_shift = 0; - - if (mh != 0) - { - rtx t1, t2, t3, t4; - - extra_cost = (shift_cost[post_shift - 1] - + shift_cost[1] + 2 * add_cost); - t1 = expand_mult_highpart (compute_mode, op0, ml, - NULL_RTX, 1, - max_cost - extra_cost); - if (t1 == 0) - goto fail1; - t2 = force_operand (gen_rtx (MINUS, compute_mode, - op0, t1), - NULL_RTX); - t3 = expand_shift (RSHIFT_EXPR, compute_mode, t2, - build_int_2 (1, 0), NULL_RTX,1); - t4 = force_operand (gen_rtx (PLUS, compute_mode, - t1, t3), - NULL_RTX); - quotient = - expand_shift (RSHIFT_EXPR, compute_mode, t4, - build_int_2 (post_shift - 1, 0), - tquotient, 1); - } - else - { - rtx t1, t2; - - t1 = expand_shift (RSHIFT_EXPR, compute_mode, op0, - build_int_2 (pre_shift, 0), - NULL_RTX, 1); - extra_cost = (shift_cost[pre_shift] - + shift_cost[post_shift]); - t2 = expand_mult_highpart (compute_mode, t1, ml, - NULL_RTX, 1, - max_cost - extra_cost); - if (t2 == 0) - goto fail1; - quotient = - expand_shift (RSHIFT_EXPR, compute_mode, t2, - build_int_2 (post_shift, 0), - tquotient, 1); - } - } - } - else /* Too wide mode to use tricky code */ - break; - - insn = get_last_insn (); - if (insn != last - && (set = single_set (insn)) != 0 - && SET_DEST (set) == quotient) - REG_NOTES (insn) - = gen_rtx (EXPR_LIST, REG_EQUAL, - gen_rtx (UDIV, compute_mode, op0, op1), - REG_NOTES (insn)); - } - else /* TRUNC_DIV, signed */ - { - unsigned HOST_WIDE_INT ml; - int lgup, post_shift; - HOST_WIDE_INT d = INTVAL (op1); - unsigned HOST_WIDE_INT abs_d = d >= 0 ? d : -d; - - /* n rem d = n rem -d */ - if (rem_flag && d < 0) - { - d = abs_d; - op1 = GEN_INT (abs_d); - } - - if (d == 1) - quotient = op0; - else if (d == -1) - quotient = expand_unop (compute_mode, neg_optab, op0, - tquotient, 0); - else if (abs_d == (unsigned HOST_WIDE_INT) 1 << (size - 1)) - { - /* This case is not handled correctly below. */ - quotient = emit_store_flag (tquotient, EQ, op0, op1, - compute_mode, 1, 1); - if (quotient == 0) - goto fail1; - } - else if (EXACT_POWER_OF_2_OR_ZERO_P (d) - && (rem_flag ? smod_pow2_cheap : sdiv_pow2_cheap)) - ; - else if (EXACT_POWER_OF_2_OR_ZERO_P (abs_d)) - { - lgup = floor_log2 (abs_d); - if (abs_d != 2 && BRANCH_COST < 3) - { - rtx label = gen_label_rtx (); - rtx t1; - - t1 = copy_to_mode_reg (compute_mode, op0); - emit_cmp_insn (t1, const0_rtx, GE, - NULL_RTX, compute_mode, 0, 0); - emit_jump_insn (gen_bge (label)); - expand_inc (t1, GEN_INT (abs_d - 1)); - emit_label (label); - quotient = expand_shift (RSHIFT_EXPR, compute_mode, t1, - build_int_2 (lgup, 0), - tquotient, 0); - } - else - { - rtx t1, t2, t3; - t1 = expand_shift (RSHIFT_EXPR, compute_mode, op0, - build_int_2 (size - 1, 0), - NULL_RTX, 0); - t2 = expand_shift (RSHIFT_EXPR, compute_mode, t1, - build_int_2 (size - lgup, 0), - NULL_RTX, 1); - t3 = force_operand (gen_rtx (PLUS, compute_mode, - op0, t2), - NULL_RTX); - quotient = expand_shift (RSHIFT_EXPR, compute_mode, t3, - build_int_2 (lgup, 0), - tquotient, 0); - } - - /* We have computed OP0 / abs(OP1). If OP1 is negative, negate - the quotient. */ - if (d < 0) - { - insn = get_last_insn (); - if (insn != last - && (set = single_set (insn)) != 0 - && SET_DEST (set) == quotient) - REG_NOTES (insn) - = gen_rtx (EXPR_LIST, REG_EQUAL, - gen_rtx (DIV, compute_mode, op0, - GEN_INT (abs_d)), - REG_NOTES (insn)); - - quotient = expand_unop (compute_mode, neg_optab, - quotient, quotient, 0); - } - } - else if (size <= HOST_BITS_PER_WIDE_INT) - { - choose_multiplier (abs_d, size, size - 1, - &ml, &post_shift, &lgup); - if (ml < (unsigned HOST_WIDE_INT) 1 << (size - 1)) - { - rtx t1, t2, t3; - - extra_cost = (shift_cost[post_shift] - + shift_cost[size - 1] + add_cost); - t1 = expand_mult_highpart (compute_mode, op0, ml, - NULL_RTX, 0, - max_cost - extra_cost); - if (t1 == 0) - goto fail1; - t2 = expand_shift (RSHIFT_EXPR, compute_mode, t1, - build_int_2 (post_shift, 0), NULL_RTX, 0); - t3 = expand_shift (RSHIFT_EXPR, compute_mode, op0, - build_int_2 (size - 1, 0), NULL_RTX, 0); - if (d < 0) - quotient = force_operand (gen_rtx (MINUS, compute_mode, t3, t2), - tquotient); - else - quotient = force_operand (gen_rtx (MINUS, compute_mode, t2, t3), - tquotient); - } - else - { - rtx t1, t2, t3, t4; - - ml |= (~(unsigned HOST_WIDE_INT) 0) << (size - 1); - extra_cost = (shift_cost[post_shift] - + shift_cost[size - 1] + 2 * add_cost); - t1 = expand_mult_highpart (compute_mode, op0, ml, - NULL_RTX, 0, - max_cost - extra_cost); - if (t1 == 0) - goto fail1; - t2 = force_operand (gen_rtx (PLUS, compute_mode, t1, op0), - NULL_RTX); - t3 = expand_shift (RSHIFT_EXPR, compute_mode, t2, - build_int_2 (post_shift, 0), NULL_RTX, 0); - t4 = expand_shift (RSHIFT_EXPR, compute_mode, op0, - build_int_2 (size - 1, 0), NULL_RTX, 0); - if (d < 0) - quotient = force_operand (gen_rtx (MINUS, compute_mode, t4, t3), - tquotient); - else - quotient = force_operand (gen_rtx (MINUS, compute_mode, t3, t4), - tquotient); - } - } - else /* Too wide mode to use tricky code */ - break; - - insn = get_last_insn (); - if (insn != last - && (set = single_set (insn)) != 0 - && SET_DEST (set) == quotient) - REG_NOTES (insn) - = gen_rtx (EXPR_LIST, REG_EQUAL, - gen_rtx (DIV, compute_mode, op0, op1), - REG_NOTES (insn)); - } - break; - } - fail1: - delete_insns_since (last); - break; - - case FLOOR_DIV_EXPR: - case FLOOR_MOD_EXPR: - /* We will come here only for signed operations. */ - if (op1_is_constant && HOST_BITS_PER_WIDE_INT >= size) - { - unsigned HOST_WIDE_INT mh, ml; - int pre_shift, lgup, post_shift; - HOST_WIDE_INT d = INTVAL (op1); - - if (d > 0) - { - /* We could just as easily deal with negative constants here, - but it does not seem worth the trouble for GCC 2.6. */ - if (EXACT_POWER_OF_2_OR_ZERO_P (d)) - { - pre_shift = floor_log2 (d); - if (rem_flag) - { - remainder = expand_binop (compute_mode, and_optab, op0, - GEN_INT (((HOST_WIDE_INT) 1 << pre_shift) - 1), - remainder, 0, OPTAB_LIB_WIDEN); - if (remainder) - return gen_lowpart (mode, remainder); - } - quotient = expand_shift (RSHIFT_EXPR, compute_mode, op0, - build_int_2 (pre_shift, 0), - tquotient, 0); - } - else - { - rtx t1, t2, t3, t4; - - mh = choose_multiplier (d, size, size - 1, - &ml, &post_shift, &lgup); - if (mh) - abort (); - - t1 = expand_shift (RSHIFT_EXPR, compute_mode, op0, - build_int_2 (size - 1, 0), NULL_RTX, 0); - t2 = expand_binop (compute_mode, xor_optab, op0, t1, - NULL_RTX, 0, OPTAB_WIDEN); - extra_cost = (shift_cost[post_shift] - + shift_cost[size - 1] + 2 * add_cost); - t3 = expand_mult_highpart (compute_mode, t2, ml, - NULL_RTX, 1, - max_cost - extra_cost); - if (t3 != 0) - { - t4 = expand_shift (RSHIFT_EXPR, compute_mode, t3, - build_int_2 (post_shift, 0), - NULL_RTX, 1); - quotient = expand_binop (compute_mode, xor_optab, - t4, t1, tquotient, 0, - OPTAB_WIDEN); - } - } - } - else - { - rtx nsign, t1, t2, t3, t4; - t1 = force_operand (gen_rtx (PLUS, compute_mode, - op0, constm1_rtx), NULL_RTX); - t2 = expand_binop (compute_mode, ior_optab, op0, t1, NULL_RTX, - 0, OPTAB_WIDEN); - nsign = expand_shift (RSHIFT_EXPR, compute_mode, t2, - build_int_2 (size - 1, 0), NULL_RTX, 0); - t3 = force_operand (gen_rtx (MINUS, compute_mode, t1, nsign), - NULL_RTX); - t4 = expand_divmod (0, TRUNC_DIV_EXPR, compute_mode, t3, op1, - NULL_RTX, 0); - if (t4) - { - rtx t5; - t5 = expand_unop (compute_mode, one_cmpl_optab, nsign, - NULL_RTX, 0); - quotient = force_operand (gen_rtx (PLUS, compute_mode, - t4, t5), - tquotient); - } - } - } - - if (quotient != 0) - break; - delete_insns_since (last); - - /* Try using an instruction that produces both the quotient and - remainder, using truncation. We can easily compensate the quotient - or remainder to get floor rounding, once we have the remainder. - Notice that we compute also the final remainder value here, - and return the result right away. */ - if (target == 0 || GET_MODE (target) != compute_mode) - target = gen_reg_rtx (compute_mode); - - if (rem_flag) - { - remainder - = GET_CODE (target) == REG ? target : gen_reg_rtx (compute_mode); - quotient = gen_reg_rtx (compute_mode); - } - else - { - quotient - = GET_CODE (target) == REG ? target : gen_reg_rtx (compute_mode); - remainder = gen_reg_rtx (compute_mode); - } - - if (expand_twoval_binop (sdivmod_optab, op0, op1, - quotient, remainder, 0)) - { - /* This could be computed with a branch-less sequence. - Save that for later. */ - rtx tem; - rtx label = gen_label_rtx (); - emit_cmp_insn (remainder, const0_rtx, EQ, NULL_RTX, - compute_mode, 0, 0); - emit_jump_insn (gen_beq (label)); - tem = expand_binop (compute_mode, xor_optab, op0, op1, - NULL_RTX, 0, OPTAB_WIDEN); - emit_cmp_insn (tem, const0_rtx, GE, NULL_RTX, compute_mode, 0, 0); - emit_jump_insn (gen_bge (label)); - expand_dec (quotient, const1_rtx); - expand_inc (remainder, op1); - emit_label (label); - return gen_lowpart (mode, rem_flag ? remainder : quotient); - } - - /* No luck with division elimination or divmod. Have to do it - by conditionally adjusting op0 *and* the result. */ - { - rtx label1, label2, label3, label4, label5; - rtx adjusted_op0; - rtx tem; - - quotient = gen_reg_rtx (compute_mode); - adjusted_op0 = copy_to_mode_reg (compute_mode, op0); - label1 = gen_label_rtx (); - label2 = gen_label_rtx (); - label3 = gen_label_rtx (); - label4 = gen_label_rtx (); - label5 = gen_label_rtx (); - emit_cmp_insn (op1, const0_rtx, LT, NULL_RTX, compute_mode, 0, 0); - emit_jump_insn (gen_blt (label2)); - emit_cmp_insn (adjusted_op0, const0_rtx, LT, NULL_RTX, - compute_mode, 0, 0); - emit_jump_insn (gen_blt (label1)); - tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1, - quotient, 0, OPTAB_LIB_WIDEN); - if (tem != quotient) - emit_move_insn (quotient, tem); - emit_jump_insn (gen_jump (label5)); - emit_barrier (); - emit_label (label1); - expand_inc (adjusted_op0, const1_rtx); - emit_jump_insn (gen_jump (label4)); - emit_barrier (); - emit_label (label2); - emit_cmp_insn (adjusted_op0, const0_rtx, GT, NULL_RTX, - compute_mode, 0, 0); - emit_jump_insn (gen_bgt (label3)); - tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1, - quotient, 0, OPTAB_LIB_WIDEN); - if (tem != quotient) - emit_move_insn (quotient, tem); - emit_jump_insn (gen_jump (label5)); - emit_barrier (); - emit_label (label3); - expand_dec (adjusted_op0, const1_rtx); - emit_label (label4); - tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1, - quotient, 0, OPTAB_LIB_WIDEN); - if (tem != quotient) - emit_move_insn (quotient, tem); - expand_dec (quotient, const1_rtx); - emit_label (label5); - } - break; - - case CEIL_DIV_EXPR: - case CEIL_MOD_EXPR: - if (unsignedp) - { - if (op1_is_constant && EXACT_POWER_OF_2_OR_ZERO_P (INTVAL (op1))) - { - rtx t1, t2, t3; - unsigned HOST_WIDE_INT d = INTVAL (op1); - t1 = expand_shift (RSHIFT_EXPR, compute_mode, op0, - build_int_2 (floor_log2 (d), 0), - tquotient, 1); - t2 = expand_binop (compute_mode, and_optab, op0, - GEN_INT (d - 1), - NULL_RTX, 1, OPTAB_LIB_WIDEN); - t3 = gen_reg_rtx (compute_mode); - t3 = emit_store_flag (t3, NE, t2, const0_rtx, - compute_mode, 1, 1); - if (t3 == 0) - { - rtx lab; - lab = gen_label_rtx (); - emit_cmp_insn (t2, const0_rtx, EQ, NULL_RTX, - compute_mode, 0, 0); - emit_jump_insn (gen_beq (lab)); - expand_inc (t1, const1_rtx); - emit_label (lab); - quotient = t1; - } - else - quotient = force_operand (gen_rtx (PLUS, compute_mode, - t1, t3), - tquotient); - break; - } - - /* Try using an instruction that produces both the quotient and - remainder, using truncation. We can easily compensate the - quotient or remainder to get ceiling rounding, once we have the - remainder. Notice that we compute also the final remainder - value here, and return the result right away. */ - if (target == 0 || GET_MODE (target) != compute_mode) - target = gen_reg_rtx (compute_mode); - - if (rem_flag) - { - remainder = (GET_CODE (target) == REG - ? target : gen_reg_rtx (compute_mode)); - quotient = gen_reg_rtx (compute_mode); - } - else - { - quotient = (GET_CODE (target) == REG - ? target : gen_reg_rtx (compute_mode)); - remainder = gen_reg_rtx (compute_mode); - } - - if (expand_twoval_binop (udivmod_optab, op0, op1, quotient, - remainder, 1)) - { - /* This could be computed with a branch-less sequence. - Save that for later. */ - rtx label = gen_label_rtx (); - emit_cmp_insn (remainder, const0_rtx, EQ, NULL_RTX, - compute_mode, 0, 0); - emit_jump_insn (gen_beq (label)); - expand_inc (quotient, const1_rtx); - expand_dec (remainder, op1); - emit_label (label); - return gen_lowpart (mode, rem_flag ? remainder : quotient); - } - - /* No luck with division elimination or divmod. Have to do it - by conditionally adjusting op0 *and* the result. */ - { - rtx label1, label2; - rtx adjusted_op0, tem; - - quotient = gen_reg_rtx (compute_mode); - adjusted_op0 = copy_to_mode_reg (compute_mode, op0); - label1 = gen_label_rtx (); - label2 = gen_label_rtx (); - emit_cmp_insn (adjusted_op0, const0_rtx, NE, NULL_RTX, - compute_mode, 0, 0); - emit_jump_insn (gen_bne (label1)); - emit_move_insn (quotient, const0_rtx); - emit_jump_insn (gen_jump (label2)); - emit_barrier (); - emit_label (label1); - expand_dec (adjusted_op0, const1_rtx); - tem = expand_binop (compute_mode, udiv_optab, adjusted_op0, op1, - quotient, 1, OPTAB_LIB_WIDEN); - if (tem != quotient) - emit_move_insn (quotient, tem); - expand_inc (quotient, const1_rtx); - emit_label (label2); - } - } - else /* signed */ - { - if (op1_is_constant && EXACT_POWER_OF_2_OR_ZERO_P (INTVAL (op1)) - && INTVAL (op1) >= 0) - { - /* This is extremely similar to the code for the unsigned case - above. For 2.7 we should merge these variants, but for - 2.6.1 I don't want to touch the code for unsigned since that - get used in C. The signed case will only be used by other - languages (Ada). */ - - rtx t1, t2, t3; - unsigned HOST_WIDE_INT d = INTVAL (op1); - t1 = expand_shift (RSHIFT_EXPR, compute_mode, op0, - build_int_2 (floor_log2 (d), 0), - tquotient, 0); - t2 = expand_binop (compute_mode, and_optab, op0, - GEN_INT (d - 1), - NULL_RTX, 1, OPTAB_LIB_WIDEN); - t3 = gen_reg_rtx (compute_mode); - t3 = emit_store_flag (t3, NE, t2, const0_rtx, - compute_mode, 1, 1); - if (t3 == 0) - { - rtx lab; - lab = gen_label_rtx (); - emit_cmp_insn (t2, const0_rtx, EQ, NULL_RTX, - compute_mode, 0, 0); - emit_jump_insn (gen_beq (lab)); - expand_inc (t1, const1_rtx); - emit_label (lab); - quotient = t1; - } - else - quotient = force_operand (gen_rtx (PLUS, compute_mode, - t1, t3), - tquotient); - break; - } - - /* Try using an instruction that produces both the quotient and - remainder, using truncation. We can easily compensate the - quotient or remainder to get ceiling rounding, once we have the - remainder. Notice that we compute also the final remainder - value here, and return the result right away. */ - if (target == 0 || GET_MODE (target) != compute_mode) - target = gen_reg_rtx (compute_mode); - if (rem_flag) - { - remainder= (GET_CODE (target) == REG - ? target : gen_reg_rtx (compute_mode)); - quotient = gen_reg_rtx (compute_mode); - } - else - { - quotient = (GET_CODE (target) == REG - ? target : gen_reg_rtx (compute_mode)); - remainder = gen_reg_rtx (compute_mode); - } - - if (expand_twoval_binop (sdivmod_optab, op0, op1, quotient, - remainder, 0)) - { - /* This could be computed with a branch-less sequence. - Save that for later. */ - rtx tem; - rtx label = gen_label_rtx (); - emit_cmp_insn (remainder, const0_rtx, EQ, NULL_RTX, - compute_mode, 0, 0); - emit_jump_insn (gen_beq (label)); - tem = expand_binop (compute_mode, xor_optab, op0, op1, - NULL_RTX, 0, OPTAB_WIDEN); - emit_cmp_insn (tem, const0_rtx, LT, NULL_RTX, - compute_mode, 0, 0); - emit_jump_insn (gen_blt (label)); - expand_inc (quotient, const1_rtx); - expand_dec (remainder, op1); - emit_label (label); - return gen_lowpart (mode, rem_flag ? remainder : quotient); - } - - /* No luck with division elimination or divmod. Have to do it - by conditionally adjusting op0 *and* the result. */ - { - rtx label1, label2, label3, label4, label5; - rtx adjusted_op0; - rtx tem; - - quotient = gen_reg_rtx (compute_mode); - adjusted_op0 = copy_to_mode_reg (compute_mode, op0); - label1 = gen_label_rtx (); - label2 = gen_label_rtx (); - label3 = gen_label_rtx (); - label4 = gen_label_rtx (); - label5 = gen_label_rtx (); - emit_cmp_insn (op1, const0_rtx, LT, NULL_RTX, - compute_mode, 0, 0); - emit_jump_insn (gen_blt (label2)); - emit_cmp_insn (adjusted_op0, const0_rtx, GT, NULL_RTX, - compute_mode, 0, 0); - emit_jump_insn (gen_bgt (label1)); - tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1, - quotient, 0, OPTAB_LIB_WIDEN); - if (tem != quotient) - emit_move_insn (quotient, tem); - emit_jump_insn (gen_jump (label5)); - emit_barrier (); - emit_label (label1); - expand_dec (adjusted_op0, const1_rtx); - emit_jump_insn (gen_jump (label4)); - emit_barrier (); - emit_label (label2); - emit_cmp_insn (adjusted_op0, const0_rtx, LT, NULL_RTX, - compute_mode, 0, 0); - emit_jump_insn (gen_blt (label3)); - tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1, - quotient, 0, OPTAB_LIB_WIDEN); - if (tem != quotient) - emit_move_insn (quotient, tem); - emit_jump_insn (gen_jump (label5)); - emit_barrier (); - emit_label (label3); - expand_inc (adjusted_op0, const1_rtx); - emit_label (label4); - tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1, - quotient, 0, OPTAB_LIB_WIDEN); - if (tem != quotient) - emit_move_insn (quotient, tem); - expand_inc (quotient, const1_rtx); - emit_label (label5); - } - } - break; - - case EXACT_DIV_EXPR: - if (op1_is_constant && HOST_BITS_PER_WIDE_INT >= size) - { - HOST_WIDE_INT d = INTVAL (op1); - unsigned HOST_WIDE_INT ml; - int post_shift; - rtx t1; - - post_shift = floor_log2 (d & -d); - ml = invert_mod2n (d >> post_shift, size); - t1 = expand_mult (compute_mode, op0, GEN_INT (ml), NULL_RTX, - unsignedp); - quotient = expand_shift (RSHIFT_EXPR, compute_mode, t1, - build_int_2 (post_shift, 0), - NULL_RTX, unsignedp); - - insn = get_last_insn (); - REG_NOTES (insn) - = gen_rtx (EXPR_LIST, REG_EQUAL, - gen_rtx (unsignedp ? UDIV : DIV, compute_mode, - op0, op1), - REG_NOTES (insn)); - } - break; - - case ROUND_DIV_EXPR: - case ROUND_MOD_EXPR: - if (unsignedp) - { - rtx tem; - rtx label; - label = gen_label_rtx (); - quotient = gen_reg_rtx (compute_mode); - remainder = gen_reg_rtx (compute_mode); - if (expand_twoval_binop (udivmod_optab, op0, op1, quotient, remainder, 1) == 0) - { - rtx tem; - quotient = expand_binop (compute_mode, udiv_optab, op0, op1, - quotient, 1, OPTAB_LIB_WIDEN); - tem = expand_mult (compute_mode, quotient, op1, NULL_RTX, 1); - remainder = expand_binop (compute_mode, sub_optab, op0, tem, - remainder, 1, OPTAB_LIB_WIDEN); - } - tem = plus_constant (op1, -1); - tem = expand_shift (RSHIFT_EXPR, compute_mode, tem, - build_int_2 (1, 0), NULL_RTX, 1); - emit_cmp_insn (remainder, tem, LEU, NULL_RTX, compute_mode, 0, 0); - emit_jump_insn (gen_bleu (label)); - expand_inc (quotient, const1_rtx); - expand_dec (remainder, op1); - emit_label (label); - } - else - { - rtx abs_rem, abs_op1, tem, mask; - rtx label; - label = gen_label_rtx (); - quotient = gen_reg_rtx (compute_mode); - remainder = gen_reg_rtx (compute_mode); - if (expand_twoval_binop (sdivmod_optab, op0, op1, quotient, remainder, 0) == 0) - { - rtx tem; - quotient = expand_binop (compute_mode, sdiv_optab, op0, op1, - quotient, 0, OPTAB_LIB_WIDEN); - tem = expand_mult (compute_mode, quotient, op1, NULL_RTX, 0); - remainder = expand_binop (compute_mode, sub_optab, op0, tem, - remainder, 0, OPTAB_LIB_WIDEN); - } - abs_rem = expand_abs (compute_mode, remainder, NULL_RTX, 0, 0); - abs_op1 = expand_abs (compute_mode, op1, NULL_RTX, 0, 0); - tem = expand_shift (LSHIFT_EXPR, compute_mode, abs_rem, - build_int_2 (1, 0), NULL_RTX, 1); - emit_cmp_insn (tem, abs_op1, LTU, NULL_RTX, compute_mode, 0, 0); - emit_jump_insn (gen_bltu (label)); - tem = expand_binop (compute_mode, xor_optab, op0, op1, - NULL_RTX, 0, OPTAB_WIDEN); - mask = expand_shift (RSHIFT_EXPR, compute_mode, tem, - build_int_2 (size - 1, 0), NULL_RTX, 0); - tem = expand_binop (compute_mode, xor_optab, mask, const1_rtx, - NULL_RTX, 0, OPTAB_WIDEN); - tem = expand_binop (compute_mode, sub_optab, tem, mask, - NULL_RTX, 0, OPTAB_WIDEN); - expand_inc (quotient, tem); - tem = expand_binop (compute_mode, xor_optab, mask, op1, - NULL_RTX, 0, OPTAB_WIDEN); - tem = expand_binop (compute_mode, sub_optab, tem, mask, - NULL_RTX, 0, OPTAB_WIDEN); - expand_dec (remainder, tem); - emit_label (label); - } - return gen_lowpart (mode, rem_flag ? remainder : quotient); - } - - if (quotient == 0) - { - if (target && GET_MODE (target) != compute_mode) - target = 0; - - if (rem_flag) - { - /* Try to produce the remainder directly without a library call. */ - remainder = sign_expand_binop (compute_mode, umod_optab, smod_optab, - op0, op1, target, - unsignedp, OPTAB_WIDEN); - if (remainder == 0) - { - /* No luck there. Can we do remainder and divide at once - without a library call? */ - remainder = gen_reg_rtx (compute_mode); - if (! expand_twoval_binop ((unsignedp - ? udivmod_optab - : sdivmod_optab), - op0, op1, - NULL_RTX, remainder, unsignedp)) - remainder = 0; - } - - if (remainder) - return gen_lowpart (mode, remainder); - } - - /* Produce the quotient. Try a quotient insn, but not a library call. - If we have a divmod in this mode, use it in preference to widening - the div (for this test we assume it will not fail). Note that optab2 - is set to the one of the two optabs that the call below will use. */ - quotient - = sign_expand_binop (compute_mode, udiv_optab, sdiv_optab, - op0, op1, rem_flag ? NULL_RTX : target, - unsignedp, - ((optab2->handlers[(int) compute_mode].insn_code - != CODE_FOR_nothing) - ? OPTAB_DIRECT : OPTAB_WIDEN)); - - if (quotient == 0) - { - /* No luck there. Try a quotient-and-remainder insn, - keeping the quotient alone. */ - quotient = gen_reg_rtx (compute_mode); - if (! expand_twoval_binop (unsignedp ? udivmod_optab : sdivmod_optab, - op0, op1, - quotient, NULL_RTX, unsignedp)) - { - quotient = 0; - if (! rem_flag) - /* Still no luck. If we are not computing the remainder, - use a library call for the quotient. */ - quotient = sign_expand_binop (compute_mode, - udiv_optab, sdiv_optab, - op0, op1, target, - unsignedp, OPTAB_LIB_WIDEN); - } - } - } - - if (rem_flag) - { - if (target && GET_MODE (target) != compute_mode) - target = 0; - - if (quotient == 0) - /* No divide instruction either. Use library for remainder. */ - remainder = sign_expand_binop (compute_mode, umod_optab, smod_optab, - op0, op1, target, - unsignedp, OPTAB_LIB_WIDEN); - else - { - /* We divided. Now finish doing X - Y * (X / Y). */ - remainder = expand_mult (compute_mode, quotient, op1, - NULL_RTX, unsignedp); - remainder = expand_binop (compute_mode, sub_optab, op0, - remainder, target, unsignedp, - OPTAB_LIB_WIDEN); - } - } - - return gen_lowpart (mode, rem_flag ? remainder : quotient); -} - -/* Return a tree node with data type TYPE, describing the value of X. - Usually this is an RTL_EXPR, if there is no obvious better choice. - X may be an expression, however we only support those expressions - generated by loop.c. */ - -tree -make_tree (type, x) - tree type; - rtx x; -{ - tree t; - - switch (GET_CODE (x)) - { - case CONST_INT: - t = build_int_2 (INTVAL (x), - TREE_UNSIGNED (type) || INTVAL (x) >= 0 ? 0 : -1); - TREE_TYPE (t) = type; - return t; - - case CONST_DOUBLE: - if (GET_MODE (x) == VOIDmode) - { - t = build_int_2 (CONST_DOUBLE_LOW (x), CONST_DOUBLE_HIGH (x)); - TREE_TYPE (t) = type; - } - else - { - REAL_VALUE_TYPE d; - - REAL_VALUE_FROM_CONST_DOUBLE (d, x); - t = build_real (type, d); - } - - return t; - - case PLUS: - return fold (build (PLUS_EXPR, type, make_tree (type, XEXP (x, 0)), - make_tree (type, XEXP (x, 1)))); - - case MINUS: - return fold (build (MINUS_EXPR, type, make_tree (type, XEXP (x, 0)), - make_tree (type, XEXP (x, 1)))); - - case NEG: - return fold (build1 (NEGATE_EXPR, type, make_tree (type, XEXP (x, 0)))); - - case MULT: - return fold (build (MULT_EXPR, type, make_tree (type, XEXP (x, 0)), - make_tree (type, XEXP (x, 1)))); - - case ASHIFT: - return fold (build (LSHIFT_EXPR, type, make_tree (type, XEXP (x, 0)), - make_tree (type, XEXP (x, 1)))); - - case LSHIFTRT: - return fold (convert (type, - build (RSHIFT_EXPR, unsigned_type (type), - make_tree (unsigned_type (type), - XEXP (x, 0)), - make_tree (type, XEXP (x, 1))))); - - case ASHIFTRT: - return fold (convert (type, - build (RSHIFT_EXPR, signed_type (type), - make_tree (signed_type (type), XEXP (x, 0)), - make_tree (type, XEXP (x, 1))))); - - case DIV: - if (TREE_CODE (type) != REAL_TYPE) - t = signed_type (type); - else - t = type; - - return fold (convert (type, - build (TRUNC_DIV_EXPR, t, - make_tree (t, XEXP (x, 0)), - make_tree (t, XEXP (x, 1))))); - case UDIV: - t = unsigned_type (type); - return fold (convert (type, - build (TRUNC_DIV_EXPR, t, - make_tree (t, XEXP (x, 0)), - make_tree (t, XEXP (x, 1))))); - default: - t = make_node (RTL_EXPR); - TREE_TYPE (t) = type; - RTL_EXPR_RTL (t) = x; - /* There are no insns to be output - when this rtl_expr is used. */ - RTL_EXPR_SEQUENCE (t) = 0; - return t; - } -} - -/* Return an rtx representing the value of X * MULT + ADD. - TARGET is a suggestion for where to store the result (an rtx). - MODE is the machine mode for the computation. - X and MULT must have mode MODE. ADD may have a different mode. - So can X (defaults to same as MODE). - UNSIGNEDP is non-zero to do unsigned multiplication. - This may emit insns. */ - -rtx -expand_mult_add (x, target, mult, add, mode, unsignedp) - rtx x, target, mult, add; - enum machine_mode mode; - int unsignedp; -{ - tree type = type_for_mode (mode, unsignedp); - tree add_type = (GET_MODE (add) == VOIDmode - ? type : type_for_mode (GET_MODE (add), unsignedp)); - tree result = fold (build (PLUS_EXPR, type, - fold (build (MULT_EXPR, type, - make_tree (type, x), - make_tree (type, mult))), - make_tree (add_type, add))); - - return expand_expr (result, target, VOIDmode, 0); -} - -/* Compute the logical-and of OP0 and OP1, storing it in TARGET - and returning TARGET. - - If TARGET is 0, a pseudo-register or constant is returned. */ - -rtx -expand_and (op0, op1, target) - rtx op0, op1, target; -{ - enum machine_mode mode = VOIDmode; - rtx tem; - - if (GET_MODE (op0) != VOIDmode) - mode = GET_MODE (op0); - else if (GET_MODE (op1) != VOIDmode) - mode = GET_MODE (op1); - - if (mode != VOIDmode) - tem = expand_binop (mode, and_optab, op0, op1, target, 0, OPTAB_LIB_WIDEN); - else if (GET_CODE (op0) == CONST_INT && GET_CODE (op1) == CONST_INT) - tem = GEN_INT (INTVAL (op0) & INTVAL (op1)); - else - abort (); - - if (target == 0) - target = tem; - else if (tem != target) - emit_move_insn (target, tem); - return target; -} - -/* Emit a store-flags instruction for comparison CODE on OP0 and OP1 - and storing in TARGET. Normally return TARGET. - Return 0 if that cannot be done. - - MODE is the mode to use for OP0 and OP1 should they be CONST_INTs. If - it is VOIDmode, they cannot both be CONST_INT. - - UNSIGNEDP is for the case where we have to widen the operands - to perform the operation. It says to use zero-extension. - - NORMALIZEP is 1 if we should convert the result to be either zero - or one. Normalize is -1 if we should convert the result to be - either zero or -1. If NORMALIZEP is zero, the result will be left - "raw" out of the scc insn. */ - -rtx -emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep) - rtx target; - enum rtx_code code; - rtx op0, op1; - enum machine_mode mode; - int unsignedp; - int normalizep; -{ - rtx subtarget; - enum insn_code icode; - enum machine_mode compare_mode; - enum machine_mode target_mode = GET_MODE (target); - rtx tem; - rtx last = get_last_insn (); - rtx pattern, comparison; - - /* If one operand is constant, make it the second one. Only do this - if the other operand is not constant as well. */ - - if ((CONSTANT_P (op0) && ! CONSTANT_P (op1)) - || (GET_CODE (op0) == CONST_INT && GET_CODE (op1) != CONST_INT)) - { - tem = op0; - op0 = op1; - op1 = tem; - code = swap_condition (code); - } - - if (mode == VOIDmode) - mode = GET_MODE (op0); - - /* For some comparisons with 1 and -1, we can convert this to - comparisons with zero. This will often produce more opportunities for - store-flag insns. */ - - switch (code) - { - case LT: - if (op1 == const1_rtx) - op1 = const0_rtx, code = LE; - break; - case LE: - if (op1 == constm1_rtx) - op1 = const0_rtx, code = LT; - break; - case GE: - if (op1 == const1_rtx) - op1 = const0_rtx, code = GT; - break; - case GT: - if (op1 == constm1_rtx) - op1 = const0_rtx, code = GE; - break; - case GEU: - if (op1 == const1_rtx) - op1 = const0_rtx, code = NE; - break; - case LTU: - if (op1 == const1_rtx) - op1 = const0_rtx, code = EQ; - break; - } - - /* From now on, we won't change CODE, so set ICODE now. */ - icode = setcc_gen_code[(int) code]; - - /* If this is A < 0 or A >= 0, we can do this by taking the ones - complement of A (for GE) and shifting the sign bit to the low bit. */ - if (op1 == const0_rtx && (code == LT || code == GE) - && GET_MODE_CLASS (mode) == MODE_INT - && (normalizep || STORE_FLAG_VALUE == 1 - || (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT - && ((STORE_FLAG_VALUE & GET_MODE_MASK (mode)) - == (HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1))))) - { - subtarget = target; - - /* If the result is to be wider than OP0, it is best to convert it - first. If it is to be narrower, it is *incorrect* to convert it - first. */ - if (GET_MODE_SIZE (target_mode) > GET_MODE_SIZE (mode)) - { - op0 = protect_from_queue (op0, 0); - op0 = convert_modes (target_mode, mode, op0, 0); - mode = target_mode; - } - - if (target_mode != mode) - subtarget = 0; - - if (code == GE) - op0 = expand_unop (mode, one_cmpl_optab, op0, - ((STORE_FLAG_VALUE == 1 || normalizep) - ? 0 : subtarget), 0); - - if (STORE_FLAG_VALUE == 1 || normalizep) - /* If we are supposed to produce a 0/1 value, we want to do - a logical shift from the sign bit to the low-order bit; for - a -1/0 value, we do an arithmetic shift. */ - op0 = expand_shift (RSHIFT_EXPR, mode, op0, - size_int (GET_MODE_BITSIZE (mode) - 1), - subtarget, normalizep != -1); - - if (mode != target_mode) - op0 = convert_modes (target_mode, mode, op0, 0); - - return op0; - } - - if (icode != CODE_FOR_nothing) - { - /* We think we may be able to do this with a scc insn. Emit the - comparison and then the scc insn. - - compare_from_rtx may call emit_queue, which would be deleted below - if the scc insn fails. So call it ourselves before setting LAST. */ - - emit_queue (); - last = get_last_insn (); - - comparison - = compare_from_rtx (op0, op1, code, unsignedp, mode, NULL_RTX, 0); - if (GET_CODE (comparison) == CONST_INT) - return (comparison == const0_rtx ? const0_rtx - : normalizep == 1 ? const1_rtx - : normalizep == -1 ? constm1_rtx - : const_true_rtx); - - /* If the code of COMPARISON doesn't match CODE, something is - wrong; we can no longer be sure that we have the operation. - We could handle this case, but it should not happen. */ - - if (GET_CODE (comparison) != code) - abort (); - - /* Get a reference to the target in the proper mode for this insn. */ - compare_mode = insn_operand_mode[(int) icode][0]; - subtarget = target; - if (preserve_subexpressions_p () - || ! (*insn_operand_predicate[(int) icode][0]) (subtarget, compare_mode)) - subtarget = gen_reg_rtx (compare_mode); - - pattern = GEN_FCN (icode) (subtarget); - if (pattern) - { - emit_insn (pattern); - - /* If we are converting to a wider mode, first convert to - TARGET_MODE, then normalize. This produces better combining - opportunities on machines that have a SIGN_EXTRACT when we are - testing a single bit. This mostly benefits the 68k. - - If STORE_FLAG_VALUE does not have the sign bit set when - interpreted in COMPARE_MODE, we can do this conversion as - unsigned, which is usually more efficient. */ - if (GET_MODE_SIZE (target_mode) > GET_MODE_SIZE (compare_mode)) - { - convert_move (target, subtarget, - (GET_MODE_BITSIZE (compare_mode) - <= HOST_BITS_PER_WIDE_INT) - && 0 == (STORE_FLAG_VALUE - & ((HOST_WIDE_INT) 1 - << (GET_MODE_BITSIZE (compare_mode) -1)))); - op0 = target; - compare_mode = target_mode; - } - else - op0 = subtarget; - - /* If we want to keep subexpressions around, don't reuse our - last target. */ - - if (preserve_subexpressions_p ()) - subtarget = 0; - - /* Now normalize to the proper value in COMPARE_MODE. Sometimes - we don't have to do anything. */ - if (normalizep == 0 || normalizep == STORE_FLAG_VALUE) - ; - else if (normalizep == - STORE_FLAG_VALUE) - op0 = expand_unop (compare_mode, neg_optab, op0, subtarget, 0); - - /* We don't want to use STORE_FLAG_VALUE < 0 below since this - makes it hard to use a value of just the sign bit due to - ANSI integer constant typing rules. */ - else if (GET_MODE_BITSIZE (compare_mode) <= HOST_BITS_PER_WIDE_INT - && (STORE_FLAG_VALUE - & ((HOST_WIDE_INT) 1 - << (GET_MODE_BITSIZE (compare_mode) - 1)))) - op0 = expand_shift (RSHIFT_EXPR, compare_mode, op0, - size_int (GET_MODE_BITSIZE (compare_mode) - 1), - subtarget, normalizep == 1); - else if (STORE_FLAG_VALUE & 1) - { - op0 = expand_and (op0, const1_rtx, subtarget); - if (normalizep == -1) - op0 = expand_unop (compare_mode, neg_optab, op0, op0, 0); - } - else - abort (); - - /* If we were converting to a smaller mode, do the - conversion now. */ - if (target_mode != compare_mode) - { - convert_move (target, op0, 0); - return target; - } - else - return op0; - } - } - - delete_insns_since (last); - - /* If expensive optimizations, use different pseudo registers for each - insn, instead of reusing the same pseudo. This leads to better CSE, - but slows down the compiler, since there are more pseudos */ - subtarget = (!flag_expensive_optimizations - && (target_mode == mode)) ? target : NULL_RTX; - - /* If we reached here, we can't do this with a scc insn. However, there - are some comparisons that can be done directly. For example, if - this is an equality comparison of integers, we can try to exclusive-or - (or subtract) the two operands and use a recursive call to try the - comparison with zero. Don't do any of these cases if branches are - very cheap. */ - - if (BRANCH_COST > 0 - && GET_MODE_CLASS (mode) == MODE_INT && (code == EQ || code == NE) - && op1 != const0_rtx) - { - tem = expand_binop (mode, xor_optab, op0, op1, subtarget, 1, - OPTAB_WIDEN); - - if (tem == 0) - tem = expand_binop (mode, sub_optab, op0, op1, subtarget, 1, - OPTAB_WIDEN); - if (tem != 0) - tem = emit_store_flag (target, code, tem, const0_rtx, - mode, unsignedp, normalizep); - if (tem == 0) - delete_insns_since (last); - return tem; - } - - /* Some other cases we can do are EQ, NE, LE, and GT comparisons with - the constant zero. Reject all other comparisons at this point. Only - do LE and GT if branches are expensive since they are expensive on - 2-operand machines. */ - - if (BRANCH_COST == 0 - || GET_MODE_CLASS (mode) != MODE_INT || op1 != const0_rtx - || (code != EQ && code != NE - && (BRANCH_COST <= 1 || (code != LE && code != GT)))) - return 0; - - /* See what we need to return. We can only return a 1, -1, or the - sign bit. */ - - if (normalizep == 0) - { - if (STORE_FLAG_VALUE == 1 || STORE_FLAG_VALUE == -1) - normalizep = STORE_FLAG_VALUE; - - else if (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT - && ((STORE_FLAG_VALUE & GET_MODE_MASK (mode)) - == (HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1))) - ; - else - return 0; - } - - /* Try to put the result of the comparison in the sign bit. Assume we can't - do the necessary operation below. */ - - tem = 0; - - /* To see if A <= 0, compute (A | (A - 1)). A <= 0 iff that result has - the sign bit set. */ - - if (code == LE) - { - /* This is destructive, so SUBTARGET can't be OP0. */ - if (rtx_equal_p (subtarget, op0)) - subtarget = 0; - - tem = expand_binop (mode, sub_optab, op0, const1_rtx, subtarget, 0, - OPTAB_WIDEN); - if (tem) - tem = expand_binop (mode, ior_optab, op0, tem, subtarget, 0, - OPTAB_WIDEN); - } - - /* To see if A > 0, compute (((signed) A) << BITS) - A, where BITS is the - number of bits in the mode of OP0, minus one. */ - - if (code == GT) - { - if (rtx_equal_p (subtarget, op0)) - subtarget = 0; - - tem = expand_shift (RSHIFT_EXPR, mode, op0, - size_int (GET_MODE_BITSIZE (mode) - 1), - subtarget, 0); - tem = expand_binop (mode, sub_optab, tem, op0, subtarget, 0, - OPTAB_WIDEN); - } - - if (code == EQ || code == NE) - { - /* For EQ or NE, one way to do the comparison is to apply an operation - that converts the operand into a positive number if it is non-zero - or zero if it was originally zero. Then, for EQ, we subtract 1 and - for NE we negate. This puts the result in the sign bit. Then we - normalize with a shift, if needed. - - Two operations that can do the above actions are ABS and FFS, so try - them. If that doesn't work, and MODE is smaller than a full word, - we can use zero-extension to the wider mode (an unsigned conversion) - as the operation. */ - - if (abs_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) - tem = expand_unop (mode, abs_optab, op0, subtarget, 1); - else if (ffs_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) - tem = expand_unop (mode, ffs_optab, op0, subtarget, 1); - else if (GET_MODE_SIZE (mode) < UNITS_PER_WORD) - { - op0 = protect_from_queue (op0, 0); - tem = convert_modes (word_mode, mode, op0, 1); - mode = word_mode; - } - - if (tem != 0) - { - if (code == EQ) - tem = expand_binop (mode, sub_optab, tem, const1_rtx, subtarget, - 0, OPTAB_WIDEN); - else - tem = expand_unop (mode, neg_optab, tem, subtarget, 0); - } - - /* If we couldn't do it that way, for NE we can "or" the two's complement - of the value with itself. For EQ, we take the one's complement of - that "or", which is an extra insn, so we only handle EQ if branches - are expensive. */ - - if (tem == 0 && (code == NE || BRANCH_COST > 1)) - { - if (rtx_equal_p (subtarget, op0)) - subtarget = 0; - - tem = expand_unop (mode, neg_optab, op0, subtarget, 0); - tem = expand_binop (mode, ior_optab, tem, op0, subtarget, 0, - OPTAB_WIDEN); - - if (tem && code == EQ) - tem = expand_unop (mode, one_cmpl_optab, tem, subtarget, 0); - } - } - - if (tem && normalizep) - tem = expand_shift (RSHIFT_EXPR, mode, tem, - size_int (GET_MODE_BITSIZE (mode) - 1), - subtarget, normalizep == 1); - - if (tem) - { - if (GET_MODE (tem) != target_mode) - { - convert_move (target, tem, 0); - tem = target; - } - else if (!subtarget) - { - emit_move_insn (target, tem); - tem = target; - } - } - else - delete_insns_since (last); - - return tem; -} - -/* Like emit_store_flag, but always succeeds. */ - -rtx -emit_store_flag_force (target, code, op0, op1, mode, unsignedp, normalizep) - rtx target; - enum rtx_code code; - rtx op0, op1; - enum machine_mode mode; - int unsignedp; - int normalizep; -{ - rtx tem, label; - - /* First see if emit_store_flag can do the job. */ - tem = emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep); - if (tem != 0) - return tem; - - if (normalizep == 0) - normalizep = 1; - - /* If this failed, we have to do this with set/compare/jump/set code. */ - - if (GET_CODE (target) != REG - || reg_mentioned_p (target, op0) || reg_mentioned_p (target, op1)) - target = gen_reg_rtx (GET_MODE (target)); - - emit_move_insn (target, const1_rtx); - tem = compare_from_rtx (op0, op1, code, unsignedp, mode, NULL_RTX, 0); - if (GET_CODE (tem) == CONST_INT) - return tem; - - label = gen_label_rtx (); - if (bcc_gen_fctn[(int) code] == 0) - abort (); - - emit_jump_insn ((*bcc_gen_fctn[(int) code]) (label)); - emit_move_insn (target, const0_rtx); - emit_label (label); - - return target; -} |