ExceptionRegion modeling issues and proposed improvements
Adam Sotona
adam.sotona at oracle.com
Wed Oct 9 08:37:37 UTC 2024
When looking back to the example I realized it is not the best illustrative case I could use.
All nested exception regions in the example have identical start and end positions, so they can be theoretically merged during lift and lifted model can be much simpler (and I'm taking a ToDo note to implement this merge in the BytecodeLift).
However, in many cases nested try blocks differ in try start or try end position, so they cannot be merged into one exception region start according to the current model. And that is the situation with significantly raised complexity.
Here is better example of jumping out of the nested exception stack:
@CodeReflection
static void nestedCatch(int i) {
while (true) {
try {
try {
if (i == 0) {
break;
} else if (i == 1) {
continue;
}
} catch (NullPointerException npe) {
npe.printStackTrace();
}
doSomething();
} catch (RuntimeException re) {
re.printStackTrace();
}
}
}
The above source is compiled into following model:
func @"nestedCatch" @loc="26:5:file:///Users/asotona/NetBeansProjects/JavaApplication6/src/javaapplication6/NewClass10.java" (%0 : int)void -> {
%1 : Var<int> = var %0 @"i";
branch ^block_1;
^block_1:
%2 : boolean = constant @"true";
cbranch %2 ^block_2 ^block_16;
^block_2:
%3 : java.lang.reflect.code.op.CoreOp$ExceptionRegion = exception.region.enter ^block_3 ^block_14;
^block_3:
%4 : java.lang.reflect.code.op.CoreOp$ExceptionRegion = exception.region.enter ^block_4 ^block_11;
^block_4:
%5 : int = var.load %1;
%6 : int = constant @"0";
%7 : boolean = eq %5 %6;
cbranch %7 ^block_5 ^block_6;
^block_5:
branch ^block_16;
^block_6:
%8 : int = var.load %1;
%9 : int = constant @"1";
%10 : boolean = eq %8 %9;
cbranch %10 ^block_7 ^block_8;
^block_7:
branch ^block_1;
^block_8:
branch ^block_9;
^block_9:
branch ^block_10;
^block_10:
exception.region.exit %4 ^block_12;
^block_11(%11 : java.lang.NullPointerException):
%12 : Var<java.lang.NullPointerException> = var %11 @"npe";
%13 : java.lang.NullPointerException = var.load %12;
invoke %13 @"java.lang.NullPointerException::printStackTrace()void";
branch ^block_12;
^block_12:
invoke @"javaapplication6.NewClass10::doSomething()void";
branch ^block_13;
^block_13:
exception.region.exit %3 ^block_15;
^block_14(%14 : java.lang.RuntimeException):
%15 : Var<java.lang.RuntimeException> = var %14 @"re";
%16 : java.lang.RuntimeException = var.load %15;
invoke %16 @"java.lang.RuntimeException::printStackTrace()void";
branch ^block_15;
^block_15:
branch ^block_1;
^block_16:
return;
};
Which seems to invalid because the exception regions are not always leaved (break; and continue; seems to have a bug).
Here is lifted version of the same code, where 11 more synthetic blocks are necessary to exit the exception regions correctly:
func @"nestedCatch" (%0 : int)void -> {
branch ^block_1;
^block_1:
%1 : java.lang.reflect.code.op.CoreOp$ExceptionRegion = exception.region.enter ^block_2 ^block_27;
^block_2:
%2 : java.lang.reflect.code.op.CoreOp$ExceptionRegion = exception.region.enter ^block_3 ^block_20;
^block_3:
%3 : int = constant @"0";
%4 : boolean = eq %0 %3;
cbranch %4 ^block_4 ^block_7;
^block_4:
exception.region.exit %2 ^block_5;
^block_5:
exception.region.exit %1 ^block_6;
^block_6:
return;
^block_7:
exception.region.exit %2 ^block_8;
^block_8:
exception.region.exit %1 ^block_9;
^block_9:
%5 : java.lang.reflect.code.op.CoreOp$ExceptionRegion = exception.region.enter ^block_10 ^block_27;
^block_10:
%6 : java.lang.reflect.code.op.CoreOp$ExceptionRegion = exception.region.enter ^block_11 ^block_19;
^block_11:
%7 : int = constant @"1";
%8 : boolean = eq %0 %7;
cbranch %8 ^block_12 ^block_15;
^block_12:
exception.region.exit %6 ^block_13;
^block_13:
exception.region.exit %5 ^block_14;
^block_14:
branch ^block_1;
^block_15:
exception.region.exit %6 ^block_16;
^block_16:
exception.region.exit %5 ^block_17;
^block_17:
%9 : java.lang.reflect.code.op.CoreOp$ExceptionRegion = exception.region.enter ^block_18 ^block_27;
^block_18:
exception.region.exit %9 ^block_24;
^block_19(%10 : java.lang.NullPointerException):
exception.region.exit %5 ^block_21(%10);
^block_20(%11 : java.lang.NullPointerException):
exception.region.exit %1 ^block_21(%11);
^block_21(%12 : java.lang.NullPointerException):
%13 : java.lang.reflect.code.op.CoreOp$ExceptionRegion = exception.region.enter ^block_22 ^block_27;
^block_22:
invoke %12 @"java.lang.NullPointerException::printStackTrace()void";
exception.region.exit %13 ^block_23;
^block_23:
branch ^block_24;
^block_24:
%14 : java.lang.reflect.code.op.CoreOp$ExceptionRegion = exception.region.enter ^block_25 ^block_27;
^block_25:
invoke @"javaapplication6.NewClass10::doSomething()void";
exception.region.exit %14 ^block_26;
^block_26:
branch ^block_1;
^block_27(%15 : java.lang.RuntimeException):
invoke %15 @"java.lang.RuntimeException::printStackTrace()void";
branch ^block_1;
};
With proposed changes the above example model may look like this:
func @"nestedCatch" (%0 : int)void -> {
%1 : java.lang.reflect.code.op.CoreOp$ExceptionRegion<java.lang.RuntimeException> = exception.region ^block_13;
%2 : java.lang.reflect.code.op.CoreOp$ExceptionRegion<java.lang.NullPointerException> = exception.region ^block_9;
branch ^block_1;
^block_1:
exception.region.enter %1 %2 ^block_2; // entering nested try blocks
^block_2:
%3 : int = constant @"0";
%4 : boolean = eq %0 %3;
cbranch %4 ^block_3 ^block_5;
^block_3:
exception.region.exit %2 %1 ^block_4; // leaving nested try blocks
^block_4:
return;
^block_5:
%5 : int = constant @"1";
%6 : boolean = eq %0 %5;
cbranch %6 ^block_6 ^block_8;
^block_6:
exception.region.exit %2 %1 ^block_7; ; // leaving nested try blocks
^block_7:
branch ^block_1;
^block_8:
exception.region.exit %2 ^block_11; // leaving only the inner NPE try block
^block_9(%7 : java.lang.NullPointerException):
exception.region.enter %1 ^block_10; // re-entering the outer try block
^block_10:
invoke %7 @"java.lang.NullPointerException::printStackTrace()void";
branch ^block_11;
^block_11:
invoke @"javaapplication6.NewClass10::doSomething()void";
exception.region.exit %1 ^block_12; // leaving the outer try block
^block_12:
branch ^block_1;
^block_13(%8 : java.lang.RuntimeException):
invoke %8 @"java.lang.RuntimeException::printStackTrace()void";
branch ^block_1;
};
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/babylon-dev/attachments/20241009/9718a472/attachment-0001.htm>
More information about the babylon-dev
mailing list