summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Wennborg <hans@hanshq.net>2019-02-12 08:35:38 +0000
committerHans Wennborg <hans@hanshq.net>2019-02-12 08:35:38 +0000
commitdc0d2a5d37f4474b828e06d2dd0198e9adb0c9df (patch)
tree0af3ca47639dfb8359fc48a1493747e943dcd5b0
parenta57d20da0ab43ad5025bb40f20195619abb0764b (diff)
Merging r353495:
------------------------------------------------------------------------ r353495 | jfb | 2019-02-08 02:29:17 +0100 (Fri, 08 Feb 2019) | 32 lines Variable auto-init: fix __block initialization Summary: Automatic initialization [1] of __block variables was trampling over the block's headers after they'd been initialized, which caused self-init usage to crash, such as here: typedef struct XYZ { void (^block)(); } *xyz_t; __attribute__((noinline)) xyz_t create(void (^block)()) { xyz_t myself = malloc(sizeof(struct XYZ)); myself->block = block; return myself; } int main() { __block xyz_t captured = create(^(){ (void)captured; }); } This type of code shouldn't be broken by variable auto-init, even if it's sketchy. [1] With -ftrivial-auto-var-init=pattern <rdar://problem/47798396> Reviewers: rjmccall, pcc, kcc Subscribers: jkorous, dexonsmith, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D57797 ------------------------------------------------------------------------ git-svn-id: https://llvm.org/svn/llvm-project/cfe/branches/release_80@353807 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/CodeGen/CGDecl.cpp10
-rw-r--r--test/CodeGenCXX/trivial-auto-var-init.cpp26
2 files changed, 33 insertions, 3 deletions
diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp
index 5959d889b4..b98657ffd8 100644
--- a/lib/CodeGen/CGDecl.cpp
+++ b/lib/CodeGen/CGDecl.cpp
@@ -1631,11 +1631,15 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
? LangOptions::TrivialAutoVarInitKind::Uninitialized
: getContext().getLangOpts().getTrivialAutoVarInit()));
- auto initializeWhatIsTechnicallyUninitialized = [&]() {
+ auto initializeWhatIsTechnicallyUninitialized = [&](Address Loc) {
if (trivialAutoVarInit ==
LangOptions::TrivialAutoVarInitKind::Uninitialized)
return;
+ // Only initialize a __block's storage: we always initialize the header.
+ if (emission.IsEscapingByRef)
+ Loc = emitBlockByrefAddress(Loc, &D, /*follow=*/false);
+
CharUnits Size = getContext().getTypeSizeInChars(type);
if (!Size.isZero()) {
switch (trivialAutoVarInit) {
@@ -1713,7 +1717,7 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
};
if (isTrivialInitializer(Init)) {
- initializeWhatIsTechnicallyUninitialized();
+ initializeWhatIsTechnicallyUninitialized(Loc);
return;
}
@@ -1727,7 +1731,7 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
}
if (!constant) {
- initializeWhatIsTechnicallyUninitialized();
+ initializeWhatIsTechnicallyUninitialized(Loc);
LValue lv = MakeAddrLValue(Loc, type);
lv.setNonGC(true);
return EmitExprAsInit(Init, &D, lv, capturedByInit);
diff --git a/test/CodeGenCXX/trivial-auto-var-init.cpp b/test/CodeGenCXX/trivial-auto-var-init.cpp
index b795c0755b..37ff770abf 100644
--- a/test/CodeGenCXX/trivial-auto-var-init.cpp
+++ b/test/CodeGenCXX/trivial-auto-var-init.cpp
@@ -30,6 +30,32 @@ void test_block() {
used(block);
}
+// Using the variable being initialized is typically UB in C, but for blocks we
+// can be nice: they imply extra book-keeping and we can do the auto-init before
+// any of said book-keeping.
+//
+// UNINIT-LABEL: test_block_self_init(
+// ZERO-LABEL: test_block_self_init(
+// ZERO: %block = alloca <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>, align 8
+// ZERO: %captured1 = getelementptr inbounds %struct.__block_byref_captured, %struct.__block_byref_captured* %captured, i32 0, i32 4
+// ZERO-NEXT: store %struct.XYZ* null, %struct.XYZ** %captured1, align 8
+// ZERO: %call = call %struct.XYZ* @create(
+// PATTERN-LABEL: test_block_self_init(
+// PATTERN: %block = alloca <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, i8* }>, align 8
+// PATTERN: %captured1 = getelementptr inbounds %struct.__block_byref_captured, %struct.__block_byref_captured* %captured, i32 0, i32 4
+// PATTERN-NEXT: store %struct.XYZ* inttoptr (i64 -6148914691236517206 to %struct.XYZ*), %struct.XYZ** %captured1, align 8
+// PATTERN: %call = call %struct.XYZ* @create(
+void test_block_self_init() {
+ using Block = void (^)();
+ typedef struct XYZ {
+ Block block;
+ } * xyz_t;
+ extern xyz_t create(Block block);
+ __block xyz_t captured = create(^() {
+ (void)captured;
+ });
+}
+
// This type of code is currently not handled by zero / pattern initialization.
// The test will break when that is fixed.
// UNINIT-LABEL: test_goto_unreachable_value(