Compare commits
	
		
			4 Commits
		
	
	
		
			82c26acfc8
			...
			ffe730219d
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| ffe730219d | |||
| 60c926c921 | |||
| 9371a09b71 | |||
| 4c3a7386b0 | 
| @@ -31,7 +31,11 @@ if(WITH_LLVM) | |||||||
| 		src/vm/llvm/fp_impl.cpp | 		src/vm/llvm/fp_impl.cpp | ||||||
| 	) | 	) | ||||||
| endif() | endif() | ||||||
|  | if(WITH_ASMJIT) | ||||||
|  | 	list(APPEND LIB_SOURCES | ||||||
|  | 	   src/vm/asmjit/vm_tgc5c.cpp | ||||||
|  |     ) | ||||||
|  | endif() | ||||||
| # library files | # library files | ||||||
| FILE(GLOB GEN_ISS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/iss/arch/*.cpp) | FILE(GLOB GEN_ISS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/iss/arch/*.cpp) | ||||||
| FILE(GLOB GEN_VM_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/vm/interp/vm_*.cpp) | FILE(GLOB GEN_VM_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/vm/interp/vm_*.cpp) | ||||||
| @@ -53,7 +57,10 @@ if(WITH_TCC) | |||||||
| 	FILE(GLOB TCC_GEN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/vm/tcc/vm_*.cpp) | 	FILE(GLOB TCC_GEN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/vm/tcc/vm_*.cpp) | ||||||
| 	list(APPEND LIB_SOURCES ${TCC_GEN_SOURCES}) | 	list(APPEND LIB_SOURCES ${TCC_GEN_SOURCES}) | ||||||
| endif() | endif() | ||||||
|  | if(WITH_ASMJIT) | ||||||
|  | 	FILE(GLOB TCC_GEN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/vm/asmjit/vm_*.cpp) | ||||||
|  | 	list(APPEND LIB_SOURCES ${TCC_GEN_SOURCES}) | ||||||
|  | endif() | ||||||
| if(TARGET yaml-cpp::yaml-cpp) | if(TARGET yaml-cpp::yaml-cpp) | ||||||
|     list(APPEND LIB_SOURCES  |     list(APPEND LIB_SOURCES  | ||||||
|     	src/iss/plugin/cycle_estimate.cpp |     	src/iss/plugin/cycle_estimate.cpp | ||||||
| @@ -182,6 +189,10 @@ if(BUILD_TESTING) | |||||||
| 	add_test(NAME tgc-sim-llvm | 	add_test(NAME tgc-sim-llvm | ||||||
| 	         COMMAND tgc-sim -f ${CMAKE_BINARY_DIR}/../../Firmwares/hello-world/hello --backend llvm) | 	         COMMAND tgc-sim -f ${CMAKE_BINARY_DIR}/../../Firmwares/hello-world/hello --backend llvm) | ||||||
| 	endif() | 	endif() | ||||||
|  |     if(WITH_ASMJIT) | ||||||
|  | 	add_test(NAME tgc-sim-asmjit | ||||||
|  | 	         COMMAND tgc-sim -f ${CMAKE_BINARY_DIR}/../../Firmwares/hello-world/hello --backend asmjit) | ||||||
|  | 	endif() | ||||||
| endif() | endif() | ||||||
| ############################################################################### | ############################################################################### | ||||||
| # | # | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								contrib/instr/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								contrib/instr/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | /*.yaml | ||||||
							
								
								
									
										280
									
								
								gen_input/templates/asmjit/CORENAME.cpp.gtl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								gen_input/templates/asmjit/CORENAME.cpp.gtl
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,280 @@ | |||||||
|  | /******************************************************************************* | ||||||
|  |  * Copyright (C) 2017, 2023 MINRES Technologies GmbH | ||||||
|  |  * All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright notice, | ||||||
|  |  *    this list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |  *    this list of conditions and the following disclaimer in the documentation | ||||||
|  |  *    and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * 3. Neither the name of the copyright holder nor the names of its contributors | ||||||
|  |  *    may be used to endorse or promote products derived from this software | ||||||
|  |  *    without specific prior written permission. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||||
|  |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||||
|  |  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | ||||||
|  |  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||||||
|  |  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||||||
|  |  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||||||
|  |  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||||||
|  |  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||||||
|  |  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||||||
|  |  * POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  * | ||||||
|  |  *******************************************************************************/ | ||||||
|  |  | ||||||
|  | #include <iss/arch/${coreDef.name.toLowerCase()}.h> | ||||||
|  | #include <iss/debugger/gdb_session.h> | ||||||
|  | #include <iss/debugger/server.h> | ||||||
|  | #include <iss/iss.h> | ||||||
|  | #include <iss/asmjit/vm_base.h> | ||||||
|  | #include <asmjit/asmjit.h> | ||||||
|  | #include <util/logging.h> | ||||||
|  |  | ||||||
|  | #ifndef FMT_HEADER_ONLY | ||||||
|  | #define FMT_HEADER_ONLY | ||||||
|  | #endif | ||||||
|  | #include <fmt/format.h> | ||||||
|  |  | ||||||
|  | #include <array> | ||||||
|  | #include <iss/debugger/riscv_target_adapter.h> | ||||||
|  |  | ||||||
|  | namespace iss { | ||||||
|  | namespace asmjit { | ||||||
|  |  | ||||||
|  |  | ||||||
|  | namespace ${coreDef.name.toLowerCase()} { | ||||||
|  | using namespace ::asmjit; | ||||||
|  | using namespace iss::arch; | ||||||
|  | using namespace iss::debugger; | ||||||
|  |  | ||||||
|  | template <typename ARCH> class vm_impl : public iss::asmjit::vm_base<ARCH> { | ||||||
|  | public: | ||||||
|  |     using traits = arch::traits<ARCH>; | ||||||
|  |     using super = typename iss::asmjit::vm_base<ARCH>; | ||||||
|  |     using virt_addr_t = typename super::virt_addr_t; | ||||||
|  |     using phys_addr_t = typename super::phys_addr_t; | ||||||
|  |     using code_word_t = typename super::code_word_t; | ||||||
|  |     using mem_type_e = typename super::mem_type_e; | ||||||
|  |     using addr_t = typename super::addr_t; | ||||||
|  |  | ||||||
|  |     vm_impl(); | ||||||
|  |  | ||||||
|  |     vm_impl(ARCH &core, unsigned core_id = 0, unsigned cluster_id = 0); | ||||||
|  |  | ||||||
|  |     void enableDebug(bool enable) { super::sync_exec = super::ALL_SYNC; } | ||||||
|  |  | ||||||
|  |     target_adapter_if *accquire_target_adapter(server_if *srv) override { | ||||||
|  |         debugger_if::dbg_enabled = true; | ||||||
|  |         if (vm_base<ARCH>::tgt_adapter == nullptr) | ||||||
|  |             vm_base<ARCH>::tgt_adapter = new riscv_target_adapter<ARCH>(srv, this->get_arch()); | ||||||
|  |         return vm_base<ARCH>::tgt_adapter; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | protected: | ||||||
|  |     using vm_base<ARCH>::get_reg_ptr; | ||||||
|  |     using this_class = vm_impl<ARCH>; | ||||||
|  |     using compile_func = continuation_e (this_class::*)(virt_addr_t&, code_word_t, jit_holder&); | ||||||
|  |  | ||||||
|  |     continuation_e gen_single_inst_behavior(virt_addr_t&, unsigned int &, jit_holder&) override; | ||||||
|  |     inline const char *name(size_t index){return traits::reg_aliases.at(index);} | ||||||
|  |  | ||||||
|  |     template<unsigned W, typename U, typename S = typename std::make_signed<U>::type> | ||||||
|  |     inline S sext(U from) { | ||||||
|  |         auto mask = (1ULL<<W) - 1; | ||||||
|  |         auto sign_mask = 1ULL<<(W-1); | ||||||
|  |         return (from & mask) | ((from & sign_mask) ? ~mask : 0); | ||||||
|  |     }  | ||||||
|  | #include "helper_func.h" | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     /**************************************************************************** | ||||||
|  |      * start opcode definitions | ||||||
|  |      ****************************************************************************/ | ||||||
|  |     struct instruction_descriptor { | ||||||
|  |         size_t length; | ||||||
|  |         uint32_t value; | ||||||
|  |         uint32_t mask; | ||||||
|  |         compile_func op; | ||||||
|  |     }; | ||||||
|  |     struct decoding_tree_node{ | ||||||
|  |         std::vector<instruction_descriptor> instrs; | ||||||
|  |         std::vector<decoding_tree_node*> children; | ||||||
|  |         uint32_t submask = std::numeric_limits<uint32_t>::max(); | ||||||
|  |         uint32_t value; | ||||||
|  |         decoding_tree_node(uint32_t value) : value(value){} | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     decoding_tree_node* root {nullptr}; | ||||||
|  |  | ||||||
|  |     const std::array<instruction_descriptor, ${instructions.size}> instr_descr = {{ | ||||||
|  |          /* entries are: size, valid value, valid mask, function ptr */<%instructions.each{instr -> %> | ||||||
|  |         /* instruction ${instr.instruction.name}, encoding '${instr.encoding}' */ | ||||||
|  |         {${instr.length}, ${instr.encoding}, ${instr.mask}, &this_class::__${generator.functionName(instr.name)}},<%}%> | ||||||
|  |     }}; | ||||||
|  |   | ||||||
|  |     /* instruction definitions */<%instructions.eachWithIndex{instr, idx -> %> | ||||||
|  |     /* instruction ${idx}: ${instr.name} */ | ||||||
|  |     continuation_e __${generator.functionName(instr.name)}(virt_addr_t& pc, code_word_t instr, jit_holder& jh){ | ||||||
|  |         uint64_t PC = pc.val; | ||||||
|  |         <%instr.fields.eachLine{%>${it} | ||||||
|  |         <%}%>if(this->disass_enabled){ | ||||||
|  |             /* generate disass */ | ||||||
|  |         } | ||||||
|  |         x86::Compiler& cc = jh.cc; | ||||||
|  |         //ideally only do this if necessary (someone / plugin needs it) | ||||||
|  |         cc.mov(jh.pc,PC); | ||||||
|  |         cc.comment(fmt::format("\\n${instr.name}_{:#x}:",pc.val).c_str()); | ||||||
|  |         this->gen_sync(jh, PRE_SYNC, ${idx}); | ||||||
|  |         pc=pc+ ${instr.length/8}; | ||||||
|  |         | ||||||
|  |         gen_instr_prologue(jh, pc.val); | ||||||
|  |         cc.comment("\\n//behavior:"); | ||||||
|  |         /*generate behavior*/ | ||||||
|  |         <%instr.behavior.eachLine{%>${it} | ||||||
|  |         <%}%> | ||||||
|  |         gen_instr_epilogue(jh); | ||||||
|  |         this->gen_sync(jh, POST_SYNC, ${idx}); | ||||||
|  |     	return returnValue;         | ||||||
|  |     } | ||||||
|  |     <%}%> | ||||||
|  |     /**************************************************************************** | ||||||
|  |      * end opcode definitions | ||||||
|  |      ****************************************************************************/ | ||||||
|  |     continuation_e illegal_intruction(virt_addr_t &pc, code_word_t instr, jit_holder& jh ) { | ||||||
|  |  | ||||||
|  |         return BRANCH; | ||||||
|  |     }     | ||||||
|  |     //decoding functionality | ||||||
|  |  | ||||||
|  |     void populate_decoding_tree(decoding_tree_node* root){ | ||||||
|  |         //create submask | ||||||
|  |         for(auto instr: root->instrs){ | ||||||
|  |             root->submask &= instr.mask; | ||||||
|  |         } | ||||||
|  |         //put each instr according to submask&encoding into children | ||||||
|  |         for(auto instr: root->instrs){ | ||||||
|  |             bool foundMatch = false; | ||||||
|  |             for(auto child: root->children){ | ||||||
|  |                 //use value as identifying trait | ||||||
|  |                 if(child->value == (instr.value&root->submask)){ | ||||||
|  |                     child->instrs.push_back(instr); | ||||||
|  |                     foundMatch = true; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             if(!foundMatch){ | ||||||
|  |                 decoding_tree_node* child = new decoding_tree_node(instr.value&root->submask); | ||||||
|  |                 child->instrs.push_back(instr); | ||||||
|  |                 root->children.push_back(child); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         root->instrs.clear(); | ||||||
|  |         //call populate_decoding_tree for all children | ||||||
|  |         if(root->children.size() >1) | ||||||
|  |             for(auto child: root->children){ | ||||||
|  |                 populate_decoding_tree(child);       | ||||||
|  |             } | ||||||
|  |         else{ | ||||||
|  |             //sort instrs by value of the mask, this works bc we want to have the least restrictive one last | ||||||
|  |             std::sort(root->children[0]->instrs.begin(), root->children[0]->instrs.end(), [](const instruction_descriptor& instr1, const instruction_descriptor& instr2) { | ||||||
|  |             return instr1.mask > instr2.mask; | ||||||
|  |             });  | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     compile_func decode_instr(decoding_tree_node* node, code_word_t word){ | ||||||
|  |         if(!node->children.size()){ | ||||||
|  |             if(node->instrs.size() == 1) return node->instrs[0].op; | ||||||
|  |             for(auto instr : node->instrs){ | ||||||
|  |                 if((instr.mask&word) == instr.value) return instr.op; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else{ | ||||||
|  |             for(auto child : node->children){ | ||||||
|  |                 if (child->value == (node->submask&word)){ | ||||||
|  |                     return decode_instr(child, word); | ||||||
|  |                 }   | ||||||
|  |             }   | ||||||
|  |         } | ||||||
|  |         return nullptr; | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | template <typename CODE_WORD> void debug_fn(CODE_WORD instr) { | ||||||
|  |     volatile CODE_WORD x = instr; | ||||||
|  |     instr = 2 * x; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <typename ARCH> vm_impl<ARCH>::vm_impl() { this(new ARCH()); } | ||||||
|  |  | ||||||
|  | template <typename ARCH> | ||||||
|  | vm_impl<ARCH>::vm_impl(ARCH &core, unsigned core_id, unsigned cluster_id) | ||||||
|  | : vm_base<ARCH>(core, core_id, cluster_id) { | ||||||
|  |     root = new decoding_tree_node(std::numeric_limits<uint32_t>::max()); | ||||||
|  |     for(auto instr: instr_descr){ | ||||||
|  |         root->instrs.push_back(instr); | ||||||
|  |     } | ||||||
|  |     populate_decoding_tree(root); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <typename ARCH> | ||||||
|  | continuation_e | ||||||
|  | vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, unsigned int &inst_cnt, jit_holder& jh) { | ||||||
|  |     enum {TRAP_ID=1<<16}; | ||||||
|  |     code_word_t instr = 0; | ||||||
|  |     phys_addr_t paddr(pc); | ||||||
|  |     auto *const data = (uint8_t *)&instr; | ||||||
|  |     if(this->core.has_mmu()) | ||||||
|  |         paddr = this->core.virt2phys(pc); | ||||||
|  |     auto res = this->core.read(paddr, 4, data); | ||||||
|  |     if (res != iss::Ok) | ||||||
|  |         throw trap_access(TRAP_ID, pc.val); | ||||||
|  |     if (instr == 0x0000006f || (instr&0xffff)==0xa001) | ||||||
|  |         throw simulation_stopped(0); // 'J 0' or 'C.J 0' | ||||||
|  |     ++inst_cnt; | ||||||
|  |     auto f = decode_instr(root, instr); | ||||||
|  |     if (f == nullptr)  | ||||||
|  |         f = &this_class::illegal_intruction; | ||||||
|  |     return (this->*f)(pc, instr, jh); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | } // namespace ${coreDef.name.toLowerCase()} | ||||||
|  |  | ||||||
|  | template <> | ||||||
|  | std::unique_ptr<vm_if> create<arch::${coreDef.name.toLowerCase()}>(arch::${coreDef.name.toLowerCase()} *core, unsigned short port, bool dump) { | ||||||
|  |     auto ret = new ${coreDef.name.toLowerCase()}::vm_impl<arch::${coreDef.name.toLowerCase()}>(*core, dump); | ||||||
|  |     if (port != 0) debugger::server<debugger::gdb_session>::run_server(ret, port); | ||||||
|  |     return std::unique_ptr<vm_if>(ret); | ||||||
|  | } | ||||||
|  | } // namespace asmjit | ||||||
|  | } // namespace iss | ||||||
|  |  | ||||||
|  | #include <iss/factory.h> | ||||||
|  | #include <iss/arch/riscv_hart_m_p.h> | ||||||
|  | #include <iss/arch/riscv_hart_mu_p.h> | ||||||
|  | namespace iss { | ||||||
|  | namespace { | ||||||
|  | volatile std::array<bool, 2> dummy = { | ||||||
|  |         core_factory::instance().register_creator("${coreDef.name.toLowerCase()}|m_p|asmjit", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{ | ||||||
|  |             auto* cpu = new iss::arch::riscv_hart_m_p<iss::arch::${coreDef.name.toLowerCase()}>(); | ||||||
|  |             auto* vm = new asmjit::${coreDef.name.toLowerCase()}::vm_impl<arch::${coreDef.name.toLowerCase()}>(*cpu, false); | ||||||
|  |             if (port != 0) debugger::server<debugger::gdb_session>::run_server(vm, port); | ||||||
|  |             return {cpu_ptr{cpu}, vm_ptr{vm}}; | ||||||
|  |         }), | ||||||
|  |         core_factory::instance().register_creator("${coreDef.name.toLowerCase()}|mu_p|asmjit", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{ | ||||||
|  |             auto* cpu = new iss::arch::riscv_hart_mu_p<iss::arch::${coreDef.name.toLowerCase()}>(); | ||||||
|  |             auto* vm = new asmjit::${coreDef.name.toLowerCase()}::vm_impl<arch::${coreDef.name.toLowerCase()}>(*cpu, false); | ||||||
|  |             if (port != 0) debugger::server<debugger::gdb_session>::run_server(vm, port); | ||||||
|  |             return {cpu_ptr{cpu}, vm_ptr{vm}}; | ||||||
|  |         }) | ||||||
|  | }; | ||||||
|  | } | ||||||
|  | } | ||||||
							
								
								
									
										474
									
								
								src/vm/asmjit/helper_func.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										474
									
								
								src/vm/asmjit/helper_func.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,474 @@ | |||||||
|  |  | ||||||
|  |          | ||||||
|  | x86::Mem get_reg_ptr(jit_holder& jh, unsigned idx){  | ||||||
|  |     | ||||||
|  |         x86::Gp tmp_ptr = jh.cc.newUIntPtr("tmp_ptr"); | ||||||
|  |         jh.cc.mov(tmp_ptr, jh.regs_base_ptr); | ||||||
|  |         jh.cc.add(tmp_ptr, traits::reg_byte_offsets[idx]); | ||||||
|  |         switch(traits::reg_bit_widths[idx]){ | ||||||
|  |         case 8: | ||||||
|  |             return x86::ptr_8(tmp_ptr); | ||||||
|  |         case 16: | ||||||
|  |             return x86::ptr_16(tmp_ptr); | ||||||
|  |         case 32: | ||||||
|  |             return x86::ptr_32(tmp_ptr); | ||||||
|  |         case 64: | ||||||
|  |             return x86::ptr_64(tmp_ptr); | ||||||
|  |         default: | ||||||
|  |             throw std::runtime_error("Invalid reg size in get_reg_ptr"); | ||||||
|  |         } | ||||||
|  | } | ||||||
|  | x86::Gp get_reg_for(jit_holder& jh, unsigned idx){ | ||||||
|  |     //can check for regs in jh and return them instead of creating new ones | ||||||
|  |     switch(traits::reg_bit_widths[idx]){ | ||||||
|  |         case 8: | ||||||
|  |             return jh.cc.newInt8(); | ||||||
|  |         case 16: | ||||||
|  |             return jh.cc.newInt16(); | ||||||
|  |         case 32: | ||||||
|  |             return jh.cc.newInt32(); | ||||||
|  |         case 64: | ||||||
|  |             return jh.cc.newInt64(); | ||||||
|  |         default: | ||||||
|  |             throw std::runtime_error("Invalid reg size in get_reg_ptr"); | ||||||
|  |         } | ||||||
|  | } | ||||||
|  | x86::Gp get_reg_for(jit_holder& jh, unsigned size, bool is_signed){ | ||||||
|  |     if(is_signed) | ||||||
|  |         switch(size){ | ||||||
|  |             case 8: | ||||||
|  |                 return jh.cc.newInt8(); | ||||||
|  |             case 16: | ||||||
|  |                 return jh.cc.newInt16(); | ||||||
|  |             case 32: | ||||||
|  |                 return jh.cc.newInt32(); | ||||||
|  |             case 64: | ||||||
|  |                 return jh.cc.newInt64(); | ||||||
|  |             default: | ||||||
|  |                 throw std::runtime_error("Invalid reg size in get_reg_ptr"); | ||||||
|  |             } | ||||||
|  |     else | ||||||
|  |         switch(size){ | ||||||
|  |         case 8: | ||||||
|  |             return jh.cc.newUInt8(); | ||||||
|  |         case 16: | ||||||
|  |             return jh.cc.newUInt16(); | ||||||
|  |         case 32: | ||||||
|  |             return jh.cc.newUInt32(); | ||||||
|  |         case 64: | ||||||
|  |             return jh.cc.newUInt64(); | ||||||
|  |         default: | ||||||
|  |             throw std::runtime_error("Invalid reg size in get_reg_ptr"); | ||||||
|  |         } | ||||||
|  | } | ||||||
|  | inline x86::Gp load_reg_from_mem(jit_holder& jh, unsigned idx){ | ||||||
|  |     auto ptr = get_reg_ptr(jh, idx); | ||||||
|  |     auto reg = get_reg_for(jh, idx); | ||||||
|  |     jh.cc.mov(reg, ptr); | ||||||
|  |     return reg; | ||||||
|  | } | ||||||
|  | inline void write_reg_to_mem(jit_holder& jh, x86::Gp reg, unsigned idx){ | ||||||
|  |     auto ptr = get_reg_ptr(jh, idx); | ||||||
|  |     jh.cc.mov(ptr, reg); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void gen_instr_prologue(jit_holder& jh, addr_t pc){ | ||||||
|  |     auto& cc = jh.cc; | ||||||
|  |  | ||||||
|  |     cc.comment("\n//(*icount)++;"); | ||||||
|  |     cc.inc(get_reg_ptr(jh, traits::ICOUNT)); | ||||||
|  |  | ||||||
|  |     cc.comment("\n//*pc=*next_pc;"); | ||||||
|  |     cc.mov(get_reg_ptr(jh, traits::PC), jh.next_pc); | ||||||
|  |  | ||||||
|  |     cc.comment("\n//*trap_state=*pending_trap;"); | ||||||
|  |     cc.mov(get_reg_ptr(jh, traits::PENDING_TRAP), jh.trap_state); | ||||||
|  |      | ||||||
|  |     cc.comment("\n//increment *next_pc"); | ||||||
|  |     cc.mov(jh.next_pc, pc); | ||||||
|  |  | ||||||
|  | } | ||||||
|  | void gen_instr_epilogue(jit_holder& jh){ | ||||||
|  |     auto& cc = jh.cc; | ||||||
|  |     | ||||||
|  |     cc.comment("\n//if(*trap_state!=0) goto trap_entry;"); | ||||||
|  |     cc.test(jh.trap_state, jh.trap_state); | ||||||
|  |     cc.jnz(jh.trap_entry); | ||||||
|  |  | ||||||
|  |     //Does this need to be done after every single instruction? | ||||||
|  |     cc.comment("\n//write back regs to mem"); | ||||||
|  |     write_reg_to_mem(jh, jh.pc, traits::PC); | ||||||
|  |     write_reg_to_mem(jh, jh.next_pc, traits::NEXT_PC); | ||||||
|  |     write_reg_to_mem(jh, jh.trap_state, traits::TRAP_STATE); | ||||||
|  |  | ||||||
|  |    | ||||||
|  | } | ||||||
|  | void gen_block_prologue(jit_holder& jh) override{ | ||||||
|  |  | ||||||
|  |     jh.pc = load_reg_from_mem(jh, traits::PC); | ||||||
|  |     jh.next_pc = load_reg_from_mem(jh, traits::NEXT_PC); | ||||||
|  |     jh.trap_state = load_reg_from_mem(jh, traits::TRAP_STATE); | ||||||
|  | } | ||||||
|  | void gen_block_epilogue(jit_holder& jh) override{ | ||||||
|  |     x86::Compiler& cc = jh.cc; | ||||||
|  |     cc.comment("\n//return *next_pc;"); | ||||||
|  |     cc.ret(jh.next_pc); | ||||||
|  |  | ||||||
|  |     cc.bind(jh.trap_entry); | ||||||
|  |     cc.comment("\n//enter_trap(core_ptr, *trap_state, *pc, 0);"); | ||||||
|  |  | ||||||
|  |     x86::Gp current_trap_state =  get_reg_for(jh, traits::TRAP_STATE); | ||||||
|  |     cc.mov(current_trap_state, get_reg_ptr(jh, traits::TRAP_STATE)); | ||||||
|  |  | ||||||
|  |     x86::Gp current_pc = get_reg_for(jh, traits::PC); | ||||||
|  |     cc.mov(current_pc,  get_reg_ptr(jh, traits::PC)); | ||||||
|  |  | ||||||
|  |     x86::Gp instr = cc.newInt32("instr"); | ||||||
|  |     cc.mov(instr, 0); | ||||||
|  |     InvokeNode* call_enter_trap; | ||||||
|  |     cc.invoke(&call_enter_trap, &enter_trap, FuncSignatureT<uint64_t, void*, uint64_t, uint64_t, uint64_t>()); | ||||||
|  |     call_enter_trap->setArg(0, jh.arch_if_ptr); | ||||||
|  |     call_enter_trap->setArg(1, current_trap_state); | ||||||
|  |     call_enter_trap->setArg(2, current_pc); | ||||||
|  |     call_enter_trap->setArg(3, instr); | ||||||
|  |     cc.comment("\n//*last_branch = std::numeric_limits<uint32_t>::max();"); | ||||||
|  |     cc.mov(get_reg_ptr(jh,traits::LAST_BRANCH), std::numeric_limits<uint32_t>::max()); | ||||||
|  |     cc.comment("\n//return *next_pc;"); | ||||||
|  |     cc.ret(jh.next_pc); | ||||||
|  |  | ||||||
|  | } | ||||||
|  | //TODO implement | ||||||
|  |  | ||||||
|  | void gen_raise(jit_holder& jh, uint16_t trap_id, uint16_t cause) { | ||||||
|  |     jh.cc.comment("//gen_raise"); | ||||||
|  | } | ||||||
|  | void gen_wait(jit_holder& jh, unsigned type) { | ||||||
|  |     jh.cc.comment("//gen_wait"); | ||||||
|  | } | ||||||
|  | void gen_leave(jit_holder& jh, unsigned lvl){ | ||||||
|  |     jh.cc.comment("//gen_leave"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | enum operation {add, sub, band, bor, bxor, shl, sar , shr}; | ||||||
|  |  | ||||||
|  | template <typename T, typename = std::enable_if_t<std::is_integral<T>::value || std::is_same<T, x86::Gp>::value>> | ||||||
|  | x86::Gp gen_operation(jit_holder& jh, operation op, x86::Gp a, T b){ | ||||||
|  |     x86::Compiler& cc = jh.cc; | ||||||
|  |     switch (op) { | ||||||
|  |         case add: { cc.add(a, b); break; } | ||||||
|  |         case sub: { cc.sub(a, b); break; } | ||||||
|  |         case band: { cc.and_(a, b); break; } | ||||||
|  |         case bor: { cc.or_(a, b); break; } | ||||||
|  |         case bxor: { cc.xor_(a, b); break; } | ||||||
|  |         case shl: { cc.shl(a, b); break; } | ||||||
|  |         case sar: { cc.sar(a, b); break; } | ||||||
|  |         case shr: { cc.shr(a, b); break; } | ||||||
|  |         default: throw std::runtime_error(fmt::format("Current operation {} not supported in gen_operation (operation)", op)); | ||||||
|  |     } | ||||||
|  |     return a; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | enum three_operand_operation{imul, mul, idiv, div, srem, urem}; | ||||||
|  |  | ||||||
|  | x86::Gp gen_operation(jit_holder& jh, three_operand_operation op, x86::Gp a, x86::Gp b){ | ||||||
|  |     x86::Compiler& cc = jh.cc; | ||||||
|  |     switch (op) { | ||||||
|  |         case imul: {             | ||||||
|  |             x86::Gp dummy = cc.newInt64(); | ||||||
|  |             cc.imul(dummy, a.r64(), b.r64()); | ||||||
|  |             return a; | ||||||
|  |         } | ||||||
|  |         case mul: {             | ||||||
|  |             x86::Gp dummy = cc.newInt64(); | ||||||
|  |             cc.mul(dummy, a.r64(), b.r64()); | ||||||
|  |             return a; | ||||||
|  |         } | ||||||
|  |         case idiv: { | ||||||
|  |             x86::Gp dummy = cc.newInt64(); | ||||||
|  |             cc.mov(dummy, 0); | ||||||
|  |             cc.idiv(dummy, a.r64(), b.r64()); | ||||||
|  |             return a; | ||||||
|  |         } | ||||||
|  |         case div: { | ||||||
|  |             x86::Gp dummy = cc.newInt64(); | ||||||
|  |             cc.mov(dummy, 0); | ||||||
|  |             cc.div(dummy, a.r64(), b.r64()); | ||||||
|  |             return a; | ||||||
|  |         } | ||||||
|  |         case srem:{ | ||||||
|  |             x86::Gp rem = cc.newInt32(); | ||||||
|  |             cc.mov(rem, 0); | ||||||
|  |             auto a_reg = cc.newInt32(); | ||||||
|  |             cc.mov(a_reg, a.r32()); | ||||||
|  |             cc.idiv(rem, a_reg, b.r32()); | ||||||
|  |             return rem; | ||||||
|  |         }   | ||||||
|  |         case urem:{ | ||||||
|  |             x86::Gp rem = cc.newInt32(); | ||||||
|  |             cc.mov(rem, 0); | ||||||
|  |             auto a_reg = cc.newInt32(); | ||||||
|  |             cc.mov(a_reg, a.r32()); | ||||||
|  |             cc.div(rem, a_reg, b.r32()); | ||||||
|  |             return rem; | ||||||
|  |         }          | ||||||
|  |  | ||||||
|  |         default: throw std::runtime_error(fmt::format("Current operation {} not supported in gen_operation (three_operand)", op)); | ||||||
|  |     } | ||||||
|  |     return a; | ||||||
|  | } | ||||||
|  | template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>> | ||||||
|  | x86::Gp gen_operation(jit_holder& jh, three_operand_operation op, x86::Gp a, T b){ | ||||||
|  |     x86::Gp b_reg = jh.cc.newInt32(); | ||||||
|  | /*     switch(a.size()){ | ||||||
|  |         case 1: b_reg = jh.cc.newInt8(); break; | ||||||
|  |         case 2: b_reg = jh.cc.newInt16(); break; | ||||||
|  |         case 4: b_reg = jh.cc.newInt32(); break; | ||||||
|  |         case 8: b_reg = jh.cc.newInt64(); break; | ||||||
|  |         default: throw std::runtime_error(fmt::format("Invalid size ({}) in gen operation", a.size())); | ||||||
|  |     } */ | ||||||
|  |     jh.cc.mov(b_reg, b); | ||||||
|  |     return gen_operation(jh, op, a, b_reg); | ||||||
|  | } | ||||||
|  | enum comparison_operation{land, lor, eq, ne, lt, ltu, gt, gtu, lte, lteu, gte, gteu}; | ||||||
|  |  | ||||||
|  | template <typename T, typename = std::enable_if_t<std::is_integral<T>::value || std::is_same<T, x86::Gp>::value>> | ||||||
|  | x86::Gp gen_operation(jit_holder& jh, comparison_operation op, x86::Gp a, T b){ | ||||||
|  |     x86::Compiler& cc = jh.cc; | ||||||
|  |     x86::Gp tmp = cc.newInt8(); | ||||||
|  |     cc.mov(tmp,1); | ||||||
|  |     Label label_then = cc.newLabel(); | ||||||
|  |     cc.cmp(a,b); | ||||||
|  |     switch (op) { | ||||||
|  |     case eq: cc.je(label_then); break; | ||||||
|  |     case ne: cc.jne(label_then); break; | ||||||
|  |     case lt: cc.jl(label_then); break; | ||||||
|  |     case ltu: cc.jb(label_then); break; | ||||||
|  |     case gt: cc.jg(label_then); break; | ||||||
|  |     case gtu: cc.ja(label_then); break; | ||||||
|  |     case lte: cc.jle(label_then); break; | ||||||
|  |     case lteu: cc.jbe(label_then); break; | ||||||
|  |     case gte: cc.jge(label_then); break; | ||||||
|  |     case gteu: cc.jae(label_then); break; | ||||||
|  |     case land: { | ||||||
|  |         Label label_false = cc.newLabel(); | ||||||
|  |         cc.cmp(a, 0); | ||||||
|  |         cc.je(label_false); | ||||||
|  |         auto b_reg = cc.newInt8(); | ||||||
|  |         cc.mov(b_reg, b); | ||||||
|  |         cc.cmp(b_reg, 0); | ||||||
|  |         cc.je(label_false); | ||||||
|  |         cc.jmp(label_then); | ||||||
|  |         cc.bind(label_false); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     case lor: { | ||||||
|  |         cc.cmp(a, 0); | ||||||
|  |         cc.jne(label_then); | ||||||
|  |         auto b_reg = cc.newInt8(); | ||||||
|  |         cc.mov(b_reg, b); | ||||||
|  |         cc.cmp(b_reg, 0); | ||||||
|  |         cc.jne(label_then);    | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     default: throw std::runtime_error(fmt::format("Current operation {} not supported in gen_operation (comparison)", op)); | ||||||
|  |     } | ||||||
|  |     cc.mov(tmp,0); | ||||||
|  |     cc.bind(label_then); | ||||||
|  |     return tmp; | ||||||
|  | } | ||||||
|  | enum binary_operation{lnot, inc, dec, bnot, neg}; | ||||||
|  |  | ||||||
|  | x86::Gp gen_operation(jit_holder& jh, binary_operation op, x86::Gp a){ | ||||||
|  |     x86::Compiler& cc = jh.cc; | ||||||
|  |     switch (op) { | ||||||
|  |         case lnot: throw std::runtime_error("Current operation not supported in gen_operation(lnot)"); | ||||||
|  |         case inc: { cc.inc(a); break; } | ||||||
|  |         case dec: { cc.dec(a); break; } | ||||||
|  |         case bnot: { cc.not_(a); break; } | ||||||
|  |         case neg: { cc.neg(a); break; } | ||||||
|  |         default: throw std::runtime_error(fmt::format("Current operation {} not supported in gen_operation (unary)", op)); | ||||||
|  |     } | ||||||
|  |     return a; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* template <typename T> | ||||||
|  | inline typename std::enable_if_t<std::is_unsigned<T>::value, x86::Gp> gen_ext(jit_holder& jh, T val, unsigned size, bool is_signed) const { | ||||||
|  |     auto val_reg = get_reg_for(jh, sizeof(val)*8); | ||||||
|  |     auto tmp = get_reg_for(jh, size); | ||||||
|  |     jh.cc.mov(val_reg, val); | ||||||
|  |     if(is_signed) jh.cc.movsx(tmp, val_reg); | ||||||
|  |     else jh.cc.movzx(tmp,val_reg); | ||||||
|  |     return tmp; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | template <typename T> | ||||||
|  | inline typename std::enable_if_t<std::is_signed<T>::value, x86::Gp> gen_ext(jit_holder& jh, T val, unsigned size, bool is_signed) const { | ||||||
|  |     auto val_reg = get_reg_for(jh, sizeof(val)*8); | ||||||
|  |     auto tmp = get_reg_for(jh, size); | ||||||
|  |     jh.cc.mov(val_reg, val); | ||||||
|  |     if(is_signed) jh.cc.movsx(tmp, val_reg); | ||||||
|  |     else jh.cc.movzx(tmp,val_reg); | ||||||
|  |     return tmp; | ||||||
|  | } */ | ||||||
|  | template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>> | ||||||
|  | inline x86::Gp gen_ext(jit_holder& jh, T val, unsigned size, bool is_signed) { | ||||||
|  |     auto val_reg = get_reg_for(jh, sizeof(val)*8); | ||||||
|  |     jh.cc.mov(val_reg, val); | ||||||
|  |     return gen_ext(jh, val_reg, size, is_signed); | ||||||
|  | } | ||||||
|  | //explicit Gp size cast | ||||||
|  | inline x86::Gp gen_ext(jit_holder& jh, x86::Gp val, unsigned size, bool is_signed) { | ||||||
|  |     auto& cc = jh.cc; | ||||||
|  |     if(is_signed){ | ||||||
|  |         switch(val.size()){ | ||||||
|  |             case 1:    cc.cbw(val); break; | ||||||
|  |             case 2:    cc.cwde(val); break; | ||||||
|  |             case 4:    cc.cdqe(val); break; | ||||||
|  |             case 8:    break; | ||||||
|  |             default: throw std::runtime_error("Invalid register size in gen_ext"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     switch(size){ | ||||||
|  |         case 8:  cc.and_(val,std::numeric_limits<uint8_t>::max()); return val.r8(); | ||||||
|  |         case 16: cc.and_(val,std::numeric_limits<uint16_t>::max()); return val.r16(); | ||||||
|  |         case 32: cc.and_(val,std::numeric_limits<uint32_t>::max()); return val.r32(); | ||||||
|  |         case 64: cc.and_(val,std::numeric_limits<uint64_t>::max()); return val.r64(); | ||||||
|  |         case 128: return val.r64(); | ||||||
|  |         default: throw std::runtime_error("Invalid size in gen_ext"); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | inline x86::Gp gen_read_mem(jit_holder& jh, mem_type_e type, x86::Gp addr, uint32_t length){ | ||||||
|  |     x86::Compiler& cc = jh.cc; | ||||||
|  |     auto ret_reg = cc.newInt32(); | ||||||
|  |  | ||||||
|  |     auto mem_type_reg = cc.newInt32(); | ||||||
|  |     cc.mov(mem_type_reg, type); | ||||||
|  |  | ||||||
|  |     auto space_reg = cc.newInt32(); | ||||||
|  |     cc.mov(space_reg, static_cast<uint16_t>(iss::address_type::VIRTUAL)); | ||||||
|  |      | ||||||
|  |     auto val_ptr = cc.newUIntPtr(); | ||||||
|  |     cc.mov(val_ptr, read_mem_buf); | ||||||
|  |  | ||||||
|  |     InvokeNode* invokeNode; | ||||||
|  |     uint64_t mask = 0; | ||||||
|  |     x86::Gp val_reg = cc.newInt64(); | ||||||
|  |  | ||||||
|  |     switch(length){ | ||||||
|  |     case 1:{ | ||||||
|  |         cc.invoke(&invokeNode, &read_mem1, FuncSignatureT<uint32_t, uint64_t, uint32_t, uint32_t, uint64_t, uintptr_t>()); | ||||||
|  |         mask = std::numeric_limits<uint8_t>::max(); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     case 2:{ | ||||||
|  |         cc.invoke(&invokeNode, &read_mem2, FuncSignatureT<uint32_t, uint64_t, uint32_t, uint32_t, uint64_t, uintptr_t>()); | ||||||
|  |         mask = std::numeric_limits<uint16_t>::max(); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     case 4:{ | ||||||
|  |         cc.invoke(&invokeNode, &read_mem4, FuncSignatureT<uint32_t, uint64_t, uint32_t, uint32_t, uint64_t, uintptr_t>()); | ||||||
|  |         mask = std::numeric_limits<uint32_t>::max(); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     case 8:{ | ||||||
|  |         cc.invoke(&invokeNode, &read_mem8, FuncSignatureT<uint32_t, uint64_t, uint32_t, uint32_t, uint64_t, uintptr_t>()); | ||||||
|  |         mask = std::numeric_limits<uint64_t>::max(); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     default: throw std::runtime_error(fmt::format("Invalid length ({}) in gen_read_mem",length)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     invokeNode->setRet(0, ret_reg); | ||||||
|  |     invokeNode->setArg(0, jh.arch_if_ptr); | ||||||
|  |     invokeNode->setArg(1, space_reg); | ||||||
|  |     invokeNode->setArg(2, mem_type_reg); | ||||||
|  |     invokeNode->setArg(3, addr); | ||||||
|  |     invokeNode->setArg(4, val_ptr); | ||||||
|  |  | ||||||
|  |     cc.mov(val_reg, x86::ptr_64(val_ptr)); | ||||||
|  |     cc.and_(val_reg, mask); | ||||||
|  |     cc.cmp(ret_reg,0); | ||||||
|  |     cc.jne(jh.trap_entry); | ||||||
|  |     return val_reg; | ||||||
|  | } | ||||||
|  | inline x86::Gp gen_read_mem(jit_holder& jh, mem_type_e type, x86::Gp addr, x86::Gp length){ | ||||||
|  |     uint32_t length_val = 0; | ||||||
|  |     auto length_ptr = jh.cc.newIntPtr(); | ||||||
|  |     jh.cc.mov(length_ptr, &length_val); | ||||||
|  |     jh.cc.mov(x86::ptr_32(length_ptr),length); | ||||||
|  |  | ||||||
|  |     return gen_read_mem(jh, type, addr, length); | ||||||
|  | } | ||||||
|  | inline x86::Gp gen_read_mem(jit_holder& jh, mem_type_e type, uint64_t addr, x86::Gp length){ | ||||||
|  |     auto addr_reg = jh.cc.newInt64(); | ||||||
|  |     jh.cc.mov(addr_reg, addr);     | ||||||
|  |  | ||||||
|  |     uint32_t length_val = 0; | ||||||
|  |     auto length_ptr = jh.cc.newIntPtr(); | ||||||
|  |     jh.cc.mov(length_ptr, &length_val); | ||||||
|  |     jh.cc.mov(x86::ptr_32(length_ptr),length); | ||||||
|  |  | ||||||
|  |     return gen_read_mem(jh, type, addr_reg, length_val); | ||||||
|  | } | ||||||
|  | inline x86::Gp gen_read_mem(jit_holder& jh, mem_type_e type, uint64_t addr, uint32_t length){ | ||||||
|  |     auto addr_reg = jh.cc.newInt64(); | ||||||
|  |     jh.cc.mov(addr_reg, addr); | ||||||
|  |  | ||||||
|  |     return gen_read_mem(jh, type, addr_reg, length); | ||||||
|  | } | ||||||
|  | inline void gen_write_mem(jit_holder& jh, mem_type_e type, x86::Gp addr, int64_t val){ | ||||||
|  |     auto val_reg = jh.cc.newInt64(); | ||||||
|  |     jh.cc.mov(val_reg, val); | ||||||
|  |     gen_write_mem(jh, type, addr, val_reg); | ||||||
|  |  | ||||||
|  | } | ||||||
|  | inline void gen_write_mem(jit_holder& jh, mem_type_e type, x86::Gp addr, x86::Gp val){ | ||||||
|  |     x86::Compiler& cc = jh.cc; | ||||||
|  |  | ||||||
|  |     auto mem_type_reg = cc.newInt32(); | ||||||
|  |     jh.cc.mov(mem_type_reg, type); | ||||||
|  |     auto space_reg = cc.newInt32(); | ||||||
|  |     jh.cc.mov(space_reg, static_cast<uint16_t>(iss::address_type::VIRTUAL)); | ||||||
|  |     auto ret_reg = cc.newInt32(); | ||||||
|  |     InvokeNode* invokeNode; | ||||||
|  |  | ||||||
|  |     if(val.isGpb()){ | ||||||
|  |         cc.invoke(&invokeNode, &write_mem1, FuncSignatureT<uint32_t, uint64_t, uint32_t, uint32_t, uint64_t, uint8_t>()); | ||||||
|  |     } | ||||||
|  |     else if(val.isGpw()){ | ||||||
|  |         cc.invoke(&invokeNode, &write_mem2, FuncSignatureT<uint32_t, uint64_t, uint32_t, uint32_t, uint64_t, uint16_t>()); | ||||||
|  |     } | ||||||
|  |     else if(val.isGpd()){ | ||||||
|  |         cc.invoke(&invokeNode, &write_mem4, FuncSignatureT<uint32_t, uint64_t, uint32_t, uint32_t, uint64_t, uint32_t>()); | ||||||
|  |     } | ||||||
|  |     else if(val.isGpq()){ | ||||||
|  |         cc.invoke(&invokeNode, &write_mem8, FuncSignatureT<uint32_t, uint64_t, uint32_t, uint32_t, uint64_t, uint64_t>()); | ||||||
|  |     } | ||||||
|  |     else throw std::runtime_error("Invalid register size in gen_write_mem"); | ||||||
|  |  | ||||||
|  |     invokeNode->setRet(0,ret_reg); | ||||||
|  |     invokeNode->setArg(0, jh.arch_if_ptr); | ||||||
|  |     invokeNode->setArg(1, space_reg); | ||||||
|  |     invokeNode->setArg(2, mem_type_reg); | ||||||
|  |     invokeNode->setArg(3, addr); | ||||||
|  |     invokeNode->setArg(4, val); | ||||||
|  |  | ||||||
|  |     cc.cmp(ret_reg,0); | ||||||
|  |     cc.jne(jh.trap_entry); | ||||||
|  |  | ||||||
|  | } | ||||||
|  | inline void gen_write_mem(jit_holder& jh, mem_type_e type, uint64_t addr, x86::Gp val){ | ||||||
|  |     auto addr_reg = jh.cc.newInt64(); | ||||||
|  |     jh.cc.mov(addr_reg, addr); | ||||||
|  |     gen_write_mem(jh, type, addr_reg, val); | ||||||
|  | } | ||||||
|  | inline void gen_write_mem(jit_holder& jh, mem_type_e type, uint64_t addr, int64_t val){ | ||||||
|  |     auto val_reg = jh.cc.newInt64(); | ||||||
|  |     jh.cc.mov(val_reg, val); | ||||||
|  |  | ||||||
|  |     auto addr_reg = jh.cc.newInt64(); | ||||||
|  |     jh.cc.mov(addr_reg, addr); | ||||||
|  |     gen_write_mem(jh, type, addr_reg, val_reg); | ||||||
|  |  | ||||||
|  | } | ||||||
							
								
								
									
										3786
									
								
								src/vm/asmjit/vm_tgc5c.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3786
									
								
								src/vm/asmjit/vm_tgc5c.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -36,10 +36,11 @@ | |||||||
| #include <iss/iss.h> | #include <iss/iss.h> | ||||||
| #include <iss/interp/vm_base.h> | #include <iss/interp/vm_base.h> | ||||||
| #include <util/logging.h> | #include <util/logging.h> | ||||||
| #include <sstream> |  | ||||||
| #include <boost/coroutine2/all.hpp> | #include <boost/coroutine2/all.hpp> | ||||||
| #include <functional> | #include <functional> | ||||||
| #include <exception> | #include <exception> | ||||||
|  | #include <vector> | ||||||
|  | #include <sstream> | ||||||
|  |  | ||||||
| #ifndef FMT_HEADER_ONLY | #ifndef FMT_HEADER_ONLY | ||||||
| #define FMT_HEADER_ONLY | #define FMT_HEADER_ONLY | ||||||
| @@ -2522,8 +2523,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | |||||||
|                     // execute instruction |                     // execute instruction | ||||||
|                     { |                     { | ||||||
|                                     if(rs1 && rs1 < traits::RFS) { |                                     if(rs1 && rs1 < traits::RFS) { | ||||||
|                                         uint32_t addr_mask = (uint32_t)- 2; |                                         *NEXT_PC = *(X+rs1 % traits::RFS) & ~ 0x1; | ||||||
|                                         *NEXT_PC = *(X+rs1 % traits::RFS) & addr_mask; |  | ||||||
|                                         this->core.reg.last_branch = 1; |                                         this->core.reg.last_branch = 1; | ||||||
|                                     } |                                     } | ||||||
|                                     else { |                                     else { | ||||||
| @@ -2589,10 +2589,9 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | |||||||
|                                         raise(0,  2); |                                         raise(0,  2); | ||||||
|                                     } |                                     } | ||||||
|                                     else { |                                     else { | ||||||
|                                         uint32_t addr_mask = (uint32_t)- 2; |  | ||||||
|                                         uint32_t new_pc = *(X+rs1); |                                         uint32_t new_pc = *(X+rs1); | ||||||
|                                         *(X+1) = (uint32_t)(*PC +  2); |                                         *(X+1) = (uint32_t)(*PC +  2); | ||||||
|                                         *NEXT_PC = new_pc & addr_mask; |                                         *NEXT_PC = new_pc & ~ 0x1; | ||||||
|                                         this->core.reg.last_branch = 1; |                                         this->core.reg.last_branch = 1; | ||||||
|                                     } |                                     } | ||||||
|                                 } |                                 } | ||||||
| @@ -2699,13 +2698,13 @@ volatile std::array<bool, 2> dummy = { | |||||||
| 		    auto vm = new interp::tgc5c::vm_impl<arch::tgc5c>(*cpu, false); | 		    auto vm = new interp::tgc5c::vm_impl<arch::tgc5c>(*cpu, false); | ||||||
| 		    if (port != 0) debugger::server<debugger::gdb_session>::run_server(vm, port); | 		    if (port != 0) debugger::server<debugger::gdb_session>::run_server(vm, port); | ||||||
|             return {cpu_ptr{cpu}, vm_ptr{vm}}; |             return {cpu_ptr{cpu}, vm_ptr{vm}}; | ||||||
|         })/*, |         }), | ||||||
|         core_factory::instance().register_creator("tgc5c|mu_p|interp", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{ |         core_factory::instance().register_creator("tgc5c|mu_p|interp", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{ | ||||||
|             auto* cpu = new iss::arch::riscv_hart_mu_p<iss::arch::tgc5c>(); |             auto* cpu = new iss::arch::riscv_hart_mu_p<iss::arch::tgc5c>(); | ||||||
| 		    auto vm = new interp::tgc5c::vm_impl<arch::tgc5c>(*cpu, false); | 		    auto vm = new interp::tgc5c::vm_impl<arch::tgc5c>(*cpu, false); | ||||||
| 		    if (port != 0) debugger::server<debugger::gdb_session>::run_server(vm, port); | 		    if (port != 0) debugger::server<debugger::gdb_session>::run_server(vm, port); | ||||||
|             return {cpu_ptr{cpu}, vm_ptr{vm}}; |             return {cpu_ptr{cpu}, vm_ptr{vm}}; | ||||||
|         })*/ |         }) | ||||||
| }; | }; | ||||||
| } | } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -3303,12 +3303,12 @@ private: | |||||||
|         pc=pc+ 2; |         pc=pc+ 2; | ||||||
|         gen_set_pc(tu, pc, traits::NEXT_PC); |         gen_set_pc(tu, pc, traits::NEXT_PC); | ||||||
|         tu.open_scope(); |         tu.open_scope(); | ||||||
|         if(rs1&&rs1<static_cast<uint32_t>(traits:: RFS)){ auto addr_mask = tu.assignment(tu.constant((uint32_t)- 2,32),32); |         if(rs1&&rs1<static_cast<uint32_t>(traits:: RFS)) { | ||||||
|         auto PC_val_v = tu.assignment("PC_val", tu.bitwise_and( |             auto PC_val_v = tu.assignment("PC_val", tu.bitwise_and( | ||||||
|            tu.load(rs1%static_cast<uint32_t>(traits:: RFS)+ traits::X0, 0), |                tu.load(rs1%static_cast<uint32_t>(traits:: RFS)+ traits::X0, 0), | ||||||
|            addr_mask),32); |                tu.constant(~ 0x1,8)),32); | ||||||
|         tu.store(traits::NEXT_PC, PC_val_v); |             tu.store(traits::NEXT_PC, PC_val_v); | ||||||
|         tu.store(traits::LAST_BRANCH, tu.constant(2U, 2)); |             tu.store(traits::LAST_BRANCH, tu.constant(2U, 2)); | ||||||
|         } |         } | ||||||
|         else{ |         else{ | ||||||
|         	this->gen_raise_trap(tu, 0, 2); |         	this->gen_raise_trap(tu, 0, 2); | ||||||
| @@ -3401,13 +3401,12 @@ private: | |||||||
|             this->gen_raise_trap(tu, 0,  2); |             this->gen_raise_trap(tu, 0,  2); | ||||||
|         } |         } | ||||||
|         else{ |         else{ | ||||||
|         	auto addr_mask = tu.assignment(tu.constant((uint32_t)- 2,32),32); |  | ||||||
|         	auto new_pc = tu.assignment(tu.load(rs1+ traits::X0, 0),32); |         	auto new_pc = tu.assignment(tu.load(rs1+ traits::X0, 0),32); | ||||||
|         	tu.store(1 + traits::X0, |         	tu.store(1 + traits::X0, | ||||||
|         	      tu.constant((uint32_t)(PC+ 2),32)); |         	      tu.constant((uint32_t)(PC+ 2),32)); | ||||||
|         	auto PC_val_v = tu.assignment("PC_val", tu.bitwise_and( |         	auto PC_val_v = tu.assignment("PC_val", tu.bitwise_and( | ||||||
|         	   new_pc, |         	   new_pc, | ||||||
|         	   addr_mask),32); |         	   tu.constant(~ 0x1,8)),32); | ||||||
|         	tu.store(traits::NEXT_PC, PC_val_v); |         	tu.store(traits::NEXT_PC, PC_val_v); | ||||||
|         	tu.store(traits::LAST_BRANCH, tu.constant(2U, 2)); |         	tu.store(traits::LAST_BRANCH, tu.constant(2U, 2)); | ||||||
|         } |         } | ||||||
| @@ -3433,7 +3432,7 @@ private: | |||||||
|         gen_set_pc(tu, pc, traits::NEXT_PC); |         gen_set_pc(tu, pc, traits::NEXT_PC); | ||||||
|         tu.open_scope(); |         tu.open_scope(); | ||||||
|         this->gen_raise_trap(tu, 0,  3); |         this->gen_raise_trap(tu, 0,  3); | ||||||
|         auto returnValue = std::make_tuple(TRAP); |         auto returnValue = std::make_tuple(CONT); | ||||||
|          |          | ||||||
|         tu.close_scope(); |         tu.close_scope(); | ||||||
|         gen_trap_check(tu);         |         gen_trap_check(tu);         | ||||||
| @@ -3490,7 +3489,7 @@ private: | |||||||
|         gen_set_pc(tu, pc, traits::NEXT_PC); |         gen_set_pc(tu, pc, traits::NEXT_PC); | ||||||
|         tu.open_scope(); |         tu.open_scope(); | ||||||
|         this->gen_raise_trap(tu, 0,  2); |         this->gen_raise_trap(tu, 0,  2); | ||||||
|         auto returnValue = std::make_tuple(TRAP); |         auto returnValue = std::make_tuple(CONT); | ||||||
|          |          | ||||||
|         tu.close_scope(); |         tu.close_scope(); | ||||||
|         gen_trap_check(tu);         |         gen_trap_check(tu);         | ||||||
| @@ -3655,13 +3654,13 @@ volatile std::array<bool, 2> dummy = { | |||||||
| 		    auto vm = new tcc::tgc5c::vm_impl<arch::tgc5c>(*cpu, false); | 		    auto vm = new tcc::tgc5c::vm_impl<arch::tgc5c>(*cpu, false); | ||||||
| 		    if (port != 0) debugger::server<debugger::gdb_session>::run_server(vm, port); | 		    if (port != 0) debugger::server<debugger::gdb_session>::run_server(vm, port); | ||||||
|             return {cpu_ptr{cpu}, vm_ptr{vm}}; |             return {cpu_ptr{cpu}, vm_ptr{vm}}; | ||||||
|         })/*, |         }), | ||||||
|         core_factory::instance().register_creator("tgc5c|mu_p|tcc", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{ |         core_factory::instance().register_creator("tgc5c|mu_p|tcc", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{ | ||||||
|             auto* cpu = new iss::arch::riscv_hart_mu_p<iss::arch::tgc5c>(); |             auto* cpu = new iss::arch::riscv_hart_mu_p<iss::arch::tgc5c>(); | ||||||
| 		    auto vm = new tcc::tgc5c::vm_impl<arch::tgc5c>(*cpu, false); | 		    auto vm = new tcc::tgc5c::vm_impl<arch::tgc5c>(*cpu, false); | ||||||
| 		    if (port != 0) debugger::server<debugger::gdb_session>::run_server(vm, port); | 		    if (port != 0) debugger::server<debugger::gdb_session>::run_server(vm, port); | ||||||
|             return {cpu_ptr{cpu}, vm_ptr{vm}}; |             return {cpu_ptr{cpu}, vm_ptr{vm}}; | ||||||
|         })*/ |         }) | ||||||
| }; | }; | ||||||
| } | } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user