@@ -1637,23 +1637,104 @@ class LiftoffCompiler {
16371637 wasm::ObjectAccess::ElementOffsetInTaggedFixedArray (
16381638 catch_case.maybe_tag .tag_imm .index ));
16391639
1640+ VarState exn = __ cache_state () -> stack_state.back ();
1641+
16401642 CODE_COMMENT (" compare tags" );
1641- {
1642- FREEZE_STATE (frozen);
1643- Label caught;
1644- __ emit_cond_jump (kEqual , &caught, kRefNull , imm_tag, caught_tag.gp (),
1645- frozen);
1646- // The tags don't match, merge the current state into the catch state
1647- // and jump to the next handler.
1648- __ MergeFullStackWith (block->try_info ->catch_state );
1649- __ emit_jump (&block->try_info ->catch_label );
1650- __ bind (&caught);
1643+ if (catch_case.maybe_tag .tag_imm .tag ->sig ->parameter_count () == 1 &&
1644+ catch_case.maybe_tag .tag_imm .tag ->sig ->GetParam (0 ) == kWasmExternRef ) {
1645+ // Check for the special case where the tag is WebAssembly.JSTag and the
1646+ // exception is not a WebAssembly.Exception. In this case the exception is
1647+ // caught and pushed on the operand stack.
1648+ // Only perform this check if the tag signature is the same as
1649+ // the JSTag signature, i.e. a single externref, otherwise we know
1650+ // statically that it cannot be the JSTag.
1651+ LiftoffRegister undefined =
1652+ pinned.set (__ GetUnusedRegister (kGpReg , pinned));
1653+ __ LoadFullPointer (
1654+ undefined.gp (), kRootRegister ,
1655+ IsolateData::root_slot_offset (RootIndex::kUndefinedValue ));
1656+ LiftoffRegister js_tag = pinned.set (__ GetUnusedRegister (kGpReg , pinned));
1657+ LOAD_TAGGED_PTR_INSTANCE_FIELD (js_tag.gp (), NativeContext, pinned);
1658+ __ LoadTaggedPointer (
1659+ js_tag.gp (), js_tag.gp (), no_reg,
1660+ NativeContext::SlotOffset (Context::WASM_JS_TAG_INDEX));
1661+ __ LoadTaggedPointer (
1662+ js_tag.gp (), js_tag.gp (), no_reg,
1663+ wasm::ObjectAccess::ToTagged (WasmTagObject::kTagOffset ));
1664+ {
1665+ LiftoffAssembler::CacheState initial_state (zone_);
1666+ LiftoffAssembler::CacheState end_state (zone_);
1667+ Label js_exception;
1668+ Label done;
1669+ Label uncaught;
1670+ initial_state.Split (*__ cache_state ());
1671+ {
1672+ FREEZE_STATE (state_merged_explicitly);
1673+ // If the tag is undefined, this is not a wasm exception. Go to a
1674+ // different block to process the JS exception. Otherwise compare it
1675+ // with the expected tag.
1676+ __ emit_cond_jump (kEqual , &js_exception, kRefNull , caught_tag.gp (),
1677+ undefined.gp (), state_merged_explicitly);
1678+ __ emit_cond_jump (kNotEqual , &uncaught, kRefNull , imm_tag,
1679+ caught_tag.gp (), state_merged_explicitly);
1680+ }
1681+ // Case 1: A wasm exception with a matching tag.
1682+ CODE_COMMENT (" unpack exception" );
1683+ GetExceptionValues (decoder, __ cache_state ()->stack_state .back (),
1684+ catch_case.maybe_tag .tag_imm .tag );
1685+ // GetExceptionValues modified the cache state. Remember the new state
1686+ // to merge the end state of case 2 into it.
1687+ end_state.Steal (*__ cache_state ());
1688+ __ emit_jump (&done);
1689+
1690+ __ bind (&js_exception);
1691+ __ cache_state () -> Split (initial_state);
1692+ {
1693+ FREEZE_STATE (state_merged_explicitly);
1694+ __ emit_cond_jump (kNotEqual , &uncaught, kRefNull , imm_tag,
1695+ js_tag.gp (), state_merged_explicitly);
1696+ }
1697+ // Case 2: A JS exception, and the expected tag is JSTag.
1698+ // TODO(thibaudm): Can we avoid some state splitting/stealing by
1699+ // reserving this register earlier and not modifying the state in this
1700+ // block?
1701+ CODE_COMMENT (" JS exception caught by JSTag" );
1702+ LiftoffRegister exception = __ PeekToRegister (0 , pinned);
1703+ __ PushRegister (kRefNull , exception);
1704+ // The exception is now on the stack twice: once as an implicit operand
1705+ // for rethrow, and once as the "unpacked" value.
1706+ __ MergeFullStackWith (end_state);
1707+ __ emit_jump (&done);
1708+
1709+ // Case 3: Either a wasm exception with a mismatching tag, or a JS
1710+ // exception but the expected tag is not JSTag.
1711+ __ bind (&uncaught);
1712+ __ cache_state () -> Steal (initial_state);
1713+ __ MergeFullStackWith (block->try_info ->catch_state );
1714+ __ emit_jump (&block->try_info ->catch_label );
1715+
1716+ __ bind (&done);
1717+ __ cache_state () -> Steal (end_state);
1718+ }
1719+ } else {
1720+ {
1721+ FREEZE_STATE (frozen);
1722+ Label caught;
1723+ __ emit_cond_jump (kEqual , &caught, kRefNull , imm_tag, caught_tag.gp (),
1724+ frozen);
1725+ // The tags don't match, merge the current state into the catch state
1726+ // and jump to the next handler.
1727+ __ MergeFullStackWith (block->try_info ->catch_state );
1728+ __ emit_jump (&block->try_info ->catch_label );
1729+ __ bind (&caught);
1730+ }
1731+
1732+ CODE_COMMENT (" unpack exception" );
1733+ pinned = {};
1734+ GetExceptionValues (decoder, __ cache_state ()->stack_state .back (),
1735+ catch_case.maybe_tag .tag_imm .tag );
16511736 }
16521737
1653- CODE_COMMENT (" unpack exception" );
1654- pinned = {};
1655- VarState exn = __ cache_state ()->stack_state .back ();
1656- GetExceptionValues (decoder, exn, catch_case.maybe_tag .tag_imm .tag );
16571738 if (catch_case.kind == kCatchRef ) {
16581739 // Append the exception on the operand stack.
16591740 DCHECK (exn.is_stack ());
0 commit comments