Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This changes how the JIT follows which opcode to generate code for. It's now done by following the control flow with memoization, which makes sure that the JIT never has to reason about dead code.
- Loading branch information
Evan Phoenix
committed
Nov 24, 2009
1 parent
8e382e5
commit d6b53ad
Showing
5 changed files
with
255 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
#include "llvm/opcode_iter.hpp" | ||
|
||
#include <list> | ||
#include <map> | ||
|
||
namespace rubinius { | ||
namespace jit { | ||
class ControlFlowWalker { | ||
public: | ||
typedef std::list<int> SectionList; | ||
|
||
private: | ||
VMMethod* vmm_; | ||
uint8_t* seen_; | ||
SectionList work_list_; | ||
|
||
public: | ||
ControlFlowWalker(VMMethod* vmm) | ||
: vmm_(vmm) | ||
{ | ||
seen_ = new uint8_t[vmm->total]; | ||
memset(seen_, 0, vmm->total); | ||
} | ||
|
||
~ControlFlowWalker() { | ||
delete seen_; | ||
} | ||
|
||
void add_section(int ip) { | ||
if(seen_[ip]) return; | ||
work_list_.push_back(ip); | ||
} | ||
|
||
template <class EI> | ||
void run(EI& each) { | ||
work_list_.push_back(0); | ||
|
||
OpcodeIterator iter(vmm_); | ||
|
||
while(!work_list_.empty()) { | ||
int ip = work_list_.back(); | ||
work_list_.pop_back(); | ||
|
||
iter.switch_to(ip); | ||
|
||
while(seen_[iter.ip()] == 0) { | ||
seen_[iter.ip()] = 1; | ||
|
||
each.call(iter); | ||
|
||
if(iter.goto_p()) { | ||
opcode target = iter.goto_target(); | ||
assert(target >= 0 && target < vmm_->total); | ||
|
||
add_section(target); | ||
} | ||
|
||
if(iter.terminator_p()) break; | ||
|
||
if(!iter.next_p()) break; | ||
iter.advance(); | ||
} | ||
} | ||
} | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
namespace rubinius { | ||
class OpcodeIterator { | ||
VMMethod* vmm_; | ||
|
||
opcode ip_; | ||
int width_; | ||
|
||
opcode op_; | ||
const char* name_; | ||
|
||
private: | ||
void update() { | ||
op_ = vmm_->opcodes[ip_]; | ||
|
||
switch(op_) { | ||
#define HANDLE_INST0(code, name) \ | ||
case code: \ | ||
name_ = #name; \ | ||
width_ = 1; \ | ||
break; | ||
#define HANDLE_INST1(code, name) \ | ||
case code: \ | ||
name_ = #name; \ | ||
width_ = 2; \ | ||
break; | ||
#define HANDLE_INST2(code, name) \ | ||
case code: \ | ||
name_ = #name; \ | ||
width_ = 3; \ | ||
break; | ||
#include "gen/instruction_visitors.hpp" | ||
#undef HANDLE_INST0 | ||
#undef HANDLE_INST1 | ||
#undef HANDLE_INST2 | ||
|
||
default: | ||
abort(); | ||
} | ||
} | ||
|
||
|
||
#include "gen/instruction_names.hpp" | ||
|
||
public: | ||
OpcodeIterator(VMMethod* vmm, int ip=0) | ||
: vmm_(vmm) | ||
, ip_(ip) | ||
, width_(0) | ||
{ | ||
update(); | ||
} | ||
|
||
opcode* stream() { | ||
return vmm_->opcodes; | ||
} | ||
|
||
opcode ip() { | ||
return ip_; | ||
} | ||
|
||
int next_ip() { | ||
return ip_ + width_; | ||
} | ||
|
||
bool next_p() { | ||
return ip_ < vmm_->total; | ||
} | ||
|
||
void switch_to(int ip) { | ||
ip_ = ip; | ||
update(); | ||
} | ||
|
||
void advance() { | ||
switch_to(next_ip()); | ||
} | ||
|
||
opcode op() { | ||
return op_; | ||
} | ||
|
||
const char* name() { | ||
return name_; | ||
} | ||
|
||
int operands() { | ||
return width_ - 1; | ||
} | ||
|
||
opcode operand(int which) { | ||
return vmm_->opcodes[ip_ + which + 1]; | ||
} | ||
|
||
// flags | ||
bool terminator_p() { | ||
switch(op_) { | ||
case insn_ret: | ||
case insn_goto: | ||
case insn_raise_exc: | ||
case insn_raise_return: | ||
case insn_raise_break: | ||
case insn_ensure_return: | ||
case insn_reraise: | ||
return true; | ||
default: | ||
return false; | ||
} | ||
} | ||
|
||
bool goto_p() { | ||
switch(op_) { | ||
case insn_goto: | ||
case insn_goto_if_true: | ||
case insn_goto_if_false: | ||
case insn_setup_unwind: | ||
return true; | ||
default: | ||
return false; | ||
} | ||
} | ||
|
||
opcode goto_target() { | ||
// All gotos use operand 0 as the target | ||
return operand(0); | ||
} | ||
|
||
}; | ||
} |