汇编代码中%lo(source)($ 6)和.frame是什么意思?

问题描述 投票:0回答:1

我汇编了一个简单的c程序来进行mips,并尝试理解汇编代码。通过与C代码进行比较,我几乎了解了它,但是仍然遇到了一些问题。

我使用mips-gcc生成汇编代码:$ mips-gcc -S -O2 -fno-delayed-branch -I / usr / include lab3_ex3.c -o lab3_ex3.s

这是我对汇编代码如何工作的猜测:

main是程序的入口。

$6是源数组的地址。

$7是目标数组的地址。

$3是源数组的大小。

[$2是变量k,并初始化为0。

$L3是循环

[$5$4source[k]dest[k]的地址。

[sw $3,0($5)等效于将source[k]存储在$3中。

[lw $3,4($4)等效于将source[k]分配给dest[k]

[addiu $2,$2,4等效于k++

bne $3, $0, $L3表示如果source[k]为零,则退出循环,否则跳至标签$L3

$L2只是做一些清理工作。

$2设置为零。

跳转到$31(返回地址)。

我的问题是:

  1. .frame $sp,0,$31做什么?
  2. 为什么用lw $3,4($4)代替lw $3,0($4)
  3. [%lo(source)($6)表示什么意思? ($ hi和$ lo $寄存器用于乘法运算,为什么在这里使用它们?)

谢谢。

C

int source[] = {3, 1, 4, 1, 5, 9, 0};
int dest[10];

int main ( ) {
    int k;
    for (k=0; source[k]!=0; k++) {
    dest[k] = source[k];
    }
    return 0;
}

组装

.file   1 "lab3_ex3.c"
    .section .mdebug.eabi32
    .previous
    .section .gcc_compiled_long32
    .previous
    .gnu_attribute 4, 1
    .text
    .align  2
    .globl  main
    .set    nomips16
    .ent    main
    .type   main, @function
main:
    .frame  $sp,0,$31       # vars= 0, regs= 0/0, args= 0, gp= 0
    .mask   0x00000000,0
    .fmask  0x00000000,0
    lui $6,%hi(source)
    lw  $3,%lo(source)($6)
    beq $3,$0,$L2
    lui $7,%hi(dest)
    addiu   $7,$7,%lo(dest)
    addiu   $6,$6,%lo(source)
    move    $2,$0
$L3:
    addu    $5,$7,$2
    addu    $4,$6,$2
    sw  $3,0($5)
    lw  $3,4($4)
    addiu   $2,$2,4
    bne $3,$0,$L3
$L2:
    move    $2,$0
    j   $31
    .end    main
    .size   main, .-main
    .globl  source
    .data
    .align  2
    .type   source, @object
    .size   source, 28
source:
    .word   3
    .word   1
    .word   4
    .word   1
    .word   5
    .word   9
    .word   0

    .comm   dest,40,4
    .ident  "GCC: (GNU) 4.4.1"
c assembly mips
1个回答
5
投票

首先,main$L3$L2是3个基本块的标签。您对它们的功能大致是正确的。

问题1:.frame在做什么

这不是MIPS指令。它是描述此功能(堆栈)框架的元数据:

  • [$sp指向堆栈,$29的别名。
  • 和堆栈帧的大小(0,因为该函数在堆栈上既没有局部变量也没有参数)。此外,该功能非常简单,可以与暂存器一起使用,并且不需要保存被调用者保存的寄存器$16-$23
  • 旧的返回地址(对于MIPS调用约定为$31
  • 有关MIPS调用约定的更多信息,请参见此doc

[[问题2:为什么用$ 3,4($ 4)代替$ 3,0($ 4)

这是由于循环的优化。通常,加载和存储的顺序为:

    加载源[0]
  • 商店目标[0]
  • 加载源[1]
  • 商店目标[1]....
  • 您假定循环完全在$L3中,并且包含load source[k]store dest[k]。不是。有两个线索可以看到:

    main块中的负载与循环外的任何负载都不对应
  • 在基本块$L3中,存储在加载之前。
  • 实际上,load source[0]在名为main的基本块中执行。然后,基本块$L3中的循环为store dest[k];load source[k+1];。因此,加载使用的偏移量比存储的偏移量大4,因为它正在加载下一次迭代的整数。

问题3:lo / hi语法是什么?

这与指令编码和指针有关。让我们假设一个32位体系结构,即一个指针是32位。像大多数固定大小的指令ISA一样,让我们​​假设指令大小也是32位。

source / dest数组加载和存储之前,需要将它们的指针分别加载到寄存器$6$7中。因此,您需要一条指令将32位常量地址加载到寄存器中。但是,一条32位指令必须包含一些位来编码操作码(指令是该操作),目标寄存器等。因此,一条指令还剩下不到32位用于编码常数(称为立即数)。因此,您需要两条指令将32位常量加载到寄存器中,每条指令加载16位。lo/ hi指的是常量的哪一半被加载。

示例:假设dest位于地址0xabcd1234。有两条指令将此值加载到$7

lui $7,%hi(dest) addiu $7,$7,%lo(dest)

lui是立即加载上限。它将dest0xabcd)的地址的高16位装入$7的高16位。现在,$7的值为0xabcd0000。

addiu是“添加立即无符号”。它将dest0x1234)地址的低16位与$7中的现有值相加,以获得新的$7值。因此,$7现在拥有dest的地址0xabcd0000 + 0x1234 = 0xabcd1234。

类似地,lw $3,%lo(source)($6)从由$6指向的地址(该地址已经保存了source的地址的高16位)开始加载,其偏移量为%lo(source)(该地址的低16位)。有效地,它加载source的第一个单词。

© www.soinside.com 2019 - 2024. All rights reserved.