| /* Subroutines for insn-output.c for Vax. |
| Copyright (C) 1987, 1994, 1995, 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 <stdio.h> |
| #include "config.h" |
| #include "rtl.h" |
| #include "regs.h" |
| #include "hard-reg-set.h" |
| #include "real.h" |
| #include "insn-config.h" |
| #include "conditions.h" |
| #include "insn-flags.h" |
| #include "output.h" |
| #include "insn-attr.h" |
| #ifdef VMS_TARGET |
| #include "tree.h" |
| #endif |
| |
| /* This is like nonimmediate_operand with a restriction on the type of MEM. */ |
| |
| void |
| split_quadword_operands (operands, low, n) |
| rtx *operands, *low; |
| int n; |
| { |
| int i; |
| /* Split operands. */ |
| |
| low[0] = low[1] = low[2] = 0; |
| for (i = 0; i < 3; i++) |
| { |
| if (low[i]) |
| /* it's already been figured out */; |
| else if (GET_CODE (operands[i]) == MEM |
| && (GET_CODE (XEXP (operands[i], 0)) == POST_INC)) |
| { |
| rtx addr = XEXP (operands[i], 0); |
| operands[i] = low[i] = gen_rtx (MEM, SImode, addr); |
| if (which_alternative == 0 && i == 0) |
| { |
| addr = XEXP (operands[i], 0); |
| operands[i+1] = low[i+1] = gen_rtx (MEM, SImode, addr); |
| } |
| } |
| else |
| { |
| low[i] = operand_subword (operands[i], 0, 0, DImode); |
| operands[i] = operand_subword (operands[i], 1, 0, DImode); |
| } |
| } |
| } |
| |
| print_operand_address (file, addr) |
| FILE *file; |
| register rtx addr; |
| { |
| register rtx reg1, reg2, breg, ireg; |
| rtx offset; |
| |
| retry: |
| switch (GET_CODE (addr)) |
| { |
| case MEM: |
| fprintf (file, "*"); |
| addr = XEXP (addr, 0); |
| goto retry; |
| |
| case REG: |
| fprintf (file, "(%s)", reg_names[REGNO (addr)]); |
| break; |
| |
| case PRE_DEC: |
| fprintf (file, "-(%s)", reg_names[REGNO (XEXP (addr, 0))]); |
| break; |
| |
| case POST_INC: |
| fprintf (file, "(%s)+", reg_names[REGNO (XEXP (addr, 0))]); |
| break; |
| |
| case PLUS: |
| /* There can be either two or three things added here. One must be a |
| REG. One can be either a REG or a MULT of a REG and an appropriate |
| constant, and the third can only be a constant or a MEM. |
| |
| We get these two or three things and put the constant or MEM in |
| OFFSET, the MULT or REG in IREG, and the REG in BREG. If we have |
| a register and can't tell yet if it is a base or index register, |
| put it into REG1. */ |
| |
| reg1 = 0; ireg = 0; breg = 0; offset = 0; |
| |
| if (CONSTANT_ADDRESS_P (XEXP (addr, 0)) |
| || GET_CODE (XEXP (addr, 0)) == MEM) |
| { |
| offset = XEXP (addr, 0); |
| addr = XEXP (addr, 1); |
| } |
| else if (CONSTANT_ADDRESS_P (XEXP (addr, 1)) |
| || GET_CODE (XEXP (addr, 1)) == MEM) |
| { |
| offset = XEXP (addr, 1); |
| addr = XEXP (addr, 0); |
| } |
| else if (GET_CODE (XEXP (addr, 1)) == MULT) |
| { |
| ireg = XEXP (addr, 1); |
| addr = XEXP (addr, 0); |
| } |
| else if (GET_CODE (XEXP (addr, 0)) == MULT) |
| { |
| ireg = XEXP (addr, 0); |
| addr = XEXP (addr, 1); |
| } |
| else if (GET_CODE (XEXP (addr, 1)) == REG) |
| { |
| reg1 = XEXP (addr, 1); |
| addr = XEXP (addr, 0); |
| } |
| else if (GET_CODE (XEXP (addr, 0)) == REG) |
| { |
| reg1 = XEXP (addr, 0); |
| addr = XEXP (addr, 1); |
| } |
| else |
| abort (); |
| |
| if (GET_CODE (addr) == REG) |
| { |
| if (reg1) |
| ireg = addr; |
| else |
| reg1 = addr; |
| } |
| else if (GET_CODE (addr) == MULT) |
| ireg = addr; |
| else if (GET_CODE (addr) == PLUS) |
| { |
| if (CONSTANT_ADDRESS_P (XEXP (addr, 0)) |
| || GET_CODE (XEXP (addr, 0)) == MEM) |
| { |
| if (offset) |
| { |
| if (GET_CODE (offset) == CONST_INT) |
| offset = plus_constant (XEXP (addr, 0), INTVAL (offset)); |
| else if (GET_CODE (XEXP (addr, 0)) == CONST_INT) |
| offset = plus_constant (offset, INTVAL (XEXP (addr, 0))); |
| else |
| abort (); |
| } |
| offset = XEXP (addr, 0); |
| } |
| else if (GET_CODE (XEXP (addr, 0)) == REG) |
| { |
| if (reg1) |
| ireg = reg1, breg = XEXP (addr, 0), reg1 = 0; |
| else |
| reg1 = XEXP (addr, 0); |
| } |
| else if (GET_CODE (XEXP (addr, 0)) == MULT) |
| { |
| if (ireg) |
| abort (); |
| ireg = XEXP (addr, 0); |
| } |
| else |
| abort (); |
| |
| if (CONSTANT_ADDRESS_P (XEXP (addr, 1)) |
| || GET_CODE (XEXP (addr, 1)) == MEM) |
| { |
| if (offset) |
| { |
| if (GET_CODE (offset) == CONST_INT) |
| offset = plus_constant (XEXP (addr, 1), INTVAL (offset)); |
| else if (GET_CODE (XEXP (addr, 1)) == CONST_INT) |
| offset = plus_constant (offset, INTVAL (XEXP (addr, 1))); |
| else |
| abort (); |
| } |
| offset = XEXP (addr, 1); |
| } |
| else if (GET_CODE (XEXP (addr, 1)) == REG) |
| { |
| if (reg1) |
| ireg = reg1, breg = XEXP (addr, 1), reg1 = 0; |
| else |
| reg1 = XEXP (addr, 1); |
| } |
| else if (GET_CODE (XEXP (addr, 1)) == MULT) |
| { |
| if (ireg) |
| abort (); |
| ireg = XEXP (addr, 1); |
| } |
| else |
| abort (); |
| } |
| else |
| abort (); |
| |
| /* If REG1 is non-zero, figure out if it is a base or index register. */ |
| if (reg1) |
| { |
| if (breg != 0 || (offset && GET_CODE (offset) == MEM)) |
| { |
| if (ireg) |
| abort (); |
| ireg = reg1; |
| } |
| else |
| breg = reg1; |
| } |
| |
| if (offset != 0) |
| output_address (offset); |
| |
| if (breg != 0) |
| fprintf (file, "(%s)", reg_names[REGNO (breg)]); |
| |
| if (ireg != 0) |
| { |
| if (GET_CODE (ireg) == MULT) |
| ireg = XEXP (ireg, 0); |
| if (GET_CODE (ireg) != REG) |
| abort (); |
| fprintf (file, "[%s]", reg_names[REGNO (ireg)]); |
| } |
| break; |
| |
| default: |
| output_addr_const (file, addr); |
| } |
| } |
| |
| char * |
| rev_cond_name (op) |
| rtx op; |
| { |
| switch (GET_CODE (op)) |
| { |
| case EQ: |
| return "neq"; |
| case NE: |
| return "eql"; |
| case LT: |
| return "geq"; |
| case LE: |
| return "gtr"; |
| case GT: |
| return "leq"; |
| case GE: |
| return "lss"; |
| case LTU: |
| return "gequ"; |
| case LEU: |
| return "gtru"; |
| case GTU: |
| return "lequ"; |
| case GEU: |
| return "lssu"; |
| |
| default: |
| abort (); |
| } |
| } |
| |
| int |
| vax_float_literal(c) |
| register rtx c; |
| { |
| register enum machine_mode mode; |
| int i; |
| union {double d; int i[2];} val; |
| |
| if (GET_CODE (c) != CONST_DOUBLE) |
| return 0; |
| |
| mode = GET_MODE (c); |
| |
| if (c == const_tiny_rtx[(int) mode][0] |
| || c == const_tiny_rtx[(int) mode][1] |
| || c == const_tiny_rtx[(int) mode][2]) |
| return 1; |
| |
| #if HOST_FLOAT_FORMAT == VAX_FLOAT_FORMAT |
| |
| val.i[0] = CONST_DOUBLE_LOW (c); |
| val.i[1] = CONST_DOUBLE_HIGH (c); |
| |
| for (i = 0; i < 7; i ++) |
| if (val.d == 1 << i || val.d == 1 / (1 << i)) |
| return 1; |
| #endif |
| return 0; |
| } |
| |
| |
| /* Return the cost in cycles of a memory address, relative to register |
| indirect. |
| |
| Each of the following adds the indicated number of cycles: |
| |
| 1 - symbolic address |
| 1 - pre-decrement |
| 1 - indexing and/or offset(register) |
| 2 - indirect */ |
| |
| |
| int vax_address_cost(addr) |
| register rtx addr; |
| { |
| int reg = 0, indexed = 0, indir = 0, offset = 0, predec = 0; |
| rtx plus_op0 = 0, plus_op1 = 0; |
| restart: |
| switch (GET_CODE (addr)) |
| { |
| case PRE_DEC: |
| predec = 1; |
| case REG: |
| case SUBREG: |
| case POST_INC: |
| reg = 1; |
| break; |
| case MULT: |
| indexed = 1; /* 2 on VAX 2 */ |
| break; |
| case CONST_INT: |
| /* byte offsets cost nothing (on a VAX 2, they cost 1 cycle) */ |
| if (offset == 0) |
| offset = (unsigned)(INTVAL(addr)+128) > 256; |
| break; |
| case CONST: |
| case SYMBOL_REF: |
| offset = 1; /* 2 on VAX 2 */ |
| break; |
| case LABEL_REF: /* this is probably a byte offset from the pc */ |
| if (offset == 0) |
| offset = 1; |
| break; |
| case PLUS: |
| if (plus_op0) |
| plus_op1 = XEXP (addr, 0); |
| else |
| plus_op0 = XEXP (addr, 0); |
| addr = XEXP (addr, 1); |
| goto restart; |
| case MEM: |
| indir = 2; /* 3 on VAX 2 */ |
| addr = XEXP (addr, 0); |
| goto restart; |
| } |
| |
| /* Up to 3 things can be added in an address. They are stored in |
| plus_op0, plus_op1, and addr. */ |
| |
| if (plus_op0) |
| { |
| addr = plus_op0; |
| plus_op0 = 0; |
| goto restart; |
| } |
| if (plus_op1) |
| { |
| addr = plus_op1; |
| plus_op1 = 0; |
| goto restart; |
| } |
| /* Indexing and register+offset can both be used (except on a VAX 2) |
| without increasing execution time over either one alone. */ |
| if (reg && indexed && offset) |
| return reg + indir + offset + predec; |
| return reg + indexed + indir + offset + predec; |
| } |
| |
| |
| /* Cost of an expression on a VAX. This version has costs tuned for the |
| CVAX chip (found in the VAX 3 series) with comments for variations on |
| other models. */ |
| |
| int |
| vax_rtx_cost (x) |
| register rtx x; |
| { |
| register enum rtx_code code = GET_CODE (x); |
| enum machine_mode mode = GET_MODE (x); |
| register int c; |
| int i = 0; /* may be modified in switch */ |
| char *fmt = GET_RTX_FORMAT (code); /* may be modified in switch */ |
| |
| switch (code) |
| { |
| case POST_INC: |
| return 2; |
| case PRE_DEC: |
| return 3; |
| case MULT: |
| switch (mode) |
| { |
| case DFmode: |
| c = 16; /* 4 on VAX 9000 */ |
| break; |
| case SFmode: |
| c = 9; /* 4 on VAX 9000, 12 on VAX 2 */ |
| break; |
| case DImode: |
| c = 16; /* 6 on VAX 9000, 28 on VAX 2 */ |
| break; |
| case SImode: |
| case HImode: |
| case QImode: |
| c = 10; /* 3-4 on VAX 9000, 20-28 on VAX 2 */ |
| break; |
| } |
| break; |
| case UDIV: |
| c = 17; |
| break; |
| case DIV: |
| if (mode == DImode) |
| c = 30; /* highly variable */ |
| else if (mode == DFmode) |
| /* divide takes 28 cycles if the result is not zero, 13 otherwise */ |
| c = 24; |
| else |
| c = 11; /* 25 on VAX 2 */ |
| break; |
| case MOD: |
| c = 23; |
| break; |
| case UMOD: |
| c = 29; |
| break; |
| case FLOAT: |
| c = 6 + (mode == DFmode) + (GET_MODE (XEXP (x, 0)) != SImode); |
| /* 4 on VAX 9000 */ |
| break; |
| case FIX: |
| c = 7; /* 17 on VAX 2 */ |
| break; |
| case ASHIFT: |
| case LSHIFTRT: |
| case ASHIFTRT: |
| if (mode == DImode) |
| c = 12; |
| else |
| c = 10; /* 6 on VAX 9000 */ |
| break; |
| case ROTATE: |
| case ROTATERT: |
| c = 6; /* 5 on VAX 2, 4 on VAX 9000 */ |
| if (GET_CODE (XEXP (x, 1)) == CONST_INT) |
| fmt = "e"; /* all constant rotate counts are short */ |
| break; |
| case PLUS: |
| /* Check for small negative integer operand: subl2 can be used with |
| a short positive constant instead. */ |
| if (GET_CODE (XEXP (x, 1)) == CONST_INT) |
| if ((unsigned)(INTVAL (XEXP (x, 1)) + 63) < 127) |
| fmt = "e"; |
| case MINUS: |
| c = (mode == DFmode) ? 13 : 8; /* 6/8 on VAX 9000, 16/15 on VAX 2 */ |
| case IOR: |
| case XOR: |
| c = 3; |
| break; |
| case AND: |
| /* AND is special because the first operand is complemented. */ |
| c = 3; |
| if (GET_CODE (XEXP (x, 0)) == CONST_INT) |
| { |
| if ((unsigned)~INTVAL (XEXP (x, 0)) > 63) |
| c = 4; |
| fmt = "e"; |
| i = 1; |
| } |
| break; |
| case NEG: |
| if (mode == DFmode) |
| return 9; |
| else if (mode == SFmode) |
| return 6; |
| else if (mode == DImode) |
| return 4; |
| case NOT: |
| return 2; |
| case ZERO_EXTRACT: |
| case SIGN_EXTRACT: |
| c = 15; |
| break; |
| case MEM: |
| if (mode == DImode || mode == DFmode) |
| c = 5; /* 7 on VAX 2 */ |
| else |
| c = 3; /* 4 on VAX 2 */ |
| x = XEXP (x, 0); |
| if (GET_CODE (x) == REG || GET_CODE (x) == POST_INC) |
| return c; |
| return c + vax_address_cost (x); |
| default: |
| c = 3; |
| break; |
| } |
| |
| |
| /* Now look inside the expression. Operands which are not registers or |
| short constants add to the cost. |
| |
| FMT and I may have been adjusted in the switch above for instructions |
| which require special handling */ |
| |
| while (*fmt++ == 'e') |
| { |
| register rtx op = XEXP (x, i++); |
| code = GET_CODE (op); |
| |
| /* A NOT is likely to be found as the first operand of an AND |
| (in which case the relevant cost is of the operand inside |
| the not) and not likely to be found anywhere else. */ |
| if (code == NOT) |
| op = XEXP (op, 0), code = GET_CODE (op); |
| |
| switch (code) |
| { |
| case CONST_INT: |
| if ((unsigned)INTVAL (op) > 63 && GET_MODE (x) != QImode) |
| c += 1; /* 2 on VAX 2 */ |
| break; |
| case CONST: |
| case LABEL_REF: |
| case SYMBOL_REF: |
| c += 1; /* 2 on VAX 2 */ |
| break; |
| case CONST_DOUBLE: |
| if (GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT) |
| { |
| /* Registers are faster than floating point constants -- even |
| those constants which can be encoded in a single byte. */ |
| if (vax_float_literal (op)) |
| c++; |
| else |
| c += (GET_MODE (x) == DFmode) ? 3 : 2; |
| } |
| else |
| { |
| if (CONST_DOUBLE_HIGH (op) != 0 |
| || (unsigned)CONST_DOUBLE_LOW (op) > 63) |
| c += 2; |
| } |
| break; |
| case MEM: |
| c += 1; /* 2 on VAX 2 */ |
| if (GET_CODE (XEXP (op, 0)) != REG) |
| c += vax_address_cost (XEXP (op, 0)); |
| break; |
| case REG: |
| case SUBREG: |
| break; |
| default: |
| c += 1; |
| break; |
| } |
| } |
| return c; |
| } |
| |
| /* Check a `double' value for validity for a particular machine mode. */ |
| |
| static char *float_strings[] = |
| { |
| "1.70141173319264430e+38", /* 2^127 (2^24 - 1) / 2^24 */ |
| "-1.70141173319264430e+38", |
| "2.93873587705571877e-39", /* 2^-128 */ |
| "-2.93873587705571877e-39" |
| }; |
| |
| static REAL_VALUE_TYPE float_values[4]; |
| |
| static int inited_float_values = 0; |
| |
| |
| int |
| check_float_value (mode, d, overflow) |
| enum machine_mode mode; |
| REAL_VALUE_TYPE *d; |
| int overflow; |
| { |
| if (inited_float_values == 0) |
| { |
| int i; |
| for (i = 0; i < 4; i++) |
| { |
| float_values[i] = REAL_VALUE_ATOF (float_strings[i], DFmode); |
| } |
| |
| inited_float_values = 1; |
| } |
| |
| if (overflow) |
| { |
| bcopy (&float_values[0], d, sizeof (REAL_VALUE_TYPE)); |
| return 1; |
| } |
| |
| if ((mode) == SFmode) |
| { |
| REAL_VALUE_TYPE r; |
| bcopy (d, &r, sizeof (REAL_VALUE_TYPE)); |
| if (REAL_VALUES_LESS (float_values[0], r)) |
| { |
| bcopy (&float_values[0], d, sizeof (REAL_VALUE_TYPE)); |
| return 1; |
| } |
| else if (REAL_VALUES_LESS (r, float_values[1])) |
| { |
| bcopy (&float_values[1], d, sizeof (REAL_VALUE_TYPE)); |
| return 1; |
| } |
| else if (REAL_VALUES_LESS (dconst0, r) |
| && REAL_VALUES_LESS (r, float_values[2])) |
| { |
| bcopy (&dconst0, d, sizeof (REAL_VALUE_TYPE)); |
| return 1; |
| } |
| else if (REAL_VALUES_LESS (r, dconst0) |
| && REAL_VALUES_LESS (float_values[3], r)) |
| { |
| bcopy (&dconst0, d, sizeof (REAL_VALUE_TYPE)); |
| return 1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| #ifdef VMS_TARGET |
| /* Additional support code for VMS target. */ |
| |
| /* Linked list of all externals that are to be emitted when optimizing |
| for the global pointer if they haven't been declared by the end of |
| the program with an appropriate .comm or initialization. */ |
| |
| static |
| struct extern_list { |
| struct extern_list *next; /* next external */ |
| char *name; /* name of the external */ |
| int size; /* external's actual size */ |
| int in_const; /* section type flag */ |
| } *extern_head = 0, *pending_head = 0; |
| |
| /* Check whether NAME is already on the external definition list. If not, |
| add it to either that list or the pending definition list. */ |
| |
| void |
| vms_check_external (decl, name, pending) |
| tree decl; |
| char *name; |
| int pending; |
| { |
| register struct extern_list *p, *p0; |
| |
| for (p = extern_head; p; p = p->next) |
| if (!strcmp (p->name, name)) |
| return; |
| |
| for (p = pending_head, p0 = 0; p; p0 = p, p = p->next) |
| if (!strcmp (p->name, name)) |
| { |
| if (pending) |
| return; |
| |
| /* Was pending, but has now been defined; move it to other list. */ |
| if (p == pending_head) |
| pending_head = p->next; |
| else |
| p0->next = p->next; |
| p->next = extern_head; |
| extern_head = p; |
| return; |
| } |
| |
| /* Not previously seen; create a new list entry. */ |
| p = (struct extern_list *)permalloc ((long) sizeof (struct extern_list)); |
| p->name = name; |
| |
| if (pending) |
| { |
| /* Save the size and section type and link to `pending' list. */ |
| p->size = (DECL_SIZE (decl) == 0) ? 0 : |
| TREE_INT_CST_LOW (size_binop (CEIL_DIV_EXPR, DECL_SIZE (decl), |
| size_int (BITS_PER_UNIT))); |
| p->in_const = (TREE_READONLY (decl) && ! TREE_THIS_VOLATILE (decl)); |
| |
| p->next = pending_head; |
| pending_head = p; |
| } |
| else |
| { |
| /* Size and section type don't matter; link to `declared' list. */ |
| p->size = p->in_const = 0; /* arbitrary init */ |
| |
| p->next = extern_head; |
| extern_head = p; |
| } |
| return; |
| } |
| |
| void |
| vms_flush_pending_externals (file) |
| FILE *file; |
| { |
| register struct extern_list *p; |
| |
| while (pending_head) |
| { |
| /* Move next pending declaration to the "done" list. */ |
| p = pending_head; |
| pending_head = p->next; |
| p->next = extern_head; |
| extern_head = p; |
| |
| /* Now output the actual declaration. */ |
| if (p->in_const) |
| const_section (); |
| else |
| data_section (); |
| fputs (".comm ", file); |
| assemble_name (file, p->name); |
| fprintf (file, ",%d\n", p->size); |
| } |
| } |
| #endif /* VMS_TARGET */ |
| |
| #ifdef VMS |
| /* Additional support code for VMS host. */ |
| |
| #ifdef QSORT_WORKAROUND |
| /* |
| Do not use VAXCRTL's qsort() due to a severe bug: once you've |
| sorted something which has a size that's an exact multiple of 4 |
| and is longword aligned, you cannot safely sort anything which |
| is either not a multiple of 4 in size or not longword aligned. |
| A static "move-by-longword" optimization flag inside qsort() is |
| never reset. This is known of affect VMS V4.6 through VMS V5.5-1, |
| and was finally fixed in VMS V5.5-2. |
| |
| In this work-around an insertion sort is used for simplicity. |
| The qsort code from glibc should probably be used instead. |
| */ |
| void |
| not_qsort (array, count, size, compare) |
| void *array; |
| unsigned count, size; |
| int (*compare)(); |
| { |
| |
| if (size == sizeof (short)) |
| { |
| register int i; |
| register short *next, *prev; |
| short tmp, *base = array; |
| |
| for (next = base, i = count - 1; i > 0; i--) |
| { |
| prev = next++; |
| if ((*compare)(next, prev) < 0) |
| { |
| tmp = *next; |
| do *(prev + 1) = *prev; |
| while (--prev >= base ? (*compare)(&tmp, prev) < 0 : 0); |
| *(prev + 1) = tmp; |
| } |
| } |
| } |
| else if (size == sizeof (long)) |
| { |
| register int i; |
| register long *next, *prev; |
| long tmp, *base = array; |
| |
| for (next = base, i = count - 1; i > 0; i--) |
| { |
| prev = next++; |
| if ((*compare)(next, prev) < 0) |
| { |
| tmp = *next; |
| do *(prev + 1) = *prev; |
| while (--prev >= base ? (*compare)(&tmp, prev) < 0 : 0); |
| *(prev + 1) = tmp; |
| } |
| } |
| } |
| else /* arbitrary size */ |
| { |
| register int i; |
| register char *next, *prev, *tmp = alloca (size), *base = array; |
| |
| for (next = base, i = count - 1; i > 0; i--) |
| { /* count-1 forward iterations */ |
| prev = next, next += size; /* increment front pointer */ |
| if ((*compare)(next, prev) < 0) |
| { /* found element out of order; move others up then re-insert */ |
| memcpy (tmp, next, size); /* save smaller element */ |
| do { memcpy (prev + size, prev, size); /* move larger elem. up */ |
| prev -= size; /* decrement back pointer */ |
| } while (prev >= base ? (*compare)(tmp, prev) < 0 : 0); |
| memcpy (prev + size, tmp, size); /* restore small element */ |
| } |
| } |
| #ifdef USE_C_ALLOCA |
| alloca (0); |
| #endif |
| } |
| |
| return; |
| } |
| #endif /* QSORT_WORKAROUND */ |
| |
| #endif /* VMS */ |