diff --git a/llvm/include/llvm/IR/Attributes.h b/llvm/include/llvm/IR/Attributes.h --- a/llvm/include/llvm/IR/Attributes.h +++ b/llvm/include/llvm/IR/Attributes.h @@ -151,6 +151,9 @@ /// Return true if the attribute is a type attribute. bool isTypeAttribute() const; + /// Return true if the attribute is an ABI attribute. + bool isABIRelated() const; + /// Return true if the attribute is any kind of attribute. bool isValid() const { return pImpl; } diff --git a/llvm/include/llvm/IR/Instruction.h b/llvm/include/llvm/IR/Instruction.h --- a/llvm/include/llvm/IR/Instruction.h +++ b/llvm/include/llvm/IR/Instruction.h @@ -391,6 +391,16 @@ /// having non-poison inputs. void dropPoisonGeneratingFlags(); + /// This function drops non-debug unknown metadata (through + /// dropUnknownNonDebugMetadata). For calls, it also drops parameter + /// attributes that are legal to drop. Both of these should be done by + /// passes which moves instructions in IR. + void dropContextSensitiveAttributesAndUnknownMetadata(); + + /// Use this API when we need to preserve some metadata through KnownIDs. + void + dropContextSensitiveAttributesAndUnknownMetadata(ArrayRef KnownIDs); + /// Determine whether the exact flag is set. bool isExact() const; diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -273,6 +273,12 @@ return pImpl && pImpl->isTypeAttribute(); } +bool Attribute::isABIRelated() const { + if (!pImpl) return false; + Attribute::AttrKind A = pImpl->getKindAsEnum(); + return A == Attribute::SExt || A == Attribute::ZExt; +} + Attribute::AttrKind Attribute::getKindAsEnum() const { if (!pImpl) return None; assert((isEnumAttribute() || isIntAttribute() || isTypeAttribute()) && diff --git a/llvm/lib/IR/Instruction.cpp b/llvm/lib/IR/Instruction.cpp --- a/llvm/lib/IR/Instruction.cpp +++ b/llvm/lib/IR/Instruction.cpp @@ -165,6 +165,34 @@ // TODO: FastMathFlags! } +void Instruction::dropContextSensitiveAttributesAndUnknownMetadata() { + dropContextSensitiveAttributesAndUnknownMetadata(None); +} + +void Instruction::dropContextSensitiveAttributesAndUnknownMetadata(ArrayRef KnownIDs) { + dropUnknownNonDebugMetadata(KnownIDs); + auto *CB = dyn_cast(this); + if (!CB) + return; + // For call instructions, we also need to drop parameter attributes that are + // not ABI related. These attributes may be context sensitive and not valid in + // the context this instruction is moved to. + AttributeList AL = CB->getAttributes(); + if (AL.isEmpty()) + return; + + for (unsigned ArgNo = 0; ArgNo < CB->getNumArgOperands(); ArgNo++) { + auto PAttr = AL.getParamAttributes(ArgNo); + // List of attributes that are present in parameter that we need to remove. + AttrBuilder R; + for (auto A: PAttr) { + if (!A.isABIRelated()) + R.addAttribute(A); + } + auto Index = ArgNo + AttributeList::FirstArgIndex; + CB->setAttributes(CB->getAttributes().removeAttributes(CB->getContext(), Index, R)); + } +} bool Instruction::isExact() const { return cast(this)->isExact(); diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp --- a/llvm/lib/Transforms/Scalar/LICM.cpp +++ b/llvm/lib/Transforms/Scalar/LICM.cpp @@ -1782,12 +1782,16 @@ // Conservatively strip all metadata on the instruction unless we were // guaranteed to execute I if we entered the loop, in which case the metadata // is valid in the loop preheader. - if (I.hasMetadataOtherThanDebugLoc() && + // Similarly, If I is a call and it is not guaranteed to execute in the loop, then moving + // to the preheader means we should strip parameter attributes on the call + // since we may be hoisting above conditions that allowed inferring those + // attributes. They may not be valid at the preheader. + if ((I.hasMetadataOtherThanDebugLoc() || isa(I))&& // The check on hasMetadataOtherThanDebugLoc is to prevent us from burning // time in isGuaranteedToExecute if we don't actually have anything to // drop. It is a compile time optimization, not required for correctness. !SafetyInfo->isGuaranteedToExecute(I, DT, CurLoop)) - I.dropUnknownNonDebugMetadata(); + I.dropContextSensitiveAttributesAndUnknownMetadata(); if (isa(I)) // Move the new node to the end of the phi list in the destination block. diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp --- a/llvm/lib/Transforms/Utils/Local.cpp +++ b/llvm/lib/Transforms/Utils/Local.cpp @@ -2808,7 +2808,7 @@ for (BasicBlock::iterator II = BB->begin(), IE = BB->end(); II != IE;) { Instruction *I = &*II; - I->dropUnknownNonDebugMetadata(); + I->dropContextSensitiveAttributesAndUnknownMetadata(); if (I->isUsedByMetadata()) dropDebugUsers(*I); if (I->isDebugOrPseudoInst()) { diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -1089,7 +1089,8 @@ // For an analogous reason, we must also drop all the metadata whose // semantics we don't understand. We *can* preserve !annotation, because // it is tied to the instruction itself, not the value or position. - NewBonusInst->dropUnknownNonDebugMetadata(LLVMContext::MD_annotation); + // Similarly strip attributes on call parameters that maybe invalid in location we moved to. + NewBonusInst->dropContextSensitiveAttributesAndUnknownMetadata(LLVMContext::MD_annotation); PredBlock->getInstList().insert(PTI->getIterator(), NewBonusInst); NewBonusInst->takeName(&BonusInst); @@ -2480,10 +2481,12 @@ // Conservatively strip all metadata on the instruction. Drop the debug loc // to avoid making it appear as if the condition is a constant, which would // be misleading while debugging. + // Similarly strip attributes that maybe dependent on condition we are + // hoisting above. for (auto &I : *ThenBB) { if (!SpeculatedStoreValue || &I != SpeculatedStore) I.setDebugLoc(DebugLoc()); - I.dropUnknownNonDebugMetadata(); + I.dropContextSensitiveAttributesAndUnknownMetadata(); } // Hoist the instructions. diff --git a/llvm/test/Transforms/LICM/call-hoisting.ll b/llvm/test/Transforms/LICM/call-hoisting.ll --- a/llvm/test/Transforms/LICM/call-hoisting.ll +++ b/llvm/test/Transforms/LICM/call-hoisting.ll @@ -25,14 +25,18 @@ declare i32 @spec(i32* %p) readonly argmemonly nounwind speculatable -; FIXME: We should strip the nonnull callsite attribute on spec call's argument since it is +; We should strip the nonnull callsite attribute on spec call's argument since it is ; may not be valid when hoisted to preheader. define void @test_strip_attribute(i32* noalias %loc, i32* noalias %sink, i32* %q) { -; CHECK-LABEL: test_strip_attribute -; CHECK-LABEL: entry -; CHECK-NEXT: %ret = call i32 @load(i32* %loc) -; CHECK-NEXT: %nullchk = icmp eq i32* %q, null -; CHECK-NEXT: %ret2 = call i32 @spec(i32* nonnull %q) +; CHECK-LABEL: @test_strip_attribute( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[RET:%.*]] = call i32 @load(i32* [[LOC:%.*]]) +; CHECK-NEXT: [[NULLCHK:%.*]] = icmp eq i32* [[Q:%.*]], null +; CHECK-NEXT: [[RET2:%.*]] = call i32 @spec(i32* [[Q]]) +; CHECK-NEXT: br label [[LOOP:%.*]] +; CHECK: loop: +; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[ISNULL:%.*]] ] +; CHECK-NEXT: br i1 [[NULLCHK]], label [[ISNULL]], label [[NONNULLBB:%.*]] entry: br label %loop @@ -42,11 +46,11 @@ %nullchk = icmp eq i32* %q, null br i1 %nullchk, label %isnull, label %nonnullbb -nonnullbb: +nonnullbb: %ret2 = call i32 @spec(i32* nonnull %q) br label %isnull -isnull: +isnull: store volatile i32 %ret, i32* %sink %iv.next = add i32 %iv, 1 %cmp = icmp slt i32 %iv, 200 @@ -90,7 +94,7 @@ call void @store(i32 0, i32* %loc) %iv.next = add i32 %iv, 1 br i1 %earlycnd, label %exit1, label %backedge - + backedge: %cmp = icmp slt i32 %iv, 200 br i1 %cmp, label %loop, label %exit2 @@ -174,7 +178,7 @@ %v = load i32, i32* %loc %earlycnd = icmp eq i32 %v, 198 br i1 %earlycnd, label %exit1, label %backedge - + backedge: %iv.next = add i32 %iv, 1 %cmp = icmp slt i32 %iv, 200 diff --git a/llvm/test/Transforms/SimplifyCFG/fold-branch-to-common-dest.ll b/llvm/test/Transforms/SimplifyCFG/fold-branch-to-common-dest.ll --- a/llvm/test/Transforms/SimplifyCFG/fold-branch-to-common-dest.ll +++ b/llvm/test/Transforms/SimplifyCFG/fold-branch-to-common-dest.ll @@ -118,13 +118,13 @@ ret void } -; FIXME: When we fold the dispatch block into pred, the call is moved to pred +; When we fold the dispatch block into pred, the call is moved to pred ; and the attribute nonnull is no longer valid on paramater. We should drop it. define void @one_pred_with_spec_call(i8 %v0, i8 %v1, i32* %p) { ; CHECK-LABEL: one_pred_with_spec_call ; CHECK-LABEL: pred: ; CHECK: %c0 = icmp ne i32* %p, null -; CHECK: %x = call i32 @speculate_call(i32* nonnull %p) +; CHECK: %x = call i32 @speculate_call(i32* %p) pred: %c0 = icmp ne i32* %p, null br i1 %c0, label %dispatch, label %final_right diff --git a/llvm/test/Transforms/SimplifyCFG/speculate-call.ll b/llvm/test/Transforms/SimplifyCFG/speculate-call.ll --- a/llvm/test/Transforms/SimplifyCFG/speculate-call.ll +++ b/llvm/test/Transforms/SimplifyCFG/speculate-call.ll @@ -20,13 +20,13 @@ ret i32 1 } -; FIXME: We should correctly drop the attribute since it may no longer be valid +; We should correctly drop the attribute since it may no longer be valid ; in the context the call is moved to. define i32 @strip_attr(i32 * %p) { ; CHECK-LABEL: strip_attr ; CHECK-LABEL: entry: ; CHECK: %nullchk = icmp ne i32* %p, null -; CHECK: %val = call i32 @func_deref(i32* nonnull %p) +; CHECK: %val = call i32 @func_deref(i32* %p) ; CHECK: select entry: %nullchk = icmp ne i32* %p, null