diff options
-rw-r--r-- | src/passes/StackIR.cpp | 44 | ||||
-rw-r--r-- | test/lit/passes/roundtrip-gc.wast | 37 | ||||
-rw-r--r-- | test/passes/Os_print-stack-ir_all-features_disable-gc.txt (renamed from test/passes/Os_print-stack-ir_all-features.txt) | 0 | ||||
-rw-r--r-- | test/passes/Os_print-stack-ir_all-features_disable-gc.wast (renamed from test/passes/Os_print-stack-ir_all-features.wast) | 0 |
4 files changed, 77 insertions, 4 deletions
diff --git a/src/passes/StackIR.cpp b/src/passes/StackIR.cpp index fbb990fe0..a3d9442bf 100644 --- a/src/passes/StackIR.cpp +++ b/src/passes/StackIR.cpp @@ -51,10 +51,14 @@ class StackIROptimizer { Function* func; PassOptions& passOptions; StackIR& insts; + FeatureSet features; public: - StackIROptimizer(Function* func, PassOptions& passOptions) - : func(func), passOptions(passOptions), insts(*func->stackIR.get()) { + StackIROptimizer(Function* func, + PassOptions& passOptions, + FeatureSet features) + : func(func), passOptions(passOptions), insts(*func->stackIR.get()), + features(features) { assert(func->stackIR); } @@ -65,7 +69,39 @@ public: if (passOptions.optimizeLevel >= 3 || passOptions.shrinkLevel >= 1) { local2Stack(); } - removeUnneededBlocks(); + // Removing unneeded blocks is dangerous with GC, as if we do this: + // + // (call + // (rtt) + // (block + // (nop) + // (i32) + // ) + // ) + // === remove inner block ==> + // (call + // (rtt) + // (nop) + // (i32) + // ) + // + // Then we end up with a nop that forces us to emit this during load: + // + // (call + // (block + // (local.set + // (rtt) + // ) + // (nop) + // (local.get) + // ) + // (i32) + // ) + // + // However, that is not valid as an rtt cannot be set to a local. + if (!features.hasGC()) { + removeUnneededBlocks(); + } dce(); } @@ -340,7 +376,7 @@ struct OptimizeStackIR : public WalkerPass<PostWalker<OptimizeStackIR>> { if (!func->stackIR) { return; } - StackIROptimizer(func, getPassOptions()).run(); + StackIROptimizer(func, getPassOptions(), getModule()->features).run(); } }; diff --git a/test/lit/passes/roundtrip-gc.wast b/test/lit/passes/roundtrip-gc.wast new file mode 100644 index 000000000..b26f32f8c --- /dev/null +++ b/test/lit/passes/roundtrip-gc.wast @@ -0,0 +1,37 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited. +;; RUN: wasm-opt %s -all --generate-stack-ir --optimize-stack-ir --roundtrip -S -o - | filecheck %s + +(module + (type ${i32} (struct (field i32))) + ;; CHECK: (export "export" (func $test)) + (export "export" (func $test)) + ;; CHECK: (func $test + ;; CHECK-NEXT: (call $help + ;; CHECK-NEXT: (rtt.canon $\7bi32\7d) + ;; CHECK-NEXT: (block $label$1 (result i32) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $test + (call $help + (rtt.canon ${i32}) + ;; Stack IR optimizations can remove this block, leaving a nop in an odd + ;; "stacky" location. On load, we would normally use a local to work around + ;; that, creating a block to contain the rtt before us and the nop, and then + ;; returning the local. But we can't use a local for an rtt, so we should not + ;; optimize this sort of thing in stack IR. + (block (result i32) + (nop) + (i32.const 1) + ) + ) + ) + ;; CHECK: (func $help (param $3 (rtt $\7bi32\7d)) (param $4 i32) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $help (param $3 (rtt ${i32})) (param $4 i32) + (nop) + ) +) diff --git a/test/passes/Os_print-stack-ir_all-features.txt b/test/passes/Os_print-stack-ir_all-features_disable-gc.txt index 7184df236..7184df236 100644 --- a/test/passes/Os_print-stack-ir_all-features.txt +++ b/test/passes/Os_print-stack-ir_all-features_disable-gc.txt diff --git a/test/passes/Os_print-stack-ir_all-features.wast b/test/passes/Os_print-stack-ir_all-features_disable-gc.wast index 5c03b5e23..5c03b5e23 100644 --- a/test/passes/Os_print-stack-ir_all-features.wast +++ b/test/passes/Os_print-stack-ir_all-features_disable-gc.wast |