Skip to content

Commit

Permalink
[CodeGen] Merge identical block descriptor global variables.
Browse files Browse the repository at this point in the history
Currently, clang generates a new block descriptor global variable for
each new block literal. This commit merges block descriptors that are
identical inside and across translation units using the same approach
taken in r339438.

To enable merging identical block descriptors, the size and signature of
the block and information about the captures are encoded into the name
of the block descriptor variable. Also, the block descriptor variable is
marked as linkonce_odr and unnamed_addr.

rdar://problem/42640703

Differential Revision: https://reviews.llvm.org/D50783

llvm-svn: 340041
  • Loading branch information
ahatanaka committed Aug 17, 2018
1 parent 6c9f15c commit 2ec36f0
Show file tree
Hide file tree
Showing 9 changed files with 359 additions and 148 deletions.
403 changes: 273 additions & 130 deletions clang/lib/CodeGen/CGBlocks.cpp

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions clang/lib/CodeGen/CGBlocks.h
Expand Up @@ -132,6 +132,9 @@ class BlockFieldFlags {
friend bool operator&(BlockFieldFlags l, BlockFieldFlags r) {
return (l.flags & r.flags);
}
bool operator==(BlockFieldFlags Other) const {
return flags == Other.flags;
}
};
inline BlockFieldFlags operator|(BlockFieldFlag_t l, BlockFieldFlag_t r) {
return BlockFieldFlags(l) | BlockFieldFlags(r);
Expand Down
59 changes: 57 additions & 2 deletions clang/lib/CodeGen/CGObjCMac.cpp
Expand Up @@ -37,6 +37,7 @@
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/raw_ostream.h"
#include <cstdio>

Expand Down Expand Up @@ -1085,9 +1086,14 @@ class CGObjCCommonMac : public CodeGen::CGObjCRuntime {
const CGBlockInfo &blockInfo) override;
llvm::Constant *BuildRCBlockLayout(CodeGen::CodeGenModule &CGM,
const CGBlockInfo &blockInfo) override;
std::string getRCBlockLayoutStr(CodeGen::CodeGenModule &CGM,
const CGBlockInfo &blockInfo) override;

llvm::Constant *BuildByrefLayout(CodeGen::CodeGenModule &CGM,
QualType T) override;

private:
void fillRunSkipBlockVars(CodeGenModule &CGM, const CGBlockInfo &blockInfo);
};

namespace {
Expand Down Expand Up @@ -2795,8 +2801,44 @@ llvm::Constant *CGObjCCommonMac::getBitmapBlockLayout(bool ComputeByrefLayout) {
return getConstantGEP(VMContext, Entry, 0, 0);
}

llvm::Constant *CGObjCCommonMac::BuildRCBlockLayout(CodeGenModule &CGM,
const CGBlockInfo &blockInfo) {
static std::string getBlockLayoutInfoString(
const SmallVectorImpl<CGObjCCommonMac::RUN_SKIP> &RunSkipBlockVars,
bool HasCopyDisposeHelpers) {
std::string Str;
for (const CGObjCCommonMac::RUN_SKIP &R : RunSkipBlockVars) {
if (R.opcode == CGObjCCommonMac::BLOCK_LAYOUT_UNRETAINED) {
// Copy/dispose helpers don't have any information about
// __unsafe_unretained captures, so unconditionally concatenate a string.
Str += "u";
} else if (HasCopyDisposeHelpers) {
// Information about __strong, __weak, or byref captures has already been
// encoded into the names of the copy/dispose helpers. We have to add a
// string here only when the copy/dispose helpers aren't generated (which
// happens when the block is non-escaping).
continue;
} else {
switch (R.opcode) {
case CGObjCCommonMac::BLOCK_LAYOUT_STRONG:
Str += "s";
break;
case CGObjCCommonMac::BLOCK_LAYOUT_BYREF:
Str += "r";
break;
case CGObjCCommonMac::BLOCK_LAYOUT_WEAK:
Str += "w";
break;
default:
continue;
}
}
Str += llvm::to_string(R.block_var_bytepos.getQuantity());
Str += "l" + llvm::to_string(R.block_var_size.getQuantity());
}
return Str;
}

void CGObjCCommonMac::fillRunSkipBlockVars(CodeGenModule &CGM,
const CGBlockInfo &blockInfo) {
assert(CGM.getLangOpts().getGC() == LangOptions::NonGC);

RunSkipBlockVars.clear();
Expand Down Expand Up @@ -2845,9 +2887,22 @@ llvm::Constant *CGObjCCommonMac::BuildRCBlockLayout(CodeGenModule &CGM,
UpdateRunSkipBlockVars(CI.isByRef(), getBlockCaptureLifetime(type, false),
fieldOffset, fieldSize);
}
}

llvm::Constant *
CGObjCCommonMac::BuildRCBlockLayout(CodeGenModule &CGM,
const CGBlockInfo &blockInfo) {
fillRunSkipBlockVars(CGM, blockInfo);
return getBitmapBlockLayout(false);
}

std::string CGObjCCommonMac::getRCBlockLayoutStr(CodeGenModule &CGM,
const CGBlockInfo &blockInfo) {
fillRunSkipBlockVars(CGM, blockInfo);
return getBlockLayoutInfoString(RunSkipBlockVars,
blockInfo.needsCopyDisposeHelpers());
}

llvm::Constant *CGObjCCommonMac::BuildByrefLayout(CodeGen::CodeGenModule &CGM,
QualType T) {
assert(CGM.getLangOpts().getGC() == LangOptions::NonGC);
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CodeGen/CGObjCRuntime.h
Expand Up @@ -278,6 +278,10 @@ class CGObjCRuntime {
const CodeGen::CGBlockInfo &blockInfo) = 0;
virtual llvm::Constant *BuildRCBlockLayout(CodeGen::CodeGenModule &CGM,
const CodeGen::CGBlockInfo &blockInfo) = 0;
virtual std::string getRCBlockLayoutStr(CodeGen::CodeGenModule &CGM,
const CGBlockInfo &blockInfo) {
return {};
}

/// Returns an i8* which points to the byref layout information.
virtual llvm::Constant *BuildByrefLayout(CodeGen::CodeGenModule &CGM,
Expand Down
12 changes: 9 additions & 3 deletions clang/test/CodeGenCXX/blocks.cpp
@@ -1,5 +1,8 @@
// RUN: %clang_cc1 %s -fblocks -triple x86_64-apple-darwin -emit-llvm -o - | FileCheck %s

// CHECK: %[[STRUCT_BLOCK_DESCRIPTOR:.*]] = type { i64, i64 }
// CHECK: @[[BLOCK_DESCRIPTOR22:.*]] = internal constant { i64, i64, i8*, i8*, i8*, i8* } { i64 0, i64 36, i8* bitcast (void (i8*, i8*)* @__copy_helper_block_8_32c22_ZTSN12_GLOBAL__N_11BE to i8*), i8* bitcast (void (i8*)* @__destroy_helper_block_8_32c22_ZTSN12_GLOBAL__N_11BE to i8*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i8* null }, align 8

namespace test0 {
// CHECK-LABEL: define void @_ZN5test04testEi(
// CHECK: define internal void @___ZN5test04testEi_block_invoke{{.*}}(
Expand Down Expand Up @@ -122,7 +125,7 @@ namespace test4 {
// CHECK-LABEL: define internal void @___ZN5test44testEv_block_invoke
// CHECK: [[TMP:%.*]] = alloca [[A:%.*]], align 1
// CHECK-NEXT: store i8* [[BLOCKDESC:%.*]], i8** {{.*}}, align 8
// CHECK-NEXT: bitcast i8* [[BLOCKDESC]] to <{ i8*, i32, i32, i8*, %struct.__block_descriptor* }>*
// CHECK-NEXT: bitcast i8* [[BLOCKDESC]] to <{ i8*, i32, i32, i8*, %[[STRUCT_BLOCK_DESCRIPTOR]]* }>*
// CHECK: call void @_ZN5test41AC1Ev([[A]]* [[TMP]])
// CHECK-NEXT: call void @_ZN5test43fooENS_1AE([[A]]* [[TMP]])
// CHECK-NEXT: call void @_ZN5test41AD1Ev([[A]]* [[TMP]])
Expand Down Expand Up @@ -277,8 +280,11 @@ namespace test10 {
}
}

// Copy/dispose helper functions that capture objects of non-external types
// should have internal linkage.
// Copy/dispose helper functions and block descriptors of blocks that capture
// objects that are non-external and non-trivial have internal linkage.

// CHECK-LABEL: define internal void @_ZN12_GLOBAL__N_14testEv(
// CHECK: store %[[STRUCT_BLOCK_DESCRIPTOR]]* bitcast ({ i64, i64, i8*, i8*, i8*, i8* }* @[[BLOCK_DESCRIPTOR22]] to %[[STRUCT_BLOCK_DESCRIPTOR]]*), %[[STRUCT_BLOCK_DESCRIPTOR]]** %{{.*}}, align 8

// CHECK-LABEL: define internal void @__copy_helper_block_8_32c22_ZTSN12_GLOBAL__N_11BE(
// CHECK-LABEL: define internal void @__destroy_helper_block_8_32c22_ZTSN12_GLOBAL__N_11BE(
Expand Down
13 changes: 4 additions & 9 deletions clang/test/CodeGenObjC/arc-blocks.m
Expand Up @@ -2,15 +2,10 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -o - %s | FileCheck -check-prefix=CHECK-UNOPT -check-prefix=CHECK-COMMON %s

// CHECK-COMMON: %[[STRUCT_BLOCK_DESCRIPTOR:.*]] = type { i64, i64 }
// CHECK-COMMON: @{{.*}} = internal constant { i64, i64, i8*, i8*, i8*, i64 } { i64 0, i64 40, i8* bitcast (void (i8*, i8*)* @__copy_helper_block_8_32r to i8*), i8* bitcast (void (i8*)* @__destroy_helper_block_8_32r to i8*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 16 }, align 8
// CHECK-COMMON: @[[BLOCK_DESCRIPTOR_TMP9:.*]] = internal constant { i64, i64, i8*, i8*, i8*, i64 } { i64 0, i64 40, i8* bitcast (void (i8*, i8*)* @__copy_helper_block_8_32r to i8*), i8* bitcast (void (i8*)* @__destroy_helper_block_8_32r to i8*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 16 }, align 8
// CHECK-COMMON: @{{.*}} = internal constant { i64, i64, i8*, i8*, i8*, i64 } { i64 0, i64 40, i8* bitcast (void (i8*, i8*)* @__copy_helper_block_8_32s to i8*), i8* bitcast (void (i8*)* @__destroy_helper_block_8_32s to i8*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 256 }, align 8
// CHECK-COMMON: @{{.*}} = internal constant { i64, i64, i8*, i8*, i8*, i64 } { i64 0, i64 40, i8* bitcast (void (i8*, i8*)* @__copy_helper_block_8_32s to i8*), i8* bitcast (void (i8*)* @__destroy_helper_block_8_32s to i8*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 256 }, align 8
// CHECK-COMMON: @{{.*}} = internal constant { i64, i64, i8*, i8*, i8*, i64 } { i64 0, i64 40, i8* bitcast (void (i8*, i8*)* @__copy_helper_block_8_32s to i8*), i8* bitcast (void (i8*)* @__destroy_helper_block_8_32s to i8*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 256 }, align 8
// CHECK-COMMON: @{{.*}} = internal constant { i64, i64, i8*, i8*, i8*, i64 } { i64 0, i64 40, i8* bitcast (void (i8*, i8*)* @__copy_helper_block_8_32s to i8*), i8* bitcast (void (i8*)* @__destroy_helper_block_8_32s to i8*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 256 }, align 8
// CHECK-COMMON: @[[BLOCK_DESCRIPTOR_TMP44:.*]] = internal constant { i64, i64, i8*, i8*, i8*, i64 } { i64 0, i64 40, i8* bitcast (void (i8*, i8*)* @__copy_helper_block_8_32s to i8*), i8* bitcast (void (i8*)* @__destroy_helper_block_8_32s to i8*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 256 }, align 8
// CHECK-COMMON: @[[BLOCK_DESCRIPTOR_TMP46:.*]] = internal constant { i64, i64, i8*, i8*, i8*, i8* } { i64 0, i64 48, i8* bitcast (void (i8*, i8*)* @__copy_helper_block_8_32s to i8*), i8* bitcast (void (i8*)* @__destroy_helper_block_8_32s to i8*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i8* getelementptr inbounds ([3 x i8], [3 x i8]* @{{.*}}, i32 0, i32 0) }, align 8
// CHECK-COMMON: @[[BLOCK_DESCRIPTOR_TMP48:.*]] = internal constant { i64, i64, i8*, i8*, i8*, i64 } { i64 0, i64 40, i8* bitcast (void (i8*, i8*)* @__copy_helper_block_8_32b to i8*), i8* bitcast (void (i8*)* @__destroy_helper_block_8_32s to i8*), i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str.47, i32 0, i32 0), i64 256 }, align 8
// CHECK-COMMON: @[[BLOCK_DESCRIPTOR_TMP44:.*]] = linkonce_odr hidden unnamed_addr constant { i64, i64, i8*, i8*, i8*, i64 } { i64 0, i64 40, i8* bitcast (void (i8*, i8*)* @__copy_helper_block_8_32s to i8*), i8* bitcast (void (i8*)* @__destroy_helper_block_8_32s to i8*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 256 }, align 8
// CHECK-COMMON: @[[BLOCK_DESCRIPTOR_TMP9:.*]] = linkonce_odr hidden unnamed_addr constant { i64, i64, i8*, i8*, i8*, i64 } { i64 0, i64 40, i8* bitcast (void (i8*, i8*)* @__copy_helper_block_8_32r to i8*), i8* bitcast (void (i8*)* @__destroy_helper_block_8_32r to i8*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 16 }, align 8
// CHECK-COMMON: @[[BLOCK_DESCRIPTOR_TMP46:.*]] = linkonce_odr hidden unnamed_addr constant { i64, i64, i8*, i8*, i8*, i8* } { i64 0, i64 48, i8* bitcast (void (i8*, i8*)* @__copy_helper_block_8_32s to i8*), i8* bitcast (void (i8*)* @__destroy_helper_block_8_32s to i8*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i8* getelementptr inbounds ([3 x i8], [3 x i8]* @{{.*}}, i32 0, i32 0) }, align 8
// CHECK-COMMON: @[[BLOCK_DESCRIPTOR_TMP48:.*]] = linkonce_odr hidden unnamed_addr constant { i64, i64, i8*, i8*, i8*, i64 } { i64 0, i64 40, i8* bitcast (void (i8*, i8*)* @__copy_helper_block_8_32b to i8*), i8* bitcast (void (i8*)* @__destroy_helper_block_8_32s to i8*), i8* getelementptr inbounds ([9 x i8], [9 x i8]* @{{.*}}, i32 0, i32 0), i64 256 }, align 8

// This shouldn't crash.
void test0(id (^maker)(void)) {
Expand Down
Expand Up @@ -30,7 +30,8 @@ int main() {
void (^block4)() = ^{ printf("%c %#lx", ch, fourByte); NSLog(@"%@", strong); };

// Test5
// CHECK: Inline block variable layout: 0x0100, BL_STRONG:1, BL_OPERATOR:0
// Nothing gets printed here since the descriptor of this block is merged with
// the descriptor of Test3's block.
void (^block5)() = ^{ NSLog(@"%@", strong); printf("%c %#llx", ch, eightByte); };

// Test6
Expand Down
4 changes: 2 additions & 2 deletions clang/test/CodeGenObjC/fragile-arc.m
Expand Up @@ -126,13 +126,13 @@ @implementation C @end
extern void useBlock(void (^block)(void));

// 256 == 0x100 == starts with 1 strong
// GLOBALS: @__block_descriptor_tmp{{.*}} = internal constant {{.*}}, i32 256 }
// GLOBALS: @"__block_descriptor{{.*}} = linkonce_odr hidden {{.*}}, i32 256 }
void testBlockLayoutStrong(id x) {
useBlock(^{ (void) x; });
}

// 1 == 0x001 == starts with 1 weak
// GLOBALS: @__block_descriptor_tmp{{.*}} = internal constant {{.*}}, i32 1 }
// GLOBALS: @"__block_descriptor{{.*}} = linkonce_odr hidden {{.*}}, i32 1 }
void testBlockLayoutWeak(__weak id x) {
useBlock(^{ (void) x; });
}
Expand Down
6 changes: 5 additions & 1 deletion clang/test/CodeGenObjC/noescape.m
Expand Up @@ -17,7 +17,11 @@
// helper functions.

// CHECK: %[[STRUCT_BLOCK_DESCRIPTOR:.*]] = type { i64, i64 }
// CHECK: @[[BLOCK_DESCIPTOR_TMP_2:.*]] = internal constant { i64, i64, i8*, i64 } { i64 0, i64 40, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 256 }, align 8

// When the block is non-escaping, copy/dispose helpers aren't generated, so the
// block layout string must include information about __strong captures.

// CHECK: @[[BLOCK_DESCIPTOR_TMP_2:.*ls32l8"]] = linkonce_odr hidden unnamed_addr constant { i64, i64, i8*, i64 } { i64 0, i64 40, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @{{.*}}, i32 0, i32 0), i64 256 }, align 8

// CHECK-LABEL: define void @test0(
// CHECK: call void @noescapeFunc0({{.*}}, {{.*}} nocapture {{.*}})
Expand Down

0 comments on commit 2ec36f0

Please sign in to comment.