导航:首页 > 源码编译 > 描述编译器的顺序

描述编译器的顺序

发布时间:2022-03-16 02:30:20

① 如何让编译器按代码顺序执行(((急急急)))

问题是你do的值就是c.me提供的,想要优化那就要考虑把do(c.me())这整个逻辑进行优化

② 编译器生成的汇编语句执行顺序为什么与C代码顺序不同

不影响语义的前提下编译器可以任意重排代码顺序;
在乱序执行(Out-of-Order)的CPU里,机器码的执行也可以不按照你在“汇编”层面上看到的顺序执行,只要不影响语义。
所以说这些中间步骤的顺序,作为底层细节平时不需要那么在意——它们多半跟原始源码的顺序是不一样的。

现代优化编译器优化的思路之一是“基于依赖的优化”(dependence-based optimization)。题主引用的CSAPP的例子:

int arith(int x, int y, int z) {
int t1 = x + y;
int t2 = z * 48;
int t3 = t1 & 0xFFFF;
int t4 = t2 * t3;
return t4;
}

所有涉及运算的值都是局部标量变量(local scalar variable),这是最便于编译器做分析的情况,所有依赖都可以显式分析。
由于整个函数没有分支,这里也不需要讨论控制依赖(control dependence),只要讨论数据依赖(data dependence)就好。
把数据依赖图画出来是个DAG(这里正好是棵树,特例了):

x y z 48
\ / \ /
t1 0xFFFF t2
\ / /
t3 /
\ /
t4

优化必须要满足的约束是:每个节点求值之前,其子节点(依赖的数据源)必须要先求了值。
显然,t1和t2之间没有依赖关系,它们的相对求值顺序怎样重排都没关系。

有本我很喜欢的书,里面讲的是各种基于依赖的优化:Optimizing Compilers for Modern Architectures - A Dependence-based Approach

以上是理论部分。

================================================================

下面来看例子。

我们可以用一个实际编译器来看看CSAPP的例子编译出来的结果:

.text
# -- Begin arith
.p2align 4,,15
.globl arith
.type arith, @function
arith:
.p2align 4,,7
/*.L0:*/ /* Block BB[54:2] preds: none, freq: 1.000 */
movl 8(%esp), %edx /* ia32_Load T[139:10] -:1:22 */
addl 4(%esp), %edx /* ia32_Add Iu[141:12] -:2:14 */
movzwl %dx, %edx /* ia32_Conv_I2I Iu[142:13] -:4:15 */
imull 12(%esp), %edx /* ia32_IMul Iu[143:14] -:5:15 */
leal (%edx,%edx,2), %eax /* ia32_Lea Iu[144:15] -:5:15 */
shll $0x4, %eax /* ia32_Shl Iu[146:17] -:5:15 */
ret /* ia32_Return X[152:23] -:6:3 */
.size arith, .-arith
# -- End arith

这里用的是libFirm。可见它跟CSAPP书里所说的汇编的顺序又有所不同。这也是完全合理的。
这个编译结果的顺序是:

edx = y;
edx += x;
edx = zeroextend dx; // edx = edx & 0xFFFF
edx *= z;
eax = edx * 3;
eax <<= 4; // eax = eax * 16

也是完全符合依赖关系的约束的一种顺序。
之所以用libFirm举例是因为它的中间表示(Intermediate Representation)是一种程序依赖图(Program Dependence Graph),可以很方便的看出控制与数据依赖。把CSAPP那里例子对应的libFirm IR画出来,是这个样子的:
(这张图跟我前面画的数据依赖图正好是左右翻转的,不过意思一样。(这张图跟我前面画的数据依赖图正好是左右翻转的,不过意思一样。
Arg 0、1、2分别代表x、y、z。白色方块是普通数据节点,黄色方块是常量节点,蓝色方块是内存相关节点,红色方块是控制流节点,粉红色方块是特殊的开始/结束节点。)

某版LLVM生成的代码:

; MoleID = '/tmp/webcompile/_16355_0.bc'
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-ellcc-linux"

; Function Attrs: nounwind readnone
define i32 @arith(i32 %x, i32 %y, i32 %z) #0 {
entry:
%add = add nsw i32 %y, %x
%mul = mul nsw i32 %z, 48
%and = and i32 %add, 65535
%mul1 = mul nsw i32 %mul, %and
ret i32 %mul1
}

attributes #0 = { nounwind readnone "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.ident = !{!0}

!0 = !{!"ecc 0.1.10 based on clang version 3.7.0 (trunk) (based on LLVM 3.7.0svn)"}

最终生成的x86汇编:

.text
.file "/tmp/webcompile/_15964_0.c"
.globl arith
.align 16, 0x90
.type arith,@function
arith: # @arith
# BB#0: # %entry
movl 8(%esp), %eax
addl 4(%esp), %eax
movzwl %ax, %eax
imull 12(%esp), %eax
shll $4, %eax
leal (%eax,%eax,2), %eax
retl
.Ltmp0:
.size arith, .Ltmp0-arith

.ident "ecc 0.1.10 based on clang version 3.7.0 (trunk) (based on LLVM 3.7.0svn)"
.section ".note.GNU-stack","",@progbits

GCC 4.9.2 x86-64:

arith(int, int, int):
leal (%rdx,%rdx,2), %eax
addl %edi, %esi
movzwl %si, %esi
sall $4, %eax
imull %esi, %eax
ret

Zing VM Server Compiler x86-64:

# edi: x
# esi: y
# edx: z
movl %edx, %eax
shll $0x4, %eax
leal (%rsi, %rdi, 1), %ecx
shll $0x5, %edx
addl %edx, $eax
movzwl %ecx, %edx
imull %edx, %eax

③ 编译器编译代码时,他的顺序是怎样的

编译是一个很复杂的东西 不是一两句能说清楚的
你可以搜一下编译原理,这个可以说是一门学科了

大多数C编译器遵循的一个流程
预编译(宏取代 条件编译处理等)
词法分析 根据关键字获取符号
语法分析 分析程序结构 大多数的编译错误都是这一步抛出来的
中间代码转换
转为二级制码

④ 关于译码器器的描述正确的是

尽可能通俗讲

假设我们有一篇英文说明书。

编译器负责把每一页或节或章翻译成等价的中文。

链接器负责把翻译好的章节整理成完整说明书。

由于语言问题,译本和原本之间的页数可能不一样,导致原本中位于第二页的某图在译本里可能在第五页,这些引用的修改由链接器完成。又译本中可能还要加上中文的版本历史,序以及译者的话等,甚至出于一些考虑还要改变原本的章节顺序。

上述所有这些活都是链接器的基本工作。

⑤ 如何arm编译器编译时变量顺序排列

可以 话说,目前业内一般都是用keil编译器的,它支持的芯片种类还更多

⑥ c++编译器以何种顺序编译文件的,先cpp文件,还是.h文件

1. 编译阶段 (头文件 .h)
d工程中在头文件中对导出内容(function, class, type, object, variable)进行定义.

2. 链接阶段 (库文件 .lib)
d工程在link阶段会生成.lib
用户link时需要 这个.lib 解决link时的代码定位.
3. 运行阶段
.exe

⑦ 编译器生成的汇编语句执行顺序为什么与C代码顺序不同

编译器不仅是无序的,而且还展开、合并、删除代码(如果没有发现代码),,,C语言是好的,和C++编译器编译,经常连上帝也不知道结果。


暂停现代CPU为了弥补CPU和内存之间的速度差距的接入方案,以所谓的缓存(L1,L2,L3;在CPU芯片的速度比内存快得多,但容量很小),CPU缓存中常见的事情似乎保持镜子,需要在缓存中直接读取使用的数据是好的,比内存快几倍的速度。

注意名字的区别。名称风险是两个没有数据流的指令,数据冒险是两个指令之间的数据流。

⑧ 编译器编译代码时, 他的顺序是怎样的

先定义全局变量,再按照从左至右,从上至下的顺序将源代码(也就是你写的代码)编译成机器能识别的机器码,最后再执行编译好的机器码.

⑨ c/c++中,编译器编译文件的顺序和规则

#include <filename>从磁盘安装根目录查找文件;#include "filename"从编译文件的当前目录查找文件。

⑩ linux编译器的解析顺序与windows编译器的区别

没区别,都是编译成obj lib等目标文件和库文件,然后链接为可执行的二进制代码,Win平台多了一个动态链接库。

阅读全文

与描述编译器的顺序相关的资料

热点内容
苹果咋给应用加密 浏览:146
棒棒解压法 浏览:836
机器人算法迷宫 浏览:624
和面一样的感觉是不是看着很解压 浏览:198
服务器优化怎么写 浏览:98
缓解压力的音乐轻柔 浏览:930
虐杀原形压缩包有多大 浏览:966
让php执行exe文件 浏览:299
入门程序员考证 浏览:968
移动远程服务器什么意思 浏览:337
现在有什么靠谱的脱单app 浏览:880
辽宁网页直播系统源码 浏览:866
ajax获取网页源码 浏览:382
单片机树莓派接线图 浏览:813
php安装suhosin 浏览:688
服务器地址443无法连接 浏览:736
jpg怎么批量转换成pdf 浏览:193
甄嬛传东方卫视源码 浏览:218
linuxpython下载安装 浏览:942
人工免疫算法matlab 浏览:661