如何在MIPS中推送和弹出堆栈上的地址

时间:2015-04-06 17:15:20

标签: assembly compiler-construction mips mips32

我正在为我的编译器课程进行一项任务,而且我一直在盯着我生成的mips(32位)代码,这些代码试图弄清楚它有什么问题但无济于事。在生成mips代码之后,我在我们的讲师提供的mips VM上使用gcc编译它。有人告诉我要使用像printf这样的一些C stdlib函数,我很确定部分代码是正确的(因为我们被指示这样做)我主要是从gcc的类似输出中偷走它C代码。

以下是mips代码:

  1. 在堆栈上为2个整数变量创建空间
  2. 分别将它们初始化为5和10(用于测试目的)
  3. 通过将绝对地址推入堆栈然后将其弹出并访问它来打印它们。
  4. 目前发生的事情是第二个printf似乎打印存储在堆栈第一个变量空间中的值而不是第二个变量的值。

    (代码在使用常量int时完全有效,所以我相信printf是完全正确的)

            .data
    printf_string:    .asciiz  "%d\n"
    scanf_string:    .asciiz  "%d"
    
        .text
        .globl main
    
    main:
    # make space for two ints on stack
        addiu   $sp, $sp, -8
    # store return address in a saved register
    # was going to push it onto the stack but until I figure out this issue I'm
    # being lazy and just saving it to a saved register
        move    $s0, $ra
    # make a copy of the stack pointer - likely not needed
        move    $s1, $sp
    
    # typically here i loop and initialize the 2 ints on the stack but for now I'm
    # doing it manually so I can figure out this issue with less possible things
    # that could be wrong
    
    # load some value into the register so I can store it
        li      $t7, 5
    # store into first variable
        sw      $t7, 0($sp)
    # different so I can tell if printing location works
        li      $t7, 10
    # store into second variable
        sw      %t7, 4($sp)
    
    instructions:
    
    ########################################
    ### CODE BELOW PRINTS FIRST VARIABLE ###
    ########################################
    
    # appears to work...
    
    # load standard library pointer and stuff (copied from gcc output)
    # everything below works IF you did something like
    # "WRITE 5" instead of "WRITE a"
        lui     $gp, %hi(__gnu_local_gp)
        addiu   $gp, %lo(__gnu_local_gp)
        lw      $t9, %call16(printf)($gp)
        .cprestore  16
        nop # needed after load word :-/
    
    # load print_string address - works 
        la      $4, printf_string
    # Here's where problems start
    # make space for location of visited variable
        addiu   $sp, $sp, -4
    # initialize $t0 to top of stack
        move    $t0, $s1
    # add offset of variable to $t0
        addiu   $t0, $t0, 0
    # store absolute memory address of variable to stack
        sw      $t0, 0($sp)
    
    # load absolute memory address of variable from stack
        lw      $t0, 0($sp)
        nop # needed after lw
    # undo stack allocation
        addiu   $sp, $sp, 4
    # load MEM[$t0 + 0] into $5 (arg 2)
        lw      $5, 0($t0)
        nop
    # finally call printf
        jalr    $t9
        nop
    
    #########################################
    ### CODE BELOW PRINTS SECOND VARIABLE ###
    #########################################
    
    # appears to print the value stored in the first variable
    # if I add "sw $s5, 4($sp)" here then it DOES work so I'm just very confused
    
    # everything here should be basically the same as above but with a different
    # offset/address pushed, popped, and accessed
    
    
        lui     $gp, %hi(__gnu_local_gp)
        addiu   $gp, %lo(__gnu_local_gp)
        lw      $t9, %call16(printf)($gp)
        .cprestore  16
        nop
    
        la      $4, printf_string
        addiu   $sp, $sp, -4
        move    $t0, $s1
        addiu   $t0, $t0, 4
        sw      $t0, 0($sp)
    
        lw      $t0, 0($sp)
        nop
        addiu   $sp, $sp, 4
        lw      $5, 0($t0)
        nop
        jalr    $t9
        nop
    
    
        addiu   $sp, $sp, 8
        move    $ra, $s0
        jr      $ra
        nop
    

    如果有人能找到任何看起来不合适的东西,那么我会非常感激!

1 个答案:

答案 0 :(得分:0)

这只是一个建议,而不是一个坚定的答案,你可能根本就没有给printf打电话。我注意到在将参数的地址写入堆栈之后,然后在调用printf之前恢复堆栈指针。因此,在这两个示例中,堆栈指针在调用printf时指向原始的第一个参数,这可以解释为什么它在两种情况下都会打印第一个参数。

sw      $t0, 0($sp)
lw      $t0, 0($sp)
nop
addiu   $sp, $sp, 4
...
# finally call printf
jalr    $t9