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" @@ -5130,6 +5131,13 @@ // TODO: maxnum(nnan ninf x, -flt_max) -> x break; } + case Intrinsic::noalias_arg_guard: { + // Only follow the plain path if undefined. Do not propagate null, that + // would incorrectly omit noalias information. + if (isa(Op0)) + return Op0; + break; + } default: break; } @@ -5137,8 +5145,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); + + // Only follow the plain path if undefined. Do not propagate null, that + // would incorrectly omit noalias information. + Value *Op0 = II->getOperand(0); + if (isa(Op0)) { + return Op0; + } + + // Check for compatibility: side_noalias(side_noalias) -> side_noalias + 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 +5175,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 +5224,18 @@ return V; return nullptr; } + case Intrinsic::noalias: + case Intrinsic::noalias_copy_guard: { + // Only follow the plain path if undefined. Do not propagate null, that + // would incorrectly omit noalias information. + if (auto Op0 = dyn_cast(Call->getArgOperand(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,172 @@ ; 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-NOT: llvm.noalias.p0i8 -; CHECK: ret i8* null +; CHECK-LABEL: @test02 +; CHECK: llvm.noalias.p0i8 +; CHECK: ret i8* %v } -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: llvm.side.noalias.p0i8 +; CHECK: ret i8* %v +} + +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 +} + +define i8* @test14() { + %u = call i8* @llvm.side.noalias.p0i8.p0i8.p0p0i8.p0p0i8.i32(i8* null, i8* null, i8** null, i8** null, i32 0, metadata !1) + %v = call i8* @llvm.side.noalias.p0i8.p0i8.p0p0i8.p0p0i8.i32(i8* %u, i8* null, i8** null, i8** null, i32 0, metadata !1) + ret i8* %v + +; CHECK-LABEL: @test14 +; CHECK: llvm.side.noalias.p0i8 +; CHECK-NOT: llvm.side.noalias.p0i8 +; CHECK: ret i8* %u +} + +define i8* @test15() { + %u = call i8* @llvm.side.noalias.p0i8.p0i8.p0p0i8.p0p0i8.i32(i8* null, i8* null, i8** null, i8** null, i32 1, metadata !1) + %v = call i8* @llvm.side.noalias.p0i8.p0i8.p0p0i8.p0p0i8.i32(i8* %u, i8* null, i8** null, i8** null, i32 0, metadata !1) + ret i8* %v + +; CHECK-LABEL: @test15 +; CHECK: llvm.side.noalias.p0i8 +; CHECK: llvm.side.noalias.p0i8 +; CHECK: ret i8* %v +} + +define i8* @test20() { + %u = call i8* @llvm.side.noalias.p0i8.p0i8.p0p0i8.p0p0i8.i32(i8* null, i8* null, i8** null, i8** null, i32 0, metadata !1) + %v = call i8* @llvm.noalias.arg.guard.p0i8.p0i8(i8* null, i8* %u) + ret i8* %v + +; CHECK-LABEL: @test20 +; CHECK: llvm.side.noalias.p0i8 +; CHECK: llvm.noalias.arg.guard.p0i8 +; CHECK: ret i8* %v +} + +define i8* @test21() { + %u = call i8* @llvm.side.noalias.p0i8.p0i8.p0p0i8.p0p0i8.i32(i8* null, i8* null, i8** null, i8** null, i32 0, metadata !1) + %v = call i8* @llvm.noalias.arg.guard.p0i8.p0i8(i8* undef, i8* %u) + ret i8* %v + +; CHECK-LABEL: @test21 +; CHECK-NOT: llvm.side.noalias.p0i8 +; CHECK-NOT: llvm.noalias.arg.guard.p0i8 +; CHECK: ret i8* undef +} + +define i8* @test30() { + %v = call i8* @llvm.noalias.copy.guard.p0i8.p0i8(i8* null, i8* null, metadata !2, metadata !1) + ret i8* %v + +; CHECK-LABEL: @test30 +; CHECK: llvm.noalias.copy.guard.p0i8 +; CHECK: ret i8* %v +} + +define void @test31() { + %v = call i8* @llvm.noalias.copy.guard.p0i8.p0i8(i8* null, i8* null, metadata !2, metadata !1) + ret void + +; CHECK-LABEL: @test31 +; CHECK-NOT: llvm.noalias.copy.guard.p0i8 +; CHECK: ret void +} + +define i8* @test32() { + %v = call i8* @llvm.noalias.copy.guard.p0i8.p0i8(i8* undef, i8* null, metadata !2, metadata !1) + ret i8* %v + +; CHECK-LABEL: @test32 +; CHECK-NOT: llvm.noalias.copy.guard.p0i8 +; CHECK: ret i8* undef +} + +define void @test40() { + %v = call i8* @llvm.noalias.decl.p0i8.p0p0i32.i32(i8** null, i32 0, metadata !1) + ret void + +; CHECK-LABEL: @test40 +; CHECK-NOT: llvm.noalias.decl.p0i8 +; CHECK: ret void +} + +define void @test41() { + %u = alloca i8* + %v = call i8* @llvm.noalias.decl.p0i8.p0p0i32.i32(i8** %u, i32 0, metadata !4) + ret void + +; CHECK-LABEL: @test41 +; CHECK-NOT: alloca +; CHECK-NOT: llvm.noalias.decl.p0i8 +; CHECK: ret void +} + +define i8** @test42() { + %u = alloca i8* + %v = call i8* @llvm.noalias.decl.p0i8.p0p0i32.i32(i8** %u, i32 0, metadata !0) + ret i8** %u + +; CHECK-LABEL: @test42 +; CHECK: alloca +; CHECK: llvm.noalias.decl.p0i8 +; CHECK: ret i8** %u +} + + +declare i8* @llvm.side.noalias.p0i8.p0i8.p0p0i8.p0p0i8.i32(i8*, i8*, i8**, i8**, i32, metadata ) nounwind +declare i8* @llvm.noalias.arg.guard.p0i8.p0i8(i8*, i8*) nounwind readnone +declare i8* @llvm.noalias.copy.guard.p0i8.p0i8(i8*, i8*, metadata, metadata) +declare i8* @llvm.noalias.decl.p0i8.p0p0i32.i32(i8**, i32, metadata) argmemonly nounwind !0 = !{!0, !"some domain"} !1 = !{!1, !0, !"some scope"} +!2 = !{!3} +!3 = !{ i64 -1, i64 0 } +!4 = !{!4, !"some other domain"} +!5 = !{!5, !4, !"some other scope"}