Index: lib/Analysis/InstructionSimplify.cpp =================================================================== --- lib/Analysis/InstructionSimplify.cpp +++ lib/Analysis/InstructionSimplify.cpp @@ -119,6 +119,10 @@ return ConstantInt::getTrue(Ty); } +static Type *GetCompareTy(Value *Op) { + return CmpInst::makeCmpResultType(Op->getType()); +} + /// isSameCompare - Is V equivalent to the comparison "LHS Pred RHS"? static bool isSameCompare(Value *V, CmpInst::Predicate Pred, Value *LHS, Value *RHS) { @@ -159,6 +163,28 @@ return false; } +// ne/eq comparison of a pointer produced by a noalias function can be folded to +// a constant true/false +static Value *simplifyICmpWithNoAliasPointers(unsigned Predicate, Value *LHS, + Value *RHS) { + Value *HS[] = {LHS, RHS}; + for (int i = 0; i < 2; ++i) { + if (CallInst *Call = dyn_cast(HS[i])) { + Function *Fn = Call->getCalledFunction(); + if (!Fn || !Fn->returnDoesNotAlias()) + return nullptr; + } + } + + Type *ITy = GetCompareTy(LHS); + if (Predicate == ICmpInst::ICMP_EQ) + return getFalse(ITy); + else if (Predicate == ICmpInst::ICMP_NE) + return getTrue(ITy); + + return nullptr; +} + /// Simplify "A op (B op' C)" by distributing op over op', turning it into /// "(A op B) op' (A op C)". Here "op" is given by Opcode and "op'" is /// given by OpcodeToExpand, while "A" corresponds to LHS and "B op' C" to RHS. @@ -2095,11 +2121,6 @@ return ::SimplifyXorInst(Op0, Op1, Q, RecursionLimit); } - -static Type *GetCompareTy(Value *Op) { - return CmpInst::makeCmpResultType(Op->getType()); -} - /// Rummage around inside V looking for something equivalent to the comparison /// "LHS Pred RHS". Return such a value if found, otherwise return null. /// Helper function for analyzing max/min idioms. @@ -3479,6 +3500,9 @@ if (Value *V = ThreadCmpOverPHI(Pred, LHS, RHS, Q, MaxRecurse)) return V; + if (Value *V = simplifyICmpWithNoAliasPointers(Pred, LHS, RHS)) + return V; + return nullptr; } Index: test/Transforms/InstSimplify/icmp-noalias-pointers.ll =================================================================== --- /dev/null +++ test/Transforms/InstSimplify/icmp-noalias-pointers.ll @@ -0,0 +1,198 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -instsimplify -S | FileCheck %s + +@a = global [32 x i8] c"string\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00", align 16 +@b = common global [32 x i8] zeroinitializer, align 16 + +declare noalias i8* @realloc(i8* nocapture, i64) local_unnamed_addr +declare i8* @custom_aliasing_alloc(i64) local_unnamed_addr +declare noalias i8* @__strdup(i8* nocapture readonly) local_unnamed_addr +declare noalias i8* @calloc(i64, i64) local_unnamed_addr +declare noalias i8* @malloc(i64) local_unnamed_addr + +declare i32 @foo() + +define i1 @noalias_compare_fn_ptr_eq() { +; CHECK-LABEL: @noalias_compare_fn_ptr_eq( +; CHECK-NEXT: ret i1 false +; + %call = tail call noalias i8* @malloc(i64 32) + %cmp = icmp eq i8* %call, bitcast (i32 ()* @foo to i8*) + ret i1 %cmp +} + +define i1 @noalias_compare_fn_ptr_ne() { +; CHECK-LABEL: @noalias_compare_fn_ptr_ne( +; CHECK-NEXT: ret i1 true +; + %call = tail call noalias i8* @malloc(i64 32) + %cmp = icmp ne i8* %call, bitcast (i32 ()* @foo to i8*) + ret i1 %cmp +} + +define i1 @noalias_compare_null_eq() { +; CHECK-LABEL: @noalias_compare_null_eq( +; CHECK-NEXT: ret i1 false +; + %call = tail call noalias i8* @malloc(i64 32) + %cmp = icmp eq i8* %call, null + ret i1 %cmp +} + +define i1 @noalias_compare_null_ne() { +; CHECK-LABEL: @noalias_compare_null_ne( +; CHECK-NEXT: ret i1 true +; + %call = tail call noalias i8* @malloc(i64 32) + %cmp = icmp ne i8* %call, null + ret i1 %cmp +} + +define i1 @noalias_compare_arg_eq(i8* readnone %r) { +; CHECK-LABEL: @noalias_compare_arg_eq( +; CHECK-NEXT: ret i1 false +; + %call = tail call noalias i8* @malloc(i64 32) + %cmp = icmp eq i8* %call, %r + ret i1 %cmp +} + +define i1 @noalias_compare_arg_ne(i8* readnone %r) { +; CHECK-LABEL: @noalias_compare_arg_ne( +; CHECK-NEXT: ret i1 true +; + %call = tail call noalias i8* @malloc(i64 32) + %cmp = icmp ne i8* %call, %r + ret i1 %cmp +} + +define i1 @noalias_compare_const_str_eq_malloc() { +; CHECK-LABEL: @noalias_compare_const_str_eq_malloc( +; CHECK-NEXT: ret i1 false +; + %call = tail call noalias i8* @malloc(i64 32) + %cmp = icmp eq i8* %call, getelementptr inbounds ([32 x i8], [32 x i8]* @a, i64 0, i64 0) + ret i1 %cmp +} + +define i1 @noalias_compare_const_str_eq_calloc() { +; CHECK-LABEL: @noalias_compare_const_str_eq_calloc( +; CHECK-NEXT: ret i1 false +; + %call = tail call noalias i8* @calloc(i64 1, i64 32) + %cmp = icmp eq i8* %call, getelementptr inbounds ([32 x i8], [32 x i8]* @a, i64 0, i64 0) + ret i1 %cmp +} + +define i1 @noalias_compare_const_str_eq_realloc(i8* nocapture %r) { +; CHECK-LABEL: @noalias_compare_const_str_eq_realloc( +; CHECK-NEXT: [[CALL:%.*]] = tail call i8* @realloc(i8* [[R:%.*]], i64 32) +; CHECK-NEXT: ret i1 false +; + %call = tail call i8* @realloc(i8* %r, i64 32) + %cmp = icmp eq i8* %call, getelementptr inbounds ([32 x i8], [32 x i8]* @a, i64 0, i64 0) + ret i1 %cmp +} + +define i1 @noalias_compare_const_str_eq_strdup(i8* nocapture readonly %s) { +; CHECK-LABEL: @noalias_compare_const_str_eq_strdup( +; CHECK-NEXT: [[CALL:%.*]] = tail call noalias i8* @__strdup(i8* [[S:%.*]]) +; CHECK-NEXT: ret i1 false +; + %call = tail call noalias i8* @__strdup(i8* %s) + %cmp = icmp eq i8* %call, getelementptr inbounds ([32 x i8], [32 x i8]* @a, i64 0, i64 0) + ret i1 %cmp +} + +define i1 @noalias_compare_const_str_ne() { +; CHECK-LABEL: @noalias_compare_const_str_ne( +; CHECK-NEXT: ret i1 true +; + %call = tail call noalias i8* @malloc(i64 32) + %cmp = icmp ne i8* %call, getelementptr inbounds ([32 x i8], [32 x i8]* @a, i64 0, i64 0) + ret i1 %cmp +} + +define i1 @noalias_compare_const_str2_eq() { +; CHECK-LABEL: @noalias_compare_const_str2_eq( +; CHECK-NEXT: ret i1 false +; + %call = tail call noalias i8* @malloc(i64 32) + %cmp = icmp eq i8* %call, getelementptr inbounds ([32 x i8], [32 x i8]* @b, i64 0, i64 0) + ret i1 %cmp +} + +define i1 @noalias_compare_const_str2_ne() { +; CHECK-LABEL: @noalias_compare_const_str2_ne( +; CHECK-NEXT: ret i1 true +; + %call = tail call noalias i8* @malloc(i64 32) + %cmp = icmp ne i8* %call, getelementptr inbounds ([32 x i8], [32 x i8]* @b, i64 0, i64 0) + ret i1 %cmp +} + +; Negative tests +define i1 @unknown_alias_compare_fn_ptr_eq() { +; CHECK-LABEL: @unknown_alias_compare_fn_ptr_eq( +; CHECK-NEXT: [[CALL:%.*]] = tail call i8* @custom_aliasing_alloc(i64 32) +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8* [[CALL]], bitcast (i32 ()* @foo to i8*) +; CHECK-NEXT: ret i1 [[CMP]] +; + %call = tail call i8* @custom_aliasing_alloc(i64 32) + %cmp = icmp eq i8* %call, bitcast (i32 ()* @foo to i8*) + ret i1 %cmp +} + +define i1 @unknown_alias_compare_fn_ptr_ne() { +; CHECK-LABEL: @unknown_alias_compare_fn_ptr_ne( +; CHECK-NEXT: [[CALL:%.*]] = tail call i8* @custom_aliasing_alloc(i64 32) +; CHECK-NEXT: [[CMP:%.*]] = icmp ne i8* [[CALL]], bitcast (i32 ()* @foo to i8*) +; CHECK-NEXT: ret i1 [[CMP]] +; + %call = tail call i8* @custom_aliasing_alloc(i64 32) + %cmp = icmp ne i8* %call, bitcast (i32 ()* @foo to i8*) + ret i1 %cmp +} + +define i1 @noalias_compare_fn_ptr_gt() { +; CHECK-LABEL: @noalias_compare_fn_ptr_gt( +; CHECK-NEXT: [[CALL:%.*]] = tail call noalias i8* @malloc(i64 32) +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i8* [[CALL]], bitcast (i32 ()* @foo to i8*) +; CHECK-NEXT: ret i1 [[CMP]] +; + %call = tail call noalias i8* @malloc(i64 32) + %cmp = icmp ugt i8* %call, bitcast (i32 ()* @foo to i8*) + ret i1 %cmp +} + +define i1 @noalias_compare_fn_ptr_lt() { +; CHECK-LABEL: @noalias_compare_fn_ptr_lt( +; CHECK-NEXT: [[CALL:%.*]] = tail call noalias i8* @malloc(i64 32) +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i8* [[CALL]], bitcast (i32 ()* @foo to i8*) +; CHECK-NEXT: ret i1 [[CMP]] +; + %call = tail call noalias i8* @malloc(i64 32) + %cmp = icmp ult i8* %call, bitcast (i32 ()* @foo to i8*) + ret i1 %cmp +} + + +define i1 @noalias_compare_null_gt() { +; CHECK-LABEL: @noalias_compare_null_gt( +; CHECK-NEXT: [[CALL:%.*]] = tail call noalias i8* @malloc(i64 32) +; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i8* [[CALL]], null +; CHECK-NEXT: ret i1 [[CMP]] +; + %call = tail call noalias i8* @malloc(i64 32) + %cmp = icmp ugt i8* %call, null + ret i1 %cmp +} + +define i1 @noalias_compare_noalias_compare_null_gt_lt() { +; CHECK-LABEL: @noalias_compare_noalias_compare_null_gt_lt( +; CHECK-NEXT: ret i1 false +; + %call = tail call noalias i8* @malloc(i64 32) + %cmp = icmp ult i8* %call, null + ret i1 %cmp +}