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); } }
enum eval_result_type compile_bytecodes (struct agent_expr *aexpr) { int pc = 0; int done = 0; unsigned char op, next_op; int arg; /* This is only used to build 64-bit value for constants. */ ULONGEST top; struct bytecode_address *aentry, *aentry2; #define UNHANDLED \ do \ { \ ax_debug ("Cannot compile op 0x%x\n", op); \ return expr_eval_unhandled_opcode; \ } while (0) if (aexpr->length == 0) { ax_debug ("empty agent expression\n"); return expr_eval_empty_expression; } bytecode_address_table = NULL; while (!done) { op = aexpr->bytes[pc]; ax_debug ("About to compile op 0x%x, pc=%d\n", op, pc); /* Record the compiled-code address of the bytecode, for use by jump instructions. */ aentry = XNEW (struct bytecode_address); aentry->pc = pc; aentry->address = current_insn_ptr; aentry->goto_pc = -1; aentry->from_offset = aentry->from_size = 0; aentry->next = bytecode_address_table; bytecode_address_table = aentry; ++pc; emit_error = 0; switch (op) { case gdb_agent_op_add: emit_add (); break; case gdb_agent_op_sub: emit_sub (); break; case gdb_agent_op_mul: emit_mul (); break; case gdb_agent_op_div_signed: UNHANDLED; break; case gdb_agent_op_div_unsigned: UNHANDLED; break; case gdb_agent_op_rem_signed: UNHANDLED; break; case gdb_agent_op_rem_unsigned: UNHANDLED; break; case gdb_agent_op_lsh: emit_lsh (); break; case gdb_agent_op_rsh_signed: emit_rsh_signed (); break; case gdb_agent_op_rsh_unsigned: emit_rsh_unsigned (); break; case gdb_agent_op_trace: UNHANDLED; break; case gdb_agent_op_trace_quick: UNHANDLED; break; case gdb_agent_op_log_not: emit_log_not (); break; case gdb_agent_op_bit_and: emit_bit_and (); break; case gdb_agent_op_bit_or: emit_bit_or (); break; case gdb_agent_op_bit_xor: emit_bit_xor (); break; case gdb_agent_op_bit_not: emit_bit_not (); break; case gdb_agent_op_equal: next_op = aexpr->bytes[pc]; if (next_op == gdb_agent_op_if_goto && !is_goto_target (aexpr, pc) && target_emit_ops ()->emit_eq_goto) { ax_debug ("Combining equal & if_goto"); pc += 1; aentry->pc = pc; arg = aexpr->bytes[pc++]; arg = (arg << 8) + aexpr->bytes[pc++]; aentry->goto_pc = arg; emit_eq_goto (&(aentry->from_offset), &(aentry->from_size)); } else if (next_op == gdb_agent_op_log_not && (aexpr->bytes[pc + 1] == gdb_agent_op_if_goto) && !is_goto_target (aexpr, pc + 1) && target_emit_ops ()->emit_ne_goto) { ax_debug ("Combining equal & log_not & if_goto"); pc += 2; aentry->pc = pc; arg = aexpr->bytes[pc++]; arg = (arg << 8) + aexpr->bytes[pc++]; aentry->goto_pc = arg; emit_ne_goto (&(aentry->from_offset), &(aentry->from_size)); } else emit_equal (); break; case gdb_agent_op_less_signed: next_op = aexpr->bytes[pc]; if (next_op == gdb_agent_op_if_goto && !is_goto_target (aexpr, pc)) { ax_debug ("Combining less_signed & if_goto"); pc += 1; aentry->pc = pc; arg = aexpr->bytes[pc++]; arg = (arg << 8) + aexpr->bytes[pc++]; aentry->goto_pc = arg; emit_lt_goto (&(aentry->from_offset), &(aentry->from_size)); } else if (next_op == gdb_agent_op_log_not && !is_goto_target (aexpr, pc) && (aexpr->bytes[pc + 1] == gdb_agent_op_if_goto) && !is_goto_target (aexpr, pc + 1)) { ax_debug ("Combining less_signed & log_not & if_goto"); pc += 2; aentry->pc = pc; arg = aexpr->bytes[pc++]; arg = (arg << 8) + aexpr->bytes[pc++]; aentry->goto_pc = arg; emit_ge_goto (&(aentry->from_offset), &(aentry->from_size)); } else emit_less_signed (); break; case gdb_agent_op_less_unsigned: emit_less_unsigned (); break; case gdb_agent_op_ext: arg = aexpr->bytes[pc++]; if (arg < (sizeof (LONGEST) * 8)) emit_ext (arg); break; case gdb_agent_op_ref8: emit_ref (1); break; case gdb_agent_op_ref16: emit_ref (2); break; case gdb_agent_op_ref32: emit_ref (4); break; case gdb_agent_op_ref64: emit_ref (8); break; case gdb_agent_op_if_goto: arg = aexpr->bytes[pc++]; arg = (arg << 8) + aexpr->bytes[pc++]; aentry->goto_pc = arg; emit_if_goto (&(aentry->from_offset), &(aentry->from_size)); break; case gdb_agent_op_goto: arg = aexpr->bytes[pc++]; arg = (arg << 8) + aexpr->bytes[pc++]; aentry->goto_pc = arg; emit_goto (&(aentry->from_offset), &(aentry->from_size)); break; case gdb_agent_op_const8: emit_stack_flush (); top = aexpr->bytes[pc++]; emit_const (top); break; case gdb_agent_op_const16: emit_stack_flush (); top = aexpr->bytes[pc++]; top = (top << 8) + aexpr->bytes[pc++]; emit_const (top); break; case gdb_agent_op_const32: emit_stack_flush (); top = aexpr->bytes[pc++]; top = (top << 8) + aexpr->bytes[pc++]; top = (top << 8) + aexpr->bytes[pc++]; top = (top << 8) + aexpr->bytes[pc++]; emit_const (top); break; case gdb_agent_op_const64: emit_stack_flush (); top = aexpr->bytes[pc++]; top = (top << 8) + aexpr->bytes[pc++]; top = (top << 8) + aexpr->bytes[pc++]; top = (top << 8) + aexpr->bytes[pc++]; top = (top << 8) + aexpr->bytes[pc++]; top = (top << 8) + aexpr->bytes[pc++]; top = (top << 8) + aexpr->bytes[pc++]; top = (top << 8) + aexpr->bytes[pc++]; emit_const (top); break; case gdb_agent_op_reg: emit_stack_flush (); arg = aexpr->bytes[pc++]; arg = (arg << 8) + aexpr->bytes[pc++]; emit_reg (arg); break; case gdb_agent_op_end: ax_debug ("At end of expression\n"); /* Assume there is one stack element left, and that it is cached in "top" where emit_epilogue can get to it. */ emit_stack_adjust (1); done = 1; break; case gdb_agent_op_dup: /* In our design, dup is equivalent to stack flushing. */ emit_stack_flush (); break; case gdb_agent_op_pop: emit_pop (); break; case gdb_agent_op_zero_ext: arg = aexpr->bytes[pc++]; if (arg < (sizeof (LONGEST) * 8)) emit_zero_ext (arg); break; case gdb_agent_op_swap: next_op = aexpr->bytes[pc]; /* Detect greater-than comparison sequences. */ if (next_op == gdb_agent_op_less_signed && !is_goto_target (aexpr, pc) && (aexpr->bytes[pc + 1] == gdb_agent_op_if_goto) && !is_goto_target (aexpr, pc + 1)) { ax_debug ("Combining swap & less_signed & if_goto"); pc += 2; aentry->pc = pc; arg = aexpr->bytes[pc++]; arg = (arg << 8) + aexpr->bytes[pc++]; aentry->goto_pc = arg; emit_gt_goto (&(aentry->from_offset), &(aentry->from_size)); } else if (next_op == gdb_agent_op_less_signed && !is_goto_target (aexpr, pc) && (aexpr->bytes[pc + 1] == gdb_agent_op_log_not) && !is_goto_target (aexpr, pc + 1) && (aexpr->bytes[pc + 2] == gdb_agent_op_if_goto) && !is_goto_target (aexpr, pc + 2)) { ax_debug ("Combining swap & less_signed & log_not & if_goto"); pc += 3; aentry->pc = pc; arg = aexpr->bytes[pc++]; arg = (arg << 8) + aexpr->bytes[pc++]; aentry->goto_pc = arg; emit_le_goto (&(aentry->from_offset), &(aentry->from_size)); } else emit_swap (); break; case gdb_agent_op_getv: emit_stack_flush (); arg = aexpr->bytes[pc++]; arg = (arg << 8) + aexpr->bytes[pc++]; emit_int_call_1 (get_get_tsv_func_addr (), arg); break; case gdb_agent_op_setv: arg = aexpr->bytes[pc++]; arg = (arg << 8) + aexpr->bytes[pc++]; emit_void_call_2 (get_set_tsv_func_addr (), arg); break; case gdb_agent_op_tracev: UNHANDLED; break; /* GDB never (currently) generates any of these ops. */ case gdb_agent_op_float: case gdb_agent_op_ref_float: case gdb_agent_op_ref_double: case gdb_agent_op_ref_long_double: case gdb_agent_op_l_to_d: case gdb_agent_op_d_to_l: case gdb_agent_op_trace16: UNHANDLED; break; default: ax_debug ("Agent expression op 0x%x not recognized\n", op); /* Don't struggle on, things will just get worse. */ return expr_eval_unrecognized_opcode; } /* This catches errors that occur in target-specific code emission. */ if (emit_error) { ax_debug ("Error %d while emitting code for %s\n", emit_error, gdb_agent_op_name (op)); return expr_eval_unhandled_opcode; } ax_debug ("Op %s compiled\n", gdb_agent_op_name (op)); } /* Now fill in real addresses as goto destinations. */ for (aentry = bytecode_address_table; aentry; aentry = aentry->next) { int written = 0; if (aentry->goto_pc < 0) continue; /* Find the location that we are going to, and call back into target-specific code to write the actual address or displacement. */ for (aentry2 = bytecode_address_table; aentry2; aentry2 = aentry2->next) { if (aentry2->pc == aentry->goto_pc) { ax_debug ("Want to jump from %s to %s\n", paddress (aentry->address), paddress (aentry2->address)); write_goto_address (aentry->address + aentry->from_offset, aentry2->address, aentry->from_size); written = 1; break; } } /* Error out if we didn't find a destination. */ if (!written) { ax_debug ("Destination of goto %d not found\n", aentry->goto_pc); return expr_eval_invalid_goto; } } return expr_eval_no_error; }