为什么我们有了编译器还需要汇编器?
Why do we even need assembler when we have compiler?
如果编译器将高级语言转换为机器码,为什么我们还需要汇编程序?
是否有任何汇编级语言,我们不能为此使用编译器?
在 cs.SE 上引用 @TylerAndFriends's answer on Why do we need assembly language?(此副本):
Assembly language was created as an exact shorthand for machine level
coding, so that you wouldn't have to count 0s and 1s all day. It works
the same as machine level code: with instructions and operands.
Though it's true, you probably won't find yourself writing your next
customer's app in assembly, there is still much to gain from learning
assembly.
Today, assembly language is used primarily for direct hardware
manipulation, access to specialized processor instructions, or to
address critical performance issues. Typical uses are device drivers,
low-level embedded systems, and real-time systems.
Assembly language is as close to the processor as you can get as a programmer so a well designed algorithm is blazing -- assembly is
great for speed optimization. It's all about performance and
efficiency. Assembly language gives you complete control over the
system's resources. Much like an assembly line, you write code to push
single values into registers, deal with memory addresses directly to
retrieve values or pointers. (source: codeproject.com)
相关:Does a compiler always produce an assembly code? - 更多关于为什么一些编译器 do 只编译成 asm,而不是直接编译成某些目标文件格式的机器代码。编译为 asm 而不是机器代码使编译器的工作更轻松并且编译器更易于移植有多种原因。但是编译器并不是 asm 存在的唯一原因。
why do we even need assembler?
很多人不需要懂汇编语言。
它的存在让我们可以更轻松地讨论/分析机器代码,write/debug 编译器。
编译器必须由人编写。 ,在设计新的 CPU 架构时,您总是会为操作码和寄存器命名,这样您就可以与其他人讨论设计,并发布可读的手册。
或者对于OS开发,编译器无法生成一些特殊的特权指令1。而且你不能在纯 C 中编写保存寄存器的上下文切换函数。
CPUs 运行 机器代码,不是直接的高级语言,所以 计算机安全/漏洞利用,以及任何严重的低级性能分析/单个调优循环需要查看 CPU 是 运行ning 的指令。操作码的助记名称在思考和编写它们时非常有帮助。 mov r32, imm32
比 B8+rd imm32
(该助记符的操作码范围)更容易记住且更具表现力。
脚注 1: 除非像 MSVC 一样,您为 OSes 需要使用的所有特殊指令(例如 __invlpg()
创建内部函数,所以您 可以 写一个 OS 没有 inline asm。 (他们仍然需要一些独立的 asm 来处理入口点之类的东西,可能还需要上下文切换函数。)但是这些内在函数仍然需要 C 中的名称,因此您不妨在 asm 中命名它们。
我经常使用 asm 轻松创建我想针对 microbenchmarks 测试的机器代码。编译器必须创建 高效 机器代码,而不仅仅是正确的机器代码,因此人们经常使用 asm 来准确了解各种 CPU 上什么是快什么不是s.
参见 http://agner.org/optimize/, and other performance links in the x86 tag wiki。
例如请参阅 and Micro fusion and addressing modes 了解微基准测试示例,了解什么是快速。
请参阅 了解更多关于手工编写 asm 的信息,这比我可以手持 gcc 或 clang 发射的速度更快,甚至通过调整 C 源代码使其看起来更像我想出的 asm。
(显然我必须了解 asm 才能查看编译器的 asm 输出并了解如何做得更好。编译器与完美 相差。有时还很远。错过优化的错误很常见。要考虑新的优化并建议编译器寻找它们,用 asm 指令来思考比机器代码要容易得多。)
错误代码的编译器错误有时也会发生,验证它们基本上需要查看编译器输出。
Stack Overflow 有几个问题,例如“什么更快:a++
或 ++a
?”,答案完全取决于它如何编译成 asm,而不是源代码级语法差异。要了解为什么 某些源差异会影响性能,您必须了解代码如何编译为 asm。
例如。 (人们常常没有意识到编译 with/without 优化不仅仅是线性加速,而且对未优化的代码进行基准测试基本上毫无意义。未优化的代码具有 不同 瓶颈...如果你看一下 asm,这是显而易见的。)
更多示例:
- 与中断处理程序交互以实现原子操作,例如 Linux's atomic operations on ARMv5 and earlier。
- 只有在 QEMU linux-user 中未调用信号处理程序时才调用系统调用。
- 将计算机初始化为可以使用编译器的状态,例如配置内存控制器。
- 进入和退出 to/from 中断处理程序和系统调用。
编译器可以将用高级语言编写的代码翻译成机器代码,但这并不是它可以翻译成的唯一语言。它还可以将代码翻译成汇编语言等等。
看
https://www.quora.com/Does-a-compiler-convert-code-to-assembly
但是从上面的回答中我们可以看出为什么我们通常在编译器之后使用汇编程序。
TL;DR - 如果编译器和调试器编写器是完美的,您可能不需要汇编程序来进行应用程序编程。但是,您对计算的基本理解并不完整。你将失去走出禁区的能力。
汇编程序试图将 one-to-one 机器助记符映射到底层二进制文件 op-codes。因此,它是特定机器最具表现力的语言。某些语言试图隐藏 'pointers' 或内存地址。所有语言都将寄存器分配和映射变量隐藏到栈槽或物理寄存器中。优化编译器的工作是将高级语言映射到底层机器语言。使用的算法可能非常详尽,因为计算机可以比人类更快地搜索大量解决方案并找到最佳解决方案。
编译器'fails'在没有实现机器概念时会将问题映射到最有效的解决方案。例如,在 'C' 和 'C++' 中没有 'carry bit' 的概念。对于任意大数类型,有多种解决方案。对于涉及大整数的问题,使用 'carry bit' 将较小的整数链接成较大的整数(位数)很有用。编译器开发人员已经意识到这个问题并实施了各种解决方案。最简单的就是添加越来越多的类型(long long unsigned 等)。一些编译器会检测 '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 等)来以某种较低级别的语言创建此代码。许多高效的数据结构没有内存地址就无法编码。
即使您从不使用汇编语言,这些概念也将帮助您理解代码运行缓慢的原因。高级语言可能会掩盖许多关于为什么事情 are/are 效率不高的细节。通常,如果您了解该工具如何将事物映射到汇编语言,您对高效解决方案的搜索就会快得多。
对于安全研究人员来说,汇编程序操作码的知识对于理解漏洞利用来说是非常基础的。对于 OS/systems 程序员,有许多操作码不会映射到更高级别的语言。对于编译器和语言作者来说,要找到问题集的最佳映射和表达方式,您需要了解汇编程序;甚至更多的机器架构,其中包括内存访问的细微差别。
最终,专业程序员将面临具有局限性的专有代码。此代码不会随源代码一起提供。通常,诊断和解决问题的最有效方法是检查二进制文件是否存在问题。如果你看不懂汇编语言,你就卡住了。
如果编译器将高级语言转换为机器码,为什么我们还需要汇编程序? 是否有任何汇编级语言,我们不能为此使用编译器?
在 cs.SE 上引用 @TylerAndFriends's answer on Why do we need assembly language?(此副本):
Assembly language was created as an exact shorthand for machine level coding, so that you wouldn't have to count 0s and 1s all day. It works the same as machine level code: with instructions and operands.
Though it's true, you probably won't find yourself writing your next customer's app in assembly, there is still much to gain from learning assembly.
Today, assembly language is used primarily for direct hardware manipulation, access to specialized processor instructions, or to address critical performance issues. Typical uses are device drivers, low-level embedded systems, and real-time systems.
Assembly language is as close to the processor as you can get as a programmer so a well designed algorithm is blazing -- assembly is great for speed optimization. It's all about performance and efficiency. Assembly language gives you complete control over the system's resources. Much like an assembly line, you write code to push single values into registers, deal with memory addresses directly to retrieve values or pointers. (source: codeproject.com)
相关:Does a compiler always produce an assembly code? - 更多关于为什么一些编译器 do 只编译成 asm,而不是直接编译成某些目标文件格式的机器代码。编译为 asm 而不是机器代码使编译器的工作更轻松并且编译器更易于移植有多种原因。但是编译器并不是 asm 存在的唯一原因。
why do we even need assembler?
很多人不需要懂汇编语言。
它的存在让我们可以更轻松地讨论/分析机器代码,write/debug 编译器。
编译器必须由人编写。
或者对于OS开发,编译器无法生成一些特殊的特权指令1。而且你不能在纯 C 中编写保存寄存器的上下文切换函数。
CPUs 运行 机器代码,不是直接的高级语言,所以 计算机安全/漏洞利用,以及任何严重的低级性能分析/单个调优循环需要查看 CPU 是 运行ning 的指令。操作码的助记名称在思考和编写它们时非常有帮助。 mov r32, imm32
比 B8+rd imm32
(该助记符的操作码范围)更容易记住且更具表现力。
脚注 1: 除非像 MSVC 一样,您为 OSes 需要使用的所有特殊指令(例如 __invlpg()
创建内部函数,所以您 可以 写一个 OS 没有 inline asm。 (他们仍然需要一些独立的 asm 来处理入口点之类的东西,可能还需要上下文切换函数。)但是这些内在函数仍然需要 C 中的名称,因此您不妨在 asm 中命名它们。
我经常使用 asm 轻松创建我想针对 microbenchmarks 测试的机器代码。编译器必须创建 高效 机器代码,而不仅仅是正确的机器代码,因此人们经常使用 asm 来准确了解各种 CPU 上什么是快什么不是s.
参见 http://agner.org/optimize/, and other performance links in the x86 tag wiki。
例如请参阅
请参阅
(显然我必须了解 asm 才能查看编译器的 asm 输出并了解如何做得更好。编译器与完美 相差。有时还很远。错过优化的错误很常见。要考虑新的优化并建议编译器寻找它们,用 asm 指令来思考比机器代码要容易得多。)
错误代码的编译器错误有时也会发生,验证它们基本上需要查看编译器输出。
Stack Overflow 有几个问题,例如“什么更快:a++
或 ++a
?”,答案完全取决于它如何编译成 asm,而不是源代码级语法差异。要了解为什么 某些源差异会影响性能,您必须了解代码如何编译为 asm。
例如
更多示例:
- 与中断处理程序交互以实现原子操作,例如 Linux's atomic operations on ARMv5 and earlier。
- 只有在 QEMU linux-user 中未调用信号处理程序时才调用系统调用。
- 将计算机初始化为可以使用编译器的状态,例如配置内存控制器。
- 进入和退出 to/from 中断处理程序和系统调用。
编译器可以将用高级语言编写的代码翻译成机器代码,但这并不是它可以翻译成的唯一语言。它还可以将代码翻译成汇编语言等等。 看 https://www.quora.com/Does-a-compiler-convert-code-to-assembly
但是从上面的回答中我们可以看出为什么我们通常在编译器之后使用汇编程序。
TL;DR - 如果编译器和调试器编写器是完美的,您可能不需要汇编程序来进行应用程序编程。但是,您对计算的基本理解并不完整。你将失去走出禁区的能力。
汇编程序试图将 one-to-one 机器助记符映射到底层二进制文件 op-codes。因此,它是特定机器最具表现力的语言。某些语言试图隐藏 'pointers' 或内存地址。所有语言都将寄存器分配和映射变量隐藏到栈槽或物理寄存器中。优化编译器的工作是将高级语言映射到底层机器语言。使用的算法可能非常详尽,因为计算机可以比人类更快地搜索大量解决方案并找到最佳解决方案。
编译器'fails'在没有实现机器概念时会将问题映射到最有效的解决方案。例如,在 'C' 和 'C++' 中没有 'carry bit' 的概念。对于任意大数类型,有多种解决方案。对于涉及大整数的问题,使用 'carry bit' 将较小的整数链接成较大的整数(位数)很有用。编译器开发人员已经意识到这个问题并实施了各种解决方案。最简单的就是添加越来越多的类型(long long unsigned 等)。一些编译器会检测 '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 等)来以某种较低级别的语言创建此代码。许多高效的数据结构没有内存地址就无法编码。
即使您从不使用汇编语言,这些概念也将帮助您理解代码运行缓慢的原因。高级语言可能会掩盖许多关于为什么事情 are/are 效率不高的细节。通常,如果您了解该工具如何将事物映射到汇编语言,您对高效解决方案的搜索就会快得多。
对于安全研究人员来说,汇编程序操作码的知识对于理解漏洞利用来说是非常基础的。对于 OS/systems 程序员,有许多操作码不会映射到更高级别的语言。对于编译器和语言作者来说,要找到问题集的最佳映射和表达方式,您需要了解汇编程序;甚至更多的机器架构,其中包括内存访问的细微差别。
最终,专业程序员将面临具有局限性的专有代码。此代码不会随源代码一起提供。通常,诊断和解决问题的最有效方法是检查二进制文件是否存在问题。如果你看不懂汇编语言,你就卡住了。