Skip to content

Commit

Permalink
Introduce fast path for nested constants
Browse files Browse the repository at this point in the history
We didn't have caching for constant lookup for nested constants, such as
Rubinius::VariableScope. This means that when a constants was referenced
in that way, it would not cache the VariableScope constant.

This commit introduces a constant cache very similar to not nested
constants. It also stores the scope it was used under, so we can
properly validate the have the correct constant. Also specialization is
added to the JIT to have a fast path for these cached constants if they
are still valid.
  • Loading branch information
dbussink committed Feb 10, 2013
1 parent 60db931 commit 60482df
Show file tree
Hide file tree
Showing 7 changed files with 265 additions and 2 deletions.
9 changes: 9 additions & 0 deletions lib/compiler/generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,15 @@ def push_const(name)
push_const_fast find_literal(name), allocated_asscociation_slot
end

# The find_const instruction itself is unused right now. The instruction
# parser does not emit a GeneratorMethods#find_const. This method/opcode
# was used in the compiler before the find_const_fast instruction. Rather
# than changing the compiler code, this helper was used.
def find_const(name)
allocated_asscociation_slot = add_literal(nil)
find_const_fast find_literal(name), allocated_asscociation_slot
end

def push_local(idx)
if @detected_locals <= idx
@detected_locals = idx + 1
Expand Down
28 changes: 27 additions & 1 deletion vm/builtin/global_cache_entry.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#include "builtin/global_cache_entry.hpp"
#include "builtin/class.hpp"
#include "builtin/constantscope.hpp"
#include "builtin/module.hpp"

#include "object_utils.hpp"
#include "ontology.hpp"

namespace rubinius {
Expand All @@ -11,7 +13,7 @@ namespace rubinius {
G(object), G(rubinius)));
}

GlobalCacheEntry* GlobalCacheEntry::create(STATE, Object *value,
GlobalCacheEntry* GlobalCacheEntry::create(STATE, Object* value,
ConstantScope* scope)
{
GlobalCacheEntry *entry =
Expand All @@ -21,6 +23,16 @@ namespace rubinius {
return entry;
}

GlobalCacheEntry* GlobalCacheEntry::create(STATE, Object* value, Module* under,
ConstantScope* scope)
{
GlobalCacheEntry *entry =
state->vm()->new_object_mature<GlobalCacheEntry>(G(global_cache_entry));

entry->update(state, value, under, scope);
return entry;
}

GlobalCacheEntry* GlobalCacheEntry::empty(STATE) {
GlobalCacheEntry *entry =
state->vm()->new_object_mature<GlobalCacheEntry>(G(global_cache_entry));
Expand All @@ -34,9 +46,23 @@ namespace rubinius {
scope_ == scope;
}

bool GlobalCacheEntry::valid_p(STATE, Module* under, ConstantScope* scope) {
return serial_ == state->shared().global_serial() &&
under_ == under &&
scope_ == scope;
}

void GlobalCacheEntry::update(STATE, Object* val, ConstantScope* sc) {
value(state, val);
scope(state, sc);
under(state, nil<Module>());
serial_ = state->shared().global_serial();
}

void GlobalCacheEntry::update(STATE, Object* val, Module* mc, ConstantScope* sc) {
value(state, val);
scope(state, sc);
under(state, mc);
serial_ = state->shared().global_serial();
}
}
Expand Down
9 changes: 9 additions & 0 deletions vm/builtin/global_cache_entry.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ namespace rubinius {
private:
Object* value_; // slot
ConstantScope* scope_; // slot
Module* under_; // slot
int serial_;

public:
attr_accessor(value, Object);
attr_accessor(scope, ConstantScope);
attr_accessor(under, Module);

int serial() { return serial_; }

Expand All @@ -30,12 +32,19 @@ namespace rubinius {
return &value_;
}

Module** under_location() {
return &under_;
}

static void init(STATE);
static GlobalCacheEntry* create(STATE, Object* value, ConstantScope* scope);
static GlobalCacheEntry* create(STATE, Object* value, Module* under, ConstantScope* scope);
static GlobalCacheEntry* empty(STATE);

void update(STATE, Object* value, ConstantScope* scope);
void update(STATE, Object* value, Module* under, ConstantScope* scope);
bool valid_p(STATE, ConstantScope* scope);
bool valid_p(STATE, Module* under, ConstantScope* scope);

class Info : public TypeInfo {
public:
Expand Down
54 changes: 54 additions & 0 deletions vm/instructions.def
Original file line number Diff line number Diff line change
Expand Up @@ -866,6 +866,60 @@ instruction push_const_fast(literal association) [ -- constant ]
CHECK_AND_PUSH(res);
end

# [Description]
# Pushes a constant onto the stack scoped under the module on the top of
# the stack. Caches the lookup to provide faster future lookup. This
# instruction is normally emitted only by the Generator.
# [See Also]
# find_const
# [Example]
# [Example]
# str = "abc"
# enum = Enumerable::Enumerator(str, :each_byte)

instruction find_const_fast(literal association) [ module -- constant ]
Object* res = 0;

Module* under = as<Module>(stack_pop());
Object* val = call_frame->compiled_code->literals()->at(state, association);

// See if the cache is present, if so, validate it and use the value
GlobalCacheEntry* cache;

if((cache = try_as<GlobalCacheEntry>(val)) != NULL) {
if(cache->valid_p(state, under, call_frame->constant_scope())) {
res = cache->value();
}
} else {
cache = GlobalCacheEntry::empty(state);
call_frame->compiled_code->literals()->put(state, association, cache);
}

if(!res) {
bool found = false;
flush_ip();

Symbol* sym = as<Symbol>(call_frame->compiled_code->literals()->at(state, literal));
res = Helpers::const_get_under(state, under, sym, &found);

if(found) {
OnStack<3> os(state, cache, under, res);
if(Autoload* autoload = try_as<Autoload>(res)) {
flush_ip();
res = autoload->resolve(state, gct, call_frame, under);
}

if(res) {
cache->update(state, res, under, call_frame->constant_scope());
}
} else {
res = Helpers::const_missing_under(state, under, sym, call_frame);
}
}

CHECK_AND_PUSH(res);
end

section "Send messages"

# [Description]
Expand Down
44 changes: 44 additions & 0 deletions vm/llvm/jit_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,50 @@ extern "C" {
CPP_CATCH
}

Object* rbx_find_const_fast(STATE, CallFrame* call_frame, Symbol* sym,
int association_index, Object* top) {
CPP_TRY

Object* res = 0;

Module* under = as<Module>(top);
Object* val = call_frame->compiled_code->literals()->at(state, association_index);

// See if the cache is present, if so, validate it and use the value
GlobalCacheEntry* cache;
if((cache = try_as<GlobalCacheEntry>(val)) != NULL) {
if(cache->valid_p(state, under, call_frame->constant_scope())) {
res = cache->value();
}
} else {
cache = GlobalCacheEntry::empty(state);
call_frame->compiled_code->literals()->put(state, association_index, cache);
}

if(!res) {
bool found = false;
res = Helpers::const_get_under(state, under, sym, &found);

if(found) {
GCTokenImpl gct;
OnStack<2> os(state, cache, res);
if(Autoload* autoload = try_as<Autoload>(res)) {
res = autoload->resolve(state, gct, call_frame, under);
}

if(res) {
cache->update(state, res, under, call_frame->constant_scope());
}
} else {
res = Helpers::const_missing_under(state, under, sym, call_frame);
}
}

return res;

CPP_CATCH
}

Object* rbx_instance_of(STATE, CallFrame* call_frame, Object* top, Object* b1) {
CPP_TRY

Expand Down
97 changes: 96 additions & 1 deletion vm/llvm/jit_visit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2100,7 +2100,7 @@ namespace rubinius {
constant(as<Symbol>(literal(name)))
};

CallInst* ret = b().CreateCall(func, call_args, "push_const_fast");
CallInst* ret = b().CreateCall(func, call_args, "push_const");
ret->setOnlyReadsMemory();
ret->setDoesNotThrow();

Expand Down Expand Up @@ -3138,6 +3138,101 @@ namespace rubinius {
stack_push(val);
}

void visit_find_const_fast(opcode name, opcode cache) {
BasicBlock* cont = 0;

Value* cached_value = 0;
BasicBlock* cached_block = 0;

Object* lit = literal(cache);

Value* under = stack_pop();

GlobalCacheEntry* entry = try_as<GlobalCacheEntry>(lit);
if(entry) {
assert(entry->pin());

Value* global_serial = b().CreateLoad(global_serial_pos, "global_serial");

Value* current_serial_pos = b().CreateIntToPtr(
clong((intptr_t)entry->serial_location()),
llvm::PointerType::getUnqual(ctx_->Int32Ty), "cast_to_intptr");

Value* current_serial = b().CreateLoad(current_serial_pos, "serial");

Value* cache_cmp = b().CreateICmpEQ(global_serial, current_serial, "use_under");

BasicBlock* check_under = new_block("check_under");
BasicBlock* use_cache = new_block("use_cache");
BasicBlock* use_call = new_block("use_call");
cont = new_block("continue");

b().CreateCondBr(cache_cmp, check_under, use_call);

set_block(check_under);

Value* under_pos = b().CreateIntToPtr(
clong((intptr_t)entry->under_location()),
llvm::PointerType::getUnqual(ObjType), "cast_to_objptr");

Value* cached_under = b().CreateLoad(under_pos, "cached_value");

Value* under_cmp = b().CreateICmpEQ(cached_under, under, "use_cache");

b().CreateCondBr(under_cmp, use_cache, use_call);

set_block(use_cache);

Value* value_pos = b().CreateIntToPtr(
clong((intptr_t)entry->value_location()),
llvm::PointerType::getUnqual(ObjType), "cast_to_objptr");

cached_value = b().CreateLoad(value_pos, "cached_value");
cached_block = b().GetInsertBlock();

b().CreateBr(cont);

set_block(use_call);
}

Signature sig(ctx_, ObjType);

sig << StateTy;
sig << CallFrameTy;
sig << ObjType;
sig << ctx_->Int32Ty;
sig << ObjType;

Value* call_args[] = {
state_,
call_frame_,
constant(as<Symbol>(literal(name))),
cint(cache),
under
};

flush();

CallInst* ret = sig.call("rbx_find_const_fast", call_args, 5, "constant", b());
ret->setOnlyReadsMemory();
ret->setDoesNotThrow();
check_for_exception(ret);

if(entry) {
BasicBlock* ret_block = b().GetInsertBlock();
b().CreateBr(cont);
set_block(cont);

PHINode* phi = b().CreatePHI(ObjType, 2, "constant");
phi->addIncoming(cached_value, cached_block);
phi->addIncoming(ret, ret_block);

stack_push(phi);
} else {
stack_push(ret);
}
}

void visit_instance_of() {
Signature sig(ctx_, ObjType);

Expand Down
26 changes: 26 additions & 0 deletions web/_includes/instructions.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -965,6 +965,32 @@
<ul class="insn_cross_ref">
<li><a href="#push_const">push_const</a></li>
</ul>
<h3><a class="instruction" name="find_const_fast">find_const_fast(literal, association)</a></h3>

Pushes a constant onto the stack scoped under the module on the top of
the stack. Caches the lookup to provide faster future lookup. This
instruction is normally emitted only by the Generator.


<table class="stack_effect">
<thead>
<tr><th>Before</th><th>After</th></tr>
</thead>
<tbody>
<tr><td>module</td><td>constant</td></tr>
<tr><td>...</td><td>...</td></tr>
</tbody>
</table>

#### Example
str = "abc"
enum = Enumerable::Enumerator(str, :each_byte)


<h4>See Also</h4>
<ul class="insn_cross_ref">
<li><a href="#find_const">find_const</a></li>
</ul>
<h3><a class="instruction" name="set_call_flags">set_call_flags(flags)</a></h3>

The call flags on the current execution context are set to the opcode
Expand Down

0 comments on commit 60482df

Please sign in to comment.