Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 11333] ファイルの概要

このコミットは、Go言語のコンパイラ(gc)における複合型(complex)の組み込み操作に関するバグ修正です。具体的には、complex型の組み込み関数(realimagなど)の引数として関数呼び出しが使用された場合に、コンパイラがそれを正しく処理できるようにするための変更が含まれています。

コミット

commit 5ad9e2db28a9058547983a85cfd2883788d5a704
Author: Luuk van Dijk <lvd@golang.org>
Date:   Mon Jan 23 16:56:57 2012 +0100

    gc: handle function calls in arguments to builtin complex operations.
    
    Fixes #2582
    
    R=rsc
    CC=golang-dev
    https://golang.org/cl/5574044

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/5ad9e2db28a9058547983a85cfd2883788d5a704

元コミット内容

gc: handle function calls in arguments to builtin complex operations.

Fixes #2582

R=rsc
CC=golang-dev
https://golang.org/cl/5574044

変更の背景

このコミットは、Go言語のIssue 2582を修正するために行われました。Issue 2582は、complex型を扱う組み込み関数(real()imag()など)の引数に、関数呼び出しの結果が渡された場合に、Goコンパイラが正しく処理できないというバグを報告しています。

具体的には、以下のようなコードが問題を引き起こしていました。

package main

import "fmt"

type T struct{}

func (T) cplx() complex128 {
    return complex(1, 0)
}

func main() {
    var t T
    _ = real(t.cplx()) // ここで問題が発生
    _ = imag(t.cplx()) // ここで問題が発生
}

このコードでは、t.cplx()というメソッド呼び出しの結果がreal()imag()の引数として渡されています。Goコンパイラのgc(garbage collector、ここではコンパイラのフロントエンドの一部を指す)は、このようなシナリオで関数呼び出しを適切に評価し、その結果を組み込み操作に渡すための処理が不足していました。その結果、コンパイルエラーや不正なコード生成が発生する可能性がありました。

この修正の目的は、complex型の組み込み操作の引数として、通常の関数呼び出し、メソッド呼び出し、インターフェースメソッド呼び出しが正しく扱われるように、コンパイラのコード生成ロジックを強化することです。

前提知識の解説

このコミットを理解するためには、以下のGo言語およびコンパイラの基本的な概念を理解しておく必要があります。

  1. Go言語のcomplex: Go言語には、複素数を表現するためのcomplex64complex128という組み込み型があります。これらはそれぞれfloat32float64のペアで実部と虚部を表現します。
  2. 組み込み関数 real()imag(): real(c)は複素数cの実部を、imag(c)は虚部を返します。これらはコンパイラによって特別に扱われる組み込み関数です。
  3. Goコンパイラ(gc: Go言語の公式コンパイラはgcと呼ばれます。これは、ソースコードを解析し、中間表現を生成し、最終的に実行可能なバイナリを生成する役割を担います。
  4. AST (Abstract Syntax Tree): コンパイラはソースコードを解析する際に、プログラムの構造を抽象構文木(AST)として表現します。ASTの各ノードは、変数、関数呼び出し、演算子などのプログラム要素に対応します。
  5. Node構造体とOp列挙型: Goコンパイラの内部では、ASTの各要素がNode構造体で表現されます。Nodeには、そのノードの種類を示すOp(Operation)フィールドがあります。例えば、OCALLFUNCは通常の関数呼び出し、OCALLMETHは構造体のメソッド呼び出し、OCALLINTERはインターフェースメソッド呼び出しを表します。OINDはポインタの逆参照、ONAMEは変数名を表します。
  6. complexgen関数: src/cmd/gc/cplx.cファイルにあるcomplexgen関数は、Goコンパイラ内で複素数に関する操作(realimagなどの組み込み関数を含む)を処理する役割を担っています。この関数は、ASTノードを走査し、複素数操作に必要なコードを生成します。
  7. igen関数とcomplexmove関数: igen関数は、与えられたノード(式)を評価し、その結果を一時的なレジスタやメモリ位置に格納するためのコードを生成します。complexmove関数は、複素数の値をある場所から別の場所に移動させるためのコードを生成します。

技術的詳細

このコミットの技術的な核心は、src/cmd/gc/cplx.cファイル内のcomplexgen関数が、複素数操作の引数として渡される可能性のあるASTノードの種類を拡張した点にあります。

complexgen関数は、複素数操作の引数となるノードの種類をswitch文で処理しています。修正前は、OIND(間接参照)やONAME(変数名)といった、直接的な値やメモリ位置を参照するノードタイプのみが考慮されていました。しかし、関数呼び出しの結果が引数として渡される場合、そのノードタイプはOCALLFUNC(通常の関数呼び出し)、OCALLMETH(メソッド呼び出し)、OCALLINTER(インターフェースメソッド呼び出し)となります。

修正前は、これらの関数呼び出しノードがcomplexgenswitch文で明示的に処理されていなかったため、コンパイラはこれらのノードを適切に評価し、その結果を複素数操作に渡すことができませんでした。

このコミットでは、complexgen関数のswitch文にOCALLFUNCOCALLMETHOCALLINTERのケースが追加されました。これにより、これらのノードタイプが検出された場合でも、既存のigen関数とcomplexmove関数を使用して、関数呼び出しの結果を評価し、その結果を複素数操作の引数として適切に処理できるようになりました。

igen(n, &n1, res)は、ノードn(この場合は関数呼び出し)を評価し、その結果を一時的なノードn1に格納します。その後、complexmove(&n1, res)が、この一時的な結果を最終的な結果レジスタresに移動させます。これにより、real()imag()のような組み込み関数が、関数呼び出しによって返された複素数値を正しく受け取って処理できるようになります。

また、test/fixedbugs/bug401.goという新しいテストファイルが追加されました。このテストは、complex型のメソッド呼び出しやインターフェースメソッド呼び出しがreal()imag()の引数として使用された場合に、コンパイラが正しく動作することを確認するためのものです。これにより、修正が意図通りに機能し、将来のリグレッションを防ぐことができます。

コアとなるコードの変更箇所

変更は主に2つのファイルで行われています。

  1. src/cmd/gc/cplx.c:

    --- a/src/cmd/gc/cplx.c
    +++ b/src/cmd/gc/cplx.c
    @@ -204,6 +204,8 @@ complexgen(Node *n, Node *res)
     	case OIND:
     	case ONAME:	// PHEAP or PPARAMREF var
     	case OCALLFUNC:
    +	case OCALLMETH:
    +	case OCALLINTER:
     		igen(n, &n1, res);
     		complexmove(&n1, res);
     		regfree(&n1);
    
  2. test/fixedbugs/bug401.go: 新規追加されたテストファイル

    // $G $D/$F.go || echo "Bug398"
    
    // Copyright 2011 The Go Authors.  All rights reserved.
    // Use of this source code is governed by a BSD-style
    // license that can be found in the LICENSE file.
    
    // Issue 2582
    package foo
        
    type T struct {}
    func (T) cplx() complex128 {
    	for false {}  // avoid inlining
    	return complex(1,0)
    }
    
    type I interface {
    	cplx() complex128
    }
    
    func f(e float32, t T) {
    
        	_ = real(t.cplx())
        	_ = imag(t.cplx())
    
    	var i I
    	i = t
        	_ = real(i.cplx())
        	_ = imag(i.cplx())
    }
    

コアとなるコードの解説

src/cmd/gc/cplx.c の変更

cplx.cの変更は、complexgen関数内のswitch文にOCALLFUNCOCALLMETHOCALLINTERの3つのケースを追加したことです。

  • OCALLFUNC: 通常の関数呼び出し(例: myFunc())。
  • OCALLMETH: 構造体型に紐付けられたメソッドの呼び出し(例: myStruct.myMethod())。
  • OCALLINTER: インターフェース型に紐付けられたメソッドの呼び出し(例: myInterface.myMethod())。

これらのケースが追加されたことで、complexgen関数は、real()imag()のような組み込み複素数操作の引数として、これらの種類の関数呼び出しノードが渡された場合でも、以下の処理を実行するようになります。

  1. igen(n, &n1, res);:

    • n: 現在処理しているASTノード。この場合、関数呼び出しを表すノード(OCALLFUNC, OCALLMETH, OCALLINTERのいずれか)。
    • &n1: 関数呼び出しの結果を格納するための一時的なノードへのポインタ。
    • res: 最終的な結果を格納するレジスタまたはメモリ位置。
    • この行は、コンパイラにnで表される関数呼び出しを実行させ、その戻り値(この場合はcomplex128型)を一時的な場所(n1)に格納するためのコードを生成するよう指示します。
  2. complexmove(&n1, res);:

    • &n1: 関数呼び出しの結果が格納された一時的なノードへのポインタ。
    • res: 複素数操作(realimag)が期待する引数の場所。
    • この行は、一時的な場所n1に格納された複素数値を、real()imag()が読み取れるようにresに移動させるためのコードを生成します。
  3. regfree(&n1);:

    • &n1: 一時的なノードへのポインタ。
    • この行は、一時的に使用したリソース(レジスタなど)を解放します。

この変更により、コンパイラはreal(t.cplx())のようなコードを正しく解釈し、まずt.cplx()を実行して複素数値を取得し、その値をreal()関数に渡すという一連の操作を適切にコンパイルできるようになりました。

test/fixedbugs/bug401.go の新規追加

このテストファイルは、修正が正しく機能することを確認するためのリグレッションテストです。

  • type T struct {}func (T) cplx() complex128 で、complex128を返すメソッドを持つ型Tを定義しています。for false {}は、コンパイラがこのメソッドをインライン化しないようにするための慣用的な記述です。
  • type I interface { cplx() complex128 } で、cplx()メソッドを持つインターフェースIを定義しています。
  • func f(e float32, t T) 関数内で、以下の2つのシナリオをテストしています。
    • _ = real(t.cplx())_ = imag(t.cplx()): これは、構造体のメソッド呼び出し(OCALLMETH)の結果をreal()およびimag()に渡すケースをテストします。
    • var i I; i = t; _ = real(i.cplx())_ = imag(i.cplx()): これは、インターフェースメソッド呼び出し(OCALLINTER)の結果をreal()およびimag()に渡すケースをテストします。

このテストが追加されたことで、将来的に同様のバグが再発した場合に、自動テストによって検出されるようになります。

関連リンク

参考にした情報源リンク