Skip to content

Commit 64f128f

Browse files
authored
[GTO] Avoid newly exposing prototypes when supertype fields are removed (#9278)
When GlobalTypeOptimization removes all fields from a non-empty supertype descriptor, a subtype descriptor whose unread supertype fields are also removed can shift a subsequent externref field to index 0. Check whether the supertype retained any kept fields after removal (!superHasKeptFields) rather than checking whether the unoptimized supertype had empty fields (!super->getStruct().fields.empty()) when deciding whether to insert an i8 placeholder at index 0 for exposedNoProtoDescs.
1 parent ed1fb9d commit 64f128f

2 files changed

Lines changed: 66 additions & 3 deletions

File tree

‎src/passes/GlobalTypeOptimization.cpp‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,7 @@ struct GlobalTypeOptimization : public Pass {
372372
// That is, we can only append fields: we cannot reorder or remove any
373373
// field that is in the super.
374374
Index numSuperFields = 0;
375+
bool superHasKeptFields = false;
375376
if (super) {
376377
// We have visited the super before. Get the information about its
377378
// fields.
@@ -408,6 +409,7 @@ struct GlobalTypeOptimization : public Pass {
408409
// The super kept this field, so we must keep it as well. This can
409410
// happen when we need the field in both, but also in the corner
410411
// case where we don't need the field but the super is public.
412+
superHasKeptFields = true;
411413

412414
// We need to keep it at the same index so we remain compatible.
413415
indexesAfterRemoval[i] = superIndex;
@@ -433,10 +435,10 @@ struct GlobalTypeOptimization : public Pass {
433435
}
434436
}
435437

436-
// If the type has no supertype (or its supertype has no fields), check
437-
// if its first field becomes prototype-exposing. If so, add a
438+
// If the type has no supertype (or its supertype has no kept fields),
439+
// check if its first field becomes prototype-exposing. If so, add a
438440
// placeholder at index 0 and shift all computed indices.
439-
if (isExposedNoProto && (!super || super->getStruct().fields.empty())) {
441+
if (isExposedNoProto && !superHasKeptFields) {
440442
// Find the field that will become field 0.
441443
Index i = 0;
442444
for (; i < fields.size(); ++i) {

‎test/lit/passes/gto-jsinterop.wast‎

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1753,3 +1753,64 @@
17531753
)
17541754
)
17551755
)
1756+
1757+
(module
1758+
;; All fields of $super-desc are unread and removed by GTO, leaving
1759+
;; $super-desc empty after optimization. In $sub-desc, the unread supertype
1760+
;; field is also removed and the following externref field shifts to index 0,
1761+
;; so $sub-desc must get an i8 placeholder at index 0 to avoid exposing a
1762+
;; prototype.
1763+
(rec
1764+
;; CHECK: (rec
1765+
;; CHECK-NEXT: (type $super (sub (descriptor $super-desc) (struct)))
1766+
(type $super (sub (descriptor $super-desc) (struct)))
1767+
;; CHECK: (type $super-desc (sub (describes $super) (struct)))
1768+
(type $super-desc (sub (describes $super) (struct (field f64))))
1769+
;; CHECK: (type $sub (sub final $super (descriptor $sub-desc) (struct)))
1770+
(type $sub (sub final $super (descriptor $sub-desc) (struct)))
1771+
;; CHECK: (type $sub-desc (sub final $super-desc (describes $sub) (struct (field i8) (field externref))))
1772+
(type $sub-desc (sub final $super-desc (describes $sub) (struct (field f64) (field externref))))
1773+
)
1774+
1775+
;; CHECK: (type $4 (func (param (ref $sub-desc)) (result externref)))
1776+
1777+
;; CHECK: (type $5 (func (result externref)))
1778+
1779+
;; CHECK: (import "env" "ext" (global $ext externref))
1780+
(import "env" "ext" (global $ext externref))
1781+
;; CHECK: (export "export" (func $test))
1782+
(export "export" (func $test))
1783+
1784+
;; CHECK: (func $test (type $5) (result externref)
1785+
;; CHECK-NEXT: (extern.convert_any
1786+
;; CHECK-NEXT: (struct.new_default_desc $sub
1787+
;; CHECK-NEXT: (struct.new $sub-desc
1788+
;; CHECK-NEXT: (i32.const 0)
1789+
;; CHECK-NEXT: (global.get $ext)
1790+
;; CHECK-NEXT: )
1791+
;; CHECK-NEXT: )
1792+
;; CHECK-NEXT: )
1793+
;; CHECK-NEXT: )
1794+
(func $test (result externref)
1795+
;; Convert to extern to expose the value to JS.
1796+
(extern.convert_any
1797+
(struct.new_default_desc $sub
1798+
(struct.new $sub-desc
1799+
(f64.const 0)
1800+
(global.get $ext)
1801+
)
1802+
)
1803+
)
1804+
)
1805+
1806+
;; CHECK: (func $use (type $4) (param $d (ref $sub-desc)) (result externref)
1807+
;; CHECK-NEXT: (struct.get $sub-desc 1
1808+
;; CHECK-NEXT: (local.get $d)
1809+
;; CHECK-NEXT: )
1810+
;; CHECK-NEXT: )
1811+
(func $use (param $d (ref $sub-desc)) (result externref)
1812+
(struct.get $sub-desc 1
1813+
(local.get $d)
1814+
)
1815+
)
1816+
)

0 commit comments

Comments
 (0)