There are several advantages to not using machine code directly:
- Writing ASM, which basically is machine code, is very difficult to learn, write, read and scale. It would take a multitude of time to do things we do today in abstract languages. Compare ASM vs C Hello World programs
- There are many different CPUs and assemblers out there and each has different instructions and a slightly different syntax. In Wikipedia, there are 8(!) example Hello World programs for x86 alone! Today, we also have x86_64, ARM, ARM64, MIPS, RISC and more, each of them in different versions, as well as with many extensions, like SSE, SSE2, SSE3, SSSE3, SSE4, SSE4.1, SSE4.2, AVX, AVX2, TXT, VT-x, VT-d, F16C,... and you would have to write a different program for each and every single one of them. With abstract languages, a compiler can optimize the abstract language for a certain architecture with certain extensions fully automatically! Compiling the code for a different architecture or with different extensions can be as easy as just telling the compiler to use a different target CPU (see the LLVM target list as reference; lines 49 to 164 list all CPU and OS types supported - and there are not even the extensions listed!)
- Today's optimizers can generate very very performent machine code. See this post about a Rust example, which results in machine code, that the author "could not have written [..] better [himself]"
As such, it does not make any sense to use machine language directly whenever not necessary.
Footnote: Rust uses the LLVM compiler-suite. LLVM is also used by other compilers (and even transpilers), like Clang and Emscripten.