diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -16372,6 +16372,42 @@ On the other hand, if constant folding is not run, it will never evaluate to true, even in simple cases. +.. _int_ptrmask: + +'``llvm.ptrmask``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare ptrty llvm.ptrmask(ptrty %ptr, intty %mask) readnone + +Arguments: +"""""""""" + +The first argument is a pointer. The second argument is an integer. + +Overview: +"""""""""" + +The ``llvm.ptrmask`` intrinsic can be used to mask out bits of a pointer that +must be zero when accessing it, because of ABI alignment requirements or +a restriction of the meaningful bits through the data layout. This can be used +to strip data from tagged pointers without using ptrtoint/inttoptr and allows +for better handling during alias analysis. + +Semantics: +"""""""""" + +The mask argument must only zero out bits that are not part of the relevant bits +of the pointer. Passing masks that zero out relevant bits of the pointer is +undefined behavior. The relevant bits of a pointer include the bitwidth specified +in the data layout minus the bits required by the ABI alignment requirement. +The intrinsic will get lowered to a bitwise and instruction or equivalent. Both +the returned pointer and the first argument point to the same underlying object. + Stack Map Intrinsics -------------------- 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 @@ -1021,6 +1021,9 @@ // Intrinsic to detect whether its argument is a constant. def int_is_constant : Intrinsic<[llvm_i1_ty], [llvm_any_ty], [IntrNoMem], "llvm.is.constant">; +// Intrinsic to mask out bits of a pointer that must be zero when the pointer +// is used to access memory. +def int_ptrmask: Intrinsic<[llvm_anyptr_ty], [llvm_anyptr_ty, llvm_anyint_ty], [IntrNoMem]>; //===-------------------------- Masked Intrinsics -------------------------===// // @@ -1164,6 +1167,7 @@ [llvm_anyvector_ty], [IntrNoMem]>; + //===----- Intrinsics that are used to provide predicate information -----===// def int_ssa_copy : Intrinsic<[llvm_any_ty], [LLVMMatchType<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 @@ -6804,6 +6804,17 @@ // MachineFunction in SelectionDAGISel::PrepareEHLandingPad. We can safely // delete it now. return nullptr; + case Intrinsic::ptrmask: { + SDValue Ptr = getValue(I.getOperand(0)); + SDValue Const = getValue(I.getOperand(1)); + + EVT DestVT = + EVT(DAG.getTargetLoweringInfo().getPointerTy(DAG.getDataLayout())); + + setValue(&I, DAG.getNode(ISD::AND, getCurSDLoc(), DestVT, Ptr, + DAG.getZExtOrTrunc(Const, getCurSDLoc(), DestVT))); + return nullptr; + } } } diff --git a/llvm/test/CodeGen/AArch64/lower-ptrmask.ll b/llvm/test/CodeGen/AArch64/lower-ptrmask.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/lower-ptrmask.ll @@ -0,0 +1,29 @@ +; RUN: llc -mtriple=arm64-apple-iphoneos -stop-after=expand-isel-pseudos %s -o - | FileCheck %s + +declare i8* @llvm.ptrmask.p0i8.i64(i8* , i64) + +; CHECK-LABEL: name: test1 +; CHECK: %0:gpr64 = COPY $x0 +; CHECK-NEXT: %1:gpr64sp = ANDXri %0, 8052 +; CHECK-NEXT: $x0 = COPY %1 +; CHECK-NEXT: RET_ReallyLR implicit $x0 + +define i8* @test1(i8* %src) { + %ptr = call i8* @llvm.ptrmask.p0i8.i64(i8* %src, i64 72057594037927928) + ret i8* %ptr +} + +declare i8* @llvm.ptrmask.p0i8.i32(i8*, i32) + +; CHECK-LABEL: name: test2 +; CHECK: %0:gpr64 = COPY $x0 +; CHECK-NEXT: %1:gpr32 = MOVi32imm 10000 +; CHECK-NEXT: %2:gpr64 = SUBREG_TO_REG 0, killed %1, %subreg.sub_32 +; CHECK-NEXT: %3:gpr64 = ANDXrr %0, killed %2 +; CHECK-NEXT: $x0 = COPY %3 +; CHECK-NEXT: RET_ReallyLR implicit $x0 + +define i8* @test2(i8* %src) { + %ptr = call i8* @llvm.ptrmask.p0i8.i32(i8* %src, i32 10000) + ret i8* %ptr +} diff --git a/llvm/test/CodeGen/X86/lower-ptrmask.ll b/llvm/test/CodeGen/X86/lower-ptrmask.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/lower-ptrmask.ll @@ -0,0 +1,31 @@ +; RUN: llc -mtriple=x86_64-apple-macosx -stop-after=expand-isel-pseudos %s -o - | FileCheck %s + +declare i8* @llvm.ptrmask.p0i8.i64(i8* , i64) + +; CHECK-LABEL: name: test1 +; CHECK: %0:gr64 = COPY $rdi +; CHECK-NEXT: %1:gr64 = MOV64ri 72057594037927928 +; CHECK-NEXT: %2:gr64 = AND64rr %0, killed %1, implicit-def dead $eflags +; CHECK-NEXT: $rax = COPY %2 +; CHECK-NEXT: RET 0, $rax + +define i8* @test1(i8* %src) { + %ptr = call i8* @llvm.ptrmask.p0i8.i64(i8* %src, i64 72057594037927928) + ret i8* %ptr +} + +declare i8* @llvm.ptrmask.p0i8.i32(i8*, i32) + +; CHECK-LABEL: name: test2 +; CHECK: %0:gr64 = COPY $rdi +; CHECK-NEXT: %1:gr32 = COPY %0.sub_32bit +; CHECK-NEXT: %2:gr32 = AND32ri %1, 10000, implicit-def dead $eflags +; CHECK-NEXT: %3:gr64 = SUBREG_TO_REG 0, killed %2, %subreg.sub_32bit +; CHECK-NEXT: $rax = COPY %3 +; CHECK-NEXT: RET 0, $rax + + +define i8* @test2(i8* %src) { + %ptr = call i8* @llvm.ptrmask.p0i8.i32(i8* %src, i32 10000) + ret i8* %ptr +}