public inbox for gcc@gcc.gnu.org
 help / color / mirror / Atom feed
From: Richard Earnshaw <Richard.Earnshaw@foss.arm.com>
To: Martin Sebor <msebor@gmail.com>,
	Prathamesh Kulkarni <prathamesh.kulkarni@linaro.org>,
	Richard Biener <richard.guenther@gmail.com>
Cc: GCC Development <gcc@gcc.gnu.org>
Subject: Re: [RFC] Adding a new attribute to function param to mark it as constant
Date: Fri, 6 Aug 2021 11:51:27 +0100	[thread overview]
Message-ID: <62f7087a-8adf-0a83-1fdf-d24ac49d04d2@foss.arm.com> (raw)
In-Reply-To: <0cf362fc-328f-5256-d289-e6b6fda838cd@gmail.com>



On 06/08/2021 01:06, Martin Sebor via Gcc wrote:
> On 8/4/21 3:46 AM, Richard Earnshaw wrote:
>>
>>
>> On 03/08/2021 18:44, Martin Sebor wrote:
>>> On 8/3/21 4:11 AM, Prathamesh Kulkarni via Gcc wrote:
>>>> On Tue, 27 Jul 2021 at 13:49, Richard Biener 
>>>> <richard.guenther@gmail.com> wrote:
>>>>>
>>>>> On Mon, Jul 26, 2021 at 11:06 AM Prathamesh Kulkarni via Gcc
>>>>> <gcc@gcc.gnu.org> wrote:
>>>>>>
>>>>>> On Fri, 23 Jul 2021 at 23:29, Andrew Pinski <pinskia@gmail.com> 
>>>>>> wrote:
>>>>>>>
>>>>>>> On Fri, Jul 23, 2021 at 3:55 AM Prathamesh Kulkarni via Gcc
>>>>>>> <gcc@gcc.gnu.org> wrote:
>>>>>>>>
>>>>>>>> Hi,
>>>>>>>> Continuing from this thread,
>>>>>>>> https://gcc.gnu.org/pipermail/gcc-patches/2021-July/575920.html
>>>>>>>> The proposal is to provide a mechanism to mark a parameter in a
>>>>>>>> function as a literal constant.
>>>>>>>>
>>>>>>>> Motivation:
>>>>>>>> Consider the following intrinsic vshl_n_s32 from arrm/arm_neon.h:
>>>>>>>>
>>>>>>>> __extension__ extern __inline int32x2_t
>>>>>>>> __attribute__  ((__always_inline__, __gnu_inline__, 
>>>>>>>> __artificial__))
>>>>>>>> vshl_n_s32 (int32x2_t __a, const int __b)
>>>>>>>> {
>>>>>>>>    return (int32x2_t)__builtin_neon_vshl_nv2si (__a, __b);
>>>>>>>> }
>>>>>>>>
>>>>>>>> and it's caller:
>>>>>>>>
>>>>>>>> int32x2_t f (int32x2_t x)
>>>>>>>> {
>>>>>>>>     return vshl_n_s32 (x, 1);
>>>>>>>> }
>>>>>>>
>>>>>>> Can't you do similar to what is done already in the aarch64 
>>>>>>> back-end:
>>>>>>> #define __AARCH64_NUM_LANES(__v) (sizeof (__v) / sizeof (__v[0]))
>>>>>>> #define __AARCH64_LANE_CHECK(__vec, __idx)      \
>>>>>>>          __builtin_aarch64_im_lane_boundsi (sizeof(__vec),
>>>>>>> sizeof(__vec[0]), __idx)
>>>>>>>
>>>>>>> ?
>>>>>>> Yes this is about lanes but you could even add one for min/max which
>>>>>>> is generic and such; add an argument to say the intrinsics name 
>>>>>>> even.
>>>>>>> You could do this as a non-target builtin if you want and reuse it
>>>>>>> also for the aarch64 backend.
>>>>>> Hi Andrew,
>>>>>> Thanks for the suggestions. IIUC, we could use this approach to check
>>>>>> if the argument
>>>>>> falls within a certain range (min / max), but I am not sure how it
>>>>>> will help to determine
>>>>>> if the arg is a constant immediate ? AFAIK, vshl_n intrinsics require
>>>>>> that the 2nd arg is immediate ?
>>>>>>
>>>>>> Even the current RTL builtin checking is not consistent across
>>>>>> optimization levels:
>>>>>> For eg:
>>>>>> int32x2_t f(int32_t *restrict a)
>>>>>> {
>>>>>>    int32x2_t v = vld1_s32 (a);
>>>>>>    int b = 2;
>>>>>>    return vshl_n_s32 (v, b);
>>>>>> }
>>>>>>
>>>>>> With pristine trunk, compiling with -O2 results in no errors because
>>>>>> constant propagation replaces 'b' with 2, and during expansion,
>>>>>> expand_builtin_args is happy. But at -O0, it results in the error -
>>>>>> "argument 2 must be a constant immediate".
>>>>>>
>>>>>> So I guess we need some mechanism to mark a parameter as a constant ?
>>>>>
>>>>> I guess you want to mark it in a way that the frontend should force
>>>>> constant evaluation and error if that's not possible?   C++ doesn't
>>>>> allow to declare a parameter as 'constexpr' but something like
>>>>>
>>>>> void foo (consteval int i);
>>>>>
>>>>> since I guess you do want to allow passing constexpr arguments
>>>>> in C++ or in C extended forms of constants like
>>>>>
>>>>> static const int a[4];
>>>>>
>>>>> foo (a[1]);
>>>>>
>>>>> ?  But yes, this looks useful to me.
>>>> Hi Richard,
>>>> Thanks for the suggestions and sorry for late response.
>>>> I have attached a prototype patch that implements consteval attribute.
>>>> As implemented, the attribute takes at least one argument(s), which
>>>> refer to parameter position,
>>>> and the corresponding parameter must be const qualified, failing
>>>> which, the attribute is ignored.
>>>
>>> I'm curious why the argument must be const-qualified.  If it's
>>> to keep it from being changed in ways that would prevent it from
>>> being evaluated at compile-time in the body of the function then
>>> to be effective, the enforcement of the constraint should be on
>>> the definition of the function.  Otherwise, the const qualifier
>>> could be used in a declaration of a function but left out from
>>> a subsequent definition of it, letting it modify it, like so:
>>>
>>>    __attribute__ ((consteval (1))) void f (const int);
>>>
>>>    inline __attribute__ ((always_inline)) void f (int i) { ++i; }
>>
>> In this particular case it's because the inline function is 
>> implementing an intrinsic operation in the architecture and the 
>> instruction only supports a literal constant value.  At present we 
>> catch this while trying to expand the intrinsic, but that can lead to 
>> poor diagnostics because we really want to report against the line of 
>> code calling the intrinsic.
> 
> Presumably the intrinsics can accept (or can be made to accept) any
> constant integer expressions, not just literals.  E.g., the aarch64
> builtin below accepts them.  For example, this is accepted in C++:
> 
>    __Int64x2_t void f (__Int32x2_t a)
>    {
>      constexpr int n = 2;
>      return __builtin_aarch64_vshll_nv2si (a, n + 1);
>    }
> 
> Making the intrinscis accept constant arguments in constexpr-like
> functions and introducing a constexpr-lite attribute (for C code)
> was what I was suggesting bythe constexpr comment below.  I'd find
> that a much more general and more powerful design.
> 

Yes, it would be unfortunate if the rule made it impossible to avoid 
idiomatic const-exprs like those you would write in C++, or even those 
you'd write naturally in C:

#define foo (1u << 5)


> But my comment above was to highlight that if requiring the function
> argument referenced by the proposed consteval attribute to be const
> is necessary to prevent it from being modified then the requirement
> needs to be enforced not on the declaration but on the definition.
> 
> You may rightly say: "but we get to define the inline arm function
> wrappers so we'll make sure to never declare them that way."  I don't
> have a problem with that.  What I am saying is that a consteval
> attribute that required the function argument to be declared const
> to be effective would be flawed as a general-purpose feature without
> enforcing the requirement on the definition.

I'm not totally sure I understand what you're saying.  But the point of 
putting the attribute on the declaration is to allow for reporting 
errors at the point of invocation, so if I call a function with an 
invalid 'must-be-const-expr' value, I'll get a diagnostic at the point 
in the source where that is done, not at the point in the (presumably 
inlined) function where the value ends up being used.  Most of our 
builtins are wrapped into slightly more user-friendly function names so 
that they conform to the ACLE specification, yet ultimately map onto 
names into __builtin namespace per gcc's internal standards.  If I have:

__extension__ extern __inline int8x8_t
__attribute__  ((__always_inline__, __gnu_inline__, __artificial__))
vshr_n_s8 (int8x8_t __a, const int __b)
{
   return (int8x8_t)__builtin_neon_vshrs_nv8qi (__a, __b);
}


and then use that with

extern int8x8_t x;
extern int y;

f() {
   int8x8_t z = vshr_n_s8 (x, y);  // Error y must be a const int expr.
   ...
}

I want the error to be reported on the line in f() where the problem 
really lies, not on the line in the inline function where the problem 
eventually manifests itself.

R.

> 
> Martin
> 
> 
>>
>> R.
>>>
>>> That said, if compile-time function evaluation is the goal then
>>> a fully general solution is an attribute that applies to the whole
>>> function, not just a subset of its arguments.  That way arguments
>>> can also be assigned to local variables within the function that
>>> can then be modified while still evaluated at compile time and
>>> used where constant expressions are expected.  I.e., the design
>>> goal is [a subset of] C++ constexpr.  (Obviously a much bigger
>>> project.)
>>>
>>> A few notes on the prototype patch: conventionally GCC warnings
>>> about attributes do not mention when an attribute is ignored.
>>> It may be a nice touch to add to all of them but I'd recommend
>>> against doing that in individual handlers.  Since the attribute
>>> allows pointer constants the warning issued when an argument is
>>> not one should be generalized (i.e., not refer to just integer
>>> constants).
>>>
>>> (Other than that, C/C++ warnings should start in lowercase and
>>> not end in a period).
>>>
>>> Martin
>>>
>>>>
>>>> The patch does type-checking for arguments in
>>>> check_function_consteval_attr, which
>>>> simply does a linear search to see if the corresponding argument
>>>> number is included in consteval attribute,
>>>> and if yes, it checks if the argument is CONSTANT_CLASS_P.
>>>>
>>>> This works for simple constants and the intrinsics use-case, but
>>>> rejects "extended constants" as in your above example.
>>>> I guess we can special case to detect cases like a[i] where 'a' is
>>>> const and 'i' is an immediate,
>>>> but I am not sure if there's a way to force constant evaluation in FE ?
>>>> I tried using c_fully_fold (argarray[i], false, &maybe_const) but that
>>>> didn't seem to work.
>>>> Do you have any suggestions on how to detect those in the front-end ?
>>>>
>>>> Example:
>>>>
>>>> __attribute__((consteval(1, 2)))
>>>> void f(const int x, int *p)
>>>> {}
>>>>
>>>> Calling it with:
>>>> 1) f(0, (int *) 0) is accepted.
>>>>
>>>> 2)
>>>> void g(int *p)
>>>> {
>>>>    f (0, p);
>>>> }
>>>>
>>>> emits the following error:
>>>> test.c: In function ‘g’:
>>>> test.c:7:9: error: argument 2 is not a constant.
>>>>      7 |   f (0, p);
>>>>        |         ^
>>>> test.c:2:6: note: Function ‘f’ has parameter 2 with consteval 
>>>> attribute.
>>>>      2 | void f(const int x, int *const p)
>>>>        |      ^
>>>>
>>>> Thanks,
>>>> Prathamesh
>>>>>
>>>>> Richard.
>>>>>
>>>>>>
>>>>>> Thanks,
>>>>>> Prathamesh
>>>>>>>
>>>>>>> Thanks,
>>>>>>> Andrew Pinski
>>>>>>>
>>>>>>>>
>>>>>>>> The constraint here is that, vshl_n<type> intrinsics require 
>>>>>>>> that the
>>>>>>>> second arg (__b),
>>>>>>>> should be an immediate value.
>>>>>>>> Currently, this check is performed by arm_expand_builtin_args, 
>>>>>>>> and if
>>>>>>>> a non-constant
>>>>>>>> value gets passed, it emits the following diagnostic:
>>>>>>>>
>>>>>>>> ../armhf-build/gcc/include/arm_neon.h:4904:10: error: argument 2 
>>>>>>>> must
>>>>>>>> be a constant immediate
>>>>>>>>   4904 |   return (int32x2_t)__builtin_neon_vshl_nv2si (__a, __b);
>>>>>>>>        |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>>>>>>>>
>>>>>>>> However, we're trying to replace builtin calls with gcc's C vector
>>>>>>>> extensions where
>>>>>>>> possible (PR66791), because the builtins are opaque to the 
>>>>>>>> optimizers.
>>>>>>>>
>>>>>>>> Unfortunately, we lose type checking of immediate value if we 
>>>>>>>> replace
>>>>>>>> the builtin
>>>>>>>> with << operator:
>>>>>>>>
>>>>>>>> __extension__ extern __inline int32x2_t
>>>>>>>> __attribute__  ((__always_inline__, __gnu_inline__, 
>>>>>>>> __artificial__))
>>>>>>>> vshl_n_s32 (int32x2_t __a, const int __b)
>>>>>>>> {
>>>>>>>>    return __a << __b;
>>>>>>>> }
>>>>>>>>
>>>>>>>> So, I was wondering if we should have an attribute for a 
>>>>>>>> parameter to
>>>>>>>> specifically
>>>>>>>> mark it as a constant value with optional range value info ?
>>>>>>>> As Richard suggested, sth like:
>>>>>>>> void foo(int x __attribute__((literal_constant (min_val, 
>>>>>>>> max_val)));
>>>>>>>>
>>>>>>>> Thanks,
>>>>>>>> Prathamesh
>>>
> 

  reply	other threads:[~2021-08-06 10:51 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-07-23 10:53 Prathamesh Kulkarni
2021-07-23 17:59 ` Andrew Pinski
2021-07-26  9:04   ` Prathamesh Kulkarni
2021-07-27  8:19     ` Richard Biener
2021-08-03 10:11       ` Prathamesh Kulkarni
2021-08-03 10:13         ` Prathamesh Kulkarni
2021-08-03 17:44         ` Martin Sebor
2021-08-04  9:46           ` Richard Earnshaw
2021-08-06  0:06             ` Martin Sebor
2021-08-06 10:51               ` Richard Earnshaw [this message]
2021-08-06 20:39                 ` Martin Sebor
2021-08-12  8:32                   ` Prathamesh Kulkarni
2021-08-13 17:14                     ` Martin Sebor
2021-08-18  6:52                       ` Prathamesh Kulkarni
2021-08-18 14:40                         ` Martin Sebor
2021-08-19  8:10                           ` Prathamesh Kulkarni
2021-08-03 21:55 ` Segher Boessenkool
2021-08-04  9:50   ` Prathamesh Kulkarni
2021-08-04 10:17     ` Segher Boessenkool
2021-08-04 11:50       ` Prathamesh Kulkarni
2021-08-04 12:46         ` Segher Boessenkool
2021-08-04 13:00           ` Richard Earnshaw
2021-08-04 13:40             ` Segher Boessenkool
2021-08-04 14:27               ` Richard Earnshaw
2021-08-04 16:16                 ` Segher Boessenkool
2021-08-04 17:08                   ` Florian Weimer
2021-08-04 17:59                     ` Segher Boessenkool
2021-08-05  9:32                       ` Richard Earnshaw
2021-08-05  9:01             ` Prathamesh Kulkarni
2021-08-05 15:06               ` Segher Boessenkool
2021-08-06 20:10 Martin Uecker

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=62f7087a-8adf-0a83-1fdf-d24ac49d04d2@foss.arm.com \
    --to=richard.earnshaw@foss.arm.com \
    --cc=gcc@gcc.gnu.org \
    --cc=msebor@gmail.com \
    --cc=prathamesh.kulkarni@linaro.org \
    --cc=richard.guenther@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).