C 将加载的目标地址保留在寄存器中,直到指令失效
我想使用精确的基于事件的采样(PEBS)在XeonE5 Sandy桥上记录特定事件(例如缓存未命中)的所有地址 但是,《CoreTM i7处理器和英特尔至强5500处理器性能分析指南》第24页包含以下警告: 由于PEBS机制在以下位置捕获寄存器的值: 完成指令后,该指令的解引用地址 无法使用以下类型的加载指令(英特尔asm约定) 重建。C 将加载的目标地址保留在寄存器中,直到指令失效,c,assembly,intel,cpu-registers,performancecounter,C,Assembly,Intel,Cpu Registers,Performancecounter,我想使用精确的基于事件的采样(PEBS)在XeonE5 Sandy桥上记录特定事件(例如缓存未命中)的所有地址 但是,《CoreTM i7处理器和英特尔至强5500处理器性能分析指南》第24页包含以下警告: 由于PEBS机制在以下位置捕获寄存器的值: 完成指令后,该指令的解引用地址 无法使用以下类型的加载指令(英特尔asm约定) 重建。MOV-RAX,[RAX+const]这种 指令主要与指针跟踪相关 mystruc=mystruc->next 这是该方法的一个显著缺点 获取内存指令地址的方法
MOV-RAX,[RAX+const]
这种 指令主要与指针跟踪相关
mystruc=mystruc->next代码>
这是该方法的一个显著缺点
获取内存指令地址的方法
根据objdump,我的程序中有许多这种形式的加载指令有什么办法可以避免这些吗?
由于这是一个英特尔特有的问题,因此解决方案不必以任何方式进行移植,只需工作即可。我的代码是用C编写的,我正在寻找一个理想的编译器级解决方案(gcc或icc),但欢迎任何建议
一些例子:
mov 0x18(%rdi),%rdi
mov (%rcx,%rax,8),%rax
在这两种情况下,在指令失效后(因此当我查看寄存器值以确定加载到/从何处加载时),地址的值(在这些示例中分别为%rdi+18
和%rcx+8*%rax
)被mov
的结果覆盖。我现在能想到的唯一方法是使用&(与符号)汇编程序约束。这意味着,无论在哪里出现这样的指令,我都必须遍历我的代码,并替换掉所有取消引用mystruc=mystruc->next的指针代码>类似于:
asm volatile(“mov(%1),%0):“=&r”(mystruc):“r”(&(mystruc->next))
然而,这是一种非常普通的方法,并且可能存在比结构中的指针更复杂的情况。我知道这基本上增加了寄存器压力,因此编译器正在积极尝试避免这种情况,我仍在寻找其他方法来做到这一点。您要做的是转换表单的所有指令:
mov (%rcx,%rax,8),%rax
进入:
通过修改编译器生成的汇编程序源代码,可以更轻松地完成这项工作。下面是一个perl
脚本,它将通过读取和修改.s
文件来执行所有必要的转换
只需将构建更改为生成.s
文件而不是.o
文件,应用脚本,然后使用as
或gcc
生成.o
这是实际的脚本。我已经在我自己的一些源代码上测试了它,遵循下面评论中的构建过程
该脚本具有以下功能:
扫描并定位所有函数定义
标识给定函数中使用的所有寄存器
查找函数的所有返回点
根据函数的寄存器用法选择要使用的临时寄存器(即,它将使用函数尚未使用的临时寄存器)
用双指令序列替换所有“麻烦”指令
在尝试使用被调用方保存的寄存器之前,尝试使用未使用的临时寄存器(例如,%r11
或未使用的参数寄存器)
如果所选寄存器被调用者保存,将添加push
到函数prolog和pop
到函数[multiple]ret
语句
维护所有分析和转换的日志,并将其作为注释附加到输出.s
文件中
#/usr/bin/perl
#pebsfix/pebsfixup——修复PEBS使用的汇编程序源代码
#
#命令行选项:
#“-a”--仅使用完整的64位目标
#“-l”--不要使用lea
#“-D[diff file]”--显示差异(默认输出:“./diff”)
#“-n10”--不要将寄存器%r10用作临时寄存器(默认为使用它)
#“-o”--覆盖输入文件(可以是多个)
#“-O”--输出文件(只允许一个.s输入)
#“-q”--抑制警告
#“-T[lvl]”--调试跟踪
#
#“-o”和“-o”是互斥的
#
#命令行脚本测试选项:
#“-N[TPA]”--禁用临时寄存器类型[用于测试]
#“-P”--强制推/弹出所有功能
#
#命令行参数:
#1--要处理的.s文件列表[或要搜索的目录]
#对于给定的文件“foo.s”,输出为“foo.TMP”
#如果给出(-o),则将“foo.TMP”重命名为“foo.s”)
#
#建议用法:
#更改生成以生成.s文件
#发件人:
#cc[选项]-c foo.c
#致:
#cc[options]-c-S foo.c
#pebsfixup-o foo.s
#cc-cfoo.s
#
#建议的编译器选项:
#[可能只有在需要push/pop时才真正需要。使用-NP进行验证]
#(1)使用以下任何一项:
#-O2-fno优化同级呼叫
#-O1
#(2)使用-mno省略叶帧指针
#(3)使用-mno红色区域[在任何情况下可能不需要]
#
#注:
#(1)红色区域仅对叶函数(即如果fncA调用
#fncB,fncA的红色区域将被摧毁)
#(2)如果存在正式的堆栈框架,则推送到堆栈上不是问题
#(3)如果函数的参数不超过六个(即。
#不使用%rsp的正偏移量访问它们)
#布拉格语
使用严格的qw(vars接头);
我们的$pgmtail;
我们的$opt_a;
我们的$opt\T;
我们的$opt_D;
我们的$opt_l;
我们的$opt_n10;
我们的$optn;
我们的$opt_P;
我们的$opt_q;
我们的$opt_o;
我们的$opt_O;
我们的$opt_;
我们的@reguse;
我们的%reguse_tobase;
我们的%reguse_isbase;
我们的$regusergx;
我们的@regtmplist;
我们的%regtmp_类型;
我们的$diff;
我们的$sepflg;
我们的美元是致命的;
我们的@cmtprt;
船长(@ARGV);
出口(0);
#主控——主控
副船长
{
我的(@argv)=;
我的($xfsrc);
我的($file,@files);
我的($bf);
mov (%rcx,%rax,8),%r11
mov %r11,%rax
# function_original -- original function before pebsfixup
# RETURNS: 23
function_original:
mov $23,-4(%rsp) # red zone code generated by compiler
...
mov -4(%rsp),%rax # will still have $23
ret
# function_pebsfixup -- pebsfixup modified
# RETURNS: 23
function_pebsfixup:
push %r12 # pebsfixup injected
mov $23,-4(%rsp) # red zone code generated by compiler
...
mov -4(%rsp),%rax # will still have $23
pop %r12 # pebsfixup injected
ret
# function_inline -- function with inline asm block and red zone
# RETURNS: unknown value
function_inline:
mov $23,-4(%rsp) # red zone code generated by compiler
# inline asm block -- steps on red zone
push %rdx
push %rcx
...
pop %rcx
pop %rdx
...
mov -4(%rsp),%rax # now -4(%rsp) no longer has $23
ret
mov 32(%rsp),%rax
mov 40(%rsp),%rax