return true;
}
-/* Handle "cdecl", "stdcall", "fastcall", "regparm", "thiscall",
- and "sseregparm" calling convention attributes;
+/* Handle "cdecl", "stdcall", "fastcall", "regparm" and "sseregparm"
+ calling convention attributes;
arguments as in struct attribute_spec.handler. */
static tree
error ("fastcall and regparm attributes are not compatible");
}
- if (lookup_attribute ("thiscall", TYPE_ATTRIBUTES (*node)))
- {
- error ("regparam and thiscall attributes are not compatible");
- }
-
cst = TREE_VALUE (args);
if (TREE_CODE (cst) != INTEGER_CST)
{
if (TARGET_64BIT)
{
/* Do not warn when emulating the MS ABI. */
- if ((TREE_CODE (*node) != FUNCTION_TYPE
- && TREE_CODE (*node) != METHOD_TYPE)
+ if (TREE_CODE (*node) != FUNCTION_TYPE
|| ix86_function_type_abi (*node) != MS_ABI)
warning (OPT_Wattributes, "%qE attribute ignored",
name);
{
error ("fastcall and regparm attributes are not compatible");
}
- if (lookup_attribute ("thiscall", TYPE_ATTRIBUTES (*node)))
- {
- error ("fastcall and thiscall attributes are not compatible");
- }
}
/* Can combine stdcall with fastcall (redundant), regparm and
{
error ("stdcall and fastcall attributes are not compatible");
}
- if (lookup_attribute ("thiscall", TYPE_ATTRIBUTES (*node)))
- {
- error ("stdcall and thiscall attributes are not compatible");
- }
}
/* Can combine cdecl with regparm and sseregparm. */
{
error ("fastcall and cdecl attributes are not compatible");
}
- if (lookup_attribute ("thiscall", TYPE_ATTRIBUTES (*node)))
- {
- error ("cdecl and thiscall attributes are not compatible");
- }
- }
- else if (is_attribute_p ("thiscall", name))
- {
- if (TREE_CODE (*node) != METHOD_TYPE && pedantic)
- warning (OPT_Wattributes, "%qE attribute is used for none class-method",
- name);
- if (lookup_attribute ("stdcall", TYPE_ATTRIBUTES (*node)))
- {
- error ("stdcall and thiscall attributes are not compatible");
- }
- if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (*node)))
- {
- error ("fastcall and thiscall attributes are not compatible");
- }
- if (lookup_attribute ("cdecl", TYPE_ATTRIBUTES (*node)))
- {
- error ("cdecl and thiscall attributes are not compatible");
- }
}
/* Can combine sseregparm with all attributes. */
!= !lookup_attribute ("sseregparm", TYPE_ATTRIBUTES (type2)))
return 0;
- /* Check for mismatched thiscall types. */
- if (!lookup_attribute ("thiscall", TYPE_ATTRIBUTES (type1))
- != !lookup_attribute ("thiscall", TYPE_ATTRIBUTES (type2)))
- return 0;
-
/* Check for mismatched return types (cdecl vs stdcall). */
if (!lookup_attribute (rtdstr, TYPE_ATTRIBUTES (type1))
!= !lookup_attribute (rtdstr, TYPE_ATTRIBUTES (type2)))
if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (type)))
return 2;
- if (lookup_attribute ("thiscall", TYPE_ATTRIBUTES (type)))
- return 1;
-
/* Use register calling convention for local functions when possible. */
if (decl
&& TREE_CODE (decl) == FUNCTION_DECL
/* Stdcall and fastcall functions will pop the stack if not
variable args. */
if (lookup_attribute ("stdcall", TYPE_ATTRIBUTES (funtype))
- || lookup_attribute ("fastcall", TYPE_ATTRIBUTES (funtype))
- || lookup_attribute ("thiscall", TYPE_ATTRIBUTES (funtype)))
+ || lookup_attribute ("fastcall", TYPE_ATTRIBUTES (funtype)))
rtd = 1;
if (rtd && ! stdarg_p (funtype))
else look for regparm information. */
if (fntype)
{
- if (lookup_attribute ("thiscall", TYPE_ATTRIBUTES (fntype)))
- {
- cum->nregs = 1;
- cum->fastcall = 1; /* Same first register as in fastcall. */
- }
- else if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (fntype)))
+ if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (fntype)))
{
cum->nregs = 2;
cum->fastcall = 1;
{
rtx save_area, mem;
rtx label;
+ rtx label_ref;
rtx tmp_reg;
rtx nsse_reg;
alias_set_type set;
SSE saves. We need some preparation work to get this working. */
label = gen_label_rtx ();
+ label_ref = gen_rtx_LABEL_REF (Pmode, label);
+ /* Compute address to jump to :
+ label - eax*4 + nnamed_sse_arguments*4 Or
+ label - eax*5 + nnamed_sse_arguments*5 for AVX. */
+ tmp_reg = gen_reg_rtx (Pmode);
nsse_reg = gen_reg_rtx (Pmode);
emit_insn (gen_zero_extendqidi2 (nsse_reg, gen_rtx_REG (QImode, AX_REG)));
+ emit_insn (gen_rtx_SET (VOIDmode, tmp_reg,
+ gen_rtx_MULT (Pmode, nsse_reg,
+ GEN_INT (4))));
+
+ /* vmovaps is one byte longer than movaps. */
+ if (TARGET_AVX)
+ emit_insn (gen_rtx_SET (VOIDmode, tmp_reg,
+ gen_rtx_PLUS (Pmode, tmp_reg,
+ nsse_reg)));
+
+ if (cum->sse_regno)
+ emit_move_insn
+ (nsse_reg,
+ gen_rtx_CONST (DImode,
+ gen_rtx_PLUS (DImode,
+ label_ref,
+ GEN_INT (cum->sse_regno
+ * (TARGET_AVX ? 5 : 4)))));
+ else
+ emit_move_insn (nsse_reg, label_ref);
+ emit_insn (gen_subdi3 (nsse_reg, nsse_reg, tmp_reg));
/* Compute address of memory block we save into. We always use pointer
pointing 127 bytes after first byte to store - this is needed to keep
mem = gen_rtx_MEM (BLKmode, plus_constant (tmp_reg, -127));
MEM_NOTRAP_P (mem) = 1;
set_mem_alias_set (mem, set);
- set_mem_align (mem, 64);
+ set_mem_align (mem, BITS_PER_WORD);
/* And finally do the dirty job! */
emit_insn (gen_sse_prologue_save (mem, nsse_reg,
- GEN_INT (cum->sse_regno), label,
- gen_reg_rtx (Pmode)));
+ GEN_INT (cum->sse_regno), label));
}
}
int indirect_p = 0;
tree ptrtype;
enum machine_mode nat_mode;
- unsigned int arg_boundary;
+ int arg_boundary;
/* Only 64bit target needs something special. */
if (!TARGET_64BIT || is_va_list_char_pointer (TREE_TYPE (valist)))
t = build2 (BIT_AND_EXPR, TREE_TYPE (t), t,
size_int (-align));
t = fold_convert (TREE_TYPE (ovf), t);
- if (crtl->stack_alignment_needed < arg_boundary)
- crtl->stack_alignment_needed = arg_boundary;
}
gimplify_expr (&t, pre_p, NULL, is_gimple_val, fb_rvalue);
gimplify_assign (addr, t, pre_p);
passing. */
if (ix86_function_regparm (TREE_TYPE (decl), decl) <= 2
&& !lookup_attribute ("fastcall",
- TYPE_ATTRIBUTES (TREE_TYPE (decl)))
- && !lookup_attribute ("thiscall",
TYPE_ATTRIBUTES (TREE_TYPE (decl))))
return CX_REG;
else
ix86_cfa_state->reg == stack_pointer_rtx);
else
{
+ /* Only valid for Win32. */
rtx eax = gen_rtx_REG (Pmode, AX_REG);
bool eax_live;
rtx t;
+ gcc_assert (!TARGET_64BIT || cfun->machine->call_abi == MS_ABI);
+
if (cfun->machine->call_abi == MS_ABI)
eax_live = false;
else
L,W,B,Q,S,T -- print the opcode suffix for specified size of operand.
C -- print opcode suffix for set/cmov insn.
c -- like C, but print reversed condition
+ E,e -- likewise, but for compare-and-branch fused insn.
F,f -- likewise, but for floating-point.
O -- if HAVE_AS_IX86_CMOV_SUN_SYNTAX, expand to "w.", "l." or "q.",
otherwise nothing
put_condition_code (GET_CODE (x), GET_MODE (XEXP (x, 0)), 1, 1, file);
return;
+ case 'E':
+ put_condition_code (GET_CODE (x), CCmode, 0, 0, file);
+ return;
+
+ case 'e':
+ put_condition_code (GET_CODE (x), CCmode, 1, 0, file);
+ return;
+
case 'H':
/* It doesn't actually matter what mode we use here, as we're
only going to use this for printing. */
: gen_x86_64_shld) (high[0], low[0], operands[2]));
}
- emit_insn ((mode == DImode
- ? gen_ashlsi3
- : gen_ashldi3) (low[0], low[0], operands[2]));
+ emit_insn ((mode == DImode ? gen_ashlsi3 : gen_ashldi3) (low[0], low[0], operands[2]));
if (TARGET_CMOVE && scratch)
{
ix86_expand_clear (scratch);
emit_insn ((mode == DImode
- ? gen_x86_shiftsi_adj_1
- : gen_x86_shiftdi_adj_1) (high[0], low[0], operands[2],
- scratch));
+ ? gen_x86_shift_adj_1
+ : gen_x86_64_shift_adj_1) (high[0], low[0], operands[2],
+ scratch));
}
else
emit_insn ((mode == DImode
- ? gen_x86_shiftsi_adj_2
- : gen_x86_shiftdi_adj_2) (high[0], low[0], operands[2]));
+ ? gen_x86_shift_adj_2
+ : gen_x86_64_shift_adj_2) (high[0], low[0], operands[2]));
}
void
: gen_ashrdi3) (scratch, scratch,
GEN_INT (single_width - 1)));
emit_insn ((mode == DImode
- ? gen_x86_shiftsi_adj_1
- : gen_x86_shiftdi_adj_1) (low[0], high[0], operands[2],
- scratch));
+ ? gen_x86_shift_adj_1
+ : gen_x86_64_shift_adj_1) (low[0], high[0], operands[2],
+ scratch));
}
else
emit_insn ((mode == DImode
- ? gen_x86_shiftsi_adj_3
- : gen_x86_shiftdi_adj_3) (low[0], high[0], operands[2]));
+ ? gen_x86_shift_adj_3
+ : gen_x86_64_shift_adj_3) (low[0], high[0], operands[2]));
}
}
{
ix86_expand_clear (scratch);
emit_insn ((mode == DImode
- ? gen_x86_shiftsi_adj_1
- : gen_x86_shiftdi_adj_1) (low[0], high[0], operands[2],
- scratch));
+ ? gen_x86_shift_adj_1
+ : gen_x86_64_shift_adj_1) (low[0], high[0], operands[2],
+ scratch));
}
else
emit_insn ((mode == DImode
- ? gen_x86_shiftsi_adj_2
- : gen_x86_shiftdi_adj_2) (low[0], high[0], operands[2]));
+ ? gen_x86_shift_adj_2
+ : gen_x86_64_shift_adj_2) (low[0], high[0], operands[2]));
}
}
}
/* x86-64 ABI requires arrays greater than 16 bytes to be aligned
- to 16byte boundary. Exact wording is:
-
- An array uses the same alignment as its elements, except that a local or
- global array variable of length at least 16 bytes or
- a C99 variable-length array variable always has alignment of at least 16 bytes.
-
- This was added to allow use of aligned SSE instructions at arrays. This
- rule is meant for static storage (where compiler can not do the analysis
- by itself). We follow it for automatic variables only when convenient.
- We fully control everything in the function compiled and functions from
- other unit can not rely on the alignment.
-
- Exclude va_list type. It is the common case of local array where
- we can not benefit from the alignment. */
- if (TARGET_64BIT && optimize_function_for_speed_p (cfun)
- && TARGET_SSE)
+ to 16byte boundary. */
+ if (TARGET_64BIT)
{
if (AGGREGATE_TYPE_P (type)
- && (TYPE_MAIN_VARIANT (type)
- != TYPE_MAIN_VARIANT (va_list_type_node))
&& TYPE_SIZE (type)
&& TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST
&& (TREE_INT_CST_LOW (TYPE_SIZE (type)) >= 16
us with EAX for the static chain. */
regno = AX_REG;
}
- else if (lookup_attribute ("thiscall", TYPE_ATTRIBUTES (fntype)))
- {
- /* Thiscall functions use ecx for arguments, which leaves
- us with EAX for the static chain. */
- regno = AX_REG;
- }
else if (ix86_function_regparm (fntype, fndecl) == 3)
{
/* For regparm 3, we have no free call-clobbered registers in
{ OPTION_MASK_ISA_XOP, CODE_FOR_xop_pcom_tfv4si3, "__builtin_ia32_vpcomtrueud", IX86_BUILTIN_VPCOMTRUEUD, (enum rtx_code) PCOM_TRUE, (int)MULTI_ARG_2_SI_TF },
{ OPTION_MASK_ISA_XOP, CODE_FOR_xop_pcom_tfv2di3, "__builtin_ia32_vpcomtrueuq", IX86_BUILTIN_VPCOMTRUEUQ, (enum rtx_code) PCOM_TRUE, (int)MULTI_ARG_2_DI_TF },
- { OPTION_MASK_ISA_XOP, CODE_FOR_xop_vpermil2v2df3, "__builtin_ia32_vpermil2pd", IX86_BUILTIN_VPERMIL2PD, UNKNOWN, (int)MULTI_ARG_4_DF2_DI_I },
- { OPTION_MASK_ISA_XOP, CODE_FOR_xop_vpermil2v4sf3, "__builtin_ia32_vpermil2ps", IX86_BUILTIN_VPERMIL2PS, UNKNOWN, (int)MULTI_ARG_4_SF2_SI_I },
- { OPTION_MASK_ISA_XOP, CODE_FOR_xop_vpermil2v4df3, "__builtin_ia32_vpermil2pd256", IX86_BUILTIN_VPERMIL2PD256, UNKNOWN, (int)MULTI_ARG_4_DF2_DI_I1 },
- { OPTION_MASK_ISA_XOP, CODE_FOR_xop_vpermil2v8sf3, "__builtin_ia32_vpermil2ps256", IX86_BUILTIN_VPERMIL2PS256, UNKNOWN, (int)MULTI_ARG_4_SF2_SI_I1 },
+ { OPTION_MASK_ISA_AVX, CODE_FOR_xop_vpermil2v2df3, "__builtin_ia32_vpermil2pd", IX86_BUILTIN_VPERMIL2PD, UNKNOWN, (int)MULTI_ARG_4_DF2_DI_I },
+ { OPTION_MASK_ISA_AVX, CODE_FOR_xop_vpermil2v4sf3, "__builtin_ia32_vpermil2ps", IX86_BUILTIN_VPERMIL2PS, UNKNOWN, (int)MULTI_ARG_4_SF2_SI_I },
+ { OPTION_MASK_ISA_AVX, CODE_FOR_xop_vpermil2v4df3, "__builtin_ia32_vpermil2pd256", IX86_BUILTIN_VPERMIL2PD256, UNKNOWN, (int)MULTI_ARG_4_DF2_DI_I1 },
+ { OPTION_MASK_ISA_AVX, CODE_FOR_xop_vpermil2v8sf3, "__builtin_ia32_vpermil2ps256", IX86_BUILTIN_VPERMIL2PS256, UNKNOWN, (int)MULTI_ARG_4_SF2_SI_I1 },
};
nargs = 3;
nargs_constant = 2;
break;
- case V2DF_FTYPE_V2DF_V2DF_V2DI_INT:
- case V4DF_FTYPE_V4DF_V4DF_V4DI_INT:
- case V4SF_FTYPE_V4SF_V4SF_V4SI_INT:
- case V8SF_FTYPE_V8SF_V8SF_V8SI_INT:
+ case MULTI_ARG_4_DF2_DI_I:
+ case MULTI_ARG_4_DF2_DI_I1:
+ case MULTI_ARG_4_SF2_SI_I:
+ case MULTI_ARG_4_SF2_SI_I1:
nargs = 4;
nargs_constant = 1;
break;
if it is not available. */
static tree
-ix86_builtin_vectorized_function (tree fndecl, tree type_out,
+ix86_builtin_vectorized_function (unsigned int fn, tree type_out,
tree type_in)
{
enum machine_mode in_mode, out_mode;
int in_n, out_n;
- enum built_in_function fn = DECL_FUNCTION_CODE (fndecl);
if (TREE_CODE (type_out) != VECTOR_TYPE
- || TREE_CODE (type_in) != VECTOR_TYPE
- || DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL)
+ || TREE_CODE (type_in) != VECTOR_TYPE)
return NULL_TREE;
out_mode = TYPE_MODE (TREE_TYPE (type_out));
/* Returns a decl of a function that implements conversion of an integer vector
- into a floating-point vector, or vice-versa. DEST_TYPE and SRC_TYPE
- are the types involved when converting according to CODE.
+ into a floating-point vector, or vice-versa. TYPE is the type of the integer
+ side of the conversion.
Return NULL_TREE if it is not available. */
static tree
-ix86_vectorize_builtin_conversion (unsigned int code,
- tree dest_type, tree src_type)
+ix86_vectorize_builtin_conversion (unsigned int code, tree type)
{
- if (! TARGET_SSE2)
+ if (! (TARGET_SSE2 && TREE_CODE (type) == VECTOR_TYPE))
return NULL_TREE;
switch (code)
{
case FLOAT_EXPR:
- switch (TYPE_MODE (src_type))
+ switch (TYPE_MODE (type))
{
case V4SImode:
- switch (TYPE_MODE (dest_type))
- {
- case V4SFmode:
- return (TYPE_UNSIGNED (src_type)
- ? ix86_builtins[IX86_BUILTIN_CVTUDQ2PS]
- : ix86_builtins[IX86_BUILTIN_CVTDQ2PS]);
- case V4DFmode:
- return (TYPE_UNSIGNED (src_type)
- ? NULL_TREE
- : ix86_builtins[IX86_BUILTIN_CVTDQ2PD256]);
- default:
- return NULL_TREE;
- }
- break;
- case V8SImode:
- switch (TYPE_MODE (dest_type))
- {
- case V8SFmode:
- return (TYPE_UNSIGNED (src_type)
- ? NULL_TREE
- : ix86_builtins[IX86_BUILTIN_CVTDQ2PS]);
- default:
- return NULL_TREE;
- }
- break;
+ return TYPE_UNSIGNED (type)
+ ? ix86_builtins[IX86_BUILTIN_CVTUDQ2PS]
+ : ix86_builtins[IX86_BUILTIN_CVTDQ2PS];
default:
return NULL_TREE;
}
case FIX_TRUNC_EXPR:
- switch (TYPE_MODE (dest_type))
+ switch (TYPE_MODE (type))
{
case V4SImode:
- switch (TYPE_MODE (src_type))
- {
- case V4SFmode:
- return (TYPE_UNSIGNED (dest_type)
- ? NULL_TREE
- : ix86_builtins[IX86_BUILTIN_CVTTPS2DQ]);
- case V4DFmode:
- return (TYPE_UNSIGNED (dest_type)
- ? NULL_TREE
- : ix86_builtins[IX86_BUILTIN_CVTTPD2DQ256]);
- default:
- return NULL_TREE;
- }
- break;
-
- case V8SImode:
- switch (TYPE_MODE (src_type))
- {
- case V8SFmode:
- return (TYPE_UNSIGNED (dest_type)
- ? NULL_TREE
- : ix86_builtins[IX86_BUILTIN_CVTTPS2DQ256]);
- default:
- return NULL_TREE;
- }
- break;
-
+ return TYPE_UNSIGNED (type)
+ ? NULL_TREE
+ : ix86_builtins[IX86_BUILTIN_CVTTPS2DQ];
default:
return NULL_TREE;
}
-
default:
return NULL_TREE;
- }
- return NULL_TREE;
+ }
}
/* Returns a code for a target-specific builtin that implements
if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (type)))
regno = aggr ? DX_REG : CX_REG;
- else if (lookup_attribute ("thiscall", TYPE_ATTRIBUTES (type)))
- {
- regno = CX_REG;
- if (aggr)
- return gen_rtx_MEM (SImode,
- plus_constant (stack_pointer_rtx, 4));
- }
else
{
regno = AX_REG;
/* Adjust the this parameter by a fixed constant. */
if (delta)
{
- xops[0] = GEN_INT (delta);
+ /* Make things pretty and `subl $4,%eax' rather than `addl $-4,%eax'.
+ Exceptions: -128 encodes smaller than 128, so swap sign and op. */
+ bool sub = delta < 0 || delta == 128;
+ xops[0] = GEN_INT (sub ? -delta : delta);
xops[1] = this_reg ? this_reg : this_param;
if (TARGET_64BIT)
{
xops[0] = tmp;
xops[1] = this_param;
}
- if (x86_maybe_negate_const_int (&xops[0], DImode))
+ if (sub)
output_asm_insn ("sub{q}\t{%0, %1|%1, %0}", xops);
else
output_asm_insn ("add{q}\t{%0, %1|%1, %0}", xops);
}
- else if (x86_maybe_negate_const_int (&xops[0], SImode))
+ else if (sub)
output_asm_insn ("sub{l}\t{%0, %1|%1, %0}", xops);
else
output_asm_insn ("add{l}\t{%0, %1|%1, %0}", xops);
{
int tmp_regno = CX_REG;
if (lookup_attribute ("fastcall",
- TYPE_ATTRIBUTES (TREE_TYPE (function)))
- || lookup_attribute ("thiscall",
- TYPE_ATTRIBUTES (TREE_TYPE (function))))
+ TYPE_ATTRIBUTES (TREE_TYPE (function))))
tmp_regno = AX_REG;
tmp = gen_rtx_REG (SImode, tmp_regno);
}
extended_reg_mentioned_1, NULL);
}
-/* If profitable, negate (without causing overflow) integer constant
- of mode MODE at location LOC. Return true in this case. */
-bool
-x86_maybe_negate_const_int (rtx *loc, enum machine_mode mode)
-{
- HOST_WIDE_INT val;
-
- if (!CONST_INT_P (*loc))
- return false;
-
- switch (mode)
- {
- case DImode:
- /* DImode x86_64 constants must fit in 32 bits. */
- gcc_assert (x86_64_immediate_operand (*loc, mode));
-
- mode = SImode;
- break;
-
- case SImode:
- case HImode:
- case QImode:
- break;
-
- default:
- gcc_unreachable ();
- }
-
- /* Avoid overflows. */
- if (mode_signbit_p (mode, *loc))
- return false;
-
- val = INTVAL (*loc);
-
- /* Make things pretty and `subl $4,%eax' rather than `addl $-4,%eax'.
- Exceptions: -128 encodes smaller than 128, so swap sign and op. */
- if ((val < 0 && val != -128)
- || val == 128)
- {
- *loc = GEN_INT (-val);
- return true;
- }
-
- return false;
-}
-
/* Generate an unsigned DImode/SImode to FP conversion. This is the same code
optabs would emit if we didn't have TFmode patterns. */
/* Fastcall attribute says callee is responsible for popping arguments
if they are not variable. */
{ "fastcall", 0, 0, false, true, true, ix86_handle_cconv_attribute },
- /* Thiscall attribute says callee is responsible for popping arguments
- if they are not variable. */
- { "thiscall", 0, 0, false, true, true, ix86_handle_cconv_attribute },
/* Cdecl attribute says the callee is a normal C declaration */
{ "cdecl", 0, 0, false, true, true, ix86_handle_cconv_attribute },
/* Regparm attribute specifies how many integer arguments are to be