/* 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; }
// 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; }
/* 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)); }
/* patch pointer addition in vtop so that pointer dereferencing is also tested */ void gen_bounded_ptr_deref(void) { int func; int size, align; Elf32_Rel *rel; Sym *sym; size = 0; /* XXX: put that code in generic part of tcc */ if (!is_float(vtop->type.t)) { if (vtop->r & VT_LVAL_BYTE) size = 1; else if (vtop->r & VT_LVAL_SHORT) size = 2; } if (!size) size = type_size(&vtop->type, &align); switch(size) { case 1: func = TOK___bound_ptr_indir1; break; case 2: func = TOK___bound_ptr_indir2; break; case 4: func = TOK___bound_ptr_indir4; break; case 8: func = TOK___bound_ptr_indir8; break; case 12: func = TOK___bound_ptr_indir12; break; case 16: func = TOK___bound_ptr_indir16; break; default: error("unhandled size when derefencing bounded pointer"); func = 0; break; } /* patch relocation */ /* XXX: find a better solution ? */ rel = (Elf32_Rel *)(cur_text_section->reloc->data + vtop->c.ul); sym = external_global_sym(func, &func_old_type, 0); if (!sym->c) put_extern_sym(sym, NULL, 0, 0); rel->r_info = ELF32_R_INFO(sym->c, ELF32_R_TYPE(rel->r_info)); }
void gcode(void) { int i, n, t, r, stacksize, addr, pc, disp, rel, errs, more, func_start; Branch *b, *bn; // Generate function prolog func_start = cur_text_section->data_offset; if (!func_naked) { // Align local size to word stacksize = (-loc + 3) & -4; if (stacksize >= 4096) { // Generate stack guard since parameters can cross page boundary Sym *sym = external_global_sym(TOK___chkstk, &func_old_type, 0); gen(0xb8); // mov stacksize, %eax genword(stacksize); gen(0xe8); // call __chkstk, (does the stackframe too) put_reloc(cur_text_section, sym, cur_text_section->data_offset, R_386_PC32); genword(-4); } else { if (do_debug || loc || !func_noargs) { gen(0x55); // push %ebp gen(0x89); // mov %esp, %ebp gen(0xe5); } if (stacksize > 0) { if (stacksize == (char) stacksize) { gen(0x83); // sub esp, stacksize gen(0xec); gen(stacksize); } else { gen(0x81); // sub esp, stacksize gen(0xec); genword(stacksize); } } } // Save callee-saved registers used by function. for (r = 0; r < NB_REGS; ++r) { if ((reg_classes[r] & RC_SAVE) && (regs_used & (1 << r))) { gen(0x50 + r); // push r } } } // Optimize jumps more = 1; while (more) { more = 0; for (i = 0; i < br; ++i) { b = branch + i; if (b->type == CodeLabel) b->target = 0; if (b->type != CodeJump) continue; t = skip_nops(b->target, 1); if (branch[t].type == CodeJump && !branch[t].param && b->target != branch[t].target) { // Eliminate jump to jump b->target = branch[t].target; more = 1; continue; } // Find next non-nop n = i + 1; while (branch[n].type == CodeNop || branch[n].type == CodeLine) n++; bn = branch + n; if (b->ind != bn->ind) continue; if (!b->param && bn->type == CodeJump) { // Eliminate dead jump instruction bn->type = CodeNop; more = 1; continue; } if (b->target > i && b->target <= n) { // Eliminate jump to next instruction b->type = CodeNop; more = 1; continue; } t = skip_nops(n + 1, 0); if (bn->type == CodeJump && !bn->param && b->target == t && bn->ind == branch[t].ind) { // Optimize inverted jump if (b->param) b->param ^= 1; b->target = bn->target; bn->type = CodeNop; more = 1; continue; } } // Eliminate unused labels for (i = 0; i < br; ++i) { b = branch + i; if (b->type == CodeJump) branch[b->target].target++; } for (i = 0; i < br; ++i) { b = branch + i; if (b->type == CodeLabel && !b->target && !b->sym) { // Remove label with no references b->type = CodeNop; more = 1; } } } // Assign addresses to branch points, assuming only long jumps addr = cur_text_section->data_offset; pc = 0; for (i = 0; i < br; ++i) { b = branch + i; addr += b->ind - pc; b->addr = addr; switch (b->type) { case CodeJump: addr += 5; if (b->param != 0) addr++; break; case CodeAlign: // Use convervative estimate for short/long jump estimation addr += b->param - 1; break; } pc = b->ind; } // Find jumps which can be encoded as short jumps for (i = 0; i < br; ++i) { b = branch + i; if (b->type == CodeJump) { disp = branch[b->target].addr - b->addr - 2; if (b->param) disp--; if (disp == (char) disp) b->type = CodeShortJump; } } // Assign final addresses to branch points addr = cur_text_section->data_offset; pc = 0; for (i = 0; i < br; ++i) { b = branch + i; addr += b->ind - pc; b->addr = addr; switch (b->type) { case CodeJump: addr += 5; if (b->param) addr++; break; case CodeShortJump: addr += 2; break; case CodeAlign: addr = (addr + b->param - 1) & -b->param; break; } pc = b->ind; } // Generate code blocks pc = 0; errs = 0; for (i = 0; i < br; ++i) { b = branch + i; // Output code block before branch point if (b->ind != pc) { genblk(code + pc, b->ind - pc); pc = b->ind; } switch (b->type) { case CodeLabel: // Export label if symbol defined if (b->sym) { put_extern_sym_ex(b->sym, cur_text_section, b->addr, 0, 0); sym_free(b->sym); } break; case CodeJump: // Generate long jump instruction if (branch[b->target].type != CodeLabel) { printf("internal error: jump %d to non-label %d\n", i, b->target); errs++; } if (b->param > 0xff) error("invalid displacement"); if (b->param == 0) { gen(0xe9); genword(branch[b->target].addr - (b->addr + 5)); } else { gen(0x0f); gen(b->param - 0x10); genword(branch[b->target].addr - (b->addr + 6)); } break; case CodeShortJump: // Generate short jump instruction if (branch[b->target].type != CodeLabel) { printf("internal error: jump %d to non-label %d\n", i, b->target); errs++; } if (b->param == 0) { gen(0xeb); } else { gen(b->param - 0x20); } gen(branch[b->target].addr - (b->addr + 2)); break; case CodeReloc: if (b->param) { rel = R_386_PC32; } else { rel = R_386_32; } put_elf_reloc(symtab_section, cur_text_section, b->addr, rel, b->target); break; case CodeAlign: i = addr; while (i & (b->param - 1)) { gen(b->target); i++; } break; case CodeLine: put_stabn(N_SLINE, 0, b->target, b->addr - func_start); break; } } // Generate function epilog if (!func_naked) { // Restore callee-saved registers used by function. for (r = NB_REGS; r >= 0; --r) { if ((reg_classes[r] & RC_SAVE) && (regs_used & (1 << r))) { gen(0x58 + r); // pop r } } if (do_debug || loc || !func_noargs) gen(0xc9); // leave // Generate return if (func_ret_sub == 0) { gen(0xc3); // ret } else { gen(0xc2); // ret n gen(func_ret_sub); gen(func_ret_sub >> 8); } } #ifdef DEBUG_BRANCH printf("\nbranch table for %s\n", func_name); printf(" # t targ parm ind addr\n"); printf("---- - ---- ----- -------- --------\n"); for (i = 0; i < br; ++i) { b = branch + i; printf("%04d %c %04d %04x %08x %08x", i, "SLJjRANlE"[b->type], b->target, b->param, b->ind, b->addr); if (branch[i].sym) { printf(" sym=%s", get_tok_str(b->sym->v, NULL)); } printf("\n"); } printf("\n"); #endif if (errs) error("internal error: code generation"); }
/* 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; }