vkd3d-shader/dxil: Implement the DXIL BINOP instruction.
Merge request reports
Activity
added 56 commits
-
4505f66e...f75bdd6e - 47 commits from branch
wine:master
- 87f0859f - vkd3d-shader/dxil: Replace register_address_init() with register_index_address_init().
- b3e4bc5b - vkd3d-shader/dxil: Implement DX instruction CreateHandle.
- 0dc5f632 - vkd3d-shader/dxil: Implement DX instruction CBufferLoadLegacy.
- 0339c657 - vkd3d-shader/spirv: Support scalar swizzle of vector SSA registers.
- 2304f87e - vkd3d-shader/dxil: Implement the DXIL EXTRACTVAL instruction.
- 536f506c - vkd3d-shader/spirv: Introduce an FREM instruction.
- 83d26fc2 - vkd3d-shader/spirv: Introduce an IDIV instruction.
- d5221ff5 - vkd3d-shader/spirv: Handle the UMUL instruction.
- b1225243 - vkd3d-shader/dxil: Implement the DXIL BINOP instruction.
Toggle commit list-
4505f66e...f75bdd6e - 47 commits from branch
added 9 commits
- 79746e73 - vkd3d-shader/dxil: Implement DX instruction CreateHandle.
- b3d1e179 - vkd3d-shader/dxil: Implement DX instruction CBufferLoadLegacy.
- c02adb99 - vkd3d-shader/spirv: Support scalar swizzle of vector SSA registers.
- 3289565a - vkd3d-shader/dxil: Implement the DXIL EXTRACTVAL instruction.
- 167d0400 - vkd3d-shader/spirv: Introduce an FREM instruction.
- 34497dcb - vkd3d-shader/spirv: Introduce an IDIV instruction.
- 8c14ca90 - vkd3d-shader/spirv: Handle the UMUL instruction.
- c3b9d2f7 - vkd3d-shader/spirv: Support VKD3D_DATA_UINT in spirv_compiler_emit_neg().
- 05f2e533 - vkd3d-shader/dxil: Implement the DXIL BINOP instruction.
Toggle commit listadded 43 commits
-
05f2e533...ecdc3f39 - 38 commits from branch
wine:master
- df4104c5 - vkd3d-shader/spirv: Introduce an FREM instruction.
- e6fcdfda - vkd3d-shader/spirv: Introduce an IDIV instruction.
- bbca7fee - vkd3d-shader/spirv: Handle the UMUL instruction.
- 24dfdc48 - vkd3d-shader/spirv: Support VKD3D_DATA_UINT in spirv_compiler_emit_neg().
- 3bede522 - vkd3d-shader/dxil: Implement the DXIL BINOP instruction.
Toggle commit list-
05f2e533...ecdc3f39 - 38 commits from branch
- Resolved by Giovanni Mascellani
- Resolved by Giovanni Mascellani
- Resolved by Giovanni Mascellani
2839 2840 if (handler_idx == VKD3DSIH_UMUL || handler_idx == VKD3DSIH_UDIV || handler_idx == VKD3DSIH_IDIV) 2841 { 2842 struct vkd3d_shader_dst_param *dst_params = instruction_dst_params_alloc(ins, 2, sm6); 2843 unsigned int index = code != BINOP_UDIV && code != BINOP_SDIV; 2844 2845 dst_param_init(&dst_params[0]); 2846 dst_param_init(&dst_params[1]); 2847 register_init_ssa_scalar(&dst_params[index].reg, a->type, sm6); 2848 dst_params[index ^ 1].reg.type = VKD3DSPR_NULL; 2849 dst->u.reg = dst_params[index].reg; 2850 } 2851 else 2852 { 2853 instruction_dst_param_init_ssa_scalar(ins, sm6); 2854 } - Comment on lines +2840 to +2854
This really makes me think that it's time to unlock VSIR from SM4 and let it live its own life. We shouldn't block the MR on this, sure, and separating VSIR and SM4 will probably be a pain because of the disassembler, but I don't think the disassembler should be the main decision factor for an IR.
added 1 commit
- 16d91536 - vkd3d-shader/dxil: Implement the DXIL BINOP instruction.
This really makes me think that it's time to unlock VSIR from SM4 and let it live its own life. We shouldn't block the MR on this, sure, and separating VSIR and SM4 will probably be a pain because of the disassembler, but I don't think the disassembler should be the main decision factor for an IR.
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.
In a similar vein, I didn't add a
DREM
to go withFREM
. Separate opcodes fordouble
are redundant because we have the register data types. I'm not planning on addinghalf
variants for native 16-bit support either. To keep things consistent I think we should normalise all TPFdouble
variant opcodes tofloat
opcodes, and validate the register data types.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.
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?
added 1 commit
- 3e8621da - vkd3d-shader/dxil: Implement the DXIL BINOP instruction.
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.
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 alternative I was considering would rather be to have only one IR, and use it for all shader language conversions, but not for disassembling. And then, for each language for which we support disassembling, have a dedicated disassembler (which probably doesn't really need an IR: it can emit as it parses). If we supported assembling too, we'd also have a dedicated assembler.
In my mind, assembling/disassembling and compiling (or transpiling, if we want to look more modern!) are two different beasts. For compiling it's useful to have an IR which is flexible and simple, but it doesn't need to faithfully represent precisely all the features of any other language. OTOH for assembling/disassembling you don't really care about flexibility, but it's important to represent faithfully every detail of the language.
My feeling is that trying to shove all these features (simplicity, flexibility, faithfullness to any language) on a single language is a bit overconstraining. Write dedicated assemblers and disassemblers is some additional work too, but I'm not sure the balance is in favor of our solution.
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.
If VSIR features are useful for translating between languages, then I agree it's sensible to have then. The part I don't like is having features only because some of the languages we support need to faithfully represent all their features (e.g., having to keep operations like NEG and ABS as register modifiers instead of as regular operators).
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.
I agree that finding the right balance is not easy here. But I can't help thinking our current situation is not ideal.
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.
I don't know much about gap buffers, but after some reading on Wikipedia I'm not convinced. It seems that a gap buffers makes sense when you have a concept of a cursor that mostly moves locally, while our passes usually scan the whole program each time. With a gap buffer you would end up copying the whole program each time, and by that token you could directly rewrite it in a new array each time. Also, random insertion with gap buffers seems to be comparable to arrays.
Following some link on Wikipedia, a rope might be a better match for us, being essentially a compromise between an array and a link-based structure.