Let's say you wrote this code:
a = 2 * b + c * d;
writeToDisk(a)
e = b + b - a;
sendToNetwork(e)
Naively, you would expect the compiler to emit CPU instructions that perform the computations left-to-right, top-to-bottom. In pseudocode assembly:
mul 2, b -> a
mul c, d -> reg1
add a, reg1 -> a
push a
call writeToDisk
mov b -> reg1
add b, reg1 -> reg1
sub a, reg -> e
push e
call sendToNetwork
The externally observable effects may be considered to be the function calls. If you're dealing with I/O devices then it would be some similar specific hardware event such as writing to a GPIO register.
It should be immediately obvious that the compiler can compute 2*b just once, cache it in a register, and use it to compute both 'a' and 'e', even though the formulas are different and interspersed with function calls. Similarly, it can noticed that e has a 2b-2b in its overall formula and hence eliminate some computations entirely.
As long as the inputs to the functions don't change and the order of their calls aren't modified, "all is fine" and the compiler is free to do this.
In fact, modern compilers can completely eliminate function calls, inlining them if they perform pure computations or have no side-effects! This is done to such a point that it makes benchmarking challenging: merely timing some computations will often take 0 time unless the result of the computation is written to the console or some similarly externally-visible destination.
Compilers are allowed to this even for abstractions like virtual functions or interfaces. The JVM does this regularly. If it notices there's only a single implementation of an interface, it'll inline it, possibly eliminating the call entirely if it turns out to be a no-op.