Index: llvm/lib/Analysis/InstructionSimplify.cpp =================================================================== --- llvm/lib/Analysis/InstructionSimplify.cpp +++ llvm/lib/Analysis/InstructionSimplify.cpp @@ -35,6 +35,7 @@ #include "llvm/IR/GlobalAlias.h" #include "llvm/IR/InstrTypes.h" #include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Operator.h" #include "llvm/IR/PatternMatch.h" #include "llvm/IR/ValueHandle.h" @@ -5137,8 +5138,29 @@ return nullptr; } -static Value *simplifyIntrinsic(CallBase *Call, const SimplifyQuery &Q) { +static Value *simplifySideNoalias(const Value *V) { + const IntrinsicInst *II = cast(V); + assert(II->getIntrinsicID() == Intrinsic::side_noalias); + + // check for undef + Value *Op0 = II->getOperand(0); + if (isa(Op0) || (isa(Op0) && + Op0->getType()->getPointerAddressSpace() == 0)) { + return Op0; + } + + // check for compatibility + if (llvm::IntrinsicInst *DepII = dyn_cast(Op0)) { + if (DepII->getIntrinsicID() == Intrinsic::side_noalias) { + if (llvm::areSideNoaliasCompatible(DepII, II)) { + return DepII; + } + } + } + return nullptr; +} +static Value *simplifyIntrinsic(CallBase *Call, const SimplifyQuery &Q) { // Intrinsics with no operands have some kind of side effect. Don't simplify. unsigned NumOperands = Call->getNumArgOperands(); if (!NumOperands) @@ -5146,18 +5168,6 @@ Function *F = cast(Call->getCalledFunction()); Intrinsic::ID IID = F->getIntrinsicID(); - - // We can remove a noalias intrinsic if the pointer value is undef (by - // forwarding the undef). The same goes for an address-space-zero null - // pointer. - if (IID == Intrinsic::noalias) { - Value *Arg0 = Call->getArgOperand(0); - if (isa(Arg0) || - (isa(Arg0) && - Arg0->getType()->getPointerAddressSpace() == 0)) - return Arg0; - } - if (NumOperands == 1) return simplifyUnaryIntrinsic(F, Call->getArgOperand(0), Q); @@ -5207,6 +5217,20 @@ return V; return nullptr; } + case Intrinsic::noalias: + case Intrinsic::noalias_arg_guard: { + // only follow the plain path if undefined + Value *Op0 = Call->getArgOperand(0); + if (isa(Op0) || + (isa(Op0) && + Op0->getType()->getPointerAddressSpace() == 0)) { + return Op0; + } + return nullptr; + } + case Intrinsic::side_noalias: { + return simplifySideNoalias(Call); + } default: return nullptr; } Index: llvm/lib/Transforms/Utils/Local.cpp =================================================================== --- llvm/lib/Transforms/Utils/Local.cpp +++ llvm/lib/Transforms/Utils/Local.cpp @@ -421,8 +421,25 @@ // noalias intrinsics are dead if they have no uses (they're tagged as // writing, but that is only to maintain control dependencies, not because // they actually write anything). - if (II->getIntrinsicID() == Intrinsic::noalias) + if (II->getIntrinsicID() == Intrinsic::noalias || + II->getIntrinsicID() == Intrinsic::side_noalias || + II->getIntrinsicID() == Intrinsic::noalias_copy_guard || + II->getIntrinsicID() == Intrinsic::noalias_arg_guard) return II->use_empty(); + + // llvm.noalias.decl which operand only has a single use are trivially dead. + // NOTE: this assumes that noalias intrinsics nodes are never combined ! + if (II->getIntrinsicID() == Intrinsic::noalias_decl) { + auto *DAA = II->getOperand(Intrinsic::NoAliasDeclAllocaArg); + if (isa(DAA)) { + // no associated alloca -> if there are no uses, it is trivially dead + return II->getNumUses() == 0; + } else { + return (DAA->getNumUses() == 1) && + (II->getOperand(Intrinsic::NoAliasDeclScopeArg)->getNumUses() == + 1); + } + } } if (isAllocLikeFn(I, TLI)) Index: llvm/test/Transforms/InstSimplify/noalias.ll =================================================================== --- llvm/test/Transforms/InstSimplify/noalias.ll +++ llvm/test/Transforms/InstSimplify/noalias.ll @@ -1,33 +1,62 @@ ; RUN: opt -instsimplify -S < %s | FileCheck %s -define void @test1(i8* %ptr) { - call i8* @llvm.noalias.p0i8(i8* %ptr, metadata !1) +define void @test01(i8* %ptr) { + call i8* @llvm.noalias.p0i8.p0i8.p0p0i8.i32(i8* %ptr, i8* null, i8** null, i32 0, metadata !1) ret void -; CHECK-LABEL: @test1 +; CHECK-LABEL: @test01 ; CHECK-NOT: llvm.noalias.p0i8 ; CHECK: ret void } -define i8* @test2a() { - %v = call i8* @llvm.noalias.p0i8(i8* null, metadata !1) +define i8* @test02() { + %v = call i8* @llvm.noalias.p0i8.p0i8.p0p0i8.i32(i8* null, i8* null, i8** null, i32 0, metadata !1) ret i8* %v -; CHECK-LABEL: @test2a +; CHECK-LABEL: @test02 ; CHECK-NOT: llvm.noalias.p0i8 ; CHECK: ret i8* null } -define i8* @test2() { - %v = call i8* @llvm.noalias.p0i8(i8* undef, metadata !1) +define i8* @test03() { + %v = call i8* @llvm.noalias.p0i8.p0i8.p0p0i8.i32(i8* undef, i8* null, i8** null, i32 0, metadata !1) ret i8* %v -; CHECK-LABEL: @test2 +; CHECK-LABEL: @test03 ; CHECK-NOT: llvm.noalias.p0i8 ; CHECK: ret i8* undef } -declare i8* @llvm.noalias.p0i8(i8*, metadata) nounwind +declare i8* @llvm.noalias.p0i8.p0i8.p0p0i8.i32(i8*, i8*, i8**, i32, metadata ) nounwind + +define void @test11(i8* %ptr) { + call i8* @llvm.side.noalias.p0i8.p0i8.p0p0i8.p0p0i8.i32(i8* %ptr, i8* null, i8** null, i8** null, i32 0, metadata !1) + ret void + +; CHECK-LABEL: @test11 +; CHECK-NOT: llvm.side.noalias.p0i8 +; CHECK: ret void +} + +define i8* @test12() { + %v = call i8* @llvm.side.noalias.p0i8.p0i8.p0p0i8.p0p0i8.i32(i8* null, i8* null, i8** null, i8** null, i32 0, metadata !1) + ret i8* %v + +; CHECK-LABEL: @test12 +; CHECK-NOT: llvm.side.noalias.p0i8 +; CHECK: ret i8* null +} + +define i8* @test13() { + %v = call i8* @llvm.side.noalias.p0i8.p0i8.p0p0i8.p0p0i8.i32(i8* undef, i8* null, i8** null, i8** null, i32 0, metadata !1) + ret i8* %v + +; CHECK-LABEL: @test13 +; CHECK-NOT: llvm.side.noalias.p0i8 +; CHECK: ret i8* undef +} + +declare i8* @llvm.side.noalias.p0i8.p0i8.p0p0i8.p0p0i8.i32(i8*, i8*, i8**, i8**, i32, metadata ) nounwind !0 = !{!0, !"some domain"} !1 = !{!1, !0, !"some scope"}