Page 2 of 4 FirstFirst 1234 LastLast
Results 11 to 20 of 39

Thread: Feedback: New SPIR-V common intermediate language used by both OpenCL 2.1 and Vulkan

  1. #11
    Senior Member
    Join Date
    Mar 2015
    Posts
    179
    I do got a question how are undefined intermediates handled (result of opUndef)?

    Is it by default that any result of an opCode using an undefined intermediate is also undefined except things like opSelect?

    Then how is opCompositeInsert handled with an undefined composite, an entirely undefined result or is the inserted portion defined while the rest remains undefined?

  2. #12
    Junior Member
    Join Date
    Nov 2012
    Posts
    13
    Writing my decoder noticed that some constant fields are masked fields. For example with Function Control Mask. I can do (Pure | Const), but it may be better if I could just treat it as a set: {"Pure", "Const"}

    Could you clearly tell when a constant field can mask in the specification, so they can be appropriately treated?
    Last edited by cheery; 03-13-2015 at 09:36 AM.

  3. #13
    Senior Member
    Join Date
    Mar 2015
    Posts
    179
    Why is the default multi-variable return handled by variable parameters instead of structures? Like the modf function of the glsl extension, why not make the result type a 2-member structure where the members must be the same type as x? The return by pointer stems from the olden C days where returning structs was not allowed in the spec these days it's not necessary at all; the result will probably be put into 2 registers anyway and a bad optimizer will then need to store one into memory and when used load it in again.

    Using a structure return would be much cleaner and more straightforward in use and closer to the metal anyway.

  4. #14
    It's probably because returning by structure requires that this structure be defined somewhere. So... how does that happen, exactly? Who defines it?

    modf, for example, is allowed to take any numeric type. And its return type is the exact same type. Since there can be lots and lots of numeric types (various bit-sizes), and each type is considered to be different, there would need to be lots and lots of structs for them.

    Where do they come from? Where is the Result<id> that declared these types?

    No, it's much simpler for opcode return values to happen as values, rather than via objects like this.

    As to whether it would be "closer to the metal"... I fail to see how. It's not like registers are organized into structs or something. In fact, I wonder if assembly languages even have a direct opcode equivalent for "modf". I'd guess (admittedly based on nothing specific) that this function would be implemented as two distinct opcodes: one for the division and one for the remainder. And if you don't use one or the other, that opcode can be culled during optimization.

    If that's the case, it's much easier for the compiler to see whether you're using a register than whether you're using a particular member of a structure. Not that much easier probably, but it's certainly more explicitly visible when such a value has gone unused.

  5. #15
    Senior Member
    Join Date
    Mar 2015
    Posts
    179
    Quote Originally Posted by Alfonse Reinheart View Post
    It's probably because returning by structure requires that this structure be defined somewhere. So... how does that happen, exactly? Who defines it?

    modf, for example, is allowed to take any numeric type. And its return type is the exact same type. Since there can be lots and lots of numeric types (various bit-sizes), and each type is considered to be different, there would need to be lots and lots of structs for them.

    Where do they come from? Where is the Result<id> that declared these types?

    No, it's much simpler for opcode return values to happen as values, rather than via objects like this.

    As to whether it would be "closer to the metal"... I fail to see how. It's not like registers are organized into structs or something. In fact, I wonder if assembly languages even have a direct opcode equivalent for "modf". I'd guess (admittedly based on nothing specific) that this function would be implemented as two distinct opcodes: one for the division and one for the remainder. And if you don't use one or the other, that opcode can be culled during optimization.

    If that's the case, it's much easier for the compiler to see whether you're using a register than whether you're using a particular member of a structure. Not that much easier probably, but it's certainly more explicitly visible when such a value has gone unused.
    With the structure output the optimizer just has to look for the opCompositeExtract on the result and see if any use index 0 (for the frac part) or 1 (for the integral part). Otherwise it has to look for the opLoad using the output parameter. There will be more opLoads than opCompositeExtracts in the average program.

    the modf direct opcode would use a single input register and 2 output registers. The output pointer would necessarily follow that up with a opStore. By that point all structures would be flattened anyway and the assembler can consider the extra output thrashed.

    Currently modf requires "Result Type, the type of x, and the type i points to must all be the same type" So the type of i must be the opTypePointer of the opType of x. similarly you can require result to be the "OpTypeStruct a,a" where a is the opType of x.

  6. #16
    Wait, I'm now confused as to what you're wanting, because you seem to be talking about different things. First, you asked about opcodes like modf returning structs. Now, you want to change the language to allow opcodes to have multiple return values (your "2 output registers" bit). This is a very different thing, which would require not only a significant rework of the language (extension opcode return values are part of the extension instruction), but a significant rework of drivers that are already being written.

    I don't think it's worth it. Either of them.

    And I really don't see the point. Even you point out that, while it won't hurt optimizers, it won't help them either. So what's the point? This seems to be a feature that does nothing but make the language easier for a human to use. But humans aren't supposed to write SPIR-V, so the need for such a feature is... dubious.

  7. #17
    Senior Member
    Join Date
    Mar 2015
    Posts
    179
    Quote Originally Posted by Alfonse Reinheart View Post
    Wait, I'm now confused as to what you're wanting, because you seem to be talking about different things. First, you asked about opcodes like modf returning structs. Now, you want to change the language to allow opcodes to have multiple return values (your "2 output registers" bit). This is a very different thing, which would require not only a significant rework of the language (extension opcode return values are part of the extension instruction), but a significant rework of drivers that are already being written.

    I don't think it's worth it. Either of them.

    And I really don't see the point. Even you point out that, while it won't hurt optimizers, it won't help them either. So what's the point? This seems to be a feature that does nothing but make the language easier for a human to use. But humans aren't supposed to write SPIR-V, so the need for such a feature is... dubious.
    It's not a feature it's a convention to avoid out-parameters in general.

    If you are going to design an instruction set with a combined round-to-zero/frac instruction (requiring a shift, mask and subtraction) you are not going to design it with an implicit store to memory and have the program risk a cache miss every time it's used.

    This means that some of the first steps in compilation to machine code would be to expand modf to modf + opStore and then hope that the variable opStore-opLoad elimination will be sufficient to find and remove that variable.

    With the structured output from the get-go there is no variable that may survive beyond the block/function unless the program explicitly opStores the result.

  8. #18
    Senior Member
    Join Date
    Mar 2015
    Posts
    179
    Let me rephrase a bit. First a few definitions for clarity: opCode means spir-V opcode, ILcode means a driver-specific intermediate language opcode used for optimizing beyond what you can do with just spir-v (with the assumption that structures in the ILcode are flattened and it only works with scalars or vectors and the modf ILcode only uses registers).

    I want the modf opCode (and the frexp opCode in the openCL extension) to return a struct and avoid the pointer store and later load that the driver needs to optimize out.

    With the current style it means that that the modf opcode will expand to a modf ILcode (which uses 2 output registers) and a store ILcode for the second output then the optimizer will need to eliminate the redundant load of the stored value.

    With the structured output the relevant structures will already be flattened into their individual components in the IL and that the register allocator can have its way with the assigned intermediates.

  9. #19
    With the current style it means that that the modf opcode will expand to a modf ILcode (which uses 2 output registers) and a store ILcode for the second output then the optimizer will need to eliminate the redundant load of the stored value.
    What if the ILcode doesn't have a direct modf ILcode equivalent at all (like LLVM)? It would have to use multiple opcodes anyway.

    Or what if the modf ILcode works exactly like the modf OPcode?

    Or what if the OPcode-to-ILcode translator turns it into a function call? One that returns a value and has a pointer that it can fill in.

    Or what if the OPcode-to-ILcode translator was written by someone who actually did their job correctly and filtered out such minor differences? You know, the kind of thing a translation layer is supposed to do.

    You're talking about a problem that is back-end specific, and easily solvable in those instances when it occurs.

  10. #20
    Senior Member
    Join Date
    Mar 2015
    Posts
    179
    Quote Originally Posted by Alfonse Reinheart View Post
    What if the ILcode doesn't have a direct modf ILcode equivalent at all (like LLVM)? It would have to use multiple opcodes anyway.
    modf can be emulated with a trunc and a subtract. frexp require a bitcast a mask and some shifts (plus a opStore for both in its current form).

    But there still shouldn't be a store needed to get the values
    Quote Originally Posted by Alfonse Reinheart View Post
    Or what if the modf ILcode works exactly like the modf OPcode?
    Then it's no trouble adding the opload
    Quote Originally Posted by Alfonse Reinheart View Post
    Or what if the OPcode-to-ILcode translator turns it into a function call? One that returns a value and has a pointer that it can fill in.
    That function should be changed to return a struct and would then get inlined
    Quote Originally Posted by Alfonse Reinheart View Post
    Or what if the OPcode-to-ILcode translator was written by someone who actually did their job correctly and filtered out such minor differences? You know, the kind of thing a translation layer is supposed to do.
    w00t one guy did his job correctly, now for all the other implementations
    Quote Originally Posted by Alfonse Reinheart View Post
    You're talking about a problem that is back-end specific, and easily solvable in those instances when it occurs.
    But it still puts pressure on the optimizers to get it correct.

Page 2 of 4 FirstFirst 1234 LastLast

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Proudly hosted by Digital Ocean