ARM 程序集嵌套循环

时间:2021-02-10 05:46:51

标签: assembly arm

我是 ARM 汇编语言的新手,我知道如何制作简单的 for 循环,但是,当我尝试将概念应用于嵌套循环时,我对如何设置它感到非常困惑。我想知道是否有一般设置 for 循环的规则?也许我可以使用该规则来创建嵌套循环?

举个例子,如果我有一个像下面这样用 C 编写的简单嵌套循环代码,它在汇编中会是什么样子?非常感谢详细的解释,谢谢!

for(int i = 0; i < a; i++){
  for(int j = 0; j < b; j++){
  int sum = 0;
    for(k = 0; k < c; k++){
      for( l = 0; l < d; l++){
        int temp1 = i+k;
        int temp2 = j+k;
      }
    }
  }
}

2 个答案:

答案 0 :(得分:1)

对于两个嵌套循环,我通常会做这样的事情。 *请记住,在此代码中,i(外循环计数器)将是 R0,而 j(内循环计数器)将是 R1。而R2用于计算内外循环的循环次数。

            AREA        myCode, CODE, READONLY
            ENTRY
            EXPORT __main
                
__main
            
            MOV     R2, #0
            
            MOV     R0, #2      ; ** outer loop (loop 1) counter initialization ** 
LOOP1       CBZ     R0, STOP    ; if (R0 == 0 && R1 == 0) then branch to somewhere. (I'm just ending program here)
            ; between outer and inner loop
            ; could do anything but I'm just icreamenting R2
            ADD     R2, R2, #1
            
            MOV     R1, #3      ; ** inner loop (loop 2) counter initialization **
LOOP2       CBZ     R1, CNTULP1
            ; code inside of inner loop will be here
            ; could do anything but I'm just increamenting R2
            ADD     R2, R2, #1

; Notice) when you need to continue loop2 => branch to CNTULP2. else if you wanted to continue loop1 => branch to CNTULP1
;           and when you wanted to break loops => branch out of them for example branch to STOP 
CNTULP2     ; continue loop2 (inner loop) 
            SUB     R1, R1, #1  ; decreament inner loop counter
            B       LOOP2

CNTULP1     ; continue loop1 (outer loop)
            SUB     R0, R0, #1  ; decreament outer loop counter
            B       LOOP1


STOP        B       STOP
            END

答案 1 :(得分:0)

这与您嵌套它们的单个循环没有什么不同。与 C 或其他语言相同(为每个循环使用不同的变量/寄存器,以便它们可以不受干扰地嵌套)。

something holds i (register or memory)
something holds a (register or memory)

i = 0;
loop0:
   get i
   get a
   compare i and a
   if signed greater than or equal jump to loop0_done
   do stuff
   get i
   increment i
   jmp to loop0
loop0_done:

你也可以这样设计:

something holds i (register or memory)
something holds a (register or memory)

i = 0;
loop0:
   get i
   get a
   compare i and a
   if signed greater than or equal jump to loop0_done
   do stuff
   get i
   increment i
loop0_mid:
   get a
   compare i and a
   if signed less than jump to loop0

让我们假设 a 和 b 作为 r0 和 r1 传入。我们希望这段代码遵循正常的 arm 调用约定。

for(int i = 0; i < a; i++){
  for(int j = 0; j < b; j++)
    do stuff
  }
}

push {r4,r5,r6,r7}
;@ A loop
mov r4,#0 ;@ i
mov r5,r0 ;@ a
mov r7,r1 ;@ b
b a_loop_mid
a_loop:

  mov r6,#0 ;@ j
  b b_loop_mid
b_loop:

    do stuff

    add r6,#1
b_loop_mid:
    cmp r6,r7
    blt b_loop

  add r4,#1
  a_loop_mid
  cmp r4,r5
  blt a_loop

pop {r4,r5,r6,r7}

两个循环看起来一样,只是使用了不同的寄存器/变量:

mov r4,#0 ;@ i
mov r5,r0 ;@ a
b a_loop_mid
a_loop:

    do stuff

  add r4,#1
  a_loop_mid
  cmp r4,r5
  blt a_loop

  mov r6,#0 ;@ j
  b b_loop_mid
b_loop:

    do stuff

    add r6,#1
b_loop_mid:
    cmp r6,r7
    blt b_loop

然后将它们嵌套。现在你会用完寄存器,所以有些东西会想在堆栈上,所以访问说 b 可能是

ldr r1,[sp,#12]

并且不需要烧录r7。

同样,for 循环将计数为 n,但循环中未使用计数变量,那么您可以根据指令集保存一条或几条指令。有多个 arm 指令集,而不是计数和执行

add this,#1
cmp this,that
blt somewhere

可以在循环内保存一条指令

subs this,#1
bne somewhere

你需要正确地初始化它,如果

for(i=0;i<a;++)

a 是 5 那么我会数 0,1,2,3,4, 五件事。所以你可以改为数 5,4,3,2,1, 五件事

this=a
label:
...
subs this,#1
bne label

并将该指令保存在循环中

某些 arm 指令集有 cbz 和 cbnz(如果为零或不为零,则比较并分支)

所以

sub this,#1
cbnz label

那里没有真正的节省。一些指令集(不是 arm)有一个递减,如果不是零则跳转

this = a
label:

djnz this, label

保存另一条指令。

如果您使用典型的 arm 调用约定与编译代码链接,那么 r0-r3 是易失性的,而 r4 以上的大部分是非易失性的(您必须保留它们)。这意味着,如果您想在循环内调用另一个函数,那么该函数可能会与 r0-r3 混淆,因此您不能将它们用作跨越调用边界的循环的一部分。

如果 r0 中有 a

mov r2,#0
a_loop:
   bl somewhere
   cmp r2,r0
   blt a_loop

这有两个问题,r2 和 r0 都可以在某处函数中改变,所以比较和东西可能会搞砸。

这样做

mov r2,r0
a_loop:
   bl somewhere
   subs r2,#1
   bne a_loop

只有 r2 被这个循环搞砸了,但是如果在这个可能已经被破坏的代码之后需要 r0 中的 a 值。这就是为什么对于更简单的事情你可能会看到这个

push {r4,...}
mov r4,r0
...
pop {r4,...}

保存调用者 r4,然后在此函数中使用 r4 来保存 a 值。或者最坏的情况

push {r0,...}
ldr rn,[sp,??]  read a
pop {r0,...}

sp 偏移量的位置取决于堆栈上有多少东西以及 r0 值的位置。或者使用堆栈框架并从中引用。

归根结底,嵌套循环与任何其他语言没有什么不同。您必须拥有不干扰其他嵌套循环的循环变量。您的循环完全位于外循环中。