<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>第 2 章 汇编与调用约定 on Go 语言原本</title>
    <link>/zh-cn/part1overview/ch02asm/</link>
    <description>Recent content in 第 2 章 汇编与调用约定 on Go 语言原本</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <atom:link href="/zh-cn/part1overview/ch02asm/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>2.1 Plan 9 汇编语言</title>
      <link>/zh-cn/part1overview/ch02asm/asm/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/zh-cn/part1overview/ch02asm/asm/</guid>
      <description>&lt;h1 id=&#34;21-plan-9-汇编语言&#34;&gt;2.1 Plan 9 汇编语言&lt;/h1&gt;&#xA;&lt;p&gt;读 Go 运行时的源码，迟早会撞见一种看起来既像汇编、又不太像任何熟悉汇编的代码，那是 Go 的&#xA;&lt;strong&gt;Plan 9 风格汇编&lt;/strong&gt;。剖析调度器、栈切换、原子操作时我们会反复回到它：&lt;code&gt;gogo&lt;/code&gt;、&lt;code&gt;mcall&lt;/code&gt;、&#xA;&lt;code&gt;morestack&lt;/code&gt;、&lt;code&gt;asyncPreempt&lt;/code&gt; 都是汇编例程。本节解释它是什么、为何存在、以及读懂它需要的几个&#xA;关键概念。我们不求让读者会写 Plan 9 汇编，那是另一本书的内容，但求把它变成一份&lt;strong&gt;阅读词汇表&lt;/strong&gt;：&#xA;当后文提到某个汇编例程时，读者知道它身处的是怎样一个抽象层，每个符号各指向什么。&lt;/p&gt;&#xA;&lt;p&gt;本节示例取自 &lt;code&gt;runtime/asm_amd64.s&lt;/code&gt; 与 &lt;code&gt;internal/runtime/atomic&lt;/code&gt;。这些例程在各版本间相当稳定，&#xA;为聚焦设计，我们对个别与本节无关的实验性分支（如 &lt;code&gt;GOEXPERIMENT&lt;/code&gt; 守卫）做了裁剪，完整定义&#xA;请对照对应源文件。&lt;/p&gt;&#xA;&lt;h2 id=&#34;211-为什么-go-要有自己的汇编&#34;&gt;2.1.1 为什么 Go 要有自己的汇编&lt;/h2&gt;&#xA;&lt;p&gt;Go 的汇编源自 &lt;strong&gt;Plan 9 操作系统&lt;/strong&gt;的汇编器传统。Plan 9 是贝尔实验室在 Unix 之后的研究型系统，&#xA;Go 的几位设计者（Ken Thompson、Rob Pike、Russ Cox）正是从那里走来，把那套工具链的思路一并带进了&#xA;Go。它的关键特征是：&lt;strong&gt;它不是某一种 CPU 的原生汇编，而是一种半抽象、跨架构统一的中间汇编&lt;/strong&gt;。&#xA;同一套语法、同一批伪指令与伪寄存器，由工具链 &lt;code&gt;cmd/asm&lt;/code&gt; 翻译到 amd64、arm64、riscv64 等各目标&#xA;架构的真实指令。这与 GNU as 那种「每个架构一套方言」的设计截然不同。&lt;/p&gt;&#xA;&lt;p&gt;这个选择服务于 Go 最初的一个硬目标：&lt;strong&gt;一套工具链、交叉编译开箱即用&lt;/strong&gt;。当汇编层本身就是跨架构的，&#xA;为新架构移植运行时就退化成「为这套统一语法补一个后端」，而不是「重学一门汇编再重写一遍运行时」。&#xA;代价同样真实：Go 要自己维护汇编器、链接器与目标文件格式（&lt;code&gt;cmd/internal/obj&lt;/code&gt;），多养一整套基础设施。&#xA;买到的是对代码生成、调用约定、与运行时协同的&lt;strong&gt;完全掌控&lt;/strong&gt;，这条「自己造而非复用现成件」的取舍，&#xA;会在 &lt;a href=&#34;.././callconv&#34;&gt;2.3 调用约定&lt;/a&gt; 的自定义 ABI 与 &lt;a href=&#34;../../../part2lang/ch06func/func&#34;&gt;6.1&lt;/a&gt; 的函数调用里&#xA;再次出现，是理解 Go 运行时的一条主线。&lt;/p&gt;</description>
    </item>
    <item>
      <title>2.2 汇编中的栈帧与符号</title>
      <link>/zh-cn/part1overview/ch02asm/frame/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/zh-cn/part1overview/ch02asm/frame/</guid>
      <description>&lt;h1 id=&#34;22-汇编中的栈帧与符号&#34;&gt;2.2 汇编中的栈帧与符号&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;.././asm&#34;&gt;2.1&lt;/a&gt; 给出了 Plan 9 汇编的伪寄存器与寻址语法，那是「怎么写一行汇编」。本节把视线&#xA;抬高到一段完整例程：一个手写汇编函数如何用 &lt;code&gt;TEXT ·name(SB)&lt;/code&gt; 声明自己的符号，如何用 &lt;code&gt;$framesize-argsize&lt;/code&gt;&#xA;申明栈帧大小，&lt;code&gt;NOSPLIT&lt;/code&gt; 又意味着什么。我们用运行时里三段真实例程（&lt;code&gt;Cas&lt;/code&gt;、&lt;code&gt;gogo&lt;/code&gt;、&lt;code&gt;morestack&lt;/code&gt;）&#xA;把这些约定逐处对上号，读完之后，调度器与栈管理章节里那些汇编符号就从天书变成可读的现场操作。&lt;/p&gt;&#xA;&lt;h2 id=&#34;221-从-go-源码到-plan-9-汇编cas-的逐槽对应&#34;&gt;2.2.1 从 Go 源码到 Plan 9 汇编：CAS 的逐槽对应&lt;/h2&gt;&#xA;&lt;p&gt;把抽象落到实处。运行时的原子比较交换 &lt;code&gt;Cas&lt;/code&gt; 在 amd64 上是手写汇编，它的 Go 原型与汇编实现&#xA;逐字段对得上，是观察「Go 源码如何映射到 Plan 9 汇编」的好样本。Go 这一侧只有声明（函数体在&#xA;&lt;code&gt;.s&lt;/code&gt; 文件里）：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// internal/runtime/atomic：仅声明，实现在 atomic_amd64.s&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;//&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//&#x9;if *ptr == old { *ptr = new; return true } else { return false }&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Cas&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ptr&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;uint32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;old&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;uint32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;对应的 amd64 实现：&lt;/p&gt;</description>
    </item>
    <item>
      <title>2.3 调用约定与寄存器 ABI</title>
      <link>/zh-cn/part1overview/ch02asm/callconv/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/zh-cn/part1overview/ch02asm/callconv/</guid>
      <description>&lt;h1 id=&#34;23-调用约定与寄存器-abi&#34;&gt;2.3 调用约定与寄存器 ABI&lt;/h1&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;文中的寄存器名、栈帧布局与序言代码以 amd64 为例，&#xA;其余体系结构（arm64、riscv64 等）结构相同而寄存器名不同，可对照&#xA;&lt;code&gt;src/cmd/compile/abi-internal&lt;/code&gt; 的「Architecture specifics」一节。&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;一次函数调用，在机器层面要回答一连串具体的问题：参数放哪里，返回值放哪里，谁负责保存哪些&#xA;寄存器，栈帧怎样布局，返回地址压在何处。把这些问题的答案固定下来，就是调用规范（calling&#xA;convention），它也常被称作 ABI（application binary interface）。ABI 不是某一段代码，而是一份&#xA;&lt;strong&gt;契约&lt;/strong&gt;：编译器生成的代码、手写的 Plan 9 汇编（&lt;a href=&#34;.././asm&#34;&gt;2.1&lt;/a&gt;）、运行时里的底层例程，三方各自&#xA;独立编写，却要在调用的那一刻严丝合缝地对接。契约一旦写定，三方就都按它来摆放数据，谁也不必&#xA;知道对方的内部细节。&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;../../../part2lang/ch06func/func&#34;&gt;6.1&lt;/a&gt; 已从语言视角讲过函数调用从栈到寄存器的演进，那里关心的是&#xA;「一次 &lt;code&gt;f(a, b)&lt;/code&gt; 在 Go 语义上发生了什么」。本节与 &lt;a href=&#34;.././args&#34;&gt;2.4 参数传递与栈帧布局&lt;/a&gt; 补足它在汇编与&#xA;运行时层面的另一半：同一次调用，落到机器指令上是如何约定的。两者合起来，函数调用这件事才算讲完整。&lt;/p&gt;&#xA;&lt;h2 id=&#34;231-两套-abiabi0-与-abiinternal&#34;&gt;2.3.1 两套 ABI：ABI0 与 ABIInternal&lt;/h2&gt;&#xA;&lt;p&gt;Go 同时维护着两套调用规范，理解它们的分工是读懂运行时里那些奇怪符号标注的钥匙。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;ABI0&lt;/strong&gt; 是早期的、&lt;strong&gt;基于栈&lt;/strong&gt;的规范：所有参数与返回值一律通过栈内存传递，调用方在自己的栈帧里&#xA;按顺序摆好参数，被调用方从固定偏移处取用。它的好处是布局稳定、可预测，人脑能算清每个参数的&#xA;偏移。手写汇编因此一律遵循 ABI0，&lt;code&gt;go.dev/doc/asm&lt;/code&gt; 描述的就是这套稳定 ABI。ABI0 还有一个常被&#xA;忽略的性质：它是 Go 唯一&lt;strong&gt;承诺稳定&lt;/strong&gt;的 ABI，是汇编与 Go 之间唯一可靠的接缝。&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;ABIInternal&lt;/strong&gt; 是 Go 1.17 引入的、&lt;strong&gt;基于寄存器&lt;/strong&gt;的内部规范：尽量把参数与返回值放进寄存器，省去&#xA;大量进出栈内存的读写。所有由 Go 源码编译出的函数都走 ABIInternal。它的名字里「Internal」二字是&#xA;郑重的警告：这套 ABI &lt;strong&gt;不稳定&lt;/strong&gt;，会随 Go 版本变化，任何外部代码都不应依赖它的细节。换来的是&#xA;约 5% 的整体提速，且这份提速对用户代码完全透明，源码一字不改，重新编译即享。&lt;/p&gt;</description>
    </item>
    <item>
      <title>2.4 参数传递与栈帧布局</title>
      <link>/zh-cn/part1overview/ch02asm/args/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/zh-cn/part1overview/ch02asm/args/</guid>
      <description>&lt;h1 id=&#34;24-参数传递与栈帧布局&#34;&gt;2.4 参数传递与栈帧布局&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;.././callconv&#34;&gt;2.3&lt;/a&gt; 给出了两套 ABI 的分工与参数分配的递归算法，那是「规则」。本节把规则落到&#xA;一个具体例子上：一个同时含标量与数组的签名，参数究竟在栈帧里怎样摆放，寄存器参数为何还要在&#xA;栈上留一格溢出槽，序言里那条几乎人人都付的栈增长检查又是怎么搭上抢占的便车。最后我们回到&#xA;「为何自定义 ABI」这个总账，把这两节合成一个完整的回答。&lt;/p&gt;&#xA;&lt;h2 id=&#34;241-一个掺杂栈传与寄存器传的例子&#34;&gt;2.4.1 一个掺杂栈传与寄存器传的例子&lt;/h2&gt;&#xA;&lt;p&gt;抽象规则不如一个例子来得清楚。取 ABI 规范里那个精心设计的签名，它故意同时含有能进寄存器的&#xA;标量与必须走栈的数组：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;uint8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;uintptr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;uint8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;r1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;uintptr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;y&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;uintptr&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;r2&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;按 &lt;a href=&#34;.././callconv&#34;&gt;2.3.1&lt;/a&gt; 的递归分配，在 amd64（整数寄存器 &lt;code&gt;RAX, RBX, ...&lt;/code&gt;）上结果是：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;a1&lt;/code&gt;、&lt;code&gt;a3&lt;/code&gt; 是能进寄存器的标量，分到 &lt;code&gt;RAX&lt;/code&gt;、&lt;code&gt;RBX&lt;/code&gt;；&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;a2&lt;/code&gt; 是非平凡数组，&lt;strong&gt;整个退回栈上&lt;/strong&gt;；&lt;/li&gt;&#xA;&lt;li&gt;返回值 &lt;code&gt;r1&lt;/code&gt; 含数组，也走栈；&lt;code&gt;r2&lt;/code&gt; 是 &lt;code&gt;string&lt;/code&gt;，拆成 base 与 len 两部分，回填 &lt;code&gt;RAX&lt;/code&gt;、&lt;code&gt;RBX&lt;/code&gt;&#xA;（返回时寄存器重新从头计数，与入参不冲突）。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;于是入口时只有 &lt;code&gt;a2&lt;/code&gt; 在栈上有初值，栈帧布局如下，其余区域调用时一律不初始化：&lt;/p&gt;&#xA;&#xA;&lt;script src=&#34;/mermaid.min.js&#34;&gt;&lt;/script&gt;&#xA;&lt;script src=&#34;/mermaid-init.js&#34;&gt;&lt;/script&gt;&#xA;&#xA;&#xA;&lt;pre class=&#34;mermaid&#34;&gt;flowchart TB&#xA;    spill[&amp;#34;a3Spill uint8&amp;lt;br/&amp;gt;a1Spill uint8&amp;lt;br/&amp;gt;（寄存器参数的溢出槽，调用方预留，调用时空着）&amp;#34;]&#xA;    ret[&amp;#34;r1.y [2]uintptr&amp;lt;br/&amp;gt;r1.x uintptr&amp;lt;br/&amp;gt;（栈传返回值）&amp;#34;]&#xA;    arg[&amp;#34;a2 [2]uintptr&amp;lt;br/&amp;gt;（栈传参数，唯一带初值者）&amp;#34;]&#xA;    spill --&amp;gt; ret --&amp;gt; arg&#xA;    low[&amp;#34;↓ 低地址&amp;#34;]&#xA;    arg -.-&amp;gt; low&lt;/pre&gt;&#xA;&lt;p&gt;这个例子把三件事一次说清：标量优先进寄存器，含数组的值整体走栈，而&lt;strong&gt;每个寄存器参数仍在栈上&#xA;留有一格溢出槽&lt;/strong&gt;待命。把它和 &lt;a href=&#34;.././callconv&#34;&gt;2.3.1&lt;/a&gt; 那条「全栈传」的 ABI0 对照，差别一目了然：ABI0&#xA;下 &lt;code&gt;a1&lt;/code&gt;、&lt;code&gt;a3&lt;/code&gt; 连同 &lt;code&gt;a2&lt;/code&gt; 全在栈上按序排开，没有寄存器、也无需溢出槽；ABIInternal 把能搬的都搬进了&#xA;寄存器，省下的正是那几次进出栈内存的读写。&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
