<div class="gmail_quote">On 12 March 2012 19:03, Iain Buclaw <span dir="ltr"><<a href="mailto:ibuclaw@ubuntu.com">ibuclaw@ubuntu.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div class="HOEnZb"><div class="h5">On 12 March 2012 00:44, Manu <<a href="mailto:turkeyman@gmail.com">turkeyman@gmail.com</a>> wrote:<br>
> On 12 March 2012 00:58, Robert Jacques <<a href="mailto:sandford@jhu.edu">sandford@jhu.edu</a>> wrote:<br>
>><br>
>> That's an argument for using the right register for the job. And we can /<br>
>> will be doing this on x86-64, as other compilers have already done. Manu was<br>
>> arguing that MRV were somehow special and had mystical optimization<br>
>> potential. That's simply not true.<br>
><br>
><br>
> Here's some tests for you:<br>
><br>
> // first test that the argument registers allocate as expected...<br>
> int gprtest(int x, int y, int z)<br>
> {<br>
> return x+y+z;<br>
> }<br>
><br>
>    Perfect, ints pass in register sequence, return in r0, no memory access<br>
> add r0, r0, r1<br>
> add r0, r0, r2<br>
> bx lr<br>
><br>
> float fptest(float x, float y, float z)<br>
> {<br>
> return x+y+z;<br>
> }<br>
><br>
>    Same for floats<br>
> fadds s0, s0, s1<br>
> fadds s0, s0, s2<br>
> bx lr<br>
><br>
><br>
> // Some MRV tests...<br>
> auto mrv1(int x, int z)<br>
> {<br>
> return Tuple!(int, int)(x, z);<br>
> }<br>
><br>
>   Simple case, 2 ints<br>
>   FAIL, stores the 2 arguments it received in regs straight to output struct<br>
> pointer supplied<br>
> stmia r0, {r1, r2}<br>
> bx lr<br>
><br>
><br>
> auto mrv2(int x, float y, byte z)<br>
> {<br>
> return Tuple!(int, float, byte)(x, y, z);<br>
> }<br>
><br>
>   Different typed things<br>
>   EPIC FAIL<br>
> stmfd sp!, {r4, r5}<br>
> mov ip, #0<br>
> sub sp, sp, #24<br>
> mov r4, r2<br>
> str ip, [sp, #12]<br>
> str ip, [sp, #20]<br>
> ldr r2, .L27<br>
> add ip, sp, #24<br>
> mov r3, r0<br>
> mov r5, r1<br>
> str r2, [sp, #16] @ float<br>
> ldmdb ip, {r0, r1, r2}<br>
> stmia r3, {r0, r1, r2}<br>
> fsts s0, [r3, #4]<br>
> stmia sp, {r0, r1, r2}<br>
> str r5, [r3, #0]<br>
> strb r4, [r3, #8]<br>
> mov r0, r3<br>
> add sp, sp, #24<br>
> ldmfd sp!, {r4, r5}<br>
> bx lr<br>
><br>
><br>
> auto range(int *p)<br>
> {<br>
> return p[0..1];<br>
> }<br>
><br>
>   Range<br>
>   SURPRISE FAIL, even a range is returned as a struct! O_O<br>
> mov r2, #1<br>
> str r2, [r0, #0]<br>
> str r1, [r0, #4]<br>
> bx lr<br>
><br>
><br>
> So the D ABI is a complete shambles on ARM!<br>
> Unsurprisingly, it all just follows the return struct by-val ABI, which is<br>
> to write it to the stack unconditionally. And sadly, it even thinks the<br>
> internal types like range+delegate are just a struct by-val, and completely<br>
> ruins those!<br>
><br>
> Let's try again with x86...<br>
><br>
><br>
> auto mrv1(int x, int z)<br>
> {<br>
> return Tuple!(int, int)(x, z);<br>
> }<br>
><br>
> Returns in eax/edx as expected<br>
>  movl 4(%esp), %eax<br>
>  movl 8(%esp), %edx<br>
><br>
><br>
> auto mrv2(int x, float y, int z)<br>
> {<br>
> return Tuple!(int, float, int)(x, y, z);<br>
> }<br>
><br>
> FAIL! All written to a struct rather than returning in eax,edx,st0 .. This<br>
> is C ABI baggage, D can do better.<br>
>  movl 4(%esp), %eax<br>
>  movl 8(%esp), %edx<br>
>  movl %edx, (%eax)<br>
>  movl 12(%esp), %edx<br>
>  movl %edx, 4(%eax)<br>
>  movl 16(%esp), %edx<br>
>  movl %edx, 8(%eax)<br>
>  ret $4<br>
><br>
><br>
> auto range(int *p)<br>
> {<br>
> return p[0..1];<br>
> }<br>
><br>
> Obviously, the small struct optimisation allows this to work properly<br>
>  movl $1, %eax<br>
>  movl 4(%esp), %edx<br>
>  ret<br>
><br>
><br>
> All that said, x86 isn't a good test case, since all args are ALWAYS passed<br>
> on the stack. x64 would be a much better test since it actually has arg<br>
> registers, but I'm on windows, so no x64 for me...<br>
<br>
<br>
</div></div>What compiler flags are you using here?  For x86, I would have thought<br>
that small structs (< 8 bytes) would be passed back in registers...<br>
only speculating though - will need to see what codegen is being built<br>
from the D code provided to be sure.<br></blockquote><div><br></div><div>-S -O2 -msse2</div><div>And as expected, 8byte structs were returned packed in registers from my examples above. That's a traditional x86 ABI hack which conveniently allows delegates+ranges to work well on x86, but as you can see, they're proper broken on other architectures.</div>
</div>