aboutsummaryrefslogtreecommitdiff
path: root/gdb/i386-tdep.c
diff options
context:
space:
mode:
authorJoseph Myers <joseph@codesourcery.com>2008-06-27 16:22:56 +0000
committerJoseph Myers <joseph@codesourcery.com>2008-06-27 16:22:56 +0000
commit7ccc1c74d815b3b3a8bae8afb5f4eacb438b29c5 (patch)
treebc08f5cb189441ba4548537ced48797162767705 /gdb/i386-tdep.c
parentdb42f210256e79f3fa409b73e217d870989ce6cd (diff)
downloadgdb-7ccc1c74d815b3b3a8bae8afb5f4eacb438b29c5.zip
gdb-7ccc1c74d815b3b3a8bae8afb5f4eacb438b29c5.tar.gz
gdb-7ccc1c74d815b3b3a8bae8afb5f4eacb438b29c5.tar.bz2
* i386-tdep.c (i386_16_byte_align_p): New.
(i386_push_dummy_call): Determine stack space required for arguments going forwards allowing for 16-byte alignment, then push arguments going forwards.
Diffstat (limited to 'gdb/i386-tdep.c')
-rw-r--r--gdb/i386-tdep.c101
1 files changed, 84 insertions, 17 deletions
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index 91bd7d0..9dced32 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -1521,6 +1521,34 @@ i386_get_longjmp_target (struct frame_info *frame, CORE_ADDR *pc)
}
+/* Check whether TYPE must be 16-byte-aligned when passed as a
+ function argument. 16-byte vectors, _Decimal128 and structures or
+ unions containing such types must be 16-byte-aligned; other
+ arguments are 4-byte-aligned. */
+
+static int
+i386_16_byte_align_p (struct type *type)
+{
+ type = check_typedef (type);
+ if ((TYPE_CODE (type) == TYPE_CODE_DECFLOAT
+ || (TYPE_CODE (type) == TYPE_CODE_ARRAY && TYPE_VECTOR (type)))
+ && TYPE_LENGTH (type) == 16)
+ return 1;
+ if (TYPE_CODE (type) == TYPE_CODE_ARRAY)
+ return i386_16_byte_align_p (TYPE_TARGET_TYPE (type));
+ if (TYPE_CODE (type) == TYPE_CODE_STRUCT
+ || TYPE_CODE (type) == TYPE_CODE_UNION)
+ {
+ int i;
+ for (i = 0; i < TYPE_NFIELDS (type); i++)
+ {
+ if (i386_16_byte_align_p (TYPE_FIELD_TYPE (type, i)))
+ return 1;
+ }
+ }
+ return 0;
+}
+
static CORE_ADDR
i386_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
struct regcache *regcache, CORE_ADDR bp_addr, int nargs,
@@ -1529,29 +1557,68 @@ i386_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
{
gdb_byte buf[4];
int i;
+ int write_pass;
+ int args_space = 0;
- /* Push arguments in reverse order. */
- for (i = nargs - 1; i >= 0; i--)
+ /* Determine the total space required for arguments and struct
+ return address in a first pass (allowing for 16-byte-aligned
+ arguments), then push arguments in a second pass. */
+
+ for (write_pass = 0; write_pass < 2; write_pass++)
{
- int len = TYPE_LENGTH (value_enclosing_type (args[i]));
+ int args_space_used = 0;
+ int have_16_byte_aligned_arg = 0;
- /* The System V ABI says that:
+ if (struct_return)
+ {
+ if (write_pass)
+ {
+ /* Push value address. */
+ store_unsigned_integer (buf, 4, struct_addr);
+ write_memory (sp, buf, 4);
+ args_space_used += 4;
+ }
+ else
+ args_space += 4;
+ }
- "An argument's size is increased, if necessary, to make it a
- multiple of [32-bit] words. This may require tail padding,
- depending on the size of the argument."
+ for (i = 0; i < nargs; i++)
+ {
+ int len = TYPE_LENGTH (value_enclosing_type (args[i]));
- This makes sure the stack stays word-aligned. */
- sp -= (len + 3) & ~3;
- write_memory (sp, value_contents_all (args[i]), len);
- }
+ if (write_pass)
+ {
+ if (i386_16_byte_align_p (value_enclosing_type (args[i])))
+ args_space_used = align_up (args_space_used, 16);
- /* Push value address. */
- if (struct_return)
- {
- sp -= 4;
- store_unsigned_integer (buf, 4, struct_addr);
- write_memory (sp, buf, 4);
+ write_memory (sp + args_space_used,
+ value_contents_all (args[i]), len);
+ /* The System V ABI says that:
+
+ "An argument's size is increased, if necessary, to make it a
+ multiple of [32-bit] words. This may require tail padding,
+ depending on the size of the argument."
+
+ This makes sure the stack stays word-aligned. */
+ args_space_used += align_up (len, 4);
+ }
+ else
+ {
+ if (i386_16_byte_align_p (value_enclosing_type (args[i])))
+ {
+ args_space = align_up (args_space, 16);
+ have_16_byte_aligned_arg = 1;
+ }
+ args_space += align_up (len, 4);
+ }
+ }
+
+ if (!write_pass)
+ {
+ if (have_16_byte_aligned_arg)
+ args_space = align_up (args_space, 16);
+ sp -= args_space;
+ }
}
/* Store return address. */