汇编代码中的 %lo(source)($6) 和 .frame 是什么意思?
What %lo(source)($6) and .frame mean in assembly code?
我assemble一个简单的c程序mips并尝试理解汇编代码。通过与c代码的对比,我差不多明白了,但还是遇到了一些问题。
我使用mips-gcc生成汇编代码:$ mips-gcc -S -O2 -fno-delayed-branch -I/usr/include lab3_ex3.c -o lab3_ex3.s
以下是我对汇编代码工作原理的猜测:
main
为程序入口
</code>为源数组地址</p>
<p><code>
是dest数组的地址
</code>是源数组的大小。</p>
<p><code>
是变量k
,初始化为0。
$L3
是循环
</code>和<code>
是source[k]
和dest[k]
的地址。
sw ,0()
等同于 source[k]
存储在 </code>.</p>
<p><code>lw ,4()
等同于将source[k]
赋值给dest[k]
.
addiu ,,4
等同于 k++
.
bne , [=31=], $L3
表示如果 source[k]
为零则退出循环,否则跳转到标签 $L3
。
$L2
只是做一些清理工作。
将 </code> 设置为零。</p>
<p>跳转到<code>
(return地址)。
我的问题是:
.frame $sp,0,
有什么作用?
- 为什么
lw ,4()
而不是 lw ,0()
- 写法
%lo(source)()
是什么意思? ($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, # vars= 0, regs= 0/0, args= 0, gp= 0
.mask 0x00000000,0
.fmask 0x00000000,0
lui ,%hi(source)
lw ,%lo(source)()
beq ,[=11=],$L2
lui ,%hi(dest)
addiu ,,%lo(dest)
addiu ,,%lo(source)
move ,[=11=]
$L3:
addu ,,
addu ,,
sw ,0()
lw ,4()
addiu ,,4
bne ,[=11=],$L3
$L2:
move ,[=11=]
j
.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"
首先,main
、$L3
和$L2
是3个基本块的标签。您对它们的功能大致正确。
问题一:.frame 是做什么的
这不是 MIPS 指令。它是描述此函数的(堆栈)帧的元数据:
- 堆栈由
$sp
指向,</code> 的别名。</li>
<li> 和堆栈帧的大小(0,因为函数既没有局部变量,也没有堆栈上的参数)。此外,该函数足够简单,可以使用临时寄存器,不需要保存被调用者保存的寄存器 <code>-
.
- 旧的 return 地址(
</code> MIPS 调用约定)</li>
</ul>
<p>有关 MIPS 调用约定的详细信息,请参阅此 <a href="https://courses.cs.washington.edu/courses/cse410/09sp/examples/MIPSCallingConventionsSummary.pdf" rel="nofollow noreferrer">doc</a>。</p>
<p><strong>问题 2:为什么 lw $3,4($4) 而不是 lw $3,0($4)</strong></p>
<p>这是由于循环的优化。通常,加载和存储的顺序为:</p>
<ul>
<li>加载源[0]</li>
<li>存储目标[0]</li>
<li>加载源[1]</li>
<li>存储目标[1]
....</li>
</ul>
<p>您假设循环完全在 <code>$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
数组加载和存储之前,需要将它们的指针分别加载到寄存器</code>和<code>
中。因此,您需要一条指令将 32 位常量地址加载到寄存器中。然而,一条 32 位指令必须包含一些位来编码操作码(指令是什么操作)、目标寄存器等。因此,一条指令剩下不到 32 位来编码常量(称为立即数)。因此,您需要两条指令将一个 32 位常量加载到一个寄存器中,每条指令加载 16 位。 lo
/hi
指的是加载常量的一半。
例子:假设dest
在地址0xabcd1234。有两条指令将此值加载到 </code>.</p>
<pre><code> lui ,%hi(dest)
addiu ,,%lo(dest)
lui
是 Load Upper immediate。它将dest
(0xabcd
)地址的高16位加载到</code>的高16位。现在,<code>
的值为 0xabcd0000。
addiu
是无符号加法。它将dest
(0x1234
)地址的低16位与</code>中的现有值相加得到<code>
的新值。因此,</code> 现在拥有 0xabcd0000 + 0x1234 = 0xabcd1234,即 <code>dest
的地址。
类似地,lw ,%lo(source)()
从</code>指向的地址加载(它已经保存了<code>source
地址的前16位)偏移量为%lo(source)
(该地址的低 16 位)。实际上,它加载了 source
.
的第一个单词
我assemble一个简单的c程序mips并尝试理解汇编代码。通过与c代码的对比,我差不多明白了,但还是遇到了一些问题。
我使用mips-gcc生成汇编代码:$ mips-gcc -S -O2 -fno-delayed-branch -I/usr/include lab3_ex3.c -o lab3_ex3.s
以下是我对汇编代码工作原理的猜测:
main
为程序入口
</code>为源数组地址</p>
<p><code>
是dest数组的地址
</code>是源数组的大小。</p>
<p><code>
是变量k
,初始化为0。
$L3
是循环
</code>和<code>
是source[k]
和dest[k]
的地址。
sw ,0()
等同于 source[k]
存储在 </code>.</p>
<p><code>lw ,4()
等同于将source[k]
赋值给dest[k]
.
addiu ,,4
等同于 k++
.
bne , [=31=], $L3
表示如果 source[k]
为零则退出循环,否则跳转到标签 $L3
。
$L2
只是做一些清理工作。
将 </code> 设置为零。</p>
<p>跳转到<code>
(return地址)。
我的问题是:
.frame $sp,0,
有什么作用?- 为什么
lw ,4()
而不是lw ,0()
- 写法
%lo(source)()
是什么意思? ($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, # vars= 0, regs= 0/0, args= 0, gp= 0
.mask 0x00000000,0
.fmask 0x00000000,0
lui ,%hi(source)
lw ,%lo(source)()
beq ,[=11=],$L2
lui ,%hi(dest)
addiu ,,%lo(dest)
addiu ,,%lo(source)
move ,[=11=]
$L3:
addu ,,
addu ,,
sw ,0()
lw ,4()
addiu ,,4
bne ,[=11=],$L3
$L2:
move ,[=11=]
j
.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"
首先,main
、$L3
和$L2
是3个基本块的标签。您对它们的功能大致正确。
问题一:.frame 是做什么的
这不是 MIPS 指令。它是描述此函数的(堆栈)帧的元数据:
- 堆栈由
$sp
指向,</code> 的别名。</li> <li> 和堆栈帧的大小(0,因为函数既没有局部变量,也没有堆栈上的参数)。此外,该函数足够简单,可以使用临时寄存器,不需要保存被调用者保存的寄存器 <code>-
. - 旧的 return 地址(
</code> MIPS 调用约定)</li> </ul> <p>有关 MIPS 调用约定的详细信息,请参阅此 <a href="https://courses.cs.washington.edu/courses/cse410/09sp/examples/MIPSCallingConventionsSummary.pdf" rel="nofollow noreferrer">doc</a>。</p> <p><strong>问题 2:为什么 lw $3,4($4) 而不是 lw $3,0($4)</strong></p> <p>这是由于循环的优化。通常,加载和存储的顺序为:</p> <ul> <li>加载源[0]</li> <li>存储目标[0]</li> <li>加载源[1]</li> <li>存储目标[1] ....</li> </ul> <p>您假设循环完全在 <code>$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
数组加载和存储之前,需要将它们的指针分别加载到寄存器</code>和<code>
中。因此,您需要一条指令将 32 位常量地址加载到寄存器中。然而,一条 32 位指令必须包含一些位来编码操作码(指令是什么操作)、目标寄存器等。因此,一条指令剩下不到 32 位来编码常量(称为立即数)。因此,您需要两条指令将一个 32 位常量加载到一个寄存器中,每条指令加载 16 位。lo
/hi
指的是加载常量的一半。例子:假设
dest
在地址0xabcd1234。有两条指令将此值加载到</code>.</p> <pre><code> lui ,%hi(dest) addiu ,,%lo(dest)
lui
是 Load Upper immediate。它将dest
(0xabcd
)地址的高16位加载到</code>的高16位。现在,<code>
的值为 0xabcd0000。addiu
是无符号加法。它将dest
(0x1234
)地址的低16位与</code>中的现有值相加得到<code>
的新值。因此,</code> 现在拥有 0xabcd0000 + 0x1234 = 0xabcd1234,即 <code>dest
的地址。类似地,
的第一个单词lw ,%lo(source)()
从</code>指向的地址加载(它已经保存了<code>source
地址的前16位)偏移量为%lo(source)
(该地址的低 16 位)。实际上,它加载了source
. - 块