Re: Multiple return values...

2012-03-16 Thread Iain Buclaw
On 16 March 2012 02:26, Andrei Alexandrescu
seewebsiteforem...@erdani.org wrote:
 On 3/15/12 5:44 PM, foobar wrote:

 On Thursday, 15 March 2012 at 18:23:57 UTC, Andrei Alexandrescu wrote:


 I understand how the draw of reaching for new syntax is extremely
 alluring. I used to fall for it much more often, but over years I have
 hardened myself to resist it, and I think that made me a better
 language designer. In this stage of D, I think we should all have an
 understanding that adding syntax is not a win. Instead, it is an
 acknowledgment that the existing language, for all its might, is
 unable to express the desired abstraction. This is sometimes fine
 (such as in the case of introducing multiple new symbols from one
 tuple), but generally the question is not why couldn't we add syntax
 X? but instead why should we add syntax X?


 Andrei


 I agree that this is an acknowledgement of the current language's
 inability to express the abstraction. That's why many people ask for it
 to be added in the first place. We should add this syntax because it
 *is* impossible ATM to implement the required abstraction.


 I think this is a reasonable request:

 (auto a, b) = fun();

 ---

 static assert(fun().length == 2);
 auto __t = fun();
 auto a = __t[0];
 auto b = __t[1];

 To express the idiom in the example, the programmer needs to do the
 expansion by hand, which is verbose and introduces unnecessary symbols. So
 the language as is has an expressiveness problem (albeit not a pernicious
 one) that is nice to solve.


Andrei++

This could also be done for arrays too.

(int a, b) = arr[];

-

static assert(arr.length == 2);
int a = arr[0];
int b = arr[1];



Or possibly a use in variadic templates, which could make D act more
like how some scripting languages work.

void conn(T ...)(T args) {
  // vars get default init if value wasn't passed to function.
  (string server, port, username, password) = args[];
}




-- 
Iain Buclaw

*(p  e ? p++ : p) = (c  0x0f) + '0';


Re: Multiple return values...

2012-03-16 Thread Iain Buclaw
On 15 March 2012 17:51, Manu turkey...@gmail.com wrote:
 On 15 March 2012 19:05, Andrei Alexandrescu seewebsiteforem...@erdani.org
 wrote:

 On 3/15/12 11:30 AM, Manu wrote:

 On 15 March 2012 17:32, Andrei Alexandrescu
 seewebsiteforem...@erdani.org mailto:seewebsiteforem...@erdani.org

 wrote:
    One note - the code is really ingenious, but I still prefer swap()
    in this case. It's more concise and does less work in the general
 case.

    swap(a[i + k], a[j + j]);

    only computes the indexing once (in source, too).


 It all still feels to me like a generally ugly hack around the original
 example:
   a,b = b,a;


 It's a function call. Why is a function call a ugly hack?


 Because now we're involving libraries to perform a trivial assignment.


Question, what benefits would (a, b = b, a) bring over swap(a, b)  if
it was included?


-- 
Iain Buclaw

*(p  e ? p++ : p) = (c  0x0f) + '0';


Re: Multiple return values...

2012-03-16 Thread Artur Skawina
On 03/16/12 01:03, H. S. Teoh wrote:
 On Fri, Mar 16, 2012 at 12:11:44AM +0100, Simen Kjærås wrote:
 On Thu, 15 Mar 2012 23:44:09 +0100, foobar f...@bar.com wrote:

 Is swap usually inlined by the compiler?

 This is the body of swap for simple types:

   auto tmp = lhs;
   lhs = rhs;
   rhs = tmp;

 For more complex types:

   ubyte[T.sizeof] t = void;
   auto a = (cast(ubyte*) lhs)[0 .. T.sizeof];
   auto b = (cast(ubyte*) rhs)[0 .. T.sizeof];
   t[] = a[];
   a[] = b[];
   b[] = t[];

 So yeah. If that's not inlined, something's wrong.
 
 Ideally, though, a swap of large objects should be a single loop of xchg
 instructions (in x86 anyway, xchg may not exist on other architectures).
 Unless dmd is much more clever than I thought, the above code would
 generate 3 loops, which uses more memory and is (probably) slower.

x86 xchg w/ mem ref implies lock, plus you'd have to load and store one
of the operands anyway.

artur


Re: Multiple return values...

2012-03-16 Thread Manu
On 16 March 2012 04:26, Andrei Alexandrescu
seewebsiteforem...@erdani.orgwrote:

  A good design should strive to provide general features instead of
 special cases (E.g. swap is limited to the 2-tuple case). Also, why
 force an overhead of a function call on such a basic feature as
 assignment? Is swap usually inlined by the compiler?


 Sorry, this is grasping at straws.

 1. swap can be easily generalized to take any number of arguments. I'm
 very happy that's possible, we've expended great efforts on making variadic
 functions as powerful as they are. But nobody asked for swap with many
 arguments until now. Which segues into...

 2. When was the last time you needed to swap arbitrary numbers of
 elements, and so badly and frequently, you needed a new language feature
 for that?


This is called a swizzle. And constantly comes up when dealing with
x,y,z,w, or r,g,b,a.
It could just as easily be expressed this way:
  a,b = tuple(b,a); // swap
  r,g,b,a = tuple(a,r,g,b); // swizzle
At which point this multi assignment boils down to the exact same question
of syntax as MRV return assignment. I don't really distinguish this
swap/swizzle from MRV. It all comes back to the return assignment syntax.


 3. Function overhead is solved by inlining, not by adding new features.
 That improves all functions, not only swap.


Requiring inlining to make it efficient is not enough. The proposed MRV ABI
would solve this not only for inlines, but for all multi-assignment type
constructs, including distant function calls.


Re: Multiple return values...

2012-03-16 Thread Andrei Alexandrescu

On 3/16/12 6:29 AM, Manu wrote:

On 16 March 2012 04:26, Andrei Alexandrescu
seewebsiteforem...@erdani.org mailto:seewebsiteforem...@erdani.org
wrote:

A good design should strive to provide general features instead of
special cases (E.g. swap is limited to the 2-tuple case). Also, why
force an overhead of a function call on such a basic feature as
assignment? Is swap usually inlined by the compiler?


Sorry, this is grasping at straws.

1. swap can be easily generalized to take any number of arguments.
I'm very happy that's possible, we've expended great efforts on
making variadic functions as powerful as they are. But nobody asked
for swap with many arguments until now. Which segues into...

2. When was the last time you needed to swap arbitrary numbers of
elements, and so badly and frequently, you needed a new language
feature for that?


This is called a swizzle. And constantly comes up when dealing with
x,y,z,w, or r,g,b,a.
It could just as easily be expressed this way:
   a,b = tuple(b,a); // swap
   r,g,b,a = tuple(a,r,g,b); // swizzle
At which point this multi assignment boils down to the exact same
question of syntax as MRV return assignment. I don't really distinguish
this swap/swizzle from MRV. It all comes back to the return assignment
syntax.


Actually, as has been mentioned, swizzling can be done very nicely 
inside the language.



3. Function overhead is solved by inlining, not by adding new
features. That improves all functions, not only swap.

Requiring inlining to make it efficient is not enough. The proposed MRV
ABI would solve this not only for inlines, but for all multi-assignment
type constructs, including distant function calls.


If changing the ABI is on the table, I wonder whether we can improve it 
for all structures, not only for multiple return types. Overall I agree 
that defining a specialized ABI for leaving multiple values on the stack 
would be marginally more efficient, but (a) I don't know by how much, 
and (b) I don't know whether it's worth changing the language.



Andrei


Re: Multiple return values...

2012-03-16 Thread Iain Buclaw
On 16 March 2012 16:37, Andrei Alexandrescu
seewebsiteforem...@erdani.org wrote:
 On 3/16/12 6:29 AM, Manu wrote:

 On 16 March 2012 04:26, Andrei Alexandrescu
 seewebsiteforem...@erdani.org mailto:seewebsiteforem...@erdani.org

 wrote:

        A good design should strive to provide general features instead of
        special cases (E.g. swap is limited to the 2-tuple case). Also, why
        force an overhead of a function call on such a basic feature as
        assignment? Is swap usually inlined by the compiler?


    Sorry, this is grasping at straws.

    1. swap can be easily generalized to take any number of arguments.
    I'm very happy that's possible, we've expended great efforts on
    making variadic functions as powerful as they are. But nobody asked
    for swap with many arguments until now. Which segues into...

    2. When was the last time you needed to swap arbitrary numbers of
    elements, and so badly and frequently, you needed a new language
    feature for that?


 This is called a swizzle. And constantly comes up when dealing with
 x,y,z,w, or r,g,b,a.
 It could just as easily be expressed this way:
   a,b = tuple(b,a); // swap
   r,g,b,a = tuple(a,r,g,b); // swizzle
 At which point this multi assignment boils down to the exact same
 question of syntax as MRV return assignment. I don't really distinguish
 this swap/swizzle from MRV. It all comes back to the return assignment
 syntax.


 Actually, as has been mentioned, swizzling can be done very nicely inside
 the language.

    3. Function overhead is solved by inlining, not by adding new
    features. That improves all functions, not only swap.

 Requiring inlining to make it efficient is not enough. The proposed MRV
 ABI would solve this not only for inlines, but for all multi-assignment
 type constructs, including distant function calls.


 If changing the ABI is on the table, I wonder whether we can improve it for
 all structures, not only for multiple return types. Overall I agree that
 defining a specialized ABI for leaving multiple values on the stack would be
 marginally more efficient, but (a) I don't know by how much, and (b) I don't
 know whether it's worth changing the language.


 Andrei


If you were to forget all about MRV for a brief moment, the change
request being proposed here is to return *all* structures (including
delegates, complex types and vectors) in registers if at all possible
even if the underlying ABI default is to return it in memory.


-- 
Iain Buclaw

*(p  e ? p++ : p) = (c  0x0f) + '0';


Re: Multiple return values...

2012-03-16 Thread Andrei Alexandrescu

On 3/16/12 3:48 AM, Iain Buclaw wrote:

This could also be done for arrays too.

(int a, b) = arr[];

-

static assert(arr.length == 2);
int a = arr[0];
int b = arr[1];


Yah, the rewrite is purely syntactic, not tuple-specific. That's a major 
thing I like about it.



Or possibly a use in variadic templates, which could make D act more
like how some scripting languages work.

void conn(T ...)(T args) {
   // vars get default init if value wasn't passed to function.
   (string server, port, username, password) = args[];
}


I fear this is a bit error-prone.


Andrei


Re: Multiple return values...

2012-03-16 Thread Andrei Alexandrescu

On 3/16/12 11:50 AM, Iain Buclaw wrote:

If you were to forget all about MRV for a brief moment, the change
request being proposed here is to return *all* structures (including
delegates, complex types and vectors) in registers if at all possible
even if the underlying ABI default is to return it in memory.


That sounds great. Must have missed this point, this is a weird 
discussion in which one sentence is about machine code and the next 
about syntax.


Andrei




Re: Multiple return values...

2012-03-16 Thread Iain Buclaw
On 16 March 2012 16:53, Andrei Alexandrescu
seewebsiteforem...@erdani.org wrote:
 On 3/16/12 11:50 AM, Iain Buclaw wrote:

 If you were to forget all about MRV for a brief moment, the change
 request being proposed here is to return *all* structures (including
 delegates, complex types and vectors) in registers if at all possible
 even if the underlying ABI default is to return it in memory.


 That sounds great. Must have missed this point, this is a weird discussion
 in which one sentence is about machine code and the next about syntax.

 Andrei



Indeed it is.  Though in all honesty, I'm not sure how we can address
this. X86 and X86_64 architectures already return small structures
(less or equal to than 8 bytes iirc) in registers as an optimisation
trick if the function is private/static, and optimisations are of
course turned on.

Implementing this for other architectures would require explicitly
patching each backend architecture, which is simply not feasible to
do, especially when such patches may likely get rejected (A
'reg_return' attribute for ARM has been submitted before back in 2007,
but has never been accepted despite after several revisions).


-- 
Iain Buclaw

*(p  e ? p++ : p) = (c  0x0f) + '0';


Re: Multiple return values...

2012-03-16 Thread Manu
On 16 March 2012 18:37, Andrei Alexandrescu
seewebsiteforem...@erdani.orgwrote:

 Actually, as has been mentioned, swizzling can be done very nicely inside
 the language.


How? The only example I saw in this thread was your from()/to() approach,
which Timon said didn't actually work...?


On 3/16/12 6:29 AM, Manu wrote:

 Requiring inlining to make it efficient is not enough. The proposed MRV

 ABI would solve this not only for inlines, but for all multi-assignment
 type constructs, including distant function calls.


 If changing the ABI is on the table, I wonder whether we can improve it
 for all structures, not only for multiple return types. Overall I agree
 that defining a specialized ABI for leaving multiple values on the stack
 would be marginally more efficient, but (a) I don't know by how much, and
 (b) I don't know whether it's worth changing the language.


It might be that this technology is simpler to implement as applied to all
structs (which would include tuples). That way it may additionally address
the current problem with slices and delegates. If it applies too all
structs, there probably needs to be a cut-off heuristic though, since
explicit structs would tend to be a lot bigger than a few MRV values, and
named return values is probably better in that context. Implementation
details would reveal themselves when trying to write the code I think, ie,
where to draw the line (structure size/recursion, etc).

(a) 'Marginally' spread across the entire program eventually adds up to a
lot, especially if it finds its self in hot loops. C/C++ compilers have
extremely aggressive low level optimisers. There is obviously someone who
agrees that that effort and reduction in compiler performance is
worthwhile, or they wouldn't do it...
In terms of benchmarks (the quantity of value you are looking for), it
probably won't make so much difference to existing code, because people
avoid returning structs by value like the plague It's just something you
simply don't do. But if it were a real feature that were reliable, I can
see it quickly replace returning through references passed in the parameter
list (a horrible concept conceptually, effectively a hack), and it would
start to show its value as more and more code were written that way. We
free up an argument register in the call where we would have previously
passed the ref/pointer, and the return doesn't produce a memory write+read
(an LHS hazard). This is a good few opcodes, memory access, and elimination
of a major architectural hazard. Repeat this throughout your code base...
the only reason it's not in C, is because the ABI far too engraved and can
never be changed.

(b) Is it a change to the language? Just a change to the ABI with respect
to returning a tuple.
The change to the language is the unpack syntax, which should be argued on
a completely separate basis.


Re: Multiple return values...

2012-03-16 Thread Manu
On 16 March 2012 18:53, Andrei Alexandrescu
seewebsiteforem...@erdani.orgwrote:

 On 3/16/12 11:50 AM, Iain Buclaw wrote:

 If you were to forget all about MRV for a brief moment, the change
 request being proposed here is to return *all* structures (including
 delegates, complex types and vectors) in registers if at all possible
 even if the underlying ABI default is to return it in memory.


 That sounds great. Must have missed this point, this is a weird discussion
 in which one sentence is about machine code and the next about syntax.


Haha, it's true, there are 2 completely distinct, but intrinsically
connected problems being debated here :)


Re: Multiple return values...

2012-03-16 Thread Manu
On 16 March 2012 19:13, Iain Buclaw ibuc...@ubuntu.com wrote:

 On 16 March 2012 16:53, Andrei Alexandrescu
 seewebsiteforem...@erdani.org wrote:
  On 3/16/12 11:50 AM, Iain Buclaw wrote:
 
  If you were to forget all about MRV for a brief moment, the change
  request being proposed here is to return *all* structures (including
  delegates, complex types and vectors) in registers if at all possible
  even if the underlying ABI default is to return it in memory.
 
 
  That sounds great. Must have missed this point, this is a weird
 discussion
  in which one sentence is about machine code and the next about syntax.
 
  Andrei
 
 

 Indeed it is.  Though in all honesty, I'm not sure how we can address
 this. X86 and X86_64 architectures already return small structures
 (less or equal to than 8 bytes iirc) in registers as an optimisation
 trick if the function is private/static, and optimisations are of
 course turned on.


It's good for a pair of ints, but still inefficient for a struct with
smaller data types, each of which could occupy their own register, avoiding
the pack and unpack before and after the return...

Implementing this for other architectures would require explicitly
 patching each backend architecture, which is simply not feasible to
 do, especially when such patches may likely get rejected (A
 'reg_return' attribute for ARM has been submitted before back in 2007,
 but has never been accepted despite after several revisions).


Brutal. LLVM supports this efficiently out of the box.
It'd be interesting to have the GCC people weigh in on this argument
actually. I wonder if they'd see the merit here... ie, presenting it as a
fundamental language feature, it has a potential of offering a lot more
value than it would applied to C.


Re: Multiple return values...

2012-03-16 Thread Timon Gehr

On 03/16/2012 06:33 PM, Manu wrote:

On 16 March 2012 18:37, Andrei Alexandrescu
seewebsiteforem...@erdani.org mailto:seewebsiteforem...@erdani.org
wrote:

Actually, as has been mentioned, swizzling can be done very nicely
inside the language.


How?


Use opDispatch.

a = a.yxwz;


The only example I saw in this thread was your from()/to()
approach, which Timon said didn't actually work...?



It does work for assigning to simple variables, but not for assigning to 
more general expressions.



...
In terms of benchmarks (the quantity of value you are looking for), it
probably won't make so much difference to existing code, because people
avoid returning structs by value like the plague It's just something you
simply don't do. But if it were a real feature that were reliable, I can
see it quickly replace returning through references passed in the
parameter list (a horrible concept conceptually, effectively a hack),


DMD does that for you (Walter is the inventor of NRVO).




Re: Multiple return values...

2012-03-16 Thread Manu
On 16 March 2012 19:53, Timon Gehr timon.g...@gmx.ch wrote:

 On 03/16/2012 06:33 PM, Manu wrote:

 On 16 March 2012 18:37, Andrei Alexandrescu
 seewebsiteforem...@erdani.org 
 mailto:SeeWebsiteForEmail@**erdani.orgseewebsiteforem...@erdani.org
 

 wrote:

Actually, as has been mentioned, swizzling can be done very nicely
inside the language.


 How?


 Use opDispatch.

 a = a.yxwz;


The simplest possible example (I've done this is std.simd)... but if I have
a few different loosely related things?
My personal most frequent problem case is collision/physics. pos, 't',
velocity, intersectionFlag, intersection target pointer, etc.. lots of
loosely related stuff, always results in inefficient function calls in some
of the hottest code in the engine.


DMD does that for you (Walter is the inventor of NRVO).


Which is awesome for returning larger structs, but not good for returning
just a couple of unrelated things that will persist as locals in the
calling function.


Re: Multiple return values...

2012-03-16 Thread Simen Kjærås
On Fri, 16 Mar 2012 03:26:55 +0100, Andrei Alexandrescu  
seewebsiteforem...@erdani.org wrote:



I think this is a reasonable request:

(auto a, b) = fun();

---

static assert(fun().length == 2);
auto __t = fun();
auto a = __t[0];
auto b = __t[1];


That would be nice. As was mentioned in a later post, this works for  
static arrays, too.
Now, a pattern that our productive friend bearophile posted earlier was  
this:


int[] foo();

(auto a, b) = foo();

---

auto __t = foo();
assert( __t.length  0 );
auto a = __t[0];
auto b = __t[1..$];

Is this something we might also want?


Re: Multiple return values...

2012-03-16 Thread bearophile
Simen K.:

 Now, a pattern that our productive friend bearophile posted earlier was  
 this:
 
 int[] foo();
 
 (auto a, b) = foo();
 
 ---
 
 auto __t = foo();
 assert( __t.length  0 );
 auto a = __t[0];
 auto b = __t[1..$];
 
 Is this something we might also want?

That's not a good idea, because you don't know much about what 'b' contains.

Time ago I have written an example vaguely like that, but it was in the Python3 
version. So a more similar translation is:

auto (a, $b) = foo();

The $ means: 'unpack all the remaining items inside the b tuple'. A similar 
syntax is cute to have, but it's not that important because it's not a very 
common need. And if people think it's so important, then it's possible to add 
it later (it's a purely incremental feature over the basic tuple unpacking 
syntax).

What's more commonly userful instead is tuple unpacking in foreach (this isn't 
the final syntax, it's just an idea) (Hara is willing to support this first 
one):

void spam((int x, int y)) {}
auto bar = [tuple(1,2,), tuple(3,4)];
foreach (tuple(x, y); bar) {}

And in function signatures (this is not the same as using tupleof at the call 
point):

void spam(tuple(int x, int y)) {}
spam(tuple(1,2))

Bye,
bearophile


Re: Multiple return values...

2012-03-15 Thread Iain Buclaw
On 14 March 2012 22:06, Derek Parnell ddparn...@bigpond.com wrote:
 On Thu, 15 Mar 2012 08:52:26 +1100, Andrei Alexandrescu
 seewebsiteforem...@erdani.org wrote:

 On 3/14/12 3:00 PM, Simen Kjærås wrote:

 template to(T...) {
 alias T to;
 }

 auto from(T...)(T t) {
 struct Result { T t; alias t this; }
 return Result( t );
 }

 void main( ) {
 int a = 3;
 int b = 4;

 to!(a, b) = from(b, a);

 assert( a == 4 );
 assert( b == 3 );
 }


 I got reborn inside a little when seeing this code. Congratulations!


 And I died a little ... I have no idea what this code is doing. What is the
 generated code produced by this and why?

 I'd like to break the nexus between science and magic here.

 --
 Derek

It gets expanded more or less to this:

// auto from(T...)(T t)
Result from (int a, int b) {
  return {.field0 = a, .field1 = b};
}

void main() {
  int a = 3;
  int b = 4;

// to!(b, a) = from(a, b)
  ref Result __tup = from(a, b),
  a = __tup-field0,
  b = __tup-field1;
}


Regards
-- 
Iain Buclaw

*(p  e ? p++ : p) = (c  0x0f) + '0';


Re: Multiple return values...

2012-03-15 Thread Timon Gehr

On 03/14/2012 11:06 PM, Derek Parnell wrote:

On Thu, 15 Mar 2012 08:52:26 +1100, Andrei Alexandrescu
seewebsiteforem...@erdani.org wrote:


On 3/14/12 3:00 PM, Simen Kjærås wrote:

template to(T...) {
alias T to;
}

auto from(T...)(T t) {
struct Result { T t; alias t this; }
return Result( t );
}

void main( ) {
int a = 3;
int b = 4;

to!(a, b) = from(b, a);

assert( a == 4 );
assert( b == 3 );
}


I got reborn inside a little when seeing this code. Congratulations!


And I died a little ... I have no idea what this code is doing. What is
the generated code produced by this and why?

I'd like to break the nexus between science and magic here.



// this is used to access language built-in tuples
template to(T...) {
alias T to;
}

// this builds a struct akin to std.typecons.tuple
// it contains the function parameters as fields t[0], t[1],...
auto from(T...)(T t) {
struct Result { T t; alias t this; }
return Result( t );
}

// to!(a,b) creates a tuple containing aliases to a and b

to!(a,b) = from(b,a);   // cannot assign Result to two fields
to!(a,b) = from(b,a).t; // try alias this
auto __tmp = from(b,a).t; // expand tuple assign to multiple assignment
a[0] = __tmp[0], a[1] = __tmp[1];


Re: Multiple return values...

2012-03-15 Thread Derek

On Thu, 15 Mar 2012 19:23:57 +1100, Timon Gehr timon.g...@gmx.ch wrote:

I'd like to break the nexus between science and magic here.



// this is used to access language built-in tuples
template to(T...) {
 alias T to;
}

// this builds a struct akin to std.typecons.tuple
// it contains the function parameters as fields t[0], t[1],...
auto from(T...)(T t) {
 struct Result { T t; alias t this; }
 return Result( t );
}

// to!(a,b) creates a tuple containing aliases to a and b

to!(a,b) = from(b,a);   // cannot assign Result to two fields
to!(a,b) = from(b,a).t; // try alias this
auto __tmp = from(b,a).t; // expand tuple assign to multiple assignment
a[0] = __tmp[0], a[1] = __tmp[1];


Thanks, but I was hoping more for an explanation in English.

Are you saying that the generated code is something like ...

   struct __tmpS { int Fa, int Fb };
   __tempS __tmp;
   __tmp.Fa = a;
   __tmp.Fb = b;
   a = __tmp.Fb;
   b = __tmp.Fa;


--
Derek Parnell
Melbourne, Australia


Re: Multiple return values...

2012-03-15 Thread Iain Buclaw
On 15 March 2012 09:52, Derek ddparn...@bigpond.com wrote:
 On Thu, 15 Mar 2012 19:23:57 +1100, Timon Gehr timon.g...@gmx.ch wrote:

 I'd like to break the nexus between science and magic here.


 // this is used to access language built-in tuples
 template to(T...) {
     alias T to;
 }

 // this builds a struct akin to std.typecons.tuple
 // it contains the function parameters as fields t[0], t[1],...
 auto from(T...)(T t) {
     struct Result { T t; alias t this; }
     return Result( t );
 }

 // to!(a,b) creates a tuple containing aliases to a and b

 to!(a,b) = from(b,a);   // cannot assign Result to two fields
 to!(a,b) = from(b,a).t; // try alias this
 auto __tmp = from(b,a).t; // expand tuple assign to multiple assignment
 a[0] = __tmp[0], a[1] = __tmp[1];


 Thanks, but I was hoping more for an explanation in English.

 Are you saying that the generated code is something like ...

   struct __tmpS { int Fa, int Fb };
   __tempS __tmp;
   __tmp.Fa = a;
   __tmp.Fb = b;
   a = __tmp.Fb;
   b = __tmp.Fa;


In effect, yes.  Given that the call to from() is inlined. :-)

In GDC, you have a -fdump-tree-original switch that dumps a debug
representation (that just so happens to look C-like) of the AST of the
code to a file.  You could use this to unravel some of the magic going
on under the covers. ;-)

-- 
Iain Buclaw

*(p  e ? p++ : p) = (c  0x0f) + '0';


Re: Multiple return values...

2012-03-15 Thread Andrei Alexandrescu

On 3/15/12 5:14 AM, Iain Buclaw wrote:

On 15 March 2012 09:52, Derekddparn...@bigpond.com  wrote:

Are you saying that the generated code is something like ...

   struct __tmpS { int Fa, int Fb };
   __tempS __tmp;
   __tmp.Fa = a;
   __tmp.Fb = b;
   a = __tmp.Fb;
   b = __tmp.Fa;



In effect, yes.  Given that the call to from() is inlined. :-)

In GDC, you have a -fdump-tree-original switch that dumps a debug
representation (that just so happens to look C-like) of the AST of the
code to a file.  You could use this to unravel some of the magic going
on under the covers. ;-)


One note - the code is really ingenious, but I still prefer swap() in 
this case. It's more concise and does less work in the general case.


swap(a[i + k], a[j + j]);

only computes the indexing once (in source, too).


Andrei



Re: Multiple return values...

2012-03-15 Thread Manu
On 15 March 2012 17:32, Andrei Alexandrescu
seewebsiteforem...@erdani.orgwrote:

 On 3/15/12 5:14 AM, Iain Buclaw wrote:

 On 15 March 2012 09:52, Derekddparn...@bigpond.com  wrote:

 Are you saying that the generated code is something like ...


   struct __tmpS { int Fa, int Fb };
   __tempS __tmp;
   __tmp.Fa = a;
   __tmp.Fb = b;
   a = __tmp.Fb;
   b = __tmp.Fa;


 In effect, yes.  Given that the call to from() is inlined. :-)

 In GDC, you have a -fdump-tree-original switch that dumps a debug
 representation (that just so happens to look C-like) of the AST of the
 code to a file.  You could use this to unravel some of the magic going
 on under the covers. ;-)


 One note - the code is really ingenious, but I still prefer swap() in this
 case. It's more concise and does less work in the general case.

 swap(a[i + k], a[j + j]);

 only computes the indexing once (in source, too).


It all still feels to me like a generally ugly hack around the original
example:
  a,b = b,a;

What is the significant importance of the existing coma operator? What does
it offer that couldn't possibly be achieved any other way?


Re: Multiple return values...

2012-03-15 Thread Andrei Alexandrescu

On 3/15/12 11:30 AM, Manu wrote:

On 15 March 2012 17:32, Andrei Alexandrescu
seewebsiteforem...@erdani.org mailto:seewebsiteforem...@erdani.org
wrote:
One note - the code is really ingenious, but I still prefer swap()
in this case. It's more concise and does less work in the general case.

swap(a[i + k], a[j + j]);

only computes the indexing once (in source, too).


It all still feels to me like a generally ugly hack around the original
example:
   a,b = b,a;


It's a function call. Why is a function call a ugly hack?

It's also more verbose. Compare

swap(a[i + k], a[i + j]);

with

a[i + k], a[i + j] = a[i + j], a[i + k];

I mean one could even easily miss a typo in there.

There's also semantic issues to be defined. What is the order of 
evaluation in


f(), g() = h(), k();

?

All of this adds dead weight to the language. We shouldn't reach to new 
syntax for every single little thing.



Andrei


Re: Multiple return values...

2012-03-15 Thread Timon Gehr

On 03/15/2012 04:32 PM, Andrei Alexandrescu wrote:




One note - the code is really ingenious, but I still prefer swap() in
this case. It's more concise and does less work in the general case.

swap(a[i + k], a[j + j]);

only computes the indexing once (in source, too).


In the general case, the alias tuple approach does less work, because it 
does not work. to!(a[i+k], a[j+j]) = from(a[j+j],a[i+k]); is illegal.


Re: Multiple return values...

2012-03-15 Thread Manu
On 15 March 2012 19:05, Andrei Alexandrescu
seewebsiteforem...@erdani.orgwrote:

 On 3/15/12 11:30 AM, Manu wrote:

 On 15 March 2012 17:32, Andrei Alexandrescu
 seewebsiteforem...@erdani.org 
 mailto:SeeWebsiteForEmail@**erdani.orgseewebsiteforem...@erdani.org
 

 wrote:
One note - the code is really ingenious, but I still prefer swap()
in this case. It's more concise and does less work in the general case.

swap(a[i + k], a[j + j]);

only computes the indexing once (in source, too).


 It all still feels to me like a generally ugly hack around the original
 example:
   a,b = b,a;


 It's a function call. Why is a function call a ugly hack?


Because now we're involving libraries to perform a trivial assignment.

It's also more verbose. Compare

 swap(a[i + k], a[i + j]);

 with

 a[i + k], a[i + j] = a[i + j], a[i + k];

 I mean one could even easily miss a typo in there.


That's fair, and in that case you might want to call such a function. But I
think that's very contrived.
I can't remember the last time I ever wanted to perform a swap on anything
other than stack primitives.
Also I think this (and perhaps more so MRV) will find a very major use case
in maths code, and maths code tends to revolve around simple (often single
letter) maths-y type variable names.

There's also semantic issues to be defined. What is the order of evaluation
 in

 f(), g() = h(), k();

 ?


Left to right, clearly. Keep it simple.

All of this adds dead weight to the language. We shouldn't reach to new
 syntax for every single little thing.


MRV is not 'every single little thing'. I'd say it's the single major
checklist item that D just doesn't tick (and perhaps user attributes,
although there's no resistance there, it's just not done yet).
I get the impression I'm not the only one here that feels that way too. I'm
perhaps just louder, and more annoying :)


Re: Multiple return values...

2012-03-15 Thread Andrei Alexandrescu

On 3/15/12 12:51 PM, Manu wrote:

On 15 March 2012 19:05, Andrei Alexandrescu
It's a function call. Why is a function call a ugly hack?


Because now we're involving libraries to perform a trivial assignment.


It's not a trivial assignment, it's swapping two pieces of data. This 
matter has aliasing and ordering implications. Most languages don't make 
swapping a primitive.



It's also more verbose. Compare

swap(a[i + k], a[i + j]);

with

a[i + k], a[i + j] = a[i + j], a[i + k];

I mean one could even easily miss a typo in there.


That's fair, and in that case you might want to call such a function.
But I think that's very contrived.


How is it contrived? I can't figure for the life of me how to swap a 
and b call swap(a, b) is contrived. I mean swapping is even _called_ swap.


So if I can name it as a function and with a suggestive name, and if the 
function is useful, why do we _also_ need a special syntax?



I can't remember the last time I ever wanted to perform a swap on
anything other than stack primitives.


I think that's an exaggeration. swap() is very often (almost always?) 
called with expressions, see e.g. std.algorithm.



Also I think this (and perhaps more so MRV) will find a very major use
case in maths code, and maths code tends to revolve around simple (often
single letter) maths-y type variable names.


I am confident your math code will not look any worse with swap(a, b).


There's also semantic issues to be defined. What is the order of
evaluation in

f(), g() = h(), k();

?


Left to right, clearly. Keep it simple.


Problem is, that and the related issues need to go in the manual.


All of this adds dead weight to the language. We shouldn't reach to
new syntax for every single little thing.

MRV is not 'every single little thing'.


This discussion is about swap.


I'd say it's the single major
checklist item that D just doesn't tick (and perhaps user attributes,
although there's no resistance there, it's just not done yet).
I get the impression I'm not the only one here that feels that way too.


I understand how the draw of reaching for new syntax is extremely 
alluring. I used to fall for it much more often, but over years I have 
hardened myself to resist it, and I think that made me a better language 
designer. In this stage of D, I think we should all have an 
understanding that adding syntax is not a win. Instead, it is an 
acknowledgment that the existing language, for all its might, is unable 
to express the desired abstraction. This is sometimes fine (such as in 
the case of introducing multiple new symbols from one tuple), but 
generally the question is not why couldn't we add syntax X? but 
instead why should we add syntax X?



Andrei


Re: Multiple return values...

2012-03-15 Thread foobar
On Thursday, 15 March 2012 at 17:05:00 UTC, Andrei Alexandrescu 
wrote:

On 3/15/12 11:30 AM, Manu wrote:

On 15 March 2012 17:32, Andrei Alexandrescu
seewebsiteforem...@erdani.org 
mailto:seewebsiteforem...@erdani.org

wrote:
   One note - the code is really ingenious, but I still prefer 
swap()
   in this case. It's more concise and does less work in the 
general case.


   swap(a[i + k], a[j + j]);

   only computes the indexing once (in source, too).


It all still feels to me like a generally ugly hack around the 
original

example:
  a,b = b,a;


It's a function call. Why is a function call a ugly hack?

It's also more verbose. Compare

swap(a[i + k], a[i + j]);

with

a[i + k], a[i + j] = a[i + j], a[i + k];

I mean one could even easily miss a typo in there.

There's also semantic issues to be defined. What is the order 
of evaluation in


f(), g() = h(), k();

?

All of this adds dead weight to the language. We shouldn't 
reach to new syntax for every single little thing.



Andrei


even if swap is preferable here (IMO the syntax improvement here 
is negligible) it is still a special case. In the general case 
Ruby allows e.g:

a, b, c, d = b, d, a, c

How would you do this with swap?

The order of evaluation is an orthogonal issue to the syntax.
The compiler calculates n functions to produce n values so we 
could leave the order undefined.


Some FP languages define order of evaluation (e.g. left to right) 
but IMO this is more suitable for older PCs with serial execution 
(single processor). It's worth considering for current and future 
multi/many-core PCs to instead evaluate the tuple concurrently. 
or perhaps provide both options, e.g.

f(), g() // left to right
@parallel f(), g() // separate tasks/threads

Or perhaps even switch the default to make the parallel order the 
default and mark the former with a @serial annotation.




Re: Multiple return values...

2012-03-15 Thread foobar
On Thursday, 15 March 2012 at 18:23:57 UTC, Andrei Alexandrescu 
wrote:


I understand how the draw of reaching for new syntax is 
extremely alluring. I used to fall for it much more often, but 
over years I have hardened myself to resist it, and I think 
that made me a better language designer. In this stage of D, I 
think we should all have an understanding that adding syntax is 
not a win. Instead, it is an acknowledgment that the existing 
language, for all its might, is unable to express the desired 
abstraction. This is sometimes fine (such as in the case of 
introducing multiple new symbols from one tuple), but generally 
the question is not why couldn't we add syntax X? but instead 
why should we add syntax X?



Andrei


I agree that this is an acknowledgement of the current language's 
inability to express the abstraction. That's why many people ask 
for it to be added in the first place. We should add this syntax 
because it *is* impossible ATM to implement the required 
abstraction.


A good design should strive to provide general features instead 
of special cases (E.g. swap is limited to the 2-tuple case). 
Also, why force an overhead of a function call on such a basic 
feature as assignment? Is swap usually inlined by the compiler?




Re: Multiple return values...

2012-03-15 Thread bearophile
Andrei Alexandrescu:

 generally the question is not why couldn't we add syntax X? but 
 instead why should we add syntax X?

You are right. Tuples are a basic data structure, some other language 
constructs are built on them (swapping items is just one of their purposes, 
then there is multiple return values). They are not necessary, so if you don't 
want to add the syntax sugar we'll do with the libray Tuple we have. But so far 
I have not seen ways to replace that syntax sugar with with something else.

Bye,
bearophile


Re: Multiple return values...

2012-03-15 Thread Simen Kjærås

On Thu, 15 Mar 2012 23:44:09 +0100, foobar f...@bar.com wrote:


Is swap usually inlined by the compiler?


This is the body of swap for simple types:

  auto tmp = lhs;
  lhs = rhs;
  rhs = tmp;

For more complex types:

  ubyte[T.sizeof] t = void;
  auto a = (cast(ubyte*) lhs)[0 .. T.sizeof];
  auto b = (cast(ubyte*) rhs)[0 .. T.sizeof];
  t[] = a[];
  a[] = b[];
  b[] = t[];

So yeah. If that's not inlined, something's wrong.


Re: Multiple return values...

2012-03-15 Thread H. S. Teoh
On Fri, Mar 16, 2012 at 12:11:44AM +0100, Simen Kjærås wrote:
 On Thu, 15 Mar 2012 23:44:09 +0100, foobar f...@bar.com wrote:
 
 Is swap usually inlined by the compiler?
 
 This is the body of swap for simple types:
 
   auto tmp = lhs;
   lhs = rhs;
   rhs = tmp;
 
 For more complex types:
 
   ubyte[T.sizeof] t = void;
   auto a = (cast(ubyte*) lhs)[0 .. T.sizeof];
   auto b = (cast(ubyte*) rhs)[0 .. T.sizeof];
   t[] = a[];
   a[] = b[];
   b[] = t[];
 
 So yeah. If that's not inlined, something's wrong.

Ideally, though, a swap of large objects should be a single loop of xchg
instructions (in x86 anyway, xchg may not exist on other architectures).
Unless dmd is much more clever than I thought, the above code would
generate 3 loops, which uses more memory and is (probably) slower.


T

-- 
Never ascribe to malice that which is adequately explained by incompetence. -- 
Napoleon Bonaparte


Re: Multiple return values...

2012-03-15 Thread Andrei Alexandrescu

On 3/15/12 5:44 PM, foobar wrote:

On Thursday, 15 March 2012 at 18:23:57 UTC, Andrei Alexandrescu wrote:


I understand how the draw of reaching for new syntax is extremely
alluring. I used to fall for it much more often, but over years I have
hardened myself to resist it, and I think that made me a better
language designer. In this stage of D, I think we should all have an
understanding that adding syntax is not a win. Instead, it is an
acknowledgment that the existing language, for all its might, is
unable to express the desired abstraction. This is sometimes fine
(such as in the case of introducing multiple new symbols from one
tuple), but generally the question is not why couldn't we add syntax
X? but instead why should we add syntax X?


Andrei


I agree that this is an acknowledgement of the current language's
inability to express the abstraction. That's why many people ask for it
to be added in the first place. We should add this syntax because it
*is* impossible ATM to implement the required abstraction.


I think this is a reasonable request:

(auto a, b) = fun();

---

static assert(fun().length == 2);
auto __t = fun();
auto a = __t[0];
auto b = __t[1];

To express the idiom in the example, the programmer needs to do the 
expansion by hand, which is verbose and introduces unnecessary symbols. 
So the language as is has an expressiveness problem (albeit not a 
pernicious one) that is nice to solve.


It doesn't seem to me this is a reasonable request:

a, b = b, a;

---

auto __t0 = b;
auto __t1 = a;
a = __t1;
b = __t0;

This is because the alternative swap(a, b) is not only competitive, but 
arguably better at least in some respects.



A good design should strive to provide general features instead of
special cases (E.g. swap is limited to the 2-tuple case). Also, why
force an overhead of a function call on such a basic feature as
assignment? Is swap usually inlined by the compiler?


Sorry, this is grasping at straws.

1. swap can be easily generalized to take any number of arguments. I'm 
very happy that's possible, we've expended great efforts on making 
variadic functions as powerful as they are. But nobody asked for swap 
with many arguments until now. Which segues into...


2. When was the last time you needed to swap arbitrary numbers of 
elements, and so badly and frequently, you needed a new language feature 
for that?


3. Function overhead is solved by inlining, not by adding new features. 
That improves all functions, not only swap.



Andrei


Re: Multiple return values...

2012-03-15 Thread James Miller
 2. When was the last time you needed to swap arbitrary numbers of elements,
 and so badly and frequently, you needed a new language feature for that?


 Andrei

I would like to point out Boscop's vector swizzling tutorial that he
made recently as evidence that not only do you not need it that
frequently, it is fairly easy to implement a nice syntax for it in D,
no language changes needed.

--
James Miller


Re: Multiple return values...

2012-03-15 Thread H. S. Teoh
On Fri, Mar 16, 2012 at 04:46:52PM +1300, James Miller wrote:
  2. When was the last time you needed to swap arbitrary numbers of
  elements, and so badly and frequently, you needed a new language
  feature for that?
 
 
  Andrei
 
 I would like to point out Boscop's vector swizzling tutorial that he
 made recently as evidence that not only do you not need it that
 frequently, it is fairly easy to implement a nice syntax for it in D,
 no language changes needed.
[...]

Yeah, using opDispatch + CTFE to implement things like:

int[4] v1;
auto v2 = v1.zxyw;  // or *any* permutation of wxyz

is pretty amazing. You get syntax more compact than any new language
syntax can offer, *within* the confines of the current language.


T

-- 
Public parking: euphemism for paid parking. -- Flora


Re: Tuple unpacking syntax [Was: Re: Multiple return values...]

2012-03-14 Thread Manu
I'm encouraged to see that every person in this thread so far seems to feel
the same way as me regarding the syntax.


On 14 March 2012 05:25, Derek Parnell ddparn...@bigpond.com wrote:

 On Wed, 14 Mar 2012 13:33:18 +1100, Kevin Cox kevincox...@gmail.com
 wrote:

 (int i,,float f) = intBoringFloat();


 For what its worth, the Euphoria Programming Language uses ? to signify
 ignored values.


Yeah I tend to agree with that logic. I was quite liking the '_' that
someone suggested much earlier in the thread:

«x, _, int y» =  intBoringFloat ();   // Note: it seems to be unclear which
brackets to use ;)


Hey, the Japanese have some really cool brackets!
「x, _, int y」 = ...
〖x, _, int y〗 = ...
〘x, _, int y〙 = ...

Tight! ;)


Re: Tuple unpacking syntax [Was: Re: Multiple return values...]

2012-03-14 Thread RivenTheMage

On Wednesday, 14 March 2012 at 02:33:29 UTC, Kevin Cox wrote:
Kind of unrelated but I think that it is important to have a 
way to ignore

values also.  Leaving them bank would sufice.

(int i,,float f) = intBoringFloat();


or

(int i, null, float f) = intBoringFloat();

or

(int i, void, float f) = intBoringFloat();


Re: Tuple unpacking syntax [Was: Re: Multiple return values...]

2012-03-14 Thread Robert Jacques

On Wed, 14 Mar 2012 03:52:55 -0500, Manu turkey...@gmail.com wrote:


I'm encouraged to see that every person in this thread so far seems to feel
the same way as me regarding the syntax.


On 14 March 2012 05:25, Derek Parnell ddparn...@bigpond.com wrote:


On Wed, 14 Mar 2012 13:33:18 +1100, Kevin Cox kevincox...@gmail.com
wrote:


(int i,,float f) = intBoringFloat();



For what its worth, the Euphoria Programming Language uses ? to signify
ignored values.



Yeah I tend to agree with that logic. I was quite liking the '_' that
someone suggested much earlier in the thread:

«x, _, int y» =  intBoringFloat ();   // Note: it seems to be unclear which
brackets to use ;)


Hey, the Japanese have some really cool brackets!
「x, _, int y」 = ...
〖x, _, int y〗 = ...
〘x, _, int y〙 = ...

Tight! ;)


UTF math has a bunch of cool brackets as well

⟦ x, _, int y ⟧
⦃ x, _, int y ⦄
⦅ x, _, int y ⦆
⦇ x, _, int y ⦈
⦉ x, _, int y ⦊

But there's a reason we use /// instead of ⫻; we shouldn't require custom 
keyboard mappings in order to program efficiently in D.


Re: Multiple return values...

2012-03-14 Thread Ary Manzana

On 3/13/12 6:12 PM, Andrei Alexandrescu wrote:

On 3/13/12 2:57 PM, Manu wrote:

And you think that's more readable and intuitive than: (v1, v2, v3) =
fun(); ?


Yes (e.g. when I see the commas my mind starts running in all directions
because that's valid code nowadays that ignores v1 and v2 and keeps v3
as an lvalue).


Who uses that, except code generators? I'd like D to deprecate the comma 
so it can be used for other things, like tuple assignment.




Let me put it another way: I don't see one syntax over another a deal
maker or deal breaker. At all.


Well, it's sad that syntax is not very important in D. If you have to 
write less code there will be less chance for bugs and it will be more 
understandable (unless you obfuscate the code, obviously).


Here's what you can do in Ruby:

a = 1
b = 2

# Swap the contents
a, b = b, a

Can you do something like that with templates in D, with a nice syntax?


Re: Multiple return values...

2012-03-14 Thread Andrei Alexandrescu

On 3/14/12 2:02 PM, Ary Manzana wrote:

On 3/13/12 6:12 PM, Andrei Alexandrescu wrote:

On 3/13/12 2:57 PM, Manu wrote:

And you think that's more readable and intuitive than: (v1, v2, v3) =
fun(); ?


Yes (e.g. when I see the commas my mind starts running in all directions
because that's valid code nowadays that ignores v1 and v2 and keeps v3
as an lvalue).


Who uses that, except code generators? I'd like D to deprecate the comma
so it can be used for other things, like tuple assignment.


I'd like that, too, but we need to worry about breaking code, too.


Let me put it another way: I don't see one syntax over another a deal
maker or deal breaker. At all.


Well, it's sad that syntax is not very important in D.


That's an undue generalization of what I said.


If you have to
write less code there will be less chance for bugs and it will be more
understandable (unless you obfuscate the code, obviously).


I agree.


Here's what you can do in Ruby:

a = 1
b = 2

# Swap the contents
a, b = b, a

Can you do something like that with templates in D, with a nice syntax?


swap(a, b)


Andrei


Re: Multiple return values...

2012-03-14 Thread Simen Kjærås
On Wed, 14 Mar 2012 20:02:50 +0100, Ary Manzana a...@esperanto.org.ar  
wrote:



Here's what you can do in Ruby:

a = 1
b = 2

# Swap the contents
a, b = b, a

Can you do something like that with templates in D, with a nice syntax?


template to(T...) {
alias T to;
}

auto from(T...)(T t) {
struct Result { T t; alias t this; }
return Result( t );
}

void main( ) {
int a = 3;
int b = 4;

to!(a, b) = from(b, a);

assert( a == 4 );
assert( b == 3 );
}


Re: Multiple return values...

2012-03-14 Thread Ary Manzana

On 3/14/12 5:00 PM, Simen Kjærås wrote:

On Wed, 14 Mar 2012 20:02:50 +0100, Ary Manzana a...@esperanto.org.ar
wrote:


Here's what you can do in Ruby:

a = 1
b = 2

# Swap the contents
a, b = b, a

Can you do something like that with templates in D, with a nice syntax?


template to(T...) {
alias T to;
}

auto from(T...)(T t) {
struct Result { T t; alias t this; }
return Result( t );
}

void main( ) {
int a = 3;
int b = 4;

to!(a, b) = from(b, a);

assert( a == 4 );
assert( b == 3 );
}


Awesome! :-)


Re: Multiple return values...

2012-03-14 Thread Manu
On 14 March 2012 22:10, Ary Manzana a...@esperanto.org.ar wrote:

 On 3/14/12 5:00 PM, Simen Kjærås wrote:

 On Wed, 14 Mar 2012 20:02:50 +0100, Ary Manzana a...@esperanto.org.ar
 wrote:

  Here's what you can do in Ruby:

 a = 1
 b = 2

 # Swap the contents
 a, b = b, a

 Can you do something like that with templates in D, with a nice syntax?


 template to(T...) {
 alias T to;
 }

 auto from(T...)(T t) {
 struct Result { T t; alias t this; }
 return Result( t );
 }

 void main( ) {
 int a = 3;
 int b = 4;

 to!(a, b) = from(b, a);

 assert( a == 4 );
 assert( b == 3 );
 }


 Awesome! :-)


Mmmm, I still kinda like the ruby way.
I agree, the coma operator is a serious liability in D. Multi assignments
without any other rubbish around it are useful in a whole bunch of of
contexts.
How much would really break if coma was deprecated? Is it used any more
than C++ coma? (Most C++ programs wouldn't even know if the coma operator
were removed)


Re: Tuple unpacking syntax [Was: Re: Multiple return values...]

2012-03-14 Thread Manu
On 14 March 2012 15:17, Robert Jacques sandf...@jhu.edu wrote:

 But there's a reason we use /// instead of ⫻; we shouldn't require custom
 keyboard mappings in order to program efficiently in D.


Hold that thought, I think you're missing a major franchising opportunity
right there...
D branded 'pro-codah' keyboards! Nice! :P


Re: Multiple return values...

2012-03-14 Thread Andrei Alexandrescu

On 3/14/12 3:00 PM, Simen Kjærås wrote:

template to(T...) {
alias T to;
}

auto from(T...)(T t) {
struct Result { T t; alias t this; }
return Result( t );
}

void main( ) {
int a = 3;
int b = 4;

to!(a, b) = from(b, a);

assert( a == 4 );
assert( b == 3 );
}


I got reborn inside a little when seeing this code. Congratulations!

Andrei


Re: Multiple return values...

2012-03-14 Thread Derek Parnell
On Thu, 15 Mar 2012 08:52:26 +1100, Andrei Alexandrescu  
seewebsiteforem...@erdani.org wrote:



On 3/14/12 3:00 PM, Simen Kjærås wrote:

template to(T...) {
alias T to;
}

auto from(T...)(T t) {
struct Result { T t; alias t this; }
return Result( t );
}

void main( ) {
int a = 3;
int b = 4;

to!(a, b) = from(b, a);

assert( a == 4 );
assert( b == 3 );
}


I got reborn inside a little when seeing this code. Congratulations!


And I died a little ... I have no idea what this code is doing. What is  
the generated code produced by this and why?


I'd like to break the nexus between science and magic here.

--
Derek


Re: Multiple return values...

2012-03-14 Thread Simen Kjærås
On Wed, 14 Mar 2012 22:52:26 +0100, Andrei Alexandrescu  
seewebsiteforem...@erdani.org wrote:



On 3/14/12 3:00 PM, Simen Kjærås wrote:

template to(T...) {
alias T to;
}

auto from(T...)(T t) {
struct Result { T t; alias t this; }
return Result( t );
}

void main( ) {
int a = 3;
int b = 4;

to!(a, b) = from(b, a);

assert( a == 4 );
assert( b == 3 );
}


I got reborn inside a little when seeing this code. Congratulations!

Andrei


Thank you, and you're welcome.


Re: Tuple unpacking syntax [Was: Re: Multiple return values...]

2012-03-14 Thread foobar

On Wednesday, 14 March 2012 at 13:17:47 UTC, Robert Jacques wrote:
snip
But there's a reason we use /// instead of ⫻; we shouldn't 
require custom keyboard mappings in order to program 
efficiently in D.


Aren't we supposed to be moving towards more natural interfaces 
in computing? I'm sure that once the touch interface is perfected 
for text input it would be trivial to have a coding keyboard or 
a D keyboard. The keyboard mappings would simply come bundled 
with the compiler.


The current text based programming is quite limiting considering 
that we actually deal with a tree of tokens. IDEs already 
manipulate code at the AST level in order to enable refactoring. 
The next logical step would be to eliminate the text form all 
together and store code at the AST level, thus avoiding 
lexing/parsing overhead and the limits of text based 
representation.


e.g. subtextual.org has cool ideas how to design such a future 
non textual language. For example, representing Boolean logic in 
a table instead of arbitrary nested if statements.


Re: Multiple return values...

2012-03-13 Thread Manu
On 13 March 2012 06:45, Andrei Alexandrescu
seewebsiteforem...@erdani.orgwrote:

 You see, at this point I have no idea what to believe anymore. You argued
 very strongly from the position of one whose life depends on efficiency.
 Here and there you'd mix some remark about syntax, and I'd like whaa?...
 but generally discounted it as distraction from the main point, which was
 that all you must do is f(g()) where the body of g() is insignificantly
 small, which makes the cost of passing arguments around absolutely
 paramount.

 And now you come with this completely opposite viewpoint in which the
 syntax is paramount and urgent, whereas codegen is like let's leave it for
 later. I really am confused.


Okay sorry, let me clarify. My own personal stance is unchanged, but I
appreciate your assertion of priorities and I relent :)
This topic has meandered between 2 distinct threads, syntax and abi, and I
feel strongly about both, so maybe my personal sense of priority comes
across wrong as I'm discussing one topic or the other.

Trying to see it from a practicality standpoint, there is a pull request
there which would seem like a near-complete implementation of the
syntax, so that's a much easier/smaller step than messing with the ABI.
Also, the syntax element of the feature will benefit far more people, and
more immediately.
Note, I still find myself wanting this feature, at least syntactically,
every other day (my motivation starting the thread initially). But for my
purposes (simd math library currently) it wouldn't do for it to be
inefficient. At least the promise of an efficient implementation down the
road is needed to make use of it.

I think I feel a sense of urgency towards the ABI aspect because it is a
breaking change, and I suspect the longer anything like that is left, the
less likely/more risky it becomes.
If it gets delayed for 6-12 months, are you honestly more or less likely to
say it's a good idea to fiddle with the ABI?

I am sold on the Tuple approach now, so that's a big discussion that can be
dismissed. I think it was as a result of realising this that the ABI became
of higher importance in my mind, since I agree, workable syntax is
technically possible already (although ugly/verbose).


You don't see the immediate value in a convenient MRV syntax? It would
 improve code clarity in many places, and allow the code to also be more
 efficient down the road.


 I see value in Kenji's related diff, but not in adding syntax to e.g.
 return (int, int). But we want to make sure we address the matter
 holistically (for example: is Kenji's diff enough, or do we need to worry
 about assignment too?). The worst strategy in chess is to move a piece and
 then start analyzing the new situation on the board.


Shall we discuss the shortcomings of his implementation? Can someone
demonstrate the details of his implementation?
From the little examples up in the thread, it looked like you could only
declare new variables inline, but not assign out to existing ones. I'd say
this needs to be added too, and perhaps that will throw the whole design
into turmoil? ;)


Re: Multiple return values...

2012-03-13 Thread Iain Buclaw
On 13 March 2012 09:12, Manu turkey...@gmail.com wrote:
 On 13 March 2012 06:45, Andrei Alexandrescu seewebsiteforem...@erdani.org
 wrote:

 You see, at this point I have no idea what to believe anymore. You argued
 very strongly from the position of one whose life depends on efficiency.
 Here and there you'd mix some remark about syntax, and I'd like whaa?...
 but generally discounted it as distraction from the main point, which was
 that all you must do is f(g()) where the body of g() is insignificantly
 small, which makes the cost of passing arguments around absolutely
 paramount.

 And now you come with this completely opposite viewpoint in which the
 syntax is paramount and urgent, whereas codegen is like let's leave it for
 later. I really am confused.


 Okay sorry, let me clarify. My own personal stance is unchanged, but I
 appreciate your assertion of priorities and I relent :)
 This topic has meandered between 2 distinct threads, syntax and abi, and I
 feel strongly about both, so maybe my personal sense of priority comes
 across wrong as I'm discussing one topic or the other.

 Trying to see it from a practicality standpoint, there is a pull request
 there which would seem like a near-complete implementation of the syntax, so
 that's a much easier/smaller step than messing with the ABI. Also, the
 syntax element of the feature will benefit far more people, and more
 immediately.
 Note, I still find myself wanting this feature, at least syntactically,
 every other day (my motivation starting the thread initially). But for my
 purposes (simd math library currently) it wouldn't do for it to be
 inefficient. At least the promise of an efficient implementation down the
 road is needed to make use of it.

 I think I feel a sense of urgency towards the ABI aspect because it is a
 breaking change, and I suspect the longer anything like that is left, the
 less likely/more risky it becomes.
 If it gets delayed for 6-12 months, are you honestly more or less likely to
 say it's a good idea to fiddle with the ABI?

 I am sold on the Tuple approach now, so that's a big discussion that can be
 dismissed. I think it was as a result of realising this that the ABI became
 of higher importance in my mind, since I agree, workable syntax is
 technically possible already (although ugly/verbose).



What about alternative optimisations for MRV, rather than stating that
it should always be returned in registers where possible (and breaking
ABI on all target platforms).  What about, for example, using named
return value optimisation in this case to help improve the cost of
returning on non-x86 architectures.

Just throwing random thoughts out there.

-- 
Iain Buclaw

*(p  e ? p++ : p) = (c  0x0f) + '0';


Re: Multiple return values...

2012-03-13 Thread Manu
On 13 March 2012 13:27, Iain Buclaw ibuc...@ubuntu.com wrote:

 What about alternative optimisations for MRV, rather than stating that
 it should always be returned in registers where possible (and breaking
 ABI on all target platforms).  What about, for example, using named
 return value optimisation in this case to help improve the cost of
 returning on non-x86 architectures.

 Just throwing random thoughts out there.


What difference would that actually make? The effect is still the same,
unless perhaps you were returning directly into some output structure, that
might be a win in that case (but that's the opposite of what MRV is
actually for).
Definitely no good for slices, and it doesn't help calls, only returns.

The non-x86 platforms don't only suffer from return values, they suffer
passing TO functions as well. So currently they take the hit on both sides.
Slices are fundamental to the language feature, they need to be efficient :/


Re: Multiple return values...

2012-03-13 Thread Andrei Alexandrescu

On 3/13/12 4:12 AM, Manu wrote:

I think I feel a sense of urgency towards the ABI aspect because it is a
breaking change, and I suspect the longer anything like that is left,
the less likely/more risky it becomes.
If it gets delayed for 6-12 months, are you honestly more or less likely
to say it's a good idea to fiddle with the ABI?


I think Walter could answer that.


I am sold on the Tuple approach now, so that's a big discussion that can
be dismissed.


Great!


Shall we discuss the shortcomings of his implementation? Can someone
demonstrate the details of his implementation?
 From the little examples up in the thread, it looked like you could
only declare new variables inline, but not assign out to existing ones.
I'd say this needs to be added too, and perhaps that will throw the
whole design into turmoil? ;)


I thought more about it and we should be fine with two functions (untested):

enum Skip {};
@property ref Skip skip() {
static __gshared Skip result;
return result;
}

void scatter(T, U...)(auto ref T source, ref U targets) {
assert(source.length == targets.length);
foreach (i, ref target; targets) {
static if (is(typeof(target) != Skip)) {
target = source[i];
}
}
}

void gather(T, U...)(ref T target, auto ref U sources) {
assert(target.length == sources.length);
foreach (i, source; sources) {
static if (is(typeof(source) != Skip)) {
target[i] = source;
}
}
}

Usage:

auto t = tuple(1, hi, 2.3);
int a;
string b;
double c;
t.scatter(a, b, skip); // assigns a and b from tuple
b = !;
++c;
t.gather(skip, b, c); // assigns tuple from variables b and c


Andrei


Re: Multiple return values...

2012-03-13 Thread Manu
On 13 March 2012 16:44, Andrei Alexandrescu
seewebsiteforem...@erdani.orgwrote:

 I thought more about it and we should be fine with two functions
 (untested):

 enum Skip {};
 @property ref Skip skip() {
static __gshared Skip result;
return result;
 }

 void scatter(T, U...)(auto ref T source, ref U targets) {
assert(source.length == targets.length);
foreach (i, ref target; targets) {
static if (is(typeof(target) != Skip)) {
target = source[i];
}
}
 }

 void gather(T, U...)(ref T target, auto ref U sources) {
assert(target.length == sources.length);
foreach (i, source; sources) {
static if (is(typeof(source) != Skip)) {
target[i] = source;
}
}
 }

 Usage:

 auto t = tuple(1, hi, 2.3);
 int a;
 string b;
 double c;
 t.scatter(a, b, skip); // assigns a and b from tuple
 b = !;
 ++c;
 t.gather(skip, b, c); // assigns tuple from variables b and c


Well, that 'works' :) .. Is that a proposal for a 'final' syntax, or
something to work with in the mean time?
I said I've come to accept the Tuple *implementation*, but I'm absolutely
not ready to accept the syntax baggage ;)
I'd really rather see something that actually looks like a language feature
in its final manifestation. Is natural and convenient to read and type.

float t;
...
(myStruct.pos, t, _, int err) = intersectThings();

Or something to this effect. That's about as clear and concise as it gets
for my money.


Re: Multiple return values...

2012-03-13 Thread Andrei Alexandrescu

On 3/13/12 10:48 AM, Manu wrote:

float t;
...
(myStruct.pos, t, _, int err) = intersectThings();


I actually find the scatter syntax better than this. Anyway, I hope 
you'll agree there's not much difference pragmatically.


Andrei




Re: Multiple return values...

2012-03-13 Thread Manu
On 13 March 2012 18:07, Andrei Alexandrescu
seewebsiteforem...@erdani.orgwrote:

 On 3/13/12 10:48 AM, Manu wrote:

 float t;
 ...
 (myStruct.pos, t, _, int err) = intersectThings();


 I actually find the scatter syntax better than this. Anyway, I hope you'll
 agree there's not much difference pragmatically.


There's a few finicky differences. I'm still of the understanding (and I
may be wrong, still mystified by some of D's more complicated template
syntax) that once you give the returned tuple a name, it is structurally
bound to the stack. At that point, passing any member by-ref to any
function must conservatively commit the entire tuple to the stack. This
behaviour won't be intuitive to most users, and can be easily avoided; by
obscuring the Tuple from user visibility, they can only access the returned
values through their independant output assignments, which guarantees the
independence of each returned item.

Syntactically, scatter can't declare new variables inline (?), it also uses
additional lines of code (1 + as many variables as you need to declare),
which is very disruptive to flow. Maths-y code should be un-cluttered and
read sequentially. Having to put extra lines in to munge un-related things
really ruins the code IMO ('t' is of no consequence to the user, pollutes
their namespace, gets in the way with extra lines, etc).
What people want from MRV is to capture the returned values independently.
If I *wanted* to capture the returned Tuple (the extremely rare case), I'd
rather do that explicitly, something like this:
auto t = tuple(mrvFunc());

scatter/gather is nice and simple, I'll take it in the mean time, but I
think it would be a shame for it to stop there longer term...
That said though, it's all still nothing to me without at least a promise
on the ABI :) .. And I feel that should ideally come in the form of a
language policy/promise that this feature will be 'efficient' (or at very
least, not *inefficient* as it is now), and leave it to compiler
implementations to concur with that promise, ie, failing to be 'standards'
compliant if they fail to do so.


Re: Multiple return values...

2012-03-13 Thread Andrei Alexandrescu

On 3/13/12 12:02 PM, Manu wrote:

There's a few finicky differences. I'm still of the understanding (and I
may be wrong, still mystified by some of D's more complicated template
syntax) that once you give the returned tuple a name, it is structurally
bound to the stack. At that point, passing any member by-ref to any
function must conservatively commit the entire tuple to the stack. This
behaviour won't be intuitive to most users, and can be easily avoided;
by obscuring the Tuple from user visibility, they can only access the
returned values through their independant output assignments, which
guarantees the independence of each returned item.


Here we go moving the goalposts again.


Syntactically, scatter can't declare new variables inline (?), it also
uses additional lines of code (1 + as many variables as you need to
declare), which is very disruptive to flow.


This is in addition to Kenji's change.


What people want from MRV is to capture the returned
values independently. If I /wanted/ to capture the returned Tuple (the
extremely rare case), I'd rather do that explicitly, something like this:
auto t = tuple(mrvFunc());


No. Tuple stays together by default and is expanded explicitly. This is 
not negotiable.



scatter/gather is nice and simple, I'll take it in the mean time, but I
think it would be a shame for it to stop there longer term...
That said though, it's all still nothing to me without at least a
promise on the ABI :) .. And I feel that should ideally come in the form
of a language policy/promise that this feature will be 'efficient' (or
at very least, not /inefficient/ as it is now), and leave it to compiler
implementations to concur with that promise, ie, failing to be
'standards' compliant if they fail to do so.


This is not for me to promise.


Andrei


Re: Multiple return values...

2012-03-13 Thread Manu
On 13 March 2012 19:25, Andrei Alexandrescu
seewebsiteforem...@erdani.orgwrote:

 On 3/13/12 12:02 PM, Manu wrote:

 There's a few finicky differences. I'm still of the understanding (and I
 may be wrong, still mystified by some of D's more complicated template
 syntax) that once you give the returned tuple a name, it is structurally
 bound to the stack. At that point, passing any member by-ref to any
 function must conservatively commit the entire tuple to the stack. This
 behaviour won't be intuitive to most users, and can be easily avoided;
 by obscuring the Tuple from user visibility, they can only access the
 returned values through their independant output assignments, which
 guarantees the independence of each returned item.


 Here we go moving the goalposts again.


I don't see how? I'm just saying that I don't think they are pragmatically
identical.

Syntactically, scatter can't declare new variables inline (?), it also
 uses additional lines of code (1 + as many variables as you need to
 declare), which is very disruptive to flow.


 This is in addition to Kenji's change.


What value does it add over Kenji's change? Is this because Kenji's change
is unable to perform direct to existing variables?
My understanding from early in the thread was that Kenji's change hides the
returned tuple, and performs a convenient unpack. How can you perform a
scatter if the tuple instance is no longer visible?

What people want from MRV is to capture the returned
 values independently. If I /wanted/ to capture the returned Tuple (the

 extremely rare case), I'd rather do that explicitly, something like this:
 auto t = tuple(mrvFunc());


 No. Tuple stays together by default and is expanded explicitly. This is
 not negotiable.


Then I think you commit to polluting the common case with wordy redundant
noise. Why is it so important?
If it were expanded by default, all you need to do it put a tuple
constructor around it to wrap it up again.
It creates semantic multi-assignment problems I suspect? This is what I
reckon needs to be addressed to make the implementation really nice.

scatter/gather is nice and simple, I'll take it in the mean time, but I
 think it would be a shame for it to stop there longer term...
 That said though, it's all still nothing to me without at least a
 promise on the ABI :) .. And I feel that should ideally come in the form
 of a language policy/promise that this feature will be 'efficient' (or
 at very least, not /inefficient/ as it is now), and leave it to compiler

 implementations to concur with that promise, ie, failing to be
 'standards' compliant if they fail to do so.


 This is not for me to promise.


Sure, but it'd be good to get a weigh in on that issue from Walter, and
others, Iain?


Re: Multiple return values...

2012-03-13 Thread Jose Armando Garcia
On Tue, Mar 13, 2012 at 9:07 AM, Andrei Alexandrescu
seewebsiteforem...@erdani.org wrote:
 On 3/13/12 10:48 AM, Manu wrote:

 float t;
 ...
 (myStruct.pos, t, _, int err) = intersectThings();


This can be checked at compile time. The D compiler can check that the
number of arguments and the types match.


 I actually find the scatter syntax better than this. Anyway, I hope you'll
 agree there's not much difference pragmatically.


Correct if I am wrong but the scatter and gather functions cannot
check that the number of arguments and their type match at compile
time.

 Andrei




Re: Multiple return values...

2012-03-13 Thread Andrei Alexandrescu

On 3/13/12 2:07 PM, Jose Armando Garcia wrote:

On Tue, Mar 13, 2012 at 9:07 AM, Andrei Alexandrescu
seewebsiteforem...@erdani.org  wrote:

On 3/13/12 10:48 AM, Manu wrote:


float t;
...
(myStruct.pos, t, _, int err) = intersectThings();




This can be checked at compile time. The D compiler can check that the
number of arguments and the types match.


scatter() can also be compile-time checked. I left that to a runtime 
assert for more flexibility, but probably more checking is better 
particularly because skip allows skipping some values.



I actually find the scatter syntax better than this. Anyway, I hope you'll
agree there's not much difference pragmatically.



Correct if I am wrong but the scatter and gather functions cannot
check that the number of arguments and their type match at compile
time.


Just replace the two assert()s with static assert or a template constraint.


Andrei


Re: Multiple return values...

2012-03-13 Thread Andrei Alexandrescu

On 3/13/12 1:20 PM, Manu wrote:

What value does it add over Kenji's change? Is this because Kenji's
change is unable to perform direct to existing variables?


Yes.


My understanding from early in the thread was that Kenji's change hides
the returned tuple, and performs a convenient unpack. How can you
perform a scatter if the tuple instance is no longer visible?


If I understand you correctly, you just say fun().scatter(v1, v2, v3).


Andrei



Re: Multiple return values...

2012-03-13 Thread Manu
On 13 March 2012 21:40, Andrei Alexandrescu
seewebsiteforem...@erdani.orgwrote:

 On 3/13/12 1:20 PM, Manu wrote:

 What value does it add over Kenji's change? Is this because Kenji's
 change is unable to perform direct to existing variables?


 Yes.


  My understanding from early in the thread was that Kenji's change hides
 the returned tuple, and performs a convenient unpack. How can you
 perform a scatter if the tuple instance is no longer visible?


 If I understand you correctly, you just say fun().scatter(v1, v2, v3).


Ah okay, I see.
And you think that's more readable and intuitive than: (v1, v2, v3) =
fun(); ?


Re: Multiple return values...

2012-03-13 Thread Andrei Alexandrescu

On 3/13/12 2:57 PM, Manu wrote:

And you think that's more readable and intuitive than: (v1, v2, v3) =
fun(); ?


Yes (e.g. when I see the commas my mind starts running in all directions 
because that's valid code nowadays that ignores v1 and v2 and keeps v3 
as an lvalue).


Let me put it another way: I don't see one syntax over another a deal 
maker or deal breaker. At all.



Andrei


Tuple unpacking syntax [Was: Re: Multiple return values...]

2012-03-13 Thread bearophile
Andrei Alexandrescu:

 Let me put it another way: I don't see one syntax over another a deal 
 maker or deal breaker. At all.

I am usually able to follow threads, but this time I am a bit lost (this 
discussion has mixed very different topics like ABIs, implementation efficiency 
of tuples and typetuples, a hypothetical not-really-a-tuple third-kind of 
tuple, built-in syntax, library implementation code, etc). Is someone able and 
willing to summarize the current situation of this discussion?

From the last weeks (more like months) of intensive usage of functional-style 
D code I really think D will really enjoy an unpacking syntax for 
std.typecons.Tuples. Recently I have written a long post about this:

http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.Darticle_id=159627

The proposed syntax is quite simple, it's mostly implemented in a patch. The 
person that has written the patch says he's willing to add the missing part, 
for foreach looping (see below).

Walter is waiting for something, to find problems. One raw solution for this 
impasse is to apply the patch as an experiment and then look for problems. 
Practical usages helps thinking, sometimes.

In this thread I have seen something regarding problems of the patch on code 
like this, that has one already defined variable:

Tuple!(int,int) foo() { return tuple(1, 2); }
int x;
(x, int y) = foo();


If such code is a problem for the patch, then I am sure the patch author will 
improve it.

Now I am seeing a scatter()/gather library implementation. In that post of mine 
there examples like:

alias Tuple!(CTable, string, int, int) Four;
GrowableCircularQueue!Four open;
// ...
while (open.length) {
immutable item = open.pop();
immutable CTable cur = item[0];
immutable string cSol = item[1];
immutable int x = item[2];
immutable int y = item[3];


With a tuple unpacking syntax becomes:

while (open.length) {
immutable (cur, cSol, x, y) = open.pop();


Are those scatter/gather designed to solve this very very simple problem? How 
do you unpack into immutables? I don't understand.

The missing part in the DMD patch is for something like this (but the exact 
syntax for this is not yet decided):

foreach (auto (x, y); [tuple(1,2), tuple(3,4)]) {...}

How do you do this with library code?

So I think we should put this thread back on the rails. Library implementations 
are not enough here. I suggest to start discussing about what's wrong in the 
proposed D syntax patch, solve the problems Walter has with it.

Bye,
bearophile


Re: Tuple unpacking syntax [Was: Re: Multiple return values...]

2012-03-13 Thread Michael

Maybe

[x, y] = func();

?


Re: Tuple unpacking syntax [Was: Re: Multiple return values...]

2012-03-13 Thread foobar

On Tuesday, 13 March 2012 at 22:26:14 UTC, bearophile wrote:

Andrei Alexandrescu:

Let me put it another way: I don't see one syntax over another 
a deal maker or deal breaker. At all.


I am usually able to follow threads, but this time I am a bit 
lost (this discussion has mixed very different topics like 
ABIs, implementation efficiency of tuples and typetuples, a 
hypothetical not-really-a-tuple third-kind of tuple, built-in 
syntax, library implementation code, etc). Is someone able and 
willing to summarize the current situation of this discussion?



[snip]
So I think we should put this thread back on the rails. Library 
implementations are not enough here. I suggest to start 
discussing about what's wrong in the proposed D syntax patch, 
solve the problems Walter has with it.


Bye,
bearophile


Yeap, I'm confused as well. D's tuple support seems to be 
completely messed up.
This reminds me - what was the semantic problem with the auto 
unpacking in a function's parameter list?
Personally, I think D ought to learn from the experts on this. 
Take a look at how FP languages implement this. E.g take a look 
at ML or Haskell for pointers.




Re: Multiple return values...

2012-03-13 Thread Martin Nowak

Shall we discuss the shortcomings of his implementation? Can someone
demonstrate the details of his implementation?
 From the little examples up in the thread, it looked like you could
only declare new variables inline, but not assign out to existing ones.
I'd say this needs to be added too, and perhaps that will throw the
whole design into turmoil? ;)



We already have very flexible assignments to existing variables.

import std.typecons, std.typetuple, std.stdio;

Tuple!(int, int) foo()
{
return typeof(return)(1, 2);
}

void main()
{
int a, b;
TypeTuple!(a, b) = foo();
writeln(a, b);

TypeTuple!(a, b) = tuple(0, 1, 2)[0 .. 2];
writeln(a, b);

Tuple!(int, int) t;
t[] = TypeTuple!(a, b);
writeln(t);

TypeTuple!(a, b) = tuple(t[1], t[0]);
writeln(a, b);
}


Re: Tuple unpacking syntax [Was: Re: Multiple return values...]

2012-03-13 Thread Derek Parnell
On Wed, 14 Mar 2012 13:33:18 +1100, Kevin Cox kevincox...@gmail.com  
wrote:


Kind of unrelated but I think that it is important to have a way to  
ignore

values also.  Leaving them bank would sufice.

(int i,,float f) = intBoringFloat();


For what its worth, the Euphoria Programming Language uses ? to signify  
ignored values.


eg.

  integer i
  atomf
  {i, ?, f} = intBoringFloat()


We felt that leaving a blank could be a source of human error (can be  
missed when reading code) and also a result of human error (accidentally  
put in a double comma).  Using an unusual glyph to signify omission was  
the compromise we came up with.


--
Derek Parnell


Re: Multiple return values...

2012-03-12 Thread Timon Gehr

On 03/12/2012 05:01 AM, Robert Jacques wrote:

On Sun, 11 Mar 2012 21:49:52 -0500, Mantis mail.mantis...@gmail.com
wrote:

12.03.2012 4:00, Robert Jacques пишет:

On Sun, 11 Mar 2012 18:15:31 -0500, Timon Gehr timon.g...@gmx.ch
wrote:


On 03/11/2012 11:58 PM, Robert Jacques wrote:

Manu was arguing that MRV were somehow special and had mystical
optimization potential. That's simply not true.


Not exactly mystical, but it is certainly there.

void main(){
auto a = foo(); // MRV/struct return
bar(a.x); // defined in a different compilation unit
}

struct return has to write out the whole struct on the stack because of
layout guarantees, probably making the optimized struct return calling
convention somewhat slower for this case. The same does not hold for
MRV.


The layout of the struct only has to exist _when_ the address is
taken. Before that, the compiler/language/optimizer is free to (and
does) do whatever it want. Besides, in your example only the address
of a field is taken, the compiler will optimize away all the other
pieces a (dead variable elimination).


That's the point of discussion. Fields of structure may not be optimized
away, because they are not independent variables. In D you have
unchecked pointer-to-pointer casts, and results of these casts should
depend on target architecture, not on optimizer implementation. At
particular, if such optimizations are allowed, some C API will no longer
be accessible from D.


Unused fields of a structure are optimized away _today_. Unless a piece
of code takes the address of the struct, all of the fields are treated
as independent variables.


The only point I was trying to make is that the 'unless' part does not 
apply to MRV.


Re: Multiple return values...

2012-03-12 Thread Manu
On 12 March 2012 04:44, Andrei Alexandrescu
seewebsiteforem...@erdani.orgwrote:

 On 3/11/12 6:30 PM, Manu wrote:

 D should
 define an MRV ABI which is precisely the ABI for passing multiple args

 TO a function, but in reverse, for any given architecture. This also has
 the lovely side effect of guaranteeing correct argument placement for
 chain-called functions.


 I'm quoting this because it is the tersest and clearest expression of the
 actual request.

 It's a nice feature to have, but so are many others. I don't know what it
 would cost to implement (my guess is: high), and how large the benefits
 would be in various projects.


Is this basically like saying it'll never happen?
There is already a pending pull request implementing the syntax, that
addresses half of the feature straight up.. codegen can come later, I
agreed earlier that it is of lesser importance.
You don't see the immediate value in a convenient MRV syntax? It would
improve code clarity in many places, and allow the code to also be more
efficient down the road.

D seems rather feature-complete. What many other major features are on the
cards if I may ask?


Re: Multiple return values...

2012-03-12 Thread Manu
On 12 March 2012 04:00, Robert Jacques sandf...@jhu.edu wrote:

 On Sun, 11 Mar 2012 18:15:31 -0500, Timon Gehr timon.g...@gmx.ch wrote:

  On 03/11/2012 11:58 PM, Robert Jacques wrote:

 Manu was arguing that MRV were somehow special and had mystical
 optimization potential. That's simply not true.


 Not exactly mystical, but it is certainly there.

 void main(){
 auto a = foo(); // MRV/struct return
 bar(a.x); // defined in a different compilation unit
 }

 struct return has to write out the whole struct on the stack because of
 layout guarantees, probably making the optimized struct return calling
 convention somewhat slower for this case. The same does not hold for MRV.


 The layout of the struct only has to exist _when_ the address is taken.
 Before that, the compiler/language/optimizer is free to (and does) do
 whatever it want. Besides, in your example only the address of a field is
 taken, the compiler will optimize away all the other pieces a (dead
 variable elimination).


No, it can't. That's the point. It must preserve the struct in case you
fiddle with the pointer. Taking the pointer is explicit in this case, but
if you passed anything in the struct to another function by ref, you've
setup the same scenario.


Wait, ARM?! That's really cool. However, as far as I know, D on ARM is very
 experimental. Having an experimental compiler not eak out every last cycle
 is not something that should be unexpected.

 That said, I'm not sure what point you were trying to make, aside from
 backend quality-of-implementation issues. I think bringing these issues up
 is important, but they are tangent to the language changes you're asking
 for.


This is using GCC's backend which is not really experimental, it has
decades of field use. The point here is that we are seeing the effect of
the C ABI applied directly to this problem, and it's completely un-workable.
I'm trying to show that D needs to declare something of an ABI promise when
applied to this problem if it is to be a useful+efficient feature. Again, C
can't express this problem, and we won't get any value from of the C ABI to
make this contruct efficient, but a very simple and efficient solution does
exist.


Why should D place this constraint on future compilers? D currently only
 specifies the ABI for x86. I'm fairly sure it would follow the best
 practices for each of the other architecture, but none of them have been
 established yet.


Constraint? Perhaps you mean 'liberation'...
The x86 ABI is not a *best* practise by a long shot. It is only banking on
a traditional x86 trick for small structs.


I'm was giving you an example that seemed to satisfy your complaints. An
 no, actually it can't return in those registers at zero cost. There is a
 reason why we don't use all the registers to both pass and return
 arguments: we need some registers free to work on them both before and
 after the call.


D should define an MRV ABI which is precisely the ABI for passing multiple
args TO a function, but in reverse, for any given architecture. .. I've
never said anything about using ALL the registers, I say to use all the
ARGUMENT registers.
On x64, that is 4 GPR regs, and 4 XMM regs.


I know Go has MRV. What does its ABI look like? What does ARM prefer? I'd
 recommend citing some papers or a compiler or something. Otherwise, it
 looks like you're ignoring the wisdom of the masses or simply ignorant.


I don't have a Go toolchain, do you wanna run my tests above?
Are you suggesting I have no idea what I'm talking about with respect to
efficient calling conventions? The very fastest way is to return in the
registers designed for the job. This is true for x64, ARM, everything. What
to do when you exceed the argument register limit is a question for each
architecture, but I maintain it should behave exactly as it does when
calling a function, this way you create the possibility of super-efficient
chain-calls.

LLVM has support for MRV how I describe:

The biggest change in LLVM 2.3 is Multiple Return Value (MRV) support. MRVs
allow LLVM IR to directly represent functions that return multiple values
without having to pass them by reference in the LLVM IR. This allows a
front-end to generate more efficient code, *as MRVs are generally returned
in registers if a target supports them*. See the LLVM IR
Referencehttp://llvm.org/releases/2.3/docs/LangRef.html#i_getresult
for
more details.

MRVs are fully supported in the LLVM IR, but are not yet fully supported in
on all targets. However, it is generally safe to return up to 2 values from
a function: most targets should be able to handle at least that. MRV
support is a critical requirement for X86-64 ABI support, as X86-64
requires the ability to return multiple registers from functions, and we
use MRVs to accomplish this *in a direct way*.
In this case, if we have the expression defined in the language (the other
guys have convinced me we do, via tuples), it's conceivable the front end
could present it 

Re: Multiple return values...

2012-03-12 Thread Mantis

12.03.2012 6:01, Robert Jacques пишет:
On Sun, 11 Mar 2012 21:49:52 -0500, Mantis mail.mantis...@gmail.com 
wrote:

[...]
That's the point of discussion. Fields of structure may not be optimized
away, because they are not independent variables. In D you have
unchecked pointer-to-pointer casts, and results of these casts should
depend on target architecture, not on optimizer implementation. At
particular, if such optimizations are allowed, some C API will no longer
be accessible from D.


Unused fields of a structure are optimized away _today_. Unless a 
piece of code takes the address of the struct, all of the fields are 
treated as independent variables.


I can't confirm: http://pastebin.com/YgBULGfe
Prints 42\n3.14\n, compiled with dmd -release on windows x86. How 
exactly did you find out that such optimization is performed?


Re: Multiple return values...

2012-03-12 Thread RivenTheMage

So, function with MRV is basically the function that returns
Tuple where one can specify return convention?

---
auto fun()
{
 return(Windows) tuple(1, 2.0f);
}

(int x, float y) = fun();
---



Re: Multiple return values...

2012-03-12 Thread Iain Buclaw
On 12 March 2012 01:37, Andrew Wiley wiley.andre...@gmail.com wrote:
 On Sun, Mar 11, 2012 at 7:44 PM, Manu turkey...@gmail.com wrote:
 On 12 March 2012 00:58, Robert Jacques sandf...@jhu.edu wrote:

 That's an argument for using the right register for the job. And we can /
 will be doing this on x86-64, as other compilers have already done. Manu was
 arguing that MRV were somehow special and had mystical optimization
 potential. That's simply not true.


 Here's some tests for you:

 // first test that the argument registers allocate as expected...
 int gprtest(int x, int y, int z)
 {
 return x+y+z;
 }

    Perfect, ints pass in register sequence, return in r0, no memory access
 add r0, r0, r1
 add r0, r0, r2
 bx lr

 float fptest(float x, float y, float z)
 {
 return x+y+z;
 }

    Same for floats
 fadds s0, s0, s1
 fadds s0, s0, s2
 bx lr


 // Some MRV tests...
 auto mrv1(int x, int z)
 {
 return Tuple!(int, int)(x, z);
 }

   Simple case, 2 ints
   FAIL, stores the 2 arguments it received in regs straight to output struct
 pointer supplied
 stmia r0, {r1, r2}
 bx lr


 auto mrv2(int x, float y, byte z)
 {
 return Tuple!(int, float, byte)(x, y, z);
 }

   Different typed things
   EPIC FAIL
 stmfd sp!, {r4, r5}
 mov ip, #0
 sub sp, sp, #24
 mov r4, r2
 str ip, [sp, #12]
 str ip, [sp, #20]
 ldr r2, .L27
 add ip, sp, #24
 mov r3, r0
 mov r5, r1
 str r2, [sp, #16] @ float
 ldmdb ip, {r0, r1, r2}
 stmia r3, {r0, r1, r2}
 fsts s0, [r3, #4]
 stmia sp, {r0, r1, r2}
 str r5, [r3, #0]
 strb r4, [r3, #8]
 mov r0, r3
 add sp, sp, #24
 ldmfd sp!, {r4, r5}
 bx lr


 auto range(int *p)
 {
 return p[0..1];
 }

   Range
   SURPRISE FAIL, even a range is returned as a struct! O_O
 mov r2, #1
 str r2, [r0, #0]
 str r1, [r0, #4]
 bx lr


 So the D ABI is a complete shambles on ARM!
 Unsurprisingly, it all just follows the return struct by-val ABI, which is
 to write it to the stack unconditionally. And sadly, it even thinks the
 internal types like range+delegate are just a struct by-val, and completely
 ruins those!

 Let's try again with x86...


 auto mrv1(int x, int z)
 {
 return Tuple!(int, int)(x, z);
 }

 Returns in eax/edx as expected
  movl 4(%esp), %eax
  movl 8(%esp), %edx


 auto mrv2(int x, float y, int z)
 {
 return Tuple!(int, float, int)(x, y, z);
 }

 FAIL! All written to a struct rather than returning in eax,edx,st0 .. This
 is C ABI baggage, D can do better.
  movl 4(%esp), %eax
  movl 8(%esp), %edx
  movl %edx, (%eax)
  movl 12(%esp), %edx
  movl %edx, 4(%eax)
  movl 16(%esp), %edx
  movl %edx, 8(%eax)
  ret $4


 auto range(int *p)
 {
 return p[0..1];
 }

 Obviously, the small struct optimisation allows this to work properly
  movl $1, %eax
  movl 4(%esp), %edx
  ret


 All that said, x86 isn't a good test case, since all args are ALWAYS passed
 on the stack. x64 would be a much better test since it actually has arg
 registers, but I'm on windows, so no x64 for me...

 I assume this is with GDC? Pretty sure GDC doesn't match D's official
 ABI anyway because Iain didn't want to try to push D ABI support into
 GCC along with GDC.
 You're probably still right, but be aware that GDC is a bit different here.

Well, this is taken off the D ABI documentation.

The extern (C) and extern (D) calling convention matches the C calling
convention used by the supported C compiler on the host system. Except
that the extern (D) calling convention for Windows x86 is described
here.


Examining it in a literal sense, I can say that GDC is compliant with
the spec (except on x86 Windows, which it uses stdcall rather than the
calling convention described on the page).

-- 
Iain Buclaw

*(p  e ? p++ : p) = (c  0x0f) + '0';


Re: Multiple return values...

2012-03-12 Thread Iain Buclaw
On 12 March 2012 00:44, Manu turkey...@gmail.com wrote:
 On 12 March 2012 00:58, Robert Jacques sandf...@jhu.edu wrote:

 That's an argument for using the right register for the job. And we can /
 will be doing this on x86-64, as other compilers have already done. Manu was
 arguing that MRV were somehow special and had mystical optimization
 potential. That's simply not true.


 Here's some tests for you:

 // first test that the argument registers allocate as expected...
 int gprtest(int x, int y, int z)
 {
 return x+y+z;
 }

    Perfect, ints pass in register sequence, return in r0, no memory access
 add r0, r0, r1
 add r0, r0, r2
 bx lr

 float fptest(float x, float y, float z)
 {
 return x+y+z;
 }

    Same for floats
 fadds s0, s0, s1
 fadds s0, s0, s2
 bx lr


 // Some MRV tests...
 auto mrv1(int x, int z)
 {
 return Tuple!(int, int)(x, z);
 }

   Simple case, 2 ints
   FAIL, stores the 2 arguments it received in regs straight to output struct
 pointer supplied
 stmia r0, {r1, r2}
 bx lr


 auto mrv2(int x, float y, byte z)
 {
 return Tuple!(int, float, byte)(x, y, z);
 }

   Different typed things
   EPIC FAIL
 stmfd sp!, {r4, r5}
 mov ip, #0
 sub sp, sp, #24
 mov r4, r2
 str ip, [sp, #12]
 str ip, [sp, #20]
 ldr r2, .L27
 add ip, sp, #24
 mov r3, r0
 mov r5, r1
 str r2, [sp, #16] @ float
 ldmdb ip, {r0, r1, r2}
 stmia r3, {r0, r1, r2}
 fsts s0, [r3, #4]
 stmia sp, {r0, r1, r2}
 str r5, [r3, #0]
 strb r4, [r3, #8]
 mov r0, r3
 add sp, sp, #24
 ldmfd sp!, {r4, r5}
 bx lr


 auto range(int *p)
 {
 return p[0..1];
 }

   Range
   SURPRISE FAIL, even a range is returned as a struct! O_O
 mov r2, #1
 str r2, [r0, #0]
 str r1, [r0, #4]
 bx lr


 So the D ABI is a complete shambles on ARM!
 Unsurprisingly, it all just follows the return struct by-val ABI, which is
 to write it to the stack unconditionally. And sadly, it even thinks the
 internal types like range+delegate are just a struct by-val, and completely
 ruins those!

 Let's try again with x86...


 auto mrv1(int x, int z)
 {
 return Tuple!(int, int)(x, z);
 }

 Returns in eax/edx as expected
  movl 4(%esp), %eax
  movl 8(%esp), %edx


 auto mrv2(int x, float y, int z)
 {
 return Tuple!(int, float, int)(x, y, z);
 }

 FAIL! All written to a struct rather than returning in eax,edx,st0 .. This
 is C ABI baggage, D can do better.
  movl 4(%esp), %eax
  movl 8(%esp), %edx
  movl %edx, (%eax)
  movl 12(%esp), %edx
  movl %edx, 4(%eax)
  movl 16(%esp), %edx
  movl %edx, 8(%eax)
  ret $4


 auto range(int *p)
 {
 return p[0..1];
 }

 Obviously, the small struct optimisation allows this to work properly
  movl $1, %eax
  movl 4(%esp), %edx
  ret


 All that said, x86 isn't a good test case, since all args are ALWAYS passed
 on the stack. x64 would be a much better test since it actually has arg
 registers, but I'm on windows, so no x64 for me...


What compiler flags are you using here?  For x86, I would have thought
that small structs ( 8 bytes) would be passed back in registers...
only speculating though - will need to see what codegen is being built
from the D code provided to be sure.



-- 
Iain Buclaw

*(p  e ? p++ : p) = (c  0x0f) + '0';


Re: Multiple return values...

2012-03-12 Thread Manu
On 12 March 2012 19:03, Iain Buclaw ibuc...@ubuntu.com wrote:

 On 12 March 2012 00:44, Manu turkey...@gmail.com wrote:
  On 12 March 2012 00:58, Robert Jacques sandf...@jhu.edu wrote:
 
  That's an argument for using the right register for the job. And we can
 /
  will be doing this on x86-64, as other compilers have already done.
 Manu was
  arguing that MRV were somehow special and had mystical optimization
  potential. That's simply not true.
 
 
  Here's some tests for you:
 
  // first test that the argument registers allocate as expected...
  int gprtest(int x, int y, int z)
  {
  return x+y+z;
  }
 
 Perfect, ints pass in register sequence, return in r0, no memory
 access
  add r0, r0, r1
  add r0, r0, r2
  bx lr
 
  float fptest(float x, float y, float z)
  {
  return x+y+z;
  }
 
 Same for floats
  fadds s0, s0, s1
  fadds s0, s0, s2
  bx lr
 
 
  // Some MRV tests...
  auto mrv1(int x, int z)
  {
  return Tuple!(int, int)(x, z);
  }
 
Simple case, 2 ints
FAIL, stores the 2 arguments it received in regs straight to output
 struct
  pointer supplied
  stmia r0, {r1, r2}
  bx lr
 
 
  auto mrv2(int x, float y, byte z)
  {
  return Tuple!(int, float, byte)(x, y, z);
  }
 
Different typed things
EPIC FAIL
  stmfd sp!, {r4, r5}
  mov ip, #0
  sub sp, sp, #24
  mov r4, r2
  str ip, [sp, #12]
  str ip, [sp, #20]
  ldr r2, .L27
  add ip, sp, #24
  mov r3, r0
  mov r5, r1
  str r2, [sp, #16] @ float
  ldmdb ip, {r0, r1, r2}
  stmia r3, {r0, r1, r2}
  fsts s0, [r3, #4]
  stmia sp, {r0, r1, r2}
  str r5, [r3, #0]
  strb r4, [r3, #8]
  mov r0, r3
  add sp, sp, #24
  ldmfd sp!, {r4, r5}
  bx lr
 
 
  auto range(int *p)
  {
  return p[0..1];
  }
 
Range
SURPRISE FAIL, even a range is returned as a struct! O_O
  mov r2, #1
  str r2, [r0, #0]
  str r1, [r0, #4]
  bx lr
 
 
  So the D ABI is a complete shambles on ARM!
  Unsurprisingly, it all just follows the return struct by-val ABI, which
 is
  to write it to the stack unconditionally. And sadly, it even thinks the
  internal types like range+delegate are just a struct by-val, and
 completely
  ruins those!
 
  Let's try again with x86...
 
 
  auto mrv1(int x, int z)
  {
  return Tuple!(int, int)(x, z);
  }
 
  Returns in eax/edx as expected
   movl 4(%esp), %eax
   movl 8(%esp), %edx
 
 
  auto mrv2(int x, float y, int z)
  {
  return Tuple!(int, float, int)(x, y, z);
  }
 
  FAIL! All written to a struct rather than returning in eax,edx,st0 ..
 This
  is C ABI baggage, D can do better.
   movl 4(%esp), %eax
   movl 8(%esp), %edx
   movl %edx, (%eax)
   movl 12(%esp), %edx
   movl %edx, 4(%eax)
   movl 16(%esp), %edx
   movl %edx, 8(%eax)
   ret $4
 
 
  auto range(int *p)
  {
  return p[0..1];
  }
 
  Obviously, the small struct optimisation allows this to work properly
   movl $1, %eax
   movl 4(%esp), %edx
   ret
 
 
  All that said, x86 isn't a good test case, since all args are ALWAYS
 passed
  on the stack. x64 would be a much better test since it actually has arg
  registers, but I'm on windows, so no x64 for me...


 What compiler flags are you using here?  For x86, I would have thought
 that small structs ( 8 bytes) would be passed back in registers...
 only speculating though - will need to see what codegen is being built
 from the D code provided to be sure.


-S -O2 -msse2
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.


Re: Multiple return values...

2012-03-12 Thread Iain Buclaw
On 12 March 2012 17:22, Manu turkey...@gmail.com wrote:
 On 12 March 2012 19:03, Iain Buclaw ibuc...@ubuntu.com wrote:

 On 12 March 2012 00:44, Manu turkey...@gmail.com wrote:
  On 12 March 2012 00:58, Robert Jacques sandf...@jhu.edu wrote:
 
  That's an argument for using the right register for the job. And we can
  /
  will be doing this on x86-64, as other compilers have already done.
  Manu was
  arguing that MRV were somehow special and had mystical optimization
  potential. That's simply not true.
 
 
  Here's some tests for you:
 
  // first test that the argument registers allocate as expected...
  int gprtest(int x, int y, int z)
  {
  return x+y+z;
  }
 
     Perfect, ints pass in register sequence, return in r0, no memory
  access
  add r0, r0, r1
  add r0, r0, r2
  bx lr
 
  float fptest(float x, float y, float z)
  {
  return x+y+z;
  }
 
     Same for floats
  fadds s0, s0, s1
  fadds s0, s0, s2
  bx lr
 
 
  // Some MRV tests...
  auto mrv1(int x, int z)
  {
  return Tuple!(int, int)(x, z);
  }
 
    Simple case, 2 ints
    FAIL, stores the 2 arguments it received in regs straight to output
  struct
  pointer supplied
  stmia r0, {r1, r2}
  bx lr
 
 
  auto mrv2(int x, float y, byte z)
  {
  return Tuple!(int, float, byte)(x, y, z);
  }
 
    Different typed things
    EPIC FAIL
  stmfd sp!, {r4, r5}
  mov ip, #0
  sub sp, sp, #24
  mov r4, r2
  str ip, [sp, #12]
  str ip, [sp, #20]
  ldr r2, .L27
  add ip, sp, #24
  mov r3, r0
  mov r5, r1
  str r2, [sp, #16] @ float
  ldmdb ip, {r0, r1, r2}
  stmia r3, {r0, r1, r2}
  fsts s0, [r3, #4]
  stmia sp, {r0, r1, r2}
  str r5, [r3, #0]
  strb r4, [r3, #8]
  mov r0, r3
  add sp, sp, #24
  ldmfd sp!, {r4, r5}
  bx lr
 
 
  auto range(int *p)
  {
  return p[0..1];
  }
 
    Range
    SURPRISE FAIL, even a range is returned as a struct! O_O
  mov r2, #1
  str r2, [r0, #0]
  str r1, [r0, #4]
  bx lr
 
 
  So the D ABI is a complete shambles on ARM!
  Unsurprisingly, it all just follows the return struct by-val ABI, which
  is
  to write it to the stack unconditionally. And sadly, it even thinks the
  internal types like range+delegate are just a struct by-val, and
  completely
  ruins those!
 
  Let's try again with x86...
 
 
  auto mrv1(int x, int z)
  {
  return Tuple!(int, int)(x, z);
  }
 
  Returns in eax/edx as expected
   movl 4(%esp), %eax
   movl 8(%esp), %edx
 
 
  auto mrv2(int x, float y, int z)
  {
  return Tuple!(int, float, int)(x, y, z);
  }
 
  FAIL! All written to a struct rather than returning in eax,edx,st0 ..
  This
  is C ABI baggage, D can do better.
   movl 4(%esp), %eax
   movl 8(%esp), %edx
   movl %edx, (%eax)
   movl 12(%esp), %edx
   movl %edx, 4(%eax)
   movl 16(%esp), %edx
   movl %edx, 8(%eax)
   ret $4
 
 
  auto range(int *p)
  {
  return p[0..1];
  }
 
  Obviously, the small struct optimisation allows this to work properly
   movl $1, %eax
   movl 4(%esp), %edx
   ret
 
 
  All that said, x86 isn't a good test case, since all args are ALWAYS
  passed
  on the stack. x64 would be a much better test since it actually has arg
  registers, but I'm on windows, so no x64 for me...


 What compiler flags are you using here?  For x86, I would have thought
 that small structs ( 8 bytes) would be passed back in registers...
 only speculating though - will need to see what codegen is being built
 from the D code provided to be sure.


 -S -O2 -msse2
 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.

OK, -msse2 is not an ARM target option. :~)


Looking around, the Procedure Call Standard for the ARM Architecture
specifically says (section 5.4: Result Return):

A Composite Type not larger than 4 bytes is returned in R0.

A Composite Type larger than 4 bytes ... is stored in memory at an
address passed as an extra argument when the function was called ...



Feel free to correct me if that document is slightly out of date.


-- 
Iain Buclaw

*(p  e ? p++ : p) = (c  0x0f) + '0';


Re: Multiple return values...

2012-03-12 Thread Iain Buclaw
On 12 March 2012 17:49, Iain Buclaw ibuc...@ubuntu.com wrote:
 On 12 March 2012 17:22, Manu turkey...@gmail.com wrote:
 On 12 March 2012 19:03, Iain Buclaw ibuc...@ubuntu.com wrote:

 On 12 March 2012 00:44, Manu turkey...@gmail.com wrote:
  On 12 March 2012 00:58, Robert Jacques sandf...@jhu.edu wrote:
 
  That's an argument for using the right register for the job. And we can
  /
  will be doing this on x86-64, as other compilers have already done.
  Manu was
  arguing that MRV were somehow special and had mystical optimization
  potential. That's simply not true.
 
 
  Here's some tests for you:
 
  // first test that the argument registers allocate as expected...
  int gprtest(int x, int y, int z)
  {
  return x+y+z;
  }
 
     Perfect, ints pass in register sequence, return in r0, no memory
  access
  add r0, r0, r1
  add r0, r0, r2
  bx lr
 
  float fptest(float x, float y, float z)
  {
  return x+y+z;
  }
 
     Same for floats
  fadds s0, s0, s1
  fadds s0, s0, s2
  bx lr
 
 
  // Some MRV tests...
  auto mrv1(int x, int z)
  {
  return Tuple!(int, int)(x, z);
  }
 
    Simple case, 2 ints
    FAIL, stores the 2 arguments it received in regs straight to output
  struct
  pointer supplied
  stmia r0, {r1, r2}
  bx lr
 
 
  auto mrv2(int x, float y, byte z)
  {
  return Tuple!(int, float, byte)(x, y, z);
  }
 
    Different typed things
    EPIC FAIL
  stmfd sp!, {r4, r5}
  mov ip, #0
  sub sp, sp, #24
  mov r4, r2
  str ip, [sp, #12]
  str ip, [sp, #20]
  ldr r2, .L27
  add ip, sp, #24
  mov r3, r0
  mov r5, r1
  str r2, [sp, #16] @ float
  ldmdb ip, {r0, r1, r2}
  stmia r3, {r0, r1, r2}
  fsts s0, [r3, #4]
  stmia sp, {r0, r1, r2}
  str r5, [r3, #0]
  strb r4, [r3, #8]
  mov r0, r3
  add sp, sp, #24
  ldmfd sp!, {r4, r5}
  bx lr
 
 
  auto range(int *p)
  {
  return p[0..1];
  }
 
    Range
    SURPRISE FAIL, even a range is returned as a struct! O_O
  mov r2, #1
  str r2, [r0, #0]
  str r1, [r0, #4]
  bx lr
 
 
  So the D ABI is a complete shambles on ARM!
  Unsurprisingly, it all just follows the return struct by-val ABI, which
  is
  to write it to the stack unconditionally. And sadly, it even thinks the
  internal types like range+delegate are just a struct by-val, and
  completely
  ruins those!
 
  Let's try again with x86...
 
 
  auto mrv1(int x, int z)
  {
  return Tuple!(int, int)(x, z);
  }
 
  Returns in eax/edx as expected
   movl 4(%esp), %eax
   movl 8(%esp), %edx
 
 
  auto mrv2(int x, float y, int z)
  {
  return Tuple!(int, float, int)(x, y, z);
  }
 
  FAIL! All written to a struct rather than returning in eax,edx,st0 ..
  This
  is C ABI baggage, D can do better.
   movl 4(%esp), %eax
   movl 8(%esp), %edx
   movl %edx, (%eax)
   movl 12(%esp), %edx
   movl %edx, 4(%eax)
   movl 16(%esp), %edx
   movl %edx, 8(%eax)
   ret $4
 
 
  auto range(int *p)
  {
  return p[0..1];
  }
 
  Obviously, the small struct optimisation allows this to work properly
   movl $1, %eax
   movl 4(%esp), %edx
   ret
 
 
  All that said, x86 isn't a good test case, since all args are ALWAYS
  passed
  on the stack. x64 would be a much better test since it actually has arg
  registers, but I'm on windows, so no x64 for me...


 What compiler flags are you using here?  For x86, I would have thought
 that small structs ( 8 bytes) would be passed back in registers...
 only speculating though - will need to see what codegen is being built
 from the D code provided to be sure.


 -S -O2 -msse2
 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.

 OK, -msse2 is not an ARM target option. :~)


 Looking around, the Procedure Call Standard for the ARM Architecture
 specifically says (section 5.4: Result Return):

 A Composite Type not larger than 4 bytes is returned in R0.

 A Composite Type larger than 4 bytes ... is stored in memory at an
 address passed as an extra argument when the function was called ...



 Feel free to correct me if that document is slightly out of date.


 --
 Iain Buclaw

 *(p  e ? p++ : p) = (c  0x0f) + '0';



Link:  
http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042d/IHI0042D_aapcs.pdf


-- 
Iain Buclaw

*(p  e ? p++ : p) = (c  0x0f) + '0';


Re: Multiple return values...

2012-03-12 Thread Manu
On 12 March 2012 19:49, Iain Buclaw ibuc...@ubuntu.com wrote:

 OK, -msse2 is not an ARM target option. :~)


Oh sorry, I thought you were asking about the x86 codegen ;)

I used -S -O2 -float-abi=hard


Looking around, the Procedure Call Standard for the ARM Architecture
 specifically says (section 5.4: Result Return):

 A Composite Type not larger than 4 bytes is returned in R0.

 A Composite Type larger than 4 bytes ... is stored in memory at an
 address passed as an extra argument when the function was called ...


Indeed, x86 is the only architecture I know which has this magic 8byte
packing. Every other architecture will be just as bad as ARM by the
standard C ABI.
Something needs to be done about delegates and ranges at the very least, it
would seen GDC just see's these as 8 byte structs being passed around by
value, and only x86 has a hack to improve this.
Does GDC understand MRV internally? I know LLVM does at least, but I
couldn't find info about GDC.


Feel free to correct me if that document is slightly out of date.


Document? :)


Re: Multiple return values...

2012-03-12 Thread Iain Buclaw
On 12 March 2012 17:59, Manu turkey...@gmail.com wrote:
 On 12 March 2012 19:49, Iain Buclaw ibuc...@ubuntu.com wrote:

 OK, -msse2 is not an ARM target option. :~)


 Oh sorry, I thought you were asking about the x86 codegen ;)

 I used -S -O2 -float-abi=hard


 Looking around, the Procedure Call Standard for the ARM Architecture
 specifically says (section 5.4: Result Return):

 A Composite Type not larger than 4 bytes is returned in R0.

 A Composite Type larger than 4 bytes ... is stored in memory at an
 address passed as an extra argument when the function was called ...


 Indeed, x86 is the only architecture I know which has this magic 8byte
 packing. Every other architecture will be just as bad as ARM by the standard
 C ABI.
 Something needs to be done about delegates and ranges at the very least, it
 would seen GDC just see's these as 8 byte structs being passed around by
 value, and only x86 has a hack to improve this.
 Does GDC understand MRV internally? I know LLVM does at least, but I
 couldn't find info about GDC.


It does not.


 Feel free to correct me if that document is slightly out of date.


 Document? :)

Link:  
http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042d/IHI0042D_aapcs.pdf

-- 
Iain Buclaw

*(p  e ? p++ : p) = (c  0x0f) + '0';


Re: Multiple return values...

2012-03-12 Thread Robert Jacques

On Mon, 12 Mar 2012 02:15:55 -0500, Timon Gehr timon.g...@gmx.ch wrote:


On 03/12/2012 05:01 AM, Robert Jacques wrote:

On Sun, 11 Mar 2012 21:49:52 -0500, Mantis mail.mantis...@gmail.com
wrote:

12.03.2012 4:00, Robert Jacques пишет:

On Sun, 11 Mar 2012 18:15:31 -0500, Timon Gehr timon.g...@gmx.ch
wrote:


On 03/11/2012 11:58 PM, Robert Jacques wrote:

Manu was arguing that MRV were somehow special and had mystical
optimization potential. That's simply not true.


Not exactly mystical, but it is certainly there.

void main(){
auto a = foo(); // MRV/struct return
bar(a.x); // defined in a different compilation unit
}

struct return has to write out the whole struct on the stack because of
layout guarantees, probably making the optimized struct return calling
convention somewhat slower for this case. The same does not hold for
MRV.


The layout of the struct only has to exist _when_ the address is
taken. Before that, the compiler/language/optimizer is free to (and
does) do whatever it want. Besides, in your example only the address
of a field is taken, the compiler will optimize away all the other
pieces a (dead variable elimination).


That's the point of discussion. Fields of structure may not be optimized
away, because they are not independent variables. In D you have
unchecked pointer-to-pointer casts, and results of these casts should
depend on target architecture, not on optimizer implementation. At
particular, if such optimizations are allowed, some C API will no longer
be accessible from D.


Unused fields of a structure are optimized away _today_. Unless a piece
of code takes the address of the struct, all of the fields are treated
as independent variables.


The only point I was trying to make is that the 'unless' part does not
apply to MRV.



True. But the principal argument is that MRV implemented via structs is somehow 
less efficient than 'real' MRV. MRV structs would never have their address 
taken and thus the 'unless' clause never happens.


Re: Multiple return values...

2012-03-12 Thread Robert Jacques

On Mon, 12 Mar 2012 04:46:45 -0500, Mantis mail.mantis...@gmail.com wrote:

12.03.2012 6:01, Robert Jacques пишет:

On Sun, 11 Mar 2012 21:49:52 -0500, Mantis mail.mantis...@gmail.com
wrote:

[...]
That's the point of discussion. Fields of structure may not be optimized
away, because they are not independent variables. In D you have
unchecked pointer-to-pointer casts, and results of these casts should
depend on target architecture, not on optimizer implementation. At
particular, if such optimizations are allowed, some C API will no longer
be accessible from D.


Unused fields of a structure are optimized away _today_. Unless a
piece of code takes the address of the struct, all of the fields are
treated as independent variables.


I can't confirm: http://pastebin.com/YgBULGfe
Prints 42\n3.14\n, compiled with dmd -release on windows x86. How
exactly did you find out that such optimization is performed?



We are referring to values on the stack; unless inlined, the returning function 
always has to return all values for both structs and MRV. So, by 'optimized 
away' I'm referring to the ability of the optimizer to not keep values around, 
if that's more efficient. So to test it, you'd probably want to include enough 
operations for the optimizer not want to do this. Second, taking the address of 
a field might be tripping the optimizer up, so try looking at the stack 
directly. I honestly don't know if DMD does this particular optimization as the 
x86 stack is cheap and out of order chips (i.e. x86) thrive on independent 
assignments, but it's a big and very visible feature of NVCC, a GPU C/C++ 
compiler.




Re: Multiple return values...

2012-03-12 Thread Robert Jacques

On Mon, 12 Mar 2012 04:25:54 -0500, Manu turkey...@gmail.com wrote:

On 12 March 2012 04:00, Robert Jacques sandf...@jhu.edu wrote:


On Sun, 11 Mar 2012 18:15:31 -0500, Timon Gehr timon.g...@gmx.ch wrote:

 On 03/11/2012 11:58 PM, Robert Jacques wrote:



Manu was arguing that MRV were somehow special and had mystical
optimization potential. That's simply not true.



Not exactly mystical, but it is certainly there.

void main(){
auto a = foo(); // MRV/struct return
bar(a.x); // defined in a different compilation unit
}

struct return has to write out the whole struct on the stack because of
layout guarantees, probably making the optimized struct return calling
convention somewhat slower for this case. The same does not hold for MRV.



The layout of the struct only has to exist _when_ the address is taken.
Before that, the compiler/language/optimizer is free to (and does) do
whatever it want. Besides, in your example only the address of a field is
taken, the compiler will optimize away all the other pieces a (dead
variable elimination).



No, it can't. That's the point. It must preserve the struct in case you
fiddle with the pointer. Taking the pointer is explicit in this case, but
if you passed anything in the struct to another function by ref, you've
setup the same scenario.


Okay, to be clear about things, once a struct is returned the optimizer can do 
anything to it wants. Certain compilers are extremely aggressive about this 
because on their hardware it matters. C and C++ compilers do this today, so 
yes, compilers can.


Wait, ARM?! That's really cool. However, as far as I know, D on ARM is very

experimental. Having an experimental compiler not eak out every last cycle
is not something that should be unexpected.

That said, I'm not sure what point you were trying to make, aside from
backend quality-of-implementation issues. I think bringing these issues up
is important, but they are tangent to the language changes you're asking
for.



This is using GCC's backend which is not really experimental, it has
decades of field use. The point here is that we are seeing the effect of
the C ABI applied directly to this problem, and it's completely un-workable.
I'm trying to show that D needs to declare something of an ABI promise when
applied to this problem if it is to be a useful+efficient feature. Again, C
can't express this problem, and we won't get any value from of the C ABI to
make this contruct efficient, but a very simple and efficient solution does
exist.


GCC is very large collection of things and its backend has a general reputation 
of being second place to the commercial vendors by a decent margin (25+%) and I 
think also to LLVM. I was more referring to GDC's mapping to the GCC arm 
backend and the associated runtime issues, etc.

As for a simple and efficient solution existing: show me and academic paper or 
compiler that gets it right. Then show me the study on a large codebase that 
its actually more efficient. Then we will listen. Until then, I'm liable to 
trust existing wisdom.


Why should D place this constraint on future compilers? D currently only

specifies the ABI for x86. I'm fairly sure it would follow the best
practices for each of the other architecture, but none of them have been
established yet.



Constraint? Perhaps you mean 'liberation'...
The x86 ABI is not a *best* practise by a long shot. It is only banking on
a traditional x86 trick for small structs.


Let us assume for a moment that the x86 design is good for x86, but terrible 
for ARM and vice versa. Why should either backend do something subpar for the 
other. Generating code for a IOE CPU vs OOE CPU vs a stack machine vs a 
register machine are all very different operations and the backend should have 
the liberation to do whatever is best.


I'm was giving you an example that seemed to satisfy your complaints. An

no, actually it can't return in those registers at zero cost. There is a
reason why we don't use all the registers to both pass and return
arguments: we need some registers free to work on them both before and
after the call.



D should define an MRV ABI which is precisely the ABI for passing multiple
args TO a function, but in reverse, for any given architecture. .. I've
never said anything about using ALL the registers, I say to use all the
ARGUMENT registers.
On x64, that is 4 GPR regs, and 4 XMM regs.


The point is that increasing the number of return registers isn't free and that 
simply matching the best number of argument registers is not, ipso facto ideal.



I know Go has MRV. What does its ABI look like? What does ARM prefer? I'd

recommend citing some papers or a compiler or something. Otherwise, it
looks like you're ignoring the wisdom of the masses or simply ignorant.



I don't have a Go toolchain, do you wanna run my tests above?
Are you suggesting I have no idea what I'm talking about with respect to
efficient calling conventions? The very fastest way is to return in 

Re: Multiple return values...

2012-03-12 Thread Martin Nowak

Is this basically like saying it'll never happen?
There is already a pending pull request implementing the syntax, that
addresses half of the feature straight up.. codegen can come later, I
agreed earlier that it is of lesser importance.
You don't see the immediate value in a convenient MRV syntax? It would
improve code clarity in many places, and allow the code to also be more
efficient down the road.


The tuple unpacking feature has nothing to do with MRV.
Please don't conflate them, it creates a lot of confusion.

Using registers to full extend look really nice but there
are some reasons MRV is not going to happen any time soon.

- Departing from platform ABI's will put us on an isle
  where we need our own compiler backends, debuggers and
  maybe even linkers and OSes.

- Your favorite compiler should be great at inlining so
  chained function calls could have ZERO overhead passing
  return values.

- It is not very efficient to combine MRV with tuples that
  have a contiguous memory layout. Instead of in-place NRVO
  this would be 'callee stack-registers-caller stack'.


Re: Multiple return values...

2012-03-12 Thread Andrei Alexandrescu

On 3/12/12 3:37 AM, Manu wrote:

On 12 March 2012 04:44, Andrei Alexandrescu
seewebsiteforem...@erdani.org mailto:seewebsiteforem...@erdani.org
wrote:

On 3/11/12 6:30 PM, Manu wrote:

D should
define an MRV ABI which is precisely the ABI for passing
multiple args

TO a function, but in reverse, for any given architecture. This
also has
the lovely side effect of guaranteeing correct argument
placement for
chain-called functions.


I'm quoting this because it is the tersest and clearest expression
of the actual request.

It's a nice feature to have, but so are many others. I don't know
what it would cost to implement (my guess is: high), and how large
the benefits would be in various projects.


Is this basically like saying it'll never happen?


Not at all. All I'm saying is we need to have a sense of priority. Right 
now everything that comes up has absolute priority, but somehow 
shouldn't make the previous hot topics enjoy the same level. Getting 
excited is easy. Lucidly analyzing and prioritizing is difficult.



There is already a pending pull request implementing the syntax, that
addresses half of the feature straight up.. codegen can come later, I
agreed earlier that it is of lesser importance.


You see, at this point I have no idea what to believe anymore. You 
argued very strongly from the position of one whose life depends on 
efficiency. Here and there you'd mix some remark about syntax, and I'd 
like whaa?... but generally discounted it as distraction from the main 
point, which was that all you must do is f(g()) where the body of g() is 
insignificantly small, which makes the cost of passing arguments around 
absolutely paramount.


And now you come with this completely opposite viewpoint in which the 
syntax is paramount and urgent, whereas codegen is like let's leave it 
for later. I really am confused.



You don't see the immediate value in a convenient MRV syntax? It would
improve code clarity in many places, and allow the code to also be more
efficient down the road.


I see value in Kenji's related diff, but not in adding syntax to e.g. 
return (int, int). But we want to make sure we address the matter 
holistically (for example: is Kenji's diff enough, or do we need to 
worry about assignment too?). The worst strategy in chess is to move a 
piece and then start analyzing the new situation on the board.



D seems rather feature-complete. What many other major features are on
the cards if I may ask?


You mean designing new features? Not a lot, but this is a moot point 
anyway because the work for scatter initialization has already been done.



Andrei


Re: Multiple return values...

2012-03-11 Thread Manu
On 11 March 2012 03:45, Robert Jacques sandf...@jhu.edu wrote:

 On Sat, 10 Mar 2012 19:27:05 -0600, Manu turkey...@gmail.com wrote:

 On 11 March 2012 00:25, Sean Cavanaugh worksonmymach...@gmail.com
 wrote:

  On 3/10/2012 4:37 AM, Manu wrote:


 If I pass a structure TO a function by value, I know what happens, a
 copy is written to the stack which the function expects to find there.


 This is only true if the compiler is forced to use the ABI, when inlining
 is impossible, or the type being passed is too complex. Structs of pods,
 most compilers do magical things to provided you don't actively work
 against the code gen (virtual methods, dllexports etc), too many separate
 .obj units in C++ etc.


 Talking about the typical case here, obviously not inlined, calling
 through
 the ABI, struct may be simple, just 2-3 values, can you show me a case
 where C++ is able to to anything particularly fancy?

 I've never seen the compiler do wildly unexpected things unless whole
 program optimisation is enabled, which I don't imagine D will be able to
 support any time real soon?

 ...and even then, relying on WPO for the language to generate good code in
 certain circumstances is a really really bad idea. This makes the task of
 implementing an efficient compiler for the language extremely difficult.
 In most cases, making concise expression of the operation you want to
 perform possible in the language will generate better results anyway,
 without depending on an aggressive optimiser. It will also make the code
 more explicit and readable.


 Manu, please go read the D ABI (http://dlang.org/abi.html). Remember,
 your example of returning two values using Tuple vs 'real' MRV? The D ABI
 states that those values will be returned via registers. Returning
 something larger? Then the NVRO kicks in which gives you a zero copy
 approach. On x86-64 these limits are different, since you have more
 registers to play with, but the concept is the same. In fact, returning
 arguments has always been more efficient than passing arguments.


Please go read my prior posts. This has absolutely no bearing on what I'm
talking about. In fact, it fuels my argument in some cases.

That said, I've read it, and it scares the hell out of me. D states that a
small struct may be returned in up to 2 registers (8 bytes/16 bytes), I
suspect this is a hack introduced specifically to make ranges efficient?
If it is not simply a struct of 2 register sized things, they get packed
into a magic 8-16 byte struct implicitly for return, and this makes my
brain explode. If I wanted to perform a bunch of shifts, or's, and and's
until it's snugly packed into a 8-16 byte block before returning, I'd do
that explicitly... I DON'T want to do that however, and I *really* don't
want the language doing that for me. If I want to return 4 byte values,
they should be returned in 4 registers, not packed together into a 32bit
word and returned in one. Also, if there are mixed ints, floats, vectors,
even other structs, none of it works; what if there is float data? Swapping
registers now? That's one of the worst penalties there is.


Re: Multiple return values...

2012-03-11 Thread Manu
On 11 March 2012 04:35, Sean Cavanaugh worksonmymach...@gmail.com wrote:

 On 3/10/2012 8:08 PM, Mantis wrote:

 Tuple!(float, float) callee() {
 do something to achieve result in st0,st1
 fst st0, st1 into stack
 load stack values into EAX, EDX
 ret
 }

 void caller() {
 call callee()
 push EAX, EDX into a stack
 fld stack values into st0, st1
 do something with st0, st1
 }

 As opposed to:

 Tuple!(float, float) callee() {
 do something to achieve result in st0,st1
 ret
 }

 void caller() {
 call callee()
 do something with st0, st1
 }

 Is there something I miss here?


 Yes, the fact the FPU stack is deprecated :)


Don't dismiss the point, the same still stands true with XMM regs.


Re: Multiple return values...

2012-03-11 Thread Manu
On 11 March 2012 05:04, Andrei Alexandrescu
seewebsiteforem...@erdani.orgwrote:

 On 3/10/12 4:37 AM, Manu wrote:

 I still fundamentally see a clear divide between a Tuple, which is a
 deliberately structured association of multiple values, and 'multiple
 return values' which is an explicit non-association of multiple returned
 things.


 There is no difference. A Tuple is a product type, exactly like
 superposing arbitrary values together.


So you're saying that whatever I think about the implementation of Tuple is
wrong? It is not actually a structured type?


 You need to address that principle before I can begin to accept the idea
 of abusing a structured tuple as a substitute for this important
 language feature.

 My analogy is the function argument list. You don't think of the
 arguments you pass to a function as a Tuple (is it implemented
 internally in this way? if so, it is well hidden, and perhaps similar
 magic can be done...)
 You pass, TO a function, multiple un-associated values.


 This analogy is tenuous for D because functions are defined to return one
 type, e.g. typeof(fun(args)) is defined. Once we get into disallowing that
 for certain functions, we're looking at major language changes for little
 benefit.


I don't know how 'major' they'd really work out to be. It's not a paradigm
buster. There are some details, but I don't even think it would need to be
a breaking change to the language (maybe some very subtle tweaks).
Can you quantify 'little' benefit? I started this thread because I have
found myself wishing for this feature every other day. It would be of huge
value, and many others seem to agree.

D has it all, there are so many features in D which make it feel like a
modern language, but this is a missed(/rejected?) opportunity. It's a
natural thing to want to ask a computer to do, but we're mentally trained
into the returns-a-single-thing model from C and whatever and apparently it
was never considered at the initial design stage, but apart from the
expressive side, more importantly in D's case as a native language, it is
an opportunity to implement an important low level feature that no other
language offers me; an efficient multi-return syntax+ABI.

In the tight loops of every program I've ever written, I can't recall a
time when I haven't had a function that needs to return multiple things,
and C/C++ has always forced me into unnecessary memory access to express
this (ref parameters). For the first time in language/compiler history, D
would finally be able to eliminate the last of the redundant memory
accesses in my hottest code in a portable way, at a language/expression
level.

Are you saying it's impossible, that you don't think it should be done, or
it's already solved?
Is it that you don't see the value in it? Is that what I need to convince
you of?
You haven't argued with any of the other points I raised, which leads me to
suspect you either see my points, or you think the entire premise of my
rant is wrong... if so, can you show how, and present a solution that's
workable within existing constructs that meets my criteria without adding
other implicit/logical baggage? Nobody has acknowledged or disputed the
majority of my points :/

D is a native language, that's it's most attractive feature, and while I
appreciate all the high-level correct-ness D offers, it still needs to get
the low level right and ideally improve on existing offerings in meaningful
ways to motivate new users to switch. This is a big un-checked checkbox for
me and my colleagues, and I'd wager any low level realtime programmer out
there who has had to struggle with the codegen in their inner loops..
doubly so if they ever work on non-x86 systems since the penalties are so
much greater.


Re: Multiple return values...

2012-03-11 Thread Manu
On 11 March 2012 05:04, Andrei Alexandrescu
seewebsiteforem...@erdani.orgwrote:

 This analogy is tenuous for D because functions are defined to return one
 type, e.g. typeof(fun(args)) is defined. Once we get into disallowing that
 for certain functions, we're looking at major language changes for little
 benefit.


I do appreciate this point though...
Is it currently possible to enumerate a functions argument list? How do you
express that? The problems seem very similar to me.


Re: Multiple return values...

2012-03-11 Thread Timon Gehr

On 03/11/2012 01:30 PM, Manu wrote:

On 11 March 2012 05:04, Andrei Alexandrescu
seewebsiteforem...@erdani.org mailto:seewebsiteforem...@erdani.org
wrote:

This analogy is tenuous for D because functions are defined to
return one type, e.g. typeof(fun(args)) is defined. Once we get into
disallowing that for certain functions, we're looking at major
language changes for little benefit.


I do appreciate this point though...


It is not a valid concern. typeof(fun(args)) would just be a tuple of types.


Is it currently possible to enumerate a functions argument list?


Yes it is.


How do you express that? The problems seem very similar to me.


Language built-in tuples, of course.
See std.traits.ParameterTypeTuple.

It is extremely obvious how multiple return values should work.



Re: Multiple return values...

2012-03-11 Thread Timon Gehr

On 03/11/2012 12:50 PM, Manu wrote:

Nobody has acknowledged
or disputed the majority of my points :/


I agree with the majority of your points.



Re: Multiple return values...

2012-03-11 Thread David Nadlinger
On Sunday, 11 March 2012 at 03:04:38 UTC, Andrei Alexandrescu 
wrote:
This analogy is tenuous for D because functions are defined to 
return one type, e.g. typeof(fun(args)) is defined. Once we get 
into disallowing that for certain functions, we're looking at 
major language changes for little benefit.


TypeTuple!(ReturnType1, ReturnType2)?

David


Re: Multiple return values...

2012-03-11 Thread Manu
On 11 March 2012 14:56, Timon Gehr timon.g...@gmx.ch wrote:

 On 03/11/2012 12:50 PM, Manu wrote:

 Nobody has acknowledged
 or disputed the majority of my points :/


 I agree with the majority of your points.


Cool, well that's encouraging :)
I can't really argue the implementation details, all I can do is assert
criteria/requirements as I see them.

So what was the perceived issue with the pull request you mentioned in an
earlier post? I presume it only implemented the syntax, and not the ABI
bits?


On 11 March 2012 15:08, David Nadlinger s...@klickverbot.at wrote:

 On Sunday, 11 March 2012 at 03:04:38 UTC, Andrei Alexandrescu wrote:

 This analogy is tenuous for D because functions are defined to return one
 type, e.g. typeof(fun(args)) is defined. Once we get into disallowing that
 for certain functions, we're looking at major language changes for little
 benefit.


 TypeTuple!(ReturnType1, ReturnType2)?


Right, well I'm glad I'm not the only one :)
I figured this must have some important implication that I totally missed...


Re: Multiple return values...

2012-03-11 Thread Timon Gehr

On 03/11/2012 02:23 PM, Manu wrote:

On 11 March 2012 14:56, Timon Gehr timon.g...@gmx.ch
mailto:timon.g...@gmx.ch wrote:

On 03/11/2012 12:50 PM, Manu wrote:

Nobody has acknowledged
or disputed the majority of my points :/


I agree with the majority of your points.


Cool, well that's encouraging :)
I can't really argue the implementation details, all I can do is assert
criteria/requirements as I see them.

So what was the perceived issue with the pull request you mentioned in
an earlier post?


This is the pull request.
https://github.com/D-Programming-Language/dmd/pull/341

I think the issue with it is that there are no obvious issues with it.


I presume it only implemented the syntax, and not the
ABI bits?



Exactly. It implements the assignment from built-in/library tuples to 
multiple newly declared variables, but it does not implement multiple 
return values.




Re: Multiple return values...

2012-03-11 Thread Artur Skawina
On 03/11/12 02:27, Manu wrote:
 I've never seen the compiler do wildly unexpected things unless whole program 
 optimisation is enabled

The compiler can only ignore the ABI when it knows it sees the whole picture,
ie whole program, or whole unit in case of private/local functions.

 which I don't imagine D will be able to support any time real soon?

Umm, GDC supports WPO, both as LTO and std WPO, though i don't remember if
i ever tried the latter.
In fact, until GDC learns cross-module inlining, using these modes is the only
way to make the compiler generate sane code, w/o reverting to turning all
D functions into templates or compiling all sources together (at which point
you could just as well turn on WPO...).
Also, unit-at-a-time, which is on by default, can help.

It (gdc) still has a few LTO bugs which sometimes cause trouble, but often can
be worked around. And you may need to use the *.d runtime files (instead of 
*.di)
etc. 

 ...and even then, relying on WPO for the language to generate good code in 
 certain circumstances is a really really bad idea. This makes the task of 
 implementing an efficient compiler for the language extremely difficult.

There is a reason why i never even considered trying out a non-gcc-based D 
compiler... [1]

For a language like D, WPO in some form is almost required; w/o it you'd need to
always think about how the compiler will treat your code; which, while possible,
would often result in more unnatural and tricky solutions. (In C/C++ there's
a *.h/*.c split, which helps mitigate the problem)


artur

[1] LLVM is possibly an option too, but i haven't yet found the time to 
investigate.


Re: Multiple return values...

2012-03-11 Thread Manu
On 11 March 2012 15:35, Timon Gehr timon.g...@gmx.ch wrote:

 On 03/11/2012 02:23 PM, Manu wrote:

 On 11 March 2012 14:56, Timon Gehr timon.g...@gmx.ch

 mailto:timon.g...@gmx.ch wrote:

On 03/11/2012 12:50 PM, Manu wrote:

Nobody has acknowledged
or disputed the majority of my points :/


I agree with the majority of your points.


 Cool, well that's encouraging :)
 I can't really argue the implementation details, all I can do is assert
 criteria/requirements as I see them.

 So what was the perceived issue with the pull request you mentioned in
 an earlier post?


 This is the pull request.
 https://github.com/D-**Programming-Language/dmd/pull/**341https://github.com/D-Programming-Language/dmd/pull/341

 I think the issue with it is that there are no obvious issues with it.


  I presume it only implemented the syntax, and not the
 ABI bits?


 Exactly. It implements the assignment from built-in/library tuples to
 multiple newly declared variables, but it does not implement multiple
 return values.


How does the syntax work precisely?
Is it possible to do each of those things I mentioned earlier; assign to
existing locals, ignore individual return values, etc?

How would the language distinguish from this implementation of implicitly
building/returning/unpacking a tuple, and from explicit intent to do so
structurally? Does the syntax give the code generation enough information
to distinguish multiple-return from struct byval?


Re: Multiple return values...

2012-03-11 Thread Manu
On 11 March 2012 16:01, Artur Skawina art.08...@gmail.com wrote:

  which I don't imagine D will be able to support any time real soon?

 In fact, until GDC learns cross-module inlining, using these modes is the
 only
 way to make the compiler generate sane code


Yeah I'm very concerned by this currently. But it seems to we a well known
issue with a plan/intent to fix it right?
(Side note: I'd still really like an explicit @forceinline attribute)


  ...and even then, relying on WPO for the language to generate good code
 in certain circumstances is a really really bad idea. This makes the task
 of implementing an efficient compiler for the language extremely difficult.

 There is a reason why i never even considered trying out a non-gcc-based D
 compiler... [1]

 For a language like D, WPO in some form is almost required; w/o it you'd
 need to
 always think about how the compiler will treat your code; which, while
 possible,
 would often result in more unnatural and tricky solutions. (In C/C++
 there's
 a *.h/*.c split, which helps mitigate the problem)


think about how the compiler will treat your code ... 'unnatural' and
tricky solutions ... Outside of cross module inlining, what are some other
common problem cases?

C/C++ .h files only really simplify 2 things, inlining, and templates, but
I can't see how the situation is any different in D?
My understanding is that templates depends on .d/.di files being present
during compilation? So why doesn't D do inlining the same way? If I declare
some inline function in a .d/.di file, surely it should be capable of
inlining?
C/C++ can't inline something from a foreign object either without a
definition in a header...


Re: Multiple return values...

2012-03-11 Thread Artur Skawina
On 03/11/12 15:59, Manu wrote:
 On 11 March 2012 16:01, Artur Skawina art.08...@gmail.com 
 mailto:art.08...@gmail.com wrote:
 
  which I don't imagine D will be able to support any time real soon?
 
 In fact, until GDC learns cross-module inlining, using these modes is the 
 only
 way to make the compiler generate sane code
 
 
 Yeah I'm very concerned by this currently. But it seems to we a well known 
 issue with a plan/intent to fix it right?
 (Side note: I'd still really like an explicit @forceinline attribute)

Just '@forceinline' is IMHO too specific and not enough  -- because you also 
want
@noinline, @noclone, @flatten, @cold and more of theses kind of /generic/ 
attributes,
not to mention the target-specific ones. So a way to specify function (and data)
attributes is needed, together with a way to bundle them. Maybe something like
alias @attr(noinline, noclone, cold, nothrow) @errorpath;.


  ...and even then, relying on WPO for the language to generate good code 
 in certain circumstances is a really really bad idea. This makes the task of 
 implementing an efficient compiler for the language extremely difficult.
 
 There is a reason why i never even considered trying out a non-gcc-based 
 D compiler... [1]
 
 For a language like D, WPO in some form is almost required; w/o it you'd 
 need to
 always think about how the compiler will treat your code; which, while 
 possible,
 would often result in more unnatural and tricky solutions. (In C/C++ 
 there's
 a *.h/*.c split, which helps mitigate the problem)
 
 
 think about how the compiler will treat your code ... 'unnatural' and 
 tricky solutions ... Outside of cross module inlining, what are some other 
 common problem cases?
 
 C/C++ .h files only really simplify 2 things, inlining, and templates, but I 
 can't see how the situation is any different in D?

Traditionally, the pre-WPO way, the coder would put the things expected to 
become
part of the caller in header files. In D, header files are unnecessary, other 
than
for the binary-lib-interface case. And currently even if you duplicate part of 
the
code in a *.di file, it doesn't really change anything, other than preventing 
some
optimizations like inlining or cloning (for the omitted parts).

If you have a complex data type which exports eg a range, you may want just 
'.front'
etc to be inlined, and creating a *.di file just for this purpose shouldn't be 
required. Template definitions need to be visible to the caller/user too - 
having
to place them all in a *.di file would not be a good solution - in many cases
most of the code would move there...

Other than that, there is devirtualization - D does not have 'virtual'; the 
compiler
can still do it and even inline the virtual methods, but it needs to know that
nothing overrides the functions - impossible w/o WPO for public non-final 
classes. [1]

 My understanding is that templates depends on .d/.di files being present 
 during compilation? So why doesn't D do inlining the same way? If I declare 
 some inline function in a .d/.di file, surely it should be capable of 
 inlining?
 C/C++ can't inline something from a foreign object either without a 
 definition in a header...

It's the same for D, I just don't think *.di files should be necessary for 
_internal_
cross-module interfaces, or when the full source is available anyway.
Currently, cross-module inlining is a problem for GDC, but even when that's 
fixed,
WPO will help things like devirtualization or changing calling conventions (gcc 
does
it already, but i don't know if it clones non-local functions just for this, 
when not
in WPO mode).

artur

[1] BTW, this means that a @virtual attribute might be needed - to prevent
devirtualizing methods for classes used by plugins or other dynamically 
loaded code.


Re: Multiple return values...

2012-03-11 Thread Robert Jacques

On Sun, 11 Mar 2012 05:57:05 -0500, Manu turkey...@gmail.com wrote:


On 11 March 2012 04:35, Sean Cavanaugh worksonmymach...@gmail.com wrote:


On 3/10/2012 8:08 PM, Mantis wrote:


Tuple!(float, float) callee() {
do something to achieve result in st0,st1
fst st0, st1 into stack
load stack values into EAX, EDX
ret
}

void caller() {
call callee()
push EAX, EDX into a stack
fld stack values into st0, st1
do something with st0, st1
}

As opposed to:

Tuple!(float, float) callee() {
do something to achieve result in st0,st1
ret
}

void caller() {
call callee()
do something with st0, st1
}

Is there something I miss here?



Yes, the fact the FPU stack is deprecated :)



Don't dismiss the point, the same still stands true with XMM regs.



And Walter has talked about using the XMM registers to return floating point 
data for exactly this reason. But those optimization apply to all structs and 
all data types. There's nothing special about MRV. It has to return a set of 
data in a structured manner; this is identical to the case of returning a 
struct.


Re: Multiple return values...

2012-03-11 Thread Manu
On 11 March 2012 18:50, Robert Jacques sandf...@jhu.edu wrote:

 On Sun, 11 Mar 2012 05:57:05 -0500, Manu turkey...@gmail.com wrote:

  On 11 March 2012 04:35, Sean Cavanaugh worksonmymach...@gmail.com
 wrote:

  On 3/10/2012 8:08 PM, Mantis wrote:

  Tuple!(float, float) callee() {
 do something to achieve result in st0,st1
 fst st0, st1 into stack
 load stack values into EAX, EDX
 ret
 }

 void caller() {
 call callee()
 push EAX, EDX into a stack
 fld stack values into st0, st1
 do something with st0, st1
 }

 As opposed to:

 Tuple!(float, float) callee() {
 do something to achieve result in st0,st1
 ret
 }

 void caller() {
 call callee()
 do something with st0, st1
 }

 Is there something I miss here?


 Yes, the fact the FPU stack is deprecated :)


 Don't dismiss the point, the same still stands true with XMM regs.


 And Walter has talked about using the XMM registers to return floating
 point data for exactly this reason. But those optimization apply to all
 structs and all data types. There's nothing special about MRV. It has to
 return a set of data in a structured manner; this is identical to the case
 of returning a struct.


Well you can't build these sorts of tightly packed structs in XMM
registers... and even for the GPR's, it's very inefficient to do so. I
haven't done tests, but I suspect this feature is probably a
de-optimisation in all cases other than an int-pair struct (ranges,
delegates? I suspect that's why this was invented). Structure
packing/unpacking code will likely be slower than a store/load.

We just need proper multiple return values, then this can go away. And the
earlier any ABI breaking changes are implemented, the better, while there
are still few(/none?) closed source, binary distributed D libraries.


Re: Multiple return values...

2012-03-11 Thread Manu
On 11 March 2012 18:45, Artur Skawina art.08...@gmail.com wrote:

 Other than that, there is devirtualization - D does not have 'virtual';
 the compiler
 can still do it and even inline the virtual methods, but it needs to know
 that
 nothing overrides the functions - impossible w/o WPO for public non-final
 classes. [1]


Oh don't get me started! (...oops, too late!)

This is my single greatest complaint about D, and if I were to adopt D
professionally, I would almost certainly fork the language and fix this.
virtual-by-default seems like the single biggest and most harmful
*mistake*in the whole language.
Defaulting a feature that introduces very costly damage that ONLY a WPO
pass can possibly un-do can only be described as insanity.
Choosing to do this intentionally... does DMD have a powerful WPO? _

I genuinely fear for this decision. Junior programmers will write really
slow code unknowingly, and it's too easy for anyone to simply forget to
declare 'final'. Seniors will spend god only knows how much time (late
nights?) trawling through the codebase verifying non-virtuals, and marking
them final, time you probably can't afford when crunching to ship a product.
Add to that the fact that *validating* a method is not overridden anywhere
is NOT a trivial task. Being able to do this with confidence will be
tedious and waste unimaginable amounts of time and frustration... and why?
What was the harm in explicit virtual? I expect explicit 'final' will
surely overwhelm virtuals in number too when considering keyword clutter
(trivial accessors, properties, etc make up the majority of methods) :/

If I were evaluating D to use commercially, this would be the biggest red
flag to consider. The time/efficiency costs could potentially be very high.

 My understanding is that templates depends on .d/.di files being present
 during compilation? So why doesn't D do inlining the same way? If I declare
 some inline function in a .d/.di file, surely it should be capable of
 inlining?
  C/C++ can't inline something from a foreign object either without a
 definition in a header...

 It's the same for D, I just don't think *.di files should be necessary for
 _internal_
 cross-module interfaces, or when the full source is available anyway.
 Currently, cross-module inlining is a problem for GDC, but even when
 that's fixed,
 WPO will help things like devirtualization or changing calling conventions
 (gcc does
 it already, but i don't know if it clones non-local functions just for
 this, when not
 in WPO mode).


So assuming that most libraries will be closed source, what's the
implication here for inlines and tempaltes long term? It sounds no
different than .h files.
Is there a plan to be able to store that metadata inside lib/object files
somehow? (I assume not, if there was, why need .di files at all)


Re: Multiple return values...

2012-03-11 Thread Andrei Alexandrescu

On 3/11/12 7:52 AM, Timon Gehr wrote:

It is extremely obvious how multiple return values should work.


(int, int) fun();

writeln(fun());
auto a = fun();
writeln(a);

What should happen?


Andrei



  1   2   3   >