Skip to content

Commit 3ae83ab

Browse files
authored
[Custom Descriptors] CFP: Fix nullable descriptors (#7928)
We can write a null (it would trap, but it looks like a write in the opt), but cannot read a null, so we must cast.
1 parent 3296e60 commit 3ae83ab

File tree

2 files changed

+48
-0
lines changed

2 files changed

+48
-0
lines changed

src/passes/ConstantFieldPropagation.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,15 @@ struct FunctionOptimizer : public WalkerPass<PostWalker<FunctionOptimizer>> {
220220

221221
void visitRefGetDesc(RefGetDesc* curr) {
222222
optimizeRead(curr, curr->ref, StructUtils::DescriptorIndex);
223+
224+
// RefGetDesc has the interesting property that we can write a value into
225+
// the field that cannot be read from it: it is valid to write a null, but
226+
// a null can never be read (it would have trapped on the write). Fix that
227+
// up as needed to not break validation.
228+
if (!Type::isSubType(getCurrent()->type, curr->type)) {
229+
Builder builder(*getModule());
230+
replaceCurrent(builder.makeRefAs(RefAsNonNull, getCurrent()));
231+
}
223232
}
224233

225234
void optimizeRead(Expression* curr,
@@ -485,6 +494,10 @@ struct PCVScanner
485494
Index index,
486495
PossibleConstantValues& info) {
487496
info.note(expr, *getModule());
497+
// TODO: For descriptors we can ignore nullable values that are written, as
498+
// they trap. That is, if one place writes a null and another writes a
499+
// global, only the global is readable, and we can optimize there -
500+
// the null is not a second value.
488501
}
489502

490503
void noteDefault(Type fieldType,

test/lit/passes/cfp-desc.wast

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,3 +403,38 @@
403403
)
404404
)
405405

406+
(module
407+
(rec
408+
;; CHECK: (rec
409+
;; CHECK-NEXT: (type $A (descriptor $B (struct)))
410+
(type $A (descriptor $B (struct)))
411+
;; CHECK: (type $B (describes $A (struct)))
412+
(type $B (describes $A (struct)))
413+
)
414+
;; CHECK: (type $2 (func (result (ref (exact $B)))))
415+
416+
;; CHECK: (func $test (type $2) (result (ref (exact $B)))
417+
;; CHECK-NEXT: (ref.as_non_null
418+
;; CHECK-NEXT: (block (result nullref)
419+
;; CHECK-NEXT: (drop
420+
;; CHECK-NEXT: (ref.as_non_null
421+
;; CHECK-NEXT: (struct.new_default $A
422+
;; CHECK-NEXT: (ref.null none)
423+
;; CHECK-NEXT: )
424+
;; CHECK-NEXT: )
425+
;; CHECK-NEXT: )
426+
;; CHECK-NEXT: (ref.null none)
427+
;; CHECK-NEXT: )
428+
;; CHECK-NEXT: )
429+
;; CHECK-NEXT: )
430+
(func $test (result (ref (exact $B)))
431+
;; We need to add a ref.as_non_null on the descriptor that is read, as the
432+
;; function result is non-nullable.
433+
(ref.get_desc $A
434+
(struct.new_default $A
435+
(ref.null none)
436+
)
437+
)
438+
)
439+
)
440+

0 commit comments

Comments
 (0)