- 博客(851)
- 资源 (22)
- 收藏
- 关注
翻译 优化C++软件——目录
1. 介绍 1.1. 优化的代价2. 选择最优的平台 2.1. 硬件平台的选择 2.2. 微处理器的选择 2.3. 操作系统的选择 2.4. 程序语言的选择 2.5. 编译器的选择 2.6. 函数库的选择 2.7. 用户接口框架的选择 2.8. 克服C++语言的缺点3. 找出最大的时间消耗者 3.1. 一个时钟周期是多少? 3...
2019-11-15 11:40:07 406
翻译 汇编优化例程的目录(已完结)
1. 介绍1.1. 使用汇编代码的理由1.2. 不使用汇编代码的理由1.3. 本手册覆盖的操作系统2. 在你开始之前2.1. 在你开始编程之前所要做的决定2.2. 制定测试策略2.3. 常见的编程陷阱3. 汇编编程基础3.1. 可用的汇编器3.2. 寄存器集与基本指令3.3. 取址模式3.4. 指令代码格式...
2019-04-26 11:53:55 791
翻译 Intel, AMD及VIA CPU的微架构目录(已完结)
1. 介绍 1.1. 关于本手册 1.2. 本手册涵盖的微处理器版本2. 乱序执行(除了P1、PMMX的所有处理器) 2.1. 指令被分解为微操作 2.2. 寄存器重命名3. 分支预测(所有处理器) 3.1. 条件跳转的预测方法 饱和计数器 带有局部历史表的两层自适应预测器 带有全...
2017-08-18 12:35:59 1330
原创 64-ia-32架构优化手册——目录
第一章. 略第二章. Intel(r) 64与IA-32处理器架构 2.1. SKYLATE微架构 2.1.1. 前端 2.1.2. 乱序执行引擎 2.1.3. 缓存与内存子系统 2.2. Haswell微架构 2.2.1. 前端 2.2.2. 乱序引...
2017-03-10 11:18:59 1613
原创 LLVM学习笔记——目录(含7.0更新)
前言2011年前后,GCC后端代码的阅读陷入了举步维艰的境地。GGC-3.4.6后端代码的可读性不友好(当前版本没看过,不予评价。不过据说4.0进行的重构,应该会好些),充斥着动辄数千行的函数,包含着庞大的switch块以及if块,加之函数间的相互调用,看得人昏头转向。穷极思变,转而看了一下LLVM的源代码。一撇之下,惊为天人。LLVM是第一次碰到的、几乎完全用C++开发的大规模开源项...
2017-03-10 11:14:55 6113 3
原创 GCC's back-end & assemble emission - Current content
>>Directory of blogs about the front end is here 1. Overview2. Tool of genconditions2.1. Preparation for Code Emission2.1.1. Read
2011-10-15 08:51:22 1008
原创 GCC后端及汇编发布 当前目录
>> 关于前端部分的文章在此 1. 概览2. genconditions工具2.1. 代码输出的准备2.1.1. 读入rtx形式的定义2.1.2. DEFINE_INSN模式的概览2.2. 输出指令定义模式的条件部分3. genrecog工具3
2011-07-11 07:17:52 2123 8
原创 GCC-3.4.6源代码学习笔记 当前目录
>> 后端部分目录由此链接进入参考文献准备工作GCC的架构前端1.概览1.1. 前端中树的表达形式1.1.1. 树节点的定义1.1.2. tree_code —— 树节点的ID1.1.3. 树节点的内存分配1.1.3.1. 节点大小的确定1.1.3.2. 内存分配1.2. 树节点的构造1.2.1. 机器模式的概念[2]1.2.2. 内建类型的树节点1.2.2.1. 初始化临时的size_
2010-03-05 09:47:00 3373 4
原创 LLVM学习笔记(65)
X86Subtarget成员FrameLowering的类型是X86FrameLowering,它派生自TargetFrameLowering。比如栈的生长方向,每个函数入口已知的栈对齐边界,以及局部变量区相对函数入口处栈顶的偏移。X86FrameLowering构造函数的参数StackAlignOverride是initSubtargetFeatures()里设定的,如果没有特别要求,这个值是16。的实现还没有完成,这里的合法化的架构还在进一步完善中,下面我们会看到。构造函数里,开始目标机器相关的设置。
2023-12-20 15:58:12 721
翻译 优化C++软件(14)
在循环中的计算,其中每次迭代需要前面的结果,被称为循环携带依赖链。如果可以打破这样的依赖链,增益很大。当前CPU仅有一个浮点加法单元,但这个单元是流水线化的,如上所述,因此它可以在前面的加法完成周期,开始一个新的加法。非常长的依赖链对CPU的乱序资源施加压力,即使它们不带入循环的下一个迭代。具有乱序能力的微处理器可以重叠迭代,在前面迭代完成之前,开始一个迭代的计算。如果微处理器从时刻T到T+5进行对sum1的加法,它可以从时刻T+1到T+6进行另一个对sum2的加法,整个循环将仅需256时钟周期。
2023-12-13 17:46:37 218
原创 MLIR笔记(6)
69行的getGlobalDialectRegistry()返回一个类型为llvm::ManagedStatic<DialectRegistry>的对象dialectRegistry,它过可视为DialectRegistry的静态对象,这个类通过一个类型为std::map<std::string, std::pair<TypeID, DialectAllocatorFunction>>的容器registry来记录标准方言。方言对象的构造发生在载入时,在载入时刻,不仅构造方言对象,相关的接口也会一并准备好。
2023-12-11 17:26:03 477 1
原创 LLVM学习笔记(64)
因此,如果要求的类型不是由2的幂个元素组成,就需要使用这么多个元素大小的类型来替换(868行条件)。因此对扩展无望的类型,就会谋求分裂类型,即通过多个更小的向量类型来支持。对于TypeSplitVector操作,源向量类型必须是2幂次大小,getVectorTypeBreakdownMVT()获取可能的分裂类型,它的返回值是所需寄存器的个数。在1216行,getPow2VectorType()返回大小为2次幂且最接近VT的上级类型,如果VT本身不是2次幂大小,就要提升到这个类型。
2023-12-07 13:39:50 248
翻译 优化C++软件(13)
例如,多个线程可以共享相同的队列、链表、数据库或其他数据结构,你可以考虑向每个线程给出它自己的数据结构,然后在所有线程完成耗时的数据处理时,最后合并多个数据结构,是否可能的。当前CPU仅有一个浮点加法单元,但这个单元是流水线化的,如上所述,因此它可以在前面的加法完成周期,开始一个新的加法。线程的使用在第47页讨论。坏处是,如果线程使用不同的内存区,缓存将被填满,而如果线程写到相同内存区,则有缓存竞争。将文件访问与网络访问放入独立的线程,在一个线程等待硬盘或网络响应时,另一个线程可以进行计算,也是有用的。
2023-12-04 17:37:43 63
原创 MLIR笔记(5)
所有在区域里定义作为操作结果的值,作用域在区域内,可以被区域里的任意其他操作访问。在图区域中,一个基本块内存在的次序以及一个区域内基本块的次序是没有语义意义的,例如非终结符操作可以通过规范化任意重排。对没有控制流的并发语义,以及通用有向图数据结构的建模,图区域是合适的。例如,一个图区域里的操作可能表示独立线程的控制流,具有表示数据流的值。上面1230行的region是MLIR引入的关键字,而$regions里的regions将是由mlir-tblgen生成的访问函数的名字。这些参数的源由父操作的语义定义。
2023-12-01 10:31:28 362
原创 LLVM学习笔记(63)
它设置循环对齐大小。另外,如果目标机器支持乱序执行,把PredictableSelectIsExpensive设为true,因为乱序CPU可以试探执行一个预测分支后的代码,但条件移动(所谓的select)会被前面的操作阻止。接下来则是通过setTargetDAGCombine()设置通过TargetDAGCombineArray容器记录的期望回调PerformDAGCombine()的LLVM IR操作(这与X86TargetLowering::PerformDAGCombine()对应)。
2023-11-30 17:40:15 275
翻译 优化C++软件(12)
行28的前8个元素共享同一个缓存行,但这8个元素将进入列28的8个不同的缓存行,因为缓存行遵循行,但不遵循列。如表9.3所示,仅在预期一个2级缓存不命中时,不使用缓存保存数据的方法是有优势的。表9.1中我的实验结果显示,当在2级缓存中出现竞争时,比不出现竞争,需要6倍的时间转置一个矩阵。这个函数按列模式写矩阵a,其中关键步长将导致所有的写在1级与2级缓存中载入新的缓存行。这个代码的问题是,如果对角线下元素matric[r][c]是按行访问的,那么对角线上的镜像元素matric[c][r]就是按列访问的。
2023-11-29 14:13:41 69
原创 MLIR笔记(4)
操作也可能将控制流传递入在其他操作里指定的区域,特别是那些像在一个call操作里使用的给定操作定义的值或符号。所有在区域里定义作为操作结果的值,作用域在区域内,可以被区域里的任意其他操作访问。在图区域中,一个基本块内存在的次序以及一个区域内基本块的次序是没有语义意义的,例如非终结符操作可以通过规范化任意重排。在操作执行后,执行基本块里的下一个操作,直到基本块末尾的终结符操作,在这个情形下将执行其他某个操作。注意,如果操作触发了区域的异步执行,操作的调用者负责等待要执行的区域,确保任意直接使用的值保持存活。
2023-11-24 13:50:43 313
原创 LLVM学习笔记(62)
上面所调用的AddPromotedToType()是将指定的操作,指定的操作数类型,与提升类型关联。维持这个关联关系的容器是PromoteToType(类型std::map<std::pair<unsigned, MVT::SimpleValueType>, MVT::SimpleValueType>,其中std::pair记录操作与操作数原始类型)。下面Expand的含义是建议将操作数分为较小的两部分。做了不小的改动,改进了代码的结构,修改了一些指令的设置。,并修改了一些指令与指定类型操作数的处理。
2023-11-22 14:49:39 289
翻译 优化C++软件(11)
如果不能预测最终大小或预测结果太小,那么分配一个新的更大的内存块,把旧内存块的内容拷贝到新内存块是必要的。中用于访问容器元素的所谓迭代器,对许多程序员而言是笨拙的,如果你可以使用一个具有简单索引的线性列表,它们是不必要的。如果元素数在编译时刻已知,或者可以设置一个不是太大的上限,那么最优的方案是固定大小数组或不使用动态内存分配的容器。如果在保存第一个元素前,要保存的元素总数已知(或者可以做出一个合理的估计),那么最好使用允许你预先保留所需内存的容器,而不是分段分配或者在内存块变得太小时重新分配。
2023-11-21 14:39:02 61
原创 MLIR笔记(3)
类似地,区域提供了值可见性的一个自然的辖域:定义在一个区域里的值不会逃逸到包含它的区域(如果有的话)。默认地,区域里的操作可以援引定义在外部区域的值,只要包含它操作的操作数援引这些值是合法的,但这局限于使用特性(trait),比如OpTrait::IsolatedFromAbove,或定制的验证器。Standard方言利用这个功能来定义具有单入口多出口(Single-Entry-Multiple-Exit,SEME)的区域,在区域中可能流经不同的基本块,并从任一带有return操作的基本块退出。
2023-11-20 14:12:00 306
原创 LLVM学习笔记(62)
应该假设目标机器将根据对齐限制,首先使用尽可能多的最大的储存操作,然后如果需要较小的操作。接下来的代码将个别的操作设置为Expand,下面会看到X86的派生类型还会进行自己的改写。分别表示函数的最小对齐要求(用于优化代码大小时,防止显式提供的对齐要求导致错误代码),函数的期望对齐要求(用于没有对齐要求且优化速度时),以及期望的循环对齐要求。它是一个位图,每个LLVM IR操作对应一个位,如果是1,表示该操作期望使用目标机器的回调方法PerformDAGCombine()来执行指令合并。
2023-11-17 14:20:43 244
原创 优化C++软件(10)
例如,父类的成员函数在一个源文件,派生类在另一个源文件,可能是方便的。控制动态链接库函数的地址,没有容易的方法。从地址0x2710读或写一个变量,会使缓存将地址0x2700到0x273F的64或0x40字节载入0x1C组4条缓存行中的一条。将常用函数与很少使用的函数分开,将很少使用的分支,比如错误处理,放在函数末尾或一个独立的函数里。当然,使用联合不是一个安全的程序实践,因为如果a与b的使用重叠,你将不会得到编译器的警告。例如,如果程序有两个数组,a与b,以a[0],b[0],a[1],b[1],……
2023-11-16 16:59:07 87
原创 MLIR笔记(2)
C++支持类型间的自动转换(如operator =声明的转换),但在转换的调用链里自动转换只能调用一次,这固然是避免给编译器带来过分的复杂性,但更重要的是允许自动转换接力调用几乎很难避免出现递归调用,而且调用链过长会很快失去控制,给人带来意想不到的结果。也就是说,下一个对象的位置由前一个对象返回,这也是合理的,因为只有找到前一个对象才能知道它后面的对象在哪里。对于我们最终调用的第二个版本来说,所谓的转换就是一个简单的C形式的强制转换,不过,由于前面的一系列检查,这个转换是安全的。
2023-11-15 14:04:59 558 2
原创 MLIR笔记(1)
则保存了命令行上出现选项的具体信息(它是一个模板类,这里的特化以所服务的信息类型为基类,在这个上下文里就是GenInfo的派生类),这里因为在声明opt_storage基类时把ExternalStorage指定为false,因此generator的opt_storage部分将用于保存命令行上出现的选项所对应的GenInfo实例。MLIR是Multi-layer IR的缩写,它是基于LLVM IR发展的一个中间语言形式,是Clang/LLVM的发明者Chris Lattner在加盟谷歌后又一个重要的发明。
2023-11-13 15:04:00 503
原创 LLVM学习笔记(61)
X86Subtarget构造函数315行处的InstrInfo是X86InstrInfo类型的成员,因此调用了下面的构造函数。它是一个很长的函数,我们要分段看。46行的X86GenRegisterInfo构造函数是TableGen生成的,它将X86特定的寄存器信息绑定到与目标机器无关的MCRegisterInfo层,并执行相关的寄存器编号映射,比如LLVM到Dwarf。接着,在X86InstrInfo构造函数的87行调用了X86RegisterInfo的构造函数来初始化同类型的成员RI,即寄存器信息。
2023-11-13 14:57:31 200
原创 LLVM学习笔记(60)
基类X86GenSubtargetInfo的构造函数是TableGen生成的,前面我们已经看到,它将MC层的一组指针指向X86目标机器特定的参数。300行的成员TSInfo的类型是X86SelectionDAGInfo,目标机器通过它可以提供对memcpy、memmove、memset、memcmp、memchr、strcpy、strcmp、strlen,这些操作的专属处理代码(工具链的一个特性,它允许开发者创建给定函数的多个实现,在运行时使用一个解析器函数在其中选择。指示支持架构的选择是相当简单的。
2023-11-08 16:06:09 232
原创 LLVM学习笔记(59)
computeDataLayout()根据Triple值确定目标机器的各种数据布局,这个函数返回一个对此进行描述的字符串。这个字符串接着交给TargetMachine构造函数,由其中的DataLayout类型的成员DL对该字符串进行解析。显然字符串的好处是容易调试。在X86TargetMachine构造函数104行的TLOF是一个std::unique_ptr<TargetLoweringObjectFile>智能指针,createTLOF()根据编译器的triple值确定具体的目标文件格式。
2023-11-07 16:14:58 166
原创 LLVM学习笔记(58)
在main()函数的350行,TimeCompilations默认为1,可以通过隐藏的选项“-time-compilations”来指定它的值,它的作用是重复进行指定次数的编译,以得到更好的编译用时数据。不过,由于不同的函数可以使用不同的属性,因此DefaultOptions保存了根据编译命令行设置的属性(对当前编译单元而言,所谓的缺省属性),而Options会根据当前函数声明使用的属性进行更新。形式的模块,我们需要一定的安全检查,确保模块是良好的。检查调试信息的版本,丢弃过时的调试信息。
2023-10-30 16:58:12 231
原创 LLVM学习笔记(57)
Llc的入口就是其中的main()函数。RegisterTargetMachine在构造函数里调用方法TargetRegistry::RegisterTargetMachine()将自己的Allocator()方法设置为TheX86_32Target及TheX86_64Target生成目标机器实现(X86TargetMachine)的构造方法指针TargetMachineCtorFn,为后面创建目标机器对象做好准备。:尝试找出在一个域中的指令链(闭包),如果有利,将它们转换为不同域里等价的指令。
2023-10-27 14:12:29 414
原创 LLVM学习笔记(56)
其次,通过使用CurDAG- >SelectNodeTo(),我们创建一个SP::RDY指令节点,然后将所有使用ISD::MULHU节点的地方改为指向SP::RDY的结果。每个目标机器通过在名为DAGToDAGISel的SelectionDAGISel子类中实现Select()方法来处理指令选择,例如,SPARC中的SparcDAGToDAGISel::Select()。快速指令选择的目的是,以代码质量为代价,提供快速的代码生成,它适合于-O0级别优化过程的哲学。
2023-10-25 13:47:29 548
原创 LLVM学习笔记(55)
每个目标机器还重载方法,以实现如何将一个特定的、目标机器无关的高级节点降级到更接近这个机器的形式。在这个过程中,一些特殊的IR指令,比如call与ret,需要将目标机器特定的习语——例如,如何传递实参,如何从一个函数返回——翻译为SelectionDAG节点。你可能会问,如果这是指令选择的一个输入,为什么在SelectionDAG类中已经有一些目标机器特定的节点?另外,DAG合并知道运行到遍链(pass chain)的何处(例如,在合法化或向量合法化之后),并且可以更精确地使用这个信息。
2023-10-24 10:59:35 217
翻译 优化C++软件(9)
8.5.编译器优化选项所有C++编译器有各种你可以打开、关闭的优化选项。学习正在使用编译器可用的选项,并打开所有相关选项,是重要的。许多优化选项与调试不兼容。调试器可以一次执行一行代码,并显示所有变量的值。显然,在部分代码被重排、内联或优化掉时,这是不可能的。通常制作可执行程序的两个版本:带有完整调试支持,在程序开发期间使用的调试版本,以及打开所有相关优化选项的发布版本。大多数IDE(集成开发环境)有制作目标文件与可执行文件的调试版本与发布版本的设施。确保区分这两个版本,在可执行文件的优化版本里关.
2020-08-07 11:03:23 503
翻译 优化C++软件(8)
8.2.不同编译器的比较我在7个不同品牌的C++编译器上进行了一系列实验,看它们是否能够进行各种优化。结果汇总在表8.1中。这个表展示了在我的测试例子中,不同的编译器是否成功应用了各种优化方法以及代数约简。这个表可以给出哪些优化你可以预期一个特定的编译器会进行,哪些优化你必须手动进行的某些预示。必须强调,在不同的测试例子上,编译器行为会不同。你不能预期编译器的行为总是符合这个表。...
2020-01-10 12:03:11 543
原创 LLVM学习笔记(54)
3.11.2.2.匹配表的生成对象GlobalISelEmitter::run() 4267行循环遍历前面生成的所有PatternToMatch对象(PatternToMatch的生成参考DAG指令选择器的生成代码一节),对每个PatternToMatch对象调用下面的方法来构建对应的Matcher实例(类似于SelectionDAG生成代码时构建的另一个版本的Matcher)。在下面383...
2020-01-10 11:56:01 1304
翻译 优化C++软件(7)
8.1. 编译器里的优化8.1. 编译器如何优化现代编译器可以对代码进行许多优化以提升性能。程序员知道编译器能做什么,不能做什么是有好处的。下面章节描述某些程序员应该知道的编译器优化。函数内联编译器可以将被调用函数体替换函数调用。例子:// Example 8.1afloat square (float a) { return a * a;}float p...
2020-01-03 11:25:24 361
原创 LLVM学习笔记(53)
3.11.2.模式分析GlobalISel是以DAG指令选择的TD定义处理与分析为基础的。因此,GlobalISelEmitter包含了一个CodeGenDAGPatterns类型的const成员CGP(一旦创建完成,就是只读的),在GlobalISelEmitter构造函数中将完成DAG指令选择令人眼花缭乱的准备工作(参考DAG指令选择器的生成代码一节,直到Match对象序列的优化为止),...
2020-01-03 11:19:08 593
翻译 优化C++软件(6)
7.15. 函数参数在大多数情形里,函数参数通过值传递。这意味着参数的值被拷贝到一个局部变量。对简单类型,比如int,float,double,bool,enum以及指针与引用,这是高效的。数组总是以指针传递,除非它们被封装在一个类或结构体里。如果参数是复合类型,比如结构体或类,情形更复杂。如果满足以下条件,复合类型参数的传递更高效:对象很小能放入一个寄存器 对象没有拷贝构造函...
2019-12-27 10:54:29 413
原创 LLVM学习笔记(52)
3.11.GlobalISel代码的自动生成(v7.0)3.11.1.概述GlobalISel是LLVM的一个新锐指令选择器。关于它的描述,首先是LLVM的在线文档: 第一部分参考v7.0的变化一节。 通用机器IR 机器IR工作在物理寄存器、寄存器类,以及(大部分情形)目标机器特定指令上。 为了填补LLVM IR的空缺,GlobalISel引入了机器I...
2019-12-27 10:46:38 1648
翻译 优化C++软件(5)
7.6. 指针与引用指针与引用指针与引用同样高效,因为它们实际上做相同的事情。例如:// Example 7.12void FuncA (int * p) { *p = *p + 2;}void FuncB (int & r) { r = r + 2;}这两个函数做同样的事情,如果你看编译器产生的代码,你将注意到对这两个函数,代码实际上...
2019-12-20 11:38:42 258
Introduction to Theory of Computation
2018-09-23
Survey on Instruction Selection
2018-09-23
Towards a Compilation infrastructure for network processors
2018-09-23
Crafting a Compiler
2018-09-01
How Debuggers Work
2018-09-01
Compiler Construction-20th
2018-09-01
Compiler Construction-19th
2018-09-01
Compiler Construction-17th
2018-09-01
Compiler Construction-16th
2018-09-01
Compiler Construction-5th
2018-09-01
Construction and Evolution of Code Generator
2018-09-01
Compiler Construction-11th
2018-09-01
The compiler design handbook
2018-09-01
Data_Flow_Analysis_Theory_And_Practice(Bookos.org)
2018-09-01
The Compiler Design Handbook Optimizations and machine code generation
2018-09-01
instruction scheduling for instruction level parallel processor
2018-07-16
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人