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/access-builder.h" 6 #include "src/compiler/diamond.h" 7 #include "src/compiler/js-graph.h" 8 #include "src/compiler/js-intrinsic-lowering.h" 9 #include "src/compiler/js-operator.h" 10 #include "src/types-inl.h" 11 #include "test/unittests/compiler/graph-unittest.h" 12 #include "test/unittests/compiler/node-test-utils.h" 13 #include "testing/gmock-support.h" 14 15 16 using testing::_; 17 using testing::AllOf; 18 using testing::BitEq; 19 using testing::Capture; 20 using testing::CaptureEq; 21 22 23 namespace v8 { 24 namespace internal { 25 namespace compiler { 26 27 class JSIntrinsicLoweringTest : public TypedGraphTest { 28 public: 29 JSIntrinsicLoweringTest() : TypedGraphTest(3), javascript_(zone()) {} 30 ~JSIntrinsicLoweringTest() override {} 31 32 protected: 33 Reduction Reduce(Node* node, MachineOperatorBuilder::Flags flags = 34 MachineOperatorBuilder::kNoFlags) { 35 MachineOperatorBuilder machine(zone(), MachineType::PointerRepresentation(), 36 flags); 37 SimplifiedOperatorBuilder simplified(zone()); 38 JSGraph jsgraph(isolate(), graph(), common(), javascript(), &simplified, 39 &machine); 40 // TODO(titzer): mock the GraphReducer here for better unit testing. 41 GraphReducer graph_reducer(zone(), graph()); 42 JSIntrinsicLowering reducer(&graph_reducer, &jsgraph, 43 JSIntrinsicLowering::kDeoptimizationEnabled); 44 return reducer.Reduce(node); 45 } 46 47 Node* EmptyFrameState() { 48 MachineOperatorBuilder machine(zone()); 49 JSGraph jsgraph(isolate(), graph(), common(), javascript(), nullptr, 50 &machine); 51 return jsgraph.EmptyFrameState(); 52 } 53 54 JSOperatorBuilder* javascript() { return &javascript_; } 55 56 private: 57 JSOperatorBuilder javascript_; 58 }; 59 60 61 // ----------------------------------------------------------------------------- 62 // %_ConstructDouble 63 64 65 TEST_F(JSIntrinsicLoweringTest, InlineOptimizedConstructDouble) { 66 Node* const input0 = Parameter(0); 67 Node* const input1 = Parameter(1); 68 Node* const context = Parameter(2); 69 Node* const effect = graph()->start(); 70 Node* const control = graph()->start(); 71 Reduction const r = Reduce(graph()->NewNode( 72 javascript()->CallRuntime(Runtime::kInlineConstructDouble, 2), input0, 73 input1, context, effect, control)); 74 ASSERT_TRUE(r.Changed()); 75 EXPECT_THAT(r.replacement(), IsFloat64InsertHighWord32( 76 IsFloat64InsertLowWord32( 77 IsNumberConstant(BitEq(0.0)), input1), 78 input0)); 79 } 80 81 82 // ----------------------------------------------------------------------------- 83 // %_DoubleLo 84 85 86 TEST_F(JSIntrinsicLoweringTest, InlineOptimizedDoubleLo) { 87 Node* const input = Parameter(0); 88 Node* const context = Parameter(1); 89 Node* const effect = graph()->start(); 90 Node* const control = graph()->start(); 91 Reduction const r = Reduce( 92 graph()->NewNode(javascript()->CallRuntime(Runtime::kInlineDoubleLo, 1), 93 input, context, effect, control)); 94 ASSERT_TRUE(r.Changed()); 95 EXPECT_THAT(r.replacement(), IsFloat64ExtractLowWord32(input)); 96 } 97 98 99 // ----------------------------------------------------------------------------- 100 // %_DoubleHi 101 102 103 TEST_F(JSIntrinsicLoweringTest, InlineOptimizedDoubleHi) { 104 Node* const input = Parameter(0); 105 Node* const context = Parameter(1); 106 Node* const effect = graph()->start(); 107 Node* const control = graph()->start(); 108 Reduction const r = Reduce( 109 graph()->NewNode(javascript()->CallRuntime(Runtime::kInlineDoubleHi, 1), 110 input, context, effect, control)); 111 ASSERT_TRUE(r.Changed()); 112 EXPECT_THAT(r.replacement(), IsFloat64ExtractHighWord32(input)); 113 } 114 115 116 // ----------------------------------------------------------------------------- 117 // %_IsSmi 118 119 120 TEST_F(JSIntrinsicLoweringTest, InlineIsSmi) { 121 Node* const input = Parameter(0); 122 Node* const context = Parameter(1); 123 Node* const effect = graph()->start(); 124 Node* const control = graph()->start(); 125 Reduction const r = Reduce( 126 graph()->NewNode(javascript()->CallRuntime(Runtime::kInlineIsSmi, 1), 127 input, context, effect, control)); 128 ASSERT_TRUE(r.Changed()); 129 EXPECT_THAT(r.replacement(), IsObjectIsSmi(input)); 130 } 131 132 133 // ----------------------------------------------------------------------------- 134 // %_IsArray 135 136 137 TEST_F(JSIntrinsicLoweringTest, InlineIsArray) { 138 Node* const input = Parameter(0); 139 Node* const context = Parameter(1); 140 Node* const effect = graph()->start(); 141 Node* const control = graph()->start(); 142 Reduction const r = Reduce( 143 graph()->NewNode(javascript()->CallRuntime(Runtime::kInlineIsArray, 1), 144 input, context, effect, control)); 145 ASSERT_TRUE(r.Changed()); 146 147 Node* phi = r.replacement(); 148 Capture<Node*> branch, if_false; 149 EXPECT_THAT( 150 phi, 151 IsPhi( 152 MachineRepresentation::kTagged, IsFalseConstant(), 153 IsWord32Equal(IsLoadField(AccessBuilder::ForMapInstanceType(), 154 IsLoadField(AccessBuilder::ForMap(), input, 155 effect, CaptureEq(&if_false)), 156 effect, _), 157 IsInt32Constant(JS_ARRAY_TYPE)), 158 IsMerge(IsIfTrue(AllOf(CaptureEq(&branch), 159 IsBranch(IsObjectIsSmi(input), control))), 160 AllOf(CaptureEq(&if_false), IsIfFalse(CaptureEq(&branch)))))); 161 } 162 163 164 // ----------------------------------------------------------------------------- 165 // %_IsDate 166 167 168 TEST_F(JSIntrinsicLoweringTest, InlineIsDate) { 169 Node* const input = Parameter(0); 170 Node* const context = Parameter(1); 171 Node* const effect = graph()->start(); 172 Node* const control = graph()->start(); 173 Reduction const r = Reduce( 174 graph()->NewNode(javascript()->CallRuntime(Runtime::kInlineIsDate, 1), 175 input, context, effect, control)); 176 ASSERT_TRUE(r.Changed()); 177 178 Node* phi = r.replacement(); 179 Capture<Node*> branch, if_false; 180 EXPECT_THAT( 181 phi, 182 IsPhi( 183 MachineRepresentation::kTagged, IsFalseConstant(), 184 IsWord32Equal(IsLoadField(AccessBuilder::ForMapInstanceType(), 185 IsLoadField(AccessBuilder::ForMap(), input, 186 effect, CaptureEq(&if_false)), 187 effect, _), 188 IsInt32Constant(JS_DATE_TYPE)), 189 IsMerge(IsIfTrue(AllOf(CaptureEq(&branch), 190 IsBranch(IsObjectIsSmi(input), control))), 191 AllOf(CaptureEq(&if_false), IsIfFalse(CaptureEq(&branch)))))); 192 } 193 194 195 // ----------------------------------------------------------------------------- 196 // %_IsTypedArray 197 198 199 TEST_F(JSIntrinsicLoweringTest, InlineIsTypedArray) { 200 Node* const input = Parameter(0); 201 Node* const context = Parameter(1); 202 Node* const effect = graph()->start(); 203 Node* const control = graph()->start(); 204 Reduction const r = Reduce(graph()->NewNode( 205 javascript()->CallRuntime(Runtime::kInlineIsTypedArray, 1), input, 206 context, effect, control)); 207 ASSERT_TRUE(r.Changed()); 208 209 Node* phi = r.replacement(); 210 Capture<Node*> branch, if_false; 211 EXPECT_THAT( 212 phi, 213 IsPhi( 214 MachineRepresentation::kTagged, IsFalseConstant(), 215 IsWord32Equal(IsLoadField(AccessBuilder::ForMapInstanceType(), 216 IsLoadField(AccessBuilder::ForMap(), input, 217 effect, CaptureEq(&if_false)), 218 effect, _), 219 IsInt32Constant(JS_TYPED_ARRAY_TYPE)), 220 IsMerge(IsIfTrue(AllOf(CaptureEq(&branch), 221 IsBranch(IsObjectIsSmi(input), control))), 222 AllOf(CaptureEq(&if_false), IsIfFalse(CaptureEq(&branch)))))); 223 } 224 225 226 // ----------------------------------------------------------------------------- 227 // %_IsFunction 228 229 230 TEST_F(JSIntrinsicLoweringTest, InlineIsFunction) { 231 Node* const input = Parameter(Type::Any()); 232 Node* const context = Parameter(Type::Any()); 233 Node* const effect = graph()->start(); 234 Node* const control = graph()->start(); 235 Reduction const r = Reduce( 236 graph()->NewNode(javascript()->CallRuntime(Runtime::kInlineIsFunction, 1), 237 input, context, effect, control)); 238 ASSERT_TRUE(r.Changed()); 239 240 Node* phi = r.replacement(); 241 Capture<Node*> branch, if_false; 242 EXPECT_THAT( 243 phi, 244 IsPhi( 245 MachineRepresentation::kTagged, IsFalseConstant(), 246 IsUint32LessThanOrEqual( 247 IsInt32Constant(FIRST_FUNCTION_TYPE), 248 IsLoadField(AccessBuilder::ForMapInstanceType(), 249 IsLoadField(AccessBuilder::ForMap(), input, effect, 250 CaptureEq(&if_false)), 251 effect, _)), 252 IsMerge(IsIfTrue(AllOf(CaptureEq(&branch), 253 IsBranch(IsObjectIsSmi(input), control))), 254 AllOf(CaptureEq(&if_false), IsIfFalse(CaptureEq(&branch)))))); 255 } 256 257 258 // ----------------------------------------------------------------------------- 259 // %_IsRegExp 260 261 262 TEST_F(JSIntrinsicLoweringTest, InlineIsRegExp) { 263 Node* const input = Parameter(0); 264 Node* const context = Parameter(1); 265 Node* const effect = graph()->start(); 266 Node* const control = graph()->start(); 267 Reduction const r = Reduce( 268 graph()->NewNode(javascript()->CallRuntime(Runtime::kInlineIsRegExp, 1), 269 input, context, effect, control)); 270 ASSERT_TRUE(r.Changed()); 271 272 Node* phi = r.replacement(); 273 Capture<Node*> branch, if_false; 274 EXPECT_THAT( 275 phi, 276 IsPhi( 277 MachineRepresentation::kTagged, IsFalseConstant(), 278 IsWord32Equal(IsLoadField(AccessBuilder::ForMapInstanceType(), 279 IsLoadField(AccessBuilder::ForMap(), input, 280 effect, CaptureEq(&if_false)), 281 effect, _), 282 IsInt32Constant(JS_REGEXP_TYPE)), 283 IsMerge(IsIfTrue(AllOf(CaptureEq(&branch), 284 IsBranch(IsObjectIsSmi(input), control))), 285 AllOf(CaptureEq(&if_false), IsIfFalse(CaptureEq(&branch)))))); 286 } 287 288 289 // ----------------------------------------------------------------------------- 290 // %_IsJSReceiver 291 292 293 TEST_F(JSIntrinsicLoweringTest, InlineIsJSReceiverWithAny) { 294 Node* const input = Parameter(Type::Any()); 295 Node* const context = Parameter(Type::Any()); 296 Node* const effect = graph()->start(); 297 Node* const control = graph()->start(); 298 Reduction const r = Reduce(graph()->NewNode( 299 javascript()->CallRuntime(Runtime::kInlineIsJSReceiver, 1), input, 300 context, effect, control)); 301 ASSERT_TRUE(r.Changed()); 302 303 Node* phi = r.replacement(); 304 Capture<Node *> branch, if_false; 305 EXPECT_THAT( 306 phi, 307 IsPhi( 308 MachineRepresentation::kTagged, IsFalseConstant(), 309 IsUint32LessThanOrEqual( 310 IsInt32Constant(FIRST_JS_RECEIVER_TYPE), 311 IsLoadField(AccessBuilder::ForMapInstanceType(), 312 IsLoadField(AccessBuilder::ForMap(), input, effect, 313 CaptureEq(&if_false)), 314 effect, _)), 315 IsMerge(IsIfTrue(AllOf(CaptureEq(&branch), 316 IsBranch(IsObjectIsSmi(input), control))), 317 AllOf(CaptureEq(&if_false), IsIfFalse(CaptureEq(&branch)))))); 318 } 319 320 321 TEST_F(JSIntrinsicLoweringTest, InlineIsJSReceiverWithReceiver) { 322 Node* const input = Parameter(Type::Receiver()); 323 Node* const context = Parameter(Type::Any()); 324 Node* const effect = graph()->start(); 325 Node* const control = graph()->start(); 326 Reduction const r = Reduce(graph()->NewNode( 327 javascript()->CallRuntime(Runtime::kInlineIsJSReceiver, 1), input, 328 context, effect, control)); 329 ASSERT_TRUE(r.Changed()); 330 EXPECT_THAT(r.replacement(), IsTrueConstant()); 331 } 332 333 334 TEST_F(JSIntrinsicLoweringTest, InlineIsJSReceiverWithUndefined) { 335 Node* const input = Parameter(Type::Undefined()); 336 Node* const context = Parameter(Type::Any()); 337 Node* const effect = graph()->start(); 338 Node* const control = graph()->start(); 339 Reduction const r = Reduce(graph()->NewNode( 340 javascript()->CallRuntime(Runtime::kInlineIsJSReceiver, 1), input, 341 context, effect, control)); 342 ASSERT_TRUE(r.Changed()); 343 EXPECT_THAT(r.replacement(), IsFalseConstant()); 344 } 345 346 347 // ----------------------------------------------------------------------------- 348 // %_JSValueGetValue 349 350 351 TEST_F(JSIntrinsicLoweringTest, InlineJSValueGetValue) { 352 Node* const input = Parameter(0); 353 Node* const context = Parameter(1); 354 Node* const effect = graph()->start(); 355 Node* const control = graph()->start(); 356 Reduction const r = Reduce(graph()->NewNode( 357 javascript()->CallRuntime(Runtime::kInlineJSValueGetValue, 1), input, 358 context, effect, control)); 359 ASSERT_TRUE(r.Changed()); 360 EXPECT_THAT(r.replacement(), 361 IsLoadField(AccessBuilder::ForValue(), input, effect, control)); 362 } 363 364 365 // ----------------------------------------------------------------------------- 366 // %_MathFloor 367 368 369 TEST_F(JSIntrinsicLoweringTest, InlineMathFloor) { 370 Node* const input = Parameter(0); 371 Node* const context = Parameter(1); 372 Node* const effect = graph()->start(); 373 Node* const control = graph()->start(); 374 Reduction const r = Reduce( 375 graph()->NewNode(javascript()->CallRuntime(Runtime::kInlineMathFloor, 1), 376 input, context, effect, control), 377 MachineOperatorBuilder::kFloat64RoundDown); 378 ASSERT_TRUE(r.Changed()); 379 EXPECT_THAT(r.replacement(), IsFloat64RoundDown(input)); 380 } 381 382 383 // ----------------------------------------------------------------------------- 384 // %_MathSqrt 385 386 387 TEST_F(JSIntrinsicLoweringTest, InlineMathSqrt) { 388 Node* const input = Parameter(0); 389 Node* const context = Parameter(1); 390 Node* const effect = graph()->start(); 391 Node* const control = graph()->start(); 392 Reduction const r = Reduce( 393 graph()->NewNode(javascript()->CallRuntime(Runtime::kInlineMathSqrt, 1), 394 input, context, effect, control)); 395 ASSERT_TRUE(r.Changed()); 396 EXPECT_THAT(r.replacement(), IsFloat64Sqrt(input)); 397 } 398 399 400 // ----------------------------------------------------------------------------- 401 // %_MathClz32 402 403 404 TEST_F(JSIntrinsicLoweringTest, InlineMathClz32) { 405 Node* const input = Parameter(0); 406 Node* const context = Parameter(1); 407 Node* const effect = graph()->start(); 408 Node* const control = graph()->start(); 409 Reduction const r = Reduce( 410 graph()->NewNode(javascript()->CallRuntime(Runtime::kInlineMathClz32, 1), 411 input, context, effect, control)); 412 ASSERT_TRUE(r.Changed()); 413 EXPECT_THAT(r.replacement(), IsWord32Clz(input)); 414 } 415 416 417 // ----------------------------------------------------------------------------- 418 // %_ValueOf 419 420 421 TEST_F(JSIntrinsicLoweringTest, InlineValueOf) { 422 Node* const input = Parameter(0); 423 Node* const context = Parameter(1); 424 Node* const effect = graph()->start(); 425 Node* const control = graph()->start(); 426 Reduction const r = Reduce( 427 graph()->NewNode(javascript()->CallRuntime(Runtime::kInlineValueOf, 1), 428 input, context, effect, control)); 429 ASSERT_TRUE(r.Changed()); 430 431 Node* phi = r.replacement(); 432 Capture<Node*> branch0, if_false0, branch1, if_true1; 433 EXPECT_THAT( 434 phi, 435 IsPhi( 436 MachineRepresentation::kTagged, input, 437 IsPhi(MachineRepresentation::kTagged, 438 IsLoadField(AccessBuilder::ForValue(), input, effect, 439 CaptureEq(&if_true1)), 440 input, 441 IsMerge( 442 AllOf(CaptureEq(&if_true1), IsIfTrue(CaptureEq(&branch1))), 443 IsIfFalse(AllOf( 444 CaptureEq(&branch1), 445 IsBranch( 446 IsWord32Equal( 447 IsLoadField( 448 AccessBuilder::ForMapInstanceType(), 449 IsLoadField(AccessBuilder::ForMap(), input, 450 effect, CaptureEq(&if_false0)), 451 effect, _), 452 IsInt32Constant(JS_VALUE_TYPE)), 453 CaptureEq(&if_false0)))))), 454 IsMerge( 455 IsIfTrue(AllOf(CaptureEq(&branch0), 456 IsBranch(IsObjectIsSmi(input), control))), 457 AllOf(CaptureEq(&if_false0), IsIfFalse(CaptureEq(&branch0)))))); 458 } 459 460 } // namespace compiler 461 } // namespace internal 462 } // namespace v8 463