Home | History | Annotate | Download | only in compiler
      1 // Copyright 2015 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/compiler.h"
      6 
      7 #include "src/compiler/access-builder.h"
      8 #include "src/compiler/js-graph.h"
      9 #include "src/compiler/js-operator.h"
     10 #include "src/compiler/js-type-feedback.h"
     11 #include "src/compiler/machine-operator.h"
     12 #include "src/compiler/node-matchers.h"
     13 #include "src/compiler/node-properties.h"
     14 #include "src/compiler/operator-properties.h"
     15 
     16 #include "test/unittests/compiler/compiler-test-utils.h"
     17 #include "test/unittests/compiler/graph-unittest.h"
     18 #include "test/unittests/compiler/node-test-utils.h"
     19 #include "testing/gmock-support.h"
     20 
     21 using testing::Capture;
     22 
     23 
     24 namespace v8 {
     25 namespace internal {
     26 namespace compiler {
     27 
     28 class JSTypeFeedbackTest : public TypedGraphTest {
     29  public:
     30   JSTypeFeedbackTest()
     31       : TypedGraphTest(3),
     32         javascript_(zone()),
     33         dependencies_(isolate(), zone()) {}
     34   ~JSTypeFeedbackTest() override { dependencies_.Rollback(); }
     35 
     36  protected:
     37   Reduction Reduce(Node* node,
     38                    JSTypeFeedbackSpecializer::DeoptimizationMode mode) {
     39     Handle<GlobalObject> global_object(
     40         isolate()->native_context()->global_object(), isolate());
     41 
     42     MachineOperatorBuilder machine(zone());
     43     SimplifiedOperatorBuilder simplified(zone());
     44     JSGraph jsgraph(isolate(), graph(), common(), javascript(), &simplified,
     45                     &machine);
     46     JSTypeFeedbackTable table(zone());
     47     // TODO(titzer): mock the GraphReducer here for better unit testing.
     48     GraphReducer graph_reducer(zone(), graph());
     49     JSTypeFeedbackSpecializer reducer(&graph_reducer, &jsgraph, &table, nullptr,
     50                                       global_object, mode, &dependencies_);
     51     return reducer.Reduce(node);
     52   }
     53 
     54   Node* EmptyFrameState() {
     55     MachineOperatorBuilder machine(zone());
     56     JSGraph jsgraph(isolate(), graph(), common(), javascript(), nullptr,
     57                     &machine);
     58     return jsgraph.EmptyFrameState();
     59   }
     60 
     61   JSOperatorBuilder* javascript() { return &javascript_; }
     62 
     63   void SetGlobalProperty(const char* string, int value) {
     64     SetGlobalProperty(string, Handle<Smi>(Smi::FromInt(value), isolate()));
     65   }
     66 
     67   void SetGlobalProperty(const char* string, double value) {
     68     SetGlobalProperty(string, isolate()->factory()->NewNumber(value));
     69   }
     70 
     71   void SetGlobalProperty(const char* string, Handle<Object> value) {
     72     Handle<JSObject> global(isolate()->context()->global_object(), isolate());
     73     Handle<String> name =
     74         isolate()->factory()->NewStringFromAsciiChecked(string);
     75     MaybeHandle<Object> result =
     76         JSReceiver::SetProperty(global, name, value, SLOPPY);
     77     result.Assert();
     78   }
     79 
     80   Node* ReturnLoadNamedFromGlobal(
     81       const char* string, Node* effect, Node* control,
     82       JSTypeFeedbackSpecializer::DeoptimizationMode mode) {
     83     VectorSlotPair feedback;
     84     Node* vector = UndefinedConstant();
     85     Node* context = UndefinedConstant();
     86 
     87     Handle<Name> name = isolate()->factory()->InternalizeUtf8String(string);
     88     const Operator* op = javascript()->LoadGlobal(name, feedback);
     89     Node* load = graph()->NewNode(op, vector, context, EmptyFrameState(),
     90                                   EmptyFrameState(), effect, control);
     91     Node* if_success = graph()->NewNode(common()->IfSuccess(), load);
     92     return graph()->NewNode(common()->Return(), load, load, if_success);
     93   }
     94 
     95   CompilationDependencies* dependencies() { return &dependencies_; }
     96 
     97  private:
     98   JSOperatorBuilder javascript_;
     99   CompilationDependencies dependencies_;
    100 };
    101 
    102 
    103 TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalConstSmi) {
    104   const int kValue = 111;
    105   const char* kName = "banana";
    106   SetGlobalProperty(kName, kValue);
    107 
    108   Node* ret = ReturnLoadNamedFromGlobal(
    109       kName, graph()->start(), graph()->start(),
    110       JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
    111   graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
    112 
    113   Reduction r = Reduce(ret->InputAt(0),
    114                        JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
    115   EXPECT_FALSE(r.Changed());
    116   EXPECT_TRUE(dependencies()->IsEmpty());
    117 }
    118 
    119 
    120 TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalConstSmiWithDeoptimization) {
    121   const int kValue = 111;
    122   const char* kName = "banana";
    123   SetGlobalProperty(kName, kValue);
    124 
    125   Node* ret = ReturnLoadNamedFromGlobal(
    126       kName, graph()->start(), graph()->start(),
    127       JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
    128   graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
    129 
    130   Reduction r = Reduce(ret->InputAt(0),
    131                        JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
    132 
    133   // Check LoadNamed(global) => HeapConstant[kValue]
    134   ASSERT_TRUE(r.Changed());
    135   EXPECT_THAT(r.replacement(), IsNumberConstant(kValue));
    136 
    137   EXPECT_THAT(ret, IsReturn(IsNumberConstant(kValue), graph()->start(),
    138                             graph()->start()));
    139   EXPECT_THAT(graph()->end(), IsEnd(ret));
    140 
    141   EXPECT_FALSE(dependencies()->IsEmpty());
    142   dependencies()->Rollback();
    143 }
    144 
    145 
    146 TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalConstNumber) {
    147   const double kValue = -11.25;
    148   const char* kName = "kiwi";
    149   SetGlobalProperty(kName, kValue);
    150 
    151   Node* ret = ReturnLoadNamedFromGlobal(
    152       kName, graph()->start(), graph()->start(),
    153       JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
    154   graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
    155 
    156   Reduction r = Reduce(ret->InputAt(0),
    157                        JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
    158 
    159   EXPECT_FALSE(r.Changed());
    160   EXPECT_TRUE(dependencies()->IsEmpty());
    161 }
    162 
    163 
    164 TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalConstNumberWithDeoptimization) {
    165   const double kValue = -11.25;
    166   const char* kName = "kiwi";
    167   SetGlobalProperty(kName, kValue);
    168 
    169   Node* ret = ReturnLoadNamedFromGlobal(
    170       kName, graph()->start(), graph()->start(),
    171       JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
    172   graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
    173 
    174   Reduction r = Reduce(ret->InputAt(0),
    175                        JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
    176 
    177   // Check LoadNamed(global) => HeapConstant[kValue]
    178   ASSERT_TRUE(r.Changed());
    179   EXPECT_THAT(r.replacement(), IsNumberConstant(kValue));
    180 
    181   EXPECT_THAT(ret, IsReturn(IsNumberConstant(kValue), graph()->start(),
    182                             graph()->start()));
    183   EXPECT_THAT(graph()->end(), IsEnd(ret));
    184 
    185   EXPECT_FALSE(dependencies()->IsEmpty());
    186 }
    187 
    188 
    189 TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalConstString) {
    190   Handle<HeapObject> kValue = isolate()->factory()->undefined_string();
    191   const char* kName = "mango";
    192   SetGlobalProperty(kName, kValue);
    193 
    194   Node* ret = ReturnLoadNamedFromGlobal(
    195       kName, graph()->start(), graph()->start(),
    196       JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
    197   graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
    198 
    199   Reduction r = Reduce(ret->InputAt(0),
    200                        JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
    201   ASSERT_FALSE(r.Changed());
    202   EXPECT_TRUE(dependencies()->IsEmpty());
    203 }
    204 
    205 
    206 TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalConstStringWithDeoptimization) {
    207   Handle<HeapObject> kValue = isolate()->factory()->undefined_string();
    208   const char* kName = "mango";
    209   SetGlobalProperty(kName, kValue);
    210 
    211   Node* ret = ReturnLoadNamedFromGlobal(
    212       kName, graph()->start(), graph()->start(),
    213       JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
    214   graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
    215 
    216   Reduction r = Reduce(ret->InputAt(0),
    217                        JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
    218 
    219   // Check LoadNamed(global) => HeapConstant[kValue]
    220   ASSERT_TRUE(r.Changed());
    221   EXPECT_THAT(r.replacement(), IsHeapConstant(kValue));
    222 
    223   EXPECT_THAT(ret, IsReturn(IsHeapConstant(kValue), graph()->start(),
    224                             graph()->start()));
    225   EXPECT_THAT(graph()->end(), IsEnd(ret));
    226 
    227   EXPECT_FALSE(dependencies()->IsEmpty());
    228   dependencies()->Rollback();
    229 }
    230 
    231 
    232 TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalPropertyCellSmi) {
    233   const char* kName = "melon";
    234   SetGlobalProperty(kName, 123);
    235   SetGlobalProperty(kName, 124);
    236 
    237   Node* ret = ReturnLoadNamedFromGlobal(
    238       kName, graph()->start(), graph()->start(),
    239       JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
    240   graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
    241 
    242   Reduction r = Reduce(ret->InputAt(0),
    243                        JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
    244   ASSERT_FALSE(r.Changed());
    245   EXPECT_TRUE(dependencies()->IsEmpty());
    246 }
    247 
    248 
    249 TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalPropertyCellSmiWithDeoptimization) {
    250   const char* kName = "melon";
    251   SetGlobalProperty(kName, 123);
    252   SetGlobalProperty(kName, 124);
    253 
    254   Node* ret = ReturnLoadNamedFromGlobal(
    255       kName, graph()->start(), graph()->start(),
    256       JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
    257   graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
    258 
    259   Reduction r = Reduce(ret->InputAt(0),
    260                        JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
    261 
    262   // Check LoadNamed(global) => LoadField[PropertyCell::value](cell)
    263   ASSERT_TRUE(r.Changed());
    264   FieldAccess access = AccessBuilder::ForPropertyCellValue();
    265   Capture<Node*> cell_capture;
    266   Matcher<Node*> load_field_match = IsLoadField(
    267       access, CaptureEq(&cell_capture), graph()->start(), graph()->start());
    268   EXPECT_THAT(r.replacement(), load_field_match);
    269 
    270   HeapObjectMatcher cell(cell_capture.value());
    271   EXPECT_TRUE(cell.HasValue());
    272   EXPECT_TRUE(cell.Value()->IsPropertyCell());
    273 
    274   EXPECT_THAT(ret,
    275               IsReturn(load_field_match, load_field_match, graph()->start()));
    276   EXPECT_THAT(graph()->end(), IsEnd(ret));
    277 
    278   EXPECT_FALSE(dependencies()->IsEmpty());
    279   dependencies()->Rollback();
    280 }
    281 
    282 
    283 TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalPropertyCellString) {
    284   const char* kName = "pineapple";
    285   SetGlobalProperty(kName, isolate()->factory()->undefined_string());
    286   SetGlobalProperty(kName, isolate()->factory()->undefined_value());
    287 
    288   Node* ret = ReturnLoadNamedFromGlobal(
    289       kName, graph()->start(), graph()->start(),
    290       JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
    291   graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
    292 
    293   Reduction r = Reduce(ret->InputAt(0),
    294                        JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
    295   ASSERT_FALSE(r.Changed());
    296   EXPECT_TRUE(dependencies()->IsEmpty());
    297 }
    298 
    299 
    300 TEST_F(JSTypeFeedbackTest,
    301        JSLoadNamedGlobalPropertyCellStringWithDeoptimization) {
    302   const char* kName = "pineapple";
    303   SetGlobalProperty(kName, isolate()->factory()->undefined_string());
    304   SetGlobalProperty(kName, isolate()->factory()->undefined_value());
    305 
    306   Node* ret = ReturnLoadNamedFromGlobal(
    307       kName, graph()->start(), graph()->start(),
    308       JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
    309   graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
    310 
    311   Reduction r = Reduce(ret->InputAt(0),
    312                        JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
    313 
    314   // Check LoadNamed(global) => LoadField[PropertyCell::value](cell)
    315   ASSERT_TRUE(r.Changed());
    316   FieldAccess access = AccessBuilder::ForPropertyCellValue();
    317   Capture<Node*> cell_capture;
    318   Matcher<Node*> load_field_match = IsLoadField(
    319       access, CaptureEq(&cell_capture), graph()->start(), graph()->start());
    320   EXPECT_THAT(r.replacement(), load_field_match);
    321 
    322   HeapObjectMatcher cell(cell_capture.value());
    323   EXPECT_TRUE(cell.HasValue());
    324   EXPECT_TRUE(cell.Value()->IsPropertyCell());
    325 
    326   EXPECT_THAT(ret,
    327               IsReturn(load_field_match, load_field_match, graph()->start()));
    328   EXPECT_THAT(graph()->end(), IsEnd(ret));
    329 
    330   EXPECT_FALSE(dependencies()->IsEmpty());
    331   dependencies()->Rollback();
    332 }
    333 
    334 }  // namespace compiler
    335 }  // namespace internal
    336 }  // namespace v8
    337