The conversion does increase the latency of getting the value of x. If there's nothing else to do then the tool chain will insert explicit nops to wait for the expression. The same stalls will exist on other architectures for the same code, just not visibly in the code. It happens that making the nops explicit is faster than a stall; you can idle through a nop with no added overhead, but you can't restart a stall instantaneously.
Based on my limited understanding of the Mill I'd say (1) (simple_ PHIs are implemented by the pick-phase, (2) it's an exposed pipeline with known latencies, so you can't really have the "I want to avoid the longer latency inside a single basic block", and it's probably not worth doing that for a single mult vs. a single add, (3) for cases where it matters because the latency difference is much better, you'd use two basic blocks, and rearrange belt positions at the end.
If I remember it right, ADD and MUL take the same time, but when instructions differ in time the compiler is expected to reorder them. If it can't reorder, than yes, it has to wait.
How does the compiler implement PHIs connecting expressions with different latencies? Let's say I have:
`if (cond) { x = a + b; } else { x = a * b; }`
The MUL is may take a bit longer than the ADD, but the user needs to accept the argument at a given belt position. How do you avoid having the pay the latency cost for the MUL if `cond` is usually true?