Commit 4a5446fb authored by ishell's avatar ishell Committed by Commit bot

[tests] Make assertOptimized()/assertUnoptimized() great again.

The mentioned asserts did not work properly with interpreted and turbofanned functions.

To fix this issue %GetOptimizationStatus() now returns a set of flags instead of a single value.

This CL also adds more helper functions to mjsunit, like isNeverOptimize(), isAlwaysOptimize(),
isOptimized(fun), etc.

BUG=v8:5890

Review-Url: https://codereview.chromium.org/2654733004
Cr-Original-Commit-Position: refs/heads/master@{#42703}
Committed: https://chromium.googlesource.com/v8/v8/+/d1ddec785725a184fe6d01bd0813262e3ba24966
Review-Url: https://codereview.chromium.org/2654733004
Cr-Commit-Position: refs/heads/master@{#42731}
parent e29a2cd5
......@@ -314,18 +314,19 @@ void EnsureFeedbackMetadata(CompilationInfo* info) {
}
bool UseTurboFan(Handle<SharedFunctionInfo> shared) {
bool optimization_disabled = shared->optimization_disabled();
if (shared->optimization_disabled()) {
return false;
}
bool must_use_ignition_turbo = shared->must_use_ignition_turbo();
// Check the enabling conditions for Turbofan.
// 1. "use asm" code.
bool is_turbofanable_asm =
FLAG_turbo_asm && shared->asm_function() && !optimization_disabled;
bool is_turbofanable_asm = FLAG_turbo_asm && shared->asm_function();
// 2. Fallback for features unsupported by Crankshaft.
bool is_unsupported_by_crankshaft_but_turbofanable =
must_use_ignition_turbo && strcmp(FLAG_turbo_filter, "~~") == 0 &&
!optimization_disabled;
must_use_ignition_turbo && strcmp(FLAG_turbo_filter, "~~") == 0;
// 3. Explicitly enabled by the command-line filter.
bool passes_turbo_filter = shared->PassesFilter(FLAG_turbo_filter);
......
......@@ -236,21 +236,28 @@ RUNTIME_FUNCTION(Runtime_NeverOptimizeFunction) {
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_GetOptimizationStatus) {
HandleScope scope(isolate);
DCHECK(args.length() == 1 || args.length() == 2);
int status = 0;
if (!isolate->use_crankshaft()) {
return Smi::FromInt(4); // 4 == "never".
status |= static_cast<int>(OptimizationStatus::kNeverOptimize);
}
if (FLAG_always_opt || FLAG_prepare_always_opt) {
status |= static_cast<int>(OptimizationStatus::kAlwaysOptimize);
}
if (FLAG_deopt_every_n_times) {
status |= static_cast<int>(OptimizationStatus::kMaybeDeopted);
}
// This function is used by fuzzers to get coverage for optimizations
// in compiler. Ignore calls on non-function objects to avoid runtime errors.
CONVERT_ARG_HANDLE_CHECKED(Object, function_object, 0);
if (!function_object->IsJSFunction()) {
return isolate->heap()->undefined_value();
return Smi::FromInt(status);
}
Handle<JSFunction> function = Handle<JSFunction>::cast(function_object);
status |= static_cast<int>(OptimizationStatus::kIsFunction);
bool sync_with_compiler_thread = true;
if (args.length() == 2) {
......@@ -269,22 +276,16 @@ RUNTIME_FUNCTION(Runtime_GetOptimizationStatus) {
base::OS::Sleep(base::TimeDelta::FromMilliseconds(50));
}
}
if (FLAG_always_opt || FLAG_prepare_always_opt) {
// With --always-opt, optimization status expectations might not
// match up, so just return a sentinel.
return Smi::FromInt(3); // 3 == "always".
}
if (FLAG_deopt_every_n_times) {
return Smi::FromInt(6); // 6 == "maybe deopted".
if (function->IsOptimized()) {
status |= static_cast<int>(OptimizationStatus::kOptimized);
if (function->code()->is_turbofanned()) {
status |= static_cast<int>(OptimizationStatus::kTurboFanned);
}
if (function->IsOptimized() && function->code()->is_turbofanned()) {
return Smi::FromInt(7); // 7 == "TurboFan compiler".
}
if (function->IsInterpreted()) {
return Smi::FromInt(8); // 8 == "Interpreted".
status |= static_cast<int>(OptimizationStatus::kInterpreted);
}
return function->IsOptimized() ? Smi::FromInt(1) // 1 == "yes".
: Smi::FromInt(2); // 2 == "no".
return Smi::FromInt(status);
}
......
......@@ -1152,6 +1152,18 @@ class DeclareGlobalsNativeFlag : public BitField<bool, 1, 1> {};
STATIC_ASSERT(LANGUAGE_END == 2);
class DeclareGlobalsLanguageMode : public BitField<LanguageMode, 2, 1> {};
// A set of bits returned by Runtime_GetOptimizationStatus.
// These bits must be in sync with bits defined in test/mjsunit/mjsunit.js
enum class OptimizationStatus {
kIsFunction = 1 << 0,
kNeverOptimize = 1 << 1,
kAlwaysOptimize = 1 << 2,
kMaybeDeopted = 1 << 3,
kOptimized = 1 << 4,
kTurboFanned = 1 << 5,
kInterpreted = 1 << 6,
};
} // namespace internal
} // namespace v8
......
......@@ -13,6 +13,7 @@
#include "src/compilation-cache.h"
#include "src/execution.h"
#include "src/objects.h"
#include "src/runtime/runtime.h"
#include "src/unicode-inl.h"
#include "src/utils.h"
......@@ -4093,6 +4094,7 @@ THREADED_TEST(NamedPropertyHandlerGetterAttributes) {
THREADED_TEST(Regress256330) {
if (!i::FLAG_crankshaft) return;
i::FLAG_allow_natives_syntax = true;
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
......@@ -4108,7 +4110,10 @@ THREADED_TEST(Regress256330) {
"f(o); f(o); f(o);"
"%OptimizeFunctionOnNextCall(f);"
"f(o);");
ExpectBoolean("%GetOptimizationStatus(f) != 2", true);
int status = v8_run_int32value(v8_compile("%GetOptimizationStatus(f)"));
int mask = static_cast<int>(i::OptimizationStatus::kIsFunction) |
static_cast<int>(i::OptimizationStatus::kOptimized);
CHECK_EQ(mask, status & mask);
}
......
......@@ -62,14 +62,8 @@
##############################################################################
['variant == turbofan_opt', {
# TODO(mstarzinger): Debugger cannot materialize de-materialized functions.
'debug/regress/regress-crbug-323936': [FAIL],
# TODO(jarin/mstarzinger): Investigate debugger issues with TurboFan.
'debug/debug-evaluate-closure': [FAIL],
'debug/debug-evaluate-locals': [FAIL],
'debug/debug-set-variable-value': [FAIL],
'debug/es6/debug-evaluate-blockscopes': [FAIL],
}], # variant == turbofan_opt
##############################################################################
......
......@@ -43,18 +43,6 @@ function OptTracker() {
this.opt_counts_ = {};
}
/**
* The possible optimization states of a function. Must be in sync with the
* return values of Runtime_GetOptimizationStatus() in runtime.cc!
* @enum {int}
*/
OptTracker.OptimizationState = {
YES: 1,
NO: 2,
ALWAYS: 3,
NEVER: 4
};
/**
* Always call this at the beginning of your test, once for each function
* that you later want to track de/optimizations for. It is necessary because
......@@ -94,12 +82,10 @@ OptTracker.prototype.AssertIsOptimized = function(func, expect_optimized) {
if (this.DisableAsserts_(func)) {
return;
}
var raw_optimized = %GetOptimizationStatus(func);
if (expect_optimized) {
assertEquals(OptTracker.OptimizationState.YES, raw_optimized);
} else {
assertEquals(OptTracker.OptimizationState.NO, raw_optimized);
}
var opt_status = %GetOptimizationStatus(func);
assertTrue((opt_status & V8OptimizationStatus.kIsFunction) !== 0);
assertEquals(expect_optimized,
(opt_status & V8OptimizationStatus.kOptimized) !== 0);
}
/**
......@@ -119,7 +105,8 @@ OptTracker.prototype.GetOptCount_ = function(func) {
*/
OptTracker.prototype.GetDeoptCount_ = function(func) {
var count = this.GetOptCount_(func);
if (%GetOptimizationStatus(func) == OptTracker.OptimizationState.YES) {
var opt_status = %GetOptimizationStatus(func);
if ((opt_status & V8OptimizationStatus.kOptimized) !== 0) {
count -= 1;
}
return count;
......@@ -129,15 +116,9 @@ OptTracker.prototype.GetDeoptCount_ = function(func) {
* @private
*/
OptTracker.prototype.DisableAsserts_ = function(func) {
switch(%GetOptimizationStatus(func)) {
case OptTracker.OptimizationState.YES:
case OptTracker.OptimizationState.NO:
return false;
case OptTracker.OptimizationState.ALWAYS:
case OptTracker.OptimizationState.NEVER:
return true;
}
return true;
var opt_status = %GetOptimizationStatus(func);
return (opt_status & V8OptimizationStatus.kAlwaysOptimize) !== 0 ||
(opt_status & V8OptimizationStatus.kNeverOptimize) !== 0;
}
// (End of class OptTracker.)
......
......@@ -27,6 +27,7 @@
// Flags: --allow-natives-syntax --no-always-opt
// Flags: --concurrent-recompilation --block-concurrent-recompilation
// Flags: --no-always-opt
if (!%IsConcurrentRecompilationSupported()) {
print("Concurrent recompilation is disabled. Skipping this test.");
......
......@@ -6,21 +6,6 @@
"use strict";
const kDeoptimized = 2;
const kTurbofanned = 7;
const kInterpreted = 8;
function GetOptimizationStatus(fn) {
let status = %GetOptimizationStatus(fn);
switch (status) {
case kInterpreted: // Treat interpreted frames as unoptimized
status = kDeoptimized;
break;
}
return status;
}
let global = this;
let tests = {
FastElementsKind() {
......@@ -118,15 +103,27 @@ let tests = {
// TODO(bmeurer): FAST_HOLEY_DOUBLE_ELEMENTS maps generally deopt when
// a hole is encountered. Test should be fixed once that is corrected.
let status = /HOLEY_DOUBLE/.test(key) ? kDeoptimized : kTurbofanned;
let expect_deopt = /HOLEY_DOUBLE/.test(key);
assertEquals(status, GetOptimizationStatus(fn), key);
if (expect_deopt) {
assertUnoptimized(fn, '', key);
} else {
assertOptimized(fn, '', key);
}
assertEquals(expected, fn(array), key);
assertEquals(status, GetOptimizationStatus(fn), key);
if (expect_deopt) {
assertUnoptimized(fn, '', key);
} else {
assertOptimized(fn, '', key);
}
// Check no deopt when another arra with the same map is used
// Check no deopt when another array with the same map is used
assertTrue(%HaveSameMap(array, array2), key);
assertEquals(status, GetOptimizationStatus(fn), key);
if (expect_deopt) {
assertUnoptimized(fn, '', key);
} else {
assertOptimized(fn, '', key);
}
assertEquals(expected2, fn(array2), key);
// CheckMaps bailout
......@@ -134,7 +131,7 @@ let tests = {
[1, 2, 3], 2, { enumerable: false, configurable: false,
get() { return 7; } });
fn(newArray);
assertEquals(kDeoptimized, GetOptimizationStatus(fn), key);
assertUnoptimized(fn, '', key);
}
},
......@@ -222,12 +219,12 @@ let tests = {
%OptimizeFunctionOnNextCall(sum);
assertEquals(expected, sum(array), key);
assertEquals(kTurbofanned, GetOptimizationStatus(sum), key);
assertOptimized(sum, '', key);
// Not deoptimized when called on typed array of same type / map
assertTrue(%HaveSameMap(array, array2));
assertEquals(expected2, sum(array2), key);
assertEquals(kTurbofanned, GetOptimizationStatus(sum), key);
assertOptimized(sum, '', key);
// Throw when detached
let clone = new array.constructor(array);
......
......@@ -120,6 +120,38 @@ var assertContains;
// Assert that a string matches a given regex.
var assertMatches;
// These bits must be in sync with bits defined in Runtime_GetOptimizationStatus
var V8OptimizationStatus = {
kIsFunction: 1 << 0,
kNeverOptimize: 1 << 1,
kAlwaysOptimize: 1 << 2,
kMaybeDeopted: 1 << 3,
kOptimized: 1 << 4,
kTurboFanned: 1 << 5,
kInterpreted: 1 << 6
};
// Returns true if --no-crankshaft mode is on.
var isNeverOptimize;
// Returns true if --always-opt mode is on.
var isAlwaysOptimize;
// Returns true if given function in interpreted.
var isInterpreted;
// Returns true if given function is compiled by a base-line compiler.
var isBaselined;
// Returns true if given function is optimized.
var isOptimized;
// Returns true if given function is compiled by Crankshaft.
var isCrankshafted;
// Returns true if given function is compiled by TurboFan.
var isTurboFanned;
(function () { // Scope for utility functions.
......@@ -462,12 +494,81 @@ var assertMatches;
assertUnoptimized = function assertUnoptimized(fun, sync_opt, name_opt) {
if (sync_opt === undefined) sync_opt = "";
assertTrue(OptimizationStatus(fun, sync_opt) !== 1, name_opt);
var opt_status = OptimizationStatus(fun, sync_opt);
// Tests that use assertOptimized() do not make sense if --always-opt
// option is provided. Such tests must add --no-always-opt to flags comment.
assertFalse((opt_status & V8OptimizationStatus.kAlwaysOptimize) !== 0,
"test does not make sense with --always-opt");
assertTrue((opt_status & V8OptimizationStatus.kIsFunction) !== 0, name_opt);
assertFalse((opt_status & V8OptimizationStatus.kOptimized) !== 0, name_opt);
}
assertOptimized = function assertOptimized(fun, sync_opt, name_opt) {
if (sync_opt === undefined) sync_opt = "";
assertTrue(OptimizationStatus(fun, sync_opt) !== 2, name_opt);
var opt_status = OptimizationStatus(fun, sync_opt);
// Tests that use assertOptimized() do not make sense if --no-crankshaft
// option is provided. Such tests must add --crankshaft to flags comment.
assertFalse((opt_status & V8OptimizationStatus.kNeverOptimize) !== 0,
"test does not make sense with --no-crankshaft");
assertTrue((opt_status & V8OptimizationStatus.kIsFunction) !== 0, name_opt);
if ((opt_status & V8OptimizationStatus.kMaybeDeopted) !== 0) {
// When --deopt-every-n-times flag is specified it's no longer guaranteed
// that particular function is still optimized, so keep running the test
// to stress test the deoptimizer.
return;
}
assertTrue((opt_status & V8OptimizationStatus.kOptimized) !== 0, name_opt);
}
isNeverOptimize = function isNeverOptimize() {
var opt_status = OptimizationStatus(undefined, "");
return (opt_status & V8OptimizationStatus.kNeverOptimize) !== 0;
}
isAlwaysOptimize = function isAlwaysOptimize() {
var opt_status = OptimizationStatus(undefined, "");
return (opt_status & V8OptimizationStatus.kAlwaysOptimize) !== 0;
}
isInterpreted = function isInterpreted(fun) {
var opt_status = OptimizationStatus(fun, "");
assertTrue((opt_status & V8OptimizationStatus.kIsFunction) !== 0,
"not a function");
return (opt_status & V8OptimizationStatus.kOptimized) === 0 &&
(opt_status & V8OptimizationStatus.kInterpreted) !== 0;
}
// NOTE: This predicate also returns true for functions that have never
// been compiled (i.e. that have LazyCompile stub as a code).
isBaselined = function isBaselined(fun) {
var opt_status = OptimizationStatus(fun, "");
assertTrue((opt_status & V8OptimizationStatus.kIsFunction) !== 0,
"not a function");
return (opt_status & V8OptimizationStatus.kOptimized) === 0 &&
(opt_status & V8OptimizationStatus.kInterpreted) === 0;
}
isOptimized = function isOptimized(fun) {
var opt_status = OptimizationStatus(fun, "");
assertTrue((opt_status & V8OptimizationStatus.kIsFunction) !== 0,
"not a function");
return (opt_status & V8OptimizationStatus.kOptimized) !== 0;
}
isCrankshafted = function isCrankshafted(fun) {
var opt_status = OptimizationStatus(fun, "");
assertTrue((opt_status & V8OptimizationStatus.kIsFunction) !== 0,
"not a function");
return (opt_status & V8OptimizationStatus.kOptimized) !== 0 &&
(opt_status & V8OptimizationStatus.kTurboFanned) === 0;
}
isTurboFanned = function isTurboFanned(fun) {
var opt_status = OptimizationStatus(fun, "");
assertTrue((opt_status & V8OptimizationStatus.kIsFunction) !== 0,
"not a function");
return (opt_status & V8OptimizationStatus.kOptimized) !== 0 &&
(opt_status & V8OptimizationStatus.kTurboFanned) !== 0;
}
})();
......@@ -25,7 +25,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --allow-natives-syntax --crankshaft
// Flags: --allow-natives-syntax --crankshaft --no-always-opt
function mul(x, y) {
return (x * y) | 0;
......
......@@ -79,4 +79,8 @@ assertOptimized(inferrable_store);
// seeing a property name key. It should have inferred a receiver map and
// emitted an elements store, however.
inferrable_store("deopt");
assertUnoptimized(inferrable_store);
// TurboFan is not sophisticated enough to use key type provided by ICs.
if (!isTurboFanned(inferrable_store)) {
assertUnoptimized(inferrable_store);
}
......@@ -9,6 +9,7 @@ function load(o) { return o.x; }
for (var x = 0; x < 1000; ++x) {
load({x});
load({x});
%OptimizeFunctionOnNextCall(load);
try { load(); } catch (e) { }
}
......@@ -20,6 +21,7 @@ function store(o) { o.x = -1; }
for (var x = 0; x < 1000; ++x) {
store({x});
store({x});
%OptimizeFunctionOnNextCall(store);
try { store(); } catch (e) { }
}
......
......@@ -6,6 +6,10 @@
// Flags: --no-ignition --no-ignition-staging --no-turbo
// Flags: --crankshaft --no-always-opt
// If we are always or never optimizing it is useless.
assertFalse(isAlwaysOptimize());
assertFalse(isNeverOptimize());
(function() {
var sum = 0;
var i = 0;
......@@ -15,20 +19,15 @@
}
sum += f(i);
if (%GetOptimizationStatus(f) == 3 || %GetOptimizationStatus(f) == 4) {
// If we are always or never optimizing f, just exit, this test is useless.
return;
}
if (i == 1) {
// f must be baseline code.
assertEquals(2, %GetOptimizationStatus(f));
assertTrue(isBaselined(f));
// Run twice (i = 0, 1), then tier-up.
%OptimizeFunctionOnNextCall(f);
} else if (i == 2) {
// Tier-up at i = 2 should go up to crankshaft.
assertEquals(1, %GetOptimizationStatus(f));
assertTrue(isCrankshafted(f));
}
}
})()
......@@ -6,6 +6,10 @@
// Flags: --ignition-staging --no-turbo
// Flags: --crankshaft --no-always-opt
// If we are always or never optimizing it is useless.
assertFalse(isAlwaysOptimize());
assertFalse(isNeverOptimize());
(function() {
var sum = 0;
var i = 0;
......@@ -15,29 +19,25 @@
}
sum += f(i);
if (%GetOptimizationStatus(f) == 3 || %GetOptimizationStatus(f) == 4) {
// If we are always or never optimizing f, just exit, this test is useless.
return;
}
if (i == 1) {
// f must be interpreted code.
assertEquals(8, %GetOptimizationStatus(f));
assertTrue(isInterpreted(f));
// Allow it to run twice (i = 0, 1), then tier-up to baseline.
%BaselineFunctionOnNextCall(f);
} else if (i == 2) {
// Tier-up at i = 2 should only go up to baseline.
assertEquals(2, %GetOptimizationStatus(f));
assertTrue(isBaselined(f));
} else if (i == 3) {
// Now f must be baseline code.
assertEquals(2, %GetOptimizationStatus(f));
assertTrue(isBaselined(f));
// Run two more times (i = 2, 3), then tier-up to optimized.
%OptimizeFunctionOnNextCall(f);
} else if (i == 4) {
// Tier-up at i = 4 should now go up to crankshaft.
assertEquals(1, %GetOptimizationStatus(f));
assertTrue(isCrankshafted(f));
}
}
})()
......@@ -5,6 +5,10 @@
// Flags: --mark-shared-functions-for-tier-up --allow-natives-syntax
// Flags: --ignition-staging --turbo --no-always-opt
// If we are always or never optimizing it is useless.
assertFalse(isAlwaysOptimize());
assertFalse(isNeverOptimize());
(function() {
var sum = 0;
var i = 0;
......@@ -14,20 +18,15 @@
}
sum += f(i);
if (%GetOptimizationStatus(f) == 3 || %GetOptimizationStatus(f) == 4) {
// If we are always or never optimizing f, just exit, this test is useless.
return;
}
if (i == 1) {
// f must be interpreted code.
assertEquals(8, %GetOptimizationStatus(f));
assertTrue(isInterpreted(f));
// Run twice (i = 0, 1), then tier-up.
%OptimizeFunctionOnNextCall(f);
} else if (i == 2) {
// Tier-up at i = 2 should go up to turbofan.
assertEquals(7, %GetOptimizationStatus(f));
assertTrue(isTurboFanned(f));
}
}
})()
......@@ -25,7 +25,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --allow-natives-syntax --crankshaft
// Flags: --allow-natives-syntax --crankshaft --no-always-opt
function divp4(x) {
return x / 4;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment