Skip to content

[Pseudo-ObjC] Render @autoreleasepool blocks - #8525

Open
AngeloD2022 wants to merge 1 commit into
Vector35:devfrom
AngeloD2022:pseudo-objc-autoreleasepool
Open

[Pseudo-ObjC] Render @autoreleasepool blocks#8525
AngeloD2022 wants to merge 1 commit into
Vector35:devfrom
AngeloD2022:pseudo-objc-autoreleasepool

Conversation

@AngeloD2022

@AngeloD2022 AngeloD2022 commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Translate autorelease pool runtime function calls to @autoreleasepool {...} blocks.

Original Objective-C snippet:

for (NSUInteger itemNo = 1; itemNo <= 3; itemNo++) {
    @autoreleasepool {
        NSString *item = [NSString stringWithFormat:@"batch %lu, item %lu",
                            (unsigned long)batchNo,
                            (unsigned long)itemNo];
        NSString *displayItem = [item uppercaseString];
        [items addObject:displayItem];
    }
}

Pseudo-C snippet:

for (int64_t i = 1; i <= 3; i += 1)
{
    void* context = _objc_autoreleasePoolPush();
    int64_t x0;
    int64_t var_80_1 = x0;
    i_1 = i;
    id location_2 = _objc_retainAutoreleasedReturnValue(_objc_msgSend(
        _OBJC_CLASS_$_NSString, "stringWithFormat:", &cfstr_batch_%lu,_item_%lu));
    int64_t location_1 = _objc_retainAutoreleasedReturnValue(_objc_msgSend(
        location_2, "uppercaseString"));
    _objc_msgSend(var_20, "addObject:", location_1);
    _objc_storeStrong(&location_1, nullptr);
    _objc_storeStrong(&location_2, nullptr);
    _objc_autoreleasePoolPop(context);
}

Equivalent Pseudo-Objective-C:

for (int64_t i = 1; i <= 3; i += 1)
{
    @autoreleasepool
    {
        int64_t x0;
        int64_t var_80_1 = x0;
        i_1 = i;
        id location_2 =
            [[NSString stringWithFormat:@"batch %lu, item %lu"] retain];
        int64_t location_1 = [[location_2 uppercaseString] retain];
        [var_20 addObject:location_1];
        _objc_storeStrong(&location_1, nullptr);
        _objc_storeStrong(&location_2, nullptr);
    }
}

@AngeloD2022
AngeloD2022 marked this pull request as ready for review September 6, 2026 22:58
@bdash
bdash self-requested a review September 7, 2026 00:13
@AngeloD2022
AngeloD2022 force-pushed the pseudo-objc-autoreleasepool branch from 4c9e11b to 47e0fd0 Compare September 7, 2026 08:04

@bdash bdash left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! The @autoreleasepool rendering is a nice addition. Factoring the handling of HLIL_BLOCK so it can be overridden by Obj-C rendering seems like generally the right shape.

The autorelease region matching needs some work, though.

The fundamental issue is that TryEmitBlockRegion only ever looks at the immediate children of a single block. Nothing requires that a pool's pops are all siblings of the push. An autorelease pool containing a conditional return, or a conditional break or continue when the pool is inside a loop, will result in multiple pops for a single push, with at least one at a deeper level. Those nested pops are missed by the current approach, so they'll survive into the output despite referencing a variable that is no longer visible:

while (true)
{
    @autoreleasepool
    {
        ...
        if (cond)
        {
            // What is `context` that this refers to?
            _objc_autoreleasePoolPop(context);
            break;
        }
    }
    ...
}

I think the shape you want is to pair pops with pushes using the pool handle rather than by position, and then elide any pop of a handle whose region is currently open, wherever it appears inside that region.

Comment thread lang/c/pseudoc.cpp Outdated
}

const auto& statement = exprs[i];
EmitStandardBlockStatement(instr, statement, isFirst, isLast, needSeparator, tokens, settings);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code within EmitStandardBlockStatement assumes that isLast means the statement is the final statement in the block. This pairs it with the block.exprIndex == root check to detect and suppress a trailing void return. After this refactoring, isLast now only means it's the final statement in the span that was passed to EmitBlockStatements as exprs. PseudoObjCFunction::TryEmitBlockRegion passes a subspan of the block's instructions, which violates that assumption.

I'm not entirely sure how this would manifest in practice, but it makes the code harder to reason about.

Comment thread lang/c/pseudoc.cpp Outdated
const HighLevelILInstruction& statement,
bool isFirst,
bool isLast,
bool& needsSeparator,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This being an output parameter is easy to miss.

@bdash

bdash commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

This snippet, compiled with cc -fobjc-arc -Os -o test test.m, demonstrates the issue I described with pops that are not siblings of the push:

#import <Foundation/Foundation.h>

int main(int argc, char** argv)
{
    for (id obj in NSProcessInfo.processInfo.environment)
    {
        @autoreleasepool {
            if (!obj)
                break;
            NSLog(@"%@", obj);
        }
    }
}

@AngeloD2022

Copy link
Copy Markdown
Contributor Author

Appreciate the review, Mark! I'll investigate these tomorrow as soon as I can.

@AngeloD2022
AngeloD2022 force-pushed the pseudo-objc-autoreleasepool branch from 47e0fd0 to e2698d1 Compare September 11, 2026 17:16
@AngeloD2022

Copy link
Copy Markdown
Contributor Author

I made EmitBlockStatements focus solely on the span of statements provided to it, and moved the void return check up from EmitStandardBlockStatement to the loop inside of EmitBlockStatements. Part of the test happens in GetExpr_BLOCK, where the block instruction is in scope, and is passed through the EmitBlockStatements isBlockRoot parameter.

@AngeloD2022
AngeloD2022 force-pushed the pseudo-objc-autoreleasepool branch from 9a8a910 to 4f93515 Compare September 11, 2026 17:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants