U0 AsmResolve(CCompCtrl *cc, CAsmUnresolvedRef *tmpu, U8 *label, Bool undefined)
{
	CAOTImportExport	*tmpie;
	CAsmUndefHash		*tmpauh;
	I64					 res = Call(tmpu->machine_code);

	if (undefined)
	{
		tmpauh = tmpu->asm_undef_hash;
		while (tmpauh)
		{
			if (tmpauh->hash->type & HTF_UNRESOLVED)
			{
				tmpie = CAlloc(sizeof(CAOTImportExport));
				tmpie->next = tmpauh->hash->ie_list;
				tmpauh->hash->ie_list = tmpie;
				tmpie->rip = tmpu->rip;
				tmpie->aot = cc->aot;
				tmpie->type = tmpu->type;
			}
			tmpauh = tmpauh->next; //Technically, more than one won't work.
		}
	}
	else if (!(tmpu->type & IEF_IMM_NOT_REL))
	{
		res -= tmpu->rel_rip;
		if (tmpu->type == IET_REL_I8  && !(I8_MIN  <= res <= I8_MAX) ||
			tmpu->type == IET_REL_I16 && !(I16_MIN <= res <= I16_MAX))
		{
			PrintErr("Branch out of range at line:%04d %s\n",
						tmpu->line_num, label);
			LexExcept(cc);
		}
		if (tmpu->U8_avail && tmpu->type > IET_IMM_U8 && -124 <= res <= 123)
			PrintWarn("could use I8 displacement at line:%04d %s %s\n", tmpu->line_num, cc->aotc->last_label, label);
	}
	AOTStoreCodeU8At(cc, tmpu->rip, res.u8[0]);
	if (tmpu->type >= IET_REL_I16)
	{
		AOTStoreCodeU8At(cc, tmpu->rip + 1, res.u8[1]);
		if (tmpu->type >= IET_REL_I32)
		{
			AOTStoreCodeU8At(cc, tmpu->rip + 2, res.u8[2], res.u8[3]);
			if (tmpu->type >= IET_REL_I64)
				AOTStoreCodeU8At(cc, tmpu->rip + 4, res.u8[4], res.u8[5], res.u8[6], res.u8[7]);
		}
	}
}

U0 AOTLocalsResolve(CCompCtrl *cc)
{
	CAOTCtrl			*aotc = cc->aotc;
	CAsmUnresolvedRef	*tmpu = aotc->local_unresolved, *tmpu1;
	CAsmUndefHash		*tmpauh;
	Bool				 undefined;
	U8					*label = NULL;

	while (tmpu)
	{
		undefined = FALSE;
		tmpu1 = tmpu->next;
		tmpauh = tmpu->asm_undef_hash;
		while (tmpauh)
		{
			if (tmpauh->hash->type & HTF_UNRESOLVED)
			{
				PrintErr("Undefined sym at line:%04d %s %s\n", tmpu->line_num, aotc->last_label, tmpauh->hash->str);
				LexExcept(cc);
			}
			label = tmpauh->hash->str;
			tmpauh = tmpauh->next;
		}
		if (!undefined)
			AsmResolve(cc, tmpu, label, FALSE);
		Free(tmpu->machine_code);
		LinkedListDel(tmpu->asm_undef_hash);
		Free(tmpu);
		tmpu = tmpu1;
	}
	HashTableDel(cc->htc.local_hash_table);
	cc->htc.hash_table_list = cc->htc.local_hash_table = HashTableNew(16);
	cc->htc.local_hash_table->next = cc->htc.global_hash_table;
	aotc->local_unresolved = NULL;
}

U0 AOTGlobalsResolve(CCompCtrl *cc, CAOT *tmpaot)
{
	CAOTCtrl			*aotc = cc->aotc;
	CHashFun			*tmpf;
	CAsmUnresolvedRef	*tmpu = aotc->global_unresolved, *tmpu1;
	I64					 i, j;
	CAOTImportExport	*tmpie, *tmpie1;
	CAsmUndefHash		*tmpauh;
	CHashExport			*tmpex;
	U8					*label;
	Bool				 undefined;
	CExternUsage		*tmpeu, *tmpeu8;

	while (tmpu)
	{
		label = NULL;
		undefined = FALSE;
		tmpu1 = tmpu->next;
		tmpauh = tmpu->asm_undef_hash;
		while (tmpauh)
		{
			if (tmpauh->hash->type & HTF_UNRESOLVED)
			{
				tmpex = tmpauh->hash;
				if (tmpex->type & HTT_EXPORT_SYS_SYM && tmpex->type & HTF_UNRESOLVED &&
					!(tmpex->type & HTF_IMPORT) &&
					(tmpf = HashFind(tmpex->str, cc->htc.hash_table_list, HTT_FUN)) &&
					!Bt(&tmpf->flags, Cf_EXTERN))
				{
					tmpex->val = tmpf->exe_addr;
					tmpex->type &= ~HTF_UNRESOLVED;
					label = tmpauh->hash->str;
				}
				else
				{
					if (!(tmpex->type & HTF_IMPORT))
					{
						if (cc->htc.local_var_list)
						{
							tmpex->type |= HTF_GOTO_LABEL;
							tmpex->use_count++;
						}
						else
						{
							PrintErr("Undefined sym at line:%04d %s\n", tmpu->line_num, tmpex->str);
							LexExcept(cc);
						}
					}
					else if (undefined)
					{
						PrintErr("Two imports in same expression "
									"not allowed at line:%04d %s\n", tmpu->line_num, tmpex->str);
						LexExcept(cc);
					}
					undefined = TRUE;
				}
			}
			else
				label = tmpauh->hash->str;
			tmpauh = tmpauh->next;
		}
		AsmResolve(cc, tmpu, label, undefined);
		Free(tmpu->machine_code);
		LinkedListDel(tmpu->asm_undef_hash);
		Free(tmpu);
		tmpu = tmpu1;
	}

	for (i = 0; i <= cc->htc.global_hash_table->mask; i++)
	{
		tmpex = cc->htc.global_hash_table->body[i];
		while (tmpex)
		{
			if (tmpex->type & (HTF_IMPORT | HTF_GOTO_LABEL))
			{
				if (tmpex->use_count && (tmpie = tmpex->ie_list))
				{
					if (tmpex->type & HTF_GOTO_LABEL)
						tmpie->flags |= IEF_GOTO_LABEL;
					if (tmpex->import_name)
						tmpie->str = StrNew(tmpex->import_name);
					else
						tmpie->str = StrNew(tmpex->str);
					do
					{
						tmpie1 = tmpie->next;
						QueueInsert(tmpie, tmpaot->last_ie);
					}
					while (tmpie = tmpie1);

					tmpex->ie_list = NULL;
				}
			}
			else if (tmpex->type & (HTF_EXPORT | HTF_RESOLVE))
			{
				if (tmpex->type & HTF_UNRESOLVED)
				{
					PrintErr("Undefined sym at %s\n", tmpex->str);
					LexExcept(cc);
				}
				if (tmpex->type & HTF_RESOLVE)
				{
					tmpf = tmpex;
					tmpeu = tmpf->ext_list;
					while (tmpeu)
					{
						tmpeu8 = tmpeu->next;
						j = tmpf->exe_addr - (tmpeu->rip + 4);
						AOTStoreCodeU8At(cc, tmpeu->rip, j.u8[0], j.u8[1], j.u8[2], j.u8[3]);
						Free(tmpeu);
						tmpeu = tmpeu8;
					}
				}
				if (tmpex->type & HTF_EXPORT)
				{
					tmpie = CAlloc(sizeof(CAOTImportExport));
					tmpie->type = IET_REL32_EXPORT;
					if (tmpex->type & HTT_FUN)
						tmpie->rip = tmpf->exe_addr;
					else if (tmpex->type & HTT_GLOBAL_VAR)
						tmpie->rip = tmpex(CHashGlobalVar *)->data_addr_rip;
					else
						tmpie->rip = tmpex->val;
					tmpie->aot = cc->aot;
					if (tmpex->type & HTF_IMM)
						tmpie->type++;
					tmpie->str = StrNew(tmpex->str);
					tmpie->src_link = StrNew(tmpex->src_link);
					QueueInsert(tmpie, tmpaot->last_ie);
				}
			}
			tmpex = tmpex->next;
		}
	}
}

U0 AsmUnresolvedAdd(CCompCtrl *cc, U8 *machine_code, I64 type, I64 rip, I64 rel_rip,
				CAsmUndefHash *local_asm_undef_hash, CAsmUndefHash *global_asm_undef_hash,
				I64 line_num, Bool U8_avail)
{
	CAsmUnresolvedRef *tmpu = MAlloc(sizeof(CAsmUnresolvedRef));

	tmpu->machine_code	= machine_code;
	tmpu->type			= type;
	tmpu->rip			= rip;
	tmpu->rel_rip		= rel_rip;
	tmpu->aot			= cc->aot;
	tmpu->imm_flag		= FALSE;
	tmpu->line_num		= line_num;
	tmpu->U8_avail		= U8_avail;
	tmpu->str			= NULL;

	if (local_asm_undef_hash)
	{
		tmpu->asm_undef_hash = local_asm_undef_hash;
		tmpu->next = cc->aotc->local_unresolved;
		cc->aotc->local_unresolved = tmpu;
	}
	else
	{
		tmpu->asm_undef_hash = global_asm_undef_hash;
		tmpu->next = cc->aotc->global_unresolved;
		cc->aotc->global_unresolved = tmpu;
		if (global_asm_undef_hash->hash->type & HTF_IMPORT)
		{
			tmpu->str = StrNew(global_asm_undef_hash->hash->str);
			if (global_asm_undef_hash->hash->type & HTF_IMM)
				tmpu->imm_flag = TRUE;
		}
	}
}
