// Copyright 2017 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Flags: --allow-natives-syntax --crankshaft --no-always-opt var global = this; // TODO(ishell): update the test once const->mutable migration does not // create a new map. var IS_INPLACE_MAP_MODIFICATION_SUPPORTED = false; var unique_id = 0; // Creates a function with unique SharedFunctionInfo to ensure the feedback // vector is unique for each test case. function MakeFunctionWithUniqueSFI(...args) { assertTrue(args.length > 0); var body = `/* Unique comment: ${unique_id++} */ ` + args.pop(); return new Function(...args, body); } // // Load constant field from constant object directly. // function TestLoadFromConstantFieldOfAConstantObject(the_value, other_value) { function A(v) { this.v = v; } function O() { this.a = new A(the_value); } var the_object = new O(); // Ensure that {the_object.a}'s map is not stable to complicate compiler's // life. new A(the_value).blah = 0; // Ensure that constant tracking is enabled for {contant_object}. delete global.constant_object; global.constant_object = the_object; assertEquals(the_object, constant_object); assertTrue(%HasFastProperties(the_object)); // {constant_object} is known to the compiler via global property cell // tracking. var load = MakeFunctionWithUniqueSFI("return constant_object.a.v;"); load(); load(); %OptimizeFunctionOnNextCall(load); assertEquals(the_value, load()); assertOptimized(load); if (IS_INPLACE_MAP_MODIFICATION_SUPPORTED) { var a = new A(other_value); assertTrue(%HaveSameMap(a, the_object.a)); // Make constant field mutable by assigning another value // to some other instance of A. new A(the_value).v = other_value; assertTrue(%HaveSameMap(a, new A(the_value))); assertTrue(%HaveSameMap(a, the_object.a)); assertUnoptimized(load); assertEquals(the_value, load()); } else { var a = new A(other_value); assertTrue(%HaveSameMap(a, the_object.a)); // Make constant field mutable by assigning another value // to some other instance of A. new A(the_value).v = other_value; assertOptimized(load); assertTrue(!%HaveSameMap(a, new A(the_value))); assertTrue(%HaveSameMap(a, the_object.a)); // Ensure the {the_object.a} migrated to an up-to date version of a map // by loading a property through IC. assertEquals(the_value, the_object.a.v); assertTrue(!%HaveSameMap(a, the_object.a)); assertOptimized(load); // Now attempt to call load should deoptimize because of failed map check. assertEquals(the_value, load()); } assertUnoptimized(load); assertEquals(the_value, load()); } // Test constant tracking with Smi value. (function() { var the_value = 42; var other_value = 153; TestLoadFromConstantFieldOfAConstantObject(the_value, other_value); })(); // Test constant tracking with double value. (function() { var the_value = 0.9; var other_value = 0.42; TestLoadFromConstantFieldOfAConstantObject(the_value, other_value); })(); // Test constant tracking with function value. (function() { var the_value = function V() {}; var other_value = function W() {}; TestLoadFromConstantFieldOfAConstantObject(the_value, other_value); })(); // Test constant tracking with heap object value. (function() { function V() {} var the_value = new V(); var other_value = new V(); TestLoadFromConstantFieldOfAConstantObject(the_value, other_value); })(); // // Load constant field from a prototype. // function TestLoadFromConstantFieldOfAPrototype(the_value, other_value) { function Proto() { this.v = the_value; } var the_prototype = new Proto(); function O() {} O.prototype = the_prototype; var the_object = new O(); // Ensure O.prototype is in fast mode by loading from its field. function warmup() { return new O().v; } warmup(); warmup(); warmup(); assertTrue(%HasFastProperties(O.prototype)); // The parameter object is not constant but all the values have the same // map and therefore the compiler knows the prototype object and can // optimize load of "v". var load = MakeFunctionWithUniqueSFI("o", "return o.v;"); load(new O()); load(new O()); %OptimizeFunctionOnNextCall(load); assertEquals(the_value, load(new O())); assertOptimized(load); if (IS_INPLACE_MAP_MODIFICATION_SUPPORTED) { // Invalidation of mutability should trigger deoptimization with a // "field-owner" reason. the_prototype.v = other_value; } else { // Invalidation of mutability should trigger deoptimization with a // "prototype-check" (stability) reason. the_prototype.v = other_value; } assertUnoptimized(load); } // Test constant tracking with Smi value. (function() { var the_value = 42; var other_value = 153; TestLoadFromConstantFieldOfAPrototype(the_value, other_value); })(); // Test constant tracking with double value. (function() { var the_value = 0.9; var other_value = 0.42; TestLoadFromConstantFieldOfAPrototype(the_value, other_value); })(); // Test constant tracking with function value. (function() { var the_value = function V() {}; var other_value = function W() {}; TestLoadFromConstantFieldOfAPrototype(the_value, other_value); })(); // Test constant tracking with heap object value. (function() { function V() {} var the_value = new V(); var other_value = new V(); TestLoadFromConstantFieldOfAPrototype(the_value, other_value); })(); // // Store to constant field of a constant object. // function TestStoreToConstantFieldOfConstantObject(the_value, other_value) { function A(v) { this.v = v; } function O() { this.a = new A(the_value); } var the_object = new O(); // Ensure that {the_object.a}'s map is not stable to complicate compiler's // life. new A(the_value).blah = 0; // Ensure that constant tracking is enabled for {contant_object}. delete global.constant_object; global.constant_object = the_object; assertEquals(the_object, constant_object); assertTrue(%HasFastProperties(the_object)); // {constant_object} is known to the compiler via global property cell // tracking. var store = MakeFunctionWithUniqueSFI("v", "constant_object.a.v = v;"); store(the_value); store(the_value); %OptimizeFunctionOnNextCall(store); store(the_value); assertEquals(the_value, constant_object.a.v); assertOptimized(store); // Storing of the same value does not deoptimize. store(the_value); assertEquals(the_value, constant_object.a.v); assertOptimized(store); if (IS_INPLACE_MAP_MODIFICATION_SUPPORTED) { var a = new A(other_value); if (typeof the_value == "function" || typeof the_value == "object") { // For heap object fields "field-owner" dependency is installed for // any access of the field, therefore making constant field mutable by // assigning other value to some other instance of A should already // trigger deoptimization. assertTrue(%HaveSameMap(a, the_object.a)); new A(the_value).v = other_value; assertTrue(%HaveSameMap(a, new A(the_value))); assertTrue(%HaveSameMap(a, the_object.a)); assertUnoptimized(store); } else { assertOptimized(store); } // Storing other value deoptimizes because of failed value check. store(other_value); assertUnoptimized(store); assertEquals(other_value, constant_object.a.v); } else { // Storing other value deoptimizes because of failed value check. store(other_value); assertUnoptimized(store); assertEquals(other_value, constant_object.a.v); } } // Test constant tracking with Smi values. (function() { var the_value = 42; var other_value = 153; TestStoreToConstantFieldOfConstantObject(the_value, other_value); })(); // Test constant tracking with double values. (function() { var the_value = 0.9; var other_value = 0.42 TestStoreToConstantFieldOfConstantObject(the_value, other_value); })(); // Test constant tracking with function values. (function() { var the_value = function V() {}; var other_value = function W() {}; TestStoreToConstantFieldOfConstantObject(the_value, other_value); })(); // Test constant tracking with heap object values. (function() { function V() {} var the_value = new V(); var other_value = new V(); TestStoreToConstantFieldOfConstantObject(the_value, other_value); })();