LLVM学习笔记(57)

4.2. 代码入口(以下为7.0代码

LLVM有两个编译器。一个是静态编译器llc——它的输入是Clang从C、C++及ObjC源代码转换而来的LLVM IR,把IR编译为LLVM的字节码,或指定目标机器的汇编或机器码。另一个是动态编译器lli——它的输入是LLVM的字节码,并执行之。显然,指令选择与调度与llc的关系要紧密得多。文件llc.cpp就是llc的源代码所在。Llc的入口就是其中的main()函数。大致上这个main()函数是这样的:

276     int main(int argc, char **argv) {

277       InitLLVM X(argc, argv);

278    

279       // Enable debug stream buffering.

280       EnableDebugBuffering = true;

281    

282       LLVMContext Context;

283    

284      // Initialize targets first, so that --version shows registered targets.

285       InitializeAllTargets();

286       InitializeAllTargetMCs();

287       InitializeAllAsmPrinters();

288       InitializeAllAsmParsers();

289    

290       // Initialize codegen and IR passes used by llc so that the -print-after,

291       // -print-before, and -stop-after options work.

292       PassRegistry *Registry = PassRegistry::getPassRegistry();

293       initializeCore(*Registry);

294       initializeCodeGen(*Registry);

295       initializeLoopStrengthReducePass(*Registry);

296       initializeLowerIntrinsicsPass(*Registry);

297       initializeEntryExitInstrumenterPass(*Registry);

298       initializePostInlineEntryExitInstrumenterPass(*Registry);

299       initializeUnreachableBlockElimPass(*Registry);

300       initializeConstantHoistingLegacyPassPass(*Registry);

301       initializeScalarOpts(*Registry);

302       initializeVectorization(*Registry);

303       initializeScalarizeMaskedMemIntrinPass(*Registry);

304       initializeExpandReductionsPass(*Registry);

305    

306       // Initialize debugging passes.

307       initializeScavengerTestPass(*Registry);

308    

309       // Register the target printer for --version.

310       cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion);

311    

312       cl::ParseCommandLineOptions(argc, argv, "llvm system compiler\n")

313    

314       Context.setDiscardValueNames(DiscardValueNames);

315    

316       // Set a diagnostic handler that doesn't exit on the first error

317       bool HasError = false;

318       Context.setDiagnosticHandler(

319             llvm::make_unique(&HasError));

320       Context.setInlineAsmDiagnosticHandler(InlineAsmDiagHandler, &HasError);

321    

322       if (PassRemarksWithHotness)

323          Context.setDiagnosticsHotnessRequested(true);

324    

325       if (PassRemarksHotnessThreshold)

326          Context.setDiagnosticsHotnessThreshold(PassRemarksHotnessThreshold);

327    

328       std::unique_ptr YamlFile;

329       if (RemarksFilename != "") {

330          std::error_code EC;

331         YamlFile =

332                llvm::make_unique(RemarksFilename, EC, sys::fs::F_None);

333          if (EC) {

334             WithColor::error(errs(), argv[0]) << EC.message() << '\n';

335             return 1;

336          }

337          Context.setDiagnosticsOutputFile(

338                 llvm::make_unique(YamlFile->os()));

339       }

340    

341       if (InputLanguage != "" && InputLanguage != "ir" &&

342                InputLanguage != "mir") {

343          WithColor::error(errs(), argv[0])

344                  << "input language must be '', 'IR' or 'MIR'\n";

345          return 1;

346       }

347    

348       // Compile the module TimeCompilations times to give better compile time

349       // metrics.

350       for (unsigned I = TimeCompilations; I; --I)

351         if (int RetVal = compileModule(argv, Context))

352           return RetVal;

353    

354       if (YamlFile)

355          YamlFile->keep();

356          return 0;

357     }

4.3. 目标机器的初始化方法 

285~288行初始化所有与目标机器相关的模块。以X86目标机器为例,InitializeAllTargets()会依次调用LLVMInitializeX86TargetInfo()以及LLVMInitializeX86Targe()(这个函数实际上会调用所有LLVM支持的目标机器的这些方法):

14       Target &llvm::getTheX86_32Target() {                                                                                  ß v7.0增加

15         static Target TheX86_32Target;

16         return TheX86_32Target;

17       }

18       Target &llvm::getTheX86_64Target() {

19         static Target TheX86_64Target;

20         return TheX86_64Target;

21       }

22      

23       extern "C" void LLVMInitializeX86TargetInfo() {

24         RegisterTarget/*HasJIT=*/true>

25           X(getTheX86_32Target(), "x86", "32-bit X86: Pentium-Pro and above");

26      

27         RegisterTarget/*HasJIT=*/true>

28           Y(getTheX86_64Target(), "x86-64", "64-bit X86: EM64T and AMD64");

29       }

V7.0TheX86_32Target/TheX86_64Target声明为函数的静态成员,通过函数获取,避免了无意的修改。

RegisterTarget在构造函数里调用方法TargetRegistry::RegisterTarget()注册代表32位与64位X86目标机器的Target实例。这使得在后面可以通过17或20行的Triple值来查找这些Target对象。

75       extern "C" void LLVMInitializeX86Target() {

76         // Register the target.

77         RegisterTargetMachine X(TheX86_32Target);

78         RegisterTargetMachine Y(TheX86_64Target);

79      

80         PassRegistry &PR = *PassRegistry::getPassRegistry();

81         initializeGlobalISel(PR);

82         initializeWinEHStatePassPass(PR);

83         initializeFixupBWInstPassPass(PR);

84         initializeEvexToVexInstPassPass(PR);

85         initializeFixupLEAPassPass(PR);

86         initializeShadowCallStackPass(PR);

87         initializeX86CallFrameOptimizationPass(PR);

88         initializeX86CmovConverterPassPass(PR);

89         initializeX86ExecutionDomainFixPass(PR);

90         initializeX86DomainReassignmentPass(PR);

91         initializeX86AvoidSFBPassPass(PR);

92         initializeX86FlagsCopyLoweringPassPass(PR);

93       }

RegisterTargetMachine在构造函数里调用方法TargetRegistry::RegisterTargetMachine()将自己的Allocator()方法设置为TheX86_32Target及TheX86_64Target生成目标机器实现(X86TargetMachine)的构造方法指针TargetMachineCtorFn,为后面创建目标机器对象做好准备。

1102   template <class TargetMachineImpl> struct RegisterTargetMachine {

1103     RegisterTargetMachine(Target &T) {

1104       TargetRegistry::RegisterTargetMachine(T, &Allocator);

1105     }

1106  

1107   private:

1108     static TargetMachine *

1109     Allocator(const Target &T, const Triple &TT, StringRef CPU, StringRef FS,

1110                                     const TargetOptions &Options, Reloc::Model RM,

1111                                     CodeModel::Model CM, CodeGenOpt::Level OL, bool JIT) {

1112       return new TargetMachineImpl(T, TT, CPU, FS, Options, RM, CM, OL, JIT);

1113     }

1114   };

上面1104行的TargetRegistry::RegisterTargetMachine()是同一文件里的一个静态函数:

756       static void RegisterTargetMachine(Target &T, Target::TargetMachineCtorTy Fn) {

757         T.TargetMachineCtorFn = Fn;

758       }

V7.0LVMInitializeX86Target()里额外初始化x86所需的优化/处理遍。第一个就是GlobalISel相关的遍,我们看一下它要什么:

19       void llvm::initializeGlobalISel(PassRegistry &Registry) {

20         initializeIRTranslatorPass(Registry);

21         initializeLegalizerPass(Registry);

22         initializeLocalizerPass(Registry);

23         initializeRegBankSelectPass(Registry);

24         initializeInstructionSelectPass(Registry);

25       }

不出意外,GlobalISel需要这些遍:IRTranslatorLegalizerLocalizer(将常量形式指令移动/复制到靠近使用处。主要是为了绕过快速寄存器分配器的缺陷)、RegBankSelect,以及我们已经有点熟悉的InstructionSelect

剩下的遍有这些(WinEHStatePassWindows相关,我们不看):

  • FixupBWInstPass:查找使用32位指令替换64位指令的机会。这样做,一方面能消除64位寄存器不使用的高位带来的伪依赖;另一方面能减小代码大小。
  • EvexToVexInstPass:遍历使用使用EXE前缀编码的AVX-512指令,如果可能根据VEX编码替换它们,VEX编码能少2字节。
  • FixupLEAPass:查找能被重写为LEA指令的指令,以减小流水线时延。
  • ShadowCallStackPass:检查函数prologs/epilogs,确定在该函数执行期间,返回地址没有被毁坏。
  • X86CallFrameOptimizationPass:将函数参数到栈的mov,转换为push。因为两个主要原因这是有利的:
  1. Push指令的编码远小于基于栈指针的mov
  2. 有可能直接push内存实参。因此,如果这个转换在寄存器分配前执行,它有助于减轻寄存器压力。
  • X86CmovConverterPass:在有利时,将X86 cmov指令转换为分支。
  • X86ExecutionDomainFixPass:某些X86 SSE指令,如movandorxor对不同的操作数类型有不同的变形可用。这些变形是等价的,但在Nehalem及更新的CPU上,在整数域与浮点域之间传输数据有额外时延。这个遍尽量减少域跨越。
  • X86DomainReassignmentPass:尝试找出在一个域中的指令链(闭包),如果有利,将它们转换为不同域里等价的指令。
  • X86AvoidSFBPass:如果一个load跟着一个stroe后面,且重新载入这个store写入内存的数据,Intel微架构在许多情形里可以将这个数据从store直接转发给load。这个“store转发”通过使得 load可以直接获得数据,无需访问缓存或内存,节省了周期。

store转发阻塞”出现在store不能被转发到load的情形里。Store转发阻塞在Intel Core微架构上最常见,在这个架构上小的store不能转发给大的load。有个store转发阻塞的惩罚大约是13个周期。

在将memcpy调用降级为一系列loadstore时,这个遍尝试识别以及处理由编译器产生“store转发阻塞”的情形。

  • X86FlagsCopyLoweringPass:通过直接提前以及保留单独的标志位,降级EFLAGSCOPY节点。

InitializeAllTargetMCs()调用LLVMInitializeX86TargetMC()执行与目标机器MC相关方法的注册(记录在对应的Target对象里,比如下面的TheX86_32Target与TheX86_64Target)。

449     extern "C" void LLVMInitializeX86TargetMC() {

450       for (Target *T: {&getTheX86_32Target(), &getTheX86_64Target()}) {

451         // Register the MC asm info.

452         RegisterMCAsmInfoFn X(*T, createX86MCAsmInfo);

453    

454         // Register the MC instruction info.

455         TargetRegistry::RegisterMCInstrInfo(*T, createX86MCInstrInfo);

456    

457         // Register the MC register info.

458         TargetRegistry::RegisterMCRegInfo(*T, createX86MCRegisterInfo);

459    

460         // Register the MC subtarget info.

461         TargetRegistry::RegisterMCSubtargetInfo(*T,

462                                                 X86_MC::createX86MCSubtargetInfo);

463    

464         // Register the MC instruction analyzer.

465         TargetRegistry::RegisterMCInstrAnalysis(*T, createX86MCInstrAnalysis);

466    

467         // Register the code emitter.

468         TargetRegistry::RegisterMCCodeEmitter(*T, createX86MCCodeEmitter);

469    

470         // Register the obj target streamer.

471         TargetRegistry::RegisterObjectTargetStreamer(*T,

472                                                      createX86ObjectTargetStreamer);

473    

474         // Register the asm target streamer.

475         TargetRegistry::RegisterAsmTargetStreamer(*T, createX86AsmTargetStreamer);

476    

477         TargetRegistry::RegisterCOFFStreamer(*T, createX86WinCOFFStreamer);

478    

479         // Register the MCInstPrinter.

480        TargetRegistry::RegisterMCInstPrinter(*T, createX86MCInstPrinter);

481    

482         // Register the MC relocation info.

483         TargetRegistry::RegisterMCRelocationInfo(*T, createX86MCRelocationInfo);

484       }

485    

486       // Register the asm backend.

487       TargetRegistry::RegisterMCAsmBackend(TheX86_32Target,

488                                            createX86_32AsmBackend);

489       TargetRegistry::RegisterMCAsmBackend(TheX86_64Target,

490                                            createX86_64AsmBackend);

491     }

其中255行的createX86MCInstrInfo()调用InitX86MCInstrInfo(),258行的createX86MCRegisterInfo()会调用InitX86MCRegisterInfo(),462行的createX86MCSubtargetInfo()会调用InitX86MCSubtargetInfo()。这三个函数是在前面由TableGen根据.td文件生成。

InitializeAllAsmPrinters()类似地对X86目标机器调用下面的方法:

703     extern "C" void LLVMInitializeX86AsmPrinter() {

704       RegisterAsmPrinter X(TheX86_32Target);

705       RegisterAsmPrinter Y(TheX86_64Target);

706     }

InitializeAllAsmParser()调用的LLVMInitializeX86AsmPrinter()通过RegisterAsmPrinter的构造函数将X86AsmPrinter()注册为TheX86_32Target及TheX86_64Target的汇编输出器。

3449   extern "C" void LLVMInitializeX86AsmParser() {

3450     RegisterMCAsmParser X(TheX86_32Target);

3451     RegisterMCAsmParser Y(TheX86_64Target);

3452   }

方法LLVMInitializeX86AsmParser()将X86AsmParser()注册为TheX86_32Target及TheX86_64Target的汇编解析器。

Main()的293~307行注册与代码生成及IR相关的遍。我们稍后一点再来看。

你可能感兴趣的:(学习,笔记,llvm,编译)