Jump to page: 1 2
Thread overview
Missed optimisation case - internal use of STCin
Apr 19, 2014
Iain Buclaw
Apr 19, 2014
Iain Buclaw
Apr 19, 2014
Artur Skawina
Apr 19, 2014
Iain Buclaw
Apr 19, 2014
Iain Buclaw
Apr 19, 2014
Artur Skawina
Apr 19, 2014
Iain Buclaw
Apr 19, 2014
Dicebot
Apr 19, 2014
Iain Buclaw
Apr 19, 2014
Dicebot
Apr 19, 2014
Artur Skawina
Apr 20, 2014
Iain Buclaw
Apr 20, 2014
Artur Skawina
Apr 20, 2014
Iain Buclaw
Apr 21, 2014
Iain Buclaw
Apr 21, 2014
Iain Buclaw
Apr 21, 2014
Artur Skawina
April 19, 2014
Hi,

I'm currently testing out a GCC optimisation that allows you to set call argument flags.  The current assumptions being:

in parameters  =>  Assume no escape, no clobber (read-only).
ref parameters, classes and pointers  =>  Assume worst case.
default  =>  Assume no escape.


See here for implementation details:
http://gcc.gnu.org/ml/fortran/2010-05/msg00032.html


The idea for the 'in' parameters being that if we have the following:
--
bool somefunc (in char[] a);


The compiler can assume that whatever parameters are passed, they do not changes.  So the compiler can more aggressively optimise the following case, for instance.

eg:
--
char[] foo = "bar";
assert(foo == "bar");
somefunc(foo);
assert(foo == "bar");


One problem I've found with this though, is that the front-end tends to set all library function parameters as STCin, so the optimisation is fundamentally broken:

eg:
--
int[3] arr = [2,3,1];
arr.sort;  // Defined internally as _adSort(in void[], in TypeInfo);
assert(arr[0] == 1);  // Compiler infers as false, throws assert.


I think it would be more than reasonable to fix the frontend here so that the internal representation better matches that found in the internal runtime library.  Just wanted to pass this by you guys first.


Regards
Iain.
April 19, 2014
On Saturday, 19 April 2014 at 10:49:22 UTC, Iain Buclaw wrote:
> Hi,
>
> I'm currently testing out a GCC optimisation that allows you to set call argument flags.  The current assumptions being:
>
> in parameters  =>  Assume no escape, no clobber (read-only).
> ref parameters, classes and pointers  =>  Assume worst case.
> default  =>  Assume no escape.
>

That should read:

ref parameters, inout parameters, classes and pointers.

The default of assuming no escape is an experiment - I may limit this to only scalar types, and parameters marked as 'scope'  (So long as no one plans on deprecating it soon :)
April 19, 2014
On 04/19/14 13:03, Iain Buclaw via Digitalmars-d wrote:
> On Saturday, 19 April 2014 at 10:49:22 UTC, Iain Buclaw wrote:
>> Hi,
>>
>> I'm currently testing out a GCC optimisation that allows you to set call argument flags.  The current assumptions being:
>>
>> in parameters  =>  Assume no escape, no clobber (read-only). ref parameters, classes and pointers  =>  Assume worst case. default  =>  Assume no escape.
>>
> 
> That should read:
> 
> ref parameters, inout parameters, classes and pointers.
> 
> The default of assuming no escape is an experiment - I may limit this to only scalar types, and parameters marked as 'scope'  (So long as no one plans on deprecating it soon :)

What does "assume no escape" actually mean?
[The above list doesn't really make sense. W/o context, it's
hard to even tell why, hence the question.]

Also, 'inout' is about constness -- doesn't affect lifetime and reachability, but does imply no clobbering.

artur
April 19, 2014
On 19 April 2014 13:02, Artur Skawina via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> On 04/19/14 13:03, Iain Buclaw via Digitalmars-d wrote:
>> On Saturday, 19 April 2014 at 10:49:22 UTC, Iain Buclaw wrote:
>>> Hi,
>>>
>>> I'm currently testing out a GCC optimisation that allows you to set call argument flags.  The current assumptions being:
>>>
>>> in parameters  =>  Assume no escape, no clobber (read-only). ref parameters, classes and pointers  =>  Assume worst case. default  =>  Assume no escape.
>>>
>>
>> That should read:
>>
>> ref parameters, inout parameters, classes and pointers.
>>
>> The default of assuming no escape is an experiment - I may limit this to only scalar types, and parameters marked as 'scope'  (So long as no one plans on deprecating it soon :)
>
> What does "assume no escape" actually mean?
> [The above list doesn't really make sense. W/o context, it's
> hard to even tell why, hence the question.]
>
> Also, 'inout' is about constness -- doesn't affect lifetime and reachability, but does imply no clobbering.
>

'out' parameters - I sometimes confuse the two when I have a head cold. :o)
April 19, 2014
On 19 April 2014 13:02, Artur Skawina via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> On 04/19/14 13:03, Iain Buclaw via Digitalmars-d wrote:
>> On Saturday, 19 April 2014 at 10:49:22 UTC, Iain Buclaw wrote:
>>> Hi,
>>>
>>> I'm currently testing out a GCC optimisation that allows you to set call argument flags.  The current assumptions being:
>>>
>>> in parameters  =>  Assume no escape, no clobber (read-only). ref parameters, classes and pointers  =>  Assume worst case. default  =>  Assume no escape.
>>>
>>
>> That should read:
>>
>> ref parameters, inout parameters, classes and pointers.
>>
>> The default of assuming no escape is an experiment - I may limit this to only scalar types, and parameters marked as 'scope'  (So long as no one plans on deprecating it soon :)
>
> What does "assume no escape" actually mean?
> [The above list doesn't really make sense. W/o context, it's
> hard to even tell why, hence the question.]
>

Actually, I might change the default to assume worst case.  I've just tried this out, which is still valid.

class C {
   int * p;
   this(int x) {
     p = &x; // escapes the address of the parameter.
   }
}


Worse, scope doesn't error on the general case either.

class D {
   int * p;
   this(scope int x) {
     p = &x; // escapes the address of the scope parameter.
   }
}


Do these examples give you a good example?
April 19, 2014
On 04/19/14 14:37, Iain Buclaw via Digitalmars-d wrote:
> On 19 April 2014 13:02, Artur Skawina via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>> On 04/19/14 13:03, Iain Buclaw via Digitalmars-d wrote:
>>> On Saturday, 19 April 2014 at 10:49:22 UTC, Iain Buclaw wrote:
>>>> Hi,
>>>>
>>>> I'm currently testing out a GCC optimisation that allows you to set call argument flags.  The current assumptions being:
>>>>
>>>> in parameters  =>  Assume no escape, no clobber (read-only). ref parameters, classes and pointers  =>  Assume worst case. default  =>  Assume no escape.
>>>>
>>>
>>> That should read:
>>>
>>> ref parameters, inout parameters, classes and pointers.
>>>
>>> The default of assuming no escape is an experiment - I may limit this to only scalar types, and parameters marked as 'scope'  (So long as no one plans on deprecating it soon :)
>>
>> What does "assume no escape" actually mean?
>> [The above list doesn't really make sense. W/o context, it's
>> hard to even tell why, hence the question.]
>>
> 
> Actually, I might change the default to assume worst case.  I've just tried this out, which is still valid.
> 
> class C {
>    int * p;
>    this(int x) {
>      p = &x; // escapes the address of the parameter.
>    }
> }

This might be currently accepted, but it is clearly invalid
(escapes local; the only way to make it work safely would
be to silently copy 'x' to the GC-managed heap, which would be
way too costly).


   A f(A a) { g(&a); return a; } // likewise with ref instead of pointer.

This is OK (even if ideally 'g' should be forbidden from escaping 'a').

Similarly:

   A f(A a) {
      auto o = register(&a); // can modify 'a'
      o.blah();              // ditto
      doneWith(o);           // ditto
      return a;
   }


What I was wondering was things like whether that "assume no escape" property was transitive; if /locally/ escaping was disallowed, and to what extent. What does "assume no escape" mean at all? In your examples you're mentioning refs together with pointers, that would only make sense if no-escape were transitive -- but then treating all args as no-escape would be very wrong.


> Worse, scope doesn't error on the general case either.
> 
> class D {
>    int * p;
>    this(scope int x) {
>      p = &x; // escapes the address of the scope parameter.
>    }
> }

D's "scope" isn't enforced in any way right now, which means that code could exist that is invalid, but currently works. It would break silently(!) when compiled with a decent compiler, which still doesn't enforce scope.


> Do these examples give you a good example?

I'm worried about a) invalid assumptions making it into GDC;
b) certain valid assumptions making into GDC. The latter because
it could mean that code that's incorrect, but still accepted by
the other compilers could silently break when compiled with GDC.


artur
April 19, 2014
On 19 April 2014 14:33, Artur Skawina via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> On 04/19/14 14:37, Iain Buclaw via Digitalmars-d wrote:
>> On 19 April 2014 13:02, Artur Skawina via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>>> On 04/19/14 13:03, Iain Buclaw via Digitalmars-d wrote:
>>>> On Saturday, 19 April 2014 at 10:49:22 UTC, Iain Buclaw wrote:
>>>>> Hi,
>>>>>
>>>>> I'm currently testing out a GCC optimisation that allows you to set call argument flags.  The current assumptions being:
>>>>>
>>>>> in parameters  =>  Assume no escape, no clobber (read-only). ref parameters, classes and pointers  =>  Assume worst case. default  =>  Assume no escape.
>>>>>
>>>>
>>>> That should read:
>>>>
>>>> ref parameters, inout parameters, classes and pointers.
>>>>
>>>> The default of assuming no escape is an experiment - I may limit this to only scalar types, and parameters marked as 'scope'  (So long as no one plans on deprecating it soon :)
>>>
>>> What does "assume no escape" actually mean?
>>> [The above list doesn't really make sense. W/o context, it's
>>> hard to even tell why, hence the question.]
>>>
>>
>> Actually, I might change the default to assume worst case.  I've just tried this out, which is still valid.
>>
>> class C {
>>    int * p;
>>    this(int x) {
>>      p = &x; // escapes the address of the parameter.
>>    }
>> }
>
> This might be currently accepted, but it is clearly invalid
> (escapes local; the only way to make it work safely would
> be to silently copy 'x' to the GC-managed heap, which would be
> way too costly).
>
>
>    A f(A a) { g(&a); return a; } // likewise with ref instead of pointer.
>
> This is OK (even if ideally 'g' should be forbidden from escaping 'a').
>
> Similarly:
>
>    A f(A a) {
>       auto o = register(&a); // can modify 'a'
>       o.blah();              // ditto
>       doneWith(o);           // ditto
>       return a;
>    }
>
>
> What I was wondering was things like whether that "assume no escape" property was transitive; if /locally/ escaping was disallowed, and to what extent. What does "assume no escape" mean at all? In your examples you're mentioning refs together with pointers, that would only make sense if no-escape were transitive -- but then treating all args as no-escape would be very wrong.
>
>
>> Worse, scope doesn't error on the general case either.
>>
>> class D {
>>    int * p;
>>    this(scope int x) {
>>      p = &x; // escapes the address of the scope parameter.
>>    }
>> }
>
> D's "scope" isn't enforced in any way right now, which means that code could exist that is invalid, but currently works. It would break silently(!) when compiled with a decent compiler, which still doesn't enforce scope.
>

People should get bug fixing soon then.  =)


>> Do these examples give you a good example?
>
> I'm worried about a) invalid assumptions making it into GDC;
> b) certain valid assumptions making into GDC. The latter because
> it could mean that code that's incorrect, but still accepted by
> the other compilers could silently break when compiled with GDC.
>

Invalid assumptions rarely make it into GDC.  The testsuite is a good bench for this, as well as several projects (now I've got dub set-up) to test it in the wild.

Saying that, we have had to revert some optimisation cases as D's schizophrenic nature of enforcing attributes and behaviours is becoming increasingly dismal.

eg:
- nothrow has *no* guarantee, period, because it still allows
unrecoverable errors being thrown, and allows people to catch said
unrecoverable errors.
- pure is a tough nut to crack also.  The theory should allow you to
be able to cache return values, but in practise...
- The nature of debug statements breaks guarantees of both nothrow and
pure, possibly many more.
- Defining reliable strict aliasing rules, it turns out, is not that
simple (this is something that Walter has mentioned about D should
have good guarantees for, ie: D arrays).

I'm just in investigating all avenues, as I usually do.  There is no reason why 'in' shouldn't have more powerful guarantees IMO, and what I've found is not a problem with user code, it's a problem with the compiler implementation.


For reference, this is the declaration of _adSort in rt.qsort:

extern (C) void[] _adSort(void[], TypeInfo);

This is how the compiler frontend defines the function internally for its own use:

extern (C) T[] _adSort(in void[], in TypeInfo);
April 19, 2014
On Saturday, 19 April 2014 at 14:21:23 UTC, Iain Buclaw via Digitalmars-d wrote:
> eg:
> - nothrow has *no* guarantee, period, because it still allows
> unrecoverable errors being thrown, and allows people to catch said
> unrecoverable errors.

Hm, it is hard to find clear answer in spec but I _think_ that replacing error throwing with program halting in release mode for nothrow function should be valid behavior.
April 19, 2014
On 19 April 2014 15:36, Dicebot via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> On Saturday, 19 April 2014 at 14:21:23 UTC, Iain Buclaw via Digitalmars-d wrote:
>>
>> eg:
>> - nothrow has *no* guarantee, period, because it still allows
>> unrecoverable errors being thrown, and allows people to catch said
>> unrecoverable errors.
>
>
> Hm, it is hard to find clear answer in spec but I _think_ that replacing error throwing with program halting in release mode for nothrow function should be valid behavior.

Nope.  I can't recall the exact code, but an example is in the testsuite.  It was discovered when porting to ARM, which was found to omit unwind directives for D nothrow functions, causing runtime hangs when said 'nothrow' functions infact threw an Error.
April 19, 2014
On Saturday, 19 April 2014 at 15:00:26 UTC, Iain Buclaw via Digitalmars-d wrote:
> Nope.  I can't recall the exact code, but an example is in the
> testsuite.  It was discovered when porting to ARM, which was found to
> omit unwind directives for D nothrow functions, causing runtime hangs
> when said 'nothrow' functions infact threw an Error.

Now I am pretty sure _this_ is a druntime bug. I don't know about being able to catch actual exception but the fact that unwinding is not guaranteed for Errors is mentioned either on dlang.org or in TDPL.
« First   ‹ Prev
1 2