Open afd opened 3 years ago
@Mostafa-ashraf19 This would be a good one for you to work on in due course.
The idea is to create a transformation that takes the current value of memory semantics for specific atomic instruction then changes it stronger memory order with a range of 5 bits of values.
The initial structure of Transformation Changing Memory Semantics, the structure of the Protobuf
following by SPIR-V
assembly example, before and after the transformation.
message TransformationChangingMemorySemantics {
// This transformation is responsible for changing the mask of memory
// semantics with a range of 5 bits of values.
// The id of specific atomic instruction.
uint32 pointer_id = 1;
// The new value added.
uint32 memory_semantics_new_value_1 = 2;
// The n2w value-added for atomic instructions that's takes 2 memory
// semantics values, e.g. OpAtomicCompareExchange.
uint32 memory_semantics_new_value_2 = 3;
}
SPIR-V example
const std::string shader = R"(
OpCapability Shader
OpCapability Int8
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeInt 8 1
%9 = OpTypeInt 32 0
%26 = OpTypeFloat 32
%8 = OpTypeStruct %6
%10 = OpTypePointer StorageBuffer %8
%11 = OpVariable %10 StorageBuffer
%19 = OpConstant %26 0
%18 = OpConstant %9 1
%12 = OpConstant %6 0
%13 = OpTypePointer StorageBuffer %6
%15 = OpConstant %6 4
%16 = OpConstant %6 7
%17 = OpConstant %7 4
%20 = OpConstant %6 4 ; Release value
%4 = OpFunction %2 None %3
%5 = OpLabel
%14 = OpAccessChain %13 %11 %12
%21 = OpAtomicLoad %6 %14 %15 %20
%24 = OpAccessChain %13 %11 %12
OpReturn
OpFunctionEnd
)";
const std::string after_transformation = R"(
OpCapability Shader
OpCapability Int8
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeInt 8 1
%9 = OpTypeInt 32 0
%26 = OpTypeFloat 32
%8 = OpTypeStruct %6
%10 = OpTypePointer StorageBuffer %8
%11 = OpVariable %10 StorageBuffer
%19 = OpConstant %26 0
%18 = OpConstant %9 1
%12 = OpConstant %6 0
%13 = OpTypePointer StorageBuffer %6
%15 = OpConstant %6 4
%16 = OpConstant %6 7
%17 = OpConstant %7 4
%20 = OpConstant %6 8 ; AcquireRelease value
%4 = OpFunction %2 None %3
%5 = OpLabel
%14 = OpAccessChain %13 %11 %12
%21 = OpAtomicLoad %6 %14 %15 %20
%24 = OpAccessChain %13 %11 %12
OpReturn
OpFunctionEnd
)";
ping @afd, @paulthomson. What do you think @paulthomson?
Nice thanks. This is helpful, as there are definitely some changes needed, and we can iterate quickly here.
This transformation should target an atomic instruction like OpAtomicLoad, OpAtomicStore, OpControlBarrier, OpMemoryBarrier, etc. And the transformation should change the operand that is used to a different id (that is a constant), not the OpConstant value itself because the OpConstant could be used by other instructions. So, for example:
%21 = OpAtomicLoad %6 %14 %15 %20 -> %21 = OpAtomicLoad %6 %14 %15 %100
Note that many of the atomic instructions do not have result id, so it can be difficult to refer to these instructions. This is why we have the InstructionDescriptor type. So use InstructionDescriptor to specify which atomic instruction the transformation will change.
The idea is to create a transformation that takes the current value of memory semantics for specific atomic instruction then changes it stronger memory order with a range of 5 bits of values.
The initial structure of Transformation Changing Memory Semantics, the structure of the Protobuf
following by SPIR-V
assembly example, before and after the transformation.
message TransformationChangingMemorySemantics {
// This transformation is responsible for changing the mask of memory
// semantics with a range of 5 bits of values.
// The atomic instruction we are looking for.
InstructionDescriptor atomic_instruction = 1;
// The operand index needs to change, 0 for the first memory semantics operand, 1 for the second memory
// semantics operand(if found).
uint32 memory_semantics_operand_index = 2;
// The id of new value.
uint32 memory_semantics_new_value_id = 3;
}
SPIR-V example
const std::string shader = R"(
OpCapability Shader
OpCapability Int8
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeInt 8 1
%9 = OpTypeInt 32 0
%26 = OpTypeFloat 32
%8 = OpTypeStruct %6
%10 = OpTypePointer StorageBuffer %8
%11 = OpVariable %10 StorageBuffer
%19 = OpConstant %26 0
%18 = OpConstant %9 1
%12 = OpConstant %6 0
%13 = OpTypePointer StorageBuffer %6
%15 = OpConstant %6 4
%16 = OpConstant %6 7
%17 = OpConstant %7 4
%20 = OpConstant %9 4
%21 = OpConstant %6 4
%100 = OpConstant %6 8 ; AcquireRelease value
%4 = OpFunction %2 None %3
%5 = OpLabel
%14 = OpAccessChain %13 %11 %12
%24 = OpAccessChain %13 %11 %12
OpAtomicStore %14 %15 %20 %21
OpReturn
OpFunctionEnd
)";
const std::string after_transformation = R"(
OpCapability Shader
OpCapability Int8
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main"
OpExecutionMode %4 OriginUpperLeft
OpSource ESSL 320
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%6 = OpTypeInt 32 1
%7 = OpTypeInt 8 1
%9 = OpTypeInt 32 0
%26 = OpTypeFloat 32
%8 = OpTypeStruct %6
%10 = OpTypePointer StorageBuffer %8
%11 = OpVariable %10 StorageBuffer
%19 = OpConstant %26 0
%18 = OpConstant %9 1
%12 = OpConstant %6 0
%13 = OpTypePointer StorageBuffer %6
%15 = OpConstant %6 4
%16 = OpConstant %6 7
%17 = OpConstant %7 4
%20 = OpConstant %9 4
%21 = OpConstant %6 4
%100 = OpConstant %6 8 ; AcquireRelease value
%4 = OpFunction %2 None %3
%5 = OpLabel
%14 = OpAccessChain %13 %11 %12
%24 = OpAccessChain %13 %11 %12
OpAtomicStore %14 %15 %100 %21
OpReturn
OpFunctionEnd
)";
What do you think @paulthomson?
Looks good to me!
This transformation (and fuzzer pass) should strengthen the memory semantics of atomic operations and barrier operations.
For example: a relaxed load can be changed to an acquire load; a release store can be changed to a sequentially consistent store.