Skip to content

Commit 3bdcb00

Browse files
committed
[IRGenSIL] Force early materialization of liverange-extended values
At -Onone, we may opt to extend the liverange of a value by inserting fake uses of the value in blocks which postdominate its def. This keeps values available throughout the entirety of a function when stepping through it in a debugger. This patch makes it so that we also insert a fake use at the start of a value's lifetime. This forces early materialization of the value, i.e it becomes available for inspection in a debugger as soon as it's defined. This is useful when a value has no uses except fake uses. In such cases, ISel may (correctly!) decide to materialize the value at the end of its scope. This is really unintuitive and unhelpful for users, because you need to be done stepping through the value's scope before you can inspect it. For the same reasons, forcing early materialization is also useful when a value *does* have uses. Consider: let d = ... print("Here is the value of d: \(d)") Without early materialization, if you set a breakpoint on the line containing the print, you would not be able to inspect "d". That's because "d" would be materialized right before string interpolation happens, which is *after* the breakpoint PC. rdar://38465084
1 parent f98ab4a commit 3bdcb00

File tree

2 files changed

+64
-24
lines changed

2 files changed

+64
-24
lines changed

lib/IRGen/IRGenSIL.cpp

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "llvm/ADT/TinyPtrVector.h"
2929
#include "llvm/Support/SaveAndRestore.h"
3030
#include "llvm/Support/Debug.h"
31+
#include "llvm/Transforms/Utils/Local.h"
3132
#include "clang/AST/ASTContext.h"
3233
#include "clang/Basic/TargetInfo.h"
3334
#include "swift/Basic/ExternalUnion.h"
@@ -707,18 +708,13 @@ class IRGenSILFunction :
707708
// The current basic block must be a successor of the dbg.value().
708709
continue;
709710

710-
auto It = llvm::BasicBlock::iterator(Var);
711-
for (auto I = std::next(It), E = BB->end(); I != E; ++I) {
712-
auto *DVI = dyn_cast<llvm::DbgValueInst>(I);
713-
if (DVI && DVI->getValue() == Var)
711+
llvm::SmallVector<llvm::DbgValueInst *, 4> DbgValues;
712+
llvm::findDbgValues(DbgValues, Var);
713+
for (auto *DVI : DbgValues)
714+
if (DVI->getParent() == BB)
714715
IGM.DebugInfo->getBuilder().insertDbgValueIntrinsic(
715716
DVI->getValue(), DVI->getVariable(), DVI->getExpression(),
716717
DVI->getDebugLoc(), &*CurBB->getFirstInsertionPt());
717-
else
718-
// Found all dbg.value intrinsics describing this location.
719-
// FIXME: How do we know that there aren't more dbg.values?
720-
break;
721-
}
722718
}
723719
}
724720
}
@@ -772,10 +768,18 @@ class IRGenSILFunction :
772768
if (ArgNo == 0)
773769
// Otherwise only if debug value range extension is not feasible.
774770
if (!needsShadowCopy(Storage)) {
775-
// Mark for debug value range extension unless this is a constant.
776-
if (auto *Value = dyn_cast<llvm::Instruction>(Storage))
777-
if (ValueVariables.insert(Value).second)
778-
ValueDomPoints.push_back({Value, getActiveDominancePoint()});
771+
// Mark for debug value range extension unless this is a constant, or
772+
// unless it's not possible to emit lifetime-extending uses for this.
773+
if (auto *Value = dyn_cast<llvm::Instruction>(Storage)) {
774+
// Emit a use at the start of the storage lifetime to force early
775+
// materialization. This makes variables available for inspection as
776+
// soon as they are defined.
777+
bool ExtendedLifetime = emitLifetimeExtendingUse(Value);
778+
if (ExtendedLifetime)
779+
if (ValueVariables.insert(Value).second)
780+
ValueDomPoints.push_back({Value, getActiveDominancePoint()});
781+
}
782+
779783
return Storage;
780784
}
781785
return emitShadowCopy(Storage, Scope, Name, ArgNo, Align);
Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,63 @@
11
// RUN: %target-swift-frontend %s -g -emit-ir -o - | %FileCheck %s
22

3+
// REQUIRES: CPU=x86_64
4+
//
5+
// We require x86_64 to make the test easier to read. Without this,
6+
// writing check lines that ensure our asm gadgets match up with the
7+
// right values is painfully hard to do.
8+
39
func use<T>(_ x: T) {}
410

511
func getInt32() -> Int32 { return -1 }
612

13+
// CHECK-LABEL: define {{.*}}rangeExtension
714
public func rangeExtension(_ b: Bool) {
8-
// CHECK: define {{.*}}rangeExtension
915
let i = getInt32()
10-
// CHECK: llvm.dbg.value(metadata i32 [[I:.*]], metadata {{.*}}, metadata
16+
// CHECK: [[I:%.*]] = call swiftcc i32 @"{{.*}}getInt32
17+
// CHECK-NEXT: [[I_ZEXT:%.*]] = zext i32 [[I]] to i64
18+
// CHECK-NEXT: call void asm sideeffect "", "r"(i64 [[I_ZEXT]])
19+
// CHECK-NEXT: llvm.dbg.value(metadata i32 [[I]], metadata [[MD_I:!.*]], metadata
20+
1121
use(i)
22+
23+
// CHECK: br i1
24+
1225
if b {
26+
// CHECK: llvm.dbg.value(metadata i32 [[I]], metadata [[MD_I]]
27+
1328
let j = getInt32()
14-
// CHECK: llvm.dbg.value(metadata i32 [[I]], metadata {{.*}}, metadata
15-
// CHECK: llvm.dbg.value(metadata i32 [[J:.*]], metadata {{.*}}, metadata
29+
// CHECK: [[J:%.*]] = call swiftcc i32 @"{{.*}}getInt32
30+
// CHECK-NEXT: [[J_ZEXT:%.*]] = zext i32 [[J]] to i64
31+
// CHECK-NEXT: call void asm sideeffect "", "r"(i64 [[J_ZEXT]])
32+
// CHECK: llvm.dbg.value(metadata i32 [[J]], metadata [[MD_J:!.*]], metadata
33+
1634
use(j)
17-
// CHECK-DAG: {{(asm sideeffect "", "r".*)|(zext i32)}} [[J]]
18-
// CHECK-DAG: asm sideeffect "", "r"
35+
// CHECK: call swiftcc void @"{{.*}}use
36+
37+
// CHECK: [[I_ZEXT:%.*]] = zext i32 [[I]] to i64
38+
// CHECK-NEXT: call void asm sideeffect "", "r"(i64 [[I_ZEXT]])
39+
// CHECK-NEXT: [[J_ZEXT:%.*]] = zext i32 [[J]] to i64
40+
// CHECK-NEXT: call void asm sideeffect "", "r"(i64 [[J_ZEXT]])
41+
42+
// CHECK: br label
1943
}
44+
45+
// CHECK-NOT: llvm.dbg.value(metadata i32 [[J]]
46+
// CHECK: llvm.dbg.value(metadata i32 [[I]]
47+
2048
let z = getInt32()
49+
// CHECK: [[Z:%.*]] = call swiftcc i32 @"{{.*}}getInt32
50+
// CHECK: llvm.dbg.value(metadata i32 [[Z]], metadata [[MD_Z:!.*]], metadata
51+
2152
use(z)
22-
// CHECK-NOT: llvm.dbg.value(metadata i32 [[J]], metadata {{.*}}, metadata
23-
// CHECK-DAG: llvm.dbg.value(metadata i32 [[I]], metadata {{.*}}, metadata
24-
// CHECK-DAG: llvm.dbg.value(metadata i32 [[Z:.*]], metadata {{.*}}, metadata
25-
// CHECK-DAG: {{(asm sideeffect "", "r".*)|(zext i32)}} [[I]]
26-
// CHECK-DAG: asm sideeffect "", "r"
53+
// CHECK: call swiftcc void @"{{.*}}use
54+
55+
// CHECK: [[I_ZEXT:%.*]] = zext i32 [[I]] to i64
56+
// CHECK-NEXT: call void asm sideeffect "", "r"(i64 [[I_ZEXT]])
57+
// CHECK-NEXT: [[Z_ZEXT:%.*]] = zext i32 [[Z]] to i64
58+
// CHECK-NEXT: call void asm sideeffect "", "r"(i64 [[Z_ZEXT]])
2759
}
60+
61+
// CHECK-DAG: [[MD_I]] = !DILocalVariable(name: "i"
62+
// CHECK-DAG: [[MD_J]] = !DILocalVariable(name: "j"
63+
// CHECK-DAG: [[MD_Z]] = !DILocalVariable(name: "z"

0 commit comments

Comments
 (0)