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

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

このコミットは、Go言語の標準ライブラリ crypto/sha256 パッケージにおけるSHA-256ハッシュアルゴリズムのブロック処理を、AMD64アーキテクチャ向けのアセンブリ言語で実装したものです。これにより、SHA-256ハッシュ計算のパフォーマンスが大幅に向上しました。

コミット

commit 75800a9a2842438c872c6464b3a87b33fedfea27
Author: Joel Sing <jsing@google.com>
Date:   Wed Dec 11 11:41:30 2013 -0500

    crypto/sha256: block implementation in amd64 assembly
    
    Benchmark on Intel(R) Xeon(R) CPU X5650  @ 2.67GHz
    
    benchmark              old ns/op    new ns/op    delta
    BenchmarkHash8Bytes         1259          677  -46.23%
    BenchmarkHash1K            14387         6749  -53.09%
    BenchmarkHash8K           106006        50107  -52.73%
    
    benchmark               old MB/s     new MB/s  speedup
    BenchmarkHash8Bytes         6.35        11.81    1.86x
    BenchmarkHash1K            71.17       151.72    2.13x
    BenchmarkHash8K            77.28       163.49    2.12x
    
    R=agl
    CC=golang-dev
    https://golang.org/cl/28460043

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

https://github.com/golang/go/commit/75800a9a2842438c872c6464b3a87b33fedfea27

元コミット内容

このコミットは、crypto/sha256 パッケージにおいて、SHA-256ハッシュアルゴリズムのコアとなるブロック処理をAMD64アーキテクチャ向けのアセンブリ言語で実装したものです。これにより、Intel Xeon CPU X5650 @ 2.67GHzでのベンチマークにおいて、既存のGo言語実装と比較して最大で約53%の処理時間短縮(約2.1倍の高速化)が達成されました。

具体的には、以下のファイルが変更または新規追加されています。

  • src/pkg/crypto/sha256/sha256.go: FIPS 180-2からFIPS 180-4への参照更新。
  • src/pkg/crypto/sha256/sha256block.go: AMD64以外の場合にGo言語実装を使用するためのビルドタグ +build !amd64 が追加。
  • src/pkg/crypto/sha256/sha256block_amd64.s: SHA-256ブロック処理のAMD64アセンブリ実装が新規追加。
  • src/pkg/crypto/sha256/sha256block_decl.go: アセンブリ実装の関数シグネチャをGo言語に公開するための宣言ファイルが新規追加。ビルドタグ +build amd64 が付与され、//go:noescape ディレクティブが使用されています。

変更の背景

ハッシュアルゴリズムのような暗号プリミティブは、多くのアプリケーションで基盤として利用され、その性能はシステム全体のパフォーマンスに大きな影響を与えます。特にSHA-256は、TLS/SSL、ブロックチェーン、デジタル署名など、幅広いセキュリティ関連の用途で広く使われています。

Go言語の初期の実装では、多くの部分が純粋なGo言語で書かれていましたが、パフォーマンスがクリティカルな部分、特にCPU集約的な暗号処理においては、特定アーキテクチャ向けのアセンブリ言語による最適化が求められることがあります。これは、Goコンパイラが生成するコードよりも、手書きのアセンブリコードの方が、レジスタの利用効率、命令の並列性、特定のCPU命令(例: SIMD命令)の活用において優位に立つ場合があるためです。

このコミットの背景には、Go言語のSHA-256実装の性能を向上させ、より高速なハッシュ計算を提供することで、Go言語で構築されるアプリケーション全体のパフォーマンスとスループットを改善するという目的があります。ベンチマーク結果が示すように、このアセンブリ最適化は非常に効果的でした。

また、sha256.go のFIPS 180-2からFIPS 180-4への参照更新は、SHA-256の標準が改訂されたことに対応するものです。FIPS 180-4は、SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256のセキュアハッシュアルゴリズムを定義しており、FIPS 180-2からの変更点としては、主にSHA-512/tファミリーの追加や、セキュリティ要件の明確化などが挙げられますが、SHA-256のコアアルゴリズム自体に大きな変更はありません。この更新は、実装が最新の標準に準拠していることを示すものです。

前提知識の解説

1. SHA-256ハッシュアルゴリズム

SHA-256(Secure Hash Algorithm 256-bit)は、NIST(アメリカ国立標準技術研究所)によって開発された暗号学的ハッシュ関数の一つです。任意の長さの入力データ(メッセージ)から、256ビット(32バイト)の固定長のハッシュ値(メッセージダイジェスト)を生成します。このハッシュ値は、入力データが少しでも変更されると大きく変化するという「雪崩効果」を持ち、元の入力データをハッシュ値から復元することが非常に困難であるという「一方向性」を持ちます。また、異なる入力から同じハッシュ値が生成される「衝突」を見つけることが計算上困難であるという「衝突耐性」も持っています。

SHA-256の処理は、主に以下のステップで構成されます。

  • パディング: 入力メッセージを512ビット(64バイト)のブロックの倍数になるようにパディングします。パディングには、メッセージの最後に'1'ビットを追加し、その後に多数の'0'ビットを続け、最後に元のメッセージの長さを64ビットで表現した値を追加します。
  • メッセージブロックの処理: パディングされたメッセージを512ビットのブロックに分割し、各ブロックを順番に処理します。
  • 圧縮関数: 各512ビットのブロックは、8つの32ビットワード(A, B, C, D, E, F, G, H)からなる内部状態と、64ラウンドの計算を通じて処理されます。各ラウンドでは、非線形関数(Ch, Maj)、ビット単位の論理演算(AND, OR, XOR, NOT)、ビットシフト(ROTR, SHR)、加算(モジュロ2^32)が組み合わされ、定数(K_t)とメッセージスケジュールされたワード(W_t)が使用されます。
  • ハッシュ値の更新: 各ブロックの処理後、内部状態の8つのワードが、初期ハッシュ値(H0~H7)に加算され、次のブロックの処理のための新しいハッシュ値となります。
  • 最終ハッシュ値: 全てのブロックが処理された後、最終的な8つのワードが連結され、256ビットのハッシュ値が生成されます。

2. FIPS 180-4

FIPS(Federal Information Processing Standards)は、アメリカ合衆国政府が発行する情報処理標準です。FIPS 180はセキュアハッシュ標準(Secure Hash Standard, SHS)を定義しており、SHA-1、SHA-2(SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256)、SHA-3などのハッシュ関数が含まれます。

FIPS 180-4は、SHA-2ファミリーの最新版の一つであり、SHA-256のアルゴリズム自体はFIPS 180-2から変更されていませんが、標準文書としての記述が更新されています。このコミットでは、Go言語のSHA-256実装がこの最新の標準に準拠していることを明示するために、参照が更新されました。

3. アセンブリ言語による最適化

アセンブリ言語は、CPUが直接実行できる機械語命令を人間が読める形式で記述した低水準プログラミング言語です。Go言語のような高水準言語で書かれたプログラムは、コンパイラによって機械語に変換されますが、特定の部分(特にパフォーマンスがクリティカルな部分)では、手書きのアセンブリ言語を用いることで、コンパイラでは実現が難しいレベルの最適化を行うことができます。

アセンブリ言語による最適化の主な利点は以下の通りです。

  • レジスタの効率的な利用: CPUのレジスタは非常に高速な記憶領域ですが、数が限られています。アセンブリ言語では、どのデータをどのレジスタに割り当てるかをプログラマが直接制御できるため、メモリへのアクセスを最小限に抑え、計算を高速化できます。
  • 特定のCPU命令の活用: SIMD(Single Instruction, Multiple Data)命令(例: SSE, AVX)や暗号化支援命令(例: AES-NI)など、特定のCPUが持つ特殊な命令は、高水準言語からは直接利用できない場合があります。アセンブリ言語を使えば、これらの命令を直接呼び出し、大量のデータを並列に処理したり、暗号計算をハードウェアレベルで高速化したりできます。
  • パイプラインの最適化: 現代のCPUは命令パイプラインを持っており、複数の命令を同時に処理しようとします。アセンブリ言語では、命令の順序を調整することで、パイプラインのストール(停止)を減らし、CPUの利用率を最大化できます。
  • メモリレイアウトの最適化: データがメモリにどのように配置されるかを細かく制御し、キャッシュミスを減らすことで、メモリアクセスによる遅延を最小限に抑えることができます。

Go言語では、go:noescapego:linkname といった特別なディレクティブ(プラグマ)を使用して、Goコードからアセンブリ関数を呼び出すことができます。これにより、Go言語の安全性や生産性を保ちつつ、パフォーマンスが要求される部分のみをアセンブリで最適化するというハイブリッドなアプローチが可能です。

4. Go言語のビルドタグ (+build)

Go言語のソースファイルには、ファイルの先頭に+buildディレクティブを記述することで、特定のビルド条件を指定できます。これにより、異なるオペレーティングシステム、アーキテクチャ、またはカスタムビルドタグに基づいて、コンパイルするファイルを切り替えることができます。

  • // +build amd64: このタグが付いたファイルは、AMD64アーキテクチャ向けにビルドされる場合にのみコンパイルされます。
  • // +build !amd64: このタグが付いたファイルは、AMD64アーキテクチャ以外の場合にのみコンパイルされます。

このコミットでは、sha256block_amd64.s(アセンブリ実装)には+build amd64が、sha256block.go(Go言語実装)には+build !amd64がそれぞれ付与されており、これによりAMD64環境ではアセンブリ実装が、それ以外の環境ではGo言語実装が自動的に選択されるようになっています。

5. //go:noescape ディレクティブ

//go:noescape はGoコンパイラに対するディレクティブ(プラグマ)の一つで、特定の関数がヒープにメモリを割り当てないことをコンパイラに伝えます。これは、関数が引数として受け取ったポインタを、その関数のスコープ外に「エスケープ」させないことを意味します。このディレクティブは、通常、アセンブリ言語で実装された関数や、非常にパフォーマンスがクリティカルな関数に対して使用され、コンパイラがより積極的な最適化(例: スタック割り当て)を行うことを可能にします。

このコミットでは、sha256block_decl.go 内の block 関数に //go:noescape が付与されており、アセンブリ実装がヒープ割り当てを行わないことを保証しています。

技術的詳細

このコミットの核心は、SHA-256の圧縮関数をAMD64アセンブリ言語で再実装した点にあります。SHA-256の圧縮関数は、8つの32ビットレジスタ(A, B, C, D, E, F, G, H)と、メッセージスケジュールされたワード(W_t)、そして64個の定数(K_t)を用いて64ラウンドの計算を行います。

アセンブリ実装では、これらの32ビットワードをAMD64の64ビットレジスタ(R8-R15など)に効率的にマッピングし、ビット単位の論理演算(ANDL, ORL, XORL, NOTL)、ビット回転(RORL)、ビットシフト(SHRL)、加算(ADDL)といった命令を直接使用します。

SHA-256のラウンド処理

SHA-256の各ラウンドは、以下の計算を繰り返します。

  1. メッセージスケジュールの計算 (W_t):

    • 最初の16ワード (W_0からW_15) は、入力メッセージブロックから直接取得されます。
    • 残りの48ワード (W_16からW_63) は、以前のワードから以下の式で計算されます。 W_t = SIGMA1(W_{t-2}) + W_{t-7} + SIGMA0(W_{t-15}) + W_{t-16} ここで、SIGMA0SIGMA1は特定のビット回転とシフトを含む関数です。
  2. 一時変数T1, T2の計算:

    • T1 = h + BIGSIGMA1(e) + Ch(e,f,g) + K_t + W_t
    • T2 = BIGSIGMA0(a) + Maj(a,b,c) ここで、BIGSIGMA0, BIGSIGMA1はビット回転を含む関数、ChはChoose関数、MajはMajority関数です。
  3. ハッシュ値の更新:

    • h = g
    • g = f
    • f = e
    • e = d + T1
    • d = c
    • c = b
    • b = a
    • a = T1 + T2

アセンブリ実装の最適化ポイント

sha256block_amd64.s のアセンブリコードは、上記の計算をAMD64の命令セットに最適化してマッピングしています。

  • レジスタ割り当て: 8つのハッシュワード(A-H)をR8からR15までの汎用レジスタに割り当てています。これにより、メモリへのアクセスを最小限に抑え、CPUレジスタの高速性を最大限に活用しています。
  • マクロの活用: MSGSCHEDULE0, MSGSCHEDULE1, SHA256T1, SHA256T2, SHA256ROUND などのマクロを定義し、繰り返し現れる計算パターンを抽象化しています。これにより、コードの可読性を保ちつつ、効率的な命令シーケンスを生成しています。
    • MSGSCHEDULE0: メッセージブロックの最初の16ワードを処理し、バイトオーダーをビッグエンディアンからリトルエンディアンに変換(BSWAPL)しています。SHA-256はビッグエンディアンを前提としているため、リトルエンディアンのAMD64アーキテクチャではこの変換が必要です。
    • MSGSCHEDULE1: 残りの48ワードのメッセージスケジュール計算を実装しています。ROTR(ビット回転)やSHR(ビットシフト)命令を効率的に使用しています。
    • SHA256T1, SHA256T2: T1とT2の計算をそれぞれ実装しています。Ch関数とMaj関数は、ビット単位の論理演算(ANDL, XORL, NOTL)で直接表現されています。
    • SHA256ROUND: T1とT2の計算結果を用いて、ハッシュワードを更新する一連の処理をカプセル化しています。
  • ループ処理: 複数のメッセージブロックを処理するために、loopラベルとJEQ, JB命令を用いたループ構造が採用されています。
  • Goとの連携: TEXT ·block(SB),0,$264-24 のように、Go言語のリンカが認識できる形式で関数を定義しています。FP(フレームポインタ)レジスタを用いて、Go言語から渡される引数(digp)にアクセスしています。

このアセンブリ実装は、Go言語のコンパイラが生成するコードよりも、より密度の高い命令シーケンスと、レジスタのより効率的な利用を実現することで、SHA-256ハッシュ計算のパフォーマンスを大幅に向上させています。特に、ビット回転や論理演算が多用されるSHA-256のようなアルゴリズムでは、アセンブリレベルでの最適化が大きな効果を発揮します。

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

このコミットにおけるコアとなるコードの変更箇所は以下の4ファイルです。

  1. src/pkg/crypto/sha256/sha256.go

    --- a/src/pkg/crypto/sha256/sha256.go
    +++ b/src/pkg/crypto/sha256/sha256.go
    @@ -3,7 +3,7 @@
     
     // Package sha256 implements the SHA224 and SHA256 hash algorithms as defined
    -// in FIPS 180-2.
    +// in FIPS 180-4.
     package sha256
     
     import (
    
    • FIPS標準の参照が FIPS 180-2 から FIPS 180-4 に更新されました。これは、SHA-256アルゴリズムの最新の標準文書への準拠を示すものです。
  2. src/pkg/crypto/sha256/sha256block.go

    --- a/src/pkg/crypto/sha256/sha256block.go
    +++ b/src/pkg/crypto/sha256/sha256block.go
    @@ -2,6 +2,8 @@
     // Use of this source code is governed by a BSD-style
     // license that can be found in the LICENSE file.
     
    +// +build !amd64
    +
     // SHA256 block step.
     // In its own file so that a faster assembly or C version
     // can be substituted easily.
    
    • ファイルの先頭に // +build !amd64 というビルドタグが追加されました。これは、このGo言語で書かれたblock関数が、AMD64アーキテクチャ以外の場合にのみコンパイルされることを意味します。AMD64環境では、新しく追加されるアセンブリ実装が優先されます。
  3. src/pkg/crypto/sha256/sha256block_amd64.s (新規追加ファイル)

    // Copyright 2013 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.
    
    #include "../../../cmd/ld/textflag.h"
    
    // SHA256 block routine. See sha256block.go for Go equivalent.
    //
    // The algorithm is detailed in FIPS 180-4:
    //
    //  http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
    //
    // Wt = Mt; for 0 <= t <= 15
    // Wt = SIGMA1(Wt-2) + SIGMA0(Wt-15) + Wt-16; for 16 <= t <= 63
    //
    // a = H0
    // b = H1
    // c = H2
    // d = H3
    // e = H4
    // f = H5
    // g = H6
    // h = H7
    //
    // for t = 0 to 63 {
    //    T1 = h + BIGSIGMA1(e) + Ch(e,f,g) + Kt + Wt
    //    T2 = BIGSIGMA0(a) + Maj(a,b,c)
    //    h = g
    //    g = f
    //    f = e
    //    e = d + T1
    //    d = c
    //    c = b
    //    b = a
    //    a = T1 + T2
    // }
    //
    // H0 = a + H0
    // H1 = b + H1
    // H2 = c + H2
    // H3 = d + H3
    // H4 = e + H4
    // H5 = f + H5
    // H6 = g + H6
    // H7 = h + H7
    
    // Wt = Mt; for 0 <= t <= 15
    #define MSGSCHEDULE0(index) \
    	MOVL	(index*4)(SI), AX; \
    	BSWAPL	AX; \
    	MOVL	AX, (index*4)(BP)
    
    // Wt = SIGMA1(Wt-2) + Wt-7 + SIGMA0(Wt-15) + Wt-16; for 16 <= t <= 63
    //   SIGMA0(x) = ROTR(7,x) XOR ROTR(18,x) XOR SHR(3,x)
    //   SIGMA1(x) = ROTR(17,x) XOR ROTR(19,x) XOR SHR(10,x)
    #define MSGSCHEDULE1(index) \
    	MOVL	((index-2)*4)(BP), AX; \
    	MOVL	AX, CX; \
    	RORL	$17, AX; \
    	MOVL	CX, DX; \
    	RORL	$19, CX; \
    	SHRL	$10, DX; \
    	MOVL	((index-15)*4)(BP), BX; \
    	XORL	CX, AX; \
    	MOVL	BX, CX; \
    	XORL	DX, AX; \
    	RORL	$7, BX; \
    	MOVL	CX, DX; \
    	SHRL	$3, DX; \
    	RORL	$18, CX; \
    	ADDL	((index-7)*4)(BP), AX; \
    	XORL	CX, BX; \
    	XORL	DX, BX; \
    	ADDL	((index-16)*4)(BP), BX; \
    	ADDL	BX, AX; \
    	MOVL	AX, ((index)*4)(BP)
    
    // Calculate T1 in AX - uses AX, CX and DX registers.
    // h is also used as an accumulator. Wt is passed in AX.
    //   T1 = h + BIGSIGMA1(e) + Ch(e, f, g) + Kt + Wt
    //     BIGSIGMA1(x) = ROTR(6,x) XOR ROTR(11,x) XOR ROTR(25,x)
    //     Ch(x, y, z) = (x AND y) XOR (NOT x AND z)
    #define SHA256T1(const, e, f, g, h) \
    	ADDL	AX, h; \
    	MOVL	e, AX; \
    	ADDL	$const, h; \
    	MOVL	e, CX; \
    	RORL	$6, AX; \
    	MOVL	e, DX; \
    	RORL	$11, CX; \
    	XORL	CX, AX; \
    	MOVL	e, CX; \
    	RORL	$25, DX; \
    	ANDL	f, CX; \
    	XORL	AX, DX; \
    	MOVL	e, AX; \
    	NOTL	AX; \
    	ADDL	DX, h; \
    	ANDL	g, AX; \
    	XORL	CX, AX; \
    	ADDL	h, AX
    
    // Calculate T2 in BX - uses BX, CX, DX and DI registers.
    //   T2 = BIGSIGMA0(a) + Maj(a, b, c)
    //     BIGSIGMA0(x) = ROTR(2,x) XOR ROTR(13,x) XOR ROTR(22,x)
    //     Maj(x, y, z) = (x AND y) XOR (x AND z) XOR (y AND z)
    #define SHA256T2(a, b, c) \
    	MOVL	a, DI; \
    	MOVL	c, BX; \
    	RORL	$2, DI; \
    	MOVL	a, DX; \
    	ANDL	b, BX; \
    	RORL	$13, DX; \
    	MOVL	a, CX; \
    	ANDL	c, CX; \
    	XORL	DX, DI; \
    	XORL	CX, BX; \
    	MOVL	a, DX; \
    	MOVL	b, CX; \
    	RORL	$22, DX; \
    	ANDL	a, CX; \
    	XORL	CX, BX; \
    	XORL	DX, DI; \
    	ADDL	DI, BX
    
    // Calculate T1 and T2, then e = d + T1 and a = T1 + T2.
    // The values for e and a are stored in d and h, ready for rotation.
    #define SHA256ROUND(index, const, a, b, c, d, e, f, g, h) \
    	SHA256T1(const, e, f, g, h); \
    	SHA256T2(a, b, c); \
    	MOVL	BX, h; \
    	ADDL	AX, d; \
    	ADDL	AX, h
    
    #define SHA256ROUND0(index, const, a, b, c, d, e, f, g, h) \
    	MSGSCHEDULE0(index); \
    	SHA256ROUND(index, const, a, b, c, d, e, f, g, h)
    
    #define SHA256ROUND1(index, const, a, b, c, d, e, f, g, h) \
    	MSGSCHEDULE1(index); \
    	SHA256ROUND(index, const, a, b, c, d, e, f, g, h)
    
    TEXT ·block(SB),0,$264-24
    	MOVQ	p_base+8(FP), SI
    	MOVQ	p_len+16(FP), DX
    	SHRQ	$6, DX
    	SHLQ	$6, DX
    
    	LEAQ	(SI)(DX*1), DI
    	MOVQ	DI, 256(SP)
    	CMPQ	SI, DI
    	JEQ	end
    
    	MOVQ	dig+0(FP), BP
    	MOVL	(0*4)(BP), R8		// a = H0
    	MOVL	(1*4)(BP), R9		// b = H1
    	MOVL	(2*4)(BP), R10		// c = H2
    	MOVL	(3*4)(BP), R11		// d = H3
    	MOVL	(4*4)(BP), R12		// e = H4
    	MOVL	(5*4)(BP), R13		// f = H5
    	MOVL	(6*4)(BP), R14		// g = H6
    	MOVL	(7*4)(BP), R15		// h = H7
    
    loop:
    	MOVQ	SP, BP			// message schedule
    
    	SHA256ROUND0(0, 0x428a2f98, R8, R9, R10, R11, R12, R13, R14, R15)
    	SHA256ROUND0(1, 0x71374491, R15, R8, R9, R10, R11, R12, R13, R14)
    	SHA256ROUND0(2, 0xb5c0fbcf, R14, R15, R8, R9, R10, R11, R12, R13)
    	SHA256ROUND0(3, 0xe9b5dba5, R13, R14, R15, R8, R9, R10, R11, R12)
    	SHA256ROUND0(4, 0x3956c25b, R12, R13, R14, R15, R8, R9, R10, R11)
    	SHA256ROUND0(5, 0x59f111f1, R11, R12, R13, R14, R15, R8, R9, R10)
    	SHA256ROUND0(6, 0x923f82a4, R10, R11, R12, R13, R14, R15, R8, R9)
    	SHA256ROUND0(7, 0xab1c5ed5, R9, R10, R11, R12, R13, R14, R15, R8)
    	SHA256ROUND0(8, 0xd807aa98, R8, R9, R10, R11, R12, R13, R14, R15)
    	SHA256ROUND0(9, 0x12835b01, R15, R8, R9, R10, R11, R12, R13, R14)
    	SHA256ROUND0(10, 0x243185be, R14, R15, R8, R9, R10, R11, R12, R13)
    	SHA256ROUND0(11, 0x550c7dc3, R13, R14, R15, R8, R9, R10, R11, R12)
    	SHA256ROUND0(12, 0x72be5d74, R12, R13, R14, R15, R8, R9, R10, R11)
    	SHA256ROUND0(13, 0x80deb1fe, R11, R12, R13, R14, R15, R8, R9, R10)
    	SHA256ROUND0(14, 0x9bdc06a7, R10, R11, R12, R13, R14, R15, R8, R9)
    	SHA256ROUND0(15, 0xc19bf174, R9, R10, R11, R12, R13, R14, R15, R8)
    
    	SHA256ROUND1(16, 0xe49b69c1, R8, R9, R10, R11, R12, R13, R14, R15)
    	SHA256ROUND1(17, 0xefbe4786, R15, R8, R9, R10, R11, R12, R13, R14)
    	SHA256ROUND1(18, 0x0fc19dc6, R14, R15, R8, R9, R10, R11, R12, R13)
    	SHA256ROUND1(19, 0x240ca1cc, R13, R14, R15, R8, R9, R10, R11, R12)
    	SHA256ROUND1(20, 0x2de92c6f, R12, R13, R14, R15, R8, R9, R10, R11)
    	SHA256ROUND1(21, 0x4a7484aa, R11, R12, R13, R14, R15, R8, R9, R10)
    	SHA256ROUND1(22, 0x5cb0a9dc, R10, R11, R12, R13, R14, R15, R8, R9)
    	SHA256ROUND1(23, 0x76f988da, R9, R10, R11, R12, R13, R14, R15, R8)
    	SHA256ROUND1(24, 0x983e5152, R8, R9, R10, R11, R12, R13, R14, R15)
    	SHA256ROUND1(25, 0xa831c66d, R15, R8, R9, R10, R11, R12, R13, R14)
    	SHA256ROUND1(26, 0xb00327c8, R14, R15, R8, R9, R10, R11, R12, R13)
    	SHA256ROUND1(27, 0xbf597fc7, R13, R14, R15, R8, R9, R10, R11, R12)
    	SHA256ROUND1(28, 0xc6e00bf3, R12, R13, R14, R15, R8, R9, R10, R11)
    	SHA256ROUND1(29, 0xd5a79147, R11, R12, R13, R14, R15, R8, R9, R10)
    	SHA256ROUND1(30, 0x06ca6351, R10, R11, R12, R13, R14, R15, R8, R9)
    	SHA256ROUND1(31, 0x14292967, R9, R10, R11, R12, R13, R14, R15, R8)
    	SHA256ROUND1(32, 0x27b70a85, R8, R9, R10, R11, R12, R13, R14, R15)
    	SHA256ROUND1(33, 0x2e1b2138, R15, R8, R9, R10, R11, R12, R13, R14)
    	SHA256ROUND1(34, 0x4d2c6dfc, R14, R15, R8, R9, R10, R11, R12, R13)
    	SHA256ROUND1(35, 0x53380d13, R13, R14, R15, R8, R9, R10, R11, R12)
    	SHA256ROUND1(36, 0x650a7354, R12, R13, R14, R15, R8, R9, R10, R11)
    	SHA256ROUND1(37, 0x766a0abb, R11, R12, R13, R14, R15, R8, R9, R10)
    	SHA256ROUND1(38, 0x81c2c92e, R10, R11, R12, R13, R14, R15, R8, R9)
    	SHA256ROUND1(39, 0x92722c85, R9, R10, R11, R12, R13, R14, R15, R8)
    	SHA256ROUND1(40, 0xa2bfe8a1, R8, R9, R10, R11, R12, R13, R14, R15)
    	SHA256ROUND1(41, 0xa81a664b, R15, R8, R9, R10, R11, R12, R13, R14)
    	SHA256ROUND1(42, 0xc24b8b70, R14, R15, R8, R9, R10, R11, R12, R13)
    	SHA256ROUND1(43, 0xc76c51a3, R13, R14, R15, R8, R9, R10, R11, R12)
    	SHA256ROUND1(44, 0xd192e819, R12, R13, R14, R15, R8, R9, R10, R11)
    	SHA256ROUND1(45, 0xd6990624, R11, R12, R13, R14, R15, R8, R9, R10)
    	SHA256ROUND1(46, 0xf40e3585, R10, R11, R12, R13, R14, R15, R8, R9)
    	SHA256ROUND1(47, 0x106aa070, R9, R10, R11, R12, R13, R14, R15, R8)
    	SHA256ROUND1(48, 0x19a4c116, R8, R9, R10, R11, R12, R13, R14, R15)
    	SHA256ROUND1(49, 0x1e376c08, R15, R8, R9, R10, R11, R12, R13, R14)
    	SHA256ROUND1(50, 0x2748774c, R14, R15, R8, R9, R10, R11, R12, R13)
    	SHA256ROUND1(51, 0x34b0bcb5, R13, R14, R15, R8, R9, R10, R11, R12)
    	SHA256ROUND1(52, 0x391c0cb3, R12, R13, R14, R15, R8, R9, R10, R11)
    	SHA256ROUND1(53, 0x4ed8aa4a, R11, R12, R13, R14, R15, R8, R9, R10)
    	SHA256ROUND1(54, 0x5b9cca4f, R10, R11, R12, R13, R14, R15, R8, R9)
    	SHA26ROUND1(55, 0x682e6ff3, R9, R10, R11, R12, R13, R14, R15, R8)
    	SHA256ROUND1(56, 0x748f82ee, R8, R9, R10, R11, R12, R13, R14, R15)
    	SHA256ROUND1(57, 0x78a5636f, R15, R8, R9, R10, R11, R12, R13, R14)
    	SHA256ROUND1(58, 0x84c87814, R14, R15, R8, R9, R10, R11, R12, R13)
    	SHA256ROUND1(59, 0x8cc70208, R13, R14, R15, R8, R9, R10, R11, R12)
    	SHA256ROUND1(60, 0x90befffa, R12, R13, R14, R15, R8, R9, R10, R11)
    	SHA256ROUND1(61, 0xa4506ceb, R11, R12, R13, R14, R15, R8, R9, R10)
    	SHA256ROUND1(62, 0xbef9a3f7, R10, R11, R12, R13, R14, R15, R8, R9)
    	SHA256ROUND1(63, 0xc67178f2, R9, R10, R11, R12, R13, R14, R15, R8)
    
    	MOVQ	dig+0(FP), BP
    	ADDL	(0*4)(BP), R8	// H0 = a + H0
    	MOVL	R8, (0*4)(BP)
    	ADDL	(1*4)(BP), R9	// H1 = b + H1
    	MOVL	R9, (1*4)(BP)
    	ADDL	(2*4)(BP), R10	// H2 = c + H2
    	MOVL	R10, (2*4)(BP)
    	ADDL	(3*4)(BP), R11	// H3 = d + H3
    	MOVL	R11, (3*4)(BP)
    	ADDL	(4*4)(BP), R12	// H4 = e + H4
    	MOVL	R12, (4*4)(BP)
    	ADDL	(5*4)(BP), R13	// H5 = f + H5
    	MOVL	R13, (5*4)(BP)
    	ADDL	(6*4)(BP), R14	// H6 = g + H6
    	MOVL	R14, (6*4)(BP)
    	ADDL	(7*4)(BP), R15	// H7 = h + H7
    	MOVL	R15, (7*4)(BP)
    
    	ADDQ	$64, SI
    	CMPQ	SI, 256(SP)
    	JB	loop
    
    end:
    	RET
    
    • このファイルは新規追加され、SHA-256のブロック処理をAMD64アセンブリ言語で実装しています。
    • SHA-256のアルゴリズム(メッセージスケジュール、T1/T2計算、ハッシュ値更新)が、AMD64のレジスタと命令(MOVL, ADDL, XORL, RORL, SHRL, BSWAPLなど)を用いて効率的に記述されています。
    • #define を用いて、繰り返し現れる計算パターンをマクロとして定義し、コードの可読性と保守性を高めています。
    • Go言語の関数呼び出し規約に従い、引数(dig, p)へのアクセスや、戻り値の処理が行われています。
  4. src/pkg/crypto/sha256/sha256block_decl.go (新規追加ファイル)

    // Copyright 2013 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.
    
    // +build amd64
    
    package sha256
    
    //go:noescape
    
    func block(dig *digest, p []byte)
    
    • このファイルも新規追加され、sha256block_amd64.s で実装されたアセンブリ関数 block のGo言語側の宣言を提供します。
    • // +build amd64 ビルドタグにより、このファイルはAMD64アーキテクチャの場合にのみコンパイルされます。
    • //go:noescape ディレクティブは、この関数がヒープにメモリを割り当てないことをGoコンパイラに伝えます。これにより、コンパイラはより積極的な最適化を行うことができます。
    • func block(dig *digest, p []byte) は、アセンブリ関数がGo言語からどのように呼び出されるかのシグネチャを定義しています。

コアとなるコードの解説

このコミットのコアとなるコードは、src/pkg/crypto/sha256/sha256block_amd64.s に記述されたAMD64アセンブリ言語によるSHA-256ブロック処理の実装です。

このアセンブリコードは、SHA-256の圧縮関数を、CPUのレジスタを最大限に活用し、効率的な命令シーケンスで実行するように設計されています。

主要な要素と役割:

  1. TEXT ·block(SB),0,$264-24:

    • これはGo言語のリンカが認識するアセンブリ関数の宣言です。
    • ·block は、sha256 パッケージ内の block 関数を指します。Go言語では、パッケージ内の非エクスポート関数は . で始まる名前になります。
    • SB はStatic Baseレジスタを意味し、グローバルシンボルであることを示します。
    • 0 は関数がフレームポインタを必要としないことを示します(この場合はスタックフレームサイズが指定されているため、実際にはフレームポインタが使用されます)。
    • $264-24 は、この関数のスタックフレームサイズが264バイトであり、そのうち24バイトが引数領域であることを示します。このスタックフレームは、メッセージスケジュールされたワード(W_t)を格納するために使用されます。
  2. 引数の取得:

    • MOVQ p_base+8(FP), SI: Go言語から渡された p スライス(入力メッセージブロック)の基底アドレスを SI レジスタにロードします。FP はフレームポインタで、p_base+8 は引数 p の基底アドレスへのオフセットです。
    • MOVQ p_len+16(FP), DX: p スライスの長さを DX レジスタにロードします。
    • SHRQ $6, DXSHLQ $6, DX: これは、入力データの長さを64バイト(512ビット)のブロック数に変換し、再びバイト数に戻すことで、処理すべきブロックの総バイト数を計算しています。SHA-256は512ビット(64バイト)のブロック単位で処理されるため、入力データの長さを64の倍数に切り捨てています。
  3. ハッシュ状態の初期化:

    • MOVQ dig+0(FP), BP: Go言語から渡された digest 構造体(現在のハッシュ状態)へのポインタを BP レジスタにロードします。
    • MOVL (0*4)(BP), R8 から MOVL (7*4)(BP), R15: digest 構造体から、現在の8つのハッシュワード(H0~H7)をR8からR15までの汎用レジスタにロードします。これらのレジスタは、SHA-256のラウンド計算中にA~Hの内部状態として使用されます。
  4. loop:

    • 複数のメッセージブロックを処理するためのループの開始点です。
    • MOVQ SP, BP: メッセージスケジュールされたワードを格納するために、スタックポインタ SPBP レジスタにコピーします。これにより、スタック上の領域を一時的な作業領域として使用します。
  5. SHA256ROUND0SHA256ROUND1 マクロ:

    • これらはSHA-256の64ラウンドの計算を実行するためのマクロです。
    • SHA256ROUND0 は最初の16ラウンド(メッセージブロックから直接W_tをロード)に使用されます。
    • SHA256ROUND1 は残りの48ラウンド(メッセージスケジュール計算を実行してW_tを生成)に使用されます。
    • 各マクロは、現在のラウンドの定数(K_t)と、8つのハッシュワード(A, B, C, D, E, F, G, H)を引数として受け取ります。
    • 内部的には、MSGSCHEDULE0/MSGSCHEDULE1 でW_tを計算し、SHA256T1/SHA256T2 でT1/T2を計算し、最終的にSHA256ROUND でハッシュワードを更新します。
  6. ハッシュ状態の更新と保存:

    • 64ラウンドの計算が終了した後、ADDL (0*4)(BP), R8 から ADDL (7*4)(BP), R15 の命令群で、計算された新しいハッシュワード(R8-R15)を、元のハッシュ状態(dig構造体)に加算します。これは、SHA-256の仕様に従い、各ブロックの処理後に累積ハッシュ値を更新するステップです。
    • MOVL R8, (0*4)(BP) などで、更新されたハッシュ値を再びdigest構造体のメモリ領域に書き戻します。
  7. ループの継続と終了:

    • ADDQ $64, SI: 次のメッセージブロックの開始アドレスを計算するために、入力データポインタ SI を64バイト進めます。
    • CMPQ SI, 256(SP)JB loop: 処理すべきブロックが残っているかを確認し、残っていれば loop にジャンプして次のブロックの処理を開始します。256(SP) は、処理すべき入力データの終了アドレスが格納されているスタック上の場所を指します。
    • JEQ end: 処理すべきブロックがなければ end にジャンプします。
    • RET: 関数から戻ります。

このアセンブリコードは、SHA-256の複雑なビット操作と加算を、AMD64の命令セットに直接マッピングすることで、Go言語のコンパイラが生成する汎用的なコードよりも、はるかに高速な実行を実現しています。特に、レジスタの効率的な利用、バイトオーダーの変換(BSWAPL)、そしてビット回転命令(RORL)の直接使用が、パフォーマンス向上に大きく貢献しています。

関連リンク

参考にした情報源リンク