我正在寻找使用汇编程序生成机器代码的简要说明.
所以我知道汇编是机器代码的1:1翻译.但我对目标代码和链接器以及它们如何放入它感到困惑.
我不需要复杂的答案,只需要一个简单的答案就可以了
完整的代码在这里:http://pastebin.com/MM3vWmqA
在函数fast_generator中,我为两个语句添加了注释.如果切换这些语句,代码运行速度会快1.8倍.如果删除第一个语句,代码将比原始版本执行得更快,但与切换时相比更慢.
测试用例应如下.
第一 - 最慢.452ms.
counter++;
i--;
Run Code Online (Sandbox Code Playgroud)
第二 - 比第一个更快.280ms.
i--;
counter++;
Run Code Online (Sandbox Code Playgroud)
第三 - 比第一个快,但比第二个慢.421ms.
i--;
Run Code Online (Sandbox Code Playgroud)
原始语句的汇编器输出是.
inc edx
mov eax, 6
Run Code Online (Sandbox Code Playgroud)
我已经验证,当切换这些语句时,汇编程序输出保持不变,这些asm指令的唯一区别是互换.
我用VC++ 10和VC++ 11测试过它,行为相同.有人可以解释为什么切换这些语句会加速算法~1.8倍?如果您认为std :: clock()不准确,请更改size = 7.在我的机器上,size = 7的差异是12000ms vs 7000ms.
我最近开始学习装配,当我浏览互联网时,我看到越来越多的人说装配并非无用,但是用一种需要花费时间和精力的语言编写东西也是不值得的.高级语言.高级语言程序和低级语言程序之间的效率是否真的不足以引起人们现在的关注,是否还有另一种低级语言,如程序集更广泛使用?
我正在用C编写一个概念验证JIT编译器,它目前正在生成汇编代码串.C中的内联汇编功能仅处理编译时已知的字符串文字,因此我无法使用它来运行生成的运行时代码.
我已经阅读过使用mmap()在运行时执行生成的机器代码,但是如果可能的话,我想避免使用机器代码.
有谁知道任何解决方案?我曾想过将它写入文件并在所述文件上调用汇编程序和链接器,但这样做会很麻烦而且很慢.
什么是callx86机器代码中绝对指针的"正确"方法?有没有一种方法可以在一条指令中完成它?
我想做什么:
我正在尝试基于"子程序线程"构建一种简化的迷你JIT(仍然).它基本上是字节码解释器中最短的步骤:每个操作码都是作为一个单独的函数实现的,因此每个基本的字节码块都可以"JIT"到它自己的新程序中,如下所示:
{prologue}
call {opcode procedure 1}
call {opcode procedure 2}
call {opcode procedure 3}
...etc
{epilogue}
Run Code Online (Sandbox Code Playgroud)
因此,我们的想法是每个块的实际机器代码只能从模板中粘贴(根据需要扩展中间部分),并且需要"动态"处理的唯一位是将每个操作码的函数指针复制到正确的位置作为每个调用指令的一部分.
我遇到的问题是了解call ...模板部分的用途.x86似乎没有考虑到这种用法,并且有利于相对和间接调用.
它看起来像我可以使用FF 15 EFBEADDE或2E FF 15 EFBEADDE在假设调用函数DEADBEEF(通过把东西变成一个汇编和反汇编,看到什么产生有效的结果,基本上发现了这些未通过了解他们在做什么),但我不理解的东东细分,特权和相关信息足以看出差异,或者这些信息与更常见的call指令有何不同.英特尔架构手册还建议这些仅在32位模式下有效,在64位模式下"无效".
有人可以解释这些操作码以及我是如何或者是否会为此目的使用它们或其他人?
(通过寄存器使用间接调用也有明显的答案,但这似乎是"错误的"方法 - 假设实际存在直接调用指令.)
将程序编译为机器代码而不是简单地从源代码构造AST并在遍历树时执行操作有什么好处/缺点?
你有什么理由想要一个人做另一个吗?
compiler-construction interpreter machine-code abstract-syntax-tree
我需要从内存中的一个小代码段逐个读取指令,我必须找出内存中指令的大小.
以下只是解释我的问题的原始反汇编代码的示例:
(gdb) disas /r 0x400281,+8
Dump of assembler code from 0x400281 to 0x400289:
0x0000000000400281: 48 89 c7 movq %rax, %rdi
0x0000000000400284: b0 00 movb $0, %al
0x0000000000400286: e8 f2 48 00 00 callq 0x10001f30a
End of assembler dump.
Run Code Online (Sandbox Code Playgroud)
我知道第一条指令的存储器地址(在这种情况下p = 0x0000000000400281),我可以从p读取每个存储器地址.问题是我不知道*(p + offset)的值是否是操作码,我知道每个操作码的大小信息都不固定.
那么,我可以得到每个汇编指令的大小吗?或者我可以知道我读的值是操作码还是信息?
为了有效地做x = x*10 + 1,它可能是最佳使用
lea eax, [rax + rax*4] ; x*=5
lea eax, [1 + rax*2] ; x = x*2 + 1
Run Code Online (Sandbox Code Playgroud)
3组件LEA在现代Intel CPU上具有更高的延迟,例如3个周期而不是Sandybridge系列的1个,因此disp32 + index*2比disp8 + base + index*1SnB系列更快,即我们关心优化的大多数主流x86 CPU.(这主要仅适用于LEA,而不适用于加载/存储,因为LEA运行在ALU执行单元上,而不是大多数现代x86 CPU中的AGU.)AMD CPU具有3个组件的LEA较慢或scale > 1(http://agner.org/optimize /)
但NASM和YASM将通过使用[1 + rax + rax*1]第二个LEA 来优化代码大小,第二个LEA只需要disp8而不是disp32.(寻址模式始终具有基址寄存器或disp32).
即他们总是分裂reg*2成base+index,因为对于代码大小来说,这永远不会更糟.
我可以强制使用disp32 lea eax, [dword 1 + rax*2],但这并不能阻止NASM或YASM分割寻址模式.在NASM手动似乎并没有记录的方式来使用的strict关键字上规模的因素,并[1 + …
我想知道是否可以反向解码x86-64指令?
我需要这个用于运行时解析器.用户可以指向内存中的随机位置,然后应该能够向上滚动并查看指定地址之前的指令.
我想通过反向解码来做到这一点.
尽管单词的常见定义(如维基百科所述)是:
用于指定存储器中的位置的最大可能地址大小通常是硬件字(这里,“硬件字”是指处理器的全尺寸自然字,而不是使用的任何其他定义)。
根据一些消息来源,x86 系统注意到它被视为 16 位:
在 x86 PC(Intel、AMD 等)中,虽然架构很早就支持 32 位和 64 位寄存器,但其本机字大小可以追溯到 16 位起源,“单个”字为 16 位。“双”字是 32 位。请参阅 32 位计算机和 64 位计算机。
然而英特尔的官方文档(sdm 第 2 卷,第 1.3.1 节)指出:
这意味着字的字节从最低有效字节开始编号。图 1-1 说明了这些约定。
图 1-1 显示了 x86-64 上下文中单词的小端序列中的 4 个字节,而不是 2 个字节或 8 个字节(如上面链接的来源的不同定义所建议的那样):
我对这一切真正感到困惑的是如何获取和解析指令。我正在编写一个模拟器,一旦我解析 PE 格式的可执行文件并进入文本部分,如果我要遵循 4 字节小端格式,这是否意味着将首先解析第 4 个字节?
让我们组成一些字节,例如:
.text segment buffer:
< 0x10, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20 > ....
Run Code Online (Sandbox Code Playgroud)
我会将第一条指令解析为 1C、1B、1A、10、20、1F、1E、1D ...(等等,由于长度可变,显然可能有更多的单词需要读取,具体取决于这里的实际字节是什么)?
machine-code ×10
assembly ×7
x86 ×4
disassembly ×2
jit ×2
performance ×2
x86-64 ×2
c ×1
c++ ×1
cpu ×1
cpu-word ×1
endianness ×1
intel ×1
interpreter ×1
linker ×1
nasm ×1
object ×1
opcode ×1
popularity ×1