* s390-tdep.c: Rewrite inferior function call code. This may
break zSeries support; that should be fixed soon. #include "gdb_assert.h". (is_integer_like, is_pointer_like, is_simple_arg, pass_by_copy_ref, extend_simple_arg, is_double_arg, round_up, round_down, alignment_of): New functions. (s390_push_arguments): Rewritten to handle passing large arguments by value, and to make more readable.
This commit is contained in:
parent
4c8287ac0c
commit
78f8b4242f
|
@ -1,5 +1,14 @@
|
||||||
2001-11-13 Jim Blandy <jimb@redhat.com>
|
2001-11-13 Jim Blandy <jimb@redhat.com>
|
||||||
|
|
||||||
|
* s390-tdep.c: Rewrite inferior function call code. This may
|
||||||
|
break zSeries support; that should be fixed soon.
|
||||||
|
#include "gdb_assert.h".
|
||||||
|
(is_integer_like, is_pointer_like, is_simple_arg,
|
||||||
|
pass_by_copy_ref, extend_simple_arg, is_double_arg, round_up,
|
||||||
|
round_down, alignment_of): New functions.
|
||||||
|
(s390_push_arguments): Rewritten to handle passing large arguments
|
||||||
|
by value, and to make more readable.
|
||||||
|
|
||||||
* s390-tdep.c (s390_pop_frame): Call generic_pop_current_frame, to
|
* s390-tdep.c (s390_pop_frame): Call generic_pop_current_frame, to
|
||||||
interact correctly with generic dummy frames.
|
interact correctly with generic dummy frames.
|
||||||
(s390_pop_frame_regular): Move the guts of the frame-popping code
|
(s390_pop_frame_regular): Move the guts of the frame-popping code
|
||||||
|
|
399
gdb/s390-tdep.c
399
gdb/s390-tdep.c
|
@ -36,7 +36,7 @@
|
||||||
#include "floatformat.h"
|
#include "floatformat.h"
|
||||||
#include "regcache.h"
|
#include "regcache.h"
|
||||||
#include "value.h"
|
#include "value.h"
|
||||||
|
#include "gdb_assert.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1199,116 +1199,343 @@ s390_pop_frame ()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* used by call function by hand
|
/* Return non-zero if TYPE is an integer-like type, zero otherwise.
|
||||||
struct_return indicates that this function returns a structure &
|
"Integer-like" types are those that should be passed the way
|
||||||
therefore gpr2 stores a pointer to the structure to be returned as
|
integers are: integers, enums, ranges, characters, and booleans. */
|
||||||
opposed to the first argument.
|
static int
|
||||||
Currently I haven't seen a TYPE_CODE_INT whose size wasn't 2^n or less
|
is_integer_like (struct type *type)
|
||||||
than S390_GPR_SIZE this is good because I don't seem to have to worry
|
{
|
||||||
about sign extending pushed arguments (i.e. a signed char currently
|
enum type_code code = TYPE_CODE (type);
|
||||||
comes into this code with a size of 4 ). */
|
|
||||||
|
|
||||||
|
return (code == TYPE_CODE_INT
|
||||||
|
|| code == TYPE_CODE_ENUM
|
||||||
|
|| code == TYPE_CODE_RANGE
|
||||||
|
|| code == TYPE_CODE_CHAR
|
||||||
|
|| code == TYPE_CODE_BOOL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Return non-zero if TYPE is a pointer-like type, zero otherwise.
|
||||||
|
"Pointer-like" types are those that should be passed the way
|
||||||
|
pointers are: pointers and references. */
|
||||||
|
static int
|
||||||
|
is_pointer_like (struct type *type)
|
||||||
|
{
|
||||||
|
enum type_code code = TYPE_CODE (type);
|
||||||
|
|
||||||
|
return (code == TYPE_CODE_PTR
|
||||||
|
|| code == TYPE_CODE_REF);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Return non-zero if TYPE is considered a `DOUBLE_OR_FLOAT', as
|
||||||
|
defined by the parameter passing conventions described in the
|
||||||
|
"Linux for S/390 ELF Application Binary Interface Supplement".
|
||||||
|
Otherwise, return zero. */
|
||||||
|
static int
|
||||||
|
is_double_or_float (struct type *type)
|
||||||
|
{
|
||||||
|
return (TYPE_CODE (type) == TYPE_CODE_FLT
|
||||||
|
&& (TYPE_LENGTH (type) == 4
|
||||||
|
|| TYPE_LENGTH (type) == 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Return non-zero if TYPE is considered a `SIMPLE_ARG', as defined by
|
||||||
|
the parameter passing conventions described in the "Linux for S/390
|
||||||
|
ELF Application Binary Interface Supplement". Return zero otherwise. */
|
||||||
|
static int
|
||||||
|
is_simple_arg (struct type *type)
|
||||||
|
{
|
||||||
|
enum type_code code = TYPE_CODE (type);
|
||||||
|
unsigned length = TYPE_LENGTH (type);
|
||||||
|
|
||||||
|
return ((is_integer_like (type) && length <= 4)
|
||||||
|
|| is_pointer_like (type)
|
||||||
|
|| code == TYPE_CODE_STRUCT
|
||||||
|
|| code == TYPE_CODE_UNION
|
||||||
|
|| (code == TYPE_CODE_FLT && length == 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Return non-zero if TYPE should be passed as a pointer to a copy,
|
||||||
|
zero otherwise. TYPE must be a SIMPLE_ARG, as recognized by
|
||||||
|
`is_simple_arg'. */
|
||||||
|
static int
|
||||||
|
pass_by_copy_ref (struct type *type)
|
||||||
|
{
|
||||||
|
enum type_code code = TYPE_CODE (type);
|
||||||
|
unsigned length = TYPE_LENGTH (type);
|
||||||
|
|
||||||
|
return (((code == TYPE_CODE_STRUCT || code == TYPE_CODE_UNION)
|
||||||
|
&& length != 1 && length != 2 && length != 4)
|
||||||
|
|| (code == TYPE_CODE_FLT && length == 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Return ARG, a `SIMPLE_ARG', sign-extended or zero-extended to a full
|
||||||
|
word as required for the ABI. */
|
||||||
|
static LONGEST
|
||||||
|
extend_simple_arg (struct value *arg)
|
||||||
|
{
|
||||||
|
struct type *type = VALUE_TYPE (arg);
|
||||||
|
|
||||||
|
/* Even structs get passed in the least significant bits of the
|
||||||
|
register / memory word. It's not really right to extract them as
|
||||||
|
an integer, but it does take care of the extension. */
|
||||||
|
if (TYPE_UNSIGNED (type))
|
||||||
|
return extract_unsigned_integer (VALUE_CONTENTS (arg),
|
||||||
|
TYPE_LENGTH (type));
|
||||||
|
else
|
||||||
|
return extract_signed_integer (VALUE_CONTENTS (arg),
|
||||||
|
TYPE_LENGTH (type));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Return non-zero if TYPE is a `DOUBLE_ARG', as defined by the
|
||||||
|
parameter passing conventions described in the "Linux for S/390 ELF
|
||||||
|
Application Binary Interface Supplement". Return zero otherwise. */
|
||||||
|
static int
|
||||||
|
is_double_arg (struct type *type)
|
||||||
|
{
|
||||||
|
enum type_code code = TYPE_CODE (type);
|
||||||
|
unsigned length = TYPE_LENGTH (type);
|
||||||
|
|
||||||
|
return ((is_integer_like (type)
|
||||||
|
|| code == TYPE_CODE_STRUCT
|
||||||
|
|| code == TYPE_CODE_UNION)
|
||||||
|
&& length == 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Round ADDR up to the next N-byte boundary. N must be a power of
|
||||||
|
two. */
|
||||||
|
static CORE_ADDR
|
||||||
|
round_up (CORE_ADDR addr, int n)
|
||||||
|
{
|
||||||
|
/* Check that N is really a power of two. */
|
||||||
|
gdb_assert (n && (n & (n-1)) == 0);
|
||||||
|
return ((addr + n - 1) & -n);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Round ADDR down to the next N-byte boundary. N must be a power of
|
||||||
|
two. */
|
||||||
|
static CORE_ADDR
|
||||||
|
round_down (CORE_ADDR addr, int n)
|
||||||
|
{
|
||||||
|
/* Check that N is really a power of two. */
|
||||||
|
gdb_assert (n && (n & (n-1)) == 0);
|
||||||
|
return (addr & -n);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Return the alignment required by TYPE. */
|
||||||
|
static int
|
||||||
|
alignment_of (struct type *type)
|
||||||
|
{
|
||||||
|
int alignment;
|
||||||
|
|
||||||
|
if (is_integer_like (type)
|
||||||
|
|| is_pointer_like (type)
|
||||||
|
|| TYPE_CODE (type) == TYPE_CODE_FLT)
|
||||||
|
alignment = TYPE_LENGTH (type);
|
||||||
|
else if (TYPE_CODE (type) == TYPE_CODE_STRUCT
|
||||||
|
|| TYPE_CODE (type) == TYPE_CODE_UNION)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
alignment = 1;
|
||||||
|
for (i = 0; i < TYPE_NFIELDS (type); i++)
|
||||||
|
{
|
||||||
|
int field_alignment = alignment_of (TYPE_FIELD_TYPE (type, i));
|
||||||
|
|
||||||
|
if (field_alignment > alignment)
|
||||||
|
alignment = field_alignment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
alignment = 1;
|
||||||
|
|
||||||
|
/* Check that everything we ever return is a power of two. Lots of
|
||||||
|
code doesn't want to deal with aligning things to arbitrary
|
||||||
|
boundaries. */
|
||||||
|
gdb_assert ((alignment & (alignment - 1)) == 0);
|
||||||
|
|
||||||
|
return alignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Put the actual parameter values pointed to by ARGS[0..NARGS-1] in
|
||||||
|
place to be passed to a function, as specified by the "Linux for
|
||||||
|
S/390 ELF Application Binary Interface Supplement".
|
||||||
|
|
||||||
|
SP is the current stack pointer. We must put arguments, links,
|
||||||
|
padding, etc. whereever they belong, and return the new stack
|
||||||
|
pointer value.
|
||||||
|
|
||||||
|
If STRUCT_RETURN is non-zero, then the function we're calling is
|
||||||
|
going to return a structure by value; STRUCT_ADDR is the address of
|
||||||
|
a block we've allocated for it on the stack.
|
||||||
|
|
||||||
|
Our caller has taken care of any type promotions needed to satisfy
|
||||||
|
prototypes or the old K&R argument-passing rules. */
|
||||||
CORE_ADDR
|
CORE_ADDR
|
||||||
s390_push_arguments (int nargs, struct value **args, CORE_ADDR sp,
|
s390_push_arguments (int nargs, struct value **args, CORE_ADDR sp,
|
||||||
int struct_return, CORE_ADDR struct_addr)
|
int struct_return, CORE_ADDR struct_addr)
|
||||||
{
|
{
|
||||||
int num_float_args, num_gpr_args, orig_num_gpr_args, argno;
|
int i;
|
||||||
int second_pass, len, arglen, gprs_required;
|
int pointer_size = (TARGET_PTR_BIT / TARGET_CHAR_BIT);
|
||||||
CORE_ADDR outgoing_args_ptr, outgoing_args_space;
|
|
||||||
struct value *arg;
|
|
||||||
struct type *type;
|
|
||||||
int max_num_gpr_args = 5 - (struct_return ? 1 : 0);
|
|
||||||
int arg0_regnum = S390_GP0_REGNUM + 2 + (struct_return ? 1 : 0);
|
|
||||||
char *reg_buff = alloca (max (S390_FPR_SIZE, REGISTER_SIZE)), *value;
|
|
||||||
|
|
||||||
for (second_pass = 0; second_pass <= 1; second_pass++)
|
/* The number of arguments passed by reference-to-copy. */
|
||||||
{
|
int num_copies;
|
||||||
if (second_pass)
|
|
||||||
outgoing_args_ptr = sp + S390_STACK_FRAME_OVERHEAD;
|
|
||||||
else
|
|
||||||
outgoing_args_ptr = 0;
|
|
||||||
num_float_args = 0;
|
|
||||||
num_gpr_args = 0;
|
|
||||||
for (argno = 0; argno < nargs; argno++)
|
|
||||||
{
|
|
||||||
arg = args[argno];
|
|
||||||
type = check_typedef (VALUE_TYPE (arg));
|
|
||||||
len = TYPE_LENGTH (type);
|
|
||||||
if (TYPE_CODE (type) == TYPE_CODE_FLT)
|
|
||||||
{
|
|
||||||
int all_float_registers_used =
|
|
||||||
num_float_args > (GDB_TARGET_IS_ESAME ? 3 : 1);
|
|
||||||
|
|
||||||
if (second_pass)
|
/* If the i'th argument is passed as a reference to a copy, then
|
||||||
|
copy_addr[i] is the address of the copy we made. */
|
||||||
|
CORE_ADDR *copy_addr = alloca (nargs * sizeof (CORE_ADDR));
|
||||||
|
|
||||||
|
/* Build the reference-to-copy area. */
|
||||||
|
num_copies = 0;
|
||||||
|
for (i = 0; i < nargs; i++)
|
||||||
{
|
{
|
||||||
DOUBLEST tempfloat =
|
struct value *arg = args[i];
|
||||||
extract_floating (VALUE_CONTENTS (arg), len);
|
struct type *type = VALUE_TYPE (arg);
|
||||||
|
unsigned length = TYPE_LENGTH (type);
|
||||||
|
|
||||||
|
if (is_simple_arg (type)
|
||||||
floatformat_from_doublest (all_float_registers_used &&
|
&& pass_by_copy_ref (type))
|
||||||
len == (TARGET_FLOAT_BIT >> 3)
|
{
|
||||||
? &floatformat_ieee_single_big
|
sp -= length;
|
||||||
: &floatformat_ieee_double_big,
|
sp = round_down (sp, alignment_of (type));
|
||||||
&tempfloat, reg_buff);
|
write_memory (sp, VALUE_CONTENTS (arg), length);
|
||||||
if (all_float_registers_used)
|
copy_addr[i] = sp;
|
||||||
write_memory (outgoing_args_ptr, reg_buff, len);
|
num_copies++;
|
||||||
else
|
|
||||||
write_register_bytes (REGISTER_BYTE ((S390_FP0_REGNUM)
|
|
||||||
+
|
|
||||||
(2 *
|
|
||||||
num_float_args)),
|
|
||||||
reg_buff, S390_FPR_SIZE);
|
|
||||||
}
|
}
|
||||||
if (all_float_registers_used)
|
}
|
||||||
outgoing_args_ptr += len;
|
|
||||||
num_float_args++;
|
/* Reserve space for the parameter area. As a conservative
|
||||||
|
simplification, we assume that everything will be passed on the
|
||||||
|
stack. */
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < nargs; i++)
|
||||||
|
{
|
||||||
|
struct value *arg = args[i];
|
||||||
|
struct type *type = VALUE_TYPE (arg);
|
||||||
|
int length = TYPE_LENGTH (type);
|
||||||
|
|
||||||
|
sp = round_down (sp, alignment_of (type));
|
||||||
|
|
||||||
|
/* SIMPLE_ARG values get extended to 32 bits. Assume every
|
||||||
|
argument is. */
|
||||||
|
if (length < 4) length = 4;
|
||||||
|
sp -= length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Include space for any reference-to-copy pointers. */
|
||||||
|
sp = round_down (sp, pointer_size);
|
||||||
|
sp -= num_copies * pointer_size;
|
||||||
|
|
||||||
|
/* After all that, make sure it's still aligned on an eight-byte
|
||||||
|
boundary. */
|
||||||
|
sp = round_down (sp, 8);
|
||||||
|
|
||||||
|
/* Finally, place the actual parameters, working from SP towards
|
||||||
|
higher addresses. The code above is supposed to reserve enough
|
||||||
|
space for this. */
|
||||||
|
{
|
||||||
|
int fr = 0;
|
||||||
|
int gr = 2;
|
||||||
|
CORE_ADDR starg = sp;
|
||||||
|
|
||||||
|
for (i = 0; i < nargs; i++)
|
||||||
|
{
|
||||||
|
struct value *arg = args[i];
|
||||||
|
struct type *type = VALUE_TYPE (arg);
|
||||||
|
|
||||||
|
if (is_double_or_float (type)
|
||||||
|
&& fr <= 2)
|
||||||
|
{
|
||||||
|
/* When we store a single-precision value in an FP register,
|
||||||
|
it occupies the leftmost bits. */
|
||||||
|
write_register_bytes (REGISTER_BYTE (S390_FP0_REGNUM + fr),
|
||||||
|
VALUE_CONTENTS (arg),
|
||||||
|
TYPE_LENGTH (type));
|
||||||
|
fr += 2;
|
||||||
|
}
|
||||||
|
else if (is_simple_arg (type)
|
||||||
|
&& gr <= 6)
|
||||||
|
{
|
||||||
|
/* Do we need to pass a pointer to our copy of this
|
||||||
|
argument? */
|
||||||
|
if (pass_by_copy_ref (type))
|
||||||
|
write_register (S390_GP0_REGNUM + gr, copy_addr[i]);
|
||||||
|
else
|
||||||
|
write_register (S390_GP0_REGNUM + gr, extend_simple_arg (arg));
|
||||||
|
|
||||||
|
gr++;
|
||||||
|
}
|
||||||
|
else if (is_double_arg (type)
|
||||||
|
&& gr <= 5)
|
||||||
|
{
|
||||||
|
write_register_gen (S390_GP0_REGNUM + gr,
|
||||||
|
VALUE_CONTENTS (arg));
|
||||||
|
write_register_gen (S390_GP0_REGNUM + gr + 1,
|
||||||
|
VALUE_CONTENTS (arg) + 4);
|
||||||
|
gr += 2;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
gprs_required = ((len + (S390_GPR_SIZE - 1)) / S390_GPR_SIZE);
|
/* The `OTHER' case. */
|
||||||
|
enum type_code code = TYPE_CODE (type);
|
||||||
|
unsigned length = TYPE_LENGTH (type);
|
||||||
|
|
||||||
value =
|
/* If we skipped r6 because we couldn't fit a DOUBLE_ARG
|
||||||
s390_promote_integer_argument (type, VALUE_CONTENTS (arg),
|
in it, then don't go back and use it again later. */
|
||||||
reg_buff, &arglen);
|
if (is_double_arg (type) && gr == 6)
|
||||||
|
gr = 7;
|
||||||
|
|
||||||
orig_num_gpr_args = num_gpr_args;
|
if (is_simple_arg (type))
|
||||||
num_gpr_args += gprs_required;
|
|
||||||
if (num_gpr_args > max_num_gpr_args)
|
|
||||||
{
|
{
|
||||||
if (second_pass)
|
/* Simple args are always either extended to 32 bits,
|
||||||
write_memory (outgoing_args_ptr, value, arglen);
|
or pointers. */
|
||||||
outgoing_args_ptr += arglen;
|
starg = round_up (starg, 4);
|
||||||
|
|
||||||
|
/* Do we need to pass a pointer to our copy of this
|
||||||
|
argument? */
|
||||||
|
if (pass_by_copy_ref (type))
|
||||||
|
write_memory_signed_integer (starg, pointer_size,
|
||||||
|
copy_addr[i]);
|
||||||
|
else
|
||||||
|
/* Simple args are always extended to 32 bits. */
|
||||||
|
write_memory_signed_integer (starg, 4,
|
||||||
|
extend_simple_arg (arg));
|
||||||
|
starg += 4;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (second_pass)
|
starg = round_up (starg, alignment_of (type));
|
||||||
write_register_bytes (REGISTER_BYTE (arg0_regnum)
|
write_memory (starg, VALUE_CONTENTS (arg), length);
|
||||||
+
|
starg += length;
|
||||||
(orig_num_gpr_args * S390_GPR_SIZE),
|
|
||||||
value, arglen);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (second_pass)
|
}
|
||||||
{
|
|
||||||
/* Write the back chain pointer into the first word of the
|
/* Allocate the standard frame areas: the register save area, the
|
||||||
stack frame. This will help us get backtraces from
|
word reserved for the compiler (which seems kind of meaningless),
|
||||||
within functions called from GDB. */
|
and the back chain pointer. */
|
||||||
write_memory_unsigned_integer (sp,
|
sp -= 96;
|
||||||
(TARGET_PTR_BIT / TARGET_CHAR_BIT),
|
|
||||||
|
/* Write the back chain pointer into the first word of the stack
|
||||||
|
frame. This will help us get backtraces from within functions
|
||||||
|
called from GDB. */
|
||||||
|
write_memory_unsigned_integer (sp, (TARGET_PTR_BIT / TARGET_CHAR_BIT),
|
||||||
read_fp ());
|
read_fp ());
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
outgoing_args_space = outgoing_args_ptr;
|
|
||||||
/* Align to 16 bytes because because I like alignment &
|
|
||||||
some of the kernel code requires 8 byte stack alignment at least. */
|
|
||||||
sp = (sp - (S390_STACK_FRAME_OVERHEAD + outgoing_args_ptr)) & (-16);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return sp;
|
return sp;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return the GDB type object for the "standard" data type
|
/* Return the GDB type object for the "standard" data type
|
||||||
|
|
Loading…
Reference in New Issue