summaryrefslogtreecommitdiff
path: root/gcc/config/i386/i386.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/i386/i386.c')
-rw-r--r--gcc/config/i386/i386.c237
1 files changed, 135 insertions, 102 deletions
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index efce0f32382..f3d5acc59b3 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -750,10 +750,11 @@ static int ix86_variable_issue PARAMS ((FILE *, int, rtx, int));
static int ia32_use_dfa_pipeline_interface PARAMS ((void));
static int ia32_multipass_dfa_lookahead PARAMS ((void));
static void ix86_init_mmx_sse_builtins PARAMS ((void));
-static rtx ia32_this_parameter PARAMS ((tree));
-static void x86_output_mi_thunk PARAMS ((FILE *, tree, HOST_WIDE_INT, tree));
-static void x86_output_mi_vcall_thunk PARAMS ((FILE *, tree, HOST_WIDE_INT,
- HOST_WIDE_INT, tree));
+static rtx x86_this_parameter PARAMS ((tree));
+static void x86_output_mi_thunk PARAMS ((FILE *, tree, HOST_WIDE_INT,
+ HOST_WIDE_INT, tree));
+static bool x86_can_output_mi_thunk PARAMS ((tree, HOST_WIDE_INT,
+ HOST_WIDE_INT, tree));
struct ix86_address
{
@@ -902,8 +903,8 @@ static enum x86_64_reg_class merge_classes PARAMS ((enum x86_64_reg_class,
#undef TARGET_ASM_OUTPUT_MI_THUNK
#define TARGET_ASM_OUTPUT_MI_THUNK x86_output_mi_thunk
-#undef TARGET_ASM_OUTPUT_MI_VCALL_THUNK
-#define TARGET_ASM_OUTPUT_MI_VCALL_THUNK x86_output_mi_vcall_thunk
+#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
+#define TARGET_ASM_CAN_OUTPUT_MI_THUNK x86_can_output_mi_thunk
struct gcc_target targetm = TARGET_INITIALIZER;
@@ -1305,10 +1306,6 @@ override_options ()
internal_label_prefix_len = p - internal_label_prefix;
*p = '\0';
}
-
- /* In 64-bit mode, we do not have support for vcall thunks. */
- if (TARGET_64BIT)
- targetm.asm_out.output_mi_vcall_thunk = NULL;
}
void
@@ -14076,11 +14073,17 @@ x86_order_regs_for_local_alloc ()
located on entry to the FUNCTION. */
static rtx
-ia32_this_parameter (function)
+x86_this_parameter (function)
tree function;
{
tree type = TREE_TYPE (function);
+ if (TARGET_64BIT)
+ {
+ int n = aggregate_value_p (TREE_TYPE (type)) != 0;
+ return gen_rtx_REG (DImode, x86_64_int_parameter_registers[n]);
+ }
+
if (ix86_fntype_regparm (type) > 0)
{
tree parm;
@@ -14088,7 +14091,7 @@ ia32_this_parameter (function)
parm = TYPE_ARG_TYPES (type);
/* Figure out whether or not the function has a variable number of
arguments. */
- for (; parm; parm = TREE_CHAIN (parm))\
+ for (; parm; parm = TREE_CHAIN (parm))
if (TREE_VALUE (parm) == void_type_node)
break;
/* If not, the this parameter is in %eax. */
@@ -14102,122 +14105,152 @@ ia32_this_parameter (function)
return gen_rtx_MEM (SImode, plus_constant (stack_pointer_rtx, 4));
}
+/* Determine whether x86_output_mi_thunk can succeed. */
+
+static bool
+x86_can_output_mi_thunk (thunk, delta, vcall_offset, function)
+ tree thunk ATTRIBUTE_UNUSED;
+ HOST_WIDE_INT delta ATTRIBUTE_UNUSED;
+ HOST_WIDE_INT vcall_offset;
+ tree function;
+{
+ /* 64-bit can handle anything. */
+ if (TARGET_64BIT)
+ return true;
+
+ /* For 32-bit, everything's fine if we have one free register. */
+ if (ix86_fntype_regparm (TREE_TYPE (function)) < 3)
+ return true;
+
+ /* Need a free register for vcall_offset. */
+ if (vcall_offset)
+ return false;
+
+ /* Need a free register for GOT references. */
+ if (flag_pic && !(*targetm.binds_local_p) (function))
+ return false;
+
+ /* Otherwise ok. */
+ return true;
+}
+
+/* Output the assembler code for a thunk function. THUNK_DECL is the
+ declaration for the thunk function itself, FUNCTION is the decl for
+ the target function. DELTA is an immediate constant offset to be
+ added to THIS. If VCALL_OFFSET is non-zero, the word at
+ *(*this + vcall_offset) should be added to THIS. */
static void
-x86_output_mi_vcall_thunk (file, thunk, delta, vcall_index, function)
- FILE *file;
+x86_output_mi_thunk (file, thunk, delta, vcall_offset, function)
+ FILE *file ATTRIBUTE_UNUSED;
tree thunk ATTRIBUTE_UNUSED;
HOST_WIDE_INT delta;
- HOST_WIDE_INT vcall_index;
+ HOST_WIDE_INT vcall_offset;
tree function;
{
rtx xops[3];
+ rtx this = x86_this_parameter (function);
+ rtx this_reg, tmp;
- if (TARGET_64BIT)
+ /* If VCALL_OFFSET, we'll need THIS in a register. Might as well
+ pull it in now and let DELTA benefit. */
+ if (REG_P (this))
+ this_reg = this;
+ else if (vcall_offset)
+ {
+ /* Put the this parameter into %eax. */
+ xops[0] = this;
+ xops[1] = this_reg = gen_rtx_REG (Pmode, 0);
+ output_asm_insn ("mov{l}\t{%0, %1|%1, %0}", xops);
+ }
+ else
+ this_reg = NULL_RTX;
+
+ /* Adjust the this parameter by a fixed constant. */
+ if (delta)
{
- int n = aggregate_value_p (TREE_TYPE (TREE_TYPE (function))) != 0;
xops[0] = GEN_INT (delta);
- xops[1] = gen_rtx_REG (DImode, x86_64_int_parameter_registers[n]);
- output_asm_insn ("add{q} {%0, %1|%1, %0}", xops);
- if (flag_pic)
+ xops[1] = this_reg ? this_reg : this;
+ if (TARGET_64BIT)
{
- fprintf (file, "\tjmp *");
- assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0));
- fprintf (file, "@GOTPCREL(%%rip)\n");
+ if (!x86_64_general_operand (xops[0], DImode))
+ {
+ tmp = gen_rtx_REG (DImode, FIRST_REX_INT_REG + 2 /* R10 */);
+ xops[1] = tmp;
+ output_asm_insn ("mov{q}\t{%1, %0|%0, %1}", xops);
+ xops[0] = tmp;
+ xops[1] = this;
+ }
+ output_asm_insn ("add{q}\t{%0, %1|%1, %0}", xops);
}
else
- {
- fprintf (file, "\tjmp ");
- assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0));
- fprintf (file, "\n");
- }
+ output_asm_insn ("add{l}\t{%0, %1|%1, %0}", xops);
}
- else
+
+ /* Adjust the this parameter by a value stored in the vtable. */
+ if (vcall_offset)
{
- /* Adjust the this parameter by a fixed constant. */
- if (delta)
- {
- xops[0] = GEN_INT (delta);
- xops[1] = ia32_this_parameter (function);
- output_asm_insn ("add{l}\t{%0, %1|%1, %0}", xops);
- }
+ if (TARGET_64BIT)
+ tmp = gen_rtx_REG (DImode, FIRST_REX_INT_REG + 2 /* R10 */);
+ else
+ tmp = gen_rtx_REG (SImode, 2 /* ECX */);
- /* Adjust the this parameter by a value stored in the vtable. */
- if (vcall_index)
- {
- rtx this_parm;
+ xops[0] = gen_rtx_MEM (Pmode, this_reg);
+ xops[1] = tmp;
+ if (TARGET_64BIT)
+ output_asm_insn ("mov{q}\t{%0, %1|%1, %0}", xops);
+ else
+ output_asm_insn ("mov{l}\t{%0, %1|%1, %0}", xops);
- /* Put the this parameter into %eax. */
- this_parm = ia32_this_parameter (function);
- if (!REG_P (this_parm))
- {
- xops[0] = this_parm;
- xops[1] = gen_rtx_REG (Pmode, 0);
- output_asm_insn ("mov{l}\t{%0, %1|%1, %0}", xops);
- }
- /* Load the virtual table pointer into %edx. */
- if (ix86_fntype_regparm (TREE_TYPE (function)) > 2)
- error ("virtual function `%D' cannot have more than two register parameters",
- function);
- xops[0] = gen_rtx_MEM (Pmode,
- gen_rtx_REG (Pmode, 0));
- xops[1] = gen_rtx_REG (Pmode, 1);
- output_asm_insn ("mov{l}\t{%0, %1|%1, %0}", xops);
- /* Adjust the this parameter. */
- xops[0] = gen_rtx_MEM (SImode,
- plus_constant (gen_rtx_REG (Pmode, 1),
- vcall_index));
- xops[1] = gen_rtx_REG (Pmode, 0);
- output_asm_insn ("add{l}\t{%0, %1|%1, %0}", xops);
- /* Put the this parameter back where it came from. */
- if (!REG_P (this_parm))
- {
- xops[0] = gen_rtx_REG (Pmode, 0);
- xops[1] = ia32_this_parameter (function);
- output_asm_insn ("mov{l}\t{%0, %1|%1, %0}", xops);
- }
+ /* Adjust the this parameter. */
+ xops[0] = gen_rtx_MEM (Pmode, plus_constant (tmp, vcall_offset));
+ if (TARGET_64BIT && !memory_operand (xops[0], Pmode))
+ {
+ rtx tmp2 = gen_rtx_REG (DImode, FIRST_REX_INT_REG + 3 /* R11 */);
+ xops[0] = GEN_INT (vcall_offset);
+ xops[1] = tmp2;
+ output_asm_insn ("mov{q}\t{%0, %1|%1, %0}", xops);
+ xops[0] = gen_rtx_MEM (Pmode, gen_rtx_PLUS (Pmode, tmp, tmp2));
}
+ xops[1] = this_reg;
+ if (TARGET_64BIT)
+ output_asm_insn ("add{q}\t{%0, %1|%1, %0}", xops);
+ else
+ output_asm_insn ("add{l}\t{%0, %1|%1, %0}", xops);
+ }
- if (flag_pic)
- {
- xops[0] = pic_offset_table_rtx;
- xops[1] = gen_label_rtx ();
- xops[2] = gen_rtx_SYMBOL_REF (Pmode, GOT_SYMBOL_NAME);
+ /* If necessary, drop THIS back to its stack slot. */
+ if (this_reg && this_reg != this)
+ {
+ xops[0] = this_reg;
+ xops[1] = this;
+ output_asm_insn ("mov{l}\t{%0, %1|%1, %0}", xops);
+ }
- if (ix86_regparm > 2)
- abort ();
- output_asm_insn ("push{l}\t%0", xops);
- output_asm_insn ("call\t%P1", xops);
- ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (xops[1]));
- output_asm_insn ("pop{l}\t%0", xops);
- output_asm_insn
- ("add{l}\t{%2+[.-%P1], %0|%0, OFFSET FLAT: %2+[.-%P1]}", xops);
- xops[0] = gen_rtx_MEM (SImode, XEXP (DECL_RTL (function), 0));
- output_asm_insn
- ("mov{l}\t{%0@GOT(%%ebx), %%ecx|%%ecx, %0@GOT[%%ebx]}", xops);
- asm_fprintf (file, "\tpop{l\t%%ebx|\t%%ebx}\n");
- asm_fprintf (file, "\tjmp\t{*%%ecx|%%ecx}\n");
- }
+ xops[0] = DECL_RTL (function);
+ if (TARGET_64BIT)
+ {
+ if (!flag_pic || (*targetm.binds_local_p) (function))
+ output_asm_insn ("jmp\t%P0", xops);
+ else
+ output_asm_insn ("jmp\t*%P0@GOTPCREL(%%rip)", xops);
+ }
+ else
+ {
+ if (!flag_pic || (*targetm.binds_local_p) (function))
+ output_asm_insn ("jmp\t%P0", xops);
else
{
- fprintf (file, "\tjmp\t");
- assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0));
- fprintf (file, "\n");
+ tmp = gen_rtx_REG (SImode, 2 /* ECX */);
+ output_set_got (tmp);
+
+ xops[1] = tmp;
+ output_asm_insn ("mov{l}\t{%0@GOT(%1), %1|%1, %0@GOT[%1]}", xops);
+ output_asm_insn ("jmp\t{*}%1", xops);
}
}
}
-static void
-x86_output_mi_thunk (file, thunk, delta, function)
- FILE *file;
- tree thunk;
- HOST_WIDE_INT delta;
- tree function;
-{
- x86_output_mi_vcall_thunk (file, thunk, delta, /*vcall_index=*/0,
- function);
-}
-
int
x86_field_alignment (field, computed)
tree field;