Add ref.cast_nop_static (#4656)
This unsafe experimental instruction is semantically equivalent to
ref.cast_static, but V8 will unsafely turn it into a nop. This is meant to help
us measure cast overhead more precisely than we can by globally turning all
casts into nops.
diff --git a/scripts/gen-s-parser.py b/scripts/gen-s-parser.py
index 29ce838..5576960 100755
--- a/scripts/gen-s-parser.py
+++ b/scripts/gen-s-parser.py
@@ -573,6 +573,7 @@
("ref.test_static", "makeRefTestStatic(s)"),
("ref.cast", "makeRefCast(s)"),
("ref.cast_static", "makeRefCastStatic(s)"),
+ ("ref.cast_nop_static", "makeRefCastNopStatic(s)"),
("br_on_null", "makeBrOn(s, BrOnNull)"),
("br_on_non_null", "makeBrOn(s, BrOnNonNull)"),
("br_on_cast", "makeBrOn(s, BrOnCast)"),
diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc
index d9505bc..4111969 100644
--- a/src/gen-s-parser.inc
+++ b/src/gen-s-parser.inc
@@ -3027,9 +3027,17 @@
case '\0':
if (strcmp(op, "ref.cast") == 0) { return makeRefCast(s); }
goto parse_error;
- case '_':
- if (strcmp(op, "ref.cast_static") == 0) { return makeRefCastStatic(s); }
- goto parse_error;
+ case '_': {
+ switch (op[9]) {
+ case 'n':
+ if (strcmp(op, "ref.cast_nop_static") == 0) { return makeRefCastNopStatic(s); }
+ goto parse_error;
+ case 's':
+ if (strcmp(op, "ref.cast_static") == 0) { return makeRefCastStatic(s); }
+ goto parse_error;
+ default: goto parse_error;
+ }
+ }
default: goto parse_error;
}
}
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index 9a4235c..ce70f50 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -1984,7 +1984,11 @@
if (curr->rtt) {
printMedium(o, "ref.cast");
} else {
- printMedium(o, "ref.cast_static ");
+ if (curr->safety == RefCast::Unsafe) {
+ printMedium(o, "ref.cast_nop_static ");
+ } else {
+ printMedium(o, "ref.cast_static ");
+ }
printHeapType(o, curr->intendedType, wasm);
}
}
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index 12deeac..58dcd22 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -1112,6 +1112,7 @@
RefCastStatic = 0x45,
BrOnCastStatic = 0x46,
BrOnCastStaticFail = 0x47,
+ RefCastNopStatic = 0x48,
RefIsFunc = 0x50,
RefIsData = 0x51,
RefIsI31 = 0x52,
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index de26886..86de1f7 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -810,10 +810,13 @@
ret->finalize();
return ret;
}
- RefCast* makeRefCast(Expression* ref, HeapType intendedType) {
+
+ RefCast*
+ makeRefCast(Expression* ref, HeapType intendedType, RefCast::Safety safety) {
auto* ret = wasm.allocator.alloc<RefCast>();
ret->ref = ref;
ret->intendedType = intendedType;
+ ret->safety = safety;
ret->finalize();
return ret;
}
diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h
index d6f3b2f..61383f0 100644
--- a/src/wasm-s-parser.h
+++ b/src/wasm-s-parser.h
@@ -282,6 +282,7 @@
Expression* makeRefTestStatic(Element& s);
Expression* makeRefCast(Element& s);
Expression* makeRefCastStatic(Element& s);
+ Expression* makeRefCastNopStatic(Element& s);
Expression* makeBrOn(Element& s, BrOnOp op);
Expression* makeBrOnStatic(Element& s, BrOnOp op);
Expression* makeRttCanon(Element& s);
diff --git a/src/wasm.h b/src/wasm.h
index 3b736fc..b8e3d67 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -1457,6 +1457,11 @@
Expression* rtt = nullptr;
HeapType intendedType;
+ // Support the unsafe `ref.cast_nop_static` to enable precise cast overhead
+ // measurements.
+ enum Safety { Safe, Unsafe };
+ Safety safety;
+
void finalize();
// Returns the type we intend to cast to.
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index 60a4bad..9cd7e79 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -6712,10 +6712,13 @@
auto* ref = popNonVoidExpression();
out = Builder(wasm).makeRefCast(ref, rtt);
return true;
- } else if (code == BinaryConsts::RefCastStatic) {
+ } else if (code == BinaryConsts::RefCastStatic ||
+ code == BinaryConsts::RefCastNopStatic) {
auto intendedType = getIndexedHeapType();
auto* ref = popNonVoidExpression();
- out = Builder(wasm).makeRefCast(ref, intendedType);
+ auto safety =
+ code == BinaryConsts::RefCastNopStatic ? RefCast::Unsafe : RefCast::Safe;
+ out = Builder(wasm).makeRefCast(ref, intendedType, safety);
return true;
}
return false;
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index 02da4f9..8680ff8 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -2702,7 +2702,13 @@
Expression* SExpressionWasmBuilder::makeRefCastStatic(Element& s) {
auto heapType = parseHeapType(*s[1]);
auto* ref = parseExpression(*s[2]);
- return Builder(wasm).makeRefCast(ref, heapType);
+ return Builder(wasm).makeRefCast(ref, heapType, RefCast::Safe);
+}
+
+Expression* SExpressionWasmBuilder::makeRefCastNopStatic(Element& s) {
+ auto heapType = parseHeapType(*s[1]);
+ auto* ref = parseExpression(*s[2]);
+ return Builder(wasm).makeRefCast(ref, heapType, RefCast::Unsafe);
}
Expression* SExpressionWasmBuilder::makeBrOn(Element& s, BrOnOp op) {
diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp
index 8afeb67..8588a04 100644
--- a/src/wasm/wasm-stack.cpp
+++ b/src/wasm/wasm-stack.cpp
@@ -2051,7 +2051,11 @@
if (curr->rtt) {
o << U32LEB(BinaryConsts::RefCast);
} else {
- o << U32LEB(BinaryConsts::RefCastStatic);
+ if (curr->safety == RefCast::Unsafe) {
+ o << U32LEB(BinaryConsts::RefCastNopStatic);
+ } else {
+ o << U32LEB(BinaryConsts::RefCastStatic);
+ }
parent.writeIndexedHeapType(curr->intendedType);
}
}
diff --git a/test/lit/ref-cast-nop.wast b/test/lit/ref-cast-nop.wast
new file mode 100644
index 0000000..6e49508
--- /dev/null
+++ b/test/lit/ref-cast-nop.wast
@@ -0,0 +1,18 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
+
+;; RUN: wasm-opt -all %s -S --roundtrip -o - | filecheck %s
+
+(module
+ ;; CHECK: (type $struct (struct (field i32)))
+ (type $struct (struct i32))
+ ;; CHECK: (func $ref.cast_nop_static (param $x (ref any)) (result (ref $struct))
+ ;; CHECK-NEXT: (ref.cast_nop_static $struct
+ ;; CHECK-NEXT: (local.get $x)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $ref.cast_nop_static (param $x (ref any)) (result (ref $struct))
+ (ref.cast_nop_static $struct
+ (local.get $x)
+ )
+ )
+)