[llvm] 431a821 - [SPIRV] Add support for selection of G_MEMCPY_INLINE (#201925)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Jun 9 07:07:21 PDT 2026
Author: Nick Sarnie
Date: 2026-06-09T14:07:15Z
New Revision: 431a821cbbb66f89d0cd7d97c6eb7792801fd90e
URL: https://github.com/llvm/llvm-project/commit/431a821cbbb66f89d0cd7d97c6eb7792801fd90e
DIFF: https://github.com/llvm/llvm-project/commit/431a821cbbb66f89d0cd7d97c6eb7792801fd90e.diff
LOG: [SPIRV] Add support for selection of G_MEMCPY_INLINE (#201925)
`G_MEMCPY_INLINE` is the same as `G_MEMCPY` but it's supposed to
guarantee the instruction will not be lowered to an external function
call. This is useful for projects like `libc`.
In SPIR-V, we would never lower to an external function call, we always
lower `G_MEMCPY` to `OpCopyMemory` or `OpCopyMemorySized`, or the copy
is optimized out, so we should be able to handle `G_MEMCPY_INLINE` and
`G_MEMCPY` the same.
Co-Authored-By: Claude Opus 4.8
[noreply at anthropic.com](mailto:noreply at anthropic.com)
Signed-off-by: Nick Sarnie <nick.sarnie at intel.com>
Co-authored-by: Claude Opus 4.8 <noreply at anthropic.com>
Added:
llvm/test/CodeGen/SPIRV/llvm-intrinsics/logical-memcpy.inline.ll
llvm/test/CodeGen/SPIRV/llvm-intrinsics/memcpy.inline.ll
Modified:
llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
Removed:
################################################################################
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index d94e52002fdd3..dc8a7c2148aa9 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -1030,6 +1030,7 @@ bool SPIRVInstructionSelector::spvSelect(Register ResVReg,
}
case TargetOpcode::G_MEMMOVE:
case TargetOpcode::G_MEMCPY:
+ case TargetOpcode::G_MEMCPY_INLINE:
case TargetOpcode::G_MEMSET:
return selectMemOperation(ResVReg, I);
diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
index 099243acc8819..62827a2151a64 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
@@ -303,7 +303,7 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
getActionDefinitionsBuilder(G_UNMERGE_VALUES)
.legalIf(vectorElementCountIsLessThanOrEqualTo(1, MaxVectorSize));
- getActionDefinitionsBuilder({G_MEMCPY, G_MEMMOVE})
+ getActionDefinitionsBuilder({G_MEMCPY, G_MEMCPY_INLINE, G_MEMMOVE})
.unsupportedIf(LegalityPredicates::any(typeIs(0, p9), typeIs(1, p9)))
.legalIf(all(typeInSet(0, allPtrs), typeInSet(1, allPtrs)));
diff --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/logical-memcpy.inline.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/logical-memcpy.inline.ll
new file mode 100644
index 0000000000000..63f8eac9dd99b
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/logical-memcpy.inline.ll
@@ -0,0 +1,27 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv-unknown-unknown %s -o - | FileCheck -implicit-check-not=OpFunctionCall %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: OpName %[[#Dst:]] "dst"
+; CHECK-DAG: OpName %[[#Src:]] "src"
+
+; CHECK-DAG: %[[#Float32:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#StructS:]] = OpTypeStruct %[[#Float32]] %[[#Float32]] %[[#Float32]] %[[#Float32]] %[[#Float32]]
+; CHECK-DAG: %[[#Ptr_CrossWG_StructS:]] = OpTypePointer CrossWorkgroup %[[#StructS]]
+%struct.S = type <{ float, float, float, float, float }>
+
+; CHECK-DAG: %[[#Src]] = OpVariable %[[#Ptr_CrossWG_StructS]] CrossWorkgroup
+ at src = external dso_local addrspace(1) global %struct.S, align 4
+
+; CHECK-DAG: %[[#Dst]] = OpVariable %[[#Ptr_CrossWG_StructS]] CrossWorkgroup
+ at dst = external dso_local addrspace(1) global %struct.S, align 4
+
+; CHECK: OpCopyMemory %[[#Dst]] %[[#Src]]
+
+define void @main() local_unnamed_addr #0 {
+ call void @llvm.memcpy.inline.p0.p0.i64(ptr addrspace(1) align 4 @dst, ptr addrspace(1) align 4 @src, i64 20, i1 false)
+ ret void
+}
+
+declare void @llvm.memcpy.inline.p0.p0.i64(ptr addrspace(1) captures(none), ptr addrspace(1) captures(none) readonly, i64 immarg, i1 immarg)
+
+attributes #0 = { "hlsl.numthreads"="8,1,1" "hlsl.shader"="compute" }
diff --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/memcpy.inline.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/memcpy.inline.ll
new file mode 100644
index 0000000000000..7945e5a5648bb
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/memcpy.inline.ll
@@ -0,0 +1,31 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck -implicit-check-not=OpFunctionCall %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: %[[#Int8:]] = OpTypeInt 8 0
+; CHECK-DAG: %[[#Int32:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#Int64:]] = OpTypeInt 64 0
+; CHECK-DAG: %[[#Ptr_CrossWG_8:]] = OpTypePointer CrossWorkgroup %[[#Int8]]
+; CHECK-DAG: %[[#Const_64:]] = OpConstant %[[#Int32]] 64
+; CHECK-DAG: %[[#Const_30:]] = OpConstant %[[#Int32]] 30
+
+; CHECK: %[[#Param1:]] = OpFunctionParameter %[[#Ptr_CrossWG_8]]
+; CHECK: %[[#Param2:]] = OpFunctionParameter %[[#Ptr_CrossWG_8]]
+; CHECK: %[[#Size1:]] = OpUConvert %[[#Int64]] %[[#Const_64]]
+; CHECK: OpCopyMemorySized %[[#Param2]] %[[#Param1]] %[[#Size1]] Aligned 64
+
+; CHECK: %[[#Param3:]] = OpFunctionParameter %[[#Ptr_CrossWG_8]]
+; CHECK: %[[#Param4:]] = OpFunctionParameter %[[#Ptr_CrossWG_8]]
+; CHECK: %[[#Size2:]] = OpUConvert %[[#Int64]] %[[#Const_30]]
+; CHECK: OpCopyMemorySized %[[#Param4]] %[[#Param3]] %[[#Size2]] Aligned 1
+
+define spir_kernel void @test_full_copy(ptr addrspace(1) captures(none) readonly %in, ptr addrspace(1) captures(none) %out) {
+ call void @llvm.memcpy.inline.p1.p1.i32(ptr addrspace(1) align 64 %out, ptr addrspace(1) align 64 %in, i32 64, i1 false)
+ ret void
+}
+
+define spir_kernel void @test_array(ptr addrspace(1) %in, ptr addrspace(1) %out) {
+ call void @llvm.memcpy.inline.p1.p1.i32(ptr addrspace(1) %out, ptr addrspace(1) %in, i32 30, i1 false)
+ ret void
+}
+
+declare void @llvm.memcpy.inline.p1.p1.i32(ptr addrspace(1) captures(none), ptr addrspace(1) captures(none) readonly, i32 immarg, i1 immarg)
More information about the llvm-commits
mailing list