📄 i386-asm.c
字号:
op = &operands[i]; str = op->constraint; str = skip_constraint_modifiers(str); if (isnum(*str) || *str == '[') { /* this is a reference to another constraint */ k = find_constraint(operands, nb_operands, str, NULL); if ((unsigned)k >= i || i < nb_outputs) error("invalid reference in constraint %d ('%s')", i, str); op->ref_index = k; if (operands[k].input_index >= 0) error("cannot reference twice the same operand"); operands[k].input_index = i; op->priority = 5; } else { op->priority = constraint_priority(str); } } /* sort operands according to their priority */ for(i=0;i<nb_operands;i++) sorted_op[i] = i; for(i=0;i<nb_operands - 1;i++) { for(j=i+1;j<nb_operands;j++) { p1 = operands[sorted_op[i]].priority; p2 = operands[sorted_op[j]].priority; if (p2 < p1) { tmp = sorted_op[i]; sorted_op[i] = sorted_op[j]; sorted_op[j] = tmp; } } } for(i = 0;i < NB_ASM_REGS; i++) { if (clobber_regs[i]) regs_allocated[i] = REG_IN_MASK | REG_OUT_MASK; else regs_allocated[i] = 0; } /* esp cannot be used */ regs_allocated[4] = REG_IN_MASK | REG_OUT_MASK; /* ebp cannot be used yet */ regs_allocated[5] = REG_IN_MASK | REG_OUT_MASK; /* allocate registers and generate corresponding asm moves */ for(i=0;i<nb_operands;i++) { j = sorted_op[i]; op = &operands[j]; str = op->constraint; /* no need to allocate references */ if (op->ref_index >= 0) continue; /* select if register is used for output, input or both */ if (op->input_index >= 0) { reg_mask = REG_IN_MASK | REG_OUT_MASK; } else if (j < nb_outputs) { reg_mask = REG_OUT_MASK; } else { reg_mask = REG_IN_MASK; } try_next: c = *str++; switch(c) { case '=': goto try_next; case '+': op->is_rw = 1; /* FALL THRU */ case '&': if (j >= nb_outputs) error("'%c' modifier can only be applied to outputs", c); reg_mask = REG_IN_MASK | REG_OUT_MASK; goto try_next; case 'A': /* allocate both eax and edx */ if (is_reg_allocated(TREG_EAX) || is_reg_allocated(TREG_EDX)) goto try_next; op->is_llong = 1; op->reg = TREG_EAX; regs_allocated[TREG_EAX] |= reg_mask; regs_allocated[TREG_EDX] |= reg_mask; break; case 'a': reg = TREG_EAX; goto alloc_reg; case 'b': reg = 3; goto alloc_reg; case 'c': reg = TREG_ECX; goto alloc_reg; case 'd': reg = TREG_EDX; goto alloc_reg; case 'S': reg = 6; goto alloc_reg; case 'D': reg = 7; alloc_reg: if (is_reg_allocated(reg)) goto try_next; goto reg_found; case 'q': /* eax, ebx, ecx or edx */ for(reg = 0; reg < 4; reg++) { if (!is_reg_allocated(reg)) goto reg_found; } goto try_next; case 'r': /* any general register */ for(reg = 0; reg < 8; reg++) { if (!is_reg_allocated(reg)) goto reg_found; } goto try_next; reg_found: /* now we can reload in the register */ op->is_llong = 0; op->reg = reg; regs_allocated[reg] |= reg_mask; break; case 'i': if (!((op->vt->r & (VT_VALMASK | VT_LVAL)) == VT_CONST)) goto try_next; break; case 'I': case 'N': case 'M': if (!((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST)) goto try_next; break; case 'm': case 'g': /* nothing special to do because the operand is already in memory, except if the pointer itself is stored in a memory variable (VT_LLOCAL case) */ /* XXX: fix constant case */ /* if it is a reference to a memory zone, it must lie in a register, so we reserve the register in the input registers and a load will be generated later */ if (j < nb_outputs || c == 'm') { if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) { /* any general register */ for(reg = 0; reg < 8; reg++) { if (!(regs_allocated[reg] & REG_IN_MASK)) goto reg_found1; } goto try_next; reg_found1: /* now we can reload in the register */ regs_allocated[reg] |= REG_IN_MASK; op->reg = reg; op->is_memory = 1; } } break; default: error("asm constraint %d ('%s') could not be satisfied", j, op->constraint); break; } /* if a reference is present for that operand, we assign it too */ if (op->input_index >= 0) { operands[op->input_index].reg = op->reg; operands[op->input_index].is_llong = op->is_llong; } } /* compute out_reg. It is used to store outputs registers to memory locations references by pointers (VT_LLOCAL case) */ *pout_reg = -1; for(i=0;i<nb_operands;i++) { op = &operands[i]; if (op->reg >= 0 && (op->vt->r & VT_VALMASK) == VT_LLOCAL && !op->is_memory) { for(reg = 0; reg < 8; reg++) { if (!(regs_allocated[reg] & REG_OUT_MASK)) goto reg_found2; } error("could not find free output register for reloading"); reg_found2: *pout_reg = reg; break; } } /* print sorted constraints */#ifdef ASM_DEBUG for(i=0;i<nb_operands;i++) { j = sorted_op[i]; op = &operands[j]; printf("%%%d [%s]: \"%s\" r=0x%04x reg=%d\n", j, op->id ? get_tok_str(op->id, NULL) : "", op->constraint, op->vt->r, op->reg); } if (*pout_reg >= 0) printf("out_reg=%d\n", *pout_reg);#endif}static void subst_asm_operand(CString *add_str, SValue *sv, int modifier){ int r, reg, size, val; char buf[64]; r = sv->r; if ((r & VT_VALMASK) == VT_CONST) { if (!(r & VT_LVAL) && modifier != 'c' && modifier != 'n') cstr_ccat(add_str, '$'); if (r & VT_SYM) { cstr_cat(add_str, get_tok_str(sv->sym->v, NULL)); if (sv->c.i != 0) { cstr_ccat(add_str, '+'); } else { return; } } val = sv->c.i; if (modifier == 'n') val = -val; snprintf(buf, sizeof(buf), "%d", sv->c.i); cstr_cat(add_str, buf); } else if ((r & VT_VALMASK) == VT_LOCAL) { snprintf(buf, sizeof(buf), "%d(%%ebp)", sv->c.i); cstr_cat(add_str, buf); } else if (r & VT_LVAL) { reg = r & VT_VALMASK; if (reg >= VT_CONST) error("internal compiler error"); snprintf(buf, sizeof(buf), "(%%%s)", get_tok_str(TOK_ASM_eax + reg, NULL)); cstr_cat(add_str, buf); } else { /* register case */ reg = r & VT_VALMASK; if (reg >= VT_CONST) error("internal compiler error"); /* choose register operand size */ if ((sv->type.t & VT_BTYPE) == VT_BYTE) size = 1; else if ((sv->type.t & VT_BTYPE) == VT_SHORT) size = 2; else size = 4; if (size == 1 && reg >= 4) size = 4; if (modifier == 'b') { if (reg >= 4) error("cannot use byte register"); size = 1; } else if (modifier == 'h') { if (reg >= 4) error("cannot use byte register"); size = -1; } else if (modifier == 'w') { size = 2; } switch(size) { case -1: reg = TOK_ASM_ah + reg; break; case 1: reg = TOK_ASM_al + reg; break; case 2: reg = TOK_ASM_ax + reg; break; default: reg = TOK_ASM_eax + reg; break; } snprintf(buf, sizeof(buf), "%%%s", get_tok_str(reg, NULL)); cstr_cat(add_str, buf); }}/* generate prolog and epilog code for asm statment */static void asm_gen_code(ASMOperand *operands, int nb_operands, int nb_outputs, int is_output, uint8_t *clobber_regs, int out_reg){ uint8_t regs_allocated[NB_ASM_REGS]; ASMOperand *op; int i, reg; static uint8_t reg_saved[NB_SAVED_REGS] = { 3, 6, 7 }; /* mark all used registers */ memcpy(regs_allocated, clobber_regs, sizeof(regs_allocated)); for(i = 0; i < nb_operands;i++) { op = &operands[i]; if (op->reg >= 0) regs_allocated[op->reg] = 1; } if (!is_output) { /* generate reg save code */ for(i = 0; i < NB_SAVED_REGS; i++) { reg = reg_saved[i]; if (regs_allocated[reg]) g(0x50 + reg); } /* generate load code */ for(i = 0; i < nb_operands; i++) { op = &operands[i]; if (op->reg >= 0) { if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory) { /* memory reference case (for both input and output cases) */ SValue sv; sv = *op->vt; sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL; load(op->reg, &sv); } else if (i >= nb_outputs || op->is_rw) { /* load value in register */ load(op->reg, op->vt); if (op->is_llong) { SValue sv; sv = *op->vt; sv.c.ul += 4; load(TREG_EDX, &sv); } } } } } else { /* generate save code */ for(i = 0 ; i < nb_outputs; i++) { op = &operands[i]; if (op->reg >= 0) { if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) { if (!op->is_memory) { SValue sv; sv = *op->vt; sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL; load(out_reg, &sv); sv.r = (sv.r & ~VT_VALMASK) | out_reg; store(op->reg, &sv); } } else { store(op->reg, op->vt); if (op->is_llong) { SValue sv; sv = *op->vt; sv.c.ul += 4; store(TREG_EDX, &sv); } } } } /* generate reg restore code */ for(i = NB_SAVED_REGS - 1; i >= 0; i--) { reg = reg_saved[i]; if (regs_allocated[reg]) g(0x58 + reg); } }}static void asm_clobber(uint8_t *clobber_regs, const char *str){ int reg; TokenSym *ts; if (!strcmp(str, "memory") || !strcmp(str, "cc")) return; ts = tok_alloc(str, strlen(str)); reg = ts->tok; if (reg >= TOK_ASM_eax && reg <= TOK_ASM_edi) { reg -= TOK_ASM_eax; } else if (reg >= TOK_ASM_ax && reg <= TOK_ASM_di) { reg -= TOK_ASM_ax; } else { error("invalid clobber register '%s'", str); } clobber_regs[reg] = 1;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -