天天看點

Hexagon_V65_Programmers_Reference_Manual(8)7. 程式流程

Hexagon_V65_Programmers_Reference_Manual(8)

  • 7. 程式流程
    • 7.1 條件指令
    • 7.2 硬體循環
      • 7.2.1 循環設定
      • 7.2.2 循環結束
      • 7.2.3 循環執行
      • 7.2.4 流水線硬體循環
      • 7.2.5 循環限制

7. 程式流程

Hexagon 處理器支援以下程式流程工具:

  • 條件指令
  • 硬體循環
  • 軟體分支
  • 暫停
  • 異常

    軟體分支包括跳轉、調用和傳回。 支援幾種類型的跳轉:

  • 條件跳轉(Speculative jumps)
  • 比較跳轉(Compare jumps)
  • 寄存器指派跳轉(Register transfer jumps)
  • 雙跳(Dual jumps)

7.1 條件指令

許多 Hexagon 處理器指令可以有條件地執行。 例如:

if (P0) R0 = memw(R2) // conditionally load word if P0
if (!P1) jump label // conditionally jump if not P1
           

以下指令可以指定為有條件的:

  • 跳轉和調用
  • 許多加載和存儲指令
  • 邏輯指令(包括AND/OR/XOR)
  • 移位半字
  • 通過寄存器或短立即數進行 32 位加/減
  • 符号和零擴充
  • 32 位寄存器傳輸和 64 位組合字
  • 寄存器立即指派(Register transfer immediate)
  • 釋放幀并傳回

7.2 硬體循環

Hexagon 處理器包含硬體循環指令,可以執行零開銷的循環分支。 例如:

loop0(start,#3) // loop 3 times
start:
    { R0 = mpyi(R0,R0) } :endloop0
           

提供了兩組硬體循環指令 loop0 和loop1–以使硬體循環可以嵌套一層。 例如:

// Sum the rows of a 100x200 matrix.
    loop1(outer_start,#100)
outer_start:
    R0 = #0
    loop0(inner_start,#200)
inner_start:
        R3 = memw(R1++#4)
        { R0 = add(R0,R3) }:endloop0
    { memw(R2++#4) = R0 }:endloop1
           

硬體循環指令使用如下:

  • 對于非嵌套循環,使用loop0。
  • 對于嵌套循環,loop0 用于内循環,loop1 用于外循環。
注意 如果程式需要建立嵌套超過一層的循環,則最内層的兩個循環可以實作為硬體循環,其餘的外層循環實作為軟體分支。
           

每個硬體循環都與一對專用循環寄存器相關聯:

  • 循環起始位址寄存器 SAn 設定為循環中第一條指令的位址(通常用彙編語言表示為标簽)。
  • 循環計數寄存器 LCn 設定為 32 位無符号值,指定要執行的循環疊代次數。 當 PC 到達循環結束時,檢查 LCn 以确定循環是否應該重複或退出。

硬體循環設定指令一次設定這兩個寄存器——通常不需要單獨設定它們。 然而,由于循環寄存器完全指定了硬體循環狀态,它們可以被儲存和恢複(由處理器中斷自動或由程式員手動),一旦它的循環寄存器被重新加載,就可以正常恢複暫停的硬體循環。儲存的值。

Hexagon 處理器為兩個硬體循環提供了兩組循環寄存器:

  • SA0 和 LC0 被 loop0 使用
  • SA1 和 LC1 被 loop1 使用

表 7-1 列出了硬體循環指令。

文法 描述
loopN(start, Rs)

具有寄存器循環計數的硬體循環。

為硬體循環 N 設定寄存器 SAn 和 LCn:

* SAn 被配置設定了指定的循環起始位址。

* LCn 被指派為通用寄存器Rs 的值。

注意 - 循環開始操作數被編碼為 PC 相關的立即數。

loopN(start, #count)

具有即時循環計數的硬體循環。

為硬體循環 N 設定寄存器 SAn 和 LCn:

SAn 被配置設定了指定的循環起始位址。

LCn 被賦予指定的立即數 (0-1023)。

注意 - 循環開始操作數被編碼為 PC 相關的立即數。

:endloopN

硬體循環結束指令。

執行以下操作:if (LCn > 1) {PC = SAn; LCn = LCn-1}

注意:此指令在彙編中顯示為附加到循環中最後一個資料包的字尾。 它被編碼在最後一個資料包中。

SAn = Rs 将循環起始位址設定為通用寄存器 Rs
LCn = Rs 将循環計數設定為通用寄存器 Rs
注意
    循環指令配置設定給指令類 CR。
           

7.2.1 循環設定

要設定硬體循環,必須将循環寄存器 SAn 和 LCn 設定為正确的值。

這可以通過兩種方式完成:

  • loopN 指令
  • 寄存器傳輸到 SAn 和 LCn

loopN 指令執行設定 SAn 和 LCn 的所有工作。 例如:

loop0(start,#3) // SA0=&start, LC0=3
start:
    { R0 = mpyi(R0,R0) } :endloop0
           

在本例中,硬體循環(由一條乘法指令組成)執行了 3 次。 loop0 指令将寄存器 SA0 設定為标簽起始的位址值,并将 LC0 設定為 3。

當循環計數在 loopN 中表示為立即數時,循環計數被限制在 0-1023 的範圍内。 如果所需的循環計數超出此範圍,則必須将其指定為寄存器值。 例如:

使用循環N:

R1 = #20000;
    loop0(start,R1) // LC0=20000, SA0=&start
start:
    { R0 = mpyi(R0,R0) } :endloop0
           

使用寄存器指派:

R1 = #20000
    LC0 = R1 // LC0=20000
    R1 = #start
    SA0 = R1 // SA0=&start
start:
    { R0 = mpyi(R0,R0) } :endloop0
           

如果 loopN 指令距離其循環起始位址太遠,則用于指定起始位址的 PC 相對偏移值可能會超出指令起始位址操作數的最大範圍。 如果發生這種情況,要麼将 loopN 指令移近循環開始,要麼将循環開始位址指定為 32 位常量(第 10.9 節)。 例如:

使用 32 位常量:

R1 = #20000;
    loop0(##start,R1) // LC0=20000, SA0=&start
    ...
           

7.2.2 循環結束

循環結束指令訓示硬體循環中的最後一個資料包。 它用彙編語言表示,在資料包後面加上符号":endloopN",其中 N 指定硬體循環(0 或 1)。 例如:

loop0(start,#3)
start:
    { R0 = mpyi(R0,R0) } :endloop0 // last packet in loop
           

循環中的最後一條指令必須始終用彙編語言表示為一個資料包(使用花括号),即使它是資料包中的唯一指令。

嵌套的硬體循環可以指定相同的指令作為内部和外部循環的結束。 例如:

// Sum the rows of a 100x200 matrix.
// Software pipeline the outer loop.
    p0 = cmp.gt(R0,R0) // p0 = false
    loop1(outer_start,#100)
outer_start:
    { if (p0) memw(R2++#4) = R0
    p0 = cmp.eq(R0,R0) // p0 = true
    R0 = #0
    loop0(inner_start,#200) }
inner_start:
    R3 = memw(R1++#4)
    { R0 = add(R0,R3) }:endloop0:endloop1
    memw(R2++#4) = R0
           

雖然 endloopN 的行為類似于正常指令(通過實作循環測試和分支),但請注意它不會在任何指令槽中執行,并且不計為資料包中的指令。 是以,标記為循環結束的單個指令包最多可以執行六個操作:

  • 四個正常指令(一個指令包的正常限制)
  • endloop0 測試和分支
  • endloop1 測試和分支
注意 endloopN 指令被編碼在指令包中(第 10.6 節)。
           

7.2.3 循環執行

建立硬體循環後,無論指定的循環計數如何,循環體總是至少執行一次(因為直到循環中的最後一條指令才檢查循環計數)。 是以,如果一個循環需要可選地執行零次,則必須在它之前有一個顯式的條件分支。 例如:

loop0(start,R1)
    P0 = cmp.eq(R1,#0)
    if (P0) jump skip
start:
    { R0 = mpyi(R0,R0) } :endloop0
skip:
           

在此示例中,使用 R1 中的循環計數設定了一個硬體循環,但如果 R​​1 中的值為零,則軟體分支将跳過循環體。

執行完硬體循環的循環結束指令後,Hexagon 處理器會檢查相應循環計數寄存器中的值:

  • 如果該值大于 1,則處理器遞減循環計數寄存器并執行零循環分支到循環起始位址。
  • 如果該值小于或等于 1,則處理器在緊跟循環結束指令之後的指令處恢複程式執行。
注意 因為嵌套的硬體循環可以共享相同的循環結束指令,處理器可以在一次操作中檢查兩個循環計數寄存器。
           

7.2.4 流水線硬體循環

軟體流水線循環對于 Hexagon 處理器等 VLIW 架構很常見。 它們通過重疊多個循環疊代來提高循環中的代碼性能。

軟體流水線包含三個部分:

  • 循環開始的序幕
  • 核心(或穩态)部分
  • 流水線耗盡的尾聲

    最好用一個簡單的例子來說明這一點,如下所示。

int foo(int *A, int *result)
{
    int i;
    for (i=0;i<100;i++) {
        result[i]= A[i]*A[i];
    }
}
           
foo:
{   R3 = R1
    loop0(.kernel,#98) // Decrease loop count by 2
}
    R1 = memw(R0++#4) // 1st prologue stage
{   R1 = memw(R0++#4) // 2nd prologue stage
    R2 = mpyi(R1,R1)
}
    .falign
.kernel:
{   R1 = memw(R0++#4) // kernel
    R2 = mpyi(R1,R1)
    memw(R3++#4) = R2
}:endloop0
{   R2 = mpyi(R1,R1) // 1st epilogue stage
    memw(R3++#4) = R2
}
    memw(R3++#4) = R2 // 2nd epilogue stage
    jumpr lr
           

上述代碼中,流水線循環的核心部分并行執行循環的三個疊代:

  • 疊代 N+2 的負載
  • 疊代 N+1 的乘法
  • 疊代 N 的存儲

軟體流水線的一個缺點是流水線循環的序言和結尾部分所需的額外代碼。

為了解決這個問題,Hexagon 處理器提供了 spNloop0 指令,其中指令名稱中的“N”表示 1-3 範圍内的數字。 例如:

P3 = sp2loop0(start,#10) // Set up pipelined loop
           

spNloop0 是 loop0 指令的變體:它使用 SA0 和 LC0 建立一個正常的硬體循環,但還執行以下附加操作:

  • 執行spNloop0指令時,将真值false賦給條件寄存器P3。
  • 關聯循環執行 N 次後,P3 自動設定為 true。

此功能(稱為自動條件控制)使流水線循環的核心部分中的存儲指令能夠由 P3 有條件地執行,是以由于 spNloop0 控制 P3 的方式 - 在流水線預熱期間不會執行。 這可以通過消除對序言代碼的需要來減少許多軟體流水線循環的代碼大小。

spNloop0 不能用于從流水線循環中消除結尾代碼; 但是,在某些情況下,可以通過使用程式設計技術來做到這一點。

通常,影響結尾代碼删除的問題是加載安全性。 如果流水線循環的核心部分可以安全地通路其數組的末尾——要麼是因為已知它是安全的,要麼是因為數組已在末尾填充——那麼結尾代碼是不必要的。 但是,如果無法確定加載安全,則需要顯式的結尾代碼來排空軟體管道。

軟體流水線循環(使用 spNloop0)

int foo(int *A, int *result)
{
    int i;
    for (i=0;i<100;i++) {
        result[i]= A[i]*A[i];
    }
}
           
foo:
{ // load safety assumed
    P3 = sp2loop0(.kernel,#102) // set up pipelined loop
    R3 = R1
}
.falign
.kernel:
{   R1 = memw(R0++#4) // kernel
    R2 = mpyi(R1,R1)
    if (P3) memw(R3++#4) = R2
}:endloop0
    jumpr lr
           
注意 spNloop0 用來控制 P3 設定的計數值存儲在使用者狀态寄存器 USR.LPCFG 中。
           

7.2.5 循環限制

硬體循環有以下限制:

  • loopN 或 spNloop0(第 7.2.4 節)中的循環設定資料包不能包含推測性間接跳轉、新值比較跳轉或 dealloc_return。
  • 硬體循環中的最後一個資料包不能包含任何程式流指令(包括跳轉或調用)。
  • loop0 中的循環結束包不能包含任何改變SA0 或LC0 的指令。 同樣,loop1 中的循環結束包不能包含任何改變 SA1 或 LC1 的指令。
  • spNloop0中的循環結束包不能包含任何改變P3的指令。
注意 SA1 和 LC1 可以在 loop0 結束時更改,而 SA0 和 LC0 可以在 loop1 結束時更改。
           

繼續閱讀