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" @@ -5480,6 +5481,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; } @@ -5487,8 +5495,30 @@ return nullptr; } -static Value *simplifyIntrinsic(CallBase *Call, const SimplifyQuery &Q) { +static Value *simplifyProvenanceNoAlias(const Value *V) { + const IntrinsicInst *II = cast(V); + assert(II->getIntrinsicID() == Intrinsic::provenance_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: provenance.noalias(provenance.noalias) -> + // provenance.noalias + if (auto *DepII = dyn_cast(Op0)) { + if (DepII->getIntrinsicID() == Intrinsic::provenance_noalias) { + if (llvm::areProvenanceNoAliasCompatible(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) @@ -5545,6 +5575,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::provenance_noalias: { + return simplifyProvenanceNoAlias(Call); + } default: return nullptr; } Index: llvm/lib/Transforms/Utils/Local.cpp =================================================================== --- llvm/lib/Transforms/Utils/Local.cpp +++ llvm/lib/Transforms/Utils/Local.cpp @@ -447,6 +447,29 @@ return false; } + + // 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 || + II->getIntrinsicID() == Intrinsic::provenance_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 =================================================================== --- /dev/null +++ llvm/test/Transforms/InstSimplify/noalias.ll @@ -0,0 +1,172 @@ +; RUN: opt -instsimplify -S < %s | FileCheck %s + +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: @test01 +; CHECK-NOT: llvm.noalias.p0i8 +; CHECK: ret void +} + +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: @test02 +; CHECK: llvm.noalias.p0i8 +; CHECK: ret i8* %v +} + +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: @test03 +; CHECK-NOT: llvm.noalias.p0i8 +; CHECK: ret i8* undef +} + +declare i8* @llvm.noalias.p0i8.p0i8.p0p0i8.i32(i8*, i8*, i8**, i32, metadata ) nounwind + +define void @test11(i8* %ptr) { + call i8* @llvm.provenance.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.provenance.noalias.p0i8 +; CHECK: ret void +} + +define i8* @test12() { + %v = call i8* @llvm.provenance.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.provenance.noalias.p0i8 +; CHECK: ret i8* %v +} + +define i8* @test13() { + %v = call i8* @llvm.provenance.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.provenance.noalias.p0i8 +; CHECK: ret i8* undef +} + +define i8* @test14() { + %u = call i8* @llvm.provenance.noalias.p0i8.p0i8.p0p0i8.p0p0i8.i32(i8* null, i8* null, i8** null, i8** null, i32 0, metadata !1) + %v = call i8* @llvm.provenance.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.provenance.noalias.p0i8 +; CHECK-NOT: llvm.provenance.noalias.p0i8 +; CHECK: ret i8* %u +} + +define i8* @test15() { + %u = call i8* @llvm.provenance.noalias.p0i8.p0i8.p0p0i8.p0p0i8.i32(i8* null, i8* null, i8** null, i8** null, i32 1, metadata !1) + %v = call i8* @llvm.provenance.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.provenance.noalias.p0i8 +; CHECK: llvm.provenance.noalias.p0i8 +; CHECK: ret i8* %v +} + +define i8* @test20() { + %u = call i8* @llvm.provenance.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.provenance.noalias.p0i8 +; CHECK: llvm.noalias.arg.guard.p0i8 +; CHECK: ret i8* %v +} + +define i8* @test21() { + %u = call i8* @llvm.provenance.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.provenance.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.provenance.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"}