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 const auto GetRegConfig = RegisterConfiguration::Turbofan; 22 23 namespace { 24 typedef float float32; 25 typedef double float64; 26 27 // Picks a representative pair of integers from the given range. 28 // If there are less than {max_pairs} possible pairs, do them all, otherwise try 29 // to select a representative set. 30 class Pairs { 31 public: 32 Pairs(int max_pairs, int range, const int* codes) 33 : range_(range), 34 codes_(codes), 35 max_pairs_(std::min(max_pairs, range_ * range_)), 36 counter_(0) {} 37 38 bool More() { return counter_ < max_pairs_; } 39 40 void Next(int* r0, int* r1, bool same_is_ok) { 41 do { 42 // Find the next pair. 43 if (exhaustive()) { 44 *r0 = codes_[counter_ % range_]; 45 *r1 = codes_[counter_ / range_]; 46 } else { 47 // Try each integer at least once for both r0 and r1. 48 int index = counter_ / 2; 49 if (counter_ & 1) { 50 *r0 = codes_[index % range_]; 51 *r1 = codes_[index / range_]; 52 } else { 53 *r1 = codes_[index % range_]; 54 *r0 = codes_[index / range_]; 55 } 56 } 57 counter_++; 58 if ((same_is_ok) || (*r0 != *r1)) break; 59 if (counter_ == max_pairs_) { 60 // For the last hurrah, reg#0 with reg#n-1 61 *r0 = codes_[0]; 62 *r1 = codes_[range_ - 1]; 63 break; 64 } 65 } while (true); 66 } 67 68 private: 69 int range_; 70 const int* codes_; 71 int max_pairs_; 72 int counter_; 73 bool exhaustive() { return max_pairs_ == (range_ * range_); } 74 }; 75 76 77 // Pairs of general purpose registers. 78 class RegisterPairs : public Pairs { 79 public: 80 RegisterPairs() 81 : Pairs(100, GetRegConfig()->num_allocatable_general_registers(), 82 GetRegConfig()->allocatable_general_codes()) {} 83 }; 84 85 86 // Pairs of double registers. 87 class Float32RegisterPairs : public Pairs { 88 public: 89 Float32RegisterPairs() 90 : Pairs( 91 100, 92 #if V8_TARGET_ARCH_ARM 93 // TODO(bbudge) Modify wasm linkage to allow use of all float regs. 94 GetRegConfig()->num_allocatable_double_registers() / 2 - 2, 95 #else 96 GetRegConfig()->num_allocatable_double_registers(), 97 #endif 98 GetRegConfig()->allocatable_double_codes()) { 99 } 100 }; 101 102 103 // Pairs of double registers. 104 class Float64RegisterPairs : public Pairs { 105 public: 106 Float64RegisterPairs() 107 : Pairs(100, GetRegConfig()->num_allocatable_double_registers(), 108 GetRegConfig()->allocatable_double_codes()) {} 109 }; 110 111 112 // Helper for allocating either an GP or FP reg, or the next stack slot. 113 struct Allocator { 114 Allocator(int* gp, int gpc, int* fp, int fpc) 115 : gp_count(gpc), 116 gp_offset(0), 117 gp_regs(gp), 118 fp_count(fpc), 119 fp_offset(0), 120 fp_regs(fp), 121 stack_offset(0) {} 122 123 int gp_count; 124 int gp_offset; 125 int* gp_regs; 126 127 int fp_count; 128 int fp_offset; 129 int* fp_regs; 130 131 int stack_offset; 132 133 LinkageLocation Next(MachineType type) { 134 if (IsFloatingPoint(type.representation())) { 135 // Allocate a floating point register/stack location. 136 if (fp_offset < fp_count) { 137 int code = fp_regs[fp_offset++]; 138 #if V8_TARGET_ARCH_ARM 139 // TODO(bbudge) Modify wasm linkage to allow use of all float regs. 140 if (type.representation() == MachineRepresentation::kFloat32) code *= 2; 141 #endif 142 return LinkageLocation::ForRegister(code); 143 } else { 144 int offset = -1 - stack_offset; 145 stack_offset += StackWords(type); 146 return LinkageLocation::ForCallerFrameSlot(offset); 147 } 148 } else { 149 // Allocate a general purpose register/stack location. 150 if (gp_offset < gp_count) { 151 return LinkageLocation::ForRegister(gp_regs[gp_offset++]); 152 } else { 153 int offset = -1 - stack_offset; 154 stack_offset += StackWords(type); 155 return LinkageLocation::ForCallerFrameSlot(offset); 156 } 157 } 158 } 159 int StackWords(MachineType type) { 160 int size = 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(ArrayVector("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(inner->GetIsolate()->allocator()); 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(isolate->allocator()); 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(isolate->allocator()); 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(isolate->allocator()); 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(isolate->allocator()); 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(isolate->allocator()); 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(isolate->allocator()); 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 base::AccountingAllocator allocator; 623 Zone zone(&allocator); 624 RegisterPairs pairs; 625 while (pairs.More()) { 626 int parray[2]; 627 int rarray[] = {retreg}; 628 pairs.Next(&parray[0], &parray[1], false); 629 Allocator params(parray, 2, nullptr, 0); 630 Allocator rets(rarray, 1, nullptr, 0); 631 RegisterConfig config(params, rets); 632 CallDescriptor* desc = config.Create(&zone, &sig); 633 TestInt32Sub(desc); 634 } 635 } 636 637 638 // Separate tests for parallelization. 639 #define TEST_INT32_SUB_WITH_RET(x) \ 640 TEST(Run_Int32Sub_all_allocatable_pairs_##x) { \ 641 if (x < Register::kNumRegisters && \ 642 GetRegConfig()->IsAllocatableGeneralCode(x)) { \ 643 Test_RunInt32SubWithRet(x); \ 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 base::AccountingAllocator allocator; 674 Zone zone(&allocator); 675 int parray[1]; 676 int rarray[1]; 677 pairs.Next(&rarray[0], &parray[0], true); 678 Allocator params(parray, 1, nullptr, 0); 679 Allocator rets(rarray, 1, nullptr, 0); 680 RegisterConfig config(params, rets); 681 CallDescriptor* desc = config.Create(&zone, &sig); 682 TestInt32Sub(desc); 683 } 684 } 685 686 687 TEST(Run_CopyTwentyInt32_all_allocatable_pairs) { 688 Int32Signature sig(20); 689 RegisterPairs pairs; 690 while (pairs.More()) { 691 base::AccountingAllocator allocator; 692 Zone zone(&allocator); 693 int parray[2]; 694 int rarray[] = {GetRegConfig()->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 (GetRegConfig()->IsAllocatableGeneralCode(p0)) { 742 base::AccountingAllocator allocator; 743 Zone zone(&allocator); 744 745 int parray[] = {p0}; 746 int rarray[] = {GetRegConfig()->GetAllocatableGeneralCode(0)}; 747 Allocator params(parray, 1, nullptr, 0); 748 Allocator rets(rarray, 1, nullptr, 0); 749 RegisterConfig config(params, rets); 750 CallDescriptor* desc = config.Create(&zone, &sig); 751 Run_Computation<int32_t>(desc, Build_Int32_WeightedSum, 752 Compute_Int32_WeightedSum, 257 + count); 753 } 754 } 755 } 756 757 758 // Separate tests for parallelization. 759 #define TEST_INT32_WEIGHTEDSUM(x) \ 760 TEST(Run_Int32_WeightedSum_##x) { Test_Int32_WeightedSum_of_size(x); } 761 762 763 TEST_INT32_WEIGHTEDSUM(1) 764 TEST_INT32_WEIGHTEDSUM(2) 765 TEST_INT32_WEIGHTEDSUM(3) 766 TEST_INT32_WEIGHTEDSUM(4) 767 TEST_INT32_WEIGHTEDSUM(5) 768 TEST_INT32_WEIGHTEDSUM(7) 769 TEST_INT32_WEIGHTEDSUM(9) 770 TEST_INT32_WEIGHTEDSUM(11) 771 TEST_INT32_WEIGHTEDSUM(17) 772 TEST_INT32_WEIGHTEDSUM(19) 773 774 775 template <int which> 776 static void Build_Select(CallDescriptor* desc, RawMachineAssembler& raw) { 777 raw.Return(raw.Parameter(which)); 778 } 779 780 781 template <typename CType, int which> 782 static CType Compute_Select(CallDescriptor* desc, CType* inputs) { 783 return inputs[which]; 784 } 785 786 787 template <typename CType, int which> 788 static void RunSelect(CallDescriptor* desc) { 789 int count = ParamCount(desc); 790 if (count <= which) return; 791 Run_Computation<CType>(desc, Build_Select<which>, 792 Compute_Select<CType, which>, 793 1044 + which + 3 * sizeof(CType)); 794 } 795 796 797 template <int which> 798 void Test_Int32_Select() { 799 int parray[] = {GetRegConfig()->GetAllocatableGeneralCode(0)}; 800 int rarray[] = {GetRegConfig()->GetAllocatableGeneralCode(0)}; 801 Allocator params(parray, 1, nullptr, 0); 802 Allocator rets(rarray, 1, nullptr, 0); 803 RegisterConfig config(params, rets); 804 805 base::AccountingAllocator allocator; 806 Zone zone(&allocator); 807 808 for (int i = which + 1; i <= 64; i++) { 809 Int32Signature sig(i); 810 CallDescriptor* desc = config.Create(&zone, &sig); 811 RunSelect<int32_t, which>(desc); 812 } 813 } 814 815 816 // Separate tests for parallelization. 817 #define TEST_INT32_SELECT(x) \ 818 TEST(Run_Int32_Select_##x) { Test_Int32_Select<x>(); } 819 820 821 TEST_INT32_SELECT(0) 822 TEST_INT32_SELECT(1) 823 TEST_INT32_SELECT(2) 824 TEST_INT32_SELECT(3) 825 TEST_INT32_SELECT(4) 826 TEST_INT32_SELECT(5) 827 TEST_INT32_SELECT(6) 828 TEST_INT32_SELECT(11) 829 TEST_INT32_SELECT(15) 830 TEST_INT32_SELECT(19) 831 TEST_INT32_SELECT(45) 832 TEST_INT32_SELECT(62) 833 TEST_INT32_SELECT(63) 834 835 836 TEST(Int64Select_registers) { 837 if (GetRegConfig()->num_allocatable_general_registers() < 2) return; 838 if (kPointerSize < 8) return; // TODO(titzer): int64 on 32-bit platforms 839 840 int rarray[] = {GetRegConfig()->GetAllocatableGeneralCode(0)}; 841 ArgsBuffer<int64_t>::Sig sig(2); 842 843 RegisterPairs pairs; 844 base::AccountingAllocator allocator; 845 Zone zone(&allocator); 846 while (pairs.More()) { 847 int parray[2]; 848 pairs.Next(&parray[0], &parray[1], false); 849 Allocator params(parray, 2, nullptr, 0); 850 Allocator rets(rarray, 1, nullptr, 0); 851 RegisterConfig config(params, rets); 852 853 CallDescriptor* desc = config.Create(&zone, &sig); 854 RunSelect<int64_t, 0>(desc); 855 RunSelect<int64_t, 1>(desc); 856 } 857 } 858 859 860 TEST(Float32Select_registers) { 861 if (GetRegConfig()->num_allocatable_double_registers() < 2) { 862 return; 863 } 864 865 int rarray[] = {GetRegConfig()->GetAllocatableDoubleCode(0)}; 866 ArgsBuffer<float32>::Sig sig(2); 867 868 Float32RegisterPairs pairs; 869 base::AccountingAllocator allocator; 870 Zone zone(&allocator); 871 while (pairs.More()) { 872 int parray[2]; 873 pairs.Next(&parray[0], &parray[1], false); 874 Allocator params(nullptr, 0, parray, 2); 875 Allocator rets(nullptr, 0, rarray, 1); 876 RegisterConfig config(params, rets); 877 878 CallDescriptor* desc = config.Create(&zone, &sig); 879 RunSelect<float32, 0>(desc); 880 RunSelect<float32, 1>(desc); 881 } 882 } 883 884 885 TEST(Float64Select_registers) { 886 if (GetRegConfig()->num_allocatable_double_registers() < 2) return; 887 if (GetRegConfig()->num_allocatable_general_registers() < 2) return; 888 int rarray[] = {GetRegConfig()->GetAllocatableDoubleCode(0)}; 889 ArgsBuffer<float64>::Sig sig(2); 890 891 Float64RegisterPairs pairs; 892 base::AccountingAllocator allocator; 893 Zone zone(&allocator); 894 while (pairs.More()) { 895 int parray[2]; 896 pairs.Next(&parray[0], &parray[1], false); 897 Allocator params(nullptr, 0, parray, 2); 898 Allocator rets(nullptr, 0, rarray, 1); 899 RegisterConfig config(params, rets); 900 901 CallDescriptor* desc = config.Create(&zone, &sig); 902 RunSelect<float64, 0>(desc); 903 RunSelect<float64, 1>(desc); 904 } 905 } 906 907 908 TEST(Float32Select_stack_params_return_reg) { 909 int rarray[] = {GetRegConfig()->GetAllocatableDoubleCode(0)}; 910 Allocator params(nullptr, 0, nullptr, 0); 911 Allocator rets(nullptr, 0, rarray, 1); 912 RegisterConfig config(params, rets); 913 914 base::AccountingAllocator allocator; 915 Zone zone(&allocator); 916 for (int count = 1; count < 6; count++) { 917 ArgsBuffer<float32>::Sig sig(count); 918 CallDescriptor* desc = config.Create(&zone, &sig); 919 RunSelect<float32, 0>(desc); 920 RunSelect<float32, 1>(desc); 921 RunSelect<float32, 2>(desc); 922 RunSelect<float32, 3>(desc); 923 RunSelect<float32, 4>(desc); 924 RunSelect<float32, 5>(desc); 925 } 926 } 927 928 929 TEST(Float64Select_stack_params_return_reg) { 930 int rarray[] = {GetRegConfig()->GetAllocatableDoubleCode(0)}; 931 Allocator params(nullptr, 0, nullptr, 0); 932 Allocator rets(nullptr, 0, rarray, 1); 933 RegisterConfig config(params, rets); 934 935 base::AccountingAllocator allocator; 936 Zone zone(&allocator); 937 for (int count = 1; count < 6; count++) { 938 ArgsBuffer<float64>::Sig sig(count); 939 CallDescriptor* desc = config.Create(&zone, &sig); 940 RunSelect<float64, 0>(desc); 941 RunSelect<float64, 1>(desc); 942 RunSelect<float64, 2>(desc); 943 RunSelect<float64, 3>(desc); 944 RunSelect<float64, 4>(desc); 945 RunSelect<float64, 5>(desc); 946 } 947 } 948 949 950 template <typename CType, int which> 951 static void Build_Select_With_Call(CallDescriptor* desc, 952 RawMachineAssembler& raw) { 953 Handle<Code> inner = Handle<Code>::null(); 954 int num_params = ParamCount(desc); 955 CHECK_LE(num_params, kMaxParamCount); 956 { 957 Isolate* isolate = CcTest::InitIsolateOnce(); 958 // Build the actual select. 959 Zone zone(isolate->allocator()); 960 Graph graph(&zone); 961 RawMachineAssembler raw(isolate, &graph, desc); 962 raw.Return(raw.Parameter(which)); 963 inner = CompileGraph("Select-indirection", desc, &graph, raw.Export()); 964 CHECK(!inner.is_null()); 965 CHECK(inner->IsCode()); 966 } 967 968 { 969 // Build a call to the function that does the select. 970 Node* target = raw.HeapConstant(inner); 971 Node** args = raw.zone()->NewArray<Node*>(num_params); 972 for (int i = 0; i < num_params; i++) { 973 args[i] = raw.Parameter(i); 974 } 975 976 Node* call = raw.CallN(desc, target, args); 977 raw.Return(call); 978 } 979 } 980 981 982 TEST(Float64StackParamsToStackParams) { 983 int rarray[] = {GetRegConfig()->GetAllocatableDoubleCode(0)}; 984 Allocator params(nullptr, 0, nullptr, 0); 985 Allocator rets(nullptr, 0, rarray, 1); 986 987 base::AccountingAllocator allocator; 988 Zone zone(&allocator); 989 ArgsBuffer<float64>::Sig sig(2); 990 RegisterConfig config(params, rets); 991 CallDescriptor* desc = config.Create(&zone, &sig); 992 993 Run_Computation<float64>(desc, Build_Select_With_Call<float64, 0>, 994 Compute_Select<float64, 0>, 1098); 995 996 Run_Computation<float64>(desc, Build_Select_With_Call<float64, 1>, 997 Compute_Select<float64, 1>, 1099); 998 } 999 1000 1001 void MixedParamTest(int start) { 1002 if (GetRegConfig()->num_double_registers() < 2) return; 1003 1004 // TODO(titzer): mix in 64-bit types on all platforms when supported. 1005 #if V8_TARGET_ARCH_32_BIT 1006 static MachineType types[] = { 1007 MachineType::Int32(), MachineType::Float32(), MachineType::Float64(), 1008 MachineType::Int32(), MachineType::Float64(), MachineType::Float32(), 1009 MachineType::Float32(), MachineType::Float64(), MachineType::Int32(), 1010 MachineType::Float32(), MachineType::Int32(), MachineType::Float64(), 1011 MachineType::Float64(), MachineType::Float32(), MachineType::Int32(), 1012 MachineType::Float64(), MachineType::Int32(), MachineType::Float32()}; 1013 #else 1014 static MachineType types[] = { 1015 MachineType::Int32(), MachineType::Int64(), MachineType::Float32(), 1016 MachineType::Float64(), MachineType::Int32(), MachineType::Float64(), 1017 MachineType::Float32(), MachineType::Int64(), MachineType::Int64(), 1018 MachineType::Float32(), MachineType::Float32(), MachineType::Int32(), 1019 MachineType::Float64(), MachineType::Float64(), MachineType::Int64(), 1020 MachineType::Int32(), MachineType::Float64(), MachineType::Int32(), 1021 MachineType::Float32()}; 1022 #endif 1023 1024 Isolate* isolate = CcTest::InitIsolateOnce(); 1025 1026 // Build machine signature 1027 MachineType* params = &types[start]; 1028 const int num_params = static_cast<int>(arraysize(types) - start); 1029 1030 // Build call descriptor 1031 int parray_gp[] = {GetRegConfig()->GetAllocatableGeneralCode(0), 1032 GetRegConfig()->GetAllocatableGeneralCode(1)}; 1033 int rarray_gp[] = {GetRegConfig()->GetAllocatableGeneralCode(0)}; 1034 int parray_fp[] = {GetRegConfig()->GetAllocatableDoubleCode(0), 1035 GetRegConfig()->GetAllocatableDoubleCode(1)}; 1036 int rarray_fp[] = {GetRegConfig()->GetAllocatableDoubleCode(0)}; 1037 Allocator palloc(parray_gp, 2, parray_fp, 2); 1038 Allocator ralloc(rarray_gp, 1, rarray_fp, 1); 1039 RegisterConfig config(palloc, ralloc); 1040 1041 for (int which = 0; which < num_params; which++) { 1042 base::AccountingAllocator allocator; 1043 Zone zone(&allocator); 1044 HandleScope scope(isolate); 1045 MachineSignature::Builder builder(&zone, 1, num_params); 1046 builder.AddReturn(params[which]); 1047 for (int j = 0; j < num_params; j++) builder.AddParam(params[j]); 1048 MachineSignature* sig = builder.Build(); 1049 CallDescriptor* desc = config.Create(&zone, sig); 1050 1051 Handle<Code> select; 1052 { 1053 // build the select. 1054 Zone zone(&allocator); 1055 Graph graph(&zone); 1056 RawMachineAssembler raw(isolate, &graph, desc); 1057 raw.Return(raw.Parameter(which)); 1058 select = CompileGraph("Compute", desc, &graph, raw.Export()); 1059 } 1060 1061 { 1062 // call the select. 1063 Handle<Code> wrapper = Handle<Code>::null(); 1064 int32_t expected_ret; 1065 char bytes[kDoubleSize]; 1066 V8_ALIGNED(8) char output[kDoubleSize]; 1067 int expected_size = 0; 1068 CSignature0<int32_t> csig; 1069 { 1070 // Wrap the select code with a callable function that passes constants. 1071 Zone zone(&allocator); 1072 Graph graph(&zone); 1073 CallDescriptor* cdesc = Linkage::GetSimplifiedCDescriptor(&zone, &csig); 1074 RawMachineAssembler raw(isolate, &graph, cdesc); 1075 Node* target = raw.HeapConstant(select); 1076 Node** args = zone.NewArray<Node*>(num_params); 1077 int64_t constant = 0x0102030405060708; 1078 for (int i = 0; i < num_params; i++) { 1079 MachineType param_type = sig->GetParam(i); 1080 Node* konst = nullptr; 1081 if (param_type == MachineType::Int32()) { 1082 int32_t value[] = {static_cast<int32_t>(constant)}; 1083 konst = raw.Int32Constant(value[0]); 1084 if (i == which) memcpy(bytes, value, expected_size = 4); 1085 } 1086 if (param_type == MachineType::Int64()) { 1087 int64_t value[] = {static_cast<int64_t>(constant)}; 1088 konst = raw.Int64Constant(value[0]); 1089 if (i == which) memcpy(bytes, value, expected_size = 8); 1090 } 1091 if (param_type == MachineType::Float32()) { 1092 float32 value[] = {static_cast<float32>(constant)}; 1093 konst = raw.Float32Constant(value[0]); 1094 if (i == which) memcpy(bytes, value, expected_size = 4); 1095 } 1096 if (param_type == MachineType::Float64()) { 1097 float64 value[] = {static_cast<float64>(constant)}; 1098 konst = raw.Float64Constant(value[0]); 1099 if (i == which) memcpy(bytes, value, expected_size = 8); 1100 } 1101 CHECK_NOT_NULL(konst); 1102 1103 args[i] = konst; 1104 constant += 0x1010101010101010; 1105 } 1106 1107 Node* call = raw.CallN(desc, target, args); 1108 Node* store = 1109 raw.StoreToPointer(output, sig->GetReturn().representation(), call); 1110 USE(store); 1111 expected_ret = static_cast<int32_t>(constant); 1112 raw.Return(raw.Int32Constant(expected_ret)); 1113 wrapper = CompileGraph("Select-mixed-wrapper-const", cdesc, &graph, 1114 raw.Export()); 1115 } 1116 1117 CodeRunner<int32_t> runnable(isolate, wrapper, &csig); 1118 CHECK_EQ(expected_ret, runnable.Call()); 1119 for (int i = 0; i < expected_size; i++) { 1120 CHECK_EQ(static_cast<int>(bytes[i]), static_cast<int>(output[i])); 1121 } 1122 } 1123 } 1124 } 1125 1126 1127 TEST(MixedParams_0) { MixedParamTest(0); } 1128 TEST(MixedParams_1) { MixedParamTest(1); } 1129 TEST(MixedParams_2) { MixedParamTest(2); } 1130 TEST(MixedParams_3) { MixedParamTest(3); } 1131 1132 template <typename T> 1133 void TestStackSlot(MachineType slot_type, T expected) { 1134 // Test: Generate with a function f which reserves a stack slot, call an inner 1135 // function g from f which writes into the stack slot of f. 1136 1137 if (GetRegConfig()->num_allocatable_double_registers() < 2) return; 1138 1139 Isolate* isolate = CcTest::InitIsolateOnce(); 1140 1141 // Lots of code to generate the build descriptor for the inner function. 1142 int parray_gp[] = {GetRegConfig()->GetAllocatableGeneralCode(0), 1143 GetRegConfig()->GetAllocatableGeneralCode(1)}; 1144 int rarray_gp[] = {GetRegConfig()->GetAllocatableGeneralCode(0)}; 1145 int parray_fp[] = {GetRegConfig()->GetAllocatableDoubleCode(0), 1146 GetRegConfig()->GetAllocatableDoubleCode(1)}; 1147 int rarray_fp[] = {GetRegConfig()->GetAllocatableDoubleCode(0)}; 1148 Allocator palloc(parray_gp, 2, parray_fp, 2); 1149 Allocator ralloc(rarray_gp, 1, rarray_fp, 1); 1150 RegisterConfig config(palloc, ralloc); 1151 1152 Zone zone(isolate->allocator()); 1153 HandleScope scope(isolate); 1154 MachineSignature::Builder builder(&zone, 1, 12); 1155 builder.AddReturn(MachineType::Int32()); 1156 for (int i = 0; i < 10; i++) { 1157 builder.AddParam(MachineType::Int32()); 1158 } 1159 builder.AddParam(slot_type); 1160 builder.AddParam(MachineType::Pointer()); 1161 MachineSignature* sig = builder.Build(); 1162 CallDescriptor* desc = config.Create(&zone, sig); 1163 1164 // Create inner function g. g has lots of parameters so that they are passed 1165 // over the stack. 1166 Handle<Code> inner; 1167 Graph graph(&zone); 1168 RawMachineAssembler g(isolate, &graph, desc); 1169 1170 g.Store(slot_type.representation(), g.Parameter(11), g.Parameter(10), 1171 WriteBarrierKind::kNoWriteBarrier); 1172 g.Return(g.Parameter(9)); 1173 inner = CompileGraph("Compute", desc, &graph, g.Export()); 1174 1175 // Create function f with a stack slot which calls the inner function g. 1176 BufferedRawMachineAssemblerTester<T> f(slot_type); 1177 Node* target = f.HeapConstant(inner); 1178 Node* stack_slot = f.StackSlot(slot_type.representation()); 1179 Node* args[12]; 1180 for (int i = 0; i < 10; i++) { 1181 args[i] = f.Int32Constant(i); 1182 } 1183 args[10] = f.Parameter(0); 1184 args[11] = stack_slot; 1185 1186 f.CallN(desc, target, args); 1187 f.Return(f.Load(slot_type, stack_slot, f.IntPtrConstant(0))); 1188 1189 CHECK_EQ(expected, f.Call(expected)); 1190 } 1191 1192 TEST(RunStackSlotInt32) { 1193 int32_t magic = 0x12345678; 1194 TestStackSlot(MachineType::Int32(), magic); 1195 } 1196 1197 #if !V8_TARGET_ARCH_32_BIT 1198 TEST(RunStackSlotInt64) { 1199 int64_t magic = 0x123456789abcdef0; 1200 TestStackSlot(MachineType::Int64(), magic); 1201 } 1202 #endif 1203 1204 TEST(RunStackSlotFloat32) { 1205 float magic = 1234.125f; 1206 TestStackSlot(MachineType::Float32(), magic); 1207 } 1208 1209 TEST(RunStackSlotFloat64) { 1210 double magic = 3456.375; 1211 TestStackSlot(MachineType::Float64(), magic); 1212 } 1213 } // namespace compiler 1214 } // namespace internal 1215 } // namespace v8 1216