diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -3629,17 +3629,140 @@ assert(ArgNo == Call.arg_size() && "Call arguments not processed correctly."); - if (!ArgNosNonNull.empty() || !ArgNosNoCapture.empty()) { + auto AddCallAttributes = [&](const SmallVector &ArgNos, + Attribute::AttrKind Attr) { + if (ArgNos.empty()) + return false; + AttributeList AS = Call.getAttributes(); LLVMContext &Ctx = Call.getContext(); - if (!ArgNosNonNull.empty()) - AS = AS.addParamAttribute(Ctx, ArgNosNonNull, - Attribute::get(Ctx, Attribute::NonNull)); - if (!ArgNosNoCapture.empty()) - AS = AS.addParamAttribute(Ctx, ArgNosNoCapture, - Attribute::get(Ctx, Attribute::NoCapture)); + AS = AS.addParamAttribute(Ctx, ArgNos, Attribute::get(Ctx, Attr)); + Call.setAttributes(AS); Changed = true; + return true; + }; + + AddCallAttributes(ArgNosNonNull, Attribute::NonNull); + AddCallAttributes(ArgNosNoCapture, Attribute::NoCapture); + + // Try and propagate attributes of the callers function/arguments/return to + // the callsite. + if (const BasicBlock *BB = Call.getParent()) { + if (const Function *PF = BB->getParent()) { + + // Attributes of the function that apply to any instruction in the + // function (including the callsite). + std::array CallerFnAttrPropagations = {Attribute::MustProgress, + Attribute::WillReturn}; + + for (const Attribute::AttrKind Attr : CallerFnAttrPropagations) { + if (PF->hasFnAttribute(Attr) && !Call.hasFnAttr(Attr)) { + Call.addFnAttr(Attr); + Changed = true; + } + } + + // Can't apply memory access attributes blindly, allocas local to the + // caller don't count for memory access restrictions of the caller so + // may be read/written too, irrelivant of caller attributes, by the + // callsite. + std::array CallerFnMemAttrPropagations = { + Attribute::ReadNone, Attribute::ReadOnly, Attribute::WriteOnly}; + + bool MayHaveAllocaArg = false; + for (Value *V : Call.args()) { + Value *UnderlyingObj = getUnderlyingObject(V); + MayHaveAllocaArg = + !isa(UnderlyingObj) && !isa(UnderlyingObj); + if (MayHaveAllocaArg) + break; + } + + // Check readnone, readonly, and writeonly attributes (both the direct + // function attribute and memoryeffect attribute). Only if callsite has NO + // alloca arguments do we apply attribute to the callsite directly. + // + // NB: we don't need to worry about allocas being stored indirectly in + // other objects. I.e + // + // declare @bar(ptr %p) + // define @foo(ptr %p) { + // %a = alloca ptr + // store ptr %a, ptr %p + // call @bar(ptr %p) + // } + // + // If we are propagating any of the three attributes (readnone, + // readonly, writeonly), that type of nesting is IMPOSSIBLE. readnone: + // it would be illegal for foo to store `%a` in `%p` readonly: ... + // writeonly: it would be illegal for bar to read `%a` from `%p` + if (!MayHaveAllocaArg) { + for (const Attribute::AttrKind Attr : CallerFnMemAttrPropagations) { + if (PF->hasFnAttribute(Attr) && !Call.hasFnAttr(Attr)) { + Call.addFnAttr(Attr); + Changed = true; + } + } + + if (PF->doesNotAccessMemory() && !Call.doesNotAccessMemory()) { + Call.setDoesNotAccessMemory(); + Changed = true; + } + + if (PF->onlyReadsMemory() && !Call.onlyReadsMemory()) { + Call.setOnlyReadsMemory(); + Changed = true; + } + if (PF->onlyWritesMemory() && !Call.onlyWritesMemory()) { + Call.setOnlyWritesMemory(); + Changed = true; + } + } + + // Attributes that if true for the caller's return, must be true of the + // callsite's return value if it is used as the return value of the + // caller. + std::array CallerReturnAttrPropagations = {Attribute::NoUndef, + Attribute::NonNull}; + if (!Call.getType()->isVoidTy() && !PF->getType()->isVoidTy() && + BB->getTerminator() && isa(BB->getTerminator())) { + if (cast(BB->getTerminator())->getReturnValue() == &Call) { + for (const Attribute::AttrKind Attr : CallerReturnAttrPropagations) { + if (PF->hasRetAttribute(Attr) && !Call.hasRetAttr(Attr)) { + Call.addRetAttr(Attr); + Changed = true; + } + } + } + } + + // Attributes that, if true for a caller argument, must be true as well + // in the context of the callsite. + std::array CallerArgAttrPropagations = { + Attribute::NoUndef, Attribute::NonNull, Attribute::NoFree, + Attribute::ReadNone, Attribute::ReadOnly, Attribute::WriteOnly}; + + for (const Attribute::AttrKind Attr : CallerArgAttrPropagations) { + SmallPtrSet CallerArgs; + SmallVector ArgNosAttr; + for (unsigned I = 0; I < PF->arg_size(); ++I) + if (PF->getArg(I)->hasAttribute(Attr)) + CallerArgs.insert(PF->getArg(I)); + + unsigned N = 0; + // TODO: For the readnone, readonly, and writeonly attributes, we may be + // able to inherent from the callsite params underlying object if that + // underlying object is an argument. + for (Value *V : Call.args()) { + if (!Call.paramHasAttr(N, Attr) && CallerArgs.contains(V)) + ArgNosAttr.push_back(N); + N++; + } + + AddCallAttributes(ArgNosAttr, Attr); + } + } } // If the callee is a pointer to a function, attempt to move any casts to the diff --git a/llvm/test/Other/cgscc-devirt-iteration.ll b/llvm/test/Other/cgscc-devirt-iteration.ll --- a/llvm/test/Other/cgscc-devirt-iteration.ll +++ b/llvm/test/Other/cgscc-devirt-iteration.ll @@ -67,7 +67,7 @@ ; This indirect call is the first to be resolved, allowing us to deduce ; readonly but not (yet) readnone. call void %f1(ptr %ignore) -; CHECK: call void @readnone_with_arg(ptr %ignore) +; CHECK: call void @readnone_with_arg ; Bogus call to test2_b to make this a cycle. call void @test2_b() diff --git a/llvm/test/Transforms/InstCombine/freeze-integer-intrinsics.ll b/llvm/test/Transforms/InstCombine/freeze-integer-intrinsics.ll --- a/llvm/test/Transforms/InstCombine/freeze-integer-intrinsics.ll +++ b/llvm/test/Transforms/InstCombine/freeze-integer-intrinsics.ll @@ -144,7 +144,7 @@ define i32 @fshl_i32(i32 %arg0, i32 noundef %arg1, i32 noundef %arg2) { ; CHECK-LABEL: @fshl_i32( ; CHECK-NEXT: [[ARG0_FR:%.*]] = freeze i32 [[ARG0:%.*]] -; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.fshl.i32(i32 [[ARG0_FR]], i32 [[ARG1:%.*]], i32 [[ARG2:%.*]]) +; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.fshl.i32(i32 [[ARG0_FR]], i32 noundef [[ARG1:%.*]], i32 noundef [[ARG2:%.*]]) ; CHECK-NEXT: ret i32 [[CALL]] ; %call = call i32 @llvm.fshl.i32(i32 %arg0, i32 %arg1, i32 %arg2) @@ -155,7 +155,7 @@ define i32 @fshr_i32(i32 %arg0, i32 noundef %arg1, i32 noundef %arg2) { ; CHECK-LABEL: @fshr_i32( ; CHECK-NEXT: [[ARG0_FR:%.*]] = freeze i32 [[ARG0:%.*]] -; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.fshr.i32(i32 [[ARG0_FR]], i32 [[ARG1:%.*]], i32 [[ARG2:%.*]]) +; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.fshr.i32(i32 [[ARG0_FR]], i32 noundef [[ARG1:%.*]], i32 noundef [[ARG2:%.*]]) ; CHECK-NEXT: ret i32 [[CALL]] ; %call = call i32 @llvm.fshr.i32(i32 %arg0, i32 %arg1, i32 %arg2) @@ -166,7 +166,7 @@ define i32 @smax_i32(i32 %arg0, i32 noundef %arg1) { ; CHECK-LABEL: @smax_i32( ; CHECK-NEXT: [[ARG0_FR:%.*]] = freeze i32 [[ARG0:%.*]] -; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.smax.i32(i32 [[ARG0_FR]], i32 [[ARG1:%.*]]) +; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.smax.i32(i32 [[ARG0_FR]], i32 noundef [[ARG1:%.*]]) ; CHECK-NEXT: ret i32 [[CALL]] ; %call = call i32 @llvm.smax.i32(i32 %arg0, i32 %arg1) @@ -177,7 +177,7 @@ define i32 @smin_i32(i32 %arg0, i32 noundef %arg1) { ; CHECK-LABEL: @smin_i32( ; CHECK-NEXT: [[ARG0_FR:%.*]] = freeze i32 [[ARG0:%.*]] -; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.smin.i32(i32 [[ARG0_FR]], i32 [[ARG1:%.*]]) +; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.smin.i32(i32 [[ARG0_FR]], i32 noundef [[ARG1:%.*]]) ; CHECK-NEXT: ret i32 [[CALL]] ; %call = call i32 @llvm.smin.i32(i32 %arg0, i32 %arg1) @@ -188,7 +188,7 @@ define i32 @umax_i32(i32 %arg0, i32 noundef %arg1) { ; CHECK-LABEL: @umax_i32( ; CHECK-NEXT: [[ARG0_FR:%.*]] = freeze i32 [[ARG0:%.*]] -; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.umax.i32(i32 [[ARG0_FR]], i32 [[ARG1:%.*]]) +; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.umax.i32(i32 [[ARG0_FR]], i32 noundef [[ARG1:%.*]]) ; CHECK-NEXT: ret i32 [[CALL]] ; %call = call i32 @llvm.umax.i32(i32 %arg0, i32 %arg1) @@ -199,7 +199,7 @@ define i32 @umin_i32(i32 %arg0, i32 noundef %arg1) { ; CHECK-LABEL: @umin_i32( ; CHECK-NEXT: [[ARG0_FR:%.*]] = freeze i32 [[ARG0:%.*]] -; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.umin.i32(i32 [[ARG0_FR]], i32 [[ARG1:%.*]]) +; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.umin.i32(i32 [[ARG0_FR]], i32 noundef [[ARG1:%.*]]) ; CHECK-NEXT: ret i32 [[CALL]] ; %call = call i32 @llvm.umin.i32(i32 %arg0, i32 %arg1) @@ -210,7 +210,7 @@ define ptr @ptrmask_p0(ptr %arg0, i64 noundef %arg1) { ; CHECK-LABEL: @ptrmask_p0( ; CHECK-NEXT: [[ARG0_FR:%.*]] = freeze ptr [[ARG0:%.*]] -; CHECK-NEXT: [[CALL:%.*]] = call ptr @llvm.ptrmask.p0.i64(ptr [[ARG0_FR]], i64 [[ARG1:%.*]]) +; CHECK-NEXT: [[CALL:%.*]] = call ptr @llvm.ptrmask.p0.i64(ptr [[ARG0_FR]], i64 noundef [[ARG1:%.*]]) ; CHECK-NEXT: ret ptr [[CALL]] ; %call = call ptr @llvm.ptrmask.p0.i64(ptr %arg0, i64 %arg1) @@ -243,7 +243,7 @@ define i32 @sadd_sat_i32(i32 %arg0, i32 noundef %arg1) { ; CHECK-LABEL: @sadd_sat_i32( ; CHECK-NEXT: [[ARG0_FR:%.*]] = freeze i32 [[ARG0:%.*]] -; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.sadd.sat.i32(i32 [[ARG0_FR]], i32 [[ARG1:%.*]]) +; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.sadd.sat.i32(i32 [[ARG0_FR]], i32 noundef [[ARG1:%.*]]) ; CHECK-NEXT: ret i32 [[CALL]] ; %call = call i32 @llvm.sadd.sat.i32(i32 %arg0, i32 %arg1) @@ -254,7 +254,7 @@ define i32 @uadd_sat_i32(i32 %arg0, i32 noundef %arg1) { ; CHECK-LABEL: @uadd_sat_i32( ; CHECK-NEXT: [[ARG0_FR:%.*]] = freeze i32 [[ARG0:%.*]] -; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.uadd.sat.i32(i32 [[ARG0_FR]], i32 [[ARG1:%.*]]) +; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.uadd.sat.i32(i32 [[ARG0_FR]], i32 noundef [[ARG1:%.*]]) ; CHECK-NEXT: ret i32 [[CALL]] ; %call = call i32 @llvm.uadd.sat.i32(i32 %arg0, i32 %arg1) @@ -265,7 +265,7 @@ define i32 @ssub_sat_i32(i32 %arg0, i32 noundef %arg1) { ; CHECK-LABEL: @ssub_sat_i32( ; CHECK-NEXT: [[ARG0_FR:%.*]] = freeze i32 [[ARG0:%.*]] -; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.ssub.sat.i32(i32 [[ARG0_FR]], i32 [[ARG1:%.*]]) +; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.ssub.sat.i32(i32 [[ARG0_FR]], i32 noundef [[ARG1:%.*]]) ; CHECK-NEXT: ret i32 [[CALL]] ; %call = call i32 @llvm.ssub.sat.i32(i32 %arg0, i32 %arg1) @@ -276,7 +276,7 @@ define i32 @usub_sat_i32(i32 %arg0, i32 noundef %arg1) { ; CHECK-LABEL: @usub_sat_i32( ; CHECK-NEXT: [[ARG0_FR:%.*]] = freeze i32 [[ARG0:%.*]] -; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.usub.sat.i32(i32 [[ARG0_FR]], i32 [[ARG1:%.*]]) +; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.usub.sat.i32(i32 [[ARG0_FR]], i32 noundef [[ARG1:%.*]]) ; CHECK-NEXT: ret i32 [[CALL]] ; %call = call i32 @llvm.usub.sat.i32(i32 %arg0, i32 %arg1) @@ -286,7 +286,7 @@ define i32 @sshl_sat_i32(i32 %arg0, i32 noundef %arg1) { ; CHECK-LABEL: @sshl_sat_i32( -; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.sshl.sat.i32(i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) +; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.sshl.sat.i32(i32 [[ARG0:%.*]], i32 noundef [[ARG1:%.*]]) ; CHECK-NEXT: [[FREEZE:%.*]] = freeze i32 [[CALL]] ; CHECK-NEXT: ret i32 [[FREEZE]] ; @@ -297,7 +297,7 @@ define i32 @ushl_sat_i32(i32 %arg0, i32 noundef %arg1) { ; CHECK-LABEL: @ushl_sat_i32( -; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.ushl.sat.i32(i32 [[ARG0:%.*]], i32 [[ARG1:%.*]]) +; CHECK-NEXT: [[CALL:%.*]] = call i32 @llvm.ushl.sat.i32(i32 [[ARG0:%.*]], i32 noundef [[ARG1:%.*]]) ; CHECK-NEXT: [[FREEZE:%.*]] = freeze i32 [[CALL]] ; CHECK-NEXT: ret i32 [[FREEZE]] ; diff --git a/llvm/test/Transforms/InstCombine/mem-deref-bytes-addrspaces.ll b/llvm/test/Transforms/InstCombine/mem-deref-bytes-addrspaces.ll --- a/llvm/test/Transforms/InstCombine/mem-deref-bytes-addrspaces.ll +++ b/llvm/test/Transforms/InstCombine/mem-deref-bytes-addrspaces.ll @@ -5,7 +5,7 @@ define i32 @memcmp_const_size_update_deref(ptr addrspace(1) nocapture readonly %d, ptr addrspace(1) nocapture readonly %s) { ; CHECK-LABEL: @memcmp_const_size_update_deref( -; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @memcmp(ptr addrspace(1) noundef dereferenceable(16) dereferenceable_or_null(40) [[D:%.*]], ptr addrspace(1) noundef dereferenceable(16) [[S:%.*]], i64 16) +; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @memcmp(ptr addrspace(1) noundef readonly dereferenceable(16) dereferenceable_or_null(40) [[D:%.*]], ptr addrspace(1) noundef readonly dereferenceable(16) [[S:%.*]], i64 16) ; CHECK-NEXT: ret i32 [[CALL]] ; %call = tail call i32 @memcmp(ptr addrspace(1) dereferenceable_or_null(40) %d, ptr addrspace(1) %s, i64 16) @@ -14,7 +14,7 @@ define i32 @memcmp_nonconst_size_nonnnull(ptr addrspace(1) nocapture readonly %d, ptr addrspace(1) nocapture readonly %s, i64 %n) { ; CHECK-LABEL: @memcmp_nonconst_size_nonnnull( -; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @memcmp(ptr addrspace(1) nonnull dereferenceable_or_null(40) [[D:%.*]], ptr addrspace(1) nonnull [[S:%.*]], i64 [[N:%.*]]) +; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @memcmp(ptr addrspace(1) nonnull readonly dereferenceable_or_null(40) [[D:%.*]], ptr addrspace(1) nonnull readonly [[S:%.*]], i64 [[N:%.*]]) ; CHECK-NEXT: ret i32 [[CALL]] ; %call = tail call i32 @memcmp(ptr addrspace(1) nonnull dereferenceable_or_null(40) %d, ptr addrspace(1) nonnull %s, i64 %n) diff --git a/llvm/test/Transforms/InstCombine/mem-deref-bytes.ll b/llvm/test/Transforms/InstCombine/mem-deref-bytes.ll --- a/llvm/test/Transforms/InstCombine/mem-deref-bytes.ll +++ b/llvm/test/Transforms/InstCombine/mem-deref-bytes.ll @@ -12,7 +12,7 @@ define i32 @memcmp_const_size_set_deref(ptr nocapture readonly %d, ptr nocapture readonly %s) { ; CHECK-LABEL: @memcmp_const_size_set_deref( -; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @memcmp(ptr noundef nonnull dereferenceable(16) [[D:%.*]], ptr noundef nonnull dereferenceable(16) [[S:%.*]], i64 16) +; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @memcmp(ptr noundef nonnull readonly dereferenceable(16) [[D:%.*]], ptr noundef nonnull readonly dereferenceable(16) [[S:%.*]], i64 16) ; CHECK-NEXT: ret i32 [[CALL]] ; %call = tail call i32 @memcmp(ptr %d, ptr %s, i64 16) @@ -21,7 +21,7 @@ define i32 @memcmp_const_size_update_deref(ptr nocapture readonly %d, ptr nocapture readonly %s) { ; CHECK-LABEL: @memcmp_const_size_update_deref( -; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @memcmp(ptr noundef nonnull dereferenceable(16) [[D:%.*]], ptr noundef nonnull dereferenceable(16) [[S:%.*]], i64 16) +; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @memcmp(ptr noundef nonnull readonly dereferenceable(16) [[D:%.*]], ptr noundef nonnull readonly dereferenceable(16) [[S:%.*]], i64 16) ; CHECK-NEXT: ret i32 [[CALL]] ; %call = tail call i32 @memcmp(ptr dereferenceable(4) %d, ptr dereferenceable(8) %s, i64 16) @@ -30,7 +30,7 @@ define i32 @memcmp_const_size_update_deref2(ptr nocapture readonly %d, ptr nocapture readonly %s) { ; CHECK-LABEL: @memcmp_const_size_update_deref2( -; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @memcmp(ptr noundef nonnull dereferenceable(16) [[D:%.*]], ptr noundef nonnull dereferenceable(16) [[S:%.*]], i64 16) +; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @memcmp(ptr noundef nonnull readonly dereferenceable(16) [[D:%.*]], ptr noundef nonnull readonly dereferenceable(16) [[S:%.*]], i64 16) ; CHECK-NEXT: ret i32 [[CALL]] ; %call = tail call i32 @memcmp(ptr %d, ptr dereferenceable_or_null(8) %s, i64 16) @@ -39,7 +39,7 @@ define i32 @memcmp_const_size_update_deref3(ptr nocapture readonly %d, ptr nocapture readonly %s) { ; CHECK-LABEL: @memcmp_const_size_update_deref3( -; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @memcmp(ptr noundef nonnull dereferenceable(40) [[D:%.*]], ptr noundef nonnull dereferenceable(16) [[S:%.*]], i64 16) +; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @memcmp(ptr noundef nonnull readonly dereferenceable(40) [[D:%.*]], ptr noundef nonnull readonly dereferenceable(16) [[S:%.*]], i64 16) ; CHECK-NEXT: ret i32 [[CALL]] ; %call = tail call i32 @memcmp(ptr dereferenceable(40) %d, ptr %s, i64 16) @@ -48,7 +48,7 @@ define i32 @memcmp_const_size_update_deref4(ptr nocapture readonly %d, ptr nocapture readonly %s) { ; CHECK-LABEL: @memcmp_const_size_update_deref4( -; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @memcmp(ptr noundef nonnull dereferenceable(16) [[D:%.*]], ptr noundef nonnull dereferenceable(16) [[S:%.*]], i64 16) +; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @memcmp(ptr noundef nonnull readonly dereferenceable(16) [[D:%.*]], ptr noundef nonnull readonly dereferenceable(16) [[S:%.*]], i64 16) ; CHECK-NEXT: ret i32 [[CALL]] ; %call = tail call i32 @memcmp(ptr dereferenceable_or_null(16) %d, ptr %s, i64 16) @@ -57,7 +57,7 @@ define i32 @memcmp_const_size_update_deref5(ptr nocapture readonly %d, ptr nocapture readonly %s) { ; CHECK-LABEL: @memcmp_const_size_update_deref5( -; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @memcmp(ptr noundef nonnull dereferenceable(40) [[D:%.*]], ptr noundef nonnull dereferenceable(16) [[S:%.*]], i64 16) +; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @memcmp(ptr noundef nonnull readonly dereferenceable(40) [[D:%.*]], ptr noundef nonnull readonly dereferenceable(16) [[S:%.*]], i64 16) ; CHECK-NEXT: ret i32 [[CALL]] ; %call = tail call i32 @memcmp(ptr dereferenceable_or_null(40) %d, ptr %s, i64 16) @@ -66,7 +66,7 @@ define i32 @memcmp_const_size_update_deref6(ptr nocapture readonly %d, ptr nocapture readonly %s) null_pointer_is_valid { ; CHECK-LABEL: @memcmp_const_size_update_deref6( -; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @memcmp(ptr noundef dereferenceable(16) dereferenceable_or_null(40) [[D:%.*]], ptr noundef dereferenceable(16) [[S:%.*]], i64 16) +; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @memcmp(ptr noundef readonly dereferenceable(16) dereferenceable_or_null(40) [[D:%.*]], ptr noundef readonly dereferenceable(16) [[S:%.*]], i64 16) ; CHECK-NEXT: ret i32 [[CALL]] ; %call = tail call i32 @memcmp(ptr dereferenceable_or_null(40) %d, ptr %s, i64 16) @@ -75,7 +75,7 @@ define i32 @memcmp_const_size_update_deref7(ptr nocapture readonly %d, ptr nocapture readonly %s) null_pointer_is_valid { ; CHECK-LABEL: @memcmp_const_size_update_deref7( -; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @memcmp(ptr noundef nonnull dereferenceable(40) [[D:%.*]], ptr noundef dereferenceable(16) [[S:%.*]], i64 16) +; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @memcmp(ptr noundef nonnull readonly dereferenceable(40) [[D:%.*]], ptr noundef readonly dereferenceable(16) [[S:%.*]], i64 16) ; CHECK-NEXT: ret i32 [[CALL]] ; %call = tail call i32 @memcmp(ptr nonnull dereferenceable_or_null(40) %d, ptr %s, i64 16) @@ -84,7 +84,7 @@ define i32 @memcmp_const_size_no_update_deref(ptr nocapture readonly %d, ptr nocapture readonly %s) { ; CHECK-LABEL: @memcmp_const_size_no_update_deref( -; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @memcmp(ptr noundef nonnull dereferenceable(40) [[D:%.*]], ptr noundef nonnull dereferenceable(20) [[S:%.*]], i64 16) +; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @memcmp(ptr noundef nonnull readonly dereferenceable(40) [[D:%.*]], ptr noundef nonnull readonly dereferenceable(20) [[S:%.*]], i64 16) ; CHECK-NEXT: ret i32 [[CALL]] ; %call = tail call i32 @memcmp(ptr dereferenceable(40) %d, ptr dereferenceable(20) %s, i64 16) @@ -93,7 +93,7 @@ define i32 @memcmp_nonconst_size(ptr nocapture readonly %d, ptr nocapture readonly %s, i64 %n) { ; CHECK-LABEL: @memcmp_nonconst_size( -; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @memcmp(ptr [[D:%.*]], ptr [[S:%.*]], i64 [[N:%.*]]) +; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @memcmp(ptr readonly [[D:%.*]], ptr readonly [[S:%.*]], i64 [[N:%.*]]) ; CHECK-NEXT: ret i32 [[CALL]] ; %call = tail call i32 @memcmp(ptr %d, ptr %s, i64 %n) @@ -102,7 +102,7 @@ define ptr @memcpy_const_size_set_deref(ptr nocapture readonly %d, ptr nocapture readonly %s) { ; CHECK-LABEL: @memcpy_const_size_set_deref( -; CHECK-NEXT: tail call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 1 dereferenceable(64) [[D:%.*]], ptr noundef nonnull align 1 dereferenceable(64) [[S:%.*]], i64 64, i1 false) +; CHECK-NEXT: tail call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull readonly align 1 dereferenceable(64) [[D:%.*]], ptr noundef nonnull readonly align 1 dereferenceable(64) [[S:%.*]], i64 64, i1 false) ; CHECK-NEXT: ret ptr [[D]] ; %call = tail call ptr @memcpy(ptr %d, ptr %s, i64 64) @@ -111,7 +111,7 @@ define ptr @memmove_const_size_set_deref(ptr nocapture readonly %d, ptr nocapture readonly %s) { ; CHECK-LABEL: @memmove_const_size_set_deref( -; CHECK-NEXT: tail call void @llvm.memmove.p0.p0.i64(ptr noundef nonnull align 1 dereferenceable(64) [[D:%.*]], ptr noundef nonnull align 1 dereferenceable(64) [[S:%.*]], i64 64, i1 false) +; CHECK-NEXT: tail call void @llvm.memmove.p0.p0.i64(ptr noundef nonnull readonly align 1 dereferenceable(64) [[D:%.*]], ptr noundef nonnull readonly align 1 dereferenceable(64) [[S:%.*]], i64 64, i1 false) ; CHECK-NEXT: ret ptr [[D]] ; %call = tail call ptr @memmove(ptr %d, ptr %s, i64 64) @@ -121,7 +121,7 @@ define ptr @memset_const_size_set_deref(ptr nocapture readonly %s, i32 %c) { ; CHECK-LABEL: @memset_const_size_set_deref( ; CHECK-NEXT: [[TMP1:%.*]] = trunc i32 [[C:%.*]] to i8 -; CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 1 dereferenceable(64) [[S:%.*]], i8 [[TMP1]], i64 64, i1 false) +; CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef nonnull readonly align 1 dereferenceable(64) [[S:%.*]], i8 [[TMP1]], i64 64, i1 false) ; CHECK-NEXT: ret ptr [[S]] ; %call = tail call ptr @memset(ptr %s, i32 %c, i64 64) @@ -130,7 +130,7 @@ define ptr @memchr_const_size_set_deref(ptr nocapture readonly %s, i32 %c) { ; CHECK-LABEL: @memchr_const_size_set_deref( -; CHECK-NEXT: [[CALL:%.*]] = tail call ptr @memchr(ptr noundef nonnull dereferenceable(1) [[S:%.*]], i32 [[C:%.*]], i64 64) +; CHECK-NEXT: [[CALL:%.*]] = tail call ptr @memchr(ptr noundef nonnull readonly dereferenceable(1) [[S:%.*]], i32 [[C:%.*]], i64 64) ; CHECK-NEXT: ret ptr [[CALL]] ; %call = tail call ptr @memchr(ptr %s, i32 %c, i64 64) @@ -139,7 +139,7 @@ define ptr @llvm_memcpy_const_size_set_deref(ptr nocapture readonly %d, ptr nocapture readonly %s) { ; CHECK-LABEL: @llvm_memcpy_const_size_set_deref( -; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 1 dereferenceable(16) [[D:%.*]], ptr noundef nonnull align 1 dereferenceable(16) [[S:%.*]], i64 16, i1 false) +; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull readonly align 1 dereferenceable(16) [[D:%.*]], ptr noundef nonnull align 1 dereferenceable(16) [[S:%.*]], i64 16, i1 false) ; CHECK-NEXT: ret ptr [[D]] ; call void @llvm.memcpy.p0.p0.i64(ptr align 1 %d, ptr align 1 %s, i64 16, i1 false) @@ -148,7 +148,7 @@ define ptr @llvm_memmove_const_size_set_deref(ptr nocapture readonly %d, ptr nocapture readonly %s) { ; CHECK-LABEL: @llvm_memmove_const_size_set_deref( -; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr noundef nonnull align 1 dereferenceable(16) [[D:%.*]], ptr noundef nonnull align 1 dereferenceable(16) [[S:%.*]], i64 16, i1 false) +; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr noundef nonnull readonly align 1 dereferenceable(16) [[D:%.*]], ptr noundef nonnull align 1 dereferenceable(16) [[S:%.*]], i64 16, i1 false) ; CHECK-NEXT: ret ptr [[D]] ; call void @llvm.memmove.p0.p0.i64(ptr align 1 %d, ptr align 1 %s, i64 16, i1 false) @@ -156,7 +156,7 @@ } define ptr @llvm_memset_const_size_set_deref(ptr nocapture readonly %s, i8 %c) { ; CHECK-LABEL: @llvm_memset_const_size_set_deref( -; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 1 dereferenceable(16) [[S:%.*]], i8 [[C:%.*]], i64 16, i1 false) +; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull readonly align 1 dereferenceable(16) [[S:%.*]], i8 [[C:%.*]], i64 16, i1 false) ; CHECK-NEXT: ret ptr [[S]] ; call void @llvm.memset.p0.i64(ptr align 1 %s, i8 %c, i64 16, i1 false) diff --git a/llvm/test/Transforms/InstCombine/memcpy-from-global.ll b/llvm/test/Transforms/InstCombine/memcpy-from-global.ll --- a/llvm/test/Transforms/InstCombine/memcpy-from-global.ll +++ b/llvm/test/Transforms/InstCombine/memcpy-from-global.ll @@ -339,7 +339,7 @@ ; Tests that we can eliminate allocas copied from readonly noalias pointers. define void @memcpy_from_readonly_noalias(ptr readonly noalias align 8 dereferenceable(124) %arg) { ; CHECK-LABEL: @memcpy_from_readonly_noalias( -; CHECK-NEXT: call void @bar(ptr nonnull [[ARG:%.*]]) #[[ATTR3]] +; CHECK-NEXT: call void @bar(ptr nonnull readonly [[ARG:%.*]]) #[[ATTR3]] ; CHECK-NEXT: ret void ; %alloca = alloca %T, align 8 diff --git a/llvm/test/Transforms/InstCombine/strcmp-memcmp.ll b/llvm/test/Transforms/InstCombine/strcmp-memcmp.ll --- a/llvm/test/Transforms/InstCombine/strcmp-memcmp.ll +++ b/llvm/test/Transforms/InstCombine/strcmp-memcmp.ll @@ -350,7 +350,7 @@ define i32 @strcmp_memcmp_bad4(ptr nocapture readonly %buf) nofree nosync { ; CHECK-LABEL: @strcmp_memcmp_bad4( -; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @strcmp(ptr noundef nonnull dereferenceable(4) @key, ptr noundef nonnull dereferenceable(1) [[BUF:%.*]]) +; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @strcmp(ptr noundef nonnull dereferenceable(4) @key, ptr noundef nonnull readonly dereferenceable(1) [[BUF:%.*]]) ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[CALL]], 0 ; CHECK-NEXT: [[CONV:%.*]] = zext i1 [[CMP]] to i32 ; CHECK-NEXT: ret i32 [[CONV]] @@ -377,7 +377,7 @@ define i32 @strcmp_memcmp_bad6(ptr dereferenceable (4) %buf, ptr nocapture readonly %k) nofree nosync { ; CHECK-LABEL: @strcmp_memcmp_bad6( -; CHECK-NEXT: [[CALL:%.*]] = call i32 @strcmp(ptr noundef nonnull dereferenceable(1) [[BUF:%.*]], ptr noundef nonnull dereferenceable(1) [[K:%.*]]) +; CHECK-NEXT: [[CALL:%.*]] = call i32 @strcmp(ptr noundef nonnull dereferenceable(1) [[BUF:%.*]], ptr noundef nonnull readonly dereferenceable(1) [[K:%.*]]) ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[CALL]], 0 ; CHECK-NEXT: [[CONV:%.*]] = zext i1 [[CMP]] to i32 ; CHECK-NEXT: ret i32 [[CONV]] @@ -390,7 +390,7 @@ define i32 @strcmp_memcmp_bad7(ptr nocapture readonly %k) nofree nosync { ; CHECK-LABEL: @strcmp_memcmp_bad7( -; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @strcmp(ptr noundef nonnull dereferenceable(4) @key, ptr noundef nonnull dereferenceable(1) [[K:%.*]]) +; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @strcmp(ptr noundef nonnull dereferenceable(4) @key, ptr noundef nonnull readonly dereferenceable(1) [[K:%.*]]) ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[CALL]], 0 ; CHECK-NEXT: [[CONV:%.*]] = zext i1 [[CMP]] to i32 ; CHECK-NEXT: ret i32 [[CONV]] @@ -454,7 +454,7 @@ define i32 @strncmp_memcmp_bad3(ptr nocapture readonly %k) nofree nosync { ; CHECK-LABEL: @strncmp_memcmp_bad3( -; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @strncmp(ptr noundef nonnull dereferenceable(4) @key, ptr noundef nonnull dereferenceable(1) [[K:%.*]], i64 2) +; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @strncmp(ptr noundef nonnull dereferenceable(4) @key, ptr noundef nonnull readonly dereferenceable(1) [[K:%.*]], i64 2) ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[CALL]], 0 ; CHECK-NEXT: [[CONV:%.*]] = zext i1 [[CMP]] to i32 ; CHECK-NEXT: ret i32 [[CONV]] diff --git a/llvm/test/Transforms/PhaseOrdering/X86/loop-idiom-vs-indvars.ll b/llvm/test/Transforms/PhaseOrdering/X86/loop-idiom-vs-indvars.ll --- a/llvm/test/Transforms/PhaseOrdering/X86/loop-idiom-vs-indvars.ll +++ b/llvm/test/Transforms/PhaseOrdering/X86/loop-idiom-vs-indvars.ll @@ -12,7 +12,7 @@ ; ALL-LABEL: @cttz( ; ALL-NEXT: entry: ; ALL-NEXT: [[TMP0:%.*]] = shl i32 [[N:%.*]], 1 -; ALL-NEXT: [[TMP1:%.*]] = tail call i32 @llvm.cttz.i32(i32 [[TMP0]], i1 false), !range [[RNG0:![0-9]+]] +; ALL-NEXT: [[TMP1:%.*]] = tail call i32 @llvm.cttz.i32(i32 [[TMP0]], i1 false) #[[ATTR2:[0-9]+]], !range [[RNG0:![0-9]+]] ; ALL-NEXT: [[TMP2:%.*]] = sub nuw nsw i32 32, [[TMP1]] ; ALL-NEXT: [[TMP3:%.*]] = sub nuw nsw i32 75, [[TMP1]] ; ALL-NEXT: store i32 [[TMP3]], ptr [[P1:%.*]], align 4 diff --git a/llvm/test/Transforms/PhaseOrdering/memset-tail.ll b/llvm/test/Transforms/PhaseOrdering/memset-tail.ll --- a/llvm/test/Transforms/PhaseOrdering/memset-tail.ll +++ b/llvm/test/Transforms/PhaseOrdering/memset-tail.ll @@ -8,7 +8,7 @@ ; CHECK-NEXT: br i1 [[CMP_NOT1]], label [[WHILE_END:%.*]], label [[WHILE_BODY_PREHEADER:%.*]] ; CHECK: while.body.preheader: ; CHECK-NEXT: [[TMP0:%.*]] = zext i32 [[C]] to i64 -; CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr align 1 [[D:%.*]], i8 0, i64 [[TMP0]], i1 false) +; CHECK-NEXT: tail call void @llvm.memset.p0.i64(ptr noundef align 1 [[D:%.*]], i8 0, i64 [[TMP0]], i1 false) #[[ATTR2:[0-9]+]] ; CHECK-NEXT: br label [[WHILE_END]] ; CHECK: while.end: ; CHECK-NEXT: ret void diff --git a/llvm/test/Transforms/PhaseOrdering/vector-select.ll b/llvm/test/Transforms/PhaseOrdering/vector-select.ll --- a/llvm/test/Transforms/PhaseOrdering/vector-select.ll +++ b/llvm/test/Transforms/PhaseOrdering/vector-select.ll @@ -3,8 +3,8 @@ define <3 x float> @PR52631(<3 x float> %a, <3 x float> %b, <3 x i32> %c) { ; CHECK-LABEL: @PR52631( -; CHECK-NEXT: [[ISNEG3:%.*]] = icmp slt <3 x i32> [[C:%.*]], zeroinitializer -; CHECK-NEXT: [[OR_V:%.*]] = select <3 x i1> [[ISNEG3]], <3 x float> [[B:%.*]], <3 x float> [[A:%.*]] +; CHECK-NEXT: [[ISNEG:%.*]] = icmp slt <3 x i32> [[C:%.*]], zeroinitializer +; CHECK-NEXT: [[OR_V:%.*]] = select <3 x i1> [[ISNEG]], <3 x float> [[B:%.*]], <3 x float> [[A:%.*]] ; CHECK-NEXT: ret <3 x float> [[OR_V]] ; %a.addr = alloca <3 x float>, align 16 @@ -74,8 +74,8 @@ define <4 x i32> @PR42100(<4 x i32> noundef %x, <4 x i32> noundef %min) { ; CHECK-LABEL: @PR42100( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[TMP0:%.*]] = tail call <4 x i32> @llvm.smin.v4i32(<4 x i32> [[X:%.*]], <4 x i32> [[MIN:%.*]]) -; CHECK-NEXT: ret <4 x i32> [[TMP0]] +; CHECK-NEXT: [[SEL:%.*]] = tail call <4 x i32> @llvm.smin.v4i32(<4 x i32> noundef [[X:%.*]], <4 x i32> noundef [[MIN:%.*]]) #[[ATTR2:[0-9]+]] +; CHECK-NEXT: ret <4 x i32> [[SEL]] ; entry: br label %for.cond