Skip to content

Commit 7c3cf4c

Browse files
committed
[Inline][X86] Avoid inlining if it would create ABI-incompatible calls (PR52660)
X86 allows inlining functions if the callee target features are a subset of the caller target features. This ensures that we don't inline something into a caller that does not support it. However, this does not account for possible call ABI mismatches as a result of inlining. If a call passing a vector argument was originally in a -avx function, calling another -avx function, the vector is passed in xmm. If we now inline it into a +avx function, then it will be passed in ymm, even though the callee expects it in xmm. Fix this by scanning over all calls in the function and checking whether ABI incompatibility is possible. Calls that only pass scalar types are excluded, as I believe those always use the same ABI independent of target features. Fixes #52660. Differential Revision: https://reviews.llvm.org/D116036
1 parent abc388e commit 7c3cf4c

File tree

2 files changed

+51
-5
lines changed

2 files changed

+51
-5
lines changed

Diff for: llvm/lib/Target/X86/X86TargetTransformInfo.cpp

+41-1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include "llvm/CodeGen/BasicTTIImpl.h"
4444
#include "llvm/CodeGen/CostTable.h"
4545
#include "llvm/CodeGen/TargetLowering.h"
46+
#include "llvm/IR/InstIterator.h"
4647
#include "llvm/IR/IntrinsicInst.h"
4748
#include "llvm/Support/Debug.h"
4849

@@ -5187,9 +5188,48 @@ bool X86TTIImpl::areInlineCompatible(const Function *Caller,
51875188
const FeatureBitset &CalleeBits =
51885189
TM.getSubtargetImpl(*Callee)->getFeatureBits();
51895190

5191+
// Check whether features are the same (apart from the ignore list).
51905192
FeatureBitset RealCallerBits = CallerBits & ~InlineFeatureIgnoreList;
51915193
FeatureBitset RealCalleeBits = CalleeBits & ~InlineFeatureIgnoreList;
5192-
return (RealCallerBits & RealCalleeBits) == RealCalleeBits;
5194+
if (RealCallerBits == RealCalleeBits)
5195+
return true;
5196+
5197+
// If the features are a subset, we need to additionally check for calls
5198+
// that may become ABI-incompatible as a result of inlining.
5199+
if ((RealCallerBits & RealCalleeBits) != RealCalleeBits)
5200+
return false;
5201+
5202+
for (const Instruction &I : instructions(Callee)) {
5203+
if (const auto *CB = dyn_cast<CallBase>(&I)) {
5204+
SmallVector<Type *, 8> Types;
5205+
for (Value *Arg : CB->args())
5206+
Types.push_back(Arg->getType());
5207+
if (!CB->getType()->isVoidTy())
5208+
Types.push_back(CB->getType());
5209+
5210+
// Simple types are always ABI compatible.
5211+
auto IsSimpleTy = [](Type *Ty) {
5212+
return !Ty->isVectorTy() && !Ty->isAggregateType();
5213+
};
5214+
if (all_of(Types, IsSimpleTy))
5215+
continue;
5216+
5217+
if (Function *NestedCallee = CB->getCalledFunction()) {
5218+
// Assume that intrinsics are always ABI compatible.
5219+
if (NestedCallee->isIntrinsic())
5220+
continue;
5221+
5222+
// Do a precise compatibility check.
5223+
if (!areTypesABICompatible(Caller, NestedCallee, Types))
5224+
return false;
5225+
} else {
5226+
// We don't know the target features of the callee,
5227+
// assume it is incompatible.
5228+
return false;
5229+
}
5230+
}
5231+
}
5232+
return true;
51935233
}
51945234

51955235
bool X86TTIImpl::areTypesABICompatible(const Function *Caller,

Diff for: llvm/test/Transforms/Inline/X86/call-abi-compatibility.ll

+10-4
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,21 @@
55

66
; This call should not get inlined, because it would make the callee_not_avx
77
; call ABI incompatible.
8-
; TODO: Currently gets inlined.
98
define void @caller_avx() "target-features"="+avx" {
109
; CHECK-LABEL: define {{[^@]+}}@caller_avx
1110
; CHECK-SAME: () #[[ATTR0:[0-9]+]] {
12-
; CHECK-NEXT: [[TMP1:%.*]] = call i64 @callee_not_avx(<4 x i64> <i64 0, i64 1, i64 2, i64 3>)
11+
; CHECK-NEXT: call void @caller_not_avx()
1312
; CHECK-NEXT: ret void
1413
;
1514
call void @caller_not_avx()
1615
ret void
1716
}
1817

1918
define internal void @caller_not_avx() {
19+
; CHECK-LABEL: define {{[^@]+}}@caller_not_avx() {
20+
; CHECK-NEXT: [[TMP1:%.*]] = call i64 @callee_not_avx(<4 x i64> <i64 0, i64 1, i64 2, i64 3>)
21+
; CHECK-NEXT: ret void
22+
;
2023
call i64 @callee_not_avx(<4 x i64> <i64 0, i64 1, i64 2, i64 3>)
2124
ret void
2225
}
@@ -33,18 +36,21 @@ define i64 @callee_not_avx(<4 x i64> %arg) noinline {
3336

3437
; This call also shouldn't be inlined, as we don't know whether callee_unknown
3538
; is ABI compatible or not.
36-
; TODO: Currently gets inlined.
3739
define void @caller_avx2() "target-features"="+avx" {
3840
; CHECK-LABEL: define {{[^@]+}}@caller_avx2
3941
; CHECK-SAME: () #[[ATTR0]] {
40-
; CHECK-NEXT: [[TMP1:%.*]] = call i64 @callee_unknown(<4 x i64> <i64 0, i64 1, i64 2, i64 3>)
42+
; CHECK-NEXT: call void @caller_not_avx2()
4143
; CHECK-NEXT: ret void
4244
;
4345
call void @caller_not_avx2()
4446
ret void
4547
}
4648

4749
define internal void @caller_not_avx2() {
50+
; CHECK-LABEL: define {{[^@]+}}@caller_not_avx2() {
51+
; CHECK-NEXT: [[TMP1:%.*]] = call i64 @callee_unknown(<4 x i64> <i64 0, i64 1, i64 2, i64 3>)
52+
; CHECK-NEXT: ret void
53+
;
4854
call i64 @callee_unknown(<4 x i64> <i64 0, i64 1, i64 2, i64 3>)
4955
ret void
5056
}

0 commit comments

Comments
 (0)