Skip to content

Commit cf7eac9

Browse files
authored
[ObjectSizeOffsetVisitor] Bail after visiting 100 instructions (#67479)
We're running into stack overflows for huge functions with lots of phis. Even without the stack overflows, this is recursing >7000 in some auto-generated code. This fixes the stack overflow and brings down the compile time to something reasonable.
1 parent f3c417f commit cf7eac9

File tree

3 files changed

+78
-9
lines changed

3 files changed

+78
-9
lines changed

llvm/include/llvm/Analysis/MemoryBuiltins.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ class ObjectSizeOffsetVisitor
199199
unsigned IntTyBits;
200200
APInt Zero;
201201
SmallDenseMap<Instruction *, SizeOffsetType, 8> SeenInsts;
202+
unsigned InstructionsVisited;
202203

203204
APInt align(APInt Size, MaybeAlign Align);
204205

@@ -248,6 +249,7 @@ class ObjectSizeOffsetVisitor
248249
unsigned &ScannedInstCount);
249250
SizeOffsetType combineSizeOffset(SizeOffsetType LHS, SizeOffsetType RHS);
250251
SizeOffsetType computeImpl(Value *V);
252+
SizeOffsetType computeValue(Value *V);
251253
bool CheckedZextOrTrunc(APInt &I);
252254
};
253255

llvm/lib/Analysis/MemoryBuiltins.cpp

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "llvm/IR/Type.h"
3636
#include "llvm/IR/Value.h"
3737
#include "llvm/Support/Casting.h"
38+
#include "llvm/Support/CommandLine.h"
3839
#include "llvm/Support/Debug.h"
3940
#include "llvm/Support/MathExtras.h"
4041
#include "llvm/Support/raw_ostream.h"
@@ -50,6 +51,12 @@ using namespace llvm;
5051

5152
#define DEBUG_TYPE "memory-builtins"
5253

54+
static cl::opt<unsigned> ObjectSizeOffsetVisitorMaxVisitInstructions(
55+
"object-size-offset-visitor-max-visit-instructions",
56+
cl::desc("Maximum number of instructions for ObjectSizeOffsetVisitor to "
57+
"look at"),
58+
cl::init(100));
59+
5360
enum AllocType : uint8_t {
5461
OpNewLike = 1<<0, // allocates; never returns null
5562
MallocLike = 1<<1, // allocates; may return null
@@ -694,6 +701,11 @@ ObjectSizeOffsetVisitor::ObjectSizeOffsetVisitor(const DataLayout &DL,
694701
}
695702

696703
SizeOffsetType ObjectSizeOffsetVisitor::compute(Value *V) {
704+
InstructionsVisited = 0;
705+
return computeImpl(V);
706+
}
707+
708+
SizeOffsetType ObjectSizeOffsetVisitor::computeImpl(Value *V) {
697709
unsigned InitialIntTyBits = DL.getIndexTypeSizeInBits(V->getType());
698710

699711
// Stripping pointer casts can strip address space casts which can change the
@@ -710,14 +722,15 @@ SizeOffsetType ObjectSizeOffsetVisitor::compute(Value *V) {
710722
IntTyBits = DL.getIndexTypeSizeInBits(V->getType());
711723
Zero = APInt::getZero(IntTyBits);
712724

725+
SizeOffsetType SOT = computeValue(V);
726+
713727
bool IndexTypeSizeChanged = InitialIntTyBits != IntTyBits;
714728
if (!IndexTypeSizeChanged && Offset.isZero())
715-
return computeImpl(V);
729+
return SOT;
716730

717731
// We stripped an address space cast that changed the index type size or we
718732
// accumulated some constant offset (or both). Readjust the bit width to match
719733
// the argument index type size and apply the offset, as required.
720-
SizeOffsetType SOT = computeImpl(V);
721734
if (IndexTypeSizeChanged) {
722735
if (knownSize(SOT) && !::CheckedZextOrTrunc(SOT.first, InitialIntTyBits))
723736
SOT.first = APInt();
@@ -729,13 +742,16 @@ SizeOffsetType ObjectSizeOffsetVisitor::compute(Value *V) {
729742
SOT.second.getBitWidth() > 1 ? SOT.second + Offset : SOT.second};
730743
}
731744

732-
SizeOffsetType ObjectSizeOffsetVisitor::computeImpl(Value *V) {
745+
SizeOffsetType ObjectSizeOffsetVisitor::computeValue(Value *V) {
733746
if (Instruction *I = dyn_cast<Instruction>(V)) {
734747
// If we have already seen this instruction, bail out. Cycles can happen in
735748
// unreachable code after constant propagation.
736749
auto P = SeenInsts.try_emplace(I, unknown());
737750
if (!P.second)
738751
return P.first->second;
752+
++InstructionsVisited;
753+
if (InstructionsVisited > ObjectSizeOffsetVisitorMaxVisitInstructions)
754+
return unknown();
739755
SizeOffsetType Res = visit(*I);
740756
// Cache the result for later visits. If we happened to visit this during
741757
// the above recursion, we would consider it unknown until now.
@@ -830,7 +846,7 @@ ObjectSizeOffsetVisitor::visitExtractValueInst(ExtractValueInst&) {
830846
SizeOffsetType ObjectSizeOffsetVisitor::visitGlobalAlias(GlobalAlias &GA) {
831847
if (GA.isInterposable())
832848
return unknown();
833-
return compute(GA.getAliasee());
849+
return computeImpl(GA.getAliasee());
834850
}
835851

836852
SizeOffsetType ObjectSizeOffsetVisitor::visitGlobalVariable(GlobalVariable &GV){
@@ -885,7 +901,7 @@ SizeOffsetType ObjectSizeOffsetVisitor::findLoadSizeOffset(
885901
continue;
886902
case AliasResult::MustAlias:
887903
if (SI->getValueOperand()->getType()->isPointerTy())
888-
return Known(compute(SI->getValueOperand()));
904+
return Known(computeImpl(SI->getValueOperand()));
889905
else
890906
return Unknown(); // No handling of non-pointer values by `compute`.
891907
default:
@@ -998,15 +1014,15 @@ SizeOffsetType ObjectSizeOffsetVisitor::visitPHINode(PHINode &PN) {
9981014
return unknown();
9991015
auto IncomingValues = PN.incoming_values();
10001016
return std::accumulate(IncomingValues.begin() + 1, IncomingValues.end(),
1001-
compute(*IncomingValues.begin()),
1017+
computeImpl(*IncomingValues.begin()),
10021018
[this](SizeOffsetType LHS, Value *VRHS) {
1003-
return combineSizeOffset(LHS, compute(VRHS));
1019+
return combineSizeOffset(LHS, computeImpl(VRHS));
10041020
});
10051021
}
10061022

10071023
SizeOffsetType ObjectSizeOffsetVisitor::visitSelectInst(SelectInst &I) {
1008-
return combineSizeOffset(compute(I.getTrueValue()),
1009-
compute(I.getFalseValue()));
1024+
return combineSizeOffset(computeImpl(I.getTrueValue()),
1025+
computeImpl(I.getFalseValue()));
10101026
}
10111027

10121028
SizeOffsetType ObjectSizeOffsetVisitor::visitUndefValue(UndefValue&) {
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 3
2+
; RUN: opt -passes=dse -S -object-size-offset-visitor-max-visit-instructions=2 < %s | FileCheck %s --check-prefix=NO
3+
; RUN: opt -passes=dse -S -object-size-offset-visitor-max-visit-instructions=3 < %s | FileCheck %s --check-prefix=YES
4+
5+
declare void @use(ptr)
6+
7+
define void @f(i32 %i, i1 %c) {
8+
; NO-LABEL: define void @f(
9+
; NO-SAME: i32 [[I:%.*]], i1 [[C:%.*]]) {
10+
; NO-NEXT: b0:
11+
; NO-NEXT: [[A1:%.*]] = alloca i32, align 4
12+
; NO-NEXT: [[A2:%.*]] = alloca i32, align 4
13+
; NO-NEXT: br i1 [[C]], label [[B1:%.*]], label [[B2:%.*]]
14+
; NO: b1:
15+
; NO-NEXT: br label [[B2]]
16+
; NO: b2:
17+
; NO-NEXT: [[A5:%.*]] = phi ptr [ [[A1]], [[B0:%.*]] ], [ [[A2]], [[B1]] ]
18+
; NO-NEXT: [[G:%.*]] = getelementptr i8, ptr [[A5]], i32 [[I]]
19+
; NO-NEXT: store i8 1, ptr [[G]], align 1
20+
; NO-NEXT: store i32 0, ptr [[A5]], align 4
21+
; NO-NEXT: call void @use(ptr [[A5]])
22+
; NO-NEXT: ret void
23+
;
24+
; YES-LABEL: define void @f(
25+
; YES-SAME: i32 [[I:%.*]], i1 [[C:%.*]]) {
26+
; YES-NEXT: b0:
27+
; YES-NEXT: [[A1:%.*]] = alloca i32, align 4
28+
; YES-NEXT: [[A2:%.*]] = alloca i32, align 4
29+
; YES-NEXT: br i1 [[C]], label [[B1:%.*]], label [[B2:%.*]]
30+
; YES: b1:
31+
; YES-NEXT: br label [[B2]]
32+
; YES: b2:
33+
; YES-NEXT: [[A5:%.*]] = phi ptr [ [[A1]], [[B0:%.*]] ], [ [[A2]], [[B1]] ]
34+
; YES-NEXT: store i32 0, ptr [[A5]], align 4
35+
; YES-NEXT: call void @use(ptr [[A5]])
36+
; YES-NEXT: ret void
37+
;
38+
b0:
39+
%a1 = alloca i32
40+
%a2 = alloca i32
41+
br i1 %c, label %b1, label %b2
42+
b1:
43+
br label %b2
44+
b2:
45+
%a5 = phi ptr [ %a1, %b0 ], [ %a2, %b1 ]
46+
%g = getelementptr i8, ptr %a5, i32 %i
47+
store i8 1, ptr %g
48+
store i32 0, ptr %a5
49+
call void @use(ptr %a5)
50+
ret void
51+
}

0 commit comments

Comments
 (0)