Yes, for the code generation one needs to well understand semantics of the target language. And when it's a low-level language (Assembly), the "voodoo magic" mainly relates to generic understanding of the lower-level architectures. However, there might be compilation to another high-level language (in case of a transpiler), and by itself code generation at this stage is not that hard, simply because a target language is more understood in this case.
Instruction selection topic, and where to place the data -- on the stack, or into registers (and how a register allocator works) also mainly relates to the lower-level.
> until you get into the backend
Yes, and that's why I think it should be a separate class, specifically for higher-level compilation, and a low-level compilation.
We'll be covering this in "Essentials of Compilation".