This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: _Nomodify extension idea for optimization and safety


On 8/23/19 10:44 AM, MCC CS wrote:
Hi, I've thought of a simple optimization.
I'm sending this because I'd like feedback on it,
and I might submit a C proposal a few years later.

This (https://theartofmachinery.com/2019/08/12/c_const_isnt_for_performance.html)
blog post inspired me. The summary is that the const-ness of const pointers
in function prototypes can be cast away.

True, const can be cast away.  Compilers are of course free
to track the constness of declared objects independently of
the qualifier but the information gets lost at (out-of-line)
function boundaries, and there's no good way to declare
an allocated object const (at least not in C) that compilers
rely on.


I've thought of _Nomodify as a stricter const, but it can
only be used in function parameters that are pointers. (Thus it implies
const, except that it can be modified without the need of casting away,
details are in the paragraph that starts with Moreover)

C specifies that an object cannot be modified via a pointer
that's both const and restrict-qualified, so _Nomodify is
the equivalent of that.  I don't know of any compiler that
takes advantage of it (bug 81009 tracks this enhancement in
GCC).

That being said, I have been sitting on a patch that implements
a superset of this idea via a few attributes, as a solution for
bug 83859:

  read_only (ptr-arg, size-arg)
  write_only (ptr-arg, size-arg)
  read_write (ptr-arg, size-arg)
  no_side_effect

(please see the bug for more details).  My primary goal is to
enable buffer overflow diagnostics for user-defined functions
but it enables the same basic optimizations as well (the patch
implements just a small subset).  There are still a few open
design questions (also mentioned in the bug) but the basic
functionality is there.

Example of casting away const:

void a(char * h) {}

void b(const char * x) {
     a(x);
}

void c(void) {
     char x[3] = {1, 2, 3};
     b(x);
}

which only gives a warning. For safety, if _Nomodify were used instead
of const, the compiler should've given an error (or we can just flag it as
UB and optimize it?) since if a function had a parameter that was declared
in one of the caller functions' parameters as _Nomodify, the callee's that
parameter should also be tagged as _Nomodify.

(i.e. if b's x parameter is _Nomodify, so should be a's h parameter)

----

Moreover, I've thought of a way to extend its usage:

- A function may change the value pointed by a _Nomodify pointer,
given that the function guarantees to restore the value before returning
to the caller body.

Like this:

void a(_Nomodify char * h) {}

void b(_Nomodify char * x) {
     *x ^= 1;
     a(x);
     *x ^= 1;
}

Interesting.  I'm not sure this is a good idea.  It seems that
it would limit both optimization and diagnostic opportunities.
I suppose _Nomodify could be combined with const to express
the same constraint as const restrict, but still.

void c(void) {
     char x[3] = {1, 2, 3};
     b(x);
}

The only problems with this idea are not-injective functions.
While multiplying real numbers by a (nonzero) coefficient is
injective, neither int nor float multiplication is injective.
(For example (a*2)/2 may not be a for a = 2147483647)
This might open security vulnerabilities, but they could
just copy the value pointed by the pointer to a temporary
variable, and then restore it before returning.

----

Something I'm not sure about is, since it only affects pointers,
should it be used like restrict:

void a(int * _Nomodify);

or const:

void a(int _Nomodify *);

----

One way to implement this in the compilers is making the compiler read

void a(some_t * _Nomodify);

void b(some_t * x) {
     a(x);
}

as

void a(some_t * _Nomodify);

void b(some_t * x) {
     const some_t r = *x;
     a(&r);
}

as highlighted in the blog post I linked. This'd allow one to see how
much this optimization can help their code.

Besides, it should be able to be applied only to function parameters (or at
least it makes sense to only do so)

Lastly, it can't be added as an __attribute__, because it should work
for pointers-of-pointers-of-... but each __attribute__ can be used once for
a variable IIRC.

Yes, that's a limitation of the function attribute approach.

I think the limitation can be overcome by letting the attributes
apply to variables as well.  That way one could associate arrays
or pointers and object sizes not only in function arguments but
outside as well.  For instance, in C:

  struct S {
    int n;
    __attribute__ ((read_write (((struct S*)0)->n)) int a[];
  };

or in C++:

  struct S {
    int n;
    __attribute__ ((read_write (S::n)) int a[];
  };

This is just an idea.  My patch doesn't allow it and I don't plan
to implement it in the first iteration.

Martin


Please help me develop this idea by leaving feedback about possible flaws.
I'm asking for feedback here, because programmers involved in compilers
have lots of experience related to optimizations and safety warnings,
so please, let's improve this draft.



Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]