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

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

コミット

commit 6a13897bc6b92cf5c80ae6d4e58fadf55fa10e82
Author: Russ Cox <rsc@golang.org>
Date:   Tue Jul 30 22:58:38 2013 -0400

    runtime: ARM _sfloat has no arguments
    
    Fixes one build failure.
    
    R=golang-dev, dave
    CC=golang-dev
    https://golang.org/cl/12139043

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

https://github.com/golang/go/commit/6a13897bc6b92cf5c80ae6d4e58fadf55fa10e82

元コミット内容

runtime: ARM _sfloat has no arguments

Fixes one build failure.

R=golang-dev, dave
CC=golang-dev
https://golang.org/cl/12139043

変更の背景

このコミットは、GoランタイムのARMアーキテクチャ向けビルドにおける特定の問題を解決するために行われました。コミットメッセージには「Fixes one build failure.(一つのビルド失敗を修正する)」と簡潔に記されており、_sfloatというシンボルに関連するビルドエラーが発生していたことが示唆されます。

_sfloatは、GoのARMランタイムにおいてソフトウェア浮動小数点演算(Software Floating Point, Softfloat)を処理するためのルーチンを指している可能性が高いです。ARMアーキテクチャには、ハードウェア浮動小数点ユニット(FPU)を持たない、あるいはソフトウェアエミュレーションが明示的に選択されるような構成が存在します。特に、GOARM=5のような古いARMターゲットでは、ハードウェアFPUが利用できないため、浮動小数点演算はソフトウェアでエミュレートされます。

このビルド失敗は、_sfloatルーチンの定義方法、特にその引数の扱いに関する誤解または不整合が原因であったと考えられます。Goのアセンブリ言語は、一般的なGNU ARMアセンブリとは異なる独自の構文と規約を持っており、このような細かな定義の差異がビルドエラーを引き起こすことがあります。

前提知識の解説

このコミットを理解するためには、以下の前提知識が必要です。

  1. Goランタイム: Goプログラムは、Goランタイムと呼ばれる軽量な実行環境上で動作します。ランタイムは、ガベージコレクション、スケジューリング、プリミティブな操作(例えば、浮動小数点演算)など、言語の基本的な機能を提供します。
  2. ARMアーキテクチャ: ARMは、モバイルデバイスや組み込みシステムで広く使用されているCPUアーキテクチャです。ARMプロセッサには、ハードウェアFPUの有無やバージョンによって、様々なプロファイル(例: ARMv5, ARMv6, ARMv7)が存在します。
  3. ソフトウェア浮動小数点(Softfloat): ハードウェアFPUがない、または使用しない場合に、浮動小数点演算をCPUの整数演算とソフトウェアロジックでエミュレートする技術です。これにより、FPUを持たないシステムでも浮動小数点演算が可能になりますが、パフォーマンスはハードウェアFPUに比べて劣ります。
  4. Goアセンブリ言語: Go言語は、パフォーマンスが重要な部分や、特定のハードウェア機能にアクセスする必要がある場合に、Goアセンブリ言語で記述されたコードを使用します。Goアセンブリは、AT&T構文やIntel構文とは異なり、独自の擬似命令とレジスタ命名規則を持っています。
    • TEXTディレクティブ: Goアセンブリで関数を定義するために使用されます。TEXT symbol(SB), flags, framesizeのような形式を取ります。
      • symbol(SB): 関数のシンボル名。SBは「static base」を意味し、グローバルシンボルであることを示します。
      • flags: 関数の特性を示すフラグ(例: 7NOSPLITRODATAの組み合わせ)。
      • framesize: 関数のスタックフレームサイズ。これには、ローカル変数や呼び出し先関数の引数を格納するために必要なスペースが含まれます。
      • argsize: (非公式に)引数の合計サイズを示すために使用されることもありますが、GoアセンブリのTEXTディレクティブの公式な定義では、framesizeの後に引数サイズを明示的に指定する形式は一般的ではありません。しかし、このコミットの変更点から、framesizeの計算に引数サイズが影響を与えていた可能性が示唆されます。
  5. レジスタとスタック: ARMアーキテクチャでは、関数呼び出し規約に従って、引数はレジスタ(R0-R3など)またはスタックを通じて渡されます。関数は、呼び出し元レジスタ(caller-saved registers)を保存し、呼び出し先レジスタ(callee-saved registers)を復元する責任があります。

技術的詳細

このコミットの技術的詳細は、GoランタイムのARMアセンブリコードにおける_sfloat関数の定義の修正にあります。

変更前のコードは以下の通りでした。

TEXT _sfloat(SB), 7, $64 // 4 arg + 14*4 saved regs + cpsr

この行は、_sfloatという名前の関数を定義しており、そのスタックフレームサイズを$64バイトと指定しています。コメントには「4 arg + 14*4 saved regs + cpsr」とあり、これは4つの引数、14個の保存されたレジスタ(各4バイト)、およびCPSR(Current Program Status Register)の保存に必要なスペースの合計が64バイトであるという計算を示しています。

しかし、変更後のコードは以下のようになっています。

TEXT _sfloat(SB), 7, $64-0 // 4 arg + 14*4 saved regs + cpsr

ここで注目すべきは、$64$64-0に変更されている点です。この$64-0という表記は、_sfloat関数が実際には引数を取らないことを示唆しています。GoアセンブリのTEXTディレクティブにおいて、framesizeの後にargsizeframesize-argsizeのように記述する慣習がある場合、$64-0は「フレームサイズは64バイトだが、引数サイズは0バイトである」ということを明示的に示します。

元のコメント「4 arg」は、この関数が4つの引数を取ることを示唆していましたが、実際の関数定義では引数を取らない、あるいは引数がスタックフレームサイズに影響を与えない形で処理されるべきであった可能性があります。この不整合がビルドエラーの原因となっていたと考えられます。

この修正は、_sfloat関数が引数を取らないという事実をGoアセンブリコンパイラに正しく伝えることで、ビルドプロセスにおける不整合を解消し、ビルド失敗を修正したものです。これは、アセンブリレベルでの関数呼び出し規約とスタックフレームの管理に関する正確な理解が求められる、低レベルな修正と言えます。

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

変更はsrc/pkg/runtime/vlop_arm.sファイルの一箇所のみです。

--- a/src/pkg/runtime/vlop_arm.s
+++ b/src/pkg/runtime/vlop_arm.s
@@ -45,7 +45,7 @@ TEXT _mulv(SB), 7, $0
 // trampoline for _sfloat2. passes LR as arg0 and
 // saves registers R0-R13 and CPSR on the stack. R0-R12 and CPSR flags can
 // be changed by _sfloat2.
-TEXT _sfloat(SB), 7, $64 // 4 arg + 14*4 saved regs + cpsr
+TEXT _sfloat(SB), 7, $64-0 // 4 arg + 14*4 saved regs + cpsr
  	MOVW	R14, 4(R13)
  	MOVW	R0, 8(R13)
  	MOVW	$12(R13), R0

具体的には、48行目のTEXT _sfloat(SB), 7, $64TEXT _sfloat(SB), 7, $64-0に変更されました。

コアとなるコードの解説

変更された行は、Goアセンブリ言語で_sfloatという名前の関数を定義しています。

  • TEXT _sfloat(SB), 7, $64 (変更前):

    • TEXT: 関数定義の開始を示すディレクティブ。
    • _sfloat(SB): 定義する関数の名前。SBは「Static Base」を意味し、このシンボルがグローバルスコープであることを示します。
    • 7: 関数のフラグ。この場合、NOSPLIT (スタックの自動拡張を行わない) と RODATA (読み取り専用データセクションに配置) の組み合わせである可能性が高いです。
    • $64: この関数のスタックフレームサイズ。関数が使用するローカル変数や、呼び出し先関数に渡す引数を格納するために確保されるスタック領域のサイズをバイト単位で指定します。コメントには「4 arg + 14*4 saved regs + cpsr」とあり、これは4つの引数、14個のレジスタ(各4バイト)、CPSRの保存に必要な合計サイズが64バイトであるという計算を示しています。
  • TEXT _sfloat(SB), 7, $64-0 (変更後):

    • $64-0: ここが変更点です。framesize-argsizeの形式で、フレームサイズが64バイトであり、引数サイズが0バイトであることを明示しています。
    • この変更は、_sfloat関数が実際には引数をスタック経由で受け取らない、あるいは引数がスタックフレームサイズに影響を与えない形で処理されるべきであることをコンパイラに正しく伝えます。元のコメントが「4 arg」と示唆していたにもかかわらず、実際の関数定義では引数がない、またはレジスタ経由で渡されるためスタックフレームサイズには含まれない、といった状況であったと考えられます。
    • この修正により、Goアセンブリコンパイラが_sfloat関数のスタックフレームを正しく計算できるようになり、結果としてビルドエラーが解消されました。これは、アセンブリコードの正確な定義が、コンパイラによるコード生成とリンクの成功に不可欠であることを示しています。

関連リンク

参考にした情報源リンク