Skip to content

Commit

Permalink
Variable auto-init: fix __block initialization
Browse files Browse the repository at this point in the history
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

llvm-svn: 353495
  • Loading branch information
jfbastien committed Feb 8, 2019
1 parent 0719b35 commit b347e75
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 3 deletions.
10 changes: 7 additions & 3 deletions clang/lib/CodeGen/CGDecl.cpp
Expand Up @@ -1633,11 +1633,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) {
Expand Down Expand Up @@ -1714,7 +1718,7 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) {
};

if (isTrivialInitializer(Init)) {
initializeWhatIsTechnicallyUninitialized();
initializeWhatIsTechnicallyUninitialized(Loc);
return;
}

Expand All @@ -1728,7 +1732,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);
Expand Down
26 changes: 26 additions & 0 deletions clang/test/CodeGenCXX/trivial-auto-var-init.cpp
Expand Up @@ -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(
Expand Down

0 comments on commit b347e75

Please sign in to comment.