Having had to patch binaries on multiple occasions by inserting instructions, it is definitely not hard to do so for x86 as one can easily find "slack" that the compiler left behind[1], but I once had to do it for a MIPS binary, and it was definitely not easy to squeeze in the few extra instructions I needed inline; I ended up having to detour to another area with jumps instead.
Here's an old paper where the authors tried to optimise for code density manually, and you can consistently see x86 beating ARM and MIPS:
https://web.eece.maine.edu/~vweaver/papers/iccd09/iccd09_den...
[1] See https://news.ycombinator.com/item?id=15720923 for an example.
Pyrit, a ray tracing demo in 256 bytes, does all of that: https://www.pouet.net/prod.php?which=78045
You probably wouldn't want your general purpose compiler doing this sort of thing! The resulting code would be suboptimal and fragile.
Look at the demoscene for an example of dense x86 code, especially in the sub-1K categories. They routinely achieve code densities for x86 that no compilers I know of can get close to, and AFAIK I have not seen the same happen with ARM, nor MIPS or any other well-known RISC.
3-address instructions improve code density only in situations where both source operands are needed later.