Here is an explanation related to machine language.
CPUs usually do not know about variables and operator precedence. They have a few registers that they can use to operate on, and memory cells to store data that exceeds their register capacity. Memory cells are numbered, and when we write variable b, the compiler will translate that into some memory cell's number - an address. The value stored at that addess is the value of the variable b.
But remember variables are not being calculated on - all this happens in the registers. So to add two numbers they have to be loaded into the CPU registers first.
int b = 9;
So in the above instruction the memory cell representing b is loaded with the value 9.
b = b + (b=3)
In this instruction b would get copied into a register (probably the accumulator, usually called register A) to be added with (b=3). Now the value 3 is loaded into the memory cell while the register has not changed, and finally the value 3 is added to what is still in the register: the old value of b.
Hence the operation results in
b = 9 + 3
and you have the 12.
In reality the time when the registers get loaded may differ since the assignment may be a subrouting requiring to reuse the registers for other stuff. But then, to avoid side effects the registers have to be restored so the programming logic stays the same.