Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SPIR-V] HLSL version of GL_EXT_spirv_intrinsics #3919

Closed
jaebaek opened this issue Aug 30, 2021 · 8 comments
Closed

[SPIR-V] HLSL version of GL_EXT_spirv_intrinsics #3919

jaebaek opened this issue Aug 30, 2021 · 8 comments
Assignees
Labels
spirv Work related to SPIR-V

Comments

@jaebaek
Copy link
Collaborator

jaebaek commented Aug 30, 2021

Similar to KhronosGroup/glslang#2625, we allow HLSL shader developers to test arbitrary SPIR-V code (I want to call it "inlined SPIR-V").

New intrinsics, types and attributes

  • vk::ext_execution_mode(uint execution_mode, ...);
    • Stand-alone intrinsic function to emit an OpExecutionMode.
    • To specify extensions and/or capabilities needed by the OpExecutionMode, we have to use vk::ext_extension(string) and vk::ext_capability(uint) attributes (see below).
    • Return type: void
    • uint execution_mode must be a constant expression.
    • Extra parameters must be constant expressions. Some execution modes e.g., Invocations, LocalSize, .. have extra parameters. Since extra parameters of OpExecutionMode are literals, DXC will emit these extra parameters as literals as well.
  • [[vk::ext_extension(string extension_name)]]
    • An attribute to specify OpExtension instruction.
    • It can be an attribute of vk::ext_execution_mode(...), vk::ext_execution_mode_id(...), a function declared with vk::ext_instruction(...), or a function declared with [[vk::ext_type_def(...)]]. We can use multiple [[vk::ext_extension(string extension_name)]] for a single function.
    • string extension_name must be a constant string.
  • [[vk::ext_capability(uint capability)]]
    • An attribute to specify OpCapability instruction.
    • It can be an attribute of vk::ext_execution_mode(...), vk::ext_execution_mode_id(...), a function declared with vk::ext_instruction(...), or a function declared with [[vk::ext_type_def(...)]]. We can use multiple [[vk::ext_extension(string extension_name)]] for a single function.
    • uint capability must be a constant expression.

Example:

// HLSL
[[vk::ext_capability(/* StencilExportEXT */ 5013)]]
[[vk::ext_extension("SPV_EXT_shader_stencil_export")]]
vk::ext_execution_mode(/* StencilRefReplacingEXT */ 5027);

// SPIR-V
OpCapability StencilExportEXT
...
OpExtension "SPV_EXT_shader_stencil_export"
...
OpExecutionMode %main StencilRefReplacingEXT
  • vk::ext_execution_mode_id(uint execution_mode, ...);
    • Stand-alone intrinsic function to emit an OpExecutionModeId.
    • To specify extensions and/or capabilities needed by the OpExecutionModeId, we have to use vk::ext_extension(string) and vk::ext_capability(uint) attributes.
    • Return type: void
    • uint execution_mode must be a constant expression.
    • Extra parameters must be constant expressions. Some execution modes e.g., LocalSizeId have extra id parameters. Since they must be result ids of instructions, DXC will generate OpConstantXXX instructions for them.
  • [[vk::ext_instruction(uint opcode, string extended_instruction_set)]]
    return_type mock_function(parameters, …, [[vk::ext_reference]] parameter, … , [[vk::ext_literal]] parameter …);
    • [[vk::ext_instruction(uint opcode, string extended_instruction_set)]]
      • An attribute to specify that the function declaration with this attribute will be used as a SPIR-V instruction.
      • To specify extensions and/or capabilities needed by the SPIR-V instruction, we have to use vk::ext_extension(string) and vk::ext_capability(uint) attributes.
      • uint opcode must be a constant expression.
      • string extended_instruction_set is optional and it must be a constant string.
    • [[vk::ext_reference]]
      • If a parameter has a [[vk::ext_reference]] attribute, we use the pointer as the operand of SPIR-V instruction instead of loading it and using the value as the operand.
    • [[vk::ext_literal]]
      • If a parameter has an attribute [[vk::ext_literal]], we use it in a literal form as the operand of SPIR-V instruction instead of a result id of a SPIR-V instruction.
    • Parameter without [[vk::ext_reference]] and [[vk::ext_literal]] attributes
      • If it is a variable, we load it using OpLoad and use the loaded value as the operand of the SPIR-V instruction.
      • If it is a constant, we create OpConstant and use it as the operand.
      • If it is a SPIR-V result id whose type is vk::ext_result_id<T> (we will explain vk::ext_result_id<T> below), we use the SPIR-V result id as the operand.
    • Return type.
      • If the return type is void, the SPIR-V instruction will not have a result id and a result type.
      • If the return type is not void e.g., int, float, …, the SPIR-V instruction will have a result id. The result type will be the same as the return type.
      • If the caller of the mock_function is used for l-value e.g., variable initialization or function argument passing, we will generate an OpStore to store the result of the SPIR-V instruction in the l-value e.g., variable.
      • If the mock_function is used to initialize a variable with vk::ext_result_id<T> type, we do not generate an OpStore, but set the variable with vk::ext_result_id<T> type as the result id of the SPIR-V instruction. In addition, the result type of the SPIR-V instruction will be T that is the template argument of vk::ext_result_id<T>.
        • It is particularly useful when we want to use a result id of the SPIR-V instruction for the operand of another SPIR-V instruction declared by a mock function with [[vk::ext_instruction(...)]] attribute.
  • vk::ext_result_id<T>
    • A type that can be used only for the result or a parameter of a function with [[vk::ext_instruction(uint opcode, string extended_instruction_set)]] attribute
    • A variable defined with vk::ext_result_id<T> type does not have a physical storage. It becomes the result id of the SPIR-V instruction whose result type is T.

Example:

// HLSL
[[vk::ext_capability(/* ShaderClockKHR */ 5055)]]
[[vk::ext_extension("SPV_KHR_shader_clock")]]
[[vk::ext_instruction(5056)]]
uint64_t readClock(uint scope);
...
uint64_t clock = readClock(1);

// SPIR-V
OpCapability ShaderClockKHR
...
OpExtension "SPV_KHR_shader_clock"
...
%clock = OpVariable %_ptr_Function_uint64 Function
%result_id = OpReadClockKHR %uint64 %uint_1
OpStore %clock %result_id


// HLSL
[[vk::ext_instruction(/* OpLoad */ 61)]]
vk::ext_result_id<float> load([[vk::ext_reference]] float pointer,
                         [[vk::ext_literal]] int memoryOperands);

[[vk::ext_instruction(/* OpStore */ 62)]]
void store([[vk::ext_reference]] float pointer,
           vk::ext_result_id<float> value,
           [[vk::ext_literal]] int memoryOperands)
...
float foo, bar;
vk::ext_result_id<float> foo_value = load(foo, /* None */ 0x0);
store(bar, foo_value, /* Volatile */ 0x1);

// SPIR-V
%foo = OpVariable %_ptr_Function_float Function
%bar = OpVariable %_ptr_Function_float Function
%foo_value = OpLoad %float %foo None
OpStore %bar %foo_value Volatile


// HLSL
[[vk::ext_instruction(/* FMin3AMD */ 1,
                      "SPV_AMD_shader_trinary_minmax")]]
float2 FMin3AMD(float2 x, float2 y, float2 z);
...
float2 foo = FMin3AMD(x, y, z);

// SPIR-V
%30 = OpExtInstImport "SPV_AMD_shader_trinary_minmax"
...
%foo = OpVariable %_ptr_Function_float Function
...
         %26 = OpLoad %v2float %x
         %27 = OpLoad %v2float %y
         %28 = OpLoad %v2float %z
         %29 = OpExtInst %v2float %30 FMin3AMD %26 %27 %28
               OpStore %foo %29
  • [[vk::ext_type_def(uint unique_type_id, uint opcode)]]
    void createTypeXYZ(parameters, …, [[vk::ext_reference]] parameter, … , [[vk::ext_literal]] parameter …);
    • [[vk::ext_type_def(uint unique_type_id, uint opcode)]] is an attribute similar to [[vk::ext_instruction(...)]], but it specifies that a function declaration will be used to define a type with opcode.
    • The function declared with [[vk::ext_type_def(..)]] must have a void return type.
    • After declaring the function with [[vk::ext_type_def(..)]], we have to call the declared function e.g., void createTypeXYZ(..) with proper arguments to actually define the type.
    • uint unique_type_id can be any constant unsigned integer number, but it must be a unique identifier for the defined type. It will be used by vk::ext_type (see below).
    • uint opcode must be the opcode of the type instruction.
    • Parameters for the declared function are extra operands to define the type.
  • vk::ext_type
    • A special type that specifies the type created by a function declared with [[vk::ext_type_def(..)]].
    • uint unique_type_id must be the same as the one of [[vk::ext_type_def(uint unique_type_id, uint opcode)]].

Example:

// HLSL
[[vk::ext_type_def(/* Unique id for type */ 0, /* OpTypeInt */ 21)]]
void createTypeInt([[vk::ext_literal]] int sizeInBits,
                   [[vk::ext_literal]] int signedness);
createTypeInt(/* sizeInBits */ 12, /* signedness */ 0);
...
vk::ext_type</* Unique id for type */ 0> foo = 3;

// SPIR-V
%int_in_12bits = OpTypeInt 12 0
%_ptr_Function_int_in_12bits = OpTypePointer Function %int_in_12bits
%i3_in_12bits = OpConstant %int_in_12bits 3
...
%foo = OpVariable %_ptr_Function_int_in_12bits Function
OpStore %foo %i3_in_12bits


// HLSL
[[vk::ext_type_def(/* Unique id for type */ 0, /* OpTypeInt */ 21)]]
void createTypeInt([[vk::ext_literal]] int sizeInBits,
                   [[vk::ext_literal]] int signedness);
createTypeInt(/* sizeInBits */ 12, /* signedness */ 0);
...
[[vk::ext_type_def(/* Unique id for type */ 1, /* OpTypeVector */ 23)]]
void createTypeVector([[vk::ext_reference]] vk::ext_type<0> typeInt,
                      [[vk::ext_literal]] int componentCount);
createTypeVector(/* typeInt */ {}, /* componentCount */ 3);
...
vk::ext_type</* Unique id for type */ 1> foo = {1, 2, 3};

// SPIR-V
%int_in_12bits = OpTypeInt 12 0
%vint3_in_12bits = OpTypeVector %int_in_12bits 3
%_ptr_Function_vint3_in_12bits = OpTypePointer Function %vint3_in_12bits
%i1_in_12bits = OpConstant %int_in_12bits 1
%i2_in_12bits = OpConstant %int_in_12bits 2
%i3_in_12bits = OpConstant %int_in_12bits 3
%i123_in_12bits = OpConstant %vint3_in_12bits %i1_in_12bits
                                              %i2_in_12bits
                                              %i3_in_12bits
...
%foo = OpVariable %_ptr_Function_vint3_in_12bits Function
OpStore %foo %i123_in_12bits


// HLSL
[[vk::ext_capability(/* RayQueryKHR */ 4472)]]
[[vk::ext_extension("SPV_KHR_ray_query")]]
[[vk::ext_type_def(/* Unique id for type */ 2,
                   /* OpTypeRayQueryKHR */ 4472)]]
void createTypeRayQueryKHR();
createTypeRayQueryKHR();
...
[[vk::ext_instruction(/* OpRayQueryTerminateKHR */ 4474)]]
void rayQueryTerminateEXT(
       [[vk::ext_reference]] vk::ext_type(typeRayQueryKHR) rq);
...
vk::ext_type</* Unique id for type */ 2> rq;
rayQueryTerminateEXT(rq);

// SPIR-V
OpCapability RayQueryKHR
...
OpExtension "SPV_KHR_ray_query"
...
%typeRayQueryKHR = OpTypeRayQueryKHR
...
%rq = OpVariable %_ptr_Function_typeRayQueryKHR Function
OpRayQueryTerminateKHR %rq
  • [[vk::ext_storage_class(uint storage_class)]] type
    • An attribute that must be added to a type to specify the storage class.

Example:

// HLSL
[[vk::ext_storage_class(/* CrossWorkgroup */ 5)]] int foo;

// SPIR-V
%foo = OpVariable %_ptr_CrossWorkgroup_int CrossWorkgroup
  • [[vk::ext_decorate(decoration, … int / float / bool literal)]]
    • An attribute for a decoration of a variable. It takes the first integer parameter as its Decoration operand, and further parameters as Extra Operands. If it is used for a member of a struct/class type, OpMemberDecorate will be generated to decorate the member. Otherwise, OpDecorate will be generated to decorate the variable.
  • [[vk::ext_decorate_id(decoration, … constant expression)]]
    • Similar to [[vk::ext_decorate(decoration, … int / float / bool literal)]] but it generates OpDecorateId
  • [[vk::ext_decorate_string(decoration, … string literals)]]
    • Similar to [[vk::ext_decorate(decoration, … int / float / bool literal)]] but it generates OpDecorateString or OpMemberDecorateString.
@jaebaek
Copy link
Collaborator Author

jaebaek commented Aug 30, 2021

We have to implement each of

  • vk::ext_execution_mode(uint execution_mode);
  • [[vk::ext_extension(string extension_name)]]
  • [[vk::ext_capability(uint capability)]]
  • vk::ext_execution_mode_id(uint execution_mode);
  • [[vk::ext_instruction(uint opcode, string extended_instruction_set)]]
  • [[vk::ext_reference]]
  • [[vk::ext_literal]]
  • vk::ext_result_id
  • [[vk::ext_type_def(uint unique_type_id, uint opcode)]]
  • vk::ext_type
  • [[vk::ext_storage_class(uint storage_class)]]
  • [[vk::ext_decorate(decoration, … int / float / bool literal)]]
  • [[vk::ext_decorate_id(decoration, … constant expression)]]
  • [[vk::ext_decorate_string(decoration, … string literals)]]

@jaebaek
Copy link
Collaborator Author

jaebaek commented Aug 30, 2021

@jiaolu We can implement each of the above function / attribute / type one by one. I prefer separate PRs if it is possible.

We would appreciate your contributions. I will also try to send some PRs and add you for code review.

@jaebaek jaebaek added the spirv Work related to SPIR-V label Aug 30, 2021
@jiaolu
Copy link
Contributor

jiaolu commented Aug 30, 2021

@jiaolu We can implement each of the above function / attribute / type one by one. I prefer separate PRs if it is possible.

We would appreciate your contributions. I will also try to send some PRs and add you for code review.

I did not find self-assign butten, can you assign the issue to me, thanks

jiaolu added a commit to jiaolu/DirectXShaderCompiler that referenced this issue Sep 14, 2021
Related to the issue: microsoft#3919
Add these attributes

vk::ext_capability
vk::ext_extension
vk::ext_instruction
@jaebaek
Copy link
Collaborator Author

jaebaek commented Sep 17, 2021

Oops. I made a mistake that does affect the implementation of this spec, but my explanation

  • [[vk::ext_reference]]
    - In DXC, if we pass a variable as an argument of a function, we first load its value using OpLoad and pass its value.
    - If a parameter has a [[vk::ext_reference]] attribute, we use the pointer as the operand of SPIR-V instruction instead of loading it and using the value as the operand.
  • [[vk::ext_literal]]
    - In DXC, if we pass a constant (e.g., 1) as an argument of a function, DXC creates OpConstant and passes it.
    - If a parameter has an attribute [[vk::ext_literal]], we use it in a literal form as the operand of SPIR-V instruction instead of using OpConstant as the operand.

is wrong in terms of the existing DXC's behavior for argument passing.
For the call-by-value argument passing, DXC loads the value for the argument and stores it into a temporary OpVariable and passes the variable's pointer to the function.

Let me update it as:

  • [[vk::ext_reference]]
    • If a parameter has a [[vk::ext_reference]] attribute, we use the pointer as the operand of SPIR-V instruction instead of loading it and using the value as the operand.
  • [[vk::ext_literal]]
    • If a parameter has an attribute [[vk::ext_literal]], we use it in a literal form as the operand of SPIR-V instruction instead of a result id of a SPIR-V instruction.

jiaolu added a commit to jiaolu/DirectXShaderCompiler that referenced this issue Sep 18, 2021
Related to the issue: microsoft#3919
Add these attributes

vk::ext_capability
vk::ext_extension
vk::ext_instruction
jiaolu added a commit to jiaolu/DirectXShaderCompiler that referenced this issue Sep 24, 2021
Related to the issue: microsoft#3919
Add these attributes

vk::ext_capability
vk::ext_extension
vk::ext_instruction
jaebaek added a commit that referenced this issue Sep 24, 2021
[SPIRV] Add support of the GL_EXT_spirv_intrinsics

Related to the issue: #3919
Add these attributes

vk::ext_capability
vk::ext_extension
vk::ext_instruction
vk::ext_reference
vk::ext_literal

Note this commit allows the redeclaration of a HLSL intrinsic function using a function declaration with `vk::ext_instruction`.

Co-authored-by: Jaebaek Seo <jaebaek@google.com>
jiaolu added a commit to jiaolu/DirectXShaderCompiler that referenced this issue Oct 25, 2021
related to
issue:microsoft#3919

Add support to these two attributes:
 [[vk::ext_decorate(decoration, … int literal)]]
 [[vk::ext_decorate_string(decoration, … string literals)]]
jaebaek pushed a commit that referenced this issue Oct 29, 2021
related to
issue:#3919

Add support to these two attributes:
 [[vk::ext_decorate(decoration, … int literal)]]
 [[vk::ext_decorate_string(decoration, … string literals)]]
@yuriy-odonnell-epic
Copy link
Contributor

Hi, it may be necessary to add a mechanism to set OpMemoryModel. As far as I can tell, ext_instruction is not able to express it right now.

The exact use case I was hoping to achieve using SPV intrinsics is buffer_reference support, which might also need other things like OpTypeForwardPointer, OpAccessChain, OpLoad ... Aligned N, etc.

@jiaolu
Copy link
Contributor

jiaolu commented Nov 8, 2021

@jaebaek , a new PR with vk::ext_type_def

not sure how to implement vk::ext_type with vk as namespace.

I tried belowing code and did not work

======================================================================
recordDecl = DeclareUIntTemplatedTypeWithHandle(*m_context, "ext_type", "id");
recordDecl->setDeclContext(m_vkNSDecl);
recordDecl->setLexicalDeclContext(m_vkNSDecl);

======================================================================
when m_vkNSDecl is created before AddObjectTypes()
so for now i just use ext_type as type name in hlsl.

jiaolu added a commit to jiaolu/DirectXShaderCompiler that referenced this issue Nov 18, 2021
[[vk::ext_storage_class(uint storage_class)]]
this is part of PRs for the
microsoft#3919
jaebaek pushed a commit that referenced this issue Nov 18, 2021
Add support
* vk::ext_execution_mode
* [[vk::ext_storage_class(uint storage_class)]]

This is part of PRs for the
#3919
jiaolu added a commit to jiaolu/DirectXShaderCompiler that referenced this issue Nov 24, 2021
jaebaek added a commit that referenced this issue Nov 29, 2021
Support [[vk::ext_type_def]] and vk::ext_type.
This is related
#3919

Co-authored-by: Jaebaek Seo <jaebaek@google.com>
jaebaek added a commit to jaebaek/DirectXShaderCompiler that referenced this issue Jan 18, 2022
As a part of HLSL version of GL_EXT_spirv_intrinsics, this commit adds
vk::ext_decorate_id attribute.

Related to microsoft#3919
jaebaek added a commit to jaebaek/DirectXShaderCompiler that referenced this issue Jan 18, 2022
As a part of HLSL version of GL_EXT_spirv_intrinsics, this commit adds
vk::ext_result_id<T> type. We must use it for a variable definition or
a function parameter. It means we do not consider it as a physical
storage. Instead, it will be a result id of the instruction.

Related to microsoft#3919
@jaebaek
Copy link
Collaborator Author

jaebaek commented Jan 18, 2022

jaebaek added a commit that referenced this issue Jan 19, 2022
As a part of HLSL version of GL_EXT_spirv_intrinsics, this commit adds
vk::ext_result_id<T> type. We must use it for a variable definition or
a function parameter. It means we do not consider it as a physical
storage. Instead, it will be a result id of the instruction.

Related to #3919
jaebaek added a commit that referenced this issue Jan 21, 2022
As a part of HLSL version of GL_EXT_spirv_intrinsics, this commit adds
`vk::ext_execution_mode_id(..)` intrinsic function. In addition, it allows users
to enable capabilites and extensions via `vk::ext_execution_mode[_id](..)`
using `[[vk::ext_capability(..)]]` and `[[vk::ext_extension(..)]]`.

Related to #3919
jaebaek added a commit that referenced this issue Jan 21, 2022
As a part of HLSL version of GL_EXT_spirv_intrinsics, this commit adds
vk::ext_decorate_id attribute.

Related to #3919
@jaebaek
Copy link
Collaborator Author

jaebaek commented Jan 21, 2022

Now we finished the initial implementation of this feature and added a wiki page. Let me close this issue. Please open separate issues for the maintenance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
spirv Work related to SPIR-V
Projects
None yet
Development

No branches or pull requests

3 participants