I couldn't find a clear answer about whether returning from an async method always produces release semantics and whether await always produces acquire semantics. I assume yes, because otherwise any async/await code would be a minefield?
So here's an example: are returned values both guaranteed to be 100*2 and 12345*2, without any explicit locks or barriers?
private static async Task<(int, int)> AMethod()
{
    // Runs on the original thread:
    var x = 100;
    var y = 12345;
    var task = Task.Run(() =>
    {
        // Can run on another thread:
        x *= 2;
        y *= 2;
        // Implicit return here, marking the task completed.
        // Release semantics or not?
    });
    await task; // Acquire semantics or not?
    // Runs on the original thread:
    return (x, y);
}
EDIT: Of course, Task.Run also needs to produce a release and an acquire is needed when starting to run the task's code. Forgot about those in the original question.