<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/99129>99129</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
Implement the `InterlockedExchange` HLSL Function
</td>
</tr>
<tr>
<th>Labels</th>
<td>
metabug,
backend:DirectX,
HLSL,
backend:SPIR-V,
bot:HLSL
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
farzonl
</td>
</tr>
</table>
<pre>
- [ ] Implement `InterlockedExchange` clang builtin,
- [ ] Link `InterlockedExchange` clang builtin with `hlsl_intrinsics.h`
- [ ] Add sema checks for `InterlockedExchange` to `CheckHLSLBuiltinFunctionCall` in `SemaChecking.cpp`
- [ ] Add codegen for `InterlockedExchange` to `EmitHLSLBuiltinExpr` in `CGBuiltin.cpp`
- [ ] Add codegen tests to `clang/test/CodeGenHLSL/builtins/InterlockedExchange.hlsl`
- [ ] Add sema tests to `clang/test/SemaHLSL/BuiltIns/InterlockedExchange-errors.hlsl`
- [ ] Create the `int_dx_InterlockedExchange` intrinsic in `IntrinsicsDirectX.td`
- [ ] Create the `DXILOpMapping` of `int_dx_InterlockedExchange` to `78` in `DXIL.td`
- [ ] Create the `InterlockedExchange.ll` and `InterlockedExchange_errors.ll` tests in `llvm/test/CodeGen/DirectX/`
- [ ] Create the `int_spv_InterlockedExchange` intrinsic in `IntrinsicsSPIRV.td`
- [ ] In SPIRVInstructionSelector.cpp create the `InterlockedExchange` lowering and map it to `int_spv_InterlockedExchange` in `SPIRVInstructionSelector::selectIntrinsic`.
- [ ] Create SPIR-V backend test case in `llvm/test/CodeGen/SPIRV/hlsl-intrinsics/InterlockedExchange.ll`
## DirectX
| DXIL Opcode | DXIL OpName | Shader Model | Shader Stages |
| ----------- | ----------- | ------------ | ------------- |
| 78 | AtomicBinOp | 6.0 | () |
## SPIR-V
# [OpAtomicExchange](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#OpAtomicExchange):
## Description:
Perform the following steps atomically with respect to any other atomic
accesses within *Scope* to the same location:
1) load through *Pointer* to get an *Original Value*,
2) get a *New Value* from copying *Value*, and
3) store the *New Value* back through *Pointer*.
The instruction’s result is the *Original Value*.
*Result Type* must be a scalar of [*integer type*](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#Integer) or
[*floating-point type*](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#Floating).
The type of *Value* must be the same as *Result Type*. The type of the
value pointed to by *Pointer* must be the same as *Result Type*.
*Memory* is a memory [*Scope*](https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html#Scope_-id-).
<table>
<colgroup>
<col style="width: 12%" />
<col style="width: 12%" />
<col style="width: 12%" />
<col style="width: 12%" />
<col style="width: 12%" />
<col style="width: 12%" />
<col style="width: 12%" />
<col style="width: 12%" />
</colgroup>
<thead>
<tr>
<th>Word Count</th>
<th>Opcode</th>
<th>Results</th>
<th>Operands</th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p>7</p></td>
<td class="tableblock halign-left valign-top"><p>229</p></td>
<td
class="tableblock halign-left valign-top"><p><em><id></em><br />
<em>Result Type</em></p></td>
<td class="tableblock halign-left valign-top"><p><a
href="#ResultId"><em>Result <id></em></a></p></td>
<td
class="tableblock halign-left valign-top"><p><em><id></em><br />
<em>Pointer</em></p></td>
<td class="tableblock halign-left valign-top"><p><a
href="#Scope_-id-"><em>Scope <id></em></a><br />
<em>Memory</em></p></td>
<td class="tableblock halign-left valign-top"><p><a
href="#Memory_Semantics_-id-"><em>Memory Semantics
<id></em></a><br />
<em>Semantics</em></p></td>
<td
class="tableblock halign-left valign-top"><p><em><id></em><br />
<em>Value</em></p></td>
</tr>
</tbody>
</table>
## Test Case(s)
### Example 1
```hlsl
//dxc InterlockedExchange_test.hlsl -T lib_6_8 -enable-16bit-types -O0
RWStructuredBuffer<int64_t> buffer : register(u0);
[numthreads(1, 1, 1)]
export void fn(uint3 dispatchThreadID : SV_DispatchThreadID, int64_t p1, int64_t p2) {
int index = dispatchThreadID.x;
return InterlockedExchange(buffer[index], p1, p2);
}
```
### Example 2
```hlsl
//dxc InterlockedExchange_1_test.hlsl -T lib_6_8 -enable-16bit-types -O0
RWStructuredBuffer<float> buffer : register(u0);
[numthreads(1, 1, 1)]
export void fn(uint3 dispatchThreadID : SV_DispatchThreadID, float p1, float p2) {
int index = dispatchThreadID.x;
return InterlockedExchange(buffer[index], p1, p2);
}
```
### Example 3
```hlsl
//dxc InterlockedExchange_2_test.hlsl -T lib_6_8 -enable-16bit-types -O0
RWStructuredBuffer<int> buffer : register(u0);
[numthreads(1, 1, 1)]
export void fn(uint3 dispatchThreadID : SV_DispatchThreadID, int p1, uint p2) {
int index = dispatchThreadID.x;
return InterlockedExchange(buffer[index], p1, p2);
}
```
## HLSL:
Assigns value to dest and returns the original value.
## Syntax
``` syntax
void InterlockedExchange(
in R dest,
in T value,
out T original_value
);
```
## Parameters
<dl> <dt>
*dest* \[in\]
</dt> <dd>
Type: **R**
The destination address.
</dd> <dt>
*value* \[in\]
</dt> <dd>
Type: **T**
The input value.
</dd> <dt>
*original\_value* \[out\]
</dt> <dd>
Type: **T**
The original value.
</dd> </dl>
## Return value
This function does not return a value.
## Remarks
This operation can only be performed on scalar-typed resources and shared memory variables. There are two possible uses for this function. The first is when R is a shared memory variable type. In this case, the function performs the operation on the shared memory register referenced by dest. The second scenario is when R is a resource variable type. In this scenario, the function performs the operation on the resource location referenced by dest. This operation is only available when R is readable and writable.
Interlocked operations do not imply any memory fence/barrier.
### Minimum Shader Model
This function is supported in the following shader models.
| Shader Model | Supported |
|-----------------------------------------------------------------------------|-----------|
| [Shader Model 5](https://github.com/MicrosoftDocs/win32/blob/docs/desktop-src//direct3dhlsl/d3d11-graphics-reference-sm5.md) and higher shader models | yes |
This function is supported in the following types of shaders:
| Vertex | Hull | Domain | Geometry | Pixel | Compute |
|--------|------|--------|----------|-------|---------|
| x | x | x | x | x | x |
## See also
<dl> <dt>
[Intrinsic Functions](https://github.com/MicrosoftDocs/win32/blob/docs/desktop-src//direct3dhlsl/dx-graphics-hlsl-intrinsic-functions.md)
</dt> <dt>
[Shader Model 5](https://github.com/MicrosoftDocs/win32/blob/docs/desktop-src//direct3dhlsl/d3d11-graphics-reference-sm5.md)
</dt> </dl>
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzsWl1v27jS_jXMDWFDpmrHvsiFY8e7AdptkQTdvQtocSzxDUUKJJXE768_GFKSpcRJmm172osTBLY0HM7HM8OvoblzMtcAZ2R6TqbrE177wtizHbf_b7Q62RqxPxtRMj2nZLqml2WloATtKZkll9qDVSa7A3HxmBVc50BmCc0U1znd1lJ5qQlbkWR5EPBR6rtv7UsfpC-QuVBO3UrtrdROZm5ckFkykLoUgjooOc0KyO4c3Rn7ihJvsHGFrH9-vP54HrVtap15afSKK4VcUiPXNZQ8cEqdj7OqOqY5MwJy0N-k9aKUvqf04rGyB2WrPxryW5o8OO8aiQEywjZII2yzMgL-AI06CNs0SDrCNkfMGiOwL2L5ihJEpdEQLL58ScMIrDXWHVW0ssA9UF8AapDa34rH2xfA64LfIHXZJcNaWsj8P2MvXpe__ufy4-fqE68qqXMUaXZvq_WGItPp_BAjlPO6shdyYBzTimvxAsNtg1Xki-hHnUrdl88iTNim8Z2wzdvYuur-_eBef7m8-nrE20tNQ9Oldt7WYdhcg4LMG4upS7OB-hfUKvMAVuo8QFLyilLpm3R70-AwNF-wgKRLki5deOtcIbNkfAwhFDL6Src8uwMtAuo04w7eQD7oJmyDeT06zEwvDDPV5D7-s5SwlLaRi7TTFcW0op8rHOC09_4XL-P7dcEFWPrJCFB9wrXnOTikNIJGhz_6xvtzwqgn6HQempfelDI7l_pzFd5n4yR8EzYnbNHyd45FPDsSwv25ijK6CE7XhM0L7yuHoWIbwjYWcum83Y_vCmu0cWNj8wbmEeLsKgjo1lruJIhJG4Jx4UtFWPpMB1ug7AHk4DIrK8yU2EQpSZZfwO6MLUOq7oxS5gEz0nmoHOVBJFdqH1ciC2hGyFGu99T4AmzDRJIlzzJwDlzgxexhy-vMVEDYEnugAofRVCbjjRHBggnCqAwX1BfW1HmBPb8YiXnU9M3BUx4kfrYyl5or-pWrGkUTtgpSGEoJfMj2Fzx0HHRnTUkzU-3RM8KW_a449LB7it2dN7YZtE9E4PA4bt44dA8fNwWOmm44kgtG5glZLBwCVytPpWuFP3OjJ4aw5VXkv9lH9MraeboFyqnLuOI2TN3Tc8KWaEUOlvrI-TMy6zKqQHyMRfOC4p0y3EudjypE4mfq3zSaCFs8wRqVBigOIe2g6rKNO_oM0DHtd_cFkGR5jxJo8AYE5tx2_yQPv1F0P46foDR2j52lo5yW4bUJXTs2fgZmQfbtSIoRotbMAunK860Ckl7Et8yo3Jq66hOo83tkWRPGHqTwBQ7SCSNsShijaNz_mL-bmbDNM-x9AVwc3myvgaQXfxsr6MrU2sfugdhrj-vm8baYn-6ljmC5Fi-0_gQqvtnB29BxPG4dhUHg0ci5iG9I5C1uMmjBlcz1SMHO0_v47E1FGIvaEeHTqKk62CN-iFyGa-wrkkmy_NeySbqCEr_ZTHmSnktB2CzHp6goNqarrR2kViD3Z6QB888AgaQrTpJlYWEXuxOWRgMuRcfYN-stjwjb8LfM_VXItqvBL0G1P6n3cQ30d8B6zLFmofolfkXdt3ik1l5m7oiLkYV2LGF1_R5_D5K-0eVflXFxY_NNRj6fWgeTKRJ6638yPBzc4LlvxR0QNnd4eAiNYS8TOZDp4pGXlQI6wdZZEv9DZSNpNi7iMaPHTvZ4fgw1EDq6oUpub2e3czoCjQaNJrOt9CPcjjk6-tycE6_-vg4b6dqCOK93uzDqpPazD7eepBd0G2gUF9m4W8JN2rxOwsHnPO5TdV36wgIXjrD5BLf77ccCd13JEh4rYz29N1LQncb-UvuUCukq7rPiJnS-XAct119v10_oKKwxiVaTwRuLZ0M0BPfHUgt4pCRdP5M9fozmUkqpBV9bfQxAwubRYTI9D7LCrnHVaA3aGq9P1_3gHI8fe3f8Jj8qguHc8DvFLxjU4Ng8_9axS98dO_YDR9_vFDmMTcSwDo-_T9iakIUCcVuEWYZrBkfjOdMbKnDG5Vo0JsTSgGnrAoFtPKws7bXnjw2p1UZdSw1QHvcj-Co1WTGyTOLnVdAfLyeett1E7W2jqX1Lb827jQxoWgtC3_3O5C_c8hI8WNedQYXCHMIHf1iJ2DJas6Rkugp4h-91t3IF5tBLdL3i_hbHSCiZxK-mqYDgn9Sh0kS5EBacO5yEUaQ4ash9W0n415bcPLVE6qr2w4i-ZkELMpmubp9Yg6H4bnOO59jQInxWPaNCNK_iWOlij-Kko7vm3ogKA45q49tBxY9l8RWU3N65Xn-DZ9AgIOOaGq32dAu0inVJENTopuwVJikcL87UNgMXRo8ruAXRllbuuZU4q7lQ4rFAuQXqHwytjHNyq4DWDuL1mO_bHitCO2ldqNI9FKDpVSzaHFcQqkdjeqmjnCzsnFaxitri0bjQjOzOS6NjCWkgt51JqYUdWNAZCLrdhySOtjnIDLqbgeZWmqdWtqC8ZGDb751GdmLbou0L9g3CiC8YRX7PpQq2HCzFGTiQMHYPVoYtaZMhvcnrIM1RYUJSybJCmXrfQrZDKwjbbLm1EuwgzTDTPkkty7oc3BwcS1tEp65wOQJBpX5aCY-9S-zdTSDdrcXgVuI7_4K8zpDuHmL0I_-G8g53HWR6PnBleqwqmUtf1NtxZkrCNp9kZo0zO782oRT5IHXKMBrKbHH2iFQB7s6bauRs1mxSwr1PKsLOhW1EKiaTUW55VcjMjbrkGrlyOi4FruiYKIXMC7DDWAS89uAO6A2CE9ezd4c77oPMrtHlDpcoXcy_gvW4szhd0T9rFS-j1qbkUofHP8CU4O0-vHyRj8111cqUVe3heVy7x2O04UuffIjd4yF9wvMxGh3Qexyv4dbuOwAoV868voRPz7urRtr-ksD9t7Lo8ZBCwwvJURt2F7PpyLo58OE3HQTPzW4X6BNxlopFuuAncDY5ZZPJLEk-sJPibDbP5ouUM3EK8_mOT3fT2WmaiXmyg-10Ns9O5BlL2IfkdDJjSZKki_Fklk3ZAhawSzmfZQn5kEDJpRordV-Ojc1PpHM1nC0WE7Y4UXwLyoUfzTBWgufbOg_1nBVhrLlNJumyu6FvWuKPJp6xtTcbbYPxJF22vNP1iT1DI0bbOnfkQ6Kk8-5glpdewdnhlzmv37qj1C5DT2qrzl4JcHP9HZRX1vwfZJ6wTcABIxuhuD9j_wkAAP__Ehrflg">