diff options
-rw-r--r-- | src/passes/GlobalStructInference.cpp | 38 | ||||
-rw-r--r-- | test/lit/passes/gsi-debug.wast | 73 |
2 files changed, 96 insertions, 15 deletions
diff --git a/src/passes/GlobalStructInference.cpp b/src/passes/GlobalStructInference.cpp index 0ddbe9b0f..7526cdb76 100644 --- a/src/passes/GlobalStructInference.cpp +++ b/src/passes/GlobalStructInference.cpp @@ -50,6 +50,7 @@ #include <variant> +#include "ir/debuginfo.h" #include "ir/find_all.h" #include "ir/module-utils.h" #include "ir/names.h" @@ -417,29 +418,36 @@ struct GlobalStructInference : public Pass { // Helper for optimization: Given a Value, returns what we should read // for it. auto getReadValue = [&](const Value& value) -> Expression* { + Expression* ret; if (value.isConstant()) { // This is known to be a constant, so simply emit an expression for // that constant. - return value.getConstant().makeExpression(wasm); - } + ret = value.getConstant().makeExpression(wasm); + } else { + // Otherwise, this is non-constant, so we are in the situation where + // we want to un-nest the value out of the struct.new it is in. Note + // that for later work, as we cannot add a global in parallel. - // Otherwise, this is non-constant, so we are in the situation where - // we want to un-nest the value out of the struct.new it is in. Note - // that for later work, as we cannot add a global in parallel. + // There can only be one global in a value that is not constant, + // which is the global we want to read from. + assert(value.globals.size() == 1); - // There can only be one global in a value that is not constant, which - // is the global we want to read from. - assert(value.globals.size() == 1); + // Create a global.get with temporary name, leaving only the + // updating of the name to later work. + auto* get = builder.makeGlobalGet(value.globals[0], + value.getExpression()->type); - // Create a global.get with temporary name, leaving only the updating - // of the name to later work. - auto* get = builder.makeGlobalGet(value.globals[0], - value.getExpression()->type); + globalsToUnnest.emplace_back( + GlobalToUnnest{value.globals[0], fieldIndex, get}); + + ret = get; + } - globalsToUnnest.emplace_back( - GlobalToUnnest{value.globals[0], fieldIndex, get}); + // This value replaces the struct.get, so it should have the same + // source location. + debuginfo::copyOriginalToReplacement(curr, ret, getFunction()); - return get; + return ret; }; // We have some globals (at least 2), and so must have at least one diff --git a/test/lit/passes/gsi-debug.wast b/test/lit/passes/gsi-debug.wast new file mode 100644 index 000000000..a55ea3e8e --- /dev/null +++ b/test/lit/passes/gsi-debug.wast @@ -0,0 +1,73 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. +;; RUN: env BINARYEN_PRINT_FULL=1 foreach %s %t wasm-opt --gsi -all --closed-world -S -o - | filecheck %s + +;; Test that debug info is copied to the values we replace a struct.get with. +;; We use BINARYEN_PRINT_FULL=1 here because the select that contains the +;; values also gets that debug info, and normally we omit debug info of children +;; when it matches the parent (so we could not tell without +;; BINARYEN_PRINT_FULL=1 whether the children had the debug info or not). +;; (Another way to test this would be to run a followup optimization to remove +;; the select, but that would be more complex.) + +(module + ;; CHECK: (type $struct (struct (field i32))) + (type $struct (struct i32)) + + ;; CHECK: (type $1 (func (param (ref null $struct)))) + + ;; CHECK: (global $global1 (ref $struct) (struct.new $struct + ;; CHECK-NEXT: (i32.const 42) (; i32 ;) + ;; CHECK-NEXT: )) + (global $global1 (ref $struct) (struct.new $struct + (i32.const 42) + )) + + ;; CHECK: (global $global2 (ref $struct) (struct.new $struct + ;; CHECK-NEXT: (i32.const 1337) (; i32 ;) + ;; CHECK-NEXT: )) + (global $global2 (ref $struct) (struct.new $struct + (i32.const 1337) + )) + + ;; A non-reference global does not confuse us. + ;; CHECK: (global $global-other i32 (i32.const 123456)) + (global $global-other i32 (i32.const 123456)) + + ;; CHECK: (func $test (type $1) (param $struct (ref null $struct)) + ;; CHECK-NEXT: ;;@ drop.c:10:1 + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: ;;@ struct.c:20:2 + ;; CHECK-NEXT: (select + ;; CHECK-NEXT: ;;@ struct.c:20:2 + ;; CHECK-NEXT: (i32.const 42) (; i32 ;) + ;; CHECK-NEXT: ;;@ struct.c:20:2 + ;; CHECK-NEXT: (i32.const 1337) (; i32 ;) + ;; CHECK-NEXT: ;;@ + ;; CHECK-NEXT: (ref.eq + ;; CHECK-NEXT: ;;@ + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: ;;@ local.c:30:3 + ;; CHECK-NEXT: (local.get $struct) (; struct null ;) + ;; CHECK-NEXT: ) (; struct ;) + ;; CHECK-NEXT: ;;@ + ;; CHECK-NEXT: (global.get $global1) (; struct ;) + ;; CHECK-NEXT: ) (; i32 ;) + ;; CHECK-NEXT: ) (; i32 ;) + ;; CHECK-NEXT: ) (; none ;) + ;; CHECK-NEXT: ) + (func $test (param $struct (ref null $struct)) + ;; We can infer that this get can reference either $global1 or $global2, + ;; and nothing else (aside from a null), and can emit a select between + ;; those values. While doing so we copy the debug info as well to the + ;; values in the select. + ;;@ drop.c:10:1 + (drop + ;;@ struct.c:20:2 + (struct.get $struct 0 + ;;@ local.c:30:3 + (local.get $struct) + ) + ) + ) +) + |