Disallowing smc is a significant perf/power win. For cpus that run a large variety of large code (e.g. a web browser or ux stack), being able to cache a large instruction footprint and fetch/decode it quickly is important. Having to have the icache snoop data writes and entangle the i-fetch with the store buffer machinery would be a huge penalty to pay for a niche use case. Unlike loads, which are a small fraction of instructions to disambiguate with stores, you'd have to disambiguate every single instruction.
JIT is important to Apple platforms, and they seem to manage to make it work well enough even with the need for explicit invalidation.
So that is only true because we do it, there is a world where we optimize differently and that self modifying version works better, reread the synthesis kernel thesis(one, it is super easy, two they did this), we could have hardware that does this. Because we don't have hardware that does this we don't
FWIW if you look at WebKit/JavaScriptCore, the trend has been towards less-frequent code modification, e.g. inline caches are mostly no longer repatched inline. Whole-function reoptimization is still worth the overhead of sys_icache_invalidate() + `ISB`'ing everyone involved, but at smaller granularities not so much.
I love you, keep it up dude. Exactly the kinda shit I would say if I didn't want to lose more fake internet points now.