zoukankan      html  css  js  c++  java
  • Go语言汇编

    Plan 9汇编

    寄存器:

    数据寄存器:R0-R7,地址寄存器:A0-A7,浮点寄存器:F0-F7。

    伪栈寄存器:FP, SP, TOS。

    FP是frame pointer,0(FP)是第一个参数,4(FP)是第二个。

    SP是local stack pointer,保存自动变量。0(SP)是第一个。

    TOS是top of stack寄存器,用来保存过程的参数,保存局部变量。

    汇编器可以有一个变量名,比如p+0(FP),表示p是第一个参数,这个变量保存在符号表内,但是对程序运行没有影响。

    数据:

    所有的外部引用都需通过伪寄存器: PC(virtual Program Counter)/SB(Static Base register)。

    PC用来控制程序执行,SB用来引用全局变量。比如:

    把全局数组的地址压栈:MOVL $array(SB), TOS。

    把全局数组的第二个元素压栈:MOVL array+4(SB), TOS

    local<>+4(SB)是本地变量,只在本文件可见。

    定义流程:

    TEXT sum(SB), $0

    TEXT是一个伪指令,用来定义入口点。后面的参数是函数名,然后是栈大小,通常为0。

    中间可以有一个指定loader的选项,设置为1暂停函数的profiling。

    设置为2允许一个程序中有多个TEXT符号。

    子流程把运算结果放到R0中。浮点的结果放在F0中。

    子流程负责保存自己的寄存器,为caller saves模式。

     Go语言汇编

    Go语言的汇编基于Plan 9的汇编,但是有一些不同。最主要的一个区别是,Go语言的汇编指令不一定直接对应机器表示。有一些直接对应,有一些则不是。

    编译器产生的是一些中间码,具体的机器指令是在汇编生成之后才定下来的(Linker的工作)。

    FUNCDATA和PCDATA是编译器产生的,用于保存一些给垃圾收集的信息。

    Go语言的汇编和Plan 9的另一个不同是操作符的优先级。比如3&1<<2被解释成(3&1)<<2。

    符号:

    Go语言有4个伪寄存器,实际是对内存位置的一个引用。

    FP: 帧指针,保存参数和本地变量

    PC:程序指针,负责跳转和流程控制

    SB: 静态基指针,全局变量

    SP:栈指针,栈顶

    所有的符号全部携程FP和SB的偏移的形式:

    SB伪寄存器用来表示全局的变量或者函数,不如foo(SB)用来表示foo的地址。加<>表示符号本文件内可见。

    FP是用来保存参数的。(0)FP是第一个参数(8)FP是第二个(如果是64位机器)。

    SP指向本地栈顶,分别用x-8(SP), y-4(SP)表示变量。

    直接的jmp或者call指令,只能指向text符号,不能是符号的偏移。

    指令:

    TEXT指令定义一个符号,后面紧跟函数体。

    DATA指令定义一个section的内存,这段内存并不会被初始化。

    DATA    symbol+offset(SB)/width, value

    GLOBAL指令定义一个符号是全局的

    GLOBL divtab<>(SB), RODATA, $64
    
    GLOBL runtime·tlsoffset(SB), NOPTR, $4

    divtab是制度的64byte的表格,保存4个byte的整形。tlsoffset是,4byte的no pointers

    指令修饰符:

    DUPOK:允许一个二进制文件里有多个实例

    NOSPLIT: FOR TEXT,routine或者routine的子函数,必须把栈的空间的头填满,用来保护栈分隔

    RODATA:FOR DATA/GLOBL,把数据放在只读段

    NOPTR: FOR DATA/GLOBL,数据没有指针,不需要被垃圾收集扫描

    WRAPPER: FOR TEXT,wrapper function,不需要被以禁用recover计数

    NEEDCTXT:FOR TEXT,闭包

    Runtime协作:

    NOPTR和RODATA的数据不需要被垃圾收集。比指针还要小的数据也被当做NOPTR。不要在go汇编里写非只读数据。

    语法:

    plan9函数调用协议中采用的是caller-save的模式,也就是由调用者负责保存寄存器。

    TEXT !$Add(SB),$0
        MOVQ x+0(FP), BX
        MOVQ y+8(FP), BP
        ADDQ BP, BX
        MOVQ BX, ret+16(FP)
        RET

    a+8(FP)

    变量名+偏移(寄存器)。FP其实就是BP(栈基址寄存器上移一个机器字长位置的内存地址)。

    TEXT ·add(SB),NOSPLIT,$0

    00B7分隔了包名和变量名。NOSPLIT表示不用写入参数的大小,$0表示参数的大小,因为制定了NOSPLIT所以写0

    汇编文件的名字甚至变量名称并不重要。

    MOVQ $0, DX //put 0 into DX register. Q means quadword which is 8 bytes. L for 4 bytes. (src, destination) order

    ¥24-8表示24字节的帧,和8个字节的参数

    Go Array有不是指向第一个元素的指针,而是代表整个数组。赋值或者传递的时候是有一个copy的。

    Go slice有三个部分, 一个指针,一个长度,一个容量,指针8个字节,长度和容量4个字节

    一个slice没有固定的长度。slice不能指定长度。一个slice如果要增加capacity必须创建一个新的容量更大的slice,并copy内容过去。

    Examples:

  • 相关阅读:
    封装一个通用递归算法,使用TreeIterator和TreeMap来简化你的开发工作。
    优化特性(Attribute)性能
    不需要了解任何底层知识,就可以汉化!Let`s go!!!
    颠覆你对方法调用的看法!
    实际项目中面向对象的最佳实践
    递归使用触发器
    关于稀疏数组
    121-django中的Http404处理
    120-在前端使用django-ckeditor,很简单,很方便
    119-用django实现评论功能
  • 原文地址:https://www.cnblogs.com/dracohan/p/6405695.html
Copyright © 2011-2022 走看看