October 27, 2021

On Wednesday, 27 October 2021 at 19:19:31 UTC, Dukc wrote:

>

pure is also supposed to be an optimization aid.

It would be interesting to see an example of how pure can help optimization, compared to if you wouldn't badge the function.

October 27, 2021
On Wed, Oct 27, 2021 at 07:19:31PM +0000, Dukc via Digitalmars-d wrote: [...]
> ```d
> pure short coming()
> { typeof(return) result = void;
>   return result; //may return anything
> }
> ```
> 
> But when you think of it, why should this be a problem? This one returns an implementation defined value. If a compiler skips repeated calls to this one because of the `pure` attribute and just reuses the value from the first call, so what? Because the return values are implementation defined, the compiler is free to have them all to be the same with regards to each other.

Returning a void-initialized (i.e., *un*initialized) result invokes UB.
The compiler is free to do (or not do) whatever it wants in this case.


T

-- 
Only boring people get bored. -- JM
October 27, 2021

On Wednesday, 27 October 2021 at 19:26:57 UTC, IGotD- wrote:

>

On Wednesday, 27 October 2021 at 19:19:31 UTC, Dukc wrote:

>

pure is also supposed to be an optimization aid.

It would be interesting to see an example of how pure can help optimization, compared to if you wouldn't badge the function.

For example, with that coming function, the compiler may rewrite

foreach(unused; 0 .. 4) coming.writeln;

to

auto comingRes = coming;
foreach(unused; 0 .. 4) comingRes.writeln;

.

In fact, the compiler can even evaluate coming at compile time, and avoid any function call at all:

foreach(unused; 0 .. 4)
{  //or any other short value
   29677.writeln;
}
October 27, 2021
On Wednesday, 27 October 2021 at 19:31:52 UTC, H. S. Teoh wrote:
>
> Returning a void-initialized (i.e., *un*initialized) result invokes UB.
> The compiler is free to do (or not do) whatever it wants in this case.
>
>
> T

Only if the result contains pointers. With other types, the result is undefined but not the rest of the program.


October 27, 2021
On Wed, Oct 27, 2021 at 07:26:57PM +0000, IGotD- via Digitalmars-d wrote:
> On Wednesday, 27 October 2021 at 19:19:31 UTC, Dukc wrote:
> > 
> > `pure` is also supposed to be an optimization aid.
> 
> It would be interesting to see an example of how pure can help optimization, compared to if you wouldn't badge the function.

The following example supposedly shows the difference:

	int pureFunc() pure { return 1; }
	int impureFunc() { return 1; }
	void main() {
		writeln(pureFunc() + pureFunc()); // compiler may call pureFunc only once
		writeln(impureFunc() + impureFunc()); // compiler must call impureFunc twice
	}

Unfortunately, a quick test with dmd did not show the expected optimization.  Which confirms what I said before that pure's optimization benefit is really rather marginal.

The bigger benefit is the mechanical verification that you didn't accidentally depend on global state when you didn't want to, which can help with improving code quality.


T

-- 
Which is worse: ignorance or apathy? Who knows? Who cares? -- Erich Schubert
October 27, 2021
On Wed, Oct 27, 2021 at 07:40:10PM +0000, Dukc via Digitalmars-d wrote:
> On Wednesday, 27 October 2021 at 19:31:52 UTC, H. S. Teoh wrote:
> > 
> > Returning a void-initialized (i.e., *un*initialized) result invokes
> > UB.  The compiler is free to do (or not do) whatever it wants in
> > this case.
[...]
> Only if the result contains pointers. With other types, the result is undefined but not the rest of the program.

Right, but the result being undefined means the compiler is free to cache or not cache the return value without violating the spec.


T

-- 
"Holy war is an oxymoron." -- Lazarus Long
October 28, 2021

On Wednesday, 27 October 2021 at 19:53:52 UTC, H. S. Teoh wrote:

>

Unfortunately, a quick test with dmd did not show the expected optimization. Which confirms what I said before that pure's optimization benefit is really rather marginal.

The function needs to be nothrow and compiled with -O -release, because dmd needs to account for exceptions / errors, and even then the optimization shouldn't really be done. LDC doesn't do anything with pure for optimizations, but since it has cross module inlining, it doesn't need it.

>

The bigger benefit is the mechanical verification that you didn't accidentally depend on global state when you didn't want to, which can help with improving code quality.

That, but it can also help proving a lack of aliasing. For example:

int[] duplicate(scope int[] x) pure;

The returned array can safely be converted to immutable int[]. (N.b. this is not implemented yet)

void foo(int[] x, int[] y) pure nothrow;

Parameters x and y can freely be inferred scope, which is useful since dmd's scope inference from function bodies is not good.

October 28, 2021
On Wednesday, 27 October 2021 at 19:53:52 UTC, H. S. Teoh wrote:
> The bigger benefit is the mechanical verification that you didn't accidentally depend on global state when you didn't want to, which can help with improving code quality.

It could if "pure" was default and you had to type "uses_globals" if you went past that. Adding pure all over the place is just too tedious and visually annoying.



October 28, 2021
On Wednesday, 27 October 2021 at 19:40:10 UTC, Dukc wrote:
> On Wednesday, 27 October 2021 at 19:31:52 UTC, H. S. Teoh wrote:
>>
>> Returning a void-initialized (i.e., *un*initialized) result invokes UB.
>> The compiler is free to do (or not do) whatever it wants in this case.
>>
>>
>> T
>
> Only if the result contains pointers. With other types, the result is undefined but not the rest of the program.

According to the current language spec, the entire program has undefined behavior whether or not the result contains pointers. Walter has proposed to change this [1], but the proposal has not been accepted.

[1]: https://github.com/dlang/dlang.org/pull/2260
October 28, 2021

On Thursday, 28 October 2021 at 10:53:25 UTC, Dennis wrote:

>

On Wednesday, 27 October 2021 at 19:53:52 UTC, H. S. Teoh wrote:

>

Unfortunately, a quick test with dmd did not show the expected optimization. Which confirms what I said before that pure's optimization benefit is really rather marginal.

The function needs to be nothrow and compiled with -O -release, because dmd needs to account for exceptions / errors, and even then the optimization shouldn't really be done. LDC doesn't do anything with pure for optimizations, but since it has cross module inlining, it doesn't need it.

I think that, in addition to bugs, there is one hurdle in letting the compiler to fully optimise based on pure: how do we implement a function that manually frees an array? If the signature is

pure nothrow @nogc void free(int[])

, the compiler might elide consecutive frees to different arrays with the same content (so that any potential crash or infinite loop would happen in first free), if it can detect that the freed array does not alias to anything and any potential mutation of the array would have no effect.

> >

The bigger benefit is the mechanical verification that you didn't accidentally depend on global state when you didn't want to, which can help with improving code quality.

That, but it can also help proving a lack of aliasing. For example:

int[] duplicate(scope int[] x) pure;

The returned array can safely be converted to immutable int[]. (N.b. this is not implemented yet)

Great, I can malloc immutable data with this feature without casting once implemented.

>
void foo(int[] x, int[] y) pure nothrow;

Parameters x and y can freely be inferred scope, which is useful since dmd's scope inference from function bodies is not good.

If -preview=dip1021 (@live checking) isn't enabled, that is.