diff options
author | Pekka Enberg <penberg@kernel.org> | 2013-04-14 23:39:11 +0300 |
---|---|---|
committer | Pekka Enberg <penberg@kernel.org> | 2013-04-15 21:33:03 +0300 |
commit | a048d852cac71dc4753288cfd5536560d7a2a9b9 (patch) | |
tree | 466c0200e8bf2845252eb9036e175d0979311584 | |
parent | c011aa175831dbd35cecb5be4d303d43854a09ad (diff) | |
download | jato-a048d852cac71dc4753288cfd5536560d7a2a9b9.tar.gz |
x86-64: Fix stack frame alignment for stack arguments
Section 3.2.2 ("The Stack Frame") of the SVR4 x86-64 ABI states that:
The end of the input argument area shall be aligned on a 16 byte
boundary. In other words, the value (%rsp - 8) is always a multiple of
16 when control is transferred to the function entry point. The stack
pointer, %rsp, always points to the end of the latest allocated stack
frame.
Unfortunately the JIT compiler does not take that into account for
arguments that are passed on the stack for x86-64. Fix the problem by
adding a new STMT_BEFORE_ARGS statement that aligns the stack pointer
properly and teach method_args_cleanup() to deal with the alignment.
Signed-off-by: Pekka Enberg <penberg@kernel.org>
-rw-r--r-- | arch/x86/insn-selector_32.brg | 4 | ||||
-rw-r--r-- | arch/x86/insn-selector_64.brg | 29 | ||||
-rw-r--r-- | include/jit/statement.h | 3 | ||||
-rw-r--r-- | jit/invoke-bc.c | 31 | ||||
-rw-r--r-- | jit/statement.c | 1 | ||||
-rw-r--r-- | jit/tree-printer.c | 18 |
6 files changed, 85 insertions, 1 deletions
diff --git a/arch/x86/insn-selector_32.brg b/arch/x86/insn-selector_32.brg index 4b9f841b..beff0604 100644 --- a/arch/x86/insn-selector_32.brg +++ b/arch/x86/insn-selector_32.brg @@ -1547,6 +1547,10 @@ reg: EXPR_EXCEPTION_REF select_insn(s, tree, memlocal_reg_insn(INSN_MOV_MEMLOCAL_REG, slot, result)); } +stmt: STMT_BEFORE_ARGS 1 +{ +} + stmt: STMT_INVOKE(arg) 1 { invoke(s, tree); diff --git a/arch/x86/insn-selector_64.brg b/arch/x86/insn-selector_64.brg index 04d7b4f0..ce75c524 100644 --- a/arch/x86/insn-selector_64.brg +++ b/arch/x86/insn-selector_64.brg @@ -103,10 +103,16 @@ static void method_args_cleanup(struct basic_block *bb, struct tree_node *tree, { struct var_info *stack_ptr; unsigned long args_size; + unsigned long unaligned; stack_ptr = bb->b_parent->stack_ptr; args_size = args_count * sizeof(long); + unaligned = args_size % X86_STACK_ALIGN; + + if (unaligned) + args_size += X86_STACK_ALIGN - unaligned; + select_insn(bb, tree, imm_reg_insn(INSN_ADD_IMM_REG, args_size, stack_ptr)); } @@ -1328,6 +1334,29 @@ reg: EXPR_EXCEPTION_REF select_insn(s, tree, memlocal_reg_insn(INSN_MOV_MEMLOCAL_REG, slot, result)); } +stmt: STMT_BEFORE_ARGS 1 +{ + struct compilation_unit *cu = s->b_parent; + struct var_info *stack_ptr; + struct vm_method *method; + unsigned long args_size; + unsigned long unaligned; + struct statement *stmt; + int nr_stack_args; + + stmt = to_stmt(tree); + method = stmt->target_method; + + nr_stack_args = get_stack_args_count(method); + + stack_ptr = cu->stack_ptr; + args_size = nr_stack_args * sizeof(long); + unaligned = args_size % X86_STACK_ALIGN; + + if (unaligned) + select_insn(s, tree, imm_reg_insn(INSN_SUB_IMM_REG, X86_STACK_ALIGN - unaligned, stack_ptr)); +} + stmt: STMT_INVOKE(arg) 1 { select_insn(s, tree, insn(INSN_SAVE_CALLER_REGS)); diff --git a/include/jit/statement.h b/include/jit/statement.h index 20b96adc..2647d36f 100644 --- a/include/jit/statement.h +++ b/include/jit/statement.h @@ -24,6 +24,7 @@ enum statement_type { STMT_ARRAY_STORE_CHECK, STMT_TABLESWITCH, STMT_LOOKUPSWITCH_JUMP, + STMT_BEFORE_ARGS, STMT_INVOKE, STMT_INVOKEINTERFACE, STMT_INVOKEVIRTUAL, @@ -105,7 +106,7 @@ struct statement { struct tree_node *lookupswitch_target; }; - struct /* STMT_INVOKE, STMT_INVOKEVIRTUAL, STMT_INVOKEINTERFACE */ { + struct /* STMT_BEFORE_ARGS, STMT_INVOKE, STMT_INVOKEVIRTUAL, STMT_INVOKEINTERFACE */ { struct tree_node *args_list; struct vm_method *target_method; }; diff --git a/jit/invoke-bc.c b/jit/invoke-bc.c index 4b708770..5c4fd447 100644 --- a/jit/invoke-bc.c +++ b/jit/invoke-bc.c @@ -198,6 +198,21 @@ static struct statement * invoke_stmt(struct parse_context *ctx, return stmt; } +static int insert_before_args_stmt(struct parse_context *ctx, struct vm_method *target) +{ + struct statement *stmt; + + stmt = alloc_statement(STMT_BEFORE_ARGS); + if (!stmt) + return warn("out of memory"), -ENOMEM; + + stmt->target_method = target; + + convert_statement(ctx, stmt); + + return 0; +} + static void insert_invoke_stmt(struct parse_context *ctx, struct statement *stmt) { convert_statement(ctx, stmt); @@ -286,6 +301,10 @@ int convert_invokeinterface(struct parse_context *ctx) if (err) goto failed; + err = insert_before_args_stmt(ctx, invoke_target); + if (err) + goto failed; + insert_invoke_stmt(ctx, stmt); return 0; @@ -312,6 +331,10 @@ int convert_invokevirtual(struct parse_context *ctx) if (err) goto failed; + err = insert_before_args_stmt(ctx, invoke_target); + if (err) + goto failed; + insert_invoke_stmt(ctx, stmt); return 0; failed: @@ -339,6 +362,10 @@ int convert_invokespecial(struct parse_context *ctx) null_check_this_arg(to_expr(stmt->args_list)); + err = insert_before_args_stmt(ctx, invoke_target); + if (err) + goto failed; + insert_invoke_stmt(ctx, stmt); return 0; failed: @@ -364,6 +391,10 @@ int convert_invokestatic(struct parse_context *ctx) if (err) goto failed; + err = insert_before_args_stmt(ctx, invoke_target); + if (err) + goto failed; + insert_invoke_stmt(ctx, stmt); return 0; failed: diff --git a/jit/statement.c b/jit/statement.c index 60848a6f..9f944f48 100644 --- a/jit/statement.c +++ b/jit/statement.c @@ -38,6 +38,7 @@ int stmt_nr_kids(struct statement *stmt) case STMT_INVOKEINTERFACE: case STMT_INVOKEVIRTUAL: return 1; + case STMT_BEFORE_ARGS: case STMT_GOTO: case STMT_VOID_RETURN: return 0; diff --git a/jit/tree-printer.c b/jit/tree-printer.c index f0c30f12..5a5e2770 100644 --- a/jit/tree-printer.c +++ b/jit/tree-printer.c @@ -372,6 +372,23 @@ out: return err; } +static int print_before_args_stmt(int lvl, struct string *str, struct statement *stmt) +{ + struct vm_method *method; + int err; + + err = append_formatted(lvl, str, "BEFORE_ARGS:\n"); + if (err) + goto out; + + method = stmt->target_method; + + err = append_simple_attr(lvl + 1, str, "target_method", "%p '%s.%s%s' (%lu)", + method, method->class->name, method->name, method->type, stmt_method_index(stmt)); +out: + return err; +} + static int print_invoke_stmt(int lvl, struct string *str, struct statement *stmt) { @@ -407,6 +424,7 @@ static print_stmt_fn stmt_printers[] = { [STMT_ARRAY_STORE_CHECK] = print_array_store_check_stmt, [STMT_TABLESWITCH] = print_tableswitch_stmt, [STMT_LOOKUPSWITCH_JUMP] = print_lookupswitch_jump_stmt, + [STMT_BEFORE_ARGS] = print_before_args_stmt, [STMT_INVOKE] = print_invoke_stmt, [STMT_INVOKEINTERFACE] = print_invokeinterface_stmt, [STMT_INVOKEVIRTUAL] = print_invokevirtual_stmt, |