diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -20391,6 +20391,45 @@ It does not read any memory and can be speculated. +.. _int_memory_region_decl: + +'``llvm.memory.region.decl``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare i8* @llvm.memory.region.decl.p0i8(i8* nocapture readnone returned , i64 , i64 ) nofree nosync nounwind readnone speculatable willreturn + +Overview: +""""""""" + +The '``llvm.memory.region.decl``' intrinsic annotates memory region. + +Arguments: +"""""""""" + +This is an overloaded intrinsic. The memory region can belong to any address +space. The first argument is a pointer into the memory region. The returned +pointer, which is the first argument, must belong to the same address space +as the argument. The second argument specifies the offset to the pointer (the +first argument) at which the memory region begins. The third argument specifies +the offset to the pointer (the first argument) at which the memory region ends. + +Semantics: +"""""""""" + +The returned pointer, and, transitively, any pointer that is def-use based on +that pointer, points into the memory region ``[ptr+begin_offset, ptr+end_offset)``, +or is a :ref:`poison value ` otherwise. + +This intrinsic is intended to be an optimization hint, there are no correctness +concerns with completely ignoring and/or dropping it. The main use-case is +to be able to annotate array bounds in C family of languages, +which may allow alloca splitting, and better alias analysis. + .. _constrainedfp: diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -1176,6 +1176,15 @@ [LLVMMatchType<0>], [IntrSpeculatable, IntrNoMem, IntrWillReturn]>; +// Declares that the returned pointer (the first argument), +// and any pointer that is (transitively) def-use based on that pointer, +// points into the memory region [ptr+begin_offset, ptr+end_offset), +// or is poison otherwise. +def int_memory_region_decl : DefaultAttrsIntrinsic<[llvm_anyptr_ty], + [LLVMMatchType<0> /*ptr*/, llvm_i64_ty /*begin_offset*/, + llvm_i64_ty /*end_offset*/], [IntrNoMem, IntrSpeculatable, + NoCapture>, Returned>, ReadNone>]>; + //===------------------------ Stackmap Intrinsics -------------------------===// // def int_experimental_stackmap : DefaultAttrsIntrinsic<[], diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp --- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp +++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp @@ -2145,7 +2145,8 @@ case Intrinsic::annotation: case Intrinsic::ptr_annotation: case Intrinsic::launder_invariant_group: - case Intrinsic::strip_invariant_group: { + case Intrinsic::strip_invariant_group: + case Intrinsic::memory_region_decl: { // Drop the intrinsic, but forward the value. MIRBuilder.buildCopy(getOrCreateVReg(CI), getOrCreateVReg(*CI.getArgOperand(0))); diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -6620,6 +6620,7 @@ case Intrinsic::ptr_annotation: case Intrinsic::launder_invariant_group: case Intrinsic::strip_invariant_group: + case Intrinsic::memory_region_decl: // Drop the intrinsic, but forward the value setValue(&I, getValue(I.getOperand(0))); return; diff --git a/llvm/test/CodeGen/AMDGPU/GlobalISel/memory_region_decl.ll b/llvm/test/CodeGen/AMDGPU/GlobalISel/memory_region_decl.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AMDGPU/GlobalISel/memory_region_decl.ll @@ -0,0 +1,21 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -global-isel -mtriple=amdgcn-unknown-amdhsa < %s | FileCheck %s + +declare i8* @llvm.memory.region.decl.p0i8(i8*, i64, i64) + +define i8* @test_i8(i8* %ptr, i64 %begin_off, i64 %end_off) { +; CHECK-LABEL: test_i8: +; CHECK: ; %bb.0: +; CHECK-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; CHECK-NEXT: s_setpc_b64 s[30:31] + %r = call i8* @llvm.memory.region.decl.p0i8(i8* %ptr, i64 %begin_off, i64 %end_off) + ret i8* %r +} + +define i8* @test_i8_naive(i8* %ptr, i64 %begin_off, i64 %end_off) { +; CHECK-LABEL: test_i8_naive: +; CHECK: ; %bb.0: +; CHECK-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) +; CHECK-NEXT: s_setpc_b64 s[30:31] + ret i8* %ptr +} diff --git a/llvm/test/CodeGen/X86/memory_region_decl.ll b/llvm/test/CodeGen/X86/memory_region_decl.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/memory_region_decl.ll @@ -0,0 +1,32 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s -mtriple=x86_64-unknown-linux | FileCheck %s --check-prefixes=X64 +; RUN: llc < %s -mtriple=i686-unknown-linux | FileCheck %s --check-prefixes=X86 + +declare i8* @llvm.memory.region.decl.p0i8(i8*, i64, i64) + +define i8* @test_i8(i8* %ptr, i64 %begin_off, i64 %end_off) { +; X64-LABEL: test_i8: +; X64: # %bb.0: +; X64-NEXT: movq %rdi, %rax +; X64-NEXT: retq +; +; X86-LABEL: test_i8: +; X86: # %bb.0: +; X86-NEXT: movl {{[0-9]+}}(%esp), %eax +; X86-NEXT: retl + %r = call i8* @llvm.memory.region.decl.p0i8(i8* %ptr, i64 %begin_off, i64 %end_off) + ret i8* %r +} + +define i8* @test_i8_naive(i8* %ptr, i64 %begin_off, i64 %end_off) { +; X64-LABEL: test_i8_naive: +; X64: # %bb.0: +; X64-NEXT: movq %rdi, %rax +; X64-NEXT: retq +; +; X86-LABEL: test_i8_naive: +; X86: # %bb.0: +; X86-NEXT: movl {{[0-9]+}}(%esp), %eax +; X86-NEXT: retl + ret i8* %ptr +}