/* XXX: handle long long case */ ST_FUNC void gen_cvt_ftoi(int t) { int r, r2, size; Sym *sym; CType ushort_type; ushort_type.t = VT_SHORT | VT_UNSIGNED; ushort_type.ref = 0; gv(RC_FLOAT); if (t != VT_INT) size = 8; else size = 4; o(0x2dd9); /* ldcw xxx */ sym = external_global_sym(TOK___tcc_int_fpu_control, &ushort_type, VT_LVAL); greloc(cur_text_section, sym, ind, R_386_32); gen_le32(0); oad(0xec81, size); /* sub $xxx, %esp */ if (size == 4) o(0x1cdb); /* fistpl */ else o(0x3cdf); /* fistpll */ o(0x24); o(0x2dd9); /* ldcw xxx */ sym = external_global_sym(TOK___tcc_fpu_control, &ushort_type, VT_LVAL); greloc(cur_text_section, sym, ind, R_386_32); gen_le32(0); r = get_reg(RC_INT); o(0x58 + r); /* pop r */ if (size == 8) { if (t == VT_LLONG) { vtop->r = r; /* mark reg as used */ r2 = get_reg(RC_INT); o(0x58 + r2); /* pop r2 */ vtop->r2 = r2; } else { o(0x04c483); /* add $4, %esp */ } } vtop->r = r; }
// Output constant with relocation if 'r & VT_SYM' is true void gen_addr32(int r, Sym *sym, int c) { if (r & VT_SYM) { greloc(sym, c, 0); } else { gen_le32(c); } }
// Convert fp to int 't' type // TODO: handle long long case void gen_cvt_ftoi(int t) { int r, r2, size; CType ushort_type; ushort_type.t = VT_SHORT | VT_UNSIGNED; gv(RC_FLOAT); if (t != VT_INT) { size = 8; } else { size = 4; } o(0x2dd9); // ldcw xxx greloc(external_global_sym(TOK___tcc_int_fpu_control, &ushort_type, VT_LVAL), 0, 0); oad(0xec81, size); // sub $xxx, %esp if (size == 4) { o(0x1cdb); // fistpl } else { o(0x3cdf); // fistpll } o(0x24); o(0x2dd9); // ldcw xxx greloc(external_global_sym(TOK___tcc_fpu_control, &ushort_type, VT_LVAL), 0, 0); r = get_reg(RC_INT); o(0x58 + r); // pop r if (size == 8) { if (t == VT_LLONG) { vtop->r = r; // Mark reg as used r2 = get_reg(RC_INT); o(0x58 + r2); // pop r2 vtop->r2 = r2; } else { o(0x04c483); // add $4, %esp } } vtop->r = r; }
// 'is_jmp' is '1' if it is a jump void gcall_or_jmp(int is_jmp) { int r; Sym *sym; if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { // Constant case o(0xe8 + is_jmp); // call/jmp im sym = NULL; if (vtop->r & VT_SYM) sym = vtop->sym; greloc(sym, vtop->c.ul - 4, 1); } else { // Otherwise, indirect call r = gv(RC_INT); o(0xff); // call/jmp *r o(0xd0 + r + (is_jmp << 4)); } }
/* generate a bounded pointer addition */ void gen_bounded_ptr_add(void) { Sym *sym; /* prepare fast i386 function call (args in eax and edx) */ gv2(RC_EAX, RC_EDX); /* save all temporary registers */ vtop -= 2; save_regs(0); /* do a fast function call */ sym = external_global_sym(TOK___bound_ptr_add, &func_old_type, 0); greloc(cur_text_section, sym, ind + 1, R_386_PC32); oad(0xe8, -4); /* returned pointer is in eax */ vtop++; vtop->r = TREG_EAX | VT_BOUNDED; /* address of bounding function call point */ vtop->c.ul = (cur_text_section->reloc->data_offset - sizeof(Elf32_Rel)); }
/* 'is_jmp' is '1' if it is a jump */ static void gcall_or_jmp(int is_jmp) { int r; if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { /* constant case */ if (vtop->r & VT_SYM) { /* relocation case */ greloc(cur_text_section, vtop->sym, ind + 1, R_386_PC32); } else { /* put an empty PC32 relocation */ put_elf_reloc(symtab_section, cur_text_section, ind + 1, R_386_PC32, 0); } oad(0xe8 + is_jmp, vtop->c.ul - 4); /* call/jmp im */ } else { /* otherwise, indirect call */ r = gv(RC_INT); o(0xff); /* call/jmp *r */ o(0xd0 + r + (is_jmp << 4)); } }
/* generate function epilog */ void gfunc_epilog(void) { int v, saved_ind; #ifdef CONFIG_TCC_BCHECK if (tcc_state->do_bounds_check && func_bound_offset != lbounds_section->data_offset) { int saved_ind; int *bounds_ptr; Sym *sym, *sym_data; /* add end of table info */ bounds_ptr = section_ptr_add(lbounds_section, sizeof(int)); *bounds_ptr = 0; /* generate bound local allocation */ saved_ind = ind; ind = func_sub_sp_offset; sym_data = get_sym_ref(&char_pointer_type, lbounds_section, func_bound_offset, lbounds_section->data_offset); greloc(cur_text_section, sym_data, ind + 1, R_386_32); oad(0xb8, 0); /* mov %eax, xxx */ sym = external_global_sym(TOK___bound_local_new, &func_old_type, 0); greloc(cur_text_section, sym, ind + 1, R_386_PC32); oad(0xe8, -4); ind = saved_ind; /* generate bound check local freeing */ o(0x5250); /* save returned value, if any */ greloc(cur_text_section, sym_data, ind + 1, R_386_32); oad(0xb8, 0); /* mov %eax, xxx */ sym = external_global_sym(TOK___bound_local_delete, &func_old_type, 0); greloc(cur_text_section, sym, ind + 1, R_386_PC32); oad(0xe8, -4); o(0x585a); /* restore returned value, if any */ } #endif o(0xc9); /* leave */ if (func_ret_sub == 0) { o(0xc3); /* ret */ } else { o(0xc2); /* ret n */ g(func_ret_sub); g(func_ret_sub >> 8); } /* align local size to word & save local variables */ v = (-loc + 3) & -4; saved_ind = ind; ind = func_sub_sp_offset - FUNC_PROLOG_SIZE; #ifdef TCC_TARGET_PE if (v >= 4096) { Sym *sym = external_global_sym(TOK___chkstk, &func_old_type, 0); oad(0xb8, v); /* mov stacksize, %eax */ oad(0xe8, -4); /* call __chkstk, (does the stackframe too) */ greloc(cur_text_section, sym, ind-4, R_386_PC32); } else #endif { o(0xe58955); /* push %ebp, mov %esp, %ebp */ o(0xec81); /* sub esp, stacksize */ gen_le32(v); #if FUNC_PROLOG_SIZE == 10 o(0x90); /* adjust to FUNC_PROLOG_SIZE */ #endif } ind = saved_ind; }
/* output constant with relocation if 'r & VT_SYM' is true */ static void gen_addr32(int r, Sym *sym, int c) { if (r & VT_SYM) greloc(cur_text_section, sym, ind, R_386_32); gen_le32(c); }
ST_FUNC void gen_addrpc32(int r, Sym *sym, int c) { if (r & VT_SYM) greloc(cur_text_section, sym, ind, R_386_PC32); gen_le32(c - 4); }