OLD | NEW |
1 //===-- pnacl-llc.cpp - PNaCl-specific llc: pexe ---> nexe ---------------===// | 1 //===-- pnacl-llc.cpp - PNaCl-specific llc: pexe ---> nexe ---------------===// |
2 // | 2 // |
3 // The LLVM Compiler Infrastructure | 3 // The LLVM Compiler Infrastructure |
4 // | 4 // |
5 // This file is distributed under the University of Illinois Open Source | 5 // This file is distributed under the University of Illinois Open Source |
6 // License. See LICENSE.TXT for details. | 6 // License. See LICENSE.TXT for details. |
7 // | 7 // |
8 //===----------------------------------------------------------------------===// | 8 //===----------------------------------------------------------------------===// |
9 // | 9 // |
10 // pnacl-llc: the core of the PNaCl translator, compiling a pexe into a nexe. | 10 // pnacl-llc: the core of the PNaCl translator, compiling a pexe into a nexe. |
(...skipping 27 matching lines...) Expand all Loading... |
38 #include "llvm/Support/PrettyStackTrace.h" | 38 #include "llvm/Support/PrettyStackTrace.h" |
39 #include "llvm/Support/Signals.h" | 39 #include "llvm/Support/Signals.h" |
40 #include "llvm/Support/SourceMgr.h" | 40 #include "llvm/Support/SourceMgr.h" |
41 #include "llvm/Support/StreamableMemoryObject.h" | 41 #include "llvm/Support/StreamableMemoryObject.h" |
42 #include "llvm/Support/TargetRegistry.h" | 42 #include "llvm/Support/TargetRegistry.h" |
43 #include "llvm/Support/TargetSelect.h" | 43 #include "llvm/Support/TargetSelect.h" |
44 #include "llvm/Support/ToolOutputFile.h" | 44 #include "llvm/Support/ToolOutputFile.h" |
45 #include "llvm/Target/TargetLibraryInfo.h" | 45 #include "llvm/Target/TargetLibraryInfo.h" |
46 #include "llvm/Target/TargetMachine.h" | 46 #include "llvm/Target/TargetMachine.h" |
47 #include "llvm/Transforms/NaCl.h" | 47 #include "llvm/Transforms/NaCl.h" |
| 48 #include "ThreadedFunctionQueue.h" |
48 #include "ThreadedStreamingCache.h" | 49 #include "ThreadedStreamingCache.h" |
49 #include <pthread.h> | 50 #include <pthread.h> |
50 #include <memory> | 51 #include <memory> |
51 | 52 |
52 | |
53 using namespace llvm; | 53 using namespace llvm; |
54 | 54 |
55 // NOTE: When __native_client__ is defined it means pnacl-llc is built as a | 55 // NOTE: When __native_client__ is defined it means pnacl-llc is built as a |
56 // sandboxed translator (from pnacl-llc.pexe to pnacl-llc.nexe). In this mode | 56 // sandboxed translator (from pnacl-llc.pexe to pnacl-llc.nexe). In this mode |
57 // it uses SRPC operations instead of direct OS intefaces. | 57 // it uses SRPC operations instead of direct OS intefaces. |
58 #if defined(__native_client__) | 58 #if defined(__native_client__) |
59 int srpc_main(int argc, char **argv); | 59 int srpc_main(int argc, char **argv); |
60 int getObjectFileFD(unsigned index); | 60 int getObjectFileFD(unsigned index); |
61 DataStreamer *getNaClBitcodeStreamer(); | 61 DataStreamer *getNaClBitcodeStreamer(); |
62 | 62 |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
124 | 124 |
125 cl::opt<bool> | 125 cl::opt<bool> |
126 DisableSimplifyLibCalls("disable-simplify-libcalls", | 126 DisableSimplifyLibCalls("disable-simplify-libcalls", |
127 cl::desc("Disable simplify-libcalls"), | 127 cl::desc("Disable simplify-libcalls"), |
128 cl::init(false)); | 128 cl::init(false)); |
129 | 129 |
130 cl::opt<unsigned> | 130 cl::opt<unsigned> |
131 SplitModuleCount("split-module", | 131 SplitModuleCount("split-module", |
132 cl::desc("Split PNaCl module"), cl::init(1U)); | 132 cl::desc("Split PNaCl module"), cl::init(1U)); |
133 | 133 |
| 134 enum SplitModuleSchedulerKind { |
| 135 SplitModuleDynamic, |
| 136 SplitModuleStatic |
| 137 }; |
| 138 |
| 139 cl::opt<SplitModuleSchedulerKind> |
| 140 SplitModuleSched( |
| 141 "split-module-sched", |
| 142 cl::desc("Choose thread scheduler for split module compilation."), |
| 143 cl::values( |
| 144 clEnumValN(SplitModuleDynamic, "dynamic", |
| 145 "Dynamic thread scheduling (default)"), |
| 146 clEnumValN(SplitModuleStatic, "static", |
| 147 "Static thread scheduling"), |
| 148 clEnumValEnd), |
| 149 cl::init(SplitModuleDynamic)); |
| 150 |
134 /// Compile the module provided to pnacl-llc. The file name for reading the | 151 /// Compile the module provided to pnacl-llc. The file name for reading the |
135 /// module and other options are taken from globals populated by command-line | 152 /// module and other options are taken from globals populated by command-line |
136 /// option parsing. | 153 /// option parsing. |
137 static int compileModule(StringRef ProgramName); | 154 static int compileModule(StringRef ProgramName); |
138 | 155 |
139 #if !defined(__native_client__) | 156 #if !defined(__native_client__) |
140 // GetFileNameRoot - Helper function to get the basename of a filename. | 157 // GetFileNameRoot - Helper function to get the basename of a filename. |
141 static std::string | 158 static std::string |
142 GetFileNameRoot(StringRef InputFilename) { | 159 GetFileNameRoot(StringRef InputFilename) { |
143 std::string IFN = InputFilename; | 160 std::string IFN = InputFilename; |
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
328 // Err.print is prettier, so use it for the non-sandboxed translator. | 345 // Err.print is prettier, so use it for the non-sandboxed translator. |
329 Err.print(ProgramName.data(), errs()); | 346 Err.print(ProgramName.data(), errs()); |
330 return NULL; | 347 return NULL; |
331 #endif | 348 #endif |
332 } | 349 } |
333 return M; | 350 return M; |
334 } | 351 } |
335 | 352 |
336 static int runCompilePasses(Module *mod, | 353 static int runCompilePasses(Module *mod, |
337 unsigned ModuleIndex, | 354 unsigned ModuleIndex, |
| 355 ThreadedFunctionQueue *FuncQueue, |
338 const Triple &TheTriple, | 356 const Triple &TheTriple, |
339 TargetMachine &Target, | 357 TargetMachine &Target, |
340 StringRef ProgramName, | 358 StringRef ProgramName, |
341 formatted_raw_ostream &FOS){ | 359 formatted_raw_ostream &FOS){ |
342 // Add declarations for external functions required by PNaCl. The | 360 // Add declarations for external functions required by PNaCl. The |
343 // ResolvePNaClIntrinsics function pass running during streaming | 361 // ResolvePNaClIntrinsics function pass running during streaming |
344 // depends on these declarations being in the module. | 362 // depends on these declarations being in the module. |
345 OwningPtr<ModulePass> AddPNaClExternalDeclsPass( | 363 OwningPtr<ModulePass> AddPNaClExternalDeclsPass( |
346 createAddPNaClExternalDeclsPass()); | 364 createAddPNaClExternalDeclsPass()); |
347 AddPNaClExternalDeclsPass->runOnModule(*mod); | 365 AddPNaClExternalDeclsPass->runOnModule(*mod); |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
424 if (Target.addPassesToEmitFile(*PM, FOS, FileType, | 442 if (Target.addPassesToEmitFile(*PM, FOS, FileType, |
425 /* DisableVerify */ true)) { | 443 /* DisableVerify */ true)) { |
426 errs() << ProgramName | 444 errs() << ProgramName |
427 << ": target does not support generation of this file type!\n"; | 445 << ": target does not support generation of this file type!\n"; |
428 return 1; | 446 return 1; |
429 } | 447 } |
430 | 448 |
431 if (LazyBitcode) { | 449 if (LazyBitcode) { |
432 FunctionPassManager* P = static_cast<FunctionPassManager*>(PM.get()); | 450 FunctionPassManager* P = static_cast<FunctionPassManager*>(PM.get()); |
433 P->doInitialization(); | 451 P->doInitialization(); |
434 unsigned FuncIndex = 0; | 452 int FuncIndex = 0; |
435 for (Module::iterator I = mod->begin(), E = mod->end(); I != E; ++I) { | 453 switch (SplitModuleSched) { |
436 if (FuncIndex++ % SplitModuleCount == ModuleIndex) { | 454 case SplitModuleStatic: |
437 P->run(*I); | 455 for (Module::iterator I = mod->begin(), E = mod->end(); I != E; ++I) { |
438 CheckABIVerifyErrors(ABIErrorReporter, "Function " + I->getName()); | 456 if (FuncQueue->GrabFunctionStatic(FuncIndex, ModuleIndex)) { |
439 I->Dematerialize(); | 457 P->run(*I); |
| 458 CheckABIVerifyErrors(ABIErrorReporter, "Function " + I->getName()); |
| 459 I->Dematerialize(); |
| 460 } |
| 461 ++FuncIndex; |
440 } | 462 } |
| 463 break; |
| 464 case SplitModuleDynamic: |
| 465 unsigned ChunkSize = 0; |
| 466 for (Module::iterator I = mod->begin(), E = mod->end(); I != E; ) { |
| 467 ChunkSize = FuncQueue->RecommendedChunkSize(); |
| 468 int NextIndex; |
| 469 bool grabbed = FuncQueue->GrabFunctionDynamic(FuncIndex, ChunkSize, |
| 470 NextIndex); |
| 471 if (grabbed) { |
| 472 while (FuncIndex < NextIndex && I != E) { |
| 473 P->run(*I); |
| 474 CheckABIVerifyErrors(ABIErrorReporter, "Function " + I->getName()); |
| 475 I->Dematerialize(); |
| 476 ++FuncIndex; |
| 477 ++I; |
| 478 } |
| 479 } else { |
| 480 // Currently the ResolvePNaClIntrinsics function pass may |
| 481 // add more declarations as we iterate. Some threads may get |
| 482 // "lucky" and not add the related declarations, so those |
| 483 // threads would have an earlier endpoint than other |
| 484 // threads. the final NextIndex established by another |
| 485 // thread would then be out of the bounds of the current thread. |
| 486 // TODO(jvoung): Ensure that all declarations are added up front |
| 487 // and uniformly so that we don't need this I != E check. |
| 488 while (FuncIndex < NextIndex && I != E) { |
| 489 ++FuncIndex; |
| 490 ++I; |
| 491 } |
| 492 } |
| 493 } |
| 494 break; |
441 } | 495 } |
442 P->doFinalization(); | 496 P->doFinalization(); |
443 } else { | 497 } else { |
444 static_cast<PassManager*>(PM.get())->run(*mod); | 498 static_cast<PassManager*>(PM.get())->run(*mod); |
445 } | 499 } |
446 return 0; | 500 return 0; |
447 } | 501 } |
448 | 502 |
449 | 503 |
450 static int compileSplitModule(const TargetOptions &Options, | 504 static int compileSplitModule(const TargetOptions &Options, |
451 const Triple &TheTriple, | 505 const Triple &TheTriple, |
452 const Target *TheTarget, | 506 const Target *TheTarget, |
453 const std::string &FeaturesStr, | 507 const std::string &FeaturesStr, |
454 CodeGenOpt::Level OLvl, | 508 CodeGenOpt::Level OLvl, |
455 const StringRef &ProgramName, | 509 const StringRef &ProgramName, |
456 Module *GlobalModule, | 510 Module *GlobalModule, |
457 StreamingMemoryObject *StreamingObject, | 511 StreamingMemoryObject *StreamingObject, |
458 unsigned ModuleIndex) { | 512 unsigned ModuleIndex, |
| 513 ThreadedFunctionQueue *FuncQueue) { |
459 std::auto_ptr<TargetMachine> | 514 std::auto_ptr<TargetMachine> |
460 target(TheTarget->createTargetMachine(TheTriple.getTriple(), | 515 target(TheTarget->createTargetMachine(TheTriple.getTriple(), |
461 MCPU, FeaturesStr, Options, | 516 MCPU, FeaturesStr, Options, |
462 RelocModel, CMModel, OLvl)); | 517 RelocModel, CMModel, OLvl)); |
463 assert(target.get() && "Could not allocate target machine!"); | 518 assert(target.get() && "Could not allocate target machine!"); |
464 TargetMachine &Target = *target.get(); | 519 TargetMachine &Target = *target.get(); |
465 // Override default to generate verbose assembly. | 520 // Override default to generate verbose assembly. |
466 Target.setAsmVerbosityDefault(true); | 521 Target.setAsmVerbosityDefault(true); |
467 if (RelaxAll) { | 522 if (RelaxAll) { |
468 if (FileType != TargetMachine::CGFT_ObjectFile) | 523 if (FileType != TargetMachine::CGFT_ObjectFile) |
(...skipping 19 matching lines...) Expand all Loading... |
488 | 543 |
489 mod->setTargetTriple(Triple::normalize(UserDefinedTriple)); | 544 mod->setTargetTriple(Triple::normalize(UserDefinedTriple)); |
490 { | 545 { |
491 #if !defined(__native_client__) | 546 #if !defined(__native_client__) |
492 // Figure out where we are going to send the output. | 547 // Figure out where we are going to send the output. |
493 std::string N(OutputFilename); | 548 std::string N(OutputFilename); |
494 raw_string_ostream OutFileName(N); | 549 raw_string_ostream OutFileName(N); |
495 if (ModuleIndex > 0) | 550 if (ModuleIndex > 0) |
496 OutFileName << ".module" << ModuleIndex; | 551 OutFileName << ".module" << ModuleIndex; |
497 OwningPtr<tool_output_file> Out | 552 OwningPtr<tool_output_file> Out |
498 (GetOutputStream(TheTarget->getName(), TheTriple.getOS(), | 553 (GetOutputStream(TheTarget->getName(), TheTriple.getOS(), |
499 OutFileName.str())); | 554 OutFileName.str())); |
500 if (!Out) return 1; | 555 if (!Out) return 1; |
501 formatted_raw_ostream FOS(Out->os()); | 556 formatted_raw_ostream FOS(Out->os()); |
502 #else | 557 #else |
503 raw_fd_ostream ROS(getObjectFileFD(ModuleIndex), true); | 558 raw_fd_ostream ROS(getObjectFileFD(ModuleIndex), true); |
504 ROS.SetBufferSize(1 << 20); | 559 ROS.SetBufferSize(1 << 20); |
505 formatted_raw_ostream FOS(ROS); | 560 formatted_raw_ostream FOS(ROS); |
506 #endif | 561 #endif |
507 int ret = runCompilePasses(mod, ModuleIndex, TheTriple, Target, ProgramName, | 562 int ret = runCompilePasses(mod, ModuleIndex, FuncQueue, |
| 563 TheTriple, Target, ProgramName, |
508 FOS); | 564 FOS); |
509 if (ret) | 565 if (ret) |
510 return ret; | 566 return ret; |
511 #if defined (__native_client__) | 567 #if defined (__native_client__) |
512 FOS.flush(); | 568 FOS.flush(); |
513 ROS.flush(); | 569 ROS.flush(); |
514 #else | 570 #else |
515 // Declare success. | 571 // Declare success. |
516 Out->keep(); | 572 Out->keep(); |
517 #endif // __native_client__ | 573 #endif // __native_client__ |
518 } | 574 } |
519 return 0; | 575 return 0; |
520 } | 576 } |
521 | 577 |
522 struct ThreadData { | 578 struct ThreadData { |
523 const TargetOptions *Options; | 579 const TargetOptions *Options; |
524 const Triple *TheTriple; | 580 const Triple *TheTriple; |
525 const Target *TheTarget; | 581 const Target *TheTarget; |
526 std::string FeaturesStr; | 582 std::string FeaturesStr; |
527 CodeGenOpt::Level OLvl; | 583 CodeGenOpt::Level OLvl; |
528 std::string ProgramName; | 584 std::string ProgramName; |
529 Module *GlobalModule; | 585 Module *GlobalModule; |
530 StreamingMemoryObject *StreamingObject; | 586 StreamingMemoryObject *StreamingObject; |
531 unsigned ModuleIndex; | 587 unsigned ModuleIndex; |
| 588 ThreadedFunctionQueue *FuncQueue; |
532 }; | 589 }; |
533 | 590 |
534 | 591 |
535 static void *runCompileThread(void *arg) { | 592 static void *runCompileThread(void *arg) { |
536 struct ThreadData *Data = static_cast<ThreadData *>(arg); | 593 struct ThreadData *Data = static_cast<ThreadData *>(arg); |
537 int ret = compileSplitModule(*Data->Options, | 594 int ret = compileSplitModule(*Data->Options, |
538 *Data->TheTriple, | 595 *Data->TheTriple, |
539 Data->TheTarget, | 596 Data->TheTarget, |
540 Data->FeaturesStr, | 597 Data->FeaturesStr, |
541 Data->OLvl, | 598 Data->OLvl, |
542 Data->ProgramName, | 599 Data->ProgramName, |
543 Data->GlobalModule, | 600 Data->GlobalModule, |
544 Data->StreamingObject, | 601 Data->StreamingObject, |
545 Data->ModuleIndex); | 602 Data->ModuleIndex, |
| 603 Data->FuncQueue); |
546 return reinterpret_cast<void *>(static_cast<intptr_t>(ret)); | 604 return reinterpret_cast<void *>(static_cast<intptr_t>(ret)); |
547 } | 605 } |
548 | 606 |
549 static int compileModule(StringRef ProgramName) { | 607 static int compileModule(StringRef ProgramName) { |
550 // Use a new context instead of the global context for the main module. It mus
t | 608 // Use a new context instead of the global context for the main module. It mus
t |
551 // outlive the module object, declared below. We do this because | 609 // outlive the module object, declared below. We do this because |
552 // lib/CodeGen/PseudoSourceValue.cpp gets a type from the global context and | 610 // lib/CodeGen/PseudoSourceValue.cpp gets a type from the global context and |
553 // races with any other use of the context. Rather than doing an invasive | 611 // races with any other use of the context. Rather than doing an invasive |
554 // plumbing change to fix it, we work around it by using a new context here | 612 // plumbing change to fix it, we work around it by using a new context here |
555 // and leaving PseudoSourceValue as the only user of the global context. | 613 // and leaving PseudoSourceValue as the only user of the global context. |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
645 return 1; | 703 return 1; |
646 case ' ': break; | 704 case ' ': break; |
647 case '0': OLvl = CodeGenOpt::None; break; | 705 case '0': OLvl = CodeGenOpt::None; break; |
648 case '1': OLvl = CodeGenOpt::Less; break; | 706 case '1': OLvl = CodeGenOpt::Less; break; |
649 case '2': OLvl = CodeGenOpt::Default; break; | 707 case '2': OLvl = CodeGenOpt::Default; break; |
650 case '3': OLvl = CodeGenOpt::Aggressive; break; | 708 case '3': OLvl = CodeGenOpt::Aggressive; break; |
651 } | 709 } |
652 | 710 |
653 SmallVector<pthread_t, 4> Pthreads(SplitModuleCount); | 711 SmallVector<pthread_t, 4> Pthreads(SplitModuleCount); |
654 SmallVector<ThreadData, 4> ThreadDatas(SplitModuleCount); | 712 SmallVector<ThreadData, 4> ThreadDatas(SplitModuleCount); |
| 713 ThreadedFunctionQueue FuncQueue(mod.get(), SplitModuleCount); |
655 | 714 |
656 if (SplitModuleCount == 1) { | 715 if (SplitModuleCount == 1) { |
| 716 // No need for dynamic scheduling with one thread. |
| 717 SplitModuleSched = SplitModuleStatic; |
657 return compileSplitModule(Options, TheTriple, TheTarget, FeaturesStr, | 718 return compileSplitModule(Options, TheTriple, TheTarget, FeaturesStr, |
658 OLvl, ProgramName, mod.get(), NULL, 0); | 719 OLvl, ProgramName, mod.get(), NULL, 0, |
| 720 &FuncQueue); |
659 } | 721 } |
660 | 722 |
661 for(unsigned ModuleIndex = 0; ModuleIndex < SplitModuleCount; ++ModuleIndex) { | 723 for(unsigned ModuleIndex = 0; ModuleIndex < SplitModuleCount; ++ModuleIndex) { |
662 ThreadDatas[ModuleIndex].Options = &Options; | 724 ThreadDatas[ModuleIndex].Options = &Options; |
663 ThreadDatas[ModuleIndex].TheTriple = &TheTriple; | 725 ThreadDatas[ModuleIndex].TheTriple = &TheTriple; |
664 ThreadDatas[ModuleIndex].TheTarget = TheTarget; | 726 ThreadDatas[ModuleIndex].TheTarget = TheTarget; |
665 ThreadDatas[ModuleIndex].FeaturesStr = FeaturesStr; | 727 ThreadDatas[ModuleIndex].FeaturesStr = FeaturesStr; |
666 ThreadDatas[ModuleIndex].OLvl = OLvl; | 728 ThreadDatas[ModuleIndex].OLvl = OLvl; |
667 ThreadDatas[ModuleIndex].ProgramName = ProgramName.str(); | 729 ThreadDatas[ModuleIndex].ProgramName = ProgramName.str(); |
668 ThreadDatas[ModuleIndex].GlobalModule = mod.get(); | 730 ThreadDatas[ModuleIndex].GlobalModule = mod.get(); |
669 ThreadDatas[ModuleIndex].StreamingObject = StreamingObject.get(); | 731 ThreadDatas[ModuleIndex].StreamingObject = StreamingObject.get(); |
670 ThreadDatas[ModuleIndex].ModuleIndex = ModuleIndex; | 732 ThreadDatas[ModuleIndex].ModuleIndex = ModuleIndex; |
| 733 ThreadDatas[ModuleIndex].FuncQueue = &FuncQueue; |
671 if (pthread_create(&Pthreads[ModuleIndex], NULL, runCompileThread, | 734 if (pthread_create(&Pthreads[ModuleIndex], NULL, runCompileThread, |
672 &ThreadDatas[ModuleIndex])) { | 735 &ThreadDatas[ModuleIndex])) { |
673 report_fatal_error("Failed to create thread"); | 736 report_fatal_error("Failed to create thread"); |
674 } | 737 } |
675 } | 738 } |
676 for(unsigned ModuleIndex = 0; ModuleIndex < SplitModuleCount; ++ModuleIndex) { | 739 for(unsigned ModuleIndex = 0; ModuleIndex < SplitModuleCount; ++ModuleIndex) { |
677 void *retval; | 740 void *retval; |
678 if (pthread_join(Pthreads[ModuleIndex], &retval)) | 741 if (pthread_join(Pthreads[ModuleIndex], &retval)) |
679 report_fatal_error("Failed to join thread"); | 742 report_fatal_error("Failed to join thread"); |
680 intptr_t ret = reinterpret_cast<intptr_t>(retval); | 743 intptr_t ret = reinterpret_cast<intptr_t>(retval); |
681 if (ret != 0) | 744 if (ret != 0) |
682 report_fatal_error("Thread returned nonzero"); | 745 report_fatal_error("Thread returned nonzero"); |
683 } | 746 } |
684 return 0; | 747 return 0; |
685 } | 748 } |
686 | 749 |
687 int main(int argc, char **argv) { | 750 int main(int argc, char **argv) { |
688 #if defined(__native_client__) | 751 #if defined(__native_client__) |
689 return srpc_main(argc, argv); | 752 return srpc_main(argc, argv); |
690 #else | 753 #else |
691 return llc_main(argc, argv); | 754 return llc_main(argc, argv); |
692 #endif // __native_client__ | 755 #endif // __native_client__ |
693 } | 756 } |
OLD | NEW |