/* Subroutines for insn-output.c for SPARC.
|
/* Subroutines for insn-output.c for SPARC.
|
Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
|
Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
|
1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
|
1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
|
Free Software Foundation, Inc.
|
Free Software Foundation, Inc.
|
Contributed by Michael Tiemann (tiemann@cygnus.com)
|
Contributed by Michael Tiemann (tiemann@cygnus.com)
|
64-bit SPARC-V9 support by Michael Tiemann, Jim Wilson, and Doug Evans,
|
64-bit SPARC-V9 support by Michael Tiemann, Jim Wilson, and Doug Evans,
|
at Cygnus Support.
|
at Cygnus Support.
|
|
|
This file is part of GCC.
|
This file is part of GCC.
|
|
|
GCC is free software; you can redistribute it and/or modify
|
GCC is free software; you can redistribute it and/or modify
|
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
the Free Software Foundation; either version 3, or (at your option)
|
the Free Software Foundation; either version 3, or (at your option)
|
any later version.
|
any later version.
|
|
|
GCC is distributed in the hope that it will be useful,
|
GCC is distributed in the hope that it will be useful,
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
GNU General Public License for more details.
|
GNU General Public License for more details.
|
|
|
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
along with GCC; see the file COPYING3. If not see
|
along with GCC; see the file COPYING3. If not see
|
<http://www.gnu.org/licenses/>. */
|
<http://www.gnu.org/licenses/>. */
|
|
|
#include "config.h"
|
#include "config.h"
|
#include "system.h"
|
#include "system.h"
|
#include "coretypes.h"
|
#include "coretypes.h"
|
#include "tm.h"
|
#include "tm.h"
|
#include "tree.h"
|
#include "tree.h"
|
#include "rtl.h"
|
#include "rtl.h"
|
#include "regs.h"
|
#include "regs.h"
|
#include "hard-reg-set.h"
|
#include "hard-reg-set.h"
|
#include "real.h"
|
#include "real.h"
|
#include "insn-config.h"
|
#include "insn-config.h"
|
#include "insn-codes.h"
|
#include "insn-codes.h"
|
#include "conditions.h"
|
#include "conditions.h"
|
#include "output.h"
|
#include "output.h"
|
#include "insn-attr.h"
|
#include "insn-attr.h"
|
#include "flags.h"
|
#include "flags.h"
|
#include "function.h"
|
#include "function.h"
|
#include "expr.h"
|
#include "expr.h"
|
#include "optabs.h"
|
#include "optabs.h"
|
#include "recog.h"
|
#include "recog.h"
|
#include "toplev.h"
|
#include "toplev.h"
|
#include "ggc.h"
|
#include "ggc.h"
|
#include "tm_p.h"
|
#include "tm_p.h"
|
#include "debug.h"
|
#include "debug.h"
|
#include "target.h"
|
#include "target.h"
|
#include "target-def.h"
|
#include "target-def.h"
|
#include "cfglayout.h"
|
#include "cfglayout.h"
|
#include "gimple.h"
|
#include "gimple.h"
|
#include "langhooks.h"
|
#include "langhooks.h"
|
#include "params.h"
|
#include "params.h"
|
#include "df.h"
|
#include "df.h"
|
#include "dwarf2out.h"
|
#include "dwarf2out.h"
|
|
|
/* Processor costs */
|
/* Processor costs */
|
static const
|
static const
|
struct processor_costs cypress_costs = {
|
struct processor_costs cypress_costs = {
|
COSTS_N_INSNS (2), /* int load */
|
COSTS_N_INSNS (2), /* int load */
|
COSTS_N_INSNS (2), /* int signed load */
|
COSTS_N_INSNS (2), /* int signed load */
|
COSTS_N_INSNS (2), /* int zeroed load */
|
COSTS_N_INSNS (2), /* int zeroed load */
|
COSTS_N_INSNS (2), /* float load */
|
COSTS_N_INSNS (2), /* float load */
|
COSTS_N_INSNS (5), /* fmov, fneg, fabs */
|
COSTS_N_INSNS (5), /* fmov, fneg, fabs */
|
COSTS_N_INSNS (5), /* fadd, fsub */
|
COSTS_N_INSNS (5), /* fadd, fsub */
|
COSTS_N_INSNS (1), /* fcmp */
|
COSTS_N_INSNS (1), /* fcmp */
|
COSTS_N_INSNS (1), /* fmov, fmovr */
|
COSTS_N_INSNS (1), /* fmov, fmovr */
|
COSTS_N_INSNS (7), /* fmul */
|
COSTS_N_INSNS (7), /* fmul */
|
COSTS_N_INSNS (37), /* fdivs */
|
COSTS_N_INSNS (37), /* fdivs */
|
COSTS_N_INSNS (37), /* fdivd */
|
COSTS_N_INSNS (37), /* fdivd */
|
COSTS_N_INSNS (63), /* fsqrts */
|
COSTS_N_INSNS (63), /* fsqrts */
|
COSTS_N_INSNS (63), /* fsqrtd */
|
COSTS_N_INSNS (63), /* fsqrtd */
|
COSTS_N_INSNS (1), /* imul */
|
COSTS_N_INSNS (1), /* imul */
|
COSTS_N_INSNS (1), /* imulX */
|
COSTS_N_INSNS (1), /* imulX */
|
0, /* imul bit factor */
|
0, /* imul bit factor */
|
COSTS_N_INSNS (1), /* idiv */
|
COSTS_N_INSNS (1), /* idiv */
|
COSTS_N_INSNS (1), /* idivX */
|
COSTS_N_INSNS (1), /* idivX */
|
COSTS_N_INSNS (1), /* movcc/movr */
|
COSTS_N_INSNS (1), /* movcc/movr */
|
0, /* shift penalty */
|
0, /* shift penalty */
|
};
|
};
|
|
|
static const
|
static const
|
struct processor_costs supersparc_costs = {
|
struct processor_costs supersparc_costs = {
|
COSTS_N_INSNS (1), /* int load */
|
COSTS_N_INSNS (1), /* int load */
|
COSTS_N_INSNS (1), /* int signed load */
|
COSTS_N_INSNS (1), /* int signed load */
|
COSTS_N_INSNS (1), /* int zeroed load */
|
COSTS_N_INSNS (1), /* int zeroed load */
|
COSTS_N_INSNS (0), /* float load */
|
COSTS_N_INSNS (0), /* float load */
|
COSTS_N_INSNS (3), /* fmov, fneg, fabs */
|
COSTS_N_INSNS (3), /* fmov, fneg, fabs */
|
COSTS_N_INSNS (3), /* fadd, fsub */
|
COSTS_N_INSNS (3), /* fadd, fsub */
|
COSTS_N_INSNS (3), /* fcmp */
|
COSTS_N_INSNS (3), /* fcmp */
|
COSTS_N_INSNS (1), /* fmov, fmovr */
|
COSTS_N_INSNS (1), /* fmov, fmovr */
|
COSTS_N_INSNS (3), /* fmul */
|
COSTS_N_INSNS (3), /* fmul */
|
COSTS_N_INSNS (6), /* fdivs */
|
COSTS_N_INSNS (6), /* fdivs */
|
COSTS_N_INSNS (9), /* fdivd */
|
COSTS_N_INSNS (9), /* fdivd */
|
COSTS_N_INSNS (12), /* fsqrts */
|
COSTS_N_INSNS (12), /* fsqrts */
|
COSTS_N_INSNS (12), /* fsqrtd */
|
COSTS_N_INSNS (12), /* fsqrtd */
|
COSTS_N_INSNS (4), /* imul */
|
COSTS_N_INSNS (4), /* imul */
|
COSTS_N_INSNS (4), /* imulX */
|
COSTS_N_INSNS (4), /* imulX */
|
0, /* imul bit factor */
|
0, /* imul bit factor */
|
COSTS_N_INSNS (4), /* idiv */
|
COSTS_N_INSNS (4), /* idiv */
|
COSTS_N_INSNS (4), /* idivX */
|
COSTS_N_INSNS (4), /* idivX */
|
COSTS_N_INSNS (1), /* movcc/movr */
|
COSTS_N_INSNS (1), /* movcc/movr */
|
1, /* shift penalty */
|
1, /* shift penalty */
|
};
|
};
|
|
|
static const
|
static const
|
struct processor_costs hypersparc_costs = {
|
struct processor_costs hypersparc_costs = {
|
COSTS_N_INSNS (1), /* int load */
|
COSTS_N_INSNS (1), /* int load */
|
COSTS_N_INSNS (1), /* int signed load */
|
COSTS_N_INSNS (1), /* int signed load */
|
COSTS_N_INSNS (1), /* int zeroed load */
|
COSTS_N_INSNS (1), /* int zeroed load */
|
COSTS_N_INSNS (1), /* float load */
|
COSTS_N_INSNS (1), /* float load */
|
COSTS_N_INSNS (1), /* fmov, fneg, fabs */
|
COSTS_N_INSNS (1), /* fmov, fneg, fabs */
|
COSTS_N_INSNS (1), /* fadd, fsub */
|
COSTS_N_INSNS (1), /* fadd, fsub */
|
COSTS_N_INSNS (1), /* fcmp */
|
COSTS_N_INSNS (1), /* fcmp */
|
COSTS_N_INSNS (1), /* fmov, fmovr */
|
COSTS_N_INSNS (1), /* fmov, fmovr */
|
COSTS_N_INSNS (1), /* fmul */
|
COSTS_N_INSNS (1), /* fmul */
|
COSTS_N_INSNS (8), /* fdivs */
|
COSTS_N_INSNS (8), /* fdivs */
|
COSTS_N_INSNS (12), /* fdivd */
|
COSTS_N_INSNS (12), /* fdivd */
|
COSTS_N_INSNS (17), /* fsqrts */
|
COSTS_N_INSNS (17), /* fsqrts */
|
COSTS_N_INSNS (17), /* fsqrtd */
|
COSTS_N_INSNS (17), /* fsqrtd */
|
COSTS_N_INSNS (17), /* imul */
|
COSTS_N_INSNS (17), /* imul */
|
COSTS_N_INSNS (17), /* imulX */
|
COSTS_N_INSNS (17), /* imulX */
|
0, /* imul bit factor */
|
0, /* imul bit factor */
|
COSTS_N_INSNS (17), /* idiv */
|
COSTS_N_INSNS (17), /* idiv */
|
COSTS_N_INSNS (17), /* idivX */
|
COSTS_N_INSNS (17), /* idivX */
|
COSTS_N_INSNS (1), /* movcc/movr */
|
COSTS_N_INSNS (1), /* movcc/movr */
|
0, /* shift penalty */
|
0, /* shift penalty */
|
};
|
};
|
|
|
static const
|
static const
|
struct processor_costs sparclet_costs = {
|
struct processor_costs sparclet_costs = {
|
COSTS_N_INSNS (3), /* int load */
|
COSTS_N_INSNS (3), /* int load */
|
COSTS_N_INSNS (3), /* int signed load */
|
COSTS_N_INSNS (3), /* int signed load */
|
COSTS_N_INSNS (1), /* int zeroed load */
|
COSTS_N_INSNS (1), /* int zeroed load */
|
COSTS_N_INSNS (1), /* float load */
|
COSTS_N_INSNS (1), /* float load */
|
COSTS_N_INSNS (1), /* fmov, fneg, fabs */
|
COSTS_N_INSNS (1), /* fmov, fneg, fabs */
|
COSTS_N_INSNS (1), /* fadd, fsub */
|
COSTS_N_INSNS (1), /* fadd, fsub */
|
COSTS_N_INSNS (1), /* fcmp */
|
COSTS_N_INSNS (1), /* fcmp */
|
COSTS_N_INSNS (1), /* fmov, fmovr */
|
COSTS_N_INSNS (1), /* fmov, fmovr */
|
COSTS_N_INSNS (1), /* fmul */
|
COSTS_N_INSNS (1), /* fmul */
|
COSTS_N_INSNS (1), /* fdivs */
|
COSTS_N_INSNS (1), /* fdivs */
|
COSTS_N_INSNS (1), /* fdivd */
|
COSTS_N_INSNS (1), /* fdivd */
|
COSTS_N_INSNS (1), /* fsqrts */
|
COSTS_N_INSNS (1), /* fsqrts */
|
COSTS_N_INSNS (1), /* fsqrtd */
|
COSTS_N_INSNS (1), /* fsqrtd */
|
COSTS_N_INSNS (5), /* imul */
|
COSTS_N_INSNS (5), /* imul */
|
COSTS_N_INSNS (5), /* imulX */
|
COSTS_N_INSNS (5), /* imulX */
|
0, /* imul bit factor */
|
0, /* imul bit factor */
|
COSTS_N_INSNS (5), /* idiv */
|
COSTS_N_INSNS (5), /* idiv */
|
COSTS_N_INSNS (5), /* idivX */
|
COSTS_N_INSNS (5), /* idivX */
|
COSTS_N_INSNS (1), /* movcc/movr */
|
COSTS_N_INSNS (1), /* movcc/movr */
|
0, /* shift penalty */
|
0, /* shift penalty */
|
};
|
};
|
|
|
static const
|
static const
|
struct processor_costs ultrasparc_costs = {
|
struct processor_costs ultrasparc_costs = {
|
COSTS_N_INSNS (2), /* int load */
|
COSTS_N_INSNS (2), /* int load */
|
COSTS_N_INSNS (3), /* int signed load */
|
COSTS_N_INSNS (3), /* int signed load */
|
COSTS_N_INSNS (2), /* int zeroed load */
|
COSTS_N_INSNS (2), /* int zeroed load */
|
COSTS_N_INSNS (2), /* float load */
|
COSTS_N_INSNS (2), /* float load */
|
COSTS_N_INSNS (1), /* fmov, fneg, fabs */
|
COSTS_N_INSNS (1), /* fmov, fneg, fabs */
|
COSTS_N_INSNS (4), /* fadd, fsub */
|
COSTS_N_INSNS (4), /* fadd, fsub */
|
COSTS_N_INSNS (1), /* fcmp */
|
COSTS_N_INSNS (1), /* fcmp */
|
COSTS_N_INSNS (2), /* fmov, fmovr */
|
COSTS_N_INSNS (2), /* fmov, fmovr */
|
COSTS_N_INSNS (4), /* fmul */
|
COSTS_N_INSNS (4), /* fmul */
|
COSTS_N_INSNS (13), /* fdivs */
|
COSTS_N_INSNS (13), /* fdivs */
|
COSTS_N_INSNS (23), /* fdivd */
|
COSTS_N_INSNS (23), /* fdivd */
|
COSTS_N_INSNS (13), /* fsqrts */
|
COSTS_N_INSNS (13), /* fsqrts */
|
COSTS_N_INSNS (23), /* fsqrtd */
|
COSTS_N_INSNS (23), /* fsqrtd */
|
COSTS_N_INSNS (4), /* imul */
|
COSTS_N_INSNS (4), /* imul */
|
COSTS_N_INSNS (4), /* imulX */
|
COSTS_N_INSNS (4), /* imulX */
|
2, /* imul bit factor */
|
2, /* imul bit factor */
|
COSTS_N_INSNS (37), /* idiv */
|
COSTS_N_INSNS (37), /* idiv */
|
COSTS_N_INSNS (68), /* idivX */
|
COSTS_N_INSNS (68), /* idivX */
|
COSTS_N_INSNS (2), /* movcc/movr */
|
COSTS_N_INSNS (2), /* movcc/movr */
|
2, /* shift penalty */
|
2, /* shift penalty */
|
};
|
};
|
|
|
static const
|
static const
|
struct processor_costs ultrasparc3_costs = {
|
struct processor_costs ultrasparc3_costs = {
|
COSTS_N_INSNS (2), /* int load */
|
COSTS_N_INSNS (2), /* int load */
|
COSTS_N_INSNS (3), /* int signed load */
|
COSTS_N_INSNS (3), /* int signed load */
|
COSTS_N_INSNS (3), /* int zeroed load */
|
COSTS_N_INSNS (3), /* int zeroed load */
|
COSTS_N_INSNS (2), /* float load */
|
COSTS_N_INSNS (2), /* float load */
|
COSTS_N_INSNS (3), /* fmov, fneg, fabs */
|
COSTS_N_INSNS (3), /* fmov, fneg, fabs */
|
COSTS_N_INSNS (4), /* fadd, fsub */
|
COSTS_N_INSNS (4), /* fadd, fsub */
|
COSTS_N_INSNS (5), /* fcmp */
|
COSTS_N_INSNS (5), /* fcmp */
|
COSTS_N_INSNS (3), /* fmov, fmovr */
|
COSTS_N_INSNS (3), /* fmov, fmovr */
|
COSTS_N_INSNS (4), /* fmul */
|
COSTS_N_INSNS (4), /* fmul */
|
COSTS_N_INSNS (17), /* fdivs */
|
COSTS_N_INSNS (17), /* fdivs */
|
COSTS_N_INSNS (20), /* fdivd */
|
COSTS_N_INSNS (20), /* fdivd */
|
COSTS_N_INSNS (20), /* fsqrts */
|
COSTS_N_INSNS (20), /* fsqrts */
|
COSTS_N_INSNS (29), /* fsqrtd */
|
COSTS_N_INSNS (29), /* fsqrtd */
|
COSTS_N_INSNS (6), /* imul */
|
COSTS_N_INSNS (6), /* imul */
|
COSTS_N_INSNS (6), /* imulX */
|
COSTS_N_INSNS (6), /* imulX */
|
0, /* imul bit factor */
|
0, /* imul bit factor */
|
COSTS_N_INSNS (40), /* idiv */
|
COSTS_N_INSNS (40), /* idiv */
|
COSTS_N_INSNS (71), /* idivX */
|
COSTS_N_INSNS (71), /* idivX */
|
COSTS_N_INSNS (2), /* movcc/movr */
|
COSTS_N_INSNS (2), /* movcc/movr */
|
0, /* shift penalty */
|
0, /* shift penalty */
|
};
|
};
|
|
|
static const
|
static const
|
struct processor_costs niagara_costs = {
|
struct processor_costs niagara_costs = {
|
COSTS_N_INSNS (3), /* int load */
|
COSTS_N_INSNS (3), /* int load */
|
COSTS_N_INSNS (3), /* int signed load */
|
COSTS_N_INSNS (3), /* int signed load */
|
COSTS_N_INSNS (3), /* int zeroed load */
|
COSTS_N_INSNS (3), /* int zeroed load */
|
COSTS_N_INSNS (9), /* float load */
|
COSTS_N_INSNS (9), /* float load */
|
COSTS_N_INSNS (8), /* fmov, fneg, fabs */
|
COSTS_N_INSNS (8), /* fmov, fneg, fabs */
|
COSTS_N_INSNS (8), /* fadd, fsub */
|
COSTS_N_INSNS (8), /* fadd, fsub */
|
COSTS_N_INSNS (26), /* fcmp */
|
COSTS_N_INSNS (26), /* fcmp */
|
COSTS_N_INSNS (8), /* fmov, fmovr */
|
COSTS_N_INSNS (8), /* fmov, fmovr */
|
COSTS_N_INSNS (29), /* fmul */
|
COSTS_N_INSNS (29), /* fmul */
|
COSTS_N_INSNS (54), /* fdivs */
|
COSTS_N_INSNS (54), /* fdivs */
|
COSTS_N_INSNS (83), /* fdivd */
|
COSTS_N_INSNS (83), /* fdivd */
|
COSTS_N_INSNS (100), /* fsqrts - not implemented in hardware */
|
COSTS_N_INSNS (100), /* fsqrts - not implemented in hardware */
|
COSTS_N_INSNS (100), /* fsqrtd - not implemented in hardware */
|
COSTS_N_INSNS (100), /* fsqrtd - not implemented in hardware */
|
COSTS_N_INSNS (11), /* imul */
|
COSTS_N_INSNS (11), /* imul */
|
COSTS_N_INSNS (11), /* imulX */
|
COSTS_N_INSNS (11), /* imulX */
|
0, /* imul bit factor */
|
0, /* imul bit factor */
|
COSTS_N_INSNS (72), /* idiv */
|
COSTS_N_INSNS (72), /* idiv */
|
COSTS_N_INSNS (72), /* idivX */
|
COSTS_N_INSNS (72), /* idivX */
|
COSTS_N_INSNS (1), /* movcc/movr */
|
COSTS_N_INSNS (1), /* movcc/movr */
|
0, /* shift penalty */
|
0, /* shift penalty */
|
};
|
};
|
|
|
static const
|
static const
|
struct processor_costs niagara2_costs = {
|
struct processor_costs niagara2_costs = {
|
COSTS_N_INSNS (3), /* int load */
|
COSTS_N_INSNS (3), /* int load */
|
COSTS_N_INSNS (3), /* int signed load */
|
COSTS_N_INSNS (3), /* int signed load */
|
COSTS_N_INSNS (3), /* int zeroed load */
|
COSTS_N_INSNS (3), /* int zeroed load */
|
COSTS_N_INSNS (3), /* float load */
|
COSTS_N_INSNS (3), /* float load */
|
COSTS_N_INSNS (6), /* fmov, fneg, fabs */
|
COSTS_N_INSNS (6), /* fmov, fneg, fabs */
|
COSTS_N_INSNS (6), /* fadd, fsub */
|
COSTS_N_INSNS (6), /* fadd, fsub */
|
COSTS_N_INSNS (6), /* fcmp */
|
COSTS_N_INSNS (6), /* fcmp */
|
COSTS_N_INSNS (6), /* fmov, fmovr */
|
COSTS_N_INSNS (6), /* fmov, fmovr */
|
COSTS_N_INSNS (6), /* fmul */
|
COSTS_N_INSNS (6), /* fmul */
|
COSTS_N_INSNS (19), /* fdivs */
|
COSTS_N_INSNS (19), /* fdivs */
|
COSTS_N_INSNS (33), /* fdivd */
|
COSTS_N_INSNS (33), /* fdivd */
|
COSTS_N_INSNS (19), /* fsqrts */
|
COSTS_N_INSNS (19), /* fsqrts */
|
COSTS_N_INSNS (33), /* fsqrtd */
|
COSTS_N_INSNS (33), /* fsqrtd */
|
COSTS_N_INSNS (5), /* imul */
|
COSTS_N_INSNS (5), /* imul */
|
COSTS_N_INSNS (5), /* imulX */
|
COSTS_N_INSNS (5), /* imulX */
|
0, /* imul bit factor */
|
0, /* imul bit factor */
|
COSTS_N_INSNS (31), /* idiv, average of 12 - 41 cycle range */
|
COSTS_N_INSNS (31), /* idiv, average of 12 - 41 cycle range */
|
COSTS_N_INSNS (31), /* idivX, average of 12 - 41 cycle range */
|
COSTS_N_INSNS (31), /* idivX, average of 12 - 41 cycle range */
|
COSTS_N_INSNS (1), /* movcc/movr */
|
COSTS_N_INSNS (1), /* movcc/movr */
|
0, /* shift penalty */
|
0, /* shift penalty */
|
};
|
};
|
|
|
const struct processor_costs *sparc_costs = &cypress_costs;
|
const struct processor_costs *sparc_costs = &cypress_costs;
|
|
|
#ifdef HAVE_AS_RELAX_OPTION
|
#ifdef HAVE_AS_RELAX_OPTION
|
/* If 'as' and 'ld' are relaxing tail call insns into branch always, use
|
/* If 'as' and 'ld' are relaxing tail call insns into branch always, use
|
"or %o7,%g0,X; call Y; or X,%g0,%o7" always, so that it can be optimized.
|
"or %o7,%g0,X; call Y; or X,%g0,%o7" always, so that it can be optimized.
|
With sethi/jmp, neither 'as' nor 'ld' has an easy way how to find out if
|
With sethi/jmp, neither 'as' nor 'ld' has an easy way how to find out if
|
somebody does not branch between the sethi and jmp. */
|
somebody does not branch between the sethi and jmp. */
|
#define LEAF_SIBCALL_SLOT_RESERVED_P 1
|
#define LEAF_SIBCALL_SLOT_RESERVED_P 1
|
#else
|
#else
|
#define LEAF_SIBCALL_SLOT_RESERVED_P \
|
#define LEAF_SIBCALL_SLOT_RESERVED_P \
|
((TARGET_ARCH64 && !TARGET_CM_MEDLOW) || flag_pic)
|
((TARGET_ARCH64 && !TARGET_CM_MEDLOW) || flag_pic)
|
#endif
|
#endif
|
|
|
/* Global variables for machine-dependent things. */
|
/* Global variables for machine-dependent things. */
|
|
|
/* Size of frame. Need to know this to emit return insns from leaf procedures.
|
/* Size of frame. Need to know this to emit return insns from leaf procedures.
|
ACTUAL_FSIZE is set by sparc_compute_frame_size() which is called during the
|
ACTUAL_FSIZE is set by sparc_compute_frame_size() which is called during the
|
reload pass. This is important as the value is later used for scheduling
|
reload pass. This is important as the value is later used for scheduling
|
(to see what can go in a delay slot).
|
(to see what can go in a delay slot).
|
APPARENT_FSIZE is the size of the stack less the register save area and less
|
APPARENT_FSIZE is the size of the stack less the register save area and less
|
the outgoing argument area. It is used when saving call preserved regs. */
|
the outgoing argument area. It is used when saving call preserved regs. */
|
static HOST_WIDE_INT apparent_fsize;
|
static HOST_WIDE_INT apparent_fsize;
|
static HOST_WIDE_INT actual_fsize;
|
static HOST_WIDE_INT actual_fsize;
|
|
|
/* Number of live general or floating point registers needed to be
|
/* Number of live general or floating point registers needed to be
|
saved (as 4-byte quantities). */
|
saved (as 4-byte quantities). */
|
static int num_gfregs;
|
static int num_gfregs;
|
|
|
/* The alias set for prologue/epilogue register save/restore. */
|
/* The alias set for prologue/epilogue register save/restore. */
|
static GTY(()) alias_set_type sparc_sr_alias_set;
|
static GTY(()) alias_set_type sparc_sr_alias_set;
|
|
|
/* The alias set for the structure return value. */
|
/* The alias set for the structure return value. */
|
static GTY(()) alias_set_type struct_value_alias_set;
|
static GTY(()) alias_set_type struct_value_alias_set;
|
|
|
/* Vector to say how input registers are mapped to output registers.
|
/* Vector to say how input registers are mapped to output registers.
|
HARD_FRAME_POINTER_REGNUM cannot be remapped by this function to
|
HARD_FRAME_POINTER_REGNUM cannot be remapped by this function to
|
eliminate it. You must use -fomit-frame-pointer to get that. */
|
eliminate it. You must use -fomit-frame-pointer to get that. */
|
char leaf_reg_remap[] =
|
char leaf_reg_remap[] =
|
{ 0, 1, 2, 3, 4, 5, 6, 7,
|
{ 0, 1, 2, 3, 4, 5, 6, 7,
|
-1, -1, -1, -1, -1, -1, 14, -1,
|
-1, -1, -1, -1, -1, -1, 14, -1,
|
-1, -1, -1, -1, -1, -1, -1, -1,
|
-1, -1, -1, -1, -1, -1, -1, -1,
|
8, 9, 10, 11, 12, 13, -1, 15,
|
8, 9, 10, 11, 12, 13, -1, 15,
|
|
|
32, 33, 34, 35, 36, 37, 38, 39,
|
32, 33, 34, 35, 36, 37, 38, 39,
|
40, 41, 42, 43, 44, 45, 46, 47,
|
40, 41, 42, 43, 44, 45, 46, 47,
|
48, 49, 50, 51, 52, 53, 54, 55,
|
48, 49, 50, 51, 52, 53, 54, 55,
|
56, 57, 58, 59, 60, 61, 62, 63,
|
56, 57, 58, 59, 60, 61, 62, 63,
|
64, 65, 66, 67, 68, 69, 70, 71,
|
64, 65, 66, 67, 68, 69, 70, 71,
|
72, 73, 74, 75, 76, 77, 78, 79,
|
72, 73, 74, 75, 76, 77, 78, 79,
|
80, 81, 82, 83, 84, 85, 86, 87,
|
80, 81, 82, 83, 84, 85, 86, 87,
|
88, 89, 90, 91, 92, 93, 94, 95,
|
88, 89, 90, 91, 92, 93, 94, 95,
|
96, 97, 98, 99, 100};
|
96, 97, 98, 99, 100};
|
|
|
/* Vector, indexed by hard register number, which contains 1
|
/* Vector, indexed by hard register number, which contains 1
|
for a register that is allowable in a candidate for leaf
|
for a register that is allowable in a candidate for leaf
|
function treatment. */
|
function treatment. */
|
char sparc_leaf_regs[] =
|
char sparc_leaf_regs[] =
|
{ 1, 1, 1, 1, 1, 1, 1, 1,
|
{ 1, 1, 1, 1, 1, 1, 1, 1,
|
0, 0, 0, 0, 0, 0, 1, 0,
|
0, 0, 0, 0, 0, 0, 1, 0,
|
0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0,
|
1, 1, 1, 1, 1, 1, 0, 1,
|
1, 1, 1, 1, 1, 1, 0, 1,
|
1, 1, 1, 1, 1, 1, 1, 1,
|
1, 1, 1, 1, 1, 1, 1, 1,
|
1, 1, 1, 1, 1, 1, 1, 1,
|
1, 1, 1, 1, 1, 1, 1, 1,
|
1, 1, 1, 1, 1, 1, 1, 1,
|
1, 1, 1, 1, 1, 1, 1, 1,
|
1, 1, 1, 1, 1, 1, 1, 1,
|
1, 1, 1, 1, 1, 1, 1, 1,
|
1, 1, 1, 1, 1, 1, 1, 1,
|
1, 1, 1, 1, 1, 1, 1, 1,
|
1, 1, 1, 1, 1, 1, 1, 1,
|
1, 1, 1, 1, 1, 1, 1, 1,
|
1, 1, 1, 1, 1, 1, 1, 1,
|
1, 1, 1, 1, 1, 1, 1, 1,
|
1, 1, 1, 1, 1, 1, 1, 1,
|
1, 1, 1, 1, 1, 1, 1, 1,
|
1, 1, 1, 1, 1};
|
1, 1, 1, 1, 1};
|
|
|
struct GTY(()) machine_function
|
struct GTY(()) machine_function
|
{
|
{
|
/* Some local-dynamic TLS symbol name. */
|
/* Some local-dynamic TLS symbol name. */
|
const char *some_ld_name;
|
const char *some_ld_name;
|
|
|
/* True if the current function is leaf and uses only leaf regs,
|
/* True if the current function is leaf and uses only leaf regs,
|
so that the SPARC leaf function optimization can be applied.
|
so that the SPARC leaf function optimization can be applied.
|
Private version of current_function_uses_only_leaf_regs, see
|
Private version of current_function_uses_only_leaf_regs, see
|
sparc_expand_prologue for the rationale. */
|
sparc_expand_prologue for the rationale. */
|
int leaf_function_p;
|
int leaf_function_p;
|
|
|
/* True if the data calculated by sparc_expand_prologue are valid. */
|
/* True if the data calculated by sparc_expand_prologue are valid. */
|
bool prologue_data_valid_p;
|
bool prologue_data_valid_p;
|
};
|
};
|
|
|
#define sparc_leaf_function_p cfun->machine->leaf_function_p
|
#define sparc_leaf_function_p cfun->machine->leaf_function_p
|
#define sparc_prologue_data_valid_p cfun->machine->prologue_data_valid_p
|
#define sparc_prologue_data_valid_p cfun->machine->prologue_data_valid_p
|
|
|
/* Register we pretend to think the frame pointer is allocated to.
|
/* Register we pretend to think the frame pointer is allocated to.
|
Normally, this is %fp, but if we are in a leaf procedure, this
|
Normally, this is %fp, but if we are in a leaf procedure, this
|
is %sp+"something". We record "something" separately as it may
|
is %sp+"something". We record "something" separately as it may
|
be too big for reg+constant addressing. */
|
be too big for reg+constant addressing. */
|
static rtx frame_base_reg;
|
static rtx frame_base_reg;
|
static HOST_WIDE_INT frame_base_offset;
|
static HOST_WIDE_INT frame_base_offset;
|
|
|
/* 1 if the next opcode is to be specially indented. */
|
/* 1 if the next opcode is to be specially indented. */
|
int sparc_indent_opcode = 0;
|
int sparc_indent_opcode = 0;
|
|
|
static bool sparc_handle_option (size_t, const char *, int);
|
static bool sparc_handle_option (size_t, const char *, int);
|
static void sparc_init_modes (void);
|
static void sparc_init_modes (void);
|
static void scan_record_type (tree, int *, int *, int *);
|
static void scan_record_type (tree, int *, int *, int *);
|
static int function_arg_slotno (const CUMULATIVE_ARGS *, enum machine_mode,
|
static int function_arg_slotno (const CUMULATIVE_ARGS *, enum machine_mode,
|
tree, int, int, int *, int *);
|
tree, int, int, int *, int *);
|
|
|
static int supersparc_adjust_cost (rtx, rtx, rtx, int);
|
static int supersparc_adjust_cost (rtx, rtx, rtx, int);
|
static int hypersparc_adjust_cost (rtx, rtx, rtx, int);
|
static int hypersparc_adjust_cost (rtx, rtx, rtx, int);
|
|
|
static void sparc_output_addr_vec (rtx);
|
static void sparc_output_addr_vec (rtx);
|
static void sparc_output_addr_diff_vec (rtx);
|
static void sparc_output_addr_diff_vec (rtx);
|
static void sparc_output_deferred_case_vectors (void);
|
static void sparc_output_deferred_case_vectors (void);
|
static bool sparc_legitimate_address_p (enum machine_mode, rtx, bool);
|
static bool sparc_legitimate_address_p (enum machine_mode, rtx, bool);
|
static rtx sparc_builtin_saveregs (void);
|
static rtx sparc_builtin_saveregs (void);
|
static int epilogue_renumber (rtx *, int);
|
static int epilogue_renumber (rtx *, int);
|
static bool sparc_assemble_integer (rtx, unsigned int, int);
|
static bool sparc_assemble_integer (rtx, unsigned int, int);
|
static int set_extends (rtx);
|
static int set_extends (rtx);
|
static void load_pic_register (void);
|
static void load_pic_register (void);
|
static int save_or_restore_regs (int, int, rtx, int, int);
|
static int save_or_restore_regs (int, int, rtx, int, int);
|
static void emit_save_or_restore_regs (int);
|
static void emit_save_or_restore_regs (int);
|
static void sparc_asm_function_prologue (FILE *, HOST_WIDE_INT);
|
static void sparc_asm_function_prologue (FILE *, HOST_WIDE_INT);
|
static void sparc_asm_function_epilogue (FILE *, HOST_WIDE_INT);
|
static void sparc_asm_function_epilogue (FILE *, HOST_WIDE_INT);
|
static void sparc_solaris_elf_asm_named_section (const char *, unsigned int,
|
static void sparc_solaris_elf_asm_named_section (const char *, unsigned int,
|
tree) ATTRIBUTE_UNUSED;
|
tree) ATTRIBUTE_UNUSED;
|
static int sparc_adjust_cost (rtx, rtx, rtx, int);
|
static int sparc_adjust_cost (rtx, rtx, rtx, int);
|
static int sparc_issue_rate (void);
|
static int sparc_issue_rate (void);
|
static void sparc_sched_init (FILE *, int, int);
|
static void sparc_sched_init (FILE *, int, int);
|
static int sparc_use_sched_lookahead (void);
|
static int sparc_use_sched_lookahead (void);
|
|
|
static void emit_soft_tfmode_libcall (const char *, int, rtx *);
|
static void emit_soft_tfmode_libcall (const char *, int, rtx *);
|
static void emit_soft_tfmode_binop (enum rtx_code, rtx *);
|
static void emit_soft_tfmode_binop (enum rtx_code, rtx *);
|
static void emit_soft_tfmode_unop (enum rtx_code, rtx *);
|
static void emit_soft_tfmode_unop (enum rtx_code, rtx *);
|
static void emit_soft_tfmode_cvt (enum rtx_code, rtx *);
|
static void emit_soft_tfmode_cvt (enum rtx_code, rtx *);
|
static void emit_hard_tfmode_operation (enum rtx_code, rtx *);
|
static void emit_hard_tfmode_operation (enum rtx_code, rtx *);
|
|
|
static bool sparc_function_ok_for_sibcall (tree, tree);
|
static bool sparc_function_ok_for_sibcall (tree, tree);
|
static void sparc_init_libfuncs (void);
|
static void sparc_init_libfuncs (void);
|
static void sparc_init_builtins (void);
|
static void sparc_init_builtins (void);
|
static void sparc_vis_init_builtins (void);
|
static void sparc_vis_init_builtins (void);
|
static rtx sparc_expand_builtin (tree, rtx, rtx, enum machine_mode, int);
|
static rtx sparc_expand_builtin (tree, rtx, rtx, enum machine_mode, int);
|
static tree sparc_fold_builtin (tree, tree, bool);
|
static tree sparc_fold_builtin (tree, tree, bool);
|
static int sparc_vis_mul8x16 (int, int);
|
static int sparc_vis_mul8x16 (int, int);
|
static tree sparc_handle_vis_mul8x16 (int, tree, tree, tree);
|
static tree sparc_handle_vis_mul8x16 (int, tree, tree, tree);
|
static void sparc_output_mi_thunk (FILE *, tree, HOST_WIDE_INT,
|
static void sparc_output_mi_thunk (FILE *, tree, HOST_WIDE_INT,
|
HOST_WIDE_INT, tree);
|
HOST_WIDE_INT, tree);
|
static bool sparc_can_output_mi_thunk (const_tree, HOST_WIDE_INT,
|
static bool sparc_can_output_mi_thunk (const_tree, HOST_WIDE_INT,
|
HOST_WIDE_INT, const_tree);
|
HOST_WIDE_INT, const_tree);
|
static struct machine_function * sparc_init_machine_status (void);
|
static struct machine_function * sparc_init_machine_status (void);
|
static bool sparc_cannot_force_const_mem (rtx);
|
static bool sparc_cannot_force_const_mem (rtx);
|
static rtx sparc_tls_get_addr (void);
|
static rtx sparc_tls_get_addr (void);
|
static rtx sparc_tls_got (void);
|
static rtx sparc_tls_got (void);
|
static const char *get_some_local_dynamic_name (void);
|
static const char *get_some_local_dynamic_name (void);
|
static int get_some_local_dynamic_name_1 (rtx *, void *);
|
static int get_some_local_dynamic_name_1 (rtx *, void *);
|
static bool sparc_rtx_costs (rtx, int, int, int *, bool);
|
static bool sparc_rtx_costs (rtx, int, int, int *, bool);
|
static bool sparc_promote_prototypes (const_tree);
|
static bool sparc_promote_prototypes (const_tree);
|
static rtx sparc_struct_value_rtx (tree, int);
|
static rtx sparc_struct_value_rtx (tree, int);
|
static enum machine_mode sparc_promote_function_mode (const_tree, enum machine_mode,
|
static enum machine_mode sparc_promote_function_mode (const_tree, enum machine_mode,
|
int *, const_tree, int);
|
int *, const_tree, int);
|
static bool sparc_return_in_memory (const_tree, const_tree);
|
static bool sparc_return_in_memory (const_tree, const_tree);
|
static bool sparc_strict_argument_naming (CUMULATIVE_ARGS *);
|
static bool sparc_strict_argument_naming (CUMULATIVE_ARGS *);
|
static void sparc_va_start (tree, rtx);
|
static void sparc_va_start (tree, rtx);
|
static tree sparc_gimplify_va_arg (tree, tree, gimple_seq *, gimple_seq *);
|
static tree sparc_gimplify_va_arg (tree, tree, gimple_seq *, gimple_seq *);
|
static bool sparc_vector_mode_supported_p (enum machine_mode);
|
static bool sparc_vector_mode_supported_p (enum machine_mode);
|
static bool sparc_tls_referenced_p (rtx);
|
static bool sparc_tls_referenced_p (rtx);
|
static rtx legitimize_tls_address (rtx);
|
static rtx legitimize_tls_address (rtx);
|
static rtx legitimize_pic_address (rtx, rtx);
|
static rtx legitimize_pic_address (rtx, rtx);
|
static rtx sparc_legitimize_address (rtx, rtx, enum machine_mode);
|
static rtx sparc_legitimize_address (rtx, rtx, enum machine_mode);
|
static bool sparc_pass_by_reference (CUMULATIVE_ARGS *,
|
static bool sparc_pass_by_reference (CUMULATIVE_ARGS *,
|
enum machine_mode, const_tree, bool);
|
enum machine_mode, const_tree, bool);
|
static int sparc_arg_partial_bytes (CUMULATIVE_ARGS *,
|
static int sparc_arg_partial_bytes (CUMULATIVE_ARGS *,
|
enum machine_mode, tree, bool);
|
enum machine_mode, tree, bool);
|
static void sparc_dwarf_handle_frame_unspec (const char *, rtx, int);
|
static void sparc_dwarf_handle_frame_unspec (const char *, rtx, int);
|
static void sparc_output_dwarf_dtprel (FILE *, int, rtx) ATTRIBUTE_UNUSED;
|
static void sparc_output_dwarf_dtprel (FILE *, int, rtx) ATTRIBUTE_UNUSED;
|
static void sparc_file_end (void);
|
static void sparc_file_end (void);
|
static bool sparc_frame_pointer_required (void);
|
static bool sparc_frame_pointer_required (void);
|
static bool sparc_can_eliminate (const int, const int);
|
static bool sparc_can_eliminate (const int, const int);
|
#ifdef TARGET_ALTERNATE_LONG_DOUBLE_MANGLING
|
#ifdef TARGET_ALTERNATE_LONG_DOUBLE_MANGLING
|
static const char *sparc_mangle_type (const_tree);
|
static const char *sparc_mangle_type (const_tree);
|
#endif
|
#endif
|
static void sparc_trampoline_init (rtx, tree, rtx);
|
static void sparc_trampoline_init (rtx, tree, rtx);
|
|
|
#ifdef SUBTARGET_ATTRIBUTE_TABLE
|
#ifdef SUBTARGET_ATTRIBUTE_TABLE
|
/* Table of valid machine attributes. */
|
/* Table of valid machine attributes. */
|
static const struct attribute_spec sparc_attribute_table[] =
|
static const struct attribute_spec sparc_attribute_table[] =
|
{
|
{
|
/* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
|
/* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
|
SUBTARGET_ATTRIBUTE_TABLE,
|
SUBTARGET_ATTRIBUTE_TABLE,
|
{ NULL, 0, 0, false, false, false, NULL }
|
{ NULL, 0, 0, false, false, false, NULL }
|
};
|
};
|
#endif
|
#endif
|
|
|
/* Option handling. */
|
/* Option handling. */
|
|
|
/* Parsed value. */
|
/* Parsed value. */
|
enum cmodel sparc_cmodel;
|
enum cmodel sparc_cmodel;
|
|
|
char sparc_hard_reg_printed[8];
|
char sparc_hard_reg_printed[8];
|
|
|
struct sparc_cpu_select sparc_select[] =
|
struct sparc_cpu_select sparc_select[] =
|
{
|
{
|
/* switch name, tune arch */
|
/* switch name, tune arch */
|
{ (char *)0, "default", 1, 1 },
|
{ (char *)0, "default", 1, 1 },
|
{ (char *)0, "-mcpu=", 1, 1 },
|
{ (char *)0, "-mcpu=", 1, 1 },
|
{ (char *)0, "-mtune=", 1, 0 },
|
{ (char *)0, "-mtune=", 1, 0 },
|
{ 0, 0, 0, 0 }
|
{ 0, 0, 0, 0 }
|
};
|
};
|
|
|
/* CPU type. This is set from TARGET_CPU_DEFAULT and -m{cpu,tune}=xxx. */
|
/* CPU type. This is set from TARGET_CPU_DEFAULT and -m{cpu,tune}=xxx. */
|
enum processor_type sparc_cpu;
|
enum processor_type sparc_cpu;
|
|
|
/* Whetheran FPU option was specified. */
|
/* Whetheran FPU option was specified. */
|
static bool fpu_option_set = false;
|
static bool fpu_option_set = false;
|
|
|
/* Initialize the GCC target structure. */
|
/* Initialize the GCC target structure. */
|
|
|
/* The default is to use .half rather than .short for aligned HI objects. */
|
/* The default is to use .half rather than .short for aligned HI objects. */
|
#undef TARGET_ASM_ALIGNED_HI_OP
|
#undef TARGET_ASM_ALIGNED_HI_OP
|
#define TARGET_ASM_ALIGNED_HI_OP "\t.half\t"
|
#define TARGET_ASM_ALIGNED_HI_OP "\t.half\t"
|
|
|
#undef TARGET_ASM_UNALIGNED_HI_OP
|
#undef TARGET_ASM_UNALIGNED_HI_OP
|
#define TARGET_ASM_UNALIGNED_HI_OP "\t.uahalf\t"
|
#define TARGET_ASM_UNALIGNED_HI_OP "\t.uahalf\t"
|
#undef TARGET_ASM_UNALIGNED_SI_OP
|
#undef TARGET_ASM_UNALIGNED_SI_OP
|
#define TARGET_ASM_UNALIGNED_SI_OP "\t.uaword\t"
|
#define TARGET_ASM_UNALIGNED_SI_OP "\t.uaword\t"
|
#undef TARGET_ASM_UNALIGNED_DI_OP
|
#undef TARGET_ASM_UNALIGNED_DI_OP
|
#define TARGET_ASM_UNALIGNED_DI_OP "\t.uaxword\t"
|
#define TARGET_ASM_UNALIGNED_DI_OP "\t.uaxword\t"
|
|
|
/* The target hook has to handle DI-mode values. */
|
/* The target hook has to handle DI-mode values. */
|
#undef TARGET_ASM_INTEGER
|
#undef TARGET_ASM_INTEGER
|
#define TARGET_ASM_INTEGER sparc_assemble_integer
|
#define TARGET_ASM_INTEGER sparc_assemble_integer
|
|
|
#undef TARGET_ASM_FUNCTION_PROLOGUE
|
#undef TARGET_ASM_FUNCTION_PROLOGUE
|
#define TARGET_ASM_FUNCTION_PROLOGUE sparc_asm_function_prologue
|
#define TARGET_ASM_FUNCTION_PROLOGUE sparc_asm_function_prologue
|
#undef TARGET_ASM_FUNCTION_EPILOGUE
|
#undef TARGET_ASM_FUNCTION_EPILOGUE
|
#define TARGET_ASM_FUNCTION_EPILOGUE sparc_asm_function_epilogue
|
#define TARGET_ASM_FUNCTION_EPILOGUE sparc_asm_function_epilogue
|
|
|
#undef TARGET_SCHED_ADJUST_COST
|
#undef TARGET_SCHED_ADJUST_COST
|
#define TARGET_SCHED_ADJUST_COST sparc_adjust_cost
|
#define TARGET_SCHED_ADJUST_COST sparc_adjust_cost
|
#undef TARGET_SCHED_ISSUE_RATE
|
#undef TARGET_SCHED_ISSUE_RATE
|
#define TARGET_SCHED_ISSUE_RATE sparc_issue_rate
|
#define TARGET_SCHED_ISSUE_RATE sparc_issue_rate
|
#undef TARGET_SCHED_INIT
|
#undef TARGET_SCHED_INIT
|
#define TARGET_SCHED_INIT sparc_sched_init
|
#define TARGET_SCHED_INIT sparc_sched_init
|
#undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD
|
#undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD
|
#define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD sparc_use_sched_lookahead
|
#define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD sparc_use_sched_lookahead
|
|
|
#undef TARGET_FUNCTION_OK_FOR_SIBCALL
|
#undef TARGET_FUNCTION_OK_FOR_SIBCALL
|
#define TARGET_FUNCTION_OK_FOR_SIBCALL sparc_function_ok_for_sibcall
|
#define TARGET_FUNCTION_OK_FOR_SIBCALL sparc_function_ok_for_sibcall
|
|
|
#undef TARGET_INIT_LIBFUNCS
|
#undef TARGET_INIT_LIBFUNCS
|
#define TARGET_INIT_LIBFUNCS sparc_init_libfuncs
|
#define TARGET_INIT_LIBFUNCS sparc_init_libfuncs
|
#undef TARGET_INIT_BUILTINS
|
#undef TARGET_INIT_BUILTINS
|
#define TARGET_INIT_BUILTINS sparc_init_builtins
|
#define TARGET_INIT_BUILTINS sparc_init_builtins
|
|
|
#undef TARGET_LEGITIMIZE_ADDRESS
|
#undef TARGET_LEGITIMIZE_ADDRESS
|
#define TARGET_LEGITIMIZE_ADDRESS sparc_legitimize_address
|
#define TARGET_LEGITIMIZE_ADDRESS sparc_legitimize_address
|
|
|
#undef TARGET_EXPAND_BUILTIN
|
#undef TARGET_EXPAND_BUILTIN
|
#define TARGET_EXPAND_BUILTIN sparc_expand_builtin
|
#define TARGET_EXPAND_BUILTIN sparc_expand_builtin
|
#undef TARGET_FOLD_BUILTIN
|
#undef TARGET_FOLD_BUILTIN
|
#define TARGET_FOLD_BUILTIN sparc_fold_builtin
|
#define TARGET_FOLD_BUILTIN sparc_fold_builtin
|
|
|
#if TARGET_TLS
|
#if TARGET_TLS
|
#undef TARGET_HAVE_TLS
|
#undef TARGET_HAVE_TLS
|
#define TARGET_HAVE_TLS true
|
#define TARGET_HAVE_TLS true
|
#endif
|
#endif
|
|
|
#undef TARGET_CANNOT_FORCE_CONST_MEM
|
#undef TARGET_CANNOT_FORCE_CONST_MEM
|
#define TARGET_CANNOT_FORCE_CONST_MEM sparc_cannot_force_const_mem
|
#define TARGET_CANNOT_FORCE_CONST_MEM sparc_cannot_force_const_mem
|
|
|
#undef TARGET_ASM_OUTPUT_MI_THUNK
|
#undef TARGET_ASM_OUTPUT_MI_THUNK
|
#define TARGET_ASM_OUTPUT_MI_THUNK sparc_output_mi_thunk
|
#define TARGET_ASM_OUTPUT_MI_THUNK sparc_output_mi_thunk
|
#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
|
#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
|
#define TARGET_ASM_CAN_OUTPUT_MI_THUNK sparc_can_output_mi_thunk
|
#define TARGET_ASM_CAN_OUTPUT_MI_THUNK sparc_can_output_mi_thunk
|
|
|
#undef TARGET_RTX_COSTS
|
#undef TARGET_RTX_COSTS
|
#define TARGET_RTX_COSTS sparc_rtx_costs
|
#define TARGET_RTX_COSTS sparc_rtx_costs
|
#undef TARGET_ADDRESS_COST
|
#undef TARGET_ADDRESS_COST
|
#define TARGET_ADDRESS_COST hook_int_rtx_bool_0
|
#define TARGET_ADDRESS_COST hook_int_rtx_bool_0
|
|
|
#undef TARGET_PROMOTE_FUNCTION_MODE
|
#undef TARGET_PROMOTE_FUNCTION_MODE
|
#define TARGET_PROMOTE_FUNCTION_MODE sparc_promote_function_mode
|
#define TARGET_PROMOTE_FUNCTION_MODE sparc_promote_function_mode
|
|
|
#undef TARGET_PROMOTE_PROTOTYPES
|
#undef TARGET_PROMOTE_PROTOTYPES
|
#define TARGET_PROMOTE_PROTOTYPES sparc_promote_prototypes
|
#define TARGET_PROMOTE_PROTOTYPES sparc_promote_prototypes
|
|
|
#undef TARGET_STRUCT_VALUE_RTX
|
#undef TARGET_STRUCT_VALUE_RTX
|
#define TARGET_STRUCT_VALUE_RTX sparc_struct_value_rtx
|
#define TARGET_STRUCT_VALUE_RTX sparc_struct_value_rtx
|
#undef TARGET_RETURN_IN_MEMORY
|
#undef TARGET_RETURN_IN_MEMORY
|
#define TARGET_RETURN_IN_MEMORY sparc_return_in_memory
|
#define TARGET_RETURN_IN_MEMORY sparc_return_in_memory
|
#undef TARGET_MUST_PASS_IN_STACK
|
#undef TARGET_MUST_PASS_IN_STACK
|
#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size
|
#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size
|
#undef TARGET_PASS_BY_REFERENCE
|
#undef TARGET_PASS_BY_REFERENCE
|
#define TARGET_PASS_BY_REFERENCE sparc_pass_by_reference
|
#define TARGET_PASS_BY_REFERENCE sparc_pass_by_reference
|
#undef TARGET_ARG_PARTIAL_BYTES
|
#undef TARGET_ARG_PARTIAL_BYTES
|
#define TARGET_ARG_PARTIAL_BYTES sparc_arg_partial_bytes
|
#define TARGET_ARG_PARTIAL_BYTES sparc_arg_partial_bytes
|
|
|
#undef TARGET_EXPAND_BUILTIN_SAVEREGS
|
#undef TARGET_EXPAND_BUILTIN_SAVEREGS
|
#define TARGET_EXPAND_BUILTIN_SAVEREGS sparc_builtin_saveregs
|
#define TARGET_EXPAND_BUILTIN_SAVEREGS sparc_builtin_saveregs
|
#undef TARGET_STRICT_ARGUMENT_NAMING
|
#undef TARGET_STRICT_ARGUMENT_NAMING
|
#define TARGET_STRICT_ARGUMENT_NAMING sparc_strict_argument_naming
|
#define TARGET_STRICT_ARGUMENT_NAMING sparc_strict_argument_naming
|
|
|
#undef TARGET_EXPAND_BUILTIN_VA_START
|
#undef TARGET_EXPAND_BUILTIN_VA_START
|
#define TARGET_EXPAND_BUILTIN_VA_START sparc_va_start
|
#define TARGET_EXPAND_BUILTIN_VA_START sparc_va_start
|
#undef TARGET_GIMPLIFY_VA_ARG_EXPR
|
#undef TARGET_GIMPLIFY_VA_ARG_EXPR
|
#define TARGET_GIMPLIFY_VA_ARG_EXPR sparc_gimplify_va_arg
|
#define TARGET_GIMPLIFY_VA_ARG_EXPR sparc_gimplify_va_arg
|
|
|
#undef TARGET_VECTOR_MODE_SUPPORTED_P
|
#undef TARGET_VECTOR_MODE_SUPPORTED_P
|
#define TARGET_VECTOR_MODE_SUPPORTED_P sparc_vector_mode_supported_p
|
#define TARGET_VECTOR_MODE_SUPPORTED_P sparc_vector_mode_supported_p
|
|
|
#undef TARGET_DWARF_HANDLE_FRAME_UNSPEC
|
#undef TARGET_DWARF_HANDLE_FRAME_UNSPEC
|
#define TARGET_DWARF_HANDLE_FRAME_UNSPEC sparc_dwarf_handle_frame_unspec
|
#define TARGET_DWARF_HANDLE_FRAME_UNSPEC sparc_dwarf_handle_frame_unspec
|
|
|
#ifdef SUBTARGET_INSERT_ATTRIBUTES
|
#ifdef SUBTARGET_INSERT_ATTRIBUTES
|
#undef TARGET_INSERT_ATTRIBUTES
|
#undef TARGET_INSERT_ATTRIBUTES
|
#define TARGET_INSERT_ATTRIBUTES SUBTARGET_INSERT_ATTRIBUTES
|
#define TARGET_INSERT_ATTRIBUTES SUBTARGET_INSERT_ATTRIBUTES
|
#endif
|
#endif
|
|
|
#ifdef SUBTARGET_ATTRIBUTE_TABLE
|
#ifdef SUBTARGET_ATTRIBUTE_TABLE
|
#undef TARGET_ATTRIBUTE_TABLE
|
#undef TARGET_ATTRIBUTE_TABLE
|
#define TARGET_ATTRIBUTE_TABLE sparc_attribute_table
|
#define TARGET_ATTRIBUTE_TABLE sparc_attribute_table
|
#endif
|
#endif
|
|
|
#undef TARGET_RELAXED_ORDERING
|
#undef TARGET_RELAXED_ORDERING
|
#define TARGET_RELAXED_ORDERING SPARC_RELAXED_ORDERING
|
#define TARGET_RELAXED_ORDERING SPARC_RELAXED_ORDERING
|
|
|
#undef TARGET_DEFAULT_TARGET_FLAGS
|
#undef TARGET_DEFAULT_TARGET_FLAGS
|
#define TARGET_DEFAULT_TARGET_FLAGS TARGET_DEFAULT
|
#define TARGET_DEFAULT_TARGET_FLAGS TARGET_DEFAULT
|
#undef TARGET_HANDLE_OPTION
|
#undef TARGET_HANDLE_OPTION
|
#define TARGET_HANDLE_OPTION sparc_handle_option
|
#define TARGET_HANDLE_OPTION sparc_handle_option
|
|
|
#if TARGET_GNU_TLS && defined(HAVE_AS_SPARC_UA_PCREL)
|
#if TARGET_GNU_TLS && defined(HAVE_AS_SPARC_UA_PCREL)
|
#undef TARGET_ASM_OUTPUT_DWARF_DTPREL
|
#undef TARGET_ASM_OUTPUT_DWARF_DTPREL
|
#define TARGET_ASM_OUTPUT_DWARF_DTPREL sparc_output_dwarf_dtprel
|
#define TARGET_ASM_OUTPUT_DWARF_DTPREL sparc_output_dwarf_dtprel
|
#endif
|
#endif
|
|
|
#undef TARGET_ASM_FILE_END
|
#undef TARGET_ASM_FILE_END
|
#define TARGET_ASM_FILE_END sparc_file_end
|
#define TARGET_ASM_FILE_END sparc_file_end
|
|
|
#undef TARGET_FRAME_POINTER_REQUIRED
|
#undef TARGET_FRAME_POINTER_REQUIRED
|
#define TARGET_FRAME_POINTER_REQUIRED sparc_frame_pointer_required
|
#define TARGET_FRAME_POINTER_REQUIRED sparc_frame_pointer_required
|
|
|
#undef TARGET_CAN_ELIMINATE
|
#undef TARGET_CAN_ELIMINATE
|
#define TARGET_CAN_ELIMINATE sparc_can_eliminate
|
#define TARGET_CAN_ELIMINATE sparc_can_eliminate
|
|
|
#ifdef TARGET_ALTERNATE_LONG_DOUBLE_MANGLING
|
#ifdef TARGET_ALTERNATE_LONG_DOUBLE_MANGLING
|
#undef TARGET_MANGLE_TYPE
|
#undef TARGET_MANGLE_TYPE
|
#define TARGET_MANGLE_TYPE sparc_mangle_type
|
#define TARGET_MANGLE_TYPE sparc_mangle_type
|
#endif
|
#endif
|
|
|
#undef TARGET_LEGITIMATE_ADDRESS_P
|
#undef TARGET_LEGITIMATE_ADDRESS_P
|
#define TARGET_LEGITIMATE_ADDRESS_P sparc_legitimate_address_p
|
#define TARGET_LEGITIMATE_ADDRESS_P sparc_legitimate_address_p
|
|
|
#undef TARGET_TRAMPOLINE_INIT
|
#undef TARGET_TRAMPOLINE_INIT
|
#define TARGET_TRAMPOLINE_INIT sparc_trampoline_init
|
#define TARGET_TRAMPOLINE_INIT sparc_trampoline_init
|
|
|
struct gcc_target targetm = TARGET_INITIALIZER;
|
struct gcc_target targetm = TARGET_INITIALIZER;
|
|
|
/* Implement TARGET_HANDLE_OPTION. */
|
/* Implement TARGET_HANDLE_OPTION. */
|
|
|
static bool
|
static bool
|
sparc_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
|
sparc_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
|
{
|
{
|
switch (code)
|
switch (code)
|
{
|
{
|
case OPT_mfpu:
|
case OPT_mfpu:
|
case OPT_mhard_float:
|
case OPT_mhard_float:
|
case OPT_msoft_float:
|
case OPT_msoft_float:
|
fpu_option_set = true;
|
fpu_option_set = true;
|
break;
|
break;
|
|
|
case OPT_mcpu_:
|
case OPT_mcpu_:
|
sparc_select[1].string = arg;
|
sparc_select[1].string = arg;
|
break;
|
break;
|
|
|
case OPT_mtune_:
|
case OPT_mtune_:
|
sparc_select[2].string = arg;
|
sparc_select[2].string = arg;
|
break;
|
break;
|
}
|
}
|
|
|
return true;
|
return true;
|
}
|
}
|
|
|
/* Validate and override various options, and do some machine dependent
|
/* Validate and override various options, and do some machine dependent
|
initialization. */
|
initialization. */
|
|
|
void
|
void
|
sparc_override_options (void)
|
sparc_override_options (void)
|
{
|
{
|
static struct code_model {
|
static struct code_model {
|
const char *const name;
|
const char *const name;
|
const enum cmodel value;
|
const enum cmodel value;
|
} const cmodels[] = {
|
} const cmodels[] = {
|
{ "32", CM_32 },
|
{ "32", CM_32 },
|
{ "medlow", CM_MEDLOW },
|
{ "medlow", CM_MEDLOW },
|
{ "medmid", CM_MEDMID },
|
{ "medmid", CM_MEDMID },
|
{ "medany", CM_MEDANY },
|
{ "medany", CM_MEDANY },
|
{ "embmedany", CM_EMBMEDANY },
|
{ "embmedany", CM_EMBMEDANY },
|
{ NULL, (enum cmodel) 0 }
|
{ NULL, (enum cmodel) 0 }
|
};
|
};
|
const struct code_model *cmodel;
|
const struct code_model *cmodel;
|
/* Map TARGET_CPU_DEFAULT to value for -m{arch,tune}=. */
|
/* Map TARGET_CPU_DEFAULT to value for -m{arch,tune}=. */
|
static struct cpu_default {
|
static struct cpu_default {
|
const int cpu;
|
const int cpu;
|
const char *const name;
|
const char *const name;
|
} const cpu_default[] = {
|
} const cpu_default[] = {
|
/* There must be one entry here for each TARGET_CPU value. */
|
/* There must be one entry here for each TARGET_CPU value. */
|
{ TARGET_CPU_sparc, "cypress" },
|
{ TARGET_CPU_sparc, "cypress" },
|
{ TARGET_CPU_sparclet, "tsc701" },
|
{ TARGET_CPU_sparclet, "tsc701" },
|
{ TARGET_CPU_sparclite, "f930" },
|
{ TARGET_CPU_sparclite, "f930" },
|
{ TARGET_CPU_v8, "v8" },
|
{ TARGET_CPU_v8, "v8" },
|
{ TARGET_CPU_hypersparc, "hypersparc" },
|
{ TARGET_CPU_hypersparc, "hypersparc" },
|
{ TARGET_CPU_sparclite86x, "sparclite86x" },
|
{ TARGET_CPU_sparclite86x, "sparclite86x" },
|
{ TARGET_CPU_supersparc, "supersparc" },
|
{ TARGET_CPU_supersparc, "supersparc" },
|
{ TARGET_CPU_v9, "v9" },
|
{ TARGET_CPU_v9, "v9" },
|
{ TARGET_CPU_ultrasparc, "ultrasparc" },
|
{ TARGET_CPU_ultrasparc, "ultrasparc" },
|
{ TARGET_CPU_ultrasparc3, "ultrasparc3" },
|
{ TARGET_CPU_ultrasparc3, "ultrasparc3" },
|
{ TARGET_CPU_niagara, "niagara" },
|
{ TARGET_CPU_niagara, "niagara" },
|
{ TARGET_CPU_niagara2, "niagara2" },
|
{ TARGET_CPU_niagara2, "niagara2" },
|
{ 0, 0 }
|
{ 0, 0 }
|
};
|
};
|
const struct cpu_default *def;
|
const struct cpu_default *def;
|
/* Table of values for -m{cpu,tune}=. */
|
/* Table of values for -m{cpu,tune}=. */
|
static struct cpu_table {
|
static struct cpu_table {
|
const char *const name;
|
const char *const name;
|
const enum processor_type processor;
|
const enum processor_type processor;
|
const int disable;
|
const int disable;
|
const int enable;
|
const int enable;
|
} const cpu_table[] = {
|
} const cpu_table[] = {
|
{ "v7", PROCESSOR_V7, MASK_ISA, 0 },
|
{ "v7", PROCESSOR_V7, MASK_ISA, 0 },
|
{ "cypress", PROCESSOR_CYPRESS, MASK_ISA, 0 },
|
{ "cypress", PROCESSOR_CYPRESS, MASK_ISA, 0 },
|
{ "v8", PROCESSOR_V8, MASK_ISA, MASK_V8 },
|
{ "v8", PROCESSOR_V8, MASK_ISA, MASK_V8 },
|
/* TI TMS390Z55 supersparc */
|
/* TI TMS390Z55 supersparc */
|
{ "supersparc", PROCESSOR_SUPERSPARC, MASK_ISA, MASK_V8 },
|
{ "supersparc", PROCESSOR_SUPERSPARC, MASK_ISA, MASK_V8 },
|
{ "sparclite", PROCESSOR_SPARCLITE, MASK_ISA, MASK_SPARCLITE },
|
{ "sparclite", PROCESSOR_SPARCLITE, MASK_ISA, MASK_SPARCLITE },
|
/* The Fujitsu MB86930 is the original sparclite chip, with no fpu.
|
/* The Fujitsu MB86930 is the original sparclite chip, with no fpu.
|
The Fujitsu MB86934 is the recent sparclite chip, with an fpu. */
|
The Fujitsu MB86934 is the recent sparclite chip, with an fpu. */
|
{ "f930", PROCESSOR_F930, MASK_ISA|MASK_FPU, MASK_SPARCLITE },
|
{ "f930", PROCESSOR_F930, MASK_ISA|MASK_FPU, MASK_SPARCLITE },
|
{ "f934", PROCESSOR_F934, MASK_ISA, MASK_SPARCLITE|MASK_FPU },
|
{ "f934", PROCESSOR_F934, MASK_ISA, MASK_SPARCLITE|MASK_FPU },
|
{ "hypersparc", PROCESSOR_HYPERSPARC, MASK_ISA, MASK_V8|MASK_FPU },
|
{ "hypersparc", PROCESSOR_HYPERSPARC, MASK_ISA, MASK_V8|MASK_FPU },
|
{ "sparclite86x", PROCESSOR_SPARCLITE86X, MASK_ISA|MASK_FPU,
|
{ "sparclite86x", PROCESSOR_SPARCLITE86X, MASK_ISA|MASK_FPU,
|
MASK_SPARCLITE },
|
MASK_SPARCLITE },
|
{ "sparclet", PROCESSOR_SPARCLET, MASK_ISA, MASK_SPARCLET },
|
{ "sparclet", PROCESSOR_SPARCLET, MASK_ISA, MASK_SPARCLET },
|
/* TEMIC sparclet */
|
/* TEMIC sparclet */
|
{ "tsc701", PROCESSOR_TSC701, MASK_ISA, MASK_SPARCLET },
|
{ "tsc701", PROCESSOR_TSC701, MASK_ISA, MASK_SPARCLET },
|
{ "v9", PROCESSOR_V9, MASK_ISA, MASK_V9 },
|
{ "v9", PROCESSOR_V9, MASK_ISA, MASK_V9 },
|
/* TI ultrasparc I, II, IIi */
|
/* TI ultrasparc I, II, IIi */
|
{ "ultrasparc", PROCESSOR_ULTRASPARC, MASK_ISA, MASK_V9
|
{ "ultrasparc", PROCESSOR_ULTRASPARC, MASK_ISA, MASK_V9
|
/* Although insns using %y are deprecated, it is a clear win on current
|
/* Although insns using %y are deprecated, it is a clear win on current
|
ultrasparcs. */
|
ultrasparcs. */
|
|MASK_DEPRECATED_V8_INSNS},
|
|MASK_DEPRECATED_V8_INSNS},
|
/* TI ultrasparc III */
|
/* TI ultrasparc III */
|
/* ??? Check if %y issue still holds true in ultra3. */
|
/* ??? Check if %y issue still holds true in ultra3. */
|
{ "ultrasparc3", PROCESSOR_ULTRASPARC3, MASK_ISA, MASK_V9|MASK_DEPRECATED_V8_INSNS},
|
{ "ultrasparc3", PROCESSOR_ULTRASPARC3, MASK_ISA, MASK_V9|MASK_DEPRECATED_V8_INSNS},
|
/* UltraSPARC T1 */
|
/* UltraSPARC T1 */
|
{ "niagara", PROCESSOR_NIAGARA, MASK_ISA, MASK_V9|MASK_DEPRECATED_V8_INSNS},
|
{ "niagara", PROCESSOR_NIAGARA, MASK_ISA, MASK_V9|MASK_DEPRECATED_V8_INSNS},
|
{ "niagara2", PROCESSOR_NIAGARA, MASK_ISA, MASK_V9},
|
{ "niagara2", PROCESSOR_NIAGARA, MASK_ISA, MASK_V9},
|
{ 0, (enum processor_type) 0, 0, 0 }
|
{ 0, (enum processor_type) 0, 0, 0 }
|
};
|
};
|
const struct cpu_table *cpu;
|
const struct cpu_table *cpu;
|
const struct sparc_cpu_select *sel;
|
const struct sparc_cpu_select *sel;
|
int fpu;
|
int fpu;
|
|
|
#ifndef SPARC_BI_ARCH
|
#ifndef SPARC_BI_ARCH
|
/* Check for unsupported architecture size. */
|
/* Check for unsupported architecture size. */
|
if (! TARGET_64BIT != DEFAULT_ARCH32_P)
|
if (! TARGET_64BIT != DEFAULT_ARCH32_P)
|
error ("%s is not supported by this configuration",
|
error ("%s is not supported by this configuration",
|
DEFAULT_ARCH32_P ? "-m64" : "-m32");
|
DEFAULT_ARCH32_P ? "-m64" : "-m32");
|
#endif
|
#endif
|
|
|
/* We force all 64bit archs to use 128 bit long double */
|
/* We force all 64bit archs to use 128 bit long double */
|
if (TARGET_64BIT && ! TARGET_LONG_DOUBLE_128)
|
if (TARGET_64BIT && ! TARGET_LONG_DOUBLE_128)
|
{
|
{
|
error ("-mlong-double-64 not allowed with -m64");
|
error ("-mlong-double-64 not allowed with -m64");
|
target_flags |= MASK_LONG_DOUBLE_128;
|
target_flags |= MASK_LONG_DOUBLE_128;
|
}
|
}
|
|
|
/* Code model selection. */
|
/* Code model selection. */
|
sparc_cmodel = SPARC_DEFAULT_CMODEL;
|
sparc_cmodel = SPARC_DEFAULT_CMODEL;
|
|
|
#ifdef SPARC_BI_ARCH
|
#ifdef SPARC_BI_ARCH
|
if (TARGET_ARCH32)
|
if (TARGET_ARCH32)
|
sparc_cmodel = CM_32;
|
sparc_cmodel = CM_32;
|
#endif
|
#endif
|
|
|
if (sparc_cmodel_string != NULL)
|
if (sparc_cmodel_string != NULL)
|
{
|
{
|
if (TARGET_ARCH64)
|
if (TARGET_ARCH64)
|
{
|
{
|
for (cmodel = &cmodels[0]; cmodel->name; cmodel++)
|
for (cmodel = &cmodels[0]; cmodel->name; cmodel++)
|
if (strcmp (sparc_cmodel_string, cmodel->name) == 0)
|
if (strcmp (sparc_cmodel_string, cmodel->name) == 0)
|
break;
|
break;
|
if (cmodel->name == NULL)
|
if (cmodel->name == NULL)
|
error ("bad value (%s) for -mcmodel= switch", sparc_cmodel_string);
|
error ("bad value (%s) for -mcmodel= switch", sparc_cmodel_string);
|
else
|
else
|
sparc_cmodel = cmodel->value;
|
sparc_cmodel = cmodel->value;
|
}
|
}
|
else
|
else
|
error ("-mcmodel= is not supported on 32 bit systems");
|
error ("-mcmodel= is not supported on 32 bit systems");
|
}
|
}
|
|
|
fpu = target_flags & MASK_FPU; /* save current -mfpu status */
|
fpu = target_flags & MASK_FPU; /* save current -mfpu status */
|
|
|
/* Set the default CPU. */
|
/* Set the default CPU. */
|
for (def = &cpu_default[0]; def->name; ++def)
|
for (def = &cpu_default[0]; def->name; ++def)
|
if (def->cpu == TARGET_CPU_DEFAULT)
|
if (def->cpu == TARGET_CPU_DEFAULT)
|
break;
|
break;
|
gcc_assert (def->name);
|
gcc_assert (def->name);
|
sparc_select[0].string = def->name;
|
sparc_select[0].string = def->name;
|
|
|
for (sel = &sparc_select[0]; sel->name; ++sel)
|
for (sel = &sparc_select[0]; sel->name; ++sel)
|
{
|
{
|
if (sel->string)
|
if (sel->string)
|
{
|
{
|
for (cpu = &cpu_table[0]; cpu->name; ++cpu)
|
for (cpu = &cpu_table[0]; cpu->name; ++cpu)
|
if (! strcmp (sel->string, cpu->name))
|
if (! strcmp (sel->string, cpu->name))
|
{
|
{
|
if (sel->set_tune_p)
|
if (sel->set_tune_p)
|
sparc_cpu = cpu->processor;
|
sparc_cpu = cpu->processor;
|
|
|
if (sel->set_arch_p)
|
if (sel->set_arch_p)
|
{
|
{
|
target_flags &= ~cpu->disable;
|
target_flags &= ~cpu->disable;
|
target_flags |= cpu->enable;
|
target_flags |= cpu->enable;
|
}
|
}
|
break;
|
break;
|
}
|
}
|
|
|
if (! cpu->name)
|
if (! cpu->name)
|
error ("bad value (%s) for %s switch", sel->string, sel->name);
|
error ("bad value (%s) for %s switch", sel->string, sel->name);
|
}
|
}
|
}
|
}
|
|
|
/* If -mfpu or -mno-fpu was explicitly used, don't override with
|
/* If -mfpu or -mno-fpu was explicitly used, don't override with
|
the processor default. */
|
the processor default. */
|
if (fpu_option_set)
|
if (fpu_option_set)
|
target_flags = (target_flags & ~MASK_FPU) | fpu;
|
target_flags = (target_flags & ~MASK_FPU) | fpu;
|
|
|
/* Don't allow -mvis if FPU is disabled. */
|
/* Don't allow -mvis if FPU is disabled. */
|
if (! TARGET_FPU)
|
if (! TARGET_FPU)
|
target_flags &= ~MASK_VIS;
|
target_flags &= ~MASK_VIS;
|
|
|
/* -mvis assumes UltraSPARC+, so we are sure v9 instructions
|
/* -mvis assumes UltraSPARC+, so we are sure v9 instructions
|
are available.
|
are available.
|
-m64 also implies v9. */
|
-m64 also implies v9. */
|
if (TARGET_VIS || TARGET_ARCH64)
|
if (TARGET_VIS || TARGET_ARCH64)
|
{
|
{
|
target_flags |= MASK_V9;
|
target_flags |= MASK_V9;
|
target_flags &= ~(MASK_V8 | MASK_SPARCLET | MASK_SPARCLITE);
|
target_flags &= ~(MASK_V8 | MASK_SPARCLET | MASK_SPARCLITE);
|
}
|
}
|
|
|
/* Use the deprecated v8 insns for sparc64 in 32 bit mode. */
|
/* Use the deprecated v8 insns for sparc64 in 32 bit mode. */
|
if (TARGET_V9 && TARGET_ARCH32)
|
if (TARGET_V9 && TARGET_ARCH32)
|
target_flags |= MASK_DEPRECATED_V8_INSNS;
|
target_flags |= MASK_DEPRECATED_V8_INSNS;
|
|
|
/* V8PLUS requires V9, makes no sense in 64 bit mode. */
|
/* V8PLUS requires V9, makes no sense in 64 bit mode. */
|
if (! TARGET_V9 || TARGET_ARCH64)
|
if (! TARGET_V9 || TARGET_ARCH64)
|
target_flags &= ~MASK_V8PLUS;
|
target_flags &= ~MASK_V8PLUS;
|
|
|
/* Don't use stack biasing in 32 bit mode. */
|
/* Don't use stack biasing in 32 bit mode. */
|
if (TARGET_ARCH32)
|
if (TARGET_ARCH32)
|
target_flags &= ~MASK_STACK_BIAS;
|
target_flags &= ~MASK_STACK_BIAS;
|
|
|
/* Supply a default value for align_functions. */
|
/* Supply a default value for align_functions. */
|
if (align_functions == 0
|
if (align_functions == 0
|
&& (sparc_cpu == PROCESSOR_ULTRASPARC
|
&& (sparc_cpu == PROCESSOR_ULTRASPARC
|
|| sparc_cpu == PROCESSOR_ULTRASPARC3
|
|| sparc_cpu == PROCESSOR_ULTRASPARC3
|
|| sparc_cpu == PROCESSOR_NIAGARA
|
|| sparc_cpu == PROCESSOR_NIAGARA
|
|| sparc_cpu == PROCESSOR_NIAGARA2))
|
|| sparc_cpu == PROCESSOR_NIAGARA2))
|
align_functions = 32;
|
align_functions = 32;
|
|
|
/* Validate PCC_STRUCT_RETURN. */
|
/* Validate PCC_STRUCT_RETURN. */
|
if (flag_pcc_struct_return == DEFAULT_PCC_STRUCT_RETURN)
|
if (flag_pcc_struct_return == DEFAULT_PCC_STRUCT_RETURN)
|
flag_pcc_struct_return = (TARGET_ARCH64 ? 0 : 1);
|
flag_pcc_struct_return = (TARGET_ARCH64 ? 0 : 1);
|
|
|
/* Only use .uaxword when compiling for a 64-bit target. */
|
/* Only use .uaxword when compiling for a 64-bit target. */
|
if (!TARGET_ARCH64)
|
if (!TARGET_ARCH64)
|
targetm.asm_out.unaligned_op.di = NULL;
|
targetm.asm_out.unaligned_op.di = NULL;
|
|
|
/* Do various machine dependent initializations. */
|
/* Do various machine dependent initializations. */
|
sparc_init_modes ();
|
sparc_init_modes ();
|
|
|
/* Acquire unique alias sets for our private stuff. */
|
/* Acquire unique alias sets for our private stuff. */
|
sparc_sr_alias_set = new_alias_set ();
|
sparc_sr_alias_set = new_alias_set ();
|
struct_value_alias_set = new_alias_set ();
|
struct_value_alias_set = new_alias_set ();
|
|
|
/* Set up function hooks. */
|
/* Set up function hooks. */
|
init_machine_status = sparc_init_machine_status;
|
init_machine_status = sparc_init_machine_status;
|
|
|
switch (sparc_cpu)
|
switch (sparc_cpu)
|
{
|
{
|
case PROCESSOR_V7:
|
case PROCESSOR_V7:
|
case PROCESSOR_CYPRESS:
|
case PROCESSOR_CYPRESS:
|
sparc_costs = &cypress_costs;
|
sparc_costs = &cypress_costs;
|
break;
|
break;
|
case PROCESSOR_V8:
|
case PROCESSOR_V8:
|
case PROCESSOR_SPARCLITE:
|
case PROCESSOR_SPARCLITE:
|
case PROCESSOR_SUPERSPARC:
|
case PROCESSOR_SUPERSPARC:
|
sparc_costs = &supersparc_costs;
|
sparc_costs = &supersparc_costs;
|
break;
|
break;
|
case PROCESSOR_F930:
|
case PROCESSOR_F930:
|
case PROCESSOR_F934:
|
case PROCESSOR_F934:
|
case PROCESSOR_HYPERSPARC:
|
case PROCESSOR_HYPERSPARC:
|
case PROCESSOR_SPARCLITE86X:
|
case PROCESSOR_SPARCLITE86X:
|
sparc_costs = &hypersparc_costs;
|
sparc_costs = &hypersparc_costs;
|
break;
|
break;
|
case PROCESSOR_SPARCLET:
|
case PROCESSOR_SPARCLET:
|
case PROCESSOR_TSC701:
|
case PROCESSOR_TSC701:
|
sparc_costs = &sparclet_costs;
|
sparc_costs = &sparclet_costs;
|
break;
|
break;
|
case PROCESSOR_V9:
|
case PROCESSOR_V9:
|
case PROCESSOR_ULTRASPARC:
|
case PROCESSOR_ULTRASPARC:
|
sparc_costs = &ultrasparc_costs;
|
sparc_costs = &ultrasparc_costs;
|
break;
|
break;
|
case PROCESSOR_ULTRASPARC3:
|
case PROCESSOR_ULTRASPARC3:
|
sparc_costs = &ultrasparc3_costs;
|
sparc_costs = &ultrasparc3_costs;
|
break;
|
break;
|
case PROCESSOR_NIAGARA:
|
case PROCESSOR_NIAGARA:
|
sparc_costs = &niagara_costs;
|
sparc_costs = &niagara_costs;
|
break;
|
break;
|
case PROCESSOR_NIAGARA2:
|
case PROCESSOR_NIAGARA2:
|
sparc_costs = &niagara2_costs;
|
sparc_costs = &niagara2_costs;
|
break;
|
break;
|
};
|
};
|
|
|
#ifdef TARGET_DEFAULT_LONG_DOUBLE_128
|
#ifdef TARGET_DEFAULT_LONG_DOUBLE_128
|
if (!(target_flags_explicit & MASK_LONG_DOUBLE_128))
|
if (!(target_flags_explicit & MASK_LONG_DOUBLE_128))
|
target_flags |= MASK_LONG_DOUBLE_128;
|
target_flags |= MASK_LONG_DOUBLE_128;
|
#endif
|
#endif
|
|
|
if (!PARAM_SET_P (PARAM_SIMULTANEOUS_PREFETCHES))
|
if (!PARAM_SET_P (PARAM_SIMULTANEOUS_PREFETCHES))
|
set_param_value ("simultaneous-prefetches",
|
set_param_value ("simultaneous-prefetches",
|
((sparc_cpu == PROCESSOR_ULTRASPARC
|
((sparc_cpu == PROCESSOR_ULTRASPARC
|
|| sparc_cpu == PROCESSOR_NIAGARA
|
|| sparc_cpu == PROCESSOR_NIAGARA
|
|| sparc_cpu == PROCESSOR_NIAGARA2)
|
|| sparc_cpu == PROCESSOR_NIAGARA2)
|
? 2
|
? 2
|
: (sparc_cpu == PROCESSOR_ULTRASPARC3
|
: (sparc_cpu == PROCESSOR_ULTRASPARC3
|
? 8 : 3)));
|
? 8 : 3)));
|
if (!PARAM_SET_P (PARAM_L1_CACHE_LINE_SIZE))
|
if (!PARAM_SET_P (PARAM_L1_CACHE_LINE_SIZE))
|
set_param_value ("l1-cache-line-size",
|
set_param_value ("l1-cache-line-size",
|
((sparc_cpu == PROCESSOR_ULTRASPARC
|
((sparc_cpu == PROCESSOR_ULTRASPARC
|
|| sparc_cpu == PROCESSOR_ULTRASPARC3
|
|| sparc_cpu == PROCESSOR_ULTRASPARC3
|
|| sparc_cpu == PROCESSOR_NIAGARA
|
|| sparc_cpu == PROCESSOR_NIAGARA
|
|| sparc_cpu == PROCESSOR_NIAGARA2)
|
|| sparc_cpu == PROCESSOR_NIAGARA2)
|
? 64 : 32));
|
? 64 : 32));
|
}
|
}
|
|
|
/* Miscellaneous utilities. */
|
/* Miscellaneous utilities. */
|
|
|
/* Nonzero if CODE, a comparison, is suitable for use in v9 conditional move
|
/* Nonzero if CODE, a comparison, is suitable for use in v9 conditional move
|
or branch on register contents instructions. */
|
or branch on register contents instructions. */
|
|
|
int
|
int
|
v9_regcmp_p (enum rtx_code code)
|
v9_regcmp_p (enum rtx_code code)
|
{
|
{
|
return (code == EQ || code == NE || code == GE || code == LT
|
return (code == EQ || code == NE || code == GE || code == LT
|
|| code == LE || code == GT);
|
|| code == LE || code == GT);
|
}
|
}
|
|
|
/* Nonzero if OP is a floating point constant which can
|
/* Nonzero if OP is a floating point constant which can
|
be loaded into an integer register using a single
|
be loaded into an integer register using a single
|
sethi instruction. */
|
sethi instruction. */
|
|
|
int
|
int
|
fp_sethi_p (rtx op)
|
fp_sethi_p (rtx op)
|
{
|
{
|
if (GET_CODE (op) == CONST_DOUBLE)
|
if (GET_CODE (op) == CONST_DOUBLE)
|
{
|
{
|
REAL_VALUE_TYPE r;
|
REAL_VALUE_TYPE r;
|
long i;
|
long i;
|
|
|
REAL_VALUE_FROM_CONST_DOUBLE (r, op);
|
REAL_VALUE_FROM_CONST_DOUBLE (r, op);
|
REAL_VALUE_TO_TARGET_SINGLE (r, i);
|
REAL_VALUE_TO_TARGET_SINGLE (r, i);
|
return !SPARC_SIMM13_P (i) && SPARC_SETHI_P (i);
|
return !SPARC_SIMM13_P (i) && SPARC_SETHI_P (i);
|
}
|
}
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Nonzero if OP is a floating point constant which can
|
/* Nonzero if OP is a floating point constant which can
|
be loaded into an integer register using a single
|
be loaded into an integer register using a single
|
mov instruction. */
|
mov instruction. */
|
|
|
int
|
int
|
fp_mov_p (rtx op)
|
fp_mov_p (rtx op)
|
{
|
{
|
if (GET_CODE (op) == CONST_DOUBLE)
|
if (GET_CODE (op) == CONST_DOUBLE)
|
{
|
{
|
REAL_VALUE_TYPE r;
|
REAL_VALUE_TYPE r;
|
long i;
|
long i;
|
|
|
REAL_VALUE_FROM_CONST_DOUBLE (r, op);
|
REAL_VALUE_FROM_CONST_DOUBLE (r, op);
|
REAL_VALUE_TO_TARGET_SINGLE (r, i);
|
REAL_VALUE_TO_TARGET_SINGLE (r, i);
|
return SPARC_SIMM13_P (i);
|
return SPARC_SIMM13_P (i);
|
}
|
}
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Nonzero if OP is a floating point constant which can
|
/* Nonzero if OP is a floating point constant which can
|
be loaded into an integer register using a high/losum
|
be loaded into an integer register using a high/losum
|
instruction sequence. */
|
instruction sequence. */
|
|
|
int
|
int
|
fp_high_losum_p (rtx op)
|
fp_high_losum_p (rtx op)
|
{
|
{
|
/* The constraints calling this should only be in
|
/* The constraints calling this should only be in
|
SFmode move insns, so any constant which cannot
|
SFmode move insns, so any constant which cannot
|
be moved using a single insn will do. */
|
be moved using a single insn will do. */
|
if (GET_CODE (op) == CONST_DOUBLE)
|
if (GET_CODE (op) == CONST_DOUBLE)
|
{
|
{
|
REAL_VALUE_TYPE r;
|
REAL_VALUE_TYPE r;
|
long i;
|
long i;
|
|
|
REAL_VALUE_FROM_CONST_DOUBLE (r, op);
|
REAL_VALUE_FROM_CONST_DOUBLE (r, op);
|
REAL_VALUE_TO_TARGET_SINGLE (r, i);
|
REAL_VALUE_TO_TARGET_SINGLE (r, i);
|
return !SPARC_SIMM13_P (i) && !SPARC_SETHI_P (i);
|
return !SPARC_SIMM13_P (i) && !SPARC_SETHI_P (i);
|
}
|
}
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Expand a move instruction. Return true if all work is done. */
|
/* Expand a move instruction. Return true if all work is done. */
|
|
|
bool
|
bool
|
sparc_expand_move (enum machine_mode mode, rtx *operands)
|
sparc_expand_move (enum machine_mode mode, rtx *operands)
|
{
|
{
|
/* Handle sets of MEM first. */
|
/* Handle sets of MEM first. */
|
if (GET_CODE (operands[0]) == MEM)
|
if (GET_CODE (operands[0]) == MEM)
|
{
|
{
|
/* 0 is a register (or a pair of registers) on SPARC. */
|
/* 0 is a register (or a pair of registers) on SPARC. */
|
if (register_or_zero_operand (operands[1], mode))
|
if (register_or_zero_operand (operands[1], mode))
|
return false;
|
return false;
|
|
|
if (!reload_in_progress)
|
if (!reload_in_progress)
|
{
|
{
|
operands[0] = validize_mem (operands[0]);
|
operands[0] = validize_mem (operands[0]);
|
operands[1] = force_reg (mode, operands[1]);
|
operands[1] = force_reg (mode, operands[1]);
|
}
|
}
|
}
|
}
|
|
|
/* Fixup TLS cases. */
|
/* Fixup TLS cases. */
|
if (TARGET_HAVE_TLS
|
if (TARGET_HAVE_TLS
|
&& CONSTANT_P (operands[1])
|
&& CONSTANT_P (operands[1])
|
&& sparc_tls_referenced_p (operands [1]))
|
&& sparc_tls_referenced_p (operands [1]))
|
{
|
{
|
operands[1] = legitimize_tls_address (operands[1]);
|
operands[1] = legitimize_tls_address (operands[1]);
|
return false;
|
return false;
|
}
|
}
|
|
|
/* Fixup PIC cases. */
|
/* Fixup PIC cases. */
|
if (flag_pic && CONSTANT_P (operands[1]))
|
if (flag_pic && CONSTANT_P (operands[1]))
|
{
|
{
|
if (pic_address_needs_scratch (operands[1]))
|
if (pic_address_needs_scratch (operands[1]))
|
operands[1] = legitimize_pic_address (operands[1], NULL_RTX);
|
operands[1] = legitimize_pic_address (operands[1], NULL_RTX);
|
|
|
/* VxWorks does not impose a fixed gap between segments; the run-time
|
/* VxWorks does not impose a fixed gap between segments; the run-time
|
gap can be different from the object-file gap. We therefore can't
|
gap can be different from the object-file gap. We therefore can't
|
assume X - _GLOBAL_OFFSET_TABLE_ is a link-time constant unless we
|
assume X - _GLOBAL_OFFSET_TABLE_ is a link-time constant unless we
|
are absolutely sure that X is in the same segment as the GOT.
|
are absolutely sure that X is in the same segment as the GOT.
|
Unfortunately, the flexibility of linker scripts means that we
|
Unfortunately, the flexibility of linker scripts means that we
|
can't be sure of that in general, so assume that _G_O_T_-relative
|
can't be sure of that in general, so assume that _G_O_T_-relative
|
accesses are never valid on VxWorks. */
|
accesses are never valid on VxWorks. */
|
if (GET_CODE (operands[1]) == LABEL_REF && !TARGET_VXWORKS_RTP)
|
if (GET_CODE (operands[1]) == LABEL_REF && !TARGET_VXWORKS_RTP)
|
{
|
{
|
if (mode == SImode)
|
if (mode == SImode)
|
{
|
{
|
emit_insn (gen_movsi_pic_label_ref (operands[0], operands[1]));
|
emit_insn (gen_movsi_pic_label_ref (operands[0], operands[1]));
|
return true;
|
return true;
|
}
|
}
|
|
|
if (mode == DImode)
|
if (mode == DImode)
|
{
|
{
|
gcc_assert (TARGET_ARCH64);
|
gcc_assert (TARGET_ARCH64);
|
emit_insn (gen_movdi_pic_label_ref (operands[0], operands[1]));
|
emit_insn (gen_movdi_pic_label_ref (operands[0], operands[1]));
|
return true;
|
return true;
|
}
|
}
|
}
|
}
|
|
|
if (symbolic_operand (operands[1], mode))
|
if (symbolic_operand (operands[1], mode))
|
{
|
{
|
operands[1] = legitimize_pic_address (operands[1],
|
operands[1] = legitimize_pic_address (operands[1],
|
reload_in_progress
|
reload_in_progress
|
? operands[0] : NULL_RTX);
|
? operands[0] : NULL_RTX);
|
return false;
|
return false;
|
}
|
}
|
}
|
}
|
|
|
/* If we are trying to toss an integer constant into FP registers,
|
/* If we are trying to toss an integer constant into FP registers,
|
or loading a FP or vector constant, force it into memory. */
|
or loading a FP or vector constant, force it into memory. */
|
if (CONSTANT_P (operands[1])
|
if (CONSTANT_P (operands[1])
|
&& REG_P (operands[0])
|
&& REG_P (operands[0])
|
&& (SPARC_FP_REG_P (REGNO (operands[0]))
|
&& (SPARC_FP_REG_P (REGNO (operands[0]))
|
|| SCALAR_FLOAT_MODE_P (mode)
|
|| SCALAR_FLOAT_MODE_P (mode)
|
|| VECTOR_MODE_P (mode)))
|
|| VECTOR_MODE_P (mode)))
|
{
|
{
|
/* emit_group_store will send such bogosity to us when it is
|
/* emit_group_store will send such bogosity to us when it is
|
not storing directly into memory. So fix this up to avoid
|
not storing directly into memory. So fix this up to avoid
|
crashes in output_constant_pool. */
|
crashes in output_constant_pool. */
|
if (operands [1] == const0_rtx)
|
if (operands [1] == const0_rtx)
|
operands[1] = CONST0_RTX (mode);
|
operands[1] = CONST0_RTX (mode);
|
|
|
/* We can clear FP registers if TARGET_VIS, and always other regs. */
|
/* We can clear FP registers if TARGET_VIS, and always other regs. */
|
if ((TARGET_VIS || REGNO (operands[0]) < SPARC_FIRST_FP_REG)
|
if ((TARGET_VIS || REGNO (operands[0]) < SPARC_FIRST_FP_REG)
|
&& const_zero_operand (operands[1], mode))
|
&& const_zero_operand (operands[1], mode))
|
return false;
|
return false;
|
|
|
if (REGNO (operands[0]) < SPARC_FIRST_FP_REG
|
if (REGNO (operands[0]) < SPARC_FIRST_FP_REG
|
/* We are able to build any SF constant in integer registers
|
/* We are able to build any SF constant in integer registers
|
with at most 2 instructions. */
|
with at most 2 instructions. */
|
&& (mode == SFmode
|
&& (mode == SFmode
|
/* And any DF constant in integer registers. */
|
/* And any DF constant in integer registers. */
|
|| (mode == DFmode
|
|| (mode == DFmode
|
&& (reload_completed || reload_in_progress))))
|
&& (reload_completed || reload_in_progress))))
|
return false;
|
return false;
|
|
|
operands[1] = force_const_mem (mode, operands[1]);
|
operands[1] = force_const_mem (mode, operands[1]);
|
if (!reload_in_progress)
|
if (!reload_in_progress)
|
operands[1] = validize_mem (operands[1]);
|
operands[1] = validize_mem (operands[1]);
|
return false;
|
return false;
|
}
|
}
|
|
|
/* Accept non-constants and valid constants unmodified. */
|
/* Accept non-constants and valid constants unmodified. */
|
if (!CONSTANT_P (operands[1])
|
if (!CONSTANT_P (operands[1])
|
|| GET_CODE (operands[1]) == HIGH
|
|| GET_CODE (operands[1]) == HIGH
|
|| input_operand (operands[1], mode))
|
|| input_operand (operands[1], mode))
|
return false;
|
return false;
|
|
|
switch (mode)
|
switch (mode)
|
{
|
{
|
case QImode:
|
case QImode:
|
/* All QImode constants require only one insn, so proceed. */
|
/* All QImode constants require only one insn, so proceed. */
|
break;
|
break;
|
|
|
case HImode:
|
case HImode:
|
case SImode:
|
case SImode:
|
sparc_emit_set_const32 (operands[0], operands[1]);
|
sparc_emit_set_const32 (operands[0], operands[1]);
|
return true;
|
return true;
|
|
|
case DImode:
|
case DImode:
|
/* input_operand should have filtered out 32-bit mode. */
|
/* input_operand should have filtered out 32-bit mode. */
|
sparc_emit_set_const64 (operands[0], operands[1]);
|
sparc_emit_set_const64 (operands[0], operands[1]);
|
return true;
|
return true;
|
|
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
|
|
return false;
|
return false;
|
}
|
}
|
|
|
/* Load OP1, a 32-bit constant, into OP0, a register.
|
/* Load OP1, a 32-bit constant, into OP0, a register.
|
We know it can't be done in one insn when we get
|
We know it can't be done in one insn when we get
|
here, the move expander guarantees this. */
|
here, the move expander guarantees this. */
|
|
|
void
|
void
|
sparc_emit_set_const32 (rtx op0, rtx op1)
|
sparc_emit_set_const32 (rtx op0, rtx op1)
|
{
|
{
|
enum machine_mode mode = GET_MODE (op0);
|
enum machine_mode mode = GET_MODE (op0);
|
rtx temp;
|
rtx temp;
|
|
|
if (reload_in_progress || reload_completed)
|
if (reload_in_progress || reload_completed)
|
temp = op0;
|
temp = op0;
|
else
|
else
|
temp = gen_reg_rtx (mode);
|
temp = gen_reg_rtx (mode);
|
|
|
if (GET_CODE (op1) == CONST_INT)
|
if (GET_CODE (op1) == CONST_INT)
|
{
|
{
|
gcc_assert (!small_int_operand (op1, mode)
|
gcc_assert (!small_int_operand (op1, mode)
|
&& !const_high_operand (op1, mode));
|
&& !const_high_operand (op1, mode));
|
|
|
/* Emit them as real moves instead of a HIGH/LO_SUM,
|
/* Emit them as real moves instead of a HIGH/LO_SUM,
|
this way CSE can see everything and reuse intermediate
|
this way CSE can see everything and reuse intermediate
|
values if it wants. */
|
values if it wants. */
|
emit_insn (gen_rtx_SET (VOIDmode, temp,
|
emit_insn (gen_rtx_SET (VOIDmode, temp,
|
GEN_INT (INTVAL (op1)
|
GEN_INT (INTVAL (op1)
|
& ~(HOST_WIDE_INT)0x3ff)));
|
& ~(HOST_WIDE_INT)0x3ff)));
|
|
|
emit_insn (gen_rtx_SET (VOIDmode,
|
emit_insn (gen_rtx_SET (VOIDmode,
|
op0,
|
op0,
|
gen_rtx_IOR (mode, temp,
|
gen_rtx_IOR (mode, temp,
|
GEN_INT (INTVAL (op1) & 0x3ff))));
|
GEN_INT (INTVAL (op1) & 0x3ff))));
|
}
|
}
|
else
|
else
|
{
|
{
|
/* A symbol, emit in the traditional way. */
|
/* A symbol, emit in the traditional way. */
|
emit_insn (gen_rtx_SET (VOIDmode, temp,
|
emit_insn (gen_rtx_SET (VOIDmode, temp,
|
gen_rtx_HIGH (mode, op1)));
|
gen_rtx_HIGH (mode, op1)));
|
emit_insn (gen_rtx_SET (VOIDmode,
|
emit_insn (gen_rtx_SET (VOIDmode,
|
op0, gen_rtx_LO_SUM (mode, temp, op1)));
|
op0, gen_rtx_LO_SUM (mode, temp, op1)));
|
}
|
}
|
}
|
}
|
|
|
/* Load OP1, a symbolic 64-bit constant, into OP0, a DImode register.
|
/* Load OP1, a symbolic 64-bit constant, into OP0, a DImode register.
|
If TEMP is nonzero, we are forbidden to use any other scratch
|
If TEMP is nonzero, we are forbidden to use any other scratch
|
registers. Otherwise, we are allowed to generate them as needed.
|
registers. Otherwise, we are allowed to generate them as needed.
|
|
|
Note that TEMP may have TImode if the code model is TARGET_CM_MEDANY
|
Note that TEMP may have TImode if the code model is TARGET_CM_MEDANY
|
or TARGET_CM_EMBMEDANY (see the reload_indi and reload_outdi patterns). */
|
or TARGET_CM_EMBMEDANY (see the reload_indi and reload_outdi patterns). */
|
|
|
void
|
void
|
sparc_emit_set_symbolic_const64 (rtx op0, rtx op1, rtx temp)
|
sparc_emit_set_symbolic_const64 (rtx op0, rtx op1, rtx temp)
|
{
|
{
|
rtx temp1, temp2, temp3, temp4, temp5;
|
rtx temp1, temp2, temp3, temp4, temp5;
|
rtx ti_temp = 0;
|
rtx ti_temp = 0;
|
|
|
if (temp && GET_MODE (temp) == TImode)
|
if (temp && GET_MODE (temp) == TImode)
|
{
|
{
|
ti_temp = temp;
|
ti_temp = temp;
|
temp = gen_rtx_REG (DImode, REGNO (temp));
|
temp = gen_rtx_REG (DImode, REGNO (temp));
|
}
|
}
|
|
|
/* SPARC-V9 code-model support. */
|
/* SPARC-V9 code-model support. */
|
switch (sparc_cmodel)
|
switch (sparc_cmodel)
|
{
|
{
|
case CM_MEDLOW:
|
case CM_MEDLOW:
|
/* The range spanned by all instructions in the object is less
|
/* The range spanned by all instructions in the object is less
|
than 2^31 bytes (2GB) and the distance from any instruction
|
than 2^31 bytes (2GB) and the distance from any instruction
|
to the location of the label _GLOBAL_OFFSET_TABLE_ is less
|
to the location of the label _GLOBAL_OFFSET_TABLE_ is less
|
than 2^31 bytes (2GB).
|
than 2^31 bytes (2GB).
|
|
|
The executable must be in the low 4TB of the virtual address
|
The executable must be in the low 4TB of the virtual address
|
space.
|
space.
|
|
|
sethi %hi(symbol), %temp1
|
sethi %hi(symbol), %temp1
|
or %temp1, %lo(symbol), %reg */
|
or %temp1, %lo(symbol), %reg */
|
if (temp)
|
if (temp)
|
temp1 = temp; /* op0 is allowed. */
|
temp1 = temp; /* op0 is allowed. */
|
else
|
else
|
temp1 = gen_reg_rtx (DImode);
|
temp1 = gen_reg_rtx (DImode);
|
|
|
emit_insn (gen_rtx_SET (VOIDmode, temp1, gen_rtx_HIGH (DImode, op1)));
|
emit_insn (gen_rtx_SET (VOIDmode, temp1, gen_rtx_HIGH (DImode, op1)));
|
emit_insn (gen_rtx_SET (VOIDmode, op0, gen_rtx_LO_SUM (DImode, temp1, op1)));
|
emit_insn (gen_rtx_SET (VOIDmode, op0, gen_rtx_LO_SUM (DImode, temp1, op1)));
|
break;
|
break;
|
|
|
case CM_MEDMID:
|
case CM_MEDMID:
|
/* The range spanned by all instructions in the object is less
|
/* The range spanned by all instructions in the object is less
|
than 2^31 bytes (2GB) and the distance from any instruction
|
than 2^31 bytes (2GB) and the distance from any instruction
|
to the location of the label _GLOBAL_OFFSET_TABLE_ is less
|
to the location of the label _GLOBAL_OFFSET_TABLE_ is less
|
than 2^31 bytes (2GB).
|
than 2^31 bytes (2GB).
|
|
|
The executable must be in the low 16TB of the virtual address
|
The executable must be in the low 16TB of the virtual address
|
space.
|
space.
|
|
|
sethi %h44(symbol), %temp1
|
sethi %h44(symbol), %temp1
|
or %temp1, %m44(symbol), %temp2
|
or %temp1, %m44(symbol), %temp2
|
sllx %temp2, 12, %temp3
|
sllx %temp2, 12, %temp3
|
or %temp3, %l44(symbol), %reg */
|
or %temp3, %l44(symbol), %reg */
|
if (temp)
|
if (temp)
|
{
|
{
|
temp1 = op0;
|
temp1 = op0;
|
temp2 = op0;
|
temp2 = op0;
|
temp3 = temp; /* op0 is allowed. */
|
temp3 = temp; /* op0 is allowed. */
|
}
|
}
|
else
|
else
|
{
|
{
|
temp1 = gen_reg_rtx (DImode);
|
temp1 = gen_reg_rtx (DImode);
|
temp2 = gen_reg_rtx (DImode);
|
temp2 = gen_reg_rtx (DImode);
|
temp3 = gen_reg_rtx (DImode);
|
temp3 = gen_reg_rtx (DImode);
|
}
|
}
|
|
|
emit_insn (gen_seth44 (temp1, op1));
|
emit_insn (gen_seth44 (temp1, op1));
|
emit_insn (gen_setm44 (temp2, temp1, op1));
|
emit_insn (gen_setm44 (temp2, temp1, op1));
|
emit_insn (gen_rtx_SET (VOIDmode, temp3,
|
emit_insn (gen_rtx_SET (VOIDmode, temp3,
|
gen_rtx_ASHIFT (DImode, temp2, GEN_INT (12))));
|
gen_rtx_ASHIFT (DImode, temp2, GEN_INT (12))));
|
emit_insn (gen_setl44 (op0, temp3, op1));
|
emit_insn (gen_setl44 (op0, temp3, op1));
|
break;
|
break;
|
|
|
case CM_MEDANY:
|
case CM_MEDANY:
|
/* The range spanned by all instructions in the object is less
|
/* The range spanned by all instructions in the object is less
|
than 2^31 bytes (2GB) and the distance from any instruction
|
than 2^31 bytes (2GB) and the distance from any instruction
|
to the location of the label _GLOBAL_OFFSET_TABLE_ is less
|
to the location of the label _GLOBAL_OFFSET_TABLE_ is less
|
than 2^31 bytes (2GB).
|
than 2^31 bytes (2GB).
|
|
|
The executable can be placed anywhere in the virtual address
|
The executable can be placed anywhere in the virtual address
|
space.
|
space.
|
|
|
sethi %hh(symbol), %temp1
|
sethi %hh(symbol), %temp1
|
sethi %lm(symbol), %temp2
|
sethi %lm(symbol), %temp2
|
or %temp1, %hm(symbol), %temp3
|
or %temp1, %hm(symbol), %temp3
|
sllx %temp3, 32, %temp4
|
sllx %temp3, 32, %temp4
|
or %temp4, %temp2, %temp5
|
or %temp4, %temp2, %temp5
|
or %temp5, %lo(symbol), %reg */
|
or %temp5, %lo(symbol), %reg */
|
if (temp)
|
if (temp)
|
{
|
{
|
/* It is possible that one of the registers we got for operands[2]
|
/* It is possible that one of the registers we got for operands[2]
|
might coincide with that of operands[0] (which is why we made
|
might coincide with that of operands[0] (which is why we made
|
it TImode). Pick the other one to use as our scratch. */
|
it TImode). Pick the other one to use as our scratch. */
|
if (rtx_equal_p (temp, op0))
|
if (rtx_equal_p (temp, op0))
|
{
|
{
|
gcc_assert (ti_temp);
|
gcc_assert (ti_temp);
|
temp = gen_rtx_REG (DImode, REGNO (temp) + 1);
|
temp = gen_rtx_REG (DImode, REGNO (temp) + 1);
|
}
|
}
|
temp1 = op0;
|
temp1 = op0;
|
temp2 = temp; /* op0 is _not_ allowed, see above. */
|
temp2 = temp; /* op0 is _not_ allowed, see above. */
|
temp3 = op0;
|
temp3 = op0;
|
temp4 = op0;
|
temp4 = op0;
|
temp5 = op0;
|
temp5 = op0;
|
}
|
}
|
else
|
else
|
{
|
{
|
temp1 = gen_reg_rtx (DImode);
|
temp1 = gen_reg_rtx (DImode);
|
temp2 = gen_reg_rtx (DImode);
|
temp2 = gen_reg_rtx (DImode);
|
temp3 = gen_reg_rtx (DImode);
|
temp3 = gen_reg_rtx (DImode);
|
temp4 = gen_reg_rtx (DImode);
|
temp4 = gen_reg_rtx (DImode);
|
temp5 = gen_reg_rtx (DImode);
|
temp5 = gen_reg_rtx (DImode);
|
}
|
}
|
|
|
emit_insn (gen_sethh (temp1, op1));
|
emit_insn (gen_sethh (temp1, op1));
|
emit_insn (gen_setlm (temp2, op1));
|
emit_insn (gen_setlm (temp2, op1));
|
emit_insn (gen_sethm (temp3, temp1, op1));
|
emit_insn (gen_sethm (temp3, temp1, op1));
|
emit_insn (gen_rtx_SET (VOIDmode, temp4,
|
emit_insn (gen_rtx_SET (VOIDmode, temp4,
|
gen_rtx_ASHIFT (DImode, temp3, GEN_INT (32))));
|
gen_rtx_ASHIFT (DImode, temp3, GEN_INT (32))));
|
emit_insn (gen_rtx_SET (VOIDmode, temp5,
|
emit_insn (gen_rtx_SET (VOIDmode, temp5,
|
gen_rtx_PLUS (DImode, temp4, temp2)));
|
gen_rtx_PLUS (DImode, temp4, temp2)));
|
emit_insn (gen_setlo (op0, temp5, op1));
|
emit_insn (gen_setlo (op0, temp5, op1));
|
break;
|
break;
|
|
|
case CM_EMBMEDANY:
|
case CM_EMBMEDANY:
|
/* Old old old backwards compatibility kruft here.
|
/* Old old old backwards compatibility kruft here.
|
Essentially it is MEDLOW with a fixed 64-bit
|
Essentially it is MEDLOW with a fixed 64-bit
|
virtual base added to all data segment addresses.
|
virtual base added to all data segment addresses.
|
Text-segment stuff is computed like MEDANY, we can't
|
Text-segment stuff is computed like MEDANY, we can't
|
reuse the code above because the relocation knobs
|
reuse the code above because the relocation knobs
|
look different.
|
look different.
|
|
|
Data segment: sethi %hi(symbol), %temp1
|
Data segment: sethi %hi(symbol), %temp1
|
add %temp1, EMBMEDANY_BASE_REG, %temp2
|
add %temp1, EMBMEDANY_BASE_REG, %temp2
|
or %temp2, %lo(symbol), %reg */
|
or %temp2, %lo(symbol), %reg */
|
if (data_segment_operand (op1, GET_MODE (op1)))
|
if (data_segment_operand (op1, GET_MODE (op1)))
|
{
|
{
|
if (temp)
|
if (temp)
|
{
|
{
|
temp1 = temp; /* op0 is allowed. */
|
temp1 = temp; /* op0 is allowed. */
|
temp2 = op0;
|
temp2 = op0;
|
}
|
}
|
else
|
else
|
{
|
{
|
temp1 = gen_reg_rtx (DImode);
|
temp1 = gen_reg_rtx (DImode);
|
temp2 = gen_reg_rtx (DImode);
|
temp2 = gen_reg_rtx (DImode);
|
}
|
}
|
|
|
emit_insn (gen_embmedany_sethi (temp1, op1));
|
emit_insn (gen_embmedany_sethi (temp1, op1));
|
emit_insn (gen_embmedany_brsum (temp2, temp1));
|
emit_insn (gen_embmedany_brsum (temp2, temp1));
|
emit_insn (gen_embmedany_losum (op0, temp2, op1));
|
emit_insn (gen_embmedany_losum (op0, temp2, op1));
|
}
|
}
|
|
|
/* Text segment: sethi %uhi(symbol), %temp1
|
/* Text segment: sethi %uhi(symbol), %temp1
|
sethi %hi(symbol), %temp2
|
sethi %hi(symbol), %temp2
|
or %temp1, %ulo(symbol), %temp3
|
or %temp1, %ulo(symbol), %temp3
|
sllx %temp3, 32, %temp4
|
sllx %temp3, 32, %temp4
|
or %temp4, %temp2, %temp5
|
or %temp4, %temp2, %temp5
|
or %temp5, %lo(symbol), %reg */
|
or %temp5, %lo(symbol), %reg */
|
else
|
else
|
{
|
{
|
if (temp)
|
if (temp)
|
{
|
{
|
/* It is possible that one of the registers we got for operands[2]
|
/* It is possible that one of the registers we got for operands[2]
|
might coincide with that of operands[0] (which is why we made
|
might coincide with that of operands[0] (which is why we made
|
it TImode). Pick the other one to use as our scratch. */
|
it TImode). Pick the other one to use as our scratch. */
|
if (rtx_equal_p (temp, op0))
|
if (rtx_equal_p (temp, op0))
|
{
|
{
|
gcc_assert (ti_temp);
|
gcc_assert (ti_temp);
|
temp = gen_rtx_REG (DImode, REGNO (temp) + 1);
|
temp = gen_rtx_REG (DImode, REGNO (temp) + 1);
|
}
|
}
|
temp1 = op0;
|
temp1 = op0;
|
temp2 = temp; /* op0 is _not_ allowed, see above. */
|
temp2 = temp; /* op0 is _not_ allowed, see above. */
|
temp3 = op0;
|
temp3 = op0;
|
temp4 = op0;
|
temp4 = op0;
|
temp5 = op0;
|
temp5 = op0;
|
}
|
}
|
else
|
else
|
{
|
{
|
temp1 = gen_reg_rtx (DImode);
|
temp1 = gen_reg_rtx (DImode);
|
temp2 = gen_reg_rtx (DImode);
|
temp2 = gen_reg_rtx (DImode);
|
temp3 = gen_reg_rtx (DImode);
|
temp3 = gen_reg_rtx (DImode);
|
temp4 = gen_reg_rtx (DImode);
|
temp4 = gen_reg_rtx (DImode);
|
temp5 = gen_reg_rtx (DImode);
|
temp5 = gen_reg_rtx (DImode);
|
}
|
}
|
|
|
emit_insn (gen_embmedany_textuhi (temp1, op1));
|
emit_insn (gen_embmedany_textuhi (temp1, op1));
|
emit_insn (gen_embmedany_texthi (temp2, op1));
|
emit_insn (gen_embmedany_texthi (temp2, op1));
|
emit_insn (gen_embmedany_textulo (temp3, temp1, op1));
|
emit_insn (gen_embmedany_textulo (temp3, temp1, op1));
|
emit_insn (gen_rtx_SET (VOIDmode, temp4,
|
emit_insn (gen_rtx_SET (VOIDmode, temp4,
|
gen_rtx_ASHIFT (DImode, temp3, GEN_INT (32))));
|
gen_rtx_ASHIFT (DImode, temp3, GEN_INT (32))));
|
emit_insn (gen_rtx_SET (VOIDmode, temp5,
|
emit_insn (gen_rtx_SET (VOIDmode, temp5,
|
gen_rtx_PLUS (DImode, temp4, temp2)));
|
gen_rtx_PLUS (DImode, temp4, temp2)));
|
emit_insn (gen_embmedany_textlo (op0, temp5, op1));
|
emit_insn (gen_embmedany_textlo (op0, temp5, op1));
|
}
|
}
|
break;
|
break;
|
|
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
}
|
}
|
|
|
#if HOST_BITS_PER_WIDE_INT == 32
|
#if HOST_BITS_PER_WIDE_INT == 32
|
void
|
void
|
sparc_emit_set_const64 (rtx op0 ATTRIBUTE_UNUSED, rtx op1 ATTRIBUTE_UNUSED)
|
sparc_emit_set_const64 (rtx op0 ATTRIBUTE_UNUSED, rtx op1 ATTRIBUTE_UNUSED)
|
{
|
{
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
#else
|
#else
|
/* These avoid problems when cross compiling. If we do not
|
/* These avoid problems when cross compiling. If we do not
|
go through all this hair then the optimizer will see
|
go through all this hair then the optimizer will see
|
invalid REG_EQUAL notes or in some cases none at all. */
|
invalid REG_EQUAL notes or in some cases none at all. */
|
static rtx gen_safe_HIGH64 (rtx, HOST_WIDE_INT);
|
static rtx gen_safe_HIGH64 (rtx, HOST_WIDE_INT);
|
static rtx gen_safe_SET64 (rtx, HOST_WIDE_INT);
|
static rtx gen_safe_SET64 (rtx, HOST_WIDE_INT);
|
static rtx gen_safe_OR64 (rtx, HOST_WIDE_INT);
|
static rtx gen_safe_OR64 (rtx, HOST_WIDE_INT);
|
static rtx gen_safe_XOR64 (rtx, HOST_WIDE_INT);
|
static rtx gen_safe_XOR64 (rtx, HOST_WIDE_INT);
|
|
|
/* The optimizer is not to assume anything about exactly
|
/* The optimizer is not to assume anything about exactly
|
which bits are set for a HIGH, they are unspecified.
|
which bits are set for a HIGH, they are unspecified.
|
Unfortunately this leads to many missed optimizations
|
Unfortunately this leads to many missed optimizations
|
during CSE. We mask out the non-HIGH bits, and matches
|
during CSE. We mask out the non-HIGH bits, and matches
|
a plain movdi, to alleviate this problem. */
|
a plain movdi, to alleviate this problem. */
|
static rtx
|
static rtx
|
gen_safe_HIGH64 (rtx dest, HOST_WIDE_INT val)
|
gen_safe_HIGH64 (rtx dest, HOST_WIDE_INT val)
|
{
|
{
|
return gen_rtx_SET (VOIDmode, dest, GEN_INT (val & ~(HOST_WIDE_INT)0x3ff));
|
return gen_rtx_SET (VOIDmode, dest, GEN_INT (val & ~(HOST_WIDE_INT)0x3ff));
|
}
|
}
|
|
|
static rtx
|
static rtx
|
gen_safe_SET64 (rtx dest, HOST_WIDE_INT val)
|
gen_safe_SET64 (rtx dest, HOST_WIDE_INT val)
|
{
|
{
|
return gen_rtx_SET (VOIDmode, dest, GEN_INT (val));
|
return gen_rtx_SET (VOIDmode, dest, GEN_INT (val));
|
}
|
}
|
|
|
static rtx
|
static rtx
|
gen_safe_OR64 (rtx src, HOST_WIDE_INT val)
|
gen_safe_OR64 (rtx src, HOST_WIDE_INT val)
|
{
|
{
|
return gen_rtx_IOR (DImode, src, GEN_INT (val));
|
return gen_rtx_IOR (DImode, src, GEN_INT (val));
|
}
|
}
|
|
|
static rtx
|
static rtx
|
gen_safe_XOR64 (rtx src, HOST_WIDE_INT val)
|
gen_safe_XOR64 (rtx src, HOST_WIDE_INT val)
|
{
|
{
|
return gen_rtx_XOR (DImode, src, GEN_INT (val));
|
return gen_rtx_XOR (DImode, src, GEN_INT (val));
|
}
|
}
|
|
|
/* Worker routines for 64-bit constant formation on arch64.
|
/* Worker routines for 64-bit constant formation on arch64.
|
One of the key things to be doing in these emissions is
|
One of the key things to be doing in these emissions is
|
to create as many temp REGs as possible. This makes it
|
to create as many temp REGs as possible. This makes it
|
possible for half-built constants to be used later when
|
possible for half-built constants to be used later when
|
such values are similar to something required later on.
|
such values are similar to something required later on.
|
Without doing this, the optimizer cannot see such
|
Without doing this, the optimizer cannot see such
|
opportunities. */
|
opportunities. */
|
|
|
static void sparc_emit_set_const64_quick1 (rtx, rtx,
|
static void sparc_emit_set_const64_quick1 (rtx, rtx,
|
unsigned HOST_WIDE_INT, int);
|
unsigned HOST_WIDE_INT, int);
|
|
|
static void
|
static void
|
sparc_emit_set_const64_quick1 (rtx op0, rtx temp,
|
sparc_emit_set_const64_quick1 (rtx op0, rtx temp,
|
unsigned HOST_WIDE_INT low_bits, int is_neg)
|
unsigned HOST_WIDE_INT low_bits, int is_neg)
|
{
|
{
|
unsigned HOST_WIDE_INT high_bits;
|
unsigned HOST_WIDE_INT high_bits;
|
|
|
if (is_neg)
|
if (is_neg)
|
high_bits = (~low_bits) & 0xffffffff;
|
high_bits = (~low_bits) & 0xffffffff;
|
else
|
else
|
high_bits = low_bits;
|
high_bits = low_bits;
|
|
|
emit_insn (gen_safe_HIGH64 (temp, high_bits));
|
emit_insn (gen_safe_HIGH64 (temp, high_bits));
|
if (!is_neg)
|
if (!is_neg)
|
{
|
{
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
gen_safe_OR64 (temp, (high_bits & 0x3ff))));
|
gen_safe_OR64 (temp, (high_bits & 0x3ff))));
|
}
|
}
|
else
|
else
|
{
|
{
|
/* If we are XOR'ing with -1, then we should emit a one's complement
|
/* If we are XOR'ing with -1, then we should emit a one's complement
|
instead. This way the combiner will notice logical operations
|
instead. This way the combiner will notice logical operations
|
such as ANDN later on and substitute. */
|
such as ANDN later on and substitute. */
|
if ((low_bits & 0x3ff) == 0x3ff)
|
if ((low_bits & 0x3ff) == 0x3ff)
|
{
|
{
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
gen_rtx_NOT (DImode, temp)));
|
gen_rtx_NOT (DImode, temp)));
|
}
|
}
|
else
|
else
|
{
|
{
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
gen_safe_XOR64 (temp,
|
gen_safe_XOR64 (temp,
|
(-(HOST_WIDE_INT)0x400
|
(-(HOST_WIDE_INT)0x400
|
| (low_bits & 0x3ff)))));
|
| (low_bits & 0x3ff)))));
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
static void sparc_emit_set_const64_quick2 (rtx, rtx, unsigned HOST_WIDE_INT,
|
static void sparc_emit_set_const64_quick2 (rtx, rtx, unsigned HOST_WIDE_INT,
|
unsigned HOST_WIDE_INT, int);
|
unsigned HOST_WIDE_INT, int);
|
|
|
static void
|
static void
|
sparc_emit_set_const64_quick2 (rtx op0, rtx temp,
|
sparc_emit_set_const64_quick2 (rtx op0, rtx temp,
|
unsigned HOST_WIDE_INT high_bits,
|
unsigned HOST_WIDE_INT high_bits,
|
unsigned HOST_WIDE_INT low_immediate,
|
unsigned HOST_WIDE_INT low_immediate,
|
int shift_count)
|
int shift_count)
|
{
|
{
|
rtx temp2 = op0;
|
rtx temp2 = op0;
|
|
|
if ((high_bits & 0xfffffc00) != 0)
|
if ((high_bits & 0xfffffc00) != 0)
|
{
|
{
|
emit_insn (gen_safe_HIGH64 (temp, high_bits));
|
emit_insn (gen_safe_HIGH64 (temp, high_bits));
|
if ((high_bits & ~0xfffffc00) != 0)
|
if ((high_bits & ~0xfffffc00) != 0)
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
gen_safe_OR64 (temp, (high_bits & 0x3ff))));
|
gen_safe_OR64 (temp, (high_bits & 0x3ff))));
|
else
|
else
|
temp2 = temp;
|
temp2 = temp;
|
}
|
}
|
else
|
else
|
{
|
{
|
emit_insn (gen_safe_SET64 (temp, high_bits));
|
emit_insn (gen_safe_SET64 (temp, high_bits));
|
temp2 = temp;
|
temp2 = temp;
|
}
|
}
|
|
|
/* Now shift it up into place. */
|
/* Now shift it up into place. */
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
gen_rtx_ASHIFT (DImode, temp2,
|
gen_rtx_ASHIFT (DImode, temp2,
|
GEN_INT (shift_count))));
|
GEN_INT (shift_count))));
|
|
|
/* If there is a low immediate part piece, finish up by
|
/* If there is a low immediate part piece, finish up by
|
putting that in as well. */
|
putting that in as well. */
|
if (low_immediate != 0)
|
if (low_immediate != 0)
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
gen_safe_OR64 (op0, low_immediate)));
|
gen_safe_OR64 (op0, low_immediate)));
|
}
|
}
|
|
|
static void sparc_emit_set_const64_longway (rtx, rtx, unsigned HOST_WIDE_INT,
|
static void sparc_emit_set_const64_longway (rtx, rtx, unsigned HOST_WIDE_INT,
|
unsigned HOST_WIDE_INT);
|
unsigned HOST_WIDE_INT);
|
|
|
/* Full 64-bit constant decomposition. Even though this is the
|
/* Full 64-bit constant decomposition. Even though this is the
|
'worst' case, we still optimize a few things away. */
|
'worst' case, we still optimize a few things away. */
|
static void
|
static void
|
sparc_emit_set_const64_longway (rtx op0, rtx temp,
|
sparc_emit_set_const64_longway (rtx op0, rtx temp,
|
unsigned HOST_WIDE_INT high_bits,
|
unsigned HOST_WIDE_INT high_bits,
|
unsigned HOST_WIDE_INT low_bits)
|
unsigned HOST_WIDE_INT low_bits)
|
{
|
{
|
rtx sub_temp;
|
rtx sub_temp;
|
|
|
if (reload_in_progress || reload_completed)
|
if (reload_in_progress || reload_completed)
|
sub_temp = op0;
|
sub_temp = op0;
|
else
|
else
|
sub_temp = gen_reg_rtx (DImode);
|
sub_temp = gen_reg_rtx (DImode);
|
|
|
if ((high_bits & 0xfffffc00) != 0)
|
if ((high_bits & 0xfffffc00) != 0)
|
{
|
{
|
emit_insn (gen_safe_HIGH64 (temp, high_bits));
|
emit_insn (gen_safe_HIGH64 (temp, high_bits));
|
if ((high_bits & ~0xfffffc00) != 0)
|
if ((high_bits & ~0xfffffc00) != 0)
|
emit_insn (gen_rtx_SET (VOIDmode,
|
emit_insn (gen_rtx_SET (VOIDmode,
|
sub_temp,
|
sub_temp,
|
gen_safe_OR64 (temp, (high_bits & 0x3ff))));
|
gen_safe_OR64 (temp, (high_bits & 0x3ff))));
|
else
|
else
|
sub_temp = temp;
|
sub_temp = temp;
|
}
|
}
|
else
|
else
|
{
|
{
|
emit_insn (gen_safe_SET64 (temp, high_bits));
|
emit_insn (gen_safe_SET64 (temp, high_bits));
|
sub_temp = temp;
|
sub_temp = temp;
|
}
|
}
|
|
|
if (!reload_in_progress && !reload_completed)
|
if (!reload_in_progress && !reload_completed)
|
{
|
{
|
rtx temp2 = gen_reg_rtx (DImode);
|
rtx temp2 = gen_reg_rtx (DImode);
|
rtx temp3 = gen_reg_rtx (DImode);
|
rtx temp3 = gen_reg_rtx (DImode);
|
rtx temp4 = gen_reg_rtx (DImode);
|
rtx temp4 = gen_reg_rtx (DImode);
|
|
|
emit_insn (gen_rtx_SET (VOIDmode, temp4,
|
emit_insn (gen_rtx_SET (VOIDmode, temp4,
|
gen_rtx_ASHIFT (DImode, sub_temp,
|
gen_rtx_ASHIFT (DImode, sub_temp,
|
GEN_INT (32))));
|
GEN_INT (32))));
|
|
|
emit_insn (gen_safe_HIGH64 (temp2, low_bits));
|
emit_insn (gen_safe_HIGH64 (temp2, low_bits));
|
if ((low_bits & ~0xfffffc00) != 0)
|
if ((low_bits & ~0xfffffc00) != 0)
|
{
|
{
|
emit_insn (gen_rtx_SET (VOIDmode, temp3,
|
emit_insn (gen_rtx_SET (VOIDmode, temp3,
|
gen_safe_OR64 (temp2, (low_bits & 0x3ff))));
|
gen_safe_OR64 (temp2, (low_bits & 0x3ff))));
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
gen_rtx_PLUS (DImode, temp4, temp3)));
|
gen_rtx_PLUS (DImode, temp4, temp3)));
|
}
|
}
|
else
|
else
|
{
|
{
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
gen_rtx_PLUS (DImode, temp4, temp2)));
|
gen_rtx_PLUS (DImode, temp4, temp2)));
|
}
|
}
|
}
|
}
|
else
|
else
|
{
|
{
|
rtx low1 = GEN_INT ((low_bits >> (32 - 12)) & 0xfff);
|
rtx low1 = GEN_INT ((low_bits >> (32 - 12)) & 0xfff);
|
rtx low2 = GEN_INT ((low_bits >> (32 - 12 - 12)) & 0xfff);
|
rtx low2 = GEN_INT ((low_bits >> (32 - 12 - 12)) & 0xfff);
|
rtx low3 = GEN_INT ((low_bits >> (32 - 12 - 12 - 8)) & 0x0ff);
|
rtx low3 = GEN_INT ((low_bits >> (32 - 12 - 12 - 8)) & 0x0ff);
|
int to_shift = 12;
|
int to_shift = 12;
|
|
|
/* We are in the middle of reload, so this is really
|
/* We are in the middle of reload, so this is really
|
painful. However we do still make an attempt to
|
painful. However we do still make an attempt to
|
avoid emitting truly stupid code. */
|
avoid emitting truly stupid code. */
|
if (low1 != const0_rtx)
|
if (low1 != const0_rtx)
|
{
|
{
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
gen_rtx_ASHIFT (DImode, sub_temp,
|
gen_rtx_ASHIFT (DImode, sub_temp,
|
GEN_INT (to_shift))));
|
GEN_INT (to_shift))));
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
gen_rtx_IOR (DImode, op0, low1)));
|
gen_rtx_IOR (DImode, op0, low1)));
|
sub_temp = op0;
|
sub_temp = op0;
|
to_shift = 12;
|
to_shift = 12;
|
}
|
}
|
else
|
else
|
{
|
{
|
to_shift += 12;
|
to_shift += 12;
|
}
|
}
|
if (low2 != const0_rtx)
|
if (low2 != const0_rtx)
|
{
|
{
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
gen_rtx_ASHIFT (DImode, sub_temp,
|
gen_rtx_ASHIFT (DImode, sub_temp,
|
GEN_INT (to_shift))));
|
GEN_INT (to_shift))));
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
gen_rtx_IOR (DImode, op0, low2)));
|
gen_rtx_IOR (DImode, op0, low2)));
|
sub_temp = op0;
|
sub_temp = op0;
|
to_shift = 8;
|
to_shift = 8;
|
}
|
}
|
else
|
else
|
{
|
{
|
to_shift += 8;
|
to_shift += 8;
|
}
|
}
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
gen_rtx_ASHIFT (DImode, sub_temp,
|
gen_rtx_ASHIFT (DImode, sub_temp,
|
GEN_INT (to_shift))));
|
GEN_INT (to_shift))));
|
if (low3 != const0_rtx)
|
if (low3 != const0_rtx)
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
gen_rtx_IOR (DImode, op0, low3)));
|
gen_rtx_IOR (DImode, op0, low3)));
|
/* phew... */
|
/* phew... */
|
}
|
}
|
}
|
}
|
|
|
/* Analyze a 64-bit constant for certain properties. */
|
/* Analyze a 64-bit constant for certain properties. */
|
static void analyze_64bit_constant (unsigned HOST_WIDE_INT,
|
static void analyze_64bit_constant (unsigned HOST_WIDE_INT,
|
unsigned HOST_WIDE_INT,
|
unsigned HOST_WIDE_INT,
|
int *, int *, int *);
|
int *, int *, int *);
|
|
|
static void
|
static void
|
analyze_64bit_constant (unsigned HOST_WIDE_INT high_bits,
|
analyze_64bit_constant (unsigned HOST_WIDE_INT high_bits,
|
unsigned HOST_WIDE_INT low_bits,
|
unsigned HOST_WIDE_INT low_bits,
|
int *hbsp, int *lbsp, int *abbasp)
|
int *hbsp, int *lbsp, int *abbasp)
|
{
|
{
|
int lowest_bit_set, highest_bit_set, all_bits_between_are_set;
|
int lowest_bit_set, highest_bit_set, all_bits_between_are_set;
|
int i;
|
int i;
|
|
|
lowest_bit_set = highest_bit_set = -1;
|
lowest_bit_set = highest_bit_set = -1;
|
i = 0;
|
i = 0;
|
do
|
do
|
{
|
{
|
if ((lowest_bit_set == -1)
|
if ((lowest_bit_set == -1)
|
&& ((low_bits >> i) & 1))
|
&& ((low_bits >> i) & 1))
|
lowest_bit_set = i;
|
lowest_bit_set = i;
|
if ((highest_bit_set == -1)
|
if ((highest_bit_set == -1)
|
&& ((high_bits >> (32 - i - 1)) & 1))
|
&& ((high_bits >> (32 - i - 1)) & 1))
|
highest_bit_set = (64 - i - 1);
|
highest_bit_set = (64 - i - 1);
|
}
|
}
|
while (++i < 32
|
while (++i < 32
|
&& ((highest_bit_set == -1)
|
&& ((highest_bit_set == -1)
|
|| (lowest_bit_set == -1)));
|
|| (lowest_bit_set == -1)));
|
if (i == 32)
|
if (i == 32)
|
{
|
{
|
i = 0;
|
i = 0;
|
do
|
do
|
{
|
{
|
if ((lowest_bit_set == -1)
|
if ((lowest_bit_set == -1)
|
&& ((high_bits >> i) & 1))
|
&& ((high_bits >> i) & 1))
|
lowest_bit_set = i + 32;
|
lowest_bit_set = i + 32;
|
if ((highest_bit_set == -1)
|
if ((highest_bit_set == -1)
|
&& ((low_bits >> (32 - i - 1)) & 1))
|
&& ((low_bits >> (32 - i - 1)) & 1))
|
highest_bit_set = 32 - i - 1;
|
highest_bit_set = 32 - i - 1;
|
}
|
}
|
while (++i < 32
|
while (++i < 32
|
&& ((highest_bit_set == -1)
|
&& ((highest_bit_set == -1)
|
|| (lowest_bit_set == -1)));
|
|| (lowest_bit_set == -1)));
|
}
|
}
|
/* If there are no bits set this should have gone out
|
/* If there are no bits set this should have gone out
|
as one instruction! */
|
as one instruction! */
|
gcc_assert (lowest_bit_set != -1 && highest_bit_set != -1);
|
gcc_assert (lowest_bit_set != -1 && highest_bit_set != -1);
|
all_bits_between_are_set = 1;
|
all_bits_between_are_set = 1;
|
for (i = lowest_bit_set; i <= highest_bit_set; i++)
|
for (i = lowest_bit_set; i <= highest_bit_set; i++)
|
{
|
{
|
if (i < 32)
|
if (i < 32)
|
{
|
{
|
if ((low_bits & (1 << i)) != 0)
|
if ((low_bits & (1 << i)) != 0)
|
continue;
|
continue;
|
}
|
}
|
else
|
else
|
{
|
{
|
if ((high_bits & (1 << (i - 32))) != 0)
|
if ((high_bits & (1 << (i - 32))) != 0)
|
continue;
|
continue;
|
}
|
}
|
all_bits_between_are_set = 0;
|
all_bits_between_are_set = 0;
|
break;
|
break;
|
}
|
}
|
*hbsp = highest_bit_set;
|
*hbsp = highest_bit_set;
|
*lbsp = lowest_bit_set;
|
*lbsp = lowest_bit_set;
|
*abbasp = all_bits_between_are_set;
|
*abbasp = all_bits_between_are_set;
|
}
|
}
|
|
|
static int const64_is_2insns (unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT);
|
static int const64_is_2insns (unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT);
|
|
|
static int
|
static int
|
const64_is_2insns (unsigned HOST_WIDE_INT high_bits,
|
const64_is_2insns (unsigned HOST_WIDE_INT high_bits,
|
unsigned HOST_WIDE_INT low_bits)
|
unsigned HOST_WIDE_INT low_bits)
|
{
|
{
|
int highest_bit_set, lowest_bit_set, all_bits_between_are_set;
|
int highest_bit_set, lowest_bit_set, all_bits_between_are_set;
|
|
|
if (high_bits == 0
|
if (high_bits == 0
|
|| high_bits == 0xffffffff)
|
|| high_bits == 0xffffffff)
|
return 1;
|
return 1;
|
|
|
analyze_64bit_constant (high_bits, low_bits,
|
analyze_64bit_constant (high_bits, low_bits,
|
&highest_bit_set, &lowest_bit_set,
|
&highest_bit_set, &lowest_bit_set,
|
&all_bits_between_are_set);
|
&all_bits_between_are_set);
|
|
|
if ((highest_bit_set == 63
|
if ((highest_bit_set == 63
|
|| lowest_bit_set == 0)
|
|| lowest_bit_set == 0)
|
&& all_bits_between_are_set != 0)
|
&& all_bits_between_are_set != 0)
|
return 1;
|
return 1;
|
|
|
if ((highest_bit_set - lowest_bit_set) < 21)
|
if ((highest_bit_set - lowest_bit_set) < 21)
|
return 1;
|
return 1;
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
static unsigned HOST_WIDE_INT create_simple_focus_bits (unsigned HOST_WIDE_INT,
|
static unsigned HOST_WIDE_INT create_simple_focus_bits (unsigned HOST_WIDE_INT,
|
unsigned HOST_WIDE_INT,
|
unsigned HOST_WIDE_INT,
|
int, int);
|
int, int);
|
|
|
static unsigned HOST_WIDE_INT
|
static unsigned HOST_WIDE_INT
|
create_simple_focus_bits (unsigned HOST_WIDE_INT high_bits,
|
create_simple_focus_bits (unsigned HOST_WIDE_INT high_bits,
|
unsigned HOST_WIDE_INT low_bits,
|
unsigned HOST_WIDE_INT low_bits,
|
int lowest_bit_set, int shift)
|
int lowest_bit_set, int shift)
|
{
|
{
|
HOST_WIDE_INT hi, lo;
|
HOST_WIDE_INT hi, lo;
|
|
|
if (lowest_bit_set < 32)
|
if (lowest_bit_set < 32)
|
{
|
{
|
lo = (low_bits >> lowest_bit_set) << shift;
|
lo = (low_bits >> lowest_bit_set) << shift;
|
hi = ((high_bits << (32 - lowest_bit_set)) << shift);
|
hi = ((high_bits << (32 - lowest_bit_set)) << shift);
|
}
|
}
|
else
|
else
|
{
|
{
|
lo = 0;
|
lo = 0;
|
hi = ((high_bits >> (lowest_bit_set - 32)) << shift);
|
hi = ((high_bits >> (lowest_bit_set - 32)) << shift);
|
}
|
}
|
gcc_assert (! (hi & lo));
|
gcc_assert (! (hi & lo));
|
return (hi | lo);
|
return (hi | lo);
|
}
|
}
|
|
|
/* Here we are sure to be arch64 and this is an integer constant
|
/* Here we are sure to be arch64 and this is an integer constant
|
being loaded into a register. Emit the most efficient
|
being loaded into a register. Emit the most efficient
|
insn sequence possible. Detection of all the 1-insn cases
|
insn sequence possible. Detection of all the 1-insn cases
|
has been done already. */
|
has been done already. */
|
void
|
void
|
sparc_emit_set_const64 (rtx op0, rtx op1)
|
sparc_emit_set_const64 (rtx op0, rtx op1)
|
{
|
{
|
unsigned HOST_WIDE_INT high_bits, low_bits;
|
unsigned HOST_WIDE_INT high_bits, low_bits;
|
int lowest_bit_set, highest_bit_set;
|
int lowest_bit_set, highest_bit_set;
|
int all_bits_between_are_set;
|
int all_bits_between_are_set;
|
rtx temp = 0;
|
rtx temp = 0;
|
|
|
/* Sanity check that we know what we are working with. */
|
/* Sanity check that we know what we are working with. */
|
gcc_assert (TARGET_ARCH64
|
gcc_assert (TARGET_ARCH64
|
&& (GET_CODE (op0) == SUBREG
|
&& (GET_CODE (op0) == SUBREG
|
|| (REG_P (op0) && ! SPARC_FP_REG_P (REGNO (op0)))));
|
|| (REG_P (op0) && ! SPARC_FP_REG_P (REGNO (op0)))));
|
|
|
if (reload_in_progress || reload_completed)
|
if (reload_in_progress || reload_completed)
|
temp = op0;
|
temp = op0;
|
|
|
if (GET_CODE (op1) != CONST_INT)
|
if (GET_CODE (op1) != CONST_INT)
|
{
|
{
|
sparc_emit_set_symbolic_const64 (op0, op1, temp);
|
sparc_emit_set_symbolic_const64 (op0, op1, temp);
|
return;
|
return;
|
}
|
}
|
|
|
if (! temp)
|
if (! temp)
|
temp = gen_reg_rtx (DImode);
|
temp = gen_reg_rtx (DImode);
|
|
|
high_bits = ((INTVAL (op1) >> 32) & 0xffffffff);
|
high_bits = ((INTVAL (op1) >> 32) & 0xffffffff);
|
low_bits = (INTVAL (op1) & 0xffffffff);
|
low_bits = (INTVAL (op1) & 0xffffffff);
|
|
|
/* low_bits bits 0 --> 31
|
/* low_bits bits 0 --> 31
|
high_bits bits 32 --> 63 */
|
high_bits bits 32 --> 63 */
|
|
|
analyze_64bit_constant (high_bits, low_bits,
|
analyze_64bit_constant (high_bits, low_bits,
|
&highest_bit_set, &lowest_bit_set,
|
&highest_bit_set, &lowest_bit_set,
|
&all_bits_between_are_set);
|
&all_bits_between_are_set);
|
|
|
/* First try for a 2-insn sequence. */
|
/* First try for a 2-insn sequence. */
|
|
|
/* These situations are preferred because the optimizer can
|
/* These situations are preferred because the optimizer can
|
* do more things with them:
|
* do more things with them:
|
* 1) mov -1, %reg
|
* 1) mov -1, %reg
|
* sllx %reg, shift, %reg
|
* sllx %reg, shift, %reg
|
* 2) mov -1, %reg
|
* 2) mov -1, %reg
|
* srlx %reg, shift, %reg
|
* srlx %reg, shift, %reg
|
* 3) mov some_small_const, %reg
|
* 3) mov some_small_const, %reg
|
* sllx %reg, shift, %reg
|
* sllx %reg, shift, %reg
|
*/
|
*/
|
if (((highest_bit_set == 63
|
if (((highest_bit_set == 63
|
|| lowest_bit_set == 0)
|
|| lowest_bit_set == 0)
|
&& all_bits_between_are_set != 0)
|
&& all_bits_between_are_set != 0)
|
|| ((highest_bit_set - lowest_bit_set) < 12))
|
|| ((highest_bit_set - lowest_bit_set) < 12))
|
{
|
{
|
HOST_WIDE_INT the_const = -1;
|
HOST_WIDE_INT the_const = -1;
|
int shift = lowest_bit_set;
|
int shift = lowest_bit_set;
|
|
|
if ((highest_bit_set != 63
|
if ((highest_bit_set != 63
|
&& lowest_bit_set != 0)
|
&& lowest_bit_set != 0)
|
|| all_bits_between_are_set == 0)
|
|| all_bits_between_are_set == 0)
|
{
|
{
|
the_const =
|
the_const =
|
create_simple_focus_bits (high_bits, low_bits,
|
create_simple_focus_bits (high_bits, low_bits,
|
lowest_bit_set, 0);
|
lowest_bit_set, 0);
|
}
|
}
|
else if (lowest_bit_set == 0)
|
else if (lowest_bit_set == 0)
|
shift = -(63 - highest_bit_set);
|
shift = -(63 - highest_bit_set);
|
|
|
gcc_assert (SPARC_SIMM13_P (the_const));
|
gcc_assert (SPARC_SIMM13_P (the_const));
|
gcc_assert (shift != 0);
|
gcc_assert (shift != 0);
|
|
|
emit_insn (gen_safe_SET64 (temp, the_const));
|
emit_insn (gen_safe_SET64 (temp, the_const));
|
if (shift > 0)
|
if (shift > 0)
|
emit_insn (gen_rtx_SET (VOIDmode,
|
emit_insn (gen_rtx_SET (VOIDmode,
|
op0,
|
op0,
|
gen_rtx_ASHIFT (DImode,
|
gen_rtx_ASHIFT (DImode,
|
temp,
|
temp,
|
GEN_INT (shift))));
|
GEN_INT (shift))));
|
else if (shift < 0)
|
else if (shift < 0)
|
emit_insn (gen_rtx_SET (VOIDmode,
|
emit_insn (gen_rtx_SET (VOIDmode,
|
op0,
|
op0,
|
gen_rtx_LSHIFTRT (DImode,
|
gen_rtx_LSHIFTRT (DImode,
|
temp,
|
temp,
|
GEN_INT (-shift))));
|
GEN_INT (-shift))));
|
return;
|
return;
|
}
|
}
|
|
|
/* Now a range of 22 or less bits set somewhere.
|
/* Now a range of 22 or less bits set somewhere.
|
* 1) sethi %hi(focus_bits), %reg
|
* 1) sethi %hi(focus_bits), %reg
|
* sllx %reg, shift, %reg
|
* sllx %reg, shift, %reg
|
* 2) sethi %hi(focus_bits), %reg
|
* 2) sethi %hi(focus_bits), %reg
|
* srlx %reg, shift, %reg
|
* srlx %reg, shift, %reg
|
*/
|
*/
|
if ((highest_bit_set - lowest_bit_set) < 21)
|
if ((highest_bit_set - lowest_bit_set) < 21)
|
{
|
{
|
unsigned HOST_WIDE_INT focus_bits =
|
unsigned HOST_WIDE_INT focus_bits =
|
create_simple_focus_bits (high_bits, low_bits,
|
create_simple_focus_bits (high_bits, low_bits,
|
lowest_bit_set, 10);
|
lowest_bit_set, 10);
|
|
|
gcc_assert (SPARC_SETHI_P (focus_bits));
|
gcc_assert (SPARC_SETHI_P (focus_bits));
|
gcc_assert (lowest_bit_set != 10);
|
gcc_assert (lowest_bit_set != 10);
|
|
|
emit_insn (gen_safe_HIGH64 (temp, focus_bits));
|
emit_insn (gen_safe_HIGH64 (temp, focus_bits));
|
|
|
/* If lowest_bit_set == 10 then a sethi alone could have done it. */
|
/* If lowest_bit_set == 10 then a sethi alone could have done it. */
|
if (lowest_bit_set < 10)
|
if (lowest_bit_set < 10)
|
emit_insn (gen_rtx_SET (VOIDmode,
|
emit_insn (gen_rtx_SET (VOIDmode,
|
op0,
|
op0,
|
gen_rtx_LSHIFTRT (DImode, temp,
|
gen_rtx_LSHIFTRT (DImode, temp,
|
GEN_INT (10 - lowest_bit_set))));
|
GEN_INT (10 - lowest_bit_set))));
|
else if (lowest_bit_set > 10)
|
else if (lowest_bit_set > 10)
|
emit_insn (gen_rtx_SET (VOIDmode,
|
emit_insn (gen_rtx_SET (VOIDmode,
|
op0,
|
op0,
|
gen_rtx_ASHIFT (DImode, temp,
|
gen_rtx_ASHIFT (DImode, temp,
|
GEN_INT (lowest_bit_set - 10))));
|
GEN_INT (lowest_bit_set - 10))));
|
return;
|
return;
|
}
|
}
|
|
|
/* 1) sethi %hi(low_bits), %reg
|
/* 1) sethi %hi(low_bits), %reg
|
* or %reg, %lo(low_bits), %reg
|
* or %reg, %lo(low_bits), %reg
|
* 2) sethi %hi(~low_bits), %reg
|
* 2) sethi %hi(~low_bits), %reg
|
* xor %reg, %lo(-0x400 | (low_bits & 0x3ff)), %reg
|
* xor %reg, %lo(-0x400 | (low_bits & 0x3ff)), %reg
|
*/
|
*/
|
if (high_bits == 0
|
if (high_bits == 0
|
|| high_bits == 0xffffffff)
|
|| high_bits == 0xffffffff)
|
{
|
{
|
sparc_emit_set_const64_quick1 (op0, temp, low_bits,
|
sparc_emit_set_const64_quick1 (op0, temp, low_bits,
|
(high_bits == 0xffffffff));
|
(high_bits == 0xffffffff));
|
return;
|
return;
|
}
|
}
|
|
|
/* Now, try 3-insn sequences. */
|
/* Now, try 3-insn sequences. */
|
|
|
/* 1) sethi %hi(high_bits), %reg
|
/* 1) sethi %hi(high_bits), %reg
|
* or %reg, %lo(high_bits), %reg
|
* or %reg, %lo(high_bits), %reg
|
* sllx %reg, 32, %reg
|
* sllx %reg, 32, %reg
|
*/
|
*/
|
if (low_bits == 0)
|
if (low_bits == 0)
|
{
|
{
|
sparc_emit_set_const64_quick2 (op0, temp, high_bits, 0, 32);
|
sparc_emit_set_const64_quick2 (op0, temp, high_bits, 0, 32);
|
return;
|
return;
|
}
|
}
|
|
|
/* We may be able to do something quick
|
/* We may be able to do something quick
|
when the constant is negated, so try that. */
|
when the constant is negated, so try that. */
|
if (const64_is_2insns ((~high_bits) & 0xffffffff,
|
if (const64_is_2insns ((~high_bits) & 0xffffffff,
|
(~low_bits) & 0xfffffc00))
|
(~low_bits) & 0xfffffc00))
|
{
|
{
|
/* NOTE: The trailing bits get XOR'd so we need the
|
/* NOTE: The trailing bits get XOR'd so we need the
|
non-negated bits, not the negated ones. */
|
non-negated bits, not the negated ones. */
|
unsigned HOST_WIDE_INT trailing_bits = low_bits & 0x3ff;
|
unsigned HOST_WIDE_INT trailing_bits = low_bits & 0x3ff;
|
|
|
if ((((~high_bits) & 0xffffffff) == 0
|
if ((((~high_bits) & 0xffffffff) == 0
|
&& ((~low_bits) & 0x80000000) == 0)
|
&& ((~low_bits) & 0x80000000) == 0)
|
|| (((~high_bits) & 0xffffffff) == 0xffffffff
|
|| (((~high_bits) & 0xffffffff) == 0xffffffff
|
&& ((~low_bits) & 0x80000000) != 0))
|
&& ((~low_bits) & 0x80000000) != 0))
|
{
|
{
|
unsigned HOST_WIDE_INT fast_int = (~low_bits & 0xffffffff);
|
unsigned HOST_WIDE_INT fast_int = (~low_bits & 0xffffffff);
|
|
|
if ((SPARC_SETHI_P (fast_int)
|
if ((SPARC_SETHI_P (fast_int)
|
&& (~high_bits & 0xffffffff) == 0)
|
&& (~high_bits & 0xffffffff) == 0)
|
|| SPARC_SIMM13_P (fast_int))
|
|| SPARC_SIMM13_P (fast_int))
|
emit_insn (gen_safe_SET64 (temp, fast_int));
|
emit_insn (gen_safe_SET64 (temp, fast_int));
|
else
|
else
|
sparc_emit_set_const64 (temp, GEN_INT (fast_int));
|
sparc_emit_set_const64 (temp, GEN_INT (fast_int));
|
}
|
}
|
else
|
else
|
{
|
{
|
rtx negated_const;
|
rtx negated_const;
|
negated_const = GEN_INT (((~low_bits) & 0xfffffc00) |
|
negated_const = GEN_INT (((~low_bits) & 0xfffffc00) |
|
(((HOST_WIDE_INT)((~high_bits) & 0xffffffff))<<32));
|
(((HOST_WIDE_INT)((~high_bits) & 0xffffffff))<<32));
|
sparc_emit_set_const64 (temp, negated_const);
|
sparc_emit_set_const64 (temp, negated_const);
|
}
|
}
|
|
|
/* If we are XOR'ing with -1, then we should emit a one's complement
|
/* If we are XOR'ing with -1, then we should emit a one's complement
|
instead. This way the combiner will notice logical operations
|
instead. This way the combiner will notice logical operations
|
such as ANDN later on and substitute. */
|
such as ANDN later on and substitute. */
|
if (trailing_bits == 0x3ff)
|
if (trailing_bits == 0x3ff)
|
{
|
{
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
emit_insn (gen_rtx_SET (VOIDmode, op0,
|
gen_rtx_NOT (DImode, temp)));
|
gen_rtx_NOT (DImode, temp)));
|
}
|
}
|
else
|
else
|
{
|
{
|
emit_insn (gen_rtx_SET (VOIDmode,
|
emit_insn (gen_rtx_SET (VOIDmode,
|
op0,
|
op0,
|
gen_safe_XOR64 (temp,
|
gen_safe_XOR64 (temp,
|
(-0x400 | trailing_bits))));
|
(-0x400 | trailing_bits))));
|
}
|
}
|
return;
|
return;
|
}
|
}
|
|
|
/* 1) sethi %hi(xxx), %reg
|
/* 1) sethi %hi(xxx), %reg
|
* or %reg, %lo(xxx), %reg
|
* or %reg, %lo(xxx), %reg
|
* sllx %reg, yyy, %reg
|
* sllx %reg, yyy, %reg
|
*
|
*
|
* ??? This is just a generalized version of the low_bits==0
|
* ??? This is just a generalized version of the low_bits==0
|
* thing above, FIXME...
|
* thing above, FIXME...
|
*/
|
*/
|
if ((highest_bit_set - lowest_bit_set) < 32)
|
if ((highest_bit_set - lowest_bit_set) < 32)
|
{
|
{
|
unsigned HOST_WIDE_INT focus_bits =
|
unsigned HOST_WIDE_INT focus_bits =
|
create_simple_focus_bits (high_bits, low_bits,
|
create_simple_focus_bits (high_bits, low_bits,
|
lowest_bit_set, 0);
|
lowest_bit_set, 0);
|
|
|
/* We can't get here in this state. */
|
/* We can't get here in this state. */
|
gcc_assert (highest_bit_set >= 32 && lowest_bit_set < 32);
|
gcc_assert (highest_bit_set >= 32 && lowest_bit_set < 32);
|
|
|
/* So what we know is that the set bits straddle the
|
/* So what we know is that the set bits straddle the
|
middle of the 64-bit word. */
|
middle of the 64-bit word. */
|
sparc_emit_set_const64_quick2 (op0, temp,
|
sparc_emit_set_const64_quick2 (op0, temp,
|
focus_bits, 0,
|
focus_bits, 0,
|
lowest_bit_set);
|
lowest_bit_set);
|
return;
|
return;
|
}
|
}
|
|
|
/* 1) sethi %hi(high_bits), %reg
|
/* 1) sethi %hi(high_bits), %reg
|
* or %reg, %lo(high_bits), %reg
|
* or %reg, %lo(high_bits), %reg
|
* sllx %reg, 32, %reg
|
* sllx %reg, 32, %reg
|
* or %reg, low_bits, %reg
|
* or %reg, low_bits, %reg
|
*/
|
*/
|
if (SPARC_SIMM13_P(low_bits)
|
if (SPARC_SIMM13_P(low_bits)
|
&& ((int)low_bits > 0))
|
&& ((int)low_bits > 0))
|
{
|
{
|
sparc_emit_set_const64_quick2 (op0, temp, high_bits, low_bits, 32);
|
sparc_emit_set_const64_quick2 (op0, temp, high_bits, low_bits, 32);
|
return;
|
return;
|
}
|
}
|
|
|
/* The easiest way when all else fails, is full decomposition. */
|
/* The easiest way when all else fails, is full decomposition. */
|
#if 0
|
#if 0
|
printf ("sparc_emit_set_const64: Hard constant [%08lx%08lx] neg[%08lx%08lx]\n",
|
printf ("sparc_emit_set_const64: Hard constant [%08lx%08lx] neg[%08lx%08lx]\n",
|
high_bits, low_bits, ~high_bits, ~low_bits);
|
high_bits, low_bits, ~high_bits, ~low_bits);
|
#endif
|
#endif
|
sparc_emit_set_const64_longway (op0, temp, high_bits, low_bits);
|
sparc_emit_set_const64_longway (op0, temp, high_bits, low_bits);
|
}
|
}
|
#endif /* HOST_BITS_PER_WIDE_INT == 32 */
|
#endif /* HOST_BITS_PER_WIDE_INT == 32 */
|
|
|
/* Given a comparison code (EQ, NE, etc.) and the first operand of a COMPARE,
|
/* Given a comparison code (EQ, NE, etc.) and the first operand of a COMPARE,
|
return the mode to be used for the comparison. For floating-point,
|
return the mode to be used for the comparison. For floating-point,
|
CCFP[E]mode is used. CC_NOOVmode should be used when the first operand
|
CCFP[E]mode is used. CC_NOOVmode should be used when the first operand
|
is a PLUS, MINUS, NEG, or ASHIFT. CCmode should be used when no special
|
is a PLUS, MINUS, NEG, or ASHIFT. CCmode should be used when no special
|
processing is needed. */
|
processing is needed. */
|
|
|
enum machine_mode
|
enum machine_mode
|
select_cc_mode (enum rtx_code op, rtx x, rtx y ATTRIBUTE_UNUSED)
|
select_cc_mode (enum rtx_code op, rtx x, rtx y ATTRIBUTE_UNUSED)
|
{
|
{
|
if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
|
if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
|
{
|
{
|
switch (op)
|
switch (op)
|
{
|
{
|
case EQ:
|
case EQ:
|
case NE:
|
case NE:
|
case UNORDERED:
|
case UNORDERED:
|
case ORDERED:
|
case ORDERED:
|
case UNLT:
|
case UNLT:
|
case UNLE:
|
case UNLE:
|
case UNGT:
|
case UNGT:
|
case UNGE:
|
case UNGE:
|
case UNEQ:
|
case UNEQ:
|
case LTGT:
|
case LTGT:
|
return CCFPmode;
|
return CCFPmode;
|
|
|
case LT:
|
case LT:
|
case LE:
|
case LE:
|
case GT:
|
case GT:
|
case GE:
|
case GE:
|
return CCFPEmode;
|
return CCFPEmode;
|
|
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
}
|
}
|
else if (GET_CODE (x) == PLUS || GET_CODE (x) == MINUS
|
else if (GET_CODE (x) == PLUS || GET_CODE (x) == MINUS
|
|| GET_CODE (x) == NEG || GET_CODE (x) == ASHIFT)
|
|| GET_CODE (x) == NEG || GET_CODE (x) == ASHIFT)
|
{
|
{
|
if (TARGET_ARCH64 && GET_MODE (x) == DImode)
|
if (TARGET_ARCH64 && GET_MODE (x) == DImode)
|
return CCX_NOOVmode;
|
return CCX_NOOVmode;
|
else
|
else
|
return CC_NOOVmode;
|
return CC_NOOVmode;
|
}
|
}
|
else
|
else
|
{
|
{
|
if (TARGET_ARCH64 && GET_MODE (x) == DImode)
|
if (TARGET_ARCH64 && GET_MODE (x) == DImode)
|
return CCXmode;
|
return CCXmode;
|
else
|
else
|
return CCmode;
|
return CCmode;
|
}
|
}
|
}
|
}
|
|
|
/* Emit the compare insn and return the CC reg for a CODE comparison
|
/* Emit the compare insn and return the CC reg for a CODE comparison
|
with operands X and Y. */
|
with operands X and Y. */
|
|
|
static rtx
|
static rtx
|
gen_compare_reg_1 (enum rtx_code code, rtx x, rtx y)
|
gen_compare_reg_1 (enum rtx_code code, rtx x, rtx y)
|
{
|
{
|
enum machine_mode mode;
|
enum machine_mode mode;
|
rtx cc_reg;
|
rtx cc_reg;
|
|
|
if (GET_MODE_CLASS (GET_MODE (x)) == MODE_CC)
|
if (GET_MODE_CLASS (GET_MODE (x)) == MODE_CC)
|
return x;
|
return x;
|
|
|
mode = SELECT_CC_MODE (code, x, y);
|
mode = SELECT_CC_MODE (code, x, y);
|
|
|
/* ??? We don't have movcc patterns so we cannot generate pseudo regs for the
|
/* ??? We don't have movcc patterns so we cannot generate pseudo regs for the
|
fcc regs (cse can't tell they're really call clobbered regs and will
|
fcc regs (cse can't tell they're really call clobbered regs and will
|
remove a duplicate comparison even if there is an intervening function
|
remove a duplicate comparison even if there is an intervening function
|
call - it will then try to reload the cc reg via an int reg which is why
|
call - it will then try to reload the cc reg via an int reg which is why
|
we need the movcc patterns). It is possible to provide the movcc
|
we need the movcc patterns). It is possible to provide the movcc
|
patterns by using the ldxfsr/stxfsr v9 insns. I tried it: you need two
|
patterns by using the ldxfsr/stxfsr v9 insns. I tried it: you need two
|
registers (say %g1,%g5) and it takes about 6 insns. A better fix would be
|
registers (say %g1,%g5) and it takes about 6 insns. A better fix would be
|
to tell cse that CCFPE mode registers (even pseudos) are call
|
to tell cse that CCFPE mode registers (even pseudos) are call
|
clobbered. */
|
clobbered. */
|
|
|
/* ??? This is an experiment. Rather than making changes to cse which may
|
/* ??? This is an experiment. Rather than making changes to cse which may
|
or may not be easy/clean, we do our own cse. This is possible because
|
or may not be easy/clean, we do our own cse. This is possible because
|
we will generate hard registers. Cse knows they're call clobbered (it
|
we will generate hard registers. Cse knows they're call clobbered (it
|
doesn't know the same thing about pseudos). If we guess wrong, no big
|
doesn't know the same thing about pseudos). If we guess wrong, no big
|
deal, but if we win, great! */
|
deal, but if we win, great! */
|
|
|
if (TARGET_V9 && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
|
if (TARGET_V9 && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
|
#if 1 /* experiment */
|
#if 1 /* experiment */
|
{
|
{
|
int reg;
|
int reg;
|
/* We cycle through the registers to ensure they're all exercised. */
|
/* We cycle through the registers to ensure they're all exercised. */
|
static int next_fcc_reg = 0;
|
static int next_fcc_reg = 0;
|
/* Previous x,y for each fcc reg. */
|
/* Previous x,y for each fcc reg. */
|
static rtx prev_args[4][2];
|
static rtx prev_args[4][2];
|
|
|
/* Scan prev_args for x,y. */
|
/* Scan prev_args for x,y. */
|
for (reg = 0; reg < 4; reg++)
|
for (reg = 0; reg < 4; reg++)
|
if (prev_args[reg][0] == x && prev_args[reg][1] == y)
|
if (prev_args[reg][0] == x && prev_args[reg][1] == y)
|
break;
|
break;
|
if (reg == 4)
|
if (reg == 4)
|
{
|
{
|
reg = next_fcc_reg;
|
reg = next_fcc_reg;
|
prev_args[reg][0] = x;
|
prev_args[reg][0] = x;
|
prev_args[reg][1] = y;
|
prev_args[reg][1] = y;
|
next_fcc_reg = (next_fcc_reg + 1) & 3;
|
next_fcc_reg = (next_fcc_reg + 1) & 3;
|
}
|
}
|
cc_reg = gen_rtx_REG (mode, reg + SPARC_FIRST_V9_FCC_REG);
|
cc_reg = gen_rtx_REG (mode, reg + SPARC_FIRST_V9_FCC_REG);
|
}
|
}
|
#else
|
#else
|
cc_reg = gen_reg_rtx (mode);
|
cc_reg = gen_reg_rtx (mode);
|
#endif /* ! experiment */
|
#endif /* ! experiment */
|
else if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
|
else if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
|
cc_reg = gen_rtx_REG (mode, SPARC_FCC_REG);
|
cc_reg = gen_rtx_REG (mode, SPARC_FCC_REG);
|
else
|
else
|
cc_reg = gen_rtx_REG (mode, SPARC_ICC_REG);
|
cc_reg = gen_rtx_REG (mode, SPARC_ICC_REG);
|
|
|
/* We shouldn't get there for TFmode if !TARGET_HARD_QUAD. If we do, this
|
/* We shouldn't get there for TFmode if !TARGET_HARD_QUAD. If we do, this
|
will only result in an unrecognizable insn so no point in asserting. */
|
will only result in an unrecognizable insn so no point in asserting. */
|
emit_insn (gen_rtx_SET (VOIDmode, cc_reg, gen_rtx_COMPARE (mode, x, y)));
|
emit_insn (gen_rtx_SET (VOIDmode, cc_reg, gen_rtx_COMPARE (mode, x, y)));
|
|
|
return cc_reg;
|
return cc_reg;
|
}
|
}
|
|
|
|
|
/* Emit the compare insn and return the CC reg for the comparison in CMP. */
|
/* Emit the compare insn and return the CC reg for the comparison in CMP. */
|
|
|
rtx
|
rtx
|
gen_compare_reg (rtx cmp)
|
gen_compare_reg (rtx cmp)
|
{
|
{
|
return gen_compare_reg_1 (GET_CODE (cmp), XEXP (cmp, 0), XEXP (cmp, 1));
|
return gen_compare_reg_1 (GET_CODE (cmp), XEXP (cmp, 0), XEXP (cmp, 1));
|
}
|
}
|
|
|
/* This function is used for v9 only.
|
/* This function is used for v9 only.
|
DEST is the target of the Scc insn.
|
DEST is the target of the Scc insn.
|
CODE is the code for an Scc's comparison.
|
CODE is the code for an Scc's comparison.
|
X and Y are the values we compare.
|
X and Y are the values we compare.
|
|
|
This function is needed to turn
|
This function is needed to turn
|
|
|
(set (reg:SI 110)
|
(set (reg:SI 110)
|
(gt (reg:CCX 100 %icc)
|
(gt (reg:CCX 100 %icc)
|
(const_int 0)))
|
(const_int 0)))
|
into
|
into
|
(set (reg:SI 110)
|
(set (reg:SI 110)
|
(gt:DI (reg:CCX 100 %icc)
|
(gt:DI (reg:CCX 100 %icc)
|
(const_int 0)))
|
(const_int 0)))
|
|
|
IE: The instruction recognizer needs to see the mode of the comparison to
|
IE: The instruction recognizer needs to see the mode of the comparison to
|
find the right instruction. We could use "gt:DI" right in the
|
find the right instruction. We could use "gt:DI" right in the
|
define_expand, but leaving it out allows us to handle DI, SI, etc. */
|
define_expand, but leaving it out allows us to handle DI, SI, etc. */
|
|
|
static int
|
static int
|
gen_v9_scc (rtx dest, enum rtx_code compare_code, rtx x, rtx y)
|
gen_v9_scc (rtx dest, enum rtx_code compare_code, rtx x, rtx y)
|
{
|
{
|
if (! TARGET_ARCH64
|
if (! TARGET_ARCH64
|
&& (GET_MODE (x) == DImode
|
&& (GET_MODE (x) == DImode
|
|| GET_MODE (dest) == DImode))
|
|| GET_MODE (dest) == DImode))
|
return 0;
|
return 0;
|
|
|
/* Try to use the movrCC insns. */
|
/* Try to use the movrCC insns. */
|
if (TARGET_ARCH64
|
if (TARGET_ARCH64
|
&& GET_MODE_CLASS (GET_MODE (x)) == MODE_INT
|
&& GET_MODE_CLASS (GET_MODE (x)) == MODE_INT
|
&& y == const0_rtx
|
&& y == const0_rtx
|
&& v9_regcmp_p (compare_code))
|
&& v9_regcmp_p (compare_code))
|
{
|
{
|
rtx op0 = x;
|
rtx op0 = x;
|
rtx temp;
|
rtx temp;
|
|
|
/* Special case for op0 != 0. This can be done with one instruction if
|
/* Special case for op0 != 0. This can be done with one instruction if
|
dest == x. */
|
dest == x. */
|
|
|
if (compare_code == NE
|
if (compare_code == NE
|
&& GET_MODE (dest) == DImode
|
&& GET_MODE (dest) == DImode
|
&& rtx_equal_p (op0, dest))
|
&& rtx_equal_p (op0, dest))
|
{
|
{
|
emit_insn (gen_rtx_SET (VOIDmode, dest,
|
emit_insn (gen_rtx_SET (VOIDmode, dest,
|
gen_rtx_IF_THEN_ELSE (DImode,
|
gen_rtx_IF_THEN_ELSE (DImode,
|
gen_rtx_fmt_ee (compare_code, DImode,
|
gen_rtx_fmt_ee (compare_code, DImode,
|
op0, const0_rtx),
|
op0, const0_rtx),
|
const1_rtx,
|
const1_rtx,
|
dest)));
|
dest)));
|
return 1;
|
return 1;
|
}
|
}
|
|
|
if (reg_overlap_mentioned_p (dest, op0))
|
if (reg_overlap_mentioned_p (dest, op0))
|
{
|
{
|
/* Handle the case where dest == x.
|
/* Handle the case where dest == x.
|
We "early clobber" the result. */
|
We "early clobber" the result. */
|
op0 = gen_reg_rtx (GET_MODE (x));
|
op0 = gen_reg_rtx (GET_MODE (x));
|
emit_move_insn (op0, x);
|
emit_move_insn (op0, x);
|
}
|
}
|
|
|
emit_insn (gen_rtx_SET (VOIDmode, dest, const0_rtx));
|
emit_insn (gen_rtx_SET (VOIDmode, dest, const0_rtx));
|
if (GET_MODE (op0) != DImode)
|
if (GET_MODE (op0) != DImode)
|
{
|
{
|
temp = gen_reg_rtx (DImode);
|
temp = gen_reg_rtx (DImode);
|
convert_move (temp, op0, 0);
|
convert_move (temp, op0, 0);
|
}
|
}
|
else
|
else
|
temp = op0;
|
temp = op0;
|
emit_insn (gen_rtx_SET (VOIDmode, dest,
|
emit_insn (gen_rtx_SET (VOIDmode, dest,
|
gen_rtx_IF_THEN_ELSE (GET_MODE (dest),
|
gen_rtx_IF_THEN_ELSE (GET_MODE (dest),
|
gen_rtx_fmt_ee (compare_code, DImode,
|
gen_rtx_fmt_ee (compare_code, DImode,
|
temp, const0_rtx),
|
temp, const0_rtx),
|
const1_rtx,
|
const1_rtx,
|
dest)));
|
dest)));
|
return 1;
|
return 1;
|
}
|
}
|
else
|
else
|
{
|
{
|
x = gen_compare_reg_1 (compare_code, x, y);
|
x = gen_compare_reg_1 (compare_code, x, y);
|
y = const0_rtx;
|
y = const0_rtx;
|
|
|
gcc_assert (GET_MODE (x) != CC_NOOVmode
|
gcc_assert (GET_MODE (x) != CC_NOOVmode
|
&& GET_MODE (x) != CCX_NOOVmode);
|
&& GET_MODE (x) != CCX_NOOVmode);
|
|
|
emit_insn (gen_rtx_SET (VOIDmode, dest, const0_rtx));
|
emit_insn (gen_rtx_SET (VOIDmode, dest, const0_rtx));
|
emit_insn (gen_rtx_SET (VOIDmode, dest,
|
emit_insn (gen_rtx_SET (VOIDmode, dest,
|
gen_rtx_IF_THEN_ELSE (GET_MODE (dest),
|
gen_rtx_IF_THEN_ELSE (GET_MODE (dest),
|
gen_rtx_fmt_ee (compare_code,
|
gen_rtx_fmt_ee (compare_code,
|
GET_MODE (x), x, y),
|
GET_MODE (x), x, y),
|
const1_rtx, dest)));
|
const1_rtx, dest)));
|
return 1;
|
return 1;
|
}
|
}
|
}
|
}
|
|
|
|
|
/* Emit an scc insn. For seq, sne, sgeu, and sltu, we can do this
|
/* Emit an scc insn. For seq, sne, sgeu, and sltu, we can do this
|
without jumps using the addx/subx instructions. */
|
without jumps using the addx/subx instructions. */
|
|
|
bool
|
bool
|
emit_scc_insn (rtx operands[])
|
emit_scc_insn (rtx operands[])
|
{
|
{
|
rtx tem;
|
rtx tem;
|
rtx x;
|
rtx x;
|
rtx y;
|
rtx y;
|
enum rtx_code code;
|
enum rtx_code code;
|
|
|
/* The quad-word fp compare library routines all return nonzero to indicate
|
/* The quad-word fp compare library routines all return nonzero to indicate
|
true, which is different from the equivalent libgcc routines, so we must
|
true, which is different from the equivalent libgcc routines, so we must
|
handle them specially here. */
|
handle them specially here. */
|
if (GET_MODE (operands[2]) == TFmode && ! TARGET_HARD_QUAD)
|
if (GET_MODE (operands[2]) == TFmode && ! TARGET_HARD_QUAD)
|
{
|
{
|
operands[1] = sparc_emit_float_lib_cmp (operands[2], operands[3],
|
operands[1] = sparc_emit_float_lib_cmp (operands[2], operands[3],
|
GET_CODE (operands[1]));
|
GET_CODE (operands[1]));
|
operands[2] = XEXP (operands[1], 0);
|
operands[2] = XEXP (operands[1], 0);
|
operands[3] = XEXP (operands[1], 1);
|
operands[3] = XEXP (operands[1], 1);
|
}
|
}
|
|
|
code = GET_CODE (operands[1]);
|
code = GET_CODE (operands[1]);
|
x = operands[2];
|
x = operands[2];
|
y = operands[3];
|
y = operands[3];
|
|
|
/* For seq/sne on v9 we use the same code as v8 (the addx/subx method has
|
/* For seq/sne on v9 we use the same code as v8 (the addx/subx method has
|
more applications). The exception to this is "reg != 0" which can
|
more applications). The exception to this is "reg != 0" which can
|
be done in one instruction on v9 (so we do it). */
|
be done in one instruction on v9 (so we do it). */
|
if (code == EQ)
|
if (code == EQ)
|
{
|
{
|
if (GET_MODE (x) == SImode)
|
if (GET_MODE (x) == SImode)
|
{
|
{
|
rtx pat = gen_seqsi_special (operands[0], x, y);
|
rtx pat = gen_seqsi_special (operands[0], x, y);
|
emit_insn (pat);
|
emit_insn (pat);
|
return true;
|
return true;
|
}
|
}
|
else if (GET_MODE (x) == DImode)
|
else if (GET_MODE (x) == DImode)
|
{
|
{
|
rtx pat = gen_seqdi_special (operands[0], x, y);
|
rtx pat = gen_seqdi_special (operands[0], x, y);
|
emit_insn (pat);
|
emit_insn (pat);
|
return true;
|
return true;
|
}
|
}
|
}
|
}
|
|
|
if (code == NE)
|
if (code == NE)
|
{
|
{
|
if (GET_MODE (x) == SImode)
|
if (GET_MODE (x) == SImode)
|
{
|
{
|
rtx pat = gen_snesi_special (operands[0], x, y);
|
rtx pat = gen_snesi_special (operands[0], x, y);
|
emit_insn (pat);
|
emit_insn (pat);
|
return true;
|
return true;
|
}
|
}
|
else if (GET_MODE (x) == DImode)
|
else if (GET_MODE (x) == DImode)
|
{
|
{
|
rtx pat = gen_snedi_special (operands[0], x, y);
|
rtx pat = gen_snedi_special (operands[0], x, y);
|
emit_insn (pat);
|
emit_insn (pat);
|
return true;
|
return true;
|
}
|
}
|
}
|
}
|
|
|
/* For the rest, on v9 we can use conditional moves. */
|
/* For the rest, on v9 we can use conditional moves. */
|
|
|
if (TARGET_V9)
|
if (TARGET_V9)
|
{
|
{
|
if (gen_v9_scc (operands[0], code, x, y))
|
if (gen_v9_scc (operands[0], code, x, y))
|
return true;
|
return true;
|
}
|
}
|
|
|
/* We can do LTU and GEU using the addx/subx instructions too. And
|
/* We can do LTU and GEU using the addx/subx instructions too. And
|
for GTU/LEU, if both operands are registers swap them and fall
|
for GTU/LEU, if both operands are registers swap them and fall
|
back to the easy case. */
|
back to the easy case. */
|
if (code == GTU || code == LEU)
|
if (code == GTU || code == LEU)
|
{
|
{
|
if ((GET_CODE (x) == REG || GET_CODE (x) == SUBREG)
|
if ((GET_CODE (x) == REG || GET_CODE (x) == SUBREG)
|
&& (GET_CODE (y) == REG || GET_CODE (y) == SUBREG))
|
&& (GET_CODE (y) == REG || GET_CODE (y) == SUBREG))
|
{
|
{
|
tem = x;
|
tem = x;
|
x = y;
|
x = y;
|
y = tem;
|
y = tem;
|
code = swap_condition (code);
|
code = swap_condition (code);
|
}
|
}
|
}
|
}
|
|
|
if (code == LTU || code == GEU)
|
if (code == LTU || code == GEU)
|
{
|
{
|
emit_insn (gen_rtx_SET (VOIDmode, operands[0],
|
emit_insn (gen_rtx_SET (VOIDmode, operands[0],
|
gen_rtx_fmt_ee (code, SImode,
|
gen_rtx_fmt_ee (code, SImode,
|
gen_compare_reg_1 (code, x, y),
|
gen_compare_reg_1 (code, x, y),
|
const0_rtx)));
|
const0_rtx)));
|
return true;
|
return true;
|
}
|
}
|
|
|
/* Nope, do branches. */
|
/* Nope, do branches. */
|
return false;
|
return false;
|
}
|
}
|
|
|
/* Emit a conditional jump insn for the v9 architecture using comparison code
|
/* Emit a conditional jump insn for the v9 architecture using comparison code
|
CODE and jump target LABEL.
|
CODE and jump target LABEL.
|
This function exists to take advantage of the v9 brxx insns. */
|
This function exists to take advantage of the v9 brxx insns. */
|
|
|
static void
|
static void
|
emit_v9_brxx_insn (enum rtx_code code, rtx op0, rtx label)
|
emit_v9_brxx_insn (enum rtx_code code, rtx op0, rtx label)
|
{
|
{
|
emit_jump_insn (gen_rtx_SET (VOIDmode,
|
emit_jump_insn (gen_rtx_SET (VOIDmode,
|
pc_rtx,
|
pc_rtx,
|
gen_rtx_IF_THEN_ELSE (VOIDmode,
|
gen_rtx_IF_THEN_ELSE (VOIDmode,
|
gen_rtx_fmt_ee (code, GET_MODE (op0),
|
gen_rtx_fmt_ee (code, GET_MODE (op0),
|
op0, const0_rtx),
|
op0, const0_rtx),
|
gen_rtx_LABEL_REF (VOIDmode, label),
|
gen_rtx_LABEL_REF (VOIDmode, label),
|
pc_rtx)));
|
pc_rtx)));
|
}
|
}
|
|
|
void
|
void
|
emit_conditional_branch_insn (rtx operands[])
|
emit_conditional_branch_insn (rtx operands[])
|
{
|
{
|
/* The quad-word fp compare library routines all return nonzero to indicate
|
/* The quad-word fp compare library routines all return nonzero to indicate
|
true, which is different from the equivalent libgcc routines, so we must
|
true, which is different from the equivalent libgcc routines, so we must
|
handle them specially here. */
|
handle them specially here. */
|
if (GET_MODE (operands[1]) == TFmode && ! TARGET_HARD_QUAD)
|
if (GET_MODE (operands[1]) == TFmode && ! TARGET_HARD_QUAD)
|
{
|
{
|
operands[0] = sparc_emit_float_lib_cmp (operands[1], operands[2],
|
operands[0] = sparc_emit_float_lib_cmp (operands[1], operands[2],
|
GET_CODE (operands[0]));
|
GET_CODE (operands[0]));
|
operands[1] = XEXP (operands[0], 0);
|
operands[1] = XEXP (operands[0], 0);
|
operands[2] = XEXP (operands[0], 1);
|
operands[2] = XEXP (operands[0], 1);
|
}
|
}
|
|
|
if (TARGET_ARCH64 && operands[2] == const0_rtx
|
if (TARGET_ARCH64 && operands[2] == const0_rtx
|
&& GET_CODE (operands[1]) == REG
|
&& GET_CODE (operands[1]) == REG
|
&& GET_MODE (operands[1]) == DImode)
|
&& GET_MODE (operands[1]) == DImode)
|
{
|
{
|
emit_v9_brxx_insn (GET_CODE (operands[0]), operands[1], operands[3]);
|
emit_v9_brxx_insn (GET_CODE (operands[0]), operands[1], operands[3]);
|
return;
|
return;
|
}
|
}
|
|
|
operands[1] = gen_compare_reg (operands[0]);
|
operands[1] = gen_compare_reg (operands[0]);
|
operands[2] = const0_rtx;
|
operands[2] = const0_rtx;
|
operands[0] = gen_rtx_fmt_ee (GET_CODE (operands[0]), VOIDmode,
|
operands[0] = gen_rtx_fmt_ee (GET_CODE (operands[0]), VOIDmode,
|
operands[1], operands[2]);
|
operands[1], operands[2]);
|
emit_jump_insn (gen_cbranchcc4 (operands[0], operands[1], operands[2],
|
emit_jump_insn (gen_cbranchcc4 (operands[0], operands[1], operands[2],
|
operands[3]));
|
operands[3]));
|
}
|
}
|
|
|
|
|
/* Generate a DFmode part of a hard TFmode register.
|
/* Generate a DFmode part of a hard TFmode register.
|
REG is the TFmode hard register, LOW is 1 for the
|
REG is the TFmode hard register, LOW is 1 for the
|
low 64bit of the register and 0 otherwise.
|
low 64bit of the register and 0 otherwise.
|
*/
|
*/
|
rtx
|
rtx
|
gen_df_reg (rtx reg, int low)
|
gen_df_reg (rtx reg, int low)
|
{
|
{
|
int regno = REGNO (reg);
|
int regno = REGNO (reg);
|
|
|
if ((WORDS_BIG_ENDIAN == 0) ^ (low != 0))
|
if ((WORDS_BIG_ENDIAN == 0) ^ (low != 0))
|
regno += (TARGET_ARCH64 && regno < 32) ? 1 : 2;
|
regno += (TARGET_ARCH64 && regno < 32) ? 1 : 2;
|
return gen_rtx_REG (DFmode, regno);
|
return gen_rtx_REG (DFmode, regno);
|
}
|
}
|
|
|
/* Generate a call to FUNC with OPERANDS. Operand 0 is the return value.
|
/* Generate a call to FUNC with OPERANDS. Operand 0 is the return value.
|
Unlike normal calls, TFmode operands are passed by reference. It is
|
Unlike normal calls, TFmode operands are passed by reference. It is
|
assumed that no more than 3 operands are required. */
|
assumed that no more than 3 operands are required. */
|
|
|
static void
|
static void
|
emit_soft_tfmode_libcall (const char *func_name, int nargs, rtx *operands)
|
emit_soft_tfmode_libcall (const char *func_name, int nargs, rtx *operands)
|
{
|
{
|
rtx ret_slot = NULL, arg[3], func_sym;
|
rtx ret_slot = NULL, arg[3], func_sym;
|
int i;
|
int i;
|
|
|
/* We only expect to be called for conversions, unary, and binary ops. */
|
/* We only expect to be called for conversions, unary, and binary ops. */
|
gcc_assert (nargs == 2 || nargs == 3);
|
gcc_assert (nargs == 2 || nargs == 3);
|
|
|
for (i = 0; i < nargs; ++i)
|
for (i = 0; i < nargs; ++i)
|
{
|
{
|
rtx this_arg = operands[i];
|
rtx this_arg = operands[i];
|
rtx this_slot;
|
rtx this_slot;
|
|
|
/* TFmode arguments and return values are passed by reference. */
|
/* TFmode arguments and return values are passed by reference. */
|
if (GET_MODE (this_arg) == TFmode)
|
if (GET_MODE (this_arg) == TFmode)
|
{
|
{
|
int force_stack_temp;
|
int force_stack_temp;
|
|
|
force_stack_temp = 0;
|
force_stack_temp = 0;
|
if (TARGET_BUGGY_QP_LIB && i == 0)
|
if (TARGET_BUGGY_QP_LIB && i == 0)
|
force_stack_temp = 1;
|
force_stack_temp = 1;
|
|
|
if (GET_CODE (this_arg) == MEM
|
if (GET_CODE (this_arg) == MEM
|
&& ! force_stack_temp)
|
&& ! force_stack_temp)
|
this_arg = XEXP (this_arg, 0);
|
this_arg = XEXP (this_arg, 0);
|
else if (CONSTANT_P (this_arg)
|
else if (CONSTANT_P (this_arg)
|
&& ! force_stack_temp)
|
&& ! force_stack_temp)
|
{
|
{
|
this_slot = force_const_mem (TFmode, this_arg);
|
this_slot = force_const_mem (TFmode, this_arg);
|
this_arg = XEXP (this_slot, 0);
|
this_arg = XEXP (this_slot, 0);
|
}
|
}
|
else
|
else
|
{
|
{
|
this_slot = assign_stack_temp (TFmode, GET_MODE_SIZE (TFmode), 0);
|
this_slot = assign_stack_temp (TFmode, GET_MODE_SIZE (TFmode), 0);
|
|
|
/* Operand 0 is the return value. We'll copy it out later. */
|
/* Operand 0 is the return value. We'll copy it out later. */
|
if (i > 0)
|
if (i > 0)
|
emit_move_insn (this_slot, this_arg);
|
emit_move_insn (this_slot, this_arg);
|
else
|
else
|
ret_slot = this_slot;
|
ret_slot = this_slot;
|
|
|
this_arg = XEXP (this_slot, 0);
|
this_arg = XEXP (this_slot, 0);
|
}
|
}
|
}
|
}
|
|
|
arg[i] = this_arg;
|
arg[i] = this_arg;
|
}
|
}
|
|
|
func_sym = gen_rtx_SYMBOL_REF (Pmode, func_name);
|
func_sym = gen_rtx_SYMBOL_REF (Pmode, func_name);
|
|
|
if (GET_MODE (operands[0]) == TFmode)
|
if (GET_MODE (operands[0]) == TFmode)
|
{
|
{
|
if (nargs == 2)
|
if (nargs == 2)
|
emit_library_call (func_sym, LCT_NORMAL, VOIDmode, 2,
|
emit_library_call (func_sym, LCT_NORMAL, VOIDmode, 2,
|
arg[0], GET_MODE (arg[0]),
|
arg[0], GET_MODE (arg[0]),
|
arg[1], GET_MODE (arg[1]));
|
arg[1], GET_MODE (arg[1]));
|
else
|
else
|
emit_library_call (func_sym, LCT_NORMAL, VOIDmode, 3,
|
emit_library_call (func_sym, LCT_NORMAL, VOIDmode, 3,
|
arg[0], GET_MODE (arg[0]),
|
arg[0], GET_MODE (arg[0]),
|
arg[1], GET_MODE (arg[1]),
|
arg[1], GET_MODE (arg[1]),
|
arg[2], GET_MODE (arg[2]));
|
arg[2], GET_MODE (arg[2]));
|
|
|
if (ret_slot)
|
if (ret_slot)
|
emit_move_insn (operands[0], ret_slot);
|
emit_move_insn (operands[0], ret_slot);
|
}
|
}
|
else
|
else
|
{
|
{
|
rtx ret;
|
rtx ret;
|
|
|
gcc_assert (nargs == 2);
|
gcc_assert (nargs == 2);
|
|
|
ret = emit_library_call_value (func_sym, operands[0], LCT_NORMAL,
|
ret = emit_library_call_value (func_sym, operands[0], LCT_NORMAL,
|
GET_MODE (operands[0]), 1,
|
GET_MODE (operands[0]), 1,
|
arg[1], GET_MODE (arg[1]));
|
arg[1], GET_MODE (arg[1]));
|
|
|
if (ret != operands[0])
|
if (ret != operands[0])
|
emit_move_insn (operands[0], ret);
|
emit_move_insn (operands[0], ret);
|
}
|
}
|
}
|
}
|
|
|
/* Expand soft-float TFmode calls to sparc abi routines. */
|
/* Expand soft-float TFmode calls to sparc abi routines. */
|
|
|
static void
|
static void
|
emit_soft_tfmode_binop (enum rtx_code code, rtx *operands)
|
emit_soft_tfmode_binop (enum rtx_code code, rtx *operands)
|
{
|
{
|
const char *func;
|
const char *func;
|
|
|
switch (code)
|
switch (code)
|
{
|
{
|
case PLUS:
|
case PLUS:
|
func = "_Qp_add";
|
func = "_Qp_add";
|
break;
|
break;
|
case MINUS:
|
case MINUS:
|
func = "_Qp_sub";
|
func = "_Qp_sub";
|
break;
|
break;
|
case MULT:
|
case MULT:
|
func = "_Qp_mul";
|
func = "_Qp_mul";
|
break;
|
break;
|
case DIV:
|
case DIV:
|
func = "_Qp_div";
|
func = "_Qp_div";
|
break;
|
break;
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
|
|
emit_soft_tfmode_libcall (func, 3, operands);
|
emit_soft_tfmode_libcall (func, 3, operands);
|
}
|
}
|
|
|
static void
|
static void
|
emit_soft_tfmode_unop (enum rtx_code code, rtx *operands)
|
emit_soft_tfmode_unop (enum rtx_code code, rtx *operands)
|
{
|
{
|
const char *func;
|
const char *func;
|
|
|
gcc_assert (code == SQRT);
|
gcc_assert (code == SQRT);
|
func = "_Qp_sqrt";
|
func = "_Qp_sqrt";
|
|
|
emit_soft_tfmode_libcall (func, 2, operands);
|
emit_soft_tfmode_libcall (func, 2, operands);
|
}
|
}
|
|
|
static void
|
static void
|
emit_soft_tfmode_cvt (enum rtx_code code, rtx *operands)
|
emit_soft_tfmode_cvt (enum rtx_code code, rtx *operands)
|
{
|
{
|
const char *func;
|
const char *func;
|
|
|
switch (code)
|
switch (code)
|
{
|
{
|
case FLOAT_EXTEND:
|
case FLOAT_EXTEND:
|
switch (GET_MODE (operands[1]))
|
switch (GET_MODE (operands[1]))
|
{
|
{
|
case SFmode:
|
case SFmode:
|
func = "_Qp_stoq";
|
func = "_Qp_stoq";
|
break;
|
break;
|
case DFmode:
|
case DFmode:
|
func = "_Qp_dtoq";
|
func = "_Qp_dtoq";
|
break;
|
break;
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
break;
|
break;
|
|
|
case FLOAT_TRUNCATE:
|
case FLOAT_TRUNCATE:
|
switch (GET_MODE (operands[0]))
|
switch (GET_MODE (operands[0]))
|
{
|
{
|
case SFmode:
|
case SFmode:
|
func = "_Qp_qtos";
|
func = "_Qp_qtos";
|
break;
|
break;
|
case DFmode:
|
case DFmode:
|
func = "_Qp_qtod";
|
func = "_Qp_qtod";
|
break;
|
break;
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
break;
|
break;
|
|
|
case FLOAT:
|
case FLOAT:
|
switch (GET_MODE (operands[1]))
|
switch (GET_MODE (operands[1]))
|
{
|
{
|
case SImode:
|
case SImode:
|
func = "_Qp_itoq";
|
func = "_Qp_itoq";
|
if (TARGET_ARCH64)
|
if (TARGET_ARCH64)
|
operands[1] = gen_rtx_SIGN_EXTEND (DImode, operands[1]);
|
operands[1] = gen_rtx_SIGN_EXTEND (DImode, operands[1]);
|
break;
|
break;
|
case DImode:
|
case DImode:
|
func = "_Qp_xtoq";
|
func = "_Qp_xtoq";
|
break;
|
break;
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
break;
|
break;
|
|
|
case UNSIGNED_FLOAT:
|
case UNSIGNED_FLOAT:
|
switch (GET_MODE (operands[1]))
|
switch (GET_MODE (operands[1]))
|
{
|
{
|
case SImode:
|
case SImode:
|
func = "_Qp_uitoq";
|
func = "_Qp_uitoq";
|
if (TARGET_ARCH64)
|
if (TARGET_ARCH64)
|
operands[1] = gen_rtx_ZERO_EXTEND (DImode, operands[1]);
|
operands[1] = gen_rtx_ZERO_EXTEND (DImode, operands[1]);
|
break;
|
break;
|
case DImode:
|
case DImode:
|
func = "_Qp_uxtoq";
|
func = "_Qp_uxtoq";
|
break;
|
break;
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
break;
|
break;
|
|
|
case FIX:
|
case FIX:
|
switch (GET_MODE (operands[0]))
|
switch (GET_MODE (operands[0]))
|
{
|
{
|
case SImode:
|
case SImode:
|
func = "_Qp_qtoi";
|
func = "_Qp_qtoi";
|
break;
|
break;
|
case DImode:
|
case DImode:
|
func = "_Qp_qtox";
|
func = "_Qp_qtox";
|
break;
|
break;
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
break;
|
break;
|
|
|
case UNSIGNED_FIX:
|
case UNSIGNED_FIX:
|
switch (GET_MODE (operands[0]))
|
switch (GET_MODE (operands[0]))
|
{
|
{
|
case SImode:
|
case SImode:
|
func = "_Qp_qtoui";
|
func = "_Qp_qtoui";
|
break;
|
break;
|
case DImode:
|
case DImode:
|
func = "_Qp_qtoux";
|
func = "_Qp_qtoux";
|
break;
|
break;
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
break;
|
break;
|
|
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
|
|
emit_soft_tfmode_libcall (func, 2, operands);
|
emit_soft_tfmode_libcall (func, 2, operands);
|
}
|
}
|
|
|
/* Expand a hard-float tfmode operation. All arguments must be in
|
/* Expand a hard-float tfmode operation. All arguments must be in
|
registers. */
|
registers. */
|
|
|
static void
|
static void
|
emit_hard_tfmode_operation (enum rtx_code code, rtx *operands)
|
emit_hard_tfmode_operation (enum rtx_code code, rtx *operands)
|
{
|
{
|
rtx op, dest;
|
rtx op, dest;
|
|
|
if (GET_RTX_CLASS (code) == RTX_UNARY)
|
if (GET_RTX_CLASS (code) == RTX_UNARY)
|
{
|
{
|
operands[1] = force_reg (GET_MODE (operands[1]), operands[1]);
|
operands[1] = force_reg (GET_MODE (operands[1]), operands[1]);
|
op = gen_rtx_fmt_e (code, GET_MODE (operands[0]), operands[1]);
|
op = gen_rtx_fmt_e (code, GET_MODE (operands[0]), operands[1]);
|
}
|
}
|
else
|
else
|
{
|
{
|
operands[1] = force_reg (GET_MODE (operands[1]), operands[1]);
|
operands[1] = force_reg (GET_MODE (operands[1]), operands[1]);
|
operands[2] = force_reg (GET_MODE (operands[2]), operands[2]);
|
operands[2] = force_reg (GET_MODE (operands[2]), operands[2]);
|
op = gen_rtx_fmt_ee (code, GET_MODE (operands[0]),
|
op = gen_rtx_fmt_ee (code, GET_MODE (operands[0]),
|
operands[1], operands[2]);
|
operands[1], operands[2]);
|
}
|
}
|
|
|
if (register_operand (operands[0], VOIDmode))
|
if (register_operand (operands[0], VOIDmode))
|
dest = operands[0];
|
dest = operands[0];
|
else
|
else
|
dest = gen_reg_rtx (GET_MODE (operands[0]));
|
dest = gen_reg_rtx (GET_MODE (operands[0]));
|
|
|
emit_insn (gen_rtx_SET (VOIDmode, dest, op));
|
emit_insn (gen_rtx_SET (VOIDmode, dest, op));
|
|
|
if (dest != operands[0])
|
if (dest != operands[0])
|
emit_move_insn (operands[0], dest);
|
emit_move_insn (operands[0], dest);
|
}
|
}
|
|
|
void
|
void
|
emit_tfmode_binop (enum rtx_code code, rtx *operands)
|
emit_tfmode_binop (enum rtx_code code, rtx *operands)
|
{
|
{
|
if (TARGET_HARD_QUAD)
|
if (TARGET_HARD_QUAD)
|
emit_hard_tfmode_operation (code, operands);
|
emit_hard_tfmode_operation (code, operands);
|
else
|
else
|
emit_soft_tfmode_binop (code, operands);
|
emit_soft_tfmode_binop (code, operands);
|
}
|
}
|
|
|
void
|
void
|
emit_tfmode_unop (enum rtx_code code, rtx *operands)
|
emit_tfmode_unop (enum rtx_code code, rtx *operands)
|
{
|
{
|
if (TARGET_HARD_QUAD)
|
if (TARGET_HARD_QUAD)
|
emit_hard_tfmode_operation (code, operands);
|
emit_hard_tfmode_operation (code, operands);
|
else
|
else
|
emit_soft_tfmode_unop (code, operands);
|
emit_soft_tfmode_unop (code, operands);
|
}
|
}
|
|
|
void
|
void
|
emit_tfmode_cvt (enum rtx_code code, rtx *operands)
|
emit_tfmode_cvt (enum rtx_code code, rtx *operands)
|
{
|
{
|
if (TARGET_HARD_QUAD)
|
if (TARGET_HARD_QUAD)
|
emit_hard_tfmode_operation (code, operands);
|
emit_hard_tfmode_operation (code, operands);
|
else
|
else
|
emit_soft_tfmode_cvt (code, operands);
|
emit_soft_tfmode_cvt (code, operands);
|
}
|
}
|
|
|
/* Return nonzero if a branch/jump/call instruction will be emitting
|
/* Return nonzero if a branch/jump/call instruction will be emitting
|
nop into its delay slot. */
|
nop into its delay slot. */
|
|
|
int
|
int
|
empty_delay_slot (rtx insn)
|
empty_delay_slot (rtx insn)
|
{
|
{
|
rtx seq;
|
rtx seq;
|
|
|
/* If no previous instruction (should not happen), return true. */
|
/* If no previous instruction (should not happen), return true. */
|
if (PREV_INSN (insn) == NULL)
|
if (PREV_INSN (insn) == NULL)
|
return 1;
|
return 1;
|
|
|
seq = NEXT_INSN (PREV_INSN (insn));
|
seq = NEXT_INSN (PREV_INSN (insn));
|
if (GET_CODE (PATTERN (seq)) == SEQUENCE)
|
if (GET_CODE (PATTERN (seq)) == SEQUENCE)
|
return 0;
|
return 0;
|
|
|
return 1;
|
return 1;
|
}
|
}
|
|
|
/* Return nonzero if TRIAL can go into the call delay slot. */
|
/* Return nonzero if TRIAL can go into the call delay slot. */
|
|
|
int
|
int
|
tls_call_delay (rtx trial)
|
tls_call_delay (rtx trial)
|
{
|
{
|
rtx pat;
|
rtx pat;
|
|
|
/* Binutils allows
|
/* Binutils allows
|
call __tls_get_addr, %tgd_call (foo)
|
call __tls_get_addr, %tgd_call (foo)
|
add %l7, %o0, %o0, %tgd_add (foo)
|
add %l7, %o0, %o0, %tgd_add (foo)
|
while Sun as/ld does not. */
|
while Sun as/ld does not. */
|
if (TARGET_GNU_TLS || !TARGET_TLS)
|
if (TARGET_GNU_TLS || !TARGET_TLS)
|
return 1;
|
return 1;
|
|
|
pat = PATTERN (trial);
|
pat = PATTERN (trial);
|
|
|
/* We must reject tgd_add{32|64}, i.e.
|
/* We must reject tgd_add{32|64}, i.e.
|
(set (reg) (plus (reg) (unspec [(reg) (symbol_ref)] UNSPEC_TLSGD)))
|
(set (reg) (plus (reg) (unspec [(reg) (symbol_ref)] UNSPEC_TLSGD)))
|
and tldm_add{32|64}, i.e.
|
and tldm_add{32|64}, i.e.
|
(set (reg) (plus (reg) (unspec [(reg) (symbol_ref)] UNSPEC_TLSLDM)))
|
(set (reg) (plus (reg) (unspec [(reg) (symbol_ref)] UNSPEC_TLSLDM)))
|
for Sun as/ld. */
|
for Sun as/ld. */
|
if (GET_CODE (pat) == SET
|
if (GET_CODE (pat) == SET
|
&& GET_CODE (SET_SRC (pat)) == PLUS)
|
&& GET_CODE (SET_SRC (pat)) == PLUS)
|
{
|
{
|
rtx unspec = XEXP (SET_SRC (pat), 1);
|
rtx unspec = XEXP (SET_SRC (pat), 1);
|
|
|
if (GET_CODE (unspec) == UNSPEC
|
if (GET_CODE (unspec) == UNSPEC
|
&& (XINT (unspec, 1) == UNSPEC_TLSGD
|
&& (XINT (unspec, 1) == UNSPEC_TLSGD
|
|| XINT (unspec, 1) == UNSPEC_TLSLDM))
|
|| XINT (unspec, 1) == UNSPEC_TLSLDM))
|
return 0;
|
return 0;
|
}
|
}
|
|
|
return 1;
|
return 1;
|
}
|
}
|
|
|
/* Return nonzero if TRIAL, an insn, can be combined with a 'restore'
|
/* Return nonzero if TRIAL, an insn, can be combined with a 'restore'
|
instruction. RETURN_P is true if the v9 variant 'return' is to be
|
instruction. RETURN_P is true if the v9 variant 'return' is to be
|
considered in the test too.
|
considered in the test too.
|
|
|
TRIAL must be a SET whose destination is a REG appropriate for the
|
TRIAL must be a SET whose destination is a REG appropriate for the
|
'restore' instruction or, if RETURN_P is true, for the 'return'
|
'restore' instruction or, if RETURN_P is true, for the 'return'
|
instruction. */
|
instruction. */
|
|
|
static int
|
static int
|
eligible_for_restore_insn (rtx trial, bool return_p)
|
eligible_for_restore_insn (rtx trial, bool return_p)
|
{
|
{
|
rtx pat = PATTERN (trial);
|
rtx pat = PATTERN (trial);
|
rtx src = SET_SRC (pat);
|
rtx src = SET_SRC (pat);
|
|
|
/* The 'restore src,%g0,dest' pattern for word mode and below. */
|
/* The 'restore src,%g0,dest' pattern for word mode and below. */
|
if (GET_MODE_CLASS (GET_MODE (src)) != MODE_FLOAT
|
if (GET_MODE_CLASS (GET_MODE (src)) != MODE_FLOAT
|
&& arith_operand (src, GET_MODE (src)))
|
&& arith_operand (src, GET_MODE (src)))
|
{
|
{
|
if (TARGET_ARCH64)
|
if (TARGET_ARCH64)
|
return GET_MODE_SIZE (GET_MODE (src)) <= GET_MODE_SIZE (DImode);
|
return GET_MODE_SIZE (GET_MODE (src)) <= GET_MODE_SIZE (DImode);
|
else
|
else
|
return GET_MODE_SIZE (GET_MODE (src)) <= GET_MODE_SIZE (SImode);
|
return GET_MODE_SIZE (GET_MODE (src)) <= GET_MODE_SIZE (SImode);
|
}
|
}
|
|
|
/* The 'restore src,%g0,dest' pattern for double-word mode. */
|
/* The 'restore src,%g0,dest' pattern for double-word mode. */
|
else if (GET_MODE_CLASS (GET_MODE (src)) != MODE_FLOAT
|
else if (GET_MODE_CLASS (GET_MODE (src)) != MODE_FLOAT
|
&& arith_double_operand (src, GET_MODE (src)))
|
&& arith_double_operand (src, GET_MODE (src)))
|
return GET_MODE_SIZE (GET_MODE (src)) <= GET_MODE_SIZE (DImode);
|
return GET_MODE_SIZE (GET_MODE (src)) <= GET_MODE_SIZE (DImode);
|
|
|
/* The 'restore src,%g0,dest' pattern for float if no FPU. */
|
/* The 'restore src,%g0,dest' pattern for float if no FPU. */
|
else if (! TARGET_FPU && register_operand (src, SFmode))
|
else if (! TARGET_FPU && register_operand (src, SFmode))
|
return 1;
|
return 1;
|
|
|
/* The 'restore src,%g0,dest' pattern for double if no FPU. */
|
/* The 'restore src,%g0,dest' pattern for double if no FPU. */
|
else if (! TARGET_FPU && TARGET_ARCH64 && register_operand (src, DFmode))
|
else if (! TARGET_FPU && TARGET_ARCH64 && register_operand (src, DFmode))
|
return 1;
|
return 1;
|
|
|
/* If we have the 'return' instruction, anything that does not use
|
/* If we have the 'return' instruction, anything that does not use
|
local or output registers and can go into a delay slot wins. */
|
local or output registers and can go into a delay slot wins. */
|
else if (return_p && TARGET_V9 && ! epilogue_renumber (&pat, 1)
|
else if (return_p && TARGET_V9 && ! epilogue_renumber (&pat, 1)
|
&& (get_attr_in_uncond_branch_delay (trial)
|
&& (get_attr_in_uncond_branch_delay (trial)
|
== IN_UNCOND_BRANCH_DELAY_TRUE))
|
== IN_UNCOND_BRANCH_DELAY_TRUE))
|
return 1;
|
return 1;
|
|
|
/* The 'restore src1,src2,dest' pattern for SImode. */
|
/* The 'restore src1,src2,dest' pattern for SImode. */
|
else if (GET_CODE (src) == PLUS
|
else if (GET_CODE (src) == PLUS
|
&& register_operand (XEXP (src, 0), SImode)
|
&& register_operand (XEXP (src, 0), SImode)
|
&& arith_operand (XEXP (src, 1), SImode))
|
&& arith_operand (XEXP (src, 1), SImode))
|
return 1;
|
return 1;
|
|
|
/* The 'restore src1,src2,dest' pattern for DImode. */
|
/* The 'restore src1,src2,dest' pattern for DImode. */
|
else if (GET_CODE (src) == PLUS
|
else if (GET_CODE (src) == PLUS
|
&& register_operand (XEXP (src, 0), DImode)
|
&& register_operand (XEXP (src, 0), DImode)
|
&& arith_double_operand (XEXP (src, 1), DImode))
|
&& arith_double_operand (XEXP (src, 1), DImode))
|
return 1;
|
return 1;
|
|
|
/* The 'restore src1,%lo(src2),dest' pattern. */
|
/* The 'restore src1,%lo(src2),dest' pattern. */
|
else if (GET_CODE (src) == LO_SUM
|
else if (GET_CODE (src) == LO_SUM
|
&& ! TARGET_CM_MEDMID
|
&& ! TARGET_CM_MEDMID
|
&& ((register_operand (XEXP (src, 0), SImode)
|
&& ((register_operand (XEXP (src, 0), SImode)
|
&& immediate_operand (XEXP (src, 1), SImode))
|
&& immediate_operand (XEXP (src, 1), SImode))
|
|| (TARGET_ARCH64
|
|| (TARGET_ARCH64
|
&& register_operand (XEXP (src, 0), DImode)
|
&& register_operand (XEXP (src, 0), DImode)
|
&& immediate_operand (XEXP (src, 1), DImode))))
|
&& immediate_operand (XEXP (src, 1), DImode))))
|
return 1;
|
return 1;
|
|
|
/* The 'restore src,src,dest' pattern. */
|
/* The 'restore src,src,dest' pattern. */
|
else if (GET_CODE (src) == ASHIFT
|
else if (GET_CODE (src) == ASHIFT
|
&& (register_operand (XEXP (src, 0), SImode)
|
&& (register_operand (XEXP (src, 0), SImode)
|
|| register_operand (XEXP (src, 0), DImode))
|
|| register_operand (XEXP (src, 0), DImode))
|
&& XEXP (src, 1) == const1_rtx)
|
&& XEXP (src, 1) == const1_rtx)
|
return 1;
|
return 1;
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Return nonzero if TRIAL can go into the function return's
|
/* Return nonzero if TRIAL can go into the function return's
|
delay slot. */
|
delay slot. */
|
|
|
int
|
int
|
eligible_for_return_delay (rtx trial)
|
eligible_for_return_delay (rtx trial)
|
{
|
{
|
rtx pat;
|
rtx pat;
|
|
|
if (GET_CODE (trial) != INSN || GET_CODE (PATTERN (trial)) != SET)
|
if (GET_CODE (trial) != INSN || GET_CODE (PATTERN (trial)) != SET)
|
return 0;
|
return 0;
|
|
|
if (get_attr_length (trial) != 1)
|
if (get_attr_length (trial) != 1)
|
return 0;
|
return 0;
|
|
|
/* If there are any call-saved registers, we should scan TRIAL if it
|
/* If there are any call-saved registers, we should scan TRIAL if it
|
does not reference them. For now just make it easy. */
|
does not reference them. For now just make it easy. */
|
if (num_gfregs)
|
if (num_gfregs)
|
return 0;
|
return 0;
|
|
|
/* If the function uses __builtin_eh_return, the eh_return machinery
|
/* If the function uses __builtin_eh_return, the eh_return machinery
|
occupies the delay slot. */
|
occupies the delay slot. */
|
if (crtl->calls_eh_return)
|
if (crtl->calls_eh_return)
|
return 0;
|
return 0;
|
|
|
/* In the case of a true leaf function, anything can go into the slot. */
|
/* In the case of a true leaf function, anything can go into the slot. */
|
if (sparc_leaf_function_p)
|
if (sparc_leaf_function_p)
|
return get_attr_in_uncond_branch_delay (trial)
|
return get_attr_in_uncond_branch_delay (trial)
|
== IN_UNCOND_BRANCH_DELAY_TRUE;
|
== IN_UNCOND_BRANCH_DELAY_TRUE;
|
|
|
pat = PATTERN (trial);
|
pat = PATTERN (trial);
|
|
|
/* Otherwise, only operations which can be done in tandem with
|
/* Otherwise, only operations which can be done in tandem with
|
a `restore' or `return' insn can go into the delay slot. */
|
a `restore' or `return' insn can go into the delay slot. */
|
if (GET_CODE (SET_DEST (pat)) != REG
|
if (GET_CODE (SET_DEST (pat)) != REG
|
|| (REGNO (SET_DEST (pat)) >= 8 && REGNO (SET_DEST (pat)) < 24))
|
|| (REGNO (SET_DEST (pat)) >= 8 && REGNO (SET_DEST (pat)) < 24))
|
return 0;
|
return 0;
|
|
|
/* If this instruction sets up floating point register and we have a return
|
/* If this instruction sets up floating point register and we have a return
|
instruction, it can probably go in. But restore will not work
|
instruction, it can probably go in. But restore will not work
|
with FP_REGS. */
|
with FP_REGS. */
|
if (REGNO (SET_DEST (pat)) >= 32)
|
if (REGNO (SET_DEST (pat)) >= 32)
|
return (TARGET_V9
|
return (TARGET_V9
|
&& ! epilogue_renumber (&pat, 1)
|
&& ! epilogue_renumber (&pat, 1)
|
&& (get_attr_in_uncond_branch_delay (trial)
|
&& (get_attr_in_uncond_branch_delay (trial)
|
== IN_UNCOND_BRANCH_DELAY_TRUE));
|
== IN_UNCOND_BRANCH_DELAY_TRUE));
|
|
|
return eligible_for_restore_insn (trial, true);
|
return eligible_for_restore_insn (trial, true);
|
}
|
}
|
|
|
/* Return nonzero if TRIAL can go into the sibling call's
|
/* Return nonzero if TRIAL can go into the sibling call's
|
delay slot. */
|
delay slot. */
|
|
|
int
|
int
|
eligible_for_sibcall_delay (rtx trial)
|
eligible_for_sibcall_delay (rtx trial)
|
{
|
{
|
rtx pat;
|
rtx pat;
|
|
|
if (GET_CODE (trial) != INSN || GET_CODE (PATTERN (trial)) != SET)
|
if (GET_CODE (trial) != INSN || GET_CODE (PATTERN (trial)) != SET)
|
return 0;
|
return 0;
|
|
|
if (get_attr_length (trial) != 1)
|
if (get_attr_length (trial) != 1)
|
return 0;
|
return 0;
|
|
|
pat = PATTERN (trial);
|
pat = PATTERN (trial);
|
|
|
if (sparc_leaf_function_p)
|
if (sparc_leaf_function_p)
|
{
|
{
|
/* If the tail call is done using the call instruction,
|
/* If the tail call is done using the call instruction,
|
we have to restore %o7 in the delay slot. */
|
we have to restore %o7 in the delay slot. */
|
if (LEAF_SIBCALL_SLOT_RESERVED_P)
|
if (LEAF_SIBCALL_SLOT_RESERVED_P)
|
return 0;
|
return 0;
|
|
|
/* %g1 is used to build the function address */
|
/* %g1 is used to build the function address */
|
if (reg_mentioned_p (gen_rtx_REG (Pmode, 1), pat))
|
if (reg_mentioned_p (gen_rtx_REG (Pmode, 1), pat))
|
return 0;
|
return 0;
|
|
|
return 1;
|
return 1;
|
}
|
}
|
|
|
/* Otherwise, only operations which can be done in tandem with
|
/* Otherwise, only operations which can be done in tandem with
|
a `restore' insn can go into the delay slot. */
|
a `restore' insn can go into the delay slot. */
|
if (GET_CODE (SET_DEST (pat)) != REG
|
if (GET_CODE (SET_DEST (pat)) != REG
|
|| (REGNO (SET_DEST (pat)) >= 8 && REGNO (SET_DEST (pat)) < 24)
|
|| (REGNO (SET_DEST (pat)) >= 8 && REGNO (SET_DEST (pat)) < 24)
|
|| REGNO (SET_DEST (pat)) >= 32)
|
|| REGNO (SET_DEST (pat)) >= 32)
|
return 0;
|
return 0;
|
|
|
/* If it mentions %o7, it can't go in, because sibcall will clobber it
|
/* If it mentions %o7, it can't go in, because sibcall will clobber it
|
in most cases. */
|
in most cases. */
|
if (reg_mentioned_p (gen_rtx_REG (Pmode, 15), pat))
|
if (reg_mentioned_p (gen_rtx_REG (Pmode, 15), pat))
|
return 0;
|
return 0;
|
|
|
return eligible_for_restore_insn (trial, false);
|
return eligible_for_restore_insn (trial, false);
|
}
|
}
|
|
|
int
|
int
|
short_branch (int uid1, int uid2)
|
short_branch (int uid1, int uid2)
|
{
|
{
|
int delta = INSN_ADDRESSES (uid1) - INSN_ADDRESSES (uid2);
|
int delta = INSN_ADDRESSES (uid1) - INSN_ADDRESSES (uid2);
|
|
|
/* Leave a few words of "slop". */
|
/* Leave a few words of "slop". */
|
if (delta >= -1023 && delta <= 1022)
|
if (delta >= -1023 && delta <= 1022)
|
return 1;
|
return 1;
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Return nonzero if REG is not used after INSN.
|
/* Return nonzero if REG is not used after INSN.
|
We assume REG is a reload reg, and therefore does
|
We assume REG is a reload reg, and therefore does
|
not live past labels or calls or jumps. */
|
not live past labels or calls or jumps. */
|
int
|
int
|
reg_unused_after (rtx reg, rtx insn)
|
reg_unused_after (rtx reg, rtx insn)
|
{
|
{
|
enum rtx_code code, prev_code = UNKNOWN;
|
enum rtx_code code, prev_code = UNKNOWN;
|
|
|
while ((insn = NEXT_INSN (insn)))
|
while ((insn = NEXT_INSN (insn)))
|
{
|
{
|
if (prev_code == CALL_INSN && call_used_regs[REGNO (reg)])
|
if (prev_code == CALL_INSN && call_used_regs[REGNO (reg)])
|
return 1;
|
return 1;
|
|
|
code = GET_CODE (insn);
|
code = GET_CODE (insn);
|
if (GET_CODE (insn) == CODE_LABEL)
|
if (GET_CODE (insn) == CODE_LABEL)
|
return 1;
|
return 1;
|
|
|
if (INSN_P (insn))
|
if (INSN_P (insn))
|
{
|
{
|
rtx set = single_set (insn);
|
rtx set = single_set (insn);
|
int in_src = set && reg_overlap_mentioned_p (reg, SET_SRC (set));
|
int in_src = set && reg_overlap_mentioned_p (reg, SET_SRC (set));
|
if (set && in_src)
|
if (set && in_src)
|
return 0;
|
return 0;
|
if (set && reg_overlap_mentioned_p (reg, SET_DEST (set)))
|
if (set && reg_overlap_mentioned_p (reg, SET_DEST (set)))
|
return 1;
|
return 1;
|
if (set == 0 && reg_overlap_mentioned_p (reg, PATTERN (insn)))
|
if (set == 0 && reg_overlap_mentioned_p (reg, PATTERN (insn)))
|
return 0;
|
return 0;
|
}
|
}
|
prev_code = code;
|
prev_code = code;
|
}
|
}
|
return 1;
|
return 1;
|
}
|
}
|
|
|
/* Determine if it's legal to put X into the constant pool. This
|
/* Determine if it's legal to put X into the constant pool. This
|
is not possible if X contains the address of a symbol that is
|
is not possible if X contains the address of a symbol that is
|
not constant (TLS) or not known at final link time (PIC). */
|
not constant (TLS) or not known at final link time (PIC). */
|
|
|
static bool
|
static bool
|
sparc_cannot_force_const_mem (rtx x)
|
sparc_cannot_force_const_mem (rtx x)
|
{
|
{
|
switch (GET_CODE (x))
|
switch (GET_CODE (x))
|
{
|
{
|
case CONST_INT:
|
case CONST_INT:
|
case CONST_DOUBLE:
|
case CONST_DOUBLE:
|
case CONST_VECTOR:
|
case CONST_VECTOR:
|
/* Accept all non-symbolic constants. */
|
/* Accept all non-symbolic constants. */
|
return false;
|
return false;
|
|
|
case LABEL_REF:
|
case LABEL_REF:
|
/* Labels are OK iff we are non-PIC. */
|
/* Labels are OK iff we are non-PIC. */
|
return flag_pic != 0;
|
return flag_pic != 0;
|
|
|
case SYMBOL_REF:
|
case SYMBOL_REF:
|
/* 'Naked' TLS symbol references are never OK,
|
/* 'Naked' TLS symbol references are never OK,
|
non-TLS symbols are OK iff we are non-PIC. */
|
non-TLS symbols are OK iff we are non-PIC. */
|
if (SYMBOL_REF_TLS_MODEL (x))
|
if (SYMBOL_REF_TLS_MODEL (x))
|
return true;
|
return true;
|
else
|
else
|
return flag_pic != 0;
|
return flag_pic != 0;
|
|
|
case CONST:
|
case CONST:
|
return sparc_cannot_force_const_mem (XEXP (x, 0));
|
return sparc_cannot_force_const_mem (XEXP (x, 0));
|
case PLUS:
|
case PLUS:
|
case MINUS:
|
case MINUS:
|
return sparc_cannot_force_const_mem (XEXP (x, 0))
|
return sparc_cannot_force_const_mem (XEXP (x, 0))
|
|| sparc_cannot_force_const_mem (XEXP (x, 1));
|
|| sparc_cannot_force_const_mem (XEXP (x, 1));
|
case UNSPEC:
|
case UNSPEC:
|
return true;
|
return true;
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
}
|
}
|
|
|
/* PIC support. */
|
/* PIC support. */
|
static GTY(()) bool pic_helper_needed = false;
|
static GTY(()) bool pic_helper_needed = false;
|
static GTY(()) rtx pic_helper_symbol;
|
static GTY(()) rtx pic_helper_symbol;
|
static GTY(()) rtx global_offset_table;
|
static GTY(()) rtx global_offset_table;
|
|
|
/* Ensure that we are not using patterns that are not OK with PIC. */
|
/* Ensure that we are not using patterns that are not OK with PIC. */
|
|
|
int
|
int
|
check_pic (int i)
|
check_pic (int i)
|
{
|
{
|
switch (flag_pic)
|
switch (flag_pic)
|
{
|
{
|
case 1:
|
case 1:
|
gcc_assert (GET_CODE (recog_data.operand[i]) != SYMBOL_REF
|
gcc_assert (GET_CODE (recog_data.operand[i]) != SYMBOL_REF
|
&& (GET_CODE (recog_data.operand[i]) != CONST
|
&& (GET_CODE (recog_data.operand[i]) != CONST
|
|| (GET_CODE (XEXP (recog_data.operand[i], 0)) == MINUS
|
|| (GET_CODE (XEXP (recog_data.operand[i], 0)) == MINUS
|
&& (XEXP (XEXP (recog_data.operand[i], 0), 0)
|
&& (XEXP (XEXP (recog_data.operand[i], 0), 0)
|
== global_offset_table)
|
== global_offset_table)
|
&& (GET_CODE (XEXP (XEXP (recog_data.operand[i], 0), 1))
|
&& (GET_CODE (XEXP (XEXP (recog_data.operand[i], 0), 1))
|
== CONST))));
|
== CONST))));
|
case 2:
|
case 2:
|
default:
|
default:
|
return 1;
|
return 1;
|
}
|
}
|
}
|
}
|
|
|
/* Return true if X is an address which needs a temporary register when
|
/* Return true if X is an address which needs a temporary register when
|
reloaded while generating PIC code. */
|
reloaded while generating PIC code. */
|
|
|
int
|
int
|
pic_address_needs_scratch (rtx x)
|
pic_address_needs_scratch (rtx x)
|
{
|
{
|
/* An address which is a symbolic plus a non SMALL_INT needs a temp reg. */
|
/* An address which is a symbolic plus a non SMALL_INT needs a temp reg. */
|
if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS
|
if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS
|
&& GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF
|
&& GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF
|
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
|
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
|
&& ! SMALL_INT (XEXP (XEXP (x, 0), 1)))
|
&& ! SMALL_INT (XEXP (XEXP (x, 0), 1)))
|
return 1;
|
return 1;
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Determine if a given RTX is a valid constant. We already know this
|
/* Determine if a given RTX is a valid constant. We already know this
|
satisfies CONSTANT_P. */
|
satisfies CONSTANT_P. */
|
|
|
bool
|
bool
|
legitimate_constant_p (rtx x)
|
legitimate_constant_p (rtx x)
|
{
|
{
|
switch (GET_CODE (x))
|
switch (GET_CODE (x))
|
{
|
{
|
case CONST:
|
case CONST:
|
case SYMBOL_REF:
|
case SYMBOL_REF:
|
if (sparc_tls_referenced_p (x))
|
if (sparc_tls_referenced_p (x))
|
return false;
|
return false;
|
break;
|
break;
|
|
|
case CONST_DOUBLE:
|
case CONST_DOUBLE:
|
if (GET_MODE (x) == VOIDmode)
|
if (GET_MODE (x) == VOIDmode)
|
return true;
|
return true;
|
|
|
/* Floating point constants are generally not ok.
|
/* Floating point constants are generally not ok.
|
The only exception is 0.0 in VIS. */
|
The only exception is 0.0 in VIS. */
|
if (TARGET_VIS
|
if (TARGET_VIS
|
&& SCALAR_FLOAT_MODE_P (GET_MODE (x))
|
&& SCALAR_FLOAT_MODE_P (GET_MODE (x))
|
&& const_zero_operand (x, GET_MODE (x)))
|
&& const_zero_operand (x, GET_MODE (x)))
|
return true;
|
return true;
|
|
|
return false;
|
return false;
|
|
|
case CONST_VECTOR:
|
case CONST_VECTOR:
|
/* Vector constants are generally not ok.
|
/* Vector constants are generally not ok.
|
The only exception is 0 in VIS. */
|
The only exception is 0 in VIS. */
|
if (TARGET_VIS
|
if (TARGET_VIS
|
&& const_zero_operand (x, GET_MODE (x)))
|
&& const_zero_operand (x, GET_MODE (x)))
|
return true;
|
return true;
|
|
|
return false;
|
return false;
|
|
|
default:
|
default:
|
break;
|
break;
|
}
|
}
|
|
|
return true;
|
return true;
|
}
|
}
|
|
|
/* Determine if a given RTX is a valid constant address. */
|
/* Determine if a given RTX is a valid constant address. */
|
|
|
bool
|
bool
|
constant_address_p (rtx x)
|
constant_address_p (rtx x)
|
{
|
{
|
switch (GET_CODE (x))
|
switch (GET_CODE (x))
|
{
|
{
|
case LABEL_REF:
|
case LABEL_REF:
|
case CONST_INT:
|
case CONST_INT:
|
case HIGH:
|
case HIGH:
|
return true;
|
return true;
|
|
|
case CONST:
|
case CONST:
|
if (flag_pic && pic_address_needs_scratch (x))
|
if (flag_pic && pic_address_needs_scratch (x))
|
return false;
|
return false;
|
return legitimate_constant_p (x);
|
return legitimate_constant_p (x);
|
|
|
case SYMBOL_REF:
|
case SYMBOL_REF:
|
return !flag_pic && legitimate_constant_p (x);
|
return !flag_pic && legitimate_constant_p (x);
|
|
|
default:
|
default:
|
return false;
|
return false;
|
}
|
}
|
}
|
}
|
|
|
/* Nonzero if the constant value X is a legitimate general operand
|
/* Nonzero if the constant value X is a legitimate general operand
|
when generating PIC code. It is given that flag_pic is on and
|
when generating PIC code. It is given that flag_pic is on and
|
that X satisfies CONSTANT_P or is a CONST_DOUBLE. */
|
that X satisfies CONSTANT_P or is a CONST_DOUBLE. */
|
|
|
bool
|
bool
|
legitimate_pic_operand_p (rtx x)
|
legitimate_pic_operand_p (rtx x)
|
{
|
{
|
if (pic_address_needs_scratch (x))
|
if (pic_address_needs_scratch (x))
|
return false;
|
return false;
|
if (sparc_tls_referenced_p (x))
|
if (sparc_tls_referenced_p (x))
|
return false;
|
return false;
|
return true;
|
return true;
|
}
|
}
|
|
|
/* Return nonzero if ADDR is a valid memory address.
|
/* Return nonzero if ADDR is a valid memory address.
|
STRICT specifies whether strict register checking applies. */
|
STRICT specifies whether strict register checking applies. */
|
|
|
static bool
|
static bool
|
sparc_legitimate_address_p (enum machine_mode mode, rtx addr, bool strict)
|
sparc_legitimate_address_p (enum machine_mode mode, rtx addr, bool strict)
|
{
|
{
|
rtx rs1 = NULL, rs2 = NULL, imm1 = NULL;
|
rtx rs1 = NULL, rs2 = NULL, imm1 = NULL;
|
|
|
if (REG_P (addr) || GET_CODE (addr) == SUBREG)
|
if (REG_P (addr) || GET_CODE (addr) == SUBREG)
|
rs1 = addr;
|
rs1 = addr;
|
else if (GET_CODE (addr) == PLUS)
|
else if (GET_CODE (addr) == PLUS)
|
{
|
{
|
rs1 = XEXP (addr, 0);
|
rs1 = XEXP (addr, 0);
|
rs2 = XEXP (addr, 1);
|
rs2 = XEXP (addr, 1);
|
|
|
/* Canonicalize. REG comes first, if there are no regs,
|
/* Canonicalize. REG comes first, if there are no regs,
|
LO_SUM comes first. */
|
LO_SUM comes first. */
|
if (!REG_P (rs1)
|
if (!REG_P (rs1)
|
&& GET_CODE (rs1) != SUBREG
|
&& GET_CODE (rs1) != SUBREG
|
&& (REG_P (rs2)
|
&& (REG_P (rs2)
|
|| GET_CODE (rs2) == SUBREG
|
|| GET_CODE (rs2) == SUBREG
|
|| (GET_CODE (rs2) == LO_SUM && GET_CODE (rs1) != LO_SUM)))
|
|| (GET_CODE (rs2) == LO_SUM && GET_CODE (rs1) != LO_SUM)))
|
{
|
{
|
rs1 = XEXP (addr, 1);
|
rs1 = XEXP (addr, 1);
|
rs2 = XEXP (addr, 0);
|
rs2 = XEXP (addr, 0);
|
}
|
}
|
|
|
if ((flag_pic == 1
|
if ((flag_pic == 1
|
&& rs1 == pic_offset_table_rtx
|
&& rs1 == pic_offset_table_rtx
|
&& !REG_P (rs2)
|
&& !REG_P (rs2)
|
&& GET_CODE (rs2) != SUBREG
|
&& GET_CODE (rs2) != SUBREG
|
&& GET_CODE (rs2) != LO_SUM
|
&& GET_CODE (rs2) != LO_SUM
|
&& GET_CODE (rs2) != MEM
|
&& GET_CODE (rs2) != MEM
|
&& !(GET_CODE (rs2) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (rs2))
|
&& !(GET_CODE (rs2) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (rs2))
|
&& (! symbolic_operand (rs2, VOIDmode) || mode == Pmode)
|
&& (! symbolic_operand (rs2, VOIDmode) || mode == Pmode)
|
&& (GET_CODE (rs2) != CONST_INT || SMALL_INT (rs2)))
|
&& (GET_CODE (rs2) != CONST_INT || SMALL_INT (rs2)))
|
|| ((REG_P (rs1)
|
|| ((REG_P (rs1)
|
|| GET_CODE (rs1) == SUBREG)
|
|| GET_CODE (rs1) == SUBREG)
|
&& RTX_OK_FOR_OFFSET_P (rs2)))
|
&& RTX_OK_FOR_OFFSET_P (rs2)))
|
{
|
{
|
imm1 = rs2;
|
imm1 = rs2;
|
rs2 = NULL;
|
rs2 = NULL;
|
}
|
}
|
else if ((REG_P (rs1) || GET_CODE (rs1) == SUBREG)
|
else if ((REG_P (rs1) || GET_CODE (rs1) == SUBREG)
|
&& (REG_P (rs2) || GET_CODE (rs2) == SUBREG))
|
&& (REG_P (rs2) || GET_CODE (rs2) == SUBREG))
|
{
|
{
|
/* We prohibit REG + REG for TFmode when there are no quad move insns
|
/* We prohibit REG + REG for TFmode when there are no quad move insns
|
and we consequently need to split. We do this because REG+REG
|
and we consequently need to split. We do this because REG+REG
|
is not an offsettable address. If we get the situation in reload
|
is not an offsettable address. If we get the situation in reload
|
where source and destination of a movtf pattern are both MEMs with
|
where source and destination of a movtf pattern are both MEMs with
|
REG+REG address, then only one of them gets converted to an
|
REG+REG address, then only one of them gets converted to an
|
offsettable address. */
|
offsettable address. */
|
if (mode == TFmode
|
if (mode == TFmode
|
&& ! (TARGET_FPU && TARGET_ARCH64 && TARGET_HARD_QUAD))
|
&& ! (TARGET_FPU && TARGET_ARCH64 && TARGET_HARD_QUAD))
|
return 0;
|
return 0;
|
|
|
/* We prohibit REG + REG on ARCH32 if not optimizing for
|
/* We prohibit REG + REG on ARCH32 if not optimizing for
|
DFmode/DImode because then mem_min_alignment is likely to be zero
|
DFmode/DImode because then mem_min_alignment is likely to be zero
|
after reload and the forced split would lack a matching splitter
|
after reload and the forced split would lack a matching splitter
|
pattern. */
|
pattern. */
|
if (TARGET_ARCH32 && !optimize
|
if (TARGET_ARCH32 && !optimize
|
&& (mode == DFmode || mode == DImode))
|
&& (mode == DFmode || mode == DImode))
|
return 0;
|
return 0;
|
}
|
}
|
else if (USE_AS_OFFSETABLE_LO10
|
else if (USE_AS_OFFSETABLE_LO10
|
&& GET_CODE (rs1) == LO_SUM
|
&& GET_CODE (rs1) == LO_SUM
|
&& TARGET_ARCH64
|
&& TARGET_ARCH64
|
&& ! TARGET_CM_MEDMID
|
&& ! TARGET_CM_MEDMID
|
&& RTX_OK_FOR_OLO10_P (rs2))
|
&& RTX_OK_FOR_OLO10_P (rs2))
|
{
|
{
|
rs2 = NULL;
|
rs2 = NULL;
|
imm1 = XEXP (rs1, 1);
|
imm1 = XEXP (rs1, 1);
|
rs1 = XEXP (rs1, 0);
|
rs1 = XEXP (rs1, 0);
|
if (!CONSTANT_P (imm1)
|
if (!CONSTANT_P (imm1)
|
|| (GET_CODE (rs1) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (rs1)))
|
|| (GET_CODE (rs1) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (rs1)))
|
return 0;
|
return 0;
|
}
|
}
|
}
|
}
|
else if (GET_CODE (addr) == LO_SUM)
|
else if (GET_CODE (addr) == LO_SUM)
|
{
|
{
|
rs1 = XEXP (addr, 0);
|
rs1 = XEXP (addr, 0);
|
imm1 = XEXP (addr, 1);
|
imm1 = XEXP (addr, 1);
|
|
|
if (!CONSTANT_P (imm1)
|
if (!CONSTANT_P (imm1)
|
|| (GET_CODE (rs1) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (rs1)))
|
|| (GET_CODE (rs1) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (rs1)))
|
return 0;
|
return 0;
|
|
|
/* We can't allow TFmode in 32-bit mode, because an offset greater
|
/* We can't allow TFmode in 32-bit mode, because an offset greater
|
than the alignment (8) may cause the LO_SUM to overflow. */
|
than the alignment (8) may cause the LO_SUM to overflow. */
|
if (mode == TFmode && TARGET_ARCH32)
|
if (mode == TFmode && TARGET_ARCH32)
|
return 0;
|
return 0;
|
}
|
}
|
else if (GET_CODE (addr) == CONST_INT && SMALL_INT (addr))
|
else if (GET_CODE (addr) == CONST_INT && SMALL_INT (addr))
|
return 1;
|
return 1;
|
else
|
else
|
return 0;
|
return 0;
|
|
|
if (GET_CODE (rs1) == SUBREG)
|
if (GET_CODE (rs1) == SUBREG)
|
rs1 = SUBREG_REG (rs1);
|
rs1 = SUBREG_REG (rs1);
|
if (!REG_P (rs1))
|
if (!REG_P (rs1))
|
return 0;
|
return 0;
|
|
|
if (rs2)
|
if (rs2)
|
{
|
{
|
if (GET_CODE (rs2) == SUBREG)
|
if (GET_CODE (rs2) == SUBREG)
|
rs2 = SUBREG_REG (rs2);
|
rs2 = SUBREG_REG (rs2);
|
if (!REG_P (rs2))
|
if (!REG_P (rs2))
|
return 0;
|
return 0;
|
}
|
}
|
|
|
if (strict)
|
if (strict)
|
{
|
{
|
if (!REGNO_OK_FOR_BASE_P (REGNO (rs1))
|
if (!REGNO_OK_FOR_BASE_P (REGNO (rs1))
|
|| (rs2 && !REGNO_OK_FOR_BASE_P (REGNO (rs2))))
|
|| (rs2 && !REGNO_OK_FOR_BASE_P (REGNO (rs2))))
|
return 0;
|
return 0;
|
}
|
}
|
else
|
else
|
{
|
{
|
if ((REGNO (rs1) >= 32
|
if ((REGNO (rs1) >= 32
|
&& REGNO (rs1) != FRAME_POINTER_REGNUM
|
&& REGNO (rs1) != FRAME_POINTER_REGNUM
|
&& REGNO (rs1) < FIRST_PSEUDO_REGISTER)
|
&& REGNO (rs1) < FIRST_PSEUDO_REGISTER)
|
|| (rs2
|
|| (rs2
|
&& (REGNO (rs2) >= 32
|
&& (REGNO (rs2) >= 32
|
&& REGNO (rs2) != FRAME_POINTER_REGNUM
|
&& REGNO (rs2) != FRAME_POINTER_REGNUM
|
&& REGNO (rs2) < FIRST_PSEUDO_REGISTER)))
|
&& REGNO (rs2) < FIRST_PSEUDO_REGISTER)))
|
return 0;
|
return 0;
|
}
|
}
|
return 1;
|
return 1;
|
}
|
}
|
|
|
/* Construct the SYMBOL_REF for the tls_get_offset function. */
|
/* Construct the SYMBOL_REF for the tls_get_offset function. */
|
|
|
static GTY(()) rtx sparc_tls_symbol;
|
static GTY(()) rtx sparc_tls_symbol;
|
|
|
static rtx
|
static rtx
|
sparc_tls_get_addr (void)
|
sparc_tls_get_addr (void)
|
{
|
{
|
if (!sparc_tls_symbol)
|
if (!sparc_tls_symbol)
|
sparc_tls_symbol = gen_rtx_SYMBOL_REF (Pmode, "__tls_get_addr");
|
sparc_tls_symbol = gen_rtx_SYMBOL_REF (Pmode, "__tls_get_addr");
|
|
|
return sparc_tls_symbol;
|
return sparc_tls_symbol;
|
}
|
}
|
|
|
static rtx
|
static rtx
|
sparc_tls_got (void)
|
sparc_tls_got (void)
|
{
|
{
|
rtx temp;
|
rtx temp;
|
if (flag_pic)
|
if (flag_pic)
|
{
|
{
|
crtl->uses_pic_offset_table = 1;
|
crtl->uses_pic_offset_table = 1;
|
return pic_offset_table_rtx;
|
return pic_offset_table_rtx;
|
}
|
}
|
|
|
if (!global_offset_table)
|
if (!global_offset_table)
|
global_offset_table = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
|
global_offset_table = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
|
temp = gen_reg_rtx (Pmode);
|
temp = gen_reg_rtx (Pmode);
|
emit_move_insn (temp, global_offset_table);
|
emit_move_insn (temp, global_offset_table);
|
return temp;
|
return temp;
|
}
|
}
|
|
|
/* Return true if X contains a thread-local symbol. */
|
/* Return true if X contains a thread-local symbol. */
|
|
|
static bool
|
static bool
|
sparc_tls_referenced_p (rtx x)
|
sparc_tls_referenced_p (rtx x)
|
{
|
{
|
if (!TARGET_HAVE_TLS)
|
if (!TARGET_HAVE_TLS)
|
return false;
|
return false;
|
|
|
if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS)
|
if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS)
|
x = XEXP (XEXP (x, 0), 0);
|
x = XEXP (XEXP (x, 0), 0);
|
|
|
if (GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (x))
|
if (GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (x))
|
return true;
|
return true;
|
|
|
/* That's all we handle in legitimize_tls_address for now. */
|
/* That's all we handle in legitimize_tls_address for now. */
|
return false;
|
return false;
|
}
|
}
|
|
|
/* ADDR contains a thread-local SYMBOL_REF. Generate code to compute
|
/* ADDR contains a thread-local SYMBOL_REF. Generate code to compute
|
this (thread-local) address. */
|
this (thread-local) address. */
|
|
|
static rtx
|
static rtx
|
legitimize_tls_address (rtx addr)
|
legitimize_tls_address (rtx addr)
|
{
|
{
|
rtx temp1, temp2, temp3, ret, o0, got, insn;
|
rtx temp1, temp2, temp3, ret, o0, got, insn;
|
|
|
gcc_assert (can_create_pseudo_p ());
|
gcc_assert (can_create_pseudo_p ());
|
|
|
if (GET_CODE (addr) == SYMBOL_REF)
|
if (GET_CODE (addr) == SYMBOL_REF)
|
switch (SYMBOL_REF_TLS_MODEL (addr))
|
switch (SYMBOL_REF_TLS_MODEL (addr))
|
{
|
{
|
case TLS_MODEL_GLOBAL_DYNAMIC:
|
case TLS_MODEL_GLOBAL_DYNAMIC:
|
start_sequence ();
|
start_sequence ();
|
temp1 = gen_reg_rtx (SImode);
|
temp1 = gen_reg_rtx (SImode);
|
temp2 = gen_reg_rtx (SImode);
|
temp2 = gen_reg_rtx (SImode);
|
ret = gen_reg_rtx (Pmode);
|
ret = gen_reg_rtx (Pmode);
|
o0 = gen_rtx_REG (Pmode, 8);
|
o0 = gen_rtx_REG (Pmode, 8);
|
got = sparc_tls_got ();
|
got = sparc_tls_got ();
|
emit_insn (gen_tgd_hi22 (temp1, addr));
|
emit_insn (gen_tgd_hi22 (temp1, addr));
|
emit_insn (gen_tgd_lo10 (temp2, temp1, addr));
|
emit_insn (gen_tgd_lo10 (temp2, temp1, addr));
|
if (TARGET_ARCH32)
|
if (TARGET_ARCH32)
|
{
|
{
|
emit_insn (gen_tgd_add32 (o0, got, temp2, addr));
|
emit_insn (gen_tgd_add32 (o0, got, temp2, addr));
|
insn = emit_call_insn (gen_tgd_call32 (o0, sparc_tls_get_addr (),
|
insn = emit_call_insn (gen_tgd_call32 (o0, sparc_tls_get_addr (),
|
addr, const1_rtx));
|
addr, const1_rtx));
|
}
|
}
|
else
|
else
|
{
|
{
|
emit_insn (gen_tgd_add64 (o0, got, temp2, addr));
|
emit_insn (gen_tgd_add64 (o0, got, temp2, addr));
|
insn = emit_call_insn (gen_tgd_call64 (o0, sparc_tls_get_addr (),
|
insn = emit_call_insn (gen_tgd_call64 (o0, sparc_tls_get_addr (),
|
addr, const1_rtx));
|
addr, const1_rtx));
|
}
|
}
|
CALL_INSN_FUNCTION_USAGE (insn)
|
CALL_INSN_FUNCTION_USAGE (insn)
|
= gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_USE (VOIDmode, o0),
|
= gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_USE (VOIDmode, o0),
|
CALL_INSN_FUNCTION_USAGE (insn));
|
CALL_INSN_FUNCTION_USAGE (insn));
|
insn = get_insns ();
|
insn = get_insns ();
|
end_sequence ();
|
end_sequence ();
|
emit_libcall_block (insn, ret, o0, addr);
|
emit_libcall_block (insn, ret, o0, addr);
|
break;
|
break;
|
|
|
case TLS_MODEL_LOCAL_DYNAMIC:
|
case TLS_MODEL_LOCAL_DYNAMIC:
|
start_sequence ();
|
start_sequence ();
|
temp1 = gen_reg_rtx (SImode);
|
temp1 = gen_reg_rtx (SImode);
|
temp2 = gen_reg_rtx (SImode);
|
temp2 = gen_reg_rtx (SImode);
|
temp3 = gen_reg_rtx (Pmode);
|
temp3 = gen_reg_rtx (Pmode);
|
ret = gen_reg_rtx (Pmode);
|
ret = gen_reg_rtx (Pmode);
|
o0 = gen_rtx_REG (Pmode, 8);
|
o0 = gen_rtx_REG (Pmode, 8);
|
got = sparc_tls_got ();
|
got = sparc_tls_got ();
|
emit_insn (gen_tldm_hi22 (temp1));
|
emit_insn (gen_tldm_hi22 (temp1));
|
emit_insn (gen_tldm_lo10 (temp2, temp1));
|
emit_insn (gen_tldm_lo10 (temp2, temp1));
|
if (TARGET_ARCH32)
|
if (TARGET_ARCH32)
|
{
|
{
|
emit_insn (gen_tldm_add32 (o0, got, temp2));
|
emit_insn (gen_tldm_add32 (o0, got, temp2));
|
insn = emit_call_insn (gen_tldm_call32 (o0, sparc_tls_get_addr (),
|
insn = emit_call_insn (gen_tldm_call32 (o0, sparc_tls_get_addr (),
|
const1_rtx));
|
const1_rtx));
|
}
|
}
|
else
|
else
|
{
|
{
|
emit_insn (gen_tldm_add64 (o0, got, temp2));
|
emit_insn (gen_tldm_add64 (o0, got, temp2));
|
insn = emit_call_insn (gen_tldm_call64 (o0, sparc_tls_get_addr (),
|
insn = emit_call_insn (gen_tldm_call64 (o0, sparc_tls_get_addr (),
|
const1_rtx));
|
const1_rtx));
|
}
|
}
|
CALL_INSN_FUNCTION_USAGE (insn)
|
CALL_INSN_FUNCTION_USAGE (insn)
|
= gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_USE (VOIDmode, o0),
|
= gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_USE (VOIDmode, o0),
|
CALL_INSN_FUNCTION_USAGE (insn));
|
CALL_INSN_FUNCTION_USAGE (insn));
|
insn = get_insns ();
|
insn = get_insns ();
|
end_sequence ();
|
end_sequence ();
|
emit_libcall_block (insn, temp3, o0,
|
emit_libcall_block (insn, temp3, o0,
|
gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx),
|
gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx),
|
UNSPEC_TLSLD_BASE));
|
UNSPEC_TLSLD_BASE));
|
temp1 = gen_reg_rtx (SImode);
|
temp1 = gen_reg_rtx (SImode);
|
temp2 = gen_reg_rtx (SImode);
|
temp2 = gen_reg_rtx (SImode);
|
emit_insn (gen_tldo_hix22 (temp1, addr));
|
emit_insn (gen_tldo_hix22 (temp1, addr));
|
emit_insn (gen_tldo_lox10 (temp2, temp1, addr));
|
emit_insn (gen_tldo_lox10 (temp2, temp1, addr));
|
if (TARGET_ARCH32)
|
if (TARGET_ARCH32)
|
emit_insn (gen_tldo_add32 (ret, temp3, temp2, addr));
|
emit_insn (gen_tldo_add32 (ret, temp3, temp2, addr));
|
else
|
else
|
emit_insn (gen_tldo_add64 (ret, temp3, temp2, addr));
|
emit_insn (gen_tldo_add64 (ret, temp3, temp2, addr));
|
break;
|
break;
|
|
|
case TLS_MODEL_INITIAL_EXEC:
|
case TLS_MODEL_INITIAL_EXEC:
|
temp1 = gen_reg_rtx (SImode);
|
temp1 = gen_reg_rtx (SImode);
|
temp2 = gen_reg_rtx (SImode);
|
temp2 = gen_reg_rtx (SImode);
|
temp3 = gen_reg_rtx (Pmode);
|
temp3 = gen_reg_rtx (Pmode);
|
got = sparc_tls_got ();
|
got = sparc_tls_got ();
|
emit_insn (gen_tie_hi22 (temp1, addr));
|
emit_insn (gen_tie_hi22 (temp1, addr));
|
emit_insn (gen_tie_lo10 (temp2, temp1, addr));
|
emit_insn (gen_tie_lo10 (temp2, temp1, addr));
|
if (TARGET_ARCH32)
|
if (TARGET_ARCH32)
|
emit_insn (gen_tie_ld32 (temp3, got, temp2, addr));
|
emit_insn (gen_tie_ld32 (temp3, got, temp2, addr));
|
else
|
else
|
emit_insn (gen_tie_ld64 (temp3, got, temp2, addr));
|
emit_insn (gen_tie_ld64 (temp3, got, temp2, addr));
|
if (TARGET_SUN_TLS)
|
if (TARGET_SUN_TLS)
|
{
|
{
|
ret = gen_reg_rtx (Pmode);
|
ret = gen_reg_rtx (Pmode);
|
if (TARGET_ARCH32)
|
if (TARGET_ARCH32)
|
emit_insn (gen_tie_add32 (ret, gen_rtx_REG (Pmode, 7),
|
emit_insn (gen_tie_add32 (ret, gen_rtx_REG (Pmode, 7),
|
temp3, addr));
|
temp3, addr));
|
else
|
else
|
emit_insn (gen_tie_add64 (ret, gen_rtx_REG (Pmode, 7),
|
emit_insn (gen_tie_add64 (ret, gen_rtx_REG (Pmode, 7),
|
temp3, addr));
|
temp3, addr));
|
}
|
}
|
else
|
else
|
ret = gen_rtx_PLUS (Pmode, gen_rtx_REG (Pmode, 7), temp3);
|
ret = gen_rtx_PLUS (Pmode, gen_rtx_REG (Pmode, 7), temp3);
|
break;
|
break;
|
|
|
case TLS_MODEL_LOCAL_EXEC:
|
case TLS_MODEL_LOCAL_EXEC:
|
temp1 = gen_reg_rtx (Pmode);
|
temp1 = gen_reg_rtx (Pmode);
|
temp2 = gen_reg_rtx (Pmode);
|
temp2 = gen_reg_rtx (Pmode);
|
if (TARGET_ARCH32)
|
if (TARGET_ARCH32)
|
{
|
{
|
emit_insn (gen_tle_hix22_sp32 (temp1, addr));
|
emit_insn (gen_tle_hix22_sp32 (temp1, addr));
|
emit_insn (gen_tle_lox10_sp32 (temp2, temp1, addr));
|
emit_insn (gen_tle_lox10_sp32 (temp2, temp1, addr));
|
}
|
}
|
else
|
else
|
{
|
{
|
emit_insn (gen_tle_hix22_sp64 (temp1, addr));
|
emit_insn (gen_tle_hix22_sp64 (temp1, addr));
|
emit_insn (gen_tle_lox10_sp64 (temp2, temp1, addr));
|
emit_insn (gen_tle_lox10_sp64 (temp2, temp1, addr));
|
}
|
}
|
ret = gen_rtx_PLUS (Pmode, gen_rtx_REG (Pmode, 7), temp2);
|
ret = gen_rtx_PLUS (Pmode, gen_rtx_REG (Pmode, 7), temp2);
|
break;
|
break;
|
|
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
|
|
else if (GET_CODE (addr) == CONST)
|
else if (GET_CODE (addr) == CONST)
|
{
|
{
|
rtx base, offset;
|
rtx base, offset;
|
|
|
gcc_assert (GET_CODE (XEXP (addr, 0)) == PLUS);
|
gcc_assert (GET_CODE (XEXP (addr, 0)) == PLUS);
|
|
|
base = legitimize_tls_address (XEXP (XEXP (addr, 0), 0));
|
base = legitimize_tls_address (XEXP (XEXP (addr, 0), 0));
|
offset = XEXP (XEXP (addr, 0), 1);
|
offset = XEXP (XEXP (addr, 0), 1);
|
|
|
base = force_operand (base, NULL_RTX);
|
base = force_operand (base, NULL_RTX);
|
if (!(GET_CODE (offset) == CONST_INT && SMALL_INT (offset)))
|
if (!(GET_CODE (offset) == CONST_INT && SMALL_INT (offset)))
|
offset = force_reg (Pmode, offset);
|
offset = force_reg (Pmode, offset);
|
ret = gen_rtx_PLUS (Pmode, base, offset);
|
ret = gen_rtx_PLUS (Pmode, base, offset);
|
}
|
}
|
|
|
else
|
else
|
gcc_unreachable (); /* for now ... */
|
gcc_unreachable (); /* for now ... */
|
|
|
return ret;
|
return ret;
|
}
|
}
|
|
|
/* Legitimize PIC addresses. If the address is already position-independent,
|
/* Legitimize PIC addresses. If the address is already position-independent,
|
we return ORIG. Newly generated position-independent addresses go into a
|
we return ORIG. Newly generated position-independent addresses go into a
|
reg. This is REG if nonzero, otherwise we allocate register(s) as
|
reg. This is REG if nonzero, otherwise we allocate register(s) as
|
necessary. */
|
necessary. */
|
|
|
static rtx
|
static rtx
|
legitimize_pic_address (rtx orig, rtx reg)
|
legitimize_pic_address (rtx orig, rtx reg)
|
{
|
{
|
bool gotdata_op = false;
|
bool gotdata_op = false;
|
|
|
if (GET_CODE (orig) == SYMBOL_REF
|
if (GET_CODE (orig) == SYMBOL_REF
|
/* See the comment in sparc_expand_move. */
|
/* See the comment in sparc_expand_move. */
|
|| (TARGET_VXWORKS_RTP && GET_CODE (orig) == LABEL_REF))
|
|| (TARGET_VXWORKS_RTP && GET_CODE (orig) == LABEL_REF))
|
{
|
{
|
rtx pic_ref, address;
|
rtx pic_ref, address;
|
rtx insn;
|
rtx insn;
|
|
|
if (reg == 0)
|
if (reg == 0)
|
{
|
{
|
gcc_assert (! reload_in_progress && ! reload_completed);
|
gcc_assert (! reload_in_progress && ! reload_completed);
|
reg = gen_reg_rtx (Pmode);
|
reg = gen_reg_rtx (Pmode);
|
}
|
}
|
|
|
if (flag_pic == 2)
|
if (flag_pic == 2)
|
{
|
{
|
/* If not during reload, allocate another temp reg here for loading
|
/* If not during reload, allocate another temp reg here for loading
|
in the address, so that these instructions can be optimized
|
in the address, so that these instructions can be optimized
|
properly. */
|
properly. */
|
rtx temp_reg = ((reload_in_progress || reload_completed)
|
rtx temp_reg = ((reload_in_progress || reload_completed)
|
? reg : gen_reg_rtx (Pmode));
|
? reg : gen_reg_rtx (Pmode));
|
|
|
/* Must put the SYMBOL_REF inside an UNSPEC here so that cse
|
/* Must put the SYMBOL_REF inside an UNSPEC here so that cse
|
won't get confused into thinking that these two instructions
|
won't get confused into thinking that these two instructions
|
are loading in the true address of the symbol. If in the
|
are loading in the true address of the symbol. If in the
|
future a PIC rtx exists, that should be used instead. */
|
future a PIC rtx exists, that should be used instead. */
|
if (TARGET_ARCH64)
|
if (TARGET_ARCH64)
|
{
|
{
|
emit_insn (gen_movdi_high_pic (temp_reg, orig));
|
emit_insn (gen_movdi_high_pic (temp_reg, orig));
|
emit_insn (gen_movdi_lo_sum_pic (temp_reg, temp_reg, orig));
|
emit_insn (gen_movdi_lo_sum_pic (temp_reg, temp_reg, orig));
|
}
|
}
|
else
|
else
|
{
|
{
|
emit_insn (gen_movsi_high_pic (temp_reg, orig));
|
emit_insn (gen_movsi_high_pic (temp_reg, orig));
|
emit_insn (gen_movsi_lo_sum_pic (temp_reg, temp_reg, orig));
|
emit_insn (gen_movsi_lo_sum_pic (temp_reg, temp_reg, orig));
|
}
|
}
|
address = temp_reg;
|
address = temp_reg;
|
gotdata_op = true;
|
gotdata_op = true;
|
}
|
}
|
else
|
else
|
address = orig;
|
address = orig;
|
|
|
crtl->uses_pic_offset_table = 1;
|
crtl->uses_pic_offset_table = 1;
|
if (gotdata_op)
|
if (gotdata_op)
|
{
|
{
|
if (TARGET_ARCH64)
|
if (TARGET_ARCH64)
|
insn = emit_insn (gen_movdi_pic_gotdata_op (reg, pic_offset_table_rtx,
|
insn = emit_insn (gen_movdi_pic_gotdata_op (reg, pic_offset_table_rtx,
|
address, orig));
|
address, orig));
|
else
|
else
|
insn = emit_insn (gen_movsi_pic_gotdata_op (reg, pic_offset_table_rtx,
|
insn = emit_insn (gen_movsi_pic_gotdata_op (reg, pic_offset_table_rtx,
|
address, orig));
|
address, orig));
|
}
|
}
|
else
|
else
|
{
|
{
|
pic_ref = gen_const_mem (Pmode,
|
pic_ref = gen_const_mem (Pmode,
|
gen_rtx_PLUS (Pmode,
|
gen_rtx_PLUS (Pmode,
|
pic_offset_table_rtx, address));
|
pic_offset_table_rtx, address));
|
insn = emit_move_insn (reg, pic_ref);
|
insn = emit_move_insn (reg, pic_ref);
|
}
|
}
|
/* Put a REG_EQUAL note on this insn, so that it can be optimized
|
/* Put a REG_EQUAL note on this insn, so that it can be optimized
|
by loop. */
|
by loop. */
|
set_unique_reg_note (insn, REG_EQUAL, orig);
|
set_unique_reg_note (insn, REG_EQUAL, orig);
|
return reg;
|
return reg;
|
}
|
}
|
else if (GET_CODE (orig) == CONST)
|
else if (GET_CODE (orig) == CONST)
|
{
|
{
|
rtx base, offset;
|
rtx base, offset;
|
|
|
if (GET_CODE (XEXP (orig, 0)) == PLUS
|
if (GET_CODE (XEXP (orig, 0)) == PLUS
|
&& XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx)
|
&& XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx)
|
return orig;
|
return orig;
|
|
|
if (reg == 0)
|
if (reg == 0)
|
{
|
{
|
gcc_assert (! reload_in_progress && ! reload_completed);
|
gcc_assert (! reload_in_progress && ! reload_completed);
|
reg = gen_reg_rtx (Pmode);
|
reg = gen_reg_rtx (Pmode);
|
}
|
}
|
|
|
gcc_assert (GET_CODE (XEXP (orig, 0)) == PLUS);
|
gcc_assert (GET_CODE (XEXP (orig, 0)) == PLUS);
|
base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), reg);
|
base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), reg);
|
offset = legitimize_pic_address (XEXP (XEXP (orig, 0), 1),
|
offset = legitimize_pic_address (XEXP (XEXP (orig, 0), 1),
|
base == reg ? NULL_RTX : reg);
|
base == reg ? NULL_RTX : reg);
|
|
|
if (GET_CODE (offset) == CONST_INT)
|
if (GET_CODE (offset) == CONST_INT)
|
{
|
{
|
if (SMALL_INT (offset))
|
if (SMALL_INT (offset))
|
return plus_constant (base, INTVAL (offset));
|
return plus_constant (base, INTVAL (offset));
|
else if (! reload_in_progress && ! reload_completed)
|
else if (! reload_in_progress && ! reload_completed)
|
offset = force_reg (Pmode, offset);
|
offset = force_reg (Pmode, offset);
|
else
|
else
|
/* If we reach here, then something is seriously wrong. */
|
/* If we reach here, then something is seriously wrong. */
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
return gen_rtx_PLUS (Pmode, base, offset);
|
return gen_rtx_PLUS (Pmode, base, offset);
|
}
|
}
|
else if (GET_CODE (orig) == LABEL_REF)
|
else if (GET_CODE (orig) == LABEL_REF)
|
/* ??? Why do we do this? */
|
/* ??? Why do we do this? */
|
/* Now movsi_pic_label_ref uses it, but we ought to be checking that
|
/* Now movsi_pic_label_ref uses it, but we ought to be checking that
|
the register is live instead, in case it is eliminated. */
|
the register is live instead, in case it is eliminated. */
|
crtl->uses_pic_offset_table = 1;
|
crtl->uses_pic_offset_table = 1;
|
|
|
return orig;
|
return orig;
|
}
|
}
|
|
|
/* Try machine-dependent ways of modifying an illegitimate address X
|
/* Try machine-dependent ways of modifying an illegitimate address X
|
to be legitimate. If we find one, return the new, valid address.
|
to be legitimate. If we find one, return the new, valid address.
|
|
|
OLDX is the address as it was before break_out_memory_refs was called.
|
OLDX is the address as it was before break_out_memory_refs was called.
|
In some cases it is useful to look at this to decide what needs to be done.
|
In some cases it is useful to look at this to decide what needs to be done.
|
|
|
MODE is the mode of the operand pointed to by X.
|
MODE is the mode of the operand pointed to by X.
|
|
|
On SPARC, change REG+N into REG+REG, and REG+(X*Y) into REG+REG. */
|
On SPARC, change REG+N into REG+REG, and REG+(X*Y) into REG+REG. */
|
|
|
static rtx
|
static rtx
|
sparc_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
|
sparc_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
|
enum machine_mode mode)
|
enum machine_mode mode)
|
{
|
{
|
rtx orig_x = x;
|
rtx orig_x = x;
|
|
|
if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == MULT)
|
if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == MULT)
|
x = gen_rtx_PLUS (Pmode, XEXP (x, 1),
|
x = gen_rtx_PLUS (Pmode, XEXP (x, 1),
|
force_operand (XEXP (x, 0), NULL_RTX));
|
force_operand (XEXP (x, 0), NULL_RTX));
|
if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == MULT)
|
if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == MULT)
|
x = gen_rtx_PLUS (Pmode, XEXP (x, 0),
|
x = gen_rtx_PLUS (Pmode, XEXP (x, 0),
|
force_operand (XEXP (x, 1), NULL_RTX));
|
force_operand (XEXP (x, 1), NULL_RTX));
|
if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == PLUS)
|
if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == PLUS)
|
x = gen_rtx_PLUS (Pmode, force_operand (XEXP (x, 0), NULL_RTX),
|
x = gen_rtx_PLUS (Pmode, force_operand (XEXP (x, 0), NULL_RTX),
|
XEXP (x, 1));
|
XEXP (x, 1));
|
if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == PLUS)
|
if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == PLUS)
|
x = gen_rtx_PLUS (Pmode, XEXP (x, 0),
|
x = gen_rtx_PLUS (Pmode, XEXP (x, 0),
|
force_operand (XEXP (x, 1), NULL_RTX));
|
force_operand (XEXP (x, 1), NULL_RTX));
|
|
|
if (x != orig_x && sparc_legitimate_address_p (mode, x, FALSE))
|
if (x != orig_x && sparc_legitimate_address_p (mode, x, FALSE))
|
return x;
|
return x;
|
|
|
if (sparc_tls_referenced_p (x))
|
if (sparc_tls_referenced_p (x))
|
x = legitimize_tls_address (x);
|
x = legitimize_tls_address (x);
|
else if (flag_pic)
|
else if (flag_pic)
|
x = legitimize_pic_address (x, NULL_RTX);
|
x = legitimize_pic_address (x, NULL_RTX);
|
else if (GET_CODE (x) == PLUS && CONSTANT_ADDRESS_P (XEXP (x, 1)))
|
else if (GET_CODE (x) == PLUS && CONSTANT_ADDRESS_P (XEXP (x, 1)))
|
x = gen_rtx_PLUS (Pmode, XEXP (x, 0),
|
x = gen_rtx_PLUS (Pmode, XEXP (x, 0),
|
copy_to_mode_reg (Pmode, XEXP (x, 1)));
|
copy_to_mode_reg (Pmode, XEXP (x, 1)));
|
else if (GET_CODE (x) == PLUS && CONSTANT_ADDRESS_P (XEXP (x, 0)))
|
else if (GET_CODE (x) == PLUS && CONSTANT_ADDRESS_P (XEXP (x, 0)))
|
x = gen_rtx_PLUS (Pmode, XEXP (x, 1),
|
x = gen_rtx_PLUS (Pmode, XEXP (x, 1),
|
copy_to_mode_reg (Pmode, XEXP (x, 0)));
|
copy_to_mode_reg (Pmode, XEXP (x, 0)));
|
else if (GET_CODE (x) == SYMBOL_REF
|
else if (GET_CODE (x) == SYMBOL_REF
|
|| GET_CODE (x) == CONST
|
|| GET_CODE (x) == CONST
|
|| GET_CODE (x) == LABEL_REF)
|
|| GET_CODE (x) == LABEL_REF)
|
x = copy_to_suggested_reg (x, NULL_RTX, Pmode);
|
x = copy_to_suggested_reg (x, NULL_RTX, Pmode);
|
|
|
return x;
|
return x;
|
}
|
}
|
|
|
#ifdef HAVE_GAS_HIDDEN
|
#ifdef HAVE_GAS_HIDDEN
|
# define USE_HIDDEN_LINKONCE 1
|
# define USE_HIDDEN_LINKONCE 1
|
#else
|
#else
|
# define USE_HIDDEN_LINKONCE 0
|
# define USE_HIDDEN_LINKONCE 0
|
#endif
|
#endif
|
|
|
static void
|
static void
|
get_pc_thunk_name (char name[32], unsigned int regno)
|
get_pc_thunk_name (char name[32], unsigned int regno)
|
{
|
{
|
const char *pic_name = reg_names[regno];
|
const char *pic_name = reg_names[regno];
|
|
|
/* Skip the leading '%' as that cannot be used in a
|
/* Skip the leading '%' as that cannot be used in a
|
symbol name. */
|
symbol name. */
|
pic_name += 1;
|
pic_name += 1;
|
|
|
if (USE_HIDDEN_LINKONCE)
|
if (USE_HIDDEN_LINKONCE)
|
sprintf (name, "__sparc_get_pc_thunk.%s", pic_name);
|
sprintf (name, "__sparc_get_pc_thunk.%s", pic_name);
|
else
|
else
|
ASM_GENERATE_INTERNAL_LABEL (name, "LADDPC", regno);
|
ASM_GENERATE_INTERNAL_LABEL (name, "LADDPC", regno);
|
}
|
}
|
|
|
/* Emit code to load the PIC register. */
|
/* Emit code to load the PIC register. */
|
|
|
static void
|
static void
|
load_pic_register (void)
|
load_pic_register (void)
|
{
|
{
|
int orig_flag_pic = flag_pic;
|
int orig_flag_pic = flag_pic;
|
|
|
if (TARGET_VXWORKS_RTP)
|
if (TARGET_VXWORKS_RTP)
|
{
|
{
|
emit_insn (gen_vxworks_load_got ());
|
emit_insn (gen_vxworks_load_got ());
|
emit_use (pic_offset_table_rtx);
|
emit_use (pic_offset_table_rtx);
|
return;
|
return;
|
}
|
}
|
|
|
/* If we haven't initialized the special PIC symbols, do so now. */
|
/* If we haven't initialized the special PIC symbols, do so now. */
|
if (!pic_helper_needed)
|
if (!pic_helper_needed)
|
{
|
{
|
char name[32];
|
char name[32];
|
|
|
pic_helper_needed = true;
|
pic_helper_needed = true;
|
|
|
get_pc_thunk_name (name, REGNO (pic_offset_table_rtx));
|
get_pc_thunk_name (name, REGNO (pic_offset_table_rtx));
|
pic_helper_symbol = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (name));
|
pic_helper_symbol = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (name));
|
|
|
global_offset_table = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
|
global_offset_table = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
|
}
|
}
|
|
|
flag_pic = 0;
|
flag_pic = 0;
|
if (TARGET_ARCH64)
|
if (TARGET_ARCH64)
|
emit_insn (gen_load_pcrel_symdi (pic_offset_table_rtx, global_offset_table,
|
emit_insn (gen_load_pcrel_symdi (pic_offset_table_rtx, global_offset_table,
|
pic_helper_symbol));
|
pic_helper_symbol));
|
else
|
else
|
emit_insn (gen_load_pcrel_symsi (pic_offset_table_rtx, global_offset_table,
|
emit_insn (gen_load_pcrel_symsi (pic_offset_table_rtx, global_offset_table,
|
pic_helper_symbol));
|
pic_helper_symbol));
|
flag_pic = orig_flag_pic;
|
flag_pic = orig_flag_pic;
|
|
|
/* Need to emit this whether or not we obey regdecls,
|
/* Need to emit this whether or not we obey regdecls,
|
since setjmp/longjmp can cause life info to screw up.
|
since setjmp/longjmp can cause life info to screw up.
|
??? In the case where we don't obey regdecls, this is not sufficient
|
??? In the case where we don't obey regdecls, this is not sufficient
|
since we may not fall out the bottom. */
|
since we may not fall out the bottom. */
|
emit_use (pic_offset_table_rtx);
|
emit_use (pic_offset_table_rtx);
|
}
|
}
|
|
|
/* Emit a call instruction with the pattern given by PAT. ADDR is the
|
/* Emit a call instruction with the pattern given by PAT. ADDR is the
|
address of the call target. */
|
address of the call target. */
|
|
|
void
|
void
|
sparc_emit_call_insn (rtx pat, rtx addr)
|
sparc_emit_call_insn (rtx pat, rtx addr)
|
{
|
{
|
rtx insn;
|
rtx insn;
|
|
|
insn = emit_call_insn (pat);
|
insn = emit_call_insn (pat);
|
|
|
/* The PIC register is live on entry to VxWorks PIC PLT entries. */
|
/* The PIC register is live on entry to VxWorks PIC PLT entries. */
|
if (TARGET_VXWORKS_RTP
|
if (TARGET_VXWORKS_RTP
|
&& flag_pic
|
&& flag_pic
|
&& GET_CODE (addr) == SYMBOL_REF
|
&& GET_CODE (addr) == SYMBOL_REF
|
&& (SYMBOL_REF_DECL (addr)
|
&& (SYMBOL_REF_DECL (addr)
|
? !targetm.binds_local_p (SYMBOL_REF_DECL (addr))
|
? !targetm.binds_local_p (SYMBOL_REF_DECL (addr))
|
: !SYMBOL_REF_LOCAL_P (addr)))
|
: !SYMBOL_REF_LOCAL_P (addr)))
|
{
|
{
|
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
|
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
|
crtl->uses_pic_offset_table = 1;
|
crtl->uses_pic_offset_table = 1;
|
}
|
}
|
}
|
}
|
|
|
/* Return 1 if RTX is a MEM which is known to be aligned to at
|
/* Return 1 if RTX is a MEM which is known to be aligned to at
|
least a DESIRED byte boundary. */
|
least a DESIRED byte boundary. */
|
|
|
int
|
int
|
mem_min_alignment (rtx mem, int desired)
|
mem_min_alignment (rtx mem, int desired)
|
{
|
{
|
rtx addr, base, offset;
|
rtx addr, base, offset;
|
|
|
/* If it's not a MEM we can't accept it. */
|
/* If it's not a MEM we can't accept it. */
|
if (GET_CODE (mem) != MEM)
|
if (GET_CODE (mem) != MEM)
|
return 0;
|
return 0;
|
|
|
/* Obviously... */
|
/* Obviously... */
|
if (!TARGET_UNALIGNED_DOUBLES
|
if (!TARGET_UNALIGNED_DOUBLES
|
&& MEM_ALIGN (mem) / BITS_PER_UNIT >= (unsigned)desired)
|
&& MEM_ALIGN (mem) / BITS_PER_UNIT >= (unsigned)desired)
|
return 1;
|
return 1;
|
|
|
/* ??? The rest of the function predates MEM_ALIGN so
|
/* ??? The rest of the function predates MEM_ALIGN so
|
there is probably a bit of redundancy. */
|
there is probably a bit of redundancy. */
|
addr = XEXP (mem, 0);
|
addr = XEXP (mem, 0);
|
base = offset = NULL_RTX;
|
base = offset = NULL_RTX;
|
if (GET_CODE (addr) == PLUS)
|
if (GET_CODE (addr) == PLUS)
|
{
|
{
|
if (GET_CODE (XEXP (addr, 0)) == REG)
|
if (GET_CODE (XEXP (addr, 0)) == REG)
|
{
|
{
|
base = XEXP (addr, 0);
|
base = XEXP (addr, 0);
|
|
|
/* What we are saying here is that if the base
|
/* What we are saying here is that if the base
|
REG is aligned properly, the compiler will make
|
REG is aligned properly, the compiler will make
|
sure any REG based index upon it will be so
|
sure any REG based index upon it will be so
|
as well. */
|
as well. */
|
if (GET_CODE (XEXP (addr, 1)) == CONST_INT)
|
if (GET_CODE (XEXP (addr, 1)) == CONST_INT)
|
offset = XEXP (addr, 1);
|
offset = XEXP (addr, 1);
|
else
|
else
|
offset = const0_rtx;
|
offset = const0_rtx;
|
}
|
}
|
}
|
}
|
else if (GET_CODE (addr) == REG)
|
else if (GET_CODE (addr) == REG)
|
{
|
{
|
base = addr;
|
base = addr;
|
offset = const0_rtx;
|
offset = const0_rtx;
|
}
|
}
|
|
|
if (base != NULL_RTX)
|
if (base != NULL_RTX)
|
{
|
{
|
int regno = REGNO (base);
|
int regno = REGNO (base);
|
|
|
if (regno != HARD_FRAME_POINTER_REGNUM && regno != STACK_POINTER_REGNUM)
|
if (regno != HARD_FRAME_POINTER_REGNUM && regno != STACK_POINTER_REGNUM)
|
{
|
{
|
/* Check if the compiler has recorded some information
|
/* Check if the compiler has recorded some information
|
about the alignment of the base REG. If reload has
|
about the alignment of the base REG. If reload has
|
completed, we already matched with proper alignments.
|
completed, we already matched with proper alignments.
|
If not running global_alloc, reload might give us
|
If not running global_alloc, reload might give us
|
unaligned pointer to local stack though. */
|
unaligned pointer to local stack though. */
|
if (((cfun != 0
|
if (((cfun != 0
|
&& REGNO_POINTER_ALIGN (regno) >= desired * BITS_PER_UNIT)
|
&& REGNO_POINTER_ALIGN (regno) >= desired * BITS_PER_UNIT)
|
|| (optimize && reload_completed))
|
|| (optimize && reload_completed))
|
&& (INTVAL (offset) & (desired - 1)) == 0)
|
&& (INTVAL (offset) & (desired - 1)) == 0)
|
return 1;
|
return 1;
|
}
|
}
|
else
|
else
|
{
|
{
|
if (((INTVAL (offset) - SPARC_STACK_BIAS) & (desired - 1)) == 0)
|
if (((INTVAL (offset) - SPARC_STACK_BIAS) & (desired - 1)) == 0)
|
return 1;
|
return 1;
|
}
|
}
|
}
|
}
|
else if (! TARGET_UNALIGNED_DOUBLES
|
else if (! TARGET_UNALIGNED_DOUBLES
|
|| CONSTANT_P (addr)
|
|| CONSTANT_P (addr)
|
|| GET_CODE (addr) == LO_SUM)
|
|| GET_CODE (addr) == LO_SUM)
|
{
|
{
|
/* Anything else we know is properly aligned unless TARGET_UNALIGNED_DOUBLES
|
/* Anything else we know is properly aligned unless TARGET_UNALIGNED_DOUBLES
|
is true, in which case we can only assume that an access is aligned if
|
is true, in which case we can only assume that an access is aligned if
|
it is to a constant address, or the address involves a LO_SUM. */
|
it is to a constant address, or the address involves a LO_SUM. */
|
return 1;
|
return 1;
|
}
|
}
|
|
|
/* An obviously unaligned address. */
|
/* An obviously unaligned address. */
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
/* Vectors to keep interesting information about registers where it can easily
|
/* Vectors to keep interesting information about registers where it can easily
|
be got. We used to use the actual mode value as the bit number, but there
|
be got. We used to use the actual mode value as the bit number, but there
|
are more than 32 modes now. Instead we use two tables: one indexed by
|
are more than 32 modes now. Instead we use two tables: one indexed by
|
hard register number, and one indexed by mode. */
|
hard register number, and one indexed by mode. */
|
|
|
/* The purpose of sparc_mode_class is to shrink the range of modes so that
|
/* The purpose of sparc_mode_class is to shrink the range of modes so that
|
they all fit (as bit numbers) in a 32-bit word (again). Each real mode is
|
they all fit (as bit numbers) in a 32-bit word (again). Each real mode is
|
mapped into one sparc_mode_class mode. */
|
mapped into one sparc_mode_class mode. */
|
|
|
enum sparc_mode_class {
|
enum sparc_mode_class {
|
S_MODE, D_MODE, T_MODE, O_MODE,
|
S_MODE, D_MODE, T_MODE, O_MODE,
|
SF_MODE, DF_MODE, TF_MODE, OF_MODE,
|
SF_MODE, DF_MODE, TF_MODE, OF_MODE,
|
CC_MODE, CCFP_MODE
|
CC_MODE, CCFP_MODE
|
};
|
};
|
|
|
/* Modes for single-word and smaller quantities. */
|
/* Modes for single-word and smaller quantities. */
|
#define S_MODES ((1 << (int) S_MODE) | (1 << (int) SF_MODE))
|
#define S_MODES ((1 << (int) S_MODE) | (1 << (int) SF_MODE))
|
|
|
/* Modes for double-word and smaller quantities. */
|
/* Modes for double-word and smaller quantities. */
|
#define D_MODES (S_MODES | (1 << (int) D_MODE) | (1 << DF_MODE))
|
#define D_MODES (S_MODES | (1 << (int) D_MODE) | (1 << DF_MODE))
|
|
|
/* Modes for quad-word and smaller quantities. */
|
/* Modes for quad-word and smaller quantities. */
|
#define T_MODES (D_MODES | (1 << (int) T_MODE) | (1 << (int) TF_MODE))
|
#define T_MODES (D_MODES | (1 << (int) T_MODE) | (1 << (int) TF_MODE))
|
|
|
/* Modes for 8-word and smaller quantities. */
|
/* Modes for 8-word and smaller quantities. */
|
#define O_MODES (T_MODES | (1 << (int) O_MODE) | (1 << (int) OF_MODE))
|
#define O_MODES (T_MODES | (1 << (int) O_MODE) | (1 << (int) OF_MODE))
|
|
|
/* Modes for single-float quantities. We must allow any single word or
|
/* Modes for single-float quantities. We must allow any single word or
|
smaller quantity. This is because the fix/float conversion instructions
|
smaller quantity. This is because the fix/float conversion instructions
|
take integer inputs/outputs from the float registers. */
|
take integer inputs/outputs from the float registers. */
|
#define SF_MODES (S_MODES)
|
#define SF_MODES (S_MODES)
|
|
|
/* Modes for double-float and smaller quantities. */
|
/* Modes for double-float and smaller quantities. */
|
#define DF_MODES (D_MODES)
|
#define DF_MODES (D_MODES)
|
|
|
/* Modes for quad-float and smaller quantities. */
|
/* Modes for quad-float and smaller quantities. */
|
#define TF_MODES (DF_MODES | (1 << (int) TF_MODE))
|
#define TF_MODES (DF_MODES | (1 << (int) TF_MODE))
|
|
|
/* Modes for quad-float pairs and smaller quantities. */
|
/* Modes for quad-float pairs and smaller quantities. */
|
#define OF_MODES (TF_MODES | (1 << (int) OF_MODE))
|
#define OF_MODES (TF_MODES | (1 << (int) OF_MODE))
|
|
|
/* Modes for double-float only quantities. */
|
/* Modes for double-float only quantities. */
|
#define DF_MODES_NO_S ((1 << (int) D_MODE) | (1 << (int) DF_MODE))
|
#define DF_MODES_NO_S ((1 << (int) D_MODE) | (1 << (int) DF_MODE))
|
|
|
/* Modes for quad-float and double-float only quantities. */
|
/* Modes for quad-float and double-float only quantities. */
|
#define TF_MODES_NO_S (DF_MODES_NO_S | (1 << (int) TF_MODE))
|
#define TF_MODES_NO_S (DF_MODES_NO_S | (1 << (int) TF_MODE))
|
|
|
/* Modes for quad-float pairs and double-float only quantities. */
|
/* Modes for quad-float pairs and double-float only quantities. */
|
#define OF_MODES_NO_S (TF_MODES_NO_S | (1 << (int) OF_MODE))
|
#define OF_MODES_NO_S (TF_MODES_NO_S | (1 << (int) OF_MODE))
|
|
|
/* Modes for condition codes. */
|
/* Modes for condition codes. */
|
#define CC_MODES (1 << (int) CC_MODE)
|
#define CC_MODES (1 << (int) CC_MODE)
|
#define CCFP_MODES (1 << (int) CCFP_MODE)
|
#define CCFP_MODES (1 << (int) CCFP_MODE)
|
|
|
/* Value is 1 if register/mode pair is acceptable on sparc.
|
/* Value is 1 if register/mode pair is acceptable on sparc.
|
The funny mixture of D and T modes is because integer operations
|
The funny mixture of D and T modes is because integer operations
|
do not specially operate on tetra quantities, so non-quad-aligned
|
do not specially operate on tetra quantities, so non-quad-aligned
|
registers can hold quadword quantities (except %o4 and %i4 because
|
registers can hold quadword quantities (except %o4 and %i4 because
|
they cross fixed registers). */
|
they cross fixed registers). */
|
|
|
/* This points to either the 32 bit or the 64 bit version. */
|
/* This points to either the 32 bit or the 64 bit version. */
|
const int *hard_regno_mode_classes;
|
const int *hard_regno_mode_classes;
|
|
|
static const int hard_32bit_mode_classes[] = {
|
static const int hard_32bit_mode_classes[] = {
|
S_MODES, S_MODES, T_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES,
|
S_MODES, S_MODES, T_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES,
|
T_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES, D_MODES, S_MODES,
|
T_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES, D_MODES, S_MODES,
|
T_MODES, S_MODES, T_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES,
|
T_MODES, S_MODES, T_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES,
|
T_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES, D_MODES, S_MODES,
|
T_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES, D_MODES, S_MODES,
|
|
|
OF_MODES, SF_MODES, DF_MODES, SF_MODES, OF_MODES, SF_MODES, DF_MODES, SF_MODES,
|
OF_MODES, SF_MODES, DF_MODES, SF_MODES, OF_MODES, SF_MODES, DF_MODES, SF_MODES,
|
OF_MODES, SF_MODES, DF_MODES, SF_MODES, OF_MODES, SF_MODES, DF_MODES, SF_MODES,
|
OF_MODES, SF_MODES, DF_MODES, SF_MODES, OF_MODES, SF_MODES, DF_MODES, SF_MODES,
|
OF_MODES, SF_MODES, DF_MODES, SF_MODES, OF_MODES, SF_MODES, DF_MODES, SF_MODES,
|
OF_MODES, SF_MODES, DF_MODES, SF_MODES, OF_MODES, SF_MODES, DF_MODES, SF_MODES,
|
OF_MODES, SF_MODES, DF_MODES, SF_MODES, TF_MODES, SF_MODES, DF_MODES, SF_MODES,
|
OF_MODES, SF_MODES, DF_MODES, SF_MODES, TF_MODES, SF_MODES, DF_MODES, SF_MODES,
|
|
|
/* FP regs f32 to f63. Only the even numbered registers actually exist,
|
/* FP regs f32 to f63. Only the even numbered registers actually exist,
|
and none can hold SFmode/SImode values. */
|
and none can hold SFmode/SImode values. */
|
OF_MODES_NO_S, 0, DF_MODES_NO_S, 0, OF_MODES_NO_S, 0, DF_MODES_NO_S, 0,
|
OF_MODES_NO_S, 0, DF_MODES_NO_S, 0, OF_MODES_NO_S, 0, DF_MODES_NO_S, 0,
|
OF_MODES_NO_S, 0, DF_MODES_NO_S, 0, OF_MODES_NO_S, 0, DF_MODES_NO_S, 0,
|
OF_MODES_NO_S, 0, DF_MODES_NO_S, 0, OF_MODES_NO_S, 0, DF_MODES_NO_S, 0,
|
OF_MODES_NO_S, 0, DF_MODES_NO_S, 0, OF_MODES_NO_S, 0, DF_MODES_NO_S, 0,
|
OF_MODES_NO_S, 0, DF_MODES_NO_S, 0, OF_MODES_NO_S, 0, DF_MODES_NO_S, 0,
|
OF_MODES_NO_S, 0, DF_MODES_NO_S, 0, TF_MODES_NO_S, 0, DF_MODES_NO_S, 0,
|
OF_MODES_NO_S, 0, DF_MODES_NO_S, 0, TF_MODES_NO_S, 0, DF_MODES_NO_S, 0,
|
|
|
/* %fcc[0123] */
|
/* %fcc[0123] */
|
CCFP_MODES, CCFP_MODES, CCFP_MODES, CCFP_MODES,
|
CCFP_MODES, CCFP_MODES, CCFP_MODES, CCFP_MODES,
|
|
|
/* %icc */
|
/* %icc */
|
CC_MODES
|
CC_MODES
|
};
|
};
|
|
|
static const int hard_64bit_mode_classes[] = {
|
static const int hard_64bit_mode_classes[] = {
|
D_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES,
|
D_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES,
|
O_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES,
|
O_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES,
|
T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES,
|
T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES,
|
O_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES,
|
O_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES,
|
|
|
OF_MODES, SF_MODES, DF_MODES, SF_MODES, OF_MODES, SF_MODES, DF_MODES, SF_MODES,
|
OF_MODES, SF_MODES, DF_MODES, SF_MODES, OF_MODES, SF_MODES, DF_MODES, SF_MODES,
|
OF_MODES, SF_MODES, DF_MODES, SF_MODES, OF_MODES, SF_MODES, DF_MODES, SF_MODES,
|
OF_MODES, SF_MODES, DF_MODES, SF_MODES, OF_MODES, SF_MODES, DF_MODES, SF_MODES,
|
OF_MODES, SF_MODES, DF_MODES, SF_MODES, OF_MODES, SF_MODES, DF_MODES, SF_MODES,
|
OF_MODES, SF_MODES, DF_MODES, SF_MODES, OF_MODES, SF_MODES, DF_MODES, SF_MODES,
|
OF_MODES, SF_MODES, DF_MODES, SF_MODES, TF_MODES, SF_MODES, DF_MODES, SF_MODES,
|
OF_MODES, SF_MODES, DF_MODES, SF_MODES, TF_MODES, SF_MODES, DF_MODES, SF_MODES,
|
|
|
/* FP regs f32 to f63. Only the even numbered registers actually exist,
|
/* FP regs f32 to f63. Only the even numbered registers actually exist,
|
and none can hold SFmode/SImode values. */
|
and none can hold SFmode/SImode values. */
|
OF_MODES_NO_S, 0, DF_MODES_NO_S, 0, OF_MODES_NO_S, 0, DF_MODES_NO_S, 0,
|
OF_MODES_NO_S, 0, DF_MODES_NO_S, 0, OF_MODES_NO_S, 0, DF_MODES_NO_S, 0,
|
OF_MODES_NO_S, 0, DF_MODES_NO_S, 0, OF_MODES_NO_S, 0, DF_MODES_NO_S, 0,
|
OF_MODES_NO_S, 0, DF_MODES_NO_S, 0, OF_MODES_NO_S, 0, DF_MODES_NO_S, 0,
|
OF_MODES_NO_S, 0, DF_MODES_NO_S, 0, OF_MODES_NO_S, 0, DF_MODES_NO_S, 0,
|
OF_MODES_NO_S, 0, DF_MODES_NO_S, 0, OF_MODES_NO_S, 0, DF_MODES_NO_S, 0,
|
OF_MODES_NO_S, 0, DF_MODES_NO_S, 0, TF_MODES_NO_S, 0, DF_MODES_NO_S, 0,
|
OF_MODES_NO_S, 0, DF_MODES_NO_S, 0, TF_MODES_NO_S, 0, DF_MODES_NO_S, 0,
|
|
|
/* %fcc[0123] */
|
/* %fcc[0123] */
|
CCFP_MODES, CCFP_MODES, CCFP_MODES, CCFP_MODES,
|
CCFP_MODES, CCFP_MODES, CCFP_MODES, CCFP_MODES,
|
|
|
/* %icc */
|
/* %icc */
|
CC_MODES
|
CC_MODES
|
};
|
};
|
|
|
int sparc_mode_class [NUM_MACHINE_MODES];
|
int sparc_mode_class [NUM_MACHINE_MODES];
|
|
|
enum reg_class sparc_regno_reg_class[FIRST_PSEUDO_REGISTER];
|
enum reg_class sparc_regno_reg_class[FIRST_PSEUDO_REGISTER];
|
|
|
static void
|
static void
|
sparc_init_modes (void)
|
sparc_init_modes (void)
|
{
|
{
|
int i;
|
int i;
|
|
|
for (i = 0; i < NUM_MACHINE_MODES; i++)
|
for (i = 0; i < NUM_MACHINE_MODES; i++)
|
{
|
{
|
switch (GET_MODE_CLASS (i))
|
switch (GET_MODE_CLASS (i))
|
{
|
{
|
case MODE_INT:
|
case MODE_INT:
|
case MODE_PARTIAL_INT:
|
case MODE_PARTIAL_INT:
|
case MODE_COMPLEX_INT:
|
case MODE_COMPLEX_INT:
|
if (GET_MODE_SIZE (i) <= 4)
|
if (GET_MODE_SIZE (i) <= 4)
|
sparc_mode_class[i] = 1 << (int) S_MODE;
|
sparc_mode_class[i] = 1 << (int) S_MODE;
|
else if (GET_MODE_SIZE (i) == 8)
|
else if (GET_MODE_SIZE (i) == 8)
|
sparc_mode_class[i] = 1 << (int) D_MODE;
|
sparc_mode_class[i] = 1 << (int) D_MODE;
|
else if (GET_MODE_SIZE (i) == 16)
|
else if (GET_MODE_SIZE (i) == 16)
|
sparc_mode_class[i] = 1 << (int) T_MODE;
|
sparc_mode_class[i] = 1 << (int) T_MODE;
|
else if (GET_MODE_SIZE (i) == 32)
|
else if (GET_MODE_SIZE (i) == 32)
|
sparc_mode_class[i] = 1 << (int) O_MODE;
|
sparc_mode_class[i] = 1 << (int) O_MODE;
|
else
|
else
|
sparc_mode_class[i] = 0;
|
sparc_mode_class[i] = 0;
|
break;
|
break;
|
case MODE_VECTOR_INT:
|
case MODE_VECTOR_INT:
|
if (GET_MODE_SIZE (i) <= 4)
|
if (GET_MODE_SIZE (i) <= 4)
|
sparc_mode_class[i] = 1 << (int)SF_MODE;
|
sparc_mode_class[i] = 1 << (int)SF_MODE;
|
else if (GET_MODE_SIZE (i) == 8)
|
else if (GET_MODE_SIZE (i) == 8)
|
sparc_mode_class[i] = 1 << (int)DF_MODE;
|
sparc_mode_class[i] = 1 << (int)DF_MODE;
|
break;
|
break;
|
case MODE_FLOAT:
|
case MODE_FLOAT:
|
case MODE_COMPLEX_FLOAT:
|
case MODE_COMPLEX_FLOAT:
|
if (GET_MODE_SIZE (i) <= 4)
|
if (GET_MODE_SIZE (i) <= 4)
|
sparc_mode_class[i] = 1 << (int) SF_MODE;
|
sparc_mode_class[i] = 1 << (int) SF_MODE;
|
else if (GET_MODE_SIZE (i) == 8)
|
else if (GET_MODE_SIZE (i) == 8)
|
sparc_mode_class[i] = 1 << (int) DF_MODE;
|
sparc_mode_class[i] = 1 << (int) DF_MODE;
|
else if (GET_MODE_SIZE (i) == 16)
|
else if (GET_MODE_SIZE (i) == 16)
|
sparc_mode_class[i] = 1 << (int) TF_MODE;
|
sparc_mode_class[i] = 1 << (int) TF_MODE;
|
else if (GET_MODE_SIZE (i) == 32)
|
else if (GET_MODE_SIZE (i) == 32)
|
sparc_mode_class[i] = 1 << (int) OF_MODE;
|
sparc_mode_class[i] = 1 << (int) OF_MODE;
|
else
|
else
|
sparc_mode_class[i] = 0;
|
sparc_mode_class[i] = 0;
|
break;
|
break;
|
case MODE_CC:
|
case MODE_CC:
|
if (i == (int) CCFPmode || i == (int) CCFPEmode)
|
if (i == (int) CCFPmode || i == (int) CCFPEmode)
|
sparc_mode_class[i] = 1 << (int) CCFP_MODE;
|
sparc_mode_class[i] = 1 << (int) CCFP_MODE;
|
else
|
else
|
sparc_mode_class[i] = 1 << (int) CC_MODE;
|
sparc_mode_class[i] = 1 << (int) CC_MODE;
|
break;
|
break;
|
default:
|
default:
|
sparc_mode_class[i] = 0;
|
sparc_mode_class[i] = 0;
|
break;
|
break;
|
}
|
}
|
}
|
}
|
|
|
if (TARGET_ARCH64)
|
if (TARGET_ARCH64)
|
hard_regno_mode_classes = hard_64bit_mode_classes;
|
hard_regno_mode_classes = hard_64bit_mode_classes;
|
else
|
else
|
hard_regno_mode_classes = hard_32bit_mode_classes;
|
hard_regno_mode_classes = hard_32bit_mode_classes;
|
|
|
/* Initialize the array used by REGNO_REG_CLASS. */
|
/* Initialize the array used by REGNO_REG_CLASS. */
|
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
|
{
|
{
|
if (i < 16 && TARGET_V8PLUS)
|
if (i < 16 && TARGET_V8PLUS)
|
sparc_regno_reg_class[i] = I64_REGS;
|
sparc_regno_reg_class[i] = I64_REGS;
|
else if (i < 32 || i == FRAME_POINTER_REGNUM)
|
else if (i < 32 || i == FRAME_POINTER_REGNUM)
|
sparc_regno_reg_class[i] = GENERAL_REGS;
|
sparc_regno_reg_class[i] = GENERAL_REGS;
|
else if (i < 64)
|
else if (i < 64)
|
sparc_regno_reg_class[i] = FP_REGS;
|
sparc_regno_reg_class[i] = FP_REGS;
|
else if (i < 96)
|
else if (i < 96)
|
sparc_regno_reg_class[i] = EXTRA_FP_REGS;
|
sparc_regno_reg_class[i] = EXTRA_FP_REGS;
|
else if (i < 100)
|
else if (i < 100)
|
sparc_regno_reg_class[i] = FPCC_REGS;
|
sparc_regno_reg_class[i] = FPCC_REGS;
|
else
|
else
|
sparc_regno_reg_class[i] = NO_REGS;
|
sparc_regno_reg_class[i] = NO_REGS;
|
}
|
}
|
}
|
}
|
|
|
/* Compute the frame size required by the function. This function is called
|
/* Compute the frame size required by the function. This function is called
|
during the reload pass and also by sparc_expand_prologue. */
|
during the reload pass and also by sparc_expand_prologue. */
|
|
|
HOST_WIDE_INT
|
HOST_WIDE_INT
|
sparc_compute_frame_size (HOST_WIDE_INT size, int leaf_function_p)
|
sparc_compute_frame_size (HOST_WIDE_INT size, int leaf_function_p)
|
{
|
{
|
int outgoing_args_size = (crtl->outgoing_args_size
|
int outgoing_args_size = (crtl->outgoing_args_size
|
+ REG_PARM_STACK_SPACE (current_function_decl));
|
+ REG_PARM_STACK_SPACE (current_function_decl));
|
int n_regs = 0; /* N_REGS is the number of 4-byte regs saved thus far. */
|
int n_regs = 0; /* N_REGS is the number of 4-byte regs saved thus far. */
|
int i;
|
int i;
|
|
|
if (TARGET_ARCH64)
|
if (TARGET_ARCH64)
|
{
|
{
|
for (i = 0; i < 8; i++)
|
for (i = 0; i < 8; i++)
|
if (df_regs_ever_live_p (i) && ! call_used_regs[i])
|
if (df_regs_ever_live_p (i) && ! call_used_regs[i])
|
n_regs += 2;
|
n_regs += 2;
|
}
|
}
|
else
|
else
|
{
|
{
|
for (i = 0; i < 8; i += 2)
|
for (i = 0; i < 8; i += 2)
|
if ((df_regs_ever_live_p (i) && ! call_used_regs[i])
|
if ((df_regs_ever_live_p (i) && ! call_used_regs[i])
|
|| (df_regs_ever_live_p (i+1) && ! call_used_regs[i+1]))
|
|| (df_regs_ever_live_p (i+1) && ! call_used_regs[i+1]))
|
n_regs += 2;
|
n_regs += 2;
|
}
|
}
|
|
|
for (i = 32; i < (TARGET_V9 ? 96 : 64); i += 2)
|
for (i = 32; i < (TARGET_V9 ? 96 : 64); i += 2)
|
if ((df_regs_ever_live_p (i) && ! call_used_regs[i])
|
if ((df_regs_ever_live_p (i) && ! call_used_regs[i])
|
|| (df_regs_ever_live_p (i+1) && ! call_used_regs[i+1]))
|
|| (df_regs_ever_live_p (i+1) && ! call_used_regs[i+1]))
|
n_regs += 2;
|
n_regs += 2;
|
|
|
/* Set up values for use in prologue and epilogue. */
|
/* Set up values for use in prologue and epilogue. */
|
num_gfregs = n_regs;
|
num_gfregs = n_regs;
|
|
|
if (leaf_function_p
|
if (leaf_function_p
|
&& n_regs == 0
|
&& n_regs == 0
|
&& size == 0
|
&& size == 0
|
&& crtl->outgoing_args_size == 0)
|
&& crtl->outgoing_args_size == 0)
|
actual_fsize = apparent_fsize = 0;
|
actual_fsize = apparent_fsize = 0;
|
else
|
else
|
{
|
{
|
/* We subtract STARTING_FRAME_OFFSET, remember it's negative. */
|
/* We subtract STARTING_FRAME_OFFSET, remember it's negative. */
|
apparent_fsize = (size - STARTING_FRAME_OFFSET + 7) & -8;
|
apparent_fsize = (size - STARTING_FRAME_OFFSET + 7) & -8;
|
apparent_fsize += n_regs * 4;
|
apparent_fsize += n_regs * 4;
|
actual_fsize = apparent_fsize + ((outgoing_args_size + 7) & -8);
|
actual_fsize = apparent_fsize + ((outgoing_args_size + 7) & -8);
|
}
|
}
|
|
|
/* Make sure nothing can clobber our register windows.
|
/* Make sure nothing can clobber our register windows.
|
If a SAVE must be done, or there is a stack-local variable,
|
If a SAVE must be done, or there is a stack-local variable,
|
the register window area must be allocated. */
|
the register window area must be allocated. */
|
if (! leaf_function_p || size > 0)
|
if (! leaf_function_p || size > 0)
|
actual_fsize += FIRST_PARM_OFFSET (current_function_decl);
|
actual_fsize += FIRST_PARM_OFFSET (current_function_decl);
|
|
|
return SPARC_STACK_ALIGN (actual_fsize);
|
return SPARC_STACK_ALIGN (actual_fsize);
|
}
|
}
|
|
|
/* Output any necessary .register pseudo-ops. */
|
/* Output any necessary .register pseudo-ops. */
|
|
|
void
|
void
|
sparc_output_scratch_registers (FILE *file ATTRIBUTE_UNUSED)
|
sparc_output_scratch_registers (FILE *file ATTRIBUTE_UNUSED)
|
{
|
{
|
#ifdef HAVE_AS_REGISTER_PSEUDO_OP
|
#ifdef HAVE_AS_REGISTER_PSEUDO_OP
|
int i;
|
int i;
|
|
|
if (TARGET_ARCH32)
|
if (TARGET_ARCH32)
|
return;
|
return;
|
|
|
/* Check if %g[2367] were used without
|
/* Check if %g[2367] were used without
|
.register being printed for them already. */
|
.register being printed for them already. */
|
for (i = 2; i < 8; i++)
|
for (i = 2; i < 8; i++)
|
{
|
{
|
if (df_regs_ever_live_p (i)
|
if (df_regs_ever_live_p (i)
|
&& ! sparc_hard_reg_printed [i])
|
&& ! sparc_hard_reg_printed [i])
|
{
|
{
|
sparc_hard_reg_printed [i] = 1;
|
sparc_hard_reg_printed [i] = 1;
|
/* %g7 is used as TLS base register, use #ignore
|
/* %g7 is used as TLS base register, use #ignore
|
for it instead of #scratch. */
|
for it instead of #scratch. */
|
fprintf (file, "\t.register\t%%g%d, #%s\n", i,
|
fprintf (file, "\t.register\t%%g%d, #%s\n", i,
|
i == 7 ? "ignore" : "scratch");
|
i == 7 ? "ignore" : "scratch");
|
}
|
}
|
if (i == 3) i = 5;
|
if (i == 3) i = 5;
|
}
|
}
|
#endif
|
#endif
|
}
|
}
|
|
|
/* Save/restore call-saved registers from LOW to HIGH at BASE+OFFSET
|
/* Save/restore call-saved registers from LOW to HIGH at BASE+OFFSET
|
as needed. LOW should be double-word aligned for 32-bit registers.
|
as needed. LOW should be double-word aligned for 32-bit registers.
|
Return the new OFFSET. */
|
Return the new OFFSET. */
|
|
|
#define SORR_SAVE 0
|
#define SORR_SAVE 0
|
#define SORR_RESTORE 1
|
#define SORR_RESTORE 1
|
|
|
static int
|
static int
|
save_or_restore_regs (int low, int high, rtx base, int offset, int action)
|
save_or_restore_regs (int low, int high, rtx base, int offset, int action)
|
{
|
{
|
rtx mem, insn;
|
rtx mem, insn;
|
int i;
|
int i;
|
|
|
if (TARGET_ARCH64 && high <= 32)
|
if (TARGET_ARCH64 && high <= 32)
|
{
|
{
|
for (i = low; i < high; i++)
|
for (i = low; i < high; i++)
|
{
|
{
|
if (df_regs_ever_live_p (i) && ! call_used_regs[i])
|
if (df_regs_ever_live_p (i) && ! call_used_regs[i])
|
{
|
{
|
mem = gen_rtx_MEM (DImode, plus_constant (base, offset));
|
mem = gen_rtx_MEM (DImode, plus_constant (base, offset));
|
set_mem_alias_set (mem, sparc_sr_alias_set);
|
set_mem_alias_set (mem, sparc_sr_alias_set);
|
if (action == SORR_SAVE)
|
if (action == SORR_SAVE)
|
{
|
{
|
insn = emit_move_insn (mem, gen_rtx_REG (DImode, i));
|
insn = emit_move_insn (mem, gen_rtx_REG (DImode, i));
|
RTX_FRAME_RELATED_P (insn) = 1;
|
RTX_FRAME_RELATED_P (insn) = 1;
|
}
|
}
|
else /* action == SORR_RESTORE */
|
else /* action == SORR_RESTORE */
|
emit_move_insn (gen_rtx_REG (DImode, i), mem);
|
emit_move_insn (gen_rtx_REG (DImode, i), mem);
|
offset += 8;
|
offset += 8;
|
}
|
}
|
}
|
}
|
}
|
}
|
else
|
else
|
{
|
{
|
for (i = low; i < high; i += 2)
|
for (i = low; i < high; i += 2)
|
{
|
{
|
bool reg0 = df_regs_ever_live_p (i) && ! call_used_regs[i];
|
bool reg0 = df_regs_ever_live_p (i) && ! call_used_regs[i];
|
bool reg1 = df_regs_ever_live_p (i+1) && ! call_used_regs[i+1];
|
bool reg1 = df_regs_ever_live_p (i+1) && ! call_used_regs[i+1];
|
enum machine_mode mode;
|
enum machine_mode mode;
|
int regno;
|
int regno;
|
|
|
if (reg0 && reg1)
|
if (reg0 && reg1)
|
{
|
{
|
mode = i < 32 ? DImode : DFmode;
|
mode = i < 32 ? DImode : DFmode;
|
regno = i;
|
regno = i;
|
}
|
}
|
else if (reg0)
|
else if (reg0)
|
{
|
{
|
mode = i < 32 ? SImode : SFmode;
|
mode = i < 32 ? SImode : SFmode;
|
regno = i;
|
regno = i;
|
}
|
}
|
else if (reg1)
|
else if (reg1)
|
{
|
{
|
mode = i < 32 ? SImode : SFmode;
|
mode = i < 32 ? SImode : SFmode;
|
regno = i + 1;
|
regno = i + 1;
|
offset += 4;
|
offset += 4;
|
}
|
}
|
else
|
else
|
continue;
|
continue;
|
|
|
mem = gen_rtx_MEM (mode, plus_constant (base, offset));
|
mem = gen_rtx_MEM (mode, plus_constant (base, offset));
|
set_mem_alias_set (mem, sparc_sr_alias_set);
|
set_mem_alias_set (mem, sparc_sr_alias_set);
|
if (action == SORR_SAVE)
|
if (action == SORR_SAVE)
|
{
|
{
|
insn = emit_move_insn (mem, gen_rtx_REG (mode, regno));
|
insn = emit_move_insn (mem, gen_rtx_REG (mode, regno));
|
RTX_FRAME_RELATED_P (insn) = 1;
|
RTX_FRAME_RELATED_P (insn) = 1;
|
}
|
}
|
else /* action == SORR_RESTORE */
|
else /* action == SORR_RESTORE */
|
emit_move_insn (gen_rtx_REG (mode, regno), mem);
|
emit_move_insn (gen_rtx_REG (mode, regno), mem);
|
|
|
/* Always preserve double-word alignment. */
|
/* Always preserve double-word alignment. */
|
offset = (offset + 7) & -8;
|
offset = (offset + 7) & -8;
|
}
|
}
|
}
|
}
|
|
|
return offset;
|
return offset;
|
}
|
}
|
|
|
/* Emit code to save call-saved registers. */
|
/* Emit code to save call-saved registers. */
|
|
|
static void
|
static void
|
emit_save_or_restore_regs (int action)
|
emit_save_or_restore_regs (int action)
|
{
|
{
|
HOST_WIDE_INT offset;
|
HOST_WIDE_INT offset;
|
rtx base;
|
rtx base;
|
|
|
offset = frame_base_offset - apparent_fsize;
|
offset = frame_base_offset - apparent_fsize;
|
|
|
if (offset < -4096 || offset + num_gfregs * 4 > 4095)
|
if (offset < -4096 || offset + num_gfregs * 4 > 4095)
|
{
|
{
|
/* ??? This might be optimized a little as %g1 might already have a
|
/* ??? This might be optimized a little as %g1 might already have a
|
value close enough that a single add insn will do. */
|
value close enough that a single add insn will do. */
|
/* ??? Although, all of this is probably only a temporary fix
|
/* ??? Although, all of this is probably only a temporary fix
|
because if %g1 can hold a function result, then
|
because if %g1 can hold a function result, then
|
sparc_expand_epilogue will lose (the result will be
|
sparc_expand_epilogue will lose (the result will be
|
clobbered). */
|
clobbered). */
|
base = gen_rtx_REG (Pmode, 1);
|
base = gen_rtx_REG (Pmode, 1);
|
emit_move_insn (base, GEN_INT (offset));
|
emit_move_insn (base, GEN_INT (offset));
|
emit_insn (gen_rtx_SET (VOIDmode,
|
emit_insn (gen_rtx_SET (VOIDmode,
|
base,
|
base,
|
gen_rtx_PLUS (Pmode, frame_base_reg, base)));
|
gen_rtx_PLUS (Pmode, frame_base_reg, base)));
|
offset = 0;
|
offset = 0;
|
}
|
}
|
else
|
else
|
base = frame_base_reg;
|
base = frame_base_reg;
|
|
|
offset = save_or_restore_regs (0, 8, base, offset, action);
|
offset = save_or_restore_regs (0, 8, base, offset, action);
|
save_or_restore_regs (32, TARGET_V9 ? 96 : 64, base, offset, action);
|
save_or_restore_regs (32, TARGET_V9 ? 96 : 64, base, offset, action);
|
}
|
}
|
|
|
/* Generate a save_register_window insn. */
|
/* Generate a save_register_window insn. */
|
|
|
static rtx
|
static rtx
|
gen_save_register_window (rtx increment)
|
gen_save_register_window (rtx increment)
|
{
|
{
|
if (TARGET_ARCH64)
|
if (TARGET_ARCH64)
|
return gen_save_register_windowdi (increment);
|
return gen_save_register_windowdi (increment);
|
else
|
else
|
return gen_save_register_windowsi (increment);
|
return gen_save_register_windowsi (increment);
|
}
|
}
|
|
|
/* Generate an increment for the stack pointer. */
|
/* Generate an increment for the stack pointer. */
|
|
|
static rtx
|
static rtx
|
gen_stack_pointer_inc (rtx increment)
|
gen_stack_pointer_inc (rtx increment)
|
{
|
{
|
return gen_rtx_SET (VOIDmode,
|
return gen_rtx_SET (VOIDmode,
|
stack_pointer_rtx,
|
stack_pointer_rtx,
|
gen_rtx_PLUS (Pmode,
|
gen_rtx_PLUS (Pmode,
|
stack_pointer_rtx,
|
stack_pointer_rtx,
|
increment));
|
increment));
|
}
|
}
|
|
|
/* Generate a decrement for the stack pointer. */
|
/* Generate a decrement for the stack pointer. */
|
|
|
static rtx
|
static rtx
|
gen_stack_pointer_dec (rtx decrement)
|
gen_stack_pointer_dec (rtx decrement)
|
{
|
{
|
return gen_rtx_SET (VOIDmode,
|
return gen_rtx_SET (VOIDmode,
|
stack_pointer_rtx,
|
stack_pointer_rtx,
|
gen_rtx_MINUS (Pmode,
|
gen_rtx_MINUS (Pmode,
|
stack_pointer_rtx,
|
stack_pointer_rtx,
|
decrement));
|
decrement));
|
}
|
}
|
|
|
/* Expand the function prologue. The prologue is responsible for reserving
|
/* Expand the function prologue. The prologue is responsible for reserving
|
storage for the frame, saving the call-saved registers and loading the
|
storage for the frame, saving the call-saved registers and loading the
|
PIC register if needed. */
|
PIC register if needed. */
|
|
|
void
|
void
|
sparc_expand_prologue (void)
|
sparc_expand_prologue (void)
|
{
|
{
|
rtx insn;
|
rtx insn;
|
int i;
|
int i;
|
|
|
/* Compute a snapshot of current_function_uses_only_leaf_regs. Relying
|
/* Compute a snapshot of current_function_uses_only_leaf_regs. Relying
|
on the final value of the flag means deferring the prologue/epilogue
|
on the final value of the flag means deferring the prologue/epilogue
|
expansion until just before the second scheduling pass, which is too
|
expansion until just before the second scheduling pass, which is too
|
late to emit multiple epilogues or return insns.
|
late to emit multiple epilogues or return insns.
|
|
|
Of course we are making the assumption that the value of the flag
|
Of course we are making the assumption that the value of the flag
|
will not change between now and its final value. Of the three parts
|
will not change between now and its final value. Of the three parts
|
of the formula, only the last one can reasonably vary. Let's take a
|
of the formula, only the last one can reasonably vary. Let's take a
|
closer look, after assuming that the first two ones are set to true
|
closer look, after assuming that the first two ones are set to true
|
(otherwise the last value is effectively silenced).
|
(otherwise the last value is effectively silenced).
|
|
|
If only_leaf_regs_used returns false, the global predicate will also
|
If only_leaf_regs_used returns false, the global predicate will also
|
be false so the actual frame size calculated below will be positive.
|
be false so the actual frame size calculated below will be positive.
|
As a consequence, the save_register_window insn will be emitted in
|
As a consequence, the save_register_window insn will be emitted in
|
the instruction stream; now this insn explicitly references %fp
|
the instruction stream; now this insn explicitly references %fp
|
which is not a leaf register so only_leaf_regs_used will always
|
which is not a leaf register so only_leaf_regs_used will always
|
return false subsequently.
|
return false subsequently.
|
|
|
If only_leaf_regs_used returns true, we hope that the subsequent
|
If only_leaf_regs_used returns true, we hope that the subsequent
|
optimization passes won't cause non-leaf registers to pop up. For
|
optimization passes won't cause non-leaf registers to pop up. For
|
example, the regrename pass has special provisions to not rename to
|
example, the regrename pass has special provisions to not rename to
|
non-leaf registers in a leaf function. */
|
non-leaf registers in a leaf function. */
|
sparc_leaf_function_p
|
sparc_leaf_function_p
|
= optimize > 0 && leaf_function_p () && only_leaf_regs_used ();
|
= optimize > 0 && leaf_function_p () && only_leaf_regs_used ();
|
|
|
/* Need to use actual_fsize, since we are also allocating
|
/* Need to use actual_fsize, since we are also allocating
|
space for our callee (and our own register save area). */
|
space for our callee (and our own register save area). */
|
actual_fsize
|
actual_fsize
|
= sparc_compute_frame_size (get_frame_size(), sparc_leaf_function_p);
|
= sparc_compute_frame_size (get_frame_size(), sparc_leaf_function_p);
|
|
|
/* Advertise that the data calculated just above are now valid. */
|
/* Advertise that the data calculated just above are now valid. */
|
sparc_prologue_data_valid_p = true;
|
sparc_prologue_data_valid_p = true;
|
|
|
if (sparc_leaf_function_p)
|
if (sparc_leaf_function_p)
|
{
|
{
|
frame_base_reg = stack_pointer_rtx;
|
frame_base_reg = stack_pointer_rtx;
|
frame_base_offset = actual_fsize + SPARC_STACK_BIAS;
|
frame_base_offset = actual_fsize + SPARC_STACK_BIAS;
|
}
|
}
|
else
|
else
|
{
|
{
|
frame_base_reg = hard_frame_pointer_rtx;
|
frame_base_reg = hard_frame_pointer_rtx;
|
frame_base_offset = SPARC_STACK_BIAS;
|
frame_base_offset = SPARC_STACK_BIAS;
|
}
|
}
|
|
|
if (actual_fsize == 0)
|
if (actual_fsize == 0)
|
/* do nothing. */ ;
|
/* do nothing. */ ;
|
else if (sparc_leaf_function_p)
|
else if (sparc_leaf_function_p)
|
{
|
{
|
if (actual_fsize <= 4096)
|
if (actual_fsize <= 4096)
|
insn = emit_insn (gen_stack_pointer_inc (GEN_INT (-actual_fsize)));
|
insn = emit_insn (gen_stack_pointer_inc (GEN_INT (-actual_fsize)));
|
else if (actual_fsize <= 8192)
|
else if (actual_fsize <= 8192)
|
{
|
{
|
insn = emit_insn (gen_stack_pointer_inc (GEN_INT (-4096)));
|
insn = emit_insn (gen_stack_pointer_inc (GEN_INT (-4096)));
|
/* %sp is still the CFA register. */
|
/* %sp is still the CFA register. */
|
RTX_FRAME_RELATED_P (insn) = 1;
|
RTX_FRAME_RELATED_P (insn) = 1;
|
insn
|
insn
|
= emit_insn (gen_stack_pointer_inc (GEN_INT (4096-actual_fsize)));
|
= emit_insn (gen_stack_pointer_inc (GEN_INT (4096-actual_fsize)));
|
}
|
}
|
else
|
else
|
{
|
{
|
rtx reg = gen_rtx_REG (Pmode, 1);
|
rtx reg = gen_rtx_REG (Pmode, 1);
|
emit_move_insn (reg, GEN_INT (-actual_fsize));
|
emit_move_insn (reg, GEN_INT (-actual_fsize));
|
insn = emit_insn (gen_stack_pointer_inc (reg));
|
insn = emit_insn (gen_stack_pointer_inc (reg));
|
add_reg_note (insn, REG_FRAME_RELATED_EXPR,
|
add_reg_note (insn, REG_FRAME_RELATED_EXPR,
|
gen_stack_pointer_inc (GEN_INT (-actual_fsize)));
|
gen_stack_pointer_inc (GEN_INT (-actual_fsize)));
|
}
|
}
|
|
|
RTX_FRAME_RELATED_P (insn) = 1;
|
RTX_FRAME_RELATED_P (insn) = 1;
|
}
|
}
|
else
|
else
|
{
|
{
|
if (actual_fsize <= 4096)
|
if (actual_fsize <= 4096)
|
insn = emit_insn (gen_save_register_window (GEN_INT (-actual_fsize)));
|
insn = emit_insn (gen_save_register_window (GEN_INT (-actual_fsize)));
|
else if (actual_fsize <= 8192)
|
else if (actual_fsize <= 8192)
|
{
|
{
|
insn = emit_insn (gen_save_register_window (GEN_INT (-4096)));
|
insn = emit_insn (gen_save_register_window (GEN_INT (-4096)));
|
/* %sp is not the CFA register anymore. */
|
/* %sp is not the CFA register anymore. */
|
emit_insn (gen_stack_pointer_inc (GEN_INT (4096-actual_fsize)));
|
emit_insn (gen_stack_pointer_inc (GEN_INT (4096-actual_fsize)));
|
}
|
}
|
else
|
else
|
{
|
{
|
rtx reg = gen_rtx_REG (Pmode, 1);
|
rtx reg = gen_rtx_REG (Pmode, 1);
|
emit_move_insn (reg, GEN_INT (-actual_fsize));
|
emit_move_insn (reg, GEN_INT (-actual_fsize));
|
insn = emit_insn (gen_save_register_window (reg));
|
insn = emit_insn (gen_save_register_window (reg));
|
}
|
}
|
|
|
RTX_FRAME_RELATED_P (insn) = 1;
|
RTX_FRAME_RELATED_P (insn) = 1;
|
for (i=0; i < XVECLEN (PATTERN (insn), 0); i++)
|
for (i=0; i < XVECLEN (PATTERN (insn), 0); i++)
|
RTX_FRAME_RELATED_P (XVECEXP (PATTERN (insn), 0, i)) = 1;
|
RTX_FRAME_RELATED_P (XVECEXP (PATTERN (insn), 0, i)) = 1;
|
}
|
}
|
|
|
if (num_gfregs)
|
if (num_gfregs)
|
emit_save_or_restore_regs (SORR_SAVE);
|
emit_save_or_restore_regs (SORR_SAVE);
|
|
|
/* Load the PIC register if needed. */
|
/* Load the PIC register if needed. */
|
if (flag_pic && crtl->uses_pic_offset_table)
|
if (flag_pic && crtl->uses_pic_offset_table)
|
load_pic_register ();
|
load_pic_register ();
|
}
|
}
|
|
|
/* This function generates the assembly code for function entry, which boils
|
/* This function generates the assembly code for function entry, which boils
|
down to emitting the necessary .register directives. */
|
down to emitting the necessary .register directives. */
|
|
|
static void
|
static void
|
sparc_asm_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
|
sparc_asm_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
|
{
|
{
|
/* Check that the assumption we made in sparc_expand_prologue is valid. */
|
/* Check that the assumption we made in sparc_expand_prologue is valid. */
|
gcc_assert (sparc_leaf_function_p == current_function_uses_only_leaf_regs);
|
gcc_assert (sparc_leaf_function_p == current_function_uses_only_leaf_regs);
|
|
|
sparc_output_scratch_registers (file);
|
sparc_output_scratch_registers (file);
|
}
|
}
|
|
|
/* Expand the function epilogue, either normal or part of a sibcall.
|
/* Expand the function epilogue, either normal or part of a sibcall.
|
We emit all the instructions except the return or the call. */
|
We emit all the instructions except the return or the call. */
|
|
|
void
|
void
|
sparc_expand_epilogue (void)
|
sparc_expand_epilogue (void)
|
{
|
{
|
if (num_gfregs)
|
if (num_gfregs)
|
emit_save_or_restore_regs (SORR_RESTORE);
|
emit_save_or_restore_regs (SORR_RESTORE);
|
|
|
if (actual_fsize == 0)
|
if (actual_fsize == 0)
|
/* do nothing. */ ;
|
/* do nothing. */ ;
|
else if (sparc_leaf_function_p)
|
else if (sparc_leaf_function_p)
|
{
|
{
|
if (actual_fsize <= 4096)
|
if (actual_fsize <= 4096)
|
emit_insn (gen_stack_pointer_dec (GEN_INT (- actual_fsize)));
|
emit_insn (gen_stack_pointer_dec (GEN_INT (- actual_fsize)));
|
else if (actual_fsize <= 8192)
|
else if (actual_fsize <= 8192)
|
{
|
{
|
emit_insn (gen_stack_pointer_dec (GEN_INT (-4096)));
|
emit_insn (gen_stack_pointer_dec (GEN_INT (-4096)));
|
emit_insn (gen_stack_pointer_dec (GEN_INT (4096 - actual_fsize)));
|
emit_insn (gen_stack_pointer_dec (GEN_INT (4096 - actual_fsize)));
|
}
|
}
|
else
|
else
|
{
|
{
|
rtx reg = gen_rtx_REG (Pmode, 1);
|
rtx reg = gen_rtx_REG (Pmode, 1);
|
emit_move_insn (reg, GEN_INT (-actual_fsize));
|
emit_move_insn (reg, GEN_INT (-actual_fsize));
|
emit_insn (gen_stack_pointer_dec (reg));
|
emit_insn (gen_stack_pointer_dec (reg));
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/* Return true if it is appropriate to emit `return' instructions in the
|
/* Return true if it is appropriate to emit `return' instructions in the
|
body of a function. */
|
body of a function. */
|
|
|
bool
|
bool
|
sparc_can_use_return_insn_p (void)
|
sparc_can_use_return_insn_p (void)
|
{
|
{
|
return sparc_prologue_data_valid_p
|
return sparc_prologue_data_valid_p
|
&& (actual_fsize == 0 || !sparc_leaf_function_p);
|
&& (actual_fsize == 0 || !sparc_leaf_function_p);
|
}
|
}
|
|
|
/* This function generates the assembly code for function exit. */
|
/* This function generates the assembly code for function exit. */
|
|
|
static void
|
static void
|
sparc_asm_function_epilogue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
|
sparc_asm_function_epilogue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
|
{
|
{
|
/* If code does not drop into the epilogue, we have to still output
|
/* If code does not drop into the epilogue, we have to still output
|
a dummy nop for the sake of sane backtraces. Otherwise, if the
|
a dummy nop for the sake of sane backtraces. Otherwise, if the
|
last two instructions of a function were "call foo; dslot;" this
|
last two instructions of a function were "call foo; dslot;" this
|
can make the return PC of foo (i.e. address of call instruction
|
can make the return PC of foo (i.e. address of call instruction
|
plus 8) point to the first instruction in the next function. */
|
plus 8) point to the first instruction in the next function. */
|
|
|
rtx insn, last_real_insn;
|
rtx insn, last_real_insn;
|
|
|
insn = get_last_insn ();
|
insn = get_last_insn ();
|
|
|
last_real_insn = prev_real_insn (insn);
|
last_real_insn = prev_real_insn (insn);
|
if (last_real_insn
|
if (last_real_insn
|
&& GET_CODE (last_real_insn) == INSN
|
&& GET_CODE (last_real_insn) == INSN
|
&& GET_CODE (PATTERN (last_real_insn)) == SEQUENCE)
|
&& GET_CODE (PATTERN (last_real_insn)) == SEQUENCE)
|
last_real_insn = XVECEXP (PATTERN (last_real_insn), 0, 0);
|
last_real_insn = XVECEXP (PATTERN (last_real_insn), 0, 0);
|
|
|
if (last_real_insn && GET_CODE (last_real_insn) == CALL_INSN)
|
if (last_real_insn && GET_CODE (last_real_insn) == CALL_INSN)
|
fputs("\tnop\n", file);
|
fputs("\tnop\n", file);
|
|
|
sparc_output_deferred_case_vectors ();
|
sparc_output_deferred_case_vectors ();
|
}
|
}
|
|
|
/* Output a 'restore' instruction. */
|
/* Output a 'restore' instruction. */
|
|
|
static void
|
static void
|
output_restore (rtx pat)
|
output_restore (rtx pat)
|
{
|
{
|
rtx operands[3];
|
rtx operands[3];
|
|
|
if (! pat)
|
if (! pat)
|
{
|
{
|
fputs ("\t restore\n", asm_out_file);
|
fputs ("\t restore\n", asm_out_file);
|
return;
|
return;
|
}
|
}
|
|
|
gcc_assert (GET_CODE (pat) == SET);
|
gcc_assert (GET_CODE (pat) == SET);
|
|
|
operands[0] = SET_DEST (pat);
|
operands[0] = SET_DEST (pat);
|
pat = SET_SRC (pat);
|
pat = SET_SRC (pat);
|
|
|
switch (GET_CODE (pat))
|
switch (GET_CODE (pat))
|
{
|
{
|
case PLUS:
|
case PLUS:
|
operands[1] = XEXP (pat, 0);
|
operands[1] = XEXP (pat, 0);
|
operands[2] = XEXP (pat, 1);
|
operands[2] = XEXP (pat, 1);
|
output_asm_insn (" restore %r1, %2, %Y0", operands);
|
output_asm_insn (" restore %r1, %2, %Y0", operands);
|
break;
|
break;
|
case LO_SUM:
|
case LO_SUM:
|
operands[1] = XEXP (pat, 0);
|
operands[1] = XEXP (pat, 0);
|
operands[2] = XEXP (pat, 1);
|
operands[2] = XEXP (pat, 1);
|
output_asm_insn (" restore %r1, %%lo(%a2), %Y0", operands);
|
output_asm_insn (" restore %r1, %%lo(%a2), %Y0", operands);
|
break;
|
break;
|
case ASHIFT:
|
case ASHIFT:
|
operands[1] = XEXP (pat, 0);
|
operands[1] = XEXP (pat, 0);
|
gcc_assert (XEXP (pat, 1) == const1_rtx);
|
gcc_assert (XEXP (pat, 1) == const1_rtx);
|
output_asm_insn (" restore %r1, %r1, %Y0", operands);
|
output_asm_insn (" restore %r1, %r1, %Y0", operands);
|
break;
|
break;
|
default:
|
default:
|
operands[1] = pat;
|
operands[1] = pat;
|
output_asm_insn (" restore %%g0, %1, %Y0", operands);
|
output_asm_insn (" restore %%g0, %1, %Y0", operands);
|
break;
|
break;
|
}
|
}
|
}
|
}
|
|
|
/* Output a return. */
|
/* Output a return. */
|
|
|
const char *
|
const char *
|
output_return (rtx insn)
|
output_return (rtx insn)
|
{
|
{
|
if (sparc_leaf_function_p)
|
if (sparc_leaf_function_p)
|
{
|
{
|
/* This is a leaf function so we don't have to bother restoring the
|
/* This is a leaf function so we don't have to bother restoring the
|
register window, which frees us from dealing with the convoluted
|
register window, which frees us from dealing with the convoluted
|
semantics of restore/return. We simply output the jump to the
|
semantics of restore/return. We simply output the jump to the
|
return address and the insn in the delay slot (if any). */
|
return address and the insn in the delay slot (if any). */
|
|
|
gcc_assert (! crtl->calls_eh_return);
|
gcc_assert (! crtl->calls_eh_return);
|
|
|
return "jmp\t%%o7+%)%#";
|
return "jmp\t%%o7+%)%#";
|
}
|
}
|
else
|
else
|
{
|
{
|
/* This is a regular function so we have to restore the register window.
|
/* This is a regular function so we have to restore the register window.
|
We may have a pending insn for the delay slot, which will be either
|
We may have a pending insn for the delay slot, which will be either
|
combined with the 'restore' instruction or put in the delay slot of
|
combined with the 'restore' instruction or put in the delay slot of
|
the 'return' instruction. */
|
the 'return' instruction. */
|
|
|
if (crtl->calls_eh_return)
|
if (crtl->calls_eh_return)
|
{
|
{
|
/* If the function uses __builtin_eh_return, the eh_return
|
/* If the function uses __builtin_eh_return, the eh_return
|
machinery occupies the delay slot. */
|
machinery occupies the delay slot. */
|
gcc_assert (! final_sequence);
|
gcc_assert (! final_sequence);
|
|
|
if (! flag_delayed_branch)
|
if (! flag_delayed_branch)
|
fputs ("\tadd\t%fp, %g1, %fp\n", asm_out_file);
|
fputs ("\tadd\t%fp, %g1, %fp\n", asm_out_file);
|
|
|
if (TARGET_V9)
|
if (TARGET_V9)
|
fputs ("\treturn\t%i7+8\n", asm_out_file);
|
fputs ("\treturn\t%i7+8\n", asm_out_file);
|
else
|
else
|
fputs ("\trestore\n\tjmp\t%o7+8\n", asm_out_file);
|
fputs ("\trestore\n\tjmp\t%o7+8\n", asm_out_file);
|
|
|
if (flag_delayed_branch)
|
if (flag_delayed_branch)
|
fputs ("\t add\t%sp, %g1, %sp\n", asm_out_file);
|
fputs ("\t add\t%sp, %g1, %sp\n", asm_out_file);
|
else
|
else
|
fputs ("\t nop\n", asm_out_file);
|
fputs ("\t nop\n", asm_out_file);
|
}
|
}
|
else if (final_sequence)
|
else if (final_sequence)
|
{
|
{
|
rtx delay, pat;
|
rtx delay, pat;
|
|
|
delay = NEXT_INSN (insn);
|
delay = NEXT_INSN (insn);
|
gcc_assert (delay);
|
gcc_assert (delay);
|
|
|
pat = PATTERN (delay);
|
pat = PATTERN (delay);
|
|
|
if (TARGET_V9 && ! epilogue_renumber (&pat, 1))
|
if (TARGET_V9 && ! epilogue_renumber (&pat, 1))
|
{
|
{
|
epilogue_renumber (&pat, 0);
|
epilogue_renumber (&pat, 0);
|
return "return\t%%i7+%)%#";
|
return "return\t%%i7+%)%#";
|
}
|
}
|
else
|
else
|
{
|
{
|
output_asm_insn ("jmp\t%%i7+%)", NULL);
|
output_asm_insn ("jmp\t%%i7+%)", NULL);
|
output_restore (pat);
|
output_restore (pat);
|
PATTERN (delay) = gen_blockage ();
|
PATTERN (delay) = gen_blockage ();
|
INSN_CODE (delay) = -1;
|
INSN_CODE (delay) = -1;
|
}
|
}
|
}
|
}
|
else
|
else
|
{
|
{
|
/* The delay slot is empty. */
|
/* The delay slot is empty. */
|
if (TARGET_V9)
|
if (TARGET_V9)
|
return "return\t%%i7+%)\n\t nop";
|
return "return\t%%i7+%)\n\t nop";
|
else if (flag_delayed_branch)
|
else if (flag_delayed_branch)
|
return "jmp\t%%i7+%)\n\t restore";
|
return "jmp\t%%i7+%)\n\t restore";
|
else
|
else
|
return "restore\n\tjmp\t%%o7+%)\n\t nop";
|
return "restore\n\tjmp\t%%o7+%)\n\t nop";
|
}
|
}
|
}
|
}
|
|
|
return "";
|
return "";
|
}
|
}
|
|
|
/* Output a sibling call. */
|
/* Output a sibling call. */
|
|
|
const char *
|
const char *
|
output_sibcall (rtx insn, rtx call_operand)
|
output_sibcall (rtx insn, rtx call_operand)
|
{
|
{
|
rtx operands[1];
|
rtx operands[1];
|
|
|
gcc_assert (flag_delayed_branch);
|
gcc_assert (flag_delayed_branch);
|
|
|
operands[0] = call_operand;
|
operands[0] = call_operand;
|
|
|
if (sparc_leaf_function_p)
|
if (sparc_leaf_function_p)
|
{
|
{
|
/* This is a leaf function so we don't have to bother restoring the
|
/* This is a leaf function so we don't have to bother restoring the
|
register window. We simply output the jump to the function and
|
register window. We simply output the jump to the function and
|
the insn in the delay slot (if any). */
|
the insn in the delay slot (if any). */
|
|
|
gcc_assert (!(LEAF_SIBCALL_SLOT_RESERVED_P && final_sequence));
|
gcc_assert (!(LEAF_SIBCALL_SLOT_RESERVED_P && final_sequence));
|
|
|
if (final_sequence)
|
if (final_sequence)
|
output_asm_insn ("sethi\t%%hi(%a0), %%g1\n\tjmp\t%%g1 + %%lo(%a0)%#",
|
output_asm_insn ("sethi\t%%hi(%a0), %%g1\n\tjmp\t%%g1 + %%lo(%a0)%#",
|
operands);
|
operands);
|
else
|
else
|
/* Use or with rs2 %%g0 instead of mov, so that as/ld can optimize
|
/* Use or with rs2 %%g0 instead of mov, so that as/ld can optimize
|
it into branch if possible. */
|
it into branch if possible. */
|
output_asm_insn ("or\t%%o7, %%g0, %%g1\n\tcall\t%a0, 0\n\t or\t%%g1, %%g0, %%o7",
|
output_asm_insn ("or\t%%o7, %%g0, %%g1\n\tcall\t%a0, 0\n\t or\t%%g1, %%g0, %%o7",
|
operands);
|
operands);
|
}
|
}
|
else
|
else
|
{
|
{
|
/* This is a regular function so we have to restore the register window.
|
/* This is a regular function so we have to restore the register window.
|
We may have a pending insn for the delay slot, which will be combined
|
We may have a pending insn for the delay slot, which will be combined
|
with the 'restore' instruction. */
|
with the 'restore' instruction. */
|
|
|
output_asm_insn ("call\t%a0, 0", operands);
|
output_asm_insn ("call\t%a0, 0", operands);
|
|
|
if (final_sequence)
|
if (final_sequence)
|
{
|
{
|
rtx delay = NEXT_INSN (insn);
|
rtx delay = NEXT_INSN (insn);
|
gcc_assert (delay);
|
gcc_assert (delay);
|
|
|
output_restore (PATTERN (delay));
|
output_restore (PATTERN (delay));
|
|
|
PATTERN (delay) = gen_blockage ();
|
PATTERN (delay) = gen_blockage ();
|
INSN_CODE (delay) = -1;
|
INSN_CODE (delay) = -1;
|
}
|
}
|
else
|
else
|
output_restore (NULL_RTX);
|
output_restore (NULL_RTX);
|
}
|
}
|
|
|
return "";
|
return "";
|
}
|
}
|
|
|
/* Functions for handling argument passing.
|
/* Functions for handling argument passing.
|
|
|
For 32-bit, the first 6 args are normally in registers and the rest are
|
For 32-bit, the first 6 args are normally in registers and the rest are
|
pushed. Any arg that starts within the first 6 words is at least
|
pushed. Any arg that starts within the first 6 words is at least
|
partially passed in a register unless its data type forbids.
|
partially passed in a register unless its data type forbids.
|
|
|
For 64-bit, the argument registers are laid out as an array of 16 elements
|
For 64-bit, the argument registers are laid out as an array of 16 elements
|
and arguments are added sequentially. The first 6 int args and up to the
|
and arguments are added sequentially. The first 6 int args and up to the
|
first 16 fp args (depending on size) are passed in regs.
|
first 16 fp args (depending on size) are passed in regs.
|
|
|
Slot Stack Integral Float Float in structure Double Long Double
|
Slot Stack Integral Float Float in structure Double Long Double
|
---- ----- -------- ----- ------------------ ------ -----------
|
---- ----- -------- ----- ------------------ ------ -----------
|
15 [SP+248] %f31 %f30,%f31 %d30
|
15 [SP+248] %f31 %f30,%f31 %d30
|
14 [SP+240] %f29 %f28,%f29 %d28 %q28
|
14 [SP+240] %f29 %f28,%f29 %d28 %q28
|
13 [SP+232] %f27 %f26,%f27 %d26
|
13 [SP+232] %f27 %f26,%f27 %d26
|
12 [SP+224] %f25 %f24,%f25 %d24 %q24
|
12 [SP+224] %f25 %f24,%f25 %d24 %q24
|
11 [SP+216] %f23 %f22,%f23 %d22
|
11 [SP+216] %f23 %f22,%f23 %d22
|
10 [SP+208] %f21 %f20,%f21 %d20 %q20
|
10 [SP+208] %f21 %f20,%f21 %d20 %q20
|
9 [SP+200] %f19 %f18,%f19 %d18
|
9 [SP+200] %f19 %f18,%f19 %d18
|
8 [SP+192] %f17 %f16,%f17 %d16 %q16
|
8 [SP+192] %f17 %f16,%f17 %d16 %q16
|
7 [SP+184] %f15 %f14,%f15 %d14
|
7 [SP+184] %f15 %f14,%f15 %d14
|
6 [SP+176] %f13 %f12,%f13 %d12 %q12
|
6 [SP+176] %f13 %f12,%f13 %d12 %q12
|
5 [SP+168] %o5 %f11 %f10,%f11 %d10
|
5 [SP+168] %o5 %f11 %f10,%f11 %d10
|
4 [SP+160] %o4 %f9 %f8,%f9 %d8 %q8
|
4 [SP+160] %o4 %f9 %f8,%f9 %d8 %q8
|
3 [SP+152] %o3 %f7 %f6,%f7 %d6
|
3 [SP+152] %o3 %f7 %f6,%f7 %d6
|
2 [SP+144] %o2 %f5 %f4,%f5 %d4 %q4
|
2 [SP+144] %o2 %f5 %f4,%f5 %d4 %q4
|
1 [SP+136] %o1 %f3 %f2,%f3 %d2
|
1 [SP+136] %o1 %f3 %f2,%f3 %d2
|
0 [SP+128] %o0 %f1 %f0,%f1 %d0 %q0
|
0 [SP+128] %o0 %f1 %f0,%f1 %d0 %q0
|
|
|
Here SP = %sp if -mno-stack-bias or %sp+stack_bias otherwise.
|
Here SP = %sp if -mno-stack-bias or %sp+stack_bias otherwise.
|
|
|
Integral arguments are always passed as 64-bit quantities appropriately
|
Integral arguments are always passed as 64-bit quantities appropriately
|
extended.
|
extended.
|
|
|
Passing of floating point values is handled as follows.
|
Passing of floating point values is handled as follows.
|
If a prototype is in scope:
|
If a prototype is in scope:
|
If the value is in a named argument (i.e. not a stdarg function or a
|
If the value is in a named argument (i.e. not a stdarg function or a
|
value not part of the `...') then the value is passed in the appropriate
|
value not part of the `...') then the value is passed in the appropriate
|
fp reg.
|
fp reg.
|
If the value is part of the `...' and is passed in one of the first 6
|
If the value is part of the `...' and is passed in one of the first 6
|
slots then the value is passed in the appropriate int reg.
|
slots then the value is passed in the appropriate int reg.
|
If the value is part of the `...' and is not passed in one of the first 6
|
If the value is part of the `...' and is not passed in one of the first 6
|
slots then the value is passed in memory.
|
slots then the value is passed in memory.
|
If a prototype is not in scope:
|
If a prototype is not in scope:
|
If the value is one of the first 6 arguments the value is passed in the
|
If the value is one of the first 6 arguments the value is passed in the
|
appropriate integer reg and the appropriate fp reg.
|
appropriate integer reg and the appropriate fp reg.
|
If the value is not one of the first 6 arguments the value is passed in
|
If the value is not one of the first 6 arguments the value is passed in
|
the appropriate fp reg and in memory.
|
the appropriate fp reg and in memory.
|
|
|
|
|
Summary of the calling conventions implemented by GCC on the SPARC:
|
Summary of the calling conventions implemented by GCC on the SPARC:
|
|
|
32-bit ABI:
|
32-bit ABI:
|
size argument return value
|
size argument return value
|
|
|
small integer <4 int. reg. int. reg.
|
small integer <4 int. reg. int. reg.
|
word 4 int. reg. int. reg.
|
word 4 int. reg. int. reg.
|
double word 8 int. reg. int. reg.
|
double word 8 int. reg. int. reg.
|
|
|
_Complex small integer <8 int. reg. int. reg.
|
_Complex small integer <8 int. reg. int. reg.
|
_Complex word 8 int. reg. int. reg.
|
_Complex word 8 int. reg. int. reg.
|
_Complex double word 16 memory int. reg.
|
_Complex double word 16 memory int. reg.
|
|
|
vector integer <=8 int. reg. FP reg.
|
vector integer <=8 int. reg. FP reg.
|
vector integer >8 memory memory
|
vector integer >8 memory memory
|
|
|
float 4 int. reg. FP reg.
|
float 4 int. reg. FP reg.
|
double 8 int. reg. FP reg.
|
double 8 int. reg. FP reg.
|
long double 16 memory memory
|
long double 16 memory memory
|
|
|
_Complex float 8 memory FP reg.
|
_Complex float 8 memory FP reg.
|
_Complex double 16 memory FP reg.
|
_Complex double 16 memory FP reg.
|
_Complex long double 32 memory FP reg.
|
_Complex long double 32 memory FP reg.
|
|
|
vector float any memory memory
|
vector float any memory memory
|
|
|
aggregate any memory memory
|
aggregate any memory memory
|
|
|
|
|
|
|
64-bit ABI:
|
64-bit ABI:
|
size argument return value
|
size argument return value
|
|
|
small integer <8 int. reg. int. reg.
|
small integer <8 int. reg. int. reg.
|
word 8 int. reg. int. reg.
|
word 8 int. reg. int. reg.
|
double word 16 int. reg. int. reg.
|
double word 16 int. reg. int. reg.
|
|
|
_Complex small integer <16 int. reg. int. reg.
|
_Complex small integer <16 int. reg. int. reg.
|
_Complex word 16 int. reg. int. reg.
|
_Complex word 16 int. reg. int. reg.
|
_Complex double word 32 memory int. reg.
|
_Complex double word 32 memory int. reg.
|
|
|
vector integer <=16 FP reg. FP reg.
|
vector integer <=16 FP reg. FP reg.
|
vector integer 16<s<=32 memory FP reg.
|
vector integer 16<s<=32 memory FP reg.
|
vector integer >32 memory memory
|
vector integer >32 memory memory
|
|
|
float 4 FP reg. FP reg.
|
float 4 FP reg. FP reg.
|
double 8 FP reg. FP reg.
|
double 8 FP reg. FP reg.
|
long double 16 FP reg. FP reg.
|
long double 16 FP reg. FP reg.
|
|
|
_Complex float 8 FP reg. FP reg.
|
_Complex float 8 FP reg. FP reg.
|
_Complex double 16 FP reg. FP reg.
|
_Complex double 16 FP reg. FP reg.
|
_Complex long double 32 memory FP reg.
|
_Complex long double 32 memory FP reg.
|
|
|
vector float <=16 FP reg. FP reg.
|
vector float <=16 FP reg. FP reg.
|
vector float 16<s<=32 memory FP reg.
|
vector float 16<s<=32 memory FP reg.
|
vector float >32 memory memory
|
vector float >32 memory memory
|
|
|
aggregate <=16 reg. reg.
|
aggregate <=16 reg. reg.
|
aggregate 16<s<=32 memory reg.
|
aggregate 16<s<=32 memory reg.
|
aggregate >32 memory memory
|
aggregate >32 memory memory
|
|
|
|
|
|
|
Note #1: complex floating-point types follow the extended SPARC ABIs as
|
Note #1: complex floating-point types follow the extended SPARC ABIs as
|
implemented by the Sun compiler.
|
implemented by the Sun compiler.
|
|
|
Note #2: integral vector types follow the scalar floating-point types
|
Note #2: integral vector types follow the scalar floating-point types
|
conventions to match what is implemented by the Sun VIS SDK.
|
conventions to match what is implemented by the Sun VIS SDK.
|
|
|
Note #3: floating-point vector types follow the aggregate types
|
Note #3: floating-point vector types follow the aggregate types
|
conventions. */
|
conventions. */
|
|
|
|
|
/* Maximum number of int regs for args. */
|
/* Maximum number of int regs for args. */
|
#define SPARC_INT_ARG_MAX 6
|
#define SPARC_INT_ARG_MAX 6
|
/* Maximum number of fp regs for args. */
|
/* Maximum number of fp regs for args. */
|
#define SPARC_FP_ARG_MAX 16
|
#define SPARC_FP_ARG_MAX 16
|
|
|
#define ROUND_ADVANCE(SIZE) (((SIZE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
|
#define ROUND_ADVANCE(SIZE) (((SIZE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
|
|
|
/* Handle the INIT_CUMULATIVE_ARGS macro.
|
/* Handle the INIT_CUMULATIVE_ARGS macro.
|
Initialize a variable CUM of type CUMULATIVE_ARGS
|
Initialize a variable CUM of type CUMULATIVE_ARGS
|
for a call to a function whose data type is FNTYPE.
|
for a call to a function whose data type is FNTYPE.
|
For a library call, FNTYPE is 0. */
|
For a library call, FNTYPE is 0. */
|
|
|
void
|
void
|
init_cumulative_args (struct sparc_args *cum, tree fntype,
|
init_cumulative_args (struct sparc_args *cum, tree fntype,
|
rtx libname ATTRIBUTE_UNUSED,
|
rtx libname ATTRIBUTE_UNUSED,
|
tree fndecl ATTRIBUTE_UNUSED)
|
tree fndecl ATTRIBUTE_UNUSED)
|
{
|
{
|
cum->words = 0;
|
cum->words = 0;
|
cum->prototype_p = fntype && TYPE_ARG_TYPES (fntype);
|
cum->prototype_p = fntype && TYPE_ARG_TYPES (fntype);
|
cum->libcall_p = fntype == 0;
|
cum->libcall_p = fntype == 0;
|
}
|
}
|
|
|
/* Handle the TARGET_PROMOTE_PROTOTYPES target hook.
|
/* Handle the TARGET_PROMOTE_PROTOTYPES target hook.
|
When a prototype says `char' or `short', really pass an `int'. */
|
When a prototype says `char' or `short', really pass an `int'. */
|
|
|
static bool
|
static bool
|
sparc_promote_prototypes (const_tree fntype ATTRIBUTE_UNUSED)
|
sparc_promote_prototypes (const_tree fntype ATTRIBUTE_UNUSED)
|
{
|
{
|
return TARGET_ARCH32 ? true : false;
|
return TARGET_ARCH32 ? true : false;
|
}
|
}
|
|
|
/* Handle promotion of pointer and integer arguments. */
|
/* Handle promotion of pointer and integer arguments. */
|
|
|
static enum machine_mode
|
static enum machine_mode
|
sparc_promote_function_mode (const_tree type ATTRIBUTE_UNUSED,
|
sparc_promote_function_mode (const_tree type ATTRIBUTE_UNUSED,
|
enum machine_mode mode,
|
enum machine_mode mode,
|
int *punsignedp ATTRIBUTE_UNUSED,
|
int *punsignedp ATTRIBUTE_UNUSED,
|
const_tree fntype ATTRIBUTE_UNUSED,
|
const_tree fntype ATTRIBUTE_UNUSED,
|
int for_return ATTRIBUTE_UNUSED)
|
int for_return ATTRIBUTE_UNUSED)
|
{
|
{
|
if (POINTER_TYPE_P (type))
|
if (POINTER_TYPE_P (type))
|
{
|
{
|
*punsignedp = POINTERS_EXTEND_UNSIGNED;
|
*punsignedp = POINTERS_EXTEND_UNSIGNED;
|
return Pmode;
|
return Pmode;
|
}
|
}
|
|
|
/* For TARGET_ARCH64 we need this, as we don't have instructions
|
/* For TARGET_ARCH64 we need this, as we don't have instructions
|
for arithmetic operations which do zero/sign extension at the same time,
|
for arithmetic operations which do zero/sign extension at the same time,
|
so without this we end up with a srl/sra after every assignment to an
|
so without this we end up with a srl/sra after every assignment to an
|
user variable, which means very very bad code. */
|
user variable, which means very very bad code. */
|
if (TARGET_ARCH64
|
if (TARGET_ARCH64
|
&& GET_MODE_CLASS (mode) == MODE_INT
|
&& GET_MODE_CLASS (mode) == MODE_INT
|
&& GET_MODE_SIZE (mode) < UNITS_PER_WORD)
|
&& GET_MODE_SIZE (mode) < UNITS_PER_WORD)
|
return word_mode;
|
return word_mode;
|
|
|
return mode;
|
return mode;
|
}
|
}
|
|
|
/* Handle the TARGET_STRICT_ARGUMENT_NAMING target hook. */
|
/* Handle the TARGET_STRICT_ARGUMENT_NAMING target hook. */
|
|
|
static bool
|
static bool
|
sparc_strict_argument_naming (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED)
|
sparc_strict_argument_naming (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED)
|
{
|
{
|
return TARGET_ARCH64 ? true : false;
|
return TARGET_ARCH64 ? true : false;
|
}
|
}
|
|
|
/* Scan the record type TYPE and return the following predicates:
|
/* Scan the record type TYPE and return the following predicates:
|
- INTREGS_P: the record contains at least one field or sub-field
|
- INTREGS_P: the record contains at least one field or sub-field
|
that is eligible for promotion in integer registers.
|
that is eligible for promotion in integer registers.
|
- FP_REGS_P: the record contains at least one field or sub-field
|
- FP_REGS_P: the record contains at least one field or sub-field
|
that is eligible for promotion in floating-point registers.
|
that is eligible for promotion in floating-point registers.
|
- PACKED_P: the record contains at least one field that is packed.
|
- PACKED_P: the record contains at least one field that is packed.
|
|
|
Sub-fields are not taken into account for the PACKED_P predicate. */
|
Sub-fields are not taken into account for the PACKED_P predicate. */
|
|
|
static void
|
static void
|
scan_record_type (tree type, int *intregs_p, int *fpregs_p, int *packed_p)
|
scan_record_type (tree type, int *intregs_p, int *fpregs_p, int *packed_p)
|
{
|
{
|
tree field;
|
tree field;
|
|
|
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
|
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
|
{
|
{
|
if (TREE_CODE (field) == FIELD_DECL)
|
if (TREE_CODE (field) == FIELD_DECL)
|
{
|
{
|
if (TREE_CODE (TREE_TYPE (field)) == RECORD_TYPE)
|
if (TREE_CODE (TREE_TYPE (field)) == RECORD_TYPE)
|
scan_record_type (TREE_TYPE (field), intregs_p, fpregs_p, 0);
|
scan_record_type (TREE_TYPE (field), intregs_p, fpregs_p, 0);
|
else if ((FLOAT_TYPE_P (TREE_TYPE (field))
|
else if ((FLOAT_TYPE_P (TREE_TYPE (field))
|
|| TREE_CODE (TREE_TYPE (field)) == VECTOR_TYPE)
|
|| TREE_CODE (TREE_TYPE (field)) == VECTOR_TYPE)
|
&& TARGET_FPU)
|
&& TARGET_FPU)
|
*fpregs_p = 1;
|
*fpregs_p = 1;
|
else
|
else
|
*intregs_p = 1;
|
*intregs_p = 1;
|
|
|
if (packed_p && DECL_PACKED (field))
|
if (packed_p && DECL_PACKED (field))
|
*packed_p = 1;
|
*packed_p = 1;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/* Compute the slot number to pass an argument in.
|
/* Compute the slot number to pass an argument in.
|
Return the slot number or -1 if passing on the stack.
|
Return the slot number or -1 if passing on the stack.
|
|
|
CUM is a variable of type CUMULATIVE_ARGS which gives info about
|
CUM is a variable of type CUMULATIVE_ARGS which gives info about
|
the preceding args and about the function being called.
|
the preceding args and about the function being called.
|
MODE is the argument's machine mode.
|
MODE is the argument's machine mode.
|
TYPE is the data type of the argument (as a tree).
|
TYPE is the data type of the argument (as a tree).
|
This is null for libcalls where that information may
|
This is null for libcalls where that information may
|
not be available.
|
not be available.
|
NAMED is nonzero if this argument is a named parameter
|
NAMED is nonzero if this argument is a named parameter
|
(otherwise it is an extra parameter matching an ellipsis).
|
(otherwise it is an extra parameter matching an ellipsis).
|
INCOMING_P is zero for FUNCTION_ARG, nonzero for FUNCTION_INCOMING_ARG.
|
INCOMING_P is zero for FUNCTION_ARG, nonzero for FUNCTION_INCOMING_ARG.
|
*PREGNO records the register number to use if scalar type.
|
*PREGNO records the register number to use if scalar type.
|
*PPADDING records the amount of padding needed in words. */
|
*PPADDING records the amount of padding needed in words. */
|
|
|
static int
|
static int
|
function_arg_slotno (const struct sparc_args *cum, enum machine_mode mode,
|
function_arg_slotno (const struct sparc_args *cum, enum machine_mode mode,
|
tree type, int named, int incoming_p,
|
tree type, int named, int incoming_p,
|
int *pregno, int *ppadding)
|
int *pregno, int *ppadding)
|
{
|
{
|
int regbase = (incoming_p
|
int regbase = (incoming_p
|
? SPARC_INCOMING_INT_ARG_FIRST
|
? SPARC_INCOMING_INT_ARG_FIRST
|
: SPARC_OUTGOING_INT_ARG_FIRST);
|
: SPARC_OUTGOING_INT_ARG_FIRST);
|
int slotno = cum->words;
|
int slotno = cum->words;
|
enum mode_class mclass;
|
enum mode_class mclass;
|
int regno;
|
int regno;
|
|
|
*ppadding = 0;
|
*ppadding = 0;
|
|
|
if (type && TREE_ADDRESSABLE (type))
|
if (type && TREE_ADDRESSABLE (type))
|
return -1;
|
return -1;
|
|
|
if (TARGET_ARCH32
|
if (TARGET_ARCH32
|
&& mode == BLKmode
|
&& mode == BLKmode
|
&& type
|
&& type
|
&& TYPE_ALIGN (type) % PARM_BOUNDARY != 0)
|
&& TYPE_ALIGN (type) % PARM_BOUNDARY != 0)
|
return -1;
|
return -1;
|
|
|
/* For SPARC64, objects requiring 16-byte alignment get it. */
|
/* For SPARC64, objects requiring 16-byte alignment get it. */
|
if (TARGET_ARCH64
|
if (TARGET_ARCH64
|
&& (type ? TYPE_ALIGN (type) : GET_MODE_ALIGNMENT (mode)) >= 128
|
&& (type ? TYPE_ALIGN (type) : GET_MODE_ALIGNMENT (mode)) >= 128
|
&& (slotno & 1) != 0)
|
&& (slotno & 1) != 0)
|
slotno++, *ppadding = 1;
|
slotno++, *ppadding = 1;
|
|
|
mclass = GET_MODE_CLASS (mode);
|
mclass = GET_MODE_CLASS (mode);
|
if (type && TREE_CODE (type) == VECTOR_TYPE)
|
if (type && TREE_CODE (type) == VECTOR_TYPE)
|
{
|
{
|
/* Vector types deserve special treatment because they are
|
/* Vector types deserve special treatment because they are
|
polymorphic wrt their mode, depending upon whether VIS
|
polymorphic wrt their mode, depending upon whether VIS
|
instructions are enabled. */
|
instructions are enabled. */
|
if (TREE_CODE (TREE_TYPE (type)) == REAL_TYPE)
|
if (TREE_CODE (TREE_TYPE (type)) == REAL_TYPE)
|
{
|
{
|
/* The SPARC port defines no floating-point vector modes. */
|
/* The SPARC port defines no floating-point vector modes. */
|
gcc_assert (mode == BLKmode);
|
gcc_assert (mode == BLKmode);
|
}
|
}
|
else
|
else
|
{
|
{
|
/* Integral vector types should either have a vector
|
/* Integral vector types should either have a vector
|
mode or an integral mode, because we are guaranteed
|
mode or an integral mode, because we are guaranteed
|
by pass_by_reference that their size is not greater
|
by pass_by_reference that their size is not greater
|
than 16 bytes and TImode is 16-byte wide. */
|
than 16 bytes and TImode is 16-byte wide. */
|
gcc_assert (mode != BLKmode);
|
gcc_assert (mode != BLKmode);
|
|
|
/* Vector integers are handled like floats according to
|
/* Vector integers are handled like floats according to
|
the Sun VIS SDK. */
|
the Sun VIS SDK. */
|
mclass = MODE_FLOAT;
|
mclass = MODE_FLOAT;
|
}
|
}
|
}
|
}
|
|
|
switch (mclass)
|
switch (mclass)
|
{
|
{
|
case MODE_FLOAT:
|
case MODE_FLOAT:
|
case MODE_COMPLEX_FLOAT:
|
case MODE_COMPLEX_FLOAT:
|
case MODE_VECTOR_INT:
|
case MODE_VECTOR_INT:
|
if (TARGET_ARCH64 && TARGET_FPU && named)
|
if (TARGET_ARCH64 && TARGET_FPU && named)
|
{
|
{
|
if (slotno >= SPARC_FP_ARG_MAX)
|
if (slotno >= SPARC_FP_ARG_MAX)
|
return -1;
|
return -1;
|
regno = SPARC_FP_ARG_FIRST + slotno * 2;
|
regno = SPARC_FP_ARG_FIRST + slotno * 2;
|
/* Arguments filling only one single FP register are
|
/* Arguments filling only one single FP register are
|
right-justified in the outer double FP register. */
|
right-justified in the outer double FP register. */
|
if (GET_MODE_SIZE (mode) <= 4)
|
if (GET_MODE_SIZE (mode) <= 4)
|
regno++;
|
regno++;
|
break;
|
break;
|
}
|
}
|
/* fallthrough */
|
/* fallthrough */
|
|
|
case MODE_INT:
|
case MODE_INT:
|
case MODE_COMPLEX_INT:
|
case MODE_COMPLEX_INT:
|
if (slotno >= SPARC_INT_ARG_MAX)
|
if (slotno >= SPARC_INT_ARG_MAX)
|
return -1;
|
return -1;
|
regno = regbase + slotno;
|
regno = regbase + slotno;
|
break;
|
break;
|
|
|
case MODE_RANDOM:
|
case MODE_RANDOM:
|
if (mode == VOIDmode)
|
if (mode == VOIDmode)
|
/* MODE is VOIDmode when generating the actual call. */
|
/* MODE is VOIDmode when generating the actual call. */
|
return -1;
|
return -1;
|
|
|
gcc_assert (mode == BLKmode);
|
gcc_assert (mode == BLKmode);
|
|
|
if (TARGET_ARCH32
|
if (TARGET_ARCH32
|
|| !type
|
|| !type
|
|| (TREE_CODE (type) != VECTOR_TYPE
|
|| (TREE_CODE (type) != VECTOR_TYPE
|
&& TREE_CODE (type) != RECORD_TYPE))
|
&& TREE_CODE (type) != RECORD_TYPE))
|
{
|
{
|
if (slotno >= SPARC_INT_ARG_MAX)
|
if (slotno >= SPARC_INT_ARG_MAX)
|
return -1;
|
return -1;
|
regno = regbase + slotno;
|
regno = regbase + slotno;
|
}
|
}
|
else /* TARGET_ARCH64 && type */
|
else /* TARGET_ARCH64 && type */
|
{
|
{
|
int intregs_p = 0, fpregs_p = 0, packed_p = 0;
|
int intregs_p = 0, fpregs_p = 0, packed_p = 0;
|
|
|
/* First see what kinds of registers we would need. */
|
/* First see what kinds of registers we would need. */
|
if (TREE_CODE (type) == VECTOR_TYPE)
|
if (TREE_CODE (type) == VECTOR_TYPE)
|
fpregs_p = 1;
|
fpregs_p = 1;
|
else
|
else
|
scan_record_type (type, &intregs_p, &fpregs_p, &packed_p);
|
scan_record_type (type, &intregs_p, &fpregs_p, &packed_p);
|
|
|
/* The ABI obviously doesn't specify how packed structures
|
/* The ABI obviously doesn't specify how packed structures
|
are passed. These are defined to be passed in int regs
|
are passed. These are defined to be passed in int regs
|
if possible, otherwise memory. */
|
if possible, otherwise memory. */
|
if (packed_p || !named)
|
if (packed_p || !named)
|
fpregs_p = 0, intregs_p = 1;
|
fpregs_p = 0, intregs_p = 1;
|
|
|
/* If all arg slots are filled, then must pass on stack. */
|
/* If all arg slots are filled, then must pass on stack. */
|
if (fpregs_p && slotno >= SPARC_FP_ARG_MAX)
|
if (fpregs_p && slotno >= SPARC_FP_ARG_MAX)
|
return -1;
|
return -1;
|
|
|
/* If there are only int args and all int arg slots are filled,
|
/* If there are only int args and all int arg slots are filled,
|
then must pass on stack. */
|
then must pass on stack. */
|
if (!fpregs_p && intregs_p && slotno >= SPARC_INT_ARG_MAX)
|
if (!fpregs_p && intregs_p && slotno >= SPARC_INT_ARG_MAX)
|
return -1;
|
return -1;
|
|
|
/* Note that even if all int arg slots are filled, fp members may
|
/* Note that even if all int arg slots are filled, fp members may
|
still be passed in regs if such regs are available.
|
still be passed in regs if such regs are available.
|
*PREGNO isn't set because there may be more than one, it's up
|
*PREGNO isn't set because there may be more than one, it's up
|
to the caller to compute them. */
|
to the caller to compute them. */
|
return slotno;
|
return slotno;
|
}
|
}
|
break;
|
break;
|
|
|
default :
|
default :
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
|
|
*pregno = regno;
|
*pregno = regno;
|
return slotno;
|
return slotno;
|
}
|
}
|
|
|
/* Handle recursive register counting for structure field layout. */
|
/* Handle recursive register counting for structure field layout. */
|
|
|
struct function_arg_record_value_parms
|
struct function_arg_record_value_parms
|
{
|
{
|
rtx ret; /* return expression being built. */
|
rtx ret; /* return expression being built. */
|
int slotno; /* slot number of the argument. */
|
int slotno; /* slot number of the argument. */
|
int named; /* whether the argument is named. */
|
int named; /* whether the argument is named. */
|
int regbase; /* regno of the base register. */
|
int regbase; /* regno of the base register. */
|
int stack; /* 1 if part of the argument is on the stack. */
|
int stack; /* 1 if part of the argument is on the stack. */
|
int intoffset; /* offset of the first pending integer field. */
|
int intoffset; /* offset of the first pending integer field. */
|
unsigned int nregs; /* number of words passed in registers. */
|
unsigned int nregs; /* number of words passed in registers. */
|
};
|
};
|
|
|
static void function_arg_record_value_3
|
static void function_arg_record_value_3
|
(HOST_WIDE_INT, struct function_arg_record_value_parms *);
|
(HOST_WIDE_INT, struct function_arg_record_value_parms *);
|
static void function_arg_record_value_2
|
static void function_arg_record_value_2
|
(const_tree, HOST_WIDE_INT, struct function_arg_record_value_parms *, bool);
|
(const_tree, HOST_WIDE_INT, struct function_arg_record_value_parms *, bool);
|
static void function_arg_record_value_1
|
static void function_arg_record_value_1
|
(const_tree, HOST_WIDE_INT, struct function_arg_record_value_parms *, bool);
|
(const_tree, HOST_WIDE_INT, struct function_arg_record_value_parms *, bool);
|
static rtx function_arg_record_value (const_tree, enum machine_mode, int, int, int);
|
static rtx function_arg_record_value (const_tree, enum machine_mode, int, int, int);
|
static rtx function_arg_union_value (int, enum machine_mode, int, int);
|
static rtx function_arg_union_value (int, enum machine_mode, int, int);
|
|
|
/* A subroutine of function_arg_record_value. Traverse the structure
|
/* A subroutine of function_arg_record_value. Traverse the structure
|
recursively and determine how many registers will be required. */
|
recursively and determine how many registers will be required. */
|
|
|
static void
|
static void
|
function_arg_record_value_1 (const_tree type, HOST_WIDE_INT startbitpos,
|
function_arg_record_value_1 (const_tree type, HOST_WIDE_INT startbitpos,
|
struct function_arg_record_value_parms *parms,
|
struct function_arg_record_value_parms *parms,
|
bool packed_p)
|
bool packed_p)
|
{
|
{
|
tree field;
|
tree field;
|
|
|
/* We need to compute how many registers are needed so we can
|
/* We need to compute how many registers are needed so we can
|
allocate the PARALLEL but before we can do that we need to know
|
allocate the PARALLEL but before we can do that we need to know
|
whether there are any packed fields. The ABI obviously doesn't
|
whether there are any packed fields. The ABI obviously doesn't
|
specify how structures are passed in this case, so they are
|
specify how structures are passed in this case, so they are
|
defined to be passed in int regs if possible, otherwise memory,
|
defined to be passed in int regs if possible, otherwise memory,
|
regardless of whether there are fp values present. */
|
regardless of whether there are fp values present. */
|
|
|
if (! packed_p)
|
if (! packed_p)
|
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
|
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
|
{
|
{
|
if (TREE_CODE (field) == FIELD_DECL && DECL_PACKED (field))
|
if (TREE_CODE (field) == FIELD_DECL && DECL_PACKED (field))
|
{
|
{
|
packed_p = true;
|
packed_p = true;
|
break;
|
break;
|
}
|
}
|
}
|
}
|
|
|
/* Compute how many registers we need. */
|
/* Compute how many registers we need. */
|
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
|
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
|
{
|
{
|
if (TREE_CODE (field) == FIELD_DECL)
|
if (TREE_CODE (field) == FIELD_DECL)
|
{
|
{
|
HOST_WIDE_INT bitpos = startbitpos;
|
HOST_WIDE_INT bitpos = startbitpos;
|
|
|
if (DECL_SIZE (field) != 0)
|
if (DECL_SIZE (field) != 0)
|
{
|
{
|
if (integer_zerop (DECL_SIZE (field)))
|
if (integer_zerop (DECL_SIZE (field)))
|
continue;
|
continue;
|
|
|
if (host_integerp (bit_position (field), 1))
|
if (host_integerp (bit_position (field), 1))
|
bitpos += int_bit_position (field);
|
bitpos += int_bit_position (field);
|
}
|
}
|
|
|
/* ??? FIXME: else assume zero offset. */
|
/* ??? FIXME: else assume zero offset. */
|
|
|
if (TREE_CODE (TREE_TYPE (field)) == RECORD_TYPE)
|
if (TREE_CODE (TREE_TYPE (field)) == RECORD_TYPE)
|
function_arg_record_value_1 (TREE_TYPE (field),
|
function_arg_record_value_1 (TREE_TYPE (field),
|
bitpos,
|
bitpos,
|
parms,
|
parms,
|
packed_p);
|
packed_p);
|
else if ((FLOAT_TYPE_P (TREE_TYPE (field))
|
else if ((FLOAT_TYPE_P (TREE_TYPE (field))
|
|| TREE_CODE (TREE_TYPE (field)) == VECTOR_TYPE)
|
|| TREE_CODE (TREE_TYPE (field)) == VECTOR_TYPE)
|
&& TARGET_FPU
|
&& TARGET_FPU
|
&& parms->named
|
&& parms->named
|
&& ! packed_p)
|
&& ! packed_p)
|
{
|
{
|
if (parms->intoffset != -1)
|
if (parms->intoffset != -1)
|
{
|
{
|
unsigned int startbit, endbit;
|
unsigned int startbit, endbit;
|
int intslots, this_slotno;
|
int intslots, this_slotno;
|
|
|
startbit = parms->intoffset & -BITS_PER_WORD;
|
startbit = parms->intoffset & -BITS_PER_WORD;
|
endbit = (bitpos + BITS_PER_WORD - 1) & -BITS_PER_WORD;
|
endbit = (bitpos + BITS_PER_WORD - 1) & -BITS_PER_WORD;
|
|
|
intslots = (endbit - startbit) / BITS_PER_WORD;
|
intslots = (endbit - startbit) / BITS_PER_WORD;
|
this_slotno = parms->slotno + parms->intoffset
|
this_slotno = parms->slotno + parms->intoffset
|
/ BITS_PER_WORD;
|
/ BITS_PER_WORD;
|
|
|
if (intslots > 0 && intslots > SPARC_INT_ARG_MAX - this_slotno)
|
if (intslots > 0 && intslots > SPARC_INT_ARG_MAX - this_slotno)
|
{
|
{
|
intslots = MAX (0, SPARC_INT_ARG_MAX - this_slotno);
|
intslots = MAX (0, SPARC_INT_ARG_MAX - this_slotno);
|
/* We need to pass this field on the stack. */
|
/* We need to pass this field on the stack. */
|
parms->stack = 1;
|
parms->stack = 1;
|
}
|
}
|
|
|
parms->nregs += intslots;
|
parms->nregs += intslots;
|
parms->intoffset = -1;
|
parms->intoffset = -1;
|
}
|
}
|
|
|
/* There's no need to check this_slotno < SPARC_FP_ARG MAX.
|
/* There's no need to check this_slotno < SPARC_FP_ARG MAX.
|
If it wasn't true we wouldn't be here. */
|
If it wasn't true we wouldn't be here. */
|
if (TREE_CODE (TREE_TYPE (field)) == VECTOR_TYPE
|
if (TREE_CODE (TREE_TYPE (field)) == VECTOR_TYPE
|
&& DECL_MODE (field) == BLKmode)
|
&& DECL_MODE (field) == BLKmode)
|
parms->nregs += TYPE_VECTOR_SUBPARTS (TREE_TYPE (field));
|
parms->nregs += TYPE_VECTOR_SUBPARTS (TREE_TYPE (field));
|
else if (TREE_CODE (TREE_TYPE (field)) == COMPLEX_TYPE)
|
else if (TREE_CODE (TREE_TYPE (field)) == COMPLEX_TYPE)
|
parms->nregs += 2;
|
parms->nregs += 2;
|
else
|
else
|
parms->nregs += 1;
|
parms->nregs += 1;
|
}
|
}
|
else
|
else
|
{
|
{
|
if (parms->intoffset == -1)
|
if (parms->intoffset == -1)
|
parms->intoffset = bitpos;
|
parms->intoffset = bitpos;
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/* A subroutine of function_arg_record_value. Assign the bits of the
|
/* A subroutine of function_arg_record_value. Assign the bits of the
|
structure between parms->intoffset and bitpos to integer registers. */
|
structure between parms->intoffset and bitpos to integer registers. */
|
|
|
static void
|
static void
|
function_arg_record_value_3 (HOST_WIDE_INT bitpos,
|
function_arg_record_value_3 (HOST_WIDE_INT bitpos,
|
struct function_arg_record_value_parms *parms)
|
struct function_arg_record_value_parms *parms)
|
{
|
{
|
enum machine_mode mode;
|
enum machine_mode mode;
|
unsigned int regno;
|
unsigned int regno;
|
unsigned int startbit, endbit;
|
unsigned int startbit, endbit;
|
int this_slotno, intslots, intoffset;
|
int this_slotno, intslots, intoffset;
|
rtx reg;
|
rtx reg;
|
|
|
if (parms->intoffset == -1)
|
if (parms->intoffset == -1)
|
return;
|
return;
|
|
|
intoffset = parms->intoffset;
|
intoffset = parms->intoffset;
|
parms->intoffset = -1;
|
parms->intoffset = -1;
|
|
|
startbit = intoffset & -BITS_PER_WORD;
|
startbit = intoffset & -BITS_PER_WORD;
|
endbit = (bitpos + BITS_PER_WORD - 1) & -BITS_PER_WORD;
|
endbit = (bitpos + BITS_PER_WORD - 1) & -BITS_PER_WORD;
|
intslots = (endbit - startbit) / BITS_PER_WORD;
|
intslots = (endbit - startbit) / BITS_PER_WORD;
|
this_slotno = parms->slotno + intoffset / BITS_PER_WORD;
|
this_slotno = parms->slotno + intoffset / BITS_PER_WORD;
|
|
|
intslots = MIN (intslots, SPARC_INT_ARG_MAX - this_slotno);
|
intslots = MIN (intslots, SPARC_INT_ARG_MAX - this_slotno);
|
if (intslots <= 0)
|
if (intslots <= 0)
|
return;
|
return;
|
|
|
/* If this is the trailing part of a word, only load that much into
|
/* If this is the trailing part of a word, only load that much into
|
the register. Otherwise load the whole register. Note that in
|
the register. Otherwise load the whole register. Note that in
|
the latter case we may pick up unwanted bits. It's not a problem
|
the latter case we may pick up unwanted bits. It's not a problem
|
at the moment but may wish to revisit. */
|
at the moment but may wish to revisit. */
|
|
|
if (intoffset % BITS_PER_WORD != 0)
|
if (intoffset % BITS_PER_WORD != 0)
|
mode = smallest_mode_for_size (BITS_PER_WORD - intoffset % BITS_PER_WORD,
|
mode = smallest_mode_for_size (BITS_PER_WORD - intoffset % BITS_PER_WORD,
|
MODE_INT);
|
MODE_INT);
|
else
|
else
|
mode = word_mode;
|
mode = word_mode;
|
|
|
intoffset /= BITS_PER_UNIT;
|
intoffset /= BITS_PER_UNIT;
|
do
|
do
|
{
|
{
|
regno = parms->regbase + this_slotno;
|
regno = parms->regbase + this_slotno;
|
reg = gen_rtx_REG (mode, regno);
|
reg = gen_rtx_REG (mode, regno);
|
XVECEXP (parms->ret, 0, parms->stack + parms->nregs)
|
XVECEXP (parms->ret, 0, parms->stack + parms->nregs)
|
= gen_rtx_EXPR_LIST (VOIDmode, reg, GEN_INT (intoffset));
|
= gen_rtx_EXPR_LIST (VOIDmode, reg, GEN_INT (intoffset));
|
|
|
this_slotno += 1;
|
this_slotno += 1;
|
intoffset = (intoffset | (UNITS_PER_WORD-1)) + 1;
|
intoffset = (intoffset | (UNITS_PER_WORD-1)) + 1;
|
mode = word_mode;
|
mode = word_mode;
|
parms->nregs += 1;
|
parms->nregs += 1;
|
intslots -= 1;
|
intslots -= 1;
|
}
|
}
|
while (intslots > 0);
|
while (intslots > 0);
|
}
|
}
|
|
|
/* A subroutine of function_arg_record_value. Traverse the structure
|
/* A subroutine of function_arg_record_value. Traverse the structure
|
recursively and assign bits to floating point registers. Track which
|
recursively and assign bits to floating point registers. Track which
|
bits in between need integer registers; invoke function_arg_record_value_3
|
bits in between need integer registers; invoke function_arg_record_value_3
|
to make that happen. */
|
to make that happen. */
|
|
|
static void
|
static void
|
function_arg_record_value_2 (const_tree type, HOST_WIDE_INT startbitpos,
|
function_arg_record_value_2 (const_tree type, HOST_WIDE_INT startbitpos,
|
struct function_arg_record_value_parms *parms,
|
struct function_arg_record_value_parms *parms,
|
bool packed_p)
|
bool packed_p)
|
{
|
{
|
tree field;
|
tree field;
|
|
|
if (! packed_p)
|
if (! packed_p)
|
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
|
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
|
{
|
{
|
if (TREE_CODE (field) == FIELD_DECL && DECL_PACKED (field))
|
if (TREE_CODE (field) == FIELD_DECL && DECL_PACKED (field))
|
{
|
{
|
packed_p = true;
|
packed_p = true;
|
break;
|
break;
|
}
|
}
|
}
|
}
|
|
|
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
|
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
|
{
|
{
|
if (TREE_CODE (field) == FIELD_DECL)
|
if (TREE_CODE (field) == FIELD_DECL)
|
{
|
{
|
HOST_WIDE_INT bitpos = startbitpos;
|
HOST_WIDE_INT bitpos = startbitpos;
|
|
|
if (DECL_SIZE (field) != 0)
|
if (DECL_SIZE (field) != 0)
|
{
|
{
|
if (integer_zerop (DECL_SIZE (field)))
|
if (integer_zerop (DECL_SIZE (field)))
|
continue;
|
continue;
|
|
|
if (host_integerp (bit_position (field), 1))
|
if (host_integerp (bit_position (field), 1))
|
bitpos += int_bit_position (field);
|
bitpos += int_bit_position (field);
|
}
|
}
|
|
|
/* ??? FIXME: else assume zero offset. */
|
/* ??? FIXME: else assume zero offset. */
|
|
|
if (TREE_CODE (TREE_TYPE (field)) == RECORD_TYPE)
|
if (TREE_CODE (TREE_TYPE (field)) == RECORD_TYPE)
|
function_arg_record_value_2 (TREE_TYPE (field),
|
function_arg_record_value_2 (TREE_TYPE (field),
|
bitpos,
|
bitpos,
|
parms,
|
parms,
|
packed_p);
|
packed_p);
|
else if ((FLOAT_TYPE_P (TREE_TYPE (field))
|
else if ((FLOAT_TYPE_P (TREE_TYPE (field))
|
|| TREE_CODE (TREE_TYPE (field)) == VECTOR_TYPE)
|
|| TREE_CODE (TREE_TYPE (field)) == VECTOR_TYPE)
|
&& TARGET_FPU
|
&& TARGET_FPU
|
&& parms->named
|
&& parms->named
|
&& ! packed_p)
|
&& ! packed_p)
|
{
|
{
|
int this_slotno = parms->slotno + bitpos / BITS_PER_WORD;
|
int this_slotno = parms->slotno + bitpos / BITS_PER_WORD;
|
int regno, nregs, pos;
|
int regno, nregs, pos;
|
enum machine_mode mode = DECL_MODE (field);
|
enum machine_mode mode = DECL_MODE (field);
|
rtx reg;
|
rtx reg;
|
|
|
function_arg_record_value_3 (bitpos, parms);
|
function_arg_record_value_3 (bitpos, parms);
|
|
|
if (TREE_CODE (TREE_TYPE (field)) == VECTOR_TYPE
|
if (TREE_CODE (TREE_TYPE (field)) == VECTOR_TYPE
|
&& mode == BLKmode)
|
&& mode == BLKmode)
|
{
|
{
|
mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (field)));
|
mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (field)));
|
nregs = TYPE_VECTOR_SUBPARTS (TREE_TYPE (field));
|
nregs = TYPE_VECTOR_SUBPARTS (TREE_TYPE (field));
|
}
|
}
|
else if (TREE_CODE (TREE_TYPE (field)) == COMPLEX_TYPE)
|
else if (TREE_CODE (TREE_TYPE (field)) == COMPLEX_TYPE)
|
{
|
{
|
mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (field)));
|
mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (field)));
|
nregs = 2;
|
nregs = 2;
|
}
|
}
|
else
|
else
|
nregs = 1;
|
nregs = 1;
|
|
|
regno = SPARC_FP_ARG_FIRST + this_slotno * 2;
|
regno = SPARC_FP_ARG_FIRST + this_slotno * 2;
|
if (GET_MODE_SIZE (mode) <= 4 && (bitpos & 32) != 0)
|
if (GET_MODE_SIZE (mode) <= 4 && (bitpos & 32) != 0)
|
regno++;
|
regno++;
|
reg = gen_rtx_REG (mode, regno);
|
reg = gen_rtx_REG (mode, regno);
|
pos = bitpos / BITS_PER_UNIT;
|
pos = bitpos / BITS_PER_UNIT;
|
XVECEXP (parms->ret, 0, parms->stack + parms->nregs)
|
XVECEXP (parms->ret, 0, parms->stack + parms->nregs)
|
= gen_rtx_EXPR_LIST (VOIDmode, reg, GEN_INT (pos));
|
= gen_rtx_EXPR_LIST (VOIDmode, reg, GEN_INT (pos));
|
parms->nregs += 1;
|
parms->nregs += 1;
|
while (--nregs > 0)
|
while (--nregs > 0)
|
{
|
{
|
regno += GET_MODE_SIZE (mode) / 4;
|
regno += GET_MODE_SIZE (mode) / 4;
|
reg = gen_rtx_REG (mode, regno);
|
reg = gen_rtx_REG (mode, regno);
|
pos += GET_MODE_SIZE (mode);
|
pos += GET_MODE_SIZE (mode);
|
XVECEXP (parms->ret, 0, parms->stack + parms->nregs)
|
XVECEXP (parms->ret, 0, parms->stack + parms->nregs)
|
= gen_rtx_EXPR_LIST (VOIDmode, reg, GEN_INT (pos));
|
= gen_rtx_EXPR_LIST (VOIDmode, reg, GEN_INT (pos));
|
parms->nregs += 1;
|
parms->nregs += 1;
|
}
|
}
|
}
|
}
|
else
|
else
|
{
|
{
|
if (parms->intoffset == -1)
|
if (parms->intoffset == -1)
|
parms->intoffset = bitpos;
|
parms->intoffset = bitpos;
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/* Used by function_arg and function_value to implement the complex
|
/* Used by function_arg and function_value to implement the complex
|
conventions of the 64-bit ABI for passing and returning structures.
|
conventions of the 64-bit ABI for passing and returning structures.
|
Return an expression valid as a return value for the two macros
|
Return an expression valid as a return value for the two macros
|
FUNCTION_ARG and FUNCTION_VALUE.
|
FUNCTION_ARG and FUNCTION_VALUE.
|
|
|
TYPE is the data type of the argument (as a tree).
|
TYPE is the data type of the argument (as a tree).
|
This is null for libcalls where that information may
|
This is null for libcalls where that information may
|
not be available.
|
not be available.
|
MODE is the argument's machine mode.
|
MODE is the argument's machine mode.
|
SLOTNO is the index number of the argument's slot in the parameter array.
|
SLOTNO is the index number of the argument's slot in the parameter array.
|
NAMED is nonzero if this argument is a named parameter
|
NAMED is nonzero if this argument is a named parameter
|
(otherwise it is an extra parameter matching an ellipsis).
|
(otherwise it is an extra parameter matching an ellipsis).
|
REGBASE is the regno of the base register for the parameter array. */
|
REGBASE is the regno of the base register for the parameter array. */
|
|
|
static rtx
|
static rtx
|
function_arg_record_value (const_tree type, enum machine_mode mode,
|
function_arg_record_value (const_tree type, enum machine_mode mode,
|
int slotno, int named, int regbase)
|
int slotno, int named, int regbase)
|
{
|
{
|
HOST_WIDE_INT typesize = int_size_in_bytes (type);
|
HOST_WIDE_INT typesize = int_size_in_bytes (type);
|
struct function_arg_record_value_parms parms;
|
struct function_arg_record_value_parms parms;
|
unsigned int nregs;
|
unsigned int nregs;
|
|
|
parms.ret = NULL_RTX;
|
parms.ret = NULL_RTX;
|
parms.slotno = slotno;
|
parms.slotno = slotno;
|
parms.named = named;
|
parms.named = named;
|
parms.regbase = regbase;
|
parms.regbase = regbase;
|
parms.stack = 0;
|
parms.stack = 0;
|
|
|
/* Compute how many registers we need. */
|
/* Compute how many registers we need. */
|
parms.nregs = 0;
|
parms.nregs = 0;
|
parms.intoffset = 0;
|
parms.intoffset = 0;
|
function_arg_record_value_1 (type, 0, &parms, false);
|
function_arg_record_value_1 (type, 0, &parms, false);
|
|
|
/* Take into account pending integer fields. */
|
/* Take into account pending integer fields. */
|
if (parms.intoffset != -1)
|
if (parms.intoffset != -1)
|
{
|
{
|
unsigned int startbit, endbit;
|
unsigned int startbit, endbit;
|
int intslots, this_slotno;
|
int intslots, this_slotno;
|
|
|
startbit = parms.intoffset & -BITS_PER_WORD;
|
startbit = parms.intoffset & -BITS_PER_WORD;
|
endbit = (typesize*BITS_PER_UNIT + BITS_PER_WORD - 1) & -BITS_PER_WORD;
|
endbit = (typesize*BITS_PER_UNIT + BITS_PER_WORD - 1) & -BITS_PER_WORD;
|
intslots = (endbit - startbit) / BITS_PER_WORD;
|
intslots = (endbit - startbit) / BITS_PER_WORD;
|
this_slotno = slotno + parms.intoffset / BITS_PER_WORD;
|
this_slotno = slotno + parms.intoffset / BITS_PER_WORD;
|
|
|
if (intslots > 0 && intslots > SPARC_INT_ARG_MAX - this_slotno)
|
if (intslots > 0 && intslots > SPARC_INT_ARG_MAX - this_slotno)
|
{
|
{
|
intslots = MAX (0, SPARC_INT_ARG_MAX - this_slotno);
|
intslots = MAX (0, SPARC_INT_ARG_MAX - this_slotno);
|
/* We need to pass this field on the stack. */
|
/* We need to pass this field on the stack. */
|
parms.stack = 1;
|
parms.stack = 1;
|
}
|
}
|
|
|
parms.nregs += intslots;
|
parms.nregs += intslots;
|
}
|
}
|
nregs = parms.nregs;
|
nregs = parms.nregs;
|
|
|
/* Allocate the vector and handle some annoying special cases. */
|
/* Allocate the vector and handle some annoying special cases. */
|
if (nregs == 0)
|
if (nregs == 0)
|
{
|
{
|
/* ??? Empty structure has no value? Duh? */
|
/* ??? Empty structure has no value? Duh? */
|
if (typesize <= 0)
|
if (typesize <= 0)
|
{
|
{
|
/* Though there's nothing really to store, return a word register
|
/* Though there's nothing really to store, return a word register
|
anyway so the rest of gcc doesn't go nuts. Returning a PARALLEL
|
anyway so the rest of gcc doesn't go nuts. Returning a PARALLEL
|
leads to breakage due to the fact that there are zero bytes to
|
leads to breakage due to the fact that there are zero bytes to
|
load. */
|
load. */
|
return gen_rtx_REG (mode, regbase);
|
return gen_rtx_REG (mode, regbase);
|
}
|
}
|
else
|
else
|
{
|
{
|
/* ??? C++ has structures with no fields, and yet a size. Give up
|
/* ??? C++ has structures with no fields, and yet a size. Give up
|
for now and pass everything back in integer registers. */
|
for now and pass everything back in integer registers. */
|
nregs = (typesize + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
|
nregs = (typesize + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
|
}
|
}
|
if (nregs + slotno > SPARC_INT_ARG_MAX)
|
if (nregs + slotno > SPARC_INT_ARG_MAX)
|
nregs = SPARC_INT_ARG_MAX - slotno;
|
nregs = SPARC_INT_ARG_MAX - slotno;
|
}
|
}
|
gcc_assert (nregs != 0);
|
gcc_assert (nregs != 0);
|
|
|
parms.ret = gen_rtx_PARALLEL (mode, rtvec_alloc (parms.stack + nregs));
|
parms.ret = gen_rtx_PARALLEL (mode, rtvec_alloc (parms.stack + nregs));
|
|
|
/* If at least one field must be passed on the stack, generate
|
/* If at least one field must be passed on the stack, generate
|
(parallel [(expr_list (nil) ...) ...]) so that all fields will
|
(parallel [(expr_list (nil) ...) ...]) so that all fields will
|
also be passed on the stack. We can't do much better because the
|
also be passed on the stack. We can't do much better because the
|
semantics of TARGET_ARG_PARTIAL_BYTES doesn't handle the case
|
semantics of TARGET_ARG_PARTIAL_BYTES doesn't handle the case
|
of structures for which the fields passed exclusively in registers
|
of structures for which the fields passed exclusively in registers
|
are not at the beginning of the structure. */
|
are not at the beginning of the structure. */
|
if (parms.stack)
|
if (parms.stack)
|
XVECEXP (parms.ret, 0, 0)
|
XVECEXP (parms.ret, 0, 0)
|
= gen_rtx_EXPR_LIST (VOIDmode, NULL_RTX, const0_rtx);
|
= gen_rtx_EXPR_LIST (VOIDmode, NULL_RTX, const0_rtx);
|
|
|
/* Fill in the entries. */
|
/* Fill in the entries. */
|
parms.nregs = 0;
|
parms.nregs = 0;
|
parms.intoffset = 0;
|
parms.intoffset = 0;
|
function_arg_record_value_2 (type, 0, &parms, false);
|
function_arg_record_value_2 (type, 0, &parms, false);
|
function_arg_record_value_3 (typesize * BITS_PER_UNIT, &parms);
|
function_arg_record_value_3 (typesize * BITS_PER_UNIT, &parms);
|
|
|
gcc_assert (parms.nregs == nregs);
|
gcc_assert (parms.nregs == nregs);
|
|
|
return parms.ret;
|
return parms.ret;
|
}
|
}
|
|
|
/* Used by function_arg and function_value to implement the conventions
|
/* Used by function_arg and function_value to implement the conventions
|
of the 64-bit ABI for passing and returning unions.
|
of the 64-bit ABI for passing and returning unions.
|
Return an expression valid as a return value for the two macros
|
Return an expression valid as a return value for the two macros
|
FUNCTION_ARG and FUNCTION_VALUE.
|
FUNCTION_ARG and FUNCTION_VALUE.
|
|
|
SIZE is the size in bytes of the union.
|
SIZE is the size in bytes of the union.
|
MODE is the argument's machine mode.
|
MODE is the argument's machine mode.
|
REGNO is the hard register the union will be passed in. */
|
REGNO is the hard register the union will be passed in. */
|
|
|
static rtx
|
static rtx
|
function_arg_union_value (int size, enum machine_mode mode, int slotno,
|
function_arg_union_value (int size, enum machine_mode mode, int slotno,
|
int regno)
|
int regno)
|
{
|
{
|
int nwords = ROUND_ADVANCE (size), i;
|
int nwords = ROUND_ADVANCE (size), i;
|
rtx regs;
|
rtx regs;
|
|
|
/* See comment in previous function for empty structures. */
|
/* See comment in previous function for empty structures. */
|
if (nwords == 0)
|
if (nwords == 0)
|
return gen_rtx_REG (mode, regno);
|
return gen_rtx_REG (mode, regno);
|
|
|
if (slotno == SPARC_INT_ARG_MAX - 1)
|
if (slotno == SPARC_INT_ARG_MAX - 1)
|
nwords = 1;
|
nwords = 1;
|
|
|
regs = gen_rtx_PARALLEL (mode, rtvec_alloc (nwords));
|
regs = gen_rtx_PARALLEL (mode, rtvec_alloc (nwords));
|
|
|
for (i = 0; i < nwords; i++)
|
for (i = 0; i < nwords; i++)
|
{
|
{
|
/* Unions are passed left-justified. */
|
/* Unions are passed left-justified. */
|
XVECEXP (regs, 0, i)
|
XVECEXP (regs, 0, i)
|
= gen_rtx_EXPR_LIST (VOIDmode,
|
= gen_rtx_EXPR_LIST (VOIDmode,
|
gen_rtx_REG (word_mode, regno),
|
gen_rtx_REG (word_mode, regno),
|
GEN_INT (UNITS_PER_WORD * i));
|
GEN_INT (UNITS_PER_WORD * i));
|
regno++;
|
regno++;
|
}
|
}
|
|
|
return regs;
|
return regs;
|
}
|
}
|
|
|
/* Used by function_arg and function_value to implement the conventions
|
/* Used by function_arg and function_value to implement the conventions
|
for passing and returning large (BLKmode) vectors.
|
for passing and returning large (BLKmode) vectors.
|
Return an expression valid as a return value for the two macros
|
Return an expression valid as a return value for the two macros
|
FUNCTION_ARG and FUNCTION_VALUE.
|
FUNCTION_ARG and FUNCTION_VALUE.
|
|
|
SIZE is the size in bytes of the vector (at least 8 bytes).
|
SIZE is the size in bytes of the vector (at least 8 bytes).
|
REGNO is the FP hard register the vector will be passed in. */
|
REGNO is the FP hard register the vector will be passed in. */
|
|
|
static rtx
|
static rtx
|
function_arg_vector_value (int size, int regno)
|
function_arg_vector_value (int size, int regno)
|
{
|
{
|
int i, nregs = size / 8;
|
int i, nregs = size / 8;
|
rtx regs;
|
rtx regs;
|
|
|
regs = gen_rtx_PARALLEL (BLKmode, rtvec_alloc (nregs));
|
regs = gen_rtx_PARALLEL (BLKmode, rtvec_alloc (nregs));
|
|
|
for (i = 0; i < nregs; i++)
|
for (i = 0; i < nregs; i++)
|
{
|
{
|
XVECEXP (regs, 0, i)
|
XVECEXP (regs, 0, i)
|
= gen_rtx_EXPR_LIST (VOIDmode,
|
= gen_rtx_EXPR_LIST (VOIDmode,
|
gen_rtx_REG (DImode, regno + 2*i),
|
gen_rtx_REG (DImode, regno + 2*i),
|
GEN_INT (i*8));
|
GEN_INT (i*8));
|
}
|
}
|
|
|
return regs;
|
return regs;
|
}
|
}
|
|
|
/* Handle the FUNCTION_ARG macro.
|
/* Handle the FUNCTION_ARG macro.
|
Determine where to put an argument to a function.
|
Determine where to put an argument to a function.
|
Value is zero to push the argument on the stack,
|
Value is zero to push the argument on the stack,
|
or a hard register in which to store the argument.
|
or a hard register in which to store the argument.
|
|
|
CUM is a variable of type CUMULATIVE_ARGS which gives info about
|
CUM is a variable of type CUMULATIVE_ARGS which gives info about
|
the preceding args and about the function being called.
|
the preceding args and about the function being called.
|
MODE is the argument's machine mode.
|
MODE is the argument's machine mode.
|
TYPE is the data type of the argument (as a tree).
|
TYPE is the data type of the argument (as a tree).
|
This is null for libcalls where that information may
|
This is null for libcalls where that information may
|
not be available.
|
not be available.
|
NAMED is nonzero if this argument is a named parameter
|
NAMED is nonzero if this argument is a named parameter
|
(otherwise it is an extra parameter matching an ellipsis).
|
(otherwise it is an extra parameter matching an ellipsis).
|
INCOMING_P is zero for FUNCTION_ARG, nonzero for FUNCTION_INCOMING_ARG. */
|
INCOMING_P is zero for FUNCTION_ARG, nonzero for FUNCTION_INCOMING_ARG. */
|
|
|
rtx
|
rtx
|
function_arg (const struct sparc_args *cum, enum machine_mode mode,
|
function_arg (const struct sparc_args *cum, enum machine_mode mode,
|
tree type, int named, int incoming_p)
|
tree type, int named, int incoming_p)
|
{
|
{
|
int regbase = (incoming_p
|
int regbase = (incoming_p
|
? SPARC_INCOMING_INT_ARG_FIRST
|
? SPARC_INCOMING_INT_ARG_FIRST
|
: SPARC_OUTGOING_INT_ARG_FIRST);
|
: SPARC_OUTGOING_INT_ARG_FIRST);
|
int slotno, regno, padding;
|
int slotno, regno, padding;
|
enum mode_class mclass = GET_MODE_CLASS (mode);
|
enum mode_class mclass = GET_MODE_CLASS (mode);
|
|
|
slotno = function_arg_slotno (cum, mode, type, named, incoming_p,
|
slotno = function_arg_slotno (cum, mode, type, named, incoming_p,
|
®no, &padding);
|
®no, &padding);
|
if (slotno == -1)
|
if (slotno == -1)
|
return 0;
|
return 0;
|
|
|
/* Vector types deserve special treatment because they are polymorphic wrt
|
/* Vector types deserve special treatment because they are polymorphic wrt
|
their mode, depending upon whether VIS instructions are enabled. */
|
their mode, depending upon whether VIS instructions are enabled. */
|
if (type && TREE_CODE (type) == VECTOR_TYPE)
|
if (type && TREE_CODE (type) == VECTOR_TYPE)
|
{
|
{
|
HOST_WIDE_INT size = int_size_in_bytes (type);
|
HOST_WIDE_INT size = int_size_in_bytes (type);
|
gcc_assert ((TARGET_ARCH32 && size <= 8)
|
gcc_assert ((TARGET_ARCH32 && size <= 8)
|
|| (TARGET_ARCH64 && size <= 16));
|
|| (TARGET_ARCH64 && size <= 16));
|
|
|
if (mode == BLKmode)
|
if (mode == BLKmode)
|
return function_arg_vector_value (size,
|
return function_arg_vector_value (size,
|
SPARC_FP_ARG_FIRST + 2*slotno);
|
SPARC_FP_ARG_FIRST + 2*slotno);
|
else
|
else
|
mclass = MODE_FLOAT;
|
mclass = MODE_FLOAT;
|
}
|
}
|
|
|
if (TARGET_ARCH32)
|
if (TARGET_ARCH32)
|
return gen_rtx_REG (mode, regno);
|
return gen_rtx_REG (mode, regno);
|
|
|
/* Structures up to 16 bytes in size are passed in arg slots on the stack
|
/* Structures up to 16 bytes in size are passed in arg slots on the stack
|
and are promoted to registers if possible. */
|
and are promoted to registers if possible. */
|
if (type && TREE_CODE (type) == RECORD_TYPE)
|
if (type && TREE_CODE (type) == RECORD_TYPE)
|
{
|
{
|
HOST_WIDE_INT size = int_size_in_bytes (type);
|
HOST_WIDE_INT size = int_size_in_bytes (type);
|
gcc_assert (size <= 16);
|
gcc_assert (size <= 16);
|
|
|
return function_arg_record_value (type, mode, slotno, named, regbase);
|
return function_arg_record_value (type, mode, slotno, named, regbase);
|
}
|
}
|
|
|
/* Unions up to 16 bytes in size are passed in integer registers. */
|
/* Unions up to 16 bytes in size are passed in integer registers. */
|
else if (type && TREE_CODE (type) == UNION_TYPE)
|
else if (type && TREE_CODE (type) == UNION_TYPE)
|
{
|
{
|
HOST_WIDE_INT size = int_size_in_bytes (type);
|
HOST_WIDE_INT size = int_size_in_bytes (type);
|
gcc_assert (size <= 16);
|
gcc_assert (size <= 16);
|
|
|
return function_arg_union_value (size, mode, slotno, regno);
|
return function_arg_union_value (size, mode, slotno, regno);
|
}
|
}
|
|
|
/* v9 fp args in reg slots beyond the int reg slots get passed in regs
|
/* v9 fp args in reg slots beyond the int reg slots get passed in regs
|
but also have the slot allocated for them.
|
but also have the slot allocated for them.
|
If no prototype is in scope fp values in register slots get passed
|
If no prototype is in scope fp values in register slots get passed
|
in two places, either fp regs and int regs or fp regs and memory. */
|
in two places, either fp regs and int regs or fp regs and memory. */
|
else if ((mclass == MODE_FLOAT || mclass == MODE_COMPLEX_FLOAT)
|
else if ((mclass == MODE_FLOAT || mclass == MODE_COMPLEX_FLOAT)
|
&& SPARC_FP_REG_P (regno))
|
&& SPARC_FP_REG_P (regno))
|
{
|
{
|
rtx reg = gen_rtx_REG (mode, regno);
|
rtx reg = gen_rtx_REG (mode, regno);
|
if (cum->prototype_p || cum->libcall_p)
|
if (cum->prototype_p || cum->libcall_p)
|
{
|
{
|
/* "* 2" because fp reg numbers are recorded in 4 byte
|
/* "* 2" because fp reg numbers are recorded in 4 byte
|
quantities. */
|
quantities. */
|
#if 0
|
#if 0
|
/* ??? This will cause the value to be passed in the fp reg and
|
/* ??? This will cause the value to be passed in the fp reg and
|
in the stack. When a prototype exists we want to pass the
|
in the stack. When a prototype exists we want to pass the
|
value in the reg but reserve space on the stack. That's an
|
value in the reg but reserve space on the stack. That's an
|
optimization, and is deferred [for a bit]. */
|
optimization, and is deferred [for a bit]. */
|
if ((regno - SPARC_FP_ARG_FIRST) >= SPARC_INT_ARG_MAX * 2)
|
if ((regno - SPARC_FP_ARG_FIRST) >= SPARC_INT_ARG_MAX * 2)
|
return gen_rtx_PARALLEL (mode,
|
return gen_rtx_PARALLEL (mode,
|
gen_rtvec (2,
|
gen_rtvec (2,
|
gen_rtx_EXPR_LIST (VOIDmode,
|
gen_rtx_EXPR_LIST (VOIDmode,
|
NULL_RTX, const0_rtx),
|
NULL_RTX, const0_rtx),
|
gen_rtx_EXPR_LIST (VOIDmode,
|
gen_rtx_EXPR_LIST (VOIDmode,
|
reg, const0_rtx)));
|
reg, const0_rtx)));
|
else
|
else
|
#else
|
#else
|
/* ??? It seems that passing back a register even when past
|
/* ??? It seems that passing back a register even when past
|
the area declared by REG_PARM_STACK_SPACE will allocate
|
the area declared by REG_PARM_STACK_SPACE will allocate
|
space appropriately, and will not copy the data onto the
|
space appropriately, and will not copy the data onto the
|
stack, exactly as we desire.
|
stack, exactly as we desire.
|
|
|
This is due to locate_and_pad_parm being called in
|
This is due to locate_and_pad_parm being called in
|
expand_call whenever reg_parm_stack_space > 0, which
|
expand_call whenever reg_parm_stack_space > 0, which
|
while beneficial to our example here, would seem to be
|
while beneficial to our example here, would seem to be
|
in error from what had been intended. Ho hum... -- r~ */
|
in error from what had been intended. Ho hum... -- r~ */
|
#endif
|
#endif
|
return reg;
|
return reg;
|
}
|
}
|
else
|
else
|
{
|
{
|
rtx v0, v1;
|
rtx v0, v1;
|
|
|
if ((regno - SPARC_FP_ARG_FIRST) < SPARC_INT_ARG_MAX * 2)
|
if ((regno - SPARC_FP_ARG_FIRST) < SPARC_INT_ARG_MAX * 2)
|
{
|
{
|
int intreg;
|
int intreg;
|
|
|
/* On incoming, we don't need to know that the value
|
/* On incoming, we don't need to know that the value
|
is passed in %f0 and %i0, and it confuses other parts
|
is passed in %f0 and %i0, and it confuses other parts
|
causing needless spillage even on the simplest cases. */
|
causing needless spillage even on the simplest cases. */
|
if (incoming_p)
|
if (incoming_p)
|
return reg;
|
return reg;
|
|
|
intreg = (SPARC_OUTGOING_INT_ARG_FIRST
|
intreg = (SPARC_OUTGOING_INT_ARG_FIRST
|
+ (regno - SPARC_FP_ARG_FIRST) / 2);
|
+ (regno - SPARC_FP_ARG_FIRST) / 2);
|
|
|
v0 = gen_rtx_EXPR_LIST (VOIDmode, reg, const0_rtx);
|
v0 = gen_rtx_EXPR_LIST (VOIDmode, reg, const0_rtx);
|
v1 = gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_REG (mode, intreg),
|
v1 = gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_REG (mode, intreg),
|
const0_rtx);
|
const0_rtx);
|
return gen_rtx_PARALLEL (mode, gen_rtvec (2, v0, v1));
|
return gen_rtx_PARALLEL (mode, gen_rtvec (2, v0, v1));
|
}
|
}
|
else
|
else
|
{
|
{
|
v0 = gen_rtx_EXPR_LIST (VOIDmode, NULL_RTX, const0_rtx);
|
v0 = gen_rtx_EXPR_LIST (VOIDmode, NULL_RTX, const0_rtx);
|
v1 = gen_rtx_EXPR_LIST (VOIDmode, reg, const0_rtx);
|
v1 = gen_rtx_EXPR_LIST (VOIDmode, reg, const0_rtx);
|
return gen_rtx_PARALLEL (mode, gen_rtvec (2, v0, v1));
|
return gen_rtx_PARALLEL (mode, gen_rtvec (2, v0, v1));
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/* All other aggregate types are passed in an integer register in a mode
|
/* All other aggregate types are passed in an integer register in a mode
|
corresponding to the size of the type. */
|
corresponding to the size of the type. */
|
else if (type && AGGREGATE_TYPE_P (type))
|
else if (type && AGGREGATE_TYPE_P (type))
|
{
|
{
|
HOST_WIDE_INT size = int_size_in_bytes (type);
|
HOST_WIDE_INT size = int_size_in_bytes (type);
|
gcc_assert (size <= 16);
|
gcc_assert (size <= 16);
|
|
|
mode = mode_for_size (size * BITS_PER_UNIT, MODE_INT, 0);
|
mode = mode_for_size (size * BITS_PER_UNIT, MODE_INT, 0);
|
}
|
}
|
|
|
return gen_rtx_REG (mode, regno);
|
return gen_rtx_REG (mode, regno);
|
}
|
}
|
|
|
/* For an arg passed partly in registers and partly in memory,
|
/* For an arg passed partly in registers and partly in memory,
|
this is the number of bytes of registers used.
|
this is the number of bytes of registers used.
|
For args passed entirely in registers or entirely in memory, zero.
|
For args passed entirely in registers or entirely in memory, zero.
|
|
|
Any arg that starts in the first 6 regs but won't entirely fit in them
|
Any arg that starts in the first 6 regs but won't entirely fit in them
|
needs partial registers on v8. On v9, structures with integer
|
needs partial registers on v8. On v9, structures with integer
|
values in arg slots 5,6 will be passed in %o5 and SP+176, and complex fp
|
values in arg slots 5,6 will be passed in %o5 and SP+176, and complex fp
|
values that begin in the last fp reg [where "last fp reg" varies with the
|
values that begin in the last fp reg [where "last fp reg" varies with the
|
mode] will be split between that reg and memory. */
|
mode] will be split between that reg and memory. */
|
|
|
static int
|
static int
|
sparc_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
|
sparc_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
|
tree type, bool named)
|
tree type, bool named)
|
{
|
{
|
int slotno, regno, padding;
|
int slotno, regno, padding;
|
|
|
/* We pass 0 for incoming_p here, it doesn't matter. */
|
/* We pass 0 for incoming_p here, it doesn't matter. */
|
slotno = function_arg_slotno (cum, mode, type, named, 0, ®no, &padding);
|
slotno = function_arg_slotno (cum, mode, type, named, 0, ®no, &padding);
|
|
|
if (slotno == -1)
|
if (slotno == -1)
|
return 0;
|
return 0;
|
|
|
if (TARGET_ARCH32)
|
if (TARGET_ARCH32)
|
{
|
{
|
if ((slotno + (mode == BLKmode
|
if ((slotno + (mode == BLKmode
|
? ROUND_ADVANCE (int_size_in_bytes (type))
|
? ROUND_ADVANCE (int_size_in_bytes (type))
|
: ROUND_ADVANCE (GET_MODE_SIZE (mode))))
|
: ROUND_ADVANCE (GET_MODE_SIZE (mode))))
|
> SPARC_INT_ARG_MAX)
|
> SPARC_INT_ARG_MAX)
|
return (SPARC_INT_ARG_MAX - slotno) * UNITS_PER_WORD;
|
return (SPARC_INT_ARG_MAX - slotno) * UNITS_PER_WORD;
|
}
|
}
|
else
|
else
|
{
|
{
|
/* We are guaranteed by pass_by_reference that the size of the
|
/* We are guaranteed by pass_by_reference that the size of the
|
argument is not greater than 16 bytes, so we only need to return
|
argument is not greater than 16 bytes, so we only need to return
|
one word if the argument is partially passed in registers. */
|
one word if the argument is partially passed in registers. */
|
|
|
if (type && AGGREGATE_TYPE_P (type))
|
if (type && AGGREGATE_TYPE_P (type))
|
{
|
{
|
int size = int_size_in_bytes (type);
|
int size = int_size_in_bytes (type);
|
|
|
if (size > UNITS_PER_WORD
|
if (size > UNITS_PER_WORD
|
&& slotno == SPARC_INT_ARG_MAX - 1)
|
&& slotno == SPARC_INT_ARG_MAX - 1)
|
return UNITS_PER_WORD;
|
return UNITS_PER_WORD;
|
}
|
}
|
else if (GET_MODE_CLASS (mode) == MODE_COMPLEX_INT
|
else if (GET_MODE_CLASS (mode) == MODE_COMPLEX_INT
|
|| (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT
|
|| (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT
|
&& ! (TARGET_FPU && named)))
|
&& ! (TARGET_FPU && named)))
|
{
|
{
|
/* The complex types are passed as packed types. */
|
/* The complex types are passed as packed types. */
|
if (GET_MODE_SIZE (mode) > UNITS_PER_WORD
|
if (GET_MODE_SIZE (mode) > UNITS_PER_WORD
|
&& slotno == SPARC_INT_ARG_MAX - 1)
|
&& slotno == SPARC_INT_ARG_MAX - 1)
|
return UNITS_PER_WORD;
|
return UNITS_PER_WORD;
|
}
|
}
|
else if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
|
else if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
|
{
|
{
|
if ((slotno + GET_MODE_SIZE (mode) / UNITS_PER_WORD)
|
if ((slotno + GET_MODE_SIZE (mode) / UNITS_PER_WORD)
|
> SPARC_FP_ARG_MAX)
|
> SPARC_FP_ARG_MAX)
|
return UNITS_PER_WORD;
|
return UNITS_PER_WORD;
|
}
|
}
|
}
|
}
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Handle the TARGET_PASS_BY_REFERENCE target hook.
|
/* Handle the TARGET_PASS_BY_REFERENCE target hook.
|
Specify whether to pass the argument by reference. */
|
Specify whether to pass the argument by reference. */
|
|
|
static bool
|
static bool
|
sparc_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
|
sparc_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
|
enum machine_mode mode, const_tree type,
|
enum machine_mode mode, const_tree type,
|
bool named ATTRIBUTE_UNUSED)
|
bool named ATTRIBUTE_UNUSED)
|
{
|
{
|
if (TARGET_ARCH32)
|
if (TARGET_ARCH32)
|
/* Original SPARC 32-bit ABI says that structures and unions,
|
/* Original SPARC 32-bit ABI says that structures and unions,
|
and quad-precision floats are passed by reference. For Pascal,
|
and quad-precision floats are passed by reference. For Pascal,
|
also pass arrays by reference. All other base types are passed
|
also pass arrays by reference. All other base types are passed
|
in registers.
|
in registers.
|
|
|
Extended ABI (as implemented by the Sun compiler) says that all
|
Extended ABI (as implemented by the Sun compiler) says that all
|
complex floats are passed by reference. Pass complex integers
|
complex floats are passed by reference. Pass complex integers
|
in registers up to 8 bytes. More generally, enforce the 2-word
|
in registers up to 8 bytes. More generally, enforce the 2-word
|
cap for passing arguments in registers.
|
cap for passing arguments in registers.
|
|
|
Vector ABI (as implemented by the Sun VIS SDK) says that vector
|
Vector ABI (as implemented by the Sun VIS SDK) says that vector
|
integers are passed like floats of the same size, that is in
|
integers are passed like floats of the same size, that is in
|
registers up to 8 bytes. Pass all vector floats by reference
|
registers up to 8 bytes. Pass all vector floats by reference
|
like structure and unions. */
|
like structure and unions. */
|
return ((type && (AGGREGATE_TYPE_P (type) || VECTOR_FLOAT_TYPE_P (type)))
|
return ((type && (AGGREGATE_TYPE_P (type) || VECTOR_FLOAT_TYPE_P (type)))
|
|| mode == SCmode
|
|| mode == SCmode
|
/* Catch CDImode, TFmode, DCmode and TCmode. */
|
/* Catch CDImode, TFmode, DCmode and TCmode. */
|
|| GET_MODE_SIZE (mode) > 8
|
|| GET_MODE_SIZE (mode) > 8
|
|| (type
|
|| (type
|
&& TREE_CODE (type) == VECTOR_TYPE
|
&& TREE_CODE (type) == VECTOR_TYPE
|
&& (unsigned HOST_WIDE_INT) int_size_in_bytes (type) > 8));
|
&& (unsigned HOST_WIDE_INT) int_size_in_bytes (type) > 8));
|
else
|
else
|
/* Original SPARC 64-bit ABI says that structures and unions
|
/* Original SPARC 64-bit ABI says that structures and unions
|
smaller than 16 bytes are passed in registers, as well as
|
smaller than 16 bytes are passed in registers, as well as
|
all other base types.
|
all other base types.
|
|
|
Extended ABI (as implemented by the Sun compiler) says that
|
Extended ABI (as implemented by the Sun compiler) says that
|
complex floats are passed in registers up to 16 bytes. Pass
|
complex floats are passed in registers up to 16 bytes. Pass
|
all complex integers in registers up to 16 bytes. More generally,
|
all complex integers in registers up to 16 bytes. More generally,
|
enforce the 2-word cap for passing arguments in registers.
|
enforce the 2-word cap for passing arguments in registers.
|
|
|
Vector ABI (as implemented by the Sun VIS SDK) says that vector
|
Vector ABI (as implemented by the Sun VIS SDK) says that vector
|
integers are passed like floats of the same size, that is in
|
integers are passed like floats of the same size, that is in
|
registers (up to 16 bytes). Pass all vector floats like structure
|
registers (up to 16 bytes). Pass all vector floats like structure
|
and unions. */
|
and unions. */
|
return ((type
|
return ((type
|
&& (AGGREGATE_TYPE_P (type) || TREE_CODE (type) == VECTOR_TYPE)
|
&& (AGGREGATE_TYPE_P (type) || TREE_CODE (type) == VECTOR_TYPE)
|
&& (unsigned HOST_WIDE_INT) int_size_in_bytes (type) > 16)
|
&& (unsigned HOST_WIDE_INT) int_size_in_bytes (type) > 16)
|
/* Catch CTImode and TCmode. */
|
/* Catch CTImode and TCmode. */
|
|| GET_MODE_SIZE (mode) > 16);
|
|| GET_MODE_SIZE (mode) > 16);
|
}
|
}
|
|
|
/* Handle the FUNCTION_ARG_ADVANCE macro.
|
/* Handle the FUNCTION_ARG_ADVANCE macro.
|
Update the data in CUM to advance over an argument
|
Update the data in CUM to advance over an argument
|
of mode MODE and data type TYPE.
|
of mode MODE and data type TYPE.
|
TYPE is null for libcalls where that information may not be available. */
|
TYPE is null for libcalls where that information may not be available. */
|
|
|
void
|
void
|
function_arg_advance (struct sparc_args *cum, enum machine_mode mode,
|
function_arg_advance (struct sparc_args *cum, enum machine_mode mode,
|
tree type, int named)
|
tree type, int named)
|
{
|
{
|
int slotno, regno, padding;
|
int regno, padding;
|
|
|
/* We pass 0 for incoming_p here, it doesn't matter. */
|
/* We pass 0 for incoming_p here, it doesn't matter. */
|
slotno = function_arg_slotno (cum, mode, type, named, 0, ®no, &padding);
|
function_arg_slotno (cum, mode, type, named, 0, ®no, &padding);
|
|
|
/* If register required leading padding, add it. */
|
/* If argument requires leading padding, add it. */
|
if (slotno != -1)
|
|
cum->words += padding;
|
cum->words += padding;
|
|
|
if (TARGET_ARCH32)
|
if (TARGET_ARCH32)
|
{
|
{
|
cum->words += (mode != BLKmode
|
cum->words += (mode != BLKmode
|
? ROUND_ADVANCE (GET_MODE_SIZE (mode))
|
? ROUND_ADVANCE (GET_MODE_SIZE (mode))
|
: ROUND_ADVANCE (int_size_in_bytes (type)));
|
: ROUND_ADVANCE (int_size_in_bytes (type)));
|
}
|
}
|
else
|
else
|
{
|
{
|
if (type && AGGREGATE_TYPE_P (type))
|
if (type && AGGREGATE_TYPE_P (type))
|
{
|
{
|
int size = int_size_in_bytes (type);
|
int size = int_size_in_bytes (type);
|
|
|
if (size <= 8)
|
if (size <= 8)
|
++cum->words;
|
++cum->words;
|
else if (size <= 16)
|
else if (size <= 16)
|
cum->words += 2;
|
cum->words += 2;
|
else /* passed by reference */
|
else /* passed by reference */
|
++cum->words;
|
++cum->words;
|
}
|
}
|
else
|
else
|
{
|
{
|
cum->words += (mode != BLKmode
|
cum->words += (mode != BLKmode
|
? ROUND_ADVANCE (GET_MODE_SIZE (mode))
|
? ROUND_ADVANCE (GET_MODE_SIZE (mode))
|
: ROUND_ADVANCE (int_size_in_bytes (type)));
|
: ROUND_ADVANCE (int_size_in_bytes (type)));
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/* Handle the FUNCTION_ARG_PADDING macro.
|
/* Handle the FUNCTION_ARG_PADDING macro.
|
For the 64 bit ABI structs are always stored left shifted in their
|
For the 64 bit ABI structs are always stored left shifted in their
|
argument slot. */
|
argument slot. */
|
|
|
enum direction
|
enum direction
|
function_arg_padding (enum machine_mode mode, const_tree type)
|
function_arg_padding (enum machine_mode mode, const_tree type)
|
{
|
{
|
if (TARGET_ARCH64 && type != 0 && AGGREGATE_TYPE_P (type))
|
if (TARGET_ARCH64 && type != 0 && AGGREGATE_TYPE_P (type))
|
return upward;
|
return upward;
|
|
|
/* Fall back to the default. */
|
/* Fall back to the default. */
|
return DEFAULT_FUNCTION_ARG_PADDING (mode, type);
|
return DEFAULT_FUNCTION_ARG_PADDING (mode, type);
|
}
|
}
|
|
|
/* Handle the TARGET_RETURN_IN_MEMORY target hook.
|
/* Handle the TARGET_RETURN_IN_MEMORY target hook.
|
Specify whether to return the return value in memory. */
|
Specify whether to return the return value in memory. */
|
|
|
static bool
|
static bool
|
sparc_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
|
sparc_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
|
{
|
{
|
if (TARGET_ARCH32)
|
if (TARGET_ARCH32)
|
/* Original SPARC 32-bit ABI says that structures and unions,
|
/* Original SPARC 32-bit ABI says that structures and unions,
|
and quad-precision floats are returned in memory. All other
|
and quad-precision floats are returned in memory. All other
|
base types are returned in registers.
|
base types are returned in registers.
|
|
|
Extended ABI (as implemented by the Sun compiler) says that
|
Extended ABI (as implemented by the Sun compiler) says that
|
all complex floats are returned in registers (8 FP registers
|
all complex floats are returned in registers (8 FP registers
|
at most for '_Complex long double'). Return all complex integers
|
at most for '_Complex long double'). Return all complex integers
|
in registers (4 at most for '_Complex long long').
|
in registers (4 at most for '_Complex long long').
|
|
|
Vector ABI (as implemented by the Sun VIS SDK) says that vector
|
Vector ABI (as implemented by the Sun VIS SDK) says that vector
|
integers are returned like floats of the same size, that is in
|
integers are returned like floats of the same size, that is in
|
registers up to 8 bytes and in memory otherwise. Return all
|
registers up to 8 bytes and in memory otherwise. Return all
|
vector floats in memory like structure and unions; note that
|
vector floats in memory like structure and unions; note that
|
they always have BLKmode like the latter. */
|
they always have BLKmode like the latter. */
|
return (TYPE_MODE (type) == BLKmode
|
return (TYPE_MODE (type) == BLKmode
|
|| TYPE_MODE (type) == TFmode
|
|| TYPE_MODE (type) == TFmode
|
|| (TREE_CODE (type) == VECTOR_TYPE
|
|| (TREE_CODE (type) == VECTOR_TYPE
|
&& (unsigned HOST_WIDE_INT) int_size_in_bytes (type) > 8));
|
&& (unsigned HOST_WIDE_INT) int_size_in_bytes (type) > 8));
|
else
|
else
|
/* Original SPARC 64-bit ABI says that structures and unions
|
/* Original SPARC 64-bit ABI says that structures and unions
|
smaller than 32 bytes are returned in registers, as well as
|
smaller than 32 bytes are returned in registers, as well as
|
all other base types.
|
all other base types.
|
|
|
Extended ABI (as implemented by the Sun compiler) says that all
|
Extended ABI (as implemented by the Sun compiler) says that all
|
complex floats are returned in registers (8 FP registers at most
|
complex floats are returned in registers (8 FP registers at most
|
for '_Complex long double'). Return all complex integers in
|
for '_Complex long double'). Return all complex integers in
|
registers (4 at most for '_Complex TItype').
|
registers (4 at most for '_Complex TItype').
|
|
|
Vector ABI (as implemented by the Sun VIS SDK) says that vector
|
Vector ABI (as implemented by the Sun VIS SDK) says that vector
|
integers are returned like floats of the same size, that is in
|
integers are returned like floats of the same size, that is in
|
registers. Return all vector floats like structure and unions;
|
registers. Return all vector floats like structure and unions;
|
note that they always have BLKmode like the latter. */
|
note that they always have BLKmode like the latter. */
|
return ((TYPE_MODE (type) == BLKmode
|
return ((TYPE_MODE (type) == BLKmode
|
&& (unsigned HOST_WIDE_INT) int_size_in_bytes (type) > 32));
|
&& (unsigned HOST_WIDE_INT) int_size_in_bytes (type) > 32));
|
}
|
}
|
|
|
/* Handle the TARGET_STRUCT_VALUE target hook.
|
/* Handle the TARGET_STRUCT_VALUE target hook.
|
Return where to find the structure return value address. */
|
Return where to find the structure return value address. */
|
|
|
static rtx
|
static rtx
|
sparc_struct_value_rtx (tree fndecl, int incoming)
|
sparc_struct_value_rtx (tree fndecl, int incoming)
|
{
|
{
|
if (TARGET_ARCH64)
|
if (TARGET_ARCH64)
|
return 0;
|
return 0;
|
else
|
else
|
{
|
{
|
rtx mem;
|
rtx mem;
|
|
|
if (incoming)
|
if (incoming)
|
mem = gen_rtx_MEM (Pmode, plus_constant (frame_pointer_rtx,
|
mem = gen_rtx_MEM (Pmode, plus_constant (frame_pointer_rtx,
|
STRUCT_VALUE_OFFSET));
|
STRUCT_VALUE_OFFSET));
|
else
|
else
|
mem = gen_rtx_MEM (Pmode, plus_constant (stack_pointer_rtx,
|
mem = gen_rtx_MEM (Pmode, plus_constant (stack_pointer_rtx,
|
STRUCT_VALUE_OFFSET));
|
STRUCT_VALUE_OFFSET));
|
|
|
/* Only follow the SPARC ABI for fixed-size structure returns.
|
/* Only follow the SPARC ABI for fixed-size structure returns.
|
Variable size structure returns are handled per the normal
|
Variable size structure returns are handled per the normal
|
procedures in GCC. This is enabled by -mstd-struct-return */
|
procedures in GCC. This is enabled by -mstd-struct-return */
|
if (incoming == 2
|
if (incoming == 2
|
&& sparc_std_struct_return
|
&& sparc_std_struct_return
|
&& TYPE_SIZE_UNIT (TREE_TYPE (fndecl))
|
&& TYPE_SIZE_UNIT (TREE_TYPE (fndecl))
|
&& TREE_CODE (TYPE_SIZE_UNIT (TREE_TYPE (fndecl))) == INTEGER_CST)
|
&& TREE_CODE (TYPE_SIZE_UNIT (TREE_TYPE (fndecl))) == INTEGER_CST)
|
{
|
{
|
/* We must check and adjust the return address, as it is
|
/* We must check and adjust the return address, as it is
|
optional as to whether the return object is really
|
optional as to whether the return object is really
|
provided. */
|
provided. */
|
rtx ret_rtx = gen_rtx_REG (Pmode, 31);
|
rtx ret_rtx = gen_rtx_REG (Pmode, 31);
|
rtx scratch = gen_reg_rtx (SImode);
|
rtx scratch = gen_reg_rtx (SImode);
|
rtx endlab = gen_label_rtx ();
|
rtx endlab = gen_label_rtx ();
|
|
|
/* Calculate the return object size */
|
/* Calculate the return object size */
|
tree size = TYPE_SIZE_UNIT (TREE_TYPE (fndecl));
|
tree size = TYPE_SIZE_UNIT (TREE_TYPE (fndecl));
|
rtx size_rtx = GEN_INT (TREE_INT_CST_LOW (size) & 0xfff);
|
rtx size_rtx = GEN_INT (TREE_INT_CST_LOW (size) & 0xfff);
|
/* Construct a temporary return value */
|
/* Construct a temporary return value */
|
rtx temp_val = assign_stack_local (Pmode, TREE_INT_CST_LOW (size), 0);
|
rtx temp_val = assign_stack_local (Pmode, TREE_INT_CST_LOW (size), 0);
|
|
|
/* Implement SPARC 32-bit psABI callee returns struck checking
|
/* Implement SPARC 32-bit psABI callee returns struck checking
|
requirements:
|
requirements:
|
|
|
Fetch the instruction where we will return to and see if
|
Fetch the instruction where we will return to and see if
|
it's an unimp instruction (the most significant 10 bits
|
it's an unimp instruction (the most significant 10 bits
|
will be zero). */
|
will be zero). */
|
emit_move_insn (scratch, gen_rtx_MEM (SImode,
|
emit_move_insn (scratch, gen_rtx_MEM (SImode,
|
plus_constant (ret_rtx, 8)));
|
plus_constant (ret_rtx, 8)));
|
/* Assume the size is valid and pre-adjust */
|
/* Assume the size is valid and pre-adjust */
|
emit_insn (gen_add3_insn (ret_rtx, ret_rtx, GEN_INT (4)));
|
emit_insn (gen_add3_insn (ret_rtx, ret_rtx, GEN_INT (4)));
|
emit_cmp_and_jump_insns (scratch, size_rtx, EQ, const0_rtx, SImode, 0, endlab);
|
emit_cmp_and_jump_insns (scratch, size_rtx, EQ, const0_rtx, SImode, 0, endlab);
|
emit_insn (gen_sub3_insn (ret_rtx, ret_rtx, GEN_INT (4)));
|
emit_insn (gen_sub3_insn (ret_rtx, ret_rtx, GEN_INT (4)));
|
/* Assign stack temp:
|
/* Assign stack temp:
|
Write the address of the memory pointed to by temp_val into
|
Write the address of the memory pointed to by temp_val into
|
the memory pointed to by mem */
|
the memory pointed to by mem */
|
emit_move_insn (mem, XEXP (temp_val, 0));
|
emit_move_insn (mem, XEXP (temp_val, 0));
|
emit_label (endlab);
|
emit_label (endlab);
|
}
|
}
|
|
|
set_mem_alias_set (mem, struct_value_alias_set);
|
set_mem_alias_set (mem, struct_value_alias_set);
|
return mem;
|
return mem;
|
}
|
}
|
}
|
}
|
|
|
/* Handle FUNCTION_VALUE, FUNCTION_OUTGOING_VALUE, and LIBCALL_VALUE macros.
|
/* Handle FUNCTION_VALUE, FUNCTION_OUTGOING_VALUE, and LIBCALL_VALUE macros.
|
For v9, function return values are subject to the same rules as arguments,
|
For v9, function return values are subject to the same rules as arguments,
|
except that up to 32 bytes may be returned in registers. */
|
except that up to 32 bytes may be returned in registers. */
|
|
|
rtx
|
rtx
|
function_value (const_tree type, enum machine_mode mode, int incoming_p)
|
function_value (const_tree type, enum machine_mode mode, int incoming_p)
|
{
|
{
|
/* Beware that the two values are swapped here wrt function_arg. */
|
/* Beware that the two values are swapped here wrt function_arg. */
|
int regbase = (incoming_p
|
int regbase = (incoming_p
|
? SPARC_OUTGOING_INT_ARG_FIRST
|
? SPARC_OUTGOING_INT_ARG_FIRST
|
: SPARC_INCOMING_INT_ARG_FIRST);
|
: SPARC_INCOMING_INT_ARG_FIRST);
|
enum mode_class mclass = GET_MODE_CLASS (mode);
|
enum mode_class mclass = GET_MODE_CLASS (mode);
|
int regno;
|
int regno;
|
|
|
/* Vector types deserve special treatment because they are polymorphic wrt
|
/* Vector types deserve special treatment because they are polymorphic wrt
|
their mode, depending upon whether VIS instructions are enabled. */
|
their mode, depending upon whether VIS instructions are enabled. */
|
if (type && TREE_CODE (type) == VECTOR_TYPE)
|
if (type && TREE_CODE (type) == VECTOR_TYPE)
|
{
|
{
|
HOST_WIDE_INT size = int_size_in_bytes (type);
|
HOST_WIDE_INT size = int_size_in_bytes (type);
|
gcc_assert ((TARGET_ARCH32 && size <= 8)
|
gcc_assert ((TARGET_ARCH32 && size <= 8)
|
|| (TARGET_ARCH64 && size <= 32));
|
|| (TARGET_ARCH64 && size <= 32));
|
|
|
if (mode == BLKmode)
|
if (mode == BLKmode)
|
return function_arg_vector_value (size,
|
return function_arg_vector_value (size,
|
SPARC_FP_ARG_FIRST);
|
SPARC_FP_ARG_FIRST);
|
else
|
else
|
mclass = MODE_FLOAT;
|
mclass = MODE_FLOAT;
|
}
|
}
|
|
|
if (TARGET_ARCH64 && type)
|
if (TARGET_ARCH64 && type)
|
{
|
{
|
/* Structures up to 32 bytes in size are returned in registers. */
|
/* Structures up to 32 bytes in size are returned in registers. */
|
if (TREE_CODE (type) == RECORD_TYPE)
|
if (TREE_CODE (type) == RECORD_TYPE)
|
{
|
{
|
HOST_WIDE_INT size = int_size_in_bytes (type);
|
HOST_WIDE_INT size = int_size_in_bytes (type);
|
gcc_assert (size <= 32);
|
gcc_assert (size <= 32);
|
|
|
return function_arg_record_value (type, mode, 0, 1, regbase);
|
return function_arg_record_value (type, mode, 0, 1, regbase);
|
}
|
}
|
|
|
/* Unions up to 32 bytes in size are returned in integer registers. */
|
/* Unions up to 32 bytes in size are returned in integer registers. */
|
else if (TREE_CODE (type) == UNION_TYPE)
|
else if (TREE_CODE (type) == UNION_TYPE)
|
{
|
{
|
HOST_WIDE_INT size = int_size_in_bytes (type);
|
HOST_WIDE_INT size = int_size_in_bytes (type);
|
gcc_assert (size <= 32);
|
gcc_assert (size <= 32);
|
|
|
return function_arg_union_value (size, mode, 0, regbase);
|
return function_arg_union_value (size, mode, 0, regbase);
|
}
|
}
|
|
|
/* Objects that require it are returned in FP registers. */
|
/* Objects that require it are returned in FP registers. */
|
else if (mclass == MODE_FLOAT || mclass == MODE_COMPLEX_FLOAT)
|
else if (mclass == MODE_FLOAT || mclass == MODE_COMPLEX_FLOAT)
|
;
|
;
|
|
|
/* All other aggregate types are returned in an integer register in a
|
/* All other aggregate types are returned in an integer register in a
|
mode corresponding to the size of the type. */
|
mode corresponding to the size of the type. */
|
else if (AGGREGATE_TYPE_P (type))
|
else if (AGGREGATE_TYPE_P (type))
|
{
|
{
|
/* All other aggregate types are passed in an integer register
|
/* All other aggregate types are passed in an integer register
|
in a mode corresponding to the size of the type. */
|
in a mode corresponding to the size of the type. */
|
HOST_WIDE_INT size = int_size_in_bytes (type);
|
HOST_WIDE_INT size = int_size_in_bytes (type);
|
gcc_assert (size <= 32);
|
gcc_assert (size <= 32);
|
|
|
mode = mode_for_size (size * BITS_PER_UNIT, MODE_INT, 0);
|
mode = mode_for_size (size * BITS_PER_UNIT, MODE_INT, 0);
|
|
|
/* ??? We probably should have made the same ABI change in
|
/* ??? We probably should have made the same ABI change in
|
3.4.0 as the one we made for unions. The latter was
|
3.4.0 as the one we made for unions. The latter was
|
required by the SCD though, while the former is not
|
required by the SCD though, while the former is not
|
specified, so we favored compatibility and efficiency.
|
specified, so we favored compatibility and efficiency.
|
|
|
Now we're stuck for aggregates larger than 16 bytes,
|
Now we're stuck for aggregates larger than 16 bytes,
|
because OImode vanished in the meantime. Let's not
|
because OImode vanished in the meantime. Let's not
|
try to be unduly clever, and simply follow the ABI
|
try to be unduly clever, and simply follow the ABI
|
for unions in that case. */
|
for unions in that case. */
|
if (mode == BLKmode)
|
if (mode == BLKmode)
|
return function_arg_union_value (size, mode, 0, regbase);
|
return function_arg_union_value (size, mode, 0, regbase);
|
else
|
else
|
mclass = MODE_INT;
|
mclass = MODE_INT;
|
}
|
}
|
|
|
/* This must match sparc_promote_function_mode.
|
/* This must match sparc_promote_function_mode.
|
??? Maybe 32-bit pointers should actually remain in Pmode? */
|
??? Maybe 32-bit pointers should actually remain in Pmode? */
|
else if (mclass == MODE_INT && GET_MODE_SIZE (mode) < UNITS_PER_WORD)
|
else if (mclass == MODE_INT && GET_MODE_SIZE (mode) < UNITS_PER_WORD)
|
mode = word_mode;
|
mode = word_mode;
|
}
|
}
|
|
|
if ((mclass == MODE_FLOAT || mclass == MODE_COMPLEX_FLOAT) && TARGET_FPU)
|
if ((mclass == MODE_FLOAT || mclass == MODE_COMPLEX_FLOAT) && TARGET_FPU)
|
regno = SPARC_FP_ARG_FIRST;
|
regno = SPARC_FP_ARG_FIRST;
|
else
|
else
|
regno = regbase;
|
regno = regbase;
|
|
|
return gen_rtx_REG (mode, regno);
|
return gen_rtx_REG (mode, regno);
|
}
|
}
|
|
|
/* Do what is necessary for `va_start'. We look at the current function
|
/* Do what is necessary for `va_start'. We look at the current function
|
to determine if stdarg or varargs is used and return the address of
|
to determine if stdarg or varargs is used and return the address of
|
the first unnamed parameter. */
|
the first unnamed parameter. */
|
|
|
static rtx
|
static rtx
|
sparc_builtin_saveregs (void)
|
sparc_builtin_saveregs (void)
|
{
|
{
|
int first_reg = crtl->args.info.words;
|
int first_reg = crtl->args.info.words;
|
rtx address;
|
rtx address;
|
int regno;
|
int regno;
|
|
|
for (regno = first_reg; regno < SPARC_INT_ARG_MAX; regno++)
|
for (regno = first_reg; regno < SPARC_INT_ARG_MAX; regno++)
|
emit_move_insn (gen_rtx_MEM (word_mode,
|
emit_move_insn (gen_rtx_MEM (word_mode,
|
gen_rtx_PLUS (Pmode,
|
gen_rtx_PLUS (Pmode,
|
frame_pointer_rtx,
|
frame_pointer_rtx,
|
GEN_INT (FIRST_PARM_OFFSET (0)
|
GEN_INT (FIRST_PARM_OFFSET (0)
|
+ (UNITS_PER_WORD
|
+ (UNITS_PER_WORD
|
* regno)))),
|
* regno)))),
|
gen_rtx_REG (word_mode,
|
gen_rtx_REG (word_mode,
|
SPARC_INCOMING_INT_ARG_FIRST + regno));
|
SPARC_INCOMING_INT_ARG_FIRST + regno));
|
|
|
address = gen_rtx_PLUS (Pmode,
|
address = gen_rtx_PLUS (Pmode,
|
frame_pointer_rtx,
|
frame_pointer_rtx,
|
GEN_INT (FIRST_PARM_OFFSET (0)
|
GEN_INT (FIRST_PARM_OFFSET (0)
|
+ UNITS_PER_WORD * first_reg));
|
+ UNITS_PER_WORD * first_reg));
|
|
|
return address;
|
return address;
|
}
|
}
|
|
|
/* Implement `va_start' for stdarg. */
|
/* Implement `va_start' for stdarg. */
|
|
|
static void
|
static void
|
sparc_va_start (tree valist, rtx nextarg)
|
sparc_va_start (tree valist, rtx nextarg)
|
{
|
{
|
nextarg = expand_builtin_saveregs ();
|
nextarg = expand_builtin_saveregs ();
|
std_expand_builtin_va_start (valist, nextarg);
|
std_expand_builtin_va_start (valist, nextarg);
|
}
|
}
|
|
|
/* Implement `va_arg' for stdarg. */
|
/* Implement `va_arg' for stdarg. */
|
|
|
static tree
|
static tree
|
sparc_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
|
sparc_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
|
gimple_seq *post_p)
|
gimple_seq *post_p)
|
{
|
{
|
HOST_WIDE_INT size, rsize, align;
|
HOST_WIDE_INT size, rsize, align;
|
tree addr, incr;
|
tree addr, incr;
|
bool indirect;
|
bool indirect;
|
tree ptrtype = build_pointer_type (type);
|
tree ptrtype = build_pointer_type (type);
|
|
|
if (pass_by_reference (NULL, TYPE_MODE (type), type, false))
|
if (pass_by_reference (NULL, TYPE_MODE (type), type, false))
|
{
|
{
|
indirect = true;
|
indirect = true;
|
size = rsize = UNITS_PER_WORD;
|
size = rsize = UNITS_PER_WORD;
|
align = 0;
|
align = 0;
|
}
|
}
|
else
|
else
|
{
|
{
|
indirect = false;
|
indirect = false;
|
size = int_size_in_bytes (type);
|
size = int_size_in_bytes (type);
|
rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
|
rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
|
align = 0;
|
align = 0;
|
|
|
if (TARGET_ARCH64)
|
if (TARGET_ARCH64)
|
{
|
{
|
/* For SPARC64, objects requiring 16-byte alignment get it. */
|
/* For SPARC64, objects requiring 16-byte alignment get it. */
|
if (TYPE_ALIGN (type) >= 2 * (unsigned) BITS_PER_WORD)
|
if (TYPE_ALIGN (type) >= 2 * (unsigned) BITS_PER_WORD)
|
align = 2 * UNITS_PER_WORD;
|
align = 2 * UNITS_PER_WORD;
|
|
|
/* SPARC-V9 ABI states that structures up to 16 bytes in size
|
/* SPARC-V9 ABI states that structures up to 16 bytes in size
|
are left-justified in their slots. */
|
are left-justified in their slots. */
|
if (AGGREGATE_TYPE_P (type))
|
if (AGGREGATE_TYPE_P (type))
|
{
|
{
|
if (size == 0)
|
if (size == 0)
|
size = rsize = UNITS_PER_WORD;
|
size = rsize = UNITS_PER_WORD;
|
else
|
else
|
size = rsize;
|
size = rsize;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
incr = valist;
|
incr = valist;
|
if (align)
|
if (align)
|
{
|
{
|
incr = fold_build2 (POINTER_PLUS_EXPR, ptr_type_node, incr,
|
incr = fold_build2 (POINTER_PLUS_EXPR, ptr_type_node, incr,
|
size_int (align - 1));
|
size_int (align - 1));
|
incr = fold_convert (sizetype, incr);
|
incr = fold_convert (sizetype, incr);
|
incr = fold_build2 (BIT_AND_EXPR, sizetype, incr,
|
incr = fold_build2 (BIT_AND_EXPR, sizetype, incr,
|
size_int (-align));
|
size_int (-align));
|
incr = fold_convert (ptr_type_node, incr);
|
incr = fold_convert (ptr_type_node, incr);
|
}
|
}
|
|
|
gimplify_expr (&incr, pre_p, post_p, is_gimple_val, fb_rvalue);
|
gimplify_expr (&incr, pre_p, post_p, is_gimple_val, fb_rvalue);
|
addr = incr;
|
addr = incr;
|
|
|
if (BYTES_BIG_ENDIAN && size < rsize)
|
if (BYTES_BIG_ENDIAN && size < rsize)
|
addr = fold_build2 (POINTER_PLUS_EXPR, ptr_type_node, incr,
|
addr = fold_build2 (POINTER_PLUS_EXPR, ptr_type_node, incr,
|
size_int (rsize - size));
|
size_int (rsize - size));
|
|
|
if (indirect)
|
if (indirect)
|
{
|
{
|
addr = fold_convert (build_pointer_type (ptrtype), addr);
|
addr = fold_convert (build_pointer_type (ptrtype), addr);
|
addr = build_va_arg_indirect_ref (addr);
|
addr = build_va_arg_indirect_ref (addr);
|
}
|
}
|
|
|
/* If the address isn't aligned properly for the type, we need a temporary.
|
/* If the address isn't aligned properly for the type, we need a temporary.
|
FIXME: This is inefficient, usually we can do this in registers. */
|
FIXME: This is inefficient, usually we can do this in registers. */
|
else if (align == 0 && TYPE_ALIGN (type) > BITS_PER_WORD)
|
else if (align == 0 && TYPE_ALIGN (type) > BITS_PER_WORD)
|
{
|
{
|
tree tmp = create_tmp_var (type, "va_arg_tmp");
|
tree tmp = create_tmp_var (type, "va_arg_tmp");
|
tree dest_addr = build_fold_addr_expr (tmp);
|
tree dest_addr = build_fold_addr_expr (tmp);
|
tree copy = build_call_expr (implicit_built_in_decls[BUILT_IN_MEMCPY],
|
tree copy = build_call_expr (implicit_built_in_decls[BUILT_IN_MEMCPY],
|
3, dest_addr, addr, size_int (rsize));
|
3, dest_addr, addr, size_int (rsize));
|
TREE_ADDRESSABLE (tmp) = 1;
|
TREE_ADDRESSABLE (tmp) = 1;
|
gimplify_and_add (copy, pre_p);
|
gimplify_and_add (copy, pre_p);
|
addr = dest_addr;
|
addr = dest_addr;
|
}
|
}
|
|
|
else
|
else
|
addr = fold_convert (ptrtype, addr);
|
addr = fold_convert (ptrtype, addr);
|
|
|
incr
|
incr
|
= fold_build2 (POINTER_PLUS_EXPR, ptr_type_node, incr, size_int (rsize));
|
= fold_build2 (POINTER_PLUS_EXPR, ptr_type_node, incr, size_int (rsize));
|
gimplify_assign (valist, incr, post_p);
|
gimplify_assign (valist, incr, post_p);
|
|
|
return build_va_arg_indirect_ref (addr);
|
return build_va_arg_indirect_ref (addr);
|
}
|
}
|
|
|
/* Implement the TARGET_VECTOR_MODE_SUPPORTED_P target hook.
|
/* Implement the TARGET_VECTOR_MODE_SUPPORTED_P target hook.
|
Specify whether the vector mode is supported by the hardware. */
|
Specify whether the vector mode is supported by the hardware. */
|
|
|
static bool
|
static bool
|
sparc_vector_mode_supported_p (enum machine_mode mode)
|
sparc_vector_mode_supported_p (enum machine_mode mode)
|
{
|
{
|
return TARGET_VIS && VECTOR_MODE_P (mode) ? true : false;
|
return TARGET_VIS && VECTOR_MODE_P (mode) ? true : false;
|
}
|
}
|
|
|
/* Return the string to output an unconditional branch to LABEL, which is
|
/* Return the string to output an unconditional branch to LABEL, which is
|
the operand number of the label.
|
the operand number of the label.
|
|
|
DEST is the destination insn (i.e. the label), INSN is the source. */
|
DEST is the destination insn (i.e. the label), INSN is the source. */
|
|
|
const char *
|
const char *
|
output_ubranch (rtx dest, int label, rtx insn)
|
output_ubranch (rtx dest, int label, rtx insn)
|
{
|
{
|
static char string[64];
|
static char string[64];
|
bool v9_form = false;
|
bool v9_form = false;
|
char *p;
|
char *p;
|
|
|
if (TARGET_V9 && INSN_ADDRESSES_SET_P ())
|
if (TARGET_V9 && INSN_ADDRESSES_SET_P ())
|
{
|
{
|
int delta = (INSN_ADDRESSES (INSN_UID (dest))
|
int delta = (INSN_ADDRESSES (INSN_UID (dest))
|
- INSN_ADDRESSES (INSN_UID (insn)));
|
- INSN_ADDRESSES (INSN_UID (insn)));
|
/* Leave some instructions for "slop". */
|
/* Leave some instructions for "slop". */
|
if (delta >= -260000 && delta < 260000)
|
if (delta >= -260000 && delta < 260000)
|
v9_form = true;
|
v9_form = true;
|
}
|
}
|
|
|
if (v9_form)
|
if (v9_form)
|
strcpy (string, "ba%*,pt\t%%xcc, ");
|
strcpy (string, "ba%*,pt\t%%xcc, ");
|
else
|
else
|
strcpy (string, "b%*\t");
|
strcpy (string, "b%*\t");
|
|
|
p = strchr (string, '\0');
|
p = strchr (string, '\0');
|
*p++ = '%';
|
*p++ = '%';
|
*p++ = 'l';
|
*p++ = 'l';
|
*p++ = '0' + label;
|
*p++ = '0' + label;
|
*p++ = '%';
|
*p++ = '%';
|
*p++ = '(';
|
*p++ = '(';
|
*p = '\0';
|
*p = '\0';
|
|
|
return string;
|
return string;
|
}
|
}
|
|
|
/* Return the string to output a conditional branch to LABEL, which is
|
/* Return the string to output a conditional branch to LABEL, which is
|
the operand number of the label. OP is the conditional expression.
|
the operand number of the label. OP is the conditional expression.
|
XEXP (OP, 0) is assumed to be a condition code register (integer or
|
XEXP (OP, 0) is assumed to be a condition code register (integer or
|
floating point) and its mode specifies what kind of comparison we made.
|
floating point) and its mode specifies what kind of comparison we made.
|
|
|
DEST is the destination insn (i.e. the label), INSN is the source.
|
DEST is the destination insn (i.e. the label), INSN is the source.
|
|
|
REVERSED is nonzero if we should reverse the sense of the comparison.
|
REVERSED is nonzero if we should reverse the sense of the comparison.
|
|
|
ANNUL is nonzero if we should generate an annulling branch. */
|
ANNUL is nonzero if we should generate an annulling branch. */
|
|
|
const char *
|
const char *
|
output_cbranch (rtx op, rtx dest, int label, int reversed, int annul,
|
output_cbranch (rtx op, rtx dest, int label, int reversed, int annul,
|
rtx insn)
|
rtx insn)
|
{
|
{
|
static char string[64];
|
static char string[64];
|
enum rtx_code code = GET_CODE (op);
|
enum rtx_code code = GET_CODE (op);
|
rtx cc_reg = XEXP (op, 0);
|
rtx cc_reg = XEXP (op, 0);
|
enum machine_mode mode = GET_MODE (cc_reg);
|
enum machine_mode mode = GET_MODE (cc_reg);
|
const char *labelno, *branch;
|
const char *labelno, *branch;
|
int spaces = 8, far;
|
int spaces = 8, far;
|
char *p;
|
char *p;
|
|
|
/* v9 branches are limited to +-1MB. If it is too far away,
|
/* v9 branches are limited to +-1MB. If it is too far away,
|
change
|
change
|
|
|
bne,pt %xcc, .LC30
|
bne,pt %xcc, .LC30
|
|
|
to
|
to
|
|
|
be,pn %xcc, .+12
|
be,pn %xcc, .+12
|
nop
|
nop
|
ba .LC30
|
ba .LC30
|
|
|
and
|
and
|
|
|
fbne,a,pn %fcc2, .LC29
|
fbne,a,pn %fcc2, .LC29
|
|
|
to
|
to
|
|
|
fbe,pt %fcc2, .+16
|
fbe,pt %fcc2, .+16
|
nop
|
nop
|
ba .LC29 */
|
ba .LC29 */
|
|
|
far = TARGET_V9 && (get_attr_length (insn) >= 3);
|
far = TARGET_V9 && (get_attr_length (insn) >= 3);
|
if (reversed ^ far)
|
if (reversed ^ far)
|
{
|
{
|
/* Reversal of FP compares takes care -- an ordered compare
|
/* Reversal of FP compares takes care -- an ordered compare
|
becomes an unordered compare and vice versa. */
|
becomes an unordered compare and vice versa. */
|
if (mode == CCFPmode || mode == CCFPEmode)
|
if (mode == CCFPmode || mode == CCFPEmode)
|
code = reverse_condition_maybe_unordered (code);
|
code = reverse_condition_maybe_unordered (code);
|
else
|
else
|
code = reverse_condition (code);
|
code = reverse_condition (code);
|
}
|
}
|
|
|
/* Start by writing the branch condition. */
|
/* Start by writing the branch condition. */
|
if (mode == CCFPmode || mode == CCFPEmode)
|
if (mode == CCFPmode || mode == CCFPEmode)
|
{
|
{
|
switch (code)
|
switch (code)
|
{
|
{
|
case NE:
|
case NE:
|
branch = "fbne";
|
branch = "fbne";
|
break;
|
break;
|
case EQ:
|
case EQ:
|
branch = "fbe";
|
branch = "fbe";
|
break;
|
break;
|
case GE:
|
case GE:
|
branch = "fbge";
|
branch = "fbge";
|
break;
|
break;
|
case GT:
|
case GT:
|
branch = "fbg";
|
branch = "fbg";
|
break;
|
break;
|
case LE:
|
case LE:
|
branch = "fble";
|
branch = "fble";
|
break;
|
break;
|
case LT:
|
case LT:
|
branch = "fbl";
|
branch = "fbl";
|
break;
|
break;
|
case UNORDERED:
|
case UNORDERED:
|
branch = "fbu";
|
branch = "fbu";
|
break;
|
break;
|
case ORDERED:
|
case ORDERED:
|
branch = "fbo";
|
branch = "fbo";
|
break;
|
break;
|
case UNGT:
|
case UNGT:
|
branch = "fbug";
|
branch = "fbug";
|
break;
|
break;
|
case UNLT:
|
case UNLT:
|
branch = "fbul";
|
branch = "fbul";
|
break;
|
break;
|
case UNEQ:
|
case UNEQ:
|
branch = "fbue";
|
branch = "fbue";
|
break;
|
break;
|
case UNGE:
|
case UNGE:
|
branch = "fbuge";
|
branch = "fbuge";
|
break;
|
break;
|
case UNLE:
|
case UNLE:
|
branch = "fbule";
|
branch = "fbule";
|
break;
|
break;
|
case LTGT:
|
case LTGT:
|
branch = "fblg";
|
branch = "fblg";
|
break;
|
break;
|
|
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
|
|
/* ??? !v9: FP branches cannot be preceded by another floating point
|
/* ??? !v9: FP branches cannot be preceded by another floating point
|
insn. Because there is currently no concept of pre-delay slots,
|
insn. Because there is currently no concept of pre-delay slots,
|
we can fix this only by always emitting a nop before a floating
|
we can fix this only by always emitting a nop before a floating
|
point branch. */
|
point branch. */
|
|
|
string[0] = '\0';
|
string[0] = '\0';
|
if (! TARGET_V9)
|
if (! TARGET_V9)
|
strcpy (string, "nop\n\t");
|
strcpy (string, "nop\n\t");
|
strcat (string, branch);
|
strcat (string, branch);
|
}
|
}
|
else
|
else
|
{
|
{
|
switch (code)
|
switch (code)
|
{
|
{
|
case NE:
|
case NE:
|
branch = "bne";
|
branch = "bne";
|
break;
|
break;
|
case EQ:
|
case EQ:
|
branch = "be";
|
branch = "be";
|
break;
|
break;
|
case GE:
|
case GE:
|
if (mode == CC_NOOVmode || mode == CCX_NOOVmode)
|
if (mode == CC_NOOVmode || mode == CCX_NOOVmode)
|
branch = "bpos";
|
branch = "bpos";
|
else
|
else
|
branch = "bge";
|
branch = "bge";
|
break;
|
break;
|
case GT:
|
case GT:
|
branch = "bg";
|
branch = "bg";
|
break;
|
break;
|
case LE:
|
case LE:
|
branch = "ble";
|
branch = "ble";
|
break;
|
break;
|
case LT:
|
case LT:
|
if (mode == CC_NOOVmode || mode == CCX_NOOVmode)
|
if (mode == CC_NOOVmode || mode == CCX_NOOVmode)
|
branch = "bneg";
|
branch = "bneg";
|
else
|
else
|
branch = "bl";
|
branch = "bl";
|
break;
|
break;
|
case GEU:
|
case GEU:
|
branch = "bgeu";
|
branch = "bgeu";
|
break;
|
break;
|
case GTU:
|
case GTU:
|
branch = "bgu";
|
branch = "bgu";
|
break;
|
break;
|
case LEU:
|
case LEU:
|
branch = "bleu";
|
branch = "bleu";
|
break;
|
break;
|
case LTU:
|
case LTU:
|
branch = "blu";
|
branch = "blu";
|
break;
|
break;
|
|
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
strcpy (string, branch);
|
strcpy (string, branch);
|
}
|
}
|
spaces -= strlen (branch);
|
spaces -= strlen (branch);
|
p = strchr (string, '\0');
|
p = strchr (string, '\0');
|
|
|
/* Now add the annulling, the label, and a possible noop. */
|
/* Now add the annulling, the label, and a possible noop. */
|
if (annul && ! far)
|
if (annul && ! far)
|
{
|
{
|
strcpy (p, ",a");
|
strcpy (p, ",a");
|
p += 2;
|
p += 2;
|
spaces -= 2;
|
spaces -= 2;
|
}
|
}
|
|
|
if (TARGET_V9)
|
if (TARGET_V9)
|
{
|
{
|
rtx note;
|
rtx note;
|
int v8 = 0;
|
int v8 = 0;
|
|
|
if (! far && insn && INSN_ADDRESSES_SET_P ())
|
if (! far && insn && INSN_ADDRESSES_SET_P ())
|
{
|
{
|
int delta = (INSN_ADDRESSES (INSN_UID (dest))
|
int delta = (INSN_ADDRESSES (INSN_UID (dest))
|
- INSN_ADDRESSES (INSN_UID (insn)));
|
- INSN_ADDRESSES (INSN_UID (insn)));
|
/* Leave some instructions for "slop". */
|
/* Leave some instructions for "slop". */
|
if (delta < -260000 || delta >= 260000)
|
if (delta < -260000 || delta >= 260000)
|
v8 = 1;
|
v8 = 1;
|
}
|
}
|
|
|
if (mode == CCFPmode || mode == CCFPEmode)
|
if (mode == CCFPmode || mode == CCFPEmode)
|
{
|
{
|
static char v9_fcc_labelno[] = "%%fccX, ";
|
static char v9_fcc_labelno[] = "%%fccX, ";
|
/* Set the char indicating the number of the fcc reg to use. */
|
/* Set the char indicating the number of the fcc reg to use. */
|
v9_fcc_labelno[5] = REGNO (cc_reg) - SPARC_FIRST_V9_FCC_REG + '0';
|
v9_fcc_labelno[5] = REGNO (cc_reg) - SPARC_FIRST_V9_FCC_REG + '0';
|
labelno = v9_fcc_labelno;
|
labelno = v9_fcc_labelno;
|
if (v8)
|
if (v8)
|
{
|
{
|
gcc_assert (REGNO (cc_reg) == SPARC_FCC_REG);
|
gcc_assert (REGNO (cc_reg) == SPARC_FCC_REG);
|
labelno = "";
|
labelno = "";
|
}
|
}
|
}
|
}
|
else if (mode == CCXmode || mode == CCX_NOOVmode)
|
else if (mode == CCXmode || mode == CCX_NOOVmode)
|
{
|
{
|
labelno = "%%xcc, ";
|
labelno = "%%xcc, ";
|
gcc_assert (! v8);
|
gcc_assert (! v8);
|
}
|
}
|
else
|
else
|
{
|
{
|
labelno = "%%icc, ";
|
labelno = "%%icc, ";
|
if (v8)
|
if (v8)
|
labelno = "";
|
labelno = "";
|
}
|
}
|
|
|
if (*labelno && insn && (note = find_reg_note (insn, REG_BR_PROB, NULL_RTX)))
|
if (*labelno && insn && (note = find_reg_note (insn, REG_BR_PROB, NULL_RTX)))
|
{
|
{
|
strcpy (p,
|
strcpy (p,
|
((INTVAL (XEXP (note, 0)) >= REG_BR_PROB_BASE / 2) ^ far)
|
((INTVAL (XEXP (note, 0)) >= REG_BR_PROB_BASE / 2) ^ far)
|
? ",pt" : ",pn");
|
? ",pt" : ",pn");
|
p += 3;
|
p += 3;
|
spaces -= 3;
|
spaces -= 3;
|
}
|
}
|
}
|
}
|
else
|
else
|
labelno = "";
|
labelno = "";
|
|
|
if (spaces > 0)
|
if (spaces > 0)
|
*p++ = '\t';
|
*p++ = '\t';
|
else
|
else
|
*p++ = ' ';
|
*p++ = ' ';
|
strcpy (p, labelno);
|
strcpy (p, labelno);
|
p = strchr (p, '\0');
|
p = strchr (p, '\0');
|
if (far)
|
if (far)
|
{
|
{
|
strcpy (p, ".+12\n\t nop\n\tb\t");
|
strcpy (p, ".+12\n\t nop\n\tb\t");
|
/* Skip the next insn if requested or
|
/* Skip the next insn if requested or
|
if we know that it will be a nop. */
|
if we know that it will be a nop. */
|
if (annul || ! final_sequence)
|
if (annul || ! final_sequence)
|
p[3] = '6';
|
p[3] = '6';
|
p += 14;
|
p += 14;
|
}
|
}
|
*p++ = '%';
|
*p++ = '%';
|
*p++ = 'l';
|
*p++ = 'l';
|
*p++ = label + '0';
|
*p++ = label + '0';
|
*p++ = '%';
|
*p++ = '%';
|
*p++ = '#';
|
*p++ = '#';
|
*p = '\0';
|
*p = '\0';
|
|
|
return string;
|
return string;
|
}
|
}
|
|
|
/* Emit a library call comparison between floating point X and Y.
|
/* Emit a library call comparison between floating point X and Y.
|
COMPARISON is the operator to compare with (EQ, NE, GT, etc).
|
COMPARISON is the operator to compare with (EQ, NE, GT, etc).
|
Return the new operator to be used in the comparison sequence.
|
Return the new operator to be used in the comparison sequence.
|
|
|
TARGET_ARCH64 uses _Qp_* functions, which use pointers to TFmode
|
TARGET_ARCH64 uses _Qp_* functions, which use pointers to TFmode
|
values as arguments instead of the TFmode registers themselves,
|
values as arguments instead of the TFmode registers themselves,
|
that's why we cannot call emit_float_lib_cmp. */
|
that's why we cannot call emit_float_lib_cmp. */
|
|
|
rtx
|
rtx
|
sparc_emit_float_lib_cmp (rtx x, rtx y, enum rtx_code comparison)
|
sparc_emit_float_lib_cmp (rtx x, rtx y, enum rtx_code comparison)
|
{
|
{
|
const char *qpfunc;
|
const char *qpfunc;
|
rtx slot0, slot1, result, tem, tem2, libfunc;
|
rtx slot0, slot1, result, tem, tem2, libfunc;
|
enum machine_mode mode;
|
enum machine_mode mode;
|
enum rtx_code new_comparison;
|
enum rtx_code new_comparison;
|
|
|
switch (comparison)
|
switch (comparison)
|
{
|
{
|
case EQ:
|
case EQ:
|
qpfunc = (TARGET_ARCH64 ? "_Qp_feq" : "_Q_feq");
|
qpfunc = (TARGET_ARCH64 ? "_Qp_feq" : "_Q_feq");
|
break;
|
break;
|
|
|
case NE:
|
case NE:
|
qpfunc = (TARGET_ARCH64 ? "_Qp_fne" : "_Q_fne");
|
qpfunc = (TARGET_ARCH64 ? "_Qp_fne" : "_Q_fne");
|
break;
|
break;
|
|
|
case GT:
|
case GT:
|
qpfunc = (TARGET_ARCH64 ? "_Qp_fgt" : "_Q_fgt");
|
qpfunc = (TARGET_ARCH64 ? "_Qp_fgt" : "_Q_fgt");
|
break;
|
break;
|
|
|
case GE:
|
case GE:
|
qpfunc = (TARGET_ARCH64 ? "_Qp_fge" : "_Q_fge");
|
qpfunc = (TARGET_ARCH64 ? "_Qp_fge" : "_Q_fge");
|
break;
|
break;
|
|
|
case LT:
|
case LT:
|
qpfunc = (TARGET_ARCH64 ? "_Qp_flt" : "_Q_flt");
|
qpfunc = (TARGET_ARCH64 ? "_Qp_flt" : "_Q_flt");
|
break;
|
break;
|
|
|
case LE:
|
case LE:
|
qpfunc = (TARGET_ARCH64 ? "_Qp_fle" : "_Q_fle");
|
qpfunc = (TARGET_ARCH64 ? "_Qp_fle" : "_Q_fle");
|
break;
|
break;
|
|
|
case ORDERED:
|
case ORDERED:
|
case UNORDERED:
|
case UNORDERED:
|
case UNGT:
|
case UNGT:
|
case UNLT:
|
case UNLT:
|
case UNEQ:
|
case UNEQ:
|
case UNGE:
|
case UNGE:
|
case UNLE:
|
case UNLE:
|
case LTGT:
|
case LTGT:
|
qpfunc = (TARGET_ARCH64 ? "_Qp_cmp" : "_Q_cmp");
|
qpfunc = (TARGET_ARCH64 ? "_Qp_cmp" : "_Q_cmp");
|
break;
|
break;
|
|
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
|
|
if (TARGET_ARCH64)
|
if (TARGET_ARCH64)
|
{
|
{
|
if (MEM_P (x))
|
if (MEM_P (x))
|
slot0 = x;
|
slot0 = x;
|
else
|
else
|
{
|
{
|
slot0 = assign_stack_temp (TFmode, GET_MODE_SIZE(TFmode), 0);
|
slot0 = assign_stack_temp (TFmode, GET_MODE_SIZE(TFmode), 0);
|
emit_move_insn (slot0, x);
|
emit_move_insn (slot0, x);
|
}
|
}
|
|
|
if (MEM_P (y))
|
if (MEM_P (y))
|
slot1 = y;
|
slot1 = y;
|
else
|
else
|
{
|
{
|
slot1 = assign_stack_temp (TFmode, GET_MODE_SIZE(TFmode), 0);
|
slot1 = assign_stack_temp (TFmode, GET_MODE_SIZE(TFmode), 0);
|
emit_move_insn (slot1, y);
|
emit_move_insn (slot1, y);
|
}
|
}
|
|
|
libfunc = gen_rtx_SYMBOL_REF (Pmode, qpfunc);
|
libfunc = gen_rtx_SYMBOL_REF (Pmode, qpfunc);
|
emit_library_call (libfunc, LCT_NORMAL,
|
emit_library_call (libfunc, LCT_NORMAL,
|
DImode, 2,
|
DImode, 2,
|
XEXP (slot0, 0), Pmode,
|
XEXP (slot0, 0), Pmode,
|
XEXP (slot1, 0), Pmode);
|
XEXP (slot1, 0), Pmode);
|
mode = DImode;
|
mode = DImode;
|
}
|
}
|
else
|
else
|
{
|
{
|
libfunc = gen_rtx_SYMBOL_REF (Pmode, qpfunc);
|
libfunc = gen_rtx_SYMBOL_REF (Pmode, qpfunc);
|
emit_library_call (libfunc, LCT_NORMAL,
|
emit_library_call (libfunc, LCT_NORMAL,
|
SImode, 2,
|
SImode, 2,
|
x, TFmode, y, TFmode);
|
x, TFmode, y, TFmode);
|
mode = SImode;
|
mode = SImode;
|
}
|
}
|
|
|
|
|
/* Immediately move the result of the libcall into a pseudo
|
/* Immediately move the result of the libcall into a pseudo
|
register so reload doesn't clobber the value if it needs
|
register so reload doesn't clobber the value if it needs
|
the return register for a spill reg. */
|
the return register for a spill reg. */
|
result = gen_reg_rtx (mode);
|
result = gen_reg_rtx (mode);
|
emit_move_insn (result, hard_libcall_value (mode, libfunc));
|
emit_move_insn (result, hard_libcall_value (mode, libfunc));
|
|
|
switch (comparison)
|
switch (comparison)
|
{
|
{
|
default:
|
default:
|
return gen_rtx_NE (VOIDmode, result, const0_rtx);
|
return gen_rtx_NE (VOIDmode, result, const0_rtx);
|
case ORDERED:
|
case ORDERED:
|
case UNORDERED:
|
case UNORDERED:
|
new_comparison = (comparison == UNORDERED ? EQ : NE);
|
new_comparison = (comparison == UNORDERED ? EQ : NE);
|
return gen_rtx_fmt_ee (new_comparison, VOIDmode, result, GEN_INT(3));
|
return gen_rtx_fmt_ee (new_comparison, VOIDmode, result, GEN_INT(3));
|
case UNGT:
|
case UNGT:
|
case UNGE:
|
case UNGE:
|
new_comparison = (comparison == UNGT ? GT : NE);
|
new_comparison = (comparison == UNGT ? GT : NE);
|
return gen_rtx_fmt_ee (new_comparison, VOIDmode, result, const1_rtx);
|
return gen_rtx_fmt_ee (new_comparison, VOIDmode, result, const1_rtx);
|
case UNLE:
|
case UNLE:
|
return gen_rtx_NE (VOIDmode, result, const2_rtx);
|
return gen_rtx_NE (VOIDmode, result, const2_rtx);
|
case UNLT:
|
case UNLT:
|
tem = gen_reg_rtx (mode);
|
tem = gen_reg_rtx (mode);
|
if (TARGET_ARCH32)
|
if (TARGET_ARCH32)
|
emit_insn (gen_andsi3 (tem, result, const1_rtx));
|
emit_insn (gen_andsi3 (tem, result, const1_rtx));
|
else
|
else
|
emit_insn (gen_anddi3 (tem, result, const1_rtx));
|
emit_insn (gen_anddi3 (tem, result, const1_rtx));
|
return gen_rtx_NE (VOIDmode, tem, const0_rtx);
|
return gen_rtx_NE (VOIDmode, tem, const0_rtx);
|
case UNEQ:
|
case UNEQ:
|
case LTGT:
|
case LTGT:
|
tem = gen_reg_rtx (mode);
|
tem = gen_reg_rtx (mode);
|
if (TARGET_ARCH32)
|
if (TARGET_ARCH32)
|
emit_insn (gen_addsi3 (tem, result, const1_rtx));
|
emit_insn (gen_addsi3 (tem, result, const1_rtx));
|
else
|
else
|
emit_insn (gen_adddi3 (tem, result, const1_rtx));
|
emit_insn (gen_adddi3 (tem, result, const1_rtx));
|
tem2 = gen_reg_rtx (mode);
|
tem2 = gen_reg_rtx (mode);
|
if (TARGET_ARCH32)
|
if (TARGET_ARCH32)
|
emit_insn (gen_andsi3 (tem2, tem, const2_rtx));
|
emit_insn (gen_andsi3 (tem2, tem, const2_rtx));
|
else
|
else
|
emit_insn (gen_anddi3 (tem2, tem, const2_rtx));
|
emit_insn (gen_anddi3 (tem2, tem, const2_rtx));
|
new_comparison = (comparison == UNEQ ? EQ : NE);
|
new_comparison = (comparison == UNEQ ? EQ : NE);
|
return gen_rtx_fmt_ee (new_comparison, VOIDmode, tem2, const0_rtx);
|
return gen_rtx_fmt_ee (new_comparison, VOIDmode, tem2, const0_rtx);
|
}
|
}
|
|
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
|
|
/* Generate an unsigned DImode to FP conversion. This is the same code
|
/* Generate an unsigned DImode to FP conversion. This is the same code
|
optabs would emit if we didn't have TFmode patterns. */
|
optabs would emit if we didn't have TFmode patterns. */
|
|
|
void
|
void
|
sparc_emit_floatunsdi (rtx *operands, enum machine_mode mode)
|
sparc_emit_floatunsdi (rtx *operands, enum machine_mode mode)
|
{
|
{
|
rtx neglab, donelab, i0, i1, f0, in, out;
|
rtx neglab, donelab, i0, i1, f0, in, out;
|
|
|
out = operands[0];
|
out = operands[0];
|
in = force_reg (DImode, operands[1]);
|
in = force_reg (DImode, operands[1]);
|
neglab = gen_label_rtx ();
|
neglab = gen_label_rtx ();
|
donelab = gen_label_rtx ();
|
donelab = gen_label_rtx ();
|
i0 = gen_reg_rtx (DImode);
|
i0 = gen_reg_rtx (DImode);
|
i1 = gen_reg_rtx (DImode);
|
i1 = gen_reg_rtx (DImode);
|
f0 = gen_reg_rtx (mode);
|
f0 = gen_reg_rtx (mode);
|
|
|
emit_cmp_and_jump_insns (in, const0_rtx, LT, const0_rtx, DImode, 0, neglab);
|
emit_cmp_and_jump_insns (in, const0_rtx, LT, const0_rtx, DImode, 0, neglab);
|
|
|
emit_insn (gen_rtx_SET (VOIDmode, out, gen_rtx_FLOAT (mode, in)));
|
emit_insn (gen_rtx_SET (VOIDmode, out, gen_rtx_FLOAT (mode, in)));
|
emit_jump_insn (gen_jump (donelab));
|
emit_jump_insn (gen_jump (donelab));
|
emit_barrier ();
|
emit_barrier ();
|
|
|
emit_label (neglab);
|
emit_label (neglab);
|
|
|
emit_insn (gen_lshrdi3 (i0, in, const1_rtx));
|
emit_insn (gen_lshrdi3 (i0, in, const1_rtx));
|
emit_insn (gen_anddi3 (i1, in, const1_rtx));
|
emit_insn (gen_anddi3 (i1, in, const1_rtx));
|
emit_insn (gen_iordi3 (i0, i0, i1));
|
emit_insn (gen_iordi3 (i0, i0, i1));
|
emit_insn (gen_rtx_SET (VOIDmode, f0, gen_rtx_FLOAT (mode, i0)));
|
emit_insn (gen_rtx_SET (VOIDmode, f0, gen_rtx_FLOAT (mode, i0)));
|
emit_insn (gen_rtx_SET (VOIDmode, out, gen_rtx_PLUS (mode, f0, f0)));
|
emit_insn (gen_rtx_SET (VOIDmode, out, gen_rtx_PLUS (mode, f0, f0)));
|
|
|
emit_label (donelab);
|
emit_label (donelab);
|
}
|
}
|
|
|
/* Generate an FP to unsigned DImode conversion. This is the same code
|
/* Generate an FP to unsigned DImode conversion. This is the same code
|
optabs would emit if we didn't have TFmode patterns. */
|
optabs would emit if we didn't have TFmode patterns. */
|
|
|
void
|
void
|
sparc_emit_fixunsdi (rtx *operands, enum machine_mode mode)
|
sparc_emit_fixunsdi (rtx *operands, enum machine_mode mode)
|
{
|
{
|
rtx neglab, donelab, i0, i1, f0, in, out, limit;
|
rtx neglab, donelab, i0, i1, f0, in, out, limit;
|
|
|
out = operands[0];
|
out = operands[0];
|
in = force_reg (mode, operands[1]);
|
in = force_reg (mode, operands[1]);
|
neglab = gen_label_rtx ();
|
neglab = gen_label_rtx ();
|
donelab = gen_label_rtx ();
|
donelab = gen_label_rtx ();
|
i0 = gen_reg_rtx (DImode);
|
i0 = gen_reg_rtx (DImode);
|
i1 = gen_reg_rtx (DImode);
|
i1 = gen_reg_rtx (DImode);
|
limit = gen_reg_rtx (mode);
|
limit = gen_reg_rtx (mode);
|
f0 = gen_reg_rtx (mode);
|
f0 = gen_reg_rtx (mode);
|
|
|
emit_move_insn (limit,
|
emit_move_insn (limit,
|
CONST_DOUBLE_FROM_REAL_VALUE (
|
CONST_DOUBLE_FROM_REAL_VALUE (
|
REAL_VALUE_ATOF ("9223372036854775808.0", mode), mode));
|
REAL_VALUE_ATOF ("9223372036854775808.0", mode), mode));
|
emit_cmp_and_jump_insns (in, limit, GE, NULL_RTX, mode, 0, neglab);
|
emit_cmp_and_jump_insns (in, limit, GE, NULL_RTX, mode, 0, neglab);
|
|
|
emit_insn (gen_rtx_SET (VOIDmode,
|
emit_insn (gen_rtx_SET (VOIDmode,
|
out,
|
out,
|
gen_rtx_FIX (DImode, gen_rtx_FIX (mode, in))));
|
gen_rtx_FIX (DImode, gen_rtx_FIX (mode, in))));
|
emit_jump_insn (gen_jump (donelab));
|
emit_jump_insn (gen_jump (donelab));
|
emit_barrier ();
|
emit_barrier ();
|
|
|
emit_label (neglab);
|
emit_label (neglab);
|
|
|
emit_insn (gen_rtx_SET (VOIDmode, f0, gen_rtx_MINUS (mode, in, limit)));
|
emit_insn (gen_rtx_SET (VOIDmode, f0, gen_rtx_MINUS (mode, in, limit)));
|
emit_insn (gen_rtx_SET (VOIDmode,
|
emit_insn (gen_rtx_SET (VOIDmode,
|
i0,
|
i0,
|
gen_rtx_FIX (DImode, gen_rtx_FIX (mode, f0))));
|
gen_rtx_FIX (DImode, gen_rtx_FIX (mode, f0))));
|
emit_insn (gen_movdi (i1, const1_rtx));
|
emit_insn (gen_movdi (i1, const1_rtx));
|
emit_insn (gen_ashldi3 (i1, i1, GEN_INT (63)));
|
emit_insn (gen_ashldi3 (i1, i1, GEN_INT (63)));
|
emit_insn (gen_xordi3 (out, i0, i1));
|
emit_insn (gen_xordi3 (out, i0, i1));
|
|
|
emit_label (donelab);
|
emit_label (donelab);
|
}
|
}
|
|
|
/* Return the string to output a conditional branch to LABEL, testing
|
/* Return the string to output a conditional branch to LABEL, testing
|
register REG. LABEL is the operand number of the label; REG is the
|
register REG. LABEL is the operand number of the label; REG is the
|
operand number of the reg. OP is the conditional expression. The mode
|
operand number of the reg. OP is the conditional expression. The mode
|
of REG says what kind of comparison we made.
|
of REG says what kind of comparison we made.
|
|
|
DEST is the destination insn (i.e. the label), INSN is the source.
|
DEST is the destination insn (i.e. the label), INSN is the source.
|
|
|
REVERSED is nonzero if we should reverse the sense of the comparison.
|
REVERSED is nonzero if we should reverse the sense of the comparison.
|
|
|
ANNUL is nonzero if we should generate an annulling branch. */
|
ANNUL is nonzero if we should generate an annulling branch. */
|
|
|
const char *
|
const char *
|
output_v9branch (rtx op, rtx dest, int reg, int label, int reversed,
|
output_v9branch (rtx op, rtx dest, int reg, int label, int reversed,
|
int annul, rtx insn)
|
int annul, rtx insn)
|
{
|
{
|
static char string[64];
|
static char string[64];
|
enum rtx_code code = GET_CODE (op);
|
enum rtx_code code = GET_CODE (op);
|
enum machine_mode mode = GET_MODE (XEXP (op, 0));
|
enum machine_mode mode = GET_MODE (XEXP (op, 0));
|
rtx note;
|
rtx note;
|
int far;
|
int far;
|
char *p;
|
char *p;
|
|
|
/* branch on register are limited to +-128KB. If it is too far away,
|
/* branch on register are limited to +-128KB. If it is too far away,
|
change
|
change
|
|
|
brnz,pt %g1, .LC30
|
brnz,pt %g1, .LC30
|
|
|
to
|
to
|
|
|
brz,pn %g1, .+12
|
brz,pn %g1, .+12
|
nop
|
nop
|
ba,pt %xcc, .LC30
|
ba,pt %xcc, .LC30
|
|
|
and
|
and
|
|
|
brgez,a,pn %o1, .LC29
|
brgez,a,pn %o1, .LC29
|
|
|
to
|
to
|
|
|
brlz,pt %o1, .+16
|
brlz,pt %o1, .+16
|
nop
|
nop
|
ba,pt %xcc, .LC29 */
|
ba,pt %xcc, .LC29 */
|
|
|
far = get_attr_length (insn) >= 3;
|
far = get_attr_length (insn) >= 3;
|
|
|
/* If not floating-point or if EQ or NE, we can just reverse the code. */
|
/* If not floating-point or if EQ or NE, we can just reverse the code. */
|
if (reversed ^ far)
|
if (reversed ^ far)
|
code = reverse_condition (code);
|
code = reverse_condition (code);
|
|
|
/* Only 64 bit versions of these instructions exist. */
|
/* Only 64 bit versions of these instructions exist. */
|
gcc_assert (mode == DImode);
|
gcc_assert (mode == DImode);
|
|
|
/* Start by writing the branch condition. */
|
/* Start by writing the branch condition. */
|
|
|
switch (code)
|
switch (code)
|
{
|
{
|
case NE:
|
case NE:
|
strcpy (string, "brnz");
|
strcpy (string, "brnz");
|
break;
|
break;
|
|
|
case EQ:
|
case EQ:
|
strcpy (string, "brz");
|
strcpy (string, "brz");
|
break;
|
break;
|
|
|
case GE:
|
case GE:
|
strcpy (string, "brgez");
|
strcpy (string, "brgez");
|
break;
|
break;
|
|
|
case LT:
|
case LT:
|
strcpy (string, "brlz");
|
strcpy (string, "brlz");
|
break;
|
break;
|
|
|
case LE:
|
case LE:
|
strcpy (string, "brlez");
|
strcpy (string, "brlez");
|
break;
|
break;
|
|
|
case GT:
|
case GT:
|
strcpy (string, "brgz");
|
strcpy (string, "brgz");
|
break;
|
break;
|
|
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
|
|
p = strchr (string, '\0');
|
p = strchr (string, '\0');
|
|
|
/* Now add the annulling, reg, label, and nop. */
|
/* Now add the annulling, reg, label, and nop. */
|
if (annul && ! far)
|
if (annul && ! far)
|
{
|
{
|
strcpy (p, ",a");
|
strcpy (p, ",a");
|
p += 2;
|
p += 2;
|
}
|
}
|
|
|
if (insn && (note = find_reg_note (insn, REG_BR_PROB, NULL_RTX)))
|
if (insn && (note = find_reg_note (insn, REG_BR_PROB, NULL_RTX)))
|
{
|
{
|
strcpy (p,
|
strcpy (p,
|
((INTVAL (XEXP (note, 0)) >= REG_BR_PROB_BASE / 2) ^ far)
|
((INTVAL (XEXP (note, 0)) >= REG_BR_PROB_BASE / 2) ^ far)
|
? ",pt" : ",pn");
|
? ",pt" : ",pn");
|
p += 3;
|
p += 3;
|
}
|
}
|
|
|
*p = p < string + 8 ? '\t' : ' ';
|
*p = p < string + 8 ? '\t' : ' ';
|
p++;
|
p++;
|
*p++ = '%';
|
*p++ = '%';
|
*p++ = '0' + reg;
|
*p++ = '0' + reg;
|
*p++ = ',';
|
*p++ = ',';
|
*p++ = ' ';
|
*p++ = ' ';
|
if (far)
|
if (far)
|
{
|
{
|
int veryfar = 1, delta;
|
int veryfar = 1, delta;
|
|
|
if (INSN_ADDRESSES_SET_P ())
|
if (INSN_ADDRESSES_SET_P ())
|
{
|
{
|
delta = (INSN_ADDRESSES (INSN_UID (dest))
|
delta = (INSN_ADDRESSES (INSN_UID (dest))
|
- INSN_ADDRESSES (INSN_UID (insn)));
|
- INSN_ADDRESSES (INSN_UID (insn)));
|
/* Leave some instructions for "slop". */
|
/* Leave some instructions for "slop". */
|
if (delta >= -260000 && delta < 260000)
|
if (delta >= -260000 && delta < 260000)
|
veryfar = 0;
|
veryfar = 0;
|
}
|
}
|
|
|
strcpy (p, ".+12\n\t nop\n\t");
|
strcpy (p, ".+12\n\t nop\n\t");
|
/* Skip the next insn if requested or
|
/* Skip the next insn if requested or
|
if we know that it will be a nop. */
|
if we know that it will be a nop. */
|
if (annul || ! final_sequence)
|
if (annul || ! final_sequence)
|
p[3] = '6';
|
p[3] = '6';
|
p += 12;
|
p += 12;
|
if (veryfar)
|
if (veryfar)
|
{
|
{
|
strcpy (p, "b\t");
|
strcpy (p, "b\t");
|
p += 2;
|
p += 2;
|
}
|
}
|
else
|
else
|
{
|
{
|
strcpy (p, "ba,pt\t%%xcc, ");
|
strcpy (p, "ba,pt\t%%xcc, ");
|
p += 13;
|
p += 13;
|
}
|
}
|
}
|
}
|
*p++ = '%';
|
*p++ = '%';
|
*p++ = 'l';
|
*p++ = 'l';
|
*p++ = '0' + label;
|
*p++ = '0' + label;
|
*p++ = '%';
|
*p++ = '%';
|
*p++ = '#';
|
*p++ = '#';
|
*p = '\0';
|
*p = '\0';
|
|
|
return string;
|
return string;
|
}
|
}
|
|
|
/* Return 1, if any of the registers of the instruction are %l[0-7] or %o[0-7].
|
/* Return 1, if any of the registers of the instruction are %l[0-7] or %o[0-7].
|
Such instructions cannot be used in the delay slot of return insn on v9.
|
Such instructions cannot be used in the delay slot of return insn on v9.
|
If TEST is 0, also rename all %i[0-7] registers to their %o[0-7] counterparts.
|
If TEST is 0, also rename all %i[0-7] registers to their %o[0-7] counterparts.
|
*/
|
*/
|
|
|
static int
|
static int
|
epilogue_renumber (register rtx *where, int test)
|
epilogue_renumber (register rtx *where, int test)
|
{
|
{
|
register const char *fmt;
|
register const char *fmt;
|
register int i;
|
register int i;
|
register enum rtx_code code;
|
register enum rtx_code code;
|
|
|
if (*where == 0)
|
if (*where == 0)
|
return 0;
|
return 0;
|
|
|
code = GET_CODE (*where);
|
code = GET_CODE (*where);
|
|
|
switch (code)
|
switch (code)
|
{
|
{
|
case REG:
|
case REG:
|
if (REGNO (*where) >= 8 && REGNO (*where) < 24) /* oX or lX */
|
if (REGNO (*where) >= 8 && REGNO (*where) < 24) /* oX or lX */
|
return 1;
|
return 1;
|
if (! test && REGNO (*where) >= 24 && REGNO (*where) < 32)
|
if (! test && REGNO (*where) >= 24 && REGNO (*where) < 32)
|
*where = gen_rtx_REG (GET_MODE (*where), OUTGOING_REGNO (REGNO(*where)));
|
*where = gen_rtx_REG (GET_MODE (*where), OUTGOING_REGNO (REGNO(*where)));
|
case SCRATCH:
|
case SCRATCH:
|
case CC0:
|
case CC0:
|
case PC:
|
case PC:
|
case CONST_INT:
|
case CONST_INT:
|
case CONST_DOUBLE:
|
case CONST_DOUBLE:
|
return 0;
|
return 0;
|
|
|
/* Do not replace the frame pointer with the stack pointer because
|
/* Do not replace the frame pointer with the stack pointer because
|
it can cause the delayed instruction to load below the stack.
|
it can cause the delayed instruction to load below the stack.
|
This occurs when instructions like:
|
This occurs when instructions like:
|
|
|
(set (reg/i:SI 24 %i0)
|
(set (reg/i:SI 24 %i0)
|
(mem/f:SI (plus:SI (reg/f:SI 30 %fp)
|
(mem/f:SI (plus:SI (reg/f:SI 30 %fp)
|
(const_int -20 [0xffffffec])) 0))
|
(const_int -20 [0xffffffec])) 0))
|
|
|
are in the return delayed slot. */
|
are in the return delayed slot. */
|
case PLUS:
|
case PLUS:
|
if (GET_CODE (XEXP (*where, 0)) == REG
|
if (GET_CODE (XEXP (*where, 0)) == REG
|
&& REGNO (XEXP (*where, 0)) == HARD_FRAME_POINTER_REGNUM
|
&& REGNO (XEXP (*where, 0)) == HARD_FRAME_POINTER_REGNUM
|
&& (GET_CODE (XEXP (*where, 1)) != CONST_INT
|
&& (GET_CODE (XEXP (*where, 1)) != CONST_INT
|
|| INTVAL (XEXP (*where, 1)) < SPARC_STACK_BIAS))
|
|| INTVAL (XEXP (*where, 1)) < SPARC_STACK_BIAS))
|
return 1;
|
return 1;
|
break;
|
break;
|
|
|
case MEM:
|
case MEM:
|
if (SPARC_STACK_BIAS
|
if (SPARC_STACK_BIAS
|
&& GET_CODE (XEXP (*where, 0)) == REG
|
&& GET_CODE (XEXP (*where, 0)) == REG
|
&& REGNO (XEXP (*where, 0)) == HARD_FRAME_POINTER_REGNUM)
|
&& REGNO (XEXP (*where, 0)) == HARD_FRAME_POINTER_REGNUM)
|
return 1;
|
return 1;
|
break;
|
break;
|
|
|
default:
|
default:
|
break;
|
break;
|
}
|
}
|
|
|
fmt = GET_RTX_FORMAT (code);
|
fmt = GET_RTX_FORMAT (code);
|
|
|
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
|
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
|
{
|
{
|
if (fmt[i] == 'E')
|
if (fmt[i] == 'E')
|
{
|
{
|
register int j;
|
register int j;
|
for (j = XVECLEN (*where, i) - 1; j >= 0; j--)
|
for (j = XVECLEN (*where, i) - 1; j >= 0; j--)
|
if (epilogue_renumber (&(XVECEXP (*where, i, j)), test))
|
if (epilogue_renumber (&(XVECEXP (*where, i, j)), test))
|
return 1;
|
return 1;
|
}
|
}
|
else if (fmt[i] == 'e'
|
else if (fmt[i] == 'e'
|
&& epilogue_renumber (&(XEXP (*where, i)), test))
|
&& epilogue_renumber (&(XEXP (*where, i)), test))
|
return 1;
|
return 1;
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Leaf functions and non-leaf functions have different needs. */
|
/* Leaf functions and non-leaf functions have different needs. */
|
|
|
static const int
|
static const int
|
reg_leaf_alloc_order[] = REG_LEAF_ALLOC_ORDER;
|
reg_leaf_alloc_order[] = REG_LEAF_ALLOC_ORDER;
|
|
|
static const int
|
static const int
|
reg_nonleaf_alloc_order[] = REG_ALLOC_ORDER;
|
reg_nonleaf_alloc_order[] = REG_ALLOC_ORDER;
|
|
|
static const int *const reg_alloc_orders[] = {
|
static const int *const reg_alloc_orders[] = {
|
reg_leaf_alloc_order,
|
reg_leaf_alloc_order,
|
reg_nonleaf_alloc_order};
|
reg_nonleaf_alloc_order};
|
|
|
void
|
void
|
order_regs_for_local_alloc (void)
|
order_regs_for_local_alloc (void)
|
{
|
{
|
static int last_order_nonleaf = 1;
|
static int last_order_nonleaf = 1;
|
|
|
if (df_regs_ever_live_p (15) != last_order_nonleaf)
|
if (df_regs_ever_live_p (15) != last_order_nonleaf)
|
{
|
{
|
last_order_nonleaf = !last_order_nonleaf;
|
last_order_nonleaf = !last_order_nonleaf;
|
memcpy ((char *) reg_alloc_order,
|
memcpy ((char *) reg_alloc_order,
|
(const char *) reg_alloc_orders[last_order_nonleaf],
|
(const char *) reg_alloc_orders[last_order_nonleaf],
|
FIRST_PSEUDO_REGISTER * sizeof (int));
|
FIRST_PSEUDO_REGISTER * sizeof (int));
|
}
|
}
|
}
|
}
|
|
|
/* Return 1 if REG and MEM are legitimate enough to allow the various
|
/* Return 1 if REG and MEM are legitimate enough to allow the various
|
mem<-->reg splits to be run. */
|
mem<-->reg splits to be run. */
|
|
|
int
|
int
|
sparc_splitdi_legitimate (rtx reg, rtx mem)
|
sparc_splitdi_legitimate (rtx reg, rtx mem)
|
{
|
{
|
/* Punt if we are here by mistake. */
|
/* Punt if we are here by mistake. */
|
gcc_assert (reload_completed);
|
gcc_assert (reload_completed);
|
|
|
/* We must have an offsettable memory reference. */
|
/* We must have an offsettable memory reference. */
|
if (! offsettable_memref_p (mem))
|
if (! offsettable_memref_p (mem))
|
return 0;
|
return 0;
|
|
|
/* If we have legitimate args for ldd/std, we do not want
|
/* If we have legitimate args for ldd/std, we do not want
|
the split to happen. */
|
the split to happen. */
|
if ((REGNO (reg) % 2) == 0
|
if ((REGNO (reg) % 2) == 0
|
&& mem_min_alignment (mem, 8))
|
&& mem_min_alignment (mem, 8))
|
return 0;
|
return 0;
|
|
|
/* Success. */
|
/* Success. */
|
return 1;
|
return 1;
|
}
|
}
|
|
|
/* Return 1 if x and y are some kind of REG and they refer to
|
/* Return 1 if x and y are some kind of REG and they refer to
|
different hard registers. This test is guaranteed to be
|
different hard registers. This test is guaranteed to be
|
run after reload. */
|
run after reload. */
|
|
|
int
|
int
|
sparc_absnegfloat_split_legitimate (rtx x, rtx y)
|
sparc_absnegfloat_split_legitimate (rtx x, rtx y)
|
{
|
{
|
if (GET_CODE (x) != REG)
|
if (GET_CODE (x) != REG)
|
return 0;
|
return 0;
|
if (GET_CODE (y) != REG)
|
if (GET_CODE (y) != REG)
|
return 0;
|
return 0;
|
if (REGNO (x) == REGNO (y))
|
if (REGNO (x) == REGNO (y))
|
return 0;
|
return 0;
|
return 1;
|
return 1;
|
}
|
}
|
|
|
/* Return 1 if REGNO (reg1) is even and REGNO (reg1) == REGNO (reg2) - 1.
|
/* Return 1 if REGNO (reg1) is even and REGNO (reg1) == REGNO (reg2) - 1.
|
This makes them candidates for using ldd and std insns.
|
This makes them candidates for using ldd and std insns.
|
|
|
Note reg1 and reg2 *must* be hard registers. */
|
Note reg1 and reg2 *must* be hard registers. */
|
|
|
int
|
int
|
registers_ok_for_ldd_peep (rtx reg1, rtx reg2)
|
registers_ok_for_ldd_peep (rtx reg1, rtx reg2)
|
{
|
{
|
/* We might have been passed a SUBREG. */
|
/* We might have been passed a SUBREG. */
|
if (GET_CODE (reg1) != REG || GET_CODE (reg2) != REG)
|
if (GET_CODE (reg1) != REG || GET_CODE (reg2) != REG)
|
return 0;
|
return 0;
|
|
|
if (REGNO (reg1) % 2 != 0)
|
if (REGNO (reg1) % 2 != 0)
|
return 0;
|
return 0;
|
|
|
/* Integer ldd is deprecated in SPARC V9 */
|
/* Integer ldd is deprecated in SPARC V9 */
|
if (TARGET_V9 && REGNO (reg1) < 32)
|
if (TARGET_V9 && REGNO (reg1) < 32)
|
return 0;
|
return 0;
|
|
|
return (REGNO (reg1) == REGNO (reg2) - 1);
|
return (REGNO (reg1) == REGNO (reg2) - 1);
|
}
|
}
|
|
|
/* Return 1 if the addresses in mem1 and mem2 are suitable for use in
|
/* Return 1 if the addresses in mem1 and mem2 are suitable for use in
|
an ldd or std insn.
|
an ldd or std insn.
|
|
|
This can only happen when addr1 and addr2, the addresses in mem1
|
This can only happen when addr1 and addr2, the addresses in mem1
|
and mem2, are consecutive memory locations (addr1 + 4 == addr2).
|
and mem2, are consecutive memory locations (addr1 + 4 == addr2).
|
addr1 must also be aligned on a 64-bit boundary.
|
addr1 must also be aligned on a 64-bit boundary.
|
|
|
Also iff dependent_reg_rtx is not null it should not be used to
|
Also iff dependent_reg_rtx is not null it should not be used to
|
compute the address for mem1, i.e. we cannot optimize a sequence
|
compute the address for mem1, i.e. we cannot optimize a sequence
|
like:
|
like:
|
ld [%o0], %o0
|
ld [%o0], %o0
|
ld [%o0 + 4], %o1
|
ld [%o0 + 4], %o1
|
to
|
to
|
ldd [%o0], %o0
|
ldd [%o0], %o0
|
nor:
|
nor:
|
ld [%g3 + 4], %g3
|
ld [%g3 + 4], %g3
|
ld [%g3], %g2
|
ld [%g3], %g2
|
to
|
to
|
ldd [%g3], %g2
|
ldd [%g3], %g2
|
|
|
But, note that the transformation from:
|
But, note that the transformation from:
|
ld [%g2 + 4], %g3
|
ld [%g2 + 4], %g3
|
ld [%g2], %g2
|
ld [%g2], %g2
|
to
|
to
|
ldd [%g2], %g2
|
ldd [%g2], %g2
|
is perfectly fine. Thus, the peephole2 patterns always pass us
|
is perfectly fine. Thus, the peephole2 patterns always pass us
|
the destination register of the first load, never the second one.
|
the destination register of the first load, never the second one.
|
|
|
For stores we don't have a similar problem, so dependent_reg_rtx is
|
For stores we don't have a similar problem, so dependent_reg_rtx is
|
NULL_RTX. */
|
NULL_RTX. */
|
|
|
int
|
int
|
mems_ok_for_ldd_peep (rtx mem1, rtx mem2, rtx dependent_reg_rtx)
|
mems_ok_for_ldd_peep (rtx mem1, rtx mem2, rtx dependent_reg_rtx)
|
{
|
{
|
rtx addr1, addr2;
|
rtx addr1, addr2;
|
unsigned int reg1;
|
unsigned int reg1;
|
HOST_WIDE_INT offset1;
|
HOST_WIDE_INT offset1;
|
|
|
/* The mems cannot be volatile. */
|
/* The mems cannot be volatile. */
|
if (MEM_VOLATILE_P (mem1) || MEM_VOLATILE_P (mem2))
|
if (MEM_VOLATILE_P (mem1) || MEM_VOLATILE_P (mem2))
|
return 0;
|
return 0;
|
|
|
/* MEM1 should be aligned on a 64-bit boundary. */
|
/* MEM1 should be aligned on a 64-bit boundary. */
|
if (MEM_ALIGN (mem1) < 64)
|
if (MEM_ALIGN (mem1) < 64)
|
return 0;
|
return 0;
|
|
|
addr1 = XEXP (mem1, 0);
|
addr1 = XEXP (mem1, 0);
|
addr2 = XEXP (mem2, 0);
|
addr2 = XEXP (mem2, 0);
|
|
|
/* Extract a register number and offset (if used) from the first addr. */
|
/* Extract a register number and offset (if used) from the first addr. */
|
if (GET_CODE (addr1) == PLUS)
|
if (GET_CODE (addr1) == PLUS)
|
{
|
{
|
/* If not a REG, return zero. */
|
/* If not a REG, return zero. */
|
if (GET_CODE (XEXP (addr1, 0)) != REG)
|
if (GET_CODE (XEXP (addr1, 0)) != REG)
|
return 0;
|
return 0;
|
else
|
else
|
{
|
{
|
reg1 = REGNO (XEXP (addr1, 0));
|
reg1 = REGNO (XEXP (addr1, 0));
|
/* The offset must be constant! */
|
/* The offset must be constant! */
|
if (GET_CODE (XEXP (addr1, 1)) != CONST_INT)
|
if (GET_CODE (XEXP (addr1, 1)) != CONST_INT)
|
return 0;
|
return 0;
|
offset1 = INTVAL (XEXP (addr1, 1));
|
offset1 = INTVAL (XEXP (addr1, 1));
|
}
|
}
|
}
|
}
|
else if (GET_CODE (addr1) != REG)
|
else if (GET_CODE (addr1) != REG)
|
return 0;
|
return 0;
|
else
|
else
|
{
|
{
|
reg1 = REGNO (addr1);
|
reg1 = REGNO (addr1);
|
/* This was a simple (mem (reg)) expression. Offset is 0. */
|
/* This was a simple (mem (reg)) expression. Offset is 0. */
|
offset1 = 0;
|
offset1 = 0;
|
}
|
}
|
|
|
/* Make sure the second address is a (mem (plus (reg) (const_int). */
|
/* Make sure the second address is a (mem (plus (reg) (const_int). */
|
if (GET_CODE (addr2) != PLUS)
|
if (GET_CODE (addr2) != PLUS)
|
return 0;
|
return 0;
|
|
|
if (GET_CODE (XEXP (addr2, 0)) != REG
|
if (GET_CODE (XEXP (addr2, 0)) != REG
|
|| GET_CODE (XEXP (addr2, 1)) != CONST_INT)
|
|| GET_CODE (XEXP (addr2, 1)) != CONST_INT)
|
return 0;
|
return 0;
|
|
|
if (reg1 != REGNO (XEXP (addr2, 0)))
|
if (reg1 != REGNO (XEXP (addr2, 0)))
|
return 0;
|
return 0;
|
|
|
if (dependent_reg_rtx != NULL_RTX && reg1 == REGNO (dependent_reg_rtx))
|
if (dependent_reg_rtx != NULL_RTX && reg1 == REGNO (dependent_reg_rtx))
|
return 0;
|
return 0;
|
|
|
/* The first offset must be evenly divisible by 8 to ensure the
|
/* The first offset must be evenly divisible by 8 to ensure the
|
address is 64 bit aligned. */
|
address is 64 bit aligned. */
|
if (offset1 % 8 != 0)
|
if (offset1 % 8 != 0)
|
return 0;
|
return 0;
|
|
|
/* The offset for the second addr must be 4 more than the first addr. */
|
/* The offset for the second addr must be 4 more than the first addr. */
|
if (INTVAL (XEXP (addr2, 1)) != offset1 + 4)
|
if (INTVAL (XEXP (addr2, 1)) != offset1 + 4)
|
return 0;
|
return 0;
|
|
|
/* All the tests passed. addr1 and addr2 are valid for ldd and std
|
/* All the tests passed. addr1 and addr2 are valid for ldd and std
|
instructions. */
|
instructions. */
|
return 1;
|
return 1;
|
}
|
}
|
|
|
/* Return 1 if reg is a pseudo, or is the first register in
|
/* Return 1 if reg is a pseudo, or is the first register in
|
a hard register pair. This makes it suitable for use in
|
a hard register pair. This makes it suitable for use in
|
ldd and std insns. */
|
ldd and std insns. */
|
|
|
int
|
int
|
register_ok_for_ldd (rtx reg)
|
register_ok_for_ldd (rtx reg)
|
{
|
{
|
/* We might have been passed a SUBREG. */
|
/* We might have been passed a SUBREG. */
|
if (!REG_P (reg))
|
if (!REG_P (reg))
|
return 0;
|
return 0;
|
|
|
if (REGNO (reg) < FIRST_PSEUDO_REGISTER)
|
if (REGNO (reg) < FIRST_PSEUDO_REGISTER)
|
return (REGNO (reg) % 2 == 0);
|
return (REGNO (reg) % 2 == 0);
|
|
|
return 1;
|
return 1;
|
}
|
}
|
|
|
/* Return 1 if OP is a memory whose address is known to be
|
/* Return 1 if OP is a memory whose address is known to be
|
aligned to 8-byte boundary, or a pseudo during reload.
|
aligned to 8-byte boundary, or a pseudo during reload.
|
This makes it suitable for use in ldd and std insns. */
|
This makes it suitable for use in ldd and std insns. */
|
|
|
int
|
int
|
memory_ok_for_ldd (rtx op)
|
memory_ok_for_ldd (rtx op)
|
{
|
{
|
if (MEM_P (op))
|
if (MEM_P (op))
|
{
|
{
|
/* In 64-bit mode, we assume that the address is word-aligned. */
|
/* In 64-bit mode, we assume that the address is word-aligned. */
|
if (TARGET_ARCH32 && !mem_min_alignment (op, 8))
|
if (TARGET_ARCH32 && !mem_min_alignment (op, 8))
|
return 0;
|
return 0;
|
|
|
if ((reload_in_progress || reload_completed)
|
if ((reload_in_progress || reload_completed)
|
&& !strict_memory_address_p (Pmode, XEXP (op, 0)))
|
&& !strict_memory_address_p (Pmode, XEXP (op, 0)))
|
return 0;
|
return 0;
|
}
|
}
|
else if (REG_P (op) && REGNO (op) >= FIRST_PSEUDO_REGISTER)
|
else if (REG_P (op) && REGNO (op) >= FIRST_PSEUDO_REGISTER)
|
{
|
{
|
if (!(reload_in_progress && reg_renumber [REGNO (op)] < 0))
|
if (!(reload_in_progress && reg_renumber [REGNO (op)] < 0))
|
return 0;
|
return 0;
|
}
|
}
|
else
|
else
|
return 0;
|
return 0;
|
|
|
return 1;
|
return 1;
|
}
|
}
|
|
|
/* Print operand X (an rtx) in assembler syntax to file FILE.
|
/* Print operand X (an rtx) in assembler syntax to file FILE.
|
CODE is a letter or dot (`z' in `%z0') or 0 if no letter was specified.
|
CODE is a letter or dot (`z' in `%z0') or 0 if no letter was specified.
|
For `%' followed by punctuation, CODE is the punctuation and X is null. */
|
For `%' followed by punctuation, CODE is the punctuation and X is null. */
|
|
|
void
|
void
|
print_operand (FILE *file, rtx x, int code)
|
print_operand (FILE *file, rtx x, int code)
|
{
|
{
|
switch (code)
|
switch (code)
|
{
|
{
|
case '#':
|
case '#':
|
/* Output an insn in a delay slot. */
|
/* Output an insn in a delay slot. */
|
if (final_sequence)
|
if (final_sequence)
|
sparc_indent_opcode = 1;
|
sparc_indent_opcode = 1;
|
else
|
else
|
fputs ("\n\t nop", file);
|
fputs ("\n\t nop", file);
|
return;
|
return;
|
case '*':
|
case '*':
|
/* Output an annul flag if there's nothing for the delay slot and we
|
/* Output an annul flag if there's nothing for the delay slot and we
|
are optimizing. This is always used with '(' below.
|
are optimizing. This is always used with '(' below.
|
Sun OS 4.1.1 dbx can't handle an annulled unconditional branch;
|
Sun OS 4.1.1 dbx can't handle an annulled unconditional branch;
|
this is a dbx bug. So, we only do this when optimizing.
|
this is a dbx bug. So, we only do this when optimizing.
|
On UltraSPARC, a branch in a delay slot causes a pipeline flush.
|
On UltraSPARC, a branch in a delay slot causes a pipeline flush.
|
Always emit a nop in case the next instruction is a branch. */
|
Always emit a nop in case the next instruction is a branch. */
|
if (! final_sequence && (optimize && (int)sparc_cpu < PROCESSOR_V9))
|
if (! final_sequence && (optimize && (int)sparc_cpu < PROCESSOR_V9))
|
fputs (",a", file);
|
fputs (",a", file);
|
return;
|
return;
|
case '(':
|
case '(':
|
/* Output a 'nop' if there's nothing for the delay slot and we are
|
/* Output a 'nop' if there's nothing for the delay slot and we are
|
not optimizing. This is always used with '*' above. */
|
not optimizing. This is always used with '*' above. */
|
if (! final_sequence && ! (optimize && (int)sparc_cpu < PROCESSOR_V9))
|
if (! final_sequence && ! (optimize && (int)sparc_cpu < PROCESSOR_V9))
|
fputs ("\n\t nop", file);
|
fputs ("\n\t nop", file);
|
else if (final_sequence)
|
else if (final_sequence)
|
sparc_indent_opcode = 1;
|
sparc_indent_opcode = 1;
|
return;
|
return;
|
case ')':
|
case ')':
|
/* Output the right displacement from the saved PC on function return.
|
/* Output the right displacement from the saved PC on function return.
|
The caller may have placed an "unimp" insn immediately after the call
|
The caller may have placed an "unimp" insn immediately after the call
|
so we have to account for it. This insn is used in the 32-bit ABI
|
so we have to account for it. This insn is used in the 32-bit ABI
|
when calling a function that returns a non zero-sized structure. The
|
when calling a function that returns a non zero-sized structure. The
|
64-bit ABI doesn't have it. Be careful to have this test be the same
|
64-bit ABI doesn't have it. Be careful to have this test be the same
|
as that for the call. The exception is when sparc_std_struct_return
|
as that for the call. The exception is when sparc_std_struct_return
|
is enabled, the psABI is followed exactly and the adjustment is made
|
is enabled, the psABI is followed exactly and the adjustment is made
|
by the code in sparc_struct_value_rtx. The call emitted is the same
|
by the code in sparc_struct_value_rtx. The call emitted is the same
|
when sparc_std_struct_return is enabled. */
|
when sparc_std_struct_return is enabled. */
|
if (!TARGET_ARCH64
|
if (!TARGET_ARCH64
|
&& cfun->returns_struct
|
&& cfun->returns_struct
|
&& !sparc_std_struct_return
|
&& !sparc_std_struct_return
|
&& DECL_SIZE (DECL_RESULT (current_function_decl))
|
&& DECL_SIZE (DECL_RESULT (current_function_decl))
|
&& TREE_CODE (DECL_SIZE (DECL_RESULT (current_function_decl)))
|
&& TREE_CODE (DECL_SIZE (DECL_RESULT (current_function_decl)))
|
== INTEGER_CST
|
== INTEGER_CST
|
&& !integer_zerop (DECL_SIZE (DECL_RESULT (current_function_decl))))
|
&& !integer_zerop (DECL_SIZE (DECL_RESULT (current_function_decl))))
|
fputs ("12", file);
|
fputs ("12", file);
|
else
|
else
|
fputc ('8', file);
|
fputc ('8', file);
|
return;
|
return;
|
case '_':
|
case '_':
|
/* Output the Embedded Medium/Anywhere code model base register. */
|
/* Output the Embedded Medium/Anywhere code model base register. */
|
fputs (EMBMEDANY_BASE_REG, file);
|
fputs (EMBMEDANY_BASE_REG, file);
|
return;
|
return;
|
case '&':
|
case '&':
|
/* Print some local dynamic TLS name. */
|
/* Print some local dynamic TLS name. */
|
assemble_name (file, get_some_local_dynamic_name ());
|
assemble_name (file, get_some_local_dynamic_name ());
|
return;
|
return;
|
|
|
case 'Y':
|
case 'Y':
|
/* Adjust the operand to take into account a RESTORE operation. */
|
/* Adjust the operand to take into account a RESTORE operation. */
|
if (GET_CODE (x) == CONST_INT)
|
if (GET_CODE (x) == CONST_INT)
|
break;
|
break;
|
else if (GET_CODE (x) != REG)
|
else if (GET_CODE (x) != REG)
|
output_operand_lossage ("invalid %%Y operand");
|
output_operand_lossage ("invalid %%Y operand");
|
else if (REGNO (x) < 8)
|
else if (REGNO (x) < 8)
|
fputs (reg_names[REGNO (x)], file);
|
fputs (reg_names[REGNO (x)], file);
|
else if (REGNO (x) >= 24 && REGNO (x) < 32)
|
else if (REGNO (x) >= 24 && REGNO (x) < 32)
|
fputs (reg_names[REGNO (x)-16], file);
|
fputs (reg_names[REGNO (x)-16], file);
|
else
|
else
|
output_operand_lossage ("invalid %%Y operand");
|
output_operand_lossage ("invalid %%Y operand");
|
return;
|
return;
|
case 'L':
|
case 'L':
|
/* Print out the low order register name of a register pair. */
|
/* Print out the low order register name of a register pair. */
|
if (WORDS_BIG_ENDIAN)
|
if (WORDS_BIG_ENDIAN)
|
fputs (reg_names[REGNO (x)+1], file);
|
fputs (reg_names[REGNO (x)+1], file);
|
else
|
else
|
fputs (reg_names[REGNO (x)], file);
|
fputs (reg_names[REGNO (x)], file);
|
return;
|
return;
|
case 'H':
|
case 'H':
|
/* Print out the high order register name of a register pair. */
|
/* Print out the high order register name of a register pair. */
|
if (WORDS_BIG_ENDIAN)
|
if (WORDS_BIG_ENDIAN)
|
fputs (reg_names[REGNO (x)], file);
|
fputs (reg_names[REGNO (x)], file);
|
else
|
else
|
fputs (reg_names[REGNO (x)+1], file);
|
fputs (reg_names[REGNO (x)+1], file);
|
return;
|
return;
|
case 'R':
|
case 'R':
|
/* Print out the second register name of a register pair or quad.
|
/* Print out the second register name of a register pair or quad.
|
I.e., R (%o0) => %o1. */
|
I.e., R (%o0) => %o1. */
|
fputs (reg_names[REGNO (x)+1], file);
|
fputs (reg_names[REGNO (x)+1], file);
|
return;
|
return;
|
case 'S':
|
case 'S':
|
/* Print out the third register name of a register quad.
|
/* Print out the third register name of a register quad.
|
I.e., S (%o0) => %o2. */
|
I.e., S (%o0) => %o2. */
|
fputs (reg_names[REGNO (x)+2], file);
|
fputs (reg_names[REGNO (x)+2], file);
|
return;
|
return;
|
case 'T':
|
case 'T':
|
/* Print out the fourth register name of a register quad.
|
/* Print out the fourth register name of a register quad.
|
I.e., T (%o0) => %o3. */
|
I.e., T (%o0) => %o3. */
|
fputs (reg_names[REGNO (x)+3], file);
|
fputs (reg_names[REGNO (x)+3], file);
|
return;
|
return;
|
case 'x':
|
case 'x':
|
/* Print a condition code register. */
|
/* Print a condition code register. */
|
if (REGNO (x) == SPARC_ICC_REG)
|
if (REGNO (x) == SPARC_ICC_REG)
|
{
|
{
|
/* We don't handle CC[X]_NOOVmode because they're not supposed
|
/* We don't handle CC[X]_NOOVmode because they're not supposed
|
to occur here. */
|
to occur here. */
|
if (GET_MODE (x) == CCmode)
|
if (GET_MODE (x) == CCmode)
|
fputs ("%icc", file);
|
fputs ("%icc", file);
|
else if (GET_MODE (x) == CCXmode)
|
else if (GET_MODE (x) == CCXmode)
|
fputs ("%xcc", file);
|
fputs ("%xcc", file);
|
else
|
else
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
else
|
else
|
/* %fccN register */
|
/* %fccN register */
|
fputs (reg_names[REGNO (x)], file);
|
fputs (reg_names[REGNO (x)], file);
|
return;
|
return;
|
case 'm':
|
case 'm':
|
/* Print the operand's address only. */
|
/* Print the operand's address only. */
|
output_address (XEXP (x, 0));
|
output_address (XEXP (x, 0));
|
return;
|
return;
|
case 'r':
|
case 'r':
|
/* In this case we need a register. Use %g0 if the
|
/* In this case we need a register. Use %g0 if the
|
operand is const0_rtx. */
|
operand is const0_rtx. */
|
if (x == const0_rtx
|
if (x == const0_rtx
|
|| (GET_MODE (x) != VOIDmode && x == CONST0_RTX (GET_MODE (x))))
|
|| (GET_MODE (x) != VOIDmode && x == CONST0_RTX (GET_MODE (x))))
|
{
|
{
|
fputs ("%g0", file);
|
fputs ("%g0", file);
|
return;
|
return;
|
}
|
}
|
else
|
else
|
break;
|
break;
|
|
|
case 'A':
|
case 'A':
|
switch (GET_CODE (x))
|
switch (GET_CODE (x))
|
{
|
{
|
case IOR: fputs ("or", file); break;
|
case IOR: fputs ("or", file); break;
|
case AND: fputs ("and", file); break;
|
case AND: fputs ("and", file); break;
|
case XOR: fputs ("xor", file); break;
|
case XOR: fputs ("xor", file); break;
|
default: output_operand_lossage ("invalid %%A operand");
|
default: output_operand_lossage ("invalid %%A operand");
|
}
|
}
|
return;
|
return;
|
|
|
case 'B':
|
case 'B':
|
switch (GET_CODE (x))
|
switch (GET_CODE (x))
|
{
|
{
|
case IOR: fputs ("orn", file); break;
|
case IOR: fputs ("orn", file); break;
|
case AND: fputs ("andn", file); break;
|
case AND: fputs ("andn", file); break;
|
case XOR: fputs ("xnor", file); break;
|
case XOR: fputs ("xnor", file); break;
|
default: output_operand_lossage ("invalid %%B operand");
|
default: output_operand_lossage ("invalid %%B operand");
|
}
|
}
|
return;
|
return;
|
|
|
/* These are used by the conditional move instructions. */
|
/* These are used by the conditional move instructions. */
|
case 'c' :
|
case 'c' :
|
case 'C':
|
case 'C':
|
{
|
{
|
enum rtx_code rc = GET_CODE (x);
|
enum rtx_code rc = GET_CODE (x);
|
|
|
if (code == 'c')
|
if (code == 'c')
|
{
|
{
|
enum machine_mode mode = GET_MODE (XEXP (x, 0));
|
enum machine_mode mode = GET_MODE (XEXP (x, 0));
|
if (mode == CCFPmode || mode == CCFPEmode)
|
if (mode == CCFPmode || mode == CCFPEmode)
|
rc = reverse_condition_maybe_unordered (GET_CODE (x));
|
rc = reverse_condition_maybe_unordered (GET_CODE (x));
|
else
|
else
|
rc = reverse_condition (GET_CODE (x));
|
rc = reverse_condition (GET_CODE (x));
|
}
|
}
|
switch (rc)
|
switch (rc)
|
{
|
{
|
case NE: fputs ("ne", file); break;
|
case NE: fputs ("ne", file); break;
|
case EQ: fputs ("e", file); break;
|
case EQ: fputs ("e", file); break;
|
case GE: fputs ("ge", file); break;
|
case GE: fputs ("ge", file); break;
|
case GT: fputs ("g", file); break;
|
case GT: fputs ("g", file); break;
|
case LE: fputs ("le", file); break;
|
case LE: fputs ("le", file); break;
|
case LT: fputs ("l", file); break;
|
case LT: fputs ("l", file); break;
|
case GEU: fputs ("geu", file); break;
|
case GEU: fputs ("geu", file); break;
|
case GTU: fputs ("gu", file); break;
|
case GTU: fputs ("gu", file); break;
|
case LEU: fputs ("leu", file); break;
|
case LEU: fputs ("leu", file); break;
|
case LTU: fputs ("lu", file); break;
|
case LTU: fputs ("lu", file); break;
|
case LTGT: fputs ("lg", file); break;
|
case LTGT: fputs ("lg", file); break;
|
case UNORDERED: fputs ("u", file); break;
|
case UNORDERED: fputs ("u", file); break;
|
case ORDERED: fputs ("o", file); break;
|
case ORDERED: fputs ("o", file); break;
|
case UNLT: fputs ("ul", file); break;
|
case UNLT: fputs ("ul", file); break;
|
case UNLE: fputs ("ule", file); break;
|
case UNLE: fputs ("ule", file); break;
|
case UNGT: fputs ("ug", file); break;
|
case UNGT: fputs ("ug", file); break;
|
case UNGE: fputs ("uge", file); break;
|
case UNGE: fputs ("uge", file); break;
|
case UNEQ: fputs ("ue", file); break;
|
case UNEQ: fputs ("ue", file); break;
|
default: output_operand_lossage (code == 'c'
|
default: output_operand_lossage (code == 'c'
|
? "invalid %%c operand"
|
? "invalid %%c operand"
|
: "invalid %%C operand");
|
: "invalid %%C operand");
|
}
|
}
|
return;
|
return;
|
}
|
}
|
|
|
/* These are used by the movr instruction pattern. */
|
/* These are used by the movr instruction pattern. */
|
case 'd':
|
case 'd':
|
case 'D':
|
case 'D':
|
{
|
{
|
enum rtx_code rc = (code == 'd'
|
enum rtx_code rc = (code == 'd'
|
? reverse_condition (GET_CODE (x))
|
? reverse_condition (GET_CODE (x))
|
: GET_CODE (x));
|
: GET_CODE (x));
|
switch (rc)
|
switch (rc)
|
{
|
{
|
case NE: fputs ("ne", file); break;
|
case NE: fputs ("ne", file); break;
|
case EQ: fputs ("e", file); break;
|
case EQ: fputs ("e", file); break;
|
case GE: fputs ("gez", file); break;
|
case GE: fputs ("gez", file); break;
|
case LT: fputs ("lz", file); break;
|
case LT: fputs ("lz", file); break;
|
case LE: fputs ("lez", file); break;
|
case LE: fputs ("lez", file); break;
|
case GT: fputs ("gz", file); break;
|
case GT: fputs ("gz", file); break;
|
default: output_operand_lossage (code == 'd'
|
default: output_operand_lossage (code == 'd'
|
? "invalid %%d operand"
|
? "invalid %%d operand"
|
: "invalid %%D operand");
|
: "invalid %%D operand");
|
}
|
}
|
return;
|
return;
|
}
|
}
|
|
|
case 'b':
|
case 'b':
|
{
|
{
|
/* Print a sign-extended character. */
|
/* Print a sign-extended character. */
|
int i = trunc_int_for_mode (INTVAL (x), QImode);
|
int i = trunc_int_for_mode (INTVAL (x), QImode);
|
fprintf (file, "%d", i);
|
fprintf (file, "%d", i);
|
return;
|
return;
|
}
|
}
|
|
|
case 'f':
|
case 'f':
|
/* Operand must be a MEM; write its address. */
|
/* Operand must be a MEM; write its address. */
|
if (GET_CODE (x) != MEM)
|
if (GET_CODE (x) != MEM)
|
output_operand_lossage ("invalid %%f operand");
|
output_operand_lossage ("invalid %%f operand");
|
output_address (XEXP (x, 0));
|
output_address (XEXP (x, 0));
|
return;
|
return;
|
|
|
case 's':
|
case 's':
|
{
|
{
|
/* Print a sign-extended 32-bit value. */
|
/* Print a sign-extended 32-bit value. */
|
HOST_WIDE_INT i;
|
HOST_WIDE_INT i;
|
if (GET_CODE(x) == CONST_INT)
|
if (GET_CODE(x) == CONST_INT)
|
i = INTVAL (x);
|
i = INTVAL (x);
|
else if (GET_CODE(x) == CONST_DOUBLE)
|
else if (GET_CODE(x) == CONST_DOUBLE)
|
i = CONST_DOUBLE_LOW (x);
|
i = CONST_DOUBLE_LOW (x);
|
else
|
else
|
{
|
{
|
output_operand_lossage ("invalid %%s operand");
|
output_operand_lossage ("invalid %%s operand");
|
return;
|
return;
|
}
|
}
|
i = trunc_int_for_mode (i, SImode);
|
i = trunc_int_for_mode (i, SImode);
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC, i);
|
fprintf (file, HOST_WIDE_INT_PRINT_DEC, i);
|
return;
|
return;
|
}
|
}
|
|
|
case 0:
|
case 0:
|
/* Do nothing special. */
|
/* Do nothing special. */
|
break;
|
break;
|
|
|
default:
|
default:
|
/* Undocumented flag. */
|
/* Undocumented flag. */
|
output_operand_lossage ("invalid operand output code");
|
output_operand_lossage ("invalid operand output code");
|
}
|
}
|
|
|
if (GET_CODE (x) == REG)
|
if (GET_CODE (x) == REG)
|
fputs (reg_names[REGNO (x)], file);
|
fputs (reg_names[REGNO (x)], file);
|
else if (GET_CODE (x) == MEM)
|
else if (GET_CODE (x) == MEM)
|
{
|
{
|
fputc ('[', file);
|
fputc ('[', file);
|
/* Poor Sun assembler doesn't understand absolute addressing. */
|
/* Poor Sun assembler doesn't understand absolute addressing. */
|
if (CONSTANT_P (XEXP (x, 0)))
|
if (CONSTANT_P (XEXP (x, 0)))
|
fputs ("%g0+", file);
|
fputs ("%g0+", file);
|
output_address (XEXP (x, 0));
|
output_address (XEXP (x, 0));
|
fputc (']', file);
|
fputc (']', file);
|
}
|
}
|
else if (GET_CODE (x) == HIGH)
|
else if (GET_CODE (x) == HIGH)
|
{
|
{
|
fputs ("%hi(", file);
|
fputs ("%hi(", file);
|
output_addr_const (file, XEXP (x, 0));
|
output_addr_const (file, XEXP (x, 0));
|
fputc (')', file);
|
fputc (')', file);
|
}
|
}
|
else if (GET_CODE (x) == LO_SUM)
|
else if (GET_CODE (x) == LO_SUM)
|
{
|
{
|
print_operand (file, XEXP (x, 0), 0);
|
print_operand (file, XEXP (x, 0), 0);
|
if (TARGET_CM_MEDMID)
|
if (TARGET_CM_MEDMID)
|
fputs ("+%l44(", file);
|
fputs ("+%l44(", file);
|
else
|
else
|
fputs ("+%lo(", file);
|
fputs ("+%lo(", file);
|
output_addr_const (file, XEXP (x, 1));
|
output_addr_const (file, XEXP (x, 1));
|
fputc (')', file);
|
fputc (')', file);
|
}
|
}
|
else if (GET_CODE (x) == CONST_DOUBLE
|
else if (GET_CODE (x) == CONST_DOUBLE
|
&& (GET_MODE (x) == VOIDmode
|
&& (GET_MODE (x) == VOIDmode
|
|| GET_MODE_CLASS (GET_MODE (x)) == MODE_INT))
|
|| GET_MODE_CLASS (GET_MODE (x)) == MODE_INT))
|
{
|
{
|
if (CONST_DOUBLE_HIGH (x) == 0)
|
if (CONST_DOUBLE_HIGH (x) == 0)
|
fprintf (file, "%u", (unsigned int) CONST_DOUBLE_LOW (x));
|
fprintf (file, "%u", (unsigned int) CONST_DOUBLE_LOW (x));
|
else if (CONST_DOUBLE_HIGH (x) == -1
|
else if (CONST_DOUBLE_HIGH (x) == -1
|
&& CONST_DOUBLE_LOW (x) < 0)
|
&& CONST_DOUBLE_LOW (x) < 0)
|
fprintf (file, "%d", (int) CONST_DOUBLE_LOW (x));
|
fprintf (file, "%d", (int) CONST_DOUBLE_LOW (x));
|
else
|
else
|
output_operand_lossage ("long long constant not a valid immediate operand");
|
output_operand_lossage ("long long constant not a valid immediate operand");
|
}
|
}
|
else if (GET_CODE (x) == CONST_DOUBLE)
|
else if (GET_CODE (x) == CONST_DOUBLE)
|
output_operand_lossage ("floating point constant not a valid immediate operand");
|
output_operand_lossage ("floating point constant not a valid immediate operand");
|
else { output_addr_const (file, x); }
|
else { output_addr_const (file, x); }
|
}
|
}
|
|
|
/* Target hook for assembling integer objects. The sparc version has
|
/* Target hook for assembling integer objects. The sparc version has
|
special handling for aligned DI-mode objects. */
|
special handling for aligned DI-mode objects. */
|
|
|
static bool
|
static bool
|
sparc_assemble_integer (rtx x, unsigned int size, int aligned_p)
|
sparc_assemble_integer (rtx x, unsigned int size, int aligned_p)
|
{
|
{
|
/* ??? We only output .xword's for symbols and only then in environments
|
/* ??? We only output .xword's for symbols and only then in environments
|
where the assembler can handle them. */
|
where the assembler can handle them. */
|
if (aligned_p && size == 8
|
if (aligned_p && size == 8
|
&& (GET_CODE (x) != CONST_INT && GET_CODE (x) != CONST_DOUBLE))
|
&& (GET_CODE (x) != CONST_INT && GET_CODE (x) != CONST_DOUBLE))
|
{
|
{
|
if (TARGET_V9)
|
if (TARGET_V9)
|
{
|
{
|
assemble_integer_with_op ("\t.xword\t", x);
|
assemble_integer_with_op ("\t.xword\t", x);
|
return true;
|
return true;
|
}
|
}
|
else
|
else
|
{
|
{
|
assemble_aligned_integer (4, const0_rtx);
|
assemble_aligned_integer (4, const0_rtx);
|
assemble_aligned_integer (4, x);
|
assemble_aligned_integer (4, x);
|
return true;
|
return true;
|
}
|
}
|
}
|
}
|
return default_assemble_integer (x, size, aligned_p);
|
return default_assemble_integer (x, size, aligned_p);
|
}
|
}
|
|
|
/* Return the value of a code used in the .proc pseudo-op that says
|
/* Return the value of a code used in the .proc pseudo-op that says
|
what kind of result this function returns. For non-C types, we pick
|
what kind of result this function returns. For non-C types, we pick
|
the closest C type. */
|
the closest C type. */
|
|
|
#ifndef SHORT_TYPE_SIZE
|
#ifndef SHORT_TYPE_SIZE
|
#define SHORT_TYPE_SIZE (BITS_PER_UNIT * 2)
|
#define SHORT_TYPE_SIZE (BITS_PER_UNIT * 2)
|
#endif
|
#endif
|
|
|
#ifndef INT_TYPE_SIZE
|
#ifndef INT_TYPE_SIZE
|
#define INT_TYPE_SIZE BITS_PER_WORD
|
#define INT_TYPE_SIZE BITS_PER_WORD
|
#endif
|
#endif
|
|
|
#ifndef LONG_TYPE_SIZE
|
#ifndef LONG_TYPE_SIZE
|
#define LONG_TYPE_SIZE BITS_PER_WORD
|
#define LONG_TYPE_SIZE BITS_PER_WORD
|
#endif
|
#endif
|
|
|
#ifndef LONG_LONG_TYPE_SIZE
|
#ifndef LONG_LONG_TYPE_SIZE
|
#define LONG_LONG_TYPE_SIZE (BITS_PER_WORD * 2)
|
#define LONG_LONG_TYPE_SIZE (BITS_PER_WORD * 2)
|
#endif
|
#endif
|
|
|
#ifndef FLOAT_TYPE_SIZE
|
#ifndef FLOAT_TYPE_SIZE
|
#define FLOAT_TYPE_SIZE BITS_PER_WORD
|
#define FLOAT_TYPE_SIZE BITS_PER_WORD
|
#endif
|
#endif
|
|
|
#ifndef DOUBLE_TYPE_SIZE
|
#ifndef DOUBLE_TYPE_SIZE
|
#define DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2)
|
#define DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2)
|
#endif
|
#endif
|
|
|
#ifndef LONG_DOUBLE_TYPE_SIZE
|
#ifndef LONG_DOUBLE_TYPE_SIZE
|
#define LONG_DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2)
|
#define LONG_DOUBLE_TYPE_SIZE (BITS_PER_WORD * 2)
|
#endif
|
#endif
|
|
|
unsigned long
|
unsigned long
|
sparc_type_code (register tree type)
|
sparc_type_code (register tree type)
|
{
|
{
|
register unsigned long qualifiers = 0;
|
register unsigned long qualifiers = 0;
|
register unsigned shift;
|
register unsigned shift;
|
|
|
/* Only the first 30 bits of the qualifier are valid. We must refrain from
|
/* Only the first 30 bits of the qualifier are valid. We must refrain from
|
setting more, since some assemblers will give an error for this. Also,
|
setting more, since some assemblers will give an error for this. Also,
|
we must be careful to avoid shifts of 32 bits or more to avoid getting
|
we must be careful to avoid shifts of 32 bits or more to avoid getting
|
unpredictable results. */
|
unpredictable results. */
|
|
|
for (shift = 6; shift < 30; shift += 2, type = TREE_TYPE (type))
|
for (shift = 6; shift < 30; shift += 2, type = TREE_TYPE (type))
|
{
|
{
|
switch (TREE_CODE (type))
|
switch (TREE_CODE (type))
|
{
|
{
|
case ERROR_MARK:
|
case ERROR_MARK:
|
return qualifiers;
|
return qualifiers;
|
|
|
case ARRAY_TYPE:
|
case ARRAY_TYPE:
|
qualifiers |= (3 << shift);
|
qualifiers |= (3 << shift);
|
break;
|
break;
|
|
|
case FUNCTION_TYPE:
|
case FUNCTION_TYPE:
|
case METHOD_TYPE:
|
case METHOD_TYPE:
|
qualifiers |= (2 << shift);
|
qualifiers |= (2 << shift);
|
break;
|
break;
|
|
|
case POINTER_TYPE:
|
case POINTER_TYPE:
|
case REFERENCE_TYPE:
|
case REFERENCE_TYPE:
|
case OFFSET_TYPE:
|
case OFFSET_TYPE:
|
qualifiers |= (1 << shift);
|
qualifiers |= (1 << shift);
|
break;
|
break;
|
|
|
case RECORD_TYPE:
|
case RECORD_TYPE:
|
return (qualifiers | 8);
|
return (qualifiers | 8);
|
|
|
case UNION_TYPE:
|
case UNION_TYPE:
|
case QUAL_UNION_TYPE:
|
case QUAL_UNION_TYPE:
|
return (qualifiers | 9);
|
return (qualifiers | 9);
|
|
|
case ENUMERAL_TYPE:
|
case ENUMERAL_TYPE:
|
return (qualifiers | 10);
|
return (qualifiers | 10);
|
|
|
case VOID_TYPE:
|
case VOID_TYPE:
|
return (qualifiers | 16);
|
return (qualifiers | 16);
|
|
|
case INTEGER_TYPE:
|
case INTEGER_TYPE:
|
/* If this is a range type, consider it to be the underlying
|
/* If this is a range type, consider it to be the underlying
|
type. */
|
type. */
|
if (TREE_TYPE (type) != 0)
|
if (TREE_TYPE (type) != 0)
|
break;
|
break;
|
|
|
/* Carefully distinguish all the standard types of C,
|
/* Carefully distinguish all the standard types of C,
|
without messing up if the language is not C. We do this by
|
without messing up if the language is not C. We do this by
|
testing TYPE_PRECISION and TYPE_UNSIGNED. The old code used to
|
testing TYPE_PRECISION and TYPE_UNSIGNED. The old code used to
|
look at both the names and the above fields, but that's redundant.
|
look at both the names and the above fields, but that's redundant.
|
Any type whose size is between two C types will be considered
|
Any type whose size is between two C types will be considered
|
to be the wider of the two types. Also, we do not have a
|
to be the wider of the two types. Also, we do not have a
|
special code to use for "long long", so anything wider than
|
special code to use for "long long", so anything wider than
|
long is treated the same. Note that we can't distinguish
|
long is treated the same. Note that we can't distinguish
|
between "int" and "long" in this code if they are the same
|
between "int" and "long" in this code if they are the same
|
size, but that's fine, since neither can the assembler. */
|
size, but that's fine, since neither can the assembler. */
|
|
|
if (TYPE_PRECISION (type) <= CHAR_TYPE_SIZE)
|
if (TYPE_PRECISION (type) <= CHAR_TYPE_SIZE)
|
return (qualifiers | (TYPE_UNSIGNED (type) ? 12 : 2));
|
return (qualifiers | (TYPE_UNSIGNED (type) ? 12 : 2));
|
|
|
else if (TYPE_PRECISION (type) <= SHORT_TYPE_SIZE)
|
else if (TYPE_PRECISION (type) <= SHORT_TYPE_SIZE)
|
return (qualifiers | (TYPE_UNSIGNED (type) ? 13 : 3));
|
return (qualifiers | (TYPE_UNSIGNED (type) ? 13 : 3));
|
|
|
else if (TYPE_PRECISION (type) <= INT_TYPE_SIZE)
|
else if (TYPE_PRECISION (type) <= INT_TYPE_SIZE)
|
return (qualifiers | (TYPE_UNSIGNED (type) ? 14 : 4));
|
return (qualifiers | (TYPE_UNSIGNED (type) ? 14 : 4));
|
|
|
else
|
else
|
return (qualifiers | (TYPE_UNSIGNED (type) ? 15 : 5));
|
return (qualifiers | (TYPE_UNSIGNED (type) ? 15 : 5));
|
|
|
case REAL_TYPE:
|
case REAL_TYPE:
|
/* If this is a range type, consider it to be the underlying
|
/* If this is a range type, consider it to be the underlying
|
type. */
|
type. */
|
if (TREE_TYPE (type) != 0)
|
if (TREE_TYPE (type) != 0)
|
break;
|
break;
|
|
|
/* Carefully distinguish all the standard types of C,
|
/* Carefully distinguish all the standard types of C,
|
without messing up if the language is not C. */
|
without messing up if the language is not C. */
|
|
|
if (TYPE_PRECISION (type) == FLOAT_TYPE_SIZE)
|
if (TYPE_PRECISION (type) == FLOAT_TYPE_SIZE)
|
return (qualifiers | 6);
|
return (qualifiers | 6);
|
|
|
else
|
else
|
return (qualifiers | 7);
|
return (qualifiers | 7);
|
|
|
case COMPLEX_TYPE: /* GNU Fortran COMPLEX type. */
|
case COMPLEX_TYPE: /* GNU Fortran COMPLEX type. */
|
/* ??? We need to distinguish between double and float complex types,
|
/* ??? We need to distinguish between double and float complex types,
|
but I don't know how yet because I can't reach this code from
|
but I don't know how yet because I can't reach this code from
|
existing front-ends. */
|
existing front-ends. */
|
return (qualifiers | 7); /* Who knows? */
|
return (qualifiers | 7); /* Who knows? */
|
|
|
case VECTOR_TYPE:
|
case VECTOR_TYPE:
|
case BOOLEAN_TYPE: /* Boolean truth value type. */
|
case BOOLEAN_TYPE: /* Boolean truth value type. */
|
case LANG_TYPE: /* ? */
|
case LANG_TYPE: /* ? */
|
return qualifiers;
|
return qualifiers;
|
|
|
default:
|
default:
|
gcc_unreachable (); /* Not a type! */
|
gcc_unreachable (); /* Not a type! */
|
}
|
}
|
}
|
}
|
|
|
return qualifiers;
|
return qualifiers;
|
}
|
}
|
|
|
/* Nested function support. */
|
/* Nested function support. */
|
|
|
/* Emit RTL insns to initialize the variable parts of a trampoline.
|
/* Emit RTL insns to initialize the variable parts of a trampoline.
|
FNADDR is an RTX for the address of the function's pure code.
|
FNADDR is an RTX for the address of the function's pure code.
|
CXT is an RTX for the static chain value for the function.
|
CXT is an RTX for the static chain value for the function.
|
|
|
This takes 16 insns: 2 shifts & 2 ands (to split up addresses), 4 sethi
|
This takes 16 insns: 2 shifts & 2 ands (to split up addresses), 4 sethi
|
(to load in opcodes), 4 iors (to merge address and opcodes), and 4 writes
|
(to load in opcodes), 4 iors (to merge address and opcodes), and 4 writes
|
(to store insns). This is a bit excessive. Perhaps a different
|
(to store insns). This is a bit excessive. Perhaps a different
|
mechanism would be better here.
|
mechanism would be better here.
|
|
|
Emit enough FLUSH insns to synchronize the data and instruction caches. */
|
Emit enough FLUSH insns to synchronize the data and instruction caches. */
|
|
|
static void
|
static void
|
sparc32_initialize_trampoline (rtx m_tramp, rtx fnaddr, rtx cxt)
|
sparc32_initialize_trampoline (rtx m_tramp, rtx fnaddr, rtx cxt)
|
{
|
{
|
/* SPARC 32-bit trampoline:
|
/* SPARC 32-bit trampoline:
|
|
|
sethi %hi(fn), %g1
|
sethi %hi(fn), %g1
|
sethi %hi(static), %g2
|
sethi %hi(static), %g2
|
jmp %g1+%lo(fn)
|
jmp %g1+%lo(fn)
|
or %g2, %lo(static), %g2
|
or %g2, %lo(static), %g2
|
|
|
SETHI i,r = 00rr rrr1 00ii iiii iiii iiii iiii iiii
|
SETHI i,r = 00rr rrr1 00ii iiii iiii iiii iiii iiii
|
JMPL r+i,d = 10dd ddd1 1100 0rrr rr1i iiii iiii iiii
|
JMPL r+i,d = 10dd ddd1 1100 0rrr rr1i iiii iiii iiii
|
*/
|
*/
|
|
|
emit_move_insn
|
emit_move_insn
|
(adjust_address (m_tramp, SImode, 0),
|
(adjust_address (m_tramp, SImode, 0),
|
expand_binop (SImode, ior_optab,
|
expand_binop (SImode, ior_optab,
|
expand_shift (RSHIFT_EXPR, SImode, fnaddr,
|
expand_shift (RSHIFT_EXPR, SImode, fnaddr,
|
size_int (10), 0, 1),
|
size_int (10), 0, 1),
|
GEN_INT (trunc_int_for_mode (0x03000000, SImode)),
|
GEN_INT (trunc_int_for_mode (0x03000000, SImode)),
|
NULL_RTX, 1, OPTAB_DIRECT));
|
NULL_RTX, 1, OPTAB_DIRECT));
|
|
|
emit_move_insn
|
emit_move_insn
|
(adjust_address (m_tramp, SImode, 4),
|
(adjust_address (m_tramp, SImode, 4),
|
expand_binop (SImode, ior_optab,
|
expand_binop (SImode, ior_optab,
|
expand_shift (RSHIFT_EXPR, SImode, cxt,
|
expand_shift (RSHIFT_EXPR, SImode, cxt,
|
size_int (10), 0, 1),
|
size_int (10), 0, 1),
|
GEN_INT (trunc_int_for_mode (0x05000000, SImode)),
|
GEN_INT (trunc_int_for_mode (0x05000000, SImode)),
|
NULL_RTX, 1, OPTAB_DIRECT));
|
NULL_RTX, 1, OPTAB_DIRECT));
|
|
|
emit_move_insn
|
emit_move_insn
|
(adjust_address (m_tramp, SImode, 8),
|
(adjust_address (m_tramp, SImode, 8),
|
expand_binop (SImode, ior_optab,
|
expand_binop (SImode, ior_optab,
|
expand_and (SImode, fnaddr, GEN_INT (0x3ff), NULL_RTX),
|
expand_and (SImode, fnaddr, GEN_INT (0x3ff), NULL_RTX),
|
GEN_INT (trunc_int_for_mode (0x81c06000, SImode)),
|
GEN_INT (trunc_int_for_mode (0x81c06000, SImode)),
|
NULL_RTX, 1, OPTAB_DIRECT));
|
NULL_RTX, 1, OPTAB_DIRECT));
|
|
|
emit_move_insn
|
emit_move_insn
|
(adjust_address (m_tramp, SImode, 12),
|
(adjust_address (m_tramp, SImode, 12),
|
expand_binop (SImode, ior_optab,
|
expand_binop (SImode, ior_optab,
|
expand_and (SImode, cxt, GEN_INT (0x3ff), NULL_RTX),
|
expand_and (SImode, cxt, GEN_INT (0x3ff), NULL_RTX),
|
GEN_INT (trunc_int_for_mode (0x8410a000, SImode)),
|
GEN_INT (trunc_int_for_mode (0x8410a000, SImode)),
|
NULL_RTX, 1, OPTAB_DIRECT));
|
NULL_RTX, 1, OPTAB_DIRECT));
|
|
|
/* On UltraSPARC a flush flushes an entire cache line. The trampoline is
|
/* On UltraSPARC a flush flushes an entire cache line. The trampoline is
|
aligned on a 16 byte boundary so one flush clears it all. */
|
aligned on a 16 byte boundary so one flush clears it all. */
|
emit_insn (gen_flush (validize_mem (adjust_address (m_tramp, SImode, 0))));
|
emit_insn (gen_flush (validize_mem (adjust_address (m_tramp, SImode, 0))));
|
if (sparc_cpu != PROCESSOR_ULTRASPARC
|
if (sparc_cpu != PROCESSOR_ULTRASPARC
|
&& sparc_cpu != PROCESSOR_ULTRASPARC3
|
&& sparc_cpu != PROCESSOR_ULTRASPARC3
|
&& sparc_cpu != PROCESSOR_NIAGARA
|
&& sparc_cpu != PROCESSOR_NIAGARA
|
&& sparc_cpu != PROCESSOR_NIAGARA2)
|
&& sparc_cpu != PROCESSOR_NIAGARA2)
|
emit_insn (gen_flush (validize_mem (adjust_address (m_tramp, SImode, 8))));
|
emit_insn (gen_flush (validize_mem (adjust_address (m_tramp, SImode, 8))));
|
|
|
/* Call __enable_execute_stack after writing onto the stack to make sure
|
/* Call __enable_execute_stack after writing onto the stack to make sure
|
the stack address is accessible. */
|
the stack address is accessible. */
|
#ifdef ENABLE_EXECUTE_STACK
|
#ifdef ENABLE_EXECUTE_STACK
|
emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__enable_execute_stack"),
|
emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__enable_execute_stack"),
|
LCT_NORMAL, VOIDmode, 1, XEXP (m_tramp, 0), Pmode);
|
LCT_NORMAL, VOIDmode, 1, XEXP (m_tramp, 0), Pmode);
|
#endif
|
#endif
|
|
|
}
|
}
|
|
|
/* The 64-bit version is simpler because it makes more sense to load the
|
/* The 64-bit version is simpler because it makes more sense to load the
|
values as "immediate" data out of the trampoline. It's also easier since
|
values as "immediate" data out of the trampoline. It's also easier since
|
we can read the PC without clobbering a register. */
|
we can read the PC without clobbering a register. */
|
|
|
static void
|
static void
|
sparc64_initialize_trampoline (rtx m_tramp, rtx fnaddr, rtx cxt)
|
sparc64_initialize_trampoline (rtx m_tramp, rtx fnaddr, rtx cxt)
|
{
|
{
|
/* SPARC 64-bit trampoline:
|
/* SPARC 64-bit trampoline:
|
|
|
rd %pc, %g1
|
rd %pc, %g1
|
ldx [%g1+24], %g5
|
ldx [%g1+24], %g5
|
jmp %g5
|
jmp %g5
|
ldx [%g1+16], %g5
|
ldx [%g1+16], %g5
|
+16 bytes data
|
+16 bytes data
|
*/
|
*/
|
|
|
emit_move_insn (adjust_address (m_tramp, SImode, 0),
|
emit_move_insn (adjust_address (m_tramp, SImode, 0),
|
GEN_INT (trunc_int_for_mode (0x83414000, SImode)));
|
GEN_INT (trunc_int_for_mode (0x83414000, SImode)));
|
emit_move_insn (adjust_address (m_tramp, SImode, 4),
|
emit_move_insn (adjust_address (m_tramp, SImode, 4),
|
GEN_INT (trunc_int_for_mode (0xca586018, SImode)));
|
GEN_INT (trunc_int_for_mode (0xca586018, SImode)));
|
emit_move_insn (adjust_address (m_tramp, SImode, 8),
|
emit_move_insn (adjust_address (m_tramp, SImode, 8),
|
GEN_INT (trunc_int_for_mode (0x81c14000, SImode)));
|
GEN_INT (trunc_int_for_mode (0x81c14000, SImode)));
|
emit_move_insn (adjust_address (m_tramp, SImode, 12),
|
emit_move_insn (adjust_address (m_tramp, SImode, 12),
|
GEN_INT (trunc_int_for_mode (0xca586010, SImode)));
|
GEN_INT (trunc_int_for_mode (0xca586010, SImode)));
|
emit_move_insn (adjust_address (m_tramp, DImode, 16), cxt);
|
emit_move_insn (adjust_address (m_tramp, DImode, 16), cxt);
|
emit_move_insn (adjust_address (m_tramp, DImode, 24), fnaddr);
|
emit_move_insn (adjust_address (m_tramp, DImode, 24), fnaddr);
|
emit_insn (gen_flushdi (validize_mem (adjust_address (m_tramp, DImode, 0))));
|
emit_insn (gen_flushdi (validize_mem (adjust_address (m_tramp, DImode, 0))));
|
|
|
if (sparc_cpu != PROCESSOR_ULTRASPARC
|
if (sparc_cpu != PROCESSOR_ULTRASPARC
|
&& sparc_cpu != PROCESSOR_ULTRASPARC3
|
&& sparc_cpu != PROCESSOR_ULTRASPARC3
|
&& sparc_cpu != PROCESSOR_NIAGARA
|
&& sparc_cpu != PROCESSOR_NIAGARA
|
&& sparc_cpu != PROCESSOR_NIAGARA2)
|
&& sparc_cpu != PROCESSOR_NIAGARA2)
|
emit_insn (gen_flushdi (validize_mem (adjust_address (m_tramp, DImode, 8))));
|
emit_insn (gen_flushdi (validize_mem (adjust_address (m_tramp, DImode, 8))));
|
|
|
/* Call __enable_execute_stack after writing onto the stack to make sure
|
/* Call __enable_execute_stack after writing onto the stack to make sure
|
the stack address is accessible. */
|
the stack address is accessible. */
|
#ifdef ENABLE_EXECUTE_STACK
|
#ifdef ENABLE_EXECUTE_STACK
|
emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__enable_execute_stack"),
|
emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__enable_execute_stack"),
|
LCT_NORMAL, VOIDmode, 1, XEXP (m_tramp, 0), Pmode);
|
LCT_NORMAL, VOIDmode, 1, XEXP (m_tramp, 0), Pmode);
|
#endif
|
#endif
|
}
|
}
|
|
|
/* Worker for TARGET_TRAMPOLINE_INIT. */
|
/* Worker for TARGET_TRAMPOLINE_INIT. */
|
|
|
static void
|
static void
|
sparc_trampoline_init (rtx m_tramp, tree fndecl, rtx cxt)
|
sparc_trampoline_init (rtx m_tramp, tree fndecl, rtx cxt)
|
{
|
{
|
rtx fnaddr = force_reg (Pmode, XEXP (DECL_RTL (fndecl), 0));
|
rtx fnaddr = force_reg (Pmode, XEXP (DECL_RTL (fndecl), 0));
|
cxt = force_reg (Pmode, cxt);
|
cxt = force_reg (Pmode, cxt);
|
if (TARGET_ARCH64)
|
if (TARGET_ARCH64)
|
sparc64_initialize_trampoline (m_tramp, fnaddr, cxt);
|
sparc64_initialize_trampoline (m_tramp, fnaddr, cxt);
|
else
|
else
|
sparc32_initialize_trampoline (m_tramp, fnaddr, cxt);
|
sparc32_initialize_trampoline (m_tramp, fnaddr, cxt);
|
}
|
}
|
|
|
/* Adjust the cost of a scheduling dependency. Return the new cost of
|
/* Adjust the cost of a scheduling dependency. Return the new cost of
|
a dependency LINK or INSN on DEP_INSN. COST is the current cost. */
|
a dependency LINK or INSN on DEP_INSN. COST is the current cost. */
|
|
|
static int
|
static int
|
supersparc_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost)
|
supersparc_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost)
|
{
|
{
|
enum attr_type insn_type;
|
enum attr_type insn_type;
|
|
|
if (! recog_memoized (insn))
|
if (! recog_memoized (insn))
|
return 0;
|
return 0;
|
|
|
insn_type = get_attr_type (insn);
|
insn_type = get_attr_type (insn);
|
|
|
if (REG_NOTE_KIND (link) == 0)
|
if (REG_NOTE_KIND (link) == 0)
|
{
|
{
|
/* Data dependency; DEP_INSN writes a register that INSN reads some
|
/* Data dependency; DEP_INSN writes a register that INSN reads some
|
cycles later. */
|
cycles later. */
|
|
|
/* if a load, then the dependence must be on the memory address;
|
/* if a load, then the dependence must be on the memory address;
|
add an extra "cycle". Note that the cost could be two cycles
|
add an extra "cycle". Note that the cost could be two cycles
|
if the reg was written late in an instruction group; we ca not tell
|
if the reg was written late in an instruction group; we ca not tell
|
here. */
|
here. */
|
if (insn_type == TYPE_LOAD || insn_type == TYPE_FPLOAD)
|
if (insn_type == TYPE_LOAD || insn_type == TYPE_FPLOAD)
|
return cost + 3;
|
return cost + 3;
|
|
|
/* Get the delay only if the address of the store is the dependence. */
|
/* Get the delay only if the address of the store is the dependence. */
|
if (insn_type == TYPE_STORE || insn_type == TYPE_FPSTORE)
|
if (insn_type == TYPE_STORE || insn_type == TYPE_FPSTORE)
|
{
|
{
|
rtx pat = PATTERN(insn);
|
rtx pat = PATTERN(insn);
|
rtx dep_pat = PATTERN (dep_insn);
|
rtx dep_pat = PATTERN (dep_insn);
|
|
|
if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET)
|
if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET)
|
return cost; /* This should not happen! */
|
return cost; /* This should not happen! */
|
|
|
/* The dependency between the two instructions was on the data that
|
/* The dependency between the two instructions was on the data that
|
is being stored. Assume that this implies that the address of the
|
is being stored. Assume that this implies that the address of the
|
store is not dependent. */
|
store is not dependent. */
|
if (rtx_equal_p (SET_DEST (dep_pat), SET_SRC (pat)))
|
if (rtx_equal_p (SET_DEST (dep_pat), SET_SRC (pat)))
|
return cost;
|
return cost;
|
|
|
return cost + 3; /* An approximation. */
|
return cost + 3; /* An approximation. */
|
}
|
}
|
|
|
/* A shift instruction cannot receive its data from an instruction
|
/* A shift instruction cannot receive its data from an instruction
|
in the same cycle; add a one cycle penalty. */
|
in the same cycle; add a one cycle penalty. */
|
if (insn_type == TYPE_SHIFT)
|
if (insn_type == TYPE_SHIFT)
|
return cost + 3; /* Split before cascade into shift. */
|
return cost + 3; /* Split before cascade into shift. */
|
}
|
}
|
else
|
else
|
{
|
{
|
/* Anti- or output- dependency; DEP_INSN reads/writes a register that
|
/* Anti- or output- dependency; DEP_INSN reads/writes a register that
|
INSN writes some cycles later. */
|
INSN writes some cycles later. */
|
|
|
/* These are only significant for the fpu unit; writing a fp reg before
|
/* These are only significant for the fpu unit; writing a fp reg before
|
the fpu has finished with it stalls the processor. */
|
the fpu has finished with it stalls the processor. */
|
|
|
/* Reusing an integer register causes no problems. */
|
/* Reusing an integer register causes no problems. */
|
if (insn_type == TYPE_IALU || insn_type == TYPE_SHIFT)
|
if (insn_type == TYPE_IALU || insn_type == TYPE_SHIFT)
|
return 0;
|
return 0;
|
}
|
}
|
|
|
return cost;
|
return cost;
|
}
|
}
|
|
|
static int
|
static int
|
hypersparc_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost)
|
hypersparc_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost)
|
{
|
{
|
enum attr_type insn_type, dep_type;
|
enum attr_type insn_type, dep_type;
|
rtx pat = PATTERN(insn);
|
rtx pat = PATTERN(insn);
|
rtx dep_pat = PATTERN (dep_insn);
|
rtx dep_pat = PATTERN (dep_insn);
|
|
|
if (recog_memoized (insn) < 0 || recog_memoized (dep_insn) < 0)
|
if (recog_memoized (insn) < 0 || recog_memoized (dep_insn) < 0)
|
return cost;
|
return cost;
|
|
|
insn_type = get_attr_type (insn);
|
insn_type = get_attr_type (insn);
|
dep_type = get_attr_type (dep_insn);
|
dep_type = get_attr_type (dep_insn);
|
|
|
switch (REG_NOTE_KIND (link))
|
switch (REG_NOTE_KIND (link))
|
{
|
{
|
case 0:
|
case 0:
|
/* Data dependency; DEP_INSN writes a register that INSN reads some
|
/* Data dependency; DEP_INSN writes a register that INSN reads some
|
cycles later. */
|
cycles later. */
|
|
|
switch (insn_type)
|
switch (insn_type)
|
{
|
{
|
case TYPE_STORE:
|
case TYPE_STORE:
|
case TYPE_FPSTORE:
|
case TYPE_FPSTORE:
|
/* Get the delay iff the address of the store is the dependence. */
|
/* Get the delay iff the address of the store is the dependence. */
|
if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET)
|
if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET)
|
return cost;
|
return cost;
|
|
|
if (rtx_equal_p (SET_DEST (dep_pat), SET_SRC (pat)))
|
if (rtx_equal_p (SET_DEST (dep_pat), SET_SRC (pat)))
|
return cost;
|
return cost;
|
return cost + 3;
|
return cost + 3;
|
|
|
case TYPE_LOAD:
|
case TYPE_LOAD:
|
case TYPE_SLOAD:
|
case TYPE_SLOAD:
|
case TYPE_FPLOAD:
|
case TYPE_FPLOAD:
|
/* If a load, then the dependence must be on the memory address. If
|
/* If a load, then the dependence must be on the memory address. If
|
the addresses aren't equal, then it might be a false dependency */
|
the addresses aren't equal, then it might be a false dependency */
|
if (dep_type == TYPE_STORE || dep_type == TYPE_FPSTORE)
|
if (dep_type == TYPE_STORE || dep_type == TYPE_FPSTORE)
|
{
|
{
|
if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET
|
if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET
|
|| GET_CODE (SET_DEST (dep_pat)) != MEM
|
|| GET_CODE (SET_DEST (dep_pat)) != MEM
|
|| GET_CODE (SET_SRC (pat)) != MEM
|
|| GET_CODE (SET_SRC (pat)) != MEM
|
|| ! rtx_equal_p (XEXP (SET_DEST (dep_pat), 0),
|
|| ! rtx_equal_p (XEXP (SET_DEST (dep_pat), 0),
|
XEXP (SET_SRC (pat), 0)))
|
XEXP (SET_SRC (pat), 0)))
|
return cost + 2;
|
return cost + 2;
|
|
|
return cost + 8;
|
return cost + 8;
|
}
|
}
|
break;
|
break;
|
|
|
case TYPE_BRANCH:
|
case TYPE_BRANCH:
|
/* Compare to branch latency is 0. There is no benefit from
|
/* Compare to branch latency is 0. There is no benefit from
|
separating compare and branch. */
|
separating compare and branch. */
|
if (dep_type == TYPE_COMPARE)
|
if (dep_type == TYPE_COMPARE)
|
return 0;
|
return 0;
|
/* Floating point compare to branch latency is less than
|
/* Floating point compare to branch latency is less than
|
compare to conditional move. */
|
compare to conditional move. */
|
if (dep_type == TYPE_FPCMP)
|
if (dep_type == TYPE_FPCMP)
|
return cost - 1;
|
return cost - 1;
|
break;
|
break;
|
default:
|
default:
|
break;
|
break;
|
}
|
}
|
break;
|
break;
|
|
|
case REG_DEP_ANTI:
|
case REG_DEP_ANTI:
|
/* Anti-dependencies only penalize the fpu unit. */
|
/* Anti-dependencies only penalize the fpu unit. */
|
if (insn_type == TYPE_IALU || insn_type == TYPE_SHIFT)
|
if (insn_type == TYPE_IALU || insn_type == TYPE_SHIFT)
|
return 0;
|
return 0;
|
break;
|
break;
|
|
|
default:
|
default:
|
break;
|
break;
|
}
|
}
|
|
|
return cost;
|
return cost;
|
}
|
}
|
|
|
static int
|
static int
|
sparc_adjust_cost(rtx insn, rtx link, rtx dep, int cost)
|
sparc_adjust_cost(rtx insn, rtx link, rtx dep, int cost)
|
{
|
{
|
switch (sparc_cpu)
|
switch (sparc_cpu)
|
{
|
{
|
case PROCESSOR_SUPERSPARC:
|
case PROCESSOR_SUPERSPARC:
|
cost = supersparc_adjust_cost (insn, link, dep, cost);
|
cost = supersparc_adjust_cost (insn, link, dep, cost);
|
break;
|
break;
|
case PROCESSOR_HYPERSPARC:
|
case PROCESSOR_HYPERSPARC:
|
case PROCESSOR_SPARCLITE86X:
|
case PROCESSOR_SPARCLITE86X:
|
cost = hypersparc_adjust_cost (insn, link, dep, cost);
|
cost = hypersparc_adjust_cost (insn, link, dep, cost);
|
break;
|
break;
|
default:
|
default:
|
break;
|
break;
|
}
|
}
|
return cost;
|
return cost;
|
}
|
}
|
|
|
static void
|
static void
|
sparc_sched_init (FILE *dump ATTRIBUTE_UNUSED,
|
sparc_sched_init (FILE *dump ATTRIBUTE_UNUSED,
|
int sched_verbose ATTRIBUTE_UNUSED,
|
int sched_verbose ATTRIBUTE_UNUSED,
|
int max_ready ATTRIBUTE_UNUSED)
|
int max_ready ATTRIBUTE_UNUSED)
|
{}
|
{}
|
|
|
static int
|
static int
|
sparc_use_sched_lookahead (void)
|
sparc_use_sched_lookahead (void)
|
{
|
{
|
if (sparc_cpu == PROCESSOR_NIAGARA
|
if (sparc_cpu == PROCESSOR_NIAGARA
|
|| sparc_cpu == PROCESSOR_NIAGARA2)
|
|| sparc_cpu == PROCESSOR_NIAGARA2)
|
return 0;
|
return 0;
|
if (sparc_cpu == PROCESSOR_ULTRASPARC
|
if (sparc_cpu == PROCESSOR_ULTRASPARC
|
|| sparc_cpu == PROCESSOR_ULTRASPARC3)
|
|| sparc_cpu == PROCESSOR_ULTRASPARC3)
|
return 4;
|
return 4;
|
if ((1 << sparc_cpu) &
|
if ((1 << sparc_cpu) &
|
((1 << PROCESSOR_SUPERSPARC) | (1 << PROCESSOR_HYPERSPARC) |
|
((1 << PROCESSOR_SUPERSPARC) | (1 << PROCESSOR_HYPERSPARC) |
|
(1 << PROCESSOR_SPARCLITE86X)))
|
(1 << PROCESSOR_SPARCLITE86X)))
|
return 3;
|
return 3;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
static int
|
static int
|
sparc_issue_rate (void)
|
sparc_issue_rate (void)
|
{
|
{
|
switch (sparc_cpu)
|
switch (sparc_cpu)
|
{
|
{
|
case PROCESSOR_NIAGARA:
|
case PROCESSOR_NIAGARA:
|
case PROCESSOR_NIAGARA2:
|
case PROCESSOR_NIAGARA2:
|
default:
|
default:
|
return 1;
|
return 1;
|
case PROCESSOR_V9:
|
case PROCESSOR_V9:
|
/* Assume V9 processors are capable of at least dual-issue. */
|
/* Assume V9 processors are capable of at least dual-issue. */
|
return 2;
|
return 2;
|
case PROCESSOR_SUPERSPARC:
|
case PROCESSOR_SUPERSPARC:
|
return 3;
|
return 3;
|
case PROCESSOR_HYPERSPARC:
|
case PROCESSOR_HYPERSPARC:
|
case PROCESSOR_SPARCLITE86X:
|
case PROCESSOR_SPARCLITE86X:
|
return 2;
|
return 2;
|
case PROCESSOR_ULTRASPARC:
|
case PROCESSOR_ULTRASPARC:
|
case PROCESSOR_ULTRASPARC3:
|
case PROCESSOR_ULTRASPARC3:
|
return 4;
|
return 4;
|
}
|
}
|
}
|
}
|
|
|
static int
|
static int
|
set_extends (rtx insn)
|
set_extends (rtx insn)
|
{
|
{
|
register rtx pat = PATTERN (insn);
|
register rtx pat = PATTERN (insn);
|
|
|
switch (GET_CODE (SET_SRC (pat)))
|
switch (GET_CODE (SET_SRC (pat)))
|
{
|
{
|
/* Load and some shift instructions zero extend. */
|
/* Load and some shift instructions zero extend. */
|
case MEM:
|
case MEM:
|
case ZERO_EXTEND:
|
case ZERO_EXTEND:
|
/* sethi clears the high bits */
|
/* sethi clears the high bits */
|
case HIGH:
|
case HIGH:
|
/* LO_SUM is used with sethi. sethi cleared the high
|
/* LO_SUM is used with sethi. sethi cleared the high
|
bits and the values used with lo_sum are positive */
|
bits and the values used with lo_sum are positive */
|
case LO_SUM:
|
case LO_SUM:
|
/* Store flag stores 0 or 1 */
|
/* Store flag stores 0 or 1 */
|
case LT: case LTU:
|
case LT: case LTU:
|
case GT: case GTU:
|
case GT: case GTU:
|
case LE: case LEU:
|
case LE: case LEU:
|
case GE: case GEU:
|
case GE: case GEU:
|
case EQ:
|
case EQ:
|
case NE:
|
case NE:
|
return 1;
|
return 1;
|
case AND:
|
case AND:
|
{
|
{
|
rtx op0 = XEXP (SET_SRC (pat), 0);
|
rtx op0 = XEXP (SET_SRC (pat), 0);
|
rtx op1 = XEXP (SET_SRC (pat), 1);
|
rtx op1 = XEXP (SET_SRC (pat), 1);
|
if (GET_CODE (op1) == CONST_INT)
|
if (GET_CODE (op1) == CONST_INT)
|
return INTVAL (op1) >= 0;
|
return INTVAL (op1) >= 0;
|
if (GET_CODE (op0) != REG)
|
if (GET_CODE (op0) != REG)
|
return 0;
|
return 0;
|
if (sparc_check_64 (op0, insn) == 1)
|
if (sparc_check_64 (op0, insn) == 1)
|
return 1;
|
return 1;
|
return (GET_CODE (op1) == REG && sparc_check_64 (op1, insn) == 1);
|
return (GET_CODE (op1) == REG && sparc_check_64 (op1, insn) == 1);
|
}
|
}
|
case IOR:
|
case IOR:
|
case XOR:
|
case XOR:
|
{
|
{
|
rtx op0 = XEXP (SET_SRC (pat), 0);
|
rtx op0 = XEXP (SET_SRC (pat), 0);
|
rtx op1 = XEXP (SET_SRC (pat), 1);
|
rtx op1 = XEXP (SET_SRC (pat), 1);
|
if (GET_CODE (op0) != REG || sparc_check_64 (op0, insn) <= 0)
|
if (GET_CODE (op0) != REG || sparc_check_64 (op0, insn) <= 0)
|
return 0;
|
return 0;
|
if (GET_CODE (op1) == CONST_INT)
|
if (GET_CODE (op1) == CONST_INT)
|
return INTVAL (op1) >= 0;
|
return INTVAL (op1) >= 0;
|
return (GET_CODE (op1) == REG && sparc_check_64 (op1, insn) == 1);
|
return (GET_CODE (op1) == REG && sparc_check_64 (op1, insn) == 1);
|
}
|
}
|
case LSHIFTRT:
|
case LSHIFTRT:
|
return GET_MODE (SET_SRC (pat)) == SImode;
|
return GET_MODE (SET_SRC (pat)) == SImode;
|
/* Positive integers leave the high bits zero. */
|
/* Positive integers leave the high bits zero. */
|
case CONST_DOUBLE:
|
case CONST_DOUBLE:
|
return ! (CONST_DOUBLE_LOW (SET_SRC (pat)) & 0x80000000);
|
return ! (CONST_DOUBLE_LOW (SET_SRC (pat)) & 0x80000000);
|
case CONST_INT:
|
case CONST_INT:
|
return ! (INTVAL (SET_SRC (pat)) & 0x80000000);
|
return ! (INTVAL (SET_SRC (pat)) & 0x80000000);
|
case ASHIFTRT:
|
case ASHIFTRT:
|
case SIGN_EXTEND:
|
case SIGN_EXTEND:
|
return - (GET_MODE (SET_SRC (pat)) == SImode);
|
return - (GET_MODE (SET_SRC (pat)) == SImode);
|
case REG:
|
case REG:
|
return sparc_check_64 (SET_SRC (pat), insn);
|
return sparc_check_64 (SET_SRC (pat), insn);
|
default:
|
default:
|
return 0;
|
return 0;
|
}
|
}
|
}
|
}
|
|
|
/* We _ought_ to have only one kind per function, but... */
|
/* We _ought_ to have only one kind per function, but... */
|
static GTY(()) rtx sparc_addr_diff_list;
|
static GTY(()) rtx sparc_addr_diff_list;
|
static GTY(()) rtx sparc_addr_list;
|
static GTY(()) rtx sparc_addr_list;
|
|
|
void
|
void
|
sparc_defer_case_vector (rtx lab, rtx vec, int diff)
|
sparc_defer_case_vector (rtx lab, rtx vec, int diff)
|
{
|
{
|
vec = gen_rtx_EXPR_LIST (VOIDmode, lab, vec);
|
vec = gen_rtx_EXPR_LIST (VOIDmode, lab, vec);
|
if (diff)
|
if (diff)
|
sparc_addr_diff_list
|
sparc_addr_diff_list
|
= gen_rtx_EXPR_LIST (VOIDmode, vec, sparc_addr_diff_list);
|
= gen_rtx_EXPR_LIST (VOIDmode, vec, sparc_addr_diff_list);
|
else
|
else
|
sparc_addr_list = gen_rtx_EXPR_LIST (VOIDmode, vec, sparc_addr_list);
|
sparc_addr_list = gen_rtx_EXPR_LIST (VOIDmode, vec, sparc_addr_list);
|
}
|
}
|
|
|
static void
|
static void
|
sparc_output_addr_vec (rtx vec)
|
sparc_output_addr_vec (rtx vec)
|
{
|
{
|
rtx lab = XEXP (vec, 0), body = XEXP (vec, 1);
|
rtx lab = XEXP (vec, 0), body = XEXP (vec, 1);
|
int idx, vlen = XVECLEN (body, 0);
|
int idx, vlen = XVECLEN (body, 0);
|
|
|
#ifdef ASM_OUTPUT_ADDR_VEC_START
|
#ifdef ASM_OUTPUT_ADDR_VEC_START
|
ASM_OUTPUT_ADDR_VEC_START (asm_out_file);
|
ASM_OUTPUT_ADDR_VEC_START (asm_out_file);
|
#endif
|
#endif
|
|
|
#ifdef ASM_OUTPUT_CASE_LABEL
|
#ifdef ASM_OUTPUT_CASE_LABEL
|
ASM_OUTPUT_CASE_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (lab),
|
ASM_OUTPUT_CASE_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (lab),
|
NEXT_INSN (lab));
|
NEXT_INSN (lab));
|
#else
|
#else
|
(*targetm.asm_out.internal_label) (asm_out_file, "L", CODE_LABEL_NUMBER (lab));
|
(*targetm.asm_out.internal_label) (asm_out_file, "L", CODE_LABEL_NUMBER (lab));
|
#endif
|
#endif
|
|
|
for (idx = 0; idx < vlen; idx++)
|
for (idx = 0; idx < vlen; idx++)
|
{
|
{
|
ASM_OUTPUT_ADDR_VEC_ELT
|
ASM_OUTPUT_ADDR_VEC_ELT
|
(asm_out_file, CODE_LABEL_NUMBER (XEXP (XVECEXP (body, 0, idx), 0)));
|
(asm_out_file, CODE_LABEL_NUMBER (XEXP (XVECEXP (body, 0, idx), 0)));
|
}
|
}
|
|
|
#ifdef ASM_OUTPUT_ADDR_VEC_END
|
#ifdef ASM_OUTPUT_ADDR_VEC_END
|
ASM_OUTPUT_ADDR_VEC_END (asm_out_file);
|
ASM_OUTPUT_ADDR_VEC_END (asm_out_file);
|
#endif
|
#endif
|
}
|
}
|
|
|
static void
|
static void
|
sparc_output_addr_diff_vec (rtx vec)
|
sparc_output_addr_diff_vec (rtx vec)
|
{
|
{
|
rtx lab = XEXP (vec, 0), body = XEXP (vec, 1);
|
rtx lab = XEXP (vec, 0), body = XEXP (vec, 1);
|
rtx base = XEXP (XEXP (body, 0), 0);
|
rtx base = XEXP (XEXP (body, 0), 0);
|
int idx, vlen = XVECLEN (body, 1);
|
int idx, vlen = XVECLEN (body, 1);
|
|
|
#ifdef ASM_OUTPUT_ADDR_VEC_START
|
#ifdef ASM_OUTPUT_ADDR_VEC_START
|
ASM_OUTPUT_ADDR_VEC_START (asm_out_file);
|
ASM_OUTPUT_ADDR_VEC_START (asm_out_file);
|
#endif
|
#endif
|
|
|
#ifdef ASM_OUTPUT_CASE_LABEL
|
#ifdef ASM_OUTPUT_CASE_LABEL
|
ASM_OUTPUT_CASE_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (lab),
|
ASM_OUTPUT_CASE_LABEL (asm_out_file, "L", CODE_LABEL_NUMBER (lab),
|
NEXT_INSN (lab));
|
NEXT_INSN (lab));
|
#else
|
#else
|
(*targetm.asm_out.internal_label) (asm_out_file, "L", CODE_LABEL_NUMBER (lab));
|
(*targetm.asm_out.internal_label) (asm_out_file, "L", CODE_LABEL_NUMBER (lab));
|
#endif
|
#endif
|
|
|
for (idx = 0; idx < vlen; idx++)
|
for (idx = 0; idx < vlen; idx++)
|
{
|
{
|
ASM_OUTPUT_ADDR_DIFF_ELT
|
ASM_OUTPUT_ADDR_DIFF_ELT
|
(asm_out_file,
|
(asm_out_file,
|
body,
|
body,
|
CODE_LABEL_NUMBER (XEXP (XVECEXP (body, 1, idx), 0)),
|
CODE_LABEL_NUMBER (XEXP (XVECEXP (body, 1, idx), 0)),
|
CODE_LABEL_NUMBER (base));
|
CODE_LABEL_NUMBER (base));
|
}
|
}
|
|
|
#ifdef ASM_OUTPUT_ADDR_VEC_END
|
#ifdef ASM_OUTPUT_ADDR_VEC_END
|
ASM_OUTPUT_ADDR_VEC_END (asm_out_file);
|
ASM_OUTPUT_ADDR_VEC_END (asm_out_file);
|
#endif
|
#endif
|
}
|
}
|
|
|
static void
|
static void
|
sparc_output_deferred_case_vectors (void)
|
sparc_output_deferred_case_vectors (void)
|
{
|
{
|
rtx t;
|
rtx t;
|
int align;
|
int align;
|
|
|
if (sparc_addr_list == NULL_RTX
|
if (sparc_addr_list == NULL_RTX
|
&& sparc_addr_diff_list == NULL_RTX)
|
&& sparc_addr_diff_list == NULL_RTX)
|
return;
|
return;
|
|
|
/* Align to cache line in the function's code section. */
|
/* Align to cache line in the function's code section. */
|
switch_to_section (current_function_section ());
|
switch_to_section (current_function_section ());
|
|
|
align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT);
|
align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT);
|
if (align > 0)
|
if (align > 0)
|
ASM_OUTPUT_ALIGN (asm_out_file, align);
|
ASM_OUTPUT_ALIGN (asm_out_file, align);
|
|
|
for (t = sparc_addr_list; t ; t = XEXP (t, 1))
|
for (t = sparc_addr_list; t ; t = XEXP (t, 1))
|
sparc_output_addr_vec (XEXP (t, 0));
|
sparc_output_addr_vec (XEXP (t, 0));
|
for (t = sparc_addr_diff_list; t ; t = XEXP (t, 1))
|
for (t = sparc_addr_diff_list; t ; t = XEXP (t, 1))
|
sparc_output_addr_diff_vec (XEXP (t, 0));
|
sparc_output_addr_diff_vec (XEXP (t, 0));
|
|
|
sparc_addr_list = sparc_addr_diff_list = NULL_RTX;
|
sparc_addr_list = sparc_addr_diff_list = NULL_RTX;
|
}
|
}
|
|
|
/* Return 0 if the high 32 bits of X (the low word of X, if DImode) are
|
/* Return 0 if the high 32 bits of X (the low word of X, if DImode) are
|
unknown. Return 1 if the high bits are zero, -1 if the register is
|
unknown. Return 1 if the high bits are zero, -1 if the register is
|
sign extended. */
|
sign extended. */
|
int
|
int
|
sparc_check_64 (rtx x, rtx insn)
|
sparc_check_64 (rtx x, rtx insn)
|
{
|
{
|
/* If a register is set only once it is safe to ignore insns this
|
/* If a register is set only once it is safe to ignore insns this
|
code does not know how to handle. The loop will either recognize
|
code does not know how to handle. The loop will either recognize
|
the single set and return the correct value or fail to recognize
|
the single set and return the correct value or fail to recognize
|
it and return 0. */
|
it and return 0. */
|
int set_once = 0;
|
int set_once = 0;
|
rtx y = x;
|
rtx y = x;
|
|
|
gcc_assert (GET_CODE (x) == REG);
|
gcc_assert (GET_CODE (x) == REG);
|
|
|
if (GET_MODE (x) == DImode)
|
if (GET_MODE (x) == DImode)
|
y = gen_rtx_REG (SImode, REGNO (x) + WORDS_BIG_ENDIAN);
|
y = gen_rtx_REG (SImode, REGNO (x) + WORDS_BIG_ENDIAN);
|
|
|
if (flag_expensive_optimizations
|
if (flag_expensive_optimizations
|
&& df && DF_REG_DEF_COUNT (REGNO (y)) == 1)
|
&& df && DF_REG_DEF_COUNT (REGNO (y)) == 1)
|
set_once = 1;
|
set_once = 1;
|
|
|
if (insn == 0)
|
if (insn == 0)
|
{
|
{
|
if (set_once)
|
if (set_once)
|
insn = get_last_insn_anywhere ();
|
insn = get_last_insn_anywhere ();
|
else
|
else
|
return 0;
|
return 0;
|
}
|
}
|
|
|
while ((insn = PREV_INSN (insn)))
|
while ((insn = PREV_INSN (insn)))
|
{
|
{
|
switch (GET_CODE (insn))
|
switch (GET_CODE (insn))
|
{
|
{
|
case JUMP_INSN:
|
case JUMP_INSN:
|
case NOTE:
|
case NOTE:
|
break;
|
break;
|
case CODE_LABEL:
|
case CODE_LABEL:
|
case CALL_INSN:
|
case CALL_INSN:
|
default:
|
default:
|
if (! set_once)
|
if (! set_once)
|
return 0;
|
return 0;
|
break;
|
break;
|
case INSN:
|
case INSN:
|
{
|
{
|
rtx pat = PATTERN (insn);
|
rtx pat = PATTERN (insn);
|
if (GET_CODE (pat) != SET)
|
if (GET_CODE (pat) != SET)
|
return 0;
|
return 0;
|
if (rtx_equal_p (x, SET_DEST (pat)))
|
if (rtx_equal_p (x, SET_DEST (pat)))
|
return set_extends (insn);
|
return set_extends (insn);
|
if (y && rtx_equal_p (y, SET_DEST (pat)))
|
if (y && rtx_equal_p (y, SET_DEST (pat)))
|
return set_extends (insn);
|
return set_extends (insn);
|
if (reg_overlap_mentioned_p (SET_DEST (pat), y))
|
if (reg_overlap_mentioned_p (SET_DEST (pat), y))
|
return 0;
|
return 0;
|
}
|
}
|
}
|
}
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Returns assembly code to perform a DImode shift using
|
/* Returns assembly code to perform a DImode shift using
|
a 64-bit global or out register on SPARC-V8+. */
|
a 64-bit global or out register on SPARC-V8+. */
|
const char *
|
const char *
|
output_v8plus_shift (rtx *operands, rtx insn, const char *opcode)
|
output_v8plus_shift (rtx *operands, rtx insn, const char *opcode)
|
{
|
{
|
static char asm_code[60];
|
static char asm_code[60];
|
|
|
/* The scratch register is only required when the destination
|
/* The scratch register is only required when the destination
|
register is not a 64-bit global or out register. */
|
register is not a 64-bit global or out register. */
|
if (which_alternative != 2)
|
if (which_alternative != 2)
|
operands[3] = operands[0];
|
operands[3] = operands[0];
|
|
|
/* We can only shift by constants <= 63. */
|
/* We can only shift by constants <= 63. */
|
if (GET_CODE (operands[2]) == CONST_INT)
|
if (GET_CODE (operands[2]) == CONST_INT)
|
operands[2] = GEN_INT (INTVAL (operands[2]) & 0x3f);
|
operands[2] = GEN_INT (INTVAL (operands[2]) & 0x3f);
|
|
|
if (GET_CODE (operands[1]) == CONST_INT)
|
if (GET_CODE (operands[1]) == CONST_INT)
|
{
|
{
|
output_asm_insn ("mov\t%1, %3", operands);
|
output_asm_insn ("mov\t%1, %3", operands);
|
}
|
}
|
else
|
else
|
{
|
{
|
output_asm_insn ("sllx\t%H1, 32, %3", operands);
|
output_asm_insn ("sllx\t%H1, 32, %3", operands);
|
if (sparc_check_64 (operands[1], insn) <= 0)
|
if (sparc_check_64 (operands[1], insn) <= 0)
|
output_asm_insn ("srl\t%L1, 0, %L1", operands);
|
output_asm_insn ("srl\t%L1, 0, %L1", operands);
|
output_asm_insn ("or\t%L1, %3, %3", operands);
|
output_asm_insn ("or\t%L1, %3, %3", operands);
|
}
|
}
|
|
|
strcpy(asm_code, opcode);
|
strcpy(asm_code, opcode);
|
|
|
if (which_alternative != 2)
|
if (which_alternative != 2)
|
return strcat (asm_code, "\t%0, %2, %L0\n\tsrlx\t%L0, 32, %H0");
|
return strcat (asm_code, "\t%0, %2, %L0\n\tsrlx\t%L0, 32, %H0");
|
else
|
else
|
return strcat (asm_code, "\t%3, %2, %3\n\tsrlx\t%3, 32, %H0\n\tmov\t%3, %L0");
|
return strcat (asm_code, "\t%3, %2, %3\n\tsrlx\t%3, 32, %H0\n\tmov\t%3, %L0");
|
}
|
}
|
|
|
/* Output rtl to increment the profiler label LABELNO
|
/* Output rtl to increment the profiler label LABELNO
|
for profiling a function entry. */
|
for profiling a function entry. */
|
|
|
void
|
void
|
sparc_profile_hook (int labelno)
|
sparc_profile_hook (int labelno)
|
{
|
{
|
char buf[32];
|
char buf[32];
|
rtx lab, fun;
|
rtx lab, fun;
|
|
|
fun = gen_rtx_SYMBOL_REF (Pmode, MCOUNT_FUNCTION);
|
fun = gen_rtx_SYMBOL_REF (Pmode, MCOUNT_FUNCTION);
|
if (NO_PROFILE_COUNTERS)
|
if (NO_PROFILE_COUNTERS)
|
{
|
{
|
emit_library_call (fun, LCT_NORMAL, VOIDmode, 0);
|
emit_library_call (fun, LCT_NORMAL, VOIDmode, 0);
|
}
|
}
|
else
|
else
|
{
|
{
|
ASM_GENERATE_INTERNAL_LABEL (buf, "LP", labelno);
|
ASM_GENERATE_INTERNAL_LABEL (buf, "LP", labelno);
|
lab = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf));
|
lab = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (buf));
|
emit_library_call (fun, LCT_NORMAL, VOIDmode, 1, lab, Pmode);
|
emit_library_call (fun, LCT_NORMAL, VOIDmode, 1, lab, Pmode);
|
}
|
}
|
}
|
}
|
|
|
/* Solaris implementation of TARGET_ASM_NAMED_SECTION. */
|
/* Solaris implementation of TARGET_ASM_NAMED_SECTION. */
|
|
|
static void
|
static void
|
sparc_solaris_elf_asm_named_section (const char *name, unsigned int flags,
|
sparc_solaris_elf_asm_named_section (const char *name, unsigned int flags,
|
tree decl ATTRIBUTE_UNUSED)
|
tree decl ATTRIBUTE_UNUSED)
|
{
|
{
|
fprintf (asm_out_file, "\t.section\t\"%s\"", name);
|
fprintf (asm_out_file, "\t.section\t\"%s\"", name);
|
|
|
if (!(flags & SECTION_DEBUG))
|
if (!(flags & SECTION_DEBUG))
|
fputs (",#alloc", asm_out_file);
|
fputs (",#alloc", asm_out_file);
|
if (flags & SECTION_WRITE)
|
if (flags & SECTION_WRITE)
|
fputs (",#write", asm_out_file);
|
fputs (",#write", asm_out_file);
|
if (flags & SECTION_TLS)
|
if (flags & SECTION_TLS)
|
fputs (",#tls", asm_out_file);
|
fputs (",#tls", asm_out_file);
|
if (flags & SECTION_CODE)
|
if (flags & SECTION_CODE)
|
fputs (",#execinstr", asm_out_file);
|
fputs (",#execinstr", asm_out_file);
|
|
|
/* ??? Handle SECTION_BSS. */
|
/* ??? Handle SECTION_BSS. */
|
|
|
fputc ('\n', asm_out_file);
|
fputc ('\n', asm_out_file);
|
}
|
}
|
|
|
/* We do not allow indirect calls to be optimized into sibling calls.
|
/* We do not allow indirect calls to be optimized into sibling calls.
|
|
|
We cannot use sibling calls when delayed branches are disabled
|
We cannot use sibling calls when delayed branches are disabled
|
because they will likely require the call delay slot to be filled.
|
because they will likely require the call delay slot to be filled.
|
|
|
Also, on SPARC 32-bit we cannot emit a sibling call when the
|
Also, on SPARC 32-bit we cannot emit a sibling call when the
|
current function returns a structure. This is because the "unimp
|
current function returns a structure. This is because the "unimp
|
after call" convention would cause the callee to return to the
|
after call" convention would cause the callee to return to the
|
wrong place. The generic code already disallows cases where the
|
wrong place. The generic code already disallows cases where the
|
function being called returns a structure.
|
function being called returns a structure.
|
|
|
It may seem strange how this last case could occur. Usually there
|
It may seem strange how this last case could occur. Usually there
|
is code after the call which jumps to epilogue code which dumps the
|
is code after the call which jumps to epilogue code which dumps the
|
return value into the struct return area. That ought to invalidate
|
return value into the struct return area. That ought to invalidate
|
the sibling call right? Well, in the C++ case we can end up passing
|
the sibling call right? Well, in the C++ case we can end up passing
|
the pointer to the struct return area to a constructor (which returns
|
the pointer to the struct return area to a constructor (which returns
|
void) and then nothing else happens. Such a sibling call would look
|
void) and then nothing else happens. Such a sibling call would look
|
valid without the added check here.
|
valid without the added check here.
|
|
|
VxWorks PIC PLT entries require the global pointer to be initialized
|
VxWorks PIC PLT entries require the global pointer to be initialized
|
on entry. We therefore can't emit sibling calls to them. */
|
on entry. We therefore can't emit sibling calls to them. */
|
static bool
|
static bool
|
sparc_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
|
sparc_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
|
{
|
{
|
return (decl
|
return (decl
|
&& flag_delayed_branch
|
&& flag_delayed_branch
|
&& (TARGET_ARCH64 || ! cfun->returns_struct)
|
&& (TARGET_ARCH64 || ! cfun->returns_struct)
|
&& !(TARGET_VXWORKS_RTP
|
&& !(TARGET_VXWORKS_RTP
|
&& flag_pic
|
&& flag_pic
|
&& !targetm.binds_local_p (decl)));
|
&& !targetm.binds_local_p (decl)));
|
}
|
}
|
|
|
/* libfunc renaming. */
|
/* libfunc renaming. */
|
#include "config/gofast.h"
|
#include "config/gofast.h"
|
|
|
static void
|
static void
|
sparc_init_libfuncs (void)
|
sparc_init_libfuncs (void)
|
{
|
{
|
if (TARGET_ARCH32)
|
if (TARGET_ARCH32)
|
{
|
{
|
/* Use the subroutines that Sun's library provides for integer
|
/* Use the subroutines that Sun's library provides for integer
|
multiply and divide. The `*' prevents an underscore from
|
multiply and divide. The `*' prevents an underscore from
|
being prepended by the compiler. .umul is a little faster
|
being prepended by the compiler. .umul is a little faster
|
than .mul. */
|
than .mul. */
|
set_optab_libfunc (smul_optab, SImode, "*.umul");
|
set_optab_libfunc (smul_optab, SImode, "*.umul");
|
set_optab_libfunc (sdiv_optab, SImode, "*.div");
|
set_optab_libfunc (sdiv_optab, SImode, "*.div");
|
set_optab_libfunc (udiv_optab, SImode, "*.udiv");
|
set_optab_libfunc (udiv_optab, SImode, "*.udiv");
|
set_optab_libfunc (smod_optab, SImode, "*.rem");
|
set_optab_libfunc (smod_optab, SImode, "*.rem");
|
set_optab_libfunc (umod_optab, SImode, "*.urem");
|
set_optab_libfunc (umod_optab, SImode, "*.urem");
|
|
|
/* TFmode arithmetic. These names are part of the SPARC 32bit ABI. */
|
/* TFmode arithmetic. These names are part of the SPARC 32bit ABI. */
|
set_optab_libfunc (add_optab, TFmode, "_Q_add");
|
set_optab_libfunc (add_optab, TFmode, "_Q_add");
|
set_optab_libfunc (sub_optab, TFmode, "_Q_sub");
|
set_optab_libfunc (sub_optab, TFmode, "_Q_sub");
|
set_optab_libfunc (neg_optab, TFmode, "_Q_neg");
|
set_optab_libfunc (neg_optab, TFmode, "_Q_neg");
|
set_optab_libfunc (smul_optab, TFmode, "_Q_mul");
|
set_optab_libfunc (smul_optab, TFmode, "_Q_mul");
|
set_optab_libfunc (sdiv_optab, TFmode, "_Q_div");
|
set_optab_libfunc (sdiv_optab, TFmode, "_Q_div");
|
|
|
/* We can define the TFmode sqrt optab only if TARGET_FPU. This
|
/* We can define the TFmode sqrt optab only if TARGET_FPU. This
|
is because with soft-float, the SFmode and DFmode sqrt
|
is because with soft-float, the SFmode and DFmode sqrt
|
instructions will be absent, and the compiler will notice and
|
instructions will be absent, and the compiler will notice and
|
try to use the TFmode sqrt instruction for calls to the
|
try to use the TFmode sqrt instruction for calls to the
|
builtin function sqrt, but this fails. */
|
builtin function sqrt, but this fails. */
|
if (TARGET_FPU)
|
if (TARGET_FPU)
|
set_optab_libfunc (sqrt_optab, TFmode, "_Q_sqrt");
|
set_optab_libfunc (sqrt_optab, TFmode, "_Q_sqrt");
|
|
|
set_optab_libfunc (eq_optab, TFmode, "_Q_feq");
|
set_optab_libfunc (eq_optab, TFmode, "_Q_feq");
|
set_optab_libfunc (ne_optab, TFmode, "_Q_fne");
|
set_optab_libfunc (ne_optab, TFmode, "_Q_fne");
|
set_optab_libfunc (gt_optab, TFmode, "_Q_fgt");
|
set_optab_libfunc (gt_optab, TFmode, "_Q_fgt");
|
set_optab_libfunc (ge_optab, TFmode, "_Q_fge");
|
set_optab_libfunc (ge_optab, TFmode, "_Q_fge");
|
set_optab_libfunc (lt_optab, TFmode, "_Q_flt");
|
set_optab_libfunc (lt_optab, TFmode, "_Q_flt");
|
set_optab_libfunc (le_optab, TFmode, "_Q_fle");
|
set_optab_libfunc (le_optab, TFmode, "_Q_fle");
|
|
|
set_conv_libfunc (sext_optab, TFmode, SFmode, "_Q_stoq");
|
set_conv_libfunc (sext_optab, TFmode, SFmode, "_Q_stoq");
|
set_conv_libfunc (sext_optab, TFmode, DFmode, "_Q_dtoq");
|
set_conv_libfunc (sext_optab, TFmode, DFmode, "_Q_dtoq");
|
set_conv_libfunc (trunc_optab, SFmode, TFmode, "_Q_qtos");
|
set_conv_libfunc (trunc_optab, SFmode, TFmode, "_Q_qtos");
|
set_conv_libfunc (trunc_optab, DFmode, TFmode, "_Q_qtod");
|
set_conv_libfunc (trunc_optab, DFmode, TFmode, "_Q_qtod");
|
|
|
set_conv_libfunc (sfix_optab, SImode, TFmode, "_Q_qtoi");
|
set_conv_libfunc (sfix_optab, SImode, TFmode, "_Q_qtoi");
|
set_conv_libfunc (ufix_optab, SImode, TFmode, "_Q_qtou");
|
set_conv_libfunc (ufix_optab, SImode, TFmode, "_Q_qtou");
|
set_conv_libfunc (sfloat_optab, TFmode, SImode, "_Q_itoq");
|
set_conv_libfunc (sfloat_optab, TFmode, SImode, "_Q_itoq");
|
set_conv_libfunc (ufloat_optab, TFmode, SImode, "_Q_utoq");
|
set_conv_libfunc (ufloat_optab, TFmode, SImode, "_Q_utoq");
|
|
|
if (DITF_CONVERSION_LIBFUNCS)
|
if (DITF_CONVERSION_LIBFUNCS)
|
{
|
{
|
set_conv_libfunc (sfix_optab, DImode, TFmode, "_Q_qtoll");
|
set_conv_libfunc (sfix_optab, DImode, TFmode, "_Q_qtoll");
|
set_conv_libfunc (ufix_optab, DImode, TFmode, "_Q_qtoull");
|
set_conv_libfunc (ufix_optab, DImode, TFmode, "_Q_qtoull");
|
set_conv_libfunc (sfloat_optab, TFmode, DImode, "_Q_lltoq");
|
set_conv_libfunc (sfloat_optab, TFmode, DImode, "_Q_lltoq");
|
set_conv_libfunc (ufloat_optab, TFmode, DImode, "_Q_ulltoq");
|
set_conv_libfunc (ufloat_optab, TFmode, DImode, "_Q_ulltoq");
|
}
|
}
|
|
|
if (SUN_CONVERSION_LIBFUNCS)
|
if (SUN_CONVERSION_LIBFUNCS)
|
{
|
{
|
set_conv_libfunc (sfix_optab, DImode, SFmode, "__ftoll");
|
set_conv_libfunc (sfix_optab, DImode, SFmode, "__ftoll");
|
set_conv_libfunc (ufix_optab, DImode, SFmode, "__ftoull");
|
set_conv_libfunc (ufix_optab, DImode, SFmode, "__ftoull");
|
set_conv_libfunc (sfix_optab, DImode, DFmode, "__dtoll");
|
set_conv_libfunc (sfix_optab, DImode, DFmode, "__dtoll");
|
set_conv_libfunc (ufix_optab, DImode, DFmode, "__dtoull");
|
set_conv_libfunc (ufix_optab, DImode, DFmode, "__dtoull");
|
}
|
}
|
}
|
}
|
if (TARGET_ARCH64)
|
if (TARGET_ARCH64)
|
{
|
{
|
/* In the SPARC 64bit ABI, SImode multiply and divide functions
|
/* In the SPARC 64bit ABI, SImode multiply and divide functions
|
do not exist in the library. Make sure the compiler does not
|
do not exist in the library. Make sure the compiler does not
|
emit calls to them by accident. (It should always use the
|
emit calls to them by accident. (It should always use the
|
hardware instructions.) */
|
hardware instructions.) */
|
set_optab_libfunc (smul_optab, SImode, 0);
|
set_optab_libfunc (smul_optab, SImode, 0);
|
set_optab_libfunc (sdiv_optab, SImode, 0);
|
set_optab_libfunc (sdiv_optab, SImode, 0);
|
set_optab_libfunc (udiv_optab, SImode, 0);
|
set_optab_libfunc (udiv_optab, SImode, 0);
|
set_optab_libfunc (smod_optab, SImode, 0);
|
set_optab_libfunc (smod_optab, SImode, 0);
|
set_optab_libfunc (umod_optab, SImode, 0);
|
set_optab_libfunc (umod_optab, SImode, 0);
|
|
|
if (SUN_INTEGER_MULTIPLY_64)
|
if (SUN_INTEGER_MULTIPLY_64)
|
{
|
{
|
set_optab_libfunc (smul_optab, DImode, "__mul64");
|
set_optab_libfunc (smul_optab, DImode, "__mul64");
|
set_optab_libfunc (sdiv_optab, DImode, "__div64");
|
set_optab_libfunc (sdiv_optab, DImode, "__div64");
|
set_optab_libfunc (udiv_optab, DImode, "__udiv64");
|
set_optab_libfunc (udiv_optab, DImode, "__udiv64");
|
set_optab_libfunc (smod_optab, DImode, "__rem64");
|
set_optab_libfunc (smod_optab, DImode, "__rem64");
|
set_optab_libfunc (umod_optab, DImode, "__urem64");
|
set_optab_libfunc (umod_optab, DImode, "__urem64");
|
}
|
}
|
|
|
if (SUN_CONVERSION_LIBFUNCS)
|
if (SUN_CONVERSION_LIBFUNCS)
|
{
|
{
|
set_conv_libfunc (sfix_optab, DImode, SFmode, "__ftol");
|
set_conv_libfunc (sfix_optab, DImode, SFmode, "__ftol");
|
set_conv_libfunc (ufix_optab, DImode, SFmode, "__ftoul");
|
set_conv_libfunc (ufix_optab, DImode, SFmode, "__ftoul");
|
set_conv_libfunc (sfix_optab, DImode, DFmode, "__dtol");
|
set_conv_libfunc (sfix_optab, DImode, DFmode, "__dtol");
|
set_conv_libfunc (ufix_optab, DImode, DFmode, "__dtoul");
|
set_conv_libfunc (ufix_optab, DImode, DFmode, "__dtoul");
|
}
|
}
|
}
|
}
|
|
|
gofast_maybe_init_libfuncs ();
|
gofast_maybe_init_libfuncs ();
|
}
|
}
|
|
|
#define def_builtin(NAME, CODE, TYPE) \
|
#define def_builtin(NAME, CODE, TYPE) \
|
add_builtin_function((NAME), (TYPE), (CODE), BUILT_IN_MD, NULL, \
|
add_builtin_function((NAME), (TYPE), (CODE), BUILT_IN_MD, NULL, \
|
NULL_TREE)
|
NULL_TREE)
|
|
|
/* Implement the TARGET_INIT_BUILTINS target hook.
|
/* Implement the TARGET_INIT_BUILTINS target hook.
|
Create builtin functions for special SPARC instructions. */
|
Create builtin functions for special SPARC instructions. */
|
|
|
static void
|
static void
|
sparc_init_builtins (void)
|
sparc_init_builtins (void)
|
{
|
{
|
if (TARGET_VIS)
|
if (TARGET_VIS)
|
sparc_vis_init_builtins ();
|
sparc_vis_init_builtins ();
|
}
|
}
|
|
|
/* Create builtin functions for VIS 1.0 instructions. */
|
/* Create builtin functions for VIS 1.0 instructions. */
|
|
|
static void
|
static void
|
sparc_vis_init_builtins (void)
|
sparc_vis_init_builtins (void)
|
{
|
{
|
tree v4qi = build_vector_type (unsigned_intQI_type_node, 4);
|
tree v4qi = build_vector_type (unsigned_intQI_type_node, 4);
|
tree v8qi = build_vector_type (unsigned_intQI_type_node, 8);
|
tree v8qi = build_vector_type (unsigned_intQI_type_node, 8);
|
tree v4hi = build_vector_type (intHI_type_node, 4);
|
tree v4hi = build_vector_type (intHI_type_node, 4);
|
tree v2hi = build_vector_type (intHI_type_node, 2);
|
tree v2hi = build_vector_type (intHI_type_node, 2);
|
tree v2si = build_vector_type (intSI_type_node, 2);
|
tree v2si = build_vector_type (intSI_type_node, 2);
|
|
|
tree v4qi_ftype_v4hi = build_function_type_list (v4qi, v4hi, 0);
|
tree v4qi_ftype_v4hi = build_function_type_list (v4qi, v4hi, 0);
|
tree v8qi_ftype_v2si_v8qi = build_function_type_list (v8qi, v2si, v8qi, 0);
|
tree v8qi_ftype_v2si_v8qi = build_function_type_list (v8qi, v2si, v8qi, 0);
|
tree v2hi_ftype_v2si = build_function_type_list (v2hi, v2si, 0);
|
tree v2hi_ftype_v2si = build_function_type_list (v2hi, v2si, 0);
|
tree v4hi_ftype_v4qi = build_function_type_list (v4hi, v4qi, 0);
|
tree v4hi_ftype_v4qi = build_function_type_list (v4hi, v4qi, 0);
|
tree v8qi_ftype_v4qi_v4qi = build_function_type_list (v8qi, v4qi, v4qi, 0);
|
tree v8qi_ftype_v4qi_v4qi = build_function_type_list (v8qi, v4qi, v4qi, 0);
|
tree v4hi_ftype_v4qi_v4hi = build_function_type_list (v4hi, v4qi, v4hi, 0);
|
tree v4hi_ftype_v4qi_v4hi = build_function_type_list (v4hi, v4qi, v4hi, 0);
|
tree v4hi_ftype_v4qi_v2hi = build_function_type_list (v4hi, v4qi, v2hi, 0);
|
tree v4hi_ftype_v4qi_v2hi = build_function_type_list (v4hi, v4qi, v2hi, 0);
|
tree v2si_ftype_v4qi_v2hi = build_function_type_list (v2si, v4qi, v2hi, 0);
|
tree v2si_ftype_v4qi_v2hi = build_function_type_list (v2si, v4qi, v2hi, 0);
|
tree v4hi_ftype_v8qi_v4hi = build_function_type_list (v4hi, v8qi, v4hi, 0);
|
tree v4hi_ftype_v8qi_v4hi = build_function_type_list (v4hi, v8qi, v4hi, 0);
|
tree v4hi_ftype_v4hi_v4hi = build_function_type_list (v4hi, v4hi, v4hi, 0);
|
tree v4hi_ftype_v4hi_v4hi = build_function_type_list (v4hi, v4hi, v4hi, 0);
|
tree v2si_ftype_v2si_v2si = build_function_type_list (v2si, v2si, v2si, 0);
|
tree v2si_ftype_v2si_v2si = build_function_type_list (v2si, v2si, v2si, 0);
|
tree v8qi_ftype_v8qi_v8qi = build_function_type_list (v8qi, v8qi, v8qi, 0);
|
tree v8qi_ftype_v8qi_v8qi = build_function_type_list (v8qi, v8qi, v8qi, 0);
|
tree di_ftype_v8qi_v8qi_di = build_function_type_list (intDI_type_node,
|
tree di_ftype_v8qi_v8qi_di = build_function_type_list (intDI_type_node,
|
v8qi, v8qi,
|
v8qi, v8qi,
|
intDI_type_node, 0);
|
intDI_type_node, 0);
|
tree di_ftype_di_di = build_function_type_list (intDI_type_node,
|
tree di_ftype_di_di = build_function_type_list (intDI_type_node,
|
intDI_type_node,
|
intDI_type_node,
|
intDI_type_node, 0);
|
intDI_type_node, 0);
|
tree ptr_ftype_ptr_si = build_function_type_list (ptr_type_node,
|
tree ptr_ftype_ptr_si = build_function_type_list (ptr_type_node,
|
ptr_type_node,
|
ptr_type_node,
|
intSI_type_node, 0);
|
intSI_type_node, 0);
|
tree ptr_ftype_ptr_di = build_function_type_list (ptr_type_node,
|
tree ptr_ftype_ptr_di = build_function_type_list (ptr_type_node,
|
ptr_type_node,
|
ptr_type_node,
|
intDI_type_node, 0);
|
intDI_type_node, 0);
|
|
|
/* Packing and expanding vectors. */
|
/* Packing and expanding vectors. */
|
def_builtin ("__builtin_vis_fpack16", CODE_FOR_fpack16_vis, v4qi_ftype_v4hi);
|
def_builtin ("__builtin_vis_fpack16", CODE_FOR_fpack16_vis, v4qi_ftype_v4hi);
|
def_builtin ("__builtin_vis_fpack32", CODE_FOR_fpack32_vis,
|
def_builtin ("__builtin_vis_fpack32", CODE_FOR_fpack32_vis,
|
v8qi_ftype_v2si_v8qi);
|
v8qi_ftype_v2si_v8qi);
|
def_builtin ("__builtin_vis_fpackfix", CODE_FOR_fpackfix_vis,
|
def_builtin ("__builtin_vis_fpackfix", CODE_FOR_fpackfix_vis,
|
v2hi_ftype_v2si);
|
v2hi_ftype_v2si);
|
def_builtin ("__builtin_vis_fexpand", CODE_FOR_fexpand_vis, v4hi_ftype_v4qi);
|
def_builtin ("__builtin_vis_fexpand", CODE_FOR_fexpand_vis, v4hi_ftype_v4qi);
|
def_builtin ("__builtin_vis_fpmerge", CODE_FOR_fpmerge_vis,
|
def_builtin ("__builtin_vis_fpmerge", CODE_FOR_fpmerge_vis,
|
v8qi_ftype_v4qi_v4qi);
|
v8qi_ftype_v4qi_v4qi);
|
|
|
/* Multiplications. */
|
/* Multiplications. */
|
def_builtin ("__builtin_vis_fmul8x16", CODE_FOR_fmul8x16_vis,
|
def_builtin ("__builtin_vis_fmul8x16", CODE_FOR_fmul8x16_vis,
|
v4hi_ftype_v4qi_v4hi);
|
v4hi_ftype_v4qi_v4hi);
|
def_builtin ("__builtin_vis_fmul8x16au", CODE_FOR_fmul8x16au_vis,
|
def_builtin ("__builtin_vis_fmul8x16au", CODE_FOR_fmul8x16au_vis,
|
v4hi_ftype_v4qi_v2hi);
|
v4hi_ftype_v4qi_v2hi);
|
def_builtin ("__builtin_vis_fmul8x16al", CODE_FOR_fmul8x16al_vis,
|
def_builtin ("__builtin_vis_fmul8x16al", CODE_FOR_fmul8x16al_vis,
|
v4hi_ftype_v4qi_v2hi);
|
v4hi_ftype_v4qi_v2hi);
|
def_builtin ("__builtin_vis_fmul8sux16", CODE_FOR_fmul8sux16_vis,
|
def_builtin ("__builtin_vis_fmul8sux16", CODE_FOR_fmul8sux16_vis,
|
v4hi_ftype_v8qi_v4hi);
|
v4hi_ftype_v8qi_v4hi);
|
def_builtin ("__builtin_vis_fmul8ulx16", CODE_FOR_fmul8ulx16_vis,
|
def_builtin ("__builtin_vis_fmul8ulx16", CODE_FOR_fmul8ulx16_vis,
|
v4hi_ftype_v8qi_v4hi);
|
v4hi_ftype_v8qi_v4hi);
|
def_builtin ("__builtin_vis_fmuld8sux16", CODE_FOR_fmuld8sux16_vis,
|
def_builtin ("__builtin_vis_fmuld8sux16", CODE_FOR_fmuld8sux16_vis,
|
v2si_ftype_v4qi_v2hi);
|
v2si_ftype_v4qi_v2hi);
|
def_builtin ("__builtin_vis_fmuld8ulx16", CODE_FOR_fmuld8ulx16_vis,
|
def_builtin ("__builtin_vis_fmuld8ulx16", CODE_FOR_fmuld8ulx16_vis,
|
v2si_ftype_v4qi_v2hi);
|
v2si_ftype_v4qi_v2hi);
|
|
|
/* Data aligning. */
|
/* Data aligning. */
|
def_builtin ("__builtin_vis_faligndatav4hi", CODE_FOR_faligndatav4hi_vis,
|
def_builtin ("__builtin_vis_faligndatav4hi", CODE_FOR_faligndatav4hi_vis,
|
v4hi_ftype_v4hi_v4hi);
|
v4hi_ftype_v4hi_v4hi);
|
def_builtin ("__builtin_vis_faligndatav8qi", CODE_FOR_faligndatav8qi_vis,
|
def_builtin ("__builtin_vis_faligndatav8qi", CODE_FOR_faligndatav8qi_vis,
|
v8qi_ftype_v8qi_v8qi);
|
v8qi_ftype_v8qi_v8qi);
|
def_builtin ("__builtin_vis_faligndatav2si", CODE_FOR_faligndatav2si_vis,
|
def_builtin ("__builtin_vis_faligndatav2si", CODE_FOR_faligndatav2si_vis,
|
v2si_ftype_v2si_v2si);
|
v2si_ftype_v2si_v2si);
|
def_builtin ("__builtin_vis_faligndatadi", CODE_FOR_faligndatadi_vis,
|
def_builtin ("__builtin_vis_faligndatadi", CODE_FOR_faligndatadi_vis,
|
di_ftype_di_di);
|
di_ftype_di_di);
|
if (TARGET_ARCH64)
|
if (TARGET_ARCH64)
|
def_builtin ("__builtin_vis_alignaddr", CODE_FOR_alignaddrdi_vis,
|
def_builtin ("__builtin_vis_alignaddr", CODE_FOR_alignaddrdi_vis,
|
ptr_ftype_ptr_di);
|
ptr_ftype_ptr_di);
|
else
|
else
|
def_builtin ("__builtin_vis_alignaddr", CODE_FOR_alignaddrsi_vis,
|
def_builtin ("__builtin_vis_alignaddr", CODE_FOR_alignaddrsi_vis,
|
ptr_ftype_ptr_si);
|
ptr_ftype_ptr_si);
|
|
|
/* Pixel distance. */
|
/* Pixel distance. */
|
def_builtin ("__builtin_vis_pdist", CODE_FOR_pdist_vis,
|
def_builtin ("__builtin_vis_pdist", CODE_FOR_pdist_vis,
|
di_ftype_v8qi_v8qi_di);
|
di_ftype_v8qi_v8qi_di);
|
}
|
}
|
|
|
/* Handle TARGET_EXPAND_BUILTIN target hook.
|
/* Handle TARGET_EXPAND_BUILTIN target hook.
|
Expand builtin functions for sparc intrinsics. */
|
Expand builtin functions for sparc intrinsics. */
|
|
|
static rtx
|
static rtx
|
sparc_expand_builtin (tree exp, rtx target,
|
sparc_expand_builtin (tree exp, rtx target,
|
rtx subtarget ATTRIBUTE_UNUSED,
|
rtx subtarget ATTRIBUTE_UNUSED,
|
enum machine_mode tmode ATTRIBUTE_UNUSED,
|
enum machine_mode tmode ATTRIBUTE_UNUSED,
|
int ignore ATTRIBUTE_UNUSED)
|
int ignore ATTRIBUTE_UNUSED)
|
{
|
{
|
tree arg;
|
tree arg;
|
call_expr_arg_iterator iter;
|
call_expr_arg_iterator iter;
|
tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
|
tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
|
unsigned int icode = DECL_FUNCTION_CODE (fndecl);
|
unsigned int icode = DECL_FUNCTION_CODE (fndecl);
|
rtx pat, op[4];
|
rtx pat, op[4];
|
enum machine_mode mode[4];
|
enum machine_mode mode[4];
|
int arg_count = 0;
|
int arg_count = 0;
|
|
|
mode[0] = insn_data[icode].operand[0].mode;
|
mode[0] = insn_data[icode].operand[0].mode;
|
if (!target
|
if (!target
|
|| GET_MODE (target) != mode[0]
|
|| GET_MODE (target) != mode[0]
|
|| ! (*insn_data[icode].operand[0].predicate) (target, mode[0]))
|
|| ! (*insn_data[icode].operand[0].predicate) (target, mode[0]))
|
op[0] = gen_reg_rtx (mode[0]);
|
op[0] = gen_reg_rtx (mode[0]);
|
else
|
else
|
op[0] = target;
|
op[0] = target;
|
|
|
FOR_EACH_CALL_EXPR_ARG (arg, iter, exp)
|
FOR_EACH_CALL_EXPR_ARG (arg, iter, exp)
|
{
|
{
|
arg_count++;
|
arg_count++;
|
mode[arg_count] = insn_data[icode].operand[arg_count].mode;
|
mode[arg_count] = insn_data[icode].operand[arg_count].mode;
|
op[arg_count] = expand_normal (arg);
|
op[arg_count] = expand_normal (arg);
|
|
|
if (! (*insn_data[icode].operand[arg_count].predicate) (op[arg_count],
|
if (! (*insn_data[icode].operand[arg_count].predicate) (op[arg_count],
|
mode[arg_count]))
|
mode[arg_count]))
|
op[arg_count] = copy_to_mode_reg (mode[arg_count], op[arg_count]);
|
op[arg_count] = copy_to_mode_reg (mode[arg_count], op[arg_count]);
|
}
|
}
|
|
|
switch (arg_count)
|
switch (arg_count)
|
{
|
{
|
case 1:
|
case 1:
|
pat = GEN_FCN (icode) (op[0], op[1]);
|
pat = GEN_FCN (icode) (op[0], op[1]);
|
break;
|
break;
|
case 2:
|
case 2:
|
pat = GEN_FCN (icode) (op[0], op[1], op[2]);
|
pat = GEN_FCN (icode) (op[0], op[1], op[2]);
|
break;
|
break;
|
case 3:
|
case 3:
|
pat = GEN_FCN (icode) (op[0], op[1], op[2], op[3]);
|
pat = GEN_FCN (icode) (op[0], op[1], op[2], op[3]);
|
break;
|
break;
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
|
|
if (!pat)
|
if (!pat)
|
return NULL_RTX;
|
return NULL_RTX;
|
|
|
emit_insn (pat);
|
emit_insn (pat);
|
|
|
return op[0];
|
return op[0];
|
}
|
}
|
|
|
static int
|
static int
|
sparc_vis_mul8x16 (int e8, int e16)
|
sparc_vis_mul8x16 (int e8, int e16)
|
{
|
{
|
return (e8 * e16 + 128) / 256;
|
return (e8 * e16 + 128) / 256;
|
}
|
}
|
|
|
/* Multiply the vector elements in ELTS0 to the elements in ELTS1 as specified
|
/* Multiply the vector elements in ELTS0 to the elements in ELTS1 as specified
|
by FNCODE. All of the elements in ELTS0 and ELTS1 lists must be integer
|
by FNCODE. All of the elements in ELTS0 and ELTS1 lists must be integer
|
constants. A tree list with the results of the multiplications is returned,
|
constants. A tree list with the results of the multiplications is returned,
|
and each element in the list is of INNER_TYPE. */
|
and each element in the list is of INNER_TYPE. */
|
|
|
static tree
|
static tree
|
sparc_handle_vis_mul8x16 (int fncode, tree inner_type, tree elts0, tree elts1)
|
sparc_handle_vis_mul8x16 (int fncode, tree inner_type, tree elts0, tree elts1)
|
{
|
{
|
tree n_elts = NULL_TREE;
|
tree n_elts = NULL_TREE;
|
int scale;
|
int scale;
|
|
|
switch (fncode)
|
switch (fncode)
|
{
|
{
|
case CODE_FOR_fmul8x16_vis:
|
case CODE_FOR_fmul8x16_vis:
|
for (; elts0 && elts1;
|
for (; elts0 && elts1;
|
elts0 = TREE_CHAIN (elts0), elts1 = TREE_CHAIN (elts1))
|
elts0 = TREE_CHAIN (elts0), elts1 = TREE_CHAIN (elts1))
|
{
|
{
|
int val
|
int val
|
= sparc_vis_mul8x16 (TREE_INT_CST_LOW (TREE_VALUE (elts0)),
|
= sparc_vis_mul8x16 (TREE_INT_CST_LOW (TREE_VALUE (elts0)),
|
TREE_INT_CST_LOW (TREE_VALUE (elts1)));
|
TREE_INT_CST_LOW (TREE_VALUE (elts1)));
|
n_elts = tree_cons (NULL_TREE,
|
n_elts = tree_cons (NULL_TREE,
|
build_int_cst (inner_type, val),
|
build_int_cst (inner_type, val),
|
n_elts);
|
n_elts);
|
}
|
}
|
break;
|
break;
|
|
|
case CODE_FOR_fmul8x16au_vis:
|
case CODE_FOR_fmul8x16au_vis:
|
scale = TREE_INT_CST_LOW (TREE_VALUE (elts1));
|
scale = TREE_INT_CST_LOW (TREE_VALUE (elts1));
|
|
|
for (; elts0; elts0 = TREE_CHAIN (elts0))
|
for (; elts0; elts0 = TREE_CHAIN (elts0))
|
{
|
{
|
int val
|
int val
|
= sparc_vis_mul8x16 (TREE_INT_CST_LOW (TREE_VALUE (elts0)),
|
= sparc_vis_mul8x16 (TREE_INT_CST_LOW (TREE_VALUE (elts0)),
|
scale);
|
scale);
|
n_elts = tree_cons (NULL_TREE,
|
n_elts = tree_cons (NULL_TREE,
|
build_int_cst (inner_type, val),
|
build_int_cst (inner_type, val),
|
n_elts);
|
n_elts);
|
}
|
}
|
break;
|
break;
|
|
|
case CODE_FOR_fmul8x16al_vis:
|
case CODE_FOR_fmul8x16al_vis:
|
scale = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (elts1)));
|
scale = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (elts1)));
|
|
|
for (; elts0; elts0 = TREE_CHAIN (elts0))
|
for (; elts0; elts0 = TREE_CHAIN (elts0))
|
{
|
{
|
int val
|
int val
|
= sparc_vis_mul8x16 (TREE_INT_CST_LOW (TREE_VALUE (elts0)),
|
= sparc_vis_mul8x16 (TREE_INT_CST_LOW (TREE_VALUE (elts0)),
|
scale);
|
scale);
|
n_elts = tree_cons (NULL_TREE,
|
n_elts = tree_cons (NULL_TREE,
|
build_int_cst (inner_type, val),
|
build_int_cst (inner_type, val),
|
n_elts);
|
n_elts);
|
}
|
}
|
break;
|
break;
|
|
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
|
|
return nreverse (n_elts);
|
return nreverse (n_elts);
|
|
|
}
|
}
|
/* Handle TARGET_FOLD_BUILTIN target hook.
|
/* Handle TARGET_FOLD_BUILTIN target hook.
|
Fold builtin functions for SPARC intrinsics. If IGNORE is true the
|
Fold builtin functions for SPARC intrinsics. If IGNORE is true the
|
result of the function call is ignored. NULL_TREE is returned if the
|
result of the function call is ignored. NULL_TREE is returned if the
|
function could not be folded. */
|
function could not be folded. */
|
|
|
static tree
|
static tree
|
sparc_fold_builtin (tree fndecl, tree arglist, bool ignore)
|
sparc_fold_builtin (tree fndecl, tree arglist, bool ignore)
|
{
|
{
|
tree arg0, arg1, arg2;
|
tree arg0, arg1, arg2;
|
tree rtype = TREE_TYPE (TREE_TYPE (fndecl));
|
tree rtype = TREE_TYPE (TREE_TYPE (fndecl));
|
enum insn_code icode = (enum insn_code) DECL_FUNCTION_CODE (fndecl);
|
enum insn_code icode = (enum insn_code) DECL_FUNCTION_CODE (fndecl);
|
|
|
if (ignore
|
if (ignore
|
&& icode != CODE_FOR_alignaddrsi_vis
|
&& icode != CODE_FOR_alignaddrsi_vis
|
&& icode != CODE_FOR_alignaddrdi_vis)
|
&& icode != CODE_FOR_alignaddrdi_vis)
|
return fold_convert (rtype, integer_zero_node);
|
return fold_convert (rtype, integer_zero_node);
|
|
|
switch (icode)
|
switch (icode)
|
{
|
{
|
case CODE_FOR_fexpand_vis:
|
case CODE_FOR_fexpand_vis:
|
arg0 = TREE_VALUE (arglist);
|
arg0 = TREE_VALUE (arglist);
|
STRIP_NOPS (arg0);
|
STRIP_NOPS (arg0);
|
|
|
if (TREE_CODE (arg0) == VECTOR_CST)
|
if (TREE_CODE (arg0) == VECTOR_CST)
|
{
|
{
|
tree inner_type = TREE_TYPE (rtype);
|
tree inner_type = TREE_TYPE (rtype);
|
tree elts = TREE_VECTOR_CST_ELTS (arg0);
|
tree elts = TREE_VECTOR_CST_ELTS (arg0);
|
tree n_elts = NULL_TREE;
|
tree n_elts = NULL_TREE;
|
|
|
for (; elts; elts = TREE_CHAIN (elts))
|
for (; elts; elts = TREE_CHAIN (elts))
|
{
|
{
|
unsigned int val = TREE_INT_CST_LOW (TREE_VALUE (elts)) << 4;
|
unsigned int val = TREE_INT_CST_LOW (TREE_VALUE (elts)) << 4;
|
n_elts = tree_cons (NULL_TREE,
|
n_elts = tree_cons (NULL_TREE,
|
build_int_cst (inner_type, val),
|
build_int_cst (inner_type, val),
|
n_elts);
|
n_elts);
|
}
|
}
|
return build_vector (rtype, nreverse (n_elts));
|
return build_vector (rtype, nreverse (n_elts));
|
}
|
}
|
break;
|
break;
|
|
|
case CODE_FOR_fmul8x16_vis:
|
case CODE_FOR_fmul8x16_vis:
|
case CODE_FOR_fmul8x16au_vis:
|
case CODE_FOR_fmul8x16au_vis:
|
case CODE_FOR_fmul8x16al_vis:
|
case CODE_FOR_fmul8x16al_vis:
|
arg0 = TREE_VALUE (arglist);
|
arg0 = TREE_VALUE (arglist);
|
arg1 = TREE_VALUE (TREE_CHAIN (arglist));
|
arg1 = TREE_VALUE (TREE_CHAIN (arglist));
|
STRIP_NOPS (arg0);
|
STRIP_NOPS (arg0);
|
STRIP_NOPS (arg1);
|
STRIP_NOPS (arg1);
|
|
|
if (TREE_CODE (arg0) == VECTOR_CST && TREE_CODE (arg1) == VECTOR_CST)
|
if (TREE_CODE (arg0) == VECTOR_CST && TREE_CODE (arg1) == VECTOR_CST)
|
{
|
{
|
tree inner_type = TREE_TYPE (rtype);
|
tree inner_type = TREE_TYPE (rtype);
|
tree elts0 = TREE_VECTOR_CST_ELTS (arg0);
|
tree elts0 = TREE_VECTOR_CST_ELTS (arg0);
|
tree elts1 = TREE_VECTOR_CST_ELTS (arg1);
|
tree elts1 = TREE_VECTOR_CST_ELTS (arg1);
|
tree n_elts = sparc_handle_vis_mul8x16 (icode, inner_type, elts0,
|
tree n_elts = sparc_handle_vis_mul8x16 (icode, inner_type, elts0,
|
elts1);
|
elts1);
|
|
|
return build_vector (rtype, n_elts);
|
return build_vector (rtype, n_elts);
|
}
|
}
|
break;
|
break;
|
|
|
case CODE_FOR_fpmerge_vis:
|
case CODE_FOR_fpmerge_vis:
|
arg0 = TREE_VALUE (arglist);
|
arg0 = TREE_VALUE (arglist);
|
arg1 = TREE_VALUE (TREE_CHAIN (arglist));
|
arg1 = TREE_VALUE (TREE_CHAIN (arglist));
|
STRIP_NOPS (arg0);
|
STRIP_NOPS (arg0);
|
STRIP_NOPS (arg1);
|
STRIP_NOPS (arg1);
|
|
|
if (TREE_CODE (arg0) == VECTOR_CST && TREE_CODE (arg1) == VECTOR_CST)
|
if (TREE_CODE (arg0) == VECTOR_CST && TREE_CODE (arg1) == VECTOR_CST)
|
{
|
{
|
tree elts0 = TREE_VECTOR_CST_ELTS (arg0);
|
tree elts0 = TREE_VECTOR_CST_ELTS (arg0);
|
tree elts1 = TREE_VECTOR_CST_ELTS (arg1);
|
tree elts1 = TREE_VECTOR_CST_ELTS (arg1);
|
tree n_elts = NULL_TREE;
|
tree n_elts = NULL_TREE;
|
|
|
for (; elts0 && elts1;
|
for (; elts0 && elts1;
|
elts0 = TREE_CHAIN (elts0), elts1 = TREE_CHAIN (elts1))
|
elts0 = TREE_CHAIN (elts0), elts1 = TREE_CHAIN (elts1))
|
{
|
{
|
n_elts = tree_cons (NULL_TREE, TREE_VALUE (elts0), n_elts);
|
n_elts = tree_cons (NULL_TREE, TREE_VALUE (elts0), n_elts);
|
n_elts = tree_cons (NULL_TREE, TREE_VALUE (elts1), n_elts);
|
n_elts = tree_cons (NULL_TREE, TREE_VALUE (elts1), n_elts);
|
}
|
}
|
|
|
return build_vector (rtype, nreverse (n_elts));
|
return build_vector (rtype, nreverse (n_elts));
|
}
|
}
|
break;
|
break;
|
|
|
case CODE_FOR_pdist_vis:
|
case CODE_FOR_pdist_vis:
|
arg0 = TREE_VALUE (arglist);
|
arg0 = TREE_VALUE (arglist);
|
arg1 = TREE_VALUE (TREE_CHAIN (arglist));
|
arg1 = TREE_VALUE (TREE_CHAIN (arglist));
|
arg2 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
|
arg2 = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (arglist)));
|
STRIP_NOPS (arg0);
|
STRIP_NOPS (arg0);
|
STRIP_NOPS (arg1);
|
STRIP_NOPS (arg1);
|
STRIP_NOPS (arg2);
|
STRIP_NOPS (arg2);
|
|
|
if (TREE_CODE (arg0) == VECTOR_CST
|
if (TREE_CODE (arg0) == VECTOR_CST
|
&& TREE_CODE (arg1) == VECTOR_CST
|
&& TREE_CODE (arg1) == VECTOR_CST
|
&& TREE_CODE (arg2) == INTEGER_CST)
|
&& TREE_CODE (arg2) == INTEGER_CST)
|
{
|
{
|
int overflow = 0;
|
int overflow = 0;
|
unsigned HOST_WIDE_INT low = TREE_INT_CST_LOW (arg2);
|
unsigned HOST_WIDE_INT low = TREE_INT_CST_LOW (arg2);
|
HOST_WIDE_INT high = TREE_INT_CST_HIGH (arg2);
|
HOST_WIDE_INT high = TREE_INT_CST_HIGH (arg2);
|
tree elts0 = TREE_VECTOR_CST_ELTS (arg0);
|
tree elts0 = TREE_VECTOR_CST_ELTS (arg0);
|
tree elts1 = TREE_VECTOR_CST_ELTS (arg1);
|
tree elts1 = TREE_VECTOR_CST_ELTS (arg1);
|
|
|
for (; elts0 && elts1;
|
for (; elts0 && elts1;
|
elts0 = TREE_CHAIN (elts0), elts1 = TREE_CHAIN (elts1))
|
elts0 = TREE_CHAIN (elts0), elts1 = TREE_CHAIN (elts1))
|
{
|
{
|
unsigned HOST_WIDE_INT
|
unsigned HOST_WIDE_INT
|
low0 = TREE_INT_CST_LOW (TREE_VALUE (elts0)),
|
low0 = TREE_INT_CST_LOW (TREE_VALUE (elts0)),
|
low1 = TREE_INT_CST_LOW (TREE_VALUE (elts1));
|
low1 = TREE_INT_CST_LOW (TREE_VALUE (elts1));
|
HOST_WIDE_INT high0 = TREE_INT_CST_HIGH (TREE_VALUE (elts0));
|
HOST_WIDE_INT high0 = TREE_INT_CST_HIGH (TREE_VALUE (elts0));
|
HOST_WIDE_INT high1 = TREE_INT_CST_HIGH (TREE_VALUE (elts1));
|
HOST_WIDE_INT high1 = TREE_INT_CST_HIGH (TREE_VALUE (elts1));
|
|
|
unsigned HOST_WIDE_INT l;
|
unsigned HOST_WIDE_INT l;
|
HOST_WIDE_INT h;
|
HOST_WIDE_INT h;
|
|
|
overflow |= neg_double (low1, high1, &l, &h);
|
overflow |= neg_double (low1, high1, &l, &h);
|
overflow |= add_double (low0, high0, l, h, &l, &h);
|
overflow |= add_double (low0, high0, l, h, &l, &h);
|
if (h < 0)
|
if (h < 0)
|
overflow |= neg_double (l, h, &l, &h);
|
overflow |= neg_double (l, h, &l, &h);
|
|
|
overflow |= add_double (low, high, l, h, &low, &high);
|
overflow |= add_double (low, high, l, h, &low, &high);
|
}
|
}
|
|
|
gcc_assert (overflow == 0);
|
gcc_assert (overflow == 0);
|
|
|
return build_int_cst_wide (rtype, low, high);
|
return build_int_cst_wide (rtype, low, high);
|
}
|
}
|
|
|
default:
|
default:
|
break;
|
break;
|
}
|
}
|
|
|
return NULL_TREE;
|
return NULL_TREE;
|
}
|
}
|
|
|
/* ??? This duplicates information provided to the compiler by the
|
/* ??? This duplicates information provided to the compiler by the
|
??? scheduler description. Some day, teach genautomata to output
|
??? scheduler description. Some day, teach genautomata to output
|
??? the latencies and then CSE will just use that. */
|
??? the latencies and then CSE will just use that. */
|
|
|
static bool
|
static bool
|
sparc_rtx_costs (rtx x, int code, int outer_code, int *total,
|
sparc_rtx_costs (rtx x, int code, int outer_code, int *total,
|
bool speed ATTRIBUTE_UNUSED)
|
bool speed ATTRIBUTE_UNUSED)
|
{
|
{
|
enum machine_mode mode = GET_MODE (x);
|
enum machine_mode mode = GET_MODE (x);
|
bool float_mode_p = FLOAT_MODE_P (mode);
|
bool float_mode_p = FLOAT_MODE_P (mode);
|
|
|
switch (code)
|
switch (code)
|
{
|
{
|
case CONST_INT:
|
case CONST_INT:
|
if (INTVAL (x) < 0x1000 && INTVAL (x) >= -0x1000)
|
if (INTVAL (x) < 0x1000 && INTVAL (x) >= -0x1000)
|
{
|
{
|
*total = 0;
|
*total = 0;
|
return true;
|
return true;
|
}
|
}
|
/* FALLTHRU */
|
/* FALLTHRU */
|
|
|
case HIGH:
|
case HIGH:
|
*total = 2;
|
*total = 2;
|
return true;
|
return true;
|
|
|
case CONST:
|
case CONST:
|
case LABEL_REF:
|
case LABEL_REF:
|
case SYMBOL_REF:
|
case SYMBOL_REF:
|
*total = 4;
|
*total = 4;
|
return true;
|
return true;
|
|
|
case CONST_DOUBLE:
|
case CONST_DOUBLE:
|
if (GET_MODE (x) == VOIDmode
|
if (GET_MODE (x) == VOIDmode
|
&& ((CONST_DOUBLE_HIGH (x) == 0
|
&& ((CONST_DOUBLE_HIGH (x) == 0
|
&& CONST_DOUBLE_LOW (x) < 0x1000)
|
&& CONST_DOUBLE_LOW (x) < 0x1000)
|
|| (CONST_DOUBLE_HIGH (x) == -1
|
|| (CONST_DOUBLE_HIGH (x) == -1
|
&& CONST_DOUBLE_LOW (x) < 0
|
&& CONST_DOUBLE_LOW (x) < 0
|
&& CONST_DOUBLE_LOW (x) >= -0x1000)))
|
&& CONST_DOUBLE_LOW (x) >= -0x1000)))
|
*total = 0;
|
*total = 0;
|
else
|
else
|
*total = 8;
|
*total = 8;
|
return true;
|
return true;
|
|
|
case MEM:
|
case MEM:
|
/* If outer-code was a sign or zero extension, a cost
|
/* If outer-code was a sign or zero extension, a cost
|
of COSTS_N_INSNS (1) was already added in. This is
|
of COSTS_N_INSNS (1) was already added in. This is
|
why we are subtracting it back out. */
|
why we are subtracting it back out. */
|
if (outer_code == ZERO_EXTEND)
|
if (outer_code == ZERO_EXTEND)
|
{
|
{
|
*total = sparc_costs->int_zload - COSTS_N_INSNS (1);
|
*total = sparc_costs->int_zload - COSTS_N_INSNS (1);
|
}
|
}
|
else if (outer_code == SIGN_EXTEND)
|
else if (outer_code == SIGN_EXTEND)
|
{
|
{
|
*total = sparc_costs->int_sload - COSTS_N_INSNS (1);
|
*total = sparc_costs->int_sload - COSTS_N_INSNS (1);
|
}
|
}
|
else if (float_mode_p)
|
else if (float_mode_p)
|
{
|
{
|
*total = sparc_costs->float_load;
|
*total = sparc_costs->float_load;
|
}
|
}
|
else
|
else
|
{
|
{
|
*total = sparc_costs->int_load;
|
*total = sparc_costs->int_load;
|
}
|
}
|
|
|
return true;
|
return true;
|
|
|
case PLUS:
|
case PLUS:
|
case MINUS:
|
case MINUS:
|
if (float_mode_p)
|
if (float_mode_p)
|
*total = sparc_costs->float_plusminus;
|
*total = sparc_costs->float_plusminus;
|
else
|
else
|
*total = COSTS_N_INSNS (1);
|
*total = COSTS_N_INSNS (1);
|
return false;
|
return false;
|
|
|
case MULT:
|
case MULT:
|
if (float_mode_p)
|
if (float_mode_p)
|
*total = sparc_costs->float_mul;
|
*total = sparc_costs->float_mul;
|
else if (! TARGET_HARD_MUL)
|
else if (! TARGET_HARD_MUL)
|
*total = COSTS_N_INSNS (25);
|
*total = COSTS_N_INSNS (25);
|
else
|
else
|
{
|
{
|
int bit_cost;
|
int bit_cost;
|
|
|
bit_cost = 0;
|
bit_cost = 0;
|
if (sparc_costs->int_mul_bit_factor)
|
if (sparc_costs->int_mul_bit_factor)
|
{
|
{
|
int nbits;
|
int nbits;
|
|
|
if (GET_CODE (XEXP (x, 1)) == CONST_INT)
|
if (GET_CODE (XEXP (x, 1)) == CONST_INT)
|
{
|
{
|
unsigned HOST_WIDE_INT value = INTVAL (XEXP (x, 1));
|
unsigned HOST_WIDE_INT value = INTVAL (XEXP (x, 1));
|
for (nbits = 0; value != 0; value &= value - 1)
|
for (nbits = 0; value != 0; value &= value - 1)
|
nbits++;
|
nbits++;
|
}
|
}
|
else if (GET_CODE (XEXP (x, 1)) == CONST_DOUBLE
|
else if (GET_CODE (XEXP (x, 1)) == CONST_DOUBLE
|
&& GET_MODE (XEXP (x, 1)) == VOIDmode)
|
&& GET_MODE (XEXP (x, 1)) == VOIDmode)
|
{
|
{
|
rtx x1 = XEXP (x, 1);
|
rtx x1 = XEXP (x, 1);
|
unsigned HOST_WIDE_INT value1 = CONST_DOUBLE_LOW (x1);
|
unsigned HOST_WIDE_INT value1 = CONST_DOUBLE_LOW (x1);
|
unsigned HOST_WIDE_INT value2 = CONST_DOUBLE_HIGH (x1);
|
unsigned HOST_WIDE_INT value2 = CONST_DOUBLE_HIGH (x1);
|
|
|
for (nbits = 0; value1 != 0; value1 &= value1 - 1)
|
for (nbits = 0; value1 != 0; value1 &= value1 - 1)
|
nbits++;
|
nbits++;
|
for (; value2 != 0; value2 &= value2 - 1)
|
for (; value2 != 0; value2 &= value2 - 1)
|
nbits++;
|
nbits++;
|
}
|
}
|
else
|
else
|
nbits = 7;
|
nbits = 7;
|
|
|
if (nbits < 3)
|
if (nbits < 3)
|
nbits = 3;
|
nbits = 3;
|
bit_cost = (nbits - 3) / sparc_costs->int_mul_bit_factor;
|
bit_cost = (nbits - 3) / sparc_costs->int_mul_bit_factor;
|
bit_cost = COSTS_N_INSNS (bit_cost);
|
bit_cost = COSTS_N_INSNS (bit_cost);
|
}
|
}
|
|
|
if (mode == DImode)
|
if (mode == DImode)
|
*total = sparc_costs->int_mulX + bit_cost;
|
*total = sparc_costs->int_mulX + bit_cost;
|
else
|
else
|
*total = sparc_costs->int_mul + bit_cost;
|
*total = sparc_costs->int_mul + bit_cost;
|
}
|
}
|
return false;
|
return false;
|
|
|
case ASHIFT:
|
case ASHIFT:
|
case ASHIFTRT:
|
case ASHIFTRT:
|
case LSHIFTRT:
|
case LSHIFTRT:
|
*total = COSTS_N_INSNS (1) + sparc_costs->shift_penalty;
|
*total = COSTS_N_INSNS (1) + sparc_costs->shift_penalty;
|
return false;
|
return false;
|
|
|
case DIV:
|
case DIV:
|
case UDIV:
|
case UDIV:
|
case MOD:
|
case MOD:
|
case UMOD:
|
case UMOD:
|
if (float_mode_p)
|
if (float_mode_p)
|
{
|
{
|
if (mode == DFmode)
|
if (mode == DFmode)
|
*total = sparc_costs->float_div_df;
|
*total = sparc_costs->float_div_df;
|
else
|
else
|
*total = sparc_costs->float_div_sf;
|
*total = sparc_costs->float_div_sf;
|
}
|
}
|
else
|
else
|
{
|
{
|
if (mode == DImode)
|
if (mode == DImode)
|
*total = sparc_costs->int_divX;
|
*total = sparc_costs->int_divX;
|
else
|
else
|
*total = sparc_costs->int_div;
|
*total = sparc_costs->int_div;
|
}
|
}
|
return false;
|
return false;
|
|
|
case NEG:
|
case NEG:
|
if (! float_mode_p)
|
if (! float_mode_p)
|
{
|
{
|
*total = COSTS_N_INSNS (1);
|
*total = COSTS_N_INSNS (1);
|
return false;
|
return false;
|
}
|
}
|
/* FALLTHRU */
|
/* FALLTHRU */
|
|
|
case ABS:
|
case ABS:
|
case FLOAT:
|
case FLOAT:
|
case UNSIGNED_FLOAT:
|
case UNSIGNED_FLOAT:
|
case FIX:
|
case FIX:
|
case UNSIGNED_FIX:
|
case UNSIGNED_FIX:
|
case FLOAT_EXTEND:
|
case FLOAT_EXTEND:
|
case FLOAT_TRUNCATE:
|
case FLOAT_TRUNCATE:
|
*total = sparc_costs->float_move;
|
*total = sparc_costs->float_move;
|
return false;
|
return false;
|
|
|
case SQRT:
|
case SQRT:
|
if (mode == DFmode)
|
if (mode == DFmode)
|
*total = sparc_costs->float_sqrt_df;
|
*total = sparc_costs->float_sqrt_df;
|
else
|
else
|
*total = sparc_costs->float_sqrt_sf;
|
*total = sparc_costs->float_sqrt_sf;
|
return false;
|
return false;
|
|
|
case COMPARE:
|
case COMPARE:
|
if (float_mode_p)
|
if (float_mode_p)
|
*total = sparc_costs->float_cmp;
|
*total = sparc_costs->float_cmp;
|
else
|
else
|
*total = COSTS_N_INSNS (1);
|
*total = COSTS_N_INSNS (1);
|
return false;
|
return false;
|
|
|
case IF_THEN_ELSE:
|
case IF_THEN_ELSE:
|
if (float_mode_p)
|
if (float_mode_p)
|
*total = sparc_costs->float_cmove;
|
*total = sparc_costs->float_cmove;
|
else
|
else
|
*total = sparc_costs->int_cmove;
|
*total = sparc_costs->int_cmove;
|
return false;
|
return false;
|
|
|
case IOR:
|
case IOR:
|
/* Handle the NAND vector patterns. */
|
/* Handle the NAND vector patterns. */
|
if (sparc_vector_mode_supported_p (GET_MODE (x))
|
if (sparc_vector_mode_supported_p (GET_MODE (x))
|
&& GET_CODE (XEXP (x, 0)) == NOT
|
&& GET_CODE (XEXP (x, 0)) == NOT
|
&& GET_CODE (XEXP (x, 1)) == NOT)
|
&& GET_CODE (XEXP (x, 1)) == NOT)
|
{
|
{
|
*total = COSTS_N_INSNS (1);
|
*total = COSTS_N_INSNS (1);
|
return true;
|
return true;
|
}
|
}
|
else
|
else
|
return false;
|
return false;
|
|
|
default:
|
default:
|
return false;
|
return false;
|
}
|
}
|
}
|
}
|
|
|
/* Emit the sequence of insns SEQ while preserving the registers REG and REG2.
|
/* Emit the sequence of insns SEQ while preserving the registers REG and REG2.
|
This is achieved by means of a manual dynamic stack space allocation in
|
This is achieved by means of a manual dynamic stack space allocation in
|
the current frame. We make the assumption that SEQ doesn't contain any
|
the current frame. We make the assumption that SEQ doesn't contain any
|
function calls, with the possible exception of calls to the PIC helper. */
|
function calls, with the possible exception of calls to the PIC helper. */
|
|
|
static void
|
static void
|
emit_and_preserve (rtx seq, rtx reg, rtx reg2)
|
emit_and_preserve (rtx seq, rtx reg, rtx reg2)
|
{
|
{
|
/* We must preserve the lowest 16 words for the register save area. */
|
/* We must preserve the lowest 16 words for the register save area. */
|
HOST_WIDE_INT offset = 16*UNITS_PER_WORD;
|
HOST_WIDE_INT offset = 16*UNITS_PER_WORD;
|
/* We really need only 2 words of fresh stack space. */
|
/* We really need only 2 words of fresh stack space. */
|
HOST_WIDE_INT size = SPARC_STACK_ALIGN (offset + 2*UNITS_PER_WORD);
|
HOST_WIDE_INT size = SPARC_STACK_ALIGN (offset + 2*UNITS_PER_WORD);
|
|
|
rtx slot
|
rtx slot
|
= gen_rtx_MEM (word_mode, plus_constant (stack_pointer_rtx,
|
= gen_rtx_MEM (word_mode, plus_constant (stack_pointer_rtx,
|
SPARC_STACK_BIAS + offset));
|
SPARC_STACK_BIAS + offset));
|
|
|
emit_insn (gen_stack_pointer_dec (GEN_INT (size)));
|
emit_insn (gen_stack_pointer_dec (GEN_INT (size)));
|
emit_insn (gen_rtx_SET (VOIDmode, slot, reg));
|
emit_insn (gen_rtx_SET (VOIDmode, slot, reg));
|
if (reg2)
|
if (reg2)
|
emit_insn (gen_rtx_SET (VOIDmode,
|
emit_insn (gen_rtx_SET (VOIDmode,
|
adjust_address (slot, word_mode, UNITS_PER_WORD),
|
adjust_address (slot, word_mode, UNITS_PER_WORD),
|
reg2));
|
reg2));
|
emit_insn (seq);
|
emit_insn (seq);
|
if (reg2)
|
if (reg2)
|
emit_insn (gen_rtx_SET (VOIDmode,
|
emit_insn (gen_rtx_SET (VOIDmode,
|
reg2,
|
reg2,
|
adjust_address (slot, word_mode, UNITS_PER_WORD)));
|
adjust_address (slot, word_mode, UNITS_PER_WORD)));
|
emit_insn (gen_rtx_SET (VOIDmode, reg, slot));
|
emit_insn (gen_rtx_SET (VOIDmode, reg, slot));
|
emit_insn (gen_stack_pointer_inc (GEN_INT (size)));
|
emit_insn (gen_stack_pointer_inc (GEN_INT (size)));
|
}
|
}
|
|
|
/* Output the assembler code for a thunk function. THUNK_DECL is the
|
/* Output the assembler code for a thunk function. THUNK_DECL is the
|
declaration for the thunk function itself, FUNCTION is the decl for
|
declaration for the thunk function itself, FUNCTION is the decl for
|
the target function. DELTA is an immediate constant offset to be
|
the target function. DELTA is an immediate constant offset to be
|
added to THIS. If VCALL_OFFSET is nonzero, the word at address
|
added to THIS. If VCALL_OFFSET is nonzero, the word at address
|
(*THIS + VCALL_OFFSET) should be additionally added to THIS. */
|
(*THIS + VCALL_OFFSET) should be additionally added to THIS. */
|
|
|
static void
|
static void
|
sparc_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
|
sparc_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
|
HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
|
HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
|
tree function)
|
tree function)
|
{
|
{
|
rtx this_rtx, insn, funexp;
|
rtx this_rtx, insn, funexp;
|
unsigned int int_arg_first;
|
unsigned int int_arg_first;
|
|
|
reload_completed = 1;
|
reload_completed = 1;
|
epilogue_completed = 1;
|
epilogue_completed = 1;
|
|
|
emit_note (NOTE_INSN_PROLOGUE_END);
|
emit_note (NOTE_INSN_PROLOGUE_END);
|
|
|
if (flag_delayed_branch)
|
if (flag_delayed_branch)
|
{
|
{
|
/* We will emit a regular sibcall below, so we need to instruct
|
/* We will emit a regular sibcall below, so we need to instruct
|
output_sibcall that we are in a leaf function. */
|
output_sibcall that we are in a leaf function. */
|
sparc_leaf_function_p = current_function_uses_only_leaf_regs = 1;
|
sparc_leaf_function_p = current_function_uses_only_leaf_regs = 1;
|
|
|
/* This will cause final.c to invoke leaf_renumber_regs so we
|
/* This will cause final.c to invoke leaf_renumber_regs so we
|
must behave as if we were in a not-yet-leafified function. */
|
must behave as if we were in a not-yet-leafified function. */
|
int_arg_first = SPARC_INCOMING_INT_ARG_FIRST;
|
int_arg_first = SPARC_INCOMING_INT_ARG_FIRST;
|
}
|
}
|
else
|
else
|
{
|
{
|
/* We will emit the sibcall manually below, so we will need to
|
/* We will emit the sibcall manually below, so we will need to
|
manually spill non-leaf registers. */
|
manually spill non-leaf registers. */
|
sparc_leaf_function_p = current_function_uses_only_leaf_regs = 0;
|
sparc_leaf_function_p = current_function_uses_only_leaf_regs = 0;
|
|
|
/* We really are in a leaf function. */
|
/* We really are in a leaf function. */
|
int_arg_first = SPARC_OUTGOING_INT_ARG_FIRST;
|
int_arg_first = SPARC_OUTGOING_INT_ARG_FIRST;
|
}
|
}
|
|
|
/* Find the "this" pointer. Normally in %o0, but in ARCH64 if the function
|
/* Find the "this" pointer. Normally in %o0, but in ARCH64 if the function
|
returns a structure, the structure return pointer is there instead. */
|
returns a structure, the structure return pointer is there instead. */
|
if (TARGET_ARCH64
|
if (TARGET_ARCH64
|
&& aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
|
&& aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
|
this_rtx = gen_rtx_REG (Pmode, int_arg_first + 1);
|
this_rtx = gen_rtx_REG (Pmode, int_arg_first + 1);
|
else
|
else
|
this_rtx = gen_rtx_REG (Pmode, int_arg_first);
|
this_rtx = gen_rtx_REG (Pmode, int_arg_first);
|
|
|
/* Add DELTA. When possible use a plain add, otherwise load it into
|
/* Add DELTA. When possible use a plain add, otherwise load it into
|
a register first. */
|
a register first. */
|
if (delta)
|
if (delta)
|
{
|
{
|
rtx delta_rtx = GEN_INT (delta);
|
rtx delta_rtx = GEN_INT (delta);
|
|
|
if (! SPARC_SIMM13_P (delta))
|
if (! SPARC_SIMM13_P (delta))
|
{
|
{
|
rtx scratch = gen_rtx_REG (Pmode, 1);
|
rtx scratch = gen_rtx_REG (Pmode, 1);
|
emit_move_insn (scratch, delta_rtx);
|
emit_move_insn (scratch, delta_rtx);
|
delta_rtx = scratch;
|
delta_rtx = scratch;
|
}
|
}
|
|
|
/* THIS_RTX += DELTA. */
|
/* THIS_RTX += DELTA. */
|
emit_insn (gen_add2_insn (this_rtx, delta_rtx));
|
emit_insn (gen_add2_insn (this_rtx, delta_rtx));
|
}
|
}
|
|
|
/* Add the word at address (*THIS_RTX + VCALL_OFFSET). */
|
/* Add the word at address (*THIS_RTX + VCALL_OFFSET). */
|
if (vcall_offset)
|
if (vcall_offset)
|
{
|
{
|
rtx vcall_offset_rtx = GEN_INT (vcall_offset);
|
rtx vcall_offset_rtx = GEN_INT (vcall_offset);
|
rtx scratch = gen_rtx_REG (Pmode, 1);
|
rtx scratch = gen_rtx_REG (Pmode, 1);
|
|
|
gcc_assert (vcall_offset < 0);
|
gcc_assert (vcall_offset < 0);
|
|
|
/* SCRATCH = *THIS_RTX. */
|
/* SCRATCH = *THIS_RTX. */
|
emit_move_insn (scratch, gen_rtx_MEM (Pmode, this_rtx));
|
emit_move_insn (scratch, gen_rtx_MEM (Pmode, this_rtx));
|
|
|
/* Prepare for adding VCALL_OFFSET. The difficulty is that we
|
/* Prepare for adding VCALL_OFFSET. The difficulty is that we
|
may not have any available scratch register at this point. */
|
may not have any available scratch register at this point. */
|
if (SPARC_SIMM13_P (vcall_offset))
|
if (SPARC_SIMM13_P (vcall_offset))
|
;
|
;
|
/* This is the case if ARCH64 (unless -ffixed-g5 is passed). */
|
/* This is the case if ARCH64 (unless -ffixed-g5 is passed). */
|
else if (! fixed_regs[5]
|
else if (! fixed_regs[5]
|
/* The below sequence is made up of at least 2 insns,
|
/* The below sequence is made up of at least 2 insns,
|
while the default method may need only one. */
|
while the default method may need only one. */
|
&& vcall_offset < -8192)
|
&& vcall_offset < -8192)
|
{
|
{
|
rtx scratch2 = gen_rtx_REG (Pmode, 5);
|
rtx scratch2 = gen_rtx_REG (Pmode, 5);
|
emit_move_insn (scratch2, vcall_offset_rtx);
|
emit_move_insn (scratch2, vcall_offset_rtx);
|
vcall_offset_rtx = scratch2;
|
vcall_offset_rtx = scratch2;
|
}
|
}
|
else
|
else
|
{
|
{
|
rtx increment = GEN_INT (-4096);
|
rtx increment = GEN_INT (-4096);
|
|
|
/* VCALL_OFFSET is a negative number whose typical range can be
|
/* VCALL_OFFSET is a negative number whose typical range can be
|
estimated as -32768..0 in 32-bit mode. In almost all cases
|
estimated as -32768..0 in 32-bit mode. In almost all cases
|
it is therefore cheaper to emit multiple add insns than
|
it is therefore cheaper to emit multiple add insns than
|
spilling and loading the constant into a register (at least
|
spilling and loading the constant into a register (at least
|
6 insns). */
|
6 insns). */
|
while (! SPARC_SIMM13_P (vcall_offset))
|
while (! SPARC_SIMM13_P (vcall_offset))
|
{
|
{
|
emit_insn (gen_add2_insn (scratch, increment));
|
emit_insn (gen_add2_insn (scratch, increment));
|
vcall_offset += 4096;
|
vcall_offset += 4096;
|
}
|
}
|
vcall_offset_rtx = GEN_INT (vcall_offset); /* cannot be 0 */
|
vcall_offset_rtx = GEN_INT (vcall_offset); /* cannot be 0 */
|
}
|
}
|
|
|
/* SCRATCH = *(*THIS_RTX + VCALL_OFFSET). */
|
/* SCRATCH = *(*THIS_RTX + VCALL_OFFSET). */
|
emit_move_insn (scratch, gen_rtx_MEM (Pmode,
|
emit_move_insn (scratch, gen_rtx_MEM (Pmode,
|
gen_rtx_PLUS (Pmode,
|
gen_rtx_PLUS (Pmode,
|
scratch,
|
scratch,
|
vcall_offset_rtx)));
|
vcall_offset_rtx)));
|
|
|
/* THIS_RTX += *(*THIS_RTX + VCALL_OFFSET). */
|
/* THIS_RTX += *(*THIS_RTX + VCALL_OFFSET). */
|
emit_insn (gen_add2_insn (this_rtx, scratch));
|
emit_insn (gen_add2_insn (this_rtx, scratch));
|
}
|
}
|
|
|
/* Generate a tail call to the target function. */
|
/* Generate a tail call to the target function. */
|
if (! TREE_USED (function))
|
if (! TREE_USED (function))
|
{
|
{
|
assemble_external (function);
|
assemble_external (function);
|
TREE_USED (function) = 1;
|
TREE_USED (function) = 1;
|
}
|
}
|
funexp = XEXP (DECL_RTL (function), 0);
|
funexp = XEXP (DECL_RTL (function), 0);
|
|
|
if (flag_delayed_branch)
|
if (flag_delayed_branch)
|
{
|
{
|
funexp = gen_rtx_MEM (FUNCTION_MODE, funexp);
|
funexp = gen_rtx_MEM (FUNCTION_MODE, funexp);
|
insn = emit_call_insn (gen_sibcall (funexp));
|
insn = emit_call_insn (gen_sibcall (funexp));
|
SIBLING_CALL_P (insn) = 1;
|
SIBLING_CALL_P (insn) = 1;
|
}
|
}
|
else
|
else
|
{
|
{
|
/* The hoops we have to jump through in order to generate a sibcall
|
/* The hoops we have to jump through in order to generate a sibcall
|
without using delay slots... */
|
without using delay slots... */
|
rtx spill_reg, spill_reg2, seq, scratch = gen_rtx_REG (Pmode, 1);
|
rtx spill_reg, spill_reg2, seq, scratch = gen_rtx_REG (Pmode, 1);
|
|
|
if (flag_pic)
|
if (flag_pic)
|
{
|
{
|
spill_reg = gen_rtx_REG (word_mode, 15); /* %o7 */
|
spill_reg = gen_rtx_REG (word_mode, 15); /* %o7 */
|
spill_reg2 = gen_rtx_REG (word_mode, PIC_OFFSET_TABLE_REGNUM);
|
spill_reg2 = gen_rtx_REG (word_mode, PIC_OFFSET_TABLE_REGNUM);
|
start_sequence ();
|
start_sequence ();
|
/* Delay emitting the PIC helper function because it needs to
|
/* Delay emitting the PIC helper function because it needs to
|
change the section and we are emitting assembly code. */
|
change the section and we are emitting assembly code. */
|
load_pic_register (); /* clobbers %o7 */
|
load_pic_register (); /* clobbers %o7 */
|
scratch = legitimize_pic_address (funexp, scratch);
|
scratch = legitimize_pic_address (funexp, scratch);
|
seq = get_insns ();
|
seq = get_insns ();
|
end_sequence ();
|
end_sequence ();
|
emit_and_preserve (seq, spill_reg, spill_reg2);
|
emit_and_preserve (seq, spill_reg, spill_reg2);
|
}
|
}
|
else if (TARGET_ARCH32)
|
else if (TARGET_ARCH32)
|
{
|
{
|
emit_insn (gen_rtx_SET (VOIDmode,
|
emit_insn (gen_rtx_SET (VOIDmode,
|
scratch,
|
scratch,
|
gen_rtx_HIGH (SImode, funexp)));
|
gen_rtx_HIGH (SImode, funexp)));
|
emit_insn (gen_rtx_SET (VOIDmode,
|
emit_insn (gen_rtx_SET (VOIDmode,
|
scratch,
|
scratch,
|
gen_rtx_LO_SUM (SImode, scratch, funexp)));
|
gen_rtx_LO_SUM (SImode, scratch, funexp)));
|
}
|
}
|
else /* TARGET_ARCH64 */
|
else /* TARGET_ARCH64 */
|
{
|
{
|
switch (sparc_cmodel)
|
switch (sparc_cmodel)
|
{
|
{
|
case CM_MEDLOW:
|
case CM_MEDLOW:
|
case CM_MEDMID:
|
case CM_MEDMID:
|
/* The destination can serve as a temporary. */
|
/* The destination can serve as a temporary. */
|
sparc_emit_set_symbolic_const64 (scratch, funexp, scratch);
|
sparc_emit_set_symbolic_const64 (scratch, funexp, scratch);
|
break;
|
break;
|
|
|
case CM_MEDANY:
|
case CM_MEDANY:
|
case CM_EMBMEDANY:
|
case CM_EMBMEDANY:
|
/* The destination cannot serve as a temporary. */
|
/* The destination cannot serve as a temporary. */
|
spill_reg = gen_rtx_REG (DImode, 15); /* %o7 */
|
spill_reg = gen_rtx_REG (DImode, 15); /* %o7 */
|
start_sequence ();
|
start_sequence ();
|
sparc_emit_set_symbolic_const64 (scratch, funexp, spill_reg);
|
sparc_emit_set_symbolic_const64 (scratch, funexp, spill_reg);
|
seq = get_insns ();
|
seq = get_insns ();
|
end_sequence ();
|
end_sequence ();
|
emit_and_preserve (seq, spill_reg, 0);
|
emit_and_preserve (seq, spill_reg, 0);
|
break;
|
break;
|
|
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
}
|
}
|
|
|
emit_jump_insn (gen_indirect_jump (scratch));
|
emit_jump_insn (gen_indirect_jump (scratch));
|
}
|
}
|
|
|
emit_barrier ();
|
emit_barrier ();
|
|
|
/* Run just enough of rest_of_compilation to get the insns emitted.
|
/* Run just enough of rest_of_compilation to get the insns emitted.
|
There's not really enough bulk here to make other passes such as
|
There's not really enough bulk here to make other passes such as
|
instruction scheduling worth while. Note that use_thunk calls
|
instruction scheduling worth while. Note that use_thunk calls
|
assemble_start_function and assemble_end_function. */
|
assemble_start_function and assemble_end_function. */
|
insn = get_insns ();
|
insn = get_insns ();
|
insn_locators_alloc ();
|
insn_locators_alloc ();
|
shorten_branches (insn);
|
shorten_branches (insn);
|
final_start_function (insn, file, 1);
|
final_start_function (insn, file, 1);
|
final (insn, file, 1);
|
final (insn, file, 1);
|
final_end_function ();
|
final_end_function ();
|
|
|
reload_completed = 0;
|
reload_completed = 0;
|
epilogue_completed = 0;
|
epilogue_completed = 0;
|
}
|
}
|
|
|
/* Return true if sparc_output_mi_thunk would be able to output the
|
/* Return true if sparc_output_mi_thunk would be able to output the
|
assembler code for the thunk function specified by the arguments
|
assembler code for the thunk function specified by the arguments
|
it is passed, and false otherwise. */
|
it is passed, and false otherwise. */
|
static bool
|
static bool
|
sparc_can_output_mi_thunk (const_tree thunk_fndecl ATTRIBUTE_UNUSED,
|
sparc_can_output_mi_thunk (const_tree thunk_fndecl ATTRIBUTE_UNUSED,
|
HOST_WIDE_INT delta ATTRIBUTE_UNUSED,
|
HOST_WIDE_INT delta ATTRIBUTE_UNUSED,
|
HOST_WIDE_INT vcall_offset,
|
HOST_WIDE_INT vcall_offset,
|
const_tree function ATTRIBUTE_UNUSED)
|
const_tree function ATTRIBUTE_UNUSED)
|
{
|
{
|
/* Bound the loop used in the default method above. */
|
/* Bound the loop used in the default method above. */
|
return (vcall_offset >= -32768 || ! fixed_regs[5]);
|
return (vcall_offset >= -32768 || ! fixed_regs[5]);
|
}
|
}
|
|
|
/* How to allocate a 'struct machine_function'. */
|
/* How to allocate a 'struct machine_function'. */
|
|
|
static struct machine_function *
|
static struct machine_function *
|
sparc_init_machine_status (void)
|
sparc_init_machine_status (void)
|
{
|
{
|
return GGC_CNEW (struct machine_function);
|
return GGC_CNEW (struct machine_function);
|
}
|
}
|
|
|
/* Locate some local-dynamic symbol still in use by this function
|
/* Locate some local-dynamic symbol still in use by this function
|
so that we can print its name in local-dynamic base patterns. */
|
so that we can print its name in local-dynamic base patterns. */
|
|
|
static const char *
|
static const char *
|
get_some_local_dynamic_name (void)
|
get_some_local_dynamic_name (void)
|
{
|
{
|
rtx insn;
|
rtx insn;
|
|
|
if (cfun->machine->some_ld_name)
|
if (cfun->machine->some_ld_name)
|
return cfun->machine->some_ld_name;
|
return cfun->machine->some_ld_name;
|
|
|
for (insn = get_insns (); insn ; insn = NEXT_INSN (insn))
|
for (insn = get_insns (); insn ; insn = NEXT_INSN (insn))
|
if (INSN_P (insn)
|
if (INSN_P (insn)
|
&& for_each_rtx (&PATTERN (insn), get_some_local_dynamic_name_1, 0))
|
&& for_each_rtx (&PATTERN (insn), get_some_local_dynamic_name_1, 0))
|
return cfun->machine->some_ld_name;
|
return cfun->machine->some_ld_name;
|
|
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
|
|
static int
|
static int
|
get_some_local_dynamic_name_1 (rtx *px, void *data ATTRIBUTE_UNUSED)
|
get_some_local_dynamic_name_1 (rtx *px, void *data ATTRIBUTE_UNUSED)
|
{
|
{
|
rtx x = *px;
|
rtx x = *px;
|
|
|
if (x
|
if (x
|
&& GET_CODE (x) == SYMBOL_REF
|
&& GET_CODE (x) == SYMBOL_REF
|
&& SYMBOL_REF_TLS_MODEL (x) == TLS_MODEL_LOCAL_DYNAMIC)
|
&& SYMBOL_REF_TLS_MODEL (x) == TLS_MODEL_LOCAL_DYNAMIC)
|
{
|
{
|
cfun->machine->some_ld_name = XSTR (x, 0);
|
cfun->machine->some_ld_name = XSTR (x, 0);
|
return 1;
|
return 1;
|
}
|
}
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Handle the TARGET_DWARF_HANDLE_FRAME_UNSPEC hook.
|
/* Handle the TARGET_DWARF_HANDLE_FRAME_UNSPEC hook.
|
This is called from dwarf2out.c to emit call frame instructions
|
This is called from dwarf2out.c to emit call frame instructions
|
for frame-related insns containing UNSPECs and UNSPEC_VOLATILEs. */
|
for frame-related insns containing UNSPECs and UNSPEC_VOLATILEs. */
|
static void
|
static void
|
sparc_dwarf_handle_frame_unspec (const char *label,
|
sparc_dwarf_handle_frame_unspec (const char *label,
|
rtx pattern ATTRIBUTE_UNUSED,
|
rtx pattern ATTRIBUTE_UNUSED,
|
int index ATTRIBUTE_UNUSED)
|
int index ATTRIBUTE_UNUSED)
|
{
|
{
|
gcc_assert (index == UNSPECV_SAVEW);
|
gcc_assert (index == UNSPECV_SAVEW);
|
dwarf2out_window_save (label);
|
dwarf2out_window_save (label);
|
}
|
}
|
|
|
/* This is called from dwarf2out.c via TARGET_ASM_OUTPUT_DWARF_DTPREL.
|
/* This is called from dwarf2out.c via TARGET_ASM_OUTPUT_DWARF_DTPREL.
|
We need to emit DTP-relative relocations. */
|
We need to emit DTP-relative relocations. */
|
|
|
static void
|
static void
|
sparc_output_dwarf_dtprel (FILE *file, int size, rtx x)
|
sparc_output_dwarf_dtprel (FILE *file, int size, rtx x)
|
{
|
{
|
switch (size)
|
switch (size)
|
{
|
{
|
case 4:
|
case 4:
|
fputs ("\t.word\t%r_tls_dtpoff32(", file);
|
fputs ("\t.word\t%r_tls_dtpoff32(", file);
|
break;
|
break;
|
case 8:
|
case 8:
|
fputs ("\t.xword\t%r_tls_dtpoff64(", file);
|
fputs ("\t.xword\t%r_tls_dtpoff64(", file);
|
break;
|
break;
|
default:
|
default:
|
gcc_unreachable ();
|
gcc_unreachable ();
|
}
|
}
|
output_addr_const (file, x);
|
output_addr_const (file, x);
|
fputs (")", file);
|
fputs (")", file);
|
}
|
}
|
|
|
/* Do whatever processing is required at the end of a file. */
|
/* Do whatever processing is required at the end of a file. */
|
|
|
static void
|
static void
|
sparc_file_end (void)
|
sparc_file_end (void)
|
{
|
{
|
/* If need to emit the special PIC helper function, do so now. */
|
/* If need to emit the special PIC helper function, do so now. */
|
if (pic_helper_needed)
|
if (pic_helper_needed)
|
{
|
{
|
unsigned int regno = REGNO (pic_offset_table_rtx);
|
unsigned int regno = REGNO (pic_offset_table_rtx);
|
const char *pic_name = reg_names[regno];
|
const char *pic_name = reg_names[regno];
|
char name[32];
|
char name[32];
|
#ifdef DWARF2_UNWIND_INFO
|
#ifdef DWARF2_UNWIND_INFO
|
bool do_cfi;
|
bool do_cfi;
|
#endif
|
#endif
|
|
|
get_pc_thunk_name (name, regno);
|
get_pc_thunk_name (name, regno);
|
if (USE_HIDDEN_LINKONCE)
|
if (USE_HIDDEN_LINKONCE)
|
{
|
{
|
tree decl = build_decl (BUILTINS_LOCATION, FUNCTION_DECL,
|
tree decl = build_decl (BUILTINS_LOCATION, FUNCTION_DECL,
|
get_identifier (name),
|
get_identifier (name),
|
build_function_type (void_type_node,
|
build_function_type (void_type_node,
|
void_list_node));
|
void_list_node));
|
DECL_RESULT (decl) = build_decl (BUILTINS_LOCATION, RESULT_DECL,
|
DECL_RESULT (decl) = build_decl (BUILTINS_LOCATION, RESULT_DECL,
|
NULL_TREE, void_type_node);
|
NULL_TREE, void_type_node);
|
TREE_STATIC (decl) = 1;
|
TREE_STATIC (decl) = 1;
|
make_decl_one_only (decl, DECL_ASSEMBLER_NAME (decl));
|
make_decl_one_only (decl, DECL_ASSEMBLER_NAME (decl));
|
DECL_VISIBILITY (decl) = VISIBILITY_HIDDEN;
|
DECL_VISIBILITY (decl) = VISIBILITY_HIDDEN;
|
DECL_VISIBILITY_SPECIFIED (decl) = 1;
|
DECL_VISIBILITY_SPECIFIED (decl) = 1;
|
allocate_struct_function (decl, true);
|
allocate_struct_function (decl, true);
|
current_function_decl = decl;
|
current_function_decl = decl;
|
init_varasm_status ();
|
init_varasm_status ();
|
assemble_start_function (decl, name);
|
assemble_start_function (decl, name);
|
}
|
}
|
else
|
else
|
{
|
{
|
const int align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT);
|
const int align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT);
|
switch_to_section (text_section);
|
switch_to_section (text_section);
|
if (align > 0)
|
if (align > 0)
|
ASM_OUTPUT_ALIGN (asm_out_file, align);
|
ASM_OUTPUT_ALIGN (asm_out_file, align);
|
ASM_OUTPUT_LABEL (asm_out_file, name);
|
ASM_OUTPUT_LABEL (asm_out_file, name);
|
}
|
}
|
|
|
#ifdef DWARF2_UNWIND_INFO
|
#ifdef DWARF2_UNWIND_INFO
|
do_cfi = dwarf2out_do_cfi_asm ();
|
do_cfi = dwarf2out_do_cfi_asm ();
|
if (do_cfi)
|
if (do_cfi)
|
fprintf (asm_out_file, "\t.cfi_startproc\n");
|
fprintf (asm_out_file, "\t.cfi_startproc\n");
|
#endif
|
#endif
|
if (flag_delayed_branch)
|
if (flag_delayed_branch)
|
fprintf (asm_out_file, "\tjmp\t%%o7+8\n\t add\t%%o7, %s, %s\n",
|
fprintf (asm_out_file, "\tjmp\t%%o7+8\n\t add\t%%o7, %s, %s\n",
|
pic_name, pic_name);
|
pic_name, pic_name);
|
else
|
else
|
fprintf (asm_out_file, "\tadd\t%%o7, %s, %s\n\tjmp\t%%o7+8\n\t nop\n",
|
fprintf (asm_out_file, "\tadd\t%%o7, %s, %s\n\tjmp\t%%o7+8\n\t nop\n",
|
pic_name, pic_name);
|
pic_name, pic_name);
|
#ifdef DWARF2_UNWIND_INFO
|
#ifdef DWARF2_UNWIND_INFO
|
if (do_cfi)
|
if (do_cfi)
|
fprintf (asm_out_file, "\t.cfi_endproc\n");
|
fprintf (asm_out_file, "\t.cfi_endproc\n");
|
#endif
|
#endif
|
}
|
}
|
|
|
if (NEED_INDICATE_EXEC_STACK)
|
if (NEED_INDICATE_EXEC_STACK)
|
file_end_indicate_exec_stack ();
|
file_end_indicate_exec_stack ();
|
}
|
}
|
|
|
#ifdef TARGET_ALTERNATE_LONG_DOUBLE_MANGLING
|
#ifdef TARGET_ALTERNATE_LONG_DOUBLE_MANGLING
|
/* Implement TARGET_MANGLE_TYPE. */
|
/* Implement TARGET_MANGLE_TYPE. */
|
|
|
static const char *
|
static const char *
|
sparc_mangle_type (const_tree type)
|
sparc_mangle_type (const_tree type)
|
{
|
{
|
if (!TARGET_64BIT
|
if (!TARGET_64BIT
|
&& TYPE_MAIN_VARIANT (type) == long_double_type_node
|
&& TYPE_MAIN_VARIANT (type) == long_double_type_node
|
&& TARGET_LONG_DOUBLE_128)
|
&& TARGET_LONG_DOUBLE_128)
|
return "g";
|
return "g";
|
|
|
/* For all other types, use normal C++ mangling. */
|
/* For all other types, use normal C++ mangling. */
|
return NULL;
|
return NULL;
|
}
|
}
|
#endif
|
#endif
|
|
|
/* Expand code to perform a 8 or 16-bit compare and swap by doing 32-bit
|
/* Expand code to perform a 8 or 16-bit compare and swap by doing 32-bit
|
compare and swap on the word containing the byte or half-word. */
|
compare and swap on the word containing the byte or half-word. */
|
|
|
void
|
void
|
sparc_expand_compare_and_swap_12 (rtx result, rtx mem, rtx oldval, rtx newval)
|
sparc_expand_compare_and_swap_12 (rtx result, rtx mem, rtx oldval, rtx newval)
|
{
|
{
|
rtx addr1 = force_reg (Pmode, XEXP (mem, 0));
|
rtx addr1 = force_reg (Pmode, XEXP (mem, 0));
|
rtx addr = gen_reg_rtx (Pmode);
|
rtx addr = gen_reg_rtx (Pmode);
|
rtx off = gen_reg_rtx (SImode);
|
rtx off = gen_reg_rtx (SImode);
|
rtx oldv = gen_reg_rtx (SImode);
|
rtx oldv = gen_reg_rtx (SImode);
|
rtx newv = gen_reg_rtx (SImode);
|
rtx newv = gen_reg_rtx (SImode);
|
rtx oldvalue = gen_reg_rtx (SImode);
|
rtx oldvalue = gen_reg_rtx (SImode);
|
rtx newvalue = gen_reg_rtx (SImode);
|
rtx newvalue = gen_reg_rtx (SImode);
|
rtx res = gen_reg_rtx (SImode);
|
rtx res = gen_reg_rtx (SImode);
|
rtx resv = gen_reg_rtx (SImode);
|
rtx resv = gen_reg_rtx (SImode);
|
rtx memsi, val, mask, end_label, loop_label, cc;
|
rtx memsi, val, mask, end_label, loop_label, cc;
|
|
|
emit_insn (gen_rtx_SET (VOIDmode, addr,
|
emit_insn (gen_rtx_SET (VOIDmode, addr,
|
gen_rtx_AND (Pmode, addr1, GEN_INT (-4))));
|
gen_rtx_AND (Pmode, addr1, GEN_INT (-4))));
|
|
|
if (Pmode != SImode)
|
if (Pmode != SImode)
|
addr1 = gen_lowpart (SImode, addr1);
|
addr1 = gen_lowpart (SImode, addr1);
|
emit_insn (gen_rtx_SET (VOIDmode, off,
|
emit_insn (gen_rtx_SET (VOIDmode, off,
|
gen_rtx_AND (SImode, addr1, GEN_INT (3))));
|
gen_rtx_AND (SImode, addr1, GEN_INT (3))));
|
|
|
memsi = gen_rtx_MEM (SImode, addr);
|
memsi = gen_rtx_MEM (SImode, addr);
|
set_mem_alias_set (memsi, ALIAS_SET_MEMORY_BARRIER);
|
set_mem_alias_set (memsi, ALIAS_SET_MEMORY_BARRIER);
|
MEM_VOLATILE_P (memsi) = MEM_VOLATILE_P (mem);
|
MEM_VOLATILE_P (memsi) = MEM_VOLATILE_P (mem);
|
|
|
val = force_reg (SImode, memsi);
|
val = force_reg (SImode, memsi);
|
|
|
emit_insn (gen_rtx_SET (VOIDmode, off,
|
emit_insn (gen_rtx_SET (VOIDmode, off,
|
gen_rtx_XOR (SImode, off,
|
gen_rtx_XOR (SImode, off,
|
GEN_INT (GET_MODE (mem) == QImode
|
GEN_INT (GET_MODE (mem) == QImode
|
? 3 : 2))));
|
? 3 : 2))));
|
|
|
emit_insn (gen_rtx_SET (VOIDmode, off,
|
emit_insn (gen_rtx_SET (VOIDmode, off,
|
gen_rtx_ASHIFT (SImode, off, GEN_INT (3))));
|
gen_rtx_ASHIFT (SImode, off, GEN_INT (3))));
|
|
|
if (GET_MODE (mem) == QImode)
|
if (GET_MODE (mem) == QImode)
|
mask = force_reg (SImode, GEN_INT (0xff));
|
mask = force_reg (SImode, GEN_INT (0xff));
|
else
|
else
|
mask = force_reg (SImode, GEN_INT (0xffff));
|
mask = force_reg (SImode, GEN_INT (0xffff));
|
|
|
emit_insn (gen_rtx_SET (VOIDmode, mask,
|
emit_insn (gen_rtx_SET (VOIDmode, mask,
|
gen_rtx_ASHIFT (SImode, mask, off)));
|
gen_rtx_ASHIFT (SImode, mask, off)));
|
|
|
emit_insn (gen_rtx_SET (VOIDmode, val,
|
emit_insn (gen_rtx_SET (VOIDmode, val,
|
gen_rtx_AND (SImode, gen_rtx_NOT (SImode, mask),
|
gen_rtx_AND (SImode, gen_rtx_NOT (SImode, mask),
|
val)));
|
val)));
|
|
|
oldval = gen_lowpart (SImode, oldval);
|
oldval = gen_lowpart (SImode, oldval);
|
emit_insn (gen_rtx_SET (VOIDmode, oldv,
|
emit_insn (gen_rtx_SET (VOIDmode, oldv,
|
gen_rtx_ASHIFT (SImode, oldval, off)));
|
gen_rtx_ASHIFT (SImode, oldval, off)));
|
|
|
newval = gen_lowpart_common (SImode, newval);
|
newval = gen_lowpart_common (SImode, newval);
|
emit_insn (gen_rtx_SET (VOIDmode, newv,
|
emit_insn (gen_rtx_SET (VOIDmode, newv,
|
gen_rtx_ASHIFT (SImode, newval, off)));
|
gen_rtx_ASHIFT (SImode, newval, off)));
|
|
|
emit_insn (gen_rtx_SET (VOIDmode, oldv,
|
emit_insn (gen_rtx_SET (VOIDmode, oldv,
|
gen_rtx_AND (SImode, oldv, mask)));
|
gen_rtx_AND (SImode, oldv, mask)));
|
|
|
emit_insn (gen_rtx_SET (VOIDmode, newv,
|
emit_insn (gen_rtx_SET (VOIDmode, newv,
|
gen_rtx_AND (SImode, newv, mask)));
|
gen_rtx_AND (SImode, newv, mask)));
|
|
|
end_label = gen_label_rtx ();
|
end_label = gen_label_rtx ();
|
loop_label = gen_label_rtx ();
|
loop_label = gen_label_rtx ();
|
emit_label (loop_label);
|
emit_label (loop_label);
|
|
|
emit_insn (gen_rtx_SET (VOIDmode, oldvalue,
|
emit_insn (gen_rtx_SET (VOIDmode, oldvalue,
|
gen_rtx_IOR (SImode, oldv, val)));
|
gen_rtx_IOR (SImode, oldv, val)));
|
|
|
emit_insn (gen_rtx_SET (VOIDmode, newvalue,
|
emit_insn (gen_rtx_SET (VOIDmode, newvalue,
|
gen_rtx_IOR (SImode, newv, val)));
|
gen_rtx_IOR (SImode, newv, val)));
|
|
|
emit_insn (gen_sync_compare_and_swapsi (res, memsi, oldvalue, newvalue));
|
emit_insn (gen_sync_compare_and_swapsi (res, memsi, oldvalue, newvalue));
|
|
|
emit_cmp_and_jump_insns (res, oldvalue, EQ, NULL, SImode, 0, end_label);
|
emit_cmp_and_jump_insns (res, oldvalue, EQ, NULL, SImode, 0, end_label);
|
|
|
emit_insn (gen_rtx_SET (VOIDmode, resv,
|
emit_insn (gen_rtx_SET (VOIDmode, resv,
|
gen_rtx_AND (SImode, gen_rtx_NOT (SImode, mask),
|
gen_rtx_AND (SImode, gen_rtx_NOT (SImode, mask),
|
res)));
|
res)));
|
|
|
cc = gen_compare_reg_1 (NE, resv, val);
|
cc = gen_compare_reg_1 (NE, resv, val);
|
emit_insn (gen_rtx_SET (VOIDmode, val, resv));
|
emit_insn (gen_rtx_SET (VOIDmode, val, resv));
|
|
|
/* Use cbranchcc4 to separate the compare and branch! */
|
/* Use cbranchcc4 to separate the compare and branch! */
|
emit_jump_insn (gen_cbranchcc4 (gen_rtx_NE (VOIDmode, cc, const0_rtx),
|
emit_jump_insn (gen_cbranchcc4 (gen_rtx_NE (VOIDmode, cc, const0_rtx),
|
cc, const0_rtx, loop_label));
|
cc, const0_rtx, loop_label));
|
|
|
emit_label (end_label);
|
emit_label (end_label);
|
|
|
emit_insn (gen_rtx_SET (VOIDmode, res,
|
emit_insn (gen_rtx_SET (VOIDmode, res,
|
gen_rtx_AND (SImode, res, mask)));
|
gen_rtx_AND (SImode, res, mask)));
|
|
|
emit_insn (gen_rtx_SET (VOIDmode, res,
|
emit_insn (gen_rtx_SET (VOIDmode, res,
|
gen_rtx_LSHIFTRT (SImode, res, off)));
|
gen_rtx_LSHIFTRT (SImode, res, off)));
|
|
|
emit_move_insn (result, gen_lowpart (GET_MODE (result), res));
|
emit_move_insn (result, gen_lowpart (GET_MODE (result), res));
|
}
|
}
|
|
|
/* Implement TARGET_FRAME_POINTER_REQUIRED. */
|
/* Implement TARGET_FRAME_POINTER_REQUIRED. */
|
|
|
bool
|
bool
|
sparc_frame_pointer_required (void)
|
sparc_frame_pointer_required (void)
|
{
|
{
|
return !(leaf_function_p () && only_leaf_regs_used ());
|
return !(leaf_function_p () && only_leaf_regs_used ());
|
}
|
}
|
|
|
/* The way this is structured, we can't eliminate SFP in favor of SP
|
/* The way this is structured, we can't eliminate SFP in favor of SP
|
if the frame pointer is required: we want to use the SFP->HFP elimination
|
if the frame pointer is required: we want to use the SFP->HFP elimination
|
in that case. But the test in update_eliminables doesn't know we are
|
in that case. But the test in update_eliminables doesn't know we are
|
assuming below that we only do the former elimination. */
|
assuming below that we only do the former elimination. */
|
|
|
bool
|
bool
|
sparc_can_eliminate (const int from ATTRIBUTE_UNUSED, const int to)
|
sparc_can_eliminate (const int from ATTRIBUTE_UNUSED, const int to)
|
{
|
{
|
return (to == HARD_FRAME_POINTER_REGNUM
|
return (to == HARD_FRAME_POINTER_REGNUM
|
|| !targetm.frame_pointer_required ());
|
|| !targetm.frame_pointer_required ());
|
}
|
}
|
|
|
#include "gt-sparc.h"
|
#include "gt-sparc.h"
|
|
|