ChufanSuki
ChufanSuki

Reputation: 93

What %lo(source)($6) and .frame mean in assembly code?

I assemble a simple c program to mips and try to understand the assembly code. By comparing with c code, I almost understand the it but still get some problems.

I use mips-gcc to generate assembly code: $ mips-gcc -S -O2 -fno-delayed-branch -I/usr/include lab3_ex3.c -o lab3_ex3.s

Here is my guess about how the assembly code works:

main is the entry of the program.

$6 is the address of source array.

$7 is the address of dest array.

$3 is the size of source array.

$2 is the variable k and is initialized to 0.

$L3 is the loop

$5 and $4 are addresses of source[k] and dest[k].

sw $3,0($5) is equivalent to store source[k] in $3.

lw $3,4($4) is equivalent to assign source[k] to dest[k].

addiu $2,$2,4 is equivalent to k++.

bne $3, $0, $L3 means that if source[k] is zero then exits the loop otherwise jump to lable $L3.

$L2 just do some clean up work.

Set $2 to zero.

Jump to $31 (return address).

My problems is:

  1. What .frame $sp,0,$31 does?
  2. Why lw $3,4($4) instead of lw $3,0($4)
  3. What is the notation%lo(source)($6) means? ($hi and $lo$ registers are used in multiply so why they are used here?)

Thanks.

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;
}

Assembly

.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"

Upvotes: 0

Views: 727

Answers (1)

TSG
TSG

Reputation: 887

Firstly, main, $L3 and $L2 are labels for 3 basic blocks. You are roughly correct about their functions.

Question 1: What is .frame doing

This is not a MIPS instruction. It is metadata describing the (stack) frame for this function:

  • The stack is pointed to by $sp, an alias for $29.
  • and the size of the stack frame (0, since the function has neither local variables, nor arguments on the stack). Further, the function is simple enough that it can work with scratch registers and does not need to save callee-saved registers $16-$23.
  • the old return address ($31 for MIPS calling convention)

For more information regarding the MIPS calling convention, see this doc.

Question 2: Why lw $3,4($4) instead of lw $3,0($4)

This is due to an optimization of the loop. Normally, the sequence of loads and stores would be :

  • load source[0]
  • store dest[0]
  • load source[1]
  • store dest[1] ....

You assume that the loop is entirely in $L3, and that contains load source[k] and store dest[k]. It isn't. There are two clues to see this:

  • There is a load in the block main which does not correspond to any load outside the loop
  • Within the basic block $L3, the store is before the load.

In fact, load source[0] is performed in the basic-block named main. Then, the loop in the basic block $L3 is store dest[k];load source[k+1];. Therefore, the load uses an offset of 4 more than the offset of the store, because it is loading the integer for the next iteration.

Question 3: What is the lo/hi syntax?

This has to do with instruction encodings and pointers. Let us assume a 32-bit architecture, i.e. a pointer is 32 bits. Like most fixed-size instruction ISAs, let us assume that the instruction size is also 32 bits.

Before loading and storing from the source/dest arrays, you need to load their pointers into registers $6 and $7 respectively. Therefore, you need an instruction to load a 32-bit constant address into a register. However, a 32-bit instruction must contain a few bits to encode opcodes (which operation the instruction is), destination register etc. Therefore, an instruction has less than 32 bits left to encode constants (called immediates). Therefore, you need two instructions to load a 32-bit constant into a register, each loading 16 bits. The lo/hi refer to which half of the constant is loaded.

Example: Assume that dest is at address 0xabcd1234. There are two instructions to load this value into $7.

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

lui is Load Upper immediate. It loads the top 16 bits of the address of dest (0xabcd) into the top 16 bits of $7. Now, the value of $7 is 0xabcd0000.

addiu is Add Immediate Unsigned. It adds the lower 16 bits of the address of dest (0x1234) with the existing value in $7 to get the new value of $7. Thus, $7 now holds 0xabcd0000 + 0x1234 = 0xabcd1234, the address of dest.

Similarly, lw $3,%lo(source)($6) loads from the address pointed to by $6 (which already holds the top 16 bits of the address of source) at an offset of %lo(source) (the bottom 16 bits of that address). Effectively, it loads the first word of source.

Upvotes: 5

Related Questions