Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion src/ir/properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -530,8 +530,18 @@ inline MemoryOrder getMemoryOrder(Expression* curr) {

// Whether this instruction will be unwritable in the text and binary formats
// because it requires a type index immediate computed from an expression with
// unreachable or null type, and therefore no type index.
// unreachable or null type, and therefore no type index (or is a string array
// operation with a null array operand).
inline bool hasUnwritableTypeImmediate(Expression* curr) {
if (auto* sn = curr->dynCast<StringNew>()) {
// See BinaryInstWriter::visitStringNew.
return sn->ref->type.isBottomRef();
}
if (auto* se = curr->dynCast<StringEncode>()) {
// See BinaryInstWriter::visitStringEncode.
return se->array->type.isBottomRef();
}
Comment thread
tlively marked this conversation as resolved.

#define DELEGATE_ID curr->_id

#define DELEGATE_FIELD_IMMEDIATE_TYPED_CHILD(id, field) \
Expand Down
11 changes: 10 additions & 1 deletion src/wasm/wasm-stack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3032,7 +3032,7 @@ void BinaryInstWriter::visitStringMeasure(StringMeasure* curr) {
}

void BinaryInstWriter::visitStringEncode(StringEncode* curr) {
if (curr->str->type.isBottomRef()) {
if (curr->array->type.isBottomRef()) {
// See visitStringNew.
emitUnreachable();
return;
Expand Down Expand Up @@ -3642,6 +3642,15 @@ class StackIRGenerator : public BinaryenIRWriter<StackIRGenerator> {
};

void StackIRGenerator::emit(Expression* curr) {
// Match BinaryInstWriter (and PrintSExpression::visitExpression), which emits
// unreachable instead of instructions whose type immediate cannot be written
// (see BinaryInstWriter::visitCallRef, visitRefCast, visitStructGet, etc.) or
// string array operations with null-typed array operands (see
// BinaryInstWriter::visitStringNew and visitStringEncode).
if (Properties::hasUnwritableTypeImmediate(curr)) {
emitUnreachable();
return;
}
StackInst* stackInst = nullptr;
if (curr->is<Block>()) {
stackInst = makeStackInst(StackInst::BlockBegin, curr);
Expand Down
26 changes: 20 additions & 6 deletions test/lit/basic/unreachable-string-new.wast
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,18 @@
)

;; CHECK: (func $i8-ok-none (type $4) (result (ref string))
;; CHECK-NEXT: (string.new_lossy_utf8_array
;; CHECK-NEXT: (block (result (ref none))
;; CHECK-NEXT: (block ;; (replaces unreachable StringNew we can't emit)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result (ref none))
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
Expand Down Expand Up @@ -219,11 +226,18 @@
)

;; CHECK: (func $i16-ok-none (type $4) (result (ref string))
;; CHECK-NEXT: (string.new_wtf16_array
;; CHECK-NEXT: (block (result (ref none))
;; CHECK-NEXT: (block ;; (replaces unreachable StringNew we can't emit)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result (ref none))
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
Expand Down
75 changes: 70 additions & 5 deletions test/lit/passes/optimize-stack-ir.wast
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

;; CHECK: (type $FUNCSIG$ddd (func (param f64 f64) (result f64)))

;; CHECK: (type $8 (func (result i64)))

;; CHECK: (type $10 (func (param f32) (result f32)))

;; CHECK: (type $FUNCSIG$id (func (param f64) (result i32)))
Expand All @@ -26,14 +28,15 @@
(type $6 (func (param i32) (result i32)))
;; CHECK: (type $7 (func (param f64) (result f64)))
(type $7 (func (param f64) (result f64)))
;; CHECK: (type $8 (func (result i64)))
(type $8 (func (result i64)))
;; CHECK: (type $9 (func (param i32 i64)))
(type $9 (func (param i32 i64)))

;; CHECK: (type $15 (func (param eqref) (result i32)))
;; CHECK: (type $14 (func (param eqref) (result i32)))

;; CHECK: (type $15 (func (param eqref) (result eqref)))

;; CHECK: (type $16 (func (param eqref) (result eqref)))
;; CHECK: (type $16 (func (param stringref) (result i64)))

;; CHECK: (import "env" "_emscripten_asm_const_vi" (func $_emscripten_asm_const_vi (type $FUNCSIG$v)))
(import "env" "_emscripten_asm_const_vi" (func $_emscripten_asm_const_vi))
Expand Down Expand Up @@ -1485,7 +1488,7 @@
)

;; A ref.eq with an intervening value: its operands can be swapped.
;; CHECK: (func $local-to-stack-ref-eq (type $15) (param $x eqref) (result i32)
;; CHECK: (func $local-to-stack-ref-eq (type $14) (param $x eqref) (result i32)
;; CHECK-NEXT: (local $temp eqref)
;; CHECK-NEXT: local.get $x
;; CHECK-NEXT: call $eqref-value
Expand All @@ -1505,7 +1508,7 @@
)
)

;; CHECK: (func $eqref-value (type $16) (param $x eqref) (result eqref)
;; CHECK: (func $eqref-value (type $15) (param $x eqref) (result eqref)
;; CHECK-NEXT: local.get $x
;; CHECK-NEXT: call $eqref-value
;; CHECK-NEXT: )
Expand Down Expand Up @@ -1571,4 +1574,66 @@
)
)
)

;; CHECK: (func $null-string-new (type $8) (result i64)
;; CHECK-NEXT: (local $y i64)
;; CHECK-NEXT: i64.const 1
;; CHECK-NEXT: local.set $y
;; CHECK-NEXT: ref.null none
;; CHECK-NEXT: ref.as_non_null
;; CHECK-NEXT: i32.const 0
;; CHECK-NEXT: i32.const 0
;; CHECK-NEXT: unreachable
;; CHECK-NEXT: )
(func $null-string-new (result i64)
(local $y i64)
;; Previously, local2Stack thought the un-DCE'd drop and
;; string.new_wtf16_array below consumed all three operands and returned the
;; stack to this height, so it wrongly eliminated this local.set and the
;; final local.get $y.
(local.set $y
(i64.const 1)
)
(drop
;; Has a null-typed array ref, so the binary writer emits unreachable
;; instead of consuming its three operands. StackIRGenerator must also
;; emit unreachable so dce removes the drop and local.get $y below.
(string.new_wtf16_array
(ref.as_non_null
(ref.null none)
)
(i32.const 0)
(i32.const 0)
)
)
(local.get $y)
)

;; CHECK: (func $null-string-encode (type $16) (param $str stringref) (result i64)
;; CHECK-NEXT: (local $y i64)
;; CHECK-NEXT: i64.const 1
;; CHECK-NEXT: local.set $y
;; CHECK-NEXT: local.get $str
;; CHECK-NEXT: ref.null none
;; CHECK-NEXT: ref.as_non_null
;; CHECK-NEXT: i32.const 0
;; CHECK-NEXT: unreachable
;; CHECK-NEXT: )
(func $null-string-encode (param $str stringref) (result i64)
(local $y i64)
(local.set $y
(i64.const 1)
)
(drop
;; Same as above, for string.encode_wtf16_array with a null-typed array.
(string.encode_wtf16_array
(local.get $str)
(ref.as_non_null
(ref.null none)
)
(i32.const 0)
)
)
(local.get $y)
)
)
44 changes: 44 additions & 0 deletions test/lit/strings.wast
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@

;; CHECK: (type $8 (func (param stringref (ref $array) (ref $array16))))

;; CHECK: (type $9 (func (param stringref i32) (result i32)))

;; CHECK: (import "env" "get-string-ref" (func $get-string-ref (type $4) (result externref)))
(import "env" "get-string-ref" (func $get-string-ref (result externref)))

Expand Down Expand Up @@ -202,6 +204,13 @@
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (string.encode_wtf16_array
;; CHECK-NEXT: (ref.null noextern)
;; CHECK-NEXT: (local.get $array16)
;; CHECK-NEXT: (i32.const 30)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $string.encode.gc (param $ref stringref) (param $array (ref $array)) (param $array16 (ref $array16))
(drop
Expand All @@ -220,6 +229,41 @@
(i32.const 10)
)
)
;; A null string reference is still a valid string.encode input and should
;; not be replaced with unreachable.
(drop
(string.encode_wtf16_array
(ref.null noextern)
(local.get $array16)
(i32.const 30)
)
)
)

;; CHECK: (func $string.encode.null-array (type $9) (param $ref stringref) (param $start i32) (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.as_non_null
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $start)
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $string.encode.null-array (param $ref stringref) (param $start i32) (result i32)
;; A null array reference has no array type and is replaced with
;; unreachable.
(string.encode_wtf16_array
(local.get $ref)
(ref.as_non_null
(ref.null none)
)
(local.get $start)
)
)

;; CHECK: (func $string.from_code_point (type $3)
Expand Down
Loading