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/assembler.h" 6 #include "src/codegen.h" 7 #include "src/compiler/linkage.h" 8 #include "src/compiler/raw-machine-assembler.h" 9 #include "src/machine-type.h" 10 #include "src/register-configuration.h" 11 12 #include "test/cctest/cctest.h" 13 #include "test/cctest/compiler/codegen-tester.h" 14 #include "test/cctest/compiler/graph-builder-tester.h" 15 #include "test/cctest/compiler/value-helper.h" 16 17 namespace v8 { 18 namespace internal { 19 namespace compiler { 20 21 namespace { 22 typedef float float32; 23 typedef double float64; 24 25 // Picks a representative pair of integers from the given range. 26 // If there are less than {max_pairs} possible pairs, do them all, otherwise try 27 // to select a representative set. 28 class Pairs { 29 public: 30 Pairs(int max_pairs, int range, const int* codes) 31 : range_(range), 32 codes_(codes), 33 max_pairs_(std::min(max_pairs, range_ * range_)), 34 counter_(0) {} 35 36 bool More() { return counter_ < max_pairs_; } 37 38 void Next(int* r0, int* r1, bool same_is_ok) { 39 do { 40 // Find the next pair. 41 if (exhaustive()) { 42 *r0 = codes_[counter_ % range_]; 43 *r1 = codes_[counter_ / range_]; 44 } else { 45 // Try each integer at least once for both r0 and r1. 46 int index = counter_ / 2; 47 if (counter_ & 1) { 48 *r0 = codes_[index % range_]; 49 *r1 = codes_[index / range_]; 50 } else { 51 *r1 = codes_[index % range_]; 52 *r0 = codes_[index / range_]; 53 } 54 } 55 counter_++; 56 if ((same_is_ok) || (*r0 != *r1)) break; 57 if (counter_ == max_pairs_) { 58 // For the last hurrah, reg#0 with reg#n-1 59 *r0 = codes_[0]; 60 *r1 = codes_[range_ - 1]; 61 break; 62 } 63 } while (true); 64 } 65 66 private: 67 int range_; 68 const int* codes_; 69 int max_pairs_; 70 int counter_; 71 bool exhaustive() { return max_pairs_ == (range_ * range_); } 72 }; 73 74 75 // Pairs of general purpose registers. 76 class RegisterPairs : public Pairs { 77 public: 78 RegisterPairs() 79 : Pairs( 80 100, 81 RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) 82 ->num_allocatable_general_registers(), 83 RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) 84 ->allocatable_general_codes()) {} 85 }; 86 87 88 // Pairs of double registers. 89 class Float32RegisterPairs : public Pairs { 90 public: 91 Float32RegisterPairs() 92 : Pairs( 93 100, 94 RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) 95 ->num_allocatable_aliased_double_registers(), 96 RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) 97 ->allocatable_double_codes()) {} 98 }; 99 100 101 // Pairs of double registers. 102 class Float64RegisterPairs : public Pairs { 103 public: 104 Float64RegisterPairs() 105 : Pairs( 106 100, 107 RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) 108 ->num_allocatable_aliased_double_registers(), 109 RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) 110 ->allocatable_double_codes()) {} 111 }; 112 113 114 // Helper for allocating either an GP or FP reg, or the next stack slot. 115 struct Allocator { 116 Allocator(int* gp, int gpc, int* fp, int fpc) 117 : gp_count(gpc), 118 gp_offset(0), 119 gp_regs(gp), 120 fp_count(fpc), 121 fp_offset(0), 122 fp_regs(fp), 123 stack_offset(0) {} 124 125 int gp_count; 126 int gp_offset; 127 int* gp_regs; 128 129 int fp_count; 130 int fp_offset; 131 int* fp_regs; 132 133 int stack_offset; 134 135 LinkageLocation Next(MachineType type) { 136 if (IsFloatingPoint(type.representation())) { 137 // Allocate a floating point register/stack location. 138 if (fp_offset < fp_count) { 139 return LinkageLocation::ForRegister(fp_regs[fp_offset++]); 140 } else { 141 int offset = -1 - stack_offset; 142 stack_offset += StackWords(type); 143 return LinkageLocation::ForCallerFrameSlot(offset); 144 } 145 } else { 146 // Allocate a general purpose register/stack location. 147 if (gp_offset < gp_count) { 148 return LinkageLocation::ForRegister(gp_regs[gp_offset++]); 149 } else { 150 int offset = -1 - stack_offset; 151 stack_offset += StackWords(type); 152 return LinkageLocation::ForCallerFrameSlot(offset); 153 } 154 } 155 } 156 int StackWords(MachineType type) { 157 // TODO(titzer): hack. float32 occupies 8 bytes on stack. 158 int size = IsFloatingPoint(type.representation()) 159 ? kDoubleSize 160 : (1 << ElementSizeLog2Of(type.representation())); 161 return size <= kPointerSize ? 1 : size / kPointerSize; 162 } 163 void Reset() { 164 fp_offset = 0; 165 gp_offset = 0; 166 stack_offset = 0; 167 } 168 }; 169 170 171 class RegisterConfig { 172 public: 173 RegisterConfig(Allocator& p, Allocator& r) : params(p), rets(r) {} 174 175 CallDescriptor* Create(Zone* zone, MachineSignature* msig) { 176 rets.Reset(); 177 params.Reset(); 178 179 LocationSignature::Builder locations(zone, msig->return_count(), 180 msig->parameter_count()); 181 // Add return location(s). 182 const int return_count = static_cast<int>(locations.return_count_); 183 for (int i = 0; i < return_count; i++) { 184 locations.AddReturn(rets.Next(msig->GetReturn(i))); 185 } 186 187 // Add register and/or stack parameter(s). 188 const int parameter_count = static_cast<int>(msig->parameter_count()); 189 for (int i = 0; i < parameter_count; i++) { 190 locations.AddParam(params.Next(msig->GetParam(i))); 191 } 192 193 const RegList kCalleeSaveRegisters = 0; 194 const RegList kCalleeSaveFPRegisters = 0; 195 196 MachineType target_type = MachineType::AnyTagged(); 197 LinkageLocation target_loc = LinkageLocation::ForAnyRegister(); 198 int stack_param_count = params.stack_offset; 199 return new (zone) CallDescriptor( // -- 200 CallDescriptor::kCallCodeObject, // kind 201 target_type, // target MachineType 202 target_loc, // target location 203 msig, // machine_sig 204 locations.Build(), // location_sig 205 stack_param_count, // stack_parameter_count 206 compiler::Operator::kNoProperties, // properties 207 kCalleeSaveRegisters, // callee-saved registers 208 kCalleeSaveFPRegisters, // callee-saved fp regs 209 CallDescriptor::kUseNativeStack, // flags 210 "c-call"); 211 } 212 213 private: 214 Allocator& params; 215 Allocator& rets; 216 }; 217 218 const int kMaxParamCount = 64; 219 220 MachineType kIntTypes[kMaxParamCount + 1] = { 221 MachineType::Int32(), MachineType::Int32(), MachineType::Int32(), 222 MachineType::Int32(), MachineType::Int32(), MachineType::Int32(), 223 MachineType::Int32(), MachineType::Int32(), MachineType::Int32(), 224 MachineType::Int32(), MachineType::Int32(), MachineType::Int32(), 225 MachineType::Int32(), MachineType::Int32(), MachineType::Int32(), 226 MachineType::Int32(), MachineType::Int32(), MachineType::Int32(), 227 MachineType::Int32(), MachineType::Int32(), MachineType::Int32(), 228 MachineType::Int32(), MachineType::Int32(), MachineType::Int32(), 229 MachineType::Int32(), MachineType::Int32(), MachineType::Int32(), 230 MachineType::Int32(), MachineType::Int32(), MachineType::Int32(), 231 MachineType::Int32(), MachineType::Int32(), MachineType::Int32(), 232 MachineType::Int32(), MachineType::Int32(), MachineType::Int32(), 233 MachineType::Int32(), MachineType::Int32(), MachineType::Int32(), 234 MachineType::Int32(), MachineType::Int32(), MachineType::Int32(), 235 MachineType::Int32(), MachineType::Int32(), MachineType::Int32(), 236 MachineType::Int32(), MachineType::Int32(), MachineType::Int32(), 237 MachineType::Int32(), MachineType::Int32(), MachineType::Int32(), 238 MachineType::Int32(), MachineType::Int32(), MachineType::Int32(), 239 MachineType::Int32(), MachineType::Int32(), MachineType::Int32(), 240 MachineType::Int32(), MachineType::Int32(), MachineType::Int32(), 241 MachineType::Int32(), MachineType::Int32(), MachineType::Int32(), 242 MachineType::Int32(), MachineType::Int32()}; 243 244 245 // For making uniform int32 signatures shorter. 246 class Int32Signature : public MachineSignature { 247 public: 248 explicit Int32Signature(int param_count) 249 : MachineSignature(1, param_count, kIntTypes) { 250 CHECK(param_count <= kMaxParamCount); 251 } 252 }; 253 254 255 Handle<Code> CompileGraph(const char* name, CallDescriptor* desc, Graph* graph, 256 Schedule* schedule = nullptr) { 257 Isolate* isolate = CcTest::InitIsolateOnce(); 258 CompilationInfo info("testing", isolate, graph->zone()); 259 Handle<Code> code = 260 Pipeline::GenerateCodeForTesting(&info, desc, graph, schedule); 261 CHECK(!code.is_null()); 262 #ifdef ENABLE_DISASSEMBLER 263 if (FLAG_print_opt_code) { 264 OFStream os(stdout); 265 code->Disassemble(name, os); 266 } 267 #endif 268 return code; 269 } 270 271 272 Handle<Code> WrapWithCFunction(Handle<Code> inner, CallDescriptor* desc) { 273 Zone zone; 274 MachineSignature* msig = 275 const_cast<MachineSignature*>(desc->GetMachineSignature()); 276 int param_count = static_cast<int>(msig->parameter_count()); 277 GraphAndBuilders caller(&zone); 278 { 279 GraphAndBuilders& b = caller; 280 Node* start = b.graph()->NewNode(b.common()->Start(param_count + 3)); 281 b.graph()->SetStart(start); 282 Node* target = b.graph()->NewNode(b.common()->HeapConstant(inner)); 283 284 // Add arguments to the call. 285 Node** args = zone.NewArray<Node*>(param_count + 3); 286 int index = 0; 287 args[index++] = target; 288 for (int i = 0; i < param_count; i++) { 289 args[index] = b.graph()->NewNode(b.common()->Parameter(i), start); 290 index++; 291 } 292 args[index++] = start; // effect. 293 args[index++] = start; // control. 294 295 // Build the call and return nodes. 296 Node* call = 297 b.graph()->NewNode(b.common()->Call(desc), param_count + 3, args); 298 Node* ret = b.graph()->NewNode(b.common()->Return(), call, call, start); 299 b.graph()->SetEnd(ret); 300 } 301 302 CallDescriptor* cdesc = Linkage::GetSimplifiedCDescriptor(&zone, msig); 303 304 return CompileGraph("wrapper", cdesc, caller.graph()); 305 } 306 307 308 template <typename CType> 309 class ArgsBuffer { 310 public: 311 static const int kMaxParamCount = 64; 312 313 explicit ArgsBuffer(int count, int seed = 1) : count_(count), seed_(seed) { 314 // initialize the buffer with "seed 0" 315 seed_ = 0; 316 Mutate(); 317 seed_ = seed; 318 } 319 320 class Sig : public MachineSignature { 321 public: 322 explicit Sig(int param_count) 323 : MachineSignature(1, param_count, MachTypes()) { 324 CHECK(param_count <= kMaxParamCount); 325 } 326 }; 327 328 static MachineType* MachTypes() { 329 MachineType t = MachineTypeForC<CType>(); 330 static MachineType kTypes[kMaxParamCount + 1] = { 331 t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, 332 t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, 333 t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t}; 334 return kTypes; 335 } 336 337 Node* MakeConstant(RawMachineAssembler& raw, int32_t value) { 338 return raw.Int32Constant(value); 339 } 340 341 Node* MakeConstant(RawMachineAssembler& raw, int64_t value) { 342 return raw.Int64Constant(value); 343 } 344 345 Node* MakeConstant(RawMachineAssembler& raw, float32 value) { 346 return raw.Float32Constant(value); 347 } 348 349 Node* MakeConstant(RawMachineAssembler& raw, float64 value) { 350 return raw.Float64Constant(value); 351 } 352 353 Node* LoadInput(RawMachineAssembler& raw, Node* base, int index) { 354 Node* offset = raw.Int32Constant(index * sizeof(CType)); 355 return raw.Load(MachineTypeForC<CType>(), base, offset); 356 } 357 358 Node* StoreOutput(RawMachineAssembler& raw, Node* value) { 359 Node* base = raw.PointerConstant(&output); 360 Node* offset = raw.Int32Constant(0); 361 return raw.Store(MachineTypeForC<CType>().representation(), base, offset, 362 value, kNoWriteBarrier); 363 } 364 365 // Computes the next set of inputs by updating the {input} array. 366 void Mutate(); 367 368 void Reset() { memset(input, 0, sizeof(input)); } 369 370 int count_; 371 int seed_; 372 CType input[kMaxParamCount]; 373 CType output; 374 }; 375 376 377 template <> 378 void ArgsBuffer<int32_t>::Mutate() { 379 uint32_t base = 1111111111u * seed_; 380 for (int j = 0; j < count_ && j < kMaxParamCount; j++) { 381 input[j] = static_cast<int32_t>(256 + base + j + seed_ * 13); 382 } 383 output = -1; 384 seed_++; 385 } 386 387 388 template <> 389 void ArgsBuffer<int64_t>::Mutate() { 390 uint64_t base = 11111111111111111ull * seed_; 391 for (int j = 0; j < count_ && j < kMaxParamCount; j++) { 392 input[j] = static_cast<int64_t>(256 + base + j + seed_ * 13); 393 } 394 output = -1; 395 seed_++; 396 } 397 398 399 template <> 400 void ArgsBuffer<float32>::Mutate() { 401 float64 base = -33.25 * seed_; 402 for (int j = 0; j < count_ && j < kMaxParamCount; j++) { 403 input[j] = 256 + base + j + seed_ * 13; 404 } 405 output = std::numeric_limits<float32>::quiet_NaN(); 406 seed_++; 407 } 408 409 410 template <> 411 void ArgsBuffer<float64>::Mutate() { 412 float64 base = -111.25 * seed_; 413 for (int j = 0; j < count_ && j < kMaxParamCount; j++) { 414 input[j] = 256 + base + j + seed_ * 13; 415 } 416 output = std::numeric_limits<float64>::quiet_NaN(); 417 seed_++; 418 } 419 420 421 int ParamCount(CallDescriptor* desc) { 422 return static_cast<int>(desc->GetMachineSignature()->parameter_count()); 423 } 424 425 426 template <typename CType> 427 class Computer { 428 public: 429 static void Run(CallDescriptor* desc, 430 void (*build)(CallDescriptor*, RawMachineAssembler&), 431 CType (*compute)(CallDescriptor*, CType* inputs), 432 int seed = 1) { 433 int num_params = ParamCount(desc); 434 CHECK_LE(num_params, kMaxParamCount); 435 Isolate* isolate = CcTest::InitIsolateOnce(); 436 HandleScope scope(isolate); 437 Handle<Code> inner = Handle<Code>::null(); 438 { 439 // Build the graph for the computation. 440 Zone zone; 441 Graph graph(&zone); 442 RawMachineAssembler raw(isolate, &graph, desc); 443 build(desc, raw); 444 inner = CompileGraph("Compute", desc, &graph, raw.Export()); 445 } 446 447 CSignature0<int32_t> csig; 448 ArgsBuffer<CType> io(num_params, seed); 449 450 { 451 // constant mode. 452 Handle<Code> wrapper = Handle<Code>::null(); 453 { 454 // Wrap the above code with a callable function that passes constants. 455 Zone zone; 456 Graph graph(&zone); 457 CallDescriptor* cdesc = Linkage::GetSimplifiedCDescriptor(&zone, &csig); 458 RawMachineAssembler raw(isolate, &graph, cdesc); 459 Node* target = raw.HeapConstant(inner); 460 Node** args = zone.NewArray<Node*>(num_params); 461 for (int i = 0; i < num_params; i++) { 462 args[i] = io.MakeConstant(raw, io.input[i]); 463 } 464 465 Node* call = raw.CallN(desc, target, args); 466 Node* store = io.StoreOutput(raw, call); 467 USE(store); 468 raw.Return(raw.Int32Constant(seed)); 469 wrapper = 470 CompileGraph("Compute-wrapper-const", cdesc, &graph, raw.Export()); 471 } 472 473 CodeRunner<int32_t> runnable(isolate, wrapper, &csig); 474 475 // Run the code, checking it against the reference. 476 CType expected = compute(desc, io.input); 477 int32_t check_seed = runnable.Call(); 478 CHECK_EQ(seed, check_seed); 479 CHECK_EQ(expected, io.output); 480 } 481 482 { 483 // buffer mode. 484 Handle<Code> wrapper = Handle<Code>::null(); 485 { 486 // Wrap the above code with a callable function that loads from {input}. 487 Zone zone; 488 Graph graph(&zone); 489 CallDescriptor* cdesc = Linkage::GetSimplifiedCDescriptor(&zone, &csig); 490 RawMachineAssembler raw(isolate, &graph, cdesc); 491 Node* base = raw.PointerConstant(io.input); 492 Node* target = raw.HeapConstant(inner); 493 Node** args = zone.NewArray<Node*>(kMaxParamCount); 494 for (int i = 0; i < num_params; i++) { 495 args[i] = io.LoadInput(raw, base, i); 496 } 497 498 Node* call = raw.CallN(desc, target, args); 499 Node* store = io.StoreOutput(raw, call); 500 USE(store); 501 raw.Return(raw.Int32Constant(seed)); 502 wrapper = CompileGraph("Compute-wrapper", cdesc, &graph, raw.Export()); 503 } 504 505 CodeRunner<int32_t> runnable(isolate, wrapper, &csig); 506 507 // Run the code, checking it against the reference. 508 for (int i = 0; i < 5; i++) { 509 CType expected = compute(desc, io.input); 510 int32_t check_seed = runnable.Call(); 511 CHECK_EQ(seed, check_seed); 512 CHECK_EQ(expected, io.output); 513 io.Mutate(); 514 } 515 } 516 } 517 }; 518 519 } // namespace 520 521 522 static void TestInt32Sub(CallDescriptor* desc) { 523 Isolate* isolate = CcTest::InitIsolateOnce(); 524 HandleScope scope(isolate); 525 Zone zone; 526 GraphAndBuilders inner(&zone); 527 { 528 // Build the add function. 529 GraphAndBuilders& b = inner; 530 Node* start = b.graph()->NewNode(b.common()->Start(5)); 531 b.graph()->SetStart(start); 532 Node* p0 = b.graph()->NewNode(b.common()->Parameter(0), start); 533 Node* p1 = b.graph()->NewNode(b.common()->Parameter(1), start); 534 Node* add = b.graph()->NewNode(b.machine()->Int32Sub(), p0, p1); 535 Node* ret = b.graph()->NewNode(b.common()->Return(), add, start, start); 536 b.graph()->SetEnd(ret); 537 } 538 539 Handle<Code> inner_code = CompileGraph("Int32Sub", desc, inner.graph()); 540 Handle<Code> wrapper = WrapWithCFunction(inner_code, desc); 541 MachineSignature* msig = 542 const_cast<MachineSignature*>(desc->GetMachineSignature()); 543 CodeRunner<int32_t> runnable(isolate, wrapper, 544 CSignature::FromMachine(&zone, msig)); 545 546 FOR_INT32_INPUTS(i) { 547 FOR_INT32_INPUTS(j) { 548 int32_t expected = static_cast<int32_t>(static_cast<uint32_t>(*i) - 549 static_cast<uint32_t>(*j)); 550 int32_t result = runnable.Call(*i, *j); 551 CHECK_EQ(expected, result); 552 } 553 } 554 } 555 556 557 static void CopyTwentyInt32(CallDescriptor* desc) { 558 const int kNumParams = 20; 559 int32_t input[kNumParams]; 560 int32_t output[kNumParams]; 561 Isolate* isolate = CcTest::InitIsolateOnce(); 562 HandleScope scope(isolate); 563 Handle<Code> inner = Handle<Code>::null(); 564 { 565 // Writes all parameters into the output buffer. 566 Zone zone; 567 Graph graph(&zone); 568 RawMachineAssembler raw(isolate, &graph, desc); 569 Node* base = raw.PointerConstant(output); 570 for (int i = 0; i < kNumParams; i++) { 571 Node* offset = raw.Int32Constant(i * sizeof(int32_t)); 572 raw.Store(MachineRepresentation::kWord32, base, offset, raw.Parameter(i), 573 kNoWriteBarrier); 574 } 575 raw.Return(raw.Int32Constant(42)); 576 inner = CompileGraph("CopyTwentyInt32", desc, &graph, raw.Export()); 577 } 578 579 CSignature0<int32_t> csig; 580 Handle<Code> wrapper = Handle<Code>::null(); 581 { 582 // Loads parameters from the input buffer and calls the above code. 583 Zone zone; 584 Graph graph(&zone); 585 CallDescriptor* cdesc = Linkage::GetSimplifiedCDescriptor(&zone, &csig); 586 RawMachineAssembler raw(isolate, &graph, cdesc); 587 Node* base = raw.PointerConstant(input); 588 Node* target = raw.HeapConstant(inner); 589 Node** args = zone.NewArray<Node*>(kNumParams); 590 for (int i = 0; i < kNumParams; i++) { 591 Node* offset = raw.Int32Constant(i * sizeof(int32_t)); 592 args[i] = raw.Load(MachineType::Int32(), base, offset); 593 } 594 595 Node* call = raw.CallN(desc, target, args); 596 raw.Return(call); 597 wrapper = 598 CompileGraph("CopyTwentyInt32-wrapper", cdesc, &graph, raw.Export()); 599 } 600 601 CodeRunner<int32_t> runnable(isolate, wrapper, &csig); 602 603 // Run the code, checking it correctly implements the memcpy. 604 for (int i = 0; i < 5; i++) { 605 uint32_t base = 1111111111u * i; 606 for (int j = 0; j < kNumParams; j++) { 607 input[j] = static_cast<int32_t>(base + 13 * j); 608 } 609 610 memset(output, 0, sizeof(output)); 611 CHECK_EQ(42, runnable.Call()); 612 613 for (int j = 0; j < kNumParams; j++) { 614 CHECK_EQ(input[j], output[j]); 615 } 616 } 617 } 618 619 620 static void Test_RunInt32SubWithRet(int retreg) { 621 Int32Signature sig(2); 622 Zone zone; 623 RegisterPairs pairs; 624 while (pairs.More()) { 625 int parray[2]; 626 int rarray[] = {retreg}; 627 pairs.Next(&parray[0], &parray[1], false); 628 Allocator params(parray, 2, nullptr, 0); 629 Allocator rets(rarray, 1, nullptr, 0); 630 RegisterConfig config(params, rets); 631 CallDescriptor* desc = config.Create(&zone, &sig); 632 TestInt32Sub(desc); 633 } 634 } 635 636 637 // Separate tests for parallelization. 638 #define TEST_INT32_SUB_WITH_RET(x) \ 639 TEST(Run_Int32Sub_all_allocatable_pairs_##x) { \ 640 if (x < Register::kNumRegisters && \ 641 Register::from_code(x).IsAllocatable()) { \ 642 Test_RunInt32SubWithRet(x); \ 643 } \ 644 } 645 646 647 TEST_INT32_SUB_WITH_RET(0) 648 TEST_INT32_SUB_WITH_RET(1) 649 TEST_INT32_SUB_WITH_RET(2) 650 TEST_INT32_SUB_WITH_RET(3) 651 TEST_INT32_SUB_WITH_RET(4) 652 TEST_INT32_SUB_WITH_RET(5) 653 TEST_INT32_SUB_WITH_RET(6) 654 TEST_INT32_SUB_WITH_RET(7) 655 TEST_INT32_SUB_WITH_RET(8) 656 TEST_INT32_SUB_WITH_RET(9) 657 TEST_INT32_SUB_WITH_RET(10) 658 TEST_INT32_SUB_WITH_RET(11) 659 TEST_INT32_SUB_WITH_RET(12) 660 TEST_INT32_SUB_WITH_RET(13) 661 TEST_INT32_SUB_WITH_RET(14) 662 TEST_INT32_SUB_WITH_RET(15) 663 TEST_INT32_SUB_WITH_RET(16) 664 TEST_INT32_SUB_WITH_RET(17) 665 TEST_INT32_SUB_WITH_RET(18) 666 TEST_INT32_SUB_WITH_RET(19) 667 668 669 TEST(Run_Int32Sub_all_allocatable_single) { 670 Int32Signature sig(2); 671 RegisterPairs pairs; 672 while (pairs.More()) { 673 Zone zone; 674 int parray[1]; 675 int rarray[1]; 676 pairs.Next(&rarray[0], &parray[0], true); 677 Allocator params(parray, 1, nullptr, 0); 678 Allocator rets(rarray, 1, nullptr, 0); 679 RegisterConfig config(params, rets); 680 CallDescriptor* desc = config.Create(&zone, &sig); 681 TestInt32Sub(desc); 682 } 683 } 684 685 686 TEST(Run_CopyTwentyInt32_all_allocatable_pairs) { 687 Int32Signature sig(20); 688 RegisterPairs pairs; 689 while (pairs.More()) { 690 Zone zone; 691 int parray[2]; 692 int rarray[] = { 693 RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) 694 ->GetAllocatableGeneralCode(0)}; 695 pairs.Next(&parray[0], &parray[1], false); 696 Allocator params(parray, 2, nullptr, 0); 697 Allocator rets(rarray, 1, nullptr, 0); 698 RegisterConfig config(params, rets); 699 CallDescriptor* desc = config.Create(&zone, &sig); 700 CopyTwentyInt32(desc); 701 } 702 } 703 704 705 template <typename CType> 706 static void Run_Computation( 707 CallDescriptor* desc, void (*build)(CallDescriptor*, RawMachineAssembler&), 708 CType (*compute)(CallDescriptor*, CType* inputs), int seed = 1) { 709 Computer<CType>::Run(desc, build, compute, seed); 710 } 711 712 713 static uint32_t coeff[] = {1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 714 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 715 79, 83, 89, 97, 101, 103, 107, 109, 113}; 716 717 718 static void Build_Int32_WeightedSum(CallDescriptor* desc, 719 RawMachineAssembler& raw) { 720 Node* result = raw.Int32Constant(0); 721 for (int i = 0; i < ParamCount(desc); i++) { 722 Node* term = raw.Int32Mul(raw.Parameter(i), raw.Int32Constant(coeff[i])); 723 result = raw.Int32Add(result, term); 724 } 725 raw.Return(result); 726 } 727 728 729 static int32_t Compute_Int32_WeightedSum(CallDescriptor* desc, int32_t* input) { 730 uint32_t result = 0; 731 for (int i = 0; i < ParamCount(desc); i++) { 732 result += static_cast<uint32_t>(input[i]) * coeff[i]; 733 } 734 return static_cast<int32_t>(result); 735 } 736 737 738 static void Test_Int32_WeightedSum_of_size(int count) { 739 Int32Signature sig(count); 740 for (int p0 = 0; p0 < Register::kNumRegisters; p0++) { 741 if (Register::from_code(p0).IsAllocatable()) { 742 Zone zone; 743 744 int parray[] = {p0}; 745 int rarray[] = { 746 RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) 747 ->GetAllocatableGeneralCode(0)}; 748 Allocator params(parray, 1, nullptr, 0); 749 Allocator rets(rarray, 1, nullptr, 0); 750 RegisterConfig config(params, rets); 751 CallDescriptor* desc = config.Create(&zone, &sig); 752 Run_Computation<int32_t>(desc, Build_Int32_WeightedSum, 753 Compute_Int32_WeightedSum, 257 + count); 754 } 755 } 756 } 757 758 759 // Separate tests for parallelization. 760 #define TEST_INT32_WEIGHTEDSUM(x) \ 761 TEST(Run_Int32_WeightedSum_##x) { Test_Int32_WeightedSum_of_size(x); } 762 763 764 TEST_INT32_WEIGHTEDSUM(1) 765 TEST_INT32_WEIGHTEDSUM(2) 766 TEST_INT32_WEIGHTEDSUM(3) 767 TEST_INT32_WEIGHTEDSUM(4) 768 TEST_INT32_WEIGHTEDSUM(5) 769 TEST_INT32_WEIGHTEDSUM(7) 770 TEST_INT32_WEIGHTEDSUM(9) 771 TEST_INT32_WEIGHTEDSUM(11) 772 TEST_INT32_WEIGHTEDSUM(17) 773 TEST_INT32_WEIGHTEDSUM(19) 774 775 776 template <int which> 777 static void Build_Select(CallDescriptor* desc, RawMachineAssembler& raw) { 778 raw.Return(raw.Parameter(which)); 779 } 780 781 782 template <typename CType, int which> 783 static CType Compute_Select(CallDescriptor* desc, CType* inputs) { 784 return inputs[which]; 785 } 786 787 788 template <typename CType, int which> 789 static void RunSelect(CallDescriptor* desc) { 790 int count = ParamCount(desc); 791 if (count <= which) return; 792 Run_Computation<CType>(desc, Build_Select<which>, 793 Compute_Select<CType, which>, 794 1044 + which + 3 * sizeof(CType)); 795 } 796 797 798 template <int which> 799 void Test_Int32_Select() { 800 int parray[] = { 801 RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) 802 ->GetAllocatableGeneralCode(0)}; 803 int rarray[] = { 804 RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) 805 ->GetAllocatableGeneralCode(0)}; 806 Allocator params(parray, 1, nullptr, 0); 807 Allocator rets(rarray, 1, nullptr, 0); 808 RegisterConfig config(params, rets); 809 810 Zone zone; 811 812 for (int i = which + 1; i <= 64; i++) { 813 Int32Signature sig(i); 814 CallDescriptor* desc = config.Create(&zone, &sig); 815 RunSelect<int32_t, which>(desc); 816 } 817 } 818 819 820 // Separate tests for parallelization. 821 #define TEST_INT32_SELECT(x) \ 822 TEST(Run_Int32_Select_##x) { Test_Int32_Select<x>(); } 823 824 825 TEST_INT32_SELECT(0) 826 TEST_INT32_SELECT(1) 827 TEST_INT32_SELECT(2) 828 TEST_INT32_SELECT(3) 829 TEST_INT32_SELECT(4) 830 TEST_INT32_SELECT(5) 831 TEST_INT32_SELECT(6) 832 TEST_INT32_SELECT(11) 833 TEST_INT32_SELECT(15) 834 TEST_INT32_SELECT(19) 835 TEST_INT32_SELECT(45) 836 TEST_INT32_SELECT(62) 837 TEST_INT32_SELECT(63) 838 839 840 TEST(Int64Select_registers) { 841 if (RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) 842 ->num_allocatable_general_registers() < 2) 843 return; 844 if (kPointerSize < 8) return; // TODO(titzer): int64 on 32-bit platforms 845 846 int rarray[] = { 847 RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) 848 ->GetAllocatableGeneralCode(0)}; 849 ArgsBuffer<int64_t>::Sig sig(2); 850 851 RegisterPairs pairs; 852 Zone zone; 853 while (pairs.More()) { 854 int parray[2]; 855 pairs.Next(&parray[0], &parray[1], false); 856 Allocator params(parray, 2, nullptr, 0); 857 Allocator rets(rarray, 1, nullptr, 0); 858 RegisterConfig config(params, rets); 859 860 CallDescriptor* desc = config.Create(&zone, &sig); 861 RunSelect<int64_t, 0>(desc); 862 RunSelect<int64_t, 1>(desc); 863 } 864 } 865 866 867 TEST(Float32Select_registers) { 868 if (RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) 869 ->num_allocatable_double_registers() < 2) { 870 return; 871 } 872 873 int rarray[] = { 874 RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) 875 ->GetAllocatableDoubleCode(0)}; 876 ArgsBuffer<float32>::Sig sig(2); 877 878 Float32RegisterPairs pairs; 879 Zone zone; 880 while (pairs.More()) { 881 int parray[2]; 882 pairs.Next(&parray[0], &parray[1], false); 883 Allocator params(nullptr, 0, parray, 2); 884 Allocator rets(nullptr, 0, rarray, 1); 885 RegisterConfig config(params, rets); 886 887 CallDescriptor* desc = config.Create(&zone, &sig); 888 RunSelect<float32, 0>(desc); 889 RunSelect<float32, 1>(desc); 890 } 891 } 892 893 894 TEST(Float64Select_registers) { 895 if (RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) 896 ->num_allocatable_double_registers() < 2) 897 return; 898 if (RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) 899 ->num_allocatable_general_registers() < 2) 900 return; 901 int rarray[] = { 902 RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) 903 ->GetAllocatableDoubleCode(0)}; 904 ArgsBuffer<float64>::Sig sig(2); 905 906 Float64RegisterPairs pairs; 907 Zone zone; 908 while (pairs.More()) { 909 int parray[2]; 910 pairs.Next(&parray[0], &parray[1], false); 911 Allocator params(nullptr, 0, parray, 2); 912 Allocator rets(nullptr, 0, rarray, 1); 913 RegisterConfig config(params, rets); 914 915 CallDescriptor* desc = config.Create(&zone, &sig); 916 RunSelect<float64, 0>(desc); 917 RunSelect<float64, 1>(desc); 918 } 919 } 920 921 922 TEST(Float32Select_stack_params_return_reg) { 923 int rarray[] = { 924 RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) 925 ->GetAllocatableDoubleCode(0)}; 926 Allocator params(nullptr, 0, nullptr, 0); 927 Allocator rets(nullptr, 0, rarray, 1); 928 RegisterConfig config(params, rets); 929 930 Zone zone; 931 for (int count = 1; count < 6; count++) { 932 ArgsBuffer<float32>::Sig sig(count); 933 CallDescriptor* desc = config.Create(&zone, &sig); 934 RunSelect<float32, 0>(desc); 935 RunSelect<float32, 1>(desc); 936 RunSelect<float32, 2>(desc); 937 RunSelect<float32, 3>(desc); 938 RunSelect<float32, 4>(desc); 939 RunSelect<float32, 5>(desc); 940 } 941 } 942 943 944 TEST(Float64Select_stack_params_return_reg) { 945 int rarray[] = { 946 RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) 947 ->GetAllocatableDoubleCode(0)}; 948 Allocator params(nullptr, 0, nullptr, 0); 949 Allocator rets(nullptr, 0, rarray, 1); 950 RegisterConfig config(params, rets); 951 952 Zone zone; 953 for (int count = 1; count < 6; count++) { 954 ArgsBuffer<float64>::Sig sig(count); 955 CallDescriptor* desc = config.Create(&zone, &sig); 956 RunSelect<float64, 0>(desc); 957 RunSelect<float64, 1>(desc); 958 RunSelect<float64, 2>(desc); 959 RunSelect<float64, 3>(desc); 960 RunSelect<float64, 4>(desc); 961 RunSelect<float64, 5>(desc); 962 } 963 } 964 965 966 template <typename CType, int which> 967 static void Build_Select_With_Call(CallDescriptor* desc, 968 RawMachineAssembler& raw) { 969 Handle<Code> inner = Handle<Code>::null(); 970 int num_params = ParamCount(desc); 971 CHECK_LE(num_params, kMaxParamCount); 972 { 973 Isolate* isolate = CcTest::InitIsolateOnce(); 974 // Build the actual select. 975 Zone zone; 976 Graph graph(&zone); 977 RawMachineAssembler raw(isolate, &graph, desc); 978 raw.Return(raw.Parameter(which)); 979 inner = CompileGraph("Select-indirection", desc, &graph, raw.Export()); 980 CHECK(!inner.is_null()); 981 CHECK(inner->IsCode()); 982 } 983 984 { 985 // Build a call to the function that does the select. 986 Node* target = raw.HeapConstant(inner); 987 Node** args = raw.zone()->NewArray<Node*>(num_params); 988 for (int i = 0; i < num_params; i++) { 989 args[i] = raw.Parameter(i); 990 } 991 992 Node* call = raw.CallN(desc, target, args); 993 raw.Return(call); 994 } 995 } 996 997 998 TEST(Float64StackParamsToStackParams) { 999 int rarray[] = { 1000 RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) 1001 ->GetAllocatableDoubleCode(0)}; 1002 Allocator params(nullptr, 0, nullptr, 0); 1003 Allocator rets(nullptr, 0, rarray, 1); 1004 1005 Zone zone; 1006 ArgsBuffer<float64>::Sig sig(2); 1007 RegisterConfig config(params, rets); 1008 CallDescriptor* desc = config.Create(&zone, &sig); 1009 1010 Run_Computation<float64>(desc, Build_Select_With_Call<float64, 0>, 1011 Compute_Select<float64, 0>, 1098); 1012 1013 Run_Computation<float64>(desc, Build_Select_With_Call<float64, 1>, 1014 Compute_Select<float64, 1>, 1099); 1015 } 1016 1017 1018 void MixedParamTest(int start) { 1019 if (RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) 1020 ->num_double_registers() < 2) 1021 return; 1022 1023 // TODO(titzer): mix in 64-bit types on all platforms when supported. 1024 #if V8_TARGET_ARCH_32_BIT 1025 static MachineType types[] = { 1026 MachineType::Int32(), MachineType::Float32(), MachineType::Float64(), 1027 MachineType::Int32(), MachineType::Float64(), MachineType::Float32(), 1028 MachineType::Float32(), MachineType::Float64(), MachineType::Int32(), 1029 MachineType::Float32(), MachineType::Int32(), MachineType::Float64(), 1030 MachineType::Float64(), MachineType::Float32(), MachineType::Int32(), 1031 MachineType::Float64(), MachineType::Int32(), MachineType::Float32()}; 1032 #else 1033 static MachineType types[] = { 1034 MachineType::Int32(), MachineType::Int64(), MachineType::Float32(), 1035 MachineType::Float64(), MachineType::Int32(), MachineType::Float64(), 1036 MachineType::Float32(), MachineType::Int64(), MachineType::Int64(), 1037 MachineType::Float32(), MachineType::Float32(), MachineType::Int32(), 1038 MachineType::Float64(), MachineType::Float64(), MachineType::Int64(), 1039 MachineType::Int32(), MachineType::Float64(), MachineType::Int32(), 1040 MachineType::Float32()}; 1041 #endif 1042 1043 Isolate* isolate = CcTest::InitIsolateOnce(); 1044 1045 // Build machine signature 1046 MachineType* params = &types[start]; 1047 const int num_params = static_cast<int>(arraysize(types) - start); 1048 1049 // Build call descriptor 1050 int parray_gp[] = { 1051 RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) 1052 ->GetAllocatableGeneralCode(0), 1053 RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) 1054 ->GetAllocatableGeneralCode(1)}; 1055 int rarray_gp[] = { 1056 RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) 1057 ->GetAllocatableGeneralCode(0)}; 1058 int parray_fp[] = { 1059 RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) 1060 ->GetAllocatableDoubleCode(0), 1061 RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) 1062 ->GetAllocatableDoubleCode(1)}; 1063 int rarray_fp[] = { 1064 RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN) 1065 ->GetAllocatableDoubleCode(0)}; 1066 Allocator palloc(parray_gp, 2, parray_fp, 2); 1067 Allocator ralloc(rarray_gp, 1, rarray_fp, 1); 1068 RegisterConfig config(palloc, ralloc); 1069 1070 for (int which = 0; which < num_params; which++) { 1071 Zone zone; 1072 HandleScope scope(isolate); 1073 MachineSignature::Builder builder(&zone, 1, num_params); 1074 builder.AddReturn(params[which]); 1075 for (int j = 0; j < num_params; j++) builder.AddParam(params[j]); 1076 MachineSignature* sig = builder.Build(); 1077 CallDescriptor* desc = config.Create(&zone, sig); 1078 1079 Handle<Code> select; 1080 { 1081 // build the select. 1082 Zone zone; 1083 Graph graph(&zone); 1084 RawMachineAssembler raw(isolate, &graph, desc); 1085 raw.Return(raw.Parameter(which)); 1086 select = CompileGraph("Compute", desc, &graph, raw.Export()); 1087 } 1088 1089 { 1090 // call the select. 1091 Handle<Code> wrapper = Handle<Code>::null(); 1092 int32_t expected_ret; 1093 char bytes[kDoubleSize]; 1094 V8_ALIGNED(8) char output[kDoubleSize]; 1095 int expected_size = 0; 1096 CSignature0<int32_t> csig; 1097 { 1098 // Wrap the select code with a callable function that passes constants. 1099 Zone zone; 1100 Graph graph(&zone); 1101 CallDescriptor* cdesc = Linkage::GetSimplifiedCDescriptor(&zone, &csig); 1102 RawMachineAssembler raw(isolate, &graph, cdesc); 1103 Node* target = raw.HeapConstant(select); 1104 Node** args = zone.NewArray<Node*>(num_params); 1105 int64_t constant = 0x0102030405060708; 1106 for (int i = 0; i < num_params; i++) { 1107 MachineType param_type = sig->GetParam(i); 1108 Node* konst = nullptr; 1109 if (param_type == MachineType::Int32()) { 1110 int32_t value[] = {static_cast<int32_t>(constant)}; 1111 konst = raw.Int32Constant(value[0]); 1112 if (i == which) memcpy(bytes, value, expected_size = 4); 1113 } 1114 if (param_type == MachineType::Int64()) { 1115 int64_t value[] = {static_cast<int64_t>(constant)}; 1116 konst = raw.Int64Constant(value[0]); 1117 if (i == which) memcpy(bytes, value, expected_size = 8); 1118 } 1119 if (param_type == MachineType::Float32()) { 1120 float32 value[] = {static_cast<float32>(constant)}; 1121 konst = raw.Float32Constant(value[0]); 1122 if (i == which) memcpy(bytes, value, expected_size = 4); 1123 } 1124 if (param_type == MachineType::Float64()) { 1125 float64 value[] = {static_cast<float64>(constant)}; 1126 konst = raw.Float64Constant(value[0]); 1127 if (i == which) memcpy(bytes, value, expected_size = 8); 1128 } 1129 CHECK_NOT_NULL(konst); 1130 1131 args[i] = konst; 1132 constant += 0x1010101010101010; 1133 } 1134 1135 Node* call = raw.CallN(desc, target, args); 1136 Node* store = 1137 raw.StoreToPointer(output, sig->GetReturn().representation(), call); 1138 USE(store); 1139 expected_ret = static_cast<int32_t>(constant); 1140 raw.Return(raw.Int32Constant(expected_ret)); 1141 wrapper = CompileGraph("Select-mixed-wrapper-const", cdesc, &graph, 1142 raw.Export()); 1143 } 1144 1145 CodeRunner<int32_t> runnable(isolate, wrapper, &csig); 1146 CHECK_EQ(expected_ret, runnable.Call()); 1147 for (int i = 0; i < expected_size; i++) { 1148 CHECK_EQ(static_cast<int>(bytes[i]), static_cast<int>(output[i])); 1149 } 1150 } 1151 } 1152 } 1153 1154 1155 TEST(MixedParams_0) { MixedParamTest(0); } 1156 TEST(MixedParams_1) { MixedParamTest(1); } 1157 TEST(MixedParams_2) { MixedParamTest(2); } 1158 TEST(MixedParams_3) { MixedParamTest(3); } 1159 1160 } // namespace compiler 1161 } // namespace internal 1162 } // namespace v8 1163