在Go代码中是否可以包含内联汇编?

huangapple go评论91阅读模式
英文:

Is it possible to include inline assembly in Go code?

问题

在Go代码中是否可以包含内联汇编?

这篇博客文章展示了将Go编译为单独的.s文件并进行编辑,但不支持像许多C编译器那样将内联汇编作为Go函数的一部分。

英文:

Is it possible to include inline assembly in Go code?

This blog post shows compiling Go to a separate .s file and editing it, but not inline asm as part of a Go function like many C compilers support.

答案1

得分: 54

没有对内联汇编的支持,但是你可以通过C语言链接汇编代码,使用cgo编译,并使用import "C",就像gmp.go中的示例一样。你也可以使用与Go直接兼容的汇编风格,例如asm_linux_amd64.s,但是函数名必须以"·"开头。

或者,你可以使用nasm和gccgo,这是我目前最喜欢的方式。(注意,Nasm似乎不支持以"·"开头的函数名)。

这是一个可工作的"hello world"示例:

hello.asm:

; 基于nasm的hello.asm

	SECTION .data		; 数据段
msg:	db "Hello World",10	; 要打印的字符串,10=回车
len:	equ $-msg		; "$"表示当前位置
				; len是一个值,不是一个地址

	SECTION .text		; 代码段

global go.main.hello		; 使标签对链接器(Go)可用
go.main.hello:

	; --- 设置堆栈帧
	push rbp			; 保存旧的基指针
	mov rbp,rsp   ; 使用堆栈指针作为新的基指针
	
	; --- 打印消息
	mov	edx,len		; arg3,要打印的字符串的长度
	mov	ecx,msg		; arg2,指向字符串的指针
	mov	ebx,1		; arg1,写入的位置,屏幕
	mov	eax,4		; 将sysout命令写入int 80 hex
	int	0x80		; 中断80 hex,调用内核
	
	; --- 拆除堆栈帧
	mov rsp,rbp  ; 使用基指针作为新的堆栈指针
	pop rbp      ; 获取旧的基指针

	; --- 返回
	mov rax,0		; 错误码0,正常,无错误
	ret         ; 返回

main.go:

package main

func hello();

func main() {
	hello()
    hello()
}

还有一个方便的Makefile:

main: main.go hello.o
	gccgo hello.o main.go -o main

hello.o: hello.asm
	nasm -f elf64 -o hello.o hello.asm

clean:
	rm -rf _obj *.o *~ *.6 *.gch a.out main

我在main.go中两次调用了hello(),只是为了确保hello()正确返回。

请注意,在Linux上直接调用中断80h不被认为是良好的风格,调用C语言编写的函数更加"未来证明"。还请注意,这是特定于64位Linux的汇编代码,不具备平台无关性。

我知道这不是对你问题的直接回答,但这是我所知道的在Go中使用汇编的最简单的方法,因为没有内联汇编。如果你真的需要内联汇编,可以编写一个脚本,从源文件中提取内联汇编,并按照上述模式进行准备。够接近了吗?:)

Go、C和Nasm的快速示例:gonasm.tgz

**更新:**较新版本的gccgo需要使用-g标志,并且只需要"main.hello"而不是"go.main.hello"。这是一个更新后的Go、C和Yasm示例:goyasm.tgz

英文:

There is no support for inline assembly, but you can link with code written in assembly through C, compiling with cgo and using import "C", like in gmp.go. You could alternatively write in a style of assembly that is directly compatible with Go, like in asm_linux_amd64.s, that requires that function names start with "·".

Or, you can use nasm and gccgo, my favorite way so far. (Note that Nasm does not seem to support functions starting with "·").

Here's a working "hello world" example:

hello.asm:

; Based on hello.asm from nasm

	SECTION .data		; data section
msg:	db "Hello World",10	; the string to print, 10=cr
len:	equ $-msg		; "$" means "here"
				; len is a value, not an address

	SECTION .text		; code section

global go.main.hello		; make label available to linker (Go)
go.main.hello:

	; --- setup stack frame
	push rbp			; save old base pointer
	mov rbp,rsp   ; use stack pointer as new base pointer
	
	; --- print message
	mov	edx,len		; arg3, length of string to print
	mov	ecx,msg		; arg2, pointer to string
	mov	ebx,1		; arg1, where to write, screen
	mov	eax,4		; write sysout command to int 80 hex
	int	0x80		; interrupt 80 hex, call kernel
	
	; --- takedown stack frame
	mov rsp,rbp  ; use base pointer as new stack pointer
	pop rbp      ; get the old base pointer

	; --- return
	mov rax,0		; error code 0, normal, no error
	ret         ; return

main.go:

package main

func hello();

func main() {
	hello()
    hello()
}

And a handy Makefile:

main: main.go hello.o
	gccgo hello.o main.go -o main

hello.o: hello.asm
	nasm -f elf64 -o hello.o hello.asm

clean:
	rm -rf _obj *.o *~ *.6 *.gch a.out main

I call hello() twice in main.go, just to double check that hello() returns properly.

Note that calling interrupt 80h directly is not considered good style on Linux, and calling functions written is C is more "future proof". Also note that this is assembly specifically for 64-bit Linux, and is not platform-independent in any way, shape or form.

I know it's not a direct answer to your question, but it's the easiest route I know for using assembly with Go, in lack of inlining. If you really need inlining, it's possible to write a script that extracts inline assembly from source files and prepares it in a way that follows the pattern above. Close enough? 在Go代码中是否可以包含内联汇编?

Quick example for Go, C and Nasm: gonasm.tgz

Update: Later versions of gccgo needs the -g flag and only "main.hello" is needed instead of "go.main.hello". Here is an updated example for Go, C and Yasm: goyasm.tgz

答案2

得分: 26

Go编程语言中没有支持内联汇编语言代码的功能,也没有计划这样做。Go支持链接到用汇编器C语言编写的例程。还有一个实验性功能,可以为Go添加SWIG支持

英文:

There is no facility in the Go programming language to support in-line assembler language code, and there are no plans to do so. Go does support linking to routines written in assembler and C. There is an experimental feature which adds SWIG support to Go.

答案3

得分: 15

不,你不能,但是可以通过使用Go编译器提供一个汇编实现的单个函数。不需要使用"Import C"来使用汇编。

看一个来自math库的例子:

http://golang.org/src/pkg/math/abs.go:在这个go文件中声明了Abs函数。(在这个文件中还有一个abs的go实现,但是它没有被导出,因为它的名称是小写。)

package math

// Abs返回x的绝对值。
//
// 特殊情况:
//	Abs(±Inf) = +Inf
//	Abs(NaN) = NaN
func Abs(x float64) float64

然后,在http://golang.org/src/pkg/math/abs_amd64.s中,Abs在这个文件中为64位intel实现:

#include "textflag.h"

// func Abs(x float64) float64
TEXT ·Abs(SB),NOSPLIT,$0
	MOVQ   $(1<<63), BX
	MOVQ   BX, X0 // movsd $(-0.0), x0
	MOVSD  x+0(FP), X1
	ANDNPD X1, X0
	MOVSD  X0, ret+8(FP)
	RET

这样的汇编函数的一个问题是,它们不会被Go编译器内联,所以如果你多次调用一个小函数,你能获得的性能提升是有限的。abs函数在go库中不再使用汇编实现。我可以说,随着内联的改进,使用最新的go版本编译abs函数比使用汇编更快。

英文:

No, you can't, but it is easy to provide an assembly implementation of just one function by using the go compiler. There is no need to use "Import C" to use assembly.

Take a look at an example from the math library:

http://golang.org/src/pkg/math/abs.go : The Abs function is declared in this go file. (There is also an implementation of abs in go in this file, but this isn't exported as it has a lower-case name.)

package math

// Abs returns the absolute value of x.
//
// Special cases are:
//	Abs(&#177;Inf) = +Inf
//	Abs(NaN) = NaN
func Abs(x float64) float64

Then, in http://golang.org/src/pkg/math/abs_amd64.s , Abs is implemented for intel 64 bit in this file:

#include &quot;textflag.h&quot;

// func Abs(x float64) float64
TEXT &#183;Abs(SB),NOSPLIT,$0
	MOVQ   $(1&lt;&lt;63), BX
	MOVQ   BX, X0 // movsd $(-0.0), x0
	MOVSD  x+0(FP), X1
	ANDNPD X1, X0
	MOVSD  X0, ret+8(FP)
	RET

One problem with assembly functions like this is that they aren't inlined by the go compiler, so there's a limit to how much performance you can gain if you're calling a small function many times. The abs function is no longer implemented in assembly in the go library. I would say that the go compiler has improved such that with inlining it is faster for compiling the abs function without assembly in more recent go releases.

答案4

得分: 3

标准Go编译器中的优化过程(即8g+8l,而不是gccgo)基本上是使用二进制形式的原始指令进行工作。目前还没有办法(尚未实现)让编译器区分编译器生成的汇编代码和用户提供的内联汇编代码 - 这是Go编译器不允许内联汇编的主要原因。换句话说,由于编译器架构的原因,编译器不支持内联汇编。

当然,Go语言本身并没有阻止其他Go语言实现(即其他Go编译器)支持内联汇编。内联汇编是一个特定于编译器的决策 - 它与Go语言本身几乎没有关系。

无论哪种情况,内联汇编都是不安全的,因为它无法通过Go的类型系统进行正确性检查。似乎最好是在像C这样的语言中实现任何需要使用内联汇编的函数,并从Go中调用C函数。

英文:

The optimization pass in the standard Go compiler (that is: 8g+8l, not gccgo) is basically working with raw instructions in binary form. There is currently no way (it isn't implemented) for the compiler to distinguish compiler-generated assembly from user-supplied inline assembly code - this is the primary reason why the Go compiler doesn't allow inline assembly. In other words, the compiler has no support for inline assembly because of compiler architecture.

There is of course nothing in the Go language itself which would be preventing other Go language implementations (that is: other Go compilers) to have support for inline assembly. Inline assembly is a compiler-specific decision - it has little to do with the Go language itself.

In either case, inline assembly is unsafe because it cannot be checked for correctness by Go's type system. It seems it is better to implement any function which requires usage of inline assembly in a language like C, and call the C function from Go.

huangapple
  • 本文由 发表于 2010年6月1日 23:26:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/2951028.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定