/*	
 *	clipbmgc.c
 *
 *	Bitmap GC
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2014 Naoyuki Sawa
 *
 *	* Fri Dec 12 00:29:38 JST 2014 Naoyuki Sawa
 *	- 1st リリース。
 *	* Fri Dec 12 21:20:33 JST 2014 Naoyuki Sawa
 *	- gc_scan()をアセンブラ化しました。
 *	  当モジュールの機能とは直接関係有りませんが、gc_scan()は【スタックフレームを1命令で開放する工夫】の適用例にもなっています。
 *	- gc_mark()をアセンブラ化しました。
 *	- 以上のアセンブラ化を行った後、既存の「P/ECE用ガーベージコレクター」との速度比較を行って見ました。
 *	  Tclインタプリタで速度比較したところ、clipgc.cよりもclipbmgc.cの方が、約27%高速になりました。
 *	  今後は、極端にメモリ節約が必要なアプリケーションを除き、「Bitmap GC」を使用する方が良さそうです。
 *	* Sat Dec 13 11:52:53 JST 2014 Naoyuki Sawa
 *	- これまで、GC構造体にSRAM範囲を保持していましたが、ヒープ範囲を保持するように変更しました。
 *	  よく考えたら、GCのマーク対象は、SRAM全体ではなく、ヒープ範囲だけで良いことに気付いたからです。
 *	  (pceHeapAlloc()によって、メモリブロックが割り当てられる範囲は、ヒープ範囲だけだからです。)
 *	  上記のように変更することによって、Bitmapのサイズも減らす事ができるメリットも得られました。
 *	- 上記の変更は、既存の'clipgc.c'にも適用可能なのですが、'clipgc.c'には適用しないことにしました。
 *	  'clipgc.c'はこれまで長い間使用して来たので、今、大きな変更をするのは少し怖いと思ったからです。
 *	* Sat Dec 13 13:46:03 JST 2014 Naoyuki Sawa
 *	- 「Bitmap単位をまたぐメモリブロックの、後半のBitmap単位内のアドレスを参照するポインタが有っても、メモリブロックを開放してしまうバグ」を修正しました。
 *	  これまでは上記のバグのために、意図せず'積極的な'GCの挙動となってしまっていました。
 *	  今回上記のバグを修正したので、意図通り'保守的な'GCの挙動となりました。
 *	* Sun Dec 14 01:27:04 JST 2014 Naoyuki Sawa
 *	- 以下二点の変更を行い、高速化しました。
 *	- pceHeapAlloc()が、毎回ヒープ先頭から走査を行うのではなく、最後に割り当てたメモリブロックから走査を継続するようにしました。
 *	- pceHeapFree()は何もしないようにし、メモリ開放はBitmapGC_Run()に集約しました。
 *	  _gc.phmが指しているメモリブロックが開放されないようにするためです。
 *	* Mon Dec 15 21:12:11 JST 2014 Naoyuki Sawa
 *	- pceFileCreate(),pceFileDelete()を呼び出す前に、BitmapGC_Run()を行うようにしました。
 *	- pceFileCreate(),pceFileDelete()は、カーネル内でpceHeapAlloc()に静的リンクしています。
 *	  ヒープの残り容量が4KB未満のときに、上記のAPIを呼び出すと、BitmapGC_Run()が起動されず、APIが失敗します。
 *	  そこで、pceFileCreate(),pceFileDelete()を呼び出す前に、BitmapGC_Run()を行い、可能な限りヒープを空けることにしました。
 *	- 上記のAPIは、カーネル内でpceHeapFree()にも静的リンクしており、当モジュールによるpceHeapFree()の無効化の対象外となってしまいますが、問題有りません。
 *	  pceFileCreate(),pceFileDelete()は、APIの開始時に確保したメモリブロックを、APIの終了前に開放するからです。
 *	  アプリケーションから見れば、ヒープの状態は、pceFileCreate(),pceFileDelete()を呼び出す前と処理の後とで、変わりません。
 *	- 厳密には、pceFileCreate(),pceFileDelete()だけでなく、pceFileRename()とpceFileLoad()にも同様の対策が必要なのですが、今回は対策しないことにしました。
 *	  pceFileRename(),pceFileLoad()は、アプリケーションから利用しないと判断したからです。
 *	  判断理由については、clippce.cの「ヒープマネージャAPIの排他制御」の、'{{2014/09/05追記:'のコメントを参照してください。
 *	* Fri Dec 19 22:30:18 JST 2014 Naoyuki Sawa
 *	- _gc_run(),new_pceHeapAlloc(),new_pceFileCreate(),new_pceFileDelete()の排他制御を、ENTER_CS/LEAVE_CS⇒ENTER_IL(1)/LEAVE_ILに変更しました。
 */
#include "clip.h"

//□note
//│基本的なアルゴリズムは、clipgc.cと同じです。
//│当モジュールでは、詳細なコメントは省略しました。実装についてのコメントは、clipgc.cを参照してください。

//□note
//│clipgc.cではgc_scan()をFRAM4転送に転送して高速化しましたが、clipbmgc.cではgc_scan()をSRAM上で実行することにしました。
//│clipbmgc.cではgc_scan()自体の処理が少なく、bitarray_lsb1st_set()の処理が多くて、gc_scan()をFRAM4転送に転送しても効果が小さいからです。
//│試して見たところ、gc_scan()をFRAM4転送に転送しても、0.5%程度しか高速化しませんでした。
//│0.5%程度の高速化ならば、それよりもプログラムを小さく簡潔に保つ方が有益だと思ったので、gc_scan()をFRAM4転送に転送しないことにしました。

//□note
//│高速化するために'frambary.o'を高速RAMに配置することは、得策では有りません。
//│当モジュールはbitarray_lsb1st_test()を多用しているので、bitarray_lsb1st_test()が内部で使用しているbitarray_lsb1st_get()を高速化すると、効果が高いように思うかも知れません。
//│しかし実際は、bitarray_lsb1st_test()は、極力ワード単位でテストするように工夫しており、bitarray_lsb1st_get()にはさほど依存していません。
//│'frambary.o'を高速RAMに配置しても、Bitmap GCを利用するアプリケーション全体の性能は、1%程度しか高速化しませんでした。(Tclインタプリタで試しました)

//□note
//│_gc.phmが、次の空きメモリブロックでなく、最後に割り当てたメモリブロックを保持するようにした理由は、以下の通りである。
//│もし次の空きメモリブロックを保持するようにすると、その直前のメモリブロックがリサイズされた場合に、管理構造体でない位置を指してしまう。
//│対して、割り当て済みメモリブロックを指しておけば、それがリサイズされても問題無い。
//│当モジュールがpceHeapFree()をフックして無効化しているので、知らないところで割り当て済みメモリブロックが開放される危険も無い。
//│尚、BitmapGC_Init(),BitmapGC_Run()の直後のみ、ヒープ先頭の空きメモリブロックを指しているが、直前のリサイズの危険が無いので安全である。

/****************************************************************************
 *	
 ****************************************************************************/
typedef struct _GC {														//↓Asm版を最適化し易いフィールド並び順にしました。
	uint32_t		heapsize;							//+ 0,4 ヒープサイズ[byte]	(0:未初期化)	┐
	HEAPMEM*		heaptop;							//+ 4,4 ヒープ先頭アドレス			│			┐
	uint32_t*		bitmap;								//+ 8,4 Bitmap					├gc_scanが参照する	├gc_markが参照する
	uint8_t			unit;								//+12,1 1ビット当り(1<<unit)[byte]に相当する	┘			┘
	//											//+13,3 (パディング)
	HEAPMEM*		phm;								//+16,4 最後に割り当てたメモリブロックの管理構造体のアドレス	上記noteを参照せよ
	void*			(*old_pceHeapAlloc)(unsigned long size);			//+20,4 元のpceHeapAlloc()を保存
	int			(*old_pceHeapFree)(void* memp);					//+24,4 元のpceHeapFree()を保存
//{{2014/12/15追加:pceFileCreate(),pceFileDelete()を呼び出す前に、BitmapGC_Run()を行うようにしました。
	int			(*old_pceFileCreate)(const char* fname, unsigned long size);	//+28,4 元のpceFileCreate()を保存
	int			(*old_pceFileDelete)(const char* fname);			//+32,4元のpceFileDelete()を保存
//}}2014/12/15追加:pceFileCreate(),pceFileDelete()を呼び出す前に、BitmapGC_Run()を行うようにしました。
} GC;												//=36
static GC _gc;
/*--------------------------------------------------------------------------*/
#define HMO_NOREF_USER		0x1111						//どこからも参照されていないHMO_DEFAULT_USER
/*--------------------------------------------------------------------------*/
static void _gc_run() __attribute__((unused));
#ifndef PIECE
static int gc_scan(void* top, void* end);
static int gc_mark(HEAPMEM* phm);
static void gc_sweep(HEAPMEM* phm);
#else //PIECE
/*static*/ int gc_scan(void* top, void* end);
/*static*/ int gc_mark(HEAPMEM* phm);
/*static*/ void gc_sweep(HEAPMEM* phm);
#endif//PIECE
static void* new_pceHeapAlloc(unsigned long size);
static int new_pceHeapFree(void* memp);
//{{2014/12/15追加:pceFileCreate(),pceFileDelete()を呼び出す前に、BitmapGC_Run()を行うようにしました。
static int new_pceFileCreate(const char* fname, unsigned long size);
static int new_pceFileDelete(const char* fname);
//}}2014/12/15追加:pceFileCreate(),pceFileDelete()を呼び出す前に、BitmapGC_Run()を行うようにしました。
/*--------------------------------------------------------------------------*/
extern uint8_t __START_FRAM_ADDR[];
extern uint8_t __END_FRAM_ADDR[];
extern uint8_t __START_DEFAULT_CODE[];
extern uint8_t __END_DEFAULT_CODE[];
extern uint8_t __START_DEFAULT_DATA[];
extern uint8_t __END_DEFAULT_DATA[];
extern uint8_t __START_DEFAULT_BSS[];
extern uint8_t __END_DEFAULT_BSS[];
/****************************************************************************
 *	
 ****************************************************************************/
void BitmapGC_Init(int unit) {
	const pceAPPHEAD* apphead;
	HEAPMEM* phm;
	int size;
	//まず確実に未初期化状態に戻します。
	BitmapGC_Free();
	//Bitmap単位を格納します。
	if((unit < 2) || (unit > 8)) { DIE(); }	//2以上に制限した理由はpceHeapAlloc()がワード単位であるから。8以下に制限した理由はAsm版の実装時に1命令でシフトするため。
	_gc.unit = unit;
	//ヒープ先頭アドレスとヒープサイズ[byte]を取得します。
	apphead = (const pceAPPHEAD*)__START_DEFAULT_CODE;
	_gc.phm = _gc.heaptop = (HEAPMEM*)(apphead->bss_end + apphead->stack_size);	//_gc.phm…BitmapGC_Init()の直後は、ヒープ先頭の空きメモリブロックから走査する。
	if(_gc.heaptop->mark != HEAPMEMMK) { DIE(); }
	for(phm = _gc.heaptop; phm->chain; phm = phm->chain) { /** no job **/ }	//phmがHMO_SYSTEMを指す状態で抜ける。
	_gc.heapsize = (uint32_t)phm - (uint32_t)_gc.heaptop;
	//Bitmapを確保します。
	size = (_gc.heapsize + (1 << _gc.unit) - 1) >> _gc.unit;	//bits
	_gc.bitmap = bitarray_alloc(size);
	if(!_gc.bitmap) { DIE(); }
ENTER_CS;
	//APIをフックします。
	_gc.old_pceHeapAlloc = pceVectorSetKs(KSNO_HeapAlloc, new_pceHeapAlloc);
	_gc.old_pceHeapFree  = pceVectorSetKs(KSNO_HeapFree,  new_pceHeapFree);
//{{2014/12/15追加:pceFileCreate(),pceFileDelete()を呼び出す前に、BitmapGC_Run()を行うようにしました。
	_gc.old_pceFileCreate = pceVectorSetKs(KSNO_FileCreate, new_pceFileCreate);
	_gc.old_pceFileDelete = pceVectorSetKs(KSNO_FileDelete, new_pceFileDelete);
//}}2014/12/15追加:pceFileCreate(),pceFileDelete()を呼び出す前に、BitmapGC_Run()を行うようにしました。
LEAVE_CS;
}
/****************************************************************************
 *	
 ****************************************************************************/
void BitmapGC_Free() {
	//初期化されていなければ何もしません。
	if(!_gc.heapsize) { return; }
ENTER_CS;
	//APIのフックを解除します。
	pceVectorSetKs(KSNO_HeapAlloc, _gc.old_pceHeapAlloc);
	pceVectorSetKs(KSNO_HeapFree,  _gc.old_pceHeapFree);
//{{2014/12/15追加:pceFileCreate(),pceFileDelete()を呼び出す前に、BitmapGC_Run()を行うようにしました。
	pceVectorSetKs(KSNO_FileCreate, _gc.old_pceFileCreate);
	pceVectorSetKs(KSNO_FileDelete, _gc.old_pceFileDelete);
//}}2014/12/15追加:pceFileCreate(),pceFileDelete()を呼び出す前に、BitmapGC_Run()を行うようにしました。
LEAVE_CS;
	//Bitmapを開放します。
	free(_gc.bitmap);
	//構造体をクリアします。
	memset(&_gc, 0, sizeof _gc);
}
/****************************************************************************
 *	
 ****************************************************************************/
void BitmapGC_Run();
asm("
		.code
		.align		1
		.global		BitmapGC_Run
BitmapGC_Run:
		;//呼び出し側関数が、有効なメモリブロックへのポインタを%r0-3に保持している可能性があります。
		;//ガーベージコレクターはレジスタからのメモリブロック参照を検査しないので、
		;//そのままでは、レジスタから参照されているメモリブロックを誤って開放してしまう可能性があります。
		;//この問題を回避するため、%r0-3をスタックに積んでから、ガーベージコレクター本体を呼び出すことにしました。
		pushn		%r3
		call		_gc_run
		popn		%r3
		ret
");
/*--------------------------------------------------------------------------*/
static void _gc_run() {
	HEAPMEM* phm;
	int size;
	//未初期化時に呼んではいけません。
	if(!_gc.heapsize) { DIE(); }
//{{2014/12/19変更:_gc_run(),new_pceHeapAlloc(),new_pceFileCreate(),new_pceFileDelete()の排他制御を、ENTER_CS/LEAVE_CS⇒ENTER_IL(1)/LEAVE_ILに変更しました。
//ENTER_CS;
//↓2014/12/19変更:_gc_run(),new_pceHeapAlloc(),new_pceFileCreate(),new_pceFileDelete()の排他制御を、ENTER_CS/LEAVE_CS⇒ENTER_IL(1)/LEAVE_ILに変更しました。
ENTER_IL(1);
//}}2014/12/19変更:_gc_run(),new_pceHeapAlloc(),new_pceFileCreate(),new_pceFileDelete()の排他制御を、ENTER_CS/LEAVE_CS⇒ENTER_IL(1)/LEAVE_ILに変更しました。
	//Bitmapをクリアします。
	size = (_gc.heapsize + (1 << _gc.unit) - 1) >> _gc.unit;	//bits
	size = ((size + 31) >> 5) << 2;					//bytes
	memset(_gc.bitmap, 0, size);
	//割り当て済みユーザーブロックを、仮に未参照ブロックとしておきます。
	for(phm = _gc.heaptop; phm; phm = phm->chain) {
		if(phm->mark != HEAPMEMMK) DIE();
		switch(phm->owner) {
		//空きブロック
		case 0:
			/** no job **/
			break;
		//割り当て済みシステムブロック
		case HMO_SYSTEM:
			if(phm->chain) { DIE(); }	//PieceSystem 1.20においては、HMO_SYSTEMは、ヒープ末端の空ブロックとしてのみ利用されています。
			break;
		//割り当て済みユーザーブロック
		case HMO_DEFAULT_USER:
			phm->owner = HMO_NOREF_USER;	//仮に未参照ブロックとしておき、参照を検出したら走査を行った後で、HMO_DEFAULT_USERに戻します。
			break;
		//予期しないメモリブロック種別
		default:
			DIE();
		}
	}
	//スタック領域orデータ領域から参照されているアドレスを走査します。
	gc_scan(stack_info.sysstack_top, stack_info.sysstack_end);	//システムスタック … SRAM
	gc_scan(stack_info.usrstack_top, stack_info.usrstack_end);	//ユーザースタック … SRAM or FRAM
	gc_scan(__START_DEFAULT_DATA, __END_DEFAULT_DATA);
	gc_scan(__START_DEFAULT_BSS, __END_DEFAULT_BSS);
	//新たな要走査ブロックが検出される間、マークと走査を繰り返します。
	while(gc_mark(_gc.heaptop)) { /** no job **/ }
	//未参照ブロックのまま残ったブロックを、すべて開放します。
	gc_sweep(_gc.heaptop);
	//BitmapGC_Run()の直後は、ヒープ先頭の空きメモリブロックから走査する。
	_gc.phm = _gc.heaptop;
//{{2014/12/19変更:_gc_run(),new_pceHeapAlloc(),new_pceFileCreate(),new_pceFileDelete()の排他制御を、ENTER_CS/LEAVE_CS⇒ENTER_IL(1)/LEAVE_ILに変更しました。
//LEAVE_CS;
//↓2014/12/19変更:_gc_run(),new_pceHeapAlloc(),new_pceFileCreate(),new_pceFileDelete()の排他制御を、ENTER_CS/LEAVE_CS⇒ENTER_IL(1)/LEAVE_ILに変更しました。
LEAVE_IL;
//}}2014/12/19変更:_gc_run(),new_pceHeapAlloc(),new_pceFileCreate(),new_pceFileDelete()の排他制御を、ENTER_CS/LEAVE_CS⇒ENTER_IL(1)/LEAVE_ILに変更しました。
}
/*--------------------------------------------------------------------------*/
#ifndef PIECE
static int gc_scan(void* top, void* end) {
	int result = 0;
	uint32_t* pos;
	for(pos = top; (uint32_t)pos < (uint32_t)end; pos++) {
		uint32_t addr = *pos - (uint32_t)_gc.heaptop;
		if((uint32_t)addr < (uint32_t)_gc.heapsize) {
			addr >>= _gc.unit;
			bitarray_lsb1st_set(_gc.bitmap, addr, 1, 1);
			result = 1;
		}
	}
	return result;
}
#else //PIECE
#if 0 //'素直な実装'と、'スタックフレームを1命令で開放する工夫'の、どちらを使っても結果は同じです。性能もほぼ同じです。コードサイズ節約テクニックの動作検証のために、'スタックフレームを1命令で開放する工夫'の方を使用することにしました。
//□素直な実装。
asm("
		.code
		.align		1
gc_scan:
		pushn		%r3				;//
		xsub		%sp, %sp, 4			;//[%sp+0].b := result
		xld.b		[%sp+0], %r8			;//[%sp+0].b := result = 0
		ld.w		%r0, %r12			;//%r0  := pos = top
		ld.w		%r1, %r13			;//%r1  := end
		xld.w		%r9, _gc			;//%r9  :=           &_gc.heapsize
		ld.w		%r2, [%r9]+			;//%r2  := heapsize = _gc.heapsize
		ld.w		%r3, [%r9]			;//%r3  := heaptop  = _gc.heaptop
		;//%r0       := pos
		;//%r1       := end
		;//%r2       := heapsize
		;//%r3       := heaptop
		;//[%sp+0].b := result
		cmp		%r0, %r1			;//									─┐
gc_scan_L10:							;//									　│
		jruge		gc_scan_RET			;//while(pos < end) {							←┤
		ld.w		%r13, [%r0]+			;//  %r13 := addr = *pos++						　│
		sub		%r13, %r3			;//  %r13 := addr -= heaptop						　│
		cmp		%r13, %r2			;//  if((uint32_t)addr >= (uint32_t)heapsize) { continue }		　│
		jruge.d		gc_scan_L10			;//  									　│
		cmp		%r0, %r1			;//  									─┤	*delay*			分岐しなかった場合は無駄処理となるが、分岐するケースが大半なので、この遅延スロットを利用する方が高速だと思う。
		xld.w		%r9, _gc+8			;//  %r9  :=         &_gc.bitmap					　│
		ld.w		%r12, [%r9]+			;//  %r12 := bitmap = _gc.bitmap					　│
		ld.b		%r9, [%r9]			;//  %r9  := unit   = _gc.unit			2≦unit≦8		　│
		ld.w		%r14, 1				;//  %r14 :=                           1				　│	*anti-interlock*
		srl		%r13, %r9			;//  %r13 := addr >>= unit			1命令でシフト可能	　│
		xld.b		[%sp+0], %r14			;//  [%sp+0].b := result = 1						　│
		xcall.d		bitarray_lsb1st_set		;//  bitarray_lsb1st_set(bitmap, addr, 1, 1)				　│
		ld.w		%r15, 1				;//  %r15 :=                              1				　│	*delay*
		jp.d		gc_scan_L10			;//  									　│
		cmp		%r0, %r1			;//  									─┘	*delay*
gc_scan_RET:							;//}
		xld.b		%r10, [%sp+0]			;//return result
		xadd		%sp, %sp, 4			;//
		popn		%r3				;//
		ret						;//
");
#else //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//□スタックフレームを1命令で開放する工夫をしました。レジスタ退避とスタックフレーム確保の順番を入れ替えることで、戻り時の'xadd %sp,%sp,n'を省略して1命令節約できます。当関数においてはさほど効果は無いのですが、コードサイズ節約テクニックの記録として当関数に適用することにしました。
asm("
		.code
		.align		1
gc_scan:
		xsub		%sp, %sp, 4			;//[%sp+16].b := result											┓
		pushn		%r3				;//													┣【スタックフレームを1命令で開放する工夫】
		xld.b		[%sp+16], %r8			;//[%sp+16].b := result = 0										┛
		ld.w		%r0, %r12			;//%r0  := pos = top
		ld.w		%r1, %r13			;//%r1  := end
		xld.w		%r9, _gc			;//%r9  :=           &_gc.heapsize
		ld.w		%r2, [%r9]+			;//%r2  := heapsize = _gc.heapsize
		ld.w		%r3, [%r9]			;//%r3  := heaptop  = _gc.heaptop
		;//%r0        := pos
		;//%r1        := end
		;//%r2       := heapsize
		;//%r3       := heaptop
		;//[%sp+ 0].w := %r0																	┓
		;//[%sp+ 4].w := %r1																	┃
		;//[%sp+ 8].w := %r2																	┣【スタックフレームを1命令で開放する工夫】
		;//[%sp+12].w := %r3																	┃
		;//[%sp+16].b := result																	┛
		;//[%sp+20].w := retp
		cmp		%r0, %r1			;//									─┐
gc_scan_L10:							;//									　│
		jruge		gc_scan_RET			;//while(pos < end) {							←┤
		ld.w		%r13, [%r0]+			;//  %r13 := addr = *pos++						　│
		sub		%r13, %r3			;//  %r13 := addr -= heaptop						　│
		cmp		%r13, %r2			;//  if((uint32_t)addr >= (uint32_t)heapsize) { continue }		　│
		jruge.d		gc_scan_L10			;//  									　│
		cmp		%r0, %r1			;//  									─┤	*delay*			分岐しなかった場合は無駄処理となるが、分岐するケースが大半だと思うので、この遅延スロットを利用する方が高速だと思う。
		xld.w		%r9, _gc+8			;//  %r9  :=         &_gc.bitmap					　│
		ld.w		%r12, [%r9]+			;//  %r12 := bitmap = _gc.bitmap					　│
		ld.b		%r9, [%r9]			;//  %r9  := unit   = _gc.unit			2≦unit≦8		　│
		ld.w		%r14, 1				;//  %r14 :=                           1				　│	*anti-interlock*
		srl		%r13, %r9			;//  %r13 := addr >>= unit			1命令でシフト可能	　│
		xld.b		[%sp+16], %r14			;//  [%sp+16].b := result = 1						　│				━【スタックフレームを1命令で開放する工夫】
		xcall.d		bitarray_lsb1st_set		;//  bitarray_lsb1st_set(bitmap, addr, 1, 1)				　│
		ld.w		%r15, 1				;//  %r15 :=                              1				　│	*delay*
		jp.d		gc_scan_L10			;//  									　│
		cmp		%r0, %r1			;//  									─┘	*delay*
gc_scan_RET:							;//}
		xld.b		%r10, [%sp+16]			;//return result
		popn		%r4				;//													━【スタックフレームを1命令で開放する工夫】
		ret						;//
");
#endif
#endif//PIECE
/*--------------------------------------------------------------------------*/
//'バグ有りの実装'は、Bitmap単位をまたぐメモリブロックの、後半のBitmap単位内のアドレスを参照するポインタが有っても、メモリブロックを開放してしまうバグが有った。
//'バグを修正した実装'にて、その問題を修正した。
//□検証ﾌﾟﾛｸﾞﾗﾑ
//│void app_main() {
//│  extern uint8_t __START_DEFAULT_CODE[];
//│  const pceAPPHEAD* apphead=(const pceAPPHEAD*)__START_DEFAULT_CODE;
//│  HEAPMEM* heaptop=(HEAPMEM*)(apphead->bss_end+apphead->stack_size);
//│  void* volatile p;
//│  BitmapGC_Init(4);//Bitmap単位=16ﾊﾞｲﾄ
//│  //Bitmap単位にまたがる､12ﾊﾞｲﾄのﾒﾓﾘﾌﾞﾛｯｸを確保できるまで繰り返す｡
//│  do{
//│    surface_clear(&surface,0);
//│    render_printf(&render,0,0,1,3,"%p",(p=malloc(12)));
//│    update();
//│  }while((((int)p-(int)heaptop)&15)!=(16-4));//(前半のBitmap単位に4ﾊﾞｲﾄ+後半のBitmap単位に8ﾊﾞｲﾄ)の形になるまで繰り返す｡
//│  p+=8;//参照が､12ﾊﾞｲﾄのﾒﾓﾘﾌﾞﾛｯｸの8ﾊﾞｲﾄ目(=後半のBitmap単位に含まれる8ﾊﾞｲﾄ分の中央)を指すようにずらす｡このずらし方をいろいろ変えて検証せよ｡ﾊﾞｸﾞが有ると4~11の時に不正に開放される｡正常ならば少なくとも~19までは後半ﾌﾞﾛｯｸ内を指すので開放されない｡
//│  render_printf(&render,0,88,1,3,"%p",p);	//ｽﾀｯｸﾒﾓﾘに古いpのｺﾞﾐが残っている可能性を減らすためにﾀﾞﾐｰの呼び出しを行う｡
//│  BitmapGC_Run();//参照されていないﾒﾓﾘを開放する｡
//│  do{schedule();}while(!(joy&TRG_A));//このﾀｲﾐﾝｸﾞでPC上でdmalloc.exeを実行して､ﾒﾓﾘ割り当て状態を確認せよ｡上記のﾒﾓﾘﾌﾞﾛｯｸが開放されていなければ正常である｡
//│  BitmapGC_Free();
//│}
#if 0 //'バグ有りの実装'は、記録のために残してあるだけです。必ず後者の'バグを修正した実装'を使用してください。
//×バグ有りの実装。
#ifndef PIECE
static int gc_mark(HEAPMEM* phm) {
	int result = 0;
	do {
		if(phm->mark != HEAPMEMMK) { DIE(); }				//ヒープが壊れている。									←←←Asm版ではこの検査は省略しました。
		if(phm->owner == HMO_NOREF_USER) {				//未参照ブロックならば…
			void* top = phm + 1/*必要!!*/;
			void* end = phm->chain;
			uint32_t addr = (uint32_t)top - (uint32_t)_gc.heaptop;
			uint32_t size = (uint32_t)end - (uint32_t)top;
			addr >>=       _gc.unit;
			size  += (1 << _gc.unit) - 1;
			size >>=       _gc.unit;
			if(bitarray_lsb1st_test(_gc.bitmap, addr, size)) {	//参照されているか?
				result |= gc_scan(top, end);			//走査する。
				phm->owner = HMO_DEFAULT_USER;			//走査済みに戻す。
			}
		}
	} while((phm = phm->chain));						//HMO_SYSTEMに到達したら!phmになって抜ける。
	return result;
}
#else //PIECE
asm("
		.code
		.align		1
gc_mark:
		pushn		%r3
		ld.w		%r0, 0				;//%r0  := result = 0
		xadd		%r1, %r12, 2			;//%r1  := &phm->owner
gc_mark_L10:							;//do {
		;//%r0  := result
		;//%r1  := &phm->owner
		ld.uh		%r9, [%r1]+			;//  %r9  := owner = phm->owner, %r1  := &phm->chain
		xcmp		%r9, 0x1111			;//  if(owner == HMO_NOREF_USER) {
		jrne		gc_mark_L20
		ld.w		%r2, %r1			;//    %r2  :=      &phm->chain
		ld.w		%r3, [%r2]+			;//    %r3  := end = phm->chain, %r2  := top = phm + 1
		;//%r0  := result
		;//%r1  := &phm->chain
		;//%r2  := top
		;//%r3  := end
		xld.w		%r9, _gc+4			;//    %r9  :=          &_gc.heaptop
		ld.w		%r4, [%r9]+			;//    %r4  := heaptop = _gc.heaptop
		ld.w		%r12, [%r9]+			;//    %r12 := bitmap  = _gc.bitmap
		ld.b		%r9, [%r9]			;//    %r9  := unit    = _gc.unit	2≦unit≦8
		ld.w		%r13, %r2			;//    %r13 :=        top
		sub		%r13, %r4			;//    %r13 := addr = top - heaptop
		ld.w		%r14, %r3			;//    %r14 :=        end
		sub		%r14, %r2			;//    %r14 := size = end - top
		srl		%r13, %r9			;//    %r13 := addr >>= unit		1命令でシフト可能
		ld.w		%r4, 1				;//    %r4  :=          1
		sll		%r4, %r9			;//    %r4  :=          1 << unit	1命令でシフト可能
		sub		%r4, 1				;//    %r4  :=         (1 << unit) - 1
		add		%r14, %r4			;//    %r14 := size += (1 << unit) - 1
		xcall.d		bitarray_lsb1st_test		;//    if(bitarray_lsb1st_test(bitmap, addr, size)) {
		srl		%r14, %r9			;//    %r13 := size >>= unit		1命令でシフト可能	*delay*
		cmp		%r10, 0				;//      
		jreq		gc_mark_L20			;//      
		ld.w		%r12, %r2			;//      %r12 :=               top
		xcall.d		gc_scan				;//      %r10 := tmp = gc_scan(top, end)
		ld.w		%r13, %r3			;//      %r13 :=                    end				*delay*
		or		%r0, %r10			;//      %r0  := result |= tmp
		sub		%r1, 2				;//      %r1  := &phm->owner
		ld.w		%r9, 1				;//      %r9  :=      HMO_DEFAULT_USER
		ld.h		[%r1]+, %r9			;//      phm->owner = HMO_DEFAULT_USER, %r1  := &phm->chain
gc_mark_L20:							;//  } }
		;//%r0  := result
		;//%r1  := &phm->chain
		ld.w		%r1, [%r1]			;//  %r1  := phm = phm->chain
		cmp		%r1, 0				;//} while(  phm)						!INTERLOCK!
		jrne.d		gc_mark_L10			;//  
		add		%r1, 2				;//  %r0  := &phm->owner					*delay*
		ld.w		%r10, %r0			;//%r10 := result
		popn		%r3
		ret
");
#endif//PIECE
#else //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//○バグを修正した実装。
#ifndef PIECE
//【必要!!】の行についてのコメント	//前述の「Bitmap単位をまたぐメモリブロックの、後半のBitmap単位内のアドレスを参照するポインタが有っても、メモリブロックを開放してしまうバグ」とは別件です。
//pceHeapAlloc(0)も可能であるので、0byteのメモリブロック(top==end)も生じ得る。
//0byteのメモリブロックは、topを含むBitmap単位を参照するポインタが有る場合、開放しないことにする。
//もし無条件で開放してしまうと、その後にアプリケーションが0byteのメモリブロックを開放しようとした時に、二重開放になってしまうからである。
//│p=pceHeapAlloc(0);
//│BitmapGC_Run();//もしpを開放してしまうと…
//│pceHeapFree(p);//二重開放になる｡
//実際には、pceHeapFree(p)は二重開放を検出して何もしないので安全なのだが、何らかのバグの原因になる恐れがあるので、そうならないようにしておく方が確実である。
//□検証ﾌﾟﾛｸﾞﾗﾑ
//│void app_main() {
//│  void* volatile p;
//│  BitmapGC_Init(2);//Bitmap単位=4ﾊﾞｲﾄ
//│  render_printf(&render,0,0,1,3,"%p",(p=malloc(0)));//0ﾊﾞｲﾄのﾒﾓﾘﾌﾞﾛｯｸを確保する｡
//│  update();
//│  BitmapGC_Run();//参照されていないﾒﾓﾘを開放する｡
//│  do{schedule();}while(!(joy&TRG_A));//このﾀｲﾐﾝｸﾞでPC上でdmalloc.exeを実行して､ﾒﾓﾘ割り当て状態を確認せよ｡上記のﾒﾓﾘﾌﾞﾛｯｸが開放されていなければ正常である｡
//│  BitmapGC_Free();
//│}
//★★★★★重要!!★★★★★
//既存の'clipgc.c'を見直したところ上記の問題を内包していた。これまで顕在化しなかったということは、0byteのメモリブロックの二重開放に起因する問題は、実際のアプリケーションではほとんど発生しないのだろう。
//今回、上記の対策を行ったのは当モジュール'clipbmgc.c'だけで、既存の'clipgc.c'は変更していない(今更変更するのが怖いからです)。今後、様子を見て、'clipgc.c'も修正することを検討せよ。
static int gc_mark(HEAPMEM* phm) {
	int result = 0;
	do {
		if(phm->mark != HEAPMEMMK) { DIE(); }					//ヒープが壊れている。								←←←Asm版ではこの検査は省略しました。
		if(phm->owner == HMO_NOREF_USER) {					//未参照ブロックならば…
			void* top = phm + 1/*必要!!*/;					//メモリブロックの先頭アドレス(+0)
			void* end = phm->chain;						//メモリブロックの最終アドレス(+1)
			int pos1 = ((int)top - (int)_gc.heaptop    ) >> _gc.unit;	//メモリブロックの先頭アドレス(+0)を含むBitmap単位	┬0byteのメモリブロック(top==end)も有り得るため、(pos1==(pos2-1))になる可能性も有る。
			int pos2 = ((int)end - (int)_gc.heaptop - 1) >> _gc.unit;	//メモリブロックの最終アドレス(+0)を含むBitmap単位	┘
			int size = pos2 - pos1 + 1;					//メモリブロックを含むBitmap単位の数			─0byteのメモリブロック(top==end)も有り得るため、size=0になる可能性も有る。
			if(!size) { size = 1; }						//【必要!!】この行を削除すると、'参照されている0byteのメモリブロックを開放してしまう'バグが生じる。詳細は上記のコメントを参照せよ。
			if(bitarray_lsb1st_test(_gc.bitmap, pos1, size)) {		//参照されているか?
				result |= gc_scan(top, end);				//走査する。						─0byteのメモリブロック(top==end)の場合、走査はすぐに終了するが、gc_scan()を呼び出すことは安全である。
				phm->owner = HMO_DEFAULT_USER;				//走査済みに戻す。
			}
		}
	} while((phm = phm->chain));							//HMO_SYSTEMに到達したら!phmになって抜ける。
	return result;
}
#else //PIECE
asm("
		.code
		.align		1
gc_mark:
		pushn		%r3
		ld.w		%r0, 0				;//%r0  := result = 0
		xadd		%r1, %r12, 2			;//%r1  := &phm->owner
gc_mark_L10:							;//do {
		;//%r0  := result
		;//%r1  := &phm->owner
		ld.uh		%r9, [%r1]+			;//  %r9  := owner = phm->owner, %r1  := &phm->chain
		xcmp		%r9, 0x1111			;//  if(owner == HMO_NOREF_USER) {
		jrne		gc_mark_L20
		ld.w		%r2, %r1			;//    %r2  :=      &phm->chain
		ld.w		%r3, [%r2]+			;//    %r3  := end = phm->chain, %r2  := top = phm + 1
		;//%r0  := result
		;//%r1  := &phm->chain
		;//%r2  := top
		;//%r3  := end
		xld.w		%r9, _gc+4			;//    %r9  :=          &_gc.heaptop
		ld.w		%r4, [%r9]+			;//    %r4  := heaptop = _gc.heaptop
		ld.w		%r12, [%r9]+			;//    %r12 := bitmap  = _gc.bitmap
		ld.b		%r9, [%r9]			;//    %r9  := unit    = _gc.unit			2≦unit≦8
		ld.w		%r13, %r2			;//    %r13 :=         top
		sub		%r13, %r4			;//    %r13 :=         top - heaptop
		sra		%r13, %r9			;//    %r13 := pos1 = (top - heaptop    ) >> unit	1命令でシフト可能	pos1≧ 0
		ld.w		%r14, %r3			;//    %r14 :=         end
		sub		%r14, %r4			;//    %r14 :=         end - heaptop
		sub		%r14, 1				;//    %r14 :=         end - heaptop - 1
		sra		%r14, %r9			;//    %r14 := pos2 = (end - heaptop - 1) >> unit	1命令でシフト可能	pos2≧-1
		sub		%r14, %r13			;//    %r14 :=        pos2 - pos1      ,%psr(C) := (pos2 < pos1)		┐'if(!size){size=1;}'相当の処理を、adcで1命令で実行して条件分岐を節約した。
		ld.w		%r9, 1				;//    %r9  :=                      1						├厳密にはこれでは駄目。pos1=0,pos2=-1の時に、size=0になってしまうからです。
		xcall.d		bitarray_lsb1st_test		;//    if(bitarray_lsb1st_test(bitmap, addr, size)) {				│しかし実際には、先頭のBitmap単位に偶然0byteのメモリブロックが作成されることはまず無いし、それを開放して問題が現在化する可能性はさらに薄い。
		adc		%r14, %r9			;//    %r14 := size = pos2 - pos1 + 1 + %psr(C)			*delay*		┘実用上問題無いと判断して、このままとした。厳密に処理すると、1命令増えてしまうからである。
		cmp		%r10, 0				;//      
		jreq		gc_mark_L20			;//      
		ld.w		%r12, %r2			;//      %r12 :=               top
		xcall.d		gc_scan				;//      %r10 := tmp = gc_scan(top, end)
		ld.w		%r13, %r3			;//      %r13 :=                    end				*delay*
		or		%r0, %r10			;//      %r0  := result |= tmp
		sub		%r1, 2				;//      %r1  := &phm->owner
		ld.w		%r9, 1				;//      %r9  :=      HMO_DEFAULT_USER
		ld.h		[%r1]+, %r9			;//      phm->owner = HMO_DEFAULT_USER, %r1  := &phm->chain
gc_mark_L20:							;//  } }
		;//%r0  := result
		;//%r1  := &phm->chain
		ld.w		%r1, [%r1]			;//  %r1  := phm = phm->chain
		cmp		%r1, 0				;//} while(  phm)						!INTERLOCK!
		jrne.d		gc_mark_L10			;//  
		add		%r1, 2				;//  %r0  := &phm->owner					*delay*
		ld.w		%r10, %r0			;//%r10 := result
		popn		%r3
		ret
");
#endif//PIECE
#endif
/*--------------------------------------------------------------------------*/
//{{この範囲はclipgc.cからコピーしました。clipgc.cのgc_sweep()と全く同じです。
#ifndef PIECE
static void gc_sweep(HEAPMEM* phm) {
	do {
		if(phm->mark != HEAPMEMMK) { DIE(); }					//ヒープが壊れている。								←←←Asm版ではこの検査は省略しました。
		if(!phm->owner || (phm->owner == HMO_NOREF_USER)) {			//空きブロックならば…
			HEAPMEM* phm1 = phm;						//次以降のブロックで、最初に空きブロックでないブロックを検索する。
			do {
				phm1 = phm1->chain;
				if(phm1->mark != HEAPMEMMK) { DIE(); }			//ヒープが壊れている。								←←←Asm版ではこの検査は省略しました。
			} while(!phm1->owner || (phm1->owner == HMO_NOREF_USER));	//空きブロックでなければ抜ける。	HMO_SYSTEMに到達した場合もここで抜ける。
			//分割したメモリブロックを設定する。
		//不要	phm->mark  = HEAPMEMMK;
			phm->owner = 0;							//HMO_NOREF_USER⇒0,又は,0⇒0
			phm->chain = phm1;						//次以降のブロックで、最初に空きブロックでないブロックのアドレス。
		}
	} while((phm = phm->chain));							//HMO_SYSTEMに到達したら!phmになって抜ける。
}
#else //PIECE
asm("
		.code
		.align		1
gc_sweep:
		xld.w		%r4, 0x1111A431		;//%r4  := (HEAPMEMMK,HMO_NOREF_USER)
		ld.uh		%r5, %r4		;//%r5  := (HEAPMEMMK,0)
gc_sweep_L10:						;//do {
		ld.w		%r9, [%r12]+		;//  %r9  := (mark,owner) = (phm->mark,phm->owner), %r12 := &phm->chain
		cmp		%r9, %r4		;//  if(((mark,owner) == (HEAPMEMMK,HMO_NOREF_USER)) ||
		jreq		3
		 cmp		%r9, %r5		;//     ((mark,owner) == (HEAPMEMMK,0))) {
		 jrne		gc_sweep_L30
		;//-------------------------------------;//
		ld.w		%r6, %r12		;//    %r6  := &phm1->chain = &phm->chain
gc_sweep_L20:						;//    do {
		ld.w		%r6, [%r6]		;//      %r6  := phm1 = phm1->chain
		ld.w		%r9, [%r6]+		;//      %r9  := (mark,owner) = (phm->mark,phm->owner), %r6  := &phm1->chain	!INTERLOCK!
		cmp		%r9, %r4		;//    } while(((mark,owner) == (HEAPMEMMK,HMO_NOREF_USER)) ||
		jreq		gc_sweep_L20
		cmp		%r9, %r5		;//            ((mark,owner) == (HEAPMEMMK,0)))
		jreq		gc_sweep_L20
		sub		%r12, 4			;//    %r12 := phm
		ld.w		[%r12]+, %r5		;//    (phm->mark,phm->owner) = (HEAPMEMMK,0), %r12 := &phm->chain
		sub		%r6, 4			;//    %r6  := phm1
		ld.w		[%r12], %r6		;//    phm->chain = phm1
		;//-------------------------------------;//
gc_sweep_L30:						;//  }
		ld.w		%r12, [%r12]		;//  %r12 := phm->chain
		cmp		%r12, 0			;//										!INTERLOCK!
		jrne		gc_sweep_L10		;//} while(  phm)
		ret
");
#endif//PIECE
//}}この範囲はclipgc.cからコピーしました。clipgc.cのgc_sweep()と全く同じです。
/*--------------------------------------------------------------------------*/
//{{この範囲はclippce.cからコピーして変更しました。変更点はclippce.cの同名の関数と比較してください。
#ifndef PIECE
static void* first_fit_pceHeapAlloc(unsigned long size0) {
	HEAPMEM* phm = _gc.phm;	//最後に割り当てたメモリブロックから走査を継続する。
	HEAPMEM *phm0, *phm1;
	//確保するサイズに、メモリブロック管理構造体のサイズを加え、ワードサイズの倍数に切り上げる。
	size0 = ((size0 + sizeof(HEAPMEM) + 3) & ~3);
	//メモリブロックを走査して…
	do {
		if(phm->mark != HEAPMEMMK) { DIE(); }	//ヒープが壊れている。
		phm0 = phm->chain;
		//空きメモリブロックを見つけたら…
		if(!phm->owner) {
			//このメモリブロックのサイズ(メモリブロック管理構造体のサイズ込み)を求める。
			unsigned long size1 = (unsigned long)phm0 - (unsigned long)phm;
			//確保するサイズ(メモリブロック管理構造体のサイズ込み)以上ならば…
			if(size1 >= size0) {
				//メモリブロックのサイズが、確保するサイズ(メモリブロック管理構造体のサイズ込み)と、分割した場合に増える管理構造体の合計よりも大きければ…
				if(size1 > (size0 + sizeof(HEAPMEM))) {
					//次以降のメモリブロックが空きブロックならば、分割したメモリブロックに結合する。
					phm1 = phm;
					do {
						phm1 = phm1->chain;
						if(phm1->mark != HEAPMEMMK) { DIE(); }	//ヒープが壊れている。
					} while(!phm1->owner);		//空きブロックでなければ抜ける。	HMO_SYSTEMに到達した場合もここで抜ける。
					//分割したメモリブロックを設定する。
					phm0 = (void*)phm + size0;	//(void*)への加算はgcc拡張
			/*要*/		phm0->mark  = HEAPMEMMK;
					phm0->owner = 0;
					phm0->chain = phm1;		//次以降のブロックで、最初に空きブロックでないブロックのアドレス。
				}
				//確保したメモリブロックを設定する。
			//不要	phm->mark  = HEAPMEMMK;
				phm->owner = HMO_DEFAULT_USER;
				phm->chain = phm0;			//上のifブロックを通らなかった場合は、phm0==phm->chainのままです。
				//最後に割り当てたメモリブロックの管理構造体のアドレスを格納する。
				_gc.phm = phm;
				//確保したメモリブロックの、管理構造体の後のアドレスを返す。
				phm++;
				break;	//ここまで
			}
		}
	} while((phm = phm0));	//HMO_SYSTEMに到達したら!phmになって抜け、割り当て失敗としてNULLを返す。
	return   phm;
}
#else //PIECE
/*static*/ void* first_fit_pceHeapAlloc(unsigned long size0);
asm("
		.code
		.align		1
first_fit_pceHeapAlloc:
		xld.w		%r10, [_gc+16]			;//%r10 := phm = _gc.phm
		add		%r12, 3				;//%r12 := size0' +=  3			(size0' := size0 - 8)
		and		%r12, -4			;//%r12 := size0' &= ~3
		xld.w		%r4, 0x1A431			;//%r4  := (HEAPMEMMK,HMO_DEFAULT_USER)
		ld.uh		%r5, %r4			;//%r5  := (HEAPMEMMK,0)
first_fit_pceHeapAlloc_L10:					;//do {
		;//%r4  := (HEAPMEMMK,HMO_DEFAULT_USER)
		;//%r5  := (HEAPMEMMK,0)
		;//%r10 := phm
		;//%r12 := size0'
		ld.w		%r9, [%r10]+			;//  %r9  := (mark,owner) = (phm->mark,phm->owner)
		ld.w		%r6, [%r10]+			;//  %r6  := phm0 = phm->chain, %r10 := phm + 8
		cmp		%r9, %r5			;//  if((mark,owner) == (HEAPMEMMK,0)) {
		jrne		first_fit_pceHeapAlloc_L30
		;//---------------------------------------------;//
		;//%r4  := (HEAPMEMMK,HMO_DEFAULT_USER)
		;//%r5  := (HEAPMEMMK,0)
		;//%r6  := phm0
		;//%r10 := phm + 8
		;//%r12 := size0'
		ld.w		%r7, %r6			;//    %r7  :=          phm0
		sub		%r7, %r10			;//    %r7  := size1' = phm0 - phm - 8	(size1' := size1 - 8)
		sub		%r7, %r12			;//    %r7  := size1' - size0'
		jrult		first_fit_pceHeapAlloc_L40	;//    if((size1' - size0') < 0) { goto L40 }
		add		%r12, %r10			;//    %r12 := phm + size0
		cmp		%r7, 8				;//    if((size1' - size0') > 8) {
		jrule.d	first_fit_pceHeapAlloc_L20
		sub		%r10, 8				;//      %r10 := phm						*delay*
		;//%r4  := (HEAPMEMMK,HMO_DEFAULT_USER)
		;//%r5  := (HEAPMEMMK,0)
		;//%r6  := phm0
		;//%r10 := phm
		;//%r12 := phm + size0
		ld.w		%r7, %r10			;//      %r7  := phm1 = phm
		ext		4				;//      do {
		 ld.w		%r7, [%r7]			;//        %r7  := phm1 = phm1->chain
		 ld.w		%r9, [%r7]			;//        %r9  := (mark,owner) = (phm1->mark,phm1->owner)	!INTERLOCK!
		 cmp		%r9, %r5			;//      } while((mark,owner) == (HEAPMEMMK,0))			!INTERLOCK!
		jreq		-4
		ld.uh		%r9, %r9			;//      %r9  := mark
		cmp		%r9, %r5			;//      if(mark != HEAPMEMMK) { DIE() }
		jrne		first_fit_pceHeapAlloc_DIE
		ld.w		%r6, %r12			;//      %r6  := phm0 = phm + size0
		ld.w		[%r12]+, %r5			;//      (phm0->mark,phm0->owner) = (HEAPMEMMK,0)
		ld.w		[%r12], %r7			;//      phm0->chain = phm1
first_fit_pceHeapAlloc_L20:					;//    }
		xld.w		[_gc+16], %r10			;//    _gc.phm = phm
		ld.w		[%r10]+, %r4			;//    (phm->mark,phm->owner) = (HEAPMEMMK,HMO_DEFAULT_USER)
		ld.w		[%r10]+, %r6			;//    phm->chain = phm0, %r10 := phm + 8
		ret						;//    return                     phm + 8
		;//---------------------------------------------;//  }
		;//%r4  := (HEAPMEMMK,HMO_DEFAULT_USER)
		;//%r5  := (HEAPMEMMK,0)
		;//%r6  := phm0
		;//%r9  := (mark,owner)
first_fit_pceHeapAlloc_L30:
		ld.uh		%r9, %r9			;//  %r9  :=  mark
		cmp		%r9, %r5			;//  if(mark != HEAPMEMMK) { DIE() }
		jrne		first_fit_pceHeapAlloc_DIE
first_fit_pceHeapAlloc_L40:
		cmp		%r6, 0
		jrne.d		first_fit_pceHeapAlloc_L10	;//} while(  phm)
		ld.w		%r10, %r6			;//  %r10 := phm = phm0						*delay*
		ret						;//return    phm
");
static void __attribute__((noreturn,unused))/*asmブロックから参照*/ first_fit_pceHeapAlloc_DIE() { DIE(); }
#endif//PIECE
//}}この範囲はclippce.cからコピーして変更しました。変更点はclippce.cの同名の関数と比較してください。
/*--------------------------------------------------------------------------*/
static void* new_pceHeapAlloc(unsigned long size) {
	void* ptr;
//{{2014/12/19変更:_gc_run(),new_pceHeapAlloc(),new_pceFileCreate(),new_pceFileDelete()の排他制御を、ENTER_CS/LEAVE_CS⇒ENTER_IL(1)/LEAVE_ILに変更しました。
//ENTER_CS;	//必要
//↓2014/12/19変更:_gc_run(),new_pceHeapAlloc(),new_pceFileCreate(),new_pceFileDelete()の排他制御を、ENTER_CS/LEAVE_CS⇒ENTER_IL(1)/LEAVE_ILに変更しました。
ENTER_IL(1);
//}}2014/12/19変更:_gc_run(),new_pceHeapAlloc(),new_pceFileCreate(),new_pceFileDelete()の排他制御を、ENTER_CS/LEAVE_CS⇒ENTER_IL(1)/LEAVE_ILに変更しました。
	ptr = first_fit_pceHeapAlloc(size);	//1st try
	if(!ptr) {
		BitmapGC_Run();
		ptr = first_fit_pceHeapAlloc(size);	//2nd try
		if(!ptr) { DIE(); }
	}
//{{2014/12/19変更:_gc_run(),new_pceHeapAlloc(),new_pceFileCreate(),new_pceFileDelete()の排他制御を、ENTER_CS/LEAVE_CS⇒ENTER_IL(1)/LEAVE_ILに変更しました。
//LEAVE_CS;	//必要
//↓2014/12/19変更:_gc_run(),new_pceHeapAlloc(),new_pceFileCreate(),new_pceFileDelete()の排他制御を、ENTER_CS/LEAVE_CS⇒ENTER_IL(1)/LEAVE_ILに変更しました。
LEAVE_IL;
//}}2014/12/19変更:_gc_run(),new_pceHeapAlloc(),new_pceFileCreate(),new_pceFileDelete()の排他制御を、ENTER_CS/LEAVE_CS⇒ENTER_IL(1)/LEAVE_ILに変更しました。
	return memset(ptr, -1, size);
}
/*--------------------------------------------------------------------------*/
static int new_pceHeapFree(void* memp) {
	//pceHeapFree()は何もしないようにし、メモリ開放はBitmapGC_Run()に集約しました。
	//_gc.phmが指しているメモリブロックが開放されないようにするためです。
	HEAPMEM* phm = (HEAPMEM*)memp - 1;
//ENTER_CS;	//不要
    //	if((phm->mark != HEAPMEMMK) || (phm->owner != HMO_DEFAULT_USER)) { DIE(); }
    //↓最適化
	if(*(uint32_t*)phm != (HEAPMEMMK|HMO_DEFAULT_USER<<16)) { DIE(); }
//LEAVE_CS;	//不要
	return 0;
}
/*--------------------------------------------------------------------------*/
//{{2014/12/15追加:pceFileCreate(),pceFileDelete()を呼び出す前に、BitmapGC_Run()を行うようにしました。
static int new_pceFileCreate(const char* fname, unsigned long size) {
	int retval;
//{{2014/12/19変更:_gc_run(),new_pceHeapAlloc(),new_pceFileCreate(),new_pceFileDelete()の排他制御を、ENTER_CS/LEAVE_CS⇒ENTER_IL(1)/LEAVE_ILに変更しました。
//ENTER_CS;	//必要
//↓2014/12/19変更:_gc_run(),new_pceHeapAlloc(),new_pceFileCreate(),new_pceFileDelete()の排他制御を、ENTER_CS/LEAVE_CS⇒ENTER_IL(1)/LEAVE_ILに変更しました。
ENTER_IL(1);
//}}2014/12/19変更:_gc_run(),new_pceHeapAlloc(),new_pceFileCreate(),new_pceFileDelete()の排他制御を、ENTER_CS/LEAVE_CS⇒ENTER_IL(1)/LEAVE_ILに変更しました。
	BitmapGC_Run();
	retval = (*_gc.old_pceFileCreate)(fname, size);
//{{2014/12/19変更:_gc_run(),new_pceHeapAlloc(),new_pceFileCreate(),new_pceFileDelete()の排他制御を、ENTER_CS/LEAVE_CS⇒ENTER_IL(1)/LEAVE_ILに変更しました。
//LEAVE_CS;	//必要
//↓2014/12/19変更:_gc_run(),new_pceHeapAlloc(),new_pceFileCreate(),new_pceFileDelete()の排他制御を、ENTER_CS/LEAVE_CS⇒ENTER_IL(1)/LEAVE_ILに変更しました。
LEAVE_IL;
//}}2014/12/19変更:_gc_run(),new_pceHeapAlloc(),new_pceFileCreate(),new_pceFileDelete()の排他制御を、ENTER_CS/LEAVE_CS⇒ENTER_IL(1)/LEAVE_ILに変更しました。
	return retval;
}
static int new_pceFileDelete(const char* fname) {
	int retval;
//{{2014/12/19変更:_gc_run(),new_pceHeapAlloc(),new_pceFileCreate(),new_pceFileDelete()の排他制御を、ENTER_CS/LEAVE_CS⇒ENTER_IL(1)/LEAVE_ILに変更しました。
//ENTER_CS;	//必要
//↓2014/12/19変更:_gc_run(),new_pceHeapAlloc(),new_pceFileCreate(),new_pceFileDelete()の排他制御を、ENTER_CS/LEAVE_CS⇒ENTER_IL(1)/LEAVE_ILに変更しました。
ENTER_IL(1);
//}}2014/12/19変更:_gc_run(),new_pceHeapAlloc(),new_pceFileCreate(),new_pceFileDelete()の排他制御を、ENTER_CS/LEAVE_CS⇒ENTER_IL(1)/LEAVE_ILに変更しました。
	BitmapGC_Run();
	retval = (*_gc.old_pceFileDelete)(fname);
//{{2014/12/19変更:_gc_run(),new_pceHeapAlloc(),new_pceFileCreate(),new_pceFileDelete()の排他制御を、ENTER_CS/LEAVE_CS⇒ENTER_IL(1)/LEAVE_ILに変更しました。
//LEAVE_CS;	//必要
//↓2014/12/19変更:_gc_run(),new_pceHeapAlloc(),new_pceFileCreate(),new_pceFileDelete()の排他制御を、ENTER_CS/LEAVE_CS⇒ENTER_IL(1)/LEAVE_ILに変更しました。
LEAVE_IL;
//}}2014/12/19変更:_gc_run(),new_pceHeapAlloc(),new_pceFileCreate(),new_pceFileDelete()の排他制御を、ENTER_CS/LEAVE_CS⇒ENTER_IL(1)/LEAVE_ILに変更しました。
	return retval;
}
//}}2014/12/15追加:pceFileCreate(),pceFileDelete()を呼び出す前に、BitmapGC_Run()を行うようにしました。
/*--------------------------------------------------------------------------*/
