/* 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); } }
// Output relocation void greloc(Sym *sym, int c, int rel) { int b; if (sym && !sym->c) put_extern_sym(sym, NULL, 0, 0); b = gbranch(CodeReloc); if (sym) branch[b].target = sym->c; branch[b].param = rel; gen_le32(c); }
static void asm_parse_directive(TCCState *s1) { int n, offset, v, size, tok1; Section *sec; uint8_t *ptr; /* assembler directive */ next(); sec = cur_text_section; switch(tok) { case TOK_ASM_align: case TOK_ASM_p2align: case TOK_ASM_skip: case TOK_ASM_space: tok1 = tok; next(); n = asm_int_expr(s1); if (tok1 == TOK_ASM_p2align) { if (n < 0 || n > 30) tcc_error("invalid p2align, must be between 0 and 30"); n = 1 << n; tok1 = TOK_ASM_align; } if (tok1 == TOK_ASM_align) { if (n < 0 || (n & (n-1)) != 0) tcc_error("alignment must be a positive power of two"); offset = (ind + n - 1) & -n; size = offset - ind; /* the section must have a compatible alignment */ if (sec->sh_addralign < n) sec->sh_addralign = n; } else { size = n; } v = 0; if (tok == ',') { next(); v = asm_int_expr(s1); } zero_pad: if (sec->sh_type != SHT_NOBITS) { sec->data_offset = ind; ptr = section_ptr_add(sec, size); memset(ptr, v, size); } ind += size; break; case TOK_ASM_quad: next(); for(;;) { uint64_t vl; const char *p; p = tokc.cstr->data; if (tok != TOK_PPNUM) { error_constant: tcc_error("64 bit constant"); } vl = strtoll(p, (char **)&p, 0); if (*p != '\0') goto error_constant; next(); if (sec->sh_type != SHT_NOBITS) { /* XXX: endianness */ gen_le32(vl); gen_le32(vl >> 32); } else { ind += 8; } if (tok != ',') break; next(); }
// Instruction + 4 bytes data void oad(int c, int s) { o(c); gen_le32(s); }
// Generate function call. The function address is pushed first, then // all the parameters in call order. This function pops all the // parameters and the function address. void gfunc_call(int nb_args) { int size, align, r, args_size, i, func_call, v; Sym *func_sym; args_size = 0; for (i = 0; i < nb_args; i++) { if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { size = type_size(&vtop->type, &align); // Align to stack align size size = (size + 3) & ~3; // Allocate the necessary size on stack oad(0xec81, size); // sub $xxx, %esp // Generate structure store r = get_reg(RC_INT); o(0x89); // mov %esp, r o(0xe0 + r); vset(&vtop->type, r | VT_LVAL, 0); vswap(); vstore(); args_size += size; } else if (is_float(vtop->type.t)) { gv(RC_FLOAT); // Only one float register if ((vtop->type.t & VT_BTYPE) == VT_FLOAT) { size = 4; } else if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) { size = 8; } else { size = 12; } oad(0xec81, size); // sub $xxx, %esp if (size == 12) { o(0x7cdb); } else { o(0x5cd9 + size - 4); // fstp[s|l] 0(%esp) } g(0x24); g(0x00); args_size += size; } else { // Simple type (currently always same size) // TODO: implicit cast? v = vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM); if (v == VT_CONST || v == (VT_CONST | VT_SYM)) { // Push constant if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { size = 8; if (vtop->c.word[1] == (char) vtop->c.word[1]) { g(0x6a); // push imm8 g(vtop->c.word[1]); } else { g(0x68); // push imm32 gen_le32(vtop->c.word[1]); } } else { size = 4; } if ((v & VT_SYM) == 0 && vtop->c.i == (char) vtop->c.i) { g(0x6a); // push imm8 g(vtop->c.i); } else { g(0x68); // push imm32 gen_addr32(v, vtop->sym, vtop->c.i); } } else { r = gv(RC_INT); if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { size = 8; o(0x50 + vtop->r2); // push r2 } else { size = 4; } o(0x50 + r); // push r } args_size += size; } vtop--; } save_regs(0); // Save used temporary registers func_sym = vtop->type.ref; func_call = FUNC_CALL(func_sym->r); // fast call case if ((func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) || func_call == FUNC_FASTCALLW) { int fastcall_nb_regs; uint8_t *fastcall_regs_ptr; if (func_call == FUNC_FASTCALLW) { fastcall_regs_ptr = fastcallw_regs; fastcall_nb_regs = 2; } else { fastcall_regs_ptr = fastcall_regs; fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1; } for (i = 0; i < fastcall_nb_regs; i++) { if (args_size <= 0) break; o(0x58 + fastcall_regs_ptr[i]); // pop r // TODO: incorrect for struct/floats args_size -= 4; } } gcall_or_jmp(0); if (args_size && func_call != FUNC_STDCALL) gadd_sp(args_size); vtop--; }
/* 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); }