Skip to content

Commit f44aaab

Browse files
committed
add loop labels support for jvm backend
1 parent 8d28509 commit f44aaab

File tree

3 files changed

+92
-17
lines changed

3 files changed

+92
-17
lines changed

src/vm/jvm/QAST/Compiler.nqp

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,8 +1040,10 @@ for ('', 'repeat_') -> $repness {
10401040
my $handler := 1;
10411041
my @operands;
10421042
my $orig_type;
1043+
my $label;
10431044
for $op.list {
10441045
if $_.named eq 'nohandler' { $handler := 0; }
1046+
elsif $_.named eq 'label' { $label := $_; }
10451047
else { @operands.push($_) }
10461048
}
10471049
if +@operands != 2 && +@operands != 3 {
@@ -1064,8 +1066,8 @@ for ('', 'repeat_') -> $repness {
10641066
my $l_handler_id;
10651067
my $nr_handler_id;
10661068
if $handler {
1067-
$l_handler_id := &*REGISTER_UNWIND_HANDLER($*HANDLER_IDX, $EX_CAT_LAST);
1068-
$nr_handler_id := &*REGISTER_UNWIND_HANDLER($l_handler_id, $EX_CAT_NEXT +| $EX_CAT_REDO);
1069+
$l_handler_id := &*REGISTER_UNWIND_HANDLER($*HANDLER_IDX, $EX_CAT_LAST, :ex_obj(1));
1070+
$nr_handler_id := &*REGISTER_UNWIND_HANDLER($l_handler_id, $EX_CAT_NEXT +| $EX_CAT_REDO, :ex_obj(1));
10691071
}
10701072

10711073
# Emit loop prelude, evaluating condition.
@@ -1128,7 +1130,7 @@ for ('', 'repeat_') -> $repness {
11281130
# Add redo and next handler if needed.
11291131
if $handler {
11301132
my $catch := JAST::InstructionList.new();
1131-
$qastcomp.unwind_check($catch, $nr_handler_id);
1133+
$qastcomp.unwind_check($catch, $nr_handler_id, :$label, :outer($l_handler_id));
11321134
$catch.append(JAST::Instruction.new( :op('getfield'), $TYPE_EX_UNWIND, 'category', 'Long' ));
11331135
$catch.append(JAST::PushIVal.new( :value($EX_CAT_REDO) ));
11341136
$catch.append($LCMP);
@@ -1155,7 +1157,7 @@ for ('', 'repeat_') -> $repness {
11551157
# If needed, wrap the whole thing in a last exception handler.
11561158
if $handler {
11571159
my $catch := JAST::InstructionList.new();
1158-
$qastcomp.unwind_check($catch, $l_handler_id);
1160+
$qastcomp.unwind_check($catch, $l_handler_id, :$label, :outer($*HANDLER_IDX));
11591161
$catch.append($POP);
11601162
$il := $qastcomp.delimit_handler(
11611163
JAST::TryCatch.new( :try($il), :catch($catch), :type($TYPE_EX_UNWIND) ),
@@ -1179,8 +1181,10 @@ for ('', 'repeat_') -> $repness {
11791181
QAST::OperationsJAST.add_core_op('for', -> $qastcomp, $op {
11801182
my $handler := 1;
11811183
my @operands;
1184+
my $label;
11821185
for $op.list {
11831186
if $_.named eq 'nohandler' { $handler := 0; }
1187+
elsif $_.named eq 'label' { $label := $_; }
11841188
else { @operands.push($_) }
11851189
}
11861190

@@ -1205,9 +1209,9 @@ QAST::OperationsJAST.add_core_op('for', -> $qastcomp, $op {
12051209
my $n_handler_id;
12061210
my $r_handler_id;
12071211
if $handler {
1208-
$l_handler_id := &*REGISTER_UNWIND_HANDLER($*HANDLER_IDX, $EX_CAT_LAST);
1209-
$n_handler_id := &*REGISTER_UNWIND_HANDLER($l_handler_id, $EX_CAT_NEXT);
1210-
$r_handler_id := &*REGISTER_UNWIND_HANDLER($n_handler_id, $EX_CAT_REDO);
1212+
$l_handler_id := &*REGISTER_UNWIND_HANDLER($*HANDLER_IDX, $EX_CAT_LAST, :ex_obj(1));
1213+
$n_handler_id := &*REGISTER_UNWIND_HANDLER($l_handler_id, $EX_CAT_NEXT, :ex_obj(1));
1214+
$r_handler_id := &*REGISTER_UNWIND_HANDLER($n_handler_id, $EX_CAT_REDO, :ex_obj(1));
12111215
}
12121216

12131217
# Evaluate the thing we'll iterate over, get the iterator and
@@ -1285,7 +1289,7 @@ QAST::OperationsJAST.add_core_op('for', -> $qastcomp, $op {
12851289
# Wrap block invocation in redo handler if needed.
12861290
if $handler {
12871291
my $catch := JAST::InstructionList.new();
1288-
$qastcomp.unwind_check($catch, $r_handler_id);
1292+
$qastcomp.unwind_check($catch, $r_handler_id, :$label, :outer($n_handler_id));
12891293
$catch.append($POP);
12901294
$catch.append(JAST::Instruction.new( :op('goto'), $lbl_redo ));
12911295
$inv_il := $qastcomp.delimit_handler(
@@ -1297,7 +1301,7 @@ QAST::OperationsJAST.add_core_op('for', -> $qastcomp, $op {
12971301
# Wrap value fetching and call in "next" handler if needed.
12981302
if $handler {
12991303
my $catch := JAST::InstructionList.new();
1300-
$qastcomp.unwind_check($catch, $n_handler_id);
1304+
$qastcomp.unwind_check($catch, $n_handler_id, :$label, :outer($l_handler_id));
13011305
$catch.append($POP);
13021306
$val_il := $qastcomp.delimit_handler(
13031307
JAST::TryCatch.new( :try($val_il), :$catch, :type($TYPE_EX_UNWIND) ),
@@ -1309,7 +1313,7 @@ QAST::OperationsJAST.add_core_op('for', -> $qastcomp, $op {
13091313
# Emit postlude, wrapping in last handler if needed.
13101314
if $handler {
13111315
my $catch := JAST::InstructionList.new();
1312-
$qastcomp.unwind_check($catch, $l_handler_id);
1316+
$qastcomp.unwind_check($catch, $l_handler_id, :$label, :outer($*HANDLER_IDX));
13131317
$catch.append($POP);
13141318
$catch.append(JAST::Instruction.new( :op('goto'), $lbl_done ));
13151319
$loop_il := $qastcomp.delimit_handler(
@@ -1711,15 +1715,54 @@ my %control_map := nqp::hash(
17111715
'redo', $EX_CAT_REDO
17121716
);
17131717
QAST::OperationsJAST.add_core_op('control', -> $qastcomp, $op {
1718+
my $label;
1719+
for $op.list {
1720+
if $_.named eq 'label' { $label := $_; }
1721+
}
17141722
my $name := $op.name;
17151723
if nqp::existskey(%control_map, $name) {
17161724
my $cat := %control_map{$name};
17171725
my $il := JAST::InstructionList.new();
17181726
$*STACK.spill_to_locals($il);
1719-
$il.append(JAST::PushIVal.new( :value($cat) ));
1720-
$il.append($ALOAD_1);
1721-
$il.append(savesite(JAST::Instruction.new( :op('invokestatic'), $TYPE_OPS,
1722-
'throwcatdyn_c', 'Void', 'Long', $TYPE_TC )));
1727+
if $label {
1728+
my $new_ex := $*TA.fresh_o();
1729+
1730+
# Create a new exception object
1731+
$il.append($ALOAD_1); # TC
1732+
$il.append(JAST::Instruction.new( :op('invokestatic'), $TYPE_OPS,
1733+
'newexception', $TYPE_SMO, $TYPE_TC ));
1734+
$il.append(JAST::Instruction.new( :op('astore'), $new_ex ));
1735+
1736+
# Store the label as payload
1737+
$il.append(JAST::Instruction.new( :op('aload'), $new_ex ));
1738+
my $payload := $qastcomp.as_jast($label, :want($RT_OBJ));
1739+
$il.append($payload.jast);
1740+
$*STACK.obtain($il, $payload);
1741+
$il.append(JAST::Instruction.new( :op('aload'), 'tc' ));
1742+
$il.append(JAST::Instruction.new( :op('invokestatic'), $TYPE_OPS,
1743+
'setpayload', $TYPE_SMO, $TYPE_SMO, $TYPE_SMO, $TYPE_TC ));
1744+
$il.append($POP); # discard payload
1745+
1746+
# Set exception type
1747+
$il.append(JAST::Instruction.new( :op('aload'), $new_ex ));
1748+
$il.append(JAST::PushIVal.new( :value($cat) ));
1749+
$il.append(JAST::Instruction.new( :op('aload'), 'tc' ));
1750+
$il.append(JAST::Instruction.new( :op('invokestatic'), $TYPE_OPS,
1751+
'setextype', 'Long', $TYPE_SMO, 'Long', $TYPE_TC ));
1752+
$il.append($POP2); # discard exception category
1753+
1754+
# Throw it
1755+
$il.append(JAST::Instruction.new( :op('aload'), $new_ex ));
1756+
$il.append(JAST::Instruction.new( :op('aload'), 'tc' ));
1757+
$il.append(savesite(JAST::Instruction.new( :op('invokestatic'), $TYPE_OPS,
1758+
'_throw_c', 'Void', $TYPE_SMO, $TYPE_TC )));
1759+
}
1760+
else {
1761+
$il.append(JAST::PushIVal.new( :value($cat) ));
1762+
$il.append($ALOAD_1);
1763+
$il.append(savesite(JAST::Instruction.new( :op('invokestatic'), $TYPE_OPS,
1764+
'throwcatdyn_c', 'Void', 'Long', $TYPE_TC )));
1765+
}
17231766
result_from_cf($il, $RT_OBJ);
17241767
}
17251768
else {
@@ -4325,7 +4368,7 @@ class QAST::CompilerJAST {
43254368
# rethrow of the handler. Assumes the exception is on the stack top,
43264369
# and that we will not swallow it.
43274370
my $unwind_lbl := 0;
4328-
method unwind_check($il, $desired) {
4371+
method unwind_check($il, $desired, :$label, :$outer = 0) {
43294372
my $lbl_i := JAST::Label.new( :name('unwind_' ~ $unwind_lbl++) );
43304373
my $lbl_c := JAST::Label.new( :name('unwind_' ~ $unwind_lbl++) );
43314374
$il.append($DUP);
@@ -4341,6 +4384,12 @@ class QAST::CompilerJAST {
43414384
$il.append(JAST::Instruction.new( :op('if_acmpeq'), $lbl_c ));
43424385
$il.append($ATHROW);
43434386
$il.append($lbl_c);
4387+
4388+
$il.append($DUP);
4389+
$il.append(JAST::PushIVal.new( :value($label ?? nqp::where($label.value) !! 0) ));
4390+
$il.append(JAST::PushIVal.new( :value($outer) ));
4391+
$il.append(JAST::Instruction.new( :op('aload'), 'tc' ));
4392+
$il.append(JAST::Instruction.new( :op('invokestatic'), $TYPE_OPS, '_is_same_label', 'Void', $TYPE_EX_UNWIND, 'Long', 'Long', $TYPE_TC ));
43444393
}
43454394

43464395
# Wraps a handler with code to set/clear the current handler.

src/vm/jvm/runtime/org/perl6/nqp/runtime/ExceptionHandling.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,7 @@ public static void handlerDynamic(ThreadContext tc, long category,
7979
while (tryHandler != 0) {
8080
for (int i = 0; i < handlers.length; i++) {
8181
if (handlers[i][0] == tryHandler) {
82-
// Found an active one, but is it the right category?
8382
if ((handlers[i][2] & category) != 0) {
84-
// Correct category, but ensure we aren't already in it.
8583
boolean valid = true;
8684
for (int j = 0; j < tc.handlers.size(); j++) {
8785
if (tc.handlers.get(j).handlerInfo == handlers[i]) {
@@ -139,6 +137,14 @@ private static void invokeHandler(ThreadContext tc, long[] handlerInfo,
139137
tc.unwinder.unwindTarget = handlerInfo[0];
140138
tc.unwinder.unwindCompUnit = handlerFrame.codeRef.staticInfo.compUnit;
141139
tc.unwinder.category = category;
140+
tc.unwinder.result = null;
141+
throw tc.unwinder;
142+
case EX_UNWIND_OBJECT:
143+
tc.unwinder.unwindTarget = handlerInfo[0];
144+
tc.unwinder.unwindCompUnit = handlerFrame.codeRef.staticInfo.compUnit;
145+
tc.unwinder.category = category;
146+
if (exObj != null)
147+
tc.unwinder.result = (SixModelObject)exObj;
142148
throw tc.unwinder;
143149
case EX_BLOCK:
144150
try {

src/vm/jvm/runtime/org/perl6/nqp/runtime/Ops.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4520,6 +4520,26 @@ public static void _throw_c(SixModelObject obj, ThreadContext tc) {
45204520
throw ExceptionHandling.dieInternal(tc, "throw needs an object with VMException representation");
45214521
}
45224522
}
4523+
public static void _is_same_label(UnwindException uwex, long where, long outerHandler, ThreadContext tc) {
4524+
if (uwex instanceof UnwindException) {
4525+
VMExceptionInstance vmex = (VMExceptionInstance)uwex.result;
4526+
if (vmex == null)
4527+
return;
4528+
4529+
if (vmex instanceof VMExceptionInstance) {
4530+
if (vmex.payload.hashCode() == where)
4531+
return;
4532+
/* We're moving to the outside so we do not rethrow to us. */
4533+
tc.curFrame.curHandler = outerHandler;
4534+
vmex.origin = tc.curFrame;
4535+
vmex.nativeTrace = (new Throwable()).getStackTrace();
4536+
ExceptionHandling.handlerDynamic(tc, vmex.category, false, vmex);
4537+
}
4538+
}
4539+
else {
4540+
throw ExceptionHandling.dieInternal(tc, "_is_same_label needs an object with UnwindException representation");
4541+
}
4542+
}
45234543
public static void rethrow_c(SixModelObject obj, ThreadContext tc) {
45244544
if (obj instanceof VMExceptionInstance) {
45254545
VMExceptionInstance ex = (VMExceptionInstance)obj;

0 commit comments

Comments
 (0)