It's mostly about explicit vs. implicit. If a reference is implicit - it's pass-by-reference.
Not according to the comment that I am replying to:
> A reference is basically a pointer that the language prevents you from doing pointer arithmetic on, and in many languages such as PHP, is simply automatically dereferenced for you.
Also, the implicit is usually pass-by-value.
function foo(a) { a++; }
function bar(ref a) { a++; }
b = 1;
foo(b); // Pass by value, implicit.
assert(b == 1);
bar(ref b); // Pass by reference.
assert(b == 2);
Heap vs. stack values are not the same thing as pass-by-reference and pass-by-value.
That is an implementation detail completely depends on the VM. There is one very simple exception to your example: call inlining. Pass-by-reference is a behavior specification, not an implementation specification.
> Rust
If Rust emitted purely pass-by-value machine code, zero-cost-abstractions would be impossible in the language.