summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/passes/GlobalStructInference.cpp38
-rw-r--r--test/lit/passes/gsi-debug.wast73
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)
+ )
+ )
+ )
+)
+