跳转至

灵犀块指令定义

灵犀块指令是一种结构化、层次化的指令形式,用于描述复杂计算任务的逻辑结构和执行范围。通过引入“块”的概念,灵犀指令集显著提升了指令的表达能力和处理器的执行效率。

与传统指令集逐条执行的方式不同,灵犀块指令将多条相关指令封装为一个执行单元“块”。每个块可以包含任意数量的块体指令,并作为整体进行调度、优化与执行。

灵犀块指令由两部分组成:块头(Header)块体(Body)。其中,块头用于描述块的调度与控制信息,块体负责执行具体的计算或访存等操作。

block

用于定义块头的指令称为 “块头指令”。这类指令标志块指令的开始,并且指定块类型、设置跳转逻辑以及执行要求等参数。其中有一些基本的参数,包括:

参数 说明
块类型 这个参数决定使用什么引擎执行这个块,以及引擎必须按照什么规则执行这个块。
跳转参数 这个参数定义如何找到下一个块。
块属性 这个参数是块类型的参数,用于说明这个块类型之外的其他要求,很多属性可以在多种块类型上使用,具体请参考属性说明。
输入输出 这个参数设置本块的输入输出寄存器,但它不是必须的。如果没有定义这个参数,那么认为所有的一层寄存器都用于输入输出,这有可能降低指令流水线的并行度。
块结构 这个参数定义了块头与块体的组织结构,用于说明块身的位置。

块头指令的汇编语法定义为如下形态:OPCODE {parameters}。其中,OPCODE 定义指令作用;{parameters} 描述指令参数内容。

而块体中包含实际执行的指令,如计算指令、内存操作指令等,它们被称为“块体指令”。

块体指令的形式和块类型相关。无特殊说明,块体指令形式统一为:

opcode {input_parameters} {, ->output_parameters}

指令的输入输出参数被明确分成两部分,通过,->进行分割。input_parameters描述输入参数,output_parameters描述输出参数。

块结构

根据块头和块体之间组织形式的不同,块指令支持三种定义方式:

  • 一体块(Coupled Block):块头与块体连在一起紧邻定义,由一级调度器统一取指,块体块体指令通过内部通道传递至块引擎。此为灵犀块指令的基础形式,适用于控制流密集或并行度不高的程序。详细定义请见块指令执行机制
  • 分离块(Decoupled Block):块头与块体分离,由一级调度器负责块头取指,二级块引擎负责块体解码执行。详细定义请见块指令执行机制
  • 模板块(Template Block):仅包含块头,不含显式块体。块头交由模板生成单元 CodeGen 自动生成块体指令序列或块头在某个特定执行单元直接执行,适用于固定功能模块,如内存拷贝、矩阵运算等。

不同块结构的块,他们块头的定义方式以及块内寄存器的状态会有所不同。

块类型

块类型决定需要什么引擎才能执行这个块,以及本块的执行能力。当前版本灵犀指令集中支持的块类型如下:

块类型 汇编符号 说明
整型标量块Integer Scalar Block STD 灵犀指令集的基础块,提供一般整型计算能力
系统块System Block SYS 用于维护系统状态,提供系统控制类计算能力
浮点标量块Floating-point Scalar Block FP 提供标量浮点计算能力
访存并行块Memory Access and Parallel Block MPAR 提供向量化的内存数据搬运能力,多Group并行执行
访存串行块Memory Access and Sequel Block MSEQ 提供向量化的内存数据搬运能力,多Group串行执行
向量并行块Vector and Parallel Block VPAR 提供向量数据计算能力,多Group并行执行
向量串行块Vector and Sequel Block VSEQ 提供向量数据计算能力,多Group串行执行
数据搬运块Tile and Memory Access Block TMA 提供内存与Tile寄存器间数据搬移能力
矩阵数据块Cube Block CUBE 提供矩阵运算能力,将矩阵拆成多个分型,以分型的粒度执行矩阵运算
模版数据块Template Tile Block TEPL 提供模版化的数据块(Tile)计算能力
系统调用块Cross Block XB 提供轻量化的系统调用能力

其中,访存并行块和访存并行块都用于执行内存与Tile之间的数据搬移任务,只是Group间执行要求不同,因此这两种块统称为访存数据块。向量并行块和向量串行块也只是Group间的执行要求不同,因此也可以统称为向量数据块。

所有块指令共享同一份第一层架构状态,但是不同类型的块指令可以有其独特的第二层架构状态,例如标量块与向量块的第二层架构状态可以完全不同。这种设计可以在处理标量运算、向量运算和大规模并行运算等不同复杂度的任务时,为其提供灵活且适配的计算框架,帮助处理器在不同场景中取得高效性能。

不仅如此,每个具体的块指令还可以拥有它独享的一组私有寄存器。这些寄存器在块指令初始化时被分配,块指令提交后被释放,并且只能被本块内部的指令访问和使用。

block

跳转参数

跳转参数用于定义块指令之间的执行路径,指示了如何从当前块找到下一个块。该参数包含类型和针对特定类型的跳转距离,灵犀指令集支持如下的跳转参数:

不需要跳转距离参数,或者跳转距离通过寄存器提供的:

  • FALL:Fall Through,块结束后执行顺延的下一个块。
  • IND:Indirect Branch,块结束后跳转到指定的绝对地址。
  • ICALL:Indirect Call,块结束后跳转到指令的绝对地址实现函数调用。其实际执行效果与IND一样,但是需要额外记录调用返回地址。
  • RET:Return,块结束后跳转到指定绝对地址位置实现函数返回。其实际执行效果与IND一样,但给处理器一个执行提示,以便更好预判指令的执行序列。

需要跳转距离参数的:

  • DIRECT:Direct Branch,块结束后跳转到指定偏移的位置。
  • COND:Conditional Branch,块结束后根据计算的结果条件跳转到指定偏移的位置。
  • CALL:Call,块结束后跳转到指定偏移的位置进行函数调用。其实际执行效果与DIRECT一样,但是需要额外记录调用返回地址。

块属性

块属性整体设置块的执行行为,本小节描述其中包含复杂功能的部分,其他简单的属性请参考每个具体的块类型的定义方法说明。

原子属性

原子属性赋予块内存操作的原子性,带有原子属性的块称为原子块。原子块中的内存操作中间不会被其他的内存观察者打断或者观察到。如果原子块中间被中断或者异常打断,原子块的内存操作以及寄存器输出效果或者全部无效,或者全部生效。

原子块的内存行为被限制在一个连续的范围之内(未来的版本可能升级为更多个连续范围),如果实际的操作超过这个范围,则触发该指令的E_DATA(EC_RANGE)异常

当前版本对原子块的连续访问范围定义为16个字节对齐的16字节空间。原子块在内存行为上相当于一条单独的内存宏指令,所以这个指令本身可以带有获取和释放顺序属性。

继承属性

继承属性可以保留块的内部状态给后序块,这有利于数据的连续传递。继承属性定义为一个块指令执行结束后,其内部状态默认继承给下个相同执行引擎的块指令,并且中间可以跨过不同的执行引擎的块指令。

其特点可以概括为:

  • 不是所有块指令之间都允许互相继承,而是必须具有相同块内状态且执行在同一种引擎中的块指令之间带有继承属性。
  • 继承属性是块指令默认带有的属性,不需要额外参数指定。
  • 继承具有可跨越性,允许在程序序上不连续的两个块有继承关系。

比如如下程序:

block1:
    BSTART.STD FALL
    ld [r1, r2<<3], ->t     # i0
    ld [r1, r3<<3], ->t     # i1
    setc.ne t#1, zero
    ...
block2:
    BSTART.VPAR FALL
    B.DIM 100, zero, ->LB0
    B.TEXT .foo
block3:
    BSTART.SYS FALL
    add t#1, t#2, ->t      # t#1读取i0的结果,t#2读取i1的结果  
    BSTOP
foo:    # block2的块体
    v.lwi [TA, LC0<<2], ->vt.w
    v.lwi [TB, LC0<<2], ->vt.w
    rdadd vt#1, ->t
    ...
其中,block1block3执行在相同的引擎上,而block2执行在不同的块引擎上。因此block2中不可以继承block1的内部状态,block3可以继承block1的内部状态,但是不能继承block2的内部状态。

需要特别注意的是,如果一个块的块体内读取的前序块未写过的寄存器,那么读取的内容是未定义的随机值。例如:

# 程序开始第一个块
BSTART.SYS
addi t#1, 8, ->t       # t#1的值是未定义的
ld [a0, t#1<<3], ->t
...