goコンパイルプロセスの簡単な分析

goコンパイルの原則の基本を理解する

  • 抽象構文木AST
    • image_1.png
    • 抽象構文木:これは、コードの解析とコンパイルのために多くの言語で一般的に使用されるデータ構造です。抽象構文は、スペース、セミコロン、括弧など、ソースコードで特に重要ではない要素を削除します。
    • コンパイラが構文解析を実行すると、抽象構文ツリーが出力されます。この抽象構文ツリーは、コンパイラがセマンティック分析を実行するのに役立ちます。これを使用して、正しい構文のプログラムにタイプの不一致の問題があるかどうかを判断できます。
  • 静的単一代入SSA
    • コンパイラーの設計では、静的単一代入形式(通常はSSA形式またはSSAと略されます)は中間表現(IR、中間表現)の機能であり、各変数は1回だけ割り当てられます。これは標準として理解できます。
    • SSAの理解
    1.如果每个变量在程序中有且只有一个赋值语句,那么该程序是SSA形式
    2.对于下面的代码,解释为什么需要SSA:
      x = 1
      y = x +1
      x = 2
      z = x + 1
      // 由于上面代码  y和z都等于x+1,那么编译器就会理解成 z = y,但是事实上 在z=x+1之前,对x重新赋值一次,所以z <> y的,所以就出现了一种形式(SSA),在编译器编译过程中,会将上面代码转换成下面的形式
      x1 = 1
      y1 = x1 + 1
      x2 = 2
      z1 = x2 + 1
      // 由于这样的转换,就不会造成让编译器理解成 z = y了,从而也会实现一些优化的作用,如下代码:
      x = 1
      x = 2
      y = x
      // SSA转换之后
      x1 = 1
      x2 = 2
      y1 = x2
      // 这样就会省去 对x=1的执行过程,因为x=1和下面没什么关系
      // 我们可以清晰地发现变量 y1 和 x1 是没有任何关系的,所以在机器码生成时就可以省去 x := 1 的赋值,通过减少需要执行的指令优化这段代码
    
    复制代码

goコンパイルの原則とコンパイラプロセスを理解する

Go言語コンパイラのソースコードはsrc/cmd / compileディレクトリにあり、ディレクトリ内のファイルが一緒になってGo言語コンパイラを構成します。

image_1.png

  • おおよそのプロセス:

    • 1.字句解析-ソースファイルは字句解析を通じてコードをトークンの形式に変換します。字句解析を実行するプログラムは字句解析プログラムと呼ばれます。
    package main
    import (
      "fmt"
    )
    func main() {
      fmt.Println("hello world")
    }
    // 转成
    _Package Ident
    _Import _Lparen
      quote Ident quote
    _Rparen
    _Func Ident _Lparen _Rparen
      _Lbrace
        Ident _Dot Ident _Lparen STRING
        _Rparen
      _Rbrace
     // 最后转成的 token内容:
     _Package,Ident,_Import,_Lparen...
    
    复制代码
    • 2.文法分析-生成されたトークンは、文法アナライザーによって順次解析されます。このプロセスでは、プログラミング言語で定義された文法に従って、字句解析によって生成されたトークンのボトムアップまたはトップダウンの仕様分析について説明し、最後にgoソースファイルは、最終的にAST(抽象構文ツリー)であるSourceFile構造を生成します。
    // 如下即为语法分析器Parser,根据词法分析器输出的Token序列,转化的抽象语法树
    “main.go”: SourceFile {
      PackageName: “main”,
      ImportDecl: []Import{
        “fmt”,
      },
      TopLevelDecl: ..
    }
    
    复制代码
    • 3.型チェック-goは、コンパイル時に抽象構文ツリーの内容に対して型チェックを実行し、型チェックは、次の順序でさまざまな型のノードを検証および処理します。
        1. 常量、类型和函数名及类型;
        2. 变量的赋值和初识化;
        3. 函数和闭包的主体;
        4. 哈希键值对的类型;
        5. 导入函数体;
        6. 外部的声明;
        7. 在这个阶段不止会对节点的类型进行验证,还会对一些内置的函数进行替换;如下就是make的替换å
    
    复制代码

    image_2.png

    • 4.中間コード生成-ツリー全体の構文を解析し、型チェックを実行した後、コードに構文エラーまたは型エラーがないと判断できます。この時点で、goは抽象ツリーを中間コードに変換します。ミドルウェアコードは前述の;SSA機能を使用します
    // 编译器会通过cmd/compile/internal/gc.compileFunctions 编译整个GO语言中的全部函数,而这些函数都是会交给Goroutine进行处理;最终通过compileSSA转化为SSA
    func compileFunctions() {
      if len(compilequeue) != 0 {
      // ..
        var wg sync.WaitGroup
        Ctxt.InParallel = true
        c := make(chan *Node, nBackendWorkers)
        for i := 0; i < nBackendWorkers; i++ {
          wg.Add(1)
          go func(worker int) {
            for fn := range c {
              compileSSA(fn, worker)
            }
            wg.Done()
          }(i)
        }
        // ..
        close(c)
        compilequeue = nil
        wg.Wait()
      }
    }
    
    复制代码
    • 5.マシンコードの生成-中間コード。最初にアセンブリコードに変換してからアセンブラを呼び出すと、アセンブラは対応するコードを呼び出して、コンパイル時に設定したアーキテクチャに従ってターゲットマシンコードを生成します。

    • 注:中間コードが生成された後、現在のオペレーティングシステムが不確実であり、オペレーティングシステムが異なれば効果も異なるため、すぐに最終実行コードに変換されることはありません。

    • 6.最終実行コード

  • 最終要約

image_3.png

おすすめ

転載: juejin.im/post/7087870907913338893