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 <limits> 6 7#include "src/compiler/access-builder.h" 8#include "src/compiler/control-builders.h" 9#include "src/compiler/generic-node-inl.h" 10#include "src/compiler/graph-visualizer.h" 11#include "src/compiler/node-properties-inl.h" 12#include "src/compiler/pipeline.h" 13#include "src/compiler/representation-change.h" 14#include "src/compiler/simplified-lowering.h" 15#include "src/compiler/typer.h" 16#include "src/compiler/verifier.h" 17#include "src/execution.h" 18#include "src/parser.h" 19#include "src/rewriter.h" 20#include "src/scopes.h" 21#include "test/cctest/cctest.h" 22#include "test/cctest/compiler/codegen-tester.h" 23#include "test/cctest/compiler/graph-builder-tester.h" 24#include "test/cctest/compiler/value-helper.h" 25 26using namespace v8::internal; 27using namespace v8::internal::compiler; 28 29template <typename ReturnType> 30class SimplifiedLoweringTester : public GraphBuilderTester<ReturnType> { 31 public: 32 SimplifiedLoweringTester(MachineType p0 = kMachNone, 33 MachineType p1 = kMachNone, 34 MachineType p2 = kMachNone, 35 MachineType p3 = kMachNone, 36 MachineType p4 = kMachNone) 37 : GraphBuilderTester<ReturnType>(p0, p1, p2, p3, p4), 38 typer(this->zone()), 39 javascript(this->zone()), 40 jsgraph(this->graph(), this->common(), &javascript, &typer, 41 this->machine()), 42 lowering(&jsgraph) {} 43 44 Typer typer; 45 JSOperatorBuilder javascript; 46 JSGraph jsgraph; 47 SimplifiedLowering lowering; 48 49 void LowerAllNodes() { 50 this->End(); 51 lowering.LowerAllNodes(); 52 } 53 54 Factory* factory() { return this->isolate()->factory(); } 55 Heap* heap() { return this->isolate()->heap(); } 56}; 57 58 59#ifndef V8_TARGET_ARCH_ARM64 60// TODO(titzer): these result in a stub call that doesn't work on ARM64. 61// TODO(titzer): factor these tests out to test-run-simplifiedops.cc. 62// TODO(titzer): test tagged representation for input to NumberToInt32. 63TEST(RunNumberToInt32_float64) { 64 // TODO(titzer): explicit load/stores here are only because of representations 65 double input; 66 int32_t result; 67 SimplifiedLoweringTester<Object*> t; 68 FieldAccess load = {kUntaggedBase, 0, Handle<Name>(), Type::Number(), 69 kMachFloat64}; 70 Node* loaded = t.LoadField(load, t.PointerConstant(&input)); 71 Node* convert = t.NumberToInt32(loaded); 72 FieldAccess store = {kUntaggedBase, 0, Handle<Name>(), Type::Signed32(), 73 kMachInt32}; 74 t.StoreField(store, t.PointerConstant(&result), convert); 75 t.Return(t.jsgraph.TrueConstant()); 76 t.LowerAllNodes(); 77 t.GenerateCode(); 78 79 if (Pipeline::SupportedTarget()) { 80 FOR_FLOAT64_INPUTS(i) { 81 input = *i; 82 int32_t expected = DoubleToInt32(*i); 83 t.Call(); 84 CHECK_EQ(expected, result); 85 } 86 } 87} 88 89 90// TODO(titzer): test tagged representation for input to NumberToUint32. 91TEST(RunNumberToUint32_float64) { 92 // TODO(titzer): explicit load/stores here are only because of representations 93 double input; 94 uint32_t result; 95 SimplifiedLoweringTester<Object*> t; 96 FieldAccess load = {kUntaggedBase, 0, Handle<Name>(), Type::Number(), 97 kMachFloat64}; 98 Node* loaded = t.LoadField(load, t.PointerConstant(&input)); 99 Node* convert = t.NumberToUint32(loaded); 100 FieldAccess store = {kUntaggedBase, 0, Handle<Name>(), Type::Unsigned32(), 101 kMachUint32}; 102 t.StoreField(store, t.PointerConstant(&result), convert); 103 t.Return(t.jsgraph.TrueConstant()); 104 t.LowerAllNodes(); 105 t.GenerateCode(); 106 107 if (Pipeline::SupportedTarget()) { 108 FOR_FLOAT64_INPUTS(i) { 109 input = *i; 110 uint32_t expected = DoubleToUint32(*i); 111 t.Call(); 112 CHECK_EQ(static_cast<int32_t>(expected), static_cast<int32_t>(result)); 113 } 114 } 115} 116#endif 117 118 119// Create a simple JSObject with a unique map. 120static Handle<JSObject> TestObject() { 121 static int index = 0; 122 char buffer[50]; 123 v8::base::OS::SNPrintF(buffer, 50, "({'a_%d':1})", index++); 124 return Handle<JSObject>::cast(v8::Utils::OpenHandle(*CompileRun(buffer))); 125} 126 127 128TEST(RunLoadMap) { 129 SimplifiedLoweringTester<Object*> t(kMachAnyTagged); 130 FieldAccess access = AccessBuilder::ForMap(); 131 Node* load = t.LoadField(access, t.Parameter(0)); 132 t.Return(load); 133 134 t.LowerAllNodes(); 135 t.GenerateCode(); 136 137 if (Pipeline::SupportedTarget()) { 138 Handle<JSObject> src = TestObject(); 139 Handle<Map> src_map(src->map()); 140 Object* result = t.Call(*src); // TODO(titzer): raw pointers in call 141 CHECK_EQ(*src_map, result); 142 } 143} 144 145 146TEST(RunStoreMap) { 147 SimplifiedLoweringTester<int32_t> t(kMachAnyTagged, kMachAnyTagged); 148 FieldAccess access = AccessBuilder::ForMap(); 149 t.StoreField(access, t.Parameter(1), t.Parameter(0)); 150 t.Return(t.jsgraph.TrueConstant()); 151 152 t.LowerAllNodes(); 153 t.GenerateCode(); 154 155 if (Pipeline::SupportedTarget()) { 156 Handle<JSObject> src = TestObject(); 157 Handle<Map> src_map(src->map()); 158 Handle<JSObject> dst = TestObject(); 159 CHECK(src->map() != dst->map()); 160 t.Call(*src_map, *dst); // TODO(titzer): raw pointers in call 161 CHECK(*src_map == dst->map()); 162 } 163} 164 165 166TEST(RunLoadProperties) { 167 SimplifiedLoweringTester<Object*> t(kMachAnyTagged); 168 FieldAccess access = AccessBuilder::ForJSObjectProperties(); 169 Node* load = t.LoadField(access, t.Parameter(0)); 170 t.Return(load); 171 172 t.LowerAllNodes(); 173 t.GenerateCode(); 174 175 if (Pipeline::SupportedTarget()) { 176 Handle<JSObject> src = TestObject(); 177 Handle<FixedArray> src_props(src->properties()); 178 Object* result = t.Call(*src); // TODO(titzer): raw pointers in call 179 CHECK_EQ(*src_props, result); 180 } 181} 182 183 184TEST(RunLoadStoreMap) { 185 SimplifiedLoweringTester<Object*> t(kMachAnyTagged, kMachAnyTagged); 186 FieldAccess access = AccessBuilder::ForMap(); 187 Node* load = t.LoadField(access, t.Parameter(0)); 188 t.StoreField(access, t.Parameter(1), load); 189 t.Return(load); 190 191 t.LowerAllNodes(); 192 t.GenerateCode(); 193 194 if (Pipeline::SupportedTarget()) { 195 Handle<JSObject> src = TestObject(); 196 Handle<Map> src_map(src->map()); 197 Handle<JSObject> dst = TestObject(); 198 CHECK(src->map() != dst->map()); 199 Object* result = t.Call(*src, *dst); // TODO(titzer): raw pointers in call 200 CHECK(result->IsMap()); 201 CHECK_EQ(*src_map, result); 202 CHECK(*src_map == dst->map()); 203 } 204} 205 206 207TEST(RunLoadStoreFixedArrayIndex) { 208 SimplifiedLoweringTester<Object*> t(kMachAnyTagged); 209 ElementAccess access = AccessBuilder::ForFixedArrayElement(); 210 Node* load = t.LoadElement(access, t.Parameter(0), t.Int32Constant(0), 211 t.Int32Constant(2)); 212 t.StoreElement(access, t.Parameter(0), t.Int32Constant(1), t.Int32Constant(2), 213 load); 214 t.Return(load); 215 216 t.LowerAllNodes(); 217 t.GenerateCode(); 218 219 if (Pipeline::SupportedTarget()) { 220 Handle<FixedArray> array = t.factory()->NewFixedArray(2); 221 Handle<JSObject> src = TestObject(); 222 Handle<JSObject> dst = TestObject(); 223 array->set(0, *src); 224 array->set(1, *dst); 225 Object* result = t.Call(*array); 226 CHECK_EQ(*src, result); 227 CHECK_EQ(*src, array->get(0)); 228 CHECK_EQ(*src, array->get(1)); 229 } 230} 231 232 233TEST(RunLoadStoreArrayBuffer) { 234 SimplifiedLoweringTester<Object*> t(kMachAnyTagged); 235 const int index = 12; 236 const int array_length = 2 * index; 237 ElementAccess buffer_access = 238 AccessBuilder::ForBackingStoreElement(kMachInt8); 239 Node* backing_store = t.LoadField( 240 AccessBuilder::ForJSArrayBufferBackingStore(), t.Parameter(0)); 241 Node* load = 242 t.LoadElement(buffer_access, backing_store, t.Int32Constant(index), 243 t.Int32Constant(array_length)); 244 t.StoreElement(buffer_access, backing_store, t.Int32Constant(index + 1), 245 t.Int32Constant(array_length), load); 246 t.Return(t.jsgraph.TrueConstant()); 247 248 t.LowerAllNodes(); 249 t.GenerateCode(); 250 251 if (Pipeline::SupportedTarget()) { 252 Handle<JSArrayBuffer> array = t.factory()->NewJSArrayBuffer(); 253 Runtime::SetupArrayBufferAllocatingData(t.isolate(), array, array_length); 254 uint8_t* data = reinterpret_cast<uint8_t*>(array->backing_store()); 255 for (int i = 0; i < array_length; i++) { 256 data[i] = i; 257 } 258 259 // TODO(titzer): raw pointers in call 260 Object* result = t.Call(*array); 261 CHECK_EQ(t.isolate()->heap()->true_value(), result); 262 for (int i = 0; i < array_length; i++) { 263 uint8_t expected = i; 264 if (i == (index + 1)) expected = index; 265 CHECK_EQ(data[i], expected); 266 } 267 } 268} 269 270 271TEST(RunLoadFieldFromUntaggedBase) { 272 Smi* smis[] = {Smi::FromInt(1), Smi::FromInt(2), Smi::FromInt(3)}; 273 274 for (size_t i = 0; i < arraysize(smis); i++) { 275 int offset = static_cast<int>(i * sizeof(Smi*)); 276 FieldAccess access = {kUntaggedBase, offset, Handle<Name>(), 277 Type::Integral32(), kMachAnyTagged}; 278 279 SimplifiedLoweringTester<Object*> t; 280 Node* load = t.LoadField(access, t.PointerConstant(smis)); 281 t.Return(load); 282 t.LowerAllNodes(); 283 284 if (!Pipeline::SupportedTarget()) continue; 285 286 for (int j = -5; j <= 5; j++) { 287 Smi* expected = Smi::FromInt(j); 288 smis[i] = expected; 289 CHECK_EQ(expected, t.Call()); 290 } 291 } 292} 293 294 295TEST(RunStoreFieldToUntaggedBase) { 296 Smi* smis[] = {Smi::FromInt(1), Smi::FromInt(2), Smi::FromInt(3)}; 297 298 for (size_t i = 0; i < arraysize(smis); i++) { 299 int offset = static_cast<int>(i * sizeof(Smi*)); 300 FieldAccess access = {kUntaggedBase, offset, Handle<Name>(), 301 Type::Integral32(), kMachAnyTagged}; 302 303 SimplifiedLoweringTester<Object*> t(kMachAnyTagged); 304 Node* p0 = t.Parameter(0); 305 t.StoreField(access, t.PointerConstant(smis), p0); 306 t.Return(p0); 307 t.LowerAllNodes(); 308 309 if (!Pipeline::SupportedTarget()) continue; 310 311 for (int j = -5; j <= 5; j++) { 312 Smi* expected = Smi::FromInt(j); 313 smis[i] = Smi::FromInt(-100); 314 CHECK_EQ(expected, t.Call(expected)); 315 CHECK_EQ(expected, smis[i]); 316 } 317 } 318} 319 320 321TEST(RunLoadElementFromUntaggedBase) { 322 Smi* smis[] = {Smi::FromInt(1), Smi::FromInt(2), Smi::FromInt(3), 323 Smi::FromInt(4), Smi::FromInt(5)}; 324 325 for (size_t i = 0; i < arraysize(smis); i++) { // for header sizes 326 for (size_t j = 0; (i + j) < arraysize(smis); j++) { // for element index 327 int offset = static_cast<int>(i * sizeof(Smi*)); 328 ElementAccess access = {kUntaggedBase, offset, Type::Integral32(), 329 kMachAnyTagged}; 330 331 SimplifiedLoweringTester<Object*> t; 332 Node* load = t.LoadElement( 333 access, t.PointerConstant(smis), t.Int32Constant(static_cast<int>(j)), 334 t.Int32Constant(static_cast<int>(arraysize(smis)))); 335 t.Return(load); 336 t.LowerAllNodes(); 337 338 if (!Pipeline::SupportedTarget()) continue; 339 340 for (int k = -5; k <= 5; k++) { 341 Smi* expected = Smi::FromInt(k); 342 smis[i + j] = expected; 343 CHECK_EQ(expected, t.Call()); 344 } 345 } 346 } 347} 348 349 350TEST(RunStoreElementFromUntaggedBase) { 351 Smi* smis[] = {Smi::FromInt(1), Smi::FromInt(2), Smi::FromInt(3), 352 Smi::FromInt(4), Smi::FromInt(5)}; 353 354 for (size_t i = 0; i < arraysize(smis); i++) { // for header sizes 355 for (size_t j = 0; (i + j) < arraysize(smis); j++) { // for element index 356 int offset = static_cast<int>(i * sizeof(Smi*)); 357 ElementAccess access = {kUntaggedBase, offset, Type::Integral32(), 358 kMachAnyTagged}; 359 360 SimplifiedLoweringTester<Object*> t(kMachAnyTagged); 361 Node* p0 = t.Parameter(0); 362 t.StoreElement(access, t.PointerConstant(smis), 363 t.Int32Constant(static_cast<int>(j)), 364 t.Int32Constant(static_cast<int>(arraysize(smis))), p0); 365 t.Return(p0); 366 t.LowerAllNodes(); 367 368 if (!Pipeline::SupportedTarget()) continue; 369 370 for (int k = -5; k <= 5; k++) { 371 Smi* expected = Smi::FromInt(k); 372 smis[i + j] = Smi::FromInt(-100); 373 CHECK_EQ(expected, t.Call(expected)); 374 CHECK_EQ(expected, smis[i + j]); 375 } 376 377 // TODO(titzer): assert the contents of the array. 378 } 379 } 380} 381 382 383// A helper class for accessing fields and elements of various types, on both 384// tagged and untagged base pointers. Contains both tagged and untagged buffers 385// for testing direct memory access from generated code. 386template <typename E> 387class AccessTester : public HandleAndZoneScope { 388 public: 389 bool tagged; 390 MachineType rep; 391 E* original_elements; 392 size_t num_elements; 393 E* untagged_array; 394 Handle<ByteArray> tagged_array; // TODO(titzer): use FixedArray for tagged. 395 396 AccessTester(bool t, MachineType r, E* orig, size_t num) 397 : tagged(t), 398 rep(r), 399 original_elements(orig), 400 num_elements(num), 401 untagged_array(static_cast<E*>(malloc(ByteSize()))), 402 tagged_array(main_isolate()->factory()->NewByteArray( 403 static_cast<int>(ByteSize()))) { 404 Reinitialize(); 405 } 406 407 ~AccessTester() { free(untagged_array); } 408 409 size_t ByteSize() { return num_elements * sizeof(E); } 410 411 // Nuke both {untagged_array} and {tagged_array} with {original_elements}. 412 void Reinitialize() { 413 memcpy(untagged_array, original_elements, ByteSize()); 414 CHECK_EQ(static_cast<int>(ByteSize()), tagged_array->length()); 415 E* raw = reinterpret_cast<E*>(tagged_array->GetDataStartAddress()); 416 memcpy(raw, original_elements, ByteSize()); 417 } 418 419 // Create and run code that copies the element in either {untagged_array} 420 // or {tagged_array} at index {from_index} to index {to_index}. 421 void RunCopyElement(int from_index, int to_index) { 422 // TODO(titzer): test element and field accesses where the base is not 423 // a constant in the code. 424 BoundsCheck(from_index); 425 BoundsCheck(to_index); 426 ElementAccess access = GetElementAccess(); 427 428 SimplifiedLoweringTester<Object*> t; 429 Node* ptr = GetBaseNode(&t); 430 Node* load = t.LoadElement(access, ptr, t.Int32Constant(from_index), 431 t.Int32Constant(static_cast<int>(num_elements))); 432 t.StoreElement(access, ptr, t.Int32Constant(to_index), 433 t.Int32Constant(static_cast<int>(num_elements)), load); 434 t.Return(t.jsgraph.TrueConstant()); 435 t.LowerAllNodes(); 436 t.GenerateCode(); 437 438 if (Pipeline::SupportedTarget()) { 439 Object* result = t.Call(); 440 CHECK_EQ(t.isolate()->heap()->true_value(), result); 441 } 442 } 443 444 // Create and run code that copies the field in either {untagged_array} 445 // or {tagged_array} at index {from_index} to index {to_index}. 446 void RunCopyField(int from_index, int to_index) { 447 BoundsCheck(from_index); 448 BoundsCheck(to_index); 449 FieldAccess from_access = GetFieldAccess(from_index); 450 FieldAccess to_access = GetFieldAccess(to_index); 451 452 SimplifiedLoweringTester<Object*> t; 453 Node* ptr = GetBaseNode(&t); 454 Node* load = t.LoadField(from_access, ptr); 455 t.StoreField(to_access, ptr, load); 456 t.Return(t.jsgraph.TrueConstant()); 457 t.LowerAllNodes(); 458 t.GenerateCode(); 459 460 if (Pipeline::SupportedTarget()) { 461 Object* result = t.Call(); 462 CHECK_EQ(t.isolate()->heap()->true_value(), result); 463 } 464 } 465 466 // Create and run code that copies the elements from {this} to {that}. 467 void RunCopyElements(AccessTester<E>* that) { 468// TODO(titzer): Rewrite this test without StructuredGraphBuilder support. 469#if 0 470 SimplifiedLoweringTester<Object*> t; 471 472 Node* one = t.Int32Constant(1); 473 Node* index = t.Int32Constant(0); 474 Node* limit = t.Int32Constant(static_cast<int>(num_elements)); 475 t.environment()->Push(index); 476 Node* src = this->GetBaseNode(&t); 477 Node* dst = that->GetBaseNode(&t); 478 { 479 LoopBuilder loop(&t); 480 loop.BeginLoop(); 481 // Loop exit condition 482 index = t.environment()->Top(); 483 Node* condition = t.Int32LessThan(index, limit); 484 loop.BreakUnless(condition); 485 // dst[index] = src[index] 486 index = t.environment()->Pop(); 487 Node* load = t.LoadElement(this->GetElementAccess(), src, index); 488 t.StoreElement(that->GetElementAccess(), dst, index, load); 489 // index++ 490 index = t.Int32Add(index, one); 491 t.environment()->Push(index); 492 // continue 493 loop.EndBody(); 494 loop.EndLoop(); 495 } 496 index = t.environment()->Pop(); 497 t.Return(t.jsgraph.TrueConstant()); 498 t.LowerAllNodes(); 499 t.GenerateCode(); 500 501 if (Pipeline::SupportedTarget()) { 502 Object* result = t.Call(); 503 CHECK_EQ(t.isolate()->heap()->true_value(), result); 504 } 505#endif 506 } 507 508 E GetElement(int index) { 509 BoundsCheck(index); 510 if (tagged) { 511 E* raw = reinterpret_cast<E*>(tagged_array->GetDataStartAddress()); 512 return raw[index]; 513 } else { 514 return untagged_array[index]; 515 } 516 } 517 518 private: 519 ElementAccess GetElementAccess() { 520 ElementAccess access = {tagged ? kTaggedBase : kUntaggedBase, 521 tagged ? FixedArrayBase::kHeaderSize : 0, 522 Type::Any(), rep}; 523 return access; 524 } 525 526 FieldAccess GetFieldAccess(int field) { 527 int offset = field * sizeof(E); 528 FieldAccess access = {tagged ? kTaggedBase : kUntaggedBase, 529 offset + (tagged ? FixedArrayBase::kHeaderSize : 0), 530 Handle<Name>(), Type::Any(), rep}; 531 return access; 532 } 533 534 template <typename T> 535 Node* GetBaseNode(SimplifiedLoweringTester<T>* t) { 536 return tagged ? t->HeapConstant(tagged_array) 537 : t->PointerConstant(untagged_array); 538 } 539 540 void BoundsCheck(int index) { 541 CHECK_GE(index, 0); 542 CHECK_LT(index, static_cast<int>(num_elements)); 543 CHECK_EQ(static_cast<int>(ByteSize()), tagged_array->length()); 544 } 545}; 546 547 548template <typename E> 549static void RunAccessTest(MachineType rep, E* original_elements, size_t num) { 550 int num_elements = static_cast<int>(num); 551 552 for (int taggedness = 0; taggedness < 2; taggedness++) { 553 AccessTester<E> a(taggedness == 1, rep, original_elements, num); 554 for (int field = 0; field < 2; field++) { 555 for (int i = 0; i < num_elements - 1; i++) { 556 a.Reinitialize(); 557 if (field == 0) { 558 a.RunCopyField(i, i + 1); // Test field read/write. 559 } else { 560 a.RunCopyElement(i, i + 1); // Test element read/write. 561 } 562 if (Pipeline::SupportedTarget()) { // verify. 563 for (int j = 0; j < num_elements; j++) { 564 E expect = 565 j == (i + 1) ? original_elements[i] : original_elements[j]; 566 CHECK_EQ(expect, a.GetElement(j)); 567 } 568 } 569 } 570 } 571 } 572 // Test array copy. 573 for (int tf = 0; tf < 2; tf++) { 574 for (int tt = 0; tt < 2; tt++) { 575 AccessTester<E> a(tf == 1, rep, original_elements, num); 576 AccessTester<E> b(tt == 1, rep, original_elements, num); 577 a.RunCopyElements(&b); 578 if (Pipeline::SupportedTarget()) { // verify. 579 for (int i = 0; i < num_elements; i++) { 580 CHECK_EQ(a.GetElement(i), b.GetElement(i)); 581 } 582 } 583 } 584 } 585} 586 587 588TEST(RunAccessTests_uint8) { 589 uint8_t data[] = {0x07, 0x16, 0x25, 0x34, 0x43, 0x99, 590 0xab, 0x78, 0x89, 0x19, 0x2b, 0x38}; 591 RunAccessTest<uint8_t>(kMachInt8, data, arraysize(data)); 592} 593 594 595TEST(RunAccessTests_uint16) { 596 uint16_t data[] = {0x071a, 0x162b, 0x253c, 0x344d, 0x435e, 0x7777}; 597 RunAccessTest<uint16_t>(kMachInt16, data, arraysize(data)); 598} 599 600 601TEST(RunAccessTests_int32) { 602 int32_t data[] = {-211, 211, 628347, 2000000000, -2000000000, -1, -100000034}; 603 RunAccessTest<int32_t>(kMachInt32, data, arraysize(data)); 604} 605 606 607#define V8_2PART_INT64(a, b) (((static_cast<int64_t>(a) << 32) + 0x##b##u)) 608 609 610TEST(RunAccessTests_int64) { 611 if (kPointerSize != 8) return; 612 int64_t data[] = {V8_2PART_INT64(0x10111213, 14151617), 613 V8_2PART_INT64(0x20212223, 24252627), 614 V8_2PART_INT64(0x30313233, 34353637), 615 V8_2PART_INT64(0xa0a1a2a3, a4a5a6a7), 616 V8_2PART_INT64(0xf0f1f2f3, f4f5f6f7)}; 617 RunAccessTest<int64_t>(kMachInt64, data, arraysize(data)); 618} 619 620 621TEST(RunAccessTests_float64) { 622 double data[] = {1.25, -1.25, 2.75, 11.0, 11100.8}; 623 RunAccessTest<double>(kMachFloat64, data, arraysize(data)); 624} 625 626 627TEST(RunAccessTests_Smi) { 628 Smi* data[] = {Smi::FromInt(-1), Smi::FromInt(-9), 629 Smi::FromInt(0), Smi::FromInt(666), 630 Smi::FromInt(77777), Smi::FromInt(Smi::kMaxValue)}; 631 RunAccessTest<Smi*>(kMachAnyTagged, data, arraysize(data)); 632} 633 634 635// Fills in most of the nodes of the graph in order to make tests shorter. 636class TestingGraph : public HandleAndZoneScope, public GraphAndBuilders { 637 public: 638 Typer typer; 639 JSOperatorBuilder javascript; 640 JSGraph jsgraph; 641 Node* p0; 642 Node* p1; 643 Node* p2; 644 Node* start; 645 Node* end; 646 Node* ret; 647 648 explicit TestingGraph(Type* p0_type, Type* p1_type = Type::None(), 649 Type* p2_type = Type::None()) 650 : GraphAndBuilders(main_zone()), 651 typer(main_zone()), 652 javascript(main_zone()), 653 jsgraph(graph(), common(), &javascript, &typer, machine()) { 654 start = graph()->NewNode(common()->Start(2)); 655 graph()->SetStart(start); 656 ret = 657 graph()->NewNode(common()->Return(), jsgraph.Constant(0), start, start); 658 end = graph()->NewNode(common()->End(), ret); 659 graph()->SetEnd(end); 660 p0 = graph()->NewNode(common()->Parameter(0), start); 661 p1 = graph()->NewNode(common()->Parameter(1), start); 662 p2 = graph()->NewNode(common()->Parameter(2), start); 663 NodeProperties::SetBounds(p0, Bounds(p0_type)); 664 NodeProperties::SetBounds(p1, Bounds(p1_type)); 665 NodeProperties::SetBounds(p2, Bounds(p2_type)); 666 } 667 668 void CheckLoweringBinop(IrOpcode::Value expected, const Operator* op) { 669 Node* node = Return(graph()->NewNode(op, p0, p1)); 670 Lower(); 671 CHECK_EQ(expected, node->opcode()); 672 } 673 674 void CheckLoweringTruncatedBinop(IrOpcode::Value expected, const Operator* op, 675 const Operator* trunc) { 676 Node* node = graph()->NewNode(op, p0, p1); 677 Return(graph()->NewNode(trunc, node)); 678 Lower(); 679 CHECK_EQ(expected, node->opcode()); 680 } 681 682 void Lower() { 683 SimplifiedLowering lowering(&jsgraph); 684 lowering.LowerAllNodes(); 685 } 686 687 // Inserts the node as the return value of the graph. 688 Node* Return(Node* node) { 689 ret->ReplaceInput(0, node); 690 return node; 691 } 692 693 // Inserts the node as the effect input to the return of the graph. 694 void Effect(Node* node) { ret->ReplaceInput(1, node); } 695 696 Node* ExampleWithOutput(MachineType type) { 697 // TODO(titzer): use parameters with guaranteed representations. 698 if (type & kTypeInt32) { 699 return graph()->NewNode(machine()->Int32Add(), jsgraph.Int32Constant(1), 700 jsgraph.Int32Constant(1)); 701 } else if (type & kTypeUint32) { 702 return graph()->NewNode(machine()->Word32Shr(), jsgraph.Int32Constant(1), 703 jsgraph.Int32Constant(1)); 704 } else if (type & kRepFloat64) { 705 return graph()->NewNode(machine()->Float64Add(), 706 jsgraph.Float64Constant(1), 707 jsgraph.Float64Constant(1)); 708 } else if (type & kRepBit) { 709 return graph()->NewNode(machine()->Word32Equal(), 710 jsgraph.Int32Constant(1), 711 jsgraph.Int32Constant(1)); 712 } else if (type & kRepWord64) { 713 return graph()->NewNode(machine()->Int64Add(), Int64Constant(1), 714 Int64Constant(1)); 715 } else { 716 CHECK(type & kRepTagged); 717 return p0; 718 } 719 } 720 721 Node* Use(Node* node, MachineType type) { 722 if (type & kTypeInt32) { 723 return graph()->NewNode(machine()->Int32LessThan(), node, 724 jsgraph.Int32Constant(1)); 725 } else if (type & kTypeUint32) { 726 return graph()->NewNode(machine()->Uint32LessThan(), node, 727 jsgraph.Int32Constant(1)); 728 } else if (type & kRepFloat64) { 729 return graph()->NewNode(machine()->Float64Add(), node, 730 jsgraph.Float64Constant(1)); 731 } else if (type & kRepWord64) { 732 return graph()->NewNode(machine()->Int64LessThan(), node, 733 Int64Constant(1)); 734 } else { 735 return graph()->NewNode(simplified()->ReferenceEqual(Type::Any()), node, 736 jsgraph.TrueConstant()); 737 } 738 } 739 740 Node* Branch(Node* cond) { 741 Node* br = graph()->NewNode(common()->Branch(), cond, start); 742 Node* tb = graph()->NewNode(common()->IfTrue(), br); 743 Node* fb = graph()->NewNode(common()->IfFalse(), br); 744 Node* m = graph()->NewNode(common()->Merge(2), tb, fb); 745 NodeProperties::ReplaceControlInput(ret, m); 746 return br; 747 } 748 749 Node* Int64Constant(int64_t v) { 750 return graph()->NewNode(common()->Int64Constant(v)); 751 } 752 753 SimplifiedOperatorBuilder* simplified() { return &main_simplified_; } 754 MachineOperatorBuilder* machine() { return &main_machine_; } 755 CommonOperatorBuilder* common() { return &main_common_; } 756 Graph* graph() { return main_graph_; } 757}; 758 759 760TEST(LowerBooleanNot_bit_bit) { 761 // BooleanNot(x: kRepBit) used as kRepBit 762 TestingGraph t(Type::Boolean()); 763 Node* b = t.ExampleWithOutput(kRepBit); 764 Node* inv = t.graph()->NewNode(t.simplified()->BooleanNot(), b); 765 Node* use = t.Branch(inv); 766 t.Lower(); 767 Node* cmp = use->InputAt(0); 768 CHECK_EQ(t.machine()->WordEqual()->opcode(), cmp->opcode()); 769 CHECK(b == cmp->InputAt(0) || b == cmp->InputAt(1)); 770 Node* f = t.jsgraph.Int32Constant(0); 771 CHECK(f == cmp->InputAt(0) || f == cmp->InputAt(1)); 772} 773 774 775TEST(LowerBooleanNot_bit_tagged) { 776 // BooleanNot(x: kRepBit) used as kRepTagged 777 TestingGraph t(Type::Boolean()); 778 Node* b = t.ExampleWithOutput(kRepBit); 779 Node* inv = t.graph()->NewNode(t.simplified()->BooleanNot(), b); 780 Node* use = t.Use(inv, kRepTagged); 781 t.Return(use); 782 t.Lower(); 783 CHECK_EQ(IrOpcode::kChangeBitToBool, use->InputAt(0)->opcode()); 784 Node* cmp = use->InputAt(0)->InputAt(0); 785 CHECK_EQ(t.machine()->WordEqual()->opcode(), cmp->opcode()); 786 CHECK(b == cmp->InputAt(0) || b == cmp->InputAt(1)); 787 Node* f = t.jsgraph.Int32Constant(0); 788 CHECK(f == cmp->InputAt(0) || f == cmp->InputAt(1)); 789} 790 791 792TEST(LowerBooleanNot_tagged_bit) { 793 // BooleanNot(x: kRepTagged) used as kRepBit 794 TestingGraph t(Type::Boolean()); 795 Node* b = t.p0; 796 Node* inv = t.graph()->NewNode(t.simplified()->BooleanNot(), b); 797 Node* use = t.Branch(inv); 798 t.Lower(); 799 Node* cmp = use->InputAt(0); 800 CHECK_EQ(t.machine()->WordEqual()->opcode(), cmp->opcode()); 801 CHECK(b == cmp->InputAt(0) || b == cmp->InputAt(1)); 802 Node* f = t.jsgraph.FalseConstant(); 803 CHECK(f == cmp->InputAt(0) || f == cmp->InputAt(1)); 804} 805 806 807TEST(LowerBooleanNot_tagged_tagged) { 808 // BooleanNot(x: kRepTagged) used as kRepTagged 809 TestingGraph t(Type::Boolean()); 810 Node* b = t.p0; 811 Node* inv = t.graph()->NewNode(t.simplified()->BooleanNot(), b); 812 Node* use = t.Use(inv, kRepTagged); 813 t.Return(use); 814 t.Lower(); 815 CHECK_EQ(IrOpcode::kChangeBitToBool, use->InputAt(0)->opcode()); 816 Node* cmp = use->InputAt(0)->InputAt(0); 817 CHECK_EQ(t.machine()->WordEqual()->opcode(), cmp->opcode()); 818 CHECK(b == cmp->InputAt(0) || b == cmp->InputAt(1)); 819 Node* f = t.jsgraph.FalseConstant(); 820 CHECK(f == cmp->InputAt(0) || f == cmp->InputAt(1)); 821} 822 823 824TEST(LowerBooleanToNumber_bit_int32) { 825 // BooleanToNumber(x: kRepBit) used as kMachInt32 826 TestingGraph t(Type::Boolean()); 827 Node* b = t.ExampleWithOutput(kRepBit); 828 Node* cnv = t.graph()->NewNode(t.simplified()->BooleanToNumber(), b); 829 Node* use = t.Use(cnv, kMachInt32); 830 t.Return(use); 831 t.Lower(); 832 CHECK_EQ(b, use->InputAt(0)); 833} 834 835 836TEST(LowerBooleanToNumber_tagged_int32) { 837 // BooleanToNumber(x: kRepTagged) used as kMachInt32 838 TestingGraph t(Type::Boolean()); 839 Node* b = t.p0; 840 Node* cnv = t.graph()->NewNode(t.simplified()->BooleanToNumber(), b); 841 Node* use = t.Use(cnv, kMachInt32); 842 t.Return(use); 843 t.Lower(); 844 CHECK_EQ(t.machine()->WordEqual()->opcode(), cnv->opcode()); 845 CHECK(b == cnv->InputAt(0) || b == cnv->InputAt(1)); 846 Node* c = t.jsgraph.TrueConstant(); 847 CHECK(c == cnv->InputAt(0) || c == cnv->InputAt(1)); 848} 849 850 851TEST(LowerBooleanToNumber_bit_tagged) { 852 // BooleanToNumber(x: kRepBit) used as kMachAnyTagged 853 TestingGraph t(Type::Boolean()); 854 Node* b = t.ExampleWithOutput(kRepBit); 855 Node* cnv = t.graph()->NewNode(t.simplified()->BooleanToNumber(), b); 856 Node* use = t.Use(cnv, kMachAnyTagged); 857 t.Return(use); 858 t.Lower(); 859 CHECK_EQ(b, use->InputAt(0)->InputAt(0)); 860 CHECK_EQ(IrOpcode::kChangeInt32ToTagged, use->InputAt(0)->opcode()); 861} 862 863 864TEST(LowerBooleanToNumber_tagged_tagged) { 865 // BooleanToNumber(x: kRepTagged) used as kMachAnyTagged 866 TestingGraph t(Type::Boolean()); 867 Node* b = t.p0; 868 Node* cnv = t.graph()->NewNode(t.simplified()->BooleanToNumber(), b); 869 Node* use = t.Use(cnv, kMachAnyTagged); 870 t.Return(use); 871 t.Lower(); 872 CHECK_EQ(cnv, use->InputAt(0)->InputAt(0)); 873 CHECK_EQ(IrOpcode::kChangeInt32ToTagged, use->InputAt(0)->opcode()); 874 CHECK_EQ(t.machine()->WordEqual()->opcode(), cnv->opcode()); 875 CHECK(b == cnv->InputAt(0) || b == cnv->InputAt(1)); 876 Node* c = t.jsgraph.TrueConstant(); 877 CHECK(c == cnv->InputAt(0) || c == cnv->InputAt(1)); 878} 879 880 881static Type* test_types[] = {Type::Signed32(), Type::Unsigned32(), 882 Type::Number(), Type::Any()}; 883 884 885TEST(LowerNumberCmp_to_int32) { 886 TestingGraph t(Type::Signed32(), Type::Signed32()); 887 888 t.CheckLoweringBinop(IrOpcode::kWord32Equal, t.simplified()->NumberEqual()); 889 t.CheckLoweringBinop(IrOpcode::kInt32LessThan, 890 t.simplified()->NumberLessThan()); 891 t.CheckLoweringBinop(IrOpcode::kInt32LessThanOrEqual, 892 t.simplified()->NumberLessThanOrEqual()); 893} 894 895 896TEST(LowerNumberCmp_to_uint32) { 897 TestingGraph t(Type::Unsigned32(), Type::Unsigned32()); 898 899 t.CheckLoweringBinop(IrOpcode::kWord32Equal, t.simplified()->NumberEqual()); 900 t.CheckLoweringBinop(IrOpcode::kUint32LessThan, 901 t.simplified()->NumberLessThan()); 902 t.CheckLoweringBinop(IrOpcode::kUint32LessThanOrEqual, 903 t.simplified()->NumberLessThanOrEqual()); 904} 905 906 907TEST(LowerNumberCmp_to_float64) { 908 static Type* types[] = {Type::Number(), Type::Any()}; 909 910 for (size_t i = 0; i < arraysize(types); i++) { 911 TestingGraph t(types[i], types[i]); 912 913 t.CheckLoweringBinop(IrOpcode::kFloat64Equal, 914 t.simplified()->NumberEqual()); 915 t.CheckLoweringBinop(IrOpcode::kFloat64LessThan, 916 t.simplified()->NumberLessThan()); 917 t.CheckLoweringBinop(IrOpcode::kFloat64LessThanOrEqual, 918 t.simplified()->NumberLessThanOrEqual()); 919 } 920} 921 922 923TEST(LowerNumberAddSub_to_int32) { 924 TestingGraph t(Type::Signed32(), Type::Signed32()); 925 t.CheckLoweringTruncatedBinop(IrOpcode::kInt32Add, 926 t.simplified()->NumberAdd(), 927 t.simplified()->NumberToInt32()); 928 t.CheckLoweringTruncatedBinop(IrOpcode::kInt32Sub, 929 t.simplified()->NumberSubtract(), 930 t.simplified()->NumberToInt32()); 931} 932 933 934TEST(LowerNumberAddSub_to_uint32) { 935 TestingGraph t(Type::Unsigned32(), Type::Unsigned32()); 936 t.CheckLoweringTruncatedBinop(IrOpcode::kInt32Add, 937 t.simplified()->NumberAdd(), 938 t.simplified()->NumberToUint32()); 939 t.CheckLoweringTruncatedBinop(IrOpcode::kInt32Sub, 940 t.simplified()->NumberSubtract(), 941 t.simplified()->NumberToUint32()); 942} 943 944 945TEST(LowerNumberAddSub_to_float64) { 946 for (size_t i = 0; i < arraysize(test_types); i++) { 947 TestingGraph t(test_types[i], test_types[i]); 948 949 t.CheckLoweringBinop(IrOpcode::kFloat64Add, t.simplified()->NumberAdd()); 950 t.CheckLoweringBinop(IrOpcode::kFloat64Sub, 951 t.simplified()->NumberSubtract()); 952 } 953} 954 955 956TEST(LowerNumberDivMod_to_float64) { 957 for (size_t i = 0; i < arraysize(test_types); i++) { 958 TestingGraph t(test_types[i], test_types[i]); 959 960 t.CheckLoweringBinop(IrOpcode::kFloat64Div, t.simplified()->NumberDivide()); 961 t.CheckLoweringBinop(IrOpcode::kFloat64Mod, 962 t.simplified()->NumberModulus()); 963 } 964} 965 966 967static void CheckChangeOf(IrOpcode::Value change, Node* of, Node* node) { 968 CHECK_EQ(change, node->opcode()); 969 CHECK_EQ(of, node->InputAt(0)); 970} 971 972 973TEST(LowerNumberToInt32_to_nop) { 974 // NumberToInt32(x: kRepTagged | kTypeInt32) used as kRepTagged 975 TestingGraph t(Type::Signed32()); 976 Node* trunc = t.graph()->NewNode(t.simplified()->NumberToInt32(), t.p0); 977 Node* use = t.Use(trunc, kRepTagged); 978 t.Return(use); 979 t.Lower(); 980 CHECK_EQ(t.p0, use->InputAt(0)); 981} 982 983 984TEST(LowerNumberToInt32_to_ChangeTaggedToFloat64) { 985 // NumberToInt32(x: kRepTagged | kTypeInt32) used as kRepFloat64 986 TestingGraph t(Type::Signed32()); 987 Node* trunc = t.graph()->NewNode(t.simplified()->NumberToInt32(), t.p0); 988 Node* use = t.Use(trunc, kRepFloat64); 989 t.Return(use); 990 t.Lower(); 991 CheckChangeOf(IrOpcode::kChangeTaggedToFloat64, t.p0, use->InputAt(0)); 992} 993 994 995TEST(LowerNumberToInt32_to_ChangeTaggedToInt32) { 996 // NumberToInt32(x: kRepTagged | kTypeInt32) used as kRepWord32 997 TestingGraph t(Type::Signed32()); 998 Node* trunc = t.graph()->NewNode(t.simplified()->NumberToInt32(), t.p0); 999 Node* use = t.Use(trunc, kTypeInt32); 1000 t.Return(use); 1001 t.Lower(); 1002 CheckChangeOf(IrOpcode::kChangeTaggedToInt32, t.p0, use->InputAt(0)); 1003} 1004 1005 1006TEST(LowerNumberToInt32_to_TruncateFloat64ToInt32) { 1007 // NumberToInt32(x: kRepFloat64) used as kMachInt32 1008 TestingGraph t(Type::Number()); 1009 Node* p0 = t.ExampleWithOutput(kMachFloat64); 1010 Node* trunc = t.graph()->NewNode(t.simplified()->NumberToInt32(), p0); 1011 Node* use = t.Use(trunc, kMachInt32); 1012 t.Return(use); 1013 t.Lower(); 1014 CheckChangeOf(IrOpcode::kTruncateFloat64ToInt32, p0, use->InputAt(0)); 1015} 1016 1017 1018TEST(LowerNumberToInt32_to_TruncateFloat64ToInt32_with_change) { 1019 // NumberToInt32(x: kTypeNumber | kRepTagged) used as kMachInt32 1020 TestingGraph t(Type::Number()); 1021 Node* trunc = t.graph()->NewNode(t.simplified()->NumberToInt32(), t.p0); 1022 Node* use = t.Use(trunc, kMachInt32); 1023 t.Return(use); 1024 t.Lower(); 1025 Node* node = use->InputAt(0); 1026 CHECK_EQ(IrOpcode::kTruncateFloat64ToInt32, node->opcode()); 1027 Node* of = node->InputAt(0); 1028 CHECK_EQ(IrOpcode::kChangeTaggedToFloat64, of->opcode()); 1029 CHECK_EQ(t.p0, of->InputAt(0)); 1030} 1031 1032 1033TEST(LowerNumberToInt32_to_ChangeFloat64ToTagged) { 1034 // TODO(titzer): NumberToInt32(x: kRepFloat64 | kTypeInt32) used as kRepTagged 1035} 1036 1037 1038TEST(LowerNumberToInt32_to_ChangeFloat64ToInt32) { 1039 // TODO(titzer): NumberToInt32(x: kRepFloat64 | kTypeInt32) used as kRepWord32 1040 // | kTypeInt32 1041} 1042 1043 1044TEST(LowerNumberToUint32_to_nop) { 1045 // NumberToUint32(x: kRepTagged | kTypeUint32) used as kRepTagged 1046 TestingGraph t(Type::Unsigned32()); 1047 Node* trunc = t.graph()->NewNode(t.simplified()->NumberToUint32(), t.p0); 1048 Node* use = t.Use(trunc, kRepTagged); 1049 t.Return(use); 1050 t.Lower(); 1051 CHECK_EQ(t.p0, use->InputAt(0)); 1052} 1053 1054 1055TEST(LowerNumberToUint32_to_ChangeTaggedToFloat64) { 1056 // NumberToUint32(x: kRepTagged | kTypeUint32) used as kRepWord32 1057 TestingGraph t(Type::Unsigned32()); 1058 Node* trunc = t.graph()->NewNode(t.simplified()->NumberToUint32(), t.p0); 1059 Node* use = t.Use(trunc, kRepFloat64); 1060 t.Return(use); 1061 t.Lower(); 1062 CheckChangeOf(IrOpcode::kChangeTaggedToFloat64, t.p0, use->InputAt(0)); 1063} 1064 1065 1066TEST(LowerNumberToUint32_to_ChangeTaggedToUint32) { 1067 // NumberToUint32(x: kRepTagged | kTypeUint32) used as kRepWord32 1068 TestingGraph t(Type::Unsigned32()); 1069 Node* trunc = t.graph()->NewNode(t.simplified()->NumberToUint32(), t.p0); 1070 Node* use = t.Use(trunc, kTypeUint32); 1071 t.Return(use); 1072 t.Lower(); 1073 CheckChangeOf(IrOpcode::kChangeTaggedToUint32, t.p0, use->InputAt(0)); 1074} 1075 1076 1077TEST(LowerNumberToUint32_to_TruncateFloat64ToInt32) { 1078 // NumberToUint32(x: kRepFloat64) used as kMachUint32 1079 TestingGraph t(Type::Number()); 1080 Node* p0 = t.ExampleWithOutput(kMachFloat64); 1081 Node* trunc = t.graph()->NewNode(t.simplified()->NumberToUint32(), p0); 1082 Node* use = t.Use(trunc, kMachUint32); 1083 t.Return(use); 1084 t.Lower(); 1085 CheckChangeOf(IrOpcode::kTruncateFloat64ToInt32, p0, use->InputAt(0)); 1086} 1087 1088 1089TEST(LowerNumberToUint32_to_TruncateFloat64ToInt32_with_change) { 1090 // NumberToInt32(x: kTypeNumber | kRepTagged) used as kMachUint32 1091 TestingGraph t(Type::Number()); 1092 Node* trunc = t.graph()->NewNode(t.simplified()->NumberToUint32(), t.p0); 1093 Node* use = t.Use(trunc, kMachUint32); 1094 t.Return(use); 1095 t.Lower(); 1096 Node* node = use->InputAt(0); 1097 CHECK_EQ(IrOpcode::kTruncateFloat64ToInt32, node->opcode()); 1098 Node* of = node->InputAt(0); 1099 CHECK_EQ(IrOpcode::kChangeTaggedToFloat64, of->opcode()); 1100 CHECK_EQ(t.p0, of->InputAt(0)); 1101} 1102 1103 1104TEST(LowerNumberToUint32_to_ChangeFloat64ToTagged) { 1105 // TODO(titzer): NumberToUint32(x: kRepFloat64 | kTypeUint32) used as 1106 // kRepTagged 1107} 1108 1109 1110TEST(LowerNumberToUint32_to_ChangeFloat64ToUint32) { 1111 // TODO(titzer): NumberToUint32(x: kRepFloat64 | kTypeUint32) used as 1112 // kRepWord32 1113} 1114 1115 1116TEST(LowerNumberToUint32_to_TruncateFloat64ToUint32) { 1117 // TODO(titzer): NumberToUint32(x: kRepFloat64) used as kRepWord32 1118} 1119 1120 1121TEST(LowerReferenceEqual_to_wordeq) { 1122 TestingGraph t(Type::Any(), Type::Any()); 1123 IrOpcode::Value opcode = 1124 static_cast<IrOpcode::Value>(t.machine()->WordEqual()->opcode()); 1125 t.CheckLoweringBinop(opcode, t.simplified()->ReferenceEqual(Type::Any())); 1126} 1127 1128 1129TEST(LowerStringOps_to_call_and_compare) { 1130 if (Pipeline::SupportedTarget()) { 1131 // These tests need linkage for the calls. 1132 TestingGraph t(Type::String(), Type::String()); 1133 IrOpcode::Value compare_eq = 1134 static_cast<IrOpcode::Value>(t.machine()->WordEqual()->opcode()); 1135 IrOpcode::Value compare_lt = 1136 static_cast<IrOpcode::Value>(t.machine()->IntLessThan()->opcode()); 1137 IrOpcode::Value compare_le = static_cast<IrOpcode::Value>( 1138 t.machine()->IntLessThanOrEqual()->opcode()); 1139 t.CheckLoweringBinop(compare_eq, t.simplified()->StringEqual()); 1140 t.CheckLoweringBinop(compare_lt, t.simplified()->StringLessThan()); 1141 t.CheckLoweringBinop(compare_le, t.simplified()->StringLessThanOrEqual()); 1142 t.CheckLoweringBinop(IrOpcode::kCall, t.simplified()->StringAdd()); 1143 } 1144} 1145 1146 1147void CheckChangeInsertion(IrOpcode::Value expected, MachineType from, 1148 MachineType to) { 1149 TestingGraph t(Type::Any()); 1150 Node* in = t.ExampleWithOutput(from); 1151 Node* use = t.Use(in, to); 1152 t.Return(use); 1153 t.Lower(); 1154 CHECK_EQ(expected, use->InputAt(0)->opcode()); 1155 CHECK_EQ(in, use->InputAt(0)->InputAt(0)); 1156} 1157 1158 1159TEST(InsertBasicChanges) { 1160 CheckChangeInsertion(IrOpcode::kChangeFloat64ToInt32, kRepFloat64, 1161 kTypeInt32); 1162 CheckChangeInsertion(IrOpcode::kChangeFloat64ToUint32, kRepFloat64, 1163 kTypeUint32); 1164 CheckChangeInsertion(IrOpcode::kChangeTaggedToInt32, kRepTagged, kTypeInt32); 1165 CheckChangeInsertion(IrOpcode::kChangeTaggedToUint32, kRepTagged, 1166 kTypeUint32); 1167 1168 CheckChangeInsertion(IrOpcode::kChangeFloat64ToTagged, kRepFloat64, 1169 kRepTagged); 1170 CheckChangeInsertion(IrOpcode::kChangeTaggedToFloat64, kRepTagged, 1171 kRepFloat64); 1172 1173 CheckChangeInsertion(IrOpcode::kChangeInt32ToFloat64, kTypeInt32, 1174 kRepFloat64); 1175 CheckChangeInsertion(IrOpcode::kChangeInt32ToTagged, kTypeInt32, kRepTagged); 1176 1177 CheckChangeInsertion(IrOpcode::kChangeUint32ToFloat64, kTypeUint32, 1178 kRepFloat64); 1179 CheckChangeInsertion(IrOpcode::kChangeUint32ToTagged, kTypeUint32, 1180 kRepTagged); 1181} 1182 1183 1184static void CheckChangesAroundBinop(TestingGraph* t, const Operator* op, 1185 IrOpcode::Value input_change, 1186 IrOpcode::Value output_change) { 1187 Node* binop = t->graph()->NewNode(op, t->p0, t->p1); 1188 t->Return(binop); 1189 t->Lower(); 1190 CHECK_EQ(input_change, binop->InputAt(0)->opcode()); 1191 CHECK_EQ(input_change, binop->InputAt(1)->opcode()); 1192 CHECK_EQ(t->p0, binop->InputAt(0)->InputAt(0)); 1193 CHECK_EQ(t->p1, binop->InputAt(1)->InputAt(0)); 1194 CHECK_EQ(output_change, t->ret->InputAt(0)->opcode()); 1195 CHECK_EQ(binop, t->ret->InputAt(0)->InputAt(0)); 1196} 1197 1198 1199TEST(InsertChangesAroundInt32Binops) { 1200 TestingGraph t(Type::Signed32(), Type::Signed32()); 1201 1202 const Operator* ops[] = {t.machine()->Int32Add(), t.machine()->Int32Sub(), 1203 t.machine()->Int32Mul(), t.machine()->Int32Div(), 1204 t.machine()->Int32Mod(), t.machine()->Word32And(), 1205 t.machine()->Word32Or(), t.machine()->Word32Xor(), 1206 t.machine()->Word32Shl(), t.machine()->Word32Sar()}; 1207 1208 for (size_t i = 0; i < arraysize(ops); i++) { 1209 CheckChangesAroundBinop(&t, ops[i], IrOpcode::kChangeTaggedToInt32, 1210 IrOpcode::kChangeInt32ToTagged); 1211 } 1212} 1213 1214 1215TEST(InsertChangesAroundInt32Cmp) { 1216 TestingGraph t(Type::Signed32(), Type::Signed32()); 1217 1218 const Operator* ops[] = {t.machine()->Int32LessThan(), 1219 t.machine()->Int32LessThanOrEqual()}; 1220 1221 for (size_t i = 0; i < arraysize(ops); i++) { 1222 CheckChangesAroundBinop(&t, ops[i], IrOpcode::kChangeTaggedToInt32, 1223 IrOpcode::kChangeBitToBool); 1224 } 1225} 1226 1227 1228TEST(InsertChangesAroundUint32Cmp) { 1229 TestingGraph t(Type::Unsigned32(), Type::Unsigned32()); 1230 1231 const Operator* ops[] = {t.machine()->Uint32LessThan(), 1232 t.machine()->Uint32LessThanOrEqual()}; 1233 1234 for (size_t i = 0; i < arraysize(ops); i++) { 1235 CheckChangesAroundBinop(&t, ops[i], IrOpcode::kChangeTaggedToUint32, 1236 IrOpcode::kChangeBitToBool); 1237 } 1238} 1239 1240 1241TEST(InsertChangesAroundFloat64Binops) { 1242 TestingGraph t(Type::Number(), Type::Number()); 1243 1244 const Operator* ops[] = { 1245 t.machine()->Float64Add(), t.machine()->Float64Sub(), 1246 t.machine()->Float64Mul(), t.machine()->Float64Div(), 1247 t.machine()->Float64Mod(), 1248 }; 1249 1250 for (size_t i = 0; i < arraysize(ops); i++) { 1251 CheckChangesAroundBinop(&t, ops[i], IrOpcode::kChangeTaggedToFloat64, 1252 IrOpcode::kChangeFloat64ToTagged); 1253 } 1254} 1255 1256 1257TEST(InsertChangesAroundFloat64Cmp) { 1258 TestingGraph t(Type::Number(), Type::Number()); 1259 1260 const Operator* ops[] = {t.machine()->Float64Equal(), 1261 t.machine()->Float64LessThan(), 1262 t.machine()->Float64LessThanOrEqual()}; 1263 1264 for (size_t i = 0; i < arraysize(ops); i++) { 1265 CheckChangesAroundBinop(&t, ops[i], IrOpcode::kChangeTaggedToFloat64, 1266 IrOpcode::kChangeBitToBool); 1267 } 1268} 1269 1270 1271void CheckFieldAccessArithmetic(FieldAccess access, Node* load_or_store) { 1272 Int32Matcher index = Int32Matcher(load_or_store->InputAt(1)); 1273 CHECK(index.Is(access.offset - access.tag())); 1274} 1275 1276 1277Node* CheckElementAccessArithmetic(ElementAccess access, Node* load_or_store) { 1278 Int32BinopMatcher index(load_or_store->InputAt(1)); 1279 CHECK_EQ(IrOpcode::kInt32Add, index.node()->opcode()); 1280 CHECK(index.right().Is(access.header_size - access.tag())); 1281 1282 int element_size = ElementSizeOf(access.machine_type); 1283 1284 if (element_size != 1) { 1285 Int32BinopMatcher mul(index.left().node()); 1286 CHECK_EQ(IrOpcode::kInt32Mul, mul.node()->opcode()); 1287 CHECK(mul.right().Is(element_size)); 1288 return mul.left().node(); 1289 } else { 1290 return index.left().node(); 1291 } 1292} 1293 1294 1295static const MachineType machine_reps[] = { 1296 kRepBit, kMachInt8, kMachInt16, kMachInt32, 1297 kMachInt64, kMachFloat64, kMachAnyTagged}; 1298 1299 1300TEST(LowerLoadField_to_load) { 1301 TestingGraph t(Type::Any(), Type::Signed32()); 1302 1303 for (size_t i = 0; i < arraysize(machine_reps); i++) { 1304 FieldAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize, 1305 Handle<Name>::null(), Type::Any(), machine_reps[i]}; 1306 1307 Node* load = 1308 t.graph()->NewNode(t.simplified()->LoadField(access), t.p0, t.start); 1309 Node* use = t.Use(load, machine_reps[i]); 1310 t.Return(use); 1311 t.Lower(); 1312 CHECK_EQ(IrOpcode::kLoad, load->opcode()); 1313 CHECK_EQ(t.p0, load->InputAt(0)); 1314 CheckFieldAccessArithmetic(access, load); 1315 1316 MachineType rep = OpParameter<MachineType>(load); 1317 CHECK_EQ(machine_reps[i], rep); 1318 } 1319} 1320 1321 1322TEST(LowerStoreField_to_store) { 1323 TestingGraph t(Type::Any(), Type::Signed32()); 1324 1325 for (size_t i = 0; i < arraysize(machine_reps); i++) { 1326 FieldAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize, 1327 Handle<Name>::null(), Type::Any(), machine_reps[i]}; 1328 1329 1330 Node* val = t.ExampleWithOutput(machine_reps[i]); 1331 Node* store = t.graph()->NewNode(t.simplified()->StoreField(access), t.p0, 1332 val, t.start, t.start); 1333 t.Effect(store); 1334 t.Lower(); 1335 CHECK_EQ(IrOpcode::kStore, store->opcode()); 1336 CHECK_EQ(val, store->InputAt(2)); 1337 CheckFieldAccessArithmetic(access, store); 1338 1339 StoreRepresentation rep = OpParameter<StoreRepresentation>(store); 1340 if (machine_reps[i] & kRepTagged) { 1341 CHECK_EQ(kFullWriteBarrier, rep.write_barrier_kind()); 1342 } 1343 CHECK_EQ(machine_reps[i], rep.machine_type()); 1344 } 1345} 1346 1347 1348TEST(LowerLoadElement_to_load) { 1349 TestingGraph t(Type::Any(), Type::Signed32()); 1350 1351 for (size_t i = 0; i < arraysize(machine_reps); i++) { 1352 ElementAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize, 1353 Type::Any(), machine_reps[i]}; 1354 1355 Node* load = 1356 t.graph()->NewNode(t.simplified()->LoadElement(access), t.p0, t.p1, 1357 t.jsgraph.Int32Constant(1024), t.start); 1358 Node* use = t.Use(load, machine_reps[i]); 1359 t.Return(use); 1360 t.Lower(); 1361 CHECK_EQ(IrOpcode::kLoad, load->opcode()); 1362 CHECK_EQ(t.p0, load->InputAt(0)); 1363 CheckElementAccessArithmetic(access, load); 1364 1365 MachineType rep = OpParameter<MachineType>(load); 1366 CHECK_EQ(machine_reps[i], rep); 1367 } 1368} 1369 1370 1371TEST(LowerStoreElement_to_store) { 1372 TestingGraph t(Type::Any(), Type::Signed32()); 1373 1374 for (size_t i = 0; i < arraysize(machine_reps); i++) { 1375 ElementAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize, 1376 Type::Any(), machine_reps[i]}; 1377 1378 Node* val = t.ExampleWithOutput(machine_reps[i]); 1379 Node* store = t.graph()->NewNode(t.simplified()->StoreElement(access), t.p0, 1380 t.p1, t.jsgraph.Int32Constant(1024), val, 1381 t.start, t.start); 1382 t.Effect(store); 1383 t.Lower(); 1384 CHECK_EQ(IrOpcode::kStore, store->opcode()); 1385 CHECK_EQ(val, store->InputAt(2)); 1386 CheckElementAccessArithmetic(access, store); 1387 1388 StoreRepresentation rep = OpParameter<StoreRepresentation>(store); 1389 if (machine_reps[i] & kRepTagged) { 1390 CHECK_EQ(kFullWriteBarrier, rep.write_barrier_kind()); 1391 } 1392 CHECK_EQ(machine_reps[i], rep.machine_type()); 1393 } 1394} 1395 1396 1397TEST(InsertChangeForLoadElementIndex) { 1398 // LoadElement(obj: Tagged, index: kTypeInt32 | kRepTagged, length) => 1399 // Load(obj, Int32Add(Int32Mul(ChangeTaggedToInt32(index), #k), #k)) 1400 TestingGraph t(Type::Any(), Type::Signed32(), Type::Any()); 1401 ElementAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize, Type::Any(), 1402 kMachAnyTagged}; 1403 1404 Node* load = t.graph()->NewNode(t.simplified()->LoadElement(access), t.p0, 1405 t.p1, t.p2, t.start); 1406 t.Return(load); 1407 t.Lower(); 1408 CHECK_EQ(IrOpcode::kLoad, load->opcode()); 1409 CHECK_EQ(t.p0, load->InputAt(0)); 1410 1411 Node* index = CheckElementAccessArithmetic(access, load); 1412 CheckChangeOf(IrOpcode::kChangeTaggedToInt32, t.p1, index); 1413} 1414 1415 1416TEST(InsertChangeForStoreElementIndex) { 1417 // StoreElement(obj: Tagged, index: kTypeInt32 | kRepTagged, length, val) => 1418 // Store(obj, Int32Add(Int32Mul(ChangeTaggedToInt32(index), #k), #k), val) 1419 TestingGraph t(Type::Any(), Type::Signed32(), Type::Any()); 1420 ElementAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize, Type::Any(), 1421 kMachAnyTagged}; 1422 1423 Node* store = 1424 t.graph()->NewNode(t.simplified()->StoreElement(access), t.p0, t.p1, t.p2, 1425 t.jsgraph.TrueConstant(), t.start, t.start); 1426 t.Effect(store); 1427 t.Lower(); 1428 CHECK_EQ(IrOpcode::kStore, store->opcode()); 1429 CHECK_EQ(t.p0, store->InputAt(0)); 1430 1431 Node* index = CheckElementAccessArithmetic(access, store); 1432 CheckChangeOf(IrOpcode::kChangeTaggedToInt32, t.p1, index); 1433} 1434 1435 1436TEST(InsertChangeForLoadElement) { 1437 // TODO(titzer): test all load/store representation change insertions. 1438 TestingGraph t(Type::Any(), Type::Signed32(), Type::Any()); 1439 ElementAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize, Type::Any(), 1440 kMachFloat64}; 1441 1442 Node* load = t.graph()->NewNode(t.simplified()->LoadElement(access), t.p0, 1443 t.p1, t.p1, t.start); 1444 t.Return(load); 1445 t.Lower(); 1446 CHECK_EQ(IrOpcode::kLoad, load->opcode()); 1447 CHECK_EQ(t.p0, load->InputAt(0)); 1448 CheckChangeOf(IrOpcode::kChangeFloat64ToTagged, load, t.ret->InputAt(0)); 1449} 1450 1451 1452TEST(InsertChangeForLoadField) { 1453 // TODO(titzer): test all load/store representation change insertions. 1454 TestingGraph t(Type::Any(), Type::Signed32()); 1455 FieldAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize, 1456 Handle<Name>::null(), Type::Any(), kMachFloat64}; 1457 1458 Node* load = 1459 t.graph()->NewNode(t.simplified()->LoadField(access), t.p0, t.start); 1460 t.Return(load); 1461 t.Lower(); 1462 CHECK_EQ(IrOpcode::kLoad, load->opcode()); 1463 CHECK_EQ(t.p0, load->InputAt(0)); 1464 CheckChangeOf(IrOpcode::kChangeFloat64ToTagged, load, t.ret->InputAt(0)); 1465} 1466 1467 1468TEST(InsertChangeForStoreElement) { 1469 // TODO(titzer): test all load/store representation change insertions. 1470 TestingGraph t(Type::Any(), Type::Signed32(), Type::Any()); 1471 ElementAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize, Type::Any(), 1472 kMachFloat64}; 1473 1474 Node* store = t.graph()->NewNode(t.simplified()->StoreElement(access), t.p0, 1475 t.jsgraph.Int32Constant(0), t.p2, t.p1, 1476 t.start, t.start); 1477 t.Effect(store); 1478 t.Lower(); 1479 1480 CHECK_EQ(IrOpcode::kStore, store->opcode()); 1481 CHECK_EQ(t.p0, store->InputAt(0)); 1482 CheckChangeOf(IrOpcode::kChangeTaggedToFloat64, t.p1, store->InputAt(2)); 1483} 1484 1485 1486TEST(InsertChangeForStoreField) { 1487 // TODO(titzer): test all load/store representation change insertions. 1488 TestingGraph t(Type::Any(), Type::Signed32()); 1489 FieldAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize, 1490 Handle<Name>::null(), Type::Any(), kMachFloat64}; 1491 1492 Node* store = t.graph()->NewNode(t.simplified()->StoreField(access), t.p0, 1493 t.p1, t.start, t.start); 1494 t.Effect(store); 1495 t.Lower(); 1496 1497 CHECK_EQ(IrOpcode::kStore, store->opcode()); 1498 CHECK_EQ(t.p0, store->InputAt(0)); 1499 CheckChangeOf(IrOpcode::kChangeTaggedToFloat64, t.p1, store->InputAt(2)); 1500} 1501 1502 1503TEST(UpdatePhi) { 1504 TestingGraph t(Type::Any(), Type::Signed32()); 1505 static const MachineType kMachineTypes[] = {kMachInt32, kMachUint32, 1506 kMachFloat64}; 1507 1508 for (size_t i = 0; i < arraysize(kMachineTypes); i++) { 1509 FieldAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize, 1510 Handle<Name>::null(), Type::Any(), kMachineTypes[i]}; 1511 1512 Node* load0 = 1513 t.graph()->NewNode(t.simplified()->LoadField(access), t.p0, t.start); 1514 Node* load1 = 1515 t.graph()->NewNode(t.simplified()->LoadField(access), t.p1, t.start); 1516 Node* phi = t.graph()->NewNode(t.common()->Phi(kMachAnyTagged, 2), load0, 1517 load1, t.start); 1518 t.Return(t.Use(phi, kMachineTypes[i])); 1519 t.Lower(); 1520 1521 CHECK_EQ(IrOpcode::kPhi, phi->opcode()); 1522 CHECK_EQ(RepresentationOf(kMachineTypes[i]), 1523 RepresentationOf(OpParameter<MachineType>(phi))); 1524 } 1525} 1526 1527 1528// TODO(titzer): this tests current behavior of assuming an implicit 1529// representation change in loading float32s. Fix when float32 is fully 1530// supported. 1531TEST(ImplicitFloat32ToFloat64InLoads) { 1532 TestingGraph t(Type::Any()); 1533 1534 FieldAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize, 1535 Handle<Name>::null(), Type::Any(), kMachFloat32}; 1536 1537 Node* load = 1538 t.graph()->NewNode(t.simplified()->LoadField(access), t.p0, t.start); 1539 t.Return(load); 1540 t.Lower(); 1541 CHECK_EQ(IrOpcode::kLoad, load->opcode()); 1542 CHECK_EQ(t.p0, load->InputAt(0)); 1543 CheckChangeOf(IrOpcode::kChangeFloat64ToTagged, load, t.ret->InputAt(0)); 1544} 1545 1546 1547TEST(ImplicitFloat64ToFloat32InStores) { 1548 TestingGraph t(Type::Any(), Type::Signed32()); 1549 FieldAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize, 1550 Handle<Name>::null(), Type::Any(), kMachFloat32}; 1551 1552 Node* store = t.graph()->NewNode(t.simplified()->StoreField(access), t.p0, 1553 t.p1, t.start, t.start); 1554 t.Effect(store); 1555 t.Lower(); 1556 1557 CHECK_EQ(IrOpcode::kStore, store->opcode()); 1558 CHECK_EQ(t.p0, store->InputAt(0)); 1559 CheckChangeOf(IrOpcode::kChangeTaggedToFloat64, t.p1, store->InputAt(2)); 1560} 1561