Skip to content

Commit c37dbf3

Browse files
authored
Merge pull request #15441 from vedantk/double-the-fakery
[IRGenSIL] Force early materialization of liverange-extended values
2 parents 95bd2b7 + 3bdcb00 commit c37dbf3

File tree

2 files changed

+115
-57
lines changed

2 files changed

+115
-57
lines changed

lib/IRGen/IRGenSIL.cpp

Lines changed: 68 additions & 46 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"
@@ -646,6 +647,41 @@ class IRGenSILFunction :
646647
return Name;
647648
}
648649

650+
/// Try to emit an inline assembly gadget which extends the lifetime of
651+
/// \p Var. Returns whether or not this was successful.
652+
bool emitLifetimeExtendingUse(llvm::Value *Var) {
653+
llvm::Type *ArgTys;
654+
auto *Ty = Var->getType();
655+
// Vectors, Pointers and Floats are expected to fit into a register.
656+
if (Ty->isPointerTy() || Ty->isFloatingPointTy() || Ty->isVectorTy())
657+
ArgTys = {Ty};
658+
else {
659+
// If this is not a scalar or vector type, we can't handle it.
660+
if (isa<llvm::CompositeType>(Ty))
661+
return false;
662+
// The storage is guaranteed to be no larger than the register width.
663+
// Extend the storage so it would fit into a register.
664+
llvm::Type *IntTy;
665+
switch (IGM.getClangASTContext().getTargetInfo().getRegisterWidth()) {
666+
case 64:
667+
IntTy = IGM.Int64Ty;
668+
break;
669+
case 32:
670+
IntTy = IGM.Int32Ty;
671+
break;
672+
default:
673+
llvm_unreachable("unsupported register width");
674+
}
675+
ArgTys = {IntTy};
676+
Var = Builder.CreateZExtOrBitCast(Var, IntTy);
677+
}
678+
// Emit an empty inline assembler expression depending on the register.
679+
auto *AsmFnTy = llvm::FunctionType::get(IGM.VoidTy, ArgTys, false);
680+
auto *InlineAsm = llvm::InlineAsm::get(AsmFnTy, "", "r", true);
681+
Builder.CreateAsmCall(InlineAsm, Var);
682+
return true;
683+
}
684+
649685
/// At -Onone, forcibly keep all LLVM values that are tracked by
650686
/// debug variables alive by inserting an empty inline assembler
651687
/// expression depending on the value in the blocks dominated by the
@@ -654,53 +690,31 @@ class IRGenSILFunction :
654690
if (IGM.IRGen.Opts.shouldOptimize())
655691
return;
656692
for (auto &Variable : ValueDomPoints) {
657-
auto VarDominancePoint = Variable.second;
658-
llvm::Value *Storage = Variable.first;
693+
llvm::Instruction *Var = Variable.first;
694+
DominancePoint VarDominancePoint = Variable.second;
659695
if (getActiveDominancePoint() == VarDominancePoint ||
660696
isActiveDominancePointDominatedBy(VarDominancePoint)) {
661-
llvm::Type *ArgTys;
662-
auto *Ty = Storage->getType();
663-
// Vectors, Pointers and Floats are expected to fit into a register.
664-
if (Ty->isPointerTy() || Ty->isFloatingPointTy() || Ty->isVectorTy())
665-
ArgTys = { Ty };
666-
else {
667-
// If this is not a scalar or vector type, we can't handle it.
668-
if (isa<llvm::CompositeType>(Ty))
669-
continue;
670-
// The storage is guaranteed to be no larger than the register width.
671-
// Extend the storage so it would fit into a register.
672-
llvm::Type *IntTy;
673-
switch (IGM.getClangASTContext().getTargetInfo().getRegisterWidth()) {
674-
case 64: IntTy = IGM.Int64Ty; break;
675-
case 32: IntTy = IGM.Int32Ty; break;
676-
default: llvm_unreachable("unsupported register width");
677-
}
678-
ArgTys = { IntTy };
679-
Storage = Builder.CreateZExtOrBitCast(Storage, IntTy);
680-
}
681-
// Emit an empty inline assembler expression depending on the register.
682-
auto *AsmFnTy = llvm::FunctionType::get(IGM.VoidTy, ArgTys, false);
683-
auto *InlineAsm = llvm::InlineAsm::get(AsmFnTy, "", "r", true);
684-
Builder.CreateAsmCall(InlineAsm, Storage);
685-
// Propagate the dbg.value intrinsics into the later basic blocks. Note
697+
bool ExtendedLifetime = emitLifetimeExtendingUse(Var);
698+
if (!ExtendedLifetime)
699+
continue;
700+
701+
// Propagate dbg.values for Var into the current basic block. Note
686702
// that this shouldn't be necessary. LiveDebugValues should be doing
687703
// this but can't in general because it currently only tracks register
688704
// locations.
689-
llvm::Instruction *Value = Variable.first;
690-
auto It = llvm::BasicBlock::iterator(Value);
691-
auto *BB = Value->getParent();
692-
auto *CurBB = Builder.GetInsertBlock();
693-
if (BB != CurBB)
694-
for (auto I = std::next(It), E = BB->end(); I != E; ++I) {
695-
auto *DVI = dyn_cast<llvm::DbgValueInst>(I);
696-
if (DVI && DVI->getValue() == Value)
697-
IGM.DebugInfo->getBuilder().insertDbgValueIntrinsic(
698-
DVI->getValue(), DVI->getVariable(), DVI->getExpression(),
699-
DVI->getDebugLoc(), &*CurBB->getFirstInsertionPt());
700-
else
701-
// Found all dbg.value intrinsics describing this location.
702-
break;
703-
}
705+
llvm::BasicBlock *BB = Var->getParent();
706+
llvm::BasicBlock *CurBB = Builder.GetInsertBlock();
707+
if (BB == CurBB)
708+
// The current basic block must be a successor of the dbg.value().
709+
continue;
710+
711+
llvm::SmallVector<llvm::DbgValueInst *, 4> DbgValues;
712+
llvm::findDbgValues(DbgValues, Var);
713+
for (auto *DVI : DbgValues)
714+
if (DVI->getParent() == BB)
715+
IGM.DebugInfo->getBuilder().insertDbgValueIntrinsic(
716+
DVI->getValue(), DVI->getVariable(), DVI->getExpression(),
717+
DVI->getDebugLoc(), &*CurBB->getFirstInsertionPt());
704718
}
705719
}
706720
}
@@ -754,10 +768,18 @@ class IRGenSILFunction :
754768
if (ArgNo == 0)
755769
// Otherwise only if debug value range extension is not feasible.
756770
if (!needsShadowCopy(Storage)) {
757-
// Mark for debug value range extension unless this is a constant.
758-
if (auto *Value = dyn_cast<llvm::Instruction>(Storage))
759-
if (ValueVariables.insert(Value).second)
760-
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+
761783
return Storage;
762784
}
763785
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)