diff --git a/clients/drcachesim/CMakeLists.txt b/clients/drcachesim/CMakeLists.txt index d81f7ba8ea1..2903caaf549 100644 --- a/clients/drcachesim/CMakeLists.txt +++ b/clients/drcachesim/CMakeLists.txt @@ -168,6 +168,11 @@ add_exported_library(drmemtrace_func_view STATIC tools/func_view.cpp) add_exported_library(drmemtrace_invariant_checker STATIC tools/invariant_checker.cpp) add_exported_library(drmemtrace_schedule_stats STATIC tools/schedule_stats.cpp) add_exported_library(drmemtrace_schedule_file STATIC common/schedule_file.cpp) +add_exported_library(drmemtrace_access_region STATIC tools/access_region.cpp) +add_exported_library(drmemtrace_reuse_pattern STATIC tools/reuse_pattern.cpp) + +add_subdirectory(mirage) +target_link_libraries(drmemtrace_reuse_pattern mirage) target_link_libraries(drmemtrace_invariant_checker drdecode drmemtrace_schedule_file) @@ -213,6 +218,7 @@ if (BUILD_PT_POST_PROCESSOR) add_subdirectory(drpt2trace) endif (BUILD_PT_POST_PROCESSOR) + set(raw2trace_srcs tracer/raw2trace.cpp tracer/raw2trace_shared.cpp @@ -284,7 +290,7 @@ target_link_libraries(drmemtrace_launcher drmemtrace_simulator drmemtrace_reuse_ drmemtrace_histogram drmemtrace_reuse_time drmemtrace_basic_counts drmemtrace_opcode_mix drmemtrace_syscall_mix drmemtrace_view drmemtrace_func_view drmemtrace_raw2trace directory_iterator drmemtrace_invariant_checker - drmemtrace_schedule_stats drmemtrace_record_filter) + drmemtrace_schedule_stats drmemtrace_record_filter drmemtrace_access_region drmemtrace_reuse_pattern) if (UNIX) target_link_libraries(drmemtrace_launcher dl) endif () @@ -316,6 +322,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}) if (BUILD_PT_POST_PROCESSOR) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/drpt2trace) endif (BUILD_PT_POST_PROCESSOR) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/mirage) add_exported_library(drmemtrace_analyzer STATIC analyzer.cpp @@ -366,6 +373,8 @@ install_client_nonDR_header(drmemtrace tools/view_create.h) install_client_nonDR_header(drmemtrace tools/func_view_create.h) install_client_nonDR_header(drmemtrace tools/filter/record_filter_create.h) install_client_nonDR_header(drmemtrace tools/filter/record_filter.h) +install_client_nonDR_header(drmemtrace tools/access_region_create.h) +install_client_nonDR_header(drmemtrace tools/reuse_pattern_create.h) # TODO i#6412: Create a separate directory for non-tracer headers so that # we can more cleanly separate tracer and raw2trace code. install_client_nonDR_header(drmemtrace tracer/raw2trace.h) @@ -596,6 +605,8 @@ restore_nonclient_flags(drmemtrace_analyzer) restore_nonclient_flags(drmemtrace_invariant_checker) restore_nonclient_flags(drmemtrace_schedule_stats) restore_nonclient_flags(drmemtrace_schedule_file) +restore_nonclient_flags(drmemtrace_access_region) +restore_nonclient_flags(drmemtrace_reuse_pattern) # We need to pass /EHsc and we pull in libcmtd into drcachesim from a dep lib. # Thus we need to override the /MT with /MTd. @@ -664,6 +675,8 @@ add_win32_flags(drmemtrace_invariant_checker) add_win32_flags(drmemtrace_schedule_stats) add_win32_flags(drmemtrace_schedule_file) add_win32_flags(directory_iterator) +add_win32_flags(drmemtrace_access_region) +add_win32_flags(drmemtrace_reuse_pattern) add_win32_flags(test_helpers) if (WIN32 AND DEBUG) get_target_property(sim_srcs drmemtrace_launcher SOURCES) @@ -849,7 +862,8 @@ if (BUILD_TESTS) drmemtrace_histogram drmemtrace_reuse_time drmemtrace_basic_counts drmemtrace_opcode_mix drmemtrace_syscall_mix drmemtrace_view drmemtrace_func_view drmemtrace_raw2trace directory_iterator drmemtrace_invariant_checker - drmemtrace_schedule_stats drmemtrace_analyzer drmemtrace_record_filter) + drmemtrace_schedule_stats drmemtrace_analyzer drmemtrace_record_filter drmemtrace_access_region + drmemtrace_reuse_pattern) if (UNIX) target_link_libraries(tool.drcachesim.core_sharded dl) endif () diff --git a/clients/drcachesim/analyzer_multi.cpp b/clients/drcachesim/analyzer_multi.cpp index 5eaecd20a5d..d3176fb5038 100644 --- a/clients/drcachesim/analyzer_multi.cpp +++ b/clients/drcachesim/analyzer_multi.cpp @@ -67,6 +67,8 @@ #include "tools/loader/external_config_file.h" #include "tools/loader/external_tool_creator.h" #include "tools/filter/record_filter_create.h" +#include "tools/access_region_create.h" +#include "tools/reuse_pattern_create.h" namespace dynamorio { namespace drmemtrace { @@ -270,6 +272,14 @@ analyzer_multi_t::create_analysis_tool_from_options(const std::string &tool) } else if (tool == SCHEDULE_STATS) { return schedule_stats_tool_create(op_schedule_stats_print_every.get_value(), op_verbose.get_value()); + } else if (tool == ACCESS_REGION) { + return access_region_tool_create(op_access_region_stack_start.get_value(), + op_access_region_stack_end.get_value(), + op_access_region_heap_start.get_value(), + op_access_region_heap_end.get_value()); + + } else if (tool == REUSE_PATTERN) { + return reuse_pattern_tool_create(); } else { auto ext_tool = create_external_tool(tool); if (ext_tool == nullptr) { diff --git a/clients/drcachesim/common/options.cpp b/clients/drcachesim/common/options.cpp index c6b81db67c7..bef6eedeefa 100644 --- a/clients/drcachesim/common/options.cpp +++ b/clients/drcachesim/common/options.cpp @@ -1053,5 +1053,24 @@ droption_t op_abort_on_invariant_error( "total invariant error count is printed at the end; a non-zero error count does not " "affect the exit code of the analyzer."); +droption_t op_access_region_stack_start( + DROPTION_SCOPE_ALL, "access_region_stack_start", 0x8000000000000000, + "Start of the stack region to analyze", + "Specifies the start of the stack region"); + +droption_t op_access_region_stack_end( + DROPTION_SCOPE_ALL, "access_region_stack_end", 0x7000000000000000, + "End of the stack region to analyze", + "Specifies the end of the stack region"); + +droption_t op_access_region_heap_start( + DROPTION_SCOPE_ALL, "access_region_heap_start", 0x5000000000000000, + "Start of the heap region to analyze", + "Specifies the start of the heap region"); + +droption_t op_access_region_heap_end( + DROPTION_SCOPE_ALL, "access_region_heap_end", 0x6000000000000000, + "End of the heap region to analyze", + "Specifies the end of the heap region"); } // namespace drmemtrace } // namespace dynamorio diff --git a/clients/drcachesim/common/options.h b/clients/drcachesim/common/options.h index 8d66b4a62e1..01f3b70207b 100644 --- a/clients/drcachesim/common/options.h +++ b/clients/drcachesim/common/options.h @@ -53,7 +53,8 @@ #define INVARIANT_CHECKER "invariant_checker" #define SCHEDULE_STATS "schedule_stats" #define RECORD_FILTER "record_filter" - +#define ACCESS_REGION "access_region" +#define REUSE_PATTERN "reuse_pattern" // Constants used by specific tools. #define REPLACE_POLICY_NON_SPECIFIED "" #define REPLACE_POLICY_LRU "LRU" @@ -224,6 +225,10 @@ extern dynamorio::droption::droption_t op_trim_before_timestamp; extern dynamorio::droption::droption_t op_trim_after_timestamp; extern dynamorio::droption::droption_t op_abort_on_invariant_error; +extern dynamorio::droption::droption_t op_access_region_stack_start; +extern dynamorio::droption::droption_t op_access_region_stack_end; +extern dynamorio::droption::droption_t op_access_region_heap_start; +extern dynamorio::droption::droption_t op_access_region_heap_end; } // namespace drmemtrace } // namespace dynamorio diff --git a/clients/drcachesim/mirage/CMakeLists.txt b/clients/drcachesim/mirage/CMakeLists.txt new file mode 100644 index 00000000000..c69bdf3b9fb --- /dev/null +++ b/clients/drcachesim/mirage/CMakeLists.txt @@ -0,0 +1,44 @@ +cmake_minimum_required(VERSION 3.7) + +include (../../../make/policies.cmake NO_POLICY_SCOPE) + +if (NOT LINUX OR NOT X86 OR NOT X64) + message(FATAL_ERROR "This is only for Linux x86_64.") +endif () + +add_dr_defines() + +set(mirage_srcs + dr_mir_api.cpp + # common - shared code + ./common/list.cpp + ./common/bitmap.cpp + # frontend - translating DRIR to MIR + ./frontend/gen_ops.cpp + ./frontend/gen_opnd_api.cpp + ./frontend/translate_context.cpp + # backend - interpreting MIR + ./backend/replayer.cpp + ./backend/reg_analyzer.cpp + # ./be/*.cpp + # interpreter - MIR specification + ./ir/mir_insn.cpp +) + +add_library(mirage STATIC ${mirage_srcs}) + +target_include_directories(mirage PUBLIC + $ + $ + $ + $ +) + +configure_DynamoRIO_decoder(mirage) +add_dependencies(mirage api_headers) +target_link_libraries(mirage) +install_client_nonDR_header(drmemtrace dr_mir_api.h) +DR_export_target(mirage) +install_exported_target(mirage ${INSTALL_CLIENTS_LIB}) + +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/tests) \ No newline at end of file diff --git a/clients/drcachesim/mirage/backend/abstract_backend.h b/clients/drcachesim/mirage/backend/abstract_backend.h new file mode 100644 index 00000000000..e8e3b2ee379 --- /dev/null +++ b/clients/drcachesim/mirage/backend/abstract_backend.h @@ -0,0 +1,11 @@ +#ifndef ABSTRACT_BACKEND_H +#define ABSTRACT_BACKEND_H + +#include "mir_insn.h" + +class AbstractBackend { +public: + virtual void replay(mir_insn_list_t *insn_list) = 0; +}; + +#endif // ABSTRACT_BACKEND_H \ No newline at end of file diff --git a/clients/drcachesim/mirage/backend/reg_analyzer.cpp b/clients/drcachesim/mirage/backend/reg_analyzer.cpp new file mode 100644 index 00000000000..41b2f522e17 --- /dev/null +++ b/clients/drcachesim/mirage/backend/reg_analyzer.cpp @@ -0,0 +1,164 @@ +#include "reg_analyzer.h" + +RegAnalyzer::RegAnalyzer() { + for (int i = 0; i < DR_NUM_GPR_REGS; i++) { + gp_reg_file[i] = 1 << i; + } +} + +RegAnalyzer::~RegAnalyzer() { + +} + +void RegAnalyzer::replay(mir_insn_list_t *insn_list) { + struct list_elem* e; + for (e = list_begin(insn_list); e != list_end(insn_list); e = list_next(e)) { + mir_insn_t* insn = list_entry(e, mir_insn_t, elem); + step(insn); + } +} + +void RegAnalyzer::report() { + printf("--> Memory Access Report -->\n"); + for (auto it = mem_access_pattern.begin(); it != mem_access_pattern.end(); it++) { + if (it->first == 0) { + printf("constant memory access: %lu\n", it->second); + } else { + printf("variable memory access for 0x%lx : %lu times\n", it->first, it->second); + } + } + printf("--> Register State Report -->\n"); + for (int i = 0; i < DR_NUM_GPR_REGS; i++) { + printf("register %d: %lx\n", i, gp_reg_file[i]); + } +} + +uint64_t RegAnalyzer::read_mem(mir_insn_t *insn) { + access_mem(insn); + return 0x80000000; // signed bit marks the contamination of register +} + +void RegAnalyzer::write_mem(mir_insn_t *insn) { + access_mem(insn); +} + +void RegAnalyzer::access_mem(mir_insn_t *insn) { + if (is_const_addr(insn)) { + inc_mem_access_count(0); + } else { + inc_mem_access_count(get_val_from_opnd(insn->opnd0) | get_val_from_opnd(insn->opnd1)); + } +} + +bool RegAnalyzer::is_const_addr(mir_insn_t *insn) { + return (insn->opnd0.type == MIR_OPND_IMM || + (insn->opnd0.type == MIR_OPND_REG && insn->opnd0.value.reg == REG_NULL)) && + (insn->opnd1.type == MIR_OPND_IMM || + (insn->opnd1.type == MIR_OPND_REG && insn->opnd1.value.reg == REG_NULL)); +} + +void RegAnalyzer::step(mir_insn_t *insn) { + uint64_t src0_val = get_val_from_opnd(insn->opnd0); + uint64_t src1_val = get_val_from_opnd(insn->opnd1); + uint64_t dst_val; + switch (insn->op) { + case MIR_OP_NULL: + break; + case MIR_OP_MOV: + set_val_to_opnd(insn->dst, src0_val); + // all arithmetic operations are treated the same + case MIR_OP_ADD: + case MIR_OP_SUB: + case MIR_OP_MUL: + case MIR_OP_DIV: + case MIR_OP_REM: + case MIR_OP_AND: + case MIR_OP_OR: + case MIR_OP_XOR: + case MIR_OP_SHL: + case MIR_OP_SHR: + dst_val = src0_val | src1_val; + set_val_to_opnd(insn->dst, dst_val); + break; + case MIR_OP_LD8: + case MIR_OP_LD16: + case MIR_OP_LD32: + case MIR_OP_LD64: + dst_val = read_mem(insn); + // set_val_to_opnd(insn->dst, dst_val); + break; + case MIR_OP_ST8: + case MIR_OP_ST16: + case MIR_OP_ST32: + case MIR_OP_ST64: + write_mem(insn); + break; + default: + break; + } +} + +uint64_t RegAnalyzer::get_reg_val(reg_id_t reg) { + if (reg == REG_NULL) { + return 0; + } else if (IS_DR_REG_GPR(reg)) { + return gp_reg_file[GET_DR_REG_GPR_NUM(reg)]; + } else if (IS_DR_REG_64(reg)) { + return gp_reg_file[GET_DR_REG_64_NUM(reg)]; + } else if (IS_DR_REG_32(reg)) { + return (uint32_t)gp_reg_file[GET_DR_REG_32_NUM(reg)]; + } else if (IS_DR_REG_16(reg)) { + return (uint16_t)gp_reg_file[GET_DR_REG_16_NUM(reg)]; + } else if (IS_DR_REG_8(reg)) { + return (uint8_t)gp_reg_file[GET_DR_REG_8_NUM(reg)]; + } else if (IS_TMP_REG(reg)) { + return tmp_reg_file[GET_TMP_REG_NUM(reg)]; + } else { + return 0; + } +} + +uint64_t RegAnalyzer::get_val_from_opnd(mir_opnd_t opnd) { + if (opnd.type == MIR_OPND_REG) { + return get_reg_val(opnd.value.reg); + } else if (opnd.type == MIR_OPND_IMM) { + return 0; // constants do not contribute + } + return 0; +} + +void RegAnalyzer::set_val_to_opnd(mir_opnd_t opnd, uint64_t value) { + if (opnd.type == MIR_OPND_REG) { + //debug + // if (opnd.value.reg == REG_XSP && value != 0x10) { + // printf("setting reg xsp to %lx\n", value); + // volatile uint64_t xsp = get_reg_val(REG_XSP); + // printf("xsp: %lx\n", xsp); + // } + if (opnd.value.reg == REG_NULL) { + return; + } else if (IS_DR_REG_GPR(opnd.value.reg)) { + gp_reg_file[GET_DR_REG_GPR_NUM(opnd.value.reg)] = value; + } else if (IS_DR_REG_64(opnd.value.reg)) { + gp_reg_file[GET_DR_REG_64_NUM(opnd.value.reg)] = value; + } else if (IS_DR_REG_32(opnd.value.reg)) { + gp_reg_file[GET_DR_REG_32_NUM(opnd.value.reg)] = (uint32_t)value; + } else if (IS_DR_REG_16(opnd.value.reg)) { + gp_reg_file[GET_DR_REG_16_NUM(opnd.value.reg)] = (uint16_t)value; + } else if (IS_DR_REG_8(opnd.value.reg)) { + gp_reg_file[GET_DR_REG_8_NUM(opnd.value.reg)] = (uint8_t)value; + } else if (IS_TMP_REG(opnd.value.reg)) { + tmp_reg_file[GET_TMP_REG_NUM(opnd.value.reg)] = value; + } + } else if (opnd.type == MIR_OPND_IMM) { + assert(false && "attempted to set imm value"); + } +} + +void RegAnalyzer::inc_mem_access_count(uint64_t addr) { + if (mem_access_pattern.find(addr) == mem_access_pattern.end()) { + mem_access_pattern[addr] = 1; + } else { + mem_access_pattern[addr]++; + } +} diff --git a/clients/drcachesim/mirage/backend/reg_analyzer.h b/clients/drcachesim/mirage/backend/reg_analyzer.h new file mode 100644 index 00000000000..c519dc77f22 --- /dev/null +++ b/clients/drcachesim/mirage/backend/reg_analyzer.h @@ -0,0 +1,38 @@ +#ifndef REG_ANALYZER_H +#define REG_ANALYZER_H + +#include +using std::unordered_map; +#include "dr_api.h" +#include "abstract_backend.h" +#include "translate_context.h" +#include "mir_insn.h" + +class RegAnalyzer : public AbstractBackend { +public: + RegAnalyzer(); + ~RegAnalyzer(); + void replay(mir_insn_list_t *insn_list) override; + void report(); +private: + // register file + uint64_t gp_reg_file[DR_NUM_GPR_REGS]; + // tmp register file + uint64_t tmp_reg_file[NUM_TMP_REGS]; + + // assume all memory is chaotically volatile + uint64_t read_mem(mir_insn_t *insn); + void write_mem(mir_insn_t *insn); + void access_mem(mir_insn_t *insn); + bool is_const_addr(mir_insn_t *insn); + + void step(mir_insn_t *insn); + uint64_t get_reg_val(reg_id_t reg); + uint64_t get_val_from_opnd(mir_opnd_t opnd); + void set_val_to_opnd(mir_opnd_t opnd, uint64_t value); + + // a dictionary to store the memory access pattern + unordered_map mem_access_pattern; + void inc_mem_access_count(uint64_t addr); +}; +#endif // REG_ANALYZER_H \ No newline at end of file diff --git a/clients/drcachesim/mirage/backend/replayer.cpp b/clients/drcachesim/mirage/backend/replayer.cpp new file mode 100644 index 00000000000..5cbac024094 --- /dev/null +++ b/clients/drcachesim/mirage/backend/replayer.cpp @@ -0,0 +1,239 @@ +#include "replayer.h" + +Replayer::Replayer(InitStrategy init_strategy) { + this->init_strategy = init_strategy; + if (init_strategy == INIT_STRATEGY_ZERO) { + for (int i = 0; i < DR_NUM_GPR_REGS; i++) { + gp_reg_file[i] = 0; + } + } else if (init_strategy == INIT_STRATEGY_RANDOM) { + for (int i = 0; i < DR_NUM_GPR_REGS; i++) { + gp_reg_file[i] = rand() & 0xffffffff; + } + } + log_file = fopen("replayer.log", "w"); +} + +Replayer::~Replayer() { + shadow_mem.clear(); +} + +void Replayer::replay(mir_insn_list_t *insn_list) { + struct list_elem* e; + printf("replaying...\n"); + for (e = list_begin(insn_list); e != list_end(insn_list); e = list_next(e)) { + mir_insn_t* insn = list_entry(e, mir_insn_t, elem); + step(insn); + } +} + +uint64_t Replayer::read_mem(uintptr_t addr, size_t size) { + uint64_t value = 0; + for (size_t i = 0; i < size; i++) { + auto it = shadow_mem.find(addr + i); + if (it != shadow_mem.end()) { + value |= (uint64_t(it->second) << (i * 8)); + } else { + if (this->init_strategy == INIT_STRATEGY_RANDOM) { + value |= (uint64_t(rand() & 0xff) << (i * 8)); + } else if (this->init_strategy == INIT_STRATEGY_ZERO) { + // do nothing + } + } + } + fprintf(log_file, "read: addr = %lx, size = %lx\n", addr, size); + printf("read: addr = %lx, size = %lx\n", addr, size); + return value; +} + +void Replayer::write_mem(uintptr_t addr, uint64_t value, size_t size) { + for (size_t i = 0; i < size; i++) { + shadow_mem[addr + i] = (value >> (i * 8)) & 0xff; + } + fprintf(log_file, "write: addr = %lx, size = %lx\n", addr, size); + printf("write: addr = %lx, size = %lx\n", addr, size); +} + +void Replayer::step(mir_insn_t *insn) { + uint64_t src0_val = get_val_from_opnd(insn->opnd0); + printf("src0: %lx\n", src0_val); + uint64_t src1_val = get_val_from_opnd(insn->opnd1); + printf("src1: %lx\n", src1_val); + uint64_t dst_val; + switch (insn->op) { + case MIR_OP_NULL: + break; + case MIR_OP_MOV: + // ensure src1 is unvalued + assert(src1_val == 0); + set_val_to_opnd(insn->dst, src0_val); + break; + case MIR_OP_ADD: + dst_val = src0_val + src1_val; + set_val_to_opnd(insn->dst, dst_val); + break; + // FIXME: check directionality!! + case MIR_OP_SUB: + dst_val = src0_val - src1_val; + set_val_to_opnd(insn->dst, dst_val); + break; + case MIR_OP_MUL: + dst_val = src0_val * src1_val; + set_val_to_opnd(insn->dst, dst_val); + break; + case MIR_OP_DIV: + dst_val = src0_val / src1_val; + set_val_to_opnd(insn->dst, dst_val); + break; + case MIR_OP_REM: + dst_val = src0_val % src1_val; + set_val_to_opnd(insn->dst, dst_val); + break; + case MIR_OP_AND: + dst_val = src0_val & src1_val; + set_val_to_opnd(insn->dst, dst_val); + break; + case MIR_OP_OR: + dst_val = src0_val | src1_val; + set_val_to_opnd(insn->dst, dst_val); + break; + case MIR_OP_XOR: + dst_val = src0_val ^ src1_val; + set_val_to_opnd(insn->dst, dst_val); + break; + case MIR_OP_SHL: + dst_val = src0_val << src1_val; + set_val_to_opnd(insn->dst, dst_val); + break; + case MIR_OP_SHR: + dst_val = src0_val >> src1_val; + set_val_to_opnd(insn->dst, dst_val); + break; + case MIR_OP_LD8: + dst_val = read_mem(src0_val + src1_val, 1); + set_val_to_opnd(insn->dst, dst_val); + break; + case MIR_OP_LD16: + dst_val = read_mem(src0_val + src1_val, 2); + set_val_to_opnd(insn->dst, dst_val); + break; + case MIR_OP_LD32: + dst_val = read_mem(src0_val + src1_val, 4); + set_val_to_opnd(insn->dst, dst_val); + break; + case MIR_OP_LD64: + dst_val = read_mem(src0_val + src1_val, 8); + set_val_to_opnd(insn->dst, dst_val); + break; + case MIR_OP_ST8: + dst_val = get_val_from_opnd(insn->dst); + write_mem(src0_val + src1_val, dst_val, 1); + break; + case MIR_OP_ST16: + dst_val = get_val_from_opnd(insn->dst); + write_mem(src0_val + src1_val, dst_val, 2); + break; + case MIR_OP_ST32: + dst_val = get_val_from_opnd(insn->dst); + write_mem(src0_val + src1_val, dst_val, 4); + break; + case MIR_OP_ST64: + dst_val = get_val_from_opnd(insn->dst); + write_mem(src0_val + src1_val, dst_val, 8); + break; + case MIR_OP_W_FLAG: + // assert src1 is unvalued + assert(src1_val == 0); + // set_flag_from_value(src0_val); + set_flag_hard(); + break; + default: + break; + } +} + +uint64_t Replayer::get_reg_val(reg_id_t reg) { + if (reg == REG_NULL) { + return 0; + } else if (IS_DR_REG_GPR(reg)) { + return gp_reg_file[GET_DR_REG_GPR_NUM(reg)]; + } else if (IS_DR_REG_64(reg)) { + return gp_reg_file[GET_DR_REG_64_NUM(reg)]; + } else if (IS_DR_REG_32(reg)) { + return (uint32_t)gp_reg_file[GET_DR_REG_32_NUM(reg)]; + } else if (IS_DR_REG_16(reg)) { + return (uint16_t)gp_reg_file[GET_DR_REG_16_NUM(reg)]; + } else if (IS_DR_REG_8(reg)) { + return (uint8_t)gp_reg_file[GET_DR_REG_8_NUM(reg)]; + } else if (IS_TMP_REG(reg)) { + return tmp_reg_file[GET_TMP_REG_NUM(reg)]; + } else if (IS_FLAG_REG(reg)) { + return flag_reg_file[GET_FLAG_REG_NUM(reg)]; + } else { + assert(false && "Invalid register encoding"); + } +} + +uint64_t Replayer::get_val_from_opnd(mir_opnd_t opnd) { + if (opnd.type == MIR_OPND_REG) { + return get_reg_val(opnd.value.reg); + } else if (opnd.type == MIR_OPND_IMM) { + return opnd.value.imm; + } + return 0; +} + +void Replayer::set_val_to_opnd(mir_opnd_t opnd, uint64_t value) { + printf("setting opnd %d to %lx\n", opnd.value.reg, value); + if (opnd.type == MIR_OPND_REG) { + if (IS_DR_REG_GPR(opnd.value.reg)) { + gp_reg_file[GET_DR_REG_GPR_NUM(opnd.value.reg)] = value; + } else if (IS_DR_REG_64(opnd.value.reg)) { + gp_reg_file[GET_DR_REG_64_NUM(opnd.value.reg)] = value; + } else if (IS_DR_REG_32(opnd.value.reg)) { + gp_reg_file[GET_DR_REG_32_NUM(opnd.value.reg)] = (uint32_t)value; + } else if (IS_DR_REG_16(opnd.value.reg)) { + gp_reg_file[GET_DR_REG_16_NUM(opnd.value.reg)] = (uint16_t)value; + } else if (IS_DR_REG_8(opnd.value.reg)) { + gp_reg_file[GET_DR_REG_8_NUM(opnd.value.reg)] = (uint8_t)value; + } else if (IS_TMP_REG(opnd.value.reg)) { + tmp_reg_file[GET_TMP_REG_NUM(opnd.value.reg)] = value; + } + // flags cannot be set directly + } else if (opnd.type == MIR_OPND_IMM) { + assert(false && "attempted to set imm value"); + } +} + +// void Replayer::set_flag_from_value(uint64_t value) { + // flag_reg_file[GET_FLAG_REG_NUM(FLAG_REG_CF)] = ?; + // flag_reg_file[GET_FLAG_REG_NUM(FLAG_REG_PF)] = value & 0x01; + // flag_reg_file[GET_FLAG_REG_NUM(FLAG_REG_AF)] = (value & 0x15) == 0x15; + // flag_reg_file[GET_FLAG_REG_NUM(FLAG_REG_ZF)] = value == 0; + // flag_reg_file[GET_FLAG_REG_NUM(FLAG_REG_SF)] = (value >> 63) & 1; + // flag_reg_file[GET_FLAG_REG_NUM(FLAG_REG_OF)] = (value >> 31) & 1; +// } + +// marks a flag as being set +void Replayer::set_flag_hard() { + flag_reg_file[GET_FLAG_REG_NUM(FLAG_REG_CF)] = 2; + flag_reg_file[GET_FLAG_REG_NUM(FLAG_REG_PF)] = 2; + flag_reg_file[GET_FLAG_REG_NUM(FLAG_REG_AF)] = 2; + flag_reg_file[GET_FLAG_REG_NUM(FLAG_REG_ZF)] = 2; + flag_reg_file[GET_FLAG_REG_NUM(FLAG_REG_SF)] = 2; + flag_reg_file[GET_FLAG_REG_NUM(FLAG_REG_OF)] = 2; +} + +// marks a flag as being unset +void Replayer::unset_flag_hard() { + flag_reg_file[GET_FLAG_REG_NUM(FLAG_REG_CF)] = 0; + flag_reg_file[GET_FLAG_REG_NUM(FLAG_REG_PF)] = 0; + flag_reg_file[GET_FLAG_REG_NUM(FLAG_REG_AF)] = 0; + flag_reg_file[GET_FLAG_REG_NUM(FLAG_REG_ZF)] = 0; + flag_reg_file[GET_FLAG_REG_NUM(FLAG_REG_SF)] = 0; + flag_reg_file[GET_FLAG_REG_NUM(FLAG_REG_OF)] = 0; +} + +void Replayer::report() { + printf("replayer report\n"); +} diff --git a/clients/drcachesim/mirage/backend/replayer.h b/clients/drcachesim/mirage/backend/replayer.h new file mode 100644 index 00000000000..d9b36d52d2b --- /dev/null +++ b/clients/drcachesim/mirage/backend/replayer.h @@ -0,0 +1,49 @@ +#ifndef REPLAYER_H +#define REPLAYER_H + +#include +using std::unordered_map; + +#include "dr_api.h" +#include "abstract_backend.h" +#include "translate_context.h" +#include "mir_insn.h" + +enum InitStrategy { + INIT_STRATEGY_ZERO, + INIT_STRATEGY_RANDOM +}; + +class Replayer : public AbstractBackend { +public: + Replayer(InitStrategy init_strategy); + ~Replayer(); + void replay(mir_insn_list_t *insn_list) override; + // this is public for testing purposes + uint64_t get_reg_val(reg_id_t reg); + void report(); +private: + FILE* log_file; + InitStrategy init_strategy; + // register file + uint64_t gp_reg_file[DR_NUM_GPR_REGS]; + // tmp register file + uint64_t tmp_reg_file[NUM_TMP_REGS]; + // flag register file + uint64_t flag_reg_file[NUM_FLAG_REGS]; + + // memory - using unordered_map since we're using vaddr + unordered_map shadow_mem; + // both addr and size are byte-addressable + uint64_t read_mem(uintptr_t addr, size_t size); + void write_mem(uintptr_t addr, uint64_t value, size_t size); + + void step(mir_insn_t *insn); + uint64_t get_val_from_opnd(mir_opnd_t opnd); + void set_val_to_opnd(mir_opnd_t opnd, uint64_t value); + // void set_flag_from_value(uint64_t value); + void set_flag_hard(); + void unset_flag_hard(); +}; + +#endif // REPLAYER_H diff --git a/clients/drcachesim/mirage/common/bitmap.cpp b/clients/drcachesim/mirage/common/bitmap.cpp new file mode 100644 index 00000000000..0c1c9029e1b --- /dev/null +++ b/clients/drcachesim/mirage/common/bitmap.cpp @@ -0,0 +1,21 @@ +#include "bitmap.h" + +struct bitmap_t *bitmap_create(uint32_t size) { + struct bitmap_t* b = (struct bitmap_t*)malloc(sizeof(struct bitmap_t)); + assert(b != NULL); + b->size = size; + b->bits = (bool*)calloc(size, sizeof(bool)); + assert(b->bits != NULL); + return b; +} + +int bitmap_acquire(struct bitmap_t *bitmap) { + // find the first available bit + for (uint32_t i = 0; i < bitmap->size; i++) { + if (bitmap->bits[i] == false) { + bitmap->bits[i] = true; + return i; + } + } + return -1; +} \ No newline at end of file diff --git a/clients/drcachesim/mirage/common/bitmap.h b/clients/drcachesim/mirage/common/bitmap.h new file mode 100644 index 00000000000..5e5916e5812 --- /dev/null +++ b/clients/drcachesim/mirage/common/bitmap.h @@ -0,0 +1,19 @@ +#ifndef __BITMAP_H__ +#define __BITMAP_H__ + +// a simple bitmap implementation +#include +#include +#include +#include "assert.h" + + +struct bitmap_t { + uint32_t size; + bool *bits; +}; + +struct bitmap_t *bitmap_create(uint32_t size); +int bitmap_acquire(struct bitmap_t *bitmap); + +#endif // __BITMAP_H__ \ No newline at end of file diff --git a/clients/drcachesim/mirage/common/list.cpp b/clients/drcachesim/mirage/common/list.cpp new file mode 100644 index 00000000000..f25e4387957 --- /dev/null +++ b/clients/drcachesim/mirage/common/list.cpp @@ -0,0 +1,453 @@ +/* + * This file is taken from the Pintos kernel. + * + */ + +#include "list.h" +// #include "debug.h" + +/* Our doubly linked lists have two header elements: the "head" + just before the first element and the "tail" just after the + last element. The `prev' link of the front header is null, as + is the `next' link of the back header. Their other two links + point toward each other via the interior elements of the list. + + An empty list looks like this: + + +------+ +------+ + <---| head |<--->| tail |---> + +------+ +------+ + + A list with two elements in it looks like this: + + +------+ +-------+ +-------+ +------+ + <---| head |<--->| 1 |<--->| 2 |<--->| tail |<---> + +------+ +-------+ +-------+ +------+ + + The symmetry of this arrangement eliminates lots of special + cases in list processing. For example, take a look at + list_remove(): it takes only two pointer assignments and no + conditionals. That's a lot simpler than the code would be + without header elements. + + (Because only one of the pointers in each header element is used, + we could in fact combine them into a single header element + without sacrificing this simplicity. But using two separate + elements allows us to do a little bit of checking on some + operations, which can be valuable.) */ + +/* Returns true if ELEM is a head, false otherwise. */ +static inline bool is_head(struct list_elem* elem) { + return elem != NULL && elem->prev == NULL && elem->next != NULL; +} + +/* Returns true if ELEM is an interior element, + false otherwise. */ +static inline bool is_interior(struct list_elem* elem) { + return elem != NULL && elem->prev != NULL && elem->next != NULL; +} + +/* Returns true if ELEM is a tail, false otherwise. */ +static inline bool is_tail(struct list_elem* elem) { + return elem != NULL && elem->prev != NULL && elem->next == NULL; +} + +/* Initializes LIST as an empty list. */ +void list_init(struct list* list) { + assert(list != NULL); + list->head.prev = NULL; + list->head.next = &list->tail; + list->tail.prev = &list->head; + list->tail.next = NULL; +} + +/* Returns the beginning of LIST. */ +struct list_elem* list_begin(struct list* list) { + assert(list != NULL); + return list->head.next; +} + +/* Returns the element after ELEM in its list. If ELEM is the + last element in its list, returns the list tail. Results are + undefined if ELEM is itself a list tail. */ +struct list_elem* list_next(struct list_elem* elem) { + assert(is_head(elem) || is_interior(elem)); + return elem->next; +} + +/* Returns LIST's tail. + + list_end() is often used in iterating through a list from + front to back. See the big comment at the top of list.h for + an example. */ +struct list_elem* list_end(struct list* list) { + assert(list != NULL); + return &list->tail; +} + +/* Returns the LIST's reverse beginning, for iterating through + LIST in reverse order, from back to front. */ +struct list_elem* list_rbegin(struct list* list) { + assert(list != NULL); + return list->tail.prev; +} + +/* Returns the element before ELEM in its list. If ELEM is the + first element in its list, returns the list head. Results are + undefined if ELEM is itself a list head. */ +struct list_elem* list_prev(struct list_elem* elem) { + assert(is_interior(elem) || is_tail(elem)); + return elem->prev; +} + +/* Returns LIST's head. + + list_rend() is often used in iterating through a list in + reverse order, from back to front. Here's typical usage, + following the example from the top of list.h: + + for (e = list_rbegin (&foo_list); e != list_rend (&foo_list); + e = list_prev (e)) + { + struct foo *f = list_entry (e, struct foo, elem); + ...do something with f... + } +*/ +struct list_elem* list_rend(struct list* list) { + assert(list != NULL); + return &list->head; +} + +/* Return's LIST's head. + + list_head() can be used for an alternate style of iterating + through a list, e.g.: + + e = list_head (&list); + while ((e = list_next (e)) != list_end (&list)) + { + ... + } +*/ +struct list_elem* list_head(struct list* list) { + assert(list != NULL); + return &list->head; +} + +/* Return's LIST's tail. */ +struct list_elem* list_tail(struct list* list) { + assert(list != NULL); + return &list->tail; +} + +/* Inserts ELEM just before BEFORE, which may be either an + interior element or a tail. The latter case is equivalent to + list_push_back(). */ +void list_insert_before(struct list_elem* before, struct list_elem* elem) { + assert(is_interior(before) || is_tail(before)); + assert(elem != NULL); + + elem->prev = before->prev; + elem->next = before; + before->prev->next = elem; + before->prev = elem; +} + +void list_insert_after(struct list_elem* after, struct list_elem* elem) { + assert(is_interior(after) || is_head(after)); + assert(elem != NULL); + + elem->prev = after; + elem->next = after->next; + after->next->prev = elem; + after->next = elem; +} + +/* Removes elements FIRST though LAST (exclusive) from their + current list, then inserts them just before BEFORE, which may + be either an interior element or a tail. */ +void list_splice(struct list_elem* before, struct list_elem* first, struct list_elem* last) { + assert(is_interior(before) || is_tail(before)); + if (first == last) + return; + last = list_prev(last); + + assert(is_interior(first)); + assert(is_interior(last)); + + /* Cleanly remove FIRST...LAST from its current list. */ + first->prev->next = last->next; + last->next->prev = first->prev; + + /* Splice FIRST...LAST into new list. */ + first->prev = before->prev; + last->next = before; + before->prev->next = first; + before->prev = last; +} + +/* Inserts ELEM at the beginning of LIST, so that it becomes the + front in LIST. */ +void list_push_front(struct list* list, struct list_elem* elem) { + list_insert_before(list_begin(list), elem); +} + +/* Inserts ELEM at the end of LIST, so that it becomes the + back in LIST. */ +void list_push_back(struct list* list, struct list_elem* elem) { + list_insert_before(list_end(list), elem); +} + +/* Removes ELEM from its list and returns the element that + followed it. Undefined behavior if ELEM is not in a list. + + A list element must be treated very carefully after removing + it from its list. Calling list_next() or list_prev() on ELEM + will return the item that was previously before or after ELEM, + but, e.g., list_prev(list_next(ELEM)) is no longer ELEM! + + The list_remove() return value provides a convenient way to + iterate and remove elements from a list: + + for (e = list_begin (&list); e != list_end (&list); e = list_remove (e)) + { + ...do something with e... + } + + If you need to free() elements of the list then you need to be + more conservative. Here's an alternate strategy that works + even in that case: + + while (!list_empty (&list)) + { + struct list_elem *e = list_pop_front (&list); + ...do something with e... + } +*/ +struct list_elem* list_remove(struct list_elem* elem) { + assert(is_interior(elem)); + elem->prev->next = elem->next; + elem->next->prev = elem->prev; + return elem->next; +} + +/* Removes the front element from LIST and returns it. + Undefined behavior if LIST is empty before removal. */ +struct list_elem* list_pop_front(struct list* list) { + struct list_elem* front = list_front(list); + list_remove(front); + return front; +} + +/* Removes the back element from LIST and returns it. + Undefined behavior if LIST is empty before removal. */ +struct list_elem* list_pop_back(struct list* list) { + struct list_elem* back = list_back(list); + list_remove(back); + return back; +} + +/* Returns the front element in LIST. + Undefined behavior if LIST is empty. */ +struct list_elem* list_front(struct list* list) { + assert(!list_empty(list)); + return list->head.next; +} + +/* Returns the back element in LIST. + Undefined behavior if LIST is empty. */ +struct list_elem* list_back(struct list* list) { + assert(!list_empty(list)); + return list->tail.prev; +} + +/* Returns the number of elements in LIST. + Runs in O(n) in the number of elements. */ +size_t list_size(struct list* list) { + struct list_elem* e; + size_t cnt = 0; + + for (e = list_begin(list); e != list_end(list); e = list_next(e)) + cnt++; + return cnt; +} + +/* Returns true if LIST is empty, false otherwise. */ +bool list_empty(struct list* list) { return list_begin(list) == list_end(list); } + +/* Swaps the `struct list_elem *'s that A and B point to. */ +static void swap(struct list_elem** a, struct list_elem** b) { + struct list_elem* t = *a; + *a = *b; + *b = t; +} + +/* Reverses the order of LIST. */ +void list_reverse(struct list* list) { + if (!list_empty(list)) { + struct list_elem* e; + + for (e = list_begin(list); e != list_end(list); e = e->prev) + swap(&e->prev, &e->next); + swap(&list->head.next, &list->tail.prev); + swap(&list->head.next->prev, &list->tail.prev->next); + } +} + +/* Returns true only if the list elements A through B (exclusive) + are in order according to LESS given auxiliary data AUX. */ +static bool is_sorted(struct list_elem* a, struct list_elem* b, list_less_func* less, void* aux) { + if (a != b) + while ((a = list_next(a)) != b) + if (less(a, list_prev(a), aux)) + return false; + return true; +} + +/* Finds a run, starting at A and ending not after B, of list + elements that are in nondecreasing order according to LESS + given auxiliary data AUX. Returns the (exclusive) end of the + run. + A through B (exclusive) must form a non-empty range. */ +static struct list_elem* find_end_of_run(struct list_elem* a, struct list_elem* b, + list_less_func* less, void* aux) { + assert(a != NULL); + assert(b != NULL); + assert(less != NULL); + assert(a != b); + + do { + a = list_next(a); + } while (a != b && !less(a, list_prev(a), aux)); + return a; +} + +/* Merges A0 through A1B0 (exclusive) with A1B0 through B1 + (exclusive) to form a combined range also ending at B1 + (exclusive). Both input ranges must be nonempty and sorted in + nondecreasing order according to LESS given auxiliary data + AUX. The output range will be sorted the same way. */ +static void inplace_merge(struct list_elem* a0, struct list_elem* a1b0, struct list_elem* b1, + list_less_func* less, void* aux) { + assert(a0 != NULL); + assert(a1b0 != NULL); + assert(b1 != NULL); + assert(less != NULL); + assert(is_sorted(a0, a1b0, less, aux)); + assert(is_sorted(a1b0, b1, less, aux)); + + while (a0 != a1b0 && a1b0 != b1) + if (!less(a1b0, a0, aux)) + a0 = list_next(a0); + else { + a1b0 = list_next(a1b0); + list_splice(a0, list_prev(a1b0), a1b0); + } +} + +/* Sorts LIST according to LESS given auxiliary data AUX, using a + natural iterative merge sort that runs in O(n lg n) time and + O(1) space in the number of elements in LIST. */ +void list_sort(struct list* list, list_less_func* less, void* aux) { + size_t output_run_cnt; /* Number of runs output in current pass. */ + + assert(list != NULL); + assert(less != NULL); + + /* Pass over the list repeatedly, merging adjacent runs of + nondecreasing elements, until only one run is left. */ + do { + struct list_elem* a0; /* Start of first run. */ + struct list_elem* a1b0; /* End of first run, start of second. */ + struct list_elem* b1; /* End of second run. */ + + output_run_cnt = 0; + for (a0 = list_begin(list); a0 != list_end(list); a0 = b1) { + /* Each iteration produces one output run. */ + output_run_cnt++; + + /* Locate two adjacent runs of nondecreasing elements + A0...A1B0 and A1B0...B1. */ + a1b0 = find_end_of_run(a0, list_end(list), less, aux); + if (a1b0 == list_end(list)) + break; + b1 = find_end_of_run(a1b0, list_end(list), less, aux); + + /* Merge the runs. */ + inplace_merge(a0, a1b0, b1, less, aux); + } + } while (output_run_cnt > 1); + + assert(is_sorted(list_begin(list), list_end(list), less, aux)); +} + +/* Inserts ELEM in the proper position in LIST, which must be + sorted according to LESS given auxiliary data AUX. + Runs in O(n) average case in the number of elements in LIST. */ +void list_insert_ordered(struct list* list, struct list_elem* elem, list_less_func* less, + void* aux) { + struct list_elem* e; + + assert(list != NULL); + assert(elem != NULL); + assert(less != NULL); + + for (e = list_begin(list); e != list_end(list); e = list_next(e)) + if (less(elem, e, aux)) + break; + return list_insert_before(e, elem); +} + +/* Iterates through LIST and removes all but the first in each + set of adjacent elements that are equal according to LESS + given auxiliary data AUX. If DUPLICATES is non-null, then the + elements from LIST are appended to DUPLICATES. */ +void list_unique(struct list* list, struct list* duplicates, list_less_func* less, void* aux) { + struct list_elem *elem, *next; + + assert(list != NULL); + assert(less != NULL); + if (list_empty(list)) + return; + + elem = list_begin(list); + while ((next = list_next(elem)) != list_end(list)) + if (!less(elem, next, aux) && !less(next, elem, aux)) { + list_remove(next); + if (duplicates != NULL) + list_push_back(duplicates, next); + } else + elem = next; +} + +/* Returns the element in LIST with the largest value according + to LESS given auxiliary data AUX. If there is more than one + maximum, returns the one that appears earlier in the list. If + the list is empty, returns its tail. */ +struct list_elem* list_max(struct list* list, list_less_func* less, void* aux) { + struct list_elem* max = list_begin(list); + if (max != list_end(list)) { + struct list_elem* e; + + for (e = list_next(max); e != list_end(list); e = list_next(e)) + if (less(max, e, aux)) + max = e; + } + return max; +} + +/* Returns the element in LIST with the smallest value according + to LESS given auxiliary data AUX. If there is more than one + minimum, returns the one that appears earlier in the list. If + the list is empty, returns its tail. */ +struct list_elem* list_min(struct list* list, list_less_func* less, void* aux) { + struct list_elem* min = list_begin(list); + if (min != list_end(list)) { + struct list_elem* e; + + for (e = list_next(min); e != list_end(list); e = list_next(e)) + if (less(e, min, aux)) + min = e; + } + return min; +} \ No newline at end of file diff --git a/clients/drcachesim/mirage/common/list.h b/clients/drcachesim/mirage/common/list.h new file mode 100644 index 00000000000..cbfc2365c61 --- /dev/null +++ b/clients/drcachesim/mirage/common/list.h @@ -0,0 +1,182 @@ +/* + * This file is taken from the Pintos kernel. + * + */ + +#ifndef __LIB_KERNEL_LIST_H +#define __LIB_KERNEL_LIST_H + +/* Doubly linked list. + + This implementation of a doubly linked list does not require + use of dynamically allocated memory. Instead, each structure + that is a potential list element must embed a struct list_elem + member. All of the list functions operate on these `struct + list_elem's. The list_entry macro allows conversion from a + struct list_elem back to a structure object that contains it. + + For example, suppose there is a needed for a list of `struct + foo'. `struct foo' should contain a `struct list_elem' + member, like so: + + struct foo + { + struct list_elem elem; + int bar; + ...other members... + }; + + Then a list of `struct foo' can be be declared and initialized + like so: + + struct list foo_list; + + list_init (&foo_list); + + Iteration is a typical situation where it is necessary to + convert from a struct list_elem back to its enclosing + structure. Here's an example using foo_list: + + struct list_elem *e; + + for (e = list_begin (&foo_list); e != list_end (&foo_list); + e = list_next (e)) + { + struct foo *f = list_entry (e, struct foo, elem); + ...do something with f... + } + + You can find real examples of list usage throughout the + source; for example, malloc.c, palloc.c, and thread.c in the + threads directory all use lists. + + The interface for this list is inspired by the list<> template + in the C++ STL. If you're familiar with list<>, you should + find this easy to use. However, it should be emphasized that + these lists do *no* type checking and can't do much other + correctness checking. If you screw up, it will bite you. + + Glossary of list terms: + + - "front": The first element in a list. Undefined in an + empty list. Returned by list_front(). + + - "back": The last element in a list. Undefined in an empty + list. Returned by list_back(). + + - "tail": The element figuratively just after the last + element of a list. Well defined even in an empty list. + Returned by list_end(). Used as the end sentinel for an + iteration from front to back. + + - "beginning": In a non-empty list, the front. In an empty + list, the tail. Returned by list_begin(). Used as the + starting point for an iteration from front to back. + + - "head": The element figuratively just before the first + element of a list. Well defined even in an empty list. + Returned by list_rend(). Used as the end sentinel for an + iteration from back to front. + + - "reverse beginning": In a non-empty list, the back. In an + empty list, the head. Returned by list_rbegin(). Used as + the starting point for an iteration from back to front. + + - "interior element": An element that is not the head or + tail, that is, a real list element. An empty list does + not have any interior elements. +*/ + +#include +#include +#include + +#include "assert.h" + +/* List element. */ +struct list_elem { + struct list_elem* prev; /* Previous list element. */ + struct list_elem* next; /* Next list element. */ +}; + +/* List. */ +struct list { + struct list_elem head; /* List head. */ + struct list_elem tail; /* List tail. */ +}; + +/* Converts pointer to list element LIST_ELEM into a pointer to + the structure that LIST_ELEM is embedded inside. Supply the + name of the outer structure STRUCT and the member name MEMBER + of the list element. See the big comment at the top of the + file for an example. */ +#define list_entry(LIST_ELEM, STRUCT, MEMBER) \ + ((STRUCT*)((uint8_t*)&(LIST_ELEM)->next - offsetof(STRUCT, MEMBER.next))) + +/* List initialization. + + A list may be initialized by calling list_init(): + + struct list my_list; + list_init (&my_list); + + or with an initializer using LIST_INITIALIZER: + + struct list my_list = LIST_INITIALIZER (my_list); */ +#define LIST_INITIALIZER(NAME) \ + { \ + {NULL, &(NAME).tail}, { &(NAME).head, NULL } \ + } + +void list_init(struct list*); + +/* List traversal. */ +struct list_elem* list_begin(struct list*); +struct list_elem* list_next(struct list_elem*); +struct list_elem* list_end(struct list*); + +struct list_elem* list_rbegin(struct list*); +struct list_elem* list_prev(struct list_elem*); +struct list_elem* list_rend(struct list*); + +struct list_elem* list_head(struct list*); +struct list_elem* list_tail(struct list*); + +/* List insertion. */ +void list_insert_before(struct list_elem*, struct list_elem*); +void list_insert_after(struct list_elem*, struct list_elem*); +void list_splice(struct list_elem* before, struct list_elem* first, struct list_elem* last); +void list_push_front(struct list*, struct list_elem*); +void list_push_back(struct list*, struct list_elem*); + +/* List removal. */ +struct list_elem* list_remove(struct list_elem*); +struct list_elem* list_pop_front(struct list*); +struct list_elem* list_pop_back(struct list*); + +/* List elements. */ +struct list_elem* list_front(struct list*); +struct list_elem* list_back(struct list*); + +/* List properties. */ +size_t list_size(struct list*); +bool list_empty(struct list*); + +/* Miscellaneous. */ +void list_reverse(struct list*); + +/* Compares the value of two list elements A and B, given + auxiliary data AUX. Returns true if A is less than B, or + false if A is greater than or equal to B. */ +typedef bool list_less_func(const struct list_elem* a, const struct list_elem* b, void* aux); + +/* Operations on lists with ordered elements. */ +void list_sort(struct list*, list_less_func*, void* aux); +void list_insert_ordered(struct list*, struct list_elem*, list_less_func*, void* aux); +void list_unique(struct list*, struct list* duplicates, list_less_func*, void* aux); + +/* Max and min. */ +struct list_elem* list_max(struct list*, list_less_func*, void* aux); +struct list_elem* list_min(struct list*, list_less_func*, void* aux); + +#endif /* lib/kernel/list.h */ \ No newline at end of file diff --git a/clients/drcachesim/mirage/dr_mir_api.cpp b/clients/drcachesim/mirage/dr_mir_api.cpp new file mode 100644 index 00000000000..f9d6dea5aac --- /dev/null +++ b/clients/drcachesim/mirage/dr_mir_api.cpp @@ -0,0 +1,117 @@ +#include "dr_mir_api.h" + +void dr_gen_mir_ops(instr_t *instr, mir_insn_list_t *insn_list) { + + int opc = instr_get_opcode(instr); + + struct translate_context_t *ctx = translate_context_create(); + ctx_set_curr_instr(ctx, instr); + + switch (opc) { + case OP_nop_modrm: + case OP_rdtsc: + gen_nop_op(instr, insn_list, ctx); + break; + // Arithmetic & Bitwise instructions + case OP_add: + gen_add_op(instr, insn_list, ctx); + break; + case OP_sub: + gen_sub_op(instr, insn_list, ctx); + break; + case OP_or: + gen_or_op(instr, insn_list, ctx); + break; + case OP_and: + gen_and_op(instr, insn_list, ctx); + break; + case OP_xor: + gen_xor_op(instr, insn_list, ctx); + break; + case OP_shl: + gen_shl_op(instr, insn_list, ctx); + break; + case OP_shr: + gen_shr_op(instr, insn_list, ctx); + break; + // All movs are handled the same way + case OP_mov_ld: + case OP_mov_st: + case OP_mov_imm: + case OP_mov_seg: + case OP_mov_priv: + case OP_cwde: + gen_mov_op(instr, insn_list, ctx); + break; + case OP_lea: + gen_lea_op(instr, insn_list, ctx); + break; + // Compounded instructions + case OP_push: + gen_push_op(instr, insn_list, ctx); + break; + case OP_pop: + gen_pop_op(instr, insn_list, ctx); + break; + case OP_call: + gen_call_op(instr, insn_list, ctx); + break; + case OP_ret_far: + case OP_ret: + gen_ret_op(instr, insn_list, ctx); + break; + case OP_test: + gen_test_op(instr, insn_list, ctx); + break; + case OP_cmp: + gen_cmp_op(instr, insn_list, ctx); + break; + case OP_adc: + gen_adc_op(instr, insn_list, ctx); + break; + // All jumps are trivially handled for now + case OP_jmp: + case OP_jmp_short: + case OP_jmp_ind: + case OP_jmp_far: + case OP_jmp_far_ind: + case OP_jo_short: + case OP_jno_short: + case OP_jb_short: + case OP_jnb_short: + case OP_jz_short: + case OP_jnz_short: + case OP_jbe_short: + case OP_jnbe_short: + case OP_js_short: + case OP_jns_short: + case OP_jp_short: + case OP_jnp_short: + case OP_jl_short: + case OP_jnl_short: + case OP_jle_short: + case OP_jnle_short: + case OP_jo: + case OP_jno: + case OP_jb: + case OP_jnb: + case OP_jz: + case OP_jnz: + case OP_jbe: + case OP_jnbe: + case OP_js: + case OP_jns: + case OP_jp: + case OP_jnp: + case OP_jl: + case OP_jnl: + case OP_jle: + case OP_jnle: + gen_jump_op(instr, insn_list, ctx); + break; + default: + printf("Unsupported opcode: %d\n", opc); + break; + } + print_mir_insn_list(insn_list); +} \ No newline at end of file diff --git a/clients/drcachesim/mirage/dr_mir_api.h b/clients/drcachesim/mirage/dr_mir_api.h new file mode 100644 index 00000000000..11e71c9cd19 --- /dev/null +++ b/clients/drcachesim/mirage/dr_mir_api.h @@ -0,0 +1,12 @@ +#ifndef __DR_MIR_API_H_ +#define __DR_MIR_API_H + +#include "dr_api.h" + +#include "gen_ops.h" +#include "list.h" + +void +dr_gen_mir_ops(instr_t *instr, mir_insn_list_t *insn_list); + +#endif // __DR_MIR_API_H_ \ No newline at end of file diff --git a/clients/drcachesim/mirage/frontend/gen_opnd_api.cpp b/clients/drcachesim/mirage/frontend/gen_opnd_api.cpp new file mode 100644 index 00000000000..40c4dacd6e7 --- /dev/null +++ b/clients/drcachesim/mirage/frontend/gen_opnd_api.cpp @@ -0,0 +1,311 @@ +#include "gen_opnd_api.h" + +const char* get_opnd_type(opnd_t opnd) { + if (opnd_is_reg(opnd)) { + return "reg"; + } + if (opnd_is_immed(opnd)) { + return "imm"; + } + if (opnd_is_base_disp(opnd)) { + return "base_disp"; + } + if (opnd_is_pc(opnd)) { + return "pc"; + } + if (opnd_is_instr(opnd)) { + return "instr"; + } + if (opnd_is_null(opnd)) { + return "null"; + } + if (opnd_is_near_rel_addr(opnd)) { + return "near_rel_addr"; + } + if (opnd_is_far_rel_addr(opnd)) { + return "far_rel_addr"; + } + return "unknown"; +} + +// Helper function to generate a load instruction from a memory reference +void gen_src0_from_memref(opnd_t opnd, mir_insn_t* insn, mir_insn_list_t* mir_insns_list, struct translate_context_t *ctx) { + // statically inferable address + if (opnd_is_abs_addr(opnd) || opnd_is_rel_addr(opnd)) { + gen_src0_load_from_abs_addr(opnd, insn, mir_insns_list, ctx); + } + // register-value dependent address + else if (opnd_is_base_disp(opnd)) { + gen_src0_load_from_base_disp(opnd, insn, mir_insns_list, ctx); + } else { + printf("unsupported memref opnd src0 type: %s\n", get_opnd_type(opnd)); + } +} + +// Helper function to generate a load instruction from a memory reference +void gen_src1_from_memref(opnd_t opnd, mir_insn_t* insn, mir_insn_list_t* mir_insns_list, struct translate_context_t *ctx) { + // statically inferable address + if (opnd_is_abs_addr(opnd) || opnd_is_rel_addr(opnd)) { + gen_src1_load_from_abs_addr(opnd, insn, mir_insns_list, ctx); + } + // register-value dependent address + else if (opnd_is_base_disp(opnd)) { + gen_src1_load_from_base_disp(opnd, insn, mir_insns_list, ctx); + } +} + +// Helper function to generate a load instruction from a memory reference +// called on dst of a store, but generates a load (special case handling) +void gen_src2_from_memref(opnd_t opnd, mir_insn_t* insn, mir_insn_list_t* mir_insns_list, struct translate_context_t *ctx) { + // statically inferable address + if (opnd_is_abs_addr(opnd) || opnd_is_rel_addr(opnd)) { + gen_src2_load_from_abs_addr(opnd, insn, mir_insns_list, ctx); + } + // register-value dependent address + else if (opnd_is_base_disp(opnd)) { + gen_src2_load_from_base_disp(opnd, insn, mir_insns_list, ctx); + } +} + +// Helper function to generate a store instruction to a memory reference +void gen_dst_to_memref(opnd_t opnd, mir_insn_t* insn, mir_insn_list_t* mir_insns_list, struct translate_context_t *ctx) { + // statically inferable address + if (opnd_is_abs_addr(opnd) || opnd_is_rel_addr(opnd)) { + gen_dst_store_to_abs_addr(opnd, insn, mir_insns_list, ctx); + } + // register-value dependent address + else if (opnd_is_base_disp(opnd)) { + gen_dst_store_to_base_disp(opnd, insn, mir_insns_list, ctx); + } +} + +void set_srcs_from_memref(opnd_t opnd, mir_insn_t* insn, mir_insn_list_t* mir_insns_list, struct translate_context_t *ctx) { + if (opnd_is_abs_addr(opnd) || opnd_is_rel_addr(opnd)) { + void *addr = opnd_get_addr(opnd); + mir_insn_set_src0_reg(insn, DR_REG_NULL); + mir_insn_set_src1_imm(insn, (int64_t)addr); + mir_insn_set_dst_reg(insn, DR_REG_NULL); + } + // base-displacement + else if (opnd_is_base_disp(opnd)) { + reg_id_t base = opnd_get_base(opnd); + int32_t disp = opnd_get_disp(opnd); + mir_insn_set_src0_reg(insn, base); + mir_insn_set_src1_imm(insn, disp); + mir_insn_set_dst_reg(insn, DR_REG_NULL); + } +} + +// ABSOLUTE ADDRESS +void _gen_load_from_abs_addr(opnd_t opnd, mir_insn_t* insn, + mir_insn_list_t *mir_insns_list, int src_num, struct translate_context_t *ctx) { + void *addr = opnd_get_addr(opnd); + mir_insn_t* load_insn = mir_insn_malloc(MIR_OP_LD64); + mir_insn_set_src0_imm(load_insn, (int64_t)addr); + mir_insn_set_src1_reg(load_insn, DR_REG_NULL); + reg_id_t tmp_dst_reg = alloc_tmp_reg(ctx); + mir_insn_set_dst_reg(load_insn, tmp_dst_reg); + if (src_num == 0) { + mir_insn_set_src0_reg(insn, tmp_dst_reg); + } + else if (src_num == 1) { + mir_insn_set_src1_reg(insn, tmp_dst_reg); + } + else if (src_num == 2) { + // Useful when insn is a store instruction, where dst is the src0 + mir_insn_set_dst_reg(insn, tmp_dst_reg); + } + mir_insn_insert_before(load_insn, insn); +} + +// A helper function to generate a load instruction from a absolute address src +void gen_src0_load_from_abs_addr(opnd_t opnd, mir_insn_t* insn, + mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + _gen_load_from_abs_addr(opnd, insn, mir_insns_list, 0, ctx); +} + +// A helper function to generate a load instruction from a absolute address src +void gen_src1_load_from_abs_addr(opnd_t opnd, mir_insn_t* insn, + mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + _gen_load_from_abs_addr(opnd, insn, mir_insns_list, 1, ctx); +} + +// A helper function to generate a load instruction from a absolute address src +void gen_src2_load_from_abs_addr(opnd_t opnd, mir_insn_t* insn, + mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + _gen_load_from_abs_addr(opnd, insn, mir_insns_list, 2, ctx); +} + +// A helper function to generate a store instruction to a absolute address dst +void gen_dst_store_to_abs_addr(opnd_t opnd, mir_insn_t* insn, + mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + + void *addr = opnd_get_addr(opnd); + // Patch the original instruction + reg_id_t tmp_dst_reg = alloc_tmp_reg(ctx); + mir_insn_set_dst_reg(insn, tmp_dst_reg); + + // Generate the store instruction + mir_insn_t* store_insn = mir_insn_malloc(MIR_OP_ST64); + mir_insn_set_dst_reg(store_insn, tmp_dst_reg); + mir_insn_set_src0_reg(store_insn, DR_REG_NULL); + mir_insn_set_src1_imm(store_insn, (int64_t)addr); + mir_insn_insert_after(store_insn, insn); + return; +} + +// BASE-DISPLACEMENT +// A helper function to generate a load instruction from a base-displacement src +void _gen_load_from_base_disp(opnd_t opnd, mir_insn_t* insn, + mir_insn_list_t *mir_insns_list, int src_num, struct translate_context_t *ctx) { + reg_id_t base = opnd_get_base(opnd); + int32_t disp = opnd_get_disp(opnd); + reg_id_t index = opnd_get_index(opnd); + int32_t scale = opnd_get_scale(opnd); + printf("base: %d, disp: %d, index: %d, scale: %d\n", base, disp, index, scale); + int mul_tmp_reg = DR_REG_NULL; + if (index != DR_REG_NULL && scale != 0) { + // mul = index * scale + mir_insn_t* mul_insn = mir_insn_malloc(MIR_OP_MUL); + mir_insn_set_src0_reg(mul_insn, index); + mir_insn_set_src1_imm(mul_insn, scale); + mir_insn_insert_before(mul_insn, insn); + mul_tmp_reg = alloc_tmp_reg(ctx); + mir_insn_set_dst_reg(mul_insn, mul_tmp_reg); + mir_insn_insert_before(mul_insn, insn); + // addr = base + mul + mir_insn_t* add_insn = mir_insn_malloc(MIR_OP_ADD); + mir_insn_set_src0_reg(add_insn, base); + mir_insn_set_src1_reg(add_insn, mul_tmp_reg); + mir_insn_set_dst_reg(add_insn, mul_tmp_reg); + mir_insn_insert_before(add_insn, insn); + } + // addr = base + disp + mir_insn_t* load_insn = mir_insn_malloc(MIR_OP_LD64); + if (mul_tmp_reg != DR_REG_NULL) { + mir_insn_set_src0_reg(load_insn, mul_tmp_reg); + } else { + mir_insn_set_src0_reg(load_insn, base); + } + mir_insn_set_src1_imm(load_insn, disp); + reg_id_t tmp_dst_reg = alloc_tmp_reg(ctx); + mir_insn_set_dst_reg(load_insn, tmp_dst_reg); + if (src_num == 0) { + mir_insn_set_src0_reg(insn, tmp_dst_reg); + } + else if (src_num == 1) { + mir_insn_set_src1_reg(insn, tmp_dst_reg); + } else if (src_num == 2) { + // Useful when insn is a store instruction, where dst is the src0 + mir_insn_set_dst_reg(insn, tmp_dst_reg); + } + mir_insn_insert_before(load_insn, insn); +} + +void gen_src0_load_from_base_disp(opnd_t opnd, mir_insn_t* insn, + mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + _gen_load_from_base_disp(opnd, insn, mir_insns_list, 0, ctx); +} + +void gen_src1_load_from_base_disp(opnd_t opnd, mir_insn_t* insn, + mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + _gen_load_from_base_disp(opnd, insn, mir_insns_list, 1, ctx); +} + +// src2 is an alias used when insn is STORE, where dst is used as a source +void gen_src2_load_from_base_disp(opnd_t opnd, mir_insn_t* insn, + mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + _gen_load_from_base_disp(opnd, insn, mir_insns_list, 2, ctx); +} + +// A helper function to generate a store instruction to a base-displacement dst, +// the store instruction is inserted after the original instruction, +// and the original instruction is patched to use the tmp register as the dst +void gen_dst_store_to_base_disp(opnd_t opnd, mir_insn_t* insn, + mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + reg_id_t base = opnd_get_base(opnd); + int32_t disp = opnd_get_disp(opnd); + + // Patch the original instruction + reg_id_t tmp_dst_reg = alloc_tmp_reg(ctx); + mir_insn_set_dst_reg(insn, tmp_dst_reg); + + // Generate the store instruction + mir_insn_t* store_insn = mir_insn_malloc(MIR_OP_ST64); + mir_insn_set_dst_reg(store_insn, tmp_dst_reg); + mir_insn_set_src0_reg(store_insn, base); + mir_insn_set_src1_imm(store_insn, disp); + mir_insn_insert_after(store_insn, insn); + return; +} + +// Higher-level wrapper function that distinguishes between different types of opnds +// Register: insn->src0 = reg.opnd +// Immediate: insn->src0 = imm.opnd +// Memory: call gen_src0_from_memref +void src0_set_opnd_by_type(opnd_t opnd, mir_insn_t* insn, + mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + if (opnd_is_reg(opnd)) { + mir_insn_set_src0_reg(insn, opnd_get_reg(opnd)); + } + else if (opnd_is_immed(opnd)) { + mir_insn_set_src0_imm(insn, opnd_get_immed_int(opnd)); + } + else if (opnd_is_memory_reference(opnd)) { + gen_src0_from_memref(opnd, insn, mir_insns_list, ctx); + } + // else if (opnd_is_far_rel_addr(opnd)) { + // gen_load_from_far_rel_addr(opnd, insn, mir_insns_list); + // } + else { + printf("unsupported opnd src0 type: %s\n", get_opnd_type(opnd)); + } +} + +// Higher-level wrapper function that distinguishes between different types of opnds +// Register: insn->src1 = reg.opnd +// Immediate: insn->src1 = imm.opnd +// Memory: call gen_src1_from_memref +void src1_set_opnd_by_type(opnd_t opnd, mir_insn_t* insn, + mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + if (opnd_is_reg(opnd)) { + mir_insn_set_src1_reg(insn, opnd_get_reg(opnd)); + } + else if (opnd_is_immed(opnd)) { + mir_insn_set_src1_imm(insn, opnd_get_immed_int(opnd)); + } + else if (opnd_is_memory_reference(opnd)) { + gen_src1_from_memref(opnd, insn, mir_insns_list, ctx); + } + else { + printf("unsupported opnd src1 type: %s\n", get_opnd_type(opnd)); + } +} + +// src2 is an alias used when insn is STORE, where dst is used as a source +void src2_set_opnd_by_type(opnd_t opnd, mir_insn_t* insn, + mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + if (opnd_is_reg(opnd)) { + mir_insn_set_dst_reg(insn, opnd_get_reg(opnd)); + } else if (opnd_is_memory_reference(opnd)) { + gen_src2_from_memref(opnd, insn, mir_insns_list, ctx); + } else { + printf("unsupported opnd src2 type: %s\n", get_opnd_type(opnd)); + } +} + +// Higher-level wrapper function that distinguishes between different types of opnds +// Register: insn->dst = reg.opnd +// Memory: call gen_dst_to_memref +void dst_set_opnd_by_type(opnd_t opnd, mir_insn_t* insn, + mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + if (opnd_is_reg(opnd)) { + mir_insn_set_dst_reg(insn, opnd_get_reg(opnd)); + } + else if (opnd_is_memory_reference(opnd)) { + gen_dst_to_memref(opnd, insn, mir_insns_list, ctx); + } + else { + printf("unsupported opnd dst type: %s\n", get_opnd_type(opnd)); + } +} \ No newline at end of file diff --git a/clients/drcachesim/mirage/frontend/gen_opnd_api.h b/clients/drcachesim/mirage/frontend/gen_opnd_api.h new file mode 100644 index 00000000000..73c8cd174c8 --- /dev/null +++ b/clients/drcachesim/mirage/frontend/gen_opnd_api.h @@ -0,0 +1,42 @@ +#ifndef __GEN_OPND_API_H__ +#define __GEN_OPND_API_H__ + +#include "dr_api.h" + +#include "mir_insn.h" +#include "mir_opnd.h" +#include "list.h" + +const char* get_opnd_type(opnd_t opnd); + +/* helpers for generating the opnds of mir_insn based on the type of the opnds + used when the original x86 instruction has memory reference as operand +*/ +void gen_src0_from_memref(opnd_t opnd, mir_insn_t* insn, mir_insn_list_t* mir_insns_list, struct translate_context_t *ctx); +void gen_src1_from_memref(opnd_t opnd, mir_insn_t* insn, mir_insn_list_t* mir_insns_list, struct translate_context_t *ctx); +void gen_src2_from_memref(opnd_t opnd, mir_insn_t* insn, mir_insn_list_t* mir_insns_list, struct translate_context_t *ctx); +void gen_dst_to_memref(opnd_t opnd, mir_insn_t* insn, mir_insn_list_t* mir_insns_list, struct translate_context_t *ctx); + +/* helpers for setting the opnds of mir_insn based on the type of the opnds + used when the original x86 instruction intends to jump to a memory reference +*/ +void set_srcs_from_memref(opnd_t opnd, mir_insn_t* insn, mir_insn_list_t* mir_insns_list, struct translate_context_t *ctx); + +void src0_set_opnd_by_type(opnd_t opnd, mir_insn_t* insn, mir_insn_list_t* mir_insns_list, struct translate_context_t *ctx); +void src1_set_opnd_by_type(opnd_t opnd, mir_insn_t* insn, mir_insn_list_t* mir_insns_list, struct translate_context_t *ctx); +void src2_set_opnd_by_type(opnd_t opnd, mir_insn_t* insn, mir_insn_list_t* mir_insns_list, struct translate_context_t *ctx); +void dst_set_opnd_by_type(opnd_t opnd, mir_insn_t* insn, mir_insn_list_t* mir_insns_list, struct translate_context_t *ctx); + +// ABSOLUTE ADDRESS or RELATIVE ADDRESS +void gen_src0_load_from_abs_addr(opnd_t opnd, mir_insn_t* insn, mir_insn_list_t* mir_insns_list, struct translate_context_t *ctx); +void gen_src1_load_from_abs_addr(opnd_t opnd, mir_insn_t* insn, mir_insn_list_t* mir_insns_list, struct translate_context_t *ctx); +void gen_src2_load_from_abs_addr(opnd_t opnd, mir_insn_t* insn, mir_insn_list_t* mir_insns_list, struct translate_context_t *ctx); +void gen_dst_store_to_abs_addr(opnd_t opnd, mir_insn_t* insn, mir_insn_list_t* mir_insns_list, struct translate_context_t *ctx); + +// BASE DISPLACEMENT +void gen_src0_load_from_base_disp(opnd_t opnd, mir_insn_t* insn, mir_insn_list_t* mir_insns_list, struct translate_context_t *ctx); +void gen_src1_load_from_base_disp(opnd_t opnd, mir_insn_t* insn, mir_insn_list_t* mir_insns_list, struct translate_context_t *ctx); +void gen_src2_load_from_base_disp(opnd_t opnd, mir_insn_t* insn, mir_insn_list_t* mir_insns_list, struct translate_context_t *ctx); +void gen_dst_store_to_base_disp(opnd_t opnd, mir_insn_t* insn, mir_insn_list_t* mir_insns_list, struct translate_context_t *ctx); + +#endif // __GEN_OPND_API_H__ \ No newline at end of file diff --git a/clients/drcachesim/mirage/frontend/gen_ops.cpp b/clients/drcachesim/mirage/frontend/gen_ops.cpp new file mode 100644 index 00000000000..e0debdbac49 --- /dev/null +++ b/clients/drcachesim/mirage/frontend/gen_ops.cpp @@ -0,0 +1,307 @@ +#include "gen_ops.h" + +// Do nothing for unsupported instructions +void gen_nop_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { +} + +void _gen_arith_op(instr_t *instr, mir_opc_t op, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + assert(instr_num_srcs(instr) == 2); + assert(instr_num_dsts(instr) == 1); + + opnd_t dst = instr_get_dst(instr, 0); + opnd_t src0 = instr_get_src(instr, 0); + opnd_t src1 = instr_get_src(instr, 1); + + mir_insn_t* core_insn = mir_insn_malloc(op); + mir_insn_push_front(mir_insns_list, core_insn); + + src0_set_opnd_by_type(src0, core_insn, mir_insns_list, ctx); + src1_set_opnd_by_type(src1, core_insn, mir_insns_list, ctx); + dst_set_opnd_by_type(dst, core_insn, mir_insns_list, ctx); +} + +void gen_add_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + _gen_arith_op(instr, MIR_OP_ADD, mir_insns_list, ctx); +} + +void gen_sub_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + _gen_arith_op(instr, MIR_OP_SUB, mir_insns_list, ctx); +} + +void gen_or_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + _gen_arith_op(instr, MIR_OP_OR, mir_insns_list, ctx); +} + +void gen_and_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + _gen_arith_op(instr, MIR_OP_AND, mir_insns_list, ctx); +} + +void gen_xor_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + _gen_arith_op(instr, MIR_OP_XOR, mir_insns_list, ctx); +} + +void gen_shl_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + _gen_arith_op(instr, MIR_OP_SHL, mir_insns_list, ctx); +} + +void gen_shr_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + _gen_arith_op(instr, MIR_OP_SHR, mir_insns_list, ctx); +} + +void gen_mov_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + assert(instr_num_srcs(instr) == 1); + assert(instr_num_dsts(instr) == 1); + + opnd_t src0 = instr_get_src(instr, 0); + opnd_t dst0 = instr_get_dst(instr, 0); + + mir_insn_t* core_insn = mir_insn_malloc(MIR_OP_MOV); + mir_insn_push_front(mir_insns_list, core_insn); + + src0_set_opnd_by_type(src0, core_insn, mir_insns_list, ctx); + mir_insn_set_src1_imm(core_insn, 0); + dst_set_opnd_by_type(dst0, core_insn, mir_insns_list, ctx); +} + +void gen_lea_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + assert(instr_num_srcs(instr) == 1); + assert(instr_num_dsts(instr) == 1); + + opnd_t src0 = instr_get_src(instr, 0); + opnd_t dst0 = instr_get_dst(instr, 0); + assert(opnd_is_reg(dst0)); + + // TODO: make this a helper function if more insns needs this common pattern + if (opnd_is_abs_addr(src0) || opnd_is_rel_addr(src0)) { + mir_insn_t* core_insn = mir_insn_malloc(MIR_OP_MOV); + mir_insn_push_front(mir_insns_list, core_insn); + uint64_t addr = (uint64_t)opnd_get_addr(src0); + mir_insn_set_src0_imm(core_insn, addr); + mir_insn_set_src1_reg(core_insn, DR_REG_NULL); + mir_insn_set_dst_reg(core_insn, opnd_get_reg(dst0)); + } + else if (opnd_is_base_disp(src0)) { + // ADD base, disp -> dst + reg_id_t base = opnd_get_base(src0); + int32_t disp = opnd_get_disp(src0); + mir_insn_t* add_insn = mir_insn_malloc(MIR_OP_ADD); + mir_insn_push_back(mir_insns_list, add_insn); + mir_insn_set_src0_reg(add_insn, base); + mir_insn_set_src1_imm(add_insn, disp); + mir_insn_set_dst_reg(add_insn, opnd_get_reg(dst0)); + } + else { + printf("unsupported opnd type\n"); + assert(false); + } +} + +// FIXME: performance optimize the assertions, or potentialy remove them +// push -> [sp_sub_insn, store_insn] +void gen_push_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + assert(instr_num_srcs(instr) == 2); + assert(instr_num_dsts(instr) == 2); + // assertion: push reg must be expressed as + // push {reg/mem}, sp -> sp, [sp, -size] in dr format + opnd_t src0 = instr_get_src(instr, 0); + assert(opnd_is_reg(src0) || opnd_is_memory_reference(src0)); + assert(opnd_get_reg(instr_get_src(instr, 1)) == REG_XSP); + assert(opnd_get_reg(instr_get_dst(instr, 0)) == REG_XSP); + assert(opnd_get_base(instr_get_dst(instr, 1)) == REG_XSP); + // setting up source operand + uint size = opnd_size_in_bytes(opnd_get_size(src0)); + assert(opnd_get_disp(instr_get_dst(instr, 1)) == (int)(-size)); + // SUB sp, sp, size + mir_insn_t* sp_sub_insn = mir_insn_malloc(MIR_OP_SUB); + mir_insn_push_back(mir_insns_list, sp_sub_insn); + mir_insn_set_src0_imm(sp_sub_insn, size); + mir_insn_set_src1_reg(sp_sub_insn, REG_XSP); + mir_insn_set_dst_reg(sp_sub_insn, REG_XSP); + // setting up opcode by size + mir_opc_t store_opc; + switch (size) { + case 1: store_opc = MIR_OP_ST8; break; + case 2: store_opc = MIR_OP_ST16; break; + case 4: store_opc = MIR_OP_ST32; break; + case 8: store_opc = MIR_OP_ST64; break; + default: assert(false); + } + // ST src0, [sp, size] + mir_insn_t* store_insn = mir_insn_malloc(store_opc); + mir_insn_push_back(mir_insns_list, store_insn); + src2_set_opnd_by_type(src0, store_insn, mir_insns_list, ctx); + mir_insn_set_src0_imm(store_insn, 0); + mir_insn_set_src1_reg(store_insn, REG_XSP); +} + +// pop -> [load_insn, sp_add_insn] +void gen_pop_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + assert(instr_num_srcs(instr) == 2); + assert(instr_num_dsts(instr) == 2); + // assertion: pop reg must be expressed as + // pop [sp, size], sp -> sp, {reg/mem} in dr format + opnd_t dst0 = instr_get_dst(instr, 0); + assert(opnd_is_reg(dst0) || opnd_is_memory_reference(dst0)); + assert(opnd_get_reg(instr_get_src(instr, 0)) == REG_XSP); + assert(opnd_get_base(instr_get_src(instr, 1)) == REG_XSP); + assert(opnd_get_reg(instr_get_dst(instr, 1)) == REG_XSP); + // setting up destination operand + uint size = opnd_size_in_bytes(opnd_get_size(dst0)); + assert(opnd_get_disp(instr_get_src(instr, 1)) == 0); + // setting up opcode by size + mir_opc_t load_opc; + switch (size) { + case 1: load_opc = MIR_OP_LD8; break; + case 2: load_opc = MIR_OP_LD16; break; + case 4: load_opc = MIR_OP_LD32; break; + case 8: load_opc = MIR_OP_LD64; break; + default: assert(false); + } + // LD [sp, size], dst0 + mir_insn_t* load_insn = mir_insn_malloc(load_opc); + mir_insn_push_back(mir_insns_list, load_insn); + mir_insn_set_src0_imm(load_insn, 0); + mir_insn_set_src1_reg(load_insn, REG_XSP); + dst_set_opnd_by_type(dst0, load_insn, mir_insns_list, ctx); + // ADD sp, sp, size + mir_insn_t* sp_add_insn = mir_insn_malloc(MIR_OP_ADD); + mir_insn_push_back(mir_insns_list, sp_add_insn); + mir_insn_set_src0_imm(sp_add_insn, size); + mir_insn_set_src1_reg(sp_add_insn, REG_XSP); + mir_insn_set_dst_reg(sp_add_insn, REG_XSP); +} + +// call -> [sp_sub_insn, store_insn, jmp_insn] +void gen_call_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + assert(instr_num_srcs(instr) == 2); + assert(instr_num_dsts(instr) == 2); + // assertion: call reg must be expressed as + // call addr, sp -> sp, [sp, -size] in dr format + opnd_t src0 = instr_get_src(instr, 0); + // printf("src0 type: %s\n", get_opnd_type(src0)); + assert(opnd_is_pc(src0)); + assert(opnd_get_reg(instr_get_src(instr, 1)) == REG_XSP); + assert(opnd_get_reg(instr_get_dst(instr, 0)) == REG_XSP); + assert(opnd_get_base(instr_get_dst(instr, 1)) == REG_XSP); + // setting up source operand + // opnd_t src0 = instr_get_src(instr, 0); + + // FIXME: use sp to get the size of the registers, check if this assumption is correct + opnd_t src1_sp = instr_get_src(instr, 1); + uint size = opnd_size_in_bytes(opnd_get_size(src1_sp)); + // SUB sp, sp, size + mir_insn_t* sp_sub_insn = mir_insn_malloc(MIR_OP_SUB); + mir_insn_push_back(mir_insns_list, sp_sub_insn); + mir_insn_set_src0_imm(sp_sub_insn, size); + mir_insn_set_src1_reg(sp_sub_insn, REG_XSP); + mir_insn_set_dst_reg(sp_sub_insn, REG_XSP); + // ST src0, [sp, size] + mir_insn_t* store_insn = mir_insn_malloc(MIR_OP_ST32); + mir_insn_push_back(mir_insns_list, store_insn); + app_pc call_addr = instr_get_app_pc(instr); + // get the literal pointer value + mir_insn_set_dst_imm(store_insn, (uint64_t)call_addr); + mir_insn_set_src0_reg(store_insn, REG_XSP); + mir_insn_set_src1_imm(store_insn, size); + // JMP src0 - ommitted for now as it is not needed for the simulation + mir_insn_t* jmp_insn = mir_insn_malloc(MIR_OP_JMP); + mir_insn_push_back(mir_insns_list, jmp_insn); + app_pc jmp_addr = opnd_get_pc(src0); + mir_insn_set_src0_reg(jmp_insn, REG_NULL); + mir_insn_set_src1_imm(jmp_insn, (uint64_t)jmp_addr); + mir_insn_set_dst_reg(jmp_insn, REG_NULL); +} + +void gen_ret_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + assert(instr_num_srcs(instr) == 2); + assert(instr_num_dsts(instr) == 1); + // assertion: ret reg must be expressed as + // ret sp, [sp, size] -> sp, addr in dr format + opnd_t dst0 = instr_get_dst(instr, 0); + assert(opnd_is_reg(dst0) || opnd_is_memory_reference(dst0)); + assert(opnd_get_reg(instr_get_src(instr, 0)) == REG_XSP); + assert(opnd_get_base(instr_get_src(instr, 1)) == REG_XSP); + assert(opnd_get_reg(instr_get_dst(instr, 0)) == REG_XSP); + + // FIXME: fix size to 8 for now + // LD [sp, size], tmp0 -> we only care about the address + mir_insn_t* load_insn = mir_insn_malloc(MIR_OP_LD64); + mir_insn_push_back(mir_insns_list, load_insn); + mir_insn_set_src0_imm(load_insn, 0); + mir_insn_set_src1_reg(load_insn, REG_XSP); + int tmp0 = alloc_tmp_reg(ctx); + mir_insn_set_dst_reg(load_insn, tmp0); + // ADD sp, sp, size + mir_insn_t* sp_add_insn = mir_insn_malloc(MIR_OP_ADD); + mir_insn_set_src0_imm(sp_add_insn, 8); + mir_insn_set_src1_reg(sp_add_insn, REG_XSP); + mir_insn_set_dst_reg(sp_add_insn, REG_XSP); + mir_insn_push_back(mir_insns_list, sp_add_insn); + // JMP dst0 + mir_insn_t* jmp_insn = mir_insn_malloc(MIR_OP_JMP); + mir_insn_push_back(mir_insns_list, jmp_insn); + mir_insn_set_src0_reg(jmp_insn, tmp0); + mir_insn_set_src1_reg(jmp_insn, REG_NULL); + mir_insn_set_dst_reg(jmp_insn, REG_NULL); +} + +void gen_test_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + assert(instr_num_srcs(instr) == 2); + assert(instr_num_dsts(instr) == 0); + opnd_t src0 = instr_get_src(instr, 0); + opnd_t src1 = instr_get_src(instr, 1); + // AND reg0, reg1 -> tmp0 + int tmp0 = alloc_tmp_reg(ctx); + mir_insn_t* and_insn = mir_insn_malloc(MIR_OP_AND); + mir_insn_push_back(mir_insns_list, and_insn); + src0_set_opnd_by_type(src0, and_insn, mir_insns_list, ctx); + src1_set_opnd_by_type(src1, and_insn, mir_insns_list, ctx); + mir_insn_set_dst_reg(and_insn, tmp0); + // W_FLAG tmp0 + mir_insn_t* w_flag_insn = mir_insn_malloc(MIR_OP_W_FLAG); + mir_insn_push_back(mir_insns_list, w_flag_insn); + mir_insn_set_src0_reg(w_flag_insn, tmp0); + mir_insn_set_src1_reg(w_flag_insn, REG_NULL); + mir_insn_set_dst_reg(w_flag_insn, REG_NULL); +} + +void gen_cmp_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + assert(instr_num_srcs(instr) == 2); + assert(instr_num_dsts(instr) == 0); + opnd_t src0 = instr_get_src(instr, 0); + opnd_t src1 = instr_get_src(instr, 1); + // SUB reg0, reg1 -> tmp0 + int tmp0 = alloc_tmp_reg(ctx); + mir_insn_t* sub_insn = mir_insn_malloc(MIR_OP_SUB); + mir_insn_push_back(mir_insns_list, sub_insn); + src0_set_opnd_by_type(src0, sub_insn, mir_insns_list, ctx); + src1_set_opnd_by_type(src1, sub_insn, mir_insns_list, ctx); + mir_insn_set_dst_reg(sub_insn, tmp0); + // W_FLAG tmp0 + mir_insn_t* w_flag_insn = mir_insn_malloc(MIR_OP_W_FLAG); + mir_insn_push_back(mir_insns_list, w_flag_insn); + mir_insn_set_src0_reg(w_flag_insn, tmp0); + mir_insn_set_src1_reg(w_flag_insn, REG_NULL); + mir_insn_set_dst_reg(w_flag_insn, REG_NULL); +} + +void gen_adc_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + uint r_eflags = instr_get_eflags(instr, DR_QUERY_INCLUDE_COND_SRCS); + printf("adc r_eflags: %x\n", r_eflags); + uint w_eflags = instr_get_eflags(instr, DR_QUERY_INCLUDE_COND_DSTS); + printf("adc w_eflags: %x\n", w_eflags); +} + +void gen_jump_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx) { + // DO NOTHING + // assert(instr_num_srcs(instr) == 1); + // assert(instr_num_dsts(instr) == 0); + // opnd_t src0 = instr_get_src(instr, 0); + // assert(opnd_is_pc(src0)); + // // JMP src0 + // mir_insn_t* jmp_insn = mir_insn_malloc(MIR_OP_JMP); + // mir_insn_push_back(mir_insns_list, jmp_insn); + // mir_insn_set_src0_reg(jmp_insn, REG_NULL); + // mir_insn_set_src1_imm(jmp_insn, (uint64_t)opnd_get_pc(src0)); + // mir_insn_set_dst_reg(jmp_insn, REG_NULL); +} \ No newline at end of file diff --git a/clients/drcachesim/mirage/frontend/gen_ops.h b/clients/drcachesim/mirage/frontend/gen_ops.h new file mode 100644 index 00000000000..b088a37ebaf --- /dev/null +++ b/clients/drcachesim/mirage/frontend/gen_ops.h @@ -0,0 +1,39 @@ +#ifndef __GEN_OPS_H__ +#define __GEN_OPS_H__ + +#include "dr_api.h" +#include "assert.h" + +#include "mir_insn.h" +#include "mir_opnd.h" +#include "list.h" + +#include "gen_opnd_api.h" + +void gen_nop_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx); + +void gen_add_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx); +void gen_sub_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx); + +void gen_or_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx); +void gen_and_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx); +void gen_xor_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx); + +void gen_shl_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx); +void gen_shr_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx); + +void gen_mov_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx); +void gen_lea_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx); + +void gen_push_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx); +void gen_pop_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx); +void gen_call_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx); +void gen_ret_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx); + +void gen_test_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx); +void gen_cmp_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx); +void gen_adc_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx); + +void gen_jump_op(instr_t *instr, mir_insn_list_t *mir_insns_list, struct translate_context_t *ctx); + +#endif // __GEN_OPS_H__ \ No newline at end of file diff --git a/clients/drcachesim/mirage/frontend/translate_context.cpp b/clients/drcachesim/mirage/frontend/translate_context.cpp new file mode 100644 index 00000000000..5ae0165f3a6 --- /dev/null +++ b/clients/drcachesim/mirage/frontend/translate_context.cpp @@ -0,0 +1,16 @@ +#include "translate_context.h" + +struct translate_context_t *translate_context_create() { + struct translate_context_t *ctx = (struct translate_context_t *)malloc(sizeof(struct translate_context_t)); + assert(ctx != NULL); + ctx->tmp_reg_map = bitmap_create(NUM_TMP_REGS); + return ctx; +} + +int alloc_tmp_reg(struct translate_context_t *ctx) { + return bitmap_acquire(ctx->tmp_reg_map) + MIR_TMP_REG_START; +} + +void ctx_set_curr_instr(struct translate_context_t *ctx, instr_t *instr) { + ctx->curr_instr = instr; +} \ No newline at end of file diff --git a/clients/drcachesim/mirage/frontend/translate_context.h b/clients/drcachesim/mirage/frontend/translate_context.h new file mode 100644 index 00000000000..ade32556587 --- /dev/null +++ b/clients/drcachesim/mirage/frontend/translate_context.h @@ -0,0 +1,91 @@ +#ifndef __TRANSLATE_CONTEXT_H__ +#define __TRANSLATE_CONTEXT_H__ + +// This file contains the definition of the translation context +// used by the other files in the frontend. + +#include "dr_api.h" +#include "bitmap.h" + +struct translate_context_t { + // The current instruction being translated. + instr_t *curr_instr; + struct bitmap_t *tmp_reg_map; +}; + +// register allocation context + +#define MIR_TMP_REG_START 0x1000 +#define MIR_FLAG_REG_START 0x2000 + +// TMP REGISTERS +enum tmp_reg_t { + TMP_REG_0 = MIR_TMP_REG_START, + TMP_REG_1 = MIR_TMP_REG_START + 1, + TMP_REG_2 = MIR_TMP_REG_START + 2, + TMP_REG_3 = MIR_TMP_REG_START + 3, + TMP_REG_LAST = TMP_REG_3, +}; + + +static const char *tmp_reg_names[] = { + "t0", "t1", "t2", "t3" +}; + +#define NUM_TMP_REGS (TMP_REG_LAST - MIR_TMP_REG_START + 1) + +// FLAG REGISTERS +enum flag_reg_t { + FLAG_REG_CF = MIR_FLAG_REG_START, + FLAG_REG_PF = MIR_FLAG_REG_START + 1, + FLAG_REG_AF = MIR_FLAG_REG_START + 2, + FLAG_REG_ZF = MIR_FLAG_REG_START + 3, + FLAG_REG_SF = MIR_FLAG_REG_START + 4, + FLAG_REG_OF = MIR_FLAG_REG_START + 5, + FLAG_REG_LAST = FLAG_REG_OF, +}; + +static const char *flag_reg_names[] = { + "cf", "pf", "af", "zf", "sf", "of" +}; + +#define NUM_FLAG_REGS (FLAG_REG_LAST - MIR_FLAG_REG_START + 1) + +struct translate_context_t *translate_context_create(); +int alloc_tmp_reg(struct translate_context_t *ctx); + +inline const char *get_tmp_register_name(int reg){ + return tmp_reg_names[reg - MIR_TMP_REG_START]; +} + +inline const char *get_flag_register_name(int reg){ + return flag_reg_names[reg - MIR_FLAG_REG_START]; +} + +#define IS_DR_REG_GPR(reg) ((reg) >= DR_REG_START_GPR && (reg) <= DR_REG_STOP_GPR) +#define GET_DR_REG_GPR_NUM(reg) ((reg) - DR_REG_START_GPR) + +#define IS_DR_REG_64(reg) ((reg) >= DR_REG_START_64 && (reg) <= DR_REG_STOP_64) +#define GET_DR_REG_64_NUM(reg) ((reg) - DR_REG_START_64) + +#define IS_DR_REG_32(reg) ((reg) >= DR_REG_START_32 && (reg) <= DR_REG_STOP_32) +#define GET_DR_REG_32_NUM(reg) ((reg) - DR_REG_START_32) + +#define IS_DR_REG_16(reg) ((reg) >= DR_REG_START_16 && (reg) <= DR_REG_STOP_16) +#define GET_DR_REG_16_NUM(reg) ((reg) - DR_REG_START_16) + +#define IS_DR_REG_8(reg) ((reg) >= DR_REG_START_8 && (reg) <= DR_REG_STOP_8) +#define GET_DR_REG_8_NUM(reg) ((reg) - DR_REG_START_8) + +#define IS_DR_REG_FP(reg) ((reg) >= DR_REG_START_FPR && (reg) <= DR_REG_STOP_FPR) +#define GET_DR_REG_FP_NUM(reg) ((reg) - DR_REG_START_FPR) + +#define IS_TMP_REG(reg) ((reg) >= MIR_TMP_REG_START && (reg) <= TMP_REG_LAST) +#define GET_TMP_REG_NUM(reg) ((reg) - MIR_TMP_REG_START) + +#define IS_FLAG_REG(reg) ((reg) >= MIR_FLAG_REG_START && (reg) <= FLAG_REG_LAST) +#define GET_FLAG_REG_NUM(reg) ((reg) - MIR_FLAG_REG_START) + +void ctx_set_curr_instr(struct translate_context_t *ctx, instr_t *instr); + +#endif // __TRANSLATE_CONTEXT_H__ \ No newline at end of file diff --git a/clients/drcachesim/mirage/ir/mir_insn.cpp b/clients/drcachesim/mirage/ir/mir_insn.cpp new file mode 100644 index 00000000000..0cfb58e152e --- /dev/null +++ b/clients/drcachesim/mirage/ir/mir_insn.cpp @@ -0,0 +1,123 @@ +#include "mir_insn.h" + +mir_insn_t* mir_insn_malloc(mir_opc_t op) { + mir_insn_t* insn = (mir_insn_t*)malloc(sizeof(mir_insn_t)); + assert(insn != NULL); + insn->op = op; + return insn; +} + +void mir_insn_free(mir_insn_t *insn) { + if (insn != NULL) { + free(insn); + } +} + +const char* get_mir_opnd_name(reg_id_t reg) { + if (reg >= MIR_FLAG_REG_START && reg < FLAG_REG_LAST) { + return get_flag_register_name(reg); + } else if (reg >= MIR_TMP_REG_START && reg < TMP_REG_LAST) { + return get_tmp_register_name(reg); + } else { + return get_register_name(reg); + } + return NULL; +} + +void mir_opnd_to_str(mir_opnd_t opnd, char* buffer, size_t buffer_size) { + if (opnd.type == MIR_OPND_REG) { + snprintf(buffer, buffer_size, "R[%s](%d)", get_mir_opnd_name(opnd.value.reg), opnd.value.reg); + } else if (opnd.type == MIR_OPND_IMM) { + if (opnd.value.imm < PRINT_HEX_THRESHOLD) { + snprintf(buffer, buffer_size, "I[%ld]", opnd.value.imm); + } else { + snprintf(buffer, buffer_size, "I[0x%lx]", opnd.value.imm); + } + } +} + +const char* mir_insn_to_str(mir_insn_t* insn) { + static char buffer[256]; + const char* op_str = mir_opc_to_str(insn->op); + + static char opnd0_str[256]; + static char opnd1_str[256]; + static char dst_str[256]; + + mir_opnd_to_str(insn->opnd0, opnd0_str, sizeof(opnd0_str)); + mir_opnd_to_str(insn->opnd1, opnd1_str, sizeof(opnd1_str)); + mir_opnd_to_str(insn->dst, dst_str, sizeof(dst_str)); + + if (mir_opc_is_store(insn->op)) { + snprintf(buffer, sizeof(buffer), "%s %s -> [%s + %s]", op_str, dst_str, opnd1_str, opnd0_str); + } else if (mir_opc_is_load(insn->op)) { + snprintf(buffer, sizeof(buffer), "%s [%s + %s] -> %s", op_str, opnd1_str, opnd0_str, dst_str); + } else if (mir_opc_is_wflag(insn->op)) { + snprintf(buffer, sizeof(buffer), "%s %s", op_str, opnd0_str); + } else { + snprintf(buffer, sizeof(buffer), "%s %s, %s -> %s", + op_str, opnd0_str, opnd1_str, dst_str); + } + + return buffer; +} + +void mir_insn_set_src0_reg(mir_insn_t *insn, reg_id_t reg) { + insn->opnd0.type = MIR_OPND_REG; + insn->opnd0.value.reg = reg; +} + +void mir_insn_set_src0_imm(mir_insn_t *insn, int64_t imm) { + insn->opnd0.type = MIR_OPND_IMM; + insn->opnd0.value.imm = imm; +} + +void mir_insn_set_src1_reg(mir_insn_t *insn, reg_id_t reg) { + insn->opnd1.type = MIR_OPND_REG; + insn->opnd1.value.reg = reg; +} + +void mir_insn_set_src1_imm(mir_insn_t *insn, int64_t imm) { + insn->opnd1.type = MIR_OPND_IMM; + insn->opnd1.value.imm = imm; +} + +void mir_insn_set_dst_reg(mir_insn_t *insn, reg_id_t reg) { + insn->dst.type = MIR_OPND_REG; + insn->dst.value.reg = reg; +} + +void mir_insn_set_dst_imm(mir_insn_t *insn, int64_t imm) { + insn->dst.type = MIR_OPND_IMM; + insn->dst.value.imm = imm; +} + +void init_mir_insn_list(mir_insn_list_t* list) { + list_init(list); +} + +void mir_insn_push_front(mir_insn_list_t* list, mir_insn_t* insn) { + list_push_front(list, &insn->elem); +} + +void mir_insn_push_back(mir_insn_list_t* list, mir_insn_t* insn) { + list_push_back(list, &insn->elem); +} + +void print_mir_insn_list(mir_insn_list_t* list) { + struct list_elem* e; + for (e = list_begin(list); e != list_end(list); e = list_next(e)) { + mir_insn_t* insn = list_entry(e, mir_insn_t, elem); + printf("%s\n", mir_insn_to_str(insn)); + } +} + +void mir_insn_insert_before(mir_insn_t* insn, mir_insn_t* before) { + struct list_elem* e = &before->elem; + list_insert_before(e, &insn->elem); +} + +void mir_insn_insert_after(mir_insn_t* insn, mir_insn_t* after) { + struct list_elem* e = &after->elem; + list_insert_after(e, &insn->elem); +} \ No newline at end of file diff --git a/clients/drcachesim/mirage/ir/mir_insn.h b/clients/drcachesim/mirage/ir/mir_insn.h new file mode 100644 index 00000000000..4297249d71f --- /dev/null +++ b/clients/drcachesim/mirage/ir/mir_insn.h @@ -0,0 +1,50 @@ +#ifndef __MIR_INSN_H__ +#define __MIR_INSN_H__ + +#include "dr_api.h" +#include "mir_opc.h" +#include "mir_opnd.h" + +#include "list.h" + +#include "assert.h" +#include "string.h" + +#define PRINT_HEX_THRESHOLD 0x1000 + +struct mir_insn_t { + mir_opc_t op; // The opcode of the MIR instruction + mir_opnd_t opnd0; // The first source operand of the MIR instruction + mir_opnd_t opnd1; // The second source operand of the MIR instruction + mir_opnd_t dst; // The destination operand of the MIR instruction + struct list_elem elem; // Linked list traversal element +}; + +// Allocate a new MIR instruction with a given opcode +mir_insn_t* mir_insn_malloc(mir_opc_t op); + +// Free a MIR instruction +void mir_insn_free(mir_insn_t *insn); + +// malloc a new opnd +void mir_insn_set_src0_reg(mir_insn_t *insn, reg_id_t reg); +void mir_insn_set_src0_imm(mir_insn_t *insn, int64_t imm); +void mir_insn_set_src1_reg(mir_insn_t *insn, reg_id_t reg); +void mir_insn_set_src1_imm(mir_insn_t *insn, int64_t imm); +void mir_insn_set_dst_reg(mir_insn_t *insn, reg_id_t reg); +void mir_insn_set_dst_imm(mir_insn_t *insn, int64_t imm); // useful for storing immediates + +// Set the opcode of a MIR instruction +void mir_insn_set_op(mir_insn_t *insn, mir_opc_t op); + +// LIST of MIR +typedef struct list mir_insn_list_t; + +void init_mir_insn_list(mir_insn_list_t* list); +void mir_insn_push_front(mir_insn_list_t* list, mir_insn_t* insn); +void mir_insn_push_back(mir_insn_list_t* list, mir_insn_t* insn); +void mir_insn_insert_before(mir_insn_t* insn, mir_insn_t* before); +void mir_insn_insert_after(mir_insn_t* insn, mir_insn_t* after); +void print_mir_insn_list(mir_insn_list_t* list); + +#endif // __MIR_INSN_H__ \ No newline at end of file diff --git a/clients/drcachesim/mirage/ir/mir_opc.h b/clients/drcachesim/mirage/ir/mir_opc.h new file mode 100644 index 00000000000..4c036a01b95 --- /dev/null +++ b/clients/drcachesim/mirage/ir/mir_opc.h @@ -0,0 +1,120 @@ +#ifndef __MIR_OP_H_ +#define __MIR_OP_H + +// This file defines a reduced set of operations for the Mirage IR + +typedef enum { + + // NULL OPERATION + MIR_OP_NULL, + + // REGISTER OPERATION + + // Arithmetic + MIR_OP_MOV, // reg-reg move + + MIR_OP_ADD, // reg-reg add + // MIR_OP_ADDI, // reg-imm add + + MIR_OP_SUB, // TODO: confirm this dst = src1 - src0 + // MIR_OP_SUBI, // reg-imm sub + + MIR_OP_MUL, // reg-reg mul + // MIR_OP_MULI, // reg-imm mul + + MIR_OP_DIV, // reg-reg div + // MIR_OP_DIVI, // reg-imm div + + MIR_OP_DIVU, // reg-reg unsigned-div + // MIR_OP_DIVUI,// reg-imm unsigned-div + + MIR_OP_REM, // reg-reg rem + // MIR_OP_REMI, // reg-imm rem + + MIR_OP_REMU, // reg-reg unsigned-rem + // MIR_OP_REMUI,// reg-imm unsigned-rem + + // Bitwise + MIR_OP_AND, // reg-reg and + // MIR_OP_ANDI, // reg-imm and + + MIR_OP_OR, // reg-reg or + // MIR_OP_ORI, // reg-imm or + + MIR_OP_XOR, // reg-reg xor + // MIR_OP_XORI, // reg-imm xor + + MIR_OP_SHL, // reg-reg shift left + MIR_OP_SHR, // reg-reg shift right + + // Memory + + MIR_OP_LD8, // load 8-bit + MIR_OP_LD16, // load 16-bit + MIR_OP_LD32, // load 32-bit + MIR_OP_LD64, // load 64-bit + + /* STORE is a special case + SRC0 & SRC1 compute the address + DST is the register containing the data to store + Address calculation is done before invoking store */ + // *** STORE DST(SRC2) -> [SRC0 + SRC1] *** + MIR_OP_ST8, // store 8-bit + MIR_OP_ST16, // store 16-bit + MIR_OP_ST32, // store 32-bit + MIR_OP_ST64, // store 64-bit + + // CONTROL FLOW + MIR_OP_JMP, + MIR_OP_BR, + + // EFLAGS + MIR_OP_W_FLAG // set flags based on the value in src0 +} mir_opc_t; + +static const char* mir_opc_str[] = { + "NULL", + "MOV", + "ADD", + "SUB", + "MUL", + "DIV", + "DIVU", + "REM", + "REMU", + "AND", + "OR", + "XOR", + "SHL", + "SHR", + "LD8", + "LD16", + "LD32", + "LD64", + "ST8", + "ST16", + "ST32", + "ST64", + "JMP", + "BR", + "W_FLAG" +}; + +inline const char* mir_opc_to_str(mir_opc_t op){ + // directly cast to string, not as a pointer + return mir_opc_str[op]; +} + +inline bool mir_opc_is_store(mir_opc_t op){ + return op == MIR_OP_ST8 || op == MIR_OP_ST16 || op == MIR_OP_ST32 || op == MIR_OP_ST64; +} + +inline bool mir_opc_is_load(mir_opc_t op){ + return op == MIR_OP_LD8 || op == MIR_OP_LD16 || op == MIR_OP_LD32 || op == MIR_OP_LD64; +} + +inline bool mir_opc_is_wflag(mir_opc_t op){ + return op == MIR_OP_W_FLAG; +} + +#endif // __MIR_OP_H_ \ No newline at end of file diff --git a/clients/drcachesim/mirage/ir/mir_opnd.h b/clients/drcachesim/mirage/ir/mir_opnd.h new file mode 100644 index 00000000000..1ab45133d47 --- /dev/null +++ b/clients/drcachesim/mirage/ir/mir_opnd.h @@ -0,0 +1,22 @@ +#ifndef __MIR_OPND_H_ +#define __MIR_OPND_H_ + +#include "dr_api.h" +#include "assert.h" +#include "translate_context.h" + +enum mir_opnd_type_t { + MIR_OPND_REG, + MIR_OPND_IMM, +}; + +// FIXME: inefficient alignment +struct mir_opnd_t { + mir_opnd_type_t type; + union { + reg_id_t reg; + int64_t imm; + } value; +}; + +#endif // __MIR_OPND_H_ \ No newline at end of file diff --git a/clients/drcachesim/mirage/tests/CMakeLists.txt b/clients/drcachesim/mirage/tests/CMakeLists.txt new file mode 100644 index 00000000000..8c8b6ae582f --- /dev/null +++ b/clients/drcachesim/mirage/tests/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.7) + +include (../../../../make/policies.cmake NO_POLICY_SCOPE) + +add_dr_defines() + +add_executable(test_replayer_all test_replayer_all.cpp) + +target_link_libraries(test_replayer_all mirage) \ No newline at end of file diff --git a/clients/drcachesim/mirage/tests/test_replayer_all.cpp b/clients/drcachesim/mirage/tests/test_replayer_all.cpp new file mode 100644 index 00000000000..36904403407 --- /dev/null +++ b/clients/drcachesim/mirage/tests/test_replayer_all.cpp @@ -0,0 +1,308 @@ +#include "replayer.h" +#include "mir_insn.h" + +// test basic mov operations +void test_replayer_mov() { + printf("===> simple mov ===> "); + Replayer replayer(INIT_STRATEGY_ZERO); + mir_insn_list_t insn_list; + init_mir_insn_list(&insn_list); + + // x1 <- 0xdeadbeef + mir_insn_t reg_imm_mov_insn = { + MIR_OP_MOV, + {MIR_OPND_IMM, {.imm = 0xdeadbeef}}, + {MIR_OPND_IMM, {.imm = 0}}, + {MIR_OPND_REG, {.reg = DR_REG_START_GPR}} + }; + mir_insn_push_back(&insn_list, ®_imm_mov_insn); + + // x2 <- x1 + mir_insn_t reg_reg_mov_insn = { + MIR_OP_MOV, + {MIR_OPND_REG, {.reg = DR_REG_START_GPR}}, + {MIR_OPND_IMM, {.imm = 0}}, + {MIR_OPND_REG, {.reg = DR_REG_START_GPR + 1}} + }; + mir_insn_push_back(&insn_list, ®_reg_mov_insn); + + replayer.replay(&insn_list); + assert(replayer.get_reg_val(DR_REG_START_GPR) == 0xdeadbeef); + assert(replayer.get_reg_val(DR_REG_START_GPR + 1) == 0xdeadbeef); + printf("passed!\n"); +} + +// test basic arithmetic operations +void test_replayer_add() { + printf("===> simple reg&imm ===> "); + Replayer replayer(INIT_STRATEGY_ZERO); + mir_insn_list_t insn_list; + init_mir_insn_list(&insn_list); + + // x1 <- 1 + 2 + mir_insn_t imm_imm_add_insn = { + MIR_OP_ADD, + {MIR_OPND_IMM, {.imm = 1}}, + {MIR_OPND_IMM, {.imm = 2}}, + {MIR_OPND_REG, {.reg = DR_REG_START_GPR}} // 3 + }; + mir_insn_push_back(&insn_list, &imm_imm_add_insn); + + // x2 <- x1 + 3 + mir_insn_t reg_imm_add_insn = { + MIR_OP_ADD, + {MIR_OPND_REG, {.reg = DR_REG_START_GPR}}, // 3 + {MIR_OPND_IMM, {.imm = 3}}, + {MIR_OPND_REG, {.reg = DR_REG_START_GPR + 1}} // 6 + }; + mir_insn_push_back(&insn_list, ®_imm_add_insn); + + replayer.replay(&insn_list); + + // x3 <- x1 + x2 + mir_insn_t reg_reg_add_insn = { + MIR_OP_ADD, + {MIR_OPND_REG, {.reg = DR_REG_START_GPR}}, // 3 + {MIR_OPND_REG, {.reg = DR_REG_START_GPR + 1}}, // 6 + {MIR_OPND_REG, {.reg = DR_REG_START_GPR + 2}} // 9 + }; + mir_insn_push_back(&insn_list, ®_reg_add_insn); + + // TEMP REG + // t0 <- x1 + x2 + mir_insn_t reg_reg_add_tmp_insn = { + MIR_OP_ADD, + {MIR_OPND_REG, {.reg = DR_REG_START_GPR}}, // 3 + {MIR_OPND_REG, {.reg = DR_REG_START_GPR + 1}}, // 6 + {MIR_OPND_REG, {.reg = DR_REG_START_GPR + 2}} // 9 + }; + mir_insn_push_back(&insn_list, ®_reg_add_tmp_insn); + + // x4 <- x1 + t0 + mir_insn_t reg_tmp_add_insn = { + MIR_OP_ADD, + {MIR_OPND_REG, {.reg = DR_REG_START_GPR}}, // 3 + {MIR_OPND_REG, {.reg = DR_REG_START_GPR + 2}}, // 9 + {MIR_OPND_REG, {.reg = DR_REG_START_GPR + 3}} // 12 + }; + mir_insn_push_back(&insn_list, ®_tmp_add_insn); + + replayer.replay(&insn_list); + assert(replayer.get_reg_val(DR_REG_START_GPR) == 3); + assert(replayer.get_reg_val(DR_REG_START_GPR + 1) == 6); + assert(replayer.get_reg_val(DR_REG_START_GPR + 2) == 9); + assert(replayer.get_reg_val(DR_REG_START_GPR + 3) == 12); + printf("passed!\n"); +} + +// test a mix of arithmetic operations +void test_replayer_arithmetic() { + printf("===> arithmetic ===> "); + Replayer replayer(INIT_STRATEGY_ZERO); + mir_insn_list_t insn_list; + init_mir_insn_list(&insn_list); + + // x1 <- 1 + 2 + mir_insn_t imm_imm_add_insn = { + MIR_OP_ADD, + {MIR_OPND_IMM, {.imm = 1}}, + {MIR_OPND_IMM, {.imm = 2}}, + {MIR_OPND_REG, {.reg = DR_REG_START_GPR}} // 3 + }; + mir_insn_push_back(&insn_list, &imm_imm_add_insn); + + // x2 <- x1(3) - 2 + mir_insn_t reg_imm_sub_insn = { + MIR_OP_SUB, + {MIR_OPND_REG, {.reg = DR_REG_START_GPR}}, // 3 + {MIR_OPND_IMM, {.imm = 2}}, + {MIR_OPND_REG, {.reg = DR_REG_START_GPR + 1}} // 1 + }; + mir_insn_push_back(&insn_list, ®_imm_sub_insn); + + // x3 <- x1(3) * x2(1) + mir_insn_t reg_reg_mul_insn = { + MIR_OP_MUL, + {MIR_OPND_REG, {.reg = DR_REG_START_GPR}}, // 3 + {MIR_OPND_REG, {.reg = DR_REG_START_GPR + 1}}, // 1 + {MIR_OPND_REG, {.reg = DR_REG_START_GPR + 2}} // 3 + }; + mir_insn_push_back(&insn_list, ®_reg_mul_insn); + + // x4 <- x1(3) / x2(1) + mir_insn_t reg_reg_div_insn = { + MIR_OP_DIV, + {MIR_OPND_REG, {.reg = DR_REG_START_GPR}}, // 3 + {MIR_OPND_REG, {.reg = DR_REG_START_GPR + 1}}, // 1 + {MIR_OPND_REG, {.reg = DR_REG_START_GPR + 3}} // 3 + }; + mir_insn_push_back(&insn_list, ®_reg_div_insn); + + // x5 <- x1(3) % 2 + mir_insn_t reg_imm_rem_insn = { + MIR_OP_REM, + {MIR_OPND_REG, {.reg = DR_REG_START_GPR}}, // 3 + {MIR_OPND_IMM, {.imm = 2}}, + {MIR_OPND_REG, {.reg = DR_REG_START_GPR + 4}} // 1 + }; + mir_insn_push_back(&insn_list, ®_imm_rem_insn); + + replayer.replay(&insn_list); + assert(replayer.get_reg_val(DR_REG_START_GPR) == 3); + assert(replayer.get_reg_val(DR_REG_START_GPR + 1) == 1); + assert(replayer.get_reg_val(DR_REG_START_GPR + 2) == 3); + assert(replayer.get_reg_val(DR_REG_START_GPR + 3) == 3); + assert(replayer.get_reg_val(DR_REG_START_GPR + 4) == 1); + printf("passed!\n"); +} + +// test basic logical operations +void test_replayer_logical() { + printf("===> logical ===> "); + Replayer replayer(INIT_STRATEGY_ZERO); + mir_insn_list_t insn_list; + init_mir_insn_list(&insn_list); + + // x1 <- 0x10101010 & 0x01010101 + mir_insn_t imm_imm_and_insn = { + MIR_OP_AND, + {MIR_OPND_IMM, {.imm = 0x10101010}}, + {MIR_OPND_IMM, {.imm = 0x01010101}}, + {MIR_OPND_REG, {.reg = DR_REG_START_GPR}} // 0x00000000 + }; + mir_insn_push_back(&insn_list, &imm_imm_and_insn); + + // x2 <- x1(0x00000000) | 0x0f0f0f0f + mir_insn_t reg_imm_or_insn = { + MIR_OP_OR, + {MIR_OPND_REG, {.reg = DR_REG_START_GPR}}, // 0x00000000 + {MIR_OPND_IMM, {.imm = 0x0f0f0f0f}}, + {MIR_OPND_REG, {.reg = DR_REG_START_GPR + 1}} // 0x0f0f0f0f + }; + mir_insn_push_back(&insn_list, ®_imm_or_insn); + + // x3 <- x2(0x0f0f0f0f) ^ 0xf0f0f0f0 + mir_insn_t reg_imm_xor_insn = { + MIR_OP_XOR, + {MIR_OPND_REG, {.reg = DR_REG_START_GPR + 1}}, // 0x0f0f0f0f + {MIR_OPND_IMM, {.imm = 0xf0f0f0f0}}, + {MIR_OPND_REG, {.reg = DR_REG_START_GPR + 2}} // 0xffffffff + }; + mir_insn_push_back(&insn_list, ®_imm_xor_insn); + + replayer.replay(&insn_list); + assert(replayer.get_reg_val(DR_REG_START_GPR) == 0x00000000); + assert(replayer.get_reg_val(DR_REG_START_GPR + 1) == 0x0f0f0f0f); + assert(replayer.get_reg_val(DR_REG_START_GPR + 2) == 0xffffffff); + printf("passed!\n"); +} + +// test basic memory operations +void test_replayer_memory() { + printf("===> memory ===> "); + Replayer replayer(INIT_STRATEGY_ZERO); + mir_insn_list_t insn_list; + init_mir_insn_list(&insn_list); + + // mov x1 <- 0x12345670 + mir_insn_t imm_imm_mov_insn = { + MIR_OP_MOV, + {MIR_OPND_IMM, {.imm = 0x12345670}}, + {MIR_OPND_IMM, {.imm = 0x0}}, + {MIR_OPND_REG, {.reg = DR_REG_START_GPR}} + }; + mir_insn_push_back(&insn_list, &imm_imm_mov_insn); + + + // st 0x12 -> mem[0x12345678] + mir_insn_t reg_imm_st_insn = { + MIR_OP_ST8, + {MIR_OPND_IMM, {.imm = 0x12345678}}, + {MIR_OPND_IMM, {.imm = 0x0}}, + {MIR_OPND_IMM, {.imm = 0x12}} + }; + mir_insn_push_back(&insn_list, ®_imm_st_insn); + + // ld x2 <- mem[0x12345678] + mir_insn_t imm_imm_ld_insn = { + MIR_OP_LD8, + {MIR_OPND_IMM, {.imm = 0x12345678}}, + {MIR_OPND_IMM, {.imm = 0x0}}, + {MIR_OPND_REG, {.reg = DR_REG_START_GPR + 1}} // 0x12 + }; + mir_insn_push_back(&insn_list, &imm_imm_ld_insn); + + // ld x3 <- [x1 + 0x8] + mir_insn_t reg_imm_ld_insn = { + MIR_OP_LD8, + {MIR_OPND_REG, {.reg = DR_REG_START_GPR}}, // 0x12345678 + {MIR_OPND_IMM, {.imm = 0x8}}, + {MIR_OPND_REG, {.reg = DR_REG_START_GPR + 2}} // 0x70 + }; + mir_insn_push_back(&insn_list, ®_imm_ld_insn); + + replayer.replay(&insn_list); + assert(replayer.get_reg_val(DR_REG_START_GPR) == 0x12345670); + assert(replayer.get_reg_val(DR_REG_START_GPR + 1) == 0x12); + assert(replayer.get_reg_val(DR_REG_START_GPR + 2) == 0x12); + printf("passed!\n"); +} + +// test mixed length memory operations +void test_replayer_mixed_length_memory() { + printf("===> mixed length memory ===> "); + Replayer replayer(INIT_STRATEGY_ZERO); + mir_insn_list_t insn_list; + init_mir_insn_list(&insn_list); + + // st32 0xdeadbeef -> mem[0x12345678] + mir_insn_t reg_imm_st_insn = { + MIR_OP_ST32, + {MIR_OPND_IMM, {.imm = 0x12345678}}, + {MIR_OPND_IMM, {.imm = 0x0}}, + {MIR_OPND_IMM, {.imm = 0xdeadbeef}} + }; + mir_insn_push_back(&insn_list, ®_imm_st_insn); + + // ld16 x1 <- mem[0x12345678] + mir_insn_t imm_imm_ld_16_insn = { + MIR_OP_LD16, + {MIR_OPND_IMM, {.imm = 0x12345678}}, + {MIR_OPND_IMM, {.imm = 0x0}}, + {MIR_OPND_REG, {.reg = DR_REG_START_GPR}} // 0xbeef + }; + mir_insn_push_back(&insn_list, &imm_imm_ld_16_insn); + + + // ld8 x1 <- mem[0x12345678 + 2] + mir_insn_t imm_imm_ld_8_insn = { + MIR_OP_LD8, + {MIR_OPND_IMM, {.imm = 0x12345678}}, + {MIR_OPND_IMM, {.imm = 0x2}}, + {MIR_OPND_REG, {.reg = DR_REG_START_GPR + 1}} // 0xad + }; + mir_insn_push_back(&insn_list, &imm_imm_ld_8_insn); + + replayer.replay(&insn_list); + assert(replayer.get_reg_val(DR_REG_START_GPR) == 0xbeef); + assert(replayer.get_reg_val(DR_REG_START_GPR + 1) == 0xad); + printf("passed!\n"); +} + +int main() { + printf("Running tests...\n"); + test_replayer_mov(); + test_replayer_add(); + test_replayer_arithmetic(); + test_replayer_logical(); + test_replayer_memory(); + test_replayer_mixed_length_memory(); + // test_flag_regfile(); + /* TODO: test plans: + - simple assembly program - load from file + - random testing + - corner cases? + */ + printf("All tests passed!\n"); + return 0; +} \ No newline at end of file diff --git a/clients/drcachesim/tools/access_region.cpp b/clients/drcachesim/tools/access_region.cpp new file mode 100644 index 00000000000..be617816b72 --- /dev/null +++ b/clients/drcachesim/tools/access_region.cpp @@ -0,0 +1,119 @@ +#include +#include "access_region.h" + +#include "analysis_tool.h" +#include "memref.h" +#include "trace_entry.h" + +namespace dynamorio { +namespace drmemtrace { + +analysis_tool_t * +access_region_tool_create(uint64_t stack_start, uint64_t stack_end, uint64_t heap_start, + uint64_t heap_end) +{ + return new access_region_t(stack_start, stack_end, heap_start, heap_end); +} + +access_region_t::access_region_t(uint64_t stack_start, uint64_t stack_end, + uint64_t heap_start, uint64_t heap_end) + : stack_start(stack_start) + , stack_end(stack_end) + , heap_start(heap_start) + , heap_end(heap_end) + , stack_accesses(0) + , heap_accesses(0) + , between_accesses(0) + , above_stack_accesses(0) + , below_heap_accesses(0) +{ + // Empty. + // ASSERT(stack_start > stack_end); + // ASSERT(heap_start < heap_end); +} + +access_region_t::~access_region_t() +{ + // Empty. +} + +std::string +access_region_t::initialize_stream(memtrace_stream_t *serial_stream) +{ + serial_stream_ = serial_stream; + return ""; +} + +bool +access_region_t::parallel_shard_supported() +{ + return false; +} + +void * +access_region_t::parallel_shard_init_stream(int shard_index, void *worker_data, + memtrace_stream_t *shard_stream) +{ + return shard_stream; +} + +bool +access_region_t::parallel_shard_exit(void *shard_data) +{ + return true; +} + +std::string +access_region_t::parallel_shard_error(void *shard_data) +{ + return error_string_; +} + +bool +access_region_t::process_memref(const memref_t &memref) +{ + return parallel_shard_memref(serial_stream_, memref); +} + +bool +access_region_t::parallel_shard_memref(void *shard_data, const memref_t &memref) +{ + // memtrace_stream_t *memstream = reinterpret_cast(shard_data); + if (type_is_data(memref.data.type)) { + if (memref.data.addr <= stack_start && memref.data.addr > stack_end) { + stack_accesses++; + } else if (memref.data.addr >= heap_start && memref.data.addr < heap_end) { + heap_accesses++; + } else if (memref.data.addr > stack_start) { + above_stack_accesses++; + } else if (memref.data.addr < heap_end) { + below_heap_accesses++; + } else { + between_accesses++; + } + } + // else, ignore + return true; +} + +bool +access_region_t::print_results() +{ + std::cerr << "Access region tool internal stats:\n"; + + std::cerr << "Stack start: " << stack_start << std::endl; + std::cerr << "Stack end: " << stack_end << std::endl; + std::cerr << "Heap start: " << heap_start << std::endl; + std::cerr << "Heap end: " << heap_end << std::endl; + + std::cerr << "Accesses by region:\n"; + std::cerr << "Stack accesses: " << stack_accesses << std::endl; + std::cerr << "Heap accesses: " << heap_accesses << std::endl; + std::cerr << "Between accesses: " << between_accesses << std::endl; + std::cerr << "Above stack accesses: " << above_stack_accesses << std::endl; + std::cerr << "Below heap accesses: " << below_heap_accesses << std::endl; + return true; +} + +} // namespace drmemtrace +} // namespace dynamorio \ No newline at end of file diff --git a/clients/drcachesim/tools/access_region.h b/clients/drcachesim/tools/access_region.h new file mode 100644 index 00000000000..9ae7eea5ef0 --- /dev/null +++ b/clients/drcachesim/tools/access_region.h @@ -0,0 +1,56 @@ +#ifndef _ACCESS_REGION_H_ +#define _ACCESS_REGION_H_ 1 + +#include + +#include "analysis_tool.h" +#include "memref.h" + +namespace dynamorio { +namespace drmemtrace { + +class access_region_t : public analysis_tool_t { +public: + access_region_t(uint64_t stack_start, uint64_t stack_end, uint64_t heap_start, uint64_t heap_end); + ~access_region_t(); + + std::string + initialize_stream(memtrace_stream_t *serial_stream) override; + + bool + parallel_shard_supported() override; + void * + parallel_shard_init_stream(int shard_index, void *worker_data, memtrace_stream_t *shard_stream) override; + bool + parallel_shard_exit(void *shard_data) override; + std::string + parallel_shard_error(void *shard_data) override; + + bool + process_memref(const memref_t &memref) override; + bool + parallel_shard_memref(void *shard_data, const memref_t &memref) override; + + bool + print_results() override; + + +protected: + uint64_t stack_start; + uint64_t stack_end; + uint64_t heap_start; + uint64_t heap_end; + + uint64_t stack_accesses; + uint64_t heap_accesses; + uint64_t between_accesses; + uint64_t above_stack_accesses; + uint64_t below_heap_accesses; + + shard_type_t shard_type_ = SHARD_BY_THREAD; + memtrace_stream_t *serial_stream_ = nullptr; +}; + +} // namespace drmemtrace +} // namespace dynamorio +#endif /* _ACCESS_REGION_H_ */ \ No newline at end of file diff --git a/clients/drcachesim/tools/access_region_create.h b/clients/drcachesim/tools/access_region_create.h new file mode 100644 index 00000000000..9adcb52ea92 --- /dev/null +++ b/clients/drcachesim/tools/access_region_create.h @@ -0,0 +1,15 @@ +#ifndef _ACCESS_REGION_CREATE_H_ +#define _ACCESS_REGION_CREATE_H_ 1 + +#include "analysis_tool.h" + +namespace dynamorio { +namespace drmemtrace { + +analysis_tool_t * +access_region_tool_create(uint64_t stack_start, uint64_t stack_end, uint64_t heap_start, uint64_t heap_end); + +} // namespace drmemtrace +} // namespace dynamorio + +#endif /* _ACCESS_REGION_CREATE_H_ */ \ No newline at end of file diff --git a/clients/drcachesim/tools/reuse_pattern.cpp b/clients/drcachesim/tools/reuse_pattern.cpp new file mode 100644 index 00000000000..e6a14bdebea --- /dev/null +++ b/clients/drcachesim/tools/reuse_pattern.cpp @@ -0,0 +1,142 @@ +#include +#include "reuse_pattern.h" + +namespace dynamorio { +namespace drmemtrace { + +analysis_tool_t * +reuse_pattern_tool_create() +{ + return new reuse_pattern_t(); +} + +reuse_pattern_t::reuse_pattern_t() +{ + // replayer = new RegAnalyzer(); + replayer = new Replayer(INIT_STRATEGY_RANDOM); + tracing_flag = false; +} + +reuse_pattern_t::~reuse_pattern_t() +{ + // Empty. +} + +std::string +reuse_pattern_t::initialize() +{ + dcontext_.dcontext = dr_standalone_init(); + return ""; +} + +std::string +reuse_pattern_t::initialize_stream(memtrace_stream_t *serial_stream) +{ + serial_stream_ = serial_stream; + return ""; +} + +bool +reuse_pattern_t::parallel_shard_supported() +{ + return false; +} + +void* +reuse_pattern_t::parallel_shard_init_stream(int shard_index, void *worker_data, memtrace_stream_t *shard_stream) +{ + return shard_stream; +} + +bool +reuse_pattern_t::parallel_shard_exit(void *shard_data) +{ + return true; +} + +std::string +reuse_pattern_t::parallel_shard_error(void *shard_data) +{ + return error_string_; +} + +bool +reuse_pattern_t::process_memref(const memref_t &memref) +{ + return parallel_shard_memref(serial_stream_, memref); +} + +bool +reuse_pattern_t::parallel_shard_memref(void *shard_data, const memref_t &memref) +{ + if (type_is_instr(memref.instr.type)) + { + app_pc decode_pc = const_cast(memref.instr.encoding); + // addr_t curr_inst_addr = memref.instr.addr; + const app_pc curr_pc = reinterpret_cast(memref.instr.addr); + + instr_t curr_instr; + instr_init(dcontext_.dcontext, &curr_instr); + app_pc tmp_pc = decode_from_copy(dcontext_.dcontext, decode_pc, curr_pc, &curr_instr); + if (tmp_pc == NULL || !instr_valid(&curr_instr)) + { + instr_free(dcontext_.dcontext, &curr_instr); + // shard->error = "Failed to decode instruction " + to_hex_string(memref.instr.addr); + return false; + } + // read the raw instruction bytes + byte *raw_instr = instr_get_raw_bits(&curr_instr); + int length = instr_length(dcontext_.dcontext, &curr_instr); + if (tracing_flag) { + printf("\n"); + printf("encountered instruction at %p\n", curr_pc); + mir_insn_list_t insn_list; + init_mir_insn_list(&insn_list); + dr_gen_mir_ops(&curr_instr, &insn_list); + replayer->replay(&insn_list); + } + // set tracing flag after the first pattern is found + if (!tracing_flag && check_start_pattern(raw_instr, length)) { + tracing_flag = true; + } else if (tracing_flag && check_end_pattern(raw_instr, length)) { + tracing_flag = false; + } + return true; + } + return true; +} + +bool +reuse_pattern_t::print_results() +{ + replayer->report(); + return true; +} + +// mov 1ceb00da, %rax +bool +reuse_pattern_t::check_start_pattern(byte *raw_instr, int length) +{ + if (length == 5) { + if (raw_instr[0] == 0xb8 && raw_instr[1] == 0xda && raw_instr[2] == 0x00 && raw_instr[3] == 0xeb && raw_instr[4] == 0x1c) { + printf("start pattern found\n"); + return true; + } + } + return false; +} + +// mov babecafe, %rax +bool +reuse_pattern_t::check_end_pattern(byte *raw_instr, int length) +{ + if (length == 5) { + if (raw_instr[0] == 0xb8 && raw_instr[1] == 0xfe && raw_instr[2] == 0xca && raw_instr[3] == 0xbe && raw_instr[4] == 0xba) { + printf("end pattern found\n"); + return true; + } + } + return false; +} +} // namespace drmemtrace +} // namespace dynamorio \ No newline at end of file diff --git a/clients/drcachesim/tools/reuse_pattern.h b/clients/drcachesim/tools/reuse_pattern.h new file mode 100644 index 00000000000..bfbc1840ccb --- /dev/null +++ b/clients/drcachesim/tools/reuse_pattern.h @@ -0,0 +1,89 @@ +#ifndef _REUSE_PATTERN_H_ +#define _REUSE_PATTERN_H_ 1 + +#include + +#include "dr_api.h" +#include "analysis_tool.h" +#include "memref.h" +#include "trace_entry.h" +#include "dr_mir_api.h" +#include "replayer.h" +#include "reg_analyzer.h" +// #include "abstract_replayer.h" + +namespace dynamorio { +namespace drmemtrace { + +class reuse_pattern_t : public analysis_tool_t { +public: + reuse_pattern_t(); + ~reuse_pattern_t(); + + std::string + initialize() override; + + std::string + initialize_stream(memtrace_stream_t *serial_stream) override; + + bool + parallel_shard_supported() override; + void * + parallel_shard_init_stream(int shard_index, void *worker_data, memtrace_stream_t *shard_stream) override; + bool + parallel_shard_exit(void *shard_data) override; + std::string + parallel_shard_error(void *shard_data) override; + + bool + process_memref(const memref_t &memref) override; + bool + parallel_shard_memref(void *shard_data, const memref_t &memref) override; + + bool + print_results() override; + + + + +protected: + + struct dcontext_cleanup_last_t { + public: + ~dcontext_cleanup_last_t() + { + if (dcontext != nullptr) + dr_standalone_exit(); + } + void *dcontext = nullptr; + }; + + /* We make this the first field so that dr_standalone_exit() is called after + * destroying the other fields which may use DR heap. + */ + dcontext_cleanup_last_t dcontext_; + + uint64_t direct_reuse_count; + uint64_t strided_reuse_count; + uint64_t indirection_count; + + _memref_instr_t prev_instr; + _memref_instr_t curr_instr; + + shard_type_t shard_type_ = SHARD_BY_THREAD; + memtrace_stream_t *serial_stream_ = nullptr; + + Replayer *replayer; + + bool tracing_flag; + + bool + check_start_pattern(byte *raw_instr, int length); + + bool + check_end_pattern(byte *raw_instr, int length); +}; + +} // namespace drmemtrace +} // namespace dynamorio +#endif /* _REUSE_PATTERN_H_ */ \ No newline at end of file diff --git a/clients/drcachesim/tools/reuse_pattern_create.h b/clients/drcachesim/tools/reuse_pattern_create.h new file mode 100644 index 00000000000..50c0bbd734f --- /dev/null +++ b/clients/drcachesim/tools/reuse_pattern_create.h @@ -0,0 +1,15 @@ +#ifndef _REUSE_PATTERN_CREATE_H_ +#define _REUSE_PATTERN_CREATE_H_ 1 + +#include "analysis_tool.h" + +namespace dynamorio { +namespace drmemtrace { + +analysis_tool_t * +reuse_pattern_tool_create(); + +} // namespace drmemtrace +} // namespace dynamorio + +#endif /* _REUSE_PATTERN_CREATE_H_ */ \ No newline at end of file