Commit 84ea0f58 by Eric Botcazou Committed by Eric Botcazou

re PR target/69706 (internal compiler error: in extract_constrain_insn, at recog.c:2246)

	PR target/69706
	* config/sparc/sparc.c (ROUND_ADVANCE): Rename to...
	(NWORDS_UP): ...this
	(init_cumulative_args): Minor tweaks.
	(sparc_promote_function_mode): Likewise.
	(scan_record_type): Delete.
	(traverse_record_type): New function template.
	(classify_data_t): New structure type.
	(classify_registers): New inline function.
	(function_arg_slotno): In 64-bit mode, bail out early if FP slots are
	exhausted.  Instantiate traverse_record_type on classify_registers and
	deal with the case of a structure passed in slot #15 with no FP field
	in the first word.
	(assign_data_t): New structure type.
	(compute_int_layout): New static function.
	(compute_fp_layout): Likewise.
	(count_registers): New inline function.
	(assign_int_registers): New static function.
	(assign_fp_registers): Likewise.
	(assign_registers): New inline function.
	(function_arg_record_value_1): Delete.
	(function_arg_record_value_2): Likewise.
	(function_arg_record_value_3): Likewise.
	(function_arg_record_value): Adjust to above changes.  Instantiate
	traverse_record_type on count_registers to first count the number of
	registers to be used and then on assign_registers to assign them.
	(function_arg_union_value): Adjust to above renaming.
	(sparc_function_arg_1); Minor tweaks.  Remove commented out code.
	(sparc_arg_partial_bytes): Adjust to above renaming.  Deal with the
	case of a structure passed in slot #15
	(sparc_function_arg_advance): Likewise.
	(function_arg_padding): Minor tweak.

From-SVN: r233808
parent 8ea456b9
2016-02-29 Eric Botcazou <ebotcazou@adacore.com>
PR target/69706
* config/sparc/sparc.c (ROUND_ADVANCE): Rename to...
(NWORDS_UP): ...this
(init_cumulative_args): Minor tweaks.
(sparc_promote_function_mode): Likewise.
(scan_record_type): Delete.
(traverse_record_type): New function template.
(classify_data_t): New structure type.
(classify_registers): New inline function.
(function_arg_slotno): In 64-bit mode, bail out early if FP slots are
exhausted. Instantiate traverse_record_type on classify_registers and
deal with the case of a structure passed in slot #15 with no FP field
in the first word.
(assign_data_t): New structure type.
(compute_int_layout): New static function.
(compute_fp_layout): Likewise.
(count_registers): New inline function.
(assign_int_registers): New static function.
(assign_fp_registers): Likewise.
(assign_registers): New inline function.
(function_arg_record_value_1): Delete.
(function_arg_record_value_2): Likewise.
(function_arg_record_value_3): Likewise.
(function_arg_record_value): Adjust to above changes. Instantiate
traverse_record_type on count_registers to first count the number of
registers to be used and then on assign_registers to assign them.
(function_arg_union_value): Adjust to above renaming.
(sparc_function_arg_1); Minor tweaks. Remove commented out code.
(sparc_arg_partial_bytes): Adjust to above renaming. Deal with the
case of a structure passed in slot #15
(sparc_function_arg_advance): Likewise.
(function_arg_padding): Minor tweak.
2016-02-29 Richard Biener <rguenther@suse.de> 2016-02-29 Richard Biener <rguenther@suse.de>
PR tree-optimization/69720 PR tree-optimization/69720
......
...@@ -518,7 +518,6 @@ int sparc_indent_opcode = 0; ...@@ -518,7 +518,6 @@ int sparc_indent_opcode = 0;
static void sparc_option_override (void); static void sparc_option_override (void);
static void sparc_init_modes (void); static void sparc_init_modes (void);
static void scan_record_type (const_tree, int *, int *, int *);
static int function_arg_slotno (const CUMULATIVE_ARGS *, machine_mode, static int function_arg_slotno (const CUMULATIVE_ARGS *, machine_mode,
const_tree, bool, bool, int *, int *); const_tree, bool, bool, int *, int *);
...@@ -6086,8 +6085,8 @@ conventions. */ ...@@ -6086,8 +6085,8 @@ conventions. */
#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
/* Number of words (partially) occupied for a given size in units. */
#define ROUND_ADVANCE(SIZE) (((SIZE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) #define NWORDS_UP(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
...@@ -6095,25 +6094,20 @@ conventions. */ ...@@ -6095,25 +6094,20 @@ conventions. */
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, tree)
rtx libname ATTRIBUTE_UNUSED,
tree fndecl ATTRIBUTE_UNUSED)
{ {
cum->words = 0; cum->words = 0;
cum->prototype_p = fntype && prototype_p (fntype); cum->prototype_p = fntype && prototype_p (fntype);
cum->libcall_p = fntype == 0; cum->libcall_p = !fntype;
} }
/* Handle promotion of pointer and integer arguments. */ /* Handle promotion of pointer and integer arguments. */
static machine_mode static machine_mode
sparc_promote_function_mode (const_tree type, sparc_promote_function_mode (const_tree type, machine_mode mode,
machine_mode mode, int *punsignedp, const_tree, int)
int *punsignedp,
const_tree fntype ATTRIBUTE_UNUSED,
int for_return ATTRIBUTE_UNUSED)
{ {
if (type != NULL_TREE && POINTER_TYPE_P (type)) if (type && POINTER_TYPE_P (type))
{ {
*punsignedp = POINTERS_EXTEND_UNSIGNED; *punsignedp = POINTERS_EXTEND_UNSIGNED;
return Pmode; return Pmode;
...@@ -6135,36 +6129,75 @@ sparc_strict_argument_naming (cumulative_args_t ca ATTRIBUTE_UNUSED) ...@@ -6135,36 +6129,75 @@ sparc_strict_argument_naming (cumulative_args_t ca ATTRIBUTE_UNUSED)
return TARGET_ARCH64 ? true : false; return TARGET_ARCH64 ? true : false;
} }
/* Scan the record type TYPE and return the following predicates: /* Traverse the record TYPE recursively and call FUNC on its fields.
- INTREGS_P: the record contains at least one field or sub-field NAMED is true if this is for a named parameter. DATA is passed
that is eligible for promotion in integer registers. to FUNC for each field. OFFSET is the starting position and
- FP_REGS_P: the record contains at least one field or sub-field PACKED is true if we are inside a packed record. */
that is eligible for promotion in floating-point registers.
- PACKED_P: the record contains at least one field that is packed. */
template <typename T, void Func (const_tree, HOST_WIDE_INT, bool, T*)>
static void static void
scan_record_type (const_tree type, int *intregs_p, int *fpregs_p, traverse_record_type (const_tree type, bool named, T *data,
int *packed_p) HOST_WIDE_INT offset = 0, bool packed = false)
{ {
/* The ABI obviously doesn't specify how packed structures are passed.
These are passed in integer regs if possible, otherwise memory. */
if (!packed)
for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
if (TREE_CODE (field) == FIELD_DECL && DECL_PACKED (field))
{ {
packed = true;
break;
}
/* Walk the real fields, but skip those with no size or a zero size.
??? Fields with variable offset are handled as having zero offset. */
for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
if (TREE_CODE (field) == FIELD_DECL) if (TREE_CODE (field) == FIELD_DECL)
{ {
tree field_type = TREE_TYPE (field); if (!DECL_SIZE (field) || integer_zerop (DECL_SIZE (field)))
continue;
HOST_WIDE_INT bitpos = offset;
if (TREE_CODE (DECL_FIELD_OFFSET (field)) == INTEGER_CST)
bitpos += int_bit_position (field);
tree field_type = TREE_TYPE (field);
if (TREE_CODE (field_type) == RECORD_TYPE) if (TREE_CODE (field_type) == RECORD_TYPE)
scan_record_type (field_type, intregs_p, fpregs_p, packed_p); traverse_record_type<T, Func> (field_type, named, data, bitpos,
else if ((FLOAT_TYPE_P (field_type) packed);
|| TREE_CODE (field_type) == VECTOR_TYPE)
&& TARGET_FPU)
*fpregs_p = 1;
else else
*intregs_p = 1; {
const bool fp_type
if (DECL_PACKED (field)) = FLOAT_TYPE_P (field_type) || VECTOR_TYPE_P (field_type);
*packed_p = 1; Func (field, bitpos, fp_type && named && !packed && TARGET_FPU,
data);
}
} }
}
/* Handle recursive register classifying for structure layout. */
typedef struct
{
bool int_regs; /* true if field eligible to int registers. */
bool fp_regs; /* true if field eligible to FP registers. */
bool fp_regs_in_first_word; /* true if such field in first word. */
} classify_data_t;
/* A subroutine of function_arg_slotno. Classify the field. */
inline void
classify_registers (const_tree, HOST_WIDE_INT bitpos, bool fp,
classify_data_t *data)
{
if (fp)
{
data->fp_regs = true;
if (bitpos < BITS_PER_WORD)
data->fp_regs_in_first_word = true;
} }
else
data->int_regs = true;
} }
/* Compute the slot number to pass an argument in. /* Compute the slot number to pass an argument in.
...@@ -6178,16 +6211,16 @@ scan_record_type (const_tree type, int *intregs_p, int *fpregs_p, ...@@ -6178,16 +6211,16 @@ scan_record_type (const_tree type, int *intregs_p, int *fpregs_p,
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 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, machine_mode mode, function_arg_slotno (const struct sparc_args *cum, machine_mode mode,
const_tree type, bool named, bool incoming_p, const_tree type, bool named, bool incoming,
int *pregno, int *ppadding) int *pregno, int *ppadding)
{ {
int regbase = (incoming_p int regbase = (incoming
? 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;
...@@ -6243,8 +6276,10 @@ function_arg_slotno (const struct sparc_args *cum, machine_mode mode, ...@@ -6243,8 +6276,10 @@ function_arg_slotno (const struct sparc_args *cum, machine_mode mode,
case MODE_VECTOR_INT: case MODE_VECTOR_INT:
if (TARGET_ARCH64 && TARGET_FPU && named) if (TARGET_ARCH64 && TARGET_FPU && named)
{ {
/* If all arg slots are filled, then must pass on stack. */
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. */
...@@ -6256,8 +6291,10 @@ function_arg_slotno (const struct sparc_args *cum, machine_mode mode, ...@@ -6256,8 +6291,10 @@ function_arg_slotno (const struct sparc_args *cum, machine_mode mode,
case MODE_INT: case MODE_INT:
case MODE_COMPLEX_INT: case MODE_COMPLEX_INT:
/* If all arg slots are filled, then must pass on stack. */
if (slotno >= SPARC_INT_ARG_MAX) if (slotno >= SPARC_INT_ARG_MAX)
return -1; return -1;
regno = regbase + slotno; regno = regbase + slotno;
break; break;
...@@ -6270,42 +6307,43 @@ function_arg_slotno (const struct sparc_args *cum, machine_mode mode, ...@@ -6270,42 +6307,43 @@ function_arg_slotno (const struct sparc_args *cum, machine_mode mode,
if (TARGET_ARCH32 if (TARGET_ARCH32
|| !type || !type
|| (TREE_CODE (type) != VECTOR_TYPE || (TREE_CODE (type) != RECORD_TYPE
&& TREE_CODE (type) != RECORD_TYPE)) && TREE_CODE (type) != VECTOR_TYPE))
{ {
/* If all arg slots are filled, then must pass on stack. */
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; /* If all arg slots are filled, then must pass on stack. */
if (slotno >= SPARC_FP_ARG_MAX)
/* First see what kinds of registers we would need. */ return -1;
if (TREE_CODE (type) == VECTOR_TYPE)
fpregs_p = 1;
else
scan_record_type (type, &intregs_p, &fpregs_p, &packed_p);
/* The ABI obviously doesn't specify how packed structures if (TREE_CODE (type) == RECORD_TYPE)
are passed. These are defined to be passed in int regs {
if possible, otherwise memory. */ classify_data_t data = { false, false, false };
if (packed_p || !named) traverse_record_type<classify_data_t, classify_registers>
fpregs_p = 0, intregs_p = 1; (type, named, &data);
/* If all arg slots are filled, then must pass on stack. */ /* If all slots are filled except for the last one, but there
if (fpregs_p && slotno >= SPARC_FP_ARG_MAX) is no FP field in the first word, then must pass on stack. */
if (data.fp_regs
&& !data.fp_regs_in_first_word
&& slotno >= SPARC_FP_ARG_MAX - 1)
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 slots are filled,
then must pass on stack. */ then must pass on stack. */
if (!fpregs_p && intregs_p && slotno >= SPARC_INT_ARG_MAX) if (!data.fp_regs
&& data.int_regs
&& slotno >= SPARC_INT_ARG_MAX)
return -1; return -1;
}
/* Note that even if all int arg slots are filled, fp members may /* PREGNO isn't set since both int and FP regs can be used. */
still be passed in regs if such regs are available.
*PREGNO isn't set because there may be more than one, it's up
to the caller to compute them. */
return slotno; return slotno;
} }
break; break;
...@@ -6318,277 +6356,211 @@ function_arg_slotno (const struct sparc_args *cum, machine_mode mode, ...@@ -6318,277 +6356,211 @@ function_arg_slotno (const struct sparc_args *cum, machine_mode mode,
return slotno; return slotno;
} }
/* Handle recursive register counting for structure field layout. */ /* Handle recursive register counting/assigning for structure layout. */
struct function_arg_record_value_parms typedef struct
{ {
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 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 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. */ int nregs; /* number of words passed in registers. */
}; bool stack; /* true if part of the argument is on the stack. */
rtx ret; /* return expression being built. */
static void function_arg_record_value_3 } assign_data_t;
(HOST_WIDE_INT, struct function_arg_record_value_parms *);
static void function_arg_record_value_2
(const_tree, HOST_WIDE_INT, struct function_arg_record_value_parms *, bool);
static void function_arg_record_value_1
(const_tree, HOST_WIDE_INT, struct function_arg_record_value_parms *, bool);
static rtx function_arg_record_value (const_tree, machine_mode, int, int, int);
static rtx function_arg_union_value (int, machine_mode, int, int);
/* A subroutine of function_arg_record_value. Traverse the structure /* A subroutine of function_arg_record_value. Compute the number of integer
recursively and determine how many registers will be required. */ registers to be assigned between PARMS->intoffset and BITPOS. Return
true if at least one integer register is assigned or false otherwise. */
static void static bool
function_arg_record_value_1 (const_tree type, HOST_WIDE_INT startbitpos, compute_int_layout (HOST_WIDE_INT bitpos, assign_data_t *data, int *pnregs)
struct function_arg_record_value_parms *parms,
bool packed_p)
{ {
tree field; if (data->intoffset < 0)
return false;
/* We need to compute how many registers are needed so we can const int intoffset = data->intoffset;
allocate the PARALLEL but before we can do that we need to know data->intoffset = -1;
whether there are any packed fields. The ABI obviously doesn't
specify how structures are passed in this case, so they are
defined to be passed in int regs if possible, otherwise memory,
regardless of whether there are fp values present. */
if (! packed_p) const int this_slotno = data->slotno + intoffset / BITS_PER_WORD;
for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) const unsigned int startbit = ROUND_DOWN (intoffset, BITS_PER_WORD);
{ const unsigned int endbit = ROUND_UP (bitpos, BITS_PER_WORD);
if (TREE_CODE (field) == FIELD_DECL && DECL_PACKED (field)) int nregs = (endbit - startbit) / BITS_PER_WORD;
if (nregs > 0 && nregs > SPARC_INT_ARG_MAX - this_slotno)
{ {
packed_p = true; nregs = SPARC_INT_ARG_MAX - this_slotno;
break;
} /* We need to pass this field (partly) on the stack. */
data->stack = 1;
} }
/* Compute how many registers we need. */ if (nregs <= 0)
for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) return false;
{
if (TREE_CODE (field) == FIELD_DECL)
{
HOST_WIDE_INT bitpos = startbitpos;
if (DECL_SIZE (field) != 0) *pnregs = nregs;
{ return true;
if (integer_zerop (DECL_SIZE (field))) }
continue;
if (tree_fits_uhwi_p (bit_position (field))) /* A subroutine of function_arg_record_value. Compute the number and the mode
bitpos += int_bit_position (field); of the FP registers to be assigned for FIELD. Return true if at least one
} FP register is assigned or false otherwise. */
/* ??? FIXME: else assume zero offset. */ static bool
compute_fp_layout (const_tree field, HOST_WIDE_INT bitpos,
assign_data_t *data,
int *pnregs, machine_mode *pmode)
{
const int this_slotno = data->slotno + bitpos / BITS_PER_WORD;
machine_mode mode = DECL_MODE (field);
int nregs, nslots;
if (TREE_CODE (TREE_TYPE (field)) == RECORD_TYPE) /* Slots are counted as words while regs are counted as having the size of
function_arg_record_value_1 (TREE_TYPE (field), the (inner) mode. */
bitpos, if (TREE_CODE (TREE_TYPE (field)) == VECTOR_TYPE && mode == BLKmode)
parms,
packed_p);
else if ((FLOAT_TYPE_P (TREE_TYPE (field))
|| TREE_CODE (TREE_TYPE (field)) == VECTOR_TYPE)
&& TARGET_FPU
&& parms->named
&& ! packed_p)
{ {
if (parms->intoffset != -1) mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (field)));
nregs = TYPE_VECTOR_SUBPARTS (TREE_TYPE (field));
}
else if (TREE_CODE (TREE_TYPE (field)) == COMPLEX_TYPE)
{ {
unsigned int startbit, endbit; mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (field)));
int intslots, this_slotno; nregs = 2;
}
startbit = ROUND_DOWN (parms->intoffset, BITS_PER_WORD); else
endbit = ROUND_UP (bitpos, BITS_PER_WORD); nregs = 1;
intslots = (endbit - startbit) / BITS_PER_WORD; nslots = NWORDS_UP (nregs * GET_MODE_SIZE (mode));
this_slotno = parms->slotno + parms->intoffset
/ BITS_PER_WORD;
if (intslots > 0 && intslots > SPARC_INT_ARG_MAX - this_slotno) if (nslots > SPARC_FP_ARG_MAX - this_slotno)
{ {
intslots = MAX (0, SPARC_INT_ARG_MAX - this_slotno); nslots = SPARC_FP_ARG_MAX - this_slotno;
/* We need to pass this field on the stack. */ nregs = (nslots * UNITS_PER_WORD) / GET_MODE_SIZE (mode);
parms->stack = 1;
} /* We need to pass this field (partly) on the stack. */
data->stack = 1;
parms->nregs += intslots; if (nregs <= 0)
parms->intoffset = -1; return false;
} }
/* There's no need to check this_slotno < SPARC_FP_ARG MAX. *pnregs = nregs;
If it wasn't true we wouldn't be here. */ *pmode = mode;
if (TREE_CODE (TREE_TYPE (field)) == VECTOR_TYPE return true;
&& DECL_MODE (field) == BLKmode) }
parms->nregs += TYPE_VECTOR_SUBPARTS (TREE_TYPE (field));
else if (TREE_CODE (TREE_TYPE (field)) == COMPLEX_TYPE) /* A subroutine of function_arg_record_value. Count the number of registers
parms->nregs += 2; to be assigned for FIELD and between PARMS->intoffset and BITPOS. */
else
parms->nregs += 1; inline void
count_registers (const_tree field, HOST_WIDE_INT bitpos, bool fp,
assign_data_t *data)
{
if (fp)
{
int nregs;
machine_mode mode;
if (compute_int_layout (bitpos, data, &nregs))
data->nregs += nregs;
if (compute_fp_layout (field, bitpos, data, &nregs, &mode))
data->nregs += nregs;
} }
else else
{ {
if (parms->intoffset == -1) if (data->intoffset < 0)
parms->intoffset = bitpos; data->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, assign_int_registers (HOST_WIDE_INT bitpos, assign_data_t *data)
struct function_arg_record_value_parms *parms)
{ {
int intoffset = data->intoffset;
machine_mode mode; machine_mode mode;
unsigned int regno; int nregs;
unsigned int startbit, endbit;
int this_slotno, intslots, intoffset;
rtx reg;
if (parms->intoffset == -1)
return;
intoffset = parms->intoffset;
parms->intoffset = -1;
startbit = ROUND_DOWN (intoffset, BITS_PER_WORD);
endbit = ROUND_UP (bitpos, BITS_PER_WORD);
intslots = (endbit - startbit) / BITS_PER_WORD;
this_slotno = parms->slotno + intoffset / BITS_PER_WORD;
intslots = MIN (intslots, SPARC_INT_ARG_MAX - this_slotno); if (!compute_int_layout (bitpos, data, &nregs))
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;
const int this_slotno = data->slotno + intoffset / BITS_PER_WORD;
unsigned int regno = data->regbase + this_slotno;
intoffset /= BITS_PER_UNIT; intoffset /= BITS_PER_UNIT;
do do
{ {
regno = parms->regbase + this_slotno; rtx reg = gen_rtx_REG (mode, regno);
reg = gen_rtx_REG (mode, regno); XVECEXP (data->ret, 0, data->stack + data->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));
data->nregs += 1;
this_slotno += 1;
intoffset = (intoffset | (UNITS_PER_WORD-1)) + 1;
mode = word_mode; mode = word_mode;
parms->nregs += 1; regno += 1;
intslots -= 1; intoffset = (intoffset | (UNITS_PER_WORD - 1)) + 1;
} }
while (intslots > 0); while (--nregs > 0);
} }
/* A subroutine of function_arg_record_value. Traverse the structure /* A subroutine of function_arg_record_value. Assign FIELD at position
recursively and assign bits to floating point registers. Track which BITPOS to FP registers. */
bits in between need integer registers; invoke function_arg_record_value_3
to make that happen. */
static void static void
function_arg_record_value_2 (const_tree type, HOST_WIDE_INT startbitpos, assign_fp_registers (const_tree field, HOST_WIDE_INT bitpos,
struct function_arg_record_value_parms *parms, assign_data_t *data)
bool packed_p)
{ {
tree field; int nregs;
machine_mode mode;
if (! packed_p) if (!compute_fp_layout (field, bitpos, data, &nregs, &mode))
for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) return;
{
if (TREE_CODE (field) == FIELD_DECL && DECL_PACKED (field))
{
packed_p = true;
break;
}
}
for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) const int this_slotno = data->slotno + bitpos / BITS_PER_WORD;
{ int regno = SPARC_FP_ARG_FIRST + this_slotno * 2;
if (TREE_CODE (field) == FIELD_DECL) if (GET_MODE_SIZE (mode) <= 4 && (bitpos & 32) != 0)
{ regno++;
HOST_WIDE_INT bitpos = startbitpos; int pos = bitpos / BITS_PER_UNIT;
if (DECL_SIZE (field) != 0) do
{ {
if (integer_zerop (DECL_SIZE (field))) rtx reg = gen_rtx_REG (mode, regno);
continue; XVECEXP (data->ret, 0, data->stack + data->nregs)
= gen_rtx_EXPR_LIST (VOIDmode, reg, GEN_INT (pos));
if (tree_fits_uhwi_p (bit_position (field))) data->nregs += 1;
bitpos += int_bit_position (field); regno += GET_MODE_SIZE (mode) / 4;
pos += GET_MODE_SIZE (mode);
} }
while (--nregs > 0);
}
/* ??? FIXME: else assume zero offset. */ /* A subroutine of function_arg_record_value. Assign FIELD and the bits of
the structure between PARMS->intoffset and BITPOS to registers. */
if (TREE_CODE (TREE_TYPE (field)) == RECORD_TYPE)
function_arg_record_value_2 (TREE_TYPE (field),
bitpos,
parms,
packed_p);
else if ((FLOAT_TYPE_P (TREE_TYPE (field))
|| TREE_CODE (TREE_TYPE (field)) == VECTOR_TYPE)
&& TARGET_FPU
&& parms->named
&& ! packed_p)
{
int this_slotno = parms->slotno + bitpos / BITS_PER_WORD;
int regno, nregs, pos;
machine_mode mode = DECL_MODE (field);
rtx reg;
function_arg_record_value_3 (bitpos, parms);
if (TREE_CODE (TREE_TYPE (field)) == VECTOR_TYPE inline void
&& mode == BLKmode) assign_registers (const_tree field, HOST_WIDE_INT bitpos, bool fp,
{ assign_data_t *data)
mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (field))); {
nregs = TYPE_VECTOR_SUBPARTS (TREE_TYPE (field)); if (fp)
}
else if (TREE_CODE (TREE_TYPE (field)) == COMPLEX_TYPE)
{ {
mode = TYPE_MODE (TREE_TYPE (TREE_TYPE (field))); assign_int_registers (bitpos, data);
nregs = 2;
}
else
nregs = 1;
regno = SPARC_FP_ARG_FIRST + this_slotno * 2; assign_fp_registers (field, bitpos, data);
if (GET_MODE_SIZE (mode) <= 4 && (bitpos & 32) != 0)
regno++;
reg = gen_rtx_REG (mode, regno);
pos = bitpos / BITS_PER_UNIT;
XVECEXP (parms->ret, 0, parms->stack + parms->nregs)
= gen_rtx_EXPR_LIST (VOIDmode, reg, GEN_INT (pos));
parms->nregs += 1;
while (--nregs > 0)
{
regno += GET_MODE_SIZE (mode) / 4;
reg = gen_rtx_REG (mode, regno);
pos += GET_MODE_SIZE (mode);
XVECEXP (parms->ret, 0, parms->stack + parms->nregs)
= gen_rtx_EXPR_LIST (VOIDmode, reg, GEN_INT (pos));
parms->nregs += 1;
}
} }
else else
{ {
if (parms->intoffset == -1) if (data->intoffset < 0)
parms->intoffset = bitpos; data->intoffset = bitpos;
}
}
} }
} }
...@@ -6602,52 +6574,33 @@ function_arg_record_value_2 (const_tree type, HOST_WIDE_INT startbitpos, ...@@ -6602,52 +6574,33 @@ function_arg_record_value_2 (const_tree type, HOST_WIDE_INT startbitpos,
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 true 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, machine_mode mode, function_arg_record_value (const_tree type, machine_mode mode,
int slotno, int named, int regbase) int slotno, bool 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; assign_data_t data;
unsigned int nregs; int nregs;
parms.ret = NULL_RTX; data.slotno = slotno;
parms.slotno = slotno; data.regbase = regbase;
parms.named = named;
parms.regbase = regbase;
parms.stack = 0;
/* Compute how many registers we need. */ /* Count how many registers we need. */
parms.nregs = 0; data.nregs = 0;
parms.intoffset = 0; data.intoffset = 0;
function_arg_record_value_1 (type, 0, &parms, false); data.stack = false;
traverse_record_type<assign_data_t, count_registers> (type, named, &data);
/* Take into account pending integer fields. */ /* Take into account pending integer fields. */
if (parms.intoffset != -1) if (compute_int_layout (typesize * BITS_PER_UNIT, &data, &nregs))
{ data.nregs += nregs;
unsigned int startbit, endbit;
int intslots, this_slotno;
startbit = ROUND_DOWN (parms.intoffset, BITS_PER_WORD);
endbit = ROUND_UP (typesize*BITS_PER_UNIT, BITS_PER_WORD);
intslots = (endbit - startbit) / BITS_PER_WORD;
this_slotno = slotno + parms.intoffset / BITS_PER_WORD;
if (intslots > 0 && intslots > SPARC_INT_ARG_MAX - this_slotno)
{
intslots = MAX (0, SPARC_INT_ARG_MAX - this_slotno);
/* We need to pass this field on the stack. */
parms.stack = 1;
}
parms.nregs += intslots;
}
/* Allocate the vector and handle some annoying special cases. */ /* Allocate the vector and handle some annoying special cases. */
nregs = parms.nregs; nregs = data.nregs;
if (nregs == 0) if (nregs == 0)
{ {
...@@ -6670,7 +6623,7 @@ function_arg_record_value (const_tree type, machine_mode mode, ...@@ -6670,7 +6623,7 @@ function_arg_record_value (const_tree type, machine_mode mode,
gcc_assert (nregs > 0); gcc_assert (nregs > 0);
parms.ret = gen_rtx_PARALLEL (mode, rtvec_alloc (parms.stack + nregs)); data.ret = gen_rtx_PARALLEL (mode, rtvec_alloc (data.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
...@@ -6678,19 +6631,21 @@ function_arg_record_value (const_tree type, machine_mode mode, ...@@ -6678,19 +6631,21 @@ function_arg_record_value (const_tree type, machine_mode mode,
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 (data.stack)
XVECEXP (parms.ret, 0, 0) XVECEXP (data.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. */ /* Assign the registers. */
parms.nregs = 0; data.nregs = 0;
parms.intoffset = 0; data.intoffset = 0;
function_arg_record_value_2 (type, 0, &parms, false); traverse_record_type<assign_data_t, assign_registers> (type, named, &data);
function_arg_record_value_3 (typesize * BITS_PER_UNIT, &parms);
gcc_assert (parms.nregs == nregs); /* Assign pending integer fields. */
assign_int_registers (typesize * BITS_PER_UNIT, &data);
return parms.ret; gcc_assert (data.nregs == nregs);
return data.ret;
} }
/* Used by function_arg and sparc_function_value_1 to implement the conventions /* Used by function_arg and sparc_function_value_1 to implement the conventions
...@@ -6706,7 +6661,7 @@ static rtx ...@@ -6706,7 +6661,7 @@ static rtx
function_arg_union_value (int size, machine_mode mode, int slotno, function_arg_union_value (int size, machine_mode mode, int slotno,
int regno) int regno)
{ {
int nwords = ROUND_ADVANCE (size), i; int nwords = NWORDS_UP (size), i;
rtx regs; rtx regs;
/* See comment in previous function for empty structures. */ /* See comment in previous function for empty structures. */
...@@ -6777,17 +6732,17 @@ function_arg_vector_value (int size, int regno) ...@@ -6777,17 +6732,17 @@ function_arg_vector_value (int size, int regno)
static rtx static rtx
sparc_function_arg_1 (cumulative_args_t cum_v, machine_mode mode, sparc_function_arg_1 (cumulative_args_t cum_v, machine_mode mode,
const_tree type, bool named, bool incoming_p) const_tree type, bool named, bool incoming)
{ {
const CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); const CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
int regbase = (incoming_p int regbase = (incoming
? 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,
&regno, &padding); &regno, &padding);
if (slotno == -1) if (slotno == -1)
return 0; return 0;
...@@ -6837,35 +6792,7 @@ sparc_function_arg_1 (cumulative_args_t cum_v, machine_mode mode, ...@@ -6837,35 +6792,7 @@ sparc_function_arg_1 (cumulative_args_t cum_v, machine_mode mode,
{ {
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
quantities. */
#if 0
/* ??? 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
value in the reg but reserve space on the stack. That's an
optimization, and is deferred [for a bit]. */
if ((regno - SPARC_FP_ARG_FIRST) >= SPARC_INT_ARG_MAX * 2)
return gen_rtx_PARALLEL (mode,
gen_rtvec (2,
gen_rtx_EXPR_LIST (VOIDmode,
NULL_RTX, const0_rtx),
gen_rtx_EXPR_LIST (VOIDmode,
reg, const0_rtx)));
else
#else
/* ??? It seems that passing back a register even when past
the area declared by REG_PARM_STACK_SPACE will allocate
space appropriately, and will not copy the data onto the
stack, exactly as we desire.
This is due to locate_and_pad_parm being called in
expand_call whenever reg_parm_stack_space > 0, which
while beneficial to our example here, would seem to be
in error from what had been intended. Ho hum... -- r~ */
#endif
return reg; return reg;
}
else else
{ {
rtx v0, v1; rtx v0, v1;
...@@ -6877,7 +6804,7 @@ sparc_function_arg_1 (cumulative_args_t cum_v, machine_mode mode, ...@@ -6877,7 +6804,7 @@ sparc_function_arg_1 (cumulative_args_t cum_v, machine_mode mode,
/* 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)
return reg; return reg;
intreg = (SPARC_OUTGOING_INT_ARG_FIRST intreg = (SPARC_OUTGOING_INT_ARG_FIRST
...@@ -6956,7 +6883,7 @@ sparc_arg_partial_bytes (cumulative_args_t cum, machine_mode mode, ...@@ -6956,7 +6883,7 @@ sparc_arg_partial_bytes (cumulative_args_t cum, machine_mode mode,
{ {
int slotno, regno, padding; int slotno, regno, padding;
/* We pass false for incoming_p here, it doesn't matter. */ /* We pass false for incoming here, it doesn't matter. */
slotno = function_arg_slotno (get_cumulative_args (cum), mode, type, named, slotno = function_arg_slotno (get_cumulative_args (cum), mode, type, named,
false, &regno, &padding); false, &regno, &padding);
...@@ -6966,8 +6893,8 @@ sparc_arg_partial_bytes (cumulative_args_t cum, machine_mode mode, ...@@ -6966,8 +6893,8 @@ sparc_arg_partial_bytes (cumulative_args_t cum, machine_mode mode,
if (TARGET_ARCH32) if (TARGET_ARCH32)
{ {
if ((slotno + (mode == BLKmode if ((slotno + (mode == BLKmode
? ROUND_ADVANCE (int_size_in_bytes (type)) ? NWORDS_UP (int_size_in_bytes (type))
: ROUND_ADVANCE (GET_MODE_SIZE (mode)))) : NWORDS_UP (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;
} }
...@@ -6982,7 +6909,8 @@ sparc_arg_partial_bytes (cumulative_args_t cum, machine_mode mode, ...@@ -6982,7 +6909,8 @@ sparc_arg_partial_bytes (cumulative_args_t cum, machine_mode mode,
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
|| slotno == SPARC_FP_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
...@@ -7068,18 +6996,16 @@ sparc_function_arg_advance (cumulative_args_t cum_v, machine_mode mode, ...@@ -7068,18 +6996,16 @@ sparc_function_arg_advance (cumulative_args_t cum_v, machine_mode mode,
CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v); CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
int regno, padding; int regno, padding;
/* We pass false for incoming_p here, it doesn't matter. */ /* We pass false for incoming here, it doesn't matter. */
function_arg_slotno (cum, mode, type, named, false, &regno, &padding); function_arg_slotno (cum, mode, type, named, false, &regno, &padding);
/* If argument requires leading padding, add it. */ /* If argument requires leading padding, add it. */
cum->words += padding; cum->words += padding;
if (TARGET_ARCH32) if (TARGET_ARCH32)
{ cum->words += (mode == BLKmode
cum->words += (mode != BLKmode ? NWORDS_UP (int_size_in_bytes (type))
? ROUND_ADVANCE (GET_MODE_SIZE (mode)) : NWORDS_UP (GET_MODE_SIZE (mode)));
: ROUND_ADVANCE (int_size_in_bytes (type)));
}
else else
{ {
if (type && AGGREGATE_TYPE_P (type)) if (type && AGGREGATE_TYPE_P (type))
...@@ -7094,11 +7020,9 @@ sparc_function_arg_advance (cumulative_args_t cum_v, machine_mode mode, ...@@ -7094,11 +7020,9 @@ sparc_function_arg_advance (cumulative_args_t cum_v, machine_mode mode,
++cum->words; ++cum->words;
} }
else else
{ cum->words += (mode == BLKmode
cum->words += (mode != BLKmode ? NWORDS_UP (int_size_in_bytes (type))
? ROUND_ADVANCE (GET_MODE_SIZE (mode)) : NWORDS_UP (GET_MODE_SIZE (mode)));
: ROUND_ADVANCE (int_size_in_bytes (type)));
}
} }
} }
...@@ -7109,7 +7033,7 @@ sparc_function_arg_advance (cumulative_args_t cum_v, machine_mode mode, ...@@ -7109,7 +7033,7 @@ sparc_function_arg_advance (cumulative_args_t cum_v, machine_mode mode,
enum direction enum direction
function_arg_padding (machine_mode mode, const_tree type) function_arg_padding (machine_mode mode, const_tree type)
{ {
if (TARGET_ARCH64 && type != 0 && AGGREGATE_TYPE_P (type)) if (TARGET_ARCH64 && type && AGGREGATE_TYPE_P (type))
return upward; return upward;
/* Fall back to the default. */ /* Fall back to the default. */
......
2016-02-29 Eric Botcazou <ebotcazou@adacore.com> 2016-02-29 Eric Botcazou <ebotcazou@adacore.com>
* gcc.target/sparc/20160229-1.c: New test.
2016-02-29 Eric Botcazou <ebotcazou@adacore.com>
* gnat.dg/stack_usage3.adb: New test. * gnat.dg/stack_usage3.adb: New test.
* gnat.dg/stack_usage3_pkg.ads: New helper. * gnat.dg/stack_usage3_pkg.ads: New helper.
......
/* PR target/69706 */
/* Reported by John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de> */
/* { dg-do run } */
/* { dg-options "-std=gnu99" }
/* { dg-require-effective-target lp64 } */
extern void abort (void);
/* Pass a 12-byte structure partially in slot #15 and on the stack. */
struct t_rgb { float r, g, b; };
void write_xpm (void *out, unsigned int flags, const char *title,
const char *legend, const char *label_x, const char *label_y,
int n_x, int n_y, float axis_x[], float axis_y[], float *mat[],
float lo, float hi, struct t_rgb rlo, struct t_rgb rhi)
{
register float f30 asm ("f30");
register float f31 asm ("f31");
if (f30 != 1.0f)
abort ();
if (f31 != 2.0f)
abort ();
if (rhi.r != 1.0f)
abort ();
if (rhi.g != 2.0f)
abort ();
if (rhi.b != 3.0f)
abort ();
}
/* Pass a 16-byte structure partially in slot #15 and on the stack. */
struct S1 { _Complex float f1; _Complex float f2; };
void f1 (int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8,
int p9, int p10, int p11, int p12, int p13, int p14, int p15,
struct S1 s1)
{
register float f30 asm ("f30");
register float f31 asm ("f31");
if (f30 != 4.0f)
abort ();
if (f31 != 5.0f)
abort ();
if (__real__ s1.f1 != 4.0f)
abort ();
if (__imag__ s1.f1 != 5.0f)
abort ();
if (__real__ s1.f2 != 6.0f)
abort ();
if (__imag__ s1.f2 != 7.0f)
abort ();
}
/* Pass a 16-byte structure partially in slot #15 and on the stack. */
struct S2 { double d1; double d2; };
void f2 (int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8,
int p9, int p10, int p11, int p12, int p13, int p14, int p15,
struct S2 s2)
{
register double d30 asm ("f30");
if (d30 != 1.0)
abort ();
if (s2.d1 != 1.0)
abort ();
if (s2.d2 != 2.0)
abort ();
}
/* Pass a 16-byte structure partially in slot #15 and on the stack. */
struct S3 { _Complex double d; };
void f3 (int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8,
int p9, int p10, int p11, int p12, int p13, int p14, int p15,
struct S3 s3)
{
register double d30 asm ("f30");
if (d30 != 3.0)
abort ();
if (__real__ s3.d != 3.0)
abort ();
if (__imag__ s3.d != 4.0)
abort ();
}
/* Pass a 16-byte structure entirely on the stack. */
struct S4 { long l; double d; };
void f4 (int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8,
int p9, int p10, int p11, int p12, int p13, int p14, int p15,
struct S4 s4)
{
if (s4.l != 5)
abort ();
if (s4.d != 6.0)
abort ();
}
#define PI 3.141592654
int main (void)
{
struct t_rgb lo = { -1.0f, -2.0f, -3.0f };
struct t_rgb hi = { 1.0f, 2.0f, 3.0f };
float arrf[1];
float *arrp[1];
struct S1 s1 = { 4.0f + 5.0fi, 6.0f + 7.0fi };
struct S2 s2 = { 1.0, 2.0 };
struct S3 s3 = { 3.0 + 4.0i };
struct S4 s4 = { 5, 6.0 };
register double d32 asm ("f32") = PI;
write_xpm (0, 0, "", "", "", "", 0, 0, arrf, arrf, arrp, 0.0f, 0.0f, lo, hi);
f1 (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, s1);
f2 (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, s2);
f3 (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, s3);
f4 (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, s4);
if (d32 != PI)
abort ();
return 0;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment