Unreachability

Gary Frost gary.frost at oracle.com
Thu Oct 17 11:45:38 UTC 2024


Paul,

This looks reasonable.

Debugging this my end yesterday, I concluded that where the method is supposed to return a value and I see a return without a value,  it should be a huge hint to my C99 code gen that this return is not reachable and so my C99 code gen can just omit the return.

In the case where the return is void I would indeed emit an unnecessary 'return;' which thankfully CUDA and OpenCL just accepts as noise.

I was about to make this mildy hacky change this morning, but now will await the fix you suggest, as I do think tagging this with an unreachable Op rather than return Op would be far more useful in the general case.

BTW outlook tagged the email below as in 'Slovak' 😉

Gary



________________________________
From: babylon-dev <babylon-dev-retn at openjdk.org> on behalf of Paul Sandoz <paul.sandoz at oracle.com>
Sent: Thursday, October 17, 2024 12:16 AM
To: babylon-dev at openjdk.org <babylon-dev at openjdk.org>
Subject: Re: Unreachability

Here’s a possible fix:

        CoreOp.FuncOp scanMethod() {
            scan(body);
            appendReturnOrUnreachable(body);
            CoreOp.FuncOp func = CoreOp.func(name.toString(), stack.body);
            func.setLocation(generateLocation(currentNode, true));
            return func;
        }

        private <O extends Op & Op.Terminating> void appendReturnOrUnreachable(JCTree body) {
            // Append only if an existing terminating operation is not present
            if (lastOp == null || !(lastOp instanceof Op.Terminating)) {
                if (isReachable(body)) {
                    append(CoreOp._return());
                } else {
                    append(CoreOp.unreachable());
                }
            }
        }

        boolean isReachable(JCTree node) {
            return flow.aliveAfter(typeEnvs.get(currentClassSym), node, make);
        }

Paul.

> On Oct 16, 2024, at 1:18 PM, Paul Sandoz <paul.sandoz at oracle.com> wrote:
>
> Hi,
>
> Gary encountered an issue when transforming say:
>
>    @CodeReflection
>    public static float f(float input) {
>        if (input >= 0f) {
>            return 1f;
>        } else {
>            return 0f;
>        }
>        // Unreachable
>    }
>
> Compiler produced model:
>
> func @"f" @loc="10:5:..." (%0 : float)float -> {
>    %1 : Var<float> = var %0 @"input" @loc="10:5";
>    java.if @loc="13:9"
>        ()boolean -> {
>            %2 : float = var.load %1 @loc="13:13";
>            %3 : float = constant @"0.0" @loc="13:22";
>            %4 : boolean = ge %2 %3 @loc="13:13";
>            yield %4 @loc="13:9";
>        }
>        ()void -> {
>            %5 : float = constant @"1.0" @loc="14:20";
>            return %5 @loc="14:13";
>        }
>        ()void -> {
>            %6 : float = constant @"0.0" @loc="16:20";
>            return %6 @loc="16:13";
>        };
>    return @loc="10:5";
> };
>
> Lowered model:
>
> func @"f" @loc="10:5:..." (%0 : float)float -> {
>    %1 : Var<float> = var %0 @"input" @loc="10:5";
>    %2 : float = var.load %1 @loc="13:13";
>    %3 : float = constant @"0.0" @loc="13:22";
>    %4 : boolean = ge %2 %3 @loc="13:13";
>    cbranch %4 ^block_1 ^block_2;
>
>  ^block_1:
>    %5 : float = constant @"1.0" @loc="14:20";
>    return %5 @loc="14:13";
>
>  ^block_2:
>    %6 : float = constant @"0.0" @loc="16:20";
>    return %6 @loc="16:13";
>
>  ^block_3:
>    return @loc="10:5";
> };
>
>
> The compiler produced model is incorrect placing a return operation when it should be an unreachable operation.
>
> There are some reminders in ReflectMethods:
>
>        CoreOp.FuncOp scanMethod() {
>            scan(body);
>            // @@@ Check if unreachable
>            appendTerminating(CoreOp::_return);
>            CoreOp.FuncOp func = CoreOp.func(name.toString(), stack.body);
>            func.setLocation(generateLocation(currentNode, true));
>            return func;
>        }
>
> We need to determine if the last statement yields control or not. (It’s easy to see if we lower, as ^block_3 is never referenced.)
>
> In this case we can know because there is no return value in scope, but generally we cannot rely upon that.
>
> The compiler already checks this, I believe in Flow.AliveAnalyzer. However, it does not add additional information to the attributed tree - it does not need to.
>
> Can we reuse the method Flow.aliveAfter? Although I don’t know how to get access to an instance of Env<AttrContext>. Do we get it from TypeEnvs?
>
> Paul.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/babylon-dev/attachments/20241017/61704164/attachment-0001.htm>


More information about the babylon-dev mailing list