Repository: pytorch
Issue: Support out= parameters with autograd
Labels: feature,module: autograd,module: nn,triaged

## 🚀 Feature
Being able to specify where the result of an op should be written to, rather than having ops allocating their own buffers.

## Motivation
I'm working with context blocks, as defined in https://arxiv.org/pdf/1511.07122.pdf

```python
class MultiConvolution(torch.nn.modules.Module):
    def __init__(self, inputChannels, outputChannels, stride, padding = 'zeros'):
        super(MultiConvolution, self).__init__()
        self.padding = padding
        self.a = torch.nn.Conv2d(inputChannels, outputChannels // 2, 3, stride=stride, padding=0, dilation=1)
        self.b = torch.nn.Conv2d(inputChannels, outputChannels // 4, 3, stride=stride, padding=0, dilation=2)
        self.c = torch.nn.Conv2d(inputChannels, outputChannels // 8, 3, stride=stride, padding=0, dilation=4)
        self.d = torch.nn.Conv2d(inputChannels, outputChannels // 8, 3, stride=stride, padding=0, dilation=8)

    def pad(self, x, p):
        if self.padding == 'circular':
            return padding.circular_pad(x, p)
        return padding.reflection_pad(x, p)

    def forward(self, x):
        a = self.a(self.pad(x, 1*2))
        b = self.b(self.pad(x, 2*2))
        c = self.c(self.pad(x, 4*2))
        d = self.d(self.pad(x, 8*2))
        return torch.cat([a, b, c, d], 1)
```
In this situation, it seems that PyTorch allocate four buffers for the output of a, b, c, and d.
And then a fifth one for the result of the concatenation.

## Pitch

I'd like a bit more control, with the option of manually specifying where an op writes it's output.
Something like :
```python
class MultiConvolution(torch.nn.modules.Module):
    def __init__(self, inputChannels, outputChannels, stride, padding = 'zeros'):
        super(MultiConvolution, self).__init__()
        self.outputChannels = outputChannels
        self.padding = padding
        self.a = torch.nn.Conv2d(inputChannels, outputChannels // 2, 3, stride=stride, padding=0, dilation=1)
        self.b = torch.nn.Conv2d(inputChannels, outputChannels // 4, 3, stride=stride, padding=0, dilation=2)
        self.c = torch.nn.Conv2d(inputChannels, outputChannels // 8, 3, stride=stride, padding=0, dilation=4)
        self.d = torch.nn.Conv2d(inputChannels, outputChannels // 8, 3, stride=stride, padding=0, dilation=8)

    def pad(self, x, p):
        if self.padding == 'circular':
            return padding.circular_pad(x, p)
        return padding.reflection_pad(x, p)

    def forward(self, x):
        ret = torch.Tensor(x.shape[0], self.outputChannels, x.shape[2], x.shape[3]) # One large memory allocation here
        a = self.a(self.pad(x, 1*2), outputBuffer=ret[:,0:outputChannels // 2]) # No need for alloc
        b = self.b(self.pad(x, 2*2), outputBuffer=ret[:,outputChannels // 2:outputChannels // 4 * 3]) # No need for alloc
        c = self.c(self.pad(x, 4*2), outputBuffer=ret[:,outputChannels // 4 * 3:outputChannels // 8 * 7]) # No need for alloc
        d = self.d(self.pad(x, 8*2), outputBuffer=ret[:,outputChannels // 8 * 7:]) # No need for alloc
        return ret
```


## Alternatives
See pitch

## Additional context
See linked paper


cc @ezyang @albanD @zou3519 @gqchen @pearu @nikitaved @soulitzer @mruberry @jbschlosser @gchanan @jerryzh168