Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 23 additions & 22 deletions llvm/lib/Transforms/IPO/MergeFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,11 @@ class MergeFunctions {
/// again.
void mergeTwoFunctions(Function *F, Function *G);

void mergeInstrProfMetadataInto(Function *Dst, Function *Src);
/// Merge \p Src's instruction-level annotations into the corresponding
/// instructions of \p Dst. \p Dst is the surviving function; \p Src will be
/// erased or rewritten after this call.
/// Both functions must be structurally identical.
void mergeInstrAnnotations(Function *Dst, Function *Src);

/// Fill PDIUnrelatedWL with instructions from the entry block that are
/// unrelated to parameter related debug info.
Expand Down Expand Up @@ -303,9 +307,11 @@ class MergeFunctions {
// If needed, replace G with an alias to F if possible, or a thunk to F if
// profitable. Returns false if neither is the case. If \p G is not needed
// (i.e. it is discardable and not used), \p G is removed directly.
// \p MergeProfile must be true when G's profile should be preserved, it is
// merged into F before G is erased or rewritten.
bool writeThunkOrAliasIfNeeded(Function *F, Function *G, bool MergeProfile);
// If \p MergeAnnotations is true, annotations on G such as profiling
// information and poison-generating flags are merged into F before G is
// erased or rewritten.
bool writeThunkOrAliasIfNeeded(Function *F, Function *G,
bool MergeAnnotations);

/// Replace function F with function G in the function tree.
void replaceFunctionInTree(const FunctionNode &FN, Function *G);
Expand Down Expand Up @@ -908,12 +914,8 @@ static void mergeEntryCountsAndImportsInto(Function &F, Function &G) {
F.setEntryCount(Sum, AllImports.empty() ? nullptr : &AllImports);
}

// If needed, replace G with an alias to F if possible, or a thunk to F if
// profitable. Returns false if neither is the case. If \p G is not needed (i.e.
// it is discardable and unused), \p G is removed directly. If \p MergeProfile
// is set, G's profile metadata is merged into F.
bool MergeFunctions::writeThunkOrAliasIfNeeded(Function *F, Function *G,
bool MergeProfile) {
bool MergeAnnotations) {
bool ShouldErase =
G->isDiscardableIfUnused() && G->use_empty() && !MergeFunctionsPDI;
bool ShouldAlias = canCreateAliasFor(G);
Expand All @@ -922,8 +924,8 @@ bool MergeFunctions::writeThunkOrAliasIfNeeded(Function *F, Function *G,
if (!ShouldErase && !ShouldAlias && !ShouldThunk)
return false;

if (MergeProfile) {
mergeInstrProfMetadataInto(F, G);
if (MergeAnnotations) {
mergeInstrAnnotations(F, G);
mergeEntryCountsAndImportsInto(*F, *G);
}

Expand Down Expand Up @@ -1078,11 +1080,7 @@ static void mergeValueProfileOnInstructions(Instruction *DstI,
VDs.size());
}

/// Merge \p Src's instruction-level branch weights and value profile
/// metadata into the corresponding instructions of \p Dst. \p Dst is the
/// surviving function; \p Src will be erased or rewritten after this call.
/// Both functions must be structurally identical.
void MergeFunctions::mergeInstrProfMetadataInto(Function *Dst, Function *Src) {
void MergeFunctions::mergeInstrAnnotations(Function *Dst, Function *Src) {
const BlockFrequencyInfo &DstBFI =
FAM.getResult<BlockFrequencyAnalysis>(*Dst);
const BlockFrequencyInfo &SrcBFI =
Expand All @@ -1095,6 +1093,9 @@ void MergeFunctions::mergeInstrProfMetadataInto(Function *Dst, Function *Src) {
ReversePostOrderTraversal<Function *> SrcRPOT(Src);
for (auto [DstBB, SrcBB] : llvm::zip_equal(DstRPOT, SrcRPOT)) {
for (auto [DstI, SrcI] : llvm::zip_equal(*DstBB, *SrcBB)) {
// Merge poison-generating flags.
DstI.andIRFlags(&SrcI);

MDNode *DstProf = DstI.getMetadata(LLVMContext::MD_prof);
MDNode *SrcProf = SrcI.getMetadata(LLVMContext::MD_prof);
if ((DstProf && isValueProfileMD(DstProf)) ||
Expand Down Expand Up @@ -1164,12 +1165,12 @@ void MergeFunctions::mergeTwoFunctions(Function *F, Function *G) {
const MaybeAlign GAlign = G->getAlign();

// Merge !prof, while G still has its body.
writeThunkOrAliasIfNeeded(F, G, /*MergeProfile*/ true);
writeThunkOrAliasIfNeeded(F, G, /*MergeAnnotations=*/true);
if (FEntryCount)
NewF->setEntryCount(*FEntryCount);
// NewF becomes thunk/alias to the shared body F, it has no profile to be
// merged.
writeThunkOrAliasIfNeeded(F, NewF, /*MergeProfile*/ false);
// NewF becomes thunk/alias to the shared body F, it has no annotations to
// be merged.
writeThunkOrAliasIfNeeded(F, NewF, /*MergeAnnotations=*/false);

if (NewFAlign || GAlign)
F->setAlignment(std::max(NewFAlign.valueOrOne(), GAlign.valueOrOne()));
Expand Down Expand Up @@ -1203,14 +1204,14 @@ void MergeFunctions::mergeTwoFunctions(Function *F, Function *G) {
// stop here and delete G. There's no need for a thunk. (See note on
// MergeFunctionsPDI above).
if (G->isDiscardableIfUnused() && G->use_empty() && !MergeFunctionsPDI) {
mergeInstrProfMetadataInto(F, G);
mergeInstrAnnotations(F, G);
mergeEntryCountsAndImportsInto(*F, *G);
G->eraseFromParent();
++NumFunctionsMerged;
return;
}

if (writeThunkOrAliasIfNeeded(F, G, /*MergeProfile*/ true))
if (writeThunkOrAliasIfNeeded(F, G, /*MergeAnnotations=*/true))
++NumFunctionsMerged;
}
}
Expand Down
6 changes: 0 additions & 6 deletions llvm/lib/Transforms/Utils/FunctionComparator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -658,8 +658,6 @@ int FunctionComparator::cmpOperations(const Instruction *L,

// Differences from Instruction::isSameOperationAs:
// * replace type comparison with calls to cmpTypes.
// * we test for I->getRawSubclassOptionalData (nuw/nsw/tail) at the top.
// * because of the above, we don't test for the tail bit on calls later on.
Comment on lines -661 to -662

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nit: the comment was quite stale, though perhaps we could still mention that we do test the tail bit on calls later (CallBase branch)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went ahead and just dropped this comment entirely. I don't think it really provides useful information.

if (int Res = cmpNumbers(L->getOpcode(), R->getOpcode()))
return Res;

Expand All @@ -678,10 +676,6 @@ int FunctionComparator::cmpOperations(const Instruction *L,
if (int Res = cmpTypes(L->getType(), R->getType()))
return Res;

if (int Res = cmpNumbers(L->getRawSubclassOptionalData(),
R->getRawSubclassOptionalData()))
return Res;

// We have two instructions of identical opcode and #operands. Check to see
// if all operands are the same type
for (unsigned i = 0, e = L->getNumOperands(); i != e; ++i) {
Expand Down
86 changes: 86 additions & 0 deletions llvm/test/Transforms/MergeFunc/flags.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
; RUN: opt -S -passes=mergefunc < %s | FileCheck %s

define i32 @fn_add_nuw_nsw(i32 %a) {
; CHECK-LABEL: define i32 @fn_add_nuw_nsw(
; CHECK-SAME: i32 [[A:%.*]]) {
; CHECK-NEXT: [[ADD:%.*]] = add nuw i32 [[A]], 1
; CHECK-NEXT: ret i32 [[ADD]]
;
%add = add nuw nsw i32 %a, 1
ret i32 %add
}

define internal i32 @fn_add_nuw(i32 %a) {
%add = add nuw i32 %a, 1
ret i32 %add
}

define ptr @fn_gep_inbounds(ptr %a) {
; CHECK-LABEL: define ptr @fn_gep_inbounds(
; CHECK-SAME: ptr [[A:%.*]]) {
; CHECK-NEXT: [[GEP:%.*]] = getelementptr nusw i8, ptr [[A]], i64 1
; CHECK-NEXT: ret ptr [[GEP]]
;
%gep = getelementptr inbounds i8, ptr %a, i64 1
ret ptr %gep
}

define internal ptr @fn_gep_nusw(ptr %a) {
%gep = getelementptr nusw i8, ptr %a, i64 1
ret ptr %gep
}

define ptr @fn_gep_inbounds2(ptr %a) {
; CHECK-LABEL: define ptr @fn_gep_inbounds2(
; CHECK-SAME: ptr [[A:%.*]]) {
; CHECK-NEXT: [[GEP:%.*]] = getelementptr i8, ptr [[A]], i64 2
; CHECK-NEXT: ret ptr [[GEP]]
;
%gep = getelementptr inbounds i8, ptr %a, i64 2
ret ptr %gep
}

define internal ptr @fn_gep_nuw(ptr %a) {
%gep = getelementptr nuw i8, ptr %a, i64 2
ret ptr %gep
}

define float @fn_fadd_ninf_nnan(float %a) {
; CHECK-LABEL: define float @fn_fadd_ninf_nnan(
; CHECK-SAME: float [[A:%.*]]) {
; CHECK-NEXT: [[ADD:%.*]] = fadd ninf float [[A]], 1.000000e+00
; CHECK-NEXT: ret float [[ADD]]
;
%add = fadd ninf nnan float %a, 1.0
ret float %add
}

define internal float @fn_fadd_ninf_reassoc(float %a) {
%add = fadd ninf reassoc float %a, 1.0
ret float %add
}

define void @calls(i32 %x, ptr %p, float %f) {
; CHECK-LABEL: define void @calls(
; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]], float [[F:%.*]]) {
; CHECK-NEXT: [[TMP1:%.*]] = call i32 @fn_add_nuw_nsw(i32 [[X]])
; CHECK-NEXT: [[TMP2:%.*]] = call i32 @fn_add_nuw_nsw(i32 [[X]])
; CHECK-NEXT: [[TMP3:%.*]] = call ptr @fn_gep_inbounds(ptr [[P]])
; CHECK-NEXT: [[TMP4:%.*]] = call ptr @fn_gep_inbounds(ptr [[P]])
; CHECK-NEXT: [[TMP5:%.*]] = call ptr @fn_gep_inbounds2(ptr [[P]])
; CHECK-NEXT: [[TMP6:%.*]] = call ptr @fn_gep_inbounds2(ptr [[P]])
; CHECK-NEXT: [[TMP7:%.*]] = call float @fn_fadd_ninf_nnan(float [[F]])
; CHECK-NEXT: [[TMP8:%.*]] = call float @fn_fadd_ninf_nnan(float [[F]])
; CHECK-NEXT: ret void
;
call i32 @fn_add_nuw_nsw(i32 %x)
call i32 @fn_add_nuw(i32 %x)
call ptr @fn_gep_inbounds(ptr %p)
call ptr @fn_gep_nusw(ptr %p)
call ptr @fn_gep_inbounds2(ptr %p)
call ptr @fn_gep_nuw(ptr %p)
call float @fn_fadd_ninf_nnan(float %f)
call float @fn_fadd_ninf_reassoc(float %f)
ret void
}
Loading