From 68cd7bf6171ddc71a715cb7b62c7bd7c9c6441bf Mon Sep 17 00:00:00 2001 From: Lars T Hansen Date: Fri, 28 Feb 2014 09:54:44 -0500 Subject: [PATCH] Bug 977117 - Enable inlining of the ObjectIsTypeDescr primitive. r=nmatsakis --- js/src/builtin/TypedObject.h | 35 ++++++++++++----- .../TypedObject-ObjectIsTypeDescr-multi.js | 38 ++++++++++++++++++ .../TypedObject-ObjectIsTypeDescr-unknown.js | 36 +++++++++++++++++ ...pedObject-ObjectIsTypeDescr-wrong-multi.js | 38 ++++++++++++++++++ .../TypedObject-ObjectIsTypeDescr-wrong.js | 36 +++++++++++++++++ .../inlining/TypedObject-ObjectIsTypeDescr.js | 36 +++++++++++++++++ js/src/jit/IonBuilder.h | 3 ++ js/src/jit/MCallOptimize.cpp | 39 +++++++++++++++++++ js/src/jscntxt.h | 1 + js/src/jsinfer.cpp | 31 +++++++++++++++ js/src/jsinfer.h | 15 +++++++ js/src/vm/SelfHosting.cpp | 8 +++- 12 files changed, 306 insertions(+), 10 deletions(-) create mode 100644 js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr-multi.js create mode 100644 js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr-unknown.js create mode 100644 js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr-wrong-multi.js create mode 100644 js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr-wrong.js create mode 100644 js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr.js diff --git a/js/src/builtin/TypedObject.h b/js/src/builtin/TypedObject.h index 2df726b51d9d..292da3a20d6b 100644 --- a/js/src/builtin/TypedObject.h +++ b/js/src/builtin/TypedObject.h @@ -880,6 +880,29 @@ IsTypedObjectClass(const Class *class_) class_ == &OpaqueTypedObject::class_; } +inline bool +IsSimpleTypeDescrClass(const Class* clasp) +{ + return clasp == &ScalarTypeDescr::class_ || + clasp == &ReferenceTypeDescr::class_; +} + +inline bool +IsSizedTypeDescrClass(const Class* clasp) +{ + return IsSimpleTypeDescrClass(clasp) || + clasp == &StructTypeDescr::class_ || + clasp == &SizedArrayTypeDescr::class_ || + clasp == &X4TypeDescr::class_; +} + +inline bool +IsTypeDescrClass(const Class* clasp) +{ + return IsSizedTypeDescrClass(clasp) || + clasp == &UnsizedArrayTypeDescr::class_; +} + } // namespace js JSObject * @@ -889,26 +912,21 @@ template <> inline bool JSObject::is() const { - return is() || - is(); + return IsSimpleTypeDescrClass(getClass()); } template <> inline bool JSObject::is() const { - return is() || - is() || - is() || - is(); + return IsSizedTypeDescrClass(getClass()); } template <> inline bool JSObject::is() const { - return is() || - is(); + return IsTypeDescrClass(getClass()); } template <> @@ -919,4 +937,3 @@ JSObject::is() const } #endif /* builtin_TypedObject_h */ - diff --git a/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr-multi.js b/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr-multi.js new file mode 100644 index 000000000000..5205b7d1e1b7 --- /dev/null +++ b/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr-multi.js @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Used to verify that the JIT resolves the ObjectIsTypeDescr tests + * internal to Type.toSource(). + * + * In this case the argument type is always a type descriptor object + * (though not a unique one), so ObjectIsTypeDescr resolves to true + * and there should be no exceptions. + * + * Load this into the js shell with IONFLAGS=logs, then exit and run + * iongraph. You're looking for a smallish function within the + * "self-hosted" domain. Look for a call to ObjectIsTypeDescr far + * down in the graph for pass00, with a call to DescrToSource in a + * subsequent block (all of this is at the mercy of the way the code + * is currently written). + */ + +var T = TypedObject; +var ST1 = new T.StructType({x:T.int32}); +var ST2 = new T.StructType({x:T.float64}); + +function check(v) { + return v.toSource(); +} + +function test() { + var a = [ ST1, ST2 ]; + for ( var i=0 ; i < 1000 ; i++ ) + check(a[i%2]); + return check(a[0]); +} + +print(test()); + + diff --git a/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr-unknown.js b/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr-unknown.js new file mode 100644 index 000000000000..e335643c19ff --- /dev/null +++ b/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr-unknown.js @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Used to verify that the JIT resolves the ObjectIsTypeDescr tests + * internal to Type.toSource(). + * + * In this case the argument type is never a type descriptor object, + * so ObjectIsTypeDescr resolves to false (and we have to catch + * exceptions). + * + * Load this into the js shell with IONFLAGS=logs, then exit and run + * iongraph. You're looking for a smallish function within the + * "self-hosted" domain. Look for a call to ObjectIsTypeDescr far + * down in the graph for pass00, with a call to DescrToSource in a + * subsequent block (all of this is at the mercy of the way the code + * is currently written). + */ + +var T = TypedObject; +var ST = new T.StructType({x:T.int32}); + +function check(v) { + return v.toSource(); +} + +function test() { + var fake = { toSource: ST.toSource }; + var a = [ ST, fake ]; + for ( var i=0 ; i < 1000 ; i++ ) + try { check(a[i%2]); } catch (e) {} + try { return check(a[0]); } catch (e) { return "Thrown" } +} + +print(test()); diff --git a/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr-wrong-multi.js b/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr-wrong-multi.js new file mode 100644 index 000000000000..50f1c0451f28 --- /dev/null +++ b/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr-wrong-multi.js @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Used to verify that the JIT resolves the ObjectIsTypeDescr tests + * internal to Type.toSource(). + * + * In this case the argument type is never a type descriptor object + * (though not a unique non-type-descriptor), so ObjectIsTypeDescr + * resolves to false (and we have to catch exceptions). + * + * Load this into the js shell with IONFLAGS=logs, then exit and run + * iongraph. You're looking for a smallish function within the + * "self-hosted" domain. Look for a call to ObjectIsTypeDescr far + * down in the graph for pass00, with a call to DescrToSource in a + * subsequent block (all of this is at the mercy of the way the code + * is currently written). + */ + +var T = TypedObject; +var ST = new T.StructType({x:T.int32}); + +function check(v) { + return v.toSource(); +} + +function test() { + var fake1 = { toSource: ST.toSource }; + var fake2 = []; fake2.toSource = ST.toSource; + var a = [ fake1, fake2 ]; + for ( var i=0 ; i < 1000 ; i++ ) + try { check(a[i%2]); } catch (e) {} + try { return check(a[0]); } catch (e) { return "Thrown" } +} + +print(test()); + diff --git a/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr-wrong.js b/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr-wrong.js new file mode 100644 index 000000000000..487dc5497f44 --- /dev/null +++ b/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr-wrong.js @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Used to verify that the JIT resolves the ObjectIsTypeDescr tests + * internal to Type.toSource(). + * + * In this case the argument type is never a type descriptor object, + * so ObjectIsTypeDescr resolves to false (and we have to catch + * exceptions). + * + * Load this into the js shell with IONFLAGS=logs, then exit and run + * iongraph. You're looking for a smallish function within the + * "self-hosted" domain. Look for a call to ObjectIsTypeDescr far + * down in the graph for pass00, with a call to DescrToSource in a + * subsequent block (all of this is at the mercy of the way the code + * is currently written). + */ + +var T = TypedObject; +var ST = new T.StructType({x:T.int32}); + +function check(v) { + return v.toSource(); +} + +function test() { + var fake = { toSource: ST.toSource }; + for ( var i=0 ; i < 1000 ; i++ ) + try { check(fake); } catch (e) {} + try { return check(fake); } catch (e) { return "Thrown" } +} + +print(test()); + diff --git a/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr.js b/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr.js new file mode 100644 index 000000000000..2a83ff5f01b8 --- /dev/null +++ b/js/src/jit-test/tests/ion/inlining/TypedObject-ObjectIsTypeDescr.js @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Used to verify that the JIT resolves the ObjectIsTypeDescr tests + * internal to Type.toSource(). + * + * In this case the argument type is always a type descriptor object, + * so ObjectIsTypeDescr resolves to true and there should be no + * exceptions. + * + * Load this into the js shell with IONFLAGS=logs, then exit and run + * iongraph. You're looking for a smallish function within the + * "self-hosted" domain. Look for a call to ObjectIsTypeDescr far + * down in the graph for pass00, with a call to DescrToSource in a + * subsequent block (all of this is at the mercy of the way the code + * is currently written). + */ + +var T = TypedObject; +var ST = new T.StructType({x:T.int32}); + +function check(v) { + return v.toSource(); +} + +function test() { + for ( var i=0 ; i < 1000 ; i++ ) + check(ST); + return check(ST); +} + +print(test()); + + diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 8ce560581e6f..d6643a84fe04 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -677,6 +677,9 @@ class IonBuilder : public MIRGenerator // ForkJoin intrinsics InliningStatus inlineForkJoinGetSlice(CallInfo &callInfo); + // TypedObject intrinsics. + InliningStatus inlineObjectIsTypeDescr(CallInfo &callInfo); + // Utility intrinsics. InliningStatus inlineIsCallable(CallInfo &callInfo); InliningStatus inlineHaveSameClass(CallInfo &callInfo); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index bbb14a84213c..12bb14a6ffb1 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -162,6 +162,8 @@ IonBuilder::inlineNativeCall(CallInfo &callInfo, JSNative native) return inlineHasClass(callInfo, &TransparentTypedObject::class_); if (native == intrinsic_ObjectIsOpaqueTypedObject) return inlineHasClass(callInfo, &OpaqueTypedObject::class_); + if (native == intrinsic_ObjectIsTypeDescr) + return inlineObjectIsTypeDescr(callInfo); // Testing Functions if (native == testingFunc_inParallelSection) @@ -1508,6 +1510,43 @@ IonBuilder::inlineHasClass(CallInfo &callInfo, const Class *clasp) return InliningStatus_Inlined; } +IonBuilder::InliningStatus +IonBuilder::inlineObjectIsTypeDescr(CallInfo &callInfo) +{ + if (callInfo.constructing() || callInfo.argc() != 1) + return InliningStatus_NotInlined; + + if (callInfo.getArg(0)->type() != MIRType_Object) + return InliningStatus_NotInlined; + if (getInlineReturnType() != MIRType_Boolean) + return InliningStatus_NotInlined; + + // The test is elaborate: in-line only if there is exact + // information. + + types::TemporaryTypeSet *types = callInfo.getArg(0)->resultTypeSet(); + if (!types) + return InliningStatus_NotInlined; + + bool result = false; + switch (types->forAllClasses(IsTypeDescrClass)) { + case types::TemporaryTypeSet::ForAllResult::ALL_FALSE: + case types::TemporaryTypeSet::ForAllResult::EMPTY: + result = false; + break; + case types::TemporaryTypeSet::ForAllResult::ALL_TRUE: + result = true; + break; + case types::TemporaryTypeSet::ForAllResult::MIXED: + return InliningStatus_NotInlined; + } + + pushConstant(BooleanValue(result)); + + callInfo.setImplicitlyUsedUnchecked(); + return InliningStatus_Inlined; +} + IonBuilder::InliningStatus IonBuilder::inlineUnsafeSetReservedSlot(CallInfo &callInfo) { diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index d0da4d1782db..6fa13eb22954 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -1036,6 +1036,7 @@ bool intrinsic_InParallelSection(JSContext *cx, unsigned argc, Value *vp); bool intrinsic_ObjectIsTransparentTypedObject(JSContext *cx, unsigned argc, Value *vp); bool intrinsic_ObjectIsOpaqueTypedObject(JSContext *cx, unsigned argc, Value *vp); +bool intrinsic_ObjectIsTypeDescr(JSContext *cx, unsigned argc, Value *vp); class AutoLockForExclusiveAccess { diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index a3450af894df..43553fd90444 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -1698,6 +1698,37 @@ TemporaryTypeSet::getKnownClass() return clasp; } +TemporaryTypeSet::ForAllResult +TemporaryTypeSet::forAllClasses(bool (*func)(const Class* clasp)) +{ + if (unknownObject()) + return ForAllResult::MIXED; + + unsigned count = getObjectCount(); + if (count == 0) + return ForAllResult::EMPTY; + + bool true_results = false; + bool false_results = false; + for (unsigned i = 0; i < count; i++) { + const Class *clasp = getObjectClass(i); + if (!clasp) + return ForAllResult::MIXED; + if (func(clasp)) { + true_results = true; + if (false_results) return ForAllResult::MIXED; + } + else { + false_results = true; + if (true_results) return ForAllResult::MIXED; + } + } + + JS_ASSERT(true_results != false_results); + + return true_results ? ForAllResult::ALL_TRUE : ForAllResult::ALL_FALSE; +} + int TemporaryTypeSet::getTypedArrayType() { diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index b5ed99f5d498..32864777048d 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -673,6 +673,21 @@ class TemporaryTypeSet : public TypeSet /* Get the class shared by all objects in this set, or nullptr. */ const Class *getKnownClass(); + /* Result returned from forAllClasses */ + enum ForAllResult { + EMPTY=1, // Set empty + ALL_TRUE, // Set not empty and predicate returned true for all classes + ALL_FALSE, // Set not empty and predicate returned false for all classes + MIXED, // Set not empty and predicate returned false for some classes + // and true for others, or set contains an unknown or non-object + // type + }; + + /* Apply func to the members of the set and return an appropriate result. + * The iteration may end early if the result becomes known early. + */ + ForAllResult forAllClasses(bool (*func)(const Class *clasp)); + /* Get the prototype shared by all objects in this set, or nullptr. */ JSObject *getCommonPrototype(); diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 738bf62c6947..ce91b8795e10 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -635,6 +635,12 @@ js::intrinsic_ObjectIsOpaqueTypedObject(JSContext *cx, unsigned argc, Value *vp) return js::ObjectIsOpaqueTypedObject(cx, argc, vp); } +bool +js::intrinsic_ObjectIsTypeDescr(JSContext *cx, unsigned argc, Value *vp) +{ + return js::ObjectIsTypeDescr(cx, argc, vp); +} + /** * Returns the default locale as a well-formed, but not necessarily canonicalized, * BCP-47 language tag. @@ -712,7 +718,7 @@ static const JSFunctionSpec intrinsic_functions[] = { JSNativeThreadSafeWrapper, &js::SetTypedObjectOffsetJitInfo, 2, 0), JS_FNINFO("ObjectIsTypeDescr", - JSNativeThreadSafeWrapper, + intrinsic_ObjectIsTypeDescr, &js::ObjectIsTypeDescrJitInfo, 5, 0), JS_FNINFO("ObjectIsTransparentTypedObject", intrinsic_ObjectIsTransparentTypedObject,