---
title: Go汇编迁移
description: "一般情况下，在鲲鹏处理器上搭建好编译系统就能直接编译运行代码，但对于存在Go汇编的情况，即*.s汇编文件，需额外进行迁移。"
url: https://www.hikunpeng.com/document/detail/zh/kunpenggrf/codeprtr/kunpengtaishanporting_12_0113.html
sourcePath: /source/zh/kunpenggrf/codeprtr/kunpengtaishanporting_12_0113.html
indexId: f10bf49c4f9b5d7a0e74bcdaa3024533ece18b7c9997cfc0afec880dee1a2b1f65
---
# Go汇编迁移

一般情况下，在鲲鹏处理器上搭建好编译系统就能直接编译运行代码，但对于存在Go汇编的情况，即*.s汇编文件，需额外进行迁移。

现象描述：

源码包含x86 Go汇编时，编译报错：missing function body。

Go中寄存器包括通用寄存器和伪寄存器，以下重点介绍伪寄存器。

- 伪寄存器
x86平台下Go存在四种伪寄存器FP、PC、SB、SP，实际是对内存位置的一个引用。

  - FP（frame pointer）：帧指针，保存参数和本地变量，用来标识函数参数、返回值
  - SP（stack pointer）：栈指针，存放栈的偏移地址，它指向本地栈帧的顶部
  - SB（static base）：静态基指针，用来表示全局的变量或者函数
  - PC（Program counter）：程序指针，即处理器中常见的PC寄存器，存储需执行指令地址

- 汇编代码迁移
Go编译器会输出一种抽象的汇编代码，这种汇编并不对应某种真实的硬件架构。Go汇编器使用这种伪汇编，为目标硬件生成具体的二进制文件。伪汇编的优点在于更易将Go移植到新的架构上。但即使是伪汇编，x86平台与鲲鹏平台还是存在一些差异，下面列举几点常见的差异：

  - 数据搬运的长度由MOV的后缀决定，同一长度的搬运在不同平台指令有差异，    表1
列出了常见长度在x86与鲲鹏上指令的差异。
**表1 数据搬运的长度在x86与鲲鹏上的指令差异**

| 操作数长度 | x86 | 鲲鹏 |
| --- | --- | --- |
| 8 bytes | MOVQ | MOVD |
| 4 bytes | MOVL | MOVW |
| 2 bytes | MOVW | MOVH |
| 1 byte | MOVB | MOVB |


  - 鲲鹏平台MOVD.W+栈地址偏移，代表前增量，表示栈扩容，对应指令SUBQ +栈地址偏移；同理，MOVD.P+栈地址偏移，代表后增量，表示缩栈，对应x86 ADDQ +栈地址偏移；MOVD.W与MOVD.P在汇编代码中成对出现。
    x86平台下示例：

```
SUBQ     $24, SP      //栈顶指针下移24字节
……                  //中间省略一系列具体功能汇编指令
ADDQ    $24, SP       //栈顶指针上移24字节
```


    鲲鹏平台下示例：

```
MOVD.W   R30, -24(RSP)       //栈顶指针下移24字节
……                         //中间省略一系列具体功能汇编指令
MOVD.P    24(RSP), R30       //栈顶指针上移24字节
```


  - 常用计算指令：| 指令功能 | x86 | 鲲鹏 |
| --- | --- | --- |
| 加法 | ADDQ | ADD |
| 减法 | SUBQ | SUB |
| 乘法 | IMULQ | MUL |
| 比较 | CMPQ | CMP |


    更多指令替换可参考如下链接：

    https://www.slideshare.net/linaroorg/optimizing-golang-for-high-performance-with-arm64-assembly-sfo17314(https://www.slideshare.net/linaroorg/optimizing-golang-for-high-performance-with-arm64-assembly-sfo17314)


示例：

以下示例通过Go汇编实现两个整数相加，对比不同平台下汇编实现的差异。

源代码函数：

```
func Add(a, b int64) int64 {
return a+b
}
```

- x86平台Go汇编实现：
  创建一个add.go文件，此文件中只声明函数，然后在add.go的同一目录下创建add_amd64.s文件，实现如下：

```
TEXT    ·Add+0(SB), $0-24
MOVQ    a+0(FP), BX      // 将参数a的值复制到BX寄存器中
MOVQ    b+8(FP), BP      // 将参数b的值复制到BP寄存器中
ADDQ    BP, BX           // 将BP寄存器与BX寄存器值相加，存入BX寄存器中
MOVQ    BX, ret+16(FP)   // 把BX寄存器的值复制到FP+16的位置, 即返回值的内存位置里
RET                      // 调用RET返回
```


  TEXT标识表示开头，·Add为{package}·{function}格式，如果函数属于当前package，{package}可省略，注意Add前unicode中间点"·"，这是为了包名分隔与函数名，最后的数字$0-24，其中0表示函数栈帧大小为0，24表示参数及返回值的大小；参数是2个int64变量，返回值是1个int64变量，共24字节。

- 鲲鹏平台Go汇编实现：
  创建一个add.go文件，此文件中只声明函数，然后在add.go的同一目录下创建add_arm64.s，实现如下：

```
TEXT    ·Add+0(SB), $0-24  // 解释和上文x86类似
MOVD    a+0(FP), R0
MOVD    b+8(FP), R1
ADD     R1, R0, R0
MOVD    R0, ret+16(FP)
RET
```


  通过对比发现，即使简单的源代码在Go汇编里实现也存在较大差异，如MOV指令、ADD指令和寄存器命名等。

  一般情况下，包含Go汇编的文件指令多且复杂，不同架构差异较大，对Go原理掌握有较高要求，如时间紧急、汇编转换难以实现时，在了解汇编代码实现逻辑前提下，可编写Go代码（非汇编）实现代码功能。
