pike.git / src / code / ia32.c

version» Context lines:

pike.git/src/code/ia32.c:6:      /*    * Machine code generator for IA32.    */      #include "operators.h"   #include "constants.h"   #include "object.h"   #include "builtin_functions.h"    - /* This is defined on windows */ - #ifdef REG_NONE - #undef REG_NONE - #endif + enum ia32_reg {P_REG_EAX = 0, P_REG_EBX = 3, P_REG_ECX = 1, P_REG_EDX = 2, +  P_REG_NONE = 4, P_REG_ESP = 4, P_REG_EBP = 5, P_REG_ESI = 6, +  P_REG_EDI = 7, };    - enum ia32_reg {REG_EAX = 0, REG_EBX = 3, REG_ECX = 1, REG_EDX = 2, REG_NONE = 4, -  REG_ESP = 4, REG_EBP = 5, REG_ESI = 6, REG_EDI = 7, }; + #define REG_BITMASK ((1 << P_REG_NONE) - 1)    - #define REG_BITMASK ((1 << REG_NONE) - 1) + /* Padding needed to align ESP on a 16-byte boundary after +  * a function call. This is needed to be able to call +  * functions that use SSE2 opcodes. +  */ + #define STACK_PAD 0x0c      /* #define REGISTER_DEBUG */      #ifdef REGISTER_DEBUG   static int alloc_regs = 0, valid_regs = 0;   #define ALLOC_REG(REG) do { \    alloc_regs |= (1 << (REG)); \    } while (0)   #define DEALLOC_REG(REG) do { \    alloc_regs &= ~(1 << (REG)); \
pike.git/src/code/ia32.c:64:   #define MOV_VAL32_TO_REG(VAL, REG) do { \    /* movl $val,%reg */ \    add_to_program (0xb8 | (REG)); /* Move imm32 to r32. */ \    PUSH_INT (VAL); /* Assumed to be added last. */ \    MAKE_VALID_REG (REG); \    } while (0)      #define MOV_ABSADDR_TO_REG(ADDR, REG) do { \    MAKE_VALID_REG (REG); \    /* movl addr,%reg */ \ -  if ((REG) == REG_EAX) \ +  if ((REG) == P_REG_EAX) \    add_to_program (0xa1); /* Move dword at address to EAX. */ \    else { \    add_to_program (0x8b); /* Move r/m32 to r32. */ \    add_to_program (0x5 | ((REG) << 3)); \    } \    PUSH_ADDR (&(ADDR)); \    } while (0)      #define MOV_REG_TO_REG(FROM, TO) do { \    CHECK_VALID_REG(FROM); \    MAKE_VALID_REG(TO); \    add_to_program (0x89); /* Move r32 to r/m32. */ \    add_to_program (0xc0 | ((FROM)<<3) | (TO)); \    } while(0)      #define MOV_REG_TO_ABSADDR(REG, ADDR) do { \    CHECK_VALID_REG (REG); \    /* movl %reg,addr */ \ -  if ((REG) == REG_EAX) \ +  if ((REG) == P_REG_EAX) \    add_to_program (0xa3); /* Move EAX to dword at address. */ \    else { \    add_to_program (0x89); /* Move r32 to r/m32. */ \    add_to_program (0x5 | ((REG) << 3)); \    } \    PUSH_ADDR (&(ADDR)); \    } while (0)      #define MOV_RELADDR_TO_REG(OFFSET, REG, DSTREG) do { \    INT32 off_ = (OFFSET); \
pike.git/src/code/ia32.c:248:    INT32 val_ = (VAL); \    CHECK_VALID_REG (REG); \    if (val_ == 1) \    /* incl %reg */ \    add_to_program (0x40 | (REG)); /* Increment r32. */ \    else if (val_ == -1) \    /* decl %reg */ \    add_to_program (0x48 | (REG)); /* Decrement r32. */ \    else if (val_ < -128 || val_ > 127) { \    /* addl $val,%reg */ \ -  if ((REG) == REG_EAX) \ +  if ((REG) == P_REG_EAX) \    add_to_program (0x05); /* Add imm32 to EAX. */ \    else { \    add_to_program (0x81); /* Add imm32 to r/m32. */ \    add_to_program (0xc0 | (REG)); \    } \    PUSH_INT (val_); \    } \    else if (val_) { \    /* addl $val,%reg */ \    add_to_program (0x83); /* Add sign-extended imm8 to r/m32. */ \
pike.git/src/code/ia32.c:333:    add_to_program (0x05); \    PUSH_ADDR (&(ADDR)); \    if (-128 <= val_ && val_ <= 127) \    add_to_program (val_); \    else \    PUSH_INT (val_); \    } \    } while (0)      #define CMP_REG_IMM32(REG, IMM) do { \ -  if ((REG) == REG_EAX) { \ +  if ((REG) == P_REG_EAX) { \    add_to_program(0x3d); \    } else { \    add_to_program(0x81); \    add_to_program(0xf8 | (REG)); \    } \    PUSH_INT((IMM)); \    } while (0)      #define CMP_REG_REG(REG1, REG2) do { \    add_to_program(0x39); \
pike.git/src/code/ia32.c:398: Inside #if defined(INS_ENTRY)
   valid_regs = 0xff;   #endif    /* Push all registers that the ABI requires to be preserved.    *    * cf Sys V ABI Intel386 Architecture Supplement 4th ed 3-11:    * "Registers %ebp, %ebx, %edi, %esi, and %esp ``belong'' to the cal-    * ling function."    *    * also cf Fig 3-16.    */ -  PUSH_REG(REG_EBP); -  MOV_REG_TO_REG(REG_ESP, REG_EBP); +  PUSH_REG(P_REG_EBP); +  MOV_REG_TO_REG(P_REG_ESP, P_REG_EBP);    /* FIXME: Add local storage here if needed. */ -  PUSH_REG(REG_EDI); -  PUSH_REG(REG_ESI); -  PUSH_REG(REG_EBX); -  /* Funcall arguments (4 * 4 bytes). */ -  ADD_VAL_TO_REG(-0x10, REG_ESP); +  PUSH_REG(P_REG_EDI); +  PUSH_REG(P_REG_ESI); +  PUSH_REG(P_REG_EBX); +  /* Funcall arguments (4 * 4 bytes + padding). */ +  ADD_VAL_TO_REG(-(0x10 + STACK_PAD), P_REG_ESP);      #ifdef REGISTER_DEBUG    valid_regs = 0;   #endif   }   #endif      static void update_arg1(INT32 value)   {    MOV_VAL_TO_RELSTACK (value, 0);
pike.git/src/code/ia32.c:434:   #define PIKE_CPU_VENDOR_INTEL 1   #define PIKE_CPU_VENDOR_AMD 2   static int cpu_vendor = PIKE_CPU_VENDOR_UNKNOWN;      static enum ia32_reg next_reg;   static enum ia32_reg pi_reg, sp_reg, fp_reg, mark_sp_reg;   ptrdiff_t ia32_prev_stored_pc; /* PROG_PC at the last point Pike_fp->pc was updated. */      void ia32_flush_code_generator(void)   { -  next_reg = REG_EAX; -  pi_reg = sp_reg = fp_reg = mark_sp_reg = REG_NONE; +  next_reg = P_REG_EAX; +  pi_reg = sp_reg = fp_reg = mark_sp_reg = P_REG_NONE;    CLEAR_REGS();    ia32_prev_stored_pc = -1;   }      static enum ia32_reg alloc_reg (int avoid_regs)   {    enum ia32_reg reg;    int used_regs =    (avoid_regs | (1 << sp_reg) | (1 << fp_reg) | (1 << mark_sp_reg)) &    REG_BITMASK;       if (used_regs != REG_BITMASK) {    /* There's a free register. */       for (reg = next_reg; (1 << reg) & used_regs;) { -  reg = (reg + 1) % REG_NONE; +  reg = (reg + 1) % P_REG_NONE;   #ifdef PIKE_DEBUG    if (reg == next_reg) Pike_fatal ("Failed to find a free register.\n");   #endif    }    }       else {    /* Choose a register with simple round robin. If we get more    * things to hold than there are registers then this should    * probably be replaced with an LRU strategy. */       for (reg = next_reg; (1 << reg) & avoid_regs;) { -  reg = (reg + 1) % REG_NONE; +  reg = (reg + 1) % P_REG_NONE;   #ifdef PIKE_DEBUG    if (reg == next_reg) Pike_fatal ("Failed to find a non-excluded register.\n");   #endif    }    -  if (sp_reg == reg) {sp_reg = REG_NONE; DEALLOC_REG (reg);} -  else if (fp_reg == reg) {fp_reg = REG_NONE; DEALLOC_REG (reg);} -  else if (mark_sp_reg == reg) {mark_sp_reg = REG_NONE; DEALLOC_REG (reg);} -  else if (pi_reg == reg) {pi_reg = REG_NONE; DEALLOC_REG (reg);} +  if (sp_reg == reg) {sp_reg = P_REG_NONE; DEALLOC_REG (reg);} +  else if (fp_reg == reg) {fp_reg = P_REG_NONE; DEALLOC_REG (reg);} +  else if (mark_sp_reg == reg) {mark_sp_reg = P_REG_NONE; DEALLOC_REG (reg);} +  else if (pi_reg == reg) {pi_reg = P_REG_NONE; DEALLOC_REG (reg);}    }    -  if (reg == pi_reg) {pi_reg = REG_NONE; DEALLOC_REG (reg);} +  if (reg == pi_reg) {pi_reg = P_REG_NONE; DEALLOC_REG (reg);}      #ifdef REGISTER_DEBUG    if ((1 << reg) & alloc_regs) Pike_fatal ("Clobbering allocated register.\n");    alloc_regs &= avoid_regs;   #endif    ALLOC_REG (reg);    return reg;   }      #define DEF_LOAD_REG(REG, SET) \    static void PIKE_CONCAT(load_,REG) (int avoid_regs) \    { \ -  if (REG == REG_NONE) { \ +  if (REG == P_REG_NONE) { \    REG = alloc_reg (avoid_regs); \    /* Update the round robin pointer here so that we disregard */ \    /* the direct calls to alloc_reg for temporary registers. */ \ -  next_reg = (REG + 1) % REG_NONE; \ +  next_reg = (REG + 1) % P_REG_NONE; \    {SET;} \    } \    else \    ALLOC_REG (REG); \    }      DEF_LOAD_REG (pi_reg, {    MOV_ABSADDR_TO_REG (Pike_interpreter_pointer, pi_reg);    });   DEF_LOAD_REG (sp_reg, {
pike.git/src/code/ia32.c:522:    });   DEF_LOAD_REG (mark_sp_reg, {    load_pi_reg(avoid_regs|(1 << mark_sp_reg));    MOV_RELADDR_TO_REG (OFFSETOF(Pike_interpreter_struct, mark_stack_pointer),    pi_reg, mark_sp_reg);    });      static void ia32_call_c_function(void *addr)   {    CALL_RELATIVE(addr); -  next_reg = REG_EAX; -  pi_reg = sp_reg = fp_reg = mark_sp_reg = REG_NONE; +  next_reg = P_REG_EAX; +  pi_reg = sp_reg = fp_reg = mark_sp_reg = P_REG_NONE;    CLEAR_REGS();   }      /* NOTE: This code is not safe for generic constants, since they    * can be overridden by inherit. */   static void ia32_push_constant(struct svalue *tmp)   {    int e;    if(REFCOUNTED_TYPE(TYPEOF(*tmp)))    ADD_VAL_TO_ABSADDR (1, tmp->u.refs);
pike.git/src/code/ia32.c:618:   {    ia32_push_svalue (ia32_get_local_addr (arg));   }      static void ia32_local_lvalue(INT32 arg)   {    size_t e;    struct svalue tmp[2];    enum ia32_reg addr_reg = ia32_get_local_addr(arg);    -  MEMSET(tmp, 0, sizeof(tmp)); -  tmp[0].type=T_SVALUE_PTR; -  tmp[1].type=T_VOID; +  memset(tmp, 0, sizeof(tmp)); +  SET_SVAL_TYPE(tmp[0], T_SVALUE_PTR); +  SET_SVAL_TYPE(tmp[1], T_VOID);       load_pi_reg ((1 << addr_reg)|(1 << sp_reg));    load_sp_reg ((1 << addr_reg)|(1 << pi_reg));       for(e=0;e<sizeof(tmp)/4;e++)    {    if(((INT32 *)tmp) + e == (INT32 *) &tmp[0].u.lval)    {    MOV_REG_TO_RELADDR (addr_reg, e*4, sp_reg);    }else{
pike.git/src/code/ia32.c:689:       load_pi_reg (0);    MOV_REG_TO_RELADDR (mark_sp_reg,    OFFSETOF(Pike_interpreter_struct, mark_stack_pointer),    pi_reg);   }      static void ia32_push_int(INT32 x)   {    struct svalue tmp; -  tmp.type=PIKE_T_INT; -  tmp.subtype=0; -  tmp.u.integer=x; +  SET_SVAL(tmp, PIKE_T_INT, 0, integer, x);    ia32_push_constant(&tmp);   }      static void ia32_push_string (INT32 x, int subtype)   {    size_t e;    struct svalue tmp = SVALUE_INIT (PIKE_T_STRING, subtype, 0);       enum ia32_reg tmp_reg = alloc_reg ((1 << fp_reg) | (1 << sp_reg));    load_fp_reg ((1 << tmp_reg) | (1 << sp_reg));
pike.git/src/code/ia32.c:892: Inside #if undefined(DEBUG_MALLOC)
   break;      #ifdef OPCODE_INLINE_RETURN    case F_CATCH - F_OFFSET:    {    /* Special argument for the F_CATCH instruction. */    addr = inter_return_opcode_F_CATCH;    /* FIXME: Is there really no easier way to get at EIP? */    add_to_program(0xe8);    PUSH_INT(0); -  POP_REG(REG_EAX); /* EIP ==> EAX */ -  ADD_VAL_TO_REG(0x7fffffff, REG_EAX); +  POP_REG(P_REG_EAX); /* EIP ==> EAX */ +  ADD_VAL_TO_REG(0x7fffffff, P_REG_EAX);    rel_addr = PIKE_PC; -  MOV_REG_TO_RELSTACK(REG_EAX, 0); +  MOV_REG_TO_RELSTACK(P_REG_EAX, 0);    }    break;   #endif    }   #endif /* !DEBUG_MALLOC */       ia32_call_c_function(addr);      #ifdef INS_ENTRY    if (instrs[b].flags & I_RETURN) {
pike.git/src/code/ia32.c:919: Inside #if defined(INS_ENTRY)
   valid_regs = 0xff;   #endif    if ((b + F_OFFSET) == F_RETURN_IF_TRUE) {    /* Kludge. We must check if the ret addr is    * orig_addr + JUMP_EPILOGUE_SIZE. */    /* There's no easy way to get at EIP directly,    * so we assume that the function we called    * above exited with a RET, in which case EIP    * is at ESP[-4]...    */ -  MOV_RELSTACK_TO_REG(-4, REG_ESI); -  ADD_VAL_TO_REG(JUMP_EPILOGUE_SIZE, REG_ESI); +  MOV_RELSTACK_TO_REG(-4, P_REG_ESI); +  ADD_VAL_TO_REG(JUMP_EPILOGUE_SIZE, P_REG_ESI);    } -  CMP_REG_IMM32(REG_EAX, -1); +  CMP_REG_IMM32(P_REG_EAX, -1);    JNE(0);    skip = (INT32)PIKE_PC; -  ADD_VAL_TO_REG(0x10, REG_ESP); /* Funcall arguments (4 * 4 bytes). */ -  POP_REG(REG_EBX); -  POP_REG(REG_ESI); -  POP_REG(REG_EDI); -  POP_REG(REG_EBP); +  ADD_VAL_TO_REG(0x10 + STACK_PAD, P_REG_ESP); /* Funcall arguments (4 * 4 bytes) + padding. */ +  POP_REG(P_REG_EBX); +  POP_REG(P_REG_ESI); +  POP_REG(P_REG_EDI); +  POP_REG(P_REG_EBP);    RET();    Pike_compiler->new_program->program[skip-1] = ((INT32)PIKE_PC) - skip;    if ((b + F_OFFSET) == F_RETURN_IF_TRUE) {    /* Kludge. We must check if the ret addr is    * orig_addr + JUMP_EPILOGUE_SIZE. */ -  CMP_REG_REG(REG_EAX, REG_ESI); +  CMP_REG_REG(P_REG_EAX, P_REG_ESI);    JE(0x02);    add_to_program (0xff); /* jmp *%eax */    add_to_program (0xe0);   #ifdef REGISTER_DEBUG    valid_regs = orig_valid_regs;   #endif    return;    }   #ifdef REGISTER_DEBUG    valid_regs = orig_valid_regs;
pike.git/src/code/ia32.c:1039: Inside #if undefined(DEBUG_MALLOC)
   /*    * This would work nicely for all pike types, but we would    * have to augment dumping    *    * Note: The constants table may contain UNDEFINED in case of being    * called through decode_value() in PORTABLE_BYTECODE mode.    *    * /grubba 2003-12-11    */    if(!REFCOUNTED_TYPE(TYPEOF(Pike_compiler->new_program->constants[b].sval)) && -  !Pike_compiler->new_program->constants[b].sval.subtype) +  !SUBTYPEOF(Pike_compiler->new_program->constants[b].sval))    {    ins_debug_instr_prologue (a - F_OFFSET, b, 0);    ia32_push_constant(& Pike_compiler->new_program->constants[b].sval);    return;    }    break;       /*    * And this would work nicely for all non-dynamically loaded    * functions. Unfortunately there is no way to know if it is
pike.git/src/code/ia32.c:1112:   static INT32 do_ins_jump (unsigned int op, int backward_jump)   {    INT32 ret = -1;       if(op == F_BRANCH) {    ins_debug_instr_prologue (op, 0, 0);    if (backward_jump) {    ia32_call_c_function(branch_check_threads_etc);    }    add_to_program(0xe9); -  ret=DO_NOT_WARN( (INT32) PIKE_PC ); +  ret=(INT32) PIKE_PC;    PUSH_INT(0);    }      #ifdef OPCODE_INLINE_BRANCH    else {    ia32_call_c_function (instrs[op - F_OFFSET].address);    add_to_program (0x85); /* test %eax, %eax */    add_to_program (0xc0);    if (backward_jump) {    add_to_program (0x74); /* jz rel8 */    add_to_program (5 + 1 + 4);    ia32_call_c_function (branch_check_threads_etc); /* 5 bytes */    add_to_program (0xe9); /* jmp rel32 */ -  ret = DO_NOT_WARN ((INT32) PIKE_PC); +  ret = (INT32) PIKE_PC;    PUSH_INT (0); /* 4 bytes */    }    else {    if ((cpu_vendor == PIKE_CPU_VENDOR_AMD) &&    (op == F_LOOP || op == F_FOREACH)) {    /* Slows down Intel PIII by 7%, speeds up Athlon XP by 22%. :P */    add_to_program (0x3e); /* Branch taken hint. */    }    add_to_program (0x0f); /* jnz rel32 */    add_to_program (0x85); -  ret = DO_NOT_WARN ((INT32) PIKE_PC); +  ret = (INT32) PIKE_PC;    PUSH_INT (0);    }    }   #endif       return ret;   }      INT32 ia32_ins_f_jump (unsigned int op, int backward_jump)   {
pike.git/src/code/ia32.c:1183:   void ia32_update_f_jump(INT32 offset, INT32 to_offset)   {    upd_pointer(offset, to_offset - offset - 4);   }      INT32 ia32_read_f_jump(INT32 offset)   {    return read_pointer(offset) + offset + 4;   }    - #define addstr(s, l) low_my_binary_strcat((s), (l), buf) + #define addstr(s, l) buffer_memcpy(buf, (s), (l))   #define adddata2(s,l) addstr((char *)(s),(l) * sizeof((s)[0]));    - void ia32_encode_program(struct program *p, struct dynamic_buffer_s *buf) + void ia32_encode_program(struct program *p, struct byte_buffer *buf)   {    size_t prev = 0, rel;    /* De-relocate the program... */    for (rel = 0; rel < p->num_relocations; rel++)    {    size_t off = p->relocations[rel];    INT32 opcode;   #ifdef PIKE_DEBUG    if (off < prev) {    Pike_fatal("Relocations in bad order!\n");
pike.git/src/code/ia32.c:1296: Inside #if 0
   cpu_info.feature_flags_edx,    cpu_info.clflush_size);   #endif /* 0 */       if (cpu_info.feature_flags_edx & 0x00080000) {    /* CLFLUSH present. */    /* fprintf (stderr, "Enabling clflush, size %d\n", cpu_info.clflush_size); */    ia32_clflush_size = cpu_info.clflush_size * 8;    }    } -  - #ifdef OPCODE_INLINE_RETURN -  instrs[F_CATCH - F_OFFSET].address = inter_return_opcode_F_CATCH; - #endif +    }