static void emit_expr(Node *node) { SAVE; switch (node->type) { case AST_LITERAL: emit_literal(node); return; case AST_STRING: emit_literal_string(node); return; case AST_LVAR: emit_lvar(node); return; case AST_GVAR: emit_gvar(node); return; case AST_FUNCALL: case AST_FUNCPTR_CALL: emit_func_call(node); return; case AST_DECL: emit_decl(node); return; case AST_CONV: emit_conv(node); return; case AST_ADDR: emit_addr(node->operand); return; case AST_DEREF: emit_deref(node); return; case AST_IF: case AST_TERNARY: emit_ternary(node); return; case AST_FOR: emit_for(node); return; case AST_WHILE: emit_while(node); return; case AST_DO: emit_do(node); return; case AST_SWITCH: emit_switch(node); return; case AST_CASE: emit_case(node); return; case AST_DEFAULT: emit_default(node); return; case AST_GOTO: emit_goto(node); return; case AST_LABEL: if (node->newlabel) emit_label(node->newlabel); return; case AST_RETURN: emit_return(node); return; case AST_BREAK: emit_break(node); return; case AST_CONTINUE: emit_continue(node); return; case AST_COMPOUND_STMT: emit_compound_stmt(node); return; case AST_STRUCT_REF: emit_load_struct_ref(node->struc, node->ctype, 0); return; case AST_VA_START: emit_va_start(node); return; case AST_VA_ARG: emit_va_arg(node); return; case OP_UMINUS: emit_uminus(node); return; case OP_PRE_INC: emit_pre_inc_dec(node, "add"); return; case OP_PRE_DEC: emit_pre_inc_dec(node, "sub"); return; case OP_POST_INC: emit_post_inc_dec(node, "add"); return; case OP_POST_DEC: emit_post_inc_dec(node, "sub"); return; case '!': emit_lognot(node); return; case '&': emit_bitand(node); return; case '|': emit_bitor(node); return; case '~': emit_bitnot(node); return; case OP_LOGAND: emit_logand(node); return; case OP_LOGOR: emit_logor(node); return; case OP_CAST: emit_cast(node); return; case ',': emit_comma(node); return; case '=': emit_assign(node); return; case OP_LABEL_ADDR: emit_label_addr(node); return; case AST_COMPUTED_GOTO: emit_computed_goto(node); return; default: emit_binop(node); } }
static void emit_expr(Node *node) { SAVE; maybe_print_source_loc(node); switch (node->kind) { case AST_LITERAL: emit_literal(node); return; case AST_LVAR: emit_lvar(node); return; case AST_GVAR: emit_gvar(node); return; case AST_FUNCDESG: return; case AST_FUNCALL: if (maybe_emit_builtin(node)) return; // fall through case AST_FUNCPTR_CALL: emit_func_call(node); return; case AST_DECL: emit_decl(node); return; case AST_CONV: emit_conv(node); return; case AST_ADDR: emit_addr(node->operand); return; case AST_DEREF: emit_deref(node); return; case AST_IF: case AST_TERNARY: emit_ternary(node); return; case AST_GOTO: emit_goto(node); return; case AST_LABEL: if (node->newlabel) emit_label(node->newlabel); return; case AST_RETURN: emit_return(node); return; case AST_COMPOUND_STMT: emit_compound_stmt(node); return; case AST_STRUCT_REF: emit_load_struct_ref(node->struc, node->ty, 0); return; case OP_PRE_INC: emit_pre_inc_dec(node, "add"); return; case OP_PRE_DEC: emit_pre_inc_dec(node, "sub"); return; case OP_POST_INC: emit_post_inc_dec(node, "add"); return; case OP_POST_DEC: emit_post_inc_dec(node, "sub"); return; case '!': emit_lognot(node); return; case '&': emit_bitand(node); return; case '|': emit_bitor(node); return; case '~': emit_bitnot(node); return; case OP_LOGAND: emit_logand(node); return; case OP_LOGOR: emit_logor(node); return; case OP_CAST: emit_cast(node); return; case ',': emit_comma(node); return; case '=': emit_assign(node); return; case OP_LABEL_ADDR: emit_label_addr(node); return; case AST_COMPUTED_GOTO: emit_computed_goto(node); return; default: emit_binop(node); } }
static void emit_copy_struct(Node *left, Node *right) { push("rcx"); push("r11"); emit_addr(right); emit("mov %%rax, %%rcx"); emit_addr(left); int i = 0; for (; i < left->ctype->size; i += 8) { emit("movq %d(%%rcx), %%r11", i); emit("movq %%r11, %d(%%rax)", i); } for (; i < left->ctype->size; i += 4) { emit("movl %d(%%rcx), %%r11", i); emit("movl %%r11, %d(%%rax)", i); } for (; i < left->ctype->size; i++) { emit("movb %d(%%rcx), %%r11", i); emit("movb %%r11, %d(%%rax)", i); } pop("r11"); pop("rcx"); }
static void emit_copy_struct(Node *left, Node *right) { push("rcx"); push("r11"); emit_addr(right); emit("mov #rax, #rcx"); emit_addr(left); int i = 0; for (; i < left->ty->size; i += 8) { emit("movq %d(#rcx), #r11", i); emit("movq #r11, %d(#rax)", i); } for (; i < left->ty->size; i += 4) { emit("movl %d(#rcx), #r11", i); emit("movl #r11, %d(#rax)", i); } for (; i < left->ty->size; i++) { emit("movb %d(#rcx), #r11", i); emit("movb #r11, %d(#rax)", i); } pop("r11"); pop("rcx"); }
static void emit_addr(Node *node) { switch (node->type) { case AST_LVAR: ensure_lvar_init(node); emit("lea %d(%%rbp), %%rax", node->loff); break; case AST_GVAR: emit("lea %s(%%rip), %%rax", node->glabel); break; case AST_DEREF: emit_expr(node->operand); break; case AST_STRUCT_REF: emit_addr(node->struc); emit("add $%d, %%rax", node->ctype->offset); break; default: error("internal error: %s", a2s(node)); } }
static int emit_args(Vector *vals) { SAVE; int r = 0; for (int i = 0; i < vec_len(vals); i++) { Node *v = vec_get(vals, i); if (v->ty->kind == KIND_STRUCT) { emit_addr(v); r += push_struct(v->ty->size); } else if (is_flotype(v->ty)) { emit_expr(v); push_xmm(0); r += 8; } else { emit_expr(v); push("rax"); r += 8; } } return r; }
static void emit_addr(Node *node) { switch (node->kind) { case AST_LVAR: ensure_lvar_init(node); emit("lea %d(#rbp), #rax", node->loff); break; case AST_GVAR: emit("lea %s(#rip), #rax", node->glabel); break; case AST_DEREF: emit_expr(node->operand); break; case AST_STRUCT_REF: emit_addr(node->struc); emit("add $%d, #rax", node->ty->offset); break; case AST_FUNCDESG: emit("lea %s(#rip), #rax", node->fname); break; default: error("internal error: %s", node2s(node)); } }
void emit_trip(generic_op op, oprtype *opr, bool val_output, unsigned char use_reg) { unsigned char base_reg, temp_reg; int4 offset, literal; triple *ct; if (opr->oprclass == TRIP_REF) { ct = opr->oprval.tref; if (ct->destination.oprclass) { opr = &ct->destination; } /* else lit or error */ } switch (cg_phase) { case CGP_ADDR_OPT: case CGP_APPROX_ADDR: switch (opr->oprclass) { case TRIP_REF: assert(ct->destination.oprclass == 0); assert(val_output); switch (ct->opcode) { case OC_LIT: if (run_time) { int4 pc_value_idx; switch (op) { case LOAD_ADDRESS: temp_reg = use_reg; break; case PUSH: case PUSH_ADDRESS: temp_reg = I386_REG_ECX; break; default: rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_UNIMPLOP); break; } pc_value_idx = code_idx + 5; code_idx += 1 + SIZEOF(int4) + 1; emit_addr(0, (int4)ct->operand[0].oprval.mlit->rt_addr, &offset); offset -= pc_value_idx; force_32 = 1; emit_op_base_offset(op, temp_reg, offset, temp_reg); force_32 = 0; } else { emit_op_alit(op, use_reg); code_idx += SIZEOF(int4); } if (cg_phase == CGP_APPROX_ADDR) txtrel_cnt++; break; case OC_CDLIT: if (cg_phase == CGP_APPROX_ADDR) define_symbol(GTM_LITERALS, ct->operand[0].oprval.cdlt, 0); emit_op_alit(op, use_reg); code_idx += SIZEOF(int4); break; case OC_ILIT: literal = ct->operand[0].oprval.ilit; switch(op) { case COMPARE: /* 1byte(opcode) + 1byte(ModR/M) + 4byte(literal) */ code_idx += 2 + SIZEOF(int4); break; case LOAD: code_idx += 1 + SIZEOF(int4); break; case PUSH: if (literal >= -128 && literal <= 127) code_idx += 2; else code_idx += 1 + SIZEOF(int4); break; default: rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_UNIMPLOP); break; } break; default: assertpro(FALSE && ct->opcode); break; } break; case TINT_REF: case TVAL_REF: assert(val_output); offset = sa_temps_offset[opr->oprclass]; offset -= (sa_temps[opr->oprclass] - opr->oprval.temp) * sa_class_sizes[opr->oprclass]; assertpro((0 <= offset) && (65535 >= offset)); emit_op_base_offset(op, I386_REG_EDI, offset, use_reg); break; case TCAD_REF: case TVAD_REF: case TVAR_REF: offset = sa_temps_offset[opr->oprclass]; offset -= (sa_temps[opr->oprclass] - opr->oprval.temp) * sa_class_sizes[opr->oprclass]; assertpro((0 <= offset) && (65535 >= offset)); if (opr->oprclass == TVAR_REF) base_reg = I386_REG_ESI; else base_reg = I386_REG_EDI; switch (op) { case JUMP: if (val_output) { code_idx++; emit_base_offset(I386_REG_EAX, base_reg, offset); } code_idx++; if (val_output) emit_base_offset(I386_INS_JMP_Ev, I386_REG_EAX, 0); else emit_base_offset(I386_INS_JMP_Ev, base_reg, offset); break; case LOAD_ADDRESS: code_idx++; emit_base_offset(use_reg, base_reg, offset); if (opr->oprclass == TVAR_REF) { code_idx++; emit_base_offset(use_reg, use_reg, offsetof(ht_ent_mname, value)); } break; case PUSH: if (!val_output) { code_idx++; emit_base_offset(I386_INS_PUSH_Ev, base_reg, offset); } else { code_idx++; emit_base_offset(I386_REG_ECX, base_reg, offset); code_idx++; emit_base_offset(I386_INS_PUSH_Ev, I386_REG_ECX, 0); } break; case PUSH_ADDRESS: if (val_output) { if (opr->oprclass == TVAR_REF) { code_idx++; emit_base_offset(use_reg, base_reg, offset); code_idx++; emit_base_offset(I386_INS_PUSH_Ev, use_reg, offsetof(ht_ent_mname, value)); } else { code_idx++; emit_base_offset(I386_INS_PUSH_Ev, base_reg, offset); } } else { code_idx++; emit_base_offset(I386_REG_ECX, base_reg, offset); code_idx++; } break; case STORE: if (val_output) { if (use_reg == I386_REG_EAX) temp_reg = I386_REG_EDX; else temp_reg = I386_REG_EAX; code_idx++; emit_base_offset(temp_reg, base_reg, offset); } code_idx++; if (val_output) emit_base_offset(use_reg, temp_reg, 0); else emit_base_offset(use_reg, base_reg, offset); break; default: rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_UNIMPLOP); break; } break; } break; case CGP_MACHINE: switch (opr->oprclass) { case TRIP_REF: assert(ct->destination.oprclass == 0); assert(val_output); switch (ct->opcode) { case OC_LIT: assert(ct->operand[0].oprclass == MLIT_REF); if (run_time) { int4 pc_value_idx; switch(op) { case LOAD_ADDRESS: temp_reg = use_reg; break; case PUSH: case PUSH_ADDRESS: temp_reg = I386_REG_ECX; break; default: rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_UNIMPLOP); break; } code_buf[code_idx++] = I386_INS_CALL_Jv; *((int4 *)&code_buf[code_idx]) = 0; code_idx += SIZEOF(int4); pc_value_idx = code_idx; code_buf[code_idx++] = I386_INS_POP_eAX + temp_reg; emit_addr(0, (int4)ct->operand[0].oprval.mlit->rt_addr, &offset); offset -= pc_value_idx; force_32 = 1; emit_op_base_offset(op, temp_reg, offset, temp_reg); force_32 = 0; } else { emit_op_alit(op, use_reg); emit_addr(code_reference + (code_idx * SIZEOF(unsigned char)), (int4)ct->operand[0].oprval.mlit->rt_addr, (int4 *)&code_buf[code_idx]); code_idx += SIZEOF(int4); } break; case OC_CDLIT: emit_op_alit(op, use_reg); emit_reference(code_reference + (code_idx * SIZEOF(unsigned char)), ct->operand[0].oprval.cdlt, (uint4 *)&code_buf[code_idx]); code_idx += SIZEOF(int4); break; case OC_ILIT: literal = ct->operand[0].oprval.ilit; switch (op) { case COMPARE: /* cmpl $literal,use_reg - 1byte(opcode) + 1byte(ModR/M) + 4byte(literal) */ code_buf[code_idx++] = I386_INS_Grp1_Ev_Iv_Prefix; modrm_byte.modrm.reg_opcode = I386_INS_CMP__; modrm_byte.modrm.mod = I386_MOD32_REGISTER; modrm_byte.modrm.r_m = use_reg; code_buf[code_idx++] = modrm_byte.byte; *((int4 *)&code_buf[code_idx]) = literal; code_idx += SIZEOF(int4); break; case LOAD: code_buf[code_idx++] = I386_INS_MOV_eAX + use_reg; *((int4 *)&code_buf[code_idx]) = literal; code_idx += SIZEOF(int4); break; case PUSH: if (literal >= -128 && literal <= 127) { code_buf[code_idx++] = I386_INS_PUSH_Ib; code_buf[code_idx++] = literal & 0xff; } else { code_buf[code_idx++] = I386_INS_PUSH_Iv; *((int4 *)&code_buf[code_idx]) = literal; code_idx += SIZEOF(int4); } break; default: rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_UNIMPLOP); break; } break; default: assertpro(FALSE && ct->opcode); break; } break; case TINT_REF: case TVAL_REF: assert(val_output); offset = sa_temps_offset[opr->oprclass]; offset -= (sa_temps[opr->oprclass] - opr->oprval.temp) * sa_class_sizes[opr->oprclass]; assertpro((0 <= offset) && (65535 >= offset)); emit_op_base_offset(op, I386_REG_EDI, offset, use_reg); break; case TCAD_REF: case TVAD_REF: case TVAR_REF: offset = sa_temps_offset[opr->oprclass]; offset -= (sa_temps[opr->oprclass] - opr->oprval.temp) * sa_class_sizes[opr->oprclass]; assertpro((0 <= offset) && (65535 >= offset)); if (opr->oprclass == TVAR_REF) base_reg = I386_REG_ESI; else base_reg = I386_REG_EDI; switch (op) { case JUMP: assert(use_reg == 0); if (val_output) { code_buf[code_idx++] = I386_INS_MOV_Gv_Ev; emit_base_offset(I386_REG_EAX, base_reg, offset); } code_buf[code_idx++] = I386_INS_Grp5_Prefix; if (val_output) emit_base_offset(I386_INS_JMP_Ev, I386_REG_EAX, 0); else emit_base_offset(I386_INS_JMP_Ev, base_reg, offset); break; case LOAD_ADDRESS: if (val_output) code_buf[code_idx++] = I386_INS_MOV_Gv_Ev; else code_buf[code_idx++] = I386_INS_LEA_Gv_M; emit_base_offset(use_reg, base_reg, offset); if (opr->oprclass == TVAR_REF) { code_buf[code_idx++] = I386_INS_MOV_Gv_Ev; emit_base_offset(use_reg, use_reg, offsetof(ht_ent_mname, value)); } break; case PUSH: if (val_output) { code_buf[code_idx++] = I386_INS_MOV_Gv_Ev; emit_base_offset(I386_REG_ECX, base_reg, offset); code_buf[code_idx++] = I386_INS_Grp5_Prefix; emit_base_offset(I386_INS_PUSH_Ev, I386_REG_ECX, 0); } else { code_buf[code_idx++] = I386_INS_Grp5_Prefix; emit_base_offset(I386_INS_PUSH_Ev, base_reg, offset); } break; case PUSH_ADDRESS: if (val_output) { if (opr->oprclass == TVAR_REF) { code_buf[code_idx++] = I386_INS_MOV_Gv_Ev; emit_base_offset(use_reg, base_reg, offset); code_buf[code_idx++] = I386_INS_Grp5_Prefix; emit_base_offset(I386_INS_PUSH_Ev, use_reg, offsetof(ht_ent_mname, value)); } else { code_buf[code_idx++] = I386_INS_Grp5_Prefix; emit_base_offset(I386_INS_PUSH_Ev, base_reg, offset); } } else { code_buf[code_idx++] = I386_INS_LEA_Gv_M; emit_base_offset(I386_REG_ECX, base_reg, offset); code_buf[code_idx++] = I386_INS_PUSH_eCX; } break; case STORE: if (val_output) { if (use_reg == I386_REG_EAX) temp_reg = I386_REG_EDX; else temp_reg = I386_REG_EAX; assert(temp_reg != use_reg); code_buf[code_idx++] = I386_INS_MOV_Gv_Ev; emit_base_offset(temp_reg, base_reg, offset); } code_buf[code_idx++] = I386_INS_MOV_Ev_Gv; if (val_output) emit_base_offset(use_reg, temp_reg, 0); else emit_base_offset(use_reg, base_reg, offset); break; default: rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_UNIMPLOP); break; } break; default: assertpro(FALSE && opr->oprclass); break; } break; default: assertpro(FALSE && cg_phase); break; } }