Heagon的程序流程(二)

来源:互联网 发布:python 金融数据接口 编辑:程序博客网 时间:2024/06/11 05:49

软件分支

 

与硬件循环不同,软件分支通过明确的指令来实现一次分支操作。软件分支包括了如下的几种指令:

  • 跳转(jump)
  • 调用(call)
  • 返回(return)

 

分支指令的目标地址可以通过寄存器间接寻址或程序计数器相对偏移量来确定。PC相对偏移量通常要小于32位,不过可以通过在目标操作数中使用特定的语法来定义成32位。

 

分支指令可以是无条件执行或有条件执行的,你需要使用执行预测表达式的条件指令。

下图是所有的软件分支指令:


跳转

跳转指令可以将程序流程指向一个目标地址,目标地址的值可以通过寄存器或程序计数器相对立即数的值来确定。根据预测表达式的值,跳转指令可以被有条件的执行。

 

下表列出了跳转指令的所有方式


调用

调用(call)指令被用来跳转至子路径。该指令可跳转到目标地址并在连接寄存器LR中保存返回地址。

 

调用的形式在功能上与跳转指令相似,此外,无条件或有条件形式下,调用也可以基于PC相对寻址以及寄存器间接寻址。

 

下图列出了调用指令的全部方式:


 

返回

返回指令被设计用于从字路径中返回。该指令使用间接跳转至子路径的返回地址,返回地址保存在链接寄存器LR中。

 

下图列出了返回地址的所有方式:


 

扩展分支

当跳转或调用指令使用一个PC相对偏移量作为分支目标时,偏移值通常要远小于32位。这一特性将限制程序长分支的能力,从而无法扩展至处理器更大范围的地址。

 

为了支持长分支,跳转与调用指令都有特殊的版本。这些版本可以解码成全32位的值作为PC相对偏移量。

标注:这种指令使用一个多余的字来保存32位的偏移

 

通过为目标标签添加可选的前缀如"##"或"#",我们可以表达出PC相对偏移量的大小。

 

  •  "##"指定为汇编器必须是32位的偏移量
  •  "#"指定汇编器一定不能为32位

 

如果不添加"#",汇编器会在必要的情况下自行选择是否使用32位的偏移量

 

例如:

jump ##label       // 32-bit offsetcall #label           // non 32-bit offsetjump label           // offset size determined byassembler

与分支相关的指令包

指令包是原子性的:即使其中包含多个指令,仍然可以通过包地址中的第一个指令引用。因此,包的分支可以只以包的第一个指令为目标。

 

指令包可以包含两个一上的分支。分支的终点可以是当前包或另一个包的起始点。

 

分支并不会终端当前包的执行:包中所有的指令都会被执行,即使它们是以汇编代码出现在分支指令之后。

 

如果一个包处在硬件循环的末尾,那么它将不能够包含一个分支指令

 

投机跳转

有条件指令通常取决于之前指令包中的预测位。但是dot-new预测允许包含条件指令的包中使用相同包中的指令来作为预测位。

 

当dot-new预测为被用来与条件跳转使用时,所导致的指令称作投机跳转。比如

{           P0 = cmp.eq(R9,#16) // single-packet compare-and-jump           IF (P0.new) jumpr:t R11 // ... enabled by use of P0.new}

 

投机需要程序员在跳跃指令中指定一个direction hint,从而预测下一个条件指令是否被执行。如果预测是错误的,那么投机跳转指令会花费两个周期来执行其中的一个(由于管道阻塞)

 

指示可以通过成功预测投机跳转执行代码的位置来提高程序性能:制定的hint如果与实际执行的代码匹配的越多,那么程序整体性能就越好。

 

标注:方向指示的标定是概率性的,而指令执行时动态的。程序员必须理解程序的动态行为从而确定投机跳转指令的最佳方向。

 

指示通过在汇编指令中为跳转指令添加后缀":t"或":nt"来表示。例如:

 

  • jump:t –跳转指令有很大几率会被执行
  • jump:nt –跳转指令有很大几率不会被执行

 

下图列出了投机跳转指令的所有方式:



 标注: 指示:t与:nt会与预测值进行交互从而确定指令循环的计数值。

 

 

比较跳转

为了减少Hexagon处理器的代码量,Hexagon提供了一种压缩指令,在一个单32位指令中结合了一个比较与投机跳转指令。

例如:

{           p0 = cmp.eq (R2,R5) // single-instr compare-and-jump           if (p0.new) jump:nt target // enabled by compound instr}

比较指令中的寄存器操作数被限制为R0-R7或者是R16-R23。

可用在比较跳转指令中的比较与跳转指令的种类被限制为下图中的几类。比较可以使用预测位P0或P1,而跳转必须指定比较指令中的相同预测位。


 

比较跳转指令在汇编代码中被表示为两个独立的比较与跳转指令。汇编器将两个指令合并为一个单一混合指令。

 

New-value比较跳转

 比较跳转指令可以访问相同指令包中被赋了新值的寄存器。这一特性通过在汇编语言中通过如下改变而申明:

 

在比较指令中添加.new后缀

重写比较跳转指令从而令其构成一个比较与跳转操作

 

例如:

// load-compare-and-jump packet enabled bynew-value compare jump{<span style="white-space:pre"></span>R0 = memw(R2+#8)<span style="white-space:pre"></span>if (cmp.eq(R0.new,#0)) jump:nt target}


 

New-value比较跳转指令有如下的限制:

 

在同一个包中无法与其他跳转指令结合

如果一个指令产生了64位的结果或实现了一个浮点操作,它的结果寄存器不能作为new-value的寄存器

 

如果指定的跳转指令指示是错误的,那么新值的比较跳转会选择另外一个操作。这样的火锅就是远超过一个周期的一般投机跳转,但整体性能仍然好于一般投机跳转指令

 

新值比较跳转指令:


 

寄存器转移跳转

为了减少Hexagon处理器的代码量,Hexagon提供了一种压缩指令:在一个32位指令中结合寄存器转移指令与无条件跳转指令。例如:

For example:{jump target // jump to label “target”R1 = R2 // assign contents of reg R2 to R1}

源与目标寄存器操作数使用的寄存器被限制为R0-R7或者是R16-R23。

 

跳转中的目标地址是一个有限定范围的9位PC相对地址值(与一般无条件跳转指令的22位值相反)。

 

寄存器转移跳转指令可通过在一个包中使用两个独立的汇编指令来表示。汇编器会将两个指令合并为一个压缩指令。

 

下图列出了寄存器转移跳转指令的方法:


 

双跳转

只要符合下图的条件,那么两个软件分支指令可以在同样的指令包中出现:


 

 

第一个跳转被定义为低位地址,而第二个跳转被作为高位地址。

 

与包装好的操作不同,双跳转不是并行操作的。两个跳转指令在包中以一个合理定义的序列处理:

1.第一个跳转中的预测被监测

2.如果第一个跳转执行,那么第二个被忽略

3.如果第二个跳转不被执行,那么第二个被执行

暂停

暂停将暂时停止程序的运行,并进入到低功耗模式。程序的停止时间由指令所定义。

 

暂停指令接受一个无符号八位的操作出来定义暂停的时间。最大的暂停时间为263个周期

 

在暂停时间未到来之前,Hexgon处理器的中断将导致程序退出暂停状态

 

中断指令对于实现用户层面的低功耗同步非常有用,下图列出了暂停指令的方法


异常

异常是程序流程中自发产生的崩溃

 

Hexagon 处理器操作系统能通过终止应用系统的执行来处理致命异常。用于有必要负责处理问题并重新编译应用。

 

异常产生的错误讯息包括如下的资料,从而帮助开发者定位问题:

 

代码原因-使用16位值来表示异常发生的种类

用户IP-用程序计数器来表示异常发生的指令

坏的虚拟地址-虚拟地址的值表示了异常发生时的数据访问

 

异常与中断的总结



如果出现多个异常,异常将会选择值最低的作为输出

 

如果一个包包含多个导入或者一个store指令一个load指令,那么两个操作都会有一个异常类型抛出。

 


0 0
原创粉丝点击