diff mbox

Short-cut generation of simple built-in functions

Message ID 87si45j6g1.fsf@e105548-lin.cambridge.arm.com
State New
Headers show

Commit Message

Richard Sandiford Nov. 17, 2015, 9:55 a.m. UTC
Richard Sandiford <richard.sandiford@arm.com> writes:
> Richard Biener <richard.guenther@gmail.com> writes:
>> On Tue, Nov 10, 2015 at 10:24 PM, Richard Sandiford
>> <richard.sandiford@arm.com> wrote:
>>> Richard Biener <richard.guenther@gmail.com> writes:
>>>> On Sat, Nov 7, 2015 at 2:31 PM, Richard Sandiford
>>>> <richard.sandiford@arm.com> wrote:
>>>>> This patch short-circuits the builtins.c expansion code for a particular
>>>>> gimple call if:
>>>>>
>>>>> - the function has an associated internal function
>>>>> - the target implements that internal function
>>>>> - the call has no side effects
>>>>>
>>>>> This allows a later patch to remove the builtins.c code, once calls with
>>>>> side effects have been handled.
>>>>>
>>>>> Tested on x86_64-linux-gnu, aarch64-linux-gnu and arm-linux-gnueabi.
>>>>> OK to install?
>>>>>
>>>>> Thanks,
>>>>> Richard
>>>>>
>>>>>
>>>>> gcc/
>>>>>         * builtins.h (called_as_built_in): Declare.
>>>>>         * builtins.c (called_as_built_in): Make external.
>>>>>         * internal-fn.h (expand_internal_call): Define a variant that
>>>>>         specifies the internal function explicitly.
>>>>>         * internal-fn.c (expand_load_lanes_optab_fn)
>>>>>         (expand_store_lanes_optab_fn, expand_ANNOTATE, expand_GOMP_SIMD_LANE)
>>>>>         (expand_GOMP_SIMD_VF, expand_GOMP_SIMD_LAST_LANE)
>>>>>         (expand_GOMP_SIMD_ORDERED_START, expand_GOMP_SIMD_ORDERED_END)
>>>>>         (expand_UBSAN_NULL, expand_UBSAN_BOUNDS, expand_UBSAN_VPTR)
>>>>>         (expand_UBSAN_OBJECT_SIZE, expand_ASAN_CHECK, expand_TSAN_FUNC_EXIT)
>>>>>         (expand_UBSAN_CHECK_ADD, expand_UBSAN_CHECK_SUB)
>>>>>         (expand_UBSAN_CHECK_MUL, expand_ADD_OVERFLOW, expand_SUB_OVERFLOW)
>>>>>         (expand_MUL_OVERFLOW, expand_LOOP_VECTORIZED)
>>>>>         (expand_mask_load_optab_fn, expand_mask_store_optab_fn)
>>>>>         (expand_ABNORMAL_DISPATCHER, expand_BUILTIN_EXPECT, expand_VA_ARG)
>>>>>         (expand_UNIQUE, expand_GOACC_DIM_SIZE, expand_GOACC_DIM_POS)
>>>>>         (expand_GOACC_LOOP, expand_GOACC_REDUCTION, expand_direct_optab_fn)
>>>>>         (expand_unary_optab_fn, expand_binary_optab_fn): Add an internal_fn
>>>>>         argument.
>>>>>         (internal_fn_expanders): Update prototype.
>>>>>         (expand_internal_call): Define a variant that specifies the
>>>>>         internal function explicitly. Use it to implement the previous
>>>>>         interface.
>>>>>         * cfgexpand.c (expand_call_stmt): Try to expand calls to built-in
>>>>>         functions as calls to internal functions.
>>>>>
>>>>> diff --git a/gcc/builtins.c b/gcc/builtins.c
>>>>> index f65011e..bbcc7dc3 100644
>>>>> --- a/gcc/builtins.c
>>>>> +++ b/gcc/builtins.c
>>>>> @@ -222,7 +222,7 @@ is_builtin_fn (tree decl)
>>>>>     of the optimization level.  This means whenever a function is invoked with
>>>>>     its "internal" name, which normally contains the prefix "__builtin".  */
>>>>>
>>>>> -static bool
>>>>> +bool
>>>>>  called_as_built_in (tree node)
>>>>>  {
>>>>>    /* Note that we must use DECL_NAME, not DECL_ASSEMBLER_NAME_SET_P since
>>>>> diff --git a/gcc/builtins.h b/gcc/builtins.h
>>>>> index 917eb90..1d00068 100644
>>>>> --- a/gcc/builtins.h
>>>>> +++ b/gcc/builtins.h
>>>>> @@ -50,6 +50,7 @@ extern struct target_builtins *this_target_builtins;
>>>>>  extern bool force_folding_builtin_constant_p;
>>>>>
>>>>>  extern bool is_builtin_fn (tree);
>>>>> +extern bool called_as_built_in (tree);
>>>>>  extern bool get_object_alignment_1 (tree, unsigned int *,
>>>>>                                     unsigned HOST_WIDE_INT *);
>>>>>  extern unsigned int get_object_alignment (tree);
>>>>> diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
>>>>> index bfbc958..dc7d4f5 100644
>>>>> --- a/gcc/cfgexpand.c
>>>>> +++ b/gcc/cfgexpand.c
>>>>> @@ -2551,10 +2551,25 @@ expand_call_stmt (gcall *stmt)
>>>>>        return;
>>>>>      }
>>>>>
>>>>> +  /* If this is a call to a built-in function and it has no effect other
>>>>> +     than setting the lhs, try to implement it using an internal function
>>>>> +     instead.  */
>>>>> +  decl = gimple_call_fndecl (stmt);
>>>>> +  if (gimple_call_lhs (stmt)
>>>>> +      && !gimple_vdef (stmt)
>>>>
>>>> I think you want && ! gimple_has_side_effects (stmt)
>>>> instead of checking !gimple_vdef (stmt).
>>>
>>> OK, I can do that, but what would the difference be in practice for
>>> these types of call?  I.e. are there cases for built-ins where:
>>>
>>>   (A) gimple_vdef (stmt) && !gimple_side_effects (stmt)
>>>
>>> or:
>>>
>>>   (B) !gimple_vdef (stmt) && gimple_side_effects (stmt)
>>>
>>> ?
>>
>> There was talk to make calls use volatile to prevent CSE and friends.
>>
>> Using gimple_has_side_effects is just the better check.
>>
>>> It just seems like this check should be the opposite of the one used
>>> in the call-cdce patch (when deciding whether to optimise a call
>>> with an lhs).  In order to keep them in sync I'd need to use
>>> gimple_side_effects rather than gimple_vdef there too, but is
>>> (B) a possibility there?
>>
>> Not sure if the tests should be in-sync.
>
> Well, with the series, we have two opportunities to use optabs:
>
> - in tree-call-cdce.c, if we need to keep the original call
>   (or something that sets EDOM)
>
> - here in cfgexpand.c, if we don't need to keep the original call.
>
> So both are really asking "do I need to keep the call?"
>
> If we don't do either then we don't use the optab.

Anyway, the updated patch below uses gimple_side_effects_p.

>> I'm also not sure what you really want to check with
>>
>>>>> +  /* If this is a call to a built-in function and it has no effect other
>>>>> +     than setting the lhs, try to implement it using an internal function
>>>>> +     instead.  */
>>>>> +  decl = gimple_call_fndecl (stmt);
>>>>> +  if (gimple_call_lhs (stmt)
>>>>> +      && !gimple_vdef (stmt)
>>
>> I know you want to catch errno setting here but shouldn't that use a
>> different kind of check and only done for a specific subset of
>> functions?  For example do you want to replace sqrt() with
>> an IFN in case it has a VUSE (it will have that with -frounding-math)?
>> In this case you'll lose the implicit dependence on fesetround and
>> friends.  This implicit dependence is not checked by gimple_has_side_effects
>> either btw.
>
> Yeah, we want to replace even in that case.  Remember that this is
> expand code, so we're going to generate rtl whatever happens.  It's up
> to the rtl patterns to represent any ordering requirements.  (Not that
> we do have explicit rtl dependencies for rounding mode, but we don't for
> +, -. * or / either.)
>
> If for some reason a particular .md pattern doesn't honour the current
> rounding mode when normally the optab would be expected to, the .md file
> should limit the pattern to !flag_rounding_math.

Tested as before.  OK to install?

Thanks,
Richard


gcc/
	* builtins.h (called_as_built_in): Declare.
	* builtins.c (called_as_built_in): Make external.
	* internal-fn.h (expand_internal_call): Define a variant that
	specifies the internal function explicitly.
	* internal-fn.c (expand_load_lanes_optab_fn)
	(expand_store_lanes_optab_fn, expand_ANNOTATE, expand_GOMP_SIMD_LANE)
	(expand_GOMP_SIMD_VF, expand_GOMP_SIMD_LAST_LANE)
	(expand_GOMP_SIMD_ORDERED_START, expand_GOMP_SIMD_ORDERED_END)
	(expand_UBSAN_NULL, expand_UBSAN_BOUNDS, expand_UBSAN_VPTR)
	(expand_UBSAN_OBJECT_SIZE, expand_ASAN_CHECK, expand_TSAN_FUNC_EXIT)
	(expand_UBSAN_CHECK_ADD, expand_UBSAN_CHECK_SUB)
	(expand_UBSAN_CHECK_MUL, expand_ADD_OVERFLOW, expand_SUB_OVERFLOW)
	(expand_MUL_OVERFLOW, expand_LOOP_VECTORIZED)
	(expand_mask_load_optab_fn, expand_mask_store_optab_fn)
	(expand_ABNORMAL_DISPATCHER, expand_BUILTIN_EXPECT, expand_VA_ARG)
	(expand_UNIQUE, expand_GOACC_DIM_SIZE, expand_GOACC_DIM_POS)
	(expand_GOACC_LOOP, expand_GOACC_REDUCTION, expand_direct_optab_fn)
	(expand_unary_optab_fn, expand_binary_optab_fn): Add an internal_fn
	argument.
	(internal_fn_expanders): Update prototype.
	(expand_internal_call): Define a variant that specifies the
	internal function explicitly. Use it to implement the previous
	interface.
	* cfgexpand.c (expand_call_stmt): Try to expand calls to built-in
	functions as calls to internal functions.

Comments

Richard Biener Nov. 17, 2015, 2:34 p.m. UTC | #1
On Tue, Nov 17, 2015 at 10:55 AM, Richard Sandiford
<richard.sandiford@arm.com> wrote:
> Richard Sandiford <richard.sandiford@arm.com> writes:
>> Richard Biener <richard.guenther@gmail.com> writes:
>>> On Tue, Nov 10, 2015 at 10:24 PM, Richard Sandiford
>>> <richard.sandiford@arm.com> wrote:
>>>> Richard Biener <richard.guenther@gmail.com> writes:
>>>>> On Sat, Nov 7, 2015 at 2:31 PM, Richard Sandiford
>>>>> <richard.sandiford@arm.com> wrote:
>>>>>> This patch short-circuits the builtins.c expansion code for a particular
>>>>>> gimple call if:
>>>>>>
>>>>>> - the function has an associated internal function
>>>>>> - the target implements that internal function
>>>>>> - the call has no side effects
>>>>>>
>>>>>> This allows a later patch to remove the builtins.c code, once calls with
>>>>>> side effects have been handled.
>>>>>>
>>>>>> Tested on x86_64-linux-gnu, aarch64-linux-gnu and arm-linux-gnueabi.
>>>>>> OK to install?
>>>>>>
>>>>>> Thanks,
>>>>>> Richard
>>>>>>
>>>>>>
>>>>>> gcc/
>>>>>>         * builtins.h (called_as_built_in): Declare.
>>>>>>         * builtins.c (called_as_built_in): Make external.
>>>>>>         * internal-fn.h (expand_internal_call): Define a variant that
>>>>>>         specifies the internal function explicitly.
>>>>>>         * internal-fn.c (expand_load_lanes_optab_fn)
>>>>>>         (expand_store_lanes_optab_fn, expand_ANNOTATE, expand_GOMP_SIMD_LANE)
>>>>>>         (expand_GOMP_SIMD_VF, expand_GOMP_SIMD_LAST_LANE)
>>>>>>         (expand_GOMP_SIMD_ORDERED_START, expand_GOMP_SIMD_ORDERED_END)
>>>>>>         (expand_UBSAN_NULL, expand_UBSAN_BOUNDS, expand_UBSAN_VPTR)
>>>>>>         (expand_UBSAN_OBJECT_SIZE, expand_ASAN_CHECK, expand_TSAN_FUNC_EXIT)
>>>>>>         (expand_UBSAN_CHECK_ADD, expand_UBSAN_CHECK_SUB)
>>>>>>         (expand_UBSAN_CHECK_MUL, expand_ADD_OVERFLOW, expand_SUB_OVERFLOW)
>>>>>>         (expand_MUL_OVERFLOW, expand_LOOP_VECTORIZED)
>>>>>>         (expand_mask_load_optab_fn, expand_mask_store_optab_fn)
>>>>>>         (expand_ABNORMAL_DISPATCHER, expand_BUILTIN_EXPECT, expand_VA_ARG)
>>>>>>         (expand_UNIQUE, expand_GOACC_DIM_SIZE, expand_GOACC_DIM_POS)
>>>>>>         (expand_GOACC_LOOP, expand_GOACC_REDUCTION, expand_direct_optab_fn)
>>>>>>         (expand_unary_optab_fn, expand_binary_optab_fn): Add an internal_fn
>>>>>>         argument.
>>>>>>         (internal_fn_expanders): Update prototype.
>>>>>>         (expand_internal_call): Define a variant that specifies the
>>>>>>         internal function explicitly. Use it to implement the previous
>>>>>>         interface.
>>>>>>         * cfgexpand.c (expand_call_stmt): Try to expand calls to built-in
>>>>>>         functions as calls to internal functions.
>>>>>>
>>>>>> diff --git a/gcc/builtins.c b/gcc/builtins.c
>>>>>> index f65011e..bbcc7dc3 100644
>>>>>> --- a/gcc/builtins.c
>>>>>> +++ b/gcc/builtins.c
>>>>>> @@ -222,7 +222,7 @@ is_builtin_fn (tree decl)
>>>>>>     of the optimization level.  This means whenever a function is invoked with
>>>>>>     its "internal" name, which normally contains the prefix "__builtin".  */
>>>>>>
>>>>>> -static bool
>>>>>> +bool
>>>>>>  called_as_built_in (tree node)
>>>>>>  {
>>>>>>    /* Note that we must use DECL_NAME, not DECL_ASSEMBLER_NAME_SET_P since
>>>>>> diff --git a/gcc/builtins.h b/gcc/builtins.h
>>>>>> index 917eb90..1d00068 100644
>>>>>> --- a/gcc/builtins.h
>>>>>> +++ b/gcc/builtins.h
>>>>>> @@ -50,6 +50,7 @@ extern struct target_builtins *this_target_builtins;
>>>>>>  extern bool force_folding_builtin_constant_p;
>>>>>>
>>>>>>  extern bool is_builtin_fn (tree);
>>>>>> +extern bool called_as_built_in (tree);
>>>>>>  extern bool get_object_alignment_1 (tree, unsigned int *,
>>>>>>                                     unsigned HOST_WIDE_INT *);
>>>>>>  extern unsigned int get_object_alignment (tree);
>>>>>> diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
>>>>>> index bfbc958..dc7d4f5 100644
>>>>>> --- a/gcc/cfgexpand.c
>>>>>> +++ b/gcc/cfgexpand.c
>>>>>> @@ -2551,10 +2551,25 @@ expand_call_stmt (gcall *stmt)
>>>>>>        return;
>>>>>>      }
>>>>>>
>>>>>> +  /* If this is a call to a built-in function and it has no effect other
>>>>>> +     than setting the lhs, try to implement it using an internal function
>>>>>> +     instead.  */
>>>>>> +  decl = gimple_call_fndecl (stmt);
>>>>>> +  if (gimple_call_lhs (stmt)
>>>>>> +      && !gimple_vdef (stmt)
>>>>>
>>>>> I think you want && ! gimple_has_side_effects (stmt)
>>>>> instead of checking !gimple_vdef (stmt).
>>>>
>>>> OK, I can do that, but what would the difference be in practice for
>>>> these types of call?  I.e. are there cases for built-ins where:
>>>>
>>>>   (A) gimple_vdef (stmt) && !gimple_side_effects (stmt)
>>>>
>>>> or:
>>>>
>>>>   (B) !gimple_vdef (stmt) && gimple_side_effects (stmt)
>>>>
>>>> ?
>>>
>>> There was talk to make calls use volatile to prevent CSE and friends.
>>>
>>> Using gimple_has_side_effects is just the better check.
>>>
>>>> It just seems like this check should be the opposite of the one used
>>>> in the call-cdce patch (when deciding whether to optimise a call
>>>> with an lhs).  In order to keep them in sync I'd need to use
>>>> gimple_side_effects rather than gimple_vdef there too, but is
>>>> (B) a possibility there?
>>>
>>> Not sure if the tests should be in-sync.
>>
>> Well, with the series, we have two opportunities to use optabs:
>>
>> - in tree-call-cdce.c, if we need to keep the original call
>>   (or something that sets EDOM)
>>
>> - here in cfgexpand.c, if we don't need to keep the original call.
>>
>> So both are really asking "do I need to keep the call?"
>>
>> If we don't do either then we don't use the optab.
>
> Anyway, the updated patch below uses gimple_side_effects_p.
>
>>> I'm also not sure what you really want to check with
>>>
>>>>>> +  /* If this is a call to a built-in function and it has no effect other
>>>>>> +     than setting the lhs, try to implement it using an internal function
>>>>>> +     instead.  */
>>>>>> +  decl = gimple_call_fndecl (stmt);
>>>>>> +  if (gimple_call_lhs (stmt)
>>>>>> +      && !gimple_vdef (stmt)
>>>
>>> I know you want to catch errno setting here but shouldn't that use a
>>> different kind of check and only done for a specific subset of
>>> functions?  For example do you want to replace sqrt() with
>>> an IFN in case it has a VUSE (it will have that with -frounding-math)?
>>> In this case you'll lose the implicit dependence on fesetround and
>>> friends.  This implicit dependence is not checked by gimple_has_side_effects
>>> either btw.
>>
>> Yeah, we want to replace even in that case.  Remember that this is
>> expand code, so we're going to generate rtl whatever happens.  It's up
>> to the rtl patterns to represent any ordering requirements.  (Not that
>> we do have explicit rtl dependencies for rounding mode, but we don't for
>> +, -. * or / either.)
>>
>> If for some reason a particular .md pattern doesn't honour the current
>> rounding mode when normally the optab would be expected to, the .md file
>> should limit the pattern to !flag_rounding_math.
>
> Tested as before.  OK to install?

Ok.

Thanks,
Richard.

> Thanks,
> Richard
>
>
> gcc/
>         * builtins.h (called_as_built_in): Declare.
>         * builtins.c (called_as_built_in): Make external.
>         * internal-fn.h (expand_internal_call): Define a variant that
>         specifies the internal function explicitly.
>         * internal-fn.c (expand_load_lanes_optab_fn)
>         (expand_store_lanes_optab_fn, expand_ANNOTATE, expand_GOMP_SIMD_LANE)
>         (expand_GOMP_SIMD_VF, expand_GOMP_SIMD_LAST_LANE)
>         (expand_GOMP_SIMD_ORDERED_START, expand_GOMP_SIMD_ORDERED_END)
>         (expand_UBSAN_NULL, expand_UBSAN_BOUNDS, expand_UBSAN_VPTR)
>         (expand_UBSAN_OBJECT_SIZE, expand_ASAN_CHECK, expand_TSAN_FUNC_EXIT)
>         (expand_UBSAN_CHECK_ADD, expand_UBSAN_CHECK_SUB)
>         (expand_UBSAN_CHECK_MUL, expand_ADD_OVERFLOW, expand_SUB_OVERFLOW)
>         (expand_MUL_OVERFLOW, expand_LOOP_VECTORIZED)
>         (expand_mask_load_optab_fn, expand_mask_store_optab_fn)
>         (expand_ABNORMAL_DISPATCHER, expand_BUILTIN_EXPECT, expand_VA_ARG)
>         (expand_UNIQUE, expand_GOACC_DIM_SIZE, expand_GOACC_DIM_POS)
>         (expand_GOACC_LOOP, expand_GOACC_REDUCTION, expand_direct_optab_fn)
>         (expand_unary_optab_fn, expand_binary_optab_fn): Add an internal_fn
>         argument.
>         (internal_fn_expanders): Update prototype.
>         (expand_internal_call): Define a variant that specifies the
>         internal function explicitly. Use it to implement the previous
>         interface.
>         * cfgexpand.c (expand_call_stmt): Try to expand calls to built-in
>         functions as calls to internal functions.
>
> diff --git a/gcc/builtins.c b/gcc/builtins.c
> index 6df1b9b..c422d0d 100644
> --- a/gcc/builtins.c
> +++ b/gcc/builtins.c
> @@ -222,7 +222,7 @@ is_builtin_fn (tree decl)
>     of the optimization level.  This means whenever a function is invoked with
>     its "internal" name, which normally contains the prefix "__builtin".  */
>
> -static bool
> +bool
>  called_as_built_in (tree node)
>  {
>    /* Note that we must use DECL_NAME, not DECL_ASSEMBLER_NAME_SET_P since
> diff --git a/gcc/builtins.h b/gcc/builtins.h
> index 917eb90..1d00068 100644
> --- a/gcc/builtins.h
> +++ b/gcc/builtins.h
> @@ -50,6 +50,7 @@ extern struct target_builtins *this_target_builtins;
>  extern bool force_folding_builtin_constant_p;
>
>  extern bool is_builtin_fn (tree);
> +extern bool called_as_built_in (tree);
>  extern bool get_object_alignment_1 (tree, unsigned int *,
>                                     unsigned HOST_WIDE_INT *);
>  extern unsigned int get_object_alignment (tree);
> diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
> index e55467a..1990e10 100644
> --- a/gcc/cfgexpand.c
> +++ b/gcc/cfgexpand.c
> @@ -2551,10 +2551,25 @@ expand_call_stmt (gcall *stmt)
>        return;
>      }
>
> +  /* If this is a call to a built-in function and it has no effect other
> +     than setting the lhs, try to implement it using an internal function
> +     instead.  */
> +  decl = gimple_call_fndecl (stmt);
> +  if (gimple_call_lhs (stmt)
> +      && !gimple_has_side_effects (stmt)
> +      && (optimize || (decl && called_as_built_in (decl))))
> +    {
> +      internal_fn ifn = replacement_internal_fn (stmt);
> +      if (ifn != IFN_LAST)
> +       {
> +         expand_internal_call (ifn, stmt);
> +         return;
> +       }
> +    }
> +
>    exp = build_vl_exp (CALL_EXPR, gimple_call_num_args (stmt) + 3);
>
>    CALL_EXPR_FN (exp) = gimple_call_fn (stmt);
> -  decl = gimple_call_fndecl (stmt);
>    builtin_p = decl && DECL_BUILT_IN (decl);
>
>    /* If this is not a builtin function, the function type through which the
> diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
> index b853162..f23d799 100644
> --- a/gcc/internal-fn.c
> +++ b/gcc/internal-fn.c
> @@ -103,7 +103,7 @@ get_multi_vector_move (tree array_type, convert_optab optab)
>  /* Expand LOAD_LANES call STMT using optab OPTAB.  */
>
>  static void
> -expand_load_lanes_optab_fn (gcall *stmt, convert_optab optab)
> +expand_load_lanes_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
>  {
>    struct expand_operand ops[2];
>    tree type, lhs, rhs;
> @@ -127,7 +127,7 @@ expand_load_lanes_optab_fn (gcall *stmt, convert_optab optab)
>  /* Expand STORE_LANES call STMT using optab OPTAB.  */
>
>  static void
> -expand_store_lanes_optab_fn (gcall *stmt, convert_optab optab)
> +expand_store_lanes_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
>  {
>    struct expand_operand ops[2];
>    tree type, lhs, rhs;
> @@ -149,7 +149,7 @@ expand_store_lanes_optab_fn (gcall *stmt, convert_optab optab)
>  }
>
>  static void
> -expand_ANNOTATE (gcall *)
> +expand_ANNOTATE (internal_fn, gcall *)
>  {
>    gcc_unreachable ();
>  }
> @@ -157,7 +157,7 @@ expand_ANNOTATE (gcall *)
>  /* This should get expanded in adjust_simduid_builtins.  */
>
>  static void
> -expand_GOMP_SIMD_LANE (gcall *)
> +expand_GOMP_SIMD_LANE (internal_fn, gcall *)
>  {
>    gcc_unreachable ();
>  }
> @@ -165,7 +165,7 @@ expand_GOMP_SIMD_LANE (gcall *)
>  /* This should get expanded in adjust_simduid_builtins.  */
>
>  static void
> -expand_GOMP_SIMD_VF (gcall *)
> +expand_GOMP_SIMD_VF (internal_fn, gcall *)
>  {
>    gcc_unreachable ();
>  }
> @@ -173,7 +173,7 @@ expand_GOMP_SIMD_VF (gcall *)
>  /* This should get expanded in adjust_simduid_builtins.  */
>
>  static void
> -expand_GOMP_SIMD_LAST_LANE (gcall *)
> +expand_GOMP_SIMD_LAST_LANE (internal_fn, gcall *)
>  {
>    gcc_unreachable ();
>  }
> @@ -181,7 +181,7 @@ expand_GOMP_SIMD_LAST_LANE (gcall *)
>  /* This should get expanded in adjust_simduid_builtins.  */
>
>  static void
> -expand_GOMP_SIMD_ORDERED_START (gcall *)
> +expand_GOMP_SIMD_ORDERED_START (internal_fn, gcall *)
>  {
>    gcc_unreachable ();
>  }
> @@ -189,7 +189,7 @@ expand_GOMP_SIMD_ORDERED_START (gcall *)
>  /* This should get expanded in adjust_simduid_builtins.  */
>
>  static void
> -expand_GOMP_SIMD_ORDERED_END (gcall *)
> +expand_GOMP_SIMD_ORDERED_END (internal_fn, gcall *)
>  {
>    gcc_unreachable ();
>  }
> @@ -197,7 +197,7 @@ expand_GOMP_SIMD_ORDERED_END (gcall *)
>  /* This should get expanded in the sanopt pass.  */
>
>  static void
> -expand_UBSAN_NULL (gcall *)
> +expand_UBSAN_NULL (internal_fn, gcall *)
>  {
>    gcc_unreachable ();
>  }
> @@ -205,7 +205,7 @@ expand_UBSAN_NULL (gcall *)
>  /* This should get expanded in the sanopt pass.  */
>
>  static void
> -expand_UBSAN_BOUNDS (gcall *)
> +expand_UBSAN_BOUNDS (internal_fn, gcall *)
>  {
>    gcc_unreachable ();
>  }
> @@ -213,7 +213,7 @@ expand_UBSAN_BOUNDS (gcall *)
>  /* This should get expanded in the sanopt pass.  */
>
>  static void
> -expand_UBSAN_VPTR (gcall *)
> +expand_UBSAN_VPTR (internal_fn, gcall *)
>  {
>    gcc_unreachable ();
>  }
> @@ -221,7 +221,7 @@ expand_UBSAN_VPTR (gcall *)
>  /* This should get expanded in the sanopt pass.  */
>
>  static void
> -expand_UBSAN_OBJECT_SIZE (gcall *)
> +expand_UBSAN_OBJECT_SIZE (internal_fn, gcall *)
>  {
>    gcc_unreachable ();
>  }
> @@ -229,7 +229,7 @@ expand_UBSAN_OBJECT_SIZE (gcall *)
>  /* This should get expanded in the sanopt pass.  */
>
>  static void
> -expand_ASAN_CHECK (gcall *)
> +expand_ASAN_CHECK (internal_fn, gcall *)
>  {
>    gcc_unreachable ();
>  }
> @@ -237,7 +237,7 @@ expand_ASAN_CHECK (gcall *)
>  /* This should get expanded in the tsan pass.  */
>
>  static void
> -expand_TSAN_FUNC_EXIT (gcall *)
> +expand_TSAN_FUNC_EXIT (internal_fn, gcall *)
>  {
>    gcc_unreachable ();
>  }
> @@ -1639,7 +1639,7 @@ expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
>  /* Expand UBSAN_CHECK_ADD call STMT.  */
>
>  static void
> -expand_UBSAN_CHECK_ADD (gcall *stmt)
> +expand_UBSAN_CHECK_ADD (internal_fn, gcall *stmt)
>  {
>    location_t loc = gimple_location (stmt);
>    tree lhs = gimple_call_lhs (stmt);
> @@ -1652,7 +1652,7 @@ expand_UBSAN_CHECK_ADD (gcall *stmt)
>  /* Expand UBSAN_CHECK_SUB call STMT.  */
>
>  static void
> -expand_UBSAN_CHECK_SUB (gcall *stmt)
> +expand_UBSAN_CHECK_SUB (internal_fn, gcall *stmt)
>  {
>    location_t loc = gimple_location (stmt);
>    tree lhs = gimple_call_lhs (stmt);
> @@ -1668,7 +1668,7 @@ expand_UBSAN_CHECK_SUB (gcall *stmt)
>  /* Expand UBSAN_CHECK_MUL call STMT.  */
>
>  static void
> -expand_UBSAN_CHECK_MUL (gcall *stmt)
> +expand_UBSAN_CHECK_MUL (internal_fn, gcall *stmt)
>  {
>    location_t loc = gimple_location (stmt);
>    tree lhs = gimple_call_lhs (stmt);
> @@ -1853,7 +1853,7 @@ expand_arith_overflow (enum tree_code code, gimple *stmt)
>  /* Expand ADD_OVERFLOW STMT.  */
>
>  static void
> -expand_ADD_OVERFLOW (gcall *stmt)
> +expand_ADD_OVERFLOW (internal_fn, gcall *stmt)
>  {
>    expand_arith_overflow (PLUS_EXPR, stmt);
>  }
> @@ -1861,7 +1861,7 @@ expand_ADD_OVERFLOW (gcall *stmt)
>  /* Expand SUB_OVERFLOW STMT.  */
>
>  static void
> -expand_SUB_OVERFLOW (gcall *stmt)
> +expand_SUB_OVERFLOW (internal_fn, gcall *stmt)
>  {
>    expand_arith_overflow (MINUS_EXPR, stmt);
>  }
> @@ -1869,7 +1869,7 @@ expand_SUB_OVERFLOW (gcall *stmt)
>  /* Expand MUL_OVERFLOW STMT.  */
>
>  static void
> -expand_MUL_OVERFLOW (gcall *stmt)
> +expand_MUL_OVERFLOW (internal_fn, gcall *stmt)
>  {
>    expand_arith_overflow (MULT_EXPR, stmt);
>  }
> @@ -1877,7 +1877,7 @@ expand_MUL_OVERFLOW (gcall *stmt)
>  /* This should get folded in tree-vectorizer.c.  */
>
>  static void
> -expand_LOOP_VECTORIZED (gcall *)
> +expand_LOOP_VECTORIZED (internal_fn, gcall *)
>  {
>    gcc_unreachable ();
>  }
> @@ -1885,7 +1885,7 @@ expand_LOOP_VECTORIZED (gcall *)
>  /* Expand MASK_LOAD call STMT using optab OPTAB.  */
>
>  static void
> -expand_mask_load_optab_fn (gcall *stmt, convert_optab optab)
> +expand_mask_load_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
>  {
>    struct expand_operand ops[3];
>    tree type, lhs, rhs, maskt;
> @@ -1914,7 +1914,7 @@ expand_mask_load_optab_fn (gcall *stmt, convert_optab optab)
>  /* Expand MASK_STORE call STMT using optab OPTAB.  */
>
>  static void
> -expand_mask_store_optab_fn (gcall *stmt, convert_optab optab)
> +expand_mask_store_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
>  {
>    struct expand_operand ops[3];
>    tree type, lhs, rhs, maskt;
> @@ -1939,12 +1939,12 @@ expand_mask_store_optab_fn (gcall *stmt, convert_optab optab)
>  }
>
>  static void
> -expand_ABNORMAL_DISPATCHER (gcall *)
> +expand_ABNORMAL_DISPATCHER (internal_fn, gcall *)
>  {
>  }
>
>  static void
> -expand_BUILTIN_EXPECT (gcall *stmt)
> +expand_BUILTIN_EXPECT (internal_fn, gcall *stmt)
>  {
>    /* When guessing was done, the hints should be already stripped away.  */
>    gcc_assert (!flag_guess_branch_prob || optimize == 0 || seen_error ());
> @@ -1964,7 +1964,7 @@ expand_BUILTIN_EXPECT (gcall *stmt)
>     should never be called.  */
>
>  static void
> -expand_VA_ARG (gcall *stmt ATTRIBUTE_UNUSED)
> +expand_VA_ARG (internal_fn, gcall *)
>  {
>    gcc_unreachable ();
>  }
> @@ -1972,7 +1972,7 @@ expand_VA_ARG (gcall *stmt ATTRIBUTE_UNUSED)
>  /* Expand the IFN_UNIQUE function according to its first argument.  */
>
>  static void
> -expand_UNIQUE (gcall *stmt)
> +expand_UNIQUE (internal_fn, gcall *stmt)
>  {
>    rtx pattern = NULL_RTX;
>    enum ifn_unique_kind kind
> @@ -2018,7 +2018,7 @@ expand_UNIQUE (gcall *stmt)
>  /* The size of an OpenACC compute dimension.  */
>
>  static void
> -expand_GOACC_DIM_SIZE (gcall *stmt)
> +expand_GOACC_DIM_SIZE (internal_fn, gcall *stmt)
>  {
>    tree lhs = gimple_call_lhs (stmt);
>
> @@ -2039,7 +2039,7 @@ expand_GOACC_DIM_SIZE (gcall *stmt)
>  /* The position of an OpenACC execution engine along one compute axis.  */
>
>  static void
> -expand_GOACC_DIM_POS (gcall *stmt)
> +expand_GOACC_DIM_POS (internal_fn, gcall *stmt)
>  {
>    tree lhs = gimple_call_lhs (stmt);
>
> @@ -2060,7 +2060,7 @@ expand_GOACC_DIM_POS (gcall *stmt)
>  /* This is expanded by oacc_device_lower pass.  */
>
>  static void
> -expand_GOACC_LOOP (gcall *stmt ATTRIBUTE_UNUSED)
> +expand_GOACC_LOOP (internal_fn, gcall *)
>  {
>    gcc_unreachable ();
>  }
> @@ -2068,20 +2068,20 @@ expand_GOACC_LOOP (gcall *stmt ATTRIBUTE_UNUSED)
>  /* This is expanded by oacc_device_lower pass.  */
>
>  static void
> -expand_GOACC_REDUCTION (gcall *stmt ATTRIBUTE_UNUSED)
> +expand_GOACC_REDUCTION (internal_fn, gcall *)
>  {
>    gcc_unreachable ();
>  }
>
> -/* Expand call STMT using OPTAB, which has a single output operand and
> -   NARGS input operands.  */
> +/* Expand a call to FN using the operands in STMT.  FN has a single
> +   output operand and NARGS input operands.  */
>
>  static void
> -expand_direct_optab_fn (gcall *stmt, direct_optab optab, unsigned int nargs)
> +expand_direct_optab_fn (internal_fn fn, gcall *stmt, direct_optab optab,
> +                       unsigned int nargs)
>  {
>    expand_operand *ops = XALLOCAVEC (expand_operand, nargs + 1);
>
> -  internal_fn fn = gimple_call_internal_fn (stmt);
>    tree_pair types = direct_internal_fn_types (fn, stmt);
>    insn_code icode = direct_optab_handler (optab, TYPE_MODE (types.first));
>
> @@ -2119,11 +2119,11 @@ expand_direct_optab_fn (gcall *stmt, direct_optab optab, unsigned int nargs)
>
>  /* Expanders for optabs that can use expand_direct_optab_fn.  */
>
> -#define expand_unary_optab_fn(STMT, OPTAB) \
> -  expand_direct_optab_fn (STMT, OPTAB, 1)
> +#define expand_unary_optab_fn(FN, STMT, OPTAB) \
> +  expand_direct_optab_fn (FN, STMT, OPTAB, 1)
>
> -#define expand_binary_optab_fn(STMT, OPTAB) \
> -  expand_direct_optab_fn (STMT, OPTAB, 2)
> +#define expand_binary_optab_fn(FN, STMT, OPTAB) \
> +  expand_direct_optab_fn (FN, STMT, OPTAB, 2)
>
>  /* RETURN_TYPE and ARGS are a return type and argument list that are
>     in principle compatible with FN (which satisfies direct_internal_fn_p).
> @@ -2219,9 +2219,9 @@ direct_internal_fn_supported_p (internal_fn fn, tree type)
>
>  #define DEF_INTERNAL_OPTAB_FN(CODE, FLAGS, OPTAB, TYPE) \
>    static void                                          \
> -  expand_##CODE (gcall *stmt)                          \
> +  expand_##CODE (internal_fn fn, gcall *stmt)          \
>    {                                                    \
> -    expand_##TYPE##_optab_fn (stmt, OPTAB##_optab);    \
> +    expand_##TYPE##_optab_fn (fn, stmt, OPTAB##_optab);        \
>    }
>  #include "internal-fn.def"
>
> @@ -2231,16 +2231,24 @@ direct_internal_fn_supported_p (internal_fn fn, tree type)
>         expand_<NAME> (gcall *stmt)
>
>     where STMT is the statement that performs the call. */
> -static void (*const internal_fn_expanders[]) (gcall *) = {
> +static void (*const internal_fn_expanders[]) (internal_fn, gcall *) = {
>  #define DEF_INTERNAL_FN(CODE, FLAGS, FNSPEC) expand_##CODE,
>  #include "internal-fn.def"
>    0
>  };
>
> +/* Expand STMT as though it were a call to internal function FN.  */
> +
> +void
> +expand_internal_call (internal_fn fn, gcall *stmt)
> +{
> +  internal_fn_expanders[fn] (fn, stmt);
> +}
> +
>  /* Expand STMT, which is a call to internal function FN.  */
>
>  void
>  expand_internal_call (gcall *stmt)
>  {
> -  internal_fn_expanders[(int) gimple_call_internal_fn (stmt)] (stmt);
> +  expand_internal_call (gimple_call_internal_fn (stmt), stmt);
>  }
> diff --git a/gcc/internal-fn.h b/gcc/internal-fn.h
> index 31e895e..5ee43b8 100644
> --- a/gcc/internal-fn.h
> +++ b/gcc/internal-fn.h
> @@ -162,5 +162,6 @@ extern bool direct_internal_fn_supported_p (internal_fn, tree_pair);
>  extern bool direct_internal_fn_supported_p (internal_fn, tree);
>
>  extern void expand_internal_call (gcall *);
> +extern void expand_internal_call (internal_fn, gcall *);
>
>  #endif
>
diff mbox

Patch

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 6df1b9b..c422d0d 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -222,7 +222,7 @@  is_builtin_fn (tree decl)
    of the optimization level.  This means whenever a function is invoked with
    its "internal" name, which normally contains the prefix "__builtin".  */
 
-static bool
+bool
 called_as_built_in (tree node)
 {
   /* Note that we must use DECL_NAME, not DECL_ASSEMBLER_NAME_SET_P since
diff --git a/gcc/builtins.h b/gcc/builtins.h
index 917eb90..1d00068 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -50,6 +50,7 @@  extern struct target_builtins *this_target_builtins;
 extern bool force_folding_builtin_constant_p;
 
 extern bool is_builtin_fn (tree);
+extern bool called_as_built_in (tree);
 extern bool get_object_alignment_1 (tree, unsigned int *,
 				    unsigned HOST_WIDE_INT *);
 extern unsigned int get_object_alignment (tree);
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index e55467a..1990e10 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -2551,10 +2551,25 @@  expand_call_stmt (gcall *stmt)
       return;
     }
 
+  /* If this is a call to a built-in function and it has no effect other
+     than setting the lhs, try to implement it using an internal function
+     instead.  */
+  decl = gimple_call_fndecl (stmt);
+  if (gimple_call_lhs (stmt)
+      && !gimple_has_side_effects (stmt)
+      && (optimize || (decl && called_as_built_in (decl))))
+    {
+      internal_fn ifn = replacement_internal_fn (stmt);
+      if (ifn != IFN_LAST)
+	{
+	  expand_internal_call (ifn, stmt);
+	  return;
+	}
+    }
+
   exp = build_vl_exp (CALL_EXPR, gimple_call_num_args (stmt) + 3);
 
   CALL_EXPR_FN (exp) = gimple_call_fn (stmt);
-  decl = gimple_call_fndecl (stmt);
   builtin_p = decl && DECL_BUILT_IN (decl);
 
   /* If this is not a builtin function, the function type through which the
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index b853162..f23d799 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -103,7 +103,7 @@  get_multi_vector_move (tree array_type, convert_optab optab)
 /* Expand LOAD_LANES call STMT using optab OPTAB.  */
 
 static void
-expand_load_lanes_optab_fn (gcall *stmt, convert_optab optab)
+expand_load_lanes_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
 {
   struct expand_operand ops[2];
   tree type, lhs, rhs;
@@ -127,7 +127,7 @@  expand_load_lanes_optab_fn (gcall *stmt, convert_optab optab)
 /* Expand STORE_LANES call STMT using optab OPTAB.  */
 
 static void
-expand_store_lanes_optab_fn (gcall *stmt, convert_optab optab)
+expand_store_lanes_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
 {
   struct expand_operand ops[2];
   tree type, lhs, rhs;
@@ -149,7 +149,7 @@  expand_store_lanes_optab_fn (gcall *stmt, convert_optab optab)
 }
 
 static void
-expand_ANNOTATE (gcall *)
+expand_ANNOTATE (internal_fn, gcall *)
 {
   gcc_unreachable ();
 }
@@ -157,7 +157,7 @@  expand_ANNOTATE (gcall *)
 /* This should get expanded in adjust_simduid_builtins.  */
 
 static void
-expand_GOMP_SIMD_LANE (gcall *)
+expand_GOMP_SIMD_LANE (internal_fn, gcall *)
 {
   gcc_unreachable ();
 }
@@ -165,7 +165,7 @@  expand_GOMP_SIMD_LANE (gcall *)
 /* This should get expanded in adjust_simduid_builtins.  */
 
 static void
-expand_GOMP_SIMD_VF (gcall *)
+expand_GOMP_SIMD_VF (internal_fn, gcall *)
 {
   gcc_unreachable ();
 }
@@ -173,7 +173,7 @@  expand_GOMP_SIMD_VF (gcall *)
 /* This should get expanded in adjust_simduid_builtins.  */
 
 static void
-expand_GOMP_SIMD_LAST_LANE (gcall *)
+expand_GOMP_SIMD_LAST_LANE (internal_fn, gcall *)
 {
   gcc_unreachable ();
 }
@@ -181,7 +181,7 @@  expand_GOMP_SIMD_LAST_LANE (gcall *)
 /* This should get expanded in adjust_simduid_builtins.  */
 
 static void
-expand_GOMP_SIMD_ORDERED_START (gcall *)
+expand_GOMP_SIMD_ORDERED_START (internal_fn, gcall *)
 {
   gcc_unreachable ();
 }
@@ -189,7 +189,7 @@  expand_GOMP_SIMD_ORDERED_START (gcall *)
 /* This should get expanded in adjust_simduid_builtins.  */
 
 static void
-expand_GOMP_SIMD_ORDERED_END (gcall *)
+expand_GOMP_SIMD_ORDERED_END (internal_fn, gcall *)
 {
   gcc_unreachable ();
 }
@@ -197,7 +197,7 @@  expand_GOMP_SIMD_ORDERED_END (gcall *)
 /* This should get expanded in the sanopt pass.  */
 
 static void
-expand_UBSAN_NULL (gcall *)
+expand_UBSAN_NULL (internal_fn, gcall *)
 {
   gcc_unreachable ();
 }
@@ -205,7 +205,7 @@  expand_UBSAN_NULL (gcall *)
 /* This should get expanded in the sanopt pass.  */
 
 static void
-expand_UBSAN_BOUNDS (gcall *)
+expand_UBSAN_BOUNDS (internal_fn, gcall *)
 {
   gcc_unreachable ();
 }
@@ -213,7 +213,7 @@  expand_UBSAN_BOUNDS (gcall *)
 /* This should get expanded in the sanopt pass.  */
 
 static void
-expand_UBSAN_VPTR (gcall *)
+expand_UBSAN_VPTR (internal_fn, gcall *)
 {
   gcc_unreachable ();
 }
@@ -221,7 +221,7 @@  expand_UBSAN_VPTR (gcall *)
 /* This should get expanded in the sanopt pass.  */
 
 static void
-expand_UBSAN_OBJECT_SIZE (gcall *)
+expand_UBSAN_OBJECT_SIZE (internal_fn, gcall *)
 {
   gcc_unreachable ();
 }
@@ -229,7 +229,7 @@  expand_UBSAN_OBJECT_SIZE (gcall *)
 /* This should get expanded in the sanopt pass.  */
 
 static void
-expand_ASAN_CHECK (gcall *)
+expand_ASAN_CHECK (internal_fn, gcall *)
 {
   gcc_unreachable ();
 }
@@ -237,7 +237,7 @@  expand_ASAN_CHECK (gcall *)
 /* This should get expanded in the tsan pass.  */
 
 static void
-expand_TSAN_FUNC_EXIT (gcall *)
+expand_TSAN_FUNC_EXIT (internal_fn, gcall *)
 {
   gcc_unreachable ();
 }
@@ -1639,7 +1639,7 @@  expand_mul_overflow (location_t loc, tree lhs, tree arg0, tree arg1,
 /* Expand UBSAN_CHECK_ADD call STMT.  */
 
 static void
-expand_UBSAN_CHECK_ADD (gcall *stmt)
+expand_UBSAN_CHECK_ADD (internal_fn, gcall *stmt)
 {
   location_t loc = gimple_location (stmt);
   tree lhs = gimple_call_lhs (stmt);
@@ -1652,7 +1652,7 @@  expand_UBSAN_CHECK_ADD (gcall *stmt)
 /* Expand UBSAN_CHECK_SUB call STMT.  */
 
 static void
-expand_UBSAN_CHECK_SUB (gcall *stmt)
+expand_UBSAN_CHECK_SUB (internal_fn, gcall *stmt)
 {
   location_t loc = gimple_location (stmt);
   tree lhs = gimple_call_lhs (stmt);
@@ -1668,7 +1668,7 @@  expand_UBSAN_CHECK_SUB (gcall *stmt)
 /* Expand UBSAN_CHECK_MUL call STMT.  */
 
 static void
-expand_UBSAN_CHECK_MUL (gcall *stmt)
+expand_UBSAN_CHECK_MUL (internal_fn, gcall *stmt)
 {
   location_t loc = gimple_location (stmt);
   tree lhs = gimple_call_lhs (stmt);
@@ -1853,7 +1853,7 @@  expand_arith_overflow (enum tree_code code, gimple *stmt)
 /* Expand ADD_OVERFLOW STMT.  */
 
 static void
-expand_ADD_OVERFLOW (gcall *stmt)
+expand_ADD_OVERFLOW (internal_fn, gcall *stmt)
 {
   expand_arith_overflow (PLUS_EXPR, stmt);
 }
@@ -1861,7 +1861,7 @@  expand_ADD_OVERFLOW (gcall *stmt)
 /* Expand SUB_OVERFLOW STMT.  */
 
 static void
-expand_SUB_OVERFLOW (gcall *stmt)
+expand_SUB_OVERFLOW (internal_fn, gcall *stmt)
 {
   expand_arith_overflow (MINUS_EXPR, stmt);
 }
@@ -1869,7 +1869,7 @@  expand_SUB_OVERFLOW (gcall *stmt)
 /* Expand MUL_OVERFLOW STMT.  */
 
 static void
-expand_MUL_OVERFLOW (gcall *stmt)
+expand_MUL_OVERFLOW (internal_fn, gcall *stmt)
 {
   expand_arith_overflow (MULT_EXPR, stmt);
 }
@@ -1877,7 +1877,7 @@  expand_MUL_OVERFLOW (gcall *stmt)
 /* This should get folded in tree-vectorizer.c.  */
 
 static void
-expand_LOOP_VECTORIZED (gcall *)
+expand_LOOP_VECTORIZED (internal_fn, gcall *)
 {
   gcc_unreachable ();
 }
@@ -1885,7 +1885,7 @@  expand_LOOP_VECTORIZED (gcall *)
 /* Expand MASK_LOAD call STMT using optab OPTAB.  */
 
 static void
-expand_mask_load_optab_fn (gcall *stmt, convert_optab optab)
+expand_mask_load_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
 {
   struct expand_operand ops[3];
   tree type, lhs, rhs, maskt;
@@ -1914,7 +1914,7 @@  expand_mask_load_optab_fn (gcall *stmt, convert_optab optab)
 /* Expand MASK_STORE call STMT using optab OPTAB.  */
 
 static void
-expand_mask_store_optab_fn (gcall *stmt, convert_optab optab)
+expand_mask_store_optab_fn (internal_fn, gcall *stmt, convert_optab optab)
 {
   struct expand_operand ops[3];
   tree type, lhs, rhs, maskt;
@@ -1939,12 +1939,12 @@  expand_mask_store_optab_fn (gcall *stmt, convert_optab optab)
 }
 
 static void
-expand_ABNORMAL_DISPATCHER (gcall *)
+expand_ABNORMAL_DISPATCHER (internal_fn, gcall *)
 {
 }
 
 static void
-expand_BUILTIN_EXPECT (gcall *stmt)
+expand_BUILTIN_EXPECT (internal_fn, gcall *stmt)
 {
   /* When guessing was done, the hints should be already stripped away.  */
   gcc_assert (!flag_guess_branch_prob || optimize == 0 || seen_error ());
@@ -1964,7 +1964,7 @@  expand_BUILTIN_EXPECT (gcall *stmt)
    should never be called.  */
 
 static void
-expand_VA_ARG (gcall *stmt ATTRIBUTE_UNUSED)
+expand_VA_ARG (internal_fn, gcall *)
 {
   gcc_unreachable ();
 }
@@ -1972,7 +1972,7 @@  expand_VA_ARG (gcall *stmt ATTRIBUTE_UNUSED)
 /* Expand the IFN_UNIQUE function according to its first argument.  */
 
 static void
-expand_UNIQUE (gcall *stmt)
+expand_UNIQUE (internal_fn, gcall *stmt)
 {
   rtx pattern = NULL_RTX;
   enum ifn_unique_kind kind
@@ -2018,7 +2018,7 @@  expand_UNIQUE (gcall *stmt)
 /* The size of an OpenACC compute dimension.  */
 
 static void
-expand_GOACC_DIM_SIZE (gcall *stmt)
+expand_GOACC_DIM_SIZE (internal_fn, gcall *stmt)
 {
   tree lhs = gimple_call_lhs (stmt);
 
@@ -2039,7 +2039,7 @@  expand_GOACC_DIM_SIZE (gcall *stmt)
 /* The position of an OpenACC execution engine along one compute axis.  */
 
 static void
-expand_GOACC_DIM_POS (gcall *stmt)
+expand_GOACC_DIM_POS (internal_fn, gcall *stmt)
 {
   tree lhs = gimple_call_lhs (stmt);
 
@@ -2060,7 +2060,7 @@  expand_GOACC_DIM_POS (gcall *stmt)
 /* This is expanded by oacc_device_lower pass.  */
 
 static void
-expand_GOACC_LOOP (gcall *stmt ATTRIBUTE_UNUSED)
+expand_GOACC_LOOP (internal_fn, gcall *)
 {
   gcc_unreachable ();
 }
@@ -2068,20 +2068,20 @@  expand_GOACC_LOOP (gcall *stmt ATTRIBUTE_UNUSED)
 /* This is expanded by oacc_device_lower pass.  */
 
 static void
-expand_GOACC_REDUCTION (gcall *stmt ATTRIBUTE_UNUSED)
+expand_GOACC_REDUCTION (internal_fn, gcall *)
 {
   gcc_unreachable ();
 }
 
-/* Expand call STMT using OPTAB, which has a single output operand and
-   NARGS input operands.  */
+/* Expand a call to FN using the operands in STMT.  FN has a single
+   output operand and NARGS input operands.  */
 
 static void
-expand_direct_optab_fn (gcall *stmt, direct_optab optab, unsigned int nargs)
+expand_direct_optab_fn (internal_fn fn, gcall *stmt, direct_optab optab,
+			unsigned int nargs)
 {
   expand_operand *ops = XALLOCAVEC (expand_operand, nargs + 1);
 
-  internal_fn fn = gimple_call_internal_fn (stmt);
   tree_pair types = direct_internal_fn_types (fn, stmt);
   insn_code icode = direct_optab_handler (optab, TYPE_MODE (types.first));
 
@@ -2119,11 +2119,11 @@  expand_direct_optab_fn (gcall *stmt, direct_optab optab, unsigned int nargs)
 
 /* Expanders for optabs that can use expand_direct_optab_fn.  */
 
-#define expand_unary_optab_fn(STMT, OPTAB) \
-  expand_direct_optab_fn (STMT, OPTAB, 1)
+#define expand_unary_optab_fn(FN, STMT, OPTAB) \
+  expand_direct_optab_fn (FN, STMT, OPTAB, 1)
 
-#define expand_binary_optab_fn(STMT, OPTAB) \
-  expand_direct_optab_fn (STMT, OPTAB, 2)
+#define expand_binary_optab_fn(FN, STMT, OPTAB) \
+  expand_direct_optab_fn (FN, STMT, OPTAB, 2)
 
 /* RETURN_TYPE and ARGS are a return type and argument list that are
    in principle compatible with FN (which satisfies direct_internal_fn_p).
@@ -2219,9 +2219,9 @@  direct_internal_fn_supported_p (internal_fn fn, tree type)
 
 #define DEF_INTERNAL_OPTAB_FN(CODE, FLAGS, OPTAB, TYPE) \
   static void						\
-  expand_##CODE (gcall *stmt)				\
+  expand_##CODE (internal_fn fn, gcall *stmt)		\
   {							\
-    expand_##TYPE##_optab_fn (stmt, OPTAB##_optab);	\
+    expand_##TYPE##_optab_fn (fn, stmt, OPTAB##_optab);	\
   }
 #include "internal-fn.def"
 
@@ -2231,16 +2231,24 @@  direct_internal_fn_supported_p (internal_fn fn, tree type)
        expand_<NAME> (gcall *stmt)
 
    where STMT is the statement that performs the call. */
-static void (*const internal_fn_expanders[]) (gcall *) = {
+static void (*const internal_fn_expanders[]) (internal_fn, gcall *) = {
 #define DEF_INTERNAL_FN(CODE, FLAGS, FNSPEC) expand_##CODE,
 #include "internal-fn.def"
   0
 };
 
+/* Expand STMT as though it were a call to internal function FN.  */
+
+void
+expand_internal_call (internal_fn fn, gcall *stmt)
+{
+  internal_fn_expanders[fn] (fn, stmt);
+}
+
 /* Expand STMT, which is a call to internal function FN.  */
 
 void
 expand_internal_call (gcall *stmt)
 {
-  internal_fn_expanders[(int) gimple_call_internal_fn (stmt)] (stmt);
+  expand_internal_call (gimple_call_internal_fn (stmt), stmt);
 }
diff --git a/gcc/internal-fn.h b/gcc/internal-fn.h
index 31e895e..5ee43b8 100644
--- a/gcc/internal-fn.h
+++ b/gcc/internal-fn.h
@@ -162,5 +162,6 @@  extern bool direct_internal_fn_supported_p (internal_fn, tree_pair);
 extern bool direct_internal_fn_supported_p (internal_fn, tree);
 
 extern void expand_internal_call (gcall *);
+extern void expand_internal_call (internal_fn, gcall *);
 
 #endif