16byteアライメントを持つメンバを持つオブジェクトに対応

これまでxnewで生成されたオブジェクトは16byteアライメントを持っていてもいなくても、関係なくmallocで取得したメモリに配置していました。
普通のnewも同様に、関係なく配置します。

なのでアライメントの問題はどうしようもないだろう、と思っていたのですが、
ゲームにおいては、16byteアライメントを持つことはよくありうる、といったこともあり、対応することにしました。

どうやって対応するか、この一週間悩んでやっと実装できました。


xnewで生成されたオブジェクトは、16byteアライメントを持つメンバがある場合16byteアライメントを保障します。
持たないオブジェクトに関してはこれまでどおりです。

#include <xmmintrin.h>

struct Vec128{
	__m128 a;
};

struct Spr{
	Vec128 v;
};

XTAL_BIND(Spr){
	it->def_var("v", &Spr::v);
}

void test(){
	Spr* p = new Spr();
	_mm_add_ps(p->v.a, p->v.a); // これは危険


	SmartPtr<Spr> s = xnew<Spr>();
	_mm_add_ps(s->v.a, s->v.a); // xnewで確保した場合は大丈夫
}
      • -

今回の実装の余波


AllocatorLibは次のようなインターフェイスと変更となりました。

class AllocatorLib{
public:
	virtual ~AllocatorLib(){}
	virtual void* malloc(std::size_t size);
	virtual void free(void* p, std::size_t size);

	virtual void* malloc_align(std::size_t size, std::size_t alignment);
	virtual void free_align(void* p, std::size_t size, std::size_t alignment);

	virtual void out_of_memory(){}
};

malloc_align, free_alignが増えています。
ただし、これらは実装しなくてもかまいません。デフォルトの実装でmallocを使って整列するメモリを返すようになっています。
memalignなど、もっと効率のよい関数がある場合にオーバーライドしてください。

  • -

また、RefCountingBase, Baseという基底クラスも修正しました。
仮想関数はすべてon_という接頭をつけた名前にリネームし、非仮想関数としました。
たとえばvirtual void rawmember();はvoid on_rawmember();となります。
デストラクタも非仮想とし、仮想関数を排除しました。
ただし、特別な仕組みを導入したため、それらは仮想関数と同様にオーバーライドできます。