Skip to content

Conversation

@rampitec
Copy link
Collaborator

With current codegen this only affects src_flat_scratch_base_lo/hi.

Co-authored-by: Jay Foad [email protected]

With current codegen this only affects src_flat_scratch_base_lo/hi.

Co-authored-by: Jay Foad <[email protected]>
Copy link
Collaborator Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@rampitec rampitec marked this pull request as ready for review August 18, 2025 18:59
@llvmbot
Copy link
Member

llvmbot commented Aug 18, 2025

@llvm/pr-subscribers-backend-amdgpu

Author: Stanislav Mekhanoshin (rampitec)

Changes

With current codegen this only affects src_flat_scratch_base_lo/hi.

Co-authored-by: Jay Foad <[email protected]>


Patch is 236.21 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/154183.diff

6 Files Affected:

  • (modified) llvm/lib/Target/AMDGPU/SIFoldOperands.cpp (+7-2)
  • (modified) llvm/test/CodeGen/AMDGPU/addrspacecast-gas.ll (+22-30)
  • (modified) llvm/test/CodeGen/AMDGPU/atomics-system-scope.ll (+200-210)
  • (modified) llvm/test/CodeGen/AMDGPU/flat-saddr-atomics.ll (+314-535)
  • (modified) llvm/test/CodeGen/AMDGPU/llvm.amdgcn.is.private.ll (+49)
  • (modified) llvm/test/CodeGen/AMDGPU/scale-offset-flat.ll (+5-10)
diff --git a/llvm/lib/Target/AMDGPU/SIFoldOperands.cpp b/llvm/lib/Target/AMDGPU/SIFoldOperands.cpp
index 962c276bc2123..66d1126eb4151 100644
--- a/llvm/lib/Target/AMDGPU/SIFoldOperands.cpp
+++ b/llvm/lib/Target/AMDGPU/SIFoldOperands.cpp
@@ -709,7 +709,10 @@ bool SIFoldOperandsImpl::updateOperand(FoldCandidate &Fold) const {
   // 16-bit SGPRs instead of 32-bit ones.
   if (Old.getSubReg() == AMDGPU::lo16 && TRI->isSGPRReg(*MRI, New->getReg()))
     Old.setSubReg(AMDGPU::NoSubRegister);
-  Old.substVirtReg(New->getReg(), New->getSubReg(), *TRI);
+  if (New->getReg().isPhysical())
+    Old.substPhysReg(New->getReg(), *TRI);
+  else
+    Old.substVirtReg(New->getReg(), New->getSubReg(), *TRI);
   Old.setIsUndef(New->isUndef());
   return true;
 }
@@ -1986,7 +1989,9 @@ bool SIFoldOperandsImpl::tryFoldFoldableCopy(
   if (!FoldingImm && !OpToFold.isReg())
     return false;
 
-  if (OpToFold.isReg() && !OpToFold.getReg().isVirtual())
+  // Fold virtual registers and constant physical registers.
+  if (OpToFold.isReg() && OpToFold.getReg().isPhysical() &&
+      !TRI->isConstantPhysReg(OpToFold.getReg()))
     return false;
 
   // Prevent folding operands backwards in the function. For example,
diff --git a/llvm/test/CodeGen/AMDGPU/addrspacecast-gas.ll b/llvm/test/CodeGen/AMDGPU/addrspacecast-gas.ll
index 4b6375cc60800..b4b49e90dca02 100644
--- a/llvm/test/CodeGen/AMDGPU/addrspacecast-gas.ll
+++ b/llvm/test/CodeGen/AMDGPU/addrspacecast-gas.ll
@@ -9,15 +9,14 @@ target triple = "amdgcn-amd-amdhsa"
 define amdgpu_kernel void @use_private_to_flat_addrspacecast(ptr addrspace(5) %ptr) {
 ; GFX1250-SDAG-LABEL: use_private_to_flat_addrspacecast:
 ; GFX1250-SDAG:       ; %bb.0:
-; GFX1250-SDAG-NEXT:    s_load_b32 s2, s[4:5], 0x24
+; GFX1250-SDAG-NEXT:    s_load_b32 s0, s[4:5], 0x24
 ; GFX1250-SDAG-NEXT:    v_mbcnt_lo_u32_b32 v0, -1, 0
-; GFX1250-SDAG-NEXT:    s_mov_b64 s[0:1], src_flat_scratch_base_lo
 ; GFX1250-SDAG-NEXT:    s_wait_kmcnt 0x0
 ; GFX1250-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(SKIP_2) | instid1(VALU_DEP_1)
-; GFX1250-SDAG-NEXT:    v_dual_mov_b32 v0, s2 :: v_dual_lshlrev_b32 v1, 20, v0
-; GFX1250-SDAG-NEXT:    s_cmp_lg_u32 s2, -1
+; GFX1250-SDAG-NEXT:    v_dual_mov_b32 v0, s0 :: v_dual_lshlrev_b32 v1, 20, v0
+; GFX1250-SDAG-NEXT:    s_cmp_lg_u32 s0, -1
 ; GFX1250-SDAG-NEXT:    s_cselect_b32 vcc_lo, -1, 0
-; GFX1250-SDAG-NEXT:    v_add_nc_u64_e32 v[0:1], s[0:1], v[0:1]
+; GFX1250-SDAG-NEXT:    v_add_nc_u64_e32 v[0:1], src_flat_scratch_base_lo, v[0:1]
 ; GFX1250-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_2)
 ; GFX1250-SDAG-NEXT:    v_dual_mov_b32 v2, 0 :: v_dual_cndmask_b32 v1, 0, v1
 ; GFX1250-SDAG-NEXT:    v_cndmask_b32_e32 v0, 0, v0, vcc_lo
@@ -27,20 +26,20 @@ define amdgpu_kernel void @use_private_to_flat_addrspacecast(ptr addrspace(5) %p
 ;
 ; GFX1250-GISEL-LABEL: use_private_to_flat_addrspacecast:
 ; GFX1250-GISEL:       ; %bb.0:
-; GFX1250-GISEL-NEXT:    s_load_b32 s2, s[4:5], 0x24
-; GFX1250-GISEL-NEXT:    s_mov_b64 s[0:1], src_flat_scratch_base_lo
+; GFX1250-GISEL-NEXT:    s_load_b32 s0, s[4:5], 0x24
+; GFX1250-GISEL-NEXT:    v_mov_b64_e32 v[0:1], src_flat_scratch_base_lo
 ; GFX1250-GISEL-NEXT:    v_mbcnt_lo_u32_b32 v2, -1, 0
-; GFX1250-GISEL-NEXT:    v_mov_b64_e32 v[0:1], s[0:1]
 ; GFX1250-GISEL-NEXT:    s_wait_kmcnt 0x0
-; GFX1250-GISEL-NEXT:    s_cmp_lg_u32 s2, -1
-; GFX1250-GISEL-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(SKIP_2) | instid1(SALU_CYCLE_1)
-; GFX1250-GISEL-NEXT:    v_add_co_u32 v0, vcc_lo, s2, v0
+; GFX1250-GISEL-NEXT:    s_cmp_lg_u32 s0, -1
+; GFX1250-GISEL-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(NEXT) | instid1(VALU_DEP_2)
+; GFX1250-GISEL-NEXT:    v_add_co_u32 v0, vcc_lo, s0, v0
 ; GFX1250-GISEL-NEXT:    v_lshlrev_b32_e32 v2, 20, v2
-; GFX1250-GISEL-NEXT:    s_cselect_b32 s0, 1, 0
-; GFX1250-GISEL-NEXT:    s_and_b32 s0, 1, s0
-; GFX1250-GISEL-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(SKIP_1) | instid1(VALU_DEP_2)
+; GFX1250-GISEL-NEXT:    s_cselect_b32 s1, 1, 0
+; GFX1250-GISEL-NEXT:    s_delay_alu instid0(SALU_CYCLE_1) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX1250-GISEL-NEXT:    s_and_b32 s1, 1, s1
 ; GFX1250-GISEL-NEXT:    v_add_co_ci_u32_e64 v1, null, v2, v1, vcc_lo
-; GFX1250-GISEL-NEXT:    v_cmp_ne_u32_e64 vcc_lo, 0, s0
+; GFX1250-GISEL-NEXT:    v_cmp_ne_u32_e64 vcc_lo, 0, s1
+; GFX1250-GISEL-NEXT:    s_delay_alu instid0(VALU_DEP_2)
 ; GFX1250-GISEL-NEXT:    v_dual_mov_b32 v2, 0 :: v_dual_cndmask_b32 v1, 0, v1
 ; GFX1250-GISEL-NEXT:    v_cndmask_b32_e32 v0, 0, v0, vcc_lo
 ; GFX1250-GISEL-NEXT:    flat_store_b32 v[0:1], v2 scope:SCOPE_SYS
@@ -56,27 +55,24 @@ define amdgpu_kernel void @use_private_to_flat_addrspacecast_nonnull(ptr addrspa
 ; GFX1250-SDAG:       ; %bb.0:
 ; GFX1250-SDAG-NEXT:    s_load_b32 s0, s[4:5], 0x24
 ; GFX1250-SDAG-NEXT:    v_mbcnt_lo_u32_b32 v0, -1, 0
-; GFX1250-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_1)
+; GFX1250-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(SKIP_2) | instid1(VALU_DEP_1)
 ; GFX1250-SDAG-NEXT:    v_dual_mov_b32 v2, 0 :: v_dual_lshlrev_b32 v1, 20, v0
 ; GFX1250-SDAG-NEXT:    s_wait_kmcnt 0x0
 ; GFX1250-SDAG-NEXT:    v_mov_b32_e32 v0, s0
-; GFX1250-SDAG-NEXT:    s_mov_b64 s[0:1], src_flat_scratch_base_lo
-; GFX1250-SDAG-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instid1(SALU_CYCLE_1)
-; GFX1250-SDAG-NEXT:    v_add_nc_u64_e32 v[0:1], s[0:1], v[0:1]
+; GFX1250-SDAG-NEXT:    v_add_nc_u64_e32 v[0:1], src_flat_scratch_base_lo, v[0:1]
 ; GFX1250-SDAG-NEXT:    flat_store_b32 v[0:1], v2 scope:SCOPE_SYS
 ; GFX1250-SDAG-NEXT:    s_wait_storecnt 0x0
 ; GFX1250-SDAG-NEXT:    s_endpgm
 ;
 ; GFX1250-GISEL-LABEL: use_private_to_flat_addrspacecast_nonnull:
 ; GFX1250-GISEL:       ; %bb.0:
-; GFX1250-GISEL-NEXT:    s_load_b32 s2, s[4:5], 0x24
-; GFX1250-GISEL-NEXT:    s_mov_b64 s[0:1], src_flat_scratch_base_lo
+; GFX1250-GISEL-NEXT:    s_load_b32 s0, s[4:5], 0x24
 ; GFX1250-GISEL-NEXT:    v_mbcnt_lo_u32_b32 v2, -1, 0
-; GFX1250-GISEL-NEXT:    v_mov_b64_e32 v[0:1], s[0:1]
+; GFX1250-GISEL-NEXT:    v_mov_b64_e32 v[0:1], src_flat_scratch_base_lo
 ; GFX1250-GISEL-NEXT:    s_delay_alu instid0(VALU_DEP_2) | instskip(SKIP_1) | instid1(VALU_DEP_2)
 ; GFX1250-GISEL-NEXT:    v_dual_mov_b32 v3, 0 :: v_dual_lshlrev_b32 v2, 20, v2
 ; GFX1250-GISEL-NEXT:    s_wait_kmcnt 0x0
-; GFX1250-GISEL-NEXT:    v_add_co_u32 v0, vcc_lo, s2, v0
+; GFX1250-GISEL-NEXT:    v_add_co_u32 v0, vcc_lo, s0, v0
 ; GFX1250-GISEL-NEXT:    s_delay_alu instid0(VALU_DEP_1)
 ; GFX1250-GISEL-NEXT:    v_add_co_ci_u32_e64 v1, null, v2, v1, vcc_lo
 ; GFX1250-GISEL-NEXT:    flat_store_b32 v[0:1], v3 scope:SCOPE_SYS
@@ -91,10 +87,9 @@ define amdgpu_kernel void @use_flat_to_private_addrspacecast(ptr %ptr) {
 ; GFX1250-LABEL: use_flat_to_private_addrspacecast:
 ; GFX1250:       ; %bb.0:
 ; GFX1250-NEXT:    s_load_b64 s[0:1], s[4:5], 0x24
-; GFX1250-NEXT:    s_mov_b32 s2, src_flat_scratch_base_lo
 ; GFX1250-NEXT:    v_mov_b32_e32 v0, 0
 ; GFX1250-NEXT:    s_wait_kmcnt 0x0
-; GFX1250-NEXT:    s_sub_co_i32 s2, s0, s2
+; GFX1250-NEXT:    s_sub_co_i32 s2, s0, src_flat_scratch_base_lo
 ; GFX1250-NEXT:    s_cmp_lg_u64 s[0:1], 0
 ; GFX1250-NEXT:    s_cselect_b32 s0, s2, -1
 ; GFX1250-NEXT:    scratch_store_b32 off, v0, s0 scope:SCOPE_SYS
@@ -110,9 +105,8 @@ define amdgpu_kernel void @use_flat_to_private_addrspacecast_nonnull(ptr %ptr) {
 ; GFX1250-SDAG:       ; %bb.0:
 ; GFX1250-SDAG-NEXT:    s_load_b32 s0, s[4:5], 0x24
 ; GFX1250-SDAG-NEXT:    v_mov_b32_e32 v0, 0
-; GFX1250-SDAG-NEXT:    s_mov_b32 s1, src_flat_scratch_base_lo
 ; GFX1250-SDAG-NEXT:    s_wait_kmcnt 0x0
-; GFX1250-SDAG-NEXT:    s_sub_co_i32 s0, s0, s1
+; GFX1250-SDAG-NEXT:    s_sub_co_i32 s0, s0, src_flat_scratch_base_lo
 ; GFX1250-SDAG-NEXT:    scratch_store_b32 off, v0, s0 scope:SCOPE_SYS
 ; GFX1250-SDAG-NEXT:    s_wait_storecnt 0x0
 ; GFX1250-SDAG-NEXT:    s_endpgm
@@ -122,9 +116,7 @@ define amdgpu_kernel void @use_flat_to_private_addrspacecast_nonnull(ptr %ptr) {
 ; GFX1250-GISEL-NEXT:    s_load_b64 s[0:1], s[4:5], 0x24
 ; GFX1250-GISEL-NEXT:    v_mov_b32_e32 v0, 0
 ; GFX1250-GISEL-NEXT:    s_wait_kmcnt 0x0
-; GFX1250-GISEL-NEXT:    s_mov_b32 s1, src_flat_scratch_base_lo
-; GFX1250-GISEL-NEXT:    s_delay_alu instid0(SALU_CYCLE_1)
-; GFX1250-GISEL-NEXT:    s_sub_co_i32 s0, s0, s1
+; GFX1250-GISEL-NEXT:    s_sub_co_i32 s0, s0, src_flat_scratch_base_lo
 ; GFX1250-GISEL-NEXT:    scratch_store_b32 off, v0, s0 scope:SCOPE_SYS
 ; GFX1250-GISEL-NEXT:    s_wait_storecnt 0x0
 ; GFX1250-GISEL-NEXT:    s_endpgm
diff --git a/llvm/test/CodeGen/AMDGPU/atomics-system-scope.ll b/llvm/test/CodeGen/AMDGPU/atomics-system-scope.ll
index 5fc9f4a0f8038..817e3f01c8cdd 100644
--- a/llvm/test/CodeGen/AMDGPU/atomics-system-scope.ll
+++ b/llvm/test/CodeGen/AMDGPU/atomics-system-scope.ll
@@ -534,58 +534,61 @@ define double @flat_system_atomic_fadd_f64(ptr %ptr, double %val) {
 ; GFX1250:       ; %bb.0:
 ; GFX1250-NEXT:    s_wait_loadcnt_dscnt 0x0
 ; GFX1250-NEXT:    s_wait_kmcnt 0x0
+; GFX1250-NEXT:    v_dual_mov_b32 v5, v1 :: v_dual_mov_b32 v4, v0
 ; GFX1250-NEXT:    s_mov_b64 s[0:1], src_shared_base
 ; GFX1250-NEXT:    s_mov_b32 s0, exec_lo
-; GFX1250-NEXT:    ; implicit-def: $vgpr4_vgpr5
-; GFX1250-NEXT:    v_cmpx_ne_u32_e64 s1, v1
+; GFX1250-NEXT:    ; implicit-def: $vgpr0_vgpr1
+; GFX1250-NEXT:    s_delay_alu instid0(VALU_DEP_1)
+; GFX1250-NEXT:    v_cmpx_ne_u32_e64 s1, v5
 ; GFX1250-NEXT:    s_xor_b32 s0, exec_lo, s0
-; GFX1250-NEXT:    s_cbranch_execz .LBB34_6
-; GFX1250-NEXT:  ; %bb.1: ; %atomicrmw.check.private
-; GFX1250-NEXT:    s_mov_b32 s1, src_flat_scratch_base_hi
-; GFX1250-NEXT:    s_delay_alu instid0(SALU_CYCLE_1) | instskip(NEXT) | instid1(VALU_DEP_1)
-; GFX1250-NEXT:    v_xor_b32_e32 v4, s1, v1
-; GFX1250-NEXT:    v_cmp_lt_u32_e32 vcc_lo, 0x3ffffff, v4
-; GFX1250-NEXT:    ; implicit-def: $vgpr4_vgpr5
+; GFX1250-NEXT:    s_cbranch_execnz .LBB34_3
+; GFX1250-NEXT:  ; %bb.1: ; %Flow2
+; GFX1250-NEXT:    s_and_not1_saveexec_b32 s0, s0
+; GFX1250-NEXT:    s_cbranch_execnz .LBB34_8
+; GFX1250-NEXT:  .LBB34_2: ; %atomicrmw.phi
+; GFX1250-NEXT:    s_or_b32 exec_lo, exec_lo, s0
+; GFX1250-NEXT:    s_wait_loadcnt 0x0
+; GFX1250-NEXT:    s_set_pc_i64 s[30:31]
+; GFX1250-NEXT:  .LBB34_3: ; %atomicrmw.check.private
+; GFX1250-NEXT:    v_xor_b32_e32 v0, src_flat_scratch_base_hi, v5
+; GFX1250-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(SKIP_1) | instid1(SALU_CYCLE_1)
+; GFX1250-NEXT:    v_cmp_lt_u32_e32 vcc_lo, 0x3ffffff, v0
+; GFX1250-NEXT:    ; implicit-def: $vgpr0_vgpr1
 ; GFX1250-NEXT:    s_and_saveexec_b32 s1, vcc_lo
-; GFX1250-NEXT:    s_delay_alu instid0(SALU_CYCLE_1)
 ; GFX1250-NEXT:    s_xor_b32 s1, exec_lo, s1
-; GFX1250-NEXT:    s_cbranch_execz .LBB34_3
-; GFX1250-NEXT:  ; %bb.2: ; %atomicrmw.global
-; GFX1250-NEXT:    global_atomic_add_f64 v[4:5], v[0:1], v[2:3], off th:TH_ATOMIC_RETURN scope:SCOPE_SYS
-; GFX1250-NEXT:    ; implicit-def: $vgpr0_vgpr1
+; GFX1250-NEXT:    s_cbranch_execz .LBB34_5
+; GFX1250-NEXT:  ; %bb.4: ; %atomicrmw.global
+; GFX1250-NEXT:    global_atomic_add_f64 v[0:1], v[4:5], v[2:3], off th:TH_ATOMIC_RETURN scope:SCOPE_SYS
+; GFX1250-NEXT:    ; implicit-def: $vgpr4_vgpr5
 ; GFX1250-NEXT:    ; implicit-def: $vgpr2_vgpr3
-; GFX1250-NEXT:  .LBB34_3: ; %Flow
+; GFX1250-NEXT:  .LBB34_5: ; %Flow
 ; GFX1250-NEXT:    s_wait_xcnt 0x0
 ; GFX1250-NEXT:    s_and_not1_saveexec_b32 s1, s1
-; GFX1250-NEXT:    s_cbranch_execz .LBB34_5
-; GFX1250-NEXT:  ; %bb.4: ; %atomicrmw.private
-; GFX1250-NEXT:    s_mov_b32 s2, src_flat_scratch_base_lo
-; GFX1250-NEXT:    v_cmp_ne_u64_e32 vcc_lo, 0, v[0:1]
+; GFX1250-NEXT:    s_cbranch_execz .LBB34_7
+; GFX1250-NEXT:  ; %bb.6: ; %atomicrmw.private
+; GFX1250-NEXT:    v_cmp_ne_u64_e32 vcc_lo, 0, v[4:5]
 ; GFX1250-NEXT:    s_wait_loadcnt 0x0
-; GFX1250-NEXT:    v_subrev_nc_u32_e32 v4, s2, v0
+; GFX1250-NEXT:    v_subrev_nc_u32_e32 v0, src_flat_scratch_base_lo, v4
 ; GFX1250-NEXT:    s_delay_alu instid0(VALU_DEP_1)
-; GFX1250-NEXT:    v_cndmask_b32_e32 v6, -1, v4, vcc_lo
-; GFX1250-NEXT:    scratch_load_b64 v[4:5], v6, off
+; GFX1250-NEXT:    v_cndmask_b32_e32 v4, -1, v0, vcc_lo
+; GFX1250-NEXT:    scratch_load_b64 v[0:1], v4, off
 ; GFX1250-NEXT:    s_wait_loadcnt 0x0
-; GFX1250-NEXT:    v_add_f64_e32 v[0:1], v[4:5], v[2:3]
-; GFX1250-NEXT:    scratch_store_b64 v6, v[0:1], off scope:SCOPE_SE
-; GFX1250-NEXT:  .LBB34_5: ; %Flow1
+; GFX1250-NEXT:    v_add_f64_e32 v[2:3], v[0:1], v[2:3]
+; GFX1250-NEXT:    scratch_store_b64 v4, v[2:3], off scope:SCOPE_SE
+; GFX1250-NEXT:  .LBB34_7: ; %Flow1
 ; GFX1250-NEXT:    s_wait_xcnt 0x0
 ; GFX1250-NEXT:    s_or_b32 exec_lo, exec_lo, s1
-; GFX1250-NEXT:    ; implicit-def: $vgpr0_vgpr1
+; GFX1250-NEXT:    ; implicit-def: $vgpr4_vgpr5
 ; GFX1250-NEXT:    ; implicit-def: $vgpr2_vgpr3
-; GFX1250-NEXT:  .LBB34_6: ; %Flow2
 ; GFX1250-NEXT:    s_and_not1_saveexec_b32 s0, s0
-; GFX1250-NEXT:    s_cbranch_execz .LBB34_8
-; GFX1250-NEXT:  ; %bb.7: ; %atomicrmw.shared
-; GFX1250-NEXT:    v_cmp_ne_u64_e32 vcc_lo, 0, v[0:1]
-; GFX1250-NEXT:    v_cndmask_b32_e32 v0, -1, v0, vcc_lo
+; GFX1250-NEXT:    s_cbranch_execz .LBB34_2
+; GFX1250-NEXT:  .LBB34_8: ; %atomicrmw.shared
+; GFX1250-NEXT:    v_cmp_ne_u64_e32 vcc_lo, 0, v[4:5]
 ; GFX1250-NEXT:    s_wait_loadcnt 0x0
-; GFX1250-NEXT:    ds_add_rtn_f64 v[4:5], v0, v[2:3]
-; GFX1250-NEXT:  .LBB34_8: ; %atomicrmw.phi
+; GFX1250-NEXT:    v_cndmask_b32_e32 v0, -1, v4, vcc_lo
+; GFX1250-NEXT:    ds_add_rtn_f64 v[0:1], v0, v[2:3]
 ; GFX1250-NEXT:    s_or_b32 exec_lo, exec_lo, s0
-; GFX1250-NEXT:    s_wait_loadcnt_dscnt 0x0
-; GFX1250-NEXT:    v_dual_mov_b32 v0, v4 :: v_dual_mov_b32 v1, v5
+; GFX1250-NEXT:    s_wait_dscnt 0x0
 ; GFX1250-NEXT:    s_set_pc_i64 s[30:31]
   %result = atomicrmw fadd ptr %ptr, double %val monotonic
   ret double %result
@@ -596,58 +599,61 @@ define double @flat_one_as_atomic_fadd_f64(ptr %ptr, double %val) {
 ; GFX1250:       ; %bb.0:
 ; GFX1250-NEXT:    s_wait_loadcnt_dscnt 0x0
 ; GFX1250-NEXT:    s_wait_kmcnt 0x0
+; GFX1250-NEXT:    v_dual_mov_b32 v5, v1 :: v_dual_mov_b32 v4, v0
 ; GFX1250-NEXT:    s_mov_b64 s[0:1], src_shared_base
 ; GFX1250-NEXT:    s_mov_b32 s0, exec_lo
-; GFX1250-NEXT:    ; implicit-def: $vgpr4_vgpr5
-; GFX1250-NEXT:    v_cmpx_ne_u32_e64 s1, v1
+; GFX1250-NEXT:    ; implicit-def: $vgpr0_vgpr1
+; GFX1250-NEXT:    s_delay_alu instid0(VALU_DEP_1)
+; GFX1250-NEXT:    v_cmpx_ne_u32_e64 s1, v5
 ; GFX1250-NEXT:    s_xor_b32 s0, exec_lo, s0
-; GFX1250-NEXT:    s_cbranch_execz .LBB35_6
-; GFX1250-NEXT:  ; %bb.1: ; %atomicrmw.check.private
-; GFX1250-NEXT:    s_mov_b32 s1, src_flat_scratch_base_hi
-; GFX1250-NEXT:    s_delay_alu instid0(SALU_CYCLE_1) | instskip(NEXT) | instid1(VALU_DEP_1)
-; GFX1250-NEXT:    v_xor_b32_e32 v4, s1, v1
-; GFX1250-NEXT:    v_cmp_lt_u32_e32 vcc_lo, 0x3ffffff, v4
-; GFX1250-NEXT:    ; implicit-def: $vgpr4_vgpr5
+; GFX1250-NEXT:    s_cbranch_execnz .LBB35_3
+; GFX1250-NEXT:  ; %bb.1: ; %Flow2
+; GFX1250-NEXT:    s_and_not1_saveexec_b32 s0, s0
+; GFX1250-NEXT:    s_cbranch_execnz .LBB35_8
+; GFX1250-NEXT:  .LBB35_2: ; %atomicrmw.phi
+; GFX1250-NEXT:    s_or_b32 exec_lo, exec_lo, s0
+; GFX1250-NEXT:    s_wait_loadcnt 0x0
+; GFX1250-NEXT:    s_set_pc_i64 s[30:31]
+; GFX1250-NEXT:  .LBB35_3: ; %atomicrmw.check.private
+; GFX1250-NEXT:    v_xor_b32_e32 v0, src_flat_scratch_base_hi, v5
+; GFX1250-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(SKIP_1) | instid1(SALU_CYCLE_1)
+; GFX1250-NEXT:    v_cmp_lt_u32_e32 vcc_lo, 0x3ffffff, v0
+; GFX1250-NEXT:    ; implicit-def: $vgpr0_vgpr1
 ; GFX1250-NEXT:    s_and_saveexec_b32 s1, vcc_lo
-; GFX1250-NEXT:    s_delay_alu instid0(SALU_CYCLE_1)
 ; GFX1250-NEXT:    s_xor_b32 s1, exec_lo, s1
-; GFX1250-NEXT:    s_cbranch_execz .LBB35_3
-; GFX1250-NEXT:  ; %bb.2: ; %atomicrmw.global
-; GFX1250-NEXT:    global_atomic_add_f64 v[4:5], v[0:1], v[2:3], off th:TH_ATOMIC_RETURN scope:SCOPE_SYS
-; GFX1250-NEXT:    ; implicit-def: $vgpr0_vgpr1
+; GFX1250-NEXT:    s_cbranch_execz .LBB35_5
+; GFX1250-NEXT:  ; %bb.4: ; %atomicrmw.global
+; GFX1250-NEXT:    global_atomic_add_f64 v[0:1], v[4:5], v[2:3], off th:TH_ATOMIC_RETURN scope:SCOPE_SYS
+; GFX1250-NEXT:    ; implicit-def: $vgpr4_vgpr5
 ; GFX1250-NEXT:    ; implicit-def: $vgpr2_vgpr3
-; GFX1250-NEXT:  .LBB35_3: ; %Flow
+; GFX1250-NEXT:  .LBB35_5: ; %Flow
 ; GFX1250-NEXT:    s_wait_xcnt 0x0
 ; GFX1250-NEXT:    s_and_not1_saveexec_b32 s1, s1
-; GFX1250-NEXT:    s_cbranch_execz .LBB35_5
-; GFX1250-NEXT:  ; %bb.4: ; %atomicrmw.private
-; GFX1250-NEXT:    s_mov_b32 s2, src_flat_scratch_base_lo
-; GFX1250-NEXT:    v_cmp_ne_u64_e32 vcc_lo, 0, v[0:1]
+; GFX1250-NEXT:    s_cbranch_execz .LBB35_7
+; GFX1250-NEXT:  ; %bb.6: ; %atomicrmw.private
+; GFX1250-NEXT:    v_cmp_ne_u64_e32 vcc_lo, 0, v[4:5]
 ; GFX1250-NEXT:    s_wait_loadcnt 0x0
-; GFX1250-NEXT:    v_subrev_nc_u32_e32 v4, s2, v0
+; GFX1250-NEXT:    v_subrev_nc_u32_e32 v0, src_flat_scratch_base_lo, v4
 ; GFX1250-NEXT:    s_delay_alu instid0(VALU_DEP_1)
-; GFX1250-NEXT:    v_cndmask_b32_e32 v6, -1, v4, vcc_lo
-; GFX1250-NEXT:    scratch_load_b64 v[4:5], v6, off
+; GFX1250-NEXT:    v_cndmask_b32_e32 v4, -1, v0, vcc_lo
+; GFX1250-NEXT:    scratch_load_b64 v[0:1], v4, off
 ; GFX1250-NEXT:    s_wait_loadcnt 0x0
-; GFX1250-NEXT:    v_add_f64_e32 v[0:1], v[4:5], v[2:3]
-; GFX1250-NEXT:    scratch_store_b64 v6, v[0:1], off scope:SCOPE_SE
-; GFX1250-NEXT:  .LBB35_5: ; %Flow1
+; GFX1250-NEXT:    v_add_f64_e32 v[2:3], v[0:1], v[2:3]
+; GFX1250-NEXT:    scratch_store_b64 v4, v[2:3], off scope:SCOPE_SE
+; GFX1250-NEXT:  .LBB35_7: ; %Flow1
 ; GFX1250-NEXT:    s_wait_xcnt 0x0
 ; GFX1250-NEXT:    s_or_b32 exec_lo, exec_lo, s1
-; GFX1250-NEXT:    ; implicit-def: $vgpr0_vgpr1
+; GFX1250-NEXT:    ; implicit-def: $vgpr4_vgpr5
 ; GFX1250-NEXT:    ; implicit-def: $vgpr2_vgpr3
-; GFX1250-NEXT:  .LBB35_6: ; %Flow2
 ; GFX1250-NEXT:    s_and_not1_saveexec_b32 s0, s0
-; GFX1250-NEXT:    s_cbranch_execz .LBB35_8
-; GFX1250-NEXT:  ; %bb.7: ; %atomicrmw.shared
-; GFX1250-NEXT:    v_cmp_ne_u64_e32 vcc_lo, 0, v[0:1]
-; GFX1250-NEXT:    v_cndmask_b32_e32 v0, -1, v0, vcc_lo
+; GFX1250-NEXT:    s_cbranch_execz .LBB35_2
+; GFX1250-NEXT:  .LBB35_8: ; %atomicrmw.shared
+; GFX1250-NEXT:    v_cmp_ne_u64_e32 vcc_lo, 0, v[4:5]
 ; GFX1250-NEXT:    s_wait_loadcnt 0x0
-; GFX1250-NEXT:    ds_add_rtn_f64 v[4:5], v0, v[2:3]
-; GFX1250-NEXT:  .LBB35_8: ; %atomicrmw.phi
+; GFX1250-NEXT:    v_cndmask_b32_e32 v0, -1, v4, vcc_lo
+; GFX1250-NEXT:    ds_add_rtn_f64 v[0:1], v0, v[2:3]
 ; GFX1250-NEXT:    s_or_b32 exec_lo, exec_lo, s0
-; GFX1250-NEXT:    s_wait_loadcnt_dscnt 0x0
-; GFX1250-NEXT:    v_dual_mov_b32 v0, v4 :: v_dual_mov_b32 v1, v5
+; GFX1250-NEXT:    s_wait_dscnt 0x0
 ; GFX1250-NEXT:    s_set_pc_i64 s[30:31]
   %result = atomicrmw fadd ptr %ptr, double %val syncscope("one-as") monotonic
   ret double %result
@@ -682,40 +688,42 @@ define double @flat_system_atomic_fmin_f64(ptr %ptr, double %val) {
 ; GFX1250:       ; %bb.0:
 ; GFX1250-NEXT:    s_wait_loadcnt_dscnt 0x0
 ; GFX1250-NEXT:    s_wait_kmcnt 0x0
-; GFX1250-NEXT:    s_mov_b32 s0, src_flat_scratch_base_hi
-; GFX1250-NEXT:    s_delay_alu instid0(SALU_CYCLE_1) | instskip(NEXT) | instid1(VALU_DEP_1)
-; GFX1250-NEXT:    v_xor_b32_e32 v4, s0, v1
-; GFX1250-NEXT:    v_cmp_lt_u32_e32 vcc_lo, 0x3ffffff, v4
-; GFX1250-NEXT:    ; implicit-def: $vgpr4_vgpr5
+; GFX1250-NEXT:    v_dual_mov_b32 v5, v1 :: v_dual_mov_b32 v4, v0
+; GFX1250-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
+; GFX1250-NEXT:    v_xor_b32_e32 v0, src_flat_scratch_base_hi, v5
+; GFX1250-NEXT:    v_cmp_lt_u32_e32 vcc_lo, 0x3ffffff, v0
+; GFX1250-NEXT:    ; implicit-def: $vgpr0_vgpr1
 ; GFX1250-NEXT:    s_and_saveexec_b32 s0, vcc_lo
 ; GFX1250-NEXT:    s_delay_alu instid0(SALU_CYCLE_1)
 ; GFX1250-NEXT:    s_xor_b32 s0, exec_lo, s0
-; GFX1250-NEXT:    s_cbranch_execz .LBB38_2
-; GFX1250-NEXT:  ; %bb.1: ; %atomicrmw.global
-; GFX1250-NEXT:    flat_atomic_min_num_f64 v[4:5], v[0:1], v[2:3] th:TH_ATOMIC_RETURN scope:SCOPE_SYS
-; GFX1250-NEXT:    ; implicit-def: $vgpr0_vgpr1
+; GFX1250-NEXT:    s_cbranch_execnz .LBB38_3
+; GFX1250-NEXT:  ; %bb.1: ; %Flow
+; GFX1250-NEXT:    s_and_not1_saveexec_b32 s0, s0
+; GFX1250-NEXT:    s_cbranch_execnz .LBB38_4
+; GFX1250-NEXT:  .LBB38_2: ; %atomicrmw.phi
+; GFX1250-NEXT:    s_or_b32 exec_lo, exec_lo, s0
+; GFX1250-NEXT:    s_wait_loadcnt_dscnt 0x0
+; GFX1250-NEXT:    s_set_pc_i64 s[30:31]
+; GFX1250-NEXT:  .LBB38_3: ; %atomicrmw.global
+; GFX1250-NEXT:    flat_atomic_min_num_f64 v[0:1], v[4:5],...
[truncated]

@rampitec rampitec merged commit 3395676 into main Aug 18, 2025
9 checks passed
@rampitec rampitec deleted the users/rampitec/08-18-_amdgpu_fold_copies_of_constant_physical_registers_into_their_uses branch August 18, 2025 20:07
@Kewen12
Copy link
Contributor

Kewen12 commented Aug 18, 2025

Hi @rampitec, this PR breaks our libc test. Would really appreciate if you can take a look. Thanks!

bot: https://lab.llvm.org/buildbot/#/builders/10/builds/11657

/home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/libc/test/src/string/memmove_test.cpp:98: FAILURE
Expected: CheckMemmove(Buffer, static_cast<size_t>(Size), Overlap)
Which is: 0
To be equal to: true
Which is: 1
[ FAILED ] LlvmLibcMemmoveTest.SizeSweep

@rampitec
Copy link
Collaborator Author

Hi @rampitec, this PR breaks our libc test. Would really appreciate if you can take a look. Thanks!

bot: https://lab.llvm.org/buildbot/#/builders/10/builds/11657

/home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/libc/test/src/string/memmove_test.cpp:98: FAILURE
Expected: CheckMemmove(Buffer, static_cast<size_t>(Size), Overlap)
Which is: 0
To be equal to: true
Which is: 1
[ FAILED ] LlvmLibcMemmoveTest.SizeSweep

I do not see from the log what exactly has failed.

@Kewen12
Copy link
Contributor

Kewen12 commented Aug 18, 2025

From: https://lab.llvm.org/buildbot/#/builders/10/builds/11657/steps/10/logs/stdio

It shows at line 8687:

FAILED: libc/test/src/string/libc.test.src.string.memmove_test.hermetic.cmd /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.build/runtimes/runtimes-amdgcn-amd-amdhsa-bins/libc/test/src/string/libc.test.src.string.memmove_test.hermetic.cmd
cd /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.build/runtimes/runtimes-amdgcn-amd-amdhsa-bins/libc/test/src/string && /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.build/bin/amdhsa-loader /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.build/runtimes/runtimes-amdgcn-amd-amdhsa-bins/libc/test/src/string/libc.test.src.string.memmove_test.hermetic.build
[==========] Running 7 tests from 1 test suite.
[ RUN ] LlvmLibcMemmoveTest.MoveZeroByte
[ OK ] LlvmLibcMemmoveTest.MoveZeroByte (10 us)
[ RUN ] LlvmLibcMemmoveTest.DstAndSrcPointToSameAddress
[ OK ] LlvmLibcMemmoveTest.DstAndSrcPointToSameAddress (5 us)
[ RUN ] LlvmLibcMemmoveTest.DstStartsBeforeSrc
[ OK ] LlvmLibcMemmoveTest.DstStartsBeforeSrc (5 us)
[ RUN ] LlvmLibcMemmoveTest.DstStartsAfterSrc
[ OK ] LlvmLibcMemmoveTest.DstStartsAfterSrc (5 us)
[ RUN ] LlvmLibcMemmoveTest.SrcFollowDst
[ OK ] LlvmLibcMemmoveTest.SrcFollowDst (5 us)
[ RUN ] LlvmLibcMemmoveTest.DstFollowSrc
[ OK ] LlvmLibcMemmoveTest.DstFollowSrc (4 us)
[ RUN ] LlvmLibcMemmoveTest.SizeSweep
/home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/libc/test/src/string/memmove_test.cpp:98: FAILURE
Expected: CheckMemmove(Buffer, static_cast<size_t>(Size), Overlap)
Which is: 0
To be equal to: true
Which is: 1
[ FAILED ] LlvmLibcMemmoveTest.SizeSweep

@rampitec
Copy link
Collaborator Author

From: https://lab.llvm.org/buildbot/#/builders/10/builds/11657/steps/10/logs/stdio

It shows at line 8687:

FAILED: libc/test/src/string/libc.test.src.string.memmove_test.hermetic.cmd /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.build/runtimes/runtimes-amdgcn-amd-amdhsa-bins/libc/test/src/string/libc.test.src.string.memmove_test.hermetic.cmd
cd /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.build/runtimes/runtimes-amdgcn-amd-amdhsa-bins/libc/test/src/string && /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.build/bin/amdhsa-loader /home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.build/runtimes/runtimes-amdgcn-amd-amdhsa-bins/libc/test/src/string/libc.test.src.string.memmove_test.hermetic.build
[==========] Running 7 tests from 1 test suite.
[ RUN ] LlvmLibcMemmoveTest.MoveZeroByte
[ OK ] LlvmLibcMemmoveTest.MoveZeroByte (10 us)
[ RUN ] LlvmLibcMemmoveTest.DstAndSrcPointToSameAddress
[ OK ] LlvmLibcMemmoveTest.DstAndSrcPointToSameAddress (5 us)
[ RUN ] LlvmLibcMemmoveTest.DstStartsBeforeSrc
[ OK ] LlvmLibcMemmoveTest.DstStartsBeforeSrc (5 us)
[ RUN ] LlvmLibcMemmoveTest.DstStartsAfterSrc
[ OK ] LlvmLibcMemmoveTest.DstStartsAfterSrc (5 us)
[ RUN ] LlvmLibcMemmoveTest.SrcFollowDst
[ OK ] LlvmLibcMemmoveTest.SrcFollowDst (5 us)
[ RUN ] LlvmLibcMemmoveTest.DstFollowSrc
[ OK ] LlvmLibcMemmoveTest.DstFollowSrc (4 us)
[ RUN ] LlvmLibcMemmoveTest.SizeSweep
/home/botworker/builds/openmp-offload-amdgpu-runtime-2/llvm.src/libc/test/src/string/memmove_test.cpp:98: FAILURE
Expected: CheckMemmove(Buffer, static_cast<size_t>(Size), Overlap)
Which is: 0
To be equal to: true
Which is: 1
[ FAILED ] LlvmLibcMemmoveTest.SizeSweep

I mean, I do not see here what exactly happened or even how to build and run it...

rampitec added a commit that referenced this pull request Aug 18, 2025
…r uses (#154183)"

This reverts commit 3395676.

Fails  libc/test/src/string/libc.test.src.string.memmove_test.__hermetic__
@rampitec
Copy link
Collaborator Author

I will revert this for now: #154219

In general I'll need to get to the IR of the failing test somehow.

rampitec added a commit that referenced this pull request Aug 18, 2025
…r uses (#154183)" (#154219)

This reverts commit 3395676.

Fails
libc/test/src/string/libc.test.src.string.memmove_test.__hermetic__
@Kewen12
Copy link
Contributor

Kewen12 commented Aug 18, 2025

Thanks for reverting it.

For local repro I used the cmake config:

-DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DLLVM_ENABLE_ASSERTIONS=ON '-DLLVM_LIT_ARGS=-vv --show-unsupported --timeout 120 --show-xfail -j 64' '-DLLVM_TARGETS_TO_BUILD=X86;AMDGPU' '-DLLVM_ENABLE_RUNTIMES=compiler-rt;libunwind;libcxx;libcxxabi;openmp;offload' '-DLLVM_ENABLE_PROJECTS=clang;lld;llvm' -DCLANG_DEFAULT_LINKER=lld -DLIBCXX_ENABLE_SHARED=OFF -DLIBCXX_ENABLE_STATIC=ON -DLIBCXX_INSTALL_LIBRARY=OFF -DLIBCXX_INSTALL_HEADERS=OFF -DLIBCXXABI_ENABLE_SHARED=OFF -DLIBCXXABI_ENABLE_STATIC=ON -DLIBCXXABI_INSTALL_STATIC_LIBRARY=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_ZLIB=ON -DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=ON -DCMAKE_CXX_STANDARD=17 -DBUILD_SHARED_LIBS=ON -DLLVM_ENABLE_LIBCXX=ON -DCLANG_DEFAULT_RTLIB=compiler-rt -DCLANG_DEFAULT_UNWINDLIB=libgcc -DLIBOMPTARGET_PLUGINS_TO_BUILD='amdgpu;host' -DPACKAGE_VENDOR='LLVM' -DLLVM_RUNTIME_TARGETS='default;amdgcn-amd-amdhsa' -DRUNTIMES_amdgcn-amd-amdhsa_LLVM_ENABLE_RUNTIMES=libc

and run the test with:

ninja check-libc-amdgcn-amd-amdhsa

Bot config for reference: https://github.com/llvm/llvm-zorg/blob/main/buildbot/osuosl/master/config/builders.py#L1968-L2015

@rampitec
Copy link
Collaborator Author

Thanks for reverting it.

Sure. I cannot fix it quickly.

For local repro I used the cmake config:

-DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DLLVM_ENABLE_ASSERTIONS=ON '-DLLVM_LIT_ARGS=-vv --show-unsupported --timeout 120 --show-xfail -j 64' '-DLLVM_TARGETS_TO_BUILD=X86;AMDGPU' '-DLLVM_ENABLE_RUNTIMES=compiler-rt;libunwind;libcxx;libcxxabi;openmp;offload' '-DLLVM_ENABLE_PROJECTS=clang;lld;llvm' -DCLANG_DEFAULT_LINKER=lld -DLIBCXX_ENABLE_SHARED=OFF -DLIBCXX_ENABLE_STATIC=ON -DLIBCXX_INSTALL_LIBRARY=OFF -DLIBCXX_INSTALL_HEADERS=OFF -DLIBCXXABI_ENABLE_SHARED=OFF -DLIBCXXABI_ENABLE_STATIC=ON -DLIBCXXABI_INSTALL_STATIC_LIBRARY=OFF -DLLVM_ENABLE_Z3_SOLVER=OFF -DLLVM_ENABLE_ZLIB=ON -DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=ON -DCMAKE_CXX_STANDARD=17 -DBUILD_SHARED_LIBS=ON -DLLVM_ENABLE_LIBCXX=ON -DCLANG_DEFAULT_RTLIB=compiler-rt -DCLANG_DEFAULT_UNWINDLIB=libgcc -DLIBOMPTARGET_PLUGINS_TO_BUILD='amdgpu;host' -DPACKAGE_VENDOR='LLVM' -DLLVM_RUNTIME_TARGETS='default;amdgcn-amd-amdhsa' -DRUNTIMES_amdgcn-amd-amdhsa_LLVM_ENABLE_RUNTIMES=libc

Yep, already did that.

and run the test with:

ninja check-libc-amdgcn-amd-amdhsa

That is where I really cannot see what's happening. I really unable to run the test as this is an offline builder:

-- No GPU architecture detected or provided, tests will not be built

Do you happen to know:

  1. On which gpu arch did it fail? Unclear from the log.
  2. Is there a way to force the gpu arch just to build the test?

In essence all I need is an IR of the failing test, all I need is to compare what it compiles to before and after the change. If by the chance you have the IR I would appreciate it.

@rampitec
Copy link
Collaborator Author

That is where I really cannot see what's happening. I really unable to run the test as this is an offline builder:

-- No GPU architecture detected or provided, tests will not be built

Do you happen to know:

1. On which gpu arch did it fail? Unclear from the log.

2. Is there a way to force the gpu arch just to build the test?

I.e., this line in the test log isn't really helpful:

-- Using GPU architecture detected on the system for testing: 'native'

@Kewen12
Copy link
Contributor

Kewen12 commented Aug 19, 2025

I reproduced the test failure on MI210. Hi @jplehr do you know how to get the IR for the libc test?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants