diff --git a/src/cfg/cfg-traversal.h b/src/cfg/cfg-traversal.h index 6cc758e6e2c..43e2c188d48 100644 --- a/src/cfg/cfg-traversal.h +++ b/src/cfg/cfg-traversal.h @@ -292,11 +292,12 @@ struct CFGWalker : public PostWalker { // and the target try. [[maybe_unused]] bool found = false; for (int j = i - 1; j >= 0; j--) { - if (self->tryStack[j]->template cast()->name == - tryy->delegateTarget) { - i = j; - found = true; - break; + if (auto* outerTry = self->tryStack[j]->template dynCast()) { + if (outerTry->name == tryy->delegateTarget) { + i = j; + found = true; + break; + } } } assert(found); diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 5a89a3ad3e2..2140e46adb0 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -3009,11 +3009,11 @@ void PrintSExpression::visitBlock(Block* curr) { } void PrintSExpression::visitIf(If* curr) { - controlFlowDepth++; o << '('; printExpressionContents(curr); incIndent(); printFullLine(curr->condition); + controlFlowDepth++; doIndent(o, indent); o << "(then"; incIndent(); @@ -3965,6 +3965,7 @@ static std::ostream& printStackIR(StackIR* ir, PrintSExpression& printer) { } else { curr->delegateTarget.print(o); } + catchIndexStack.pop_back(); break; } default: diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h index 214a9ad96d3..bc628518964 100644 --- a/src/tools/fuzzing.h +++ b/src/tools/fuzzing.h @@ -254,6 +254,7 @@ class TranslateToFuzzReader { TranslateToFuzzReader& parent; Function* func; std::vector breakableStack; // things we can break to + std::vector tryStack; // tries we can delegate to Index labelIndex = 0; // a list of things relevant to computing the odds of an infinite loop, diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp index 2e3fb68a8cb..0dbe697d5dc 100644 --- a/src/tools/fuzzing/fuzzing.cpp +++ b/src/tools/fuzzing/fuzzing.cpp @@ -2388,7 +2388,7 @@ void TranslateToFuzzReader::fixAfterChanges(Function* func) { } }); BranchUtils::operateOnScopeNameUses(curr, [&](Name& name) { - if (name.is()) { + if (name.is() && name != DELEGATE_CALLER_TARGET) { replaceIfInvalid(name); } }); @@ -2448,6 +2448,9 @@ void TranslateToFuzzReader::fixAfterChanges(Function* func) { // Check if a reference to a try is valid. bool isValidTryRef(Name target, Expression* curr) { + if (curr->is() && target == DELEGATE_CALLER_TARGET) { + return true; + } // The rethrow or try must be on top. assert(!expressionStack.empty()); assert(expressionStack.back() == curr); @@ -3230,7 +3233,17 @@ Expression* TranslateToFuzzReader::makeIf(Type type) { } Expression* TranslateToFuzzReader::makeTry(Type type) { + auto name = makeLabel(); + funcContext->tryStack.push_back(name); auto* body = make(type); + funcContext->tryStack.pop_back(); + if (oneIn(3)) { + Name delegateTarget = DELEGATE_CALLER_TARGET; + if (!funcContext->tryStack.empty() && !oneIn(4)) { + delegateTarget = pick(funcContext->tryStack); + } + return builder.makeTry(name, body, delegateTarget); + } std::vector catchTags; std::vector catchBodies; auto numTags = upTo(fuzzParams.MAX_TRY_CATCHES); @@ -3273,8 +3286,7 @@ Expression* TranslateToFuzzReader::makeTry(Type type) { } catchBodies.push_back(catchBody); } - // TODO: delegate stuff - return builder.makeTry(body, catchTags, catchBodies); + return builder.makeTry(name, body, catchTags, catchBodies); } Expression* TranslateToFuzzReader::makeTryTable(Type type) { diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index 9860d07b081..e31f979d321 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -3691,7 +3691,7 @@ StackInst* StackIRGenerator::makeStackInst(StackInst::Op op, stackType = Type::none; } else if (op != StackInst::BlockEnd && op != StackInst::IfEnd && op != StackInst::LoopEnd && op != StackInst::TryEnd && - op != StackInst::TryTableEnd) { + op != StackInst::Delegate && op != StackInst::TryTableEnd) { // If a concrete type is returned, we mark the end of the construct has // having that type (as it is pushed to the value stack at that point), // other parts are marked as none). diff --git a/test/lit/basic/exception-handling-legacy.wast b/test/lit/basic/exception-handling-legacy.wast index 6f9d4fcebb3..d4014fa8ce4 100644 --- a/test/lit/basic/exception-handling-legacy.wast +++ b/test/lit/basic/exception-handling-legacy.wast @@ -1301,6 +1301,48 @@ ) (nop) ) + + ;; CHECK-TEXT: (func $delegate-within-if-condition (type $0) + ;; CHECK-TEXT-NEXT: (if + ;; CHECK-TEXT-NEXT: (try (result i32) + ;; CHECK-TEXT-NEXT: (do + ;; CHECK-TEXT-NEXT: (i32.const 1) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (delegate 0) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: (then + ;; CHECK-TEXT-NEXT: (nop) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-TEXT-NEXT: ) + ;; CHECK-BIN: (func $delegate-within-if-condition (type $0) + ;; CHECK-BIN-NEXT: (if + ;; CHECK-BIN-NEXT: (try (result i32) + ;; CHECK-BIN-NEXT: (do + ;; CHECK-BIN-NEXT: (i32.const 1) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (delegate 0) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: (then + ;; CHECK-BIN-NEXT: (nop) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + ;; CHECK-BIN-NEXT: ) + (func $delegate-within-if-condition + ;; An 'if' condition is outside the 'if' control flow scope, so a delegate + ;; to the caller inside the condition should have depth 0. + (if + (try (result i32) + (do + (i32.const 1) + ) + (delegate 0) + ) + (then + (nop) + ) + ) + ) ) ;; CHECK-BIN-NODEBUG: (type $0 (func)) @@ -1790,3 +1832,17 @@ ;; CHECK-BIN-NODEBUG-NEXT: ) ;; CHECK-BIN-NODEBUG-NEXT: (nop) ;; CHECK-BIN-NODEBUG-NEXT: ) + +;; CHECK-BIN-NODEBUG: (func $25 (type $0) +;; CHECK-BIN-NODEBUG-NEXT: (if +;; CHECK-BIN-NODEBUG-NEXT: (try (result i32) +;; CHECK-BIN-NODEBUG-NEXT: (do +;; CHECK-BIN-NODEBUG-NEXT: (i32.const 1) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (delegate 0) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: (then +;; CHECK-BIN-NODEBUG-NEXT: (nop) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) +;; CHECK-BIN-NODEBUG-NEXT: ) diff --git a/test/lit/passes/rse-eh-legacy.wast b/test/lit/passes/rse-eh-legacy.wast index f90da4643d4..115ede70789 100644 --- a/test/lit/passes/rse-eh-legacy.wast +++ b/test/lit/passes/rse-eh-legacy.wast @@ -798,4 +798,56 @@ ;; catch_all runs the same local.set. So this can be dropped. (local.set $x (i32.const 1)) ) + + ;; CHECK: (func $try-delegate-across-try-table (type $0) + ;; CHECK-NEXT: (local $x i32) + ;; CHECK-NEXT: (try $l0 + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (block $catch + ;; CHECK-NEXT: (try_table (catch_all $catch) + ;; CHECK-NEXT: (try + ;; CHECK-NEXT: (do + ;; CHECK-NEXT: (throw $e + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (delegate $l0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (catch_all + ;; CHECK-NEXT: (local.set $x + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $try-delegate-across-try-table + (local $x i32) + (try $l0 + (do + (block $catch + (try_table (catch_all $catch) + (try + (do + (throw $e (i32.const 0)) + ) + (delegate $l0) + ) + ) + ) + ) + (catch_all + (local.set $x (i32.const 1)) + ) + ) + ;; The innermost try delegates to $l0 across a try_table, so the exception + ;; is caught by $l0's catch_all, which sets $x to 1. Thus this redundant set + ;; can be dropped. + (local.set $x (i32.const 1)) + ) ) diff --git a/test/lit/passes/stack-ir-eh-legacy.wast b/test/lit/passes/stack-ir-eh-legacy.wast index a8b30987538..23a65b0a148 100644 --- a/test/lit/passes/stack-ir-eh-legacy.wast +++ b/test/lit/passes/stack-ir-eh-legacy.wast @@ -1,10 +1,12 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. -;; RUN: wasm-opt %s --generate-stack-ir --optimize-stack-ir \ +;; RUN: wasm-opt %s --generate-stack-ir --optimize-stack-ir --optimize-level=3 \ ;; RUN: -all --print-stack-ir | filecheck %s (module ;; CHECK: (tag $e0 (type $0) (param i32)) (tag $e0 (param i32)) + ;; CHECK: (tag $e1 (type $0) (param i32)) + (tag $e1 (param i32)) ;; CHECK: (func $eh (type $1) ;; CHECK-NEXT: try $l0 @@ -56,4 +58,56 @@ (delegate 0) ;; delegate to caller ) ) + + ;; CHECK: (func $concrete-delegate (type $2) (result i32) + ;; CHECK-NEXT: (local $x i32) + ;; CHECK-NEXT: try (result i32) + ;; CHECK-NEXT: i32.const 42 + ;; CHECK-NEXT: delegate 0 + ;; CHECK-NEXT: ) + (func $concrete-delegate (result i32) + ;; A concrete-typed try-delegate whose result is consumed after local2Stack. + (local $x i32) + (local.set $x + (try (result i32) + (do + (i32.const 42) + ) + (delegate 0) + ) + ) + (local.get $x) + ) + + ;; CHECK: (func $delegate-in-catch (type $1) + ;; CHECK-NEXT: try + ;; CHECK-NEXT: i32.const 0 + ;; CHECK-NEXT: throw $e0 + ;; CHECK-NEXT: catch $e0 + ;; CHECK-NEXT: drop + ;; CHECK-NEXT: try + ;; CHECK-NEXT: delegate 1 + ;; CHECK-NEXT: catch $e1 + ;; CHECK-NEXT: drop + ;; CHECK-NEXT: end + ;; CHECK-NEXT: ) + (func $delegate-in-catch + ;; A try-delegate inside a catch block should properly pop catchIndexStack + ;; when printing StackIR so subsequent catches print the right tag. + (try + (do + (throw $e0 (i32.const 0)) + ) + (catch $e0 + (drop (pop i32)) + (try + (do) + (delegate 0) + ) + ) + (catch $e1 + (drop (pop i32)) + ) + ) + ) ) diff --git a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt index dc16f301d63..e119ac11323 100644 --- a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt +++ b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt @@ -1,93 +1,89 @@ Metrics total - [exports] : 61 - [funcs] : 125 + [exports] : 42 + [funcs] : 98 [globals] : 13 [imports] : 12 [memories] : 1 [memory-data] : 31 - [table-data] : 22 + [table-data] : 26 [tables] : 2 [tags] : 2 - [total] : 108273 - [vars] : 3618 - ArrayCmpxchg : 17 - ArrayCopy : 43 - ArrayFill : 41 - ArrayGet : 412 - ArrayLen : 577 - ArrayNew : 1248 - ArrayNewFixed : 333 - ArrayRMW : 15 - ArraySet : 73 - AtomicCmpxchg : 35 - AtomicFence : 74 - AtomicNotify : 13 - AtomicRMW : 37 - AtomicWait : 22 - Binary : 3873 - Block : 7253 - BrOn : 679 - Break : 1034 - Call : 788 - CallIndirect : 124 - CallRef : 268 - Const : 13838 - ContBind : 1 - ContNew : 103 - DataDrop : 16 - Drop : 852 - GlobalGet : 5460 - GlobalSet : 2141 - I31Get : 71 - If : 2575 - Load : 324 - LocalGet : 13305 - LocalSet : 4373 - Loop : 935 - MemoryCopy : 22 - MemoryFill : 23 - MemoryInit : 16 - Nop : 795 - Pop : 305 - RefAs : 10419 - RefCast : 742 - RefEq : 255 - RefFunc : 2089 - RefGetDesc : 91 - RefI31 : 647 - RefIsNull : 60 - RefNull : 11719 - RefTest : 60 - Return : 443 - SIMDExtract : 143 - SIMDLoad : 2 - SIMDShift : 4 - SIMDShuffle : 2 - SIMDTernary : 1 - Select : 344 - Store : 143 - StringConcat : 2 - StringConst : 419 - StringEncode : 71 - StringEq : 69 - StringMeasure : 76 - StringNew : 6 - StringWTF16Get : 80 - StructCmpxchg : 52 - StructGet : 495 - StructNew : 12790 - StructRMW : 48 - StructSet : 82 - Switch : 5 - TableSet : 78 - Throw : 72 - ThrowRef : 4 - Try : 430 - TryTable : 495 - TupleExtract : 226 - TupleMake : 236 - Unary : 2130 - Unreachable : 1094 - WideIntAddSub : 15 - WideIntMul : 20 + [total] : 51844 + [vars] : 3186 + ArrayCmpxchg : 15 + ArrayCopy : 24 + ArrayFill : 22 + ArrayGet : 227 + ArrayLen : 315 + ArrayNew : 626 + ArrayNewFixed : 176 + ArrayRMW : 9 + ArraySet : 35 + AtomicCmpxchg : 15 + AtomicFence : 39 + AtomicNotify : 5 + AtomicRMW : 13 + AtomicWait : 5 + Binary : 1921 + Block : 3625 + BrOn : 329 + Break : 539 + Call : 527 + CallIndirect : 44 + CallRef : 99 + Const : 6799 + ContNew : 58 + DataDrop : 4 + Drop : 443 + GlobalGet : 2648 + GlobalSet : 1044 + I31Get : 41 + If : 1304 + Load : 143 + LocalGet : 6906 + LocalSet : 2161 + Loop : 423 + MemoryCopy : 8 + MemoryFill : 8 + MemoryInit : 7 + Nop : 360 + Pop : 97 + RefAs : 4588 + RefCast : 374 + RefEq : 128 + RefFunc : 1000 + RefGetDesc : 34 + RefI31 : 318 + RefIsNull : 21 + RefNull : 4688 + RefTest : 33 + Return : 221 + SIMDExtract : 54 + SIMDShift : 1 + Select : 164 + Store : 73 + StringConst : 200 + StringEncode : 38 + StringEq : 35 + StringMeasure : 35 + StringNew : 4 + StringSliceWTF : 2 + StringWTF16Get : 36 + StructCmpxchg : 19 + StructGet : 234 + StructNew : 6141 + StructRMW : 21 + StructSet : 31 + Switch : 4 + TableSet : 29 + Throw : 37 + ThrowRef : 5 + Try : 184 + TryTable : 232 + TupleExtract : 114 + TupleMake : 107 + Unary : 1009 + Unreachable : 546 + WideIntAddSub : 12 + WideIntMul : 8