diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -10359,6 +10359,8 @@ ret i32* %t5 } +.. _getelementptr_inbounds: + If the ``inbounds`` keyword is present, the result value of the ``getelementptr`` is a :ref:`poison value ` if one of the following rules is violated: @@ -20813,6 +20815,85 @@ 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* readnone , i64 , i64 ) nofree nosync nounwind readnone speculatable willreturn + +Overview: +""""""""" + +The '``llvm.memory.region.decl``' intrinsic annotates a 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. + +Note that the ``begin_offset`` is non-positive (``begin_offset s<= 0``) +and ``end_offset`` is non-negative (``end_offset s>= 0``) by definition, +otherwise the ``ptr`` lies outside of declared memory region, +and the returned pointer is a :ref:`poison value `. + +The intent of this intrinsic is to declare a memory region that is +a (not necessarily strict) subset of an underlying allocated object, +as far as :ref:`getelementptr ` is concerned. + +The storage of the returned sub-object is shared with the parent object, +but note that the intrinsic does **not** state that the memory region +*actually* points into the allocated object. + +Once a region is declared, it is **NOT** possible to define a strict superset +of this region **in this def-use chain**, but it is legal to define nested +(not necessarily strict) subsets. + +The only ``inbounds`` addresses that can be computed from pointer ``%ptr`` +are those that lie within offsets ``[begin_offset, end_offset]`` from that +pointer. Other offsets will result in a :ref:`poison value `. + +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. + +Example: +"""""""" + +:: + + %ptr = call i8* @llvm.memory.region.decl(i8* %baseptr, i64 -24, i64 8) + +declares that the ``%baseptr`` lies `+24` bytes into the memory region +that consists of `32` bytes total. + +The only :ref:`inbounds ` addresses that can be computed +from pointer ``%ptr`` are those that lie within offsets ``[-24, 8]`` from +that pointer. Other offsets will result in a :ref:`poison value `. +(Provided that there actually is an underlying allocated object, that is +a superset of the declared memory region.) + +Note that ``%ptr + 8`` is an (``inbounds``!) `end` pointer +that can not be dereferenced. + .. _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 @@ -1187,6 +1187,14 @@ [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, 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 @@ -6636,6 +6636,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 +}