X86-64寄存器和栈帧

X86-64寄存器和栈帧
X86-64寄存器和栈帧

寄存器分配

寄存器分配,是通过将程序变量尽可能地分配到寄存器,从而提高程序执行速度的一种方法。寄存器是编译器优化中最为重要的问题之一(好的寄存器分配能够提高程序执行速度超过250%);也是编译器理论中最热点的研究领域之一(研究界已经提出来大量寄存器分配相关的算法)。 1. 图着色(graph coloring)方法是解决寄存器分配问题最常用的方法。 利用相交图(interference graph)来表示程序变量的生命期是否相交,将寄存器分配给变量的问题,可以近似地看成是给相交图着色:相交图中,相交的节点不能着同一颜色;每一种颜色对应一个寄存器。Chaitin等人最早提出了基于图着色的寄存器分配方法其着色思路采用了Kempe的着色方法,即,任意一个邻居节点数目少于k的节点,都能够被k着色。判断一个图是否能够被k(k>=3)种颜色着色,即k着色问题,被Karp证明是一个NP-complete问题。 但是,寄存器分配不仅仅是图着色的问题。当寄存器数目不足以分配某些变量时,就必须将这些变量溢出到内存中,该过程成为spill。最小化溢出代价的问题,也是一个NP-complete问题。如果简化该问题——假设所有溢出代价相等,那么最小化溢出代价的问题,等价于k 着色问题,仍然是NP-complete问题。 此外,如果两个变量的生命期仅仅因为出现在同一个拷贝指令中而相邻,那么,通过将这两个变量分配到同一个寄存器,就可以消除该

拷贝指令,成为coalescing。这个方向的努力在Chaitin的文章以后的1/4个世纪,成为推动寄存器分配的主要动力之一,涌现出了包括aggressive coalescing,conservative coalescing和optimistic coalescing。但是,将两个变量分配到同一个寄存器,等价于将这两个变量合并成同一个变量,生命期合并,因而会加剧相交图的聚簇现象,降低相交图的可着色性。Bouchez等人证明了目前的coalescing问题都是NP-complete的。 为了降低相交图的聚簇现象,提高相交图的可着色性,可以通过将变量拷贝给一个临时变量,并将以后对该变量的使用替换成对该临时变量的使用,从而将一个变量的生命期分解成两个变量的生命期,称为live range splitting。显然,这是一个与coalescing的作用相反的过程。Bouchez等人考虑了该方法的复杂度。 此外,寄存器分配还需要考虑寄存器别名(aliasing)和预着色(pre-coloring)的问题。寄存器别名是指,在某些体系结构中,一个寄存器的赋值可能会影响到另外一个寄存器。比如,在x86中,对AX寄存器的赋值,会影响AL和AH寄存器。预着色是指,某些变量必须被分配到特定的寄存器。比如,许多体系结构会采用特定寄存器来传递函数参数。 George和Appel发展了Chaitin的算法,更好地考虑了coalescing过程和赋值过程,以及各过程之间的迭代,在基于图着色的寄存器分配方法中具有广泛的影响。 3. 线性扫描算法

高中数学 含绝对值的函数图象的画法及其应用素材

含绝对值的函数图象的画法及其应用 一、三点作图法 三点作图法是画函数)0(||≠++=ak c b ax k y 的图象的一种简捷方法(该函数图形形状似“V ”,故称V 型图)。 步骤是:①先画出V 型图顶点?? ? ?? - c a b ,; ②在顶点两侧各找出一点; ③以顶点为端点分别与另两个点画两条射线,就得到函数)0(||≠++=ak c b ax k y 的图象。 例1. 作出下列各函数的图象。 (1)1|12|--=x y ;(2)|12|1+-=x y 。 解:(1)顶点?? ? ??-12 1 ,,两点(0,0) ,(1,0)。其图象如图1所示。 图1 (2)顶点?? ? ?? - 121 ,,两点(-1,0) ,(0,0)。其图象如图2所示。 图2 注:当k>0时图象开口向上,当k<0时图象开口向下。函数图象关于直线a b x -=对称。 二、翻转作图法 翻转作图法是画函数|)(|x f y =的图象的一种简捷方法。 步骤是:①先作出)(x f y =的图象;②若)(x f y =的图象不位于x 轴下方,则函数 )(x f y =的图象就是函数|)(|x f y =的图象; ③若函数)(x f y =的图象有位于x 轴下方的,则可把x 轴下方的图象绕x 轴翻转180°到x 轴上方,就得到了函数|)(|x f y =的图象。 例2. 作出下列各函数的图象。 (1)|1|||-=x y ;(2)|32|2 --=x x y ;(3)|)3lg(|+=x y 。 解:(1)先作出1||-=x y 的图象,如图3,把图3中x 轴下方的图象翻上去,得到图4。图4就是要画的函数图象。 图3 图4

linux C用户态调试追踪函数调用堆栈以及定位段错误

linux C用户态调试追踪函数调用堆栈以及定位段错误 一般察看函数运行时堆栈的方法是使用GDB(bt命令)之类的外部调试器,但是,有些时候为了分析程序的BUG,(主要针对长时间运行程序的分析),在程序出错时打印出函数的调用堆栈是非常有用的。 在glibc头文件"execinfo.h"中声明了三个函数用于获取当前线程的函数调用堆栈。 int backtrace(void **buffer,int size) 该函数用于获取当前线程的调用堆栈,获取的信息将会被存放在buffer中,它是一个指针列表。参数size 用来指定buffer中可以保存多少个void* 元素。函数返回值是实际获取的指针个数,最大不超过size大小 在buffer中的指针实际是从堆栈中获取的返回地址,每一个堆栈框架有一个返回地址 注意:某些编译器的优化选项对获取正确的调用堆栈有干扰,另外内联函数没有堆栈框架;删除框架指针也会导致无法正确解析堆栈内容 char ** backtrace_symbols (void *const *buffer, int size) backtrace_symbols将从backtrace函数获取的信息转化为一个字符串数组. 参数buffer应该是从backtrace函数获取的指针数组,size是该数组中的元素个数(backtrace的返回值) 函数返回值是一个指向字符串数组的指针,它的大小同buffer相同.每个字符串包含了一个相对于buffer中对应元素的可打印信息.它包括函数名,函数的偏移地址,和实际的返回地址 现在,只有使用ELF二进制格式的程序才能获取函数名称和偏移地址.在其他系统,只有16进制的返回地址能被获取.另外,你可能需要传递相应的符号给链接器,以能支持函数名功能(比如,在使用GNU ld链接器的系统中,你需要传递(-rdynamic),-rdynamic可用来通知链接器将所有符号添加到动态符号表中,如果你的链接器支持-rdynamic的话,建议将其加上!) 该函数的返回值是通过malloc函数申请的空间,因此调用者必须使用free函数来释放指针. 注意:如果不能为字符串获取足够的空间函数的返回值将会为NULL void backtrace_symbols_fd (void *const *buffer, int size, int fd)

图染色法的寄存器分配

图染色法的寄存器分配 计算机科学与技术系98级吴汉唐

摘要 本文讲述了寄存器分配的图染色法理论。 Chaitin 和他的同伴,在IBM 的Yorktown Heights研究中心,实现了第一个基于图染色法的全局的寄存器分配器。后来,Rice 大学的Preston Briggs 对Chaitin的算法进行了改进和扩展。 Chaitin的算法悲观地假设任何高度数的结点都不能被染色,只能被抛出(spilled)。Briggs的算法乐观地认为高度数的结点有可能被染色,从而获得更低的抛出开销(spill costs),和更快的代码。 一介绍 (一)编译器和优化 一个优化编译器分为三个层次: ●前端把源语言转换为中间代码。这个转换依赖于源语言的结构,需要几 遍扫描(pass)代码才能完成。编译时的错误检查就在这一层。我们可 以理想地认为前端是只与语言有关的、与机器无关的。 ●优化器包含几遍的扫描(pass),每一遍扫描对中间代码执行特定的转 换。我们感兴趣的是全局优化,就是从整个函数搜集有用信息,再去做 优化。一般的全局优化包括强度削减(strength reduction),循环不变 量移动(loop-invariant code motion)和公共子表达式消除(common subexpression elimination)。优化器最好是与语言和机器都无关的。 ●后端把中间代码转换为针对特定机器的目标代码。这个转换过程又称为 代码生成,也需要几遍扫描才能完成。这几遍扫描包括指令选择 (instruction selection),指令调度(instruction scheduling), 寄存器分配(register allocation)。后端在很大程度上是与语言无关 的,与机器有关的。 这个划分简化了每一个层次的开发和维护,使得在不同的编译器中复用每个层次成为可能。例如,一个完全与机器无关的FORTRAN语言前端可以用到为不同机器设计的编译器中。 当然,这还是一个理想的观点。在实际中,每个层次都表现出既与语言相关又与机器相关。这些相关限制复用和维护,也成为编译器设计人员努力要克服的难关。 (二)优化和寄存器分配 寄存器是位于CPU内部的少量的高速存储器。寄存器与内存有很大的不同: ●寄存器很少。一个寄存器可以用几个比特直接定位。内存空间很大。内 存的定位一般是通过间接的“寻址方式”,其中可能包含一个或多个对寄

用计算机绘制函数图像

用计算机绘制函数图像 利用计算机软件可以便捷、迅速地绘制各种函数图像。不同的计算机软件绘制函数图像的具体操作不尽相同,但都是基于我们熟悉的描点作图。即给子变量赋值,用计算法则算出相应的函数值,再由这些对应值生成一系列的点,最后连接这些点描绘出函数图像。下面以Excel 和《几何画板》为例,介绍用计算机软件作函数图像的方法。 1.用“Excel ”绘制函数3 y x =的图像 (1) 打开Excel ,在A 列输入自变量x 的值; (2) 把光标移到B 列,在编辑框输入计算法则“=POWER (A :A ,3)”,回车,在B 列 生成相应的函数值,如图1所示; (3) 选中数据区域A 、B 列,执行“插入→图表”命令,在“图表类型”中选择“XY 散点”,根据需要在“子图表类型”中选择其一。然后按照对话框中的提示,完成制图操作,就可得到如图2所示的函数3y x =的图像。 图1 图2 2.用《几何画板》绘制函数2(0)y bx b =≠的图像 (1) 打开几何画板,通过执行“构造/平行线”和“构造/线段”,生成平行于x 轴的 线段AB ,将A 固定于y 轴,B 为动点,选中B 点,执行“度量/横坐标”选项,画板上显示的点B 的横坐标B x 就是参数b 的值。 (2) 执行“图表/新建函数”,在对话框内输入函数表达式“*^2B x x ”,执行“图表 /绘制新函数”,即生成函数图像,如图3。

图3 图4 当你左右移动B 点的位置时,函数2(0)y bx b =≠就会“动”起来,如图4,如果有条件,请你绘制函数2(0)y ax bx c a =++≠的图像,并探究系数a 、b 、c 对函数图象的影响。

分簇结构向量寄存器分配策略研究

10 M i c r o c o n t r o l l e r s&E m b e d d e dS y s t e m s 2017年第7期w w w .m e s n e t .c o m .c n 分簇结构向量寄存器分配策略研究* 王向前,王昊 (中国电子科技集团公司第三十八研究所,合肥230088 )*基金项目: 国家 核心电子器件二高端通用芯片及基础软件产品 重大专项 面向先进雷达的高性能D S P (N o .2012Z X 010******** )资助三摘要:通过分簇结构实现向量化执行是一种高效而灵活的体系结构选择三在编译中间表示里,向量指令与标量指令交叠出现三分簇结构向量化实现的特殊方式给传统的寄存器分配框架带来了挑战三针对该问题,本文从向量指令的表示形式二C a l l e e /C a l l e r 寄存器划分二向量寄存器分配等进行研究,并给出全局与局部向量寄存器的分配方法三关键词:分簇结构;向量寄存器分配;C a l l e e /C a l l e r 中图分类号:T P 314 文献标识码:A R e s e a r c ho nV e c t o r R e g i s t e r A l l o c a t i o nM e t h o d f o rC l u s t e r i n g W a n g X i a n g q i a n ,W a n g H a o (N o .38R e s e a r c h I n s t i t u d e ,C h i n aE l e c t r o n i c sT e c h n o l o g y G r o u p C o r p o r a t i o n ,H e f e i 230088,C h i n a )A b s t r a c t :I t i s a n e f f i c i e n t a n d f l e x i b l e a r c h i t e c t u r e c h o i c e t o r e a l i z e S I M De x e c u t i o n t h r o u g h c l u s t e r i n g s t r u c t u r e .A n d t h e n t h e v e c t o r i n -s t r u c t i o n s p r o b a b l y c o m eo u t t o g e t h e rw i t ht h es c a l a r i n s t r u c t i o n s i nt h e i n t e r m e d i a t er e p r e s e n t a t i o no f c o m p i l e r .T h es p e c i a lw a y f o r S I M D i m p e m e n t a t i o no n c l u s t e r i n g s t r u c t u r eb r i n g s c h a l l e n g e s t o t h e t r a d i t i o n a l r e g i s t e r a l l o c a t i o n f r a m e w o r k .T os o l v e t h e p r o b l e m ,i n t h e p a p e r ,t h e r e p r e s e n t a t i o n s t y l e o f t h e v e c t o r i n s t r u c t i o n s ,t h e p a r t i t i o n o f C a l l e e /C a l l e r r e g i s t e r ,t h e v e c t o r r e g i s t e r a l l o c a t i o n a r e s t u d -i e d ,a n d t h e g l o b a l a n d l o c a l v e c t o r r e g i s t e r a l l o c a t i o nm e t h o d s a r e p r o p o s e d .K e y w o r d s :c l u s t i n g s t r u c t u r e ;v e c t o r r e g i s t e r a l l o c a t i o n ;C a l l e e /C a l l e r 引 言 分簇结构[1-2] 是一种可以有效增强体系结构并行性而 不会引起昂贵硬件代价的体系结构三分簇结构上向量化机制实现较为特殊,通过多个簇的组合实现高效而灵活的向量化执行三 本文针对分簇结构上向量化实现的特殊本质,研究向量化的表示形式以及在传统寄存器分配框架上实现分簇结构向量寄存器分配的方法三 1 向量指令编译后端表示 编译器后端中间语言一般为一种基于目标机指令的中间语言表示,在进入代码生成阶段时由高级中间语言生成三指令注释二寄存器分配二指令调度都运行在该中间语言上三后端层次结构通过控制流结构表示;控制流结构由若干基本块组成;基本块包括若干操作指令;操作指令主要由操作码二源操作数二目的操作数组成三 分簇结构上向量指令是通过多簇组合支持的,并不像 传统的向量指令,如表1所列,分簇结构分为x 二y 二z 二t 四个簇,每个簇相同物理编号的寄存器组成向量寄存器三例如向量寄存器x y z t R 7表示128位向量寄存器,其中分别由x 簇上的R 7寄存器二y 簇上R 7寄存器二z 簇上R 7寄存器二t 簇上R 7寄存器组合而成;而表1中向量寄存器x y z -t R 7:6则为256位向量寄存器,其中分别由x 簇上的R 7 /R 6寄存器二y 簇上R 7/R 6寄存器二z 簇上R 7/R 6寄存器二t 簇上R 7/R 6寄存器组合而成三这种向量指令的源操作数和目的操作数实际上是通过x 二y 二z 二t 四个簇上的寄存器文件组合而成,和传统的单个向量寄存器有本质区别三基于此,向量指令的后端表示有两个思路:一是分布表示方法,二是整体表示法三如表2所列,表中分簇结构也为x 二y 二z 二t 四个簇三表1 分簇结构上向量指令举例 指 令 含 义 x y z t R 7:6=[U m+U n ,1]从内存起始地址U m+U n 依次读取8 个数据 x y z t R 9:8=R 7:6+R 5:4四个簇上同时执行R 9:8=R 7:6+R 5:4 [U m+U n ,1]=x y z t R 9:8往内存起始地址U m+U n 依次写入8个数据 万方数据

0608初二数学(人教版)-画函数的图象-1教案

教案

如何画函数的图象? 问题: 正方形的面积y是边长x的函数,请画出这个函数的图象. 1.思考: (1)这个函数的解析式是什么? (2)这个函数的自变量取值范围是什么? (3)怎样获得组成图象的点? 2 (4)怎样确定满足函数y= x(x>0)的点的坐标? (5)自变量x的一个确定的值与它所对应的函数值y,是否唯一确定一个点(x,y)呢? 2.描点法画函数的图象. (1)结合函数的图象的意义研究画法. (2)描点法画函数的图象. ①探究画法:

②归纳步骤:第一步,列表;第二步,描点;第三步,连线.

可能出现的错误:1.选自变量的值不合理,2.连线 不能用平滑曲线连接. 怎样判断一个点是否在函数的图象上? 例2 (1)判断下列各点是否在函数y =x +0.5 的图象上? ① (-5,-4.5); ②(4,-3.5) . (2)判断下列各点是否在函数 的图象上? ①(12,0.5);② (-4.5,-1) . 解:(1)∵x =-5时,y = -5 +0.5= -4.5, ∴ 点(-5,-4.5)在函数 y =x +0.5的图象上. ∵x = 4时,y = 4+0.5= 4.5 ≠- 3.5. ∴点(4,-3.5)不在函数y =x +0.5的图象上. (2)∵x =12时, =0.5. ∴ 点(12,0.5)在函数 的图象上. ∵x = -4.5时, ≠ -1 , 6 y 12= 6y x = 6y x = 6y = 643y ==— —4.5

练习2 (1)画出函数 y= x 的图象; (2)判断点A (- 2.5, - 4),B (- 1.6,2.56) 是否在函数 y= x 的图象上. 解:∵点A(-2.5,-4)在第三象限, 函数y= x 的图象不经过第三象限, ∴点A(-2.5,-4),不在函 数y= x 的图象上. ∵x = -1.6时,y = =2.56, ∴B (-1.6,2.56)在函数y= x 的图象上. 2 2-(1.6) 2 2 22

C语言函数参数入栈的理解分析

先来看这样一段程序: [cpp]view plain copy print? 1.#include 2.#include 3.#include 4. 5.void print1(int a,int b,int c) 6.{ 7. printf("%p\n",&a); 8. printf("%p\n",&b); 9. printf("%p\n",&c); 10.} 11. 12.int main(void) 13.{ 14. print1(1,2,3); 15. exit(0); 16.} 它的输出是: [cpp]view plain copy print? 1.0022FF40 2.0022FF44 3.0022FF48 发现a,b,c的地址是逐渐增大的,差值是4个字节。这和我所知道的:C函数参数入栈的顺序是从右到左是相匹配的,而且地址的增大值也 与变量所占的字节数相匹配。 不过当把程序稍微做一下修改,如下: [cpp]view plain copy print? 1.#include

2.#include 3.#include 4. 5.void print2(char a,char b,char c) 6.{ 7. printf("%p\n",&a); 8. printf("%p\n",&b); 9. printf("%p\n",&c); 10.} 11. 12.int main(void) 13.{ 14. print2(1,2,3); 15. exit(0); 16.} 再观察一下它的输出: [cpp]view plain copy print? 1.0022FF2C 2.0022FF28 3.0022FF24 怎么和上面的效果是相反的!虽然我知道这肯定编译器的一个技巧,不过参数入栈的顺序是从右到左的概念却动摇了。 为了弄清楚其中的道理,必须观察程序生成的中间.s文件,为此,我执行了以下一条命令: [cpp]view plain copy print? 1.gcc -S test.c(当前C文件中保存的程序是文章一开始的那个) 在当前目录下生成test.s 文件 使用vim打开test.s文件(只截取主要内容了): esp是指向栈顶的指针,ebp是用来备份这个指针的。栈的形状如下: esp

51单片机寄存器分配表

51 单片机寄存器分配表寄存器一般使用格式30H~7FH 20H~28H 18H~1FH 10H~17H 08H~0FH 00H~07H 程序状态字PSW (D0H)D7 CY D6 AC D5 F0 D4 RS1 D3 RS0 一般数据或堆栈使用区针对固定地址的区域寄存器组 3 寄存器组 2 寄存器组 1 寄存器组0 D2 OV D1 — D0 P P位:奇偶位;P=0 表示A 中 1 的个数是偶数,P=1 表示A 中1 的个数是奇数。OV 位:益出位;OV=1 表示运算时有益出产生。RS0,RS1:寄存器组选择位。选择的寄存器组RS1 RS0 0 0 寄存器组0 0 1 寄存器组1 1 0 寄存器组2 1 1 寄存器组3 F0 位:用户自行设置位。AC 位:补助进位位;AC=1 表示运算时较低4 位有进位产生。CY 位:进位位;CY=1 表示运算时有进位产生。中断允许寄存器IE (A8H)D7 D6 D5 D4 D3 D2 D1 D0 — ET2 ES ET1 EX1 ET0 EX0 EA :整体中断允许位;EA=1 允许中断。ET2 :T2 中断允许位;ET2=1 允许中断(S52 才有)。ES :串行中断允许位;ES=1 允许中断。ET1 :T1 中断允许位;ET1=1 允许中断。EX1 :INT1 中断允许位;EX1=1 允许中断。ET0 :T0 中断允许位;ET0=1 允许中断。EX0 :INT0 中断允许位;EX0=1 允许中断。入口地址(按优先级)外中断0—03H,定时器0—0BH,外中断1—13H,定时器1—1BH,串口—23H :中断优先次序寄存器IP (B8H)EA D7 D6 D5 D4 D3 D2 D1 D0 —— PT2 PS PT1 PX1 PT0 PT2:T2 PS:串行口PT1:T1 PX1:INT1 PT0:T0 PX0:INT0 计时器计数器寄存器TL0 (8AH),TH0 (8CH),TL1 (8BH),TH1 (8DH)D7 D6 D5 D4 D3 D2 D1 D0 D7 D6 D5 D4 D3 D2 PX0 D1 D0 32768 16384 8192 4096 2048 1024 512 256 128 64 32 16 8 → TH0(1)← → TL0(1)同过设订两个寄存器中每位代表的数值来决定定时值和计数值。例:TH=#3CH ,TL=#0B0H 等于15536,它的定时值就为50000。计时器模式控制寄存器TMOD (89H)D7 D6 D5 D4 D3 D2 D1 4 2 1 ← D0 GA TE → C/T 计时器1 M1 M0 ← GA TE → C/T 计时器0 M1 M0 ←

程序调试技巧之调用堆栈

调试技巧之调用堆栈 在计算机科学中,Callstack是指存放某个程序的正在运行的函数的信息的栈。Call stack由stack frames组成,每个stack frame对应于一个未完成运行的函数。 在当今流行的计算机体系架构中,大部分计算机的参数传递,局部变量的分配和释放都是通过操纵程序栈来实现的。栈用来传递函数参数,存储返回值信息,保存寄存器以供恢复调用前处理机状态。每次调用一个函数,都要为该次调用的函数实例分配栈空间。为单个函数分配的那部分栈空间就叫做stack frame,也就是说,stack frame这个说法主要是为了描述函数调用关系的。 Stackframe组织方式的重要性和作用体现在两个方面:第一,它使调用者和被调用者达成某种约定。这个约定定义了函数调用时函数参数的传递方式,函数返回值的返回方式,寄存器如何在调用者和被调用者之间进行共享;第二,它定义了被调用者如何使用它自己的stack frame来完成局部变量的存储和使用。 简单介绍 调试是程序开发者必备技巧。如果不会调试,自己写的程序一旦出问题,往往无从下手。本人总结10年使用VC经验,对调试技巧做一个粗浅的介绍。希望对大家有所帮助。 今天简单的介绍介绍调用堆栈。调用堆栈在我的专栏的文章VC调试入门提了一下,但是没有详细介绍。 首先介绍一下什么叫调用堆栈:假设我们有几个函数,分别是function1,function2,function3,funtion4,且function1调用function2,function2调用function3,function3调用function4。在function4运行过程中,我们可以从线程当前堆栈中了解到调用他的那几个函数分别是谁。把函数的顺序关系看,function4、function3、function2、function1呈现出一种“堆栈”的特征,最后被调用的函数出现在最上方。因此称呼这种关系为调用堆栈(callstack)。 当故障发生时,如果程序被中断,我们基本上只可以看到最后出错的函数。利用call stack,我们可以知道当出错函数被谁调用的时候出错。这样一层层的看上去,有时可以猜测出错误的原因。常见的这种中断时ASSERT宏导致的中断。 在程序被中断时,debug工具条的右侧倒数第二个按钮一般是callstack按钮,这个按钮被按下后,你就可以看到当前的调用堆栈。

C语言函数调用栈

C语言函数调用栈(一) 程序的执行过程可看作连续的函数调用。当一个函数执行完毕时,程序要回到调用指令的下一条指令(紧接call指令)处继续执行。函数调用过程通常使用堆栈实现,每个用户态进程对 应一个调用栈结构(call stack)。编译器使用堆栈传递函数参数、保存返回地址、临时保存寄 存器原有值(即函数调用的上下文)以备恢复以及存储本地局部变量。 不同处理器和编译器的堆栈布局、函数调用方法都可能不同,但堆栈的基本概念是一样 的。 1 寄存器分配 寄存器是处理器加工数据或运行程序的重要载体,用于存放程序执行中用到的数据和指令。因此函数调用栈的实现与处理器寄存器组密切相关。 Intel 32位体系结构(简称IA32)处理器包含8个四字节寄存器,如下图所示: 图1 IA32处理器寄存器 最初的8086中寄存器是16位,每个都有特殊用途,寄存器名城反映其不同用途。由于 IA32平台采用平面寻址模式,对特殊寄存器的需求大大降低,但由于历史原因,这些寄存 器名称被保留下来。在大多数情况下,上图所示的前6个寄存器均可作为通用寄存器使用。某些指令可能以固定的寄存器作为源寄存器或目的寄存器,如一些特殊的算术操作指令 imull/mull/cltd/idivl/divl要求一个参数必须在%eax中,其运算结果存放在%edx(higher 32-bit)和%eax (lower32-bit)中;又如函数返回值通常保存在%eax中,等等。为避免兼容性问题,ABI规范对这组通用寄存器的具体作用加以定义(如图中所示)。 对于寄存器%eax、%ebx、%ecx和%edx,各自可作为两个独立的16位寄存器使用,而低16位寄存器还可继续分为两个独立的8位寄存器使用。编译器会根据操作数大小选择合 适的寄存器来生成汇编代码。在汇编语言层面,这组通用寄存器以%e(AT&T语法)或直接以e(Intel语法)开头来引用,例如mov $5, %eax或mov eax, 5表示将立即数5赋值给寄存器%eax。 在x86处理器中,EIP(Instruction Pointer)是指令寄存器,指向处理器下条等待执行的指

C语言函数调用参数传递栈详解

C语言调用函数过程详解 Sunny.man 1.使用环境: gcc 版本4.1.2 20071124 (Red Hat 4.1.2-42) 2.示例源代码 int foo(int a,int b) { int a1=0x123; return a1+a+b; } int main() { foo(2,3); return 0; } 3.运行程序 命令:gdb a.out Start Disassemble 4.汇编函数清单 4.1main函数的汇编 0x0804836c : lea 0x4(%esp),%ecx 0x08048370 : and $0xfffffff0,%esp 0x08048373 : pushl 0xfffffffc(%ecx) 0x08048376 : push %ebp 0x08048377 : mov %esp,%ebp 0x08048379 : push %ecx 0x0804837a : sub $0x8,%esp 0x0804837d : movl $0x3,0x4(%esp) 0x08048385 : movl $0x2,(%esp) 0x0804838c : call 0x8048354 0x08048391 : mov $0x0,%eax 0x08048396 : add $0x8,%esp 0x08048399 : pop %ecx 0x0804839a : pop %ebp 0x0804839b : lea 0xfffffffc(%ecx),%esp 0x0804839e : ret

绘制函数图象的五种技法

绘制函数图象的五种技法 如今的社会真的是靠脸吃饭的么?小编我却不以为然,还是觉得靠技术吃饭比较重要,技术不压身!现代教学是多媒体教学,那就离不开教学软件的支撑,几何画板就是其中之一。在用几何画板辅助数学教学的过程中,常常涉及到函数图象的绘制。熟练掌握绘制函数图象的方法,对提高数学教学效率很有帮助。下面小编通过实例来系统总结绘制函数图象的五种技法,如果你get以下几个新技能,离超级学霸就不远啦! 一、直接法 例1 画函数y=sinx在R上的图象。 操作步骤:单击“图表”菜单下“绘制新函数”f(x)=sinx(如图1)。 二、轨迹法 例2 画函数y=(1/4)x^2在区间[-2,3]上的图象。 操作步骤: (1)单击“绘图”菜单下“绘制点”C(-2,0),D(3,0),构造线段CD;

(2)选中线段CD,单击“构造”菜单下“线段上的点”构造点E; (3)选中点E,单击“度量”菜单下“横坐标”得点E的横坐标xE; (4)单击“数据”菜单下“计算”,计算y值; (5)依次选中xE、y值,单击“绘图”菜单下“绘制(x,y)”,得点F; (6)选中点E与F,单击“构造”菜单下“轨迹”,得函数在区间[-2,3]的图象(如图2)。 三、参数法 例3 绘制二次函数y=-x2+2x+3的图象。 操作步骤: (1)单击“数据”菜单下“新建参数”a=-1,b=2,c=3; (2)单击“绘图”菜单下“绘制新函数”f(x)= =-x2+2x+3(如图3)。

改变参数a、b、c的值(可在选中后按“+”或“-”键),可以动态地探索与发现抛物线的开口方向、顶点坐标和对称轴的变化过程. 四、辅助函数法 例4画下面函数的图象。 操作步骤: (1)单击“数据”菜单下“新建函数”f(x)=sinx,g(x)=cosx; (2)单击“绘图”菜单下“绘制新函数”。(如图4)

C语言函数调用规定

在C语言中,假设我们有这样的一个函数: int function(int a,int b) 调用时只要用result = function(1,2)这样的方式就可以使用这个函数。但是,当高级语言被编译成计算机可以识别的机器码时,有一个问题就凸现出来:在CPU中,计算机没有办法知道一个函数调用需要多少个、什么样的参数,也没有硬件可以保存这些参数。也就是说,计算机不知道怎么给这个函数传递参数,传递参数的工作必须由函数调用者和函数本身来协调。为此,计算机提供了一种被称为栈的数据结构来支持参数传递。 栈是一种先进后出的数据结构,栈有一个存储区、一个栈顶指针。栈顶指针指向堆栈中第一个可用的数据项(被称为栈顶)。用户可以在栈顶上方向栈中加入数据,这个操作被称为压栈(Push),压栈以后,栈顶自动变成新加入数据项的位置,栈顶指针也随之修改。用户也可以从堆栈中取走栈顶,称为弹出栈(pop),弹出栈后,栈顶下的一个元素变成栈顶,栈顶指针随之修改。 函数调用时,调用者依次把参数压栈,然后调用函数,函数被调用以后,在堆栈中取得数据,并进行计算。函数计算结束以后,或者调用者、或者函数本身修改堆栈,使堆栈恢复原装。 在参数传递中,有两个很重要的问题必须得到明确说明: 当参数个数多于一个时,按照什么顺序把参数压入堆栈 函数调用后,由谁来把堆栈恢复原装 在高级语言中,通过函数调用约定来说明这两个问题。常见的调用约定有:stdcall cdecl fastcall thiscall naked call stdcall调用约定 stdcall很多时候被称为pascal调用约定,因为pascal是早期很常见的一种教学用计算机程序设计语言,其语法严谨,使用的函数调用约定就是stdcall.在Microsoft C++系列的C/C++编译器中,常常用PASCAL宏来声明这个调用约定,类似的宏还有WINAPI和CALLBACK. stdcall调用约定声明的语法为(以前文的那个函数为例): int __stdcall function(int a,int b) stdcall的调用约定意味着:1)参数从右向左压入堆栈,2)函数自身修改堆栈3)函数名自动加前导的下划线,后面紧跟一个@符号,其后紧跟着参数的尺寸 以上述这个函数为例,参数b首先被压栈,然后是参数a,函数调用function(1,2)调用处翻译成汇编语言将变成: push 2 第二个参数入栈 push 1 第一个参数入栈 call function 调用参数,注意此时自动把cs:eip入栈 而对于函数自身,则可以翻译为: push ebp 保存ebp寄存器,该寄存器将用来保存堆栈的栈顶指针,可以在函数退出时恢复 mov ebp,esp 保存堆栈指针

C++函数调用过程深入分析

函数调用过程分析 刘兵QQ: 44452114 E-mail: liubing2000@https://www.360docs.net/doc/1518082507.html, 0. 引言 函数调用的过程实际上也就是一个中断的过程,那么C++中到底是怎样实现一个函数的调用的呢?参数入栈、函数跳转、保护现场、回复现场等又是怎样实现的呢?本文将对函数调用的过程进行深入的分析和详细解释,并在VC 6.0环境下进行演示。分析不到位或者存在错误的地方请批评指正,请与作者联系。 首先对三个常用的寄存器做一下说明,EIP是指令指针,即指向下一条即将执行的指令的地址;EBP 为基址指针,常用来指向栈底;ESP为栈指针,常用来指向栈顶。 看下面这个简单的程序并在VC 6.0中查看并分析汇编代码。 图1 1. 函数调用 g_func函数调用的汇编代码如图2: 图2 首先是三条push指令,分别将三个参数压入栈中,可以发现参数的压栈顺序是从右向左的。这时我们可以查看栈中的数据验证一下。如图3所示,从右边的实时寄存器表中我们可以看到ESP(栈顶指针)值为0x0012FEF0,然后从中间的内存表中找到内存地址0x0012FEF0处,我们可以看到内存中依次存储了0x00000001(即参数1),0x00000002(即参数2),0x00000003(即参数3),即此时栈顶存储的是三个参数值,说明压栈成功。 图3

然后可以看到call指令跳转到地址0x00401005,那么该地址处是什么呢?我们继续跟踪一下,在图4中我们看到这里又是一条跳转指令,跳转到0x00401030。我们再看一下地址0x00401030处,在图5中可以看到这才是真正的g_func函数,0x00401030是该函数的起始地址,这样就实现了到g_func函数的跳转。 图4 图5 2. 保存现场 此时我们再来查看一下栈中的数据,如图6所示,此时的ESP(栈顶)值为0x0012FEEC,在内存表中我们可以看到栈顶存放的是0x00401093,下面还是前面压栈的参数1,2,3,也就是执行了call指令后,系统默认的往栈中压入了一个数据(0x00401093),那么它究竟是什么呢?我们再看到图3,call指令后面一条指令的地址就是0x00401093,实际上就是函数调用结束后需要继续执行的指令地址,函数返回后会跳转到该地址。这也就是我们常说的函数中断前的“保护现场”。这一过程是编译器隐含完成的,实际上是将EIP(指令指针)压栈,即隐含执行了一条push eip指令,在中断函数返回时再从栈中弹出该值到EIP,程序继续往下执行。 图6 继续往下看,进入g_func函数后的第一条指令是push ebp,即将ebp入栈。因为每一个函数都有自己的栈区域,所以栈基址也是不一样的。现在进入了一个中断函数,函数执行过程中也需要ebp寄存器,而在进入函数之前的main函数的ebp值怎么办呢?为了不被覆盖,将它压入栈中保存。 下一条mov ebp, esp 将此时的栈顶地址作为该函数的栈基址,确定g_func函数的栈区域(ebp为栈底,

[CC++] 函数调用的栈分配

[C/C++] 函数调用的栈分配 压栈,跳转到被调函数的入口点。 进入被调函数时,函数将esp减去相应字节数获取局部变量存储空间。被调函数返回(ret)时,将esp加上相应字节数,归还栈空间,弹出主调函数压在栈中的代码执行指针(eip),跳回主调函数。再由主调函数恢复到调用前的栈。 为了访问函数局部变量,必须有方法定位每一个变量。变量相对于栈顶esp的位置在进入函数体时就已确定,但是由于esp会在函数执行期变动,所以将esp 的值保存在ebp中,并事先将原ebp的值压栈保存,以声明中的顺序(即压栈的相反顺序)来确定偏移量。 访问函数的局部变量和访问函数参数的区别: 局部变量总是通过将ebp减去偏移量来访问,函数参数总是通过将ebp加上偏移量来访问。对于32位变量而言,第一个局部变量位于ebp-4,第二个位于ebp-8,以此类推,32位局部变量在栈中形成一个逆序数组;第一个函数参数位于ebp+8,第二个位于ebp+12,以此类推,32位函数参数在栈中形成一个正序数组。 函数的返回值不同于函数参数,可以通过寄存器传递。如果返回值类型可以放入32位变量,比如int、short、char、指针等类型,将通过eax寄存器传递。如果返回值类型是64位变量,如_int64,则通过edx+eax传递,edx存储高32位,eax存储低32位。如果返回值是浮点类型,如float和double,通过专用的浮点数寄存器栈的栈顶返回。如果返回值类型是struct或class类型,编译器将通过隐式修改函数的签名,以引用型参数的形式传回。由于函数返回值通过寄存器返回,不需要空间分配等操作,所以返回值的代价很低。基于这个原因,C89规范中约定,不写明返回值类型的函数,返回值类型默认为int。这一规则与现行的C++语法相违背,因为C++中,不写明返回值类型的函数返回值类型为void,表示不返回值。这种语法不兼容性是为了加强C++的类型安全,但同时也带来了一些代码兼容性问题。 代码示例 VarType Func (Arg1, Arg2, Arg3, ... ArgN) { VarType Var1, Var2, Var3, ...VarN; //... return VarN; } 假设sizeof(VarType) = 4(DWORD), 则一次函数调用汇编代码示例为: 调用方代码: push ArgN ; 依次逆序压入调用参数 push ... push Arg1 call Func_Address ; 压入当前EIP后跳转 跳转至被调方代码:

R寄存器分配说明-新代

5.4. MLC Register Definition 5.4.1資源分配表 讀寫規則 編號 說明 巨集程式或人機介面 階梯程式 是否可位元(Bit)存取 永久保存 R0~R39 CNC 系統介面區 R40~R49 PLC 警報訊息區 唯讀 依下表定義之 是 否 R50~R80 使用者自行定義 讀/寫 讀/寫 是 否 R81~R100 對應到系統參數 3401~3420 PLC 參數 唯讀 唯讀 是 否 R101~R102 刀具狀態 讀/寫 讀/寫 是 是 R103~R255 是 R256~R511 使用者自行定義 讀/寫 讀/寫 是 否 R512~R639 是 R640~R1023 CNC 系統介面區 唯讀 依下表定義之 否 否 R1023~R4095 使用者自行定義 讀/寫 讀/寫 否 否 5.4.2CNC 系統介面區 編號 代碼 名稱、範圍 功能說明 PLC 讀寫規則 R0 R1 MCODE M Code (00~99) 控制器執行到M CODE 時 ,會將該M CODE 內容放在這理 ,供 LADDER 讀取 唯讀R2 SCODE S Code (0000~9999) 控制器執行到 S CODE 時 ,會將該S CODE 內容放在這理 ,供LADDER 讀取 唯讀R3 TCODE T Code (0000~9999) 控制器執行到 T CODE 時 ,會將該T CODE 內容放在這理 ,供LADDER 讀取 唯讀R4 合成進給速度命令值,單位LIU/min R5 R6 MPGPOS1 MPG 1 Position 第一顆MPG 位置 唯讀R7 MPGPOS2 MPG 2 Position 第二顆MPG 位置 唯讀

相关文档
最新文档