1// Copyright 2014 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/change-lowering.h" 6#include "src/compiler/compiler-test-utils.h" 7#include "src/compiler/graph-unittest.h" 8#include "src/compiler/js-graph.h" 9#include "src/compiler/node-properties-inl.h" 10#include "src/compiler/simplified-operator.h" 11#include "src/compiler/typer.h" 12#include "testing/gmock-support.h" 13 14using testing::_; 15using testing::AllOf; 16using testing::Capture; 17using testing::CaptureEq; 18 19namespace v8 { 20namespace internal { 21namespace compiler { 22 23// TODO(bmeurer): Find a new home for these functions. 24inline std::ostream& operator<<(std::ostream& os, const MachineType& type) { 25 OStringStream ost; 26 ost << type; 27 return os << ost.c_str(); 28} 29 30 31class ChangeLoweringTest : public GraphTest { 32 public: 33 ChangeLoweringTest() : simplified_(zone()) {} 34 virtual ~ChangeLoweringTest() {} 35 36 virtual MachineType WordRepresentation() const = 0; 37 38 protected: 39 int HeapNumberValueOffset() const { 40 STATIC_ASSERT(HeapNumber::kValueOffset % kApiPointerSize == 0); 41 return (HeapNumber::kValueOffset / kApiPointerSize) * PointerSize() - 42 kHeapObjectTag; 43 } 44 bool Is32() const { return WordRepresentation() == kRepWord32; } 45 int PointerSize() const { 46 switch (WordRepresentation()) { 47 case kRepWord32: 48 return 4; 49 case kRepWord64: 50 return 8; 51 default: 52 break; 53 } 54 UNREACHABLE(); 55 return 0; 56 } 57 int SmiMaxValue() const { return -(SmiMinValue() + 1); } 58 int SmiMinValue() const { 59 return static_cast<int>(0xffffffffu << (SmiValueSize() - 1)); 60 } 61 int SmiShiftAmount() const { return kSmiTagSize + SmiShiftSize(); } 62 int SmiShiftSize() const { 63 return Is32() ? SmiTagging<4>::SmiShiftSize() 64 : SmiTagging<8>::SmiShiftSize(); 65 } 66 int SmiValueSize() const { 67 return Is32() ? SmiTagging<4>::SmiValueSize() 68 : SmiTagging<8>::SmiValueSize(); 69 } 70 71 Node* Parameter(int32_t index = 0) { 72 return graph()->NewNode(common()->Parameter(index), graph()->start()); 73 } 74 75 Reduction Reduce(Node* node) { 76 Typer typer(zone()); 77 MachineOperatorBuilder machine(WordRepresentation()); 78 JSOperatorBuilder javascript(zone()); 79 JSGraph jsgraph(graph(), common(), &javascript, &typer, &machine); 80 CompilationInfo info(isolate(), zone()); 81 Linkage linkage(&info); 82 ChangeLowering reducer(&jsgraph, &linkage); 83 return reducer.Reduce(node); 84 } 85 86 SimplifiedOperatorBuilder* simplified() { return &simplified_; } 87 88 Matcher<Node*> IsAllocateHeapNumber(const Matcher<Node*>& effect_matcher, 89 const Matcher<Node*>& control_matcher) { 90 return IsCall( 91 _, IsHeapConstant(Unique<HeapObject>::CreateImmovable( 92 CEntryStub(isolate(), 1).GetCode())), 93 IsExternalConstant(ExternalReference( 94 Runtime::FunctionForId(Runtime::kAllocateHeapNumber), isolate())), 95 IsInt32Constant(0), IsNumberConstant(0.0), effect_matcher, 96 control_matcher); 97 } 98 Matcher<Node*> IsWordEqual(const Matcher<Node*>& lhs_matcher, 99 const Matcher<Node*>& rhs_matcher) { 100 return Is32() ? IsWord32Equal(lhs_matcher, rhs_matcher) 101 : IsWord64Equal(lhs_matcher, rhs_matcher); 102 } 103 104 private: 105 SimplifiedOperatorBuilder simplified_; 106}; 107 108 109// ----------------------------------------------------------------------------- 110// Common. 111 112 113class ChangeLoweringCommonTest 114 : public ChangeLoweringTest, 115 public ::testing::WithParamInterface<MachineType> { 116 public: 117 virtual ~ChangeLoweringCommonTest() {} 118 119 virtual MachineType WordRepresentation() const FINAL OVERRIDE { 120 return GetParam(); 121 } 122}; 123 124 125TARGET_TEST_P(ChangeLoweringCommonTest, ChangeBitToBool) { 126 Node* val = Parameter(0); 127 Node* node = graph()->NewNode(simplified()->ChangeBitToBool(), val); 128 Reduction reduction = Reduce(node); 129 ASSERT_TRUE(reduction.Changed()); 130 131 Node* phi = reduction.replacement(); 132 Capture<Node*> branch; 133 EXPECT_THAT(phi, 134 IsPhi(static_cast<MachineType>(kTypeBool | kRepTagged), 135 IsTrueConstant(), IsFalseConstant(), 136 IsMerge(IsIfTrue(AllOf(CaptureEq(&branch), 137 IsBranch(val, graph()->start()))), 138 IsIfFalse(CaptureEq(&branch))))); 139} 140 141 142TARGET_TEST_P(ChangeLoweringCommonTest, ChangeBoolToBit) { 143 Node* val = Parameter(0); 144 Node* node = graph()->NewNode(simplified()->ChangeBoolToBit(), val); 145 Reduction reduction = Reduce(node); 146 ASSERT_TRUE(reduction.Changed()); 147 148 EXPECT_THAT(reduction.replacement(), IsWordEqual(val, IsTrueConstant())); 149} 150 151 152TARGET_TEST_P(ChangeLoweringCommonTest, ChangeFloat64ToTagged) { 153 Node* val = Parameter(0); 154 Node* node = graph()->NewNode(simplified()->ChangeFloat64ToTagged(), val); 155 Reduction reduction = Reduce(node); 156 ASSERT_TRUE(reduction.Changed()); 157 158 Node* finish = reduction.replacement(); 159 Capture<Node*> heap_number; 160 EXPECT_THAT( 161 finish, 162 IsFinish( 163 AllOf(CaptureEq(&heap_number), 164 IsAllocateHeapNumber(IsValueEffect(val), graph()->start())), 165 IsStore(kMachFloat64, kNoWriteBarrier, CaptureEq(&heap_number), 166 IsInt32Constant(HeapNumberValueOffset()), val, 167 CaptureEq(&heap_number), graph()->start()))); 168} 169 170 171TARGET_TEST_P(ChangeLoweringCommonTest, StringAdd) { 172 Node* node = 173 graph()->NewNode(simplified()->StringAdd(), Parameter(0), Parameter(1)); 174 Reduction reduction = Reduce(node); 175 EXPECT_FALSE(reduction.Changed()); 176} 177 178 179INSTANTIATE_TEST_CASE_P(ChangeLoweringTest, ChangeLoweringCommonTest, 180 ::testing::Values(kRepWord32, kRepWord64)); 181 182 183// ----------------------------------------------------------------------------- 184// 32-bit 185 186 187class ChangeLowering32Test : public ChangeLoweringTest { 188 public: 189 virtual ~ChangeLowering32Test() {} 190 virtual MachineType WordRepresentation() const FINAL OVERRIDE { 191 return kRepWord32; 192 } 193}; 194 195 196TARGET_TEST_F(ChangeLowering32Test, ChangeInt32ToTagged) { 197 Node* val = Parameter(0); 198 Node* node = graph()->NewNode(simplified()->ChangeInt32ToTagged(), val); 199 Reduction reduction = Reduce(node); 200 ASSERT_TRUE(reduction.Changed()); 201 202 Node* phi = reduction.replacement(); 203 Capture<Node*> add, branch, heap_number, if_true; 204 EXPECT_THAT( 205 phi, 206 IsPhi(kMachAnyTagged, 207 IsFinish( 208 AllOf(CaptureEq(&heap_number), 209 IsAllocateHeapNumber(_, CaptureEq(&if_true))), 210 IsStore(kMachFloat64, kNoWriteBarrier, CaptureEq(&heap_number), 211 IsInt32Constant(HeapNumberValueOffset()), 212 IsChangeInt32ToFloat64(val), CaptureEq(&heap_number), 213 CaptureEq(&if_true))), 214 IsProjection( 215 0, AllOf(CaptureEq(&add), IsInt32AddWithOverflow(val, val))), 216 IsMerge(AllOf(CaptureEq(&if_true), IsIfTrue(CaptureEq(&branch))), 217 IsIfFalse(AllOf(CaptureEq(&branch), 218 IsBranch(IsProjection(1, CaptureEq(&add)), 219 graph()->start())))))); 220} 221 222 223TARGET_TEST_F(ChangeLowering32Test, ChangeTaggedToFloat64) { 224 STATIC_ASSERT(kSmiTag == 0); 225 STATIC_ASSERT(kSmiTagSize == 1); 226 227 Node* val = Parameter(0); 228 Node* node = graph()->NewNode(simplified()->ChangeTaggedToFloat64(), val); 229 Reduction reduction = Reduce(node); 230 ASSERT_TRUE(reduction.Changed()); 231 232 Node* phi = reduction.replacement(); 233 Capture<Node*> branch, if_true; 234 EXPECT_THAT( 235 phi, 236 IsPhi( 237 kMachFloat64, 238 IsLoad(kMachFloat64, val, IsInt32Constant(HeapNumberValueOffset()), 239 IsControlEffect(CaptureEq(&if_true))), 240 IsChangeInt32ToFloat64( 241 IsWord32Sar(val, IsInt32Constant(SmiShiftAmount()))), 242 IsMerge( 243 AllOf(CaptureEq(&if_true), 244 IsIfTrue(AllOf( 245 CaptureEq(&branch), 246 IsBranch(IsWord32And(val, IsInt32Constant(kSmiTagMask)), 247 graph()->start())))), 248 IsIfFalse(CaptureEq(&branch))))); 249} 250 251 252TARGET_TEST_F(ChangeLowering32Test, ChangeTaggedToInt32) { 253 STATIC_ASSERT(kSmiTag == 0); 254 STATIC_ASSERT(kSmiTagSize == 1); 255 256 Node* val = Parameter(0); 257 Node* node = graph()->NewNode(simplified()->ChangeTaggedToInt32(), val); 258 Reduction reduction = Reduce(node); 259 ASSERT_TRUE(reduction.Changed()); 260 261 Node* phi = reduction.replacement(); 262 Capture<Node*> branch, if_true; 263 EXPECT_THAT( 264 phi, 265 IsPhi(kMachInt32, 266 IsChangeFloat64ToInt32(IsLoad( 267 kMachFloat64, val, IsInt32Constant(HeapNumberValueOffset()), 268 IsControlEffect(CaptureEq(&if_true)))), 269 IsWord32Sar(val, IsInt32Constant(SmiShiftAmount())), 270 IsMerge(AllOf(CaptureEq(&if_true), IsIfTrue(CaptureEq(&branch))), 271 IsIfFalse(AllOf( 272 CaptureEq(&branch), 273 IsBranch(IsWord32And(val, IsInt32Constant(kSmiTagMask)), 274 graph()->start())))))); 275} 276 277 278TARGET_TEST_F(ChangeLowering32Test, ChangeTaggedToUint32) { 279 STATIC_ASSERT(kSmiTag == 0); 280 STATIC_ASSERT(kSmiTagSize == 1); 281 282 Node* val = Parameter(0); 283 Node* node = graph()->NewNode(simplified()->ChangeTaggedToUint32(), val); 284 Reduction reduction = Reduce(node); 285 ASSERT_TRUE(reduction.Changed()); 286 287 Node* phi = reduction.replacement(); 288 Capture<Node*> branch, if_true; 289 EXPECT_THAT( 290 phi, 291 IsPhi(kMachUint32, 292 IsChangeFloat64ToUint32(IsLoad( 293 kMachFloat64, val, IsInt32Constant(HeapNumberValueOffset()), 294 IsControlEffect(CaptureEq(&if_true)))), 295 IsWord32Sar(val, IsInt32Constant(SmiShiftAmount())), 296 IsMerge(AllOf(CaptureEq(&if_true), IsIfTrue(CaptureEq(&branch))), 297 IsIfFalse(AllOf( 298 CaptureEq(&branch), 299 IsBranch(IsWord32And(val, IsInt32Constant(kSmiTagMask)), 300 graph()->start())))))); 301} 302 303 304TARGET_TEST_F(ChangeLowering32Test, ChangeUint32ToTagged) { 305 STATIC_ASSERT(kSmiTag == 0); 306 STATIC_ASSERT(kSmiTagSize == 1); 307 308 Node* val = Parameter(0); 309 Node* node = graph()->NewNode(simplified()->ChangeUint32ToTagged(), val); 310 Reduction reduction = Reduce(node); 311 ASSERT_TRUE(reduction.Changed()); 312 313 Node* phi = reduction.replacement(); 314 Capture<Node*> branch, heap_number, if_false; 315 EXPECT_THAT( 316 phi, 317 IsPhi( 318 kMachAnyTagged, IsWord32Shl(val, IsInt32Constant(SmiShiftAmount())), 319 IsFinish( 320 AllOf(CaptureEq(&heap_number), 321 IsAllocateHeapNumber(_, CaptureEq(&if_false))), 322 IsStore(kMachFloat64, kNoWriteBarrier, CaptureEq(&heap_number), 323 IsInt32Constant(HeapNumberValueOffset()), 324 IsChangeUint32ToFloat64(val), CaptureEq(&heap_number), 325 CaptureEq(&if_false))), 326 IsMerge( 327 IsIfTrue(AllOf(CaptureEq(&branch), 328 IsBranch(IsUint32LessThanOrEqual( 329 val, IsInt32Constant(SmiMaxValue())), 330 graph()->start()))), 331 AllOf(CaptureEq(&if_false), IsIfFalse(CaptureEq(&branch)))))); 332} 333 334 335// ----------------------------------------------------------------------------- 336// 64-bit 337 338 339class ChangeLowering64Test : public ChangeLoweringTest { 340 public: 341 virtual ~ChangeLowering64Test() {} 342 virtual MachineType WordRepresentation() const FINAL OVERRIDE { 343 return kRepWord64; 344 } 345}; 346 347 348TARGET_TEST_F(ChangeLowering64Test, ChangeInt32ToTagged) { 349 Node* val = Parameter(0); 350 Node* node = graph()->NewNode(simplified()->ChangeInt32ToTagged(), val); 351 Reduction reduction = Reduce(node); 352 ASSERT_TRUE(reduction.Changed()); 353 354 EXPECT_THAT(reduction.replacement(), 355 IsWord64Shl(IsChangeInt32ToInt64(val), 356 IsInt32Constant(SmiShiftAmount()))); 357} 358 359 360TARGET_TEST_F(ChangeLowering64Test, ChangeTaggedToFloat64) { 361 STATIC_ASSERT(kSmiTag == 0); 362 STATIC_ASSERT(kSmiTagSize == 1); 363 364 Node* val = Parameter(0); 365 Node* node = graph()->NewNode(simplified()->ChangeTaggedToFloat64(), val); 366 Reduction reduction = Reduce(node); 367 ASSERT_TRUE(reduction.Changed()); 368 369 Node* phi = reduction.replacement(); 370 Capture<Node*> branch, if_true; 371 EXPECT_THAT( 372 phi, 373 IsPhi( 374 kMachFloat64, 375 IsLoad(kMachFloat64, val, IsInt32Constant(HeapNumberValueOffset()), 376 IsControlEffect(CaptureEq(&if_true))), 377 IsChangeInt32ToFloat64(IsTruncateInt64ToInt32( 378 IsWord64Sar(val, IsInt32Constant(SmiShiftAmount())))), 379 IsMerge( 380 AllOf(CaptureEq(&if_true), 381 IsIfTrue(AllOf( 382 CaptureEq(&branch), 383 IsBranch(IsWord64And(val, IsInt32Constant(kSmiTagMask)), 384 graph()->start())))), 385 IsIfFalse(CaptureEq(&branch))))); 386} 387 388 389TARGET_TEST_F(ChangeLowering64Test, ChangeTaggedToInt32) { 390 STATIC_ASSERT(kSmiTag == 0); 391 STATIC_ASSERT(kSmiTagSize == 1); 392 393 Node* val = Parameter(0); 394 Node* node = graph()->NewNode(simplified()->ChangeTaggedToInt32(), val); 395 Reduction reduction = Reduce(node); 396 ASSERT_TRUE(reduction.Changed()); 397 398 Node* phi = reduction.replacement(); 399 Capture<Node*> branch, if_true; 400 EXPECT_THAT( 401 phi, 402 IsPhi(kMachInt32, 403 IsChangeFloat64ToInt32(IsLoad( 404 kMachFloat64, val, IsInt32Constant(HeapNumberValueOffset()), 405 IsControlEffect(CaptureEq(&if_true)))), 406 IsTruncateInt64ToInt32( 407 IsWord64Sar(val, IsInt32Constant(SmiShiftAmount()))), 408 IsMerge(AllOf(CaptureEq(&if_true), IsIfTrue(CaptureEq(&branch))), 409 IsIfFalse(AllOf( 410 CaptureEq(&branch), 411 IsBranch(IsWord64And(val, IsInt32Constant(kSmiTagMask)), 412 graph()->start())))))); 413} 414 415 416TARGET_TEST_F(ChangeLowering64Test, ChangeTaggedToUint32) { 417 STATIC_ASSERT(kSmiTag == 0); 418 STATIC_ASSERT(kSmiTagSize == 1); 419 420 Node* val = Parameter(0); 421 Node* node = graph()->NewNode(simplified()->ChangeTaggedToUint32(), val); 422 Reduction reduction = Reduce(node); 423 ASSERT_TRUE(reduction.Changed()); 424 425 Node* phi = reduction.replacement(); 426 Capture<Node*> branch, if_true; 427 EXPECT_THAT( 428 phi, 429 IsPhi(kMachUint32, 430 IsChangeFloat64ToUint32(IsLoad( 431 kMachFloat64, val, IsInt32Constant(HeapNumberValueOffset()), 432 IsControlEffect(CaptureEq(&if_true)))), 433 IsTruncateInt64ToInt32( 434 IsWord64Sar(val, IsInt32Constant(SmiShiftAmount()))), 435 IsMerge(AllOf(CaptureEq(&if_true), IsIfTrue(CaptureEq(&branch))), 436 IsIfFalse(AllOf( 437 CaptureEq(&branch), 438 IsBranch(IsWord64And(val, IsInt32Constant(kSmiTagMask)), 439 graph()->start())))))); 440} 441 442 443TARGET_TEST_F(ChangeLowering64Test, ChangeUint32ToTagged) { 444 STATIC_ASSERT(kSmiTag == 0); 445 STATIC_ASSERT(kSmiTagSize == 1); 446 447 Node* val = Parameter(0); 448 Node* node = graph()->NewNode(simplified()->ChangeUint32ToTagged(), val); 449 Reduction reduction = Reduce(node); 450 ASSERT_TRUE(reduction.Changed()); 451 452 Node* phi = reduction.replacement(); 453 Capture<Node*> branch, heap_number, if_false; 454 EXPECT_THAT( 455 phi, 456 IsPhi( 457 kMachAnyTagged, IsWord64Shl(IsChangeUint32ToUint64(val), 458 IsInt32Constant(SmiShiftAmount())), 459 IsFinish( 460 AllOf(CaptureEq(&heap_number), 461 IsAllocateHeapNumber(_, CaptureEq(&if_false))), 462 IsStore(kMachFloat64, kNoWriteBarrier, CaptureEq(&heap_number), 463 IsInt32Constant(HeapNumberValueOffset()), 464 IsChangeUint32ToFloat64(val), CaptureEq(&heap_number), 465 CaptureEq(&if_false))), 466 IsMerge( 467 IsIfTrue(AllOf(CaptureEq(&branch), 468 IsBranch(IsUint32LessThanOrEqual( 469 val, IsInt32Constant(SmiMaxValue())), 470 graph()->start()))), 471 AllOf(CaptureEq(&if_false), IsIfFalse(CaptureEq(&branch)))))); 472} 473 474} // namespace compiler 475} // namespace internal 476} // namespace v8 477