www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 14761] New: Optimize and debloat pass by ref to pass by value

https://issues.dlang.org/show_bug.cgi?id=14761

          Issue ID: 14761
           Summary: Optimize and debloat pass by ref to pass by value (use
                    case - all output ranges)
           Product: D
           Version: D2
          Hardware: All
                OS: All
            Status: NEW
          Severity: enhancement
          Priority: P1
         Component: phobos
          Assignee: nobody puremagic.com
          Reporter: dmitry.olsh gmail.com

The problem statement is this code in Phobos (std.range.primitives):

private void doPut(R, E)(ref R r, auto ref E e)
{
    static if(is(PointerTarget!R == struct))
        enum usingPut = hasMember!(PointerTarget!R, "put");
    else
        enum usingPut = hasMember!(R, "put");

    //...
}

Observe that the range r is always taken by reference where if range is a class
instance or a pointer it makes no sense to add an extra indirection. 

Moreover ref Range* and ref Range instantiations should be exactly the same
code as (would be) Range* so there is an opportunity to debloat by factor of 2
by merging them.

In simple cases presented compiler was able to optimize it out via inlining,
but sometimes it might not. Certainly it won't do so in unoptimized or
separately compiled builds. 

See also original motivation and code:

https://github.com/D-Programming-Language/phobos/pull/2655


// Sketch of the technique to merge ref T and T* branches for output ranges 
// while passing classes/delegates by value

private enum bool isPassByValue(T) = is(T: U*, U) || is( T == class ) || 
    is (T == delegate) || is( T == function );

// Hook C-runtime to avoid being optimized out
extern(C) void putchar(int c);


//same doPut but  _stripped_ of `ref` storage class
void doPut(T)(T arg){
    pragma(msg, "Instantiated " ~ T.stringof);
    // some sensible output ...
    putchar(arg.value); 
}

template forwardRef(alias Fn, T)
{
    static if(isPassByValue!T)
    {
        pragma(msg, T.stringof ~" by value");
        alias forwardRef = Fn!T; 
    }
    else
    {
        pragma(msg, T.stringof ~" by ref");
        auto forwardRef(ref T arg)
        {
            //convert to pointer explicitly
            // ref T --> T* to debloat based on ref-ness
            return Fn(&arg);
        }
    }
}

struct Val{
    int value;
}

class CVal{
    int value;
}

void main()
{
    alias valFn = forwardRef!(doPut, Val);
    alias ptrFn = forwardRef!(doPut, Val*);
    alias classFn = forwardRef!(doPut, CVal);
    Val v = Val('A');
    CVal cv = new CVal;
    cv.value = 'C';
    // call each function to see codegen
    valFn(v);
    ptrFn(&v);
    classFn(cv);
}

--
Jul 02 2015