
If you’re going to emulate them, it has to be at the software level instead of part of the CPU (otherwise you’d effectively just have implemented the instruction, but stupidly, and still fall foul of any intellectual property issues), so you need to run custom code when an unimplemented instruction is decoded (that’s easy - you can just trap on an invalid opcode), but also need to tell software that might try to use them that you support those extensions (plenty of software checks the CPUID on startup and just errors out if required extensions aren’t reported). That’s harder to do. You could:
- run all user software with a debugger attached, single-stepping it and intervening when the CPUID instruction appears. This would be really slow as you’d have to context switch to run some debugger code after every instruction.
- run all user software on an emulated machine, in which case it doesn’t matter what your real architecture is (although having a compatible memory model helps avoid loads of locks).
- run all user software on a virtual machine, which requires virtualisation extensions, which came later than AMD64.

And it was functionally the second bullet point, with all instructions being emulated, so the emulation software was what the CPU really ran. You can do emulation of a CPU on a compatible CPU, but it doesn’t generally end up faster than doing it on an unrelated CPU. E.g. the code you’re emulating will use all the registers the CPU has, so you have to maintain a mapping from emulated registers to real registers (and because you need some registers for the emulator, some emulated registers necessarily must spill to memory), and the code you’re emulating could use any part of the address space, so you’ve got to map from the emulated address space to the real one. Every instruction potentially needs some alteration, so you can’t just run them as-is. There can be time savings, e.g. x86 has all aligned writes under 64-bits be atomic, so on unmodifed Arm, you need to insert extra locks, and if x87 instructions are used, they don’t quite conform to IEEE754, so would need doing via many integer instructions instead of a single float instruction on non-x86 platforms, but there can also be time savings from using a different architecture, e.g. Transmeta’s CPUs had way more registers than x86, so could give all emulated registers a real register and still have enough for the emulator itself to use.