Bug 1401675 - Baldr: factor out OpIter::checkIsSubtype() (r=lth)
authorLuke Wagner <luke@mozilla.com>
Tue, 12 Feb 2019 09:42:55 -0600
changeset 458710 d29b60b57b23
parent 458709 6ae6def2e6ed
child 458711 a90e2dd8b1a5
push id35546
push userrmaries@mozilla.com
push dateWed, 13 Feb 2019 04:27:59 +0000
treeherdermozilla-central@636d2c00234d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslth
bugs1401675
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1401675 - Baldr: factor out OpIter::checkIsSubtype() (r=lth)
js/src/wasm/WasmBaselineCompile.cpp
js/src/wasm/WasmOpIter.h
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -9550,17 +9550,17 @@ bool BaseCompiler::emitSelect() {
   }
 
   // I32 condition on top, then false, then true.
 
   Label done;
   BranchState b(&done);
   emitBranchSetup(&b);
 
-  switch (NonAnyToValType(type).code()) {
+  switch (NonTVarToValType(type).code()) {
     case ValType::I32: {
       RegI32 r, rs;
       pop2xI32(&r, &rs);
       emitBranchPerform(&b);
       moveI32(rs, r);
       masm.bind(&done);
       freeI32(rs);
       pushI32(r);
--- a/js/src/wasm/WasmOpIter.h
+++ b/js/src/wasm/WasmOpIter.h
@@ -99,17 +99,17 @@ class StackType {
   bool operator==(Code that) const {
     MOZ_ASSERT(that != Code::Ref);
     return code() == that;
   }
 
   bool operator!=(Code that) const { return !(*this == that); }
 };
 
-static inline ValType NonAnyToValType(StackType type) {
+static inline ValType NonTVarToValType(StackType type) {
   MOZ_ASSERT(type != StackType::TVar);
   return ValType(type.packed());
 }
 
 #ifdef DEBUG
 // Families of opcodes that share a signature and validation logic.
 enum class OpKind {
   Block,
@@ -286,22 +286,19 @@ class MOZ_STACK_CLASS OpIter : private P
   MOZ_MUST_USE bool readBlockType(ExprType* expr);
   MOZ_MUST_USE bool readStructTypeIndex(uint32_t* typeIndex);
   MOZ_MUST_USE bool readFieldIndex(uint32_t* fieldIndex,
                                    const StructType& structType);
 
   MOZ_MUST_USE bool popCallArgs(const ValTypeVector& expectedTypes,
                                 Vector<Value, 8, SystemAllocPolicy>* values);
 
-  MOZ_MUST_USE bool popAnyType(StackType* type, Value* value);
-  MOZ_MUST_USE bool typeMismatch(StackType actual, StackType expected);
-  MOZ_MUST_USE bool popWithType(StackType expectedType, Value* value);
-  MOZ_MUST_USE bool popWithType(ValType valType, Value* value) {
-    return popWithType(StackType(valType), value);
-  }
+  MOZ_MUST_USE bool failEmptyStack();
+  MOZ_MUST_USE bool popStackType(StackType* type, Value* value);
+  MOZ_MUST_USE bool popWithType(ValType valType, Value* value);
   MOZ_MUST_USE bool popWithType(ExprType expectedType, Value* value);
   MOZ_MUST_USE bool topWithType(ExprType expectedType, Value* value);
   MOZ_MUST_USE bool topWithType(ValType valType, Value* value);
 
   MOZ_MUST_USE bool pushControl(LabelKind kind, ExprType type);
   MOZ_MUST_USE bool checkStackAtEndOfBlock(ExprType* type, Value* value);
   MOZ_MUST_USE bool getControl(uint32_t relativeDepth,
                                ControlStackEntry<ControlItem>** controlEntry);
@@ -327,17 +324,18 @@ class MOZ_STACK_CLASS OpIter : private P
     valueStack_.infallibleAppend(tv);
   }
 
   void afterUnconditionalBranch() {
     valueStack_.shrinkTo(controlStack_.back().valueStackStart());
     controlStack_.back().setPolymorphicBase();
   }
 
-  inline bool Join(StackType one, StackType two, StackType* result);
+  inline bool Join(StackType one, StackType two, StackType* result) const;
+  inline bool checkIsSubtypeOf(ValType lhs, ValType rhs);
 
  public:
   typedef Vector<Value, 8, SystemAllocPolicy> ValueVector;
 
 #ifdef DEBUG
   explicit OpIter(const ModuleEnvironment& env, Decoder& decoder)
       : d_(decoder),
         env_(env),
@@ -511,52 +509,73 @@ class MOZ_STACK_CLASS OpIter : private P
 
   // Test whether the control-stack is empty, meaning we've consumed the final
   // end of the function body.
   bool controlStackEmpty() const { return controlStack_.empty(); }
 };
 
 template <typename Policy>
 inline bool OpIter<Policy>::Join(StackType one, StackType two,
-                                 StackType* result) {
+                                 StackType* result) const {
   if (MOZ_LIKELY(one == two)) {
     *result = one;
     return true;
   }
 
   if (one == StackType::TVar) {
     *result = two;
     return true;
   }
 
   if (two == StackType::TVar) {
     *result = one;
     return true;
   }
 
-  if (env_.gcTypesEnabled() && one.isReference() && two.isReference()) {
-    if (env_.isRefSubtypeOf(NonAnyToValType(two), NonAnyToValType(one))) {
+  if (one.isReference() && two.isReference()) {
+    if (env_.isRefSubtypeOf(NonTVarToValType(two), NonTVarToValType(one))) {
       *result = one;
       return true;
     }
 
-    if (env_.isRefSubtypeOf(NonAnyToValType(one), NonAnyToValType(two))) {
+    if (env_.isRefSubtypeOf(NonTVarToValType(one), NonTVarToValType(two))) {
       *result = two;
       return true;
     }
 
     // No subtyping relations between the two types.
     *result = StackType::AnyRef;
     return true;
   }
 
   return false;
 }
 
 template <typename Policy>
+inline bool OpIter<Policy>::checkIsSubtypeOf(ValType actual, ValType expected) {
+  if (actual == expected) {
+    return true;
+  }
+
+  if (actual.isReference() && expected.isReference() &&
+      env_.isRefSubtypeOf(actual, expected)) {
+    return true;
+  }
+
+  UniqueChars error(
+      JS_smprintf("type mismatch: expression has type %s but expected %s",
+                  ToCString(actual), ToCString(expected)));
+  if (!error) {
+    return false;
+  }
+
+  return fail(error.get());
+}
+
+template <typename Policy>
 inline bool OpIter<Policy>::unrecognizedOpcode(const OpBytes* expr) {
   UniqueChars error(JS_smprintf("unrecognized opcode: %x %x", expr->b0,
                                 IsPrefixByte(expr->b0) ? expr->b1 : 0));
   if (!error) {
     return false;
   }
 
   return fail(error.get());
@@ -571,101 +590,87 @@ template <typename Policy>
 inline bool OpIter<Policy>::fail_ctx(const char* fmt, const char* context) {
   UniqueChars error(JS_smprintf(fmt, context));
   if (!error) {
     return false;
   }
   return fail(error.get());
 }
 
-// This function pops exactly one value from the stack, yielding Any types in
+template <typename Policy>
+inline bool OpIter<Policy>::failEmptyStack() {
+  return valueStack_.empty() ? fail("popping value from empty stack")
+                             : fail("popping value from outside block");
+}
+
+// This function pops exactly one value from the stack, yielding TVar types in
 // various cases and therefore making it the caller's responsibility to do the
 // right thing for StackType::TVar. Prefer (pop|top)WithType.
 template <typename Policy>
-inline bool OpIter<Policy>::popAnyType(StackType* type, Value* value) {
+inline bool OpIter<Policy>::popStackType(StackType* type, Value* value) {
   ControlStackEntry<ControlItem>& block = controlStack_.back();
 
   MOZ_ASSERT(valueStack_.length() >= block.valueStackStart());
   if (MOZ_UNLIKELY(valueStack_.length() == block.valueStackStart())) {
     // If the base of this block's stack is polymorphic, then we can pop a
     // dummy value of any type; it won't be used since we're in unreachable
     // code.
     if (block.polymorphicBase()) {
       *type = StackType::TVar;
       *value = Value();
 
       // Maintain the invariant that, after a pop, there is always memory
       // reserved to push a value infallibly.
       return valueStack_.reserve(valueStack_.length() + 1);
     }
 
-    if (valueStack_.empty()) {
-      return fail("popping value from empty stack");
-    }
-    return fail("popping value from outside block");
+    return failEmptyStack();
   }
 
   TypeAndValue<Value>& tv = valueStack_.back();
   *type = tv.type();
   *value = tv.value();
   valueStack_.popBack();
   return true;
 }
 
-template <typename Policy>
-inline bool OpIter<Policy>::typeMismatch(StackType actual, StackType expected) {
-  UniqueChars error(
-      JS_smprintf("type mismatch: expression has type %s but expected %s",
-                  ToCString(NonAnyToValType(actual)),
-                  ToCString(NonAnyToValType(expected))));
-  if (!error) {
-    return false;
-  }
-
-  return fail(error.get());
-}
-
 // This function pops exactly one value from the stack, checking that it has the
 // expected type which can either be a specific value type or a type variable.
 template <typename Policy>
-inline bool OpIter<Policy>::popWithType(StackType expectedType, Value* value) {
+inline bool OpIter<Policy>::popWithType(ValType expectedType, Value* value) {
   ControlStackEntry<ControlItem>& block = controlStack_.back();
 
   MOZ_ASSERT(valueStack_.length() >= block.valueStackStart());
   if (MOZ_UNLIKELY(valueStack_.length() == block.valueStackStart())) {
     // If the base of this block's stack is polymorphic, then we can pop a
     // dummy value of any expected type; it won't be used since we're in
     // unreachable code.
     if (block.polymorphicBase()) {
       *value = Value();
 
       // Maintain the invariant that, after a pop, there is always memory
       // reserved to push a value infallibly.
       return valueStack_.reserve(valueStack_.length() + 1);
     }
 
-    if (valueStack_.empty()) {
-      return fail("popping value from empty stack");
-    }
-    return fail("popping value from outside block");
+    return failEmptyStack();
   }
 
-  TypeAndValue<Value> tv = valueStack_.popCopy();
-
-  StackType observedType = tv.type();
-  if (!(MOZ_LIKELY(observedType == expectedType) ||
-        observedType == StackType::TVar || expectedType == StackType::TVar ||
-        (env_.gcTypesEnabled() && observedType.isReference() &&
-         expectedType.isReference() &&
-         env_.isRefSubtypeOf(NonAnyToValType(observedType),
-                             NonAnyToValType(expectedType))))) {
-    return typeMismatch(observedType, expectedType);
+  TypeAndValue<Value> observed = valueStack_.popCopy();
+
+  if (observed.type() == StackType::TVar) {
+    *value = Value();
+    return true;
   }
 
-  *value = tv.value();
+  if (!checkIsSubtypeOf(NonTVarToValType(observed.type()), expectedType)) {
+    return false;
+  }
+
+  *value = observed.value();
   return true;
 }
 
 // This function pops as many types from the stack as determined by the given
 // signature. Currently, all signatures are limited to 0 or 1 types, with
 // ExprType::Void meaning 0 and all other ValTypes meaning 1, but this will be
 // generalized in the future.
 template <typename Policy>
@@ -679,54 +684,47 @@ inline bool OpIter<Policy>::popWithType(
 }
 
 // This function is just an optimization of popWithType + push.
 template <typename Policy>
 inline bool OpIter<Policy>::topWithType(ValType expectedType, Value* value) {
   ControlStackEntry<ControlItem>& block = controlStack_.back();
 
   MOZ_ASSERT(valueStack_.length() >= block.valueStackStart());
-  if (MOZ_UNLIKELY(valueStack_.length() == block.valueStackStart())) {
+  if (valueStack_.length() == block.valueStackStart()) {
     // If the base of this block's stack is polymorphic, then we can just
     // pull out a dummy value of the expected type; it won't be used since
     // we're in unreachable code. We must however push this value onto the
     // stack since it is now fixed to a specific type by this type
     // constraint.
     if (block.polymorphicBase()) {
       if (!valueStack_.emplaceBack(expectedType, Value())) {
         return false;
       }
 
       *value = Value();
       return true;
     }
 
-    if (valueStack_.empty()) {
-      return fail("reading value from empty stack");
-    }
-    return fail("reading value from outside block");
+    return failEmptyStack();
   }
 
-  TypeAndValue<Value>& tv = valueStack_.back();
-
-  StackType observed = tv.type();
-  StackType expected = StackType(expectedType);
-
-  if (!MOZ_UNLIKELY(observed == expected)) {
-    if (observed == StackType::TVar ||
-        (env_.gcTypesEnabled() && observed.isReference() &&
-         expected.isReference() &&
-         env_.isRefSubtypeOf(NonAnyToValType(observed), expectedType))) {
-      tv.typeRef() = expected;
-    } else {
-      return typeMismatch(observed, expected);
-    }
+  TypeAndValue<Value>& observed = valueStack_.back();
+
+  if (observed.type() == StackType::TVar) {
+    observed.typeRef() = StackType(expectedType);
+    *value = Value();
+    return true;
   }
 
-  *value = tv.value();
+  if (!checkIsSubtypeOf(NonTVarToValType(observed.type()), expectedType)) {
+    return false;
+  }
+
+  *value = observed.value();
   return true;
 }
 
 template <typename Policy>
 inline bool OpIter<Policy>::topWithType(ExprType expectedType, Value* value) {
   if (IsVoid(expectedType)) {
     *value = Value();
     return true;
@@ -1097,17 +1095,17 @@ inline bool OpIter<Policy>::readUnreacha
   return true;
 }
 
 template <typename Policy>
 inline bool OpIter<Policy>::readDrop() {
   MOZ_ASSERT(Classify(op_) == OpKind::Drop);
   StackType type;
   Value value;
-  return popAnyType(&type, &value);
+  return popStackType(&type, &value);
 }
 
 template <typename Policy>
 inline bool OpIter<Policy>::readUnary(ValType operandType, Value* input) {
   MOZ_ASSERT(Classify(op_) == OpKind::Unary);
 
   if (!popWithType(operandType, input)) {
     return false;
@@ -1344,22 +1342,22 @@ inline bool OpIter<Policy>::readSelect(S
                                        Value* falseValue, Value* condition) {
   MOZ_ASSERT(Classify(op_) == OpKind::Select);
 
   if (!popWithType(ValType::I32, condition)) {
     return false;
   }
 
   StackType falseType;
-  if (!popAnyType(&falseType, falseValue)) {
+  if (!popStackType(&falseType, falseValue)) {
     return false;
   }
 
   StackType trueType;
-  if (!popAnyType(&trueType, trueValue)) {
+  if (!popStackType(&trueType, trueValue)) {
     return false;
   }
 
   if (!Join(falseType, trueType, type)) {
     return fail("select operand types must match");
   }
 
   infalliblePush(*type);