了解SPARC程序集中函数调用的基本示例

you*_*t13 9 c assembly sparc mov

我正在学习SPARC程序集,您可以在下面看到一个简单的示例.我有几个关于这个例子的问题,它显示了一个过程的传递参数.

在主要部分中,我设置5为第一个输入参数%o07第二个输入参数%o1.之后,我将这些寄存器的总和放入其中%o2.然后,我调用"测试"功能,我打印这个总和.

fmt0:
  .asciz "%d\n"
  .align 4

.global main

main:
  save %sp, -64, %sp
  mov 5, %o0
  mov 7, %o1
  add %o0, %o1, %o2
  !st %o2, [%fp-4]   for storing %o2 at adress %fp-4 
  call test
  nop
  ret

test:
  mov %i2, %o1
  !ld [%fp-4], %o1   for loading from %fp-4 into %o1
  set fmt0, %o0
  call printf
  nop
  ret
Run Code Online (Sandbox Code Playgroud)

使用上面的代码,它打印值"-273929364"而不是"12"(用gcc编译).

似乎" mov %i2, %o1"不起作用.我知道,%o2在主要部分寄存器成为%i2在调用的过程,但我为什么不能直接设置的值%i2%o1寄存器这个" mov"指令?

第二个问题:如果我取消注释st %o2, [%fp-4]主要部分中的指令" "和d [%fp-4], %o1测试功能中的"l "和注释" mov %i2, %o1",它会正确打印"12".我们如何知道作为传递参数函数的正确偏移?

从我所看到的,%sp变成%fp了" save %sp, -64, %sp"之后?具有%fp相同的价值main sectiontest function

最后,我在不同的例子中看到了指令" call function, 0"或" call printf, 0":为什么我必须在函数名称之后添加"0"?这是返回值(如同int main(void){ ... return 0;})?

谢谢你的帮助

Eld*_*mov 11

我知道主要部分中的%o2寄存器在调用过程中变为%i2,但为什么我不能用这个"mov"指令直接将%i2的值设置到%o1寄存器中?

%o寄存器只%i在完成之后save,通常在被调用的函数的开头.在您的示例中,test函数没有save/ restore.

它是save/ restore旋转寄存器窗口,而不是call/ ret.既然test不是叶子函数(它printf从里面调用),它必须有自己的寄存器窗口.所以你必须test使用save/ 包装函数restore:

test:
  save %sp, -64, %sp
  mov %i2, %o1
  set fmt0, %o0
  call printf
   nop
  ret
   restore
Run Code Online (Sandbox Code Playgroud)

否则,您的参数仍可通过%i2,但无论如何代码都是错误的,因为call printf指令会破坏test存储的返回地址%o7.

UPD.

关于编辑建议中的问题(BTW不这样做,请在评论中提问):

如果%o7在非叶子程序中被覆盖,如何规避这个问题?我认为我必须在另一个寄存器的非叶子程序的开头推动%o7并在结尾弹出它,即在嵌套程序的调用之后,是不是?

在非叶子程序的情况下没有问题:save/ restore做技巧.您可能会认为save是"批处理"推送:它为您提供了一个新的注册窗口 - 一组16个寄存器(8 %i+ 8 %l),它们在嵌套的过程调用中保留它们的值.因此,restore将您带回以前保存的窗口.

所有%o寄存器均可%i在新窗口中访问.也就是说,调用者将参数设置为%o0 .. %o5(最多6个,因为%o6%和%o7是为堆栈指针和返回地址保留的).Callee制作save并获得了参数%i0 .. %i5.如果它想要返回一个值,它会将其放入%i0.返回后restore,调用者可以看到返回值(如果有)%o0.

这也回答了另一个你的问题:

从我看到,%sp在"save%sp,-64,%sp"指令后成为%fp?在主要部分和测试功能中%fp具有相同的值吗?

%sp只是别名%o6,而且%fp- for %i6.除了旋转窗口,save还可以像顺序add指令一样添加值.save %sp, -64, %sp意味着:取一个值,%sp旧的窗口,窗口旋转(%sp%fp),加-64至该值并把结果变成%sp一个的窗口.

换一种说法,

save %sp, -64, %sp
Run Code Online (Sandbox Code Playgroud)

做同样的事

save
add %fp, -64, %sp  ! notice that the source register is now %fp, not %sp
Run Code Online (Sandbox Code Playgroud)

BTW,-64就是寄存器窗口的大小(16个寄存器,每个4个字节).它是负面的,因为堆栈增长了.

这是一个很好的答案,解释了SPARC寄存器窗口的概念.

UPD.2

刚刚注意到你的"寻找可靠和/或官方来源的答案"声明.SPARC v8架构手册是必读的,特别是涉及延迟槽,寄存器窗口,叶子程序优化和软件考虑因素的章节.

  • 在我的代码中,`restore`在`ret`的延迟槽中执行(注意一个空格缩进).但是你可以先执行`restore`,但在这种情况下你应该使用`retl`(它从`%o7`获取返回地址),而不是`ret`(`%i7`). (2认同)