Skip to content

Commit

Permalink
Only promote args when function attributes are compatible
Browse files Browse the repository at this point in the history
Summary:
Check to make sure that the caller and the callee have compatible
function arguments before promoting arguments.  This uses the same
TargetTransformInfo queries that are used to determine if attributes
are compatible for inlining.

The goal here is to avoid breaking ABI when a called function's ABI
depends on a target feature that is not enabled in the caller.

This is a very conservative fix for PR37358.  Ideally we would have a more
sophisticated check for ABI compatiblity rather than checking if the
attributes are compatible for inlining.

Reviewers: echristo, chandlerc, eli.friedman, craig.topper

Reviewed By: echristo, chandlerc

Subscribers: nikic, xbolva00, rkruppe, alexcrichton, llvm-commits

Differential Revision: https://reviews.llvm.org/D53554

llvm-svn: 351296
  • Loading branch information
tstellar committed Jan 16, 2019
1 parent a5b0e55 commit 3d36e5c
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 4 deletions.
16 changes: 16 additions & 0 deletions llvm/include/llvm/Analysis/TargetTransformInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,14 @@ class TargetTransformInfo {
bool areInlineCompatible(const Function *Caller,
const Function *Callee) const;

/// \returns True if the caller and callee agree on how \p Args will be passed
/// to the callee.
/// \param[out] Args The list of compatible arguments. The implementation may
/// filter out any incompatible args from this list.
bool areFunctionArgsABICompatible(const Function *Caller,
const Function *Callee,
SmallPtrSetImpl<Argument *> &Args) const;

/// The type of load/store indexing.
enum MemIndexedMode {
MIM_Unindexed, ///< No indexing.
Expand Down Expand Up @@ -1179,6 +1187,9 @@ class TargetTransformInfo::Concept {
unsigned RemainingBytes, unsigned SrcAlign, unsigned DestAlign) const = 0;
virtual bool areInlineCompatible(const Function *Caller,
const Function *Callee) const = 0;
virtual bool
areFunctionArgsABICompatible(const Function *Caller, const Function *Callee,
SmallPtrSetImpl<Argument *> &Args) const = 0;
virtual bool isIndexedLoadLegal(MemIndexedMode Mode, Type *Ty) const = 0;
virtual bool isIndexedStoreLegal(MemIndexedMode Mode,Type *Ty) const = 0;
virtual unsigned getLoadStoreVecRegBitWidth(unsigned AddrSpace) const = 0;
Expand Down Expand Up @@ -1557,6 +1568,11 @@ class TargetTransformInfo::Model final : public TargetTransformInfo::Concept {
const Function *Callee) const override {
return Impl.areInlineCompatible(Caller, Callee);
}
bool areFunctionArgsABICompatible(
const Function *Caller, const Function *Callee,
SmallPtrSetImpl<Argument *> &Args) const override {
return Impl.areFunctionArgsABICompatible(Caller, Callee, Args);
}
bool isIndexedLoadLegal(MemIndexedMode Mode, Type *Ty) const override {
return Impl.isIndexedLoadLegal(Mode, Ty, getDataLayout());
}
Expand Down
8 changes: 8 additions & 0 deletions llvm/include/llvm/Analysis/TargetTransformInfoImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,14 @@ class TargetTransformInfoImplBase {
Callee->getFnAttribute("target-features"));
}

bool areFunctionArgsABICompatible(const Function *Caller, const Function *Callee,
SmallPtrSetImpl<Argument *> &Args) const {
return (Caller->getFnAttribute("target-cpu") ==
Callee->getFnAttribute("target-cpu")) &&
(Caller->getFnAttribute("target-features") ==
Callee->getFnAttribute("target-features"));
}

bool isIndexedLoadLegal(TTI::MemIndexedMode Mode, Type *Ty,
const DataLayout &DL) const {
return false;
Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/Analysis/TargetTransformInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,12 @@ bool TargetTransformInfo::areInlineCompatible(const Function *Caller,
return TTIImpl->areInlineCompatible(Caller, Callee);
}

bool TargetTransformInfo::areFunctionArgsABICompatible(
const Function *Caller, const Function *Callee,
SmallPtrSetImpl<Argument *> &Args) const {
return TTIImpl->areFunctionArgsABICompatible(Caller, Callee, Args);
}

bool TargetTransformInfo::isIndexedLoadLegal(MemIndexedMode Mode,
Type *Ty) const {
return TTIImpl->isIndexedLoadLegal(Mode, Ty);
Expand Down
35 changes: 31 additions & 4 deletions llvm/lib/Transforms/IPO/ArgumentPromotion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#include "llvm/Analysis/Loads.h"
#include "llvm/Analysis/MemoryLocation.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/IR/Argument.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/BasicBlock.h"
Expand Down Expand Up @@ -809,6 +810,21 @@ static bool canPaddingBeAccessed(Argument *arg) {
return false;
}

static bool areFunctionArgsABICompatible(
const Function &F, const TargetTransformInfo &TTI,
SmallPtrSetImpl<Argument *> &ArgsToPromote,
SmallPtrSetImpl<Argument *> &ByValArgsToTransform) {
for (const Use &U : F.uses()) {
CallSite CS(U.getUser());
const Function *Caller = CS.getCaller();
const Function *Callee = CS.getCalledFunction();
if (!TTI.areFunctionArgsABICompatible(Caller, Callee, ArgsToPromote) ||
!TTI.areFunctionArgsABICompatible(Caller, Callee, ByValArgsToTransform))
return false;
}
return true;
}

/// PromoteArguments - This method checks the specified function to see if there
/// are any promotable arguments and if it is safe to promote the function (for
/// example, all callers are direct). If safe to promote some arguments, it
Expand All @@ -817,7 +833,8 @@ static Function *
promoteArguments(Function *F, function_ref<AAResults &(Function &F)> AARGetter,
unsigned MaxElements,
Optional<function_ref<void(CallSite OldCS, CallSite NewCS)>>
ReplaceCallSite) {
ReplaceCallSite,
const TargetTransformInfo &TTI) {
// Don't perform argument promotion for naked functions; otherwise we can end
// up removing parameters that are seemingly 'not used' as they are referred
// to in the assembly.
Expand Down Expand Up @@ -846,7 +863,7 @@ promoteArguments(Function *F, function_ref<AAResults &(Function &F)> AARGetter,

// Second check: make sure that all callers are direct callers. We can't
// transform functions that have indirect callers. Also see if the function
// is self-recursive.
// is self-recursive and check that target features are compatible.
bool isSelfRecursive = false;
for (Use &U : F->uses()) {
CallSite CS(U.getUser());
Expand Down Expand Up @@ -955,6 +972,10 @@ promoteArguments(Function *F, function_ref<AAResults &(Function &F)> AARGetter,
if (ArgsToPromote.empty() && ByValArgsToTransform.empty())
return nullptr;

if (!areFunctionArgsABICompatible(*F, TTI, ArgsToPromote,
ByValArgsToTransform))
return nullptr;

return doPromotion(F, ArgsToPromote, ByValArgsToTransform, ReplaceCallSite);
}

Expand All @@ -980,7 +1001,9 @@ PreservedAnalyses ArgumentPromotionPass::run(LazyCallGraph::SCC &C,
return FAM.getResult<AAManager>(F);
};

Function *NewF = promoteArguments(&OldF, AARGetter, MaxElements, None);
const TargetTransformInfo &TTI = FAM.getResult<TargetIRAnalysis>(OldF);
Function *NewF =
promoteArguments(&OldF, AARGetter, MaxElements, None, TTI);
if (!NewF)
continue;
LocalChange = true;
Expand Down Expand Up @@ -1018,6 +1041,7 @@ struct ArgPromotion : public CallGraphSCCPass {
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.addRequired<AssumptionCacheTracker>();
AU.addRequired<TargetLibraryInfoWrapperPass>();
AU.addRequired<TargetTransformInfoWrapperPass>();
getAAResultsAnalysisUsage(AU);
CallGraphSCCPass::getAnalysisUsage(AU);
}
Expand All @@ -1043,6 +1067,7 @@ INITIALIZE_PASS_BEGIN(ArgPromotion, "argpromotion",
INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker)
INITIALIZE_PASS_DEPENDENCY(CallGraphWrapperPass)
INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass)
INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass)
INITIALIZE_PASS_END(ArgPromotion, "argpromotion",
"Promote 'by reference' arguments to scalars", false, false)

Expand Down Expand Up @@ -1079,8 +1104,10 @@ bool ArgPromotion::runOnSCC(CallGraphSCC &SCC) {
CallerNode->replaceCallEdge(OldCS, NewCS, NewCalleeNode);
};

const TargetTransformInfo &TTI =
getAnalysis<TargetTransformInfoWrapperPass>().getTTI(*OldF);
if (Function *NewF = promoteArguments(OldF, AARGetter, MaxElements,
{ReplaceCallSite})) {
{ReplaceCallSite}, TTI)) {
LocalChange = true;

// Update the call graph for the newly promoted function.
Expand Down
53 changes: 53 additions & 0 deletions llvm/test/Transforms/ArgumentPromotion/X86/attributes.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
; RUN: opt -S -argpromotion < %s | FileCheck %s
; RUN: opt -S -passes=argpromotion < %s | FileCheck %s
; Test that we only promote arguments when the caller/callee have compatible
; function attrubtes.

target triple = "x86_64-unknown-linux-gnu"

; CHECK-LABEL: @no_promote_avx2(<4 x i64>* %arg, <4 x i64>* readonly %arg1)
define internal fastcc void @no_promote_avx2(<4 x i64>* %arg, <4 x i64>* readonly %arg1) #0 {
bb:
%tmp = load <4 x i64>, <4 x i64>* %arg1
store <4 x i64> %tmp, <4 x i64>* %arg
ret void
}

define void @no_promote(<4 x i64>* %arg) #1 {
bb:
%tmp = alloca <4 x i64>, align 32
%tmp2 = alloca <4 x i64>, align 32
%tmp3 = bitcast <4 x i64>* %tmp to i8*
call void @llvm.memset.p0i8.i64(i8* align 32 %tmp3, i8 0, i64 32, i1 false)
call fastcc void @no_promote_avx2(<4 x i64>* %tmp2, <4 x i64>* %tmp)
%tmp4 = load <4 x i64>, <4 x i64>* %tmp2, align 32
store <4 x i64> %tmp4, <4 x i64>* %arg, align 2
ret void
}

; CHECK-LABEL: @promote_avx2(<4 x i64>* %arg, <4 x i64> %
define internal fastcc void @promote_avx2(<4 x i64>* %arg, <4 x i64>* readonly %arg1) #0 {
bb:
%tmp = load <4 x i64>, <4 x i64>* %arg1
store <4 x i64> %tmp, <4 x i64>* %arg
ret void
}

define void @promote(<4 x i64>* %arg) #0 {
bb:
%tmp = alloca <4 x i64>, align 32
%tmp2 = alloca <4 x i64>, align 32
%tmp3 = bitcast <4 x i64>* %tmp to i8*
call void @llvm.memset.p0i8.i64(i8* align 32 %tmp3, i8 0, i64 32, i1 false)
call fastcc void @promote_avx2(<4 x i64>* %tmp2, <4 x i64>* %tmp)
%tmp4 = load <4 x i64>, <4 x i64>* %tmp2, align 32
store <4 x i64> %tmp4, <4 x i64>* %arg, align 2
ret void
}

; Function Attrs: argmemonly nounwind
declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1) #2

attributes #0 = { inlinehint norecurse nounwind uwtable "target-features"="+avx2" }
attributes #1 = { nounwind uwtable }
attributes #2 = { argmemonly nounwind }

0 comments on commit 3d36e5c

Please sign in to comment.