js-type-feedback-unittest.cc 11.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
// Copyright 2015 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.

#include "src/compiler.h"

#include "src/compiler/access-builder.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/js-operator.h"
#include "src/compiler/js-type-feedback.h"
#include "src/compiler/machine-operator.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/operator-properties.h"

#include "test/unittests/compiler/compiler-test-utils.h"
#include "test/unittests/compiler/graph-unittest.h"
#include "test/unittests/compiler/node-test-utils.h"
#include "testing/gmock-support.h"

using testing::Capture;


namespace v8 {
namespace internal {
namespace compiler {

class JSTypeFeedbackTest : public TypedGraphTest {
 public:
  JSTypeFeedbackTest()
      : TypedGraphTest(3),
        javascript_(zone()),
        dependencies_(isolate(), zone()) {}
  ~JSTypeFeedbackTest() override { dependencies_.Rollback(); }

 protected:
37 38
  Reduction Reduce(Node* node,
                   JSTypeFeedbackSpecializer::DeoptimizationMode mode) {
39 40 41 42
    Handle<GlobalObject> global_object(
        isolate()->native_context()->global_object(), isolate());

    MachineOperatorBuilder machine(zone());
43 44
    SimplifiedOperatorBuilder simplified(zone());
    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 52 53 54 55
    return reducer.Reduce(node);
  }

  Node* EmptyFrameState() {
    MachineOperatorBuilder machine(zone());
56 57
    JSGraph jsgraph(isolate(), graph(), common(), javascript(), nullptr,
                    &machine);
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
    return jsgraph.EmptyFrameState();
  }

  JSOperatorBuilder* javascript() { return &javascript_; }

  void SetGlobalProperty(const char* string, int value) {
    SetGlobalProperty(string, Handle<Smi>(Smi::FromInt(value), isolate()));
  }

  void SetGlobalProperty(const char* string, double value) {
    SetGlobalProperty(string, isolate()->factory()->NewNumber(value));
  }

  void SetGlobalProperty(const char* string, Handle<Object> value) {
    Handle<JSObject> global(isolate()->context()->global_object(), isolate());
    Handle<String> name =
        isolate()->factory()->NewStringFromAsciiChecked(string);
    MaybeHandle<Object> result =
        JSReceiver::SetProperty(global, name, value, SLOPPY);
    result.Assert();
  }

80 81 82
  Node* ReturnLoadNamedFromGlobal(
      const char* string, Node* effect, Node* control,
      JSTypeFeedbackSpecializer::DeoptimizationMode mode) {
83
    VectorSlotPair feedback;
84
    Node* vector = UndefinedConstant();
85 86
    Node* context = UndefinedConstant();

87
    Handle<Name> name = isolate()->factory()->InternalizeUtf8String(string);
88
    const Operator* op = javascript()->LoadGlobal(name, feedback);
89 90
    Node* load = graph()->NewNode(op, vector, context, EmptyFrameState(),
                                  EmptyFrameState(), effect, control);
91 92 93 94 95 96 97 98 99 100 101 102
    Node* if_success = graph()->NewNode(common()->IfSuccess(), load);
    return graph()->NewNode(common()->Return(), load, load, if_success);
  }

  CompilationDependencies* dependencies() { return &dependencies_; }

 private:
  JSOperatorBuilder javascript_;
  CompilationDependencies dependencies_;
};


103 104 105 106
TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalConstSmi) {
  const int kValue = 111;
  const char* kName = "banana";
  SetGlobalProperty(kName, kValue);
107

108 109 110
  Node* ret = ReturnLoadNamedFromGlobal(
      kName, graph()->start(), graph()->start(),
      JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
111
  graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
112

113 114 115 116 117
  Reduction r = Reduce(ret->InputAt(0),
                       JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
  EXPECT_FALSE(r.Changed());
  EXPECT_TRUE(dependencies()->IsEmpty());
}
118 119


120 121 122 123
TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalConstSmiWithDeoptimization) {
  const int kValue = 111;
  const char* kName = "banana";
  SetGlobalProperty(kName, kValue);
124

125 126 127
  Node* ret = ReturnLoadNamedFromGlobal(
      kName, graph()->start(), graph()->start(),
      JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
128
  graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
129

130 131 132 133 134 135 136 137 138 139 140 141 142
  Reduction r = Reduce(ret->InputAt(0),
                       JSTypeFeedbackSpecializer::kDeoptimizationEnabled);

  // Check LoadNamed(global) => HeapConstant[kValue]
  ASSERT_TRUE(r.Changed());
  EXPECT_THAT(r.replacement(), IsNumberConstant(kValue));

  EXPECT_THAT(ret, IsReturn(IsNumberConstant(kValue), graph()->start(),
                            graph()->start()));
  EXPECT_THAT(graph()->end(), IsEnd(ret));

  EXPECT_FALSE(dependencies()->IsEmpty());
  dependencies()->Rollback();
143 144 145
}


146 147 148 149
TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalConstNumber) {
  const double kValue = -11.25;
  const char* kName = "kiwi";
  SetGlobalProperty(kName, kValue);
150

151 152 153
  Node* ret = ReturnLoadNamedFromGlobal(
      kName, graph()->start(), graph()->start(),
      JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
154
  graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
155

156 157
  Reduction r = Reduce(ret->InputAt(0),
                       JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
158

159 160 161
  EXPECT_FALSE(r.Changed());
  EXPECT_TRUE(dependencies()->IsEmpty());
}
162 163


164 165 166 167 168 169 170 171
TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalConstNumberWithDeoptimization) {
  const double kValue = -11.25;
  const char* kName = "kiwi";
  SetGlobalProperty(kName, kValue);

  Node* ret = ReturnLoadNamedFromGlobal(
      kName, graph()->start(), graph()->start(),
      JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
172
  graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
173 174 175 176 177 178 179 180 181 182 183 184 185

  Reduction r = Reduce(ret->InputAt(0),
                       JSTypeFeedbackSpecializer::kDeoptimizationEnabled);

  // Check LoadNamed(global) => HeapConstant[kValue]
  ASSERT_TRUE(r.Changed());
  EXPECT_THAT(r.replacement(), IsNumberConstant(kValue));

  EXPECT_THAT(ret, IsReturn(IsNumberConstant(kValue), graph()->start(),
                            graph()->start()));
  EXPECT_THAT(graph()->end(), IsEnd(ret));

  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 195 196

  Node* ret = ReturnLoadNamedFromGlobal(
      kName, graph()->start(), graph()->start(),
      JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
197
  graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
198 199 200 201 202

  Reduction r = Reduce(ret->InputAt(0),
                       JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
  ASSERT_FALSE(r.Changed());
  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 212 213

  Node* ret = ReturnLoadNamedFromGlobal(
      kName, graph()->start(), graph()->start(),
      JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
214
  graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
215 216 217 218 219 220 221 222 223 224 225 226 227 228

  Reduction r = Reduce(ret->InputAt(0),
                       JSTypeFeedbackSpecializer::kDeoptimizationEnabled);

  // Check LoadNamed(global) => HeapConstant[kValue]
  ASSERT_TRUE(r.Changed());
  EXPECT_THAT(r.replacement(), IsHeapConstant(kValue));

  EXPECT_THAT(ret, IsReturn(IsHeapConstant(kValue), graph()->start(),
                            graph()->start()));
  EXPECT_THAT(graph()->end(), IsEnd(ret));

  EXPECT_FALSE(dependencies()->IsEmpty());
  dependencies()->Rollback();
229 230 231
}


232 233 234 235 236 237 238 239
TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalPropertyCellSmi) {
  const char* kName = "melon";
  SetGlobalProperty(kName, 123);
  SetGlobalProperty(kName, 124);

  Node* ret = ReturnLoadNamedFromGlobal(
      kName, graph()->start(), graph()->start(),
      JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
240
  graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
241 242 243 244 245

  Reduction r = Reduce(ret->InputAt(0),
                       JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
  ASSERT_FALSE(r.Changed());
  EXPECT_TRUE(dependencies()->IsEmpty());
246
}
247 248 249 250 251 252 253 254 255 256


TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalPropertyCellSmiWithDeoptimization) {
  const char* kName = "melon";
  SetGlobalProperty(kName, 123);
  SetGlobalProperty(kName, 124);

  Node* ret = ReturnLoadNamedFromGlobal(
      kName, graph()->start(), graph()->start(),
      JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
257
  graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
258 259 260 261 262 263 264 265 266 267 268 269

  Reduction r = Reduce(ret->InputAt(0),
                       JSTypeFeedbackSpecializer::kDeoptimizationEnabled);

  // Check LoadNamed(global) => LoadField[PropertyCell::value](cell)
  ASSERT_TRUE(r.Changed());
  FieldAccess access = AccessBuilder::ForPropertyCellValue();
  Capture<Node*> cell_capture;
  Matcher<Node*> load_field_match = IsLoadField(
      access, CaptureEq(&cell_capture), graph()->start(), graph()->start());
  EXPECT_THAT(r.replacement(), load_field_match);

270
  HeapObjectMatcher cell(cell_capture.value());
271
  EXPECT_TRUE(cell.HasValue());
272
  EXPECT_TRUE(cell.Value()->IsPropertyCell());
273 274 275 276 277 278 279

  EXPECT_THAT(ret,
              IsReturn(load_field_match, load_field_match, graph()->start()));
  EXPECT_THAT(graph()->end(), IsEnd(ret));

  EXPECT_FALSE(dependencies()->IsEmpty());
  dependencies()->Rollback();
280
}
281 282 283 284 285 286 287 288 289 290


TEST_F(JSTypeFeedbackTest, JSLoadNamedGlobalPropertyCellString) {
  const char* kName = "pineapple";
  SetGlobalProperty(kName, isolate()->factory()->undefined_string());
  SetGlobalProperty(kName, isolate()->factory()->undefined_value());

  Node* ret = ReturnLoadNamedFromGlobal(
      kName, graph()->start(), graph()->start(),
      JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
291
  graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
292 293 294 295 296

  Reduction r = Reduce(ret->InputAt(0),
                       JSTypeFeedbackSpecializer::kDeoptimizationDisabled);
  ASSERT_FALSE(r.Changed());
  EXPECT_TRUE(dependencies()->IsEmpty());
297
}
298 299 300 301 302 303 304 305 306 307 308


TEST_F(JSTypeFeedbackTest,
       JSLoadNamedGlobalPropertyCellStringWithDeoptimization) {
  const char* kName = "pineapple";
  SetGlobalProperty(kName, isolate()->factory()->undefined_string());
  SetGlobalProperty(kName, isolate()->factory()->undefined_value());

  Node* ret = ReturnLoadNamedFromGlobal(
      kName, graph()->start(), graph()->start(),
      JSTypeFeedbackSpecializer::kDeoptimizationEnabled);
309
  graph()->SetEnd(graph()->NewNode(common()->End(1), ret));
310 311 312 313 314 315 316 317 318 319 320 321

  Reduction r = Reduce(ret->InputAt(0),
                       JSTypeFeedbackSpecializer::kDeoptimizationEnabled);

  // Check LoadNamed(global) => LoadField[PropertyCell::value](cell)
  ASSERT_TRUE(r.Changed());
  FieldAccess access = AccessBuilder::ForPropertyCellValue();
  Capture<Node*> cell_capture;
  Matcher<Node*> load_field_match = IsLoadField(
      access, CaptureEq(&cell_capture), graph()->start(), graph()->start());
  EXPECT_THAT(r.replacement(), load_field_match);

322
  HeapObjectMatcher cell(cell_capture.value());
323
  EXPECT_TRUE(cell.HasValue());
324
  EXPECT_TRUE(cell.Value()->IsPropertyCell());
325 326 327 328 329 330 331

  EXPECT_THAT(ret,
              IsReturn(load_field_match, load_field_match, graph()->start()));
  EXPECT_THAT(graph()->end(), IsEnd(ret));

  EXPECT_FALSE(dependencies()->IsEmpty());
  dependencies()->Rollback();
332
}
333 334 335 336

}  // namespace compiler
}  // namespace internal
}  // namespace v8