Skip to content

Commit abff495

Browse files
antoniofrighettoaleclearmind
authored andcommitted
Bitcast wrapped in a call obscures function attributes, pessimizing MemorySSA
A logic incompleteness may lead MemorySSA to be too conservative in its results. Specifically, when dealing with a call of kind `call i32 bitcast (i1 (i1)* @test to i32 (i32)*)(i32 %1)`, where function call `test` is declared with `readonly` attribute, the bitcast is not wrapped, obscuring function attributes. Hence, some methods of CallBase (e.g., `doesNotReadMemory`) could provide incomplete results. This issue was addressed with improved checks.
1 parent f1e1e48 commit abff495

5 files changed

Lines changed: 114 additions & 43 deletions

File tree

llvm/lib/IR/Instructions.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,14 +334,26 @@ bool CallBase::paramHasAttr(unsigned ArgNo, Attribute::AttrKind Kind) const {
334334
}
335335

336336
bool CallBase::hasFnAttrOnCalledFunction(Attribute::AttrKind Kind) const {
337-
if (const Function *F = getCalledFunction())
337+
Value *V = getCalledOperand();
338+
if (auto *CE = dyn_cast<ConstantExpr>(V))
339+
if (CE->getOpcode() == BitCast)
340+
V = CE->getOperand(0);
341+
342+
if (auto *F = dyn_cast<Function>(V))
338343
return F->getAttributes().hasFnAttribute(Kind);
344+
339345
return false;
340346
}
341347

342348
bool CallBase::hasFnAttrOnCalledFunction(StringRef Kind) const {
343-
if (const Function *F = getCalledFunction())
349+
Value *V = getCalledOperand();
350+
if (auto *CE = dyn_cast<ConstantExpr>(V))
351+
if (CE->getOpcode() == BitCast)
352+
V = CE->getOperand(0);
353+
354+
if (auto *F = dyn_cast<Function>(V))
344355
return F->getAttributes().hasFnAttribute(Kind);
356+
345357
return false;
346358
}
347359

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
; RUN: opt -aa-pipeline=basic-aa -passes='print<memoryssa>,verify<memoryssa>' -disable-output < %s 2>&1 | FileCheck %s
2+
;
3+
; Ensures that MemorySSA leverages the ground truth of the function being called when wrapped in a bitcast.
4+
5+
declare i1 @opaque_true(i1) nounwind readonly
6+
7+
define i1 @foo(i32* %ptr, i1 %cond) {
8+
%cond_wide = zext i1 %cond to i32
9+
; CHECK: MemoryUse(liveOnEntry) MayAlias
10+
; CHECK-NEXT: call i32 bitcast
11+
%cond_hidden_wide = call i32 bitcast (i1 (i1)* @opaque_true to i32 (i32)*)(i32 %cond_wide)
12+
%cond_hidden = trunc i32 %cond_hidden_wide to i1
13+
ret i1 %cond_hidden
14+
}

llvm/test/Transforms/Attributor/IPConstantProp/arg-count-mismatch.ll

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,16 @@ define internal i16 @bar(i16 %p1, i16 %p2) {
7070
}
7171

7272
define dso_local i16 @foo2(i16 %a) {
73-
; CHECK-LABEL: define {{[^@]+}}@foo2
74-
; CHECK-SAME: (i16 [[A:%.*]]) {
75-
; CHECK-NEXT: [[CALL:%.*]] = call i16 bitcast (i16 (i16, i16)* @bar2 to i16 (i16)*)(i16 [[A]])
76-
; CHECK-NEXT: ret i16 [[CALL]]
73+
; NOT_CGSCC_NPM-LABEL: define {{[^@]+}}@foo2
74+
; NOT_CGSCC_NPM-SAME: (i16 [[A:%.*]]) {
75+
; NOT_CGSCC_NPM-NEXT: [[CALL:%.*]] = call i16 bitcast (i16 (i16, i16)* @bar2 to i16 (i16)*)(i16 [[A]])
76+
; NOT_CGSCC_NPM-NEXT: ret i16 [[CALL]]
77+
;
78+
; IS__CGSCC_NPM: Function Attrs: nofree norecurse nosync nounwind readnone
79+
; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@foo2
80+
; IS__CGSCC_NPM-SAME: (i16 [[A:%.*]]) [[ATTR1:#.*]] {
81+
; IS__CGSCC_NPM-NEXT: [[CALL:%.*]] = call i16 bitcast (i16 (i16, i16)* @bar2 to i16 (i16)*)(i16 [[A]])
82+
; IS__CGSCC_NPM-NEXT: ret i16 [[CALL]]
7783
;
7884
%call = call i16 bitcast (i16 (i16, i16) * @bar2 to i16 (i16) *)(i16 %a)
7985
ret i16 %call
@@ -103,11 +109,18 @@ define internal i16 @bar2(i16 %p1, i16 %p2) {
103109
; been provided),
104110

105111
define dso_local i16 @vararg_tests(i16 %a) {
106-
; CHECK-LABEL: define {{[^@]+}}@vararg_tests
107-
; CHECK-SAME: (i16 [[A:%.*]]) {
108-
; CHECK-NEXT: [[CALL2:%.*]] = call i16 bitcast (i16 (i16, i16, ...)* @vararg_no_prop to i16 (i16)*)(i16 noundef 7)
109-
; CHECK-NEXT: [[ADD:%.*]] = add i16 7, [[CALL2]]
110-
; CHECK-NEXT: ret i16 [[ADD]]
112+
; NOT_CGSCC_NPM-LABEL: define {{[^@]+}}@vararg_tests
113+
; NOT_CGSCC_NPM-SAME: (i16 [[A:%.*]]) {
114+
; NOT_CGSCC_NPM-NEXT: [[CALL2:%.*]] = call i16 bitcast (i16 (i16, i16, ...)* @vararg_no_prop to i16 (i16)*)(i16 noundef 7)
115+
; NOT_CGSCC_NPM-NEXT: [[ADD:%.*]] = add i16 7, [[CALL2]]
116+
; NOT_CGSCC_NPM-NEXT: ret i16 [[ADD]]
117+
;
118+
; IS__CGSCC_NPM: Function Attrs: nofree norecurse nosync nounwind readnone
119+
; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@vararg_tests
120+
; IS__CGSCC_NPM-SAME: (i16 [[A:%.*]]) [[ATTR1]] {
121+
; IS__CGSCC_NPM-NEXT: [[CALL2:%.*]] = call i16 bitcast (i16 (i16, i16, ...)* @vararg_no_prop to i16 (i16)*)(i16 noundef 7)
122+
; IS__CGSCC_NPM-NEXT: [[ADD:%.*]] = add i16 7, [[CALL2]]
123+
; IS__CGSCC_NPM-NEXT: ret i16 [[ADD]]
111124
;
112125
%call1 = call i16 (i16, ...) @vararg_prop(i16 7, i16 8, i16 %a)
113126
%call2 = call i16 bitcast (i16 (i16, i16, ...) * @vararg_no_prop to i16 (i16) *) (i16 7)

llvm/test/Transforms/Attributor/IPConstantProp/arg-type-mismatch.ll

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,16 @@
88
; argument type between the caller and callee.
99

1010
define dso_local i16 @foo(i16 %a) {
11-
; CHECK-LABEL: define {{[^@]+}}@foo
12-
; CHECK-SAME: (i16 [[A:%.*]]) {
13-
; CHECK-NEXT: [[CALL:%.*]] = call i16 bitcast (i16 (i16, i16)* @bar to i16 (i16, i32)*)(i16 [[A]], i32 7)
14-
; CHECK-NEXT: ret i16 [[CALL]]
11+
; NOT_CGSCC_NPM-LABEL: define {{[^@]+}}@foo
12+
; NOT_CGSCC_NPM-SAME: (i16 [[A:%.*]]) {
13+
; NOT_CGSCC_NPM-NEXT: [[CALL:%.*]] = call i16 bitcast (i16 (i16, i16)* @bar to i16 (i16, i32)*)(i16 [[A]], i32 7)
14+
; NOT_CGSCC_NPM-NEXT: ret i16 [[CALL]]
15+
;
16+
; IS__CGSCC_NPM: Function Attrs: nofree norecurse nosync nounwind readnone
17+
; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@foo
18+
; IS__CGSCC_NPM-SAME: (i16 [[A:%.*]]) [[ATTR0:#.*]] {
19+
; IS__CGSCC_NPM-NEXT: [[CALL:%.*]] = call i16 bitcast (i16 (i16, i16)* @bar to i16 (i16, i32)*)(i16 [[A]], i32 7)
20+
; IS__CGSCC_NPM-NEXT: ret i16 [[CALL]]
1521
;
1622
%call = call i16 bitcast (i16 (i16, i16) * @bar to i16 (i16, i32) *)(i16 %a, i32 7)
1723
ret i16 %call
@@ -25,7 +31,7 @@ define internal i16 @bar(i16 %p1, i16 %p2) {
2531
;
2632
; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
2733
; IS__CGSCC____-LABEL: define {{[^@]+}}@bar
28-
; IS__CGSCC____-SAME: (i16 [[P1:%.*]], i16 returned [[P2:%.*]]) [[ATTR0:#.*]] {
34+
; IS__CGSCC____-SAME: (i16 [[P1:%.*]], i16 returned [[P2:%.*]]) [[ATTR1:#.*]] {
2935
; IS__CGSCC____-NEXT: ret i16 [[P2]]
3036
;
3137
ret i16 %p2

llvm/test/Transforms/Attributor/liveness.ll

Lines changed: 53 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2425,33 +2425,59 @@ indirectgoto: ; preds = %lab0, %entry
24252425
@e = global %struct.a* null
24262426

24272427
define i32 @main() {
2428-
; CHECK-LABEL: define {{[^@]+}}@main() {
2429-
; CHECK-NEXT: entry:
2430-
; CHECK-NEXT: [[F:%.*]] = alloca i32, align 4
2431-
; CHECK-NEXT: br label [[FOR_COND_0:%.*]]
2432-
; CHECK: for.cond.0:
2433-
; CHECK-NEXT: [[G_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY_0:%.*]] ]
2434-
; CHECK-NEXT: [[CMP_0:%.*]] = icmp ult i32 [[G_0]], 100
2435-
; CHECK-NEXT: br i1 [[CMP_0]], label [[FOR_BODY_0]], label [[FOR_END_0:%.*]]
2436-
; CHECK: for.body.0:
2437-
; CHECK-NEXT: [[INC]] = add nuw nsw i32 [[G_0]], 1
2438-
; CHECK-NEXT: br label [[FOR_COND_0]]
2439-
; CHECK: for.end.0:
2440-
; CHECK-NEXT: [[CALL:%.*]] = call i8* @malloc(i64 noundef 8)
2441-
; CHECK-NEXT: store i8* [[CALL]], i8** bitcast (%struct.a** @e to i8**), align 8
2442-
; CHECK-NEXT: [[B:%.*]] = bitcast i8* [[CALL]] to %struct.a**
2443-
; CHECK-NEXT: store %struct.a* null, %struct.a** [[B]], align 8
2444-
; CHECK-NEXT: br label [[FOR_COND_1:%.*]]
2445-
; CHECK: for.cond.1:
2446-
; CHECK-NEXT: [[G_1:%.*]] = phi i32 [ 0, [[FOR_END_0]] ], [ [[INC6:%.*]], [[FOR_BODY_1:%.*]] ]
2447-
; CHECK-NEXT: [[CMP_1:%.*]] = icmp ult i32 [[G_1]], 100
2448-
; CHECK-NEXT: br i1 [[CMP_1]], label [[FOR_BODY_1]], label [[FOR_END_1:%.*]]
2449-
; CHECK: for.body.1:
2450-
; CHECK-NEXT: [[CALL4:%.*]] = call i32 (i32*, ...) bitcast (i32 (i32)* @h to i32 (i32*, ...)*)(i32* nonnull [[F]])
2451-
; CHECK-NEXT: [[INC6]] = add nuw nsw i32 [[G_1]], 1
2452-
; CHECK-NEXT: br label [[FOR_COND_1]]
2453-
; CHECK: for.end.1:
2454-
; CHECK-NEXT: ret i32 0
2428+
; NOT_CGSCC_NPM-LABEL: define {{[^@]+}}@main() {
2429+
; NOT_CGSCC_NPM-NEXT: entry:
2430+
; NOT_CGSCC_NPM-NEXT: [[F:%.*]] = alloca i32, align 4
2431+
; NOT_CGSCC_NPM-NEXT: br label [[FOR_COND_0:%.*]]
2432+
; NOT_CGSCC_NPM: for.cond.0:
2433+
; NOT_CGSCC_NPM-NEXT: [[G_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY_0:%.*]] ]
2434+
; NOT_CGSCC_NPM-NEXT: [[CMP_0:%.*]] = icmp ult i32 [[G_0]], 100
2435+
; NOT_CGSCC_NPM-NEXT: br i1 [[CMP_0]], label [[FOR_BODY_0]], label [[FOR_END_0:%.*]]
2436+
; NOT_CGSCC_NPM: for.body.0:
2437+
; NOT_CGSCC_NPM-NEXT: [[INC]] = add nuw nsw i32 [[G_0]], 1
2438+
; NOT_CGSCC_NPM-NEXT: br label [[FOR_COND_0]]
2439+
; NOT_CGSCC_NPM: for.end.0:
2440+
; NOT_CGSCC_NPM-NEXT: [[CALL:%.*]] = call i8* @malloc(i64 noundef 8)
2441+
; NOT_CGSCC_NPM-NEXT: store i8* [[CALL]], i8** bitcast (%struct.a** @e to i8**), align 8
2442+
; NOT_CGSCC_NPM-NEXT: [[B:%.*]] = bitcast i8* [[CALL]] to %struct.a**
2443+
; NOT_CGSCC_NPM-NEXT: store %struct.a* null, %struct.a** [[B]], align 8
2444+
; NOT_CGSCC_NPM-NEXT: br label [[FOR_COND_1:%.*]]
2445+
; NOT_CGSCC_NPM: for.cond.1:
2446+
; NOT_CGSCC_NPM-NEXT: [[G_1:%.*]] = phi i32 [ 0, [[FOR_END_0]] ], [ [[INC6:%.*]], [[FOR_BODY_1:%.*]] ]
2447+
; NOT_CGSCC_NPM-NEXT: [[CMP_1:%.*]] = icmp ult i32 [[G_1]], 100
2448+
; NOT_CGSCC_NPM-NEXT: br i1 [[CMP_1]], label [[FOR_BODY_1]], label [[FOR_END_1:%.*]]
2449+
; NOT_CGSCC_NPM: for.body.1:
2450+
; NOT_CGSCC_NPM-NEXT: [[CALL4:%.*]] = call i32 (i32*, ...) bitcast (i32 (i32)* @h to i32 (i32*, ...)*)(i32* nonnull [[F]])
2451+
; NOT_CGSCC_NPM-NEXT: [[INC6]] = add nuw nsw i32 [[G_1]], 1
2452+
; NOT_CGSCC_NPM-NEXT: br label [[FOR_COND_1]]
2453+
; NOT_CGSCC_NPM: for.end.1:
2454+
; NOT_CGSCC_NPM-NEXT: ret i32 0
2455+
;
2456+
; IS__CGSCC____-LABEL: define {{[^@]+}}@main() {
2457+
; IS__CGSCC____-NEXT: entry:
2458+
; IS__CGSCC____-NEXT: br label [[FOR_COND_0:%.*]]
2459+
; IS__CGSCC____: for.cond.0:
2460+
; IS__CGSCC____-NEXT: [[G_0:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY_0:%.*]] ]
2461+
; IS__CGSCC____-NEXT: [[CMP_0:%.*]] = icmp ult i32 [[G_0]], 100
2462+
; IS__CGSCC____-NEXT: br i1 [[CMP_0]], label [[FOR_BODY_0]], label [[FOR_END_0:%.*]]
2463+
; IS__CGSCC____: for.body.0:
2464+
; IS__CGSCC____-NEXT: [[INC]] = add nuw nsw i32 [[G_0]], 1
2465+
; IS__CGSCC____-NEXT: br label [[FOR_COND_0]]
2466+
; IS__CGSCC____: for.end.0:
2467+
; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i8* @malloc(i64 noundef 8)
2468+
; IS__CGSCC____-NEXT: store i8* [[CALL]], i8** bitcast (%struct.a** @e to i8**), align 8
2469+
; IS__CGSCC____-NEXT: [[B:%.*]] = bitcast i8* [[CALL]] to %struct.a**
2470+
; IS__CGSCC____-NEXT: store %struct.a* null, %struct.a** [[B]], align 8
2471+
; IS__CGSCC____-NEXT: br label [[FOR_COND_1:%.*]]
2472+
; IS__CGSCC____: for.cond.1:
2473+
; IS__CGSCC____-NEXT: [[G_1:%.*]] = phi i32 [ 0, [[FOR_END_0]] ], [ [[INC6:%.*]], [[FOR_BODY_1:%.*]] ]
2474+
; IS__CGSCC____-NEXT: [[CMP_1:%.*]] = icmp ult i32 [[G_1]], 100
2475+
; IS__CGSCC____-NEXT: br i1 [[CMP_1]], label [[FOR_BODY_1]], label [[FOR_END_1:%.*]]
2476+
; IS__CGSCC____: for.body.1:
2477+
; IS__CGSCC____-NEXT: [[INC6]] = add nuw nsw i32 [[G_1]], 1
2478+
; IS__CGSCC____-NEXT: br label [[FOR_COND_1]]
2479+
; IS__CGSCC____: for.end.1:
2480+
; IS__CGSCC____-NEXT: ret i32 0
24552481
;
24562482
entry:
24572483
%f = alloca i32

0 commit comments

Comments
 (0)