It depends on what you have in mind exactly, I guess. There's certainly an argument for introducing "split" variants of these instructions with two outputs like IMUL and UDIV, but also e.g. SINCOS, particularly since we're effectively splitting them for SPIR-V (and GLSL) output anyway. I don't think that needs to be an issue for the disassembler though; we can just keep the complex instructions in the IR, and lower them to the simpler variants before producing output, much like we intend to do for some of the more complex texturing instructions from shader model 1-3.
I guess that's a potential way forward. I'm not completely sold on it, because even having different opcodes that do the same thing is an element of complexity, not necessarily welcome in an IR; it can be mitigated with other passes, true, but that's also an element of complexity, and more code that has to be maintained.
Sure. I think the main alternative would be to split the IR in two (or more) separate IRs though. I.e., you'd have a representation of the parsed TPF, then convert that to VSIR, and from there to SPIR-V. The disassembler would operate on TPF IR, as would certain lowering passes. That's certainly a valid choice, but I think it's important to point out that while it would make some thing easier, it would also make some things harder. The most obvious is perhaps that we'd need separate disassemblers for d3dbc, tpf, dxil, and vsir. Somewhat less obvious is perhaps that we may need to duplicate certain lowering passes between e.g. d3dbc and tpf, because we'd no longer be able to express them in vsir. It may also make it slightly harder to do something like HLSL IR -> vsir -> d3dbc, because we'd have to get rid of complex texturing instructions when converting HLSL IR to vsir, and then reintroduce them when converting vsir to d3dbc. Are those worth it? Well, maybe; it doesn't seem like an obvious win to me. Note also that it's not uncommon for languages/IRs to have different dialects or subsets; the obvious example here is perhaps LLVM IR/DXIL, but note that it's also true for all of d3dbc, tpf, HLSL, and GLSL to various extents.
Speaking of which (but that's not related to the assembler or the semantics), another aspect that makes the way we currently use VSIR not ideal as an IR over which to run passes is that it is stored as a big array, so adding instructions in the stream means moving a lot of data. For HLSL it's easier, because instructions are linked. I guess if we want to start doing passes on VSIR seriously we're converting that to a linked list too?
We may want to tweak the vkd3d_shader_instruction_array data structure somewhat, but I don't think we'll need to do anything as drastic as converting the instruction array to a linked list; gap buffers tend to handle this kind of thing fairly well, and we may even be able to improve on that in specific instances.