如果编译器将高级语言转换为机器代码,为什么我们甚至需要汇编器? 是否有任何汇编级语言,我们不能为此使用编译器?
答案 0 :(得分:6)
为什么我们甚至需要汇编程序?
许多人不需要懂汇编语言。
它的存在是为了让我们可以讨论/分析机器代码,并更轻松地编写/调试编译器。
编译器必须由人类编写。 As @old_timer points out,在设计新的CPU体系结构时,您总是为操作码和寄存器命名,以便您可以与其他人讨论设计并发布可读的手册。
或者对于OS开发,编译器无法生成一些特殊的特权指令。而且,您无法编写将寄存器保存在纯C中的上下文切换功能。
CPU直接运行机器代码,而不直接运行高级语言,因此计算机安全性/漏洞利用以及任何严重的性能分析/调整都需要查看CPU运行说明。操作码的助记符名称在思考和编写它们时非常有帮助。与B8+rd imm32
(该助记符的操作码范围)相比,mov r32, imm32
更容易记住和更具表达力。
我经常使用asm轻松创建要测试的微基准测试机器码。编译器必须创建有效的机器代码,而不仅仅是正确的机器代码,因此人们经常使用asm来查看各种CPU上的运行速度和不运行速度。
请参见http://agner.org/optimize/和其他性能链接in the x86 tag wiki。
例如有关微基准测试的示例,请参见Can x86's MOV really be "free"? Why can't I reproduce this at all?和Micro fusion and addressing modes,以了解有关快速实施的知识。
有关{@ 3}的更多信息,请参见C++ code for testing the Collatz conjecture faster than hand-written assembly - why?,这比手动将gcc或clang发出的速度要快,甚至可以通过调整C源代码使其看起来更像我想到的asm。
(显然,我必须了解asm才能查看编译器的asm输出并查看如何做得更好。编译器离完美还差很多。有时甚至很远。错过优化的错误是考虑到新的优化并建议编译器寻找它们,就asm指令而言,比机器代码要容易得多。)
有时还会发生错误代码的编译器错误,而验证它们基本上需要查看编译器的输出。
Stack Overflow有几个问题,例如“什么更快:a++
或++a
?”,答案完全取决于它如何编译成asm,而不取决于源代码级别的语法差异。要了解为什么某些类型的源差异会影响性能,您必须了解代码如何编译为asm。
例如Adding a redundant assignment speeds up code when compiled without optimization。 (人们常常没有意识到,有/没有优化的编译不仅是线性加速,而且对未优化的代码进行基准测试基本上是没有意义的。未优化的代码存在不同瓶颈...这如果您查看asm,则很明显。)
答案 1 :(得分:3)
从@TylerAndFriends's answer on Why do we need assembly language?的cs.SE报价(重复):
创建了汇编语言,将其作为机器级别的精确速记 编码,这样您就不必整天计算0和1。有用 与机器级代码相同:带有指令和操作数。
尽管这是事实,但您可能不会发现自己写下一篇 客户的应用程序在组装中,学习仍有很多好处 组装。
如今,汇编语言主要用于直接硬件 操作,访问专用处理器指令或 解决关键的性能问题。典型用途是设备驱动程序 低级嵌入式系统和实时系统。
汇编语言与程序员一样接近处理器,因此精心设计的算法非常出色-汇编 非常适合速度优化。一切都与性能和 效率。汇编语言使您可以完全控制 系统的资源。就像流水线一样,您编写代码来推送 将单个值存入寄存器,直接将内存地址处理为 检索值或指针。 (来源:codeproject.com)
答案 2 :(得分:2)
更多示例:
答案 3 :(得分:1)
TL; DR-如果编译器和调试器编写器很完美,则可能不需要汇编程序即可进行应用程序编程。但是,您对计算的基本理解将不完整。您将失去跳出框框的能力。
汇编器尝试将机器助记符一对一映射到基础二进制操作码。因此,它是特定机器上最富表现力的语言。一些语言试图隐藏“指针”或内存地址。所有语言都将寄存器分配和映射变量隐藏到堆栈插槽或物理寄存器中。优化编译器的工作是将高级语言映射到基础机器语言。由于计算机可以比人类更快地搜索大量解决方案并找到最佳解决方案,因此所使用的算法可能非常详尽。
如果编译器未意识到机器概念,则会“失败”,这会将问题映射到最有效的解决方案。例如,在“ C”和“ C ++”中不存在“进位”的想法。对于任意大数类型,有几种解决方案。对于涉及大整数的问题,使用“进位”将较小的整数链接为较大的整数(位数)很有用。编译器开发人员已意识到此问题,并实施了各种解决方案。最琐碎的只是添加越来越多的类型(长,长,无符号等)。有些编译器会在程序员试图使用高位链接到低位的情况下检测“ C”中的惯用语。例如,
/* a,b are two parts of one number.
c,d are two parts of another to be added.
*/
void add_big(uint *a, uint *b, const uint c,const uint d) {
unsigned long long tmp;
tmp = *b + d;
if(tmp & CARRY_BIT)
*a += c + 1;
else
*a += c;
*b = (uint)tmp;
}
复杂性说明了您想要简单高效地完成此任务有多么困难。实际上,大多数机器只允许将此映射到一些汇编程序指令。编译器编写者需要认识到用户正在使用的模式可以将几种高级构造折叠成一些汇编指令。或者,它们为下级程序集提供了语言转义。
只有借助汇编器和机器概念的知识,才能更有效地解决许多调试问题。如果您使用高级语言(例如Python)进行编程,那么这将是无关紧要的。但是,最终您需要依靠其他开发人员来制作容器(列表,集合,字典,numpy等),以使用某种较低级别的语言来创建此代码。没有内存地址就无法编码许多有效的数据结构。
即使您从不使用汇编语言,这些概念也将帮助您理解为什么代码很慢。一种高级语言可能会掩盖许多关于为什么事情效率低下的细节。通常,如果您了解了该工具如何将事物映射到汇编语言,则可以更快地找到有效的解决方案。
对于安全研究人员而言,了解汇编代码对于了解漏洞利用程序可能是非常基础的。对于OS /系统程序员,有很多操作码无法映射到更高级别的语言。对于编译器和语言作者,要找到对问题集的最佳映射及其表达方式,您需要了解汇编程序;甚至更多的计算机架构,其中包括细微的内存访问。
最终,专业程序员将面对专有代码,但这种代码有局限性。此代码将不附带源代码。诊断和解决问题的最有效方法通常是检查二进制文件中的问题。如果您听不懂汇编语言,则会陷入困境。
答案 4 :(得分:0)
编译器可以将用高级语言编写的代码转换为机器代码,但这不是唯一可以翻译的语言。它还可以将代码翻译成汇编语言等等。 看到 https://www.quora.com/Does-a-compiler-convert-code-to-assembly
但是正如上面的答案中提到的,我们可以理解为什么我们通常在编译器之后使用汇编器。