Unreachability

Paul Sandoz paul.sandoz at oracle.com
Wed Oct 16 23:16:25 UTC 2024


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.



More information about the babylon-dev mailing list