Issue 131502
Summary [compiler-rt] BlocksRuntime `_Block_dump` function doesn't dump current ABI
Labels new issue
Assignees
Reporter ADKaster
    The helper method here:

https://github.com/llvm/llvm-project/blob/508db53d1af5b01f8f8275229f087bb6407f0033/compiler-rt/lib/BlocksRuntime/runtime.c#L620-684

Prints that the compiler is "obsolete" on current clang.

The following test program prints the following with both Apple clang version 16.0.0 (clang-1600.0.26.6) and 
Homebrew clang version 19.1.7:

```
Block compiled by obsolete compiler, please recompile source for this Block
closure flags: 0x42000000
^0x16bbcf3c0 (new layout) =
isa: stack Block
flags: HASHELP
refcount: 0
invoke: 0x104233cb8
descriptor: 0x104234058
descriptor->reserved: 0
descriptor->size: 40
descriptor->copy helper: 0x104233ce8
descriptor->dispose helper: 0x104233d20
x=1
```

<details>

<summary>Program source, with most contents copied from BlocksRuntime/runtime.c</summary>

```c++
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>

struct Block_descriptor {
    unsigned long int reserved;
    unsigned long int size;
    void (*copy)(void *dst, void *src);
    void (*dispose)(void *);
};

struct Block_layout {
    void *isa;
    int flags;
    int reserved;
    void (*invoke)(void *, ...);
    struct Block_descriptor *descriptor;
    /* Imported variables. */
};

enum {
 BLOCK_REFCOUNT_MASK =     (0xffff),
    BLOCK_NEEDS_FREE =        (1 << 24),
    BLOCK_HAS_COPY_DISPOSE =  (1 << 25),
    BLOCK_HAS_CTOR = (1 << 26), /* Helpers have C++ code. */
    BLOCK_IS_GC =             (1 << 27),
    BLOCK_IS_GLOBAL =         (1 << 28),
    BLOCK_HAS_DESCRIPTOR = (1 << 29)
};

extern "C" void * _NSConcreteStackBlock[32];
extern "C" void * _NSConcreteMallocBlock[32];
extern "C" void * _NSConcreteAutoBlock[32];
extern "C" void * _NSConcreteFinalizingBlock[32];
extern "C" void * _NSConcreteGlobalBlock[32];
extern "C" void * _NSConcreteWeakBlockVariable[32];

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"

const char *_Block_dump(const void *block);
const char *_Block_dump(const void *block) {
    struct Block_layout const*closure = (struct Block_layout const*)block;
    static char buffer[512];
    char *cp = buffer;
    if (closure == NULL) {
        sprintf(cp, "NULL passed to _Block_dump\n");
        return buffer;
    }
    if (! (closure->flags & BLOCK_HAS_DESCRIPTOR)) {
        printf("Block compiled by obsolete compiler, please recompile source for this Block\n");
 printf("closure flags: 0x%08x\n", closure->flags);
        //exit(1);
 }
    cp += sprintf(cp, "^%p (new layout) =\n", (void *)closure);
 if (closure->isa == NULL) {
        cp += sprintf(cp, "isa: NULL\n");
 }
    else if (closure->isa == _NSConcreteStackBlock) {
        cp += sprintf(cp, "isa: stack Block\n");
    }
    else if (closure->isa == _NSConcreteMallocBlock) {
        cp += sprintf(cp, "isa: malloc heap Block\n");
    }
    else if (closure->isa == _NSConcreteAutoBlock) {
 cp += sprintf(cp, "isa: GC heap Block\n");
    }
    else if (closure->isa == _NSConcreteGlobalBlock) {
        cp += sprintf(cp, "isa: global Block\n");
    }
    else if (closure->isa == _NSConcreteFinalizingBlock) {
        cp += sprintf(cp, "isa: finalizing Block\n");
    }
    else {
        cp += sprintf(cp, "isa?: %p\n", (void *)closure->isa);
    }
    cp += sprintf(cp, "flags:");
    if (closure->flags & BLOCK_HAS_DESCRIPTOR) {
        cp += sprintf(cp, " HASDESCRIPTOR");
    }
    if (closure->flags & BLOCK_NEEDS_FREE) {
 cp += sprintf(cp, " FREEME");
    }
    if (closure->flags & BLOCK_IS_GC) {
        cp += sprintf(cp, " ISGC");
    }
    if (closure->flags & BLOCK_HAS_COPY_DISPOSE) {
        cp += sprintf(cp, " HASHELP");
    }
    if (closure->flags & BLOCK_HAS_CTOR) {
        cp += sprintf(cp, " HASCTOR");
    }
    cp += sprintf(cp, "\nrefcount: %u\n", closure->flags & BLOCK_REFCOUNT_MASK);
    cp += sprintf(cp, "invoke: %p\n", (void *)(uintptr_t)closure->invoke);
    {
 struct Block_descriptor *dp = closure->descriptor;
        cp += sprintf(cp, "descriptor: %p\n", (void *)dp);
        cp += sprintf(cp, "descriptor->reserved: %lu\n", dp->reserved);
        cp += sprintf(cp, "descriptor->size: %lu\n", dp->size);

        if (closure->flags & BLOCK_HAS_COPY_DISPOSE) {
            cp += sprintf(cp, "descriptor->copy helper: %p\n", (void *)(uintptr_t)dp->copy);
            cp += sprintf(cp, "descriptor->dispose helper: %p\n", (void *)(uintptr_t)dp->dispose);
 }
    }
    return buffer;
}
#pragma clang diagnostic pop

int main()
{
    __block int x = 0;
    auto b = ^{
        x++;
    };

 printf("%s", _Block_dump(b));

    b();

    printf("x=%d\n", x);
}
```

</details>

As for *why* I want an accurate dump method, I'm trying to add support to my own std::function-like type erased function for blocks.

I actually want to inspect the pointed-to Block_layout so that I can scan the captured variables for GC pointers in my naive mark-and-sweep conservative GC. And it doesn't seem like I've got a full grasp on which bytes to look for GC pointers in based on my manual debugging.

_______________________________________________
llvm-bugs mailing list
llvm-bugs@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs

Reply via email to