Skip to content

Commit e77e2bc

Browse files
committed
IRGen: don't initialize the object headers of bare objects
For `alloc_ref [bare] [stack]` and `global_value [bare]` omit the object header initialization. The `bare` flag means that the object header is not used. This was already done with a peephole optimization inside IRGen for `global_value`. But now rely on the SIL `bare` flag.
1 parent e4fa91c commit e77e2bc

File tree

6 files changed

+52
-30
lines changed

6 files changed

+52
-30
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/StripObjectHeaders.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ let stripObjectHeadersPass = FunctionPass(name: "strip-object-headers") {
2222
for inst in function.instructions {
2323
switch inst {
2424
case let gv as GlobalValueInst:
25-
if !gv.isBare && !gv.needObjectHeader(context) {
25+
if !gv.isBare && !gv.needObjectHeader() {
2626
gv.setIsBare(context)
2727
}
2828
case let ar as AllocRefInst:
29-
if !ar.isBare && !ar.needObjectHeader(context) {
29+
if !ar.isBare && !ar.needObjectHeader() {
3030
ar.setIsBare(context)
3131
}
3232
default:
@@ -36,7 +36,7 @@ let stripObjectHeadersPass = FunctionPass(name: "strip-object-headers") {
3636
}
3737

3838
private extension Value {
39-
func needObjectHeader(_ context: FunctionPassContext) -> Bool {
39+
func needObjectHeader() -> Bool {
4040
var walker = IsBareObjectWalker()
4141
return walker.walkDownUses(ofValue: self, path: SmallProjectionPath()) == .abortWalk
4242
}
@@ -47,6 +47,7 @@ private struct IsBareObjectWalker : ValueDefUseWalker {
4747

4848
mutating func walkDown(value operand: Operand, path: Path) -> WalkResult {
4949
switch operand.instruction {
50+
// White-list all instructions which don't use the object header.
5051
case is StructInst, is TupleInst, is EnumInst,
5152
is StructExtractInst, is TupleExtractInst, is UncheckedEnumDataInst,
5253
is DestructureStructInst, is DestructureTupleInst,
@@ -62,6 +63,7 @@ private struct IsBareObjectWalker : ValueDefUseWalker {
6263

6364
mutating func leafUse(value operand: Operand, path: SmallProjectionPath) -> WalkResult {
6465
switch operand.instruction {
66+
// White-list all instructions which don't use the object header.
6567
case is RefElementAddrInst, is RefTailAddrInst,
6668
is DeallocRefInst, is DeallocStackRefInst, is SetDeallocatingInst,
6769
is DebugValueInst, is FixLifetimeInst:

lib/IRGen/GenClass.cpp

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -857,7 +857,8 @@ irgen::appendSizeForTailAllocatedArrays(IRGenFunction &IGF,
857857

858858
/// Emit an allocation of a class.
859859
llvm::Value *irgen::emitClassAllocation(IRGenFunction &IGF, SILType selfType,
860-
bool objc, int &StackAllocSize,
860+
bool objc, bool isBare,
861+
int &StackAllocSize,
861862
TailArraysRef TailArrays) {
862863
auto &classTI = IGF.getTypeInfo(selfType).as<ClassTypeInfo>();
863864
auto classType = selfType.getASTType();
@@ -874,20 +875,28 @@ llvm::Value *irgen::emitClassAllocation(IRGenFunction &IGF, SILType selfType,
874875
return emitObjCAllocObjectCall(IGF, metadata, selfType);
875876
}
876877

877-
llvm::Value *metadata =
878-
emitClassHeapMetadataRef(IGF, classType, MetadataValueType::TypeMetadata,
879-
MetadataState::Complete);
880-
881878
auto &classLayout = classTI.getClassLayout(IGF.IGM, selfType,
882879
/*forBackwardDeployment=*/false);
883880

884881
llvm::Type *destType = classLayout.getType()->getPointerTo();
885882
llvm::Value *val = nullptr;
886883
if (llvm::Value *Promoted = stackPromote(IGF, classLayout, StackAllocSize,
887884
TailArrays)) {
888-
val = IGF.Builder.CreateBitCast(Promoted, IGF.IGM.RefCountedPtrTy);
889-
val = IGF.emitInitStackObjectCall(metadata, val, "reference.new");
885+
if (isBare) {
886+
val = Promoted;
887+
} else {
888+
llvm::Value *metadata =
889+
emitClassHeapMetadataRef(IGF, classType, MetadataValueType::TypeMetadata,
890+
MetadataState::Complete);
891+
892+
val = IGF.Builder.CreateBitCast(Promoted, IGF.IGM.RefCountedPtrTy);
893+
val = IGF.emitInitStackObjectCall(metadata, val, "reference.new");
894+
}
890895
} else {
896+
llvm::Value *metadata =
897+
emitClassHeapMetadataRef(IGF, classType, MetadataValueType::TypeMetadata,
898+
MetadataState::Complete);
899+
891900
llvm::Value *size, *alignMask;
892901
if (classLayout.isFixedSize()) {
893902
size = IGF.IGM.getSize(classLayout.getSize());

lib/IRGen/GenClass.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ namespace irgen {
166166
/// The returned \p StackAllocSize value is the actual size if the object is
167167
/// allocated on the stack or -1, if the object is allocated on the heap.
168168
llvm::Value *emitClassAllocation(IRGenFunction &IGF, SILType selfType,
169-
bool objc, int &StackAllocSize, TailArraysRef TailArrays);
169+
bool objc, bool isBare, int &StackAllocSize, TailArraysRef TailArrays);
170170

171171
/// Emit an allocation of a class using a metadata value.
172172
llvm::Value *emitClassAllocationDynamic(IRGenFunction &IGF,

lib/IRGen/IRGenSIL.cpp

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2987,23 +2987,6 @@ void IRGenSILFunction::visitGlobalAddrInst(GlobalAddrInst *i) {
29872987
setLoweredAddress(i, addr);
29882988
}
29892989

2990-
/// Returns true if \p val has no other uses than ref_element_addr or
2991-
/// ref_tail_addr.
2992-
static bool hasOnlyProjections(SILValue val) {
2993-
for (Operand *use : val->getUses()) {
2994-
SILInstruction *user = use->getUser();
2995-
if (auto *upCast = dyn_cast<UpcastInst>(user)) {
2996-
if (!hasOnlyProjections(upCast))
2997-
return false;
2998-
continue;
2999-
}
3000-
if (isa<RefElementAddrInst>(user) || isa<RefTailAddrInst>(user))
3001-
continue;
3002-
return false;
3003-
}
3004-
return true;
3005-
}
3006-
30072990
void IRGenSILFunction::visitGlobalValueInst(GlobalValueInst *i) {
30082991
SILGlobalVariable *var = i->getReferencedGlobal();
30092992
assert(var->isInitializedObject() &&
@@ -3017,7 +3000,7 @@ void IRGenSILFunction::visitGlobalValueInst(GlobalValueInst *i) {
30173000
NotForDefinition).getAddress();
30183001
// We don't need to initialize the global object if it's never used for
30193002
// something which can access the object header.
3020-
if (!hasOnlyProjections(i) && !IGM.canMakeStaticObjectsReadOnly()) {
3003+
if (!i->isBare() && !IGM.canMakeStaticObjectsReadOnly()) {
30213004
auto ClassType = loweredTy.getASTType();
30223005
llvm::Value *Metadata =
30233006
emitClassHeapMetadataRef(*this, ClassType, MetadataValueType::TypeMetadata,
@@ -4805,6 +4788,9 @@ void IRGenSILFunction::visitAutoreleaseValueInst(swift::AutoreleaseValueInst *i)
48054788
void IRGenSILFunction::visitSetDeallocatingInst(SetDeallocatingInst *i) {
48064789
auto *ARI = dyn_cast<AllocRefInst>(i->getOperand());
48074790
if (ARI && StackAllocs.count(ARI)) {
4791+
if (ARI->isBare())
4792+
return;
4793+
48084794
// A small peep-hole optimization: If the operand is allocated on stack and
48094795
// there is no "significant" code between the set_deallocating and the final
48104796
// dealloc_ref, the set_deallocating is not required.
@@ -5712,7 +5698,7 @@ void IRGenSILFunction::visitAllocRefInst(swift::AllocRefInst *i) {
57125698
SmallVector<std::pair<SILType, llvm::Value *>, 4> TailArrays;
57135699
buildTailArrays(*this, TailArrays, i);
57145700

5715-
llvm::Value *alloced = emitClassAllocation(*this, i->getType(), i->isObjC(),
5701+
llvm::Value *alloced = emitClassAllocation(*this, i->getType(), i->isObjC(), i->isBare(),
57165702
StackAllocSize, TailArrays);
57175703
if (StackAllocSize >= 0) {
57185704
// Remember that this alloc_ref allocates the object on the stack.

test/IRGen/class_stack_alloc.sil

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,20 @@ bb0:
105105
return %r : $()
106106
}
107107

108+
// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @bare_alloc_ref
109+
// CHECK: %reference.raw = alloca %[[C:[a-zA-Z0-9_]+]], align 8
110+
// CHECK-NEXT: [[O:%[0-9]+]] = bitcast %[[C]]* %reference.raw to i8*
111+
// CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 -1, i8* [[O]])
112+
// CHECK-NEXT: ret void
113+
sil @bare_alloc_ref : $@convention(thin) () -> () {
114+
bb0:
115+
%o1 = alloc_ref [bare] [stack] $TestClass
116+
set_deallocating %o1 : $TestClass
117+
dealloc_stack_ref %o1 : $TestClass
118+
%r = tuple()
119+
return %r : $()
120+
}
121+
108122
// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @promoted_with_inlined_devirtualized_release
109123
// CHECK: %reference.raw = alloca %[[C:[a-zA-Z0-9_]+]], align 8
110124
// CHECK-NEXT: [[MR:%[0-9]+]] = call swiftcc %swift.metadata_response @"$s17class_stack_alloc9TestClassCMa"([[INT]] 0)

test/IRGen/static_initializer.sil

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,17 @@ bb0:
185185
return %1 : $TestArray
186186
}
187187

188+
// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc i32 @bare_static_array() {{.*}} {
189+
sil @bare_static_array : $@convention(thin) () -> Int32 {
190+
bb0:
191+
// CHECK: %0 = load i32, {{.*}}@static_array
192+
// CHECK: ret i32 %0
193+
%0 = global_value [bare] @static_array : $TestArrayStorage
194+
%1 = ref_element_addr %0 : $TestArrayStorage, #TestArrayStorage.count
195+
%2 = load %1 : $*Int32
196+
return %2 : $Int32
197+
}
198+
188199
// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %T18static_initializer16TestArrayStorageC* @phi_nodes(i1 %0, %T18static_initializer16TestArrayStorageC* %1)
189200
// CHECK: [[T0:%.*]] = call %swift.refcounted* @swift_initStaticObject
190201
// CHECK: [[T1:%.*]] = bitcast %swift.refcounted* [[T0]] to %T18static_initializer16TestArrayStorageC*

0 commit comments

Comments
 (0)