summaryrefslogtreecommitdiff
path: root/gdb/s390-tdep.c
diff options
context:
space:
mode:
authorJim Blandy <jimb@codesourcery.com>2001-11-13 17:47:19 +0000
committerJim Blandy <jimb@codesourcery.com>2001-11-13 17:47:19 +0000
commit9c537cfc73d569f18c6b7ca6f692e6b61d5250aa (patch)
tree7ff69489136cb18ac2713ae09cdad521f64086f8 /gdb/s390-tdep.c
parent41f683cf68f8d0ded351419e4fefa27d21694e2b (diff)
downloadgdb-9c537cfc73d569f18c6b7ca6f692e6b61d5250aa.tar.gz
* 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.
Diffstat (limited to 'gdb/s390-tdep.c')
-rw-r--r--gdb/s390-tdep.c423
1 files changed, 325 insertions, 98 deletions
diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c
index 32e5ef1a7a4..7389637fb46 100644
--- a/gdb/s390-tdep.c
+++ b/gdb/s390-tdep.c
@@ -36,7 +36,7 @@
#include "floatformat.h"
#include "regcache.h"
#include "value.h"
-
+#include "gdb_assert.h"
@@ -1199,116 +1199,343 @@ s390_pop_frame ()
}
-/* used by call function by hand
- struct_return indicates that this function returns a structure &
- therefore gpr2 stores a pointer to the structure to be returned as
- opposed to the first argument.
- Currently I haven't seen a TYPE_CODE_INT whose size wasn't 2^n or less
- 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
- comes into this code with a size of 4 ). */
+/* Return non-zero if TYPE is an integer-like type, zero otherwise.
+ "Integer-like" types are those that should be passed the way
+ integers are: integers, enums, ranges, characters, and booleans. */
+static int
+is_integer_like (struct type *type)
+{
+ enum type_code code = TYPE_CODE (type);
+
+ 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
s390_push_arguments (int nargs, struct value **args, CORE_ADDR sp,
int struct_return, CORE_ADDR struct_addr)
{
- int num_float_args, num_gpr_args, orig_num_gpr_args, argno;
- int second_pass, len, arglen, gprs_required;
- 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;
+ int i;
+ int pointer_size = (TARGET_PTR_BIT / TARGET_CHAR_BIT);
- for (second_pass = 0; second_pass <= 1; second_pass++)
- {
- 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);
+ /* The number of arguments passed by reference-to-copy. */
+ int num_copies;
- if (second_pass)
- {
- DOUBLEST tempfloat =
- extract_floating (VALUE_CONTENTS (arg), len);
-
-
- floatformat_from_doublest (all_float_registers_used &&
- len == (TARGET_FLOAT_BIT >> 3)
- ? &floatformat_ieee_single_big
- : &floatformat_ieee_double_big,
- &tempfloat, reg_buff);
- if (all_float_registers_used)
- write_memory (outgoing_args_ptr, reg_buff, len);
- 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++;
- }
- else
- {
- gprs_required = ((len + (S390_GPR_SIZE - 1)) / S390_GPR_SIZE);
+ /* 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));
- value =
- s390_promote_integer_argument (type, VALUE_CONTENTS (arg),
- reg_buff, &arglen);
+ /* Build the reference-to-copy area. */
+ num_copies = 0;
+ for (i = 0; i < nargs; i++)
+ {
+ struct value *arg = args[i];
+ struct type *type = VALUE_TYPE (arg);
+ unsigned length = TYPE_LENGTH (type);
- orig_num_gpr_args = num_gpr_args;
- num_gpr_args += gprs_required;
- if (num_gpr_args > max_num_gpr_args)
- {
- if (second_pass)
- write_memory (outgoing_args_ptr, value, arglen);
- outgoing_args_ptr += arglen;
- }
- else
- {
- if (second_pass)
- write_register_bytes (REGISTER_BYTE (arg0_regnum)
- +
- (orig_num_gpr_args * S390_GPR_SIZE),
- value, arglen);
- }
- }
- }
- if (second_pass)
+ if (is_simple_arg (type)
+ && pass_by_copy_ref (type))
{
- /* 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 ());
+ sp -= length;
+ sp = round_down (sp, alignment_of (type));
+ write_memory (sp, VALUE_CONTENTS (arg), length);
+ copy_addr[i] = sp;
+ num_copies++;
}
- 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;
+ /* 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
+ {
+ /* The `OTHER' case. */
+ enum type_code code = TYPE_CODE (type);
+ unsigned length = TYPE_LENGTH (type);
+
+ /* If we skipped r6 because we couldn't fit a DOUBLE_ARG
+ in it, then don't go back and use it again later. */
+ if (is_double_arg (type) && gr == 6)
+ gr = 7;
+
+ if (is_simple_arg (type))
+ {
+ /* Simple args are always either extended to 32 bits,
+ or pointers. */
+ 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
+ {
+ starg = round_up (starg, alignment_of (type));
+ write_memory (starg, VALUE_CONTENTS (arg), length);
+ starg += length;
+ }
+ }
+ }
+ }
+
+ /* Allocate the standard frame areas: the register save area, the
+ word reserved for the compiler (which seems kind of meaningless),
+ and the back chain pointer. */
+ sp -= 96;
+
+ /* 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 ());
+
+ return sp;
}
/* Return the GDB type object for the "standard" data type