Index: llvm/docs/LangRef.rst =================================================================== --- llvm/docs/LangRef.rst +++ llvm/docs/LangRef.rst @@ -16443,6 +16443,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 masks out bits of the pointer according to a mask. +This allows stripping data from tagged pointers without converting them to an +integer (ptrtoint/inttoptr). As a consequence, we can preserve more information +to facilitate alias analysis and underlying object detection. + +Semantics: +"""""""""" + +The effect of this intrinsic is to ensure that +``ptrtoint(ptrmask(ptr, mask)) == (ptrtoint(ptr) & mask)``. Both the returned +pointer and the first argument are based on the same underlying object (for more +information on the *based* on terminology see +:ref:`the pointer aliasing rules `). If the bitwidth of the +mask argument does not match the pointer size of the target, the mask is +zero-extended or truncated accordingly. + Stack Map Intrinsics -------------------- Index: llvm/include/llvm/IR/Intrinsics.td =================================================================== --- llvm/include/llvm/IR/Intrinsics.td +++ llvm/include/llvm/IR/Intrinsics.td @@ -1028,6 +1028,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. +def int_ptrmask: Intrinsic<[llvm_anyptr_ty], [llvm_anyptr_ty, llvm_anyint_ty], + [IntrNoMem, IntrSpeculatable]>; //===-------------------------- Masked Intrinsics -------------------------===// // Index: llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -6809,6 +6809,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; + } } } Index: llvm/test/CodeGen/AArch64/lower-ptrmask.ll =================================================================== --- /dev/null +++ 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 +} Index: llvm/test/CodeGen/X86/lower-ptrmask.ll =================================================================== --- /dev/null +++ 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 +}