保存 Caller saved 寄存器 的动作,是硬件行为,还是软件行为? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
amiwrong123

保存 Caller saved 寄存器 的动作,是硬件行为,还是软件行为?

  •  
  •   amiwrong123 2023 年 6 月 23 日 2053 次点击
    这是一个创建于 1037 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我们知道 arm 体系或 x86 体系中(我比较关心 arm ),在进行函数调用时,会区分 Caller saved 和 Callee saved (就是把通用寄存器划分为两个范围)。

    软件行为我是指:我在汇编代码中能够看到保存的过程(就是入栈和出栈)。比如在被调用的函数( Callee )里如果改变了 Callee saved 寄存器,那么在 这个函数里的开头结尾,就会分别出一个 入栈保存和出栈恢复 的操作(如果没有改变 Callee saved 寄存器,那么就不会多这两个操作)。

    硬件行为我是指:是 CPU 自动做的,不是我在汇编代码里面能看到的指令。

    • 然后我现在理解,保存 Callee saved 寄存器的行为,应该是一个软件行为。
    • 但,存 Caller saved 寄存器的行为,是什么行为呢?如果能附上 arm 文档链接和原话就更好了。
    16 条回复    2023-06-25 09:51:16 +08:00
    thinkm
        1
    thinkm  
       2023 年 6 月 23 日
    函数调用是硬件做的,例如像是 RTOS 之类的操作系统入栈出栈是软件做的。
    多年没搞了,记忆中是这样
    leonshaw
        2
    leonshaw  
       2023 年 6 月 23 日
    软件行为,caller 自己才知道哪些需要保存
    bugu1986
        3
    bugu1986  
       2023 年 6 月 23 日   1
    os 做的
    bugu1986
        4
    bugu1986  
       2023 年 6 月 23 日
    trap 把执行权从硬件给软件
    feather12315
        5
    feather12315  
       2023 年 6 月 23 日 via Android
    一部分硬件,一部分软件。

    准确地说:
    通用寄存是是软件行为,特定的寄存器是硬件行为。
    比如函数调用是软件行为,中断异常的寄存器保存( amd64 下的 rip 、返回地址)是硬件行为
    feather12315
        6
    feather12315  
       2023 年 6 月 23 日 via Android
    arm 的话,返回地址是 r13 还是 r12 。
    这个要查手册页了,programming manual 有详细的介绍( arm 的手册很多,要仔细找找,有个 guide 简述了上述过程
    amiwrong123
        7
    amiwrong123  
    OP
       2023 年 6 月 23 日
    @thinkm
    @leonshaw
    @bugu1986
    @feather12315
    之所以我能确信,保存 Callee saved 寄存器的行为,是一个软件行为。是因为我在编译后查看汇编文件,发现大多数函数的实现,在开头部分有 PUSH {r4-r11, lr},在结束部分有 POP {r4-r11, PC}。即我发现了汇编中的 保存 Callee saved 寄存器的行为。

    但我没有找到汇编中,保存 Caller saved 寄存器的行为。虽然我也认为,应该是一个软件行为(最起码在函数调用中,应该是的。在响应中断时,就得另当别论了)。有找到两处 POP {r0}的用法( r0 是 Caller save 的),但也不是正常的函数调用时在用(我希望是调用了 POP {r0}后,马上调用函数,但并不是这么用的),其中一个用的地方是__rt_exit 和_aeabi_uldivmod
    amiwrong123
        8
    amiwrong123  
    OP
       2023 年 6 月 23 日
    @thinkm
    @leonshaw
    @bugu1986
    @feather12315

    另外,我猜是不是应该这么理解?

    保存 Caller saved 寄存器的行为是一个可选项,如果在函数调用后 不需要使用到 Caller saved 寄存器,那么在调用之前,也就不需要有保存 Caller saved 寄存器的行为了。

    Caller saved registers If the
    data in these registers needs to be used after a C function call, the caller needs to save it
    before calling a C function.
    文档原话如上。
    leonshaw
        9
    leonshaw  
       2023 年 6 月 23 日
    @amiwrong123 对,不用就不需要保存,callee-saved 如果不去动它也不需要保存。区别是 callee-saved 在入口和出口保存 /恢复一次,caller-saved 每次调用前后都要保存 /恢复。寄存器分配算法一般会尽量避免这个开销。
    amiwrong123
        10
    amiwrong123  
    OP
       2023 年 6 月 23 日
    @feather12315
    特定的寄存器是硬件行为
    =======
    确实,汇编中找不到更新 LR 寄存器的指令(即函数调用前,把函数调用的下一个指令 作为返回地址)。但这个事应该是硬件来做的。
    feather12315
        11
    feather12315  
       2023 年 6 月 23 日 via Android
    @amiwrong123 #8 right 准确地来讲这是被优化后的代码,如果禁止优化的话 O0 应该就不这样了(没测试,可以看看)


    @amiwrong123 #10 是的。这个要查手册页才能确定,而且贼琐碎。
    churchmice
        12
    churchmice  
       2023 年 6 月 23 日
    你都说堆栈了,这个东西硬件是无法帮你保存的,save/restore 都是软件来做的,这个就是俗称的 context switch 了

    硬件的话有一种叫做 banking 机制,单这个东西更多的是中断处理相关,比如 cpu 的 r0,r1...r15 寄存器,硬件可以帮你做两套,典型的在 FIQ 发生的时候,硬件进 FIQ 的时候有单独的 r0-r15 寄存器,所以软件就无需自己把 r0-r15 save 到堆栈上面

    总结一下
    硬件何德何能能知道你的堆栈开在什么地方,在堆栈上的 save/restore 都是需要软件调用指令来完成的,硬件唯一能做的是 banking,在进 FIQ 的时候能让 sw 不用把 r0-r15 存到堆栈上面去

    当然,的确也有人做 hw based save/restore,主要是嫌弃 context switch 的时候软件做太慢了,但这种都是私有实现
    detached
        13
    detached  
       2023 年 6 月 23 日
    肯定是软件做的。这个问题主要出在汇编里面的函数之间相互调用。硬件是没有办法知道的,只能是软件。这里的软件具体是编译器,在生成代码的时候写入对应的汇编语句。
    如果是你自己写的汇编程序,是不需要考虑这个问题的,因为你作为 programmer ,是明确知道哪些寄存器的值是不能改变的,哪些是临时的可以被服用。
    简而言之,caller save or callee save 都是 conventon ,目的是为了让不同的汇编程序之间相互兼容,对硬件是完全透明的。
    detached
        14
    detached  
       2023 年 6 月 23 日
    反驳一下 os 做的这个说法。
    os 只有在 context switch 的时候才会保存现场,也就是将所有寄存器的值存在某一块内存( kernel 的栈上)。这里寄存器的保存会发生在同一个进 /线程的函数调用中,与 context switch ,即从一个进 /线程切换到另一个中是两个不同的概念,前者只是正常的函数调用,后者需要 system call trap 到 kernel 中。
    bugu1986
        15
    bugu1986  
       2023 年 6 月 24 日
    @amiwrong123 可选,但为了函数调用一定得加
    mengzhuo
        16
    mengzhuo  
       2023 年 6 月 25 日
    clobber 是软件行为,楼上说的 os 是不对的。

    op 你说的是 ps ABI 的行为,手册在这 https://github.com/ARM-software/abi-aa/releases/download/2023Q1/aapcs32.pdf (仔细读 6.3 章) 。说大家都一样的,你看看隔壁 Go 用不用这个 ABI ?

    而且多看看历史就知道为啥了,早期比如 6065 就 1 个累加寄存器( ALU ) 2 个辅助寄存器,SP 、PC 、FLAG 一共 6 个寄存器,各种数据就尽量塞内存上的,比如栈地址,程序返回地址什么的。
    后来寄存器越来越多才导致了部分数据可以直接放寄存器上,渐渐才有了今天这样 ps ABI 的结果。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     899 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 51ms UTC 22:35 PVG 06:35 LAX 15:35 JFK 18:35
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86