Merge pull request #194 from amodra/master

Correct powerpc sysv stack argument accounting
This commit is contained in:
Anthony Green
2016-02-20 06:49:40 -05:00

View File

@@ -93,7 +93,7 @@ ffi_prep_cif_sysv_core (ffi_cif *cif)
{
ffi_type **ptr;
unsigned bytes;
unsigned i, fparg_count = 0, intarg_count = 0;
unsigned i, fpr_count = 0, gpr_count = 0, stack_count = 0;
unsigned flags = cif->flags;
unsigned struct_copy_size = 0;
unsigned type = cif->rtype->type;
@@ -155,7 +155,7 @@ ffi_prep_cif_sysv_core (ffi_cif *cif)
flags |= FLAG_RETURNS_SMST;
break;
}
intarg_count++;
gpr_count++;
flags |= FLAG_RETVAL_REFERENCE;
/* Fall through. */
case FFI_TYPE_VOID:
@@ -182,24 +182,41 @@ ffi_prep_cif_sysv_core (ffi_cif *cif)
{
#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
case FFI_TYPE_LONGDOUBLE:
fparg_count++;
/* Fall thru */
if (fpr_count >= NUM_FPR_ARG_REGISTERS - 1)
{
fpr_count = NUM_FPR_ARG_REGISTERS;
/* 8-byte align long doubles. */
stack_count += stack_count & 1;
stack_count += 4;
}
else
fpr_count += 2;
#ifdef __NO_FPRS__
return FFI_BAD_ABI;
#endif
break;
#endif
case FFI_TYPE_DOUBLE:
fparg_count++;
/* If this FP arg is going on the stack, it must be
8-byte-aligned. */
if (fparg_count > NUM_FPR_ARG_REGISTERS
&& intarg_count >= NUM_GPR_ARG_REGISTERS
&& intarg_count % 2 != 0)
intarg_count++;
if (fpr_count >= NUM_FPR_ARG_REGISTERS)
{
/* 8-byte align doubles. */
stack_count += stack_count & 1;
stack_count += 2;
}
else
fpr_count += 1;
#ifdef __NO_FPRS__
return FFI_BAD_ABI;
#endif
break;
case FFI_TYPE_FLOAT:
fparg_count++;
if (fpr_count >= NUM_FPR_ARG_REGISTERS)
/* Yes, we don't follow the ABI, but neither does gcc. */
stack_count += 1;
else
fpr_count += 1;
#ifdef __NO_FPRS__
return FFI_BAD_ABI;
#endif
@@ -208,11 +225,13 @@ ffi_prep_cif_sysv_core (ffi_cif *cif)
case FFI_TYPE_UINT128:
/* A long double in FFI_LINUX_SOFT_FLOAT can use only a set
of four consecutive gprs. If we do not have enough, we
have to adjust the intarg_count value. */
if (intarg_count >= NUM_GPR_ARG_REGISTERS - 3
&& intarg_count < NUM_GPR_ARG_REGISTERS)
intarg_count = NUM_GPR_ARG_REGISTERS;
intarg_count += 4;
have to adjust the gpr_count value. */
if (gpr_count >= NUM_GPR_ARG_REGISTERS - 3)
gpr_count = NUM_GPR_ARG_REGISTERS;
if (gpr_count >= NUM_GPR_ARG_REGISTERS)
stack_count += 4;
else
gpr_count += 4;
break;
case FFI_TYPE_UINT64:
@@ -225,10 +244,14 @@ ffi_prep_cif_sysv_core (ffi_cif *cif)
Also, only certain register pairs can be used for
passing long long int -- specifically (r3,r4), (r5,r6),
(r7,r8), (r9,r10). */
if (intarg_count == NUM_GPR_ARG_REGISTERS-1
|| intarg_count % 2 != 0)
intarg_count++;
intarg_count += 2;
gpr_count += gpr_count & 1;
if (gpr_count >= NUM_GPR_ARG_REGISTERS)
{
stack_count += stack_count & 1;
stack_count += 2;
}
else
gpr_count += 2;
break;
case FFI_TYPE_STRUCT:
@@ -249,7 +272,10 @@ ffi_prep_cif_sysv_core (ffi_cif *cif)
case FFI_TYPE_SINT8:
/* Everything else is passed as a 4-byte word in a GPR, either
the object itself or a pointer to it. */
intarg_count++;
if (gpr_count >= NUM_GPR_ARG_REGISTERS)
stack_count += 1;
else
gpr_count += 1;
break;
default:
@@ -257,22 +283,19 @@ ffi_prep_cif_sysv_core (ffi_cif *cif)
}
}
if (fparg_count != 0)
if (fpr_count != 0)
flags |= FLAG_FP_ARGUMENTS;
if (intarg_count > 4)
if (gpr_count > 4)
flags |= FLAG_4_GPR_ARGUMENTS;
if (struct_copy_size != 0)
flags |= FLAG_ARG_NEEDS_COPY;
/* Space for the FPR registers, if needed. */
if (fparg_count != 0)
if (fpr_count != 0)
bytes += NUM_FPR_ARG_REGISTERS * sizeof (double);
/* Stack space. */
if (intarg_count > NUM_GPR_ARG_REGISTERS)
bytes += (intarg_count - NUM_GPR_ARG_REGISTERS) * sizeof (int);
if (fparg_count > NUM_FPR_ARG_REGISTERS)
bytes += (fparg_count - NUM_FPR_ARG_REGISTERS) * sizeof (double);
bytes += stack_count * sizeof (int);
/* The stack space allocated needs to be a multiple of 16 bytes. */
bytes = (bytes + 15) & ~0xF;
@@ -367,13 +390,13 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack)
/* 'gpr_base' points at the space for gpr3, and grows upwards as
we use GPR registers. */
valp gpr_base;
int intarg_count;
valp gpr_end;
#ifndef __NO_FPRS__
/* 'fpr_base' points at the space for fpr1, and grows upwards as
we use FPR registers. */
valp fpr_base;
int fparg_count;
valp fpr_end;
#endif
/* 'copy_space' grows down as we put structures in it. It should
@@ -405,11 +428,11 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack)
unsigned gprvalue;
stacktop.c = (char *) stack + bytes;
gpr_base.u = stacktop.u - ASM_NEEDS_REGISTERS - NUM_GPR_ARG_REGISTERS;
intarg_count = 0;
gpr_end.u = stacktop.u - ASM_NEEDS_REGISTERS;
gpr_base.u = gpr_end.u - NUM_GPR_ARG_REGISTERS;
#ifndef __NO_FPRS__
fpr_base.d = gpr_base.d - NUM_FPR_ARG_REGISTERS;
fparg_count = 0;
fpr_end.d = gpr_base.d;
fpr_base.d = fpr_end.d - NUM_FPR_ARG_REGISTERS;
copy_space.c = ((flags & FLAG_FP_ARGUMENTS) ? fpr_base.c : gpr_base.c);
#else
copy_space.c = gpr_base.c;
@@ -425,10 +448,7 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack)
/* Deal with return values that are actually pass-by-reference. */
if (flags & FLAG_RETVAL_REFERENCE)
{
*gpr_base.u++ = (unsigned long) (char *) ecif->rvalue;
intarg_count++;
}
*gpr_base.u++ = (unsigned) (char *) ecif->rvalue;
/* Now for the arguments. */
p_argv.v = ecif->avalue;
@@ -448,14 +468,11 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack)
case FFI_TYPE_LONGDOUBLE:
double_tmp = (*p_argv.d)[0];
if (fparg_count >= NUM_FPR_ARG_REGISTERS - 1)
if (fpr_base.d >= fpr_end.d - 1)
{
if (intarg_count >= NUM_GPR_ARG_REGISTERS
&& intarg_count % 2 != 0)
{
intarg_count++;
next_arg.u++;
}
fpr_base.d = fpr_end.d;
if (((next_arg.u - stack) & 1) != 0)
next_arg.u += 1;
*next_arg.d = double_tmp;
next_arg.u += 2;
double_tmp = (*p_argv.d)[1];
@@ -468,42 +485,33 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack)
double_tmp = (*p_argv.d)[1];
*fpr_base.d++ = double_tmp;
}
fparg_count += 2;
FFI_ASSERT (flags & FLAG_FP_ARGUMENTS);
break;
# endif
case FFI_TYPE_DOUBLE:
double_tmp = **p_argv.d;
if (fparg_count >= NUM_FPR_ARG_REGISTERS)
if (fpr_base.d >= fpr_end.d)
{
if (intarg_count >= NUM_GPR_ARG_REGISTERS
&& intarg_count % 2 != 0)
{
intarg_count++;
next_arg.u++;
}
if (((next_arg.u - stack) & 1) != 0)
next_arg.u += 1;
*next_arg.d = double_tmp;
next_arg.u += 2;
}
else
*fpr_base.d++ = double_tmp;
fparg_count++;
FFI_ASSERT (flags & FLAG_FP_ARGUMENTS);
break;
case FFI_TYPE_FLOAT:
double_tmp = **p_argv.f;
if (fparg_count >= NUM_FPR_ARG_REGISTERS)
if (fpr_base.d >= fpr_end.d)
{
*next_arg.f = (float) double_tmp;
next_arg.u += 1;
intarg_count++;
}
else
*fpr_base.d++ = double_tmp;
fparg_count++;
FFI_ASSERT (flags & FLAG_FP_ARGUMENTS);
break;
#endif /* have FPRs */
@@ -513,42 +521,34 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack)
is passed in four consecutive GPRs if available. A maximum of 2
long doubles can be passed in gprs. If we do not have 4 GPRs
left, the long double is passed on the stack, 4-byte aligned. */
{
unsigned int int_tmp;
unsigned int ii;
if (intarg_count >= NUM_GPR_ARG_REGISTERS - 3)
{
if (intarg_count < NUM_GPR_ARG_REGISTERS)
intarg_count = NUM_GPR_ARG_REGISTERS;
for (ii = 0; ii < 4; ii++)
{
int_tmp = (*p_argv.ui)[ii];
*next_arg.u++ = int_tmp;
}
}
else
{
for (ii = 0; ii < 4; ii++)
{
int_tmp = (*p_argv.ui)[ii];
*gpr_base.u++ = int_tmp;
}
}
intarg_count += 4;
break;
}
if (gpr_base.u >= gpr_end.u - 3)
{
unsigned int ii;
gpr_base.u = gpr_end.u;
for (ii = 0; ii < 4; ii++)
{
unsigned int int_tmp = (*p_argv.ui)[ii];
*next_arg.u++ = int_tmp;
}
}
else
{
unsigned int ii;
for (ii = 0; ii < 4; ii++)
{
unsigned int int_tmp = (*p_argv.ui)[ii];
*gpr_base.u++ = int_tmp;
}
}
break;
case FFI_TYPE_UINT64:
case FFI_TYPE_SINT64:
if (intarg_count == NUM_GPR_ARG_REGISTERS-1)
intarg_count++;
if (intarg_count >= NUM_GPR_ARG_REGISTERS)
if (gpr_base.u >= gpr_end.u - 1)
{
if (intarg_count % 2 != 0)
{
intarg_count++;
next_arg.u++;
}
gpr_base.u = gpr_end.u;
if (((next_arg.u - stack) & 1) != 0)
next_arg.u++;
*next_arg.ll = **p_argv.ll;
next_arg.u += 2;
}
@@ -559,14 +559,10 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack)
(r5,r6), (r7,r8), (r9,r10). If next arg is long long
but not correct starting register of pair then skip
until the proper starting register. */
if (intarg_count % 2 != 0)
{
intarg_count ++;
gpr_base.u++;
}
if (((gpr_end.u - gpr_base.u) & 1) != 0)
gpr_base.u++;
*gpr_base.ll++ = **p_argv.ll;
}
intarg_count += 2;
break;
case FFI_TYPE_STRUCT:
@@ -601,29 +597,22 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack)
gprvalue = **p_argv.ui;
putgpr:
if (intarg_count >= NUM_GPR_ARG_REGISTERS)
if (gpr_base.u >= gpr_end.u)
*next_arg.u++ = gprvalue;
else
*gpr_base.u++ = gprvalue;
intarg_count++;
break;
}
}
/* Check that we didn't overrun the stack... */
FFI_ASSERT (copy_space.c >= next_arg.c);
FFI_ASSERT (gpr_base.u <= stacktop.u - ASM_NEEDS_REGISTERS);
/* The assert below is testing that the number of integer arguments agrees
with the number found in ffi_prep_cif_machdep(). However, intarg_count
is incremented whenever we place an FP arg on the stack, so account for
that before our assert test. */
FFI_ASSERT (gpr_base.u <= gpr_end.u);
#ifndef __NO_FPRS__
if (fparg_count > NUM_FPR_ARG_REGISTERS)
intarg_count -= fparg_count - NUM_FPR_ARG_REGISTERS;
FFI_ASSERT (fpr_base.u
<= stacktop.u - ASM_NEEDS_REGISTERS - NUM_GPR_ARG_REGISTERS);
FFI_ASSERT (fpr_base.u <= fpr_end.u);
#endif
FFI_ASSERT (flags & FLAG_4_GPR_ARGUMENTS || intarg_count <= 4);
FFI_ASSERT (((flags & FLAG_4_GPR_ARGUMENTS) != 0)
== (gpr_end.u - gpr_base.u < 4));
}
#define MIN_CACHE_LINE_SIZE 8