以下は、タグ付けされた共用体テンプレート「Storage」のダウンストリップされた例です。これは、共用体で囲まれた 2 つの型 L と R と、それらのどちらが格納されているかを示す bool を想定できます。インスタンス化では 2 つの異なるサイズの型が使用され、小さい方は実際には空です。
#include <utility>
struct Empty
{
};
struct Big
{
long a;
long b;
long c;
};
template<typename L, typename R>
class Storage final
{
public:
constexpr explicit Storage(const R& right) : payload{right}, isLeft{false}
{
}
private:
union Payload
{
constexpr Payload(const R& right) : right{right}
{
}
L left;
R right;
};
Payload payload;
bool isLeft;
};
// Toggle constexpr here
constexpr static Storage<Big, Empty> createStorage()
{
return Storage<Big, Empty>{Empty{}};
}
Storage<Big, Empty> createStorage2()
{
return createStorage();
}
- コンストラクターはRメンバーをEmptyで初期化し、そのメンバーのユニオンのコンストラクターのみを呼び出しています
- ユニオン全体がデフォルトで初期化されることはありません
- すべてのコンストラクターは constexpr です
そのため、関数「createStorage2」は bool タグのみを設定し、union はそのままにしておく必要があります。したがって、デフォルトの最適化「-O」でコンパイル結果が期待されます。
createStorage2():
mov rax, rdi
mov BYTE PTR [rdi+24], 0
ret
代わりに GCC と ICC の両方が次のようなものを生成します
createStorage2():
mov rax, rdi
mov QWORD PTR [rdi], 0
mov QWORD PTR [rdi+8], 0
mov QWORD PTR [rdi+16], 0
mov QWORD PTR [rdi+24], 0
ret
32 バイト構造全体をゼロにし、clang は予想されるコードを生成します。これはhttps://godbolt.org/z/VsDQUuで再現できます。「createStorage」静的関数からconstexprを削除すると、GCCはboolタグのみの目的の初期化に戻りますが、ICCは感銘を受けず、32バイトすべてを埋めます。
未使用のビットが「未定義」であると、ゼロに設定されて不要な CPU サイクルが消費されるなど、何でも許可されるため、これはおそらく標準的な違反ではありません。しかし、最初に効率上の理由から組合を導入し、組合員の規模が大きく異なる場合は面倒です。
ここで何が起こっているのですか?constexpr をコンストラクターから削除し、静的関数を使用できない場合、この動作を回避する方法はありますか?
補足: https://godbolt.org/z/FnjoPCのように、すべての constexpr が削除された場合でも、ICC はいくつかの追加操作を実行するようです。
createStorage2():
mov rax, rdi #44.16
mov BYTE PTR [-16+rsp], 0 #39.9
movups xmm0, XMMWORD PTR [-40+rsp] #44.16
movups xmm1, XMMWORD PTR [-24+rsp] #44.16
movups XMMWORD PTR [rdi], xmm0 #44.16
movups XMMWORD PTR [16+rdi], xmm1 #44.16
ret #44.16
これらのmovups命令の目的は何ですか?