"a[++i] = i" vs "a[i] = ++i"

H. S. Teoh hsteoh at quickfur.ath.cx
Tue Dec 24 09:46:36 PST 2013


On Tue, Dec 24, 2013 at 05:15:15PM +0100, Timon Gehr wrote:
> On 12/24/2013 01:48 AM, H. S. Teoh wrote:
> >Agreed. Note that introducing assignment into the mix may not help
> >matters, but complicate them even more. For example:
> >
> >	int x=1, y=0;
> >	writeln((y = ++x) + (++y--) * (x=y)); // what does this print?
> >...
> 
> 'y-- is not an lvalue'.

D'oh, should've tested my code before posting it. Next time I'm gonna
write all code snippets as unittests... :-P  (OK, kidding.)


> Assuming we add parens as follows:
> 
>     int x=1, y=0;
>     writeln((y = ++x) + ((++y)--) * (x=y));
> 
> We should get 8 according to strict left to right evaluation rules.
> Furthermore, we should have the value 2 stored in both x and y.

What is "strict left to right evaluation", since * has to be evaluated
before +? Or you consider all side-effects to be evaluated first (from
left to right), and then the expression?


> >Or worse yet:
> >
> >	int func1(int x) { ... }
> >	int func2(int x) { ... }
> >	int func3(int x) { ... }
> >
> >	int function(int)[] funcptrs = [ &func1, &func2, &func3 ];
> >
> >	int x=0, y=0;
> >	y += funcptrs[++x--](y = --x++); // what does this do?
> >
> 
> 'x-- is not an lvalue'
> 'x++ is not an lvalue'
> 
> Assuming we run the following code instead:
> 
>     int func1(int x) { return 1*x; }
>     int func2(int x) { return 2*x; }
>     int func3(int x) { return 3*x; }
> 
>     int function(int)[] funcptrs = [ &func1, &func2, &func3 ];
> 
>     int x=0, y=0;
>     y += funcptrs[(++x)--](y = (--x)++)
> 
> We should be left in a state where x contains 0 and y contains -2
> according to strict left to right evaluation rules.
> 
> >The only place I can see where you'd even*want*  to write code like
> >this is in an entry to the IOCCC. Make it refuse to compile, I say.
> 
> I don't think it makes sense to add some arbitrary rules to the
> compiler implementation just to ban some code that nobody writes
> anyway and potentially some perfectly fine code as well.

A potential issue is that forcing a particular evaluation order may be
detrimental to the optimizer, e.g., according to strict left-to-right
evaluation (if I understand you correctly), then to evaluate this:

	z = (y = ++x) + ((++y)--) * (x=y)

you'd have to compile it into the equivalent of:

	y = ++x;	// i.e., ++x; y = x;
	tmp1 = y;
	tmp2 = ++y;	// i.e., ++y; tmp2 = y;
	y--;
	x=y;
	z = tmp1 + tmp2 * x; // i.e., tmp3 = tmp2*x; tmp3 += tmp1; z = tmp3;

Since the order can't be permuted without changing the semantics, the
codegen would have to load x and y multiple times as well as introduce
temporaries in order to evaluate the final expression, and the optimizer
wouldn't be able to do anything about it.

Of course, this is probably not *that* big of a deal if normal
expressions without side-effects can be optimized as usual -- it'd be
the user's own fault for writing unoptimizable code. Code with
side-effects are already difficult to optimize anyway, so it probably
doesn't make that big of a difference.


T

-- 
"No, John.  I want formats that are actually useful, rather than
over-featured megaliths that address all questions by piling on
ridiculous internal links in forms which are hideously over-complex." --
Simon St. Laurent on xml-dev


More information about the Digitalmars-d-learn mailing list