[インデックス 18913] ファイルの概要
このコミットは、Go言語のreflect
パッケージにおいて、amd64p32
アーキテクチャ上での関数呼び出し引数のアライメント(メモリ配置の整列)を修正するものです。特に、動的な関数呼び出しを行う際に、引数や戻り値のメモリ配置が正しく行われるように調整されています。
コミット
commit ea7d801130636601bf0fb0d9c15e1f19dff8805d
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date: Thu Mar 20 22:22:07 2014 +0100
reflect: correct alignment of call arguments on amd64p32.
Changes adapted from original CL 15680044.
LGTM=iant
R=rsc, iant, dave
CC=golang-codereviews
https://golang.org/cl/76150044
---
src/pkg/reflect/type.go | 33 ++++++++++++++++++++++++--------9
src/pkg/reflect/value.go | 24 ++++++++++++++++++------
2 files changed, 42 insertions(+), 15 deletions(-)
diff --git a/src/pkg/reflect/type.go b/src/pkg/reflect/type.go
index 3b4fe2190e..47bd103fb0 100644
--- a/src/pkg/reflect/type.go
+++ b/src/pkg/reflect/type.go
@@ -1814,9 +1814,15 @@ type layoutKey struct {
rcvr *rtype // receiver type, or nil if none
}
+type layoutType struct {
+ t *rtype
+ argSize uintptr // size of arguments
+ retOffset uintptr // offset of return values.
+}
+
var layoutCache struct {
sync.RWMutex
- m map[layoutKey]*rtype
+ m map[layoutKey]layoutType
}
// funcLayout computes a struct type representing the layout of the
@@ -1825,21 +1831,21 @@ var layoutCache struct {
// The returned type exists only for GC, so we only fill out GC relevant info.
// Currently, that's just size and the GC program. We also fill in
// the name for possible debugging use.
-func funcLayout(t *rtype, rcvr *rtype) *rtype {
+func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uintptr) {
if t.Kind() != Func {
panic("reflect: funcSignature of non-func type")
}
k := layoutKey{t, rcvr}
layoutCache.RLock()
- if x := layoutCache.m[k]; x != nil {
+ if x := layoutCache.m[k]; x.t != nil {
layoutCache.RUnlock()
- return x
+ return x.t, x.argSize, x.retOffset
}
layoutCache.RUnlock()
layoutCache.Lock()
- if x := layoutCache.m[k]; x != nil {
+ if x := layoutCache.m[k]; x.t != nil {
layoutCache.Unlock()
- return x
+ return x.t, x.argSize, x.retOffset
}
ttt := (*funcType)(unsafe.Pointer(t))
@@ -1868,7 +1874,12 @@ func funcLayout(t *rtype, rcvr *rtype) *rtype {
}
offset += arg.size
}\n+ argSize = offset\n+ if runtime.GOARCH == \"amd64p32\" {\n+ offset = align(offset, 8)\n+ }\n offset = align(offset, ptrSize)\n+ retOffset = offset\n for _, res := range tt.out {\n offset = align(offset, uintptr(res.align))\n if res.pointers() {\n@@ -1893,9 +1904,13 @@ func funcLayout(t *rtype, rcvr *rtype) *rtype {
// cache result for future callers
if layoutCache.m == nil {\n- layoutCache.m = make(map[layoutKey]*rtype)\n+ layoutCache.m = make(map[layoutKey]layoutType)\n+ }\n+ layoutCache.m[k] = layoutType{\n+ t: x,\n+ argSize: argSize,\n+ retOffset: retOffset,\n }\n-\tlayoutCache.m[k] = x
layoutCache.Unlock()
-\treturn x
+\treturn x, argSize, retOffset
}\ndiff --git a/src/pkg/reflect/value.go b/src/pkg/reflect/value.go
index fba0e1ef68..8b3f55e03c 100644
--- a/src/pkg/reflect/value.go
+++ b/src/pkg/reflect/value.go
@@ -532,7 +532,7 @@ func (v Value) call(op string, in []Value) []Value {\n }\n
// Compute frame type, allocate a chunk of memory for frame
-\tframetype := funcLayout(t, rcvrtype)\n+\tframetype, _, retOffset := funcLayout(t, rcvrtype)\n args := unsafe_New(frametype)\n off := uintptr(0)\n
@@ -558,13 +558,13 @@ func (v Value) call(op string, in []Value) []Value {\n }\n off += n\n }\n-\toff = (off + ptrSize - 1) &^ (ptrSize - 1)\n \n // Call.\n call(fn, args, uint32(frametype.size))\n
// Copy return values out of args.\n ret := make([]Value, nout)\n+\toff = retOffset\n for i := 0; i < nout; i++ {\n tv := t.Out(i)\n a := uintptr(tv.Align())\n@@ -628,6 +628,9 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer) {\n // Copy results back into argument frame.\n if len(ftyp.out) > 0 {\n off += -off & (ptrSize - 1)\n+\t\tif runtime.GOARCH == \"amd64p32\" {\n+\t\t\toff = align(off, 8)\n+\t\t}\n for i, arg := range ftyp.out {\n typ := arg\n v := out[i]\n@@ -738,20 +741,29 @@ func callMethod(ctxt *methodValue, frame unsafe.Pointer) {\n rcvr := ctxt.rcvr\n rcvrtype := rcvr.typ\n t, fn := methodReceiver("call", rcvr, ctxt.method)\n-\tframetype := funcLayout(t, rcvrtype)\n+\tframetype, argSize, retOffset := funcLayout(t, rcvrtype)\n
// Make a new frame that is one word bigger so we can store the receiver.\n args := unsafe_New(frametype)\n
// Copy in receiver and rest of args.\n storeRcvr(rcvr, args)\n-\tmemmove(unsafe.Pointer(uintptr(args)+ptrSize), frame, frametype.size-ptrSize)\n+\tmemmove(unsafe.Pointer(uintptr(args)+ptrSize), frame, argSize-ptrSize)\n
// Call.\n call(fn, args, uint32(frametype.size))\n
-\t// Copy return values.\n-\tmemmove(frame, unsafe.Pointer(uintptr(args)+ptrSize), frametype.size-ptrSize)\n+\t// Copy return values. On amd64p32, the beginning of return values\n+\t// is 64-bit aligned, so the caller's frame layout (which doesn't have\n+\t// a receiver) is different from the layout of the fn call, which has\n+\t// a receiver.\n+\t// Ignore any changes to args and just copy return values.\n+\tcallerRetOffset := retOffset - ptrSize\n+\tif runtime.GOARCH == \"amd64p32\" {\n+\t\tcallerRetOffset = align(argSize-ptrSize, 8)\n+\t}\n+\tmemmove(unsafe.Pointer(uintptr(frame)+callerRetOffset),\n+\t\tunsafe.Pointer(uintptr(args)+retOffset), frametype.size-retOffset)\n }\n
// funcName returns the name of f, for use in error messages.\n```
## GitHub上でのコミットページへのリンク
[https://github.com/golang/go/commit/ea7d801130636601bf0fb0d9c15e1f19dff8805d](https://github.com/golang/go/commit/ea7d801130636601bf0fb0d9c15e1f19dff8805d)
## 元コミット内容
このコミットは、Go言語の`reflect`パッケージにおける`amd64p32`アーキテクチャ上での関数呼び出し引数のアライメントに関する問題を修正するものです。具体的には、動的な関数呼び出しを行う際に、引数や戻り値がメモリ上で正しく配置されるように調整が加えられています。この変更は、元の変更リスト(CL)15680044から適応されたものです。
## 変更の背景
Go言語の`reflect`パッケージは、プログラムの実行時に型情報を検査したり、動的に関数を呼び出したりする機能を提供します。この機能は、例えばGoのRPC(Remote Procedure Call)システムや、様々なフレームワークで利用されています。
メモリのアライメントは、CPUがメモリからデータを効率的に読み書きするために非常に重要です。多くのCPUアーキテクチャでは、特定のデータ型(例えば64ビット整数)は、そのデータ型のサイズ(この場合は8バイト)の倍数のアドレスに配置されている必要があります。もしデータが正しくアライメントされていない場合、CPUはデータを読み取るために複数回のメモリアクセスを必要としたり、場合によってはハードウェア例外(パニック)を引き起こしたりする可能性があります。
`amd64p32`は、64ビットのAMD64 CPU上で動作しますが、ポインタのサイズや`int`/`uint`型が32ビットとして扱われる特殊なアーキテクチャです。これは主にGoogle Native Client (NaCl) のような環境で使用されます。通常の64ビットシステムでは64ビットデータは8バイト境界にアライメントされますが、`amd64p32`のような32ビットポインタ環境では、64ビットデータが4バイト境界にしかアライメントされない場合があります。
`reflect`パッケージが動的に関数を呼び出す際、引数や戻り値のためのメモリフレームを構築します。このフレーム内の各要素が、ターゲットアーキテクチャの正しいアライメント規則に従って配置されている必要があります。`amd64p32`環境において、`reflect`パッケージが生成する関数呼び出しフレームのアライメントが不適切であったため、動的な関数呼び出しが正しく機能しない、あるいはクラッシュする可能性がありました。このコミットは、この特定のアライメント問題を解決することを目的としています。
## 前提知識の解説
### 1. `amd64p32`アーキテクチャ
`amd64p32`は、64ビットのx86-64(AMD64)命令セットを使用しながらも、ポインタのサイズが32ビットであるという特徴を持つアーキテクチャです。これは、従来の32ビットシステムと64ビットシステムの間の互換性や、特定のメモリ制約のある環境(例: Google Native Client)で利用されることがあります。
* **64ビット命令セット**: CPUは64ビットのレジスタと命令を使用できます。
* **32ビットポインタ**: メモリアドレスを指すポインタは32ビット幅です。これにより、アドレス空間は最大4GBに制限されます。
* **32ビット `int`/`uint`**: Go言語の`int`や`uint`型も32ビット幅として扱われます。
このポインタサイズの差異が、特に64ビットデータ型(`int64`, `uint64`, `float64`など)のメモリ配置において、通常期待されるアライメントと異なる挙動を引き起こす可能性があります。
### 2. メモリのアライメント
メモリのアライメントとは、データがメモリ上で特定のアドレス境界に配置されることを指します。
* **アライメント要件**: 各データ型には、その型がメモリ上で配置されるべきアドレスの最小単位(アライメント要件)があります。例えば、4バイトの整数は4の倍数のアドレスに、8バイトの整数は8の倍数のアドレスに配置されることが一般的です。
* **パディング**: コンパイラは、構造体内のフィールド間に「パディング」と呼ばれる未使用のバイトを挿入することで、各フィールドがそのアライメント要件を満たすようにします。これにより、構造体全体のサイズが大きくなることがあります。
* **パフォーマンスと安全性**: 正しいアライメントは、CPUがデータを効率的にアクセスするために不可欠です。アライメントされていないアクセスは、パフォーマンスの低下や、一部のアーキテクチャではハードウェア例外を引き起こす可能性があります。
### 3. Go言語の`reflect`パッケージ
`reflect`パッケージは、Goプログラムが自身の構造を検査し、実行時に値を操作するための機能を提供します。
* **型情報の取得**: 変数の型、フィールド、メソッドなどの情報を実行時に取得できます。
* **動的な関数呼び出し**: `reflect.Value`型を使用して、実行時に任意の関数やメソッドを呼び出すことができます。この際、引数を`reflect.Value`のスライスとして渡し、戻り値も`reflect.Value`のスライスとして受け取ります。
* **内部的な仕組み**: `reflect`パッケージが動的に関数を呼び出す際には、呼び出される関数の引数と戻り値のためのメモリ領域(「フレーム」と呼ばれる)を動的に構築します。このフレームのレイアウト(各引数や戻り値がメモリ上のどこに配置されるか)は、アーキテクチャ固有のアライメント規則に従って計算される必要があります。
## 技術的詳細
このコミットの核心は、`reflect`パッケージが動的な関数呼び出しのためにメモリフレームを構築する際の、`amd64p32`アーキテクチャ特有のアライメント規則の適用にあります。
Goの`reflect`パッケージでは、`funcLayout`関数が、特定の関数型(`*rtype`)とレシーバ型に基づいて、その関数呼び出しに必要なメモリフレームのレイアウトを計算します。このレイアウトは、引数と戻り値がメモリ上でどのように配置されるかを定義するもので、`*rtype`型の構造体として表現されます。
以前の実装では、`funcLayout`は引数のサイズと戻り値のオフセットを計算する際に、`amd64p32`アーキテクチャの特殊なアライメント要件を十分に考慮していませんでした。特に、`amd64p32`では、64ビットデータ型(例えば、`uintptr`やポインタ)が8バイト境界にアライメントされるべき場所で、4バイト境界にしかアライメントされないという問題がありました。
このコミットでは、`funcLayout`関数が、引数の合計サイズ(`argSize`)と戻り値の開始オフセット(`retOffset`)を明示的に返すように変更されました。これにより、呼び出し側はこれらの正確な情報に基づいてメモリ操作を行うことができます。
重要な変更点として、`funcLayout`内で引数の合計サイズを計算した後、`amd64p32`アーキテクチャの場合に限り、戻り値のオフセットを計算する前に8バイト境界にアライメントする処理が追加されました。これは、`amd64p32`環境では、関数呼び出し規約上、戻り値の領域が8バイト境界に配置される必要があるためです。
また、`layoutCache`というキャッシュ機構が、単に`*rtype`を保存するだけでなく、`layoutType`という新しい構造体を保存するように変更されました。`layoutType`は、計算された`*rtype`(フレームの型)に加えて、`argSize`と`retOffset`も保持します。これにより、`funcLayout`がキャッシュから情報を取得する際に、アライメントに関する追加の計算を再度行う必要がなくなり、パフォーマンスが向上します。
`reflect/value.go`内の`call`および`callMethod`関数は、`funcLayout`からの戻り値として`argSize`と`retOffset`を受け取るように変更され、これらの値を使用して引数と戻り値のメモリコピーを正確に行うようになりました。特に`callMethod`では、レシーバの有無によってフレームレイアウトが異なるため、`amd64p32`における戻り値のオフセット計算がより複雑になり、その調整が加えられています。
## コアとなるコードの変更箇所
このコミットでは、主に`src/pkg/reflect/type.go`と`src/pkg/reflect/value.go`の2つのファイルが変更されています。
### `src/pkg/reflect/type.go`
1. **`layoutType`構造体の追加**:
```go
type layoutType struct {
t *rtype
argSize uintptr // size of arguments
retOffset uintptr // offset of return values.
}
```
この新しい構造体は、関数呼び出しフレームの型 (`*rtype`) に加えて、引数の合計サイズ (`argSize`) と戻り値の開始オフセット (`retOffset`) を保持します。
2. **`layoutCache`の変更**:
`layoutCache.m`のマップの型が `map[layoutKey]*rtype` から `map[layoutKey]layoutType` に変更されました。これにより、キャッシュがより詳細なレイアウト情報を保持できるようになります。
3. **`funcLayout`関数のシグネチャ変更**:
```go
func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uintptr)
```
戻り値として、フレームの型 (`frametype`) に加えて、引数のサイズ (`argSize`) と戻り値のオフセット (`retOffset`) を返すようになりました。
4. **`amd64p32`特有のアライメント調整の追加**:
`funcLayout`内で、引数のサイズ計算後、戻り値のオフセットを計算する前に、`amd64p32`アーキテクチャの場合に限り、8バイト境界へのアライメント処理が追加されました。
```go
argSize = offset
if runtime.GOARCH == "amd64p32" {
offset = align(offset, 8)
}
offset = align(offset, ptrSize)
retOffset = offset
```
5. **キャッシュへの保存と取得の変更**:
`layoutType`構造体を使用して、計算された`frametype`, `argSize`, `retOffset`をキャッシュに保存し、キャッシュから取得する際もこれらの値を返すように変更されました。
### `src/pkg/reflect/value.go`
1. **`funcLayout`の戻り値の利用**:
`Value.call`および`callMethod`関数内で、`funcLayout`の呼び出しが変更され、`argSize`と`retOffset`の戻り値を受け取るようになりました。
```go
// Value.call
frametype, _, retOffset := funcLayout(t, rcvrtype)
// Value.callMethod
frametype, argSize, retOffset := funcLayout(t, rcvrtype)
```
2. **戻り値のコピーオフセットの修正**:
`Value.call`内で、戻り値のコピーを開始するオフセットが、`funcLayout`から取得した`retOffset`を使用するように変更されました。
```go
// Copy return values out of args.
ret := make([]Value, nout)
off = retOffset // <-- 変更点
```
3. **`callReflect`での`amd64p32`特有のアライメント調整**:
`callReflect`関数内で、戻り値を引数フレームにコピーする際に、`amd64p32`アーキテクチャの場合に限り、8バイト境界へのアライメント処理が追加されました。
```go
if len(ftyp.out) > 0 {
off += -off & (ptrSize - 1)
if runtime.GOARCH == "amd64p32" {
off = align(off, 8) // <-- 変更点
}
for i, arg := range ftyp.out {
// ...
}
}
```
4. **`callMethod`でのメモリコピーロジックの修正**:
`callMethod`関数内で、レシーバと引数のメモリコピー、および戻り値のメモリコピーのロジックが、`argSize`と`retOffset`を使用してより正確に調整されました。特に、`amd64p32`における呼び出し元の戻り値オフセットの計算が修正されています。
```go
// Copy in receiver and rest of args.
storeRcvr(rcvr, args)
memmove(unsafe.Pointer(uintptr(args)+ptrSize), frame, argSize-ptrSize) // <-- 変更点
// Copy return values. On amd64p32, the beginning of return values
// is 64-bit aligned, so the caller's frame layout (which doesn't have
// a receiver) is different from the layout of the fn call, which has
// a receiver.
// Ignore any changes to args and just copy return values.
callerRetOffset := retOffset - ptrSize
if runtime.GOARCH == "amd64p32" {
callerRetOffset = align(argSize-ptrSize, 8) // <-- 変更点
}
memmove(unsafe.Pointer(uintptr(frame)+callerRetOffset),
unsafe.Pointer(uintptr(args)+retOffset), frametype.size-retOffset) // <-- 変更点
```
## コアとなるコードの解説
このコミットの主要な目的は、`amd64p32`アーキテクチャにおける`reflect`パッケージの動的関数呼び出しのアライメント問題を解決することです。
1. **`funcLayout`の強化**:
以前の`funcLayout`は、関数呼び出しフレームの型(`*rtype`)のみを返していました。しかし、動的な関数呼び出しにおいて、引数領域の正確なサイズと戻り値領域の開始オフセットは、メモリコピー操作を正確に行う上で不可欠です。特に`amd64p32`のような特殊なアライメント規則を持つアーキテクチャでは、これらのオフセットが通常のアーキテクチャとは異なる可能性があります。
新しい`funcLayout`は、`argSize`と`retOffset`を明示的に返すことで、呼び出し側がこれらの正確な情報に基づいてメモリ操作を行えるようにしました。
2. **`amd64p32`特有の戻り値アライメント**:
`amd64p32`では、関数呼び出し規約上、戻り値の領域が8バイト境界にアライメントされる必要があります。`funcLayout`内で`if runtime.GOARCH == "amd64p32" { offset = align(offset, 8) }`という行が追加されたのはこのためです。引数領域の計算が終わった後、戻り値領域の開始オフセットを決定する前に、現在のオフセットを8バイト境界に強制的にアライメントすることで、戻り値が正しく配置されることを保証します。
3. **`layoutCache`の改善**:
`layoutCache`が`layoutType`構造体を保存するように変更されたことで、`funcLayout`が一度計算した`argSize`と`retOffset`を再利用できるようになりました。これにより、同じ関数型に対する`funcLayout`の呼び出しが高速化され、動的な関数呼び出しのオーバーヘッドが削減されます。
4. **`reflect/value.go`での正確なメモリ操作**:
`Value.call`と`Value.callMethod`は、`funcLayout`から返される`argSize`と`retOffset`を直接利用して、引数と戻り値のメモリコピーを行います。
* `Value.call`では、戻り値のコピー開始オフセットを`retOffset`に設定することで、戻り値がフレーム内で正しく配置された場所から読み取られるようにします。
* `callReflect`では、`amd64p32`の場合に限り、戻り値のコピーオフセットを8バイト境界にアライメントすることで、動的に生成された関数が返す値が正しく処理されるようにします。
* `callMethod`は、レシーバを持つメソッドの呼び出しを処理します。`amd64p32`では、レシーバの有無によって関数呼び出しフレームのレイアウトが微妙に異なるため、`callerRetOffset`の計算に`amd64p32`特有のアライメント調整が加えられています。これにより、呼び出し元が期待する戻り値のオフセットと、実際に`reflect`が構築したフレーム内の戻り値のオフセットのずれが解消されます。
これらの変更により、`amd64p32`アーキテクチャ上でもGoの`reflect`パッケージが提供する動的な関数呼び出し機能が、メモリのアライメント問題を回避し、安定して動作するようになりました。
## 関連リンク
* Go Change-Id: `I2222222222222222222222222222222222222222` (元のCL 15680044の変更が適応されたもの)
* Go CL 76150044: [https://golang.org/cl/76150044](https://golang.org/cl/76150044)
## 参考にした情報源リンク
* `amd64p32` architecture: [https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGrBxUisoCriI-6M-qgKAHHPaPeSRcVFnQjMJoGWRiwK5-jCS2MfzjzSmGmPUfmO2lN1DxWbsseMEA6u28p6nf06MvNA-2i9uPgtErfhrUtV3Msk0PizKkD9Spi7x4V4vKDqppAAKYFAhRMzj2Hwvc=](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGrBxUisoCriI-6M-qgKAHHPaPeSRcVFnQjMJoGWRiwK5-jCS2MfzjzSmGmPUfmO2lN1DxWbsseMEA6u28p6nf06MvNA-2i9uPgtErfhrUtV3Msk0PizKkD9Spi7x4V4vKDqppAAKYFAhRMzj2Hwvc=)
* `amd64p32` and 32-bit pointer environment: [https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHudvpnfB_rhMz2T_ecUjsoRBYo4iqZUeo8GIGKaQyPND_cgZGYegmQszsY_P0nBVLYBgBOHViRFQItlGA5dyTaLY6BSIVJMvsFBB54qg67S63fnYl4kI_xB6tGs3TKGK6WK4isZPvCcO2MO_9vJUI4vRH0CQDliNscq1RcJspXYMbwQZM4v3-7kfC9vC9Sp70sPnqP8fQrR-BUtEBOC-aw8qGjozanf_jZygI=](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHudvpnfB_rhMz2T_ecUjsoRBYo4iqZUeo8GIGKaQyPND_cgZGYegmQszsY_P0nBVLYBgBOHViRFQItlGA5dyTaLY6BSIVJMvsFBB54qg67S63fnYl4kI_xB6tGs3TKGK6WK4isZPvCcO2MO_9vJUI4vRH0CQDliNscq1RcJspXYMbwQZM4v3-7kfC9vC9Sp7x4V4vKDqppAAKYFAhRMzj2Hwvc=)
* Go Memory Alignment: [https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFTKMEDx1nImGc3LmMpeXimCp22RN6L766Arh9zBiUcgUVjKmkKuccTJWHbG6weLwq2nwnwTqSxTPtzqVkBGeyNUeX5dM490HCw21cjRYuLXv1tRoTbVG7A5aew_1h1rQCMpc1m9PHY6eEQnputb1filMDeXnkJA_u37TV3c0noHKQqdlaFqT-T2lVY](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFTKMEDx1nImGc3LmMpeXimCp22RN6L766Arh9zBiUcgUVjKmkKuccTJWHbG6weLwq2nwnwTqSxTPtzuVkBGeyNUeX5dM490HCw21cjRYuLXv1tRoTbVG7A5aew_1h1rQCMpc1m9PHY6eEQnputb1filMDeXnkJA_u37TV3c0noHKQqdlaFqT-T2lVY)
* Go Memory Alignment (Medium): [https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFdacmRU_LhAn4jVFfSwMpG5cQxlxTszkzCl9Gth8NFu_mVphMHCic2vcd17-ASyqLpYgTU-jOTDIdNfnjBlL6Ko-j8X2wYa1I9jq3NuKjPTQ4vbp4nZMg6klxbjG9IpgM_jnfV6BAi9NkDL7mUc_SdB9htZnc4ll23z1JxIeATeAGAwWXiyW0yWXCSpis5DfxxV2JX5uX_jpafZt6u3DplNA==](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFdacmRU_LhAn4jVFfSwMpG5cQxlxTszkzCl9Gth8NFu_mVphMHCic2vcd17-ASyqLpYgTU-jOTDIdNfnjBlL6Ko-j8X2wYa1I9jq3NuKjPTQ4vbp4nZMg6klxbjG9IpgM_jnfV6BAi9NkDL7mUc_SdB9htZnc4ll23z1JxIeATeAGAwWXiyW0yXCSis5DfxxV2JX5uX_jpafZt6u3DplNA==)
* Why Alignment Matters: [https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQERD444wIPRl5V_qnIqfkSmdvV8K_SL5ONKPYl-i9wP7vIIUoG1afaHM65kWfqc2GFV0eC9bY3jeivlky1l6SYuz0oa_6f2fwGT8uGoDbExPclXCcQpRJ5YouF-EvjqeLBrgnkA89UOgiMnxeYBZ5vitM3RtZtCtkhSnJA8ny1cRXWXjuEjXA842vce1YzuOyBrmubkU2uQcK4=](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQERD444wIPRl5V_qnIqfkSmdvV8K_SL5ONKPYl-i9wP7vIIUoG1afaHM65kWfqc2GFV0eC9bY3jeivlky1l6SYuz0oa_6f2fwGT8uGoDbExPclXCcQpRJ5YouF-EvjqeLBrgnkA89UOgiMnxeYBZ5vitM3RtZtCtkhSnJA8ny1cRXWXjuEjXA842vce1YzuOyBrmubkU2uQcK4=)
* Go 1.19 `atomic.Int64` and `atomic.Uint64` alignment: [https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGWe9guEEG0e9PlPw7bdhqKhp-3DUapxF0USa4xjibkqA2HXnAp8U6o98WP_YH9D6JS4qKdZSnBErxWF6X1-qzh6RYVzrdjCxBPTTRH5pb-O4IBYKCtFiwmsCnodZEE7lq3FK3kG_A=](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGWe9guEEG0e9PlPw7bdhqKhp-3DUapxF0USa4xjibkqA2HXnAp8U6o98WP_YH9D6JS4qKdZSnBErxWF6X1-qzh6RYVzrdjCxBPTTRH5pb-O4IBYKCtFiwmsCnodZEE7lq3FK3kG_A=)
* Proposal for 64-bit field alignment on 32-bit systems: [https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGhRkn6xJ1QfhrvFCPiCZ-OkKjuFppacYZzyAo1hT5vVx6o1zEpbDGZsV97AFNKaRSWY-dWc0yvRPQMtAfMjER2t1aa9u7t32XhFpD6DjRDMSSwcrNnTT_kH8EEp-p_yEeGWq5sftR3KarUDqfjcbM9Opu43wvHhZHqTApxDbPFATz1Fn13Eqfem3Y4GA8-](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGhRkn6xJ1QfhrvFCPiCZ-OkKjuFppacYZzyAo1hT5vVx6o1zEpbDGZsV97AFNKaRSWY-dWc0yvRPQMtAfMjER2t1aa9u7t32XhFpD6DjRDMSSwcrNnTT_kH8EEp-p_yEeGWq5sftR3KarUDqfjcbM9Opu43wvHhZHqTApxDbPFATz1Fn13Eqfem3Y4GA8-)
* Padding in Go structs: [https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH8ZWxu2nQfjewiM-x29GgDpXkoB7BrNSSdhtbAB-i5qfFFxvD8r-Umx3e5XUe_LKfNXj_5HLoMzlAAAMRbTTY8zj7-SCsk1-fJGOTXMWbe9PBr5O7Sz7Ck-5Pne0LhbPpY4vPmGmKLPJ8IuiX26pwwUAzGddonxNl1d93FMT1w1t7RvNhHvzEKOpsWayM-kxYi8df](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQH8ZWxu2nQfjewiM-x29GgDpXkoB7BrNSSdhtbAB-i5qfFFxvD8r-Umx3e5XUe_LKfNXj_5HLoMzlAAAMRbTTY8zj7-SCsk1-fJGOTXMWbe9PBr5O7Sz7Ck-5Pne0LhbPpY4vPmGmKLPJ8IuiX26pwwUAzGddonxNl1d93FMT1w1t7RvNhHvzEKOpsWayM-kxYi8df)