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

发布于 2024-09-03 22:11:28 字数 189 浏览 6 评论 0原文

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

这篇博文展示了将 Go 编译到单独的 .s 文件并编辑它,但不像许多 C 编译器支持的那样将内联 asm 作为 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

却一份温柔 2024-09-10 22:11:28

不支持内联汇编,但您可以链接通过 C 语言编写的汇编代码,使用 cgo 进行编译并使用 import "C",如 gmp.go。您也可以使用与 Go 直接兼容的汇编风格进行编写,例如 asm_linux_amd64.s ,要求函数名以“·”开头。

或者,您可以使用 nasm 和 gccgo,这是迄今为止我最喜欢的方式。 (注意Nasm似乎不支持以“·”开头的函数)。

这是一个有效的“hello world”示例:

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()
}

和一个方便的 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? :)

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

许久 2024-09-10 22:11:28

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.

肩上的翅膀 2024-09-10 22:11:28

不,你不能,但是通过使用 go 编译器很容易提供一个函数的汇编实现。无需使用“Import C”即可使用汇编。

看一下数学库中的示例:

http://golang.org/src /pkg/math/abs.go :Abs函数在此go文件中声明。 (此文件中还有一个 go 中的 abs 实现,但由于它具有小写名称,因此未导出。)

package math

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

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

#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 编译器内联的,因此如果多次调用一个小函数,可以获得的性能是有限的。 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(±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 "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

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.

怼怹恏 2024-09-10 22:11:28

标准 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文