Chromium Code Reviews| 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 cl::opt<bool> | |
| 135 NoSplitModuleDynamic("no-split-module-dynamic", | |
|
Jim Stichnoth
2014/03/18 20:49:49
Instead of naming a variable with a negative meani
jvoung (off chromium)
2014/03/18 22:01:17
Changed to small enum -- is that what you had in m
Jim Stichnoth
2014/03/18 22:23:13
I was just thinking of naming it without using the
| |
| 136 cl::desc("Disable self-assigning work to threads."), | |
| 137 cl::init(false)); | |
| 138 | |
| 134 /// Compile the module provided to pnacl-llc. The file name for reading the | 139 /// 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 | 140 /// module and other options are taken from globals populated by command-line |
| 136 /// option parsing. | 141 /// option parsing. |
| 137 static int compileModule(StringRef ProgramName); | 142 static int compileModule(StringRef ProgramName); |
| 138 | 143 |
| 139 #if !defined(__native_client__) | 144 #if !defined(__native_client__) |
| 140 // GetFileNameRoot - Helper function to get the basename of a filename. | 145 // GetFileNameRoot - Helper function to get the basename of a filename. |
| 141 static std::string | 146 static std::string |
| 142 GetFileNameRoot(StringRef InputFilename) { | 147 GetFileNameRoot(StringRef InputFilename) { |
| 143 std::string IFN = InputFilename; | 148 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. | 333 // Err.print is prettier, so use it for the non-sandboxed translator. |
| 329 Err.print(ProgramName.data(), errs()); | 334 Err.print(ProgramName.data(), errs()); |
| 330 return NULL; | 335 return NULL; |
| 331 #endif | 336 #endif |
| 332 } | 337 } |
| 333 return M; | 338 return M; |
| 334 } | 339 } |
| 335 | 340 |
| 336 static int runCompilePasses(Module *mod, | 341 static int runCompilePasses(Module *mod, |
| 337 unsigned ModuleIndex, | 342 unsigned ModuleIndex, |
| 343 ThreadedFunctionQueue *FuncQueue, | |
| 338 const Triple &TheTriple, | 344 const Triple &TheTriple, |
| 339 TargetMachine &Target, | 345 TargetMachine &Target, |
| 340 StringRef ProgramName, | 346 StringRef ProgramName, |
| 341 formatted_raw_ostream &FOS){ | 347 formatted_raw_ostream &FOS){ |
| 342 // Add declarations for external functions required by PNaCl. The | 348 // Add declarations for external functions required by PNaCl. The |
| 343 // ResolvePNaClIntrinsics function pass running during streaming | 349 // ResolvePNaClIntrinsics function pass running during streaming |
| 344 // depends on these declarations being in the module. | 350 // depends on these declarations being in the module. |
| 345 OwningPtr<ModulePass> AddPNaClExternalDeclsPass( | 351 OwningPtr<ModulePass> AddPNaClExternalDeclsPass( |
| 346 createAddPNaClExternalDeclsPass()); | 352 createAddPNaClExternalDeclsPass()); |
| 347 AddPNaClExternalDeclsPass->runOnModule(*mod); | 353 AddPNaClExternalDeclsPass->runOnModule(*mod); |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 425 /* DisableVerify */ true)) { | 431 /* DisableVerify */ true)) { |
| 426 errs() << ProgramName | 432 errs() << ProgramName |
| 427 << ": target does not support generation of this file type!\n"; | 433 << ": target does not support generation of this file type!\n"; |
| 428 return 1; | 434 return 1; |
| 429 } | 435 } |
| 430 | 436 |
| 431 if (LazyBitcode) { | 437 if (LazyBitcode) { |
| 432 FunctionPassManager* P = static_cast<FunctionPassManager*>(PM.get()); | 438 FunctionPassManager* P = static_cast<FunctionPassManager*>(PM.get()); |
| 433 P->doInitialization(); | 439 P->doInitialization(); |
| 434 unsigned FuncIndex = 0; | 440 unsigned FuncIndex = 0; |
| 435 for (Module::iterator I = mod->begin(), E = mod->end(); I != E; ++I) { | 441 if (NoSplitModuleDynamic) { |
| 436 if (FuncIndex++ % SplitModuleCount == ModuleIndex) { | 442 for (Module::iterator I = mod->begin(), E = mod->end(); I != E; ++I) { |
| 437 P->run(*I); | 443 if (FuncQueue->GrabFunctionStatic(FuncIndex, ModuleIndex)) { |
| 438 CheckABIVerifyErrors(ABIErrorReporter, "Function " + I->getName()); | 444 P->run(*I); |
| 439 I->Dematerialize(); | 445 CheckABIVerifyErrors(ABIErrorReporter, "Function " + I->getName()); |
| 446 I->Dematerialize(); | |
| 447 } | |
| 448 ++FuncIndex; | |
| 449 } | |
| 450 } else { | |
| 451 unsigned ChunkSize = 0; | |
| 452 for (Module::iterator I = mod->begin(), E = mod->end(); I != E; ) { | |
| 453 ChunkSize = FuncQueue->RecommendedChunkSize(); | |
| 454 unsigned NextIndex; | |
| 455 bool grabbed = FuncQueue->GrabFunctionDynamic(FuncIndex, ChunkSize, | |
| 456 NextIndex); | |
| 457 if (grabbed) { | |
| 458 while(FuncIndex < NextIndex) { | |
|
Jim Stichnoth
2014/03/18 20:49:49
while( ==> while (
http://llvm.org/docs/CodingStan
jvoung (off chromium)
2014/03/18 22:01:17
Done.
| |
| 459 P->run(*I); | |
| 460 CheckABIVerifyErrors(ABIErrorReporter, "Function " + I->getName()); | |
| 461 I->Dematerialize(); | |
| 462 ++FuncIndex; | |
| 463 ++I; | |
| 464 if (I == E) { | |
|
Jim Stichnoth
2014/03/18 20:49:49
Maybe add "&& I != E" to the "while" condition ins
jvoung (off chromium)
2014/03/18 22:01:17
Done. Well at least I didn't use a goto =)
| |
| 465 break; | |
| 466 } | |
| 467 } | |
| 468 } else { | |
| 469 while(FuncIndex < NextIndex) { | |
|
Jim Stichnoth
2014/03/18 20:49:49
while( ==> while (
http://llvm.org/docs/CodingStan
jvoung (off chromium)
2014/03/18 22:01:17
Done.
| |
| 470 ++FuncIndex; | |
| 471 ++I; | |
| 472 if (I == E) { | |
|
Jim Stichnoth
2014/03/18 20:49:49
Maybe add "&& I != E" to the "while" condition ins
jvoung (off chromium)
2014/03/18 22:01:17
Done.
| |
| 473 break; | |
| 474 } | |
| 475 } | |
| 476 } | |
| 440 } | 477 } |
| 441 } | 478 } |
| 442 P->doFinalization(); | 479 P->doFinalization(); |
| 443 } else { | 480 } else { |
| 444 static_cast<PassManager*>(PM.get())->run(*mod); | 481 static_cast<PassManager*>(PM.get())->run(*mod); |
| 445 } | 482 } |
| 446 return 0; | 483 return 0; |
| 447 } | 484 } |
| 448 | 485 |
| 449 | 486 |
| 450 static int compileSplitModule(const TargetOptions &Options, | 487 static int compileSplitModule(const TargetOptions &Options, |
| 451 const Triple &TheTriple, | 488 const Triple &TheTriple, |
| 452 const Target *TheTarget, | 489 const Target *TheTarget, |
| 453 const std::string &FeaturesStr, | 490 const std::string &FeaturesStr, |
| 454 CodeGenOpt::Level OLvl, | 491 CodeGenOpt::Level OLvl, |
| 455 const StringRef &ProgramName, | 492 const StringRef &ProgramName, |
| 456 Module *GlobalModule, | 493 Module *GlobalModule, |
| 457 StreamingMemoryObject *StreamingObject, | 494 StreamingMemoryObject *StreamingObject, |
| 458 unsigned ModuleIndex) { | 495 unsigned ModuleIndex, |
| 496 ThreadedFunctionQueue *FuncQueue) { | |
| 459 std::auto_ptr<TargetMachine> | 497 std::auto_ptr<TargetMachine> |
| 460 target(TheTarget->createTargetMachine(TheTriple.getTriple(), | 498 target(TheTarget->createTargetMachine(TheTriple.getTriple(), |
| 461 MCPU, FeaturesStr, Options, | 499 MCPU, FeaturesStr, Options, |
| 462 RelocModel, CMModel, OLvl)); | 500 RelocModel, CMModel, OLvl)); |
| 463 assert(target.get() && "Could not allocate target machine!"); | 501 assert(target.get() && "Could not allocate target machine!"); |
| 464 TargetMachine &Target = *target.get(); | 502 TargetMachine &Target = *target.get(); |
| 465 // Override default to generate verbose assembly. | 503 // Override default to generate verbose assembly. |
| 466 Target.setAsmVerbosityDefault(true); | 504 Target.setAsmVerbosityDefault(true); |
| 467 if (RelaxAll) { | 505 if (RelaxAll) { |
| 468 if (FileType != TargetMachine::CGFT_ObjectFile) | 506 if (FileType != TargetMachine::CGFT_ObjectFile) |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 488 | 526 |
| 489 mod->setTargetTriple(Triple::normalize(UserDefinedTriple)); | 527 mod->setTargetTriple(Triple::normalize(UserDefinedTriple)); |
| 490 { | 528 { |
| 491 #if !defined(__native_client__) | 529 #if !defined(__native_client__) |
| 492 // Figure out where we are going to send the output. | 530 // Figure out where we are going to send the output. |
| 493 std::string N(OutputFilename); | 531 std::string N(OutputFilename); |
| 494 raw_string_ostream OutFileName(N); | 532 raw_string_ostream OutFileName(N); |
| 495 if (ModuleIndex > 0) | 533 if (ModuleIndex > 0) |
| 496 OutFileName << ".module" << ModuleIndex; | 534 OutFileName << ".module" << ModuleIndex; |
| 497 OwningPtr<tool_output_file> Out | 535 OwningPtr<tool_output_file> Out |
| 498 (GetOutputStream(TheTarget->getName(), TheTriple.getOS(), | 536 (GetOutputStream(TheTarget->getName(), TheTriple.getOS(), |
| 499 OutFileName.str())); | 537 OutFileName.str())); |
| 500 if (!Out) return 1; | 538 if (!Out) return 1; |
| 501 formatted_raw_ostream FOS(Out->os()); | 539 formatted_raw_ostream FOS(Out->os()); |
| 502 #else | 540 #else |
| 503 raw_fd_ostream ROS(getObjectFileFD(ModuleIndex), true); | 541 raw_fd_ostream ROS(getObjectFileFD(ModuleIndex), true); |
| 504 ROS.SetBufferSize(1 << 20); | 542 ROS.SetBufferSize(1 << 20); |
| 505 formatted_raw_ostream FOS(ROS); | 543 formatted_raw_ostream FOS(ROS); |
| 506 #endif | 544 #endif |
| 507 int ret = runCompilePasses(mod, ModuleIndex, TheTriple, Target, ProgramName, | 545 int ret = runCompilePasses(mod, ModuleIndex, FuncQueue, |
| 546 TheTriple, Target, ProgramName, | |
| 508 FOS); | 547 FOS); |
| 509 if (ret) | 548 if (ret) |
| 510 return ret; | 549 return ret; |
| 511 #if defined (__native_client__) | 550 #if defined (__native_client__) |
| 512 FOS.flush(); | 551 FOS.flush(); |
| 513 ROS.flush(); | 552 ROS.flush(); |
| 514 #else | 553 #else |
| 515 // Declare success. | 554 // Declare success. |
| 516 Out->keep(); | 555 Out->keep(); |
| 517 #endif // __native_client__ | 556 #endif // __native_client__ |
| 518 } | 557 } |
| 519 return 0; | 558 return 0; |
| 520 } | 559 } |
| 521 | 560 |
| 522 struct ThreadData { | 561 struct ThreadData { |
| 523 const TargetOptions *Options; | 562 const TargetOptions *Options; |
| 524 const Triple *TheTriple; | 563 const Triple *TheTriple; |
| 525 const Target *TheTarget; | 564 const Target *TheTarget; |
| 526 std::string FeaturesStr; | 565 std::string FeaturesStr; |
| 527 CodeGenOpt::Level OLvl; | 566 CodeGenOpt::Level OLvl; |
| 528 std::string ProgramName; | 567 std::string ProgramName; |
| 529 Module *GlobalModule; | 568 Module *GlobalModule; |
| 530 StreamingMemoryObject *StreamingObject; | 569 StreamingMemoryObject *StreamingObject; |
| 531 unsigned ModuleIndex; | 570 unsigned ModuleIndex; |
| 571 ThreadedFunctionQueue *FuncQueue; | |
| 532 }; | 572 }; |
| 533 | 573 |
| 534 | 574 |
| 535 static void *runCompileThread(void *arg) { | 575 static void *runCompileThread(void *arg) { |
| 536 struct ThreadData *Data = static_cast<ThreadData *>(arg); | 576 struct ThreadData *Data = static_cast<ThreadData *>(arg); |
| 537 int ret = compileSplitModule(*Data->Options, | 577 int ret = compileSplitModule(*Data->Options, |
| 538 *Data->TheTriple, | 578 *Data->TheTriple, |
| 539 Data->TheTarget, | 579 Data->TheTarget, |
| 540 Data->FeaturesStr, | 580 Data->FeaturesStr, |
| 541 Data->OLvl, | 581 Data->OLvl, |
| 542 Data->ProgramName, | 582 Data->ProgramName, |
| 543 Data->GlobalModule, | 583 Data->GlobalModule, |
| 544 Data->StreamingObject, | 584 Data->StreamingObject, |
| 545 Data->ModuleIndex); | 585 Data->ModuleIndex, |
| 586 Data->FuncQueue); | |
| 546 return reinterpret_cast<void *>(static_cast<intptr_t>(ret)); | 587 return reinterpret_cast<void *>(static_cast<intptr_t>(ret)); |
| 547 } | 588 } |
| 548 | 589 |
| 549 static int compileModule(StringRef ProgramName) { | 590 static int compileModule(StringRef ProgramName) { |
| 550 // Use a new context instead of the global context for the main module. It mus t | 591 // 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 | 592 // outlive the module object, declared below. We do this because |
| 552 // lib/CodeGen/PseudoSourceValue.cpp gets a type from the global context and | 593 // 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 | 594 // 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 | 595 // 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. | 596 // 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; | 686 return 1; |
| 646 case ' ': break; | 687 case ' ': break; |
| 647 case '0': OLvl = CodeGenOpt::None; break; | 688 case '0': OLvl = CodeGenOpt::None; break; |
| 648 case '1': OLvl = CodeGenOpt::Less; break; | 689 case '1': OLvl = CodeGenOpt::Less; break; |
| 649 case '2': OLvl = CodeGenOpt::Default; break; | 690 case '2': OLvl = CodeGenOpt::Default; break; |
| 650 case '3': OLvl = CodeGenOpt::Aggressive; break; | 691 case '3': OLvl = CodeGenOpt::Aggressive; break; |
| 651 } | 692 } |
| 652 | 693 |
| 653 SmallVector<pthread_t, 4> Pthreads(SplitModuleCount); | 694 SmallVector<pthread_t, 4> Pthreads(SplitModuleCount); |
| 654 SmallVector<ThreadData, 4> ThreadDatas(SplitModuleCount); | 695 SmallVector<ThreadData, 4> ThreadDatas(SplitModuleCount); |
| 696 ThreadedFunctionQueue FuncQueue(mod.get(), SplitModuleCount); | |
| 655 | 697 |
| 656 if (SplitModuleCount == 1) { | 698 if (SplitModuleCount == 1) { |
| 699 NoSplitModuleDynamic = true; // Do static assignment when single-threaded. | |
| 657 return compileSplitModule(Options, TheTriple, TheTarget, FeaturesStr, | 700 return compileSplitModule(Options, TheTriple, TheTarget, FeaturesStr, |
| 658 OLvl, ProgramName, mod.get(), NULL, 0); | 701 OLvl, ProgramName, mod.get(), NULL, 0, |
| 702 &FuncQueue); | |
| 659 } | 703 } |
| 660 | 704 |
| 661 for(unsigned ModuleIndex = 0; ModuleIndex < SplitModuleCount; ++ModuleIndex) { | 705 for(unsigned ModuleIndex = 0; ModuleIndex < SplitModuleCount; ++ModuleIndex) { |
| 662 ThreadDatas[ModuleIndex].Options = &Options; | 706 ThreadDatas[ModuleIndex].Options = &Options; |
| 663 ThreadDatas[ModuleIndex].TheTriple = &TheTriple; | 707 ThreadDatas[ModuleIndex].TheTriple = &TheTriple; |
| 664 ThreadDatas[ModuleIndex].TheTarget = TheTarget; | 708 ThreadDatas[ModuleIndex].TheTarget = TheTarget; |
| 665 ThreadDatas[ModuleIndex].FeaturesStr = FeaturesStr; | 709 ThreadDatas[ModuleIndex].FeaturesStr = FeaturesStr; |
| 666 ThreadDatas[ModuleIndex].OLvl = OLvl; | 710 ThreadDatas[ModuleIndex].OLvl = OLvl; |
| 667 ThreadDatas[ModuleIndex].ProgramName = ProgramName.str(); | 711 ThreadDatas[ModuleIndex].ProgramName = ProgramName.str(); |
| 668 ThreadDatas[ModuleIndex].GlobalModule = mod.get(); | 712 ThreadDatas[ModuleIndex].GlobalModule = mod.get(); |
| 669 ThreadDatas[ModuleIndex].StreamingObject = StreamingObject.get(); | 713 ThreadDatas[ModuleIndex].StreamingObject = StreamingObject.get(); |
| 670 ThreadDatas[ModuleIndex].ModuleIndex = ModuleIndex; | 714 ThreadDatas[ModuleIndex].ModuleIndex = ModuleIndex; |
| 715 ThreadDatas[ModuleIndex].FuncQueue = &FuncQueue; | |
| 671 if (pthread_create(&Pthreads[ModuleIndex], NULL, runCompileThread, | 716 if (pthread_create(&Pthreads[ModuleIndex], NULL, runCompileThread, |
| 672 &ThreadDatas[ModuleIndex])) { | 717 &ThreadDatas[ModuleIndex])) { |
| 673 report_fatal_error("Failed to create thread"); | 718 report_fatal_error("Failed to create thread"); |
| 674 } | 719 } |
| 675 } | 720 } |
| 676 for(unsigned ModuleIndex = 0; ModuleIndex < SplitModuleCount; ++ModuleIndex) { | 721 for(unsigned ModuleIndex = 0; ModuleIndex < SplitModuleCount; ++ModuleIndex) { |
| 677 void *retval; | 722 void *retval; |
| 678 if (pthread_join(Pthreads[ModuleIndex], &retval)) | 723 if (pthread_join(Pthreads[ModuleIndex], &retval)) |
| 679 report_fatal_error("Failed to join thread"); | 724 report_fatal_error("Failed to join thread"); |
| 680 intptr_t ret = reinterpret_cast<intptr_t>(retval); | 725 intptr_t ret = reinterpret_cast<intptr_t>(retval); |
| 681 if (ret != 0) | 726 if (ret != 0) |
| 682 report_fatal_error("Thread returned nonzero"); | 727 report_fatal_error("Thread returned nonzero"); |
| 683 } | 728 } |
| 684 return 0; | 729 return 0; |
| 685 } | 730 } |
| 686 | 731 |
| 687 int main(int argc, char **argv) { | 732 int main(int argc, char **argv) { |
| 688 #if defined(__native_client__) | 733 #if defined(__native_client__) |
| 689 return srpc_main(argc, argv); | 734 return srpc_main(argc, argv); |
| 690 #else | 735 #else |
| 691 return llc_main(argc, argv); | 736 return llc_main(argc, argv); |
| 692 #endif // __native_client__ | 737 #endif // __native_client__ |
| 693 } | 738 } |
| OLD | NEW |