Skip to content

Commit

Permalink
Add support for custom call sites for respond_to
Browse files Browse the repository at this point in the history
This sets up a new type of call site for respond_to? calls at the place
where respond_to? is called. Currently it only stores the last symbol
and visibility tag that is has seen and returns the cached value if they
match.
  • Loading branch information
dbussink committed Apr 30, 2013
1 parent 9ed36cd commit 58f2078
Show file tree
Hide file tree
Showing 17 changed files with 255 additions and 33 deletions.
5 changes: 5 additions & 0 deletions kernel/bootstrap/executable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,10 @@ def invoke(name, mod, recv, args, block)
Rubinius.primitive :executable_invoke
raise PrimitiveFailure, "Executable#invoke primitive failed"
end

def custom_call_site
Rubinius.primitive :executable_set_custom_call_site
raise PrimitiveFailure, "Executable#set_custom_call_site primitive failed"
end
end
end
2 changes: 1 addition & 1 deletion kernel/bootstrap/kernel18.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,5 @@ def __id__

def respond_to?(meth, include_private=false)
respond_to_prim?(meth, include_private)
end
end.custom_call_site
end
2 changes: 1 addition & 1 deletion kernel/bootstrap/kernel19.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def untrusted?

def respond_to?(meth, include_private=false)
respond_to_prim?(meth, include_private) || !!respond_to_missing?(meth, include_private)
end
end.custom_call_site

def respond_to_missing?(meth, include)
false
Expand Down
4 changes: 4 additions & 0 deletions vm/builtin/call_site.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#ifndef RBX_BUILTIN_CALL_SITE_HPP
#define RBX_BUILTIN_CALL_SITE_HPP

#include "builtin/object.hpp"
#include "builtin/exception.hpp"
#include "type_info.hpp"

namespace rubinius {

class CallSite : public Object {
Expand Down
13 changes: 12 additions & 1 deletion vm/builtin/executable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ namespace rubinius {
executor execute;

protected:
int prim_index_;
Inliners* inliners_;
int prim_index_;
bool custom_call_site_;

public:
/* accessors */
Expand All @@ -60,6 +61,10 @@ namespace rubinius {
return prim_index_;
}

bool custom_call_site_p() {
return custom_call_site_;
}

/* interface */

// Rubinius.primitive :executable_allocate
Expand All @@ -71,6 +76,12 @@ namespace rubinius {
// Rubinius.primitive :executable_invoke
Object* invoke(STATE, Symbol* name, Module* mod, Object* recv, Array* args, Object* block, CallFrame* calling_environment);

// Rubinius.primitive :executable_set_custom_call_site
Object* set_custom_call_site(STATE) {
custom_call_site_ = true;
return cNil;
}

bool resolve_primitive(STATE);

void add_inliner(ObjectMemory* om, CompiledCode* code);
Expand Down
36 changes: 32 additions & 4 deletions vm/builtin/inline_cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,14 @@ namespace rubinius {
Executable* meth = ice->method();
Module* mod = ice->stored_module();

return meth->execute(state, call_frame, meth, mod, args);
if(meth->custom_call_site_p()) {
state->set_call_site_location(cache->location_);
Object* res = meth->execute(state, call_frame, meth, mod, args);
state->set_call_site_location(NULL);
return res;
} else {
return meth->execute(state, call_frame, meth, mod, args);
}
}

Object* InlineCache::empty_cache_private(STATE, CallSite* call_site, CallFrame* call_frame,
Expand Down Expand Up @@ -238,7 +245,14 @@ namespace rubinius {
Executable* meth = ice->method();
Module* mod = ice->stored_module();

return meth->execute(state, call_frame, meth, mod, args);
if(meth->custom_call_site_p()) {
state->set_call_site_location(cache->location_);
Object* res = meth->execute(state, call_frame, meth, mod, args);
state->set_call_site_location(NULL);
return res;
} else {
return meth->execute(state, call_frame, meth, mod, args);
}
}

Object* InlineCache::empty_cache_vcall(STATE, CallSite* call_site, CallFrame* call_frame,
Expand Down Expand Up @@ -282,7 +296,14 @@ namespace rubinius {
Executable* meth = ice->method();
Module* mod = ice->stored_module();

return meth->execute(state, call_frame, meth, mod, args);
if(meth->custom_call_site_p()) {
state->set_call_site_location(cache->location_);
Object* res = meth->execute(state, call_frame, meth, mod, args);
state->set_call_site_location(NULL);
return res;
} else {
return meth->execute(state, call_frame, meth, mod, args);
}
}

Object* InlineCache::empty_cache_super(STATE, CallSite* call_site, CallFrame* call_frame,
Expand Down Expand Up @@ -341,7 +362,14 @@ namespace rubinius {
Executable* meth = ice->method();
Module* mod = ice->stored_module();

return meth->execute(state, call_frame, meth, mod, args);
if(meth->custom_call_site_p()) {
state->set_call_site_location(cache->location_);
Object* res = meth->execute(state, call_frame, meth, mod, args);
state->set_call_site_location(NULL);
return res;
} else {
return meth->execute(state, call_frame, meth, mod, args);
}
}

Object* InlineCache::check_cache(STATE, CallSite* call_site, CallFrame* call_frame,
Expand Down
18 changes: 17 additions & 1 deletion vm/builtin/object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
#include "builtin/methodtable.hpp"
#include "builtin/packed_object.hpp"
#include "builtin/location.hpp"
#include "builtin/inline_cache.hpp"
#include "builtin/respond_to_cache.hpp"

#include "objectmemory.hpp"
#include "arguments.hpp"
Expand Down Expand Up @@ -833,7 +835,21 @@ namespace rubinius {

Dispatch dis(name);

return RBOOL(dis.resolve(state, name, lookup));
Object* res = RBOOL(dis.resolve(state, name, lookup));
CallSite** location = state->vm()->saved_call_site_location();

if(location && res) {
CallSite* existing = *location;
RespondToCache* cache = try_as<RespondToCache>(existing);
if(!cache) {
cache = RespondToCache::empty(state, existing, location);
}
cache->update(state, this, name, priv, res);
atomic::memory_barrier();
*location = cache;
}

return res;
}

/**
Expand Down
79 changes: 79 additions & 0 deletions vm/builtin/respond_to_cache.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#include "detection.hpp"

#include "arguments.hpp"
#include "call_frame.hpp"
#include "global_cache.hpp"
#include "ontology.hpp"

#include "builtin/respond_to_cache.hpp"
#include "builtin/class.hpp"
#include "builtin/symbol.hpp"

namespace rubinius {

void RespondToCache::init(STATE) {
GO(respond_to_cache).set(
ontology::new_class(state, "RespondToCache",
G(call_site), G(rubinius)));

}

RespondToCache* RespondToCache::empty(STATE, CallSite* fallback, CallSite** location) {
RespondToCache* cache =
state->vm()->new_object_mature<RespondToCache>(G(respond_to_cache));
cache->fallback(state, fallback);
cache->receiver_class_ = nil<Class>();
cache->visibility_ = nil<Symbol>();
cache->responds_ = cNil;
cache->initial_backend_ = empty_cache;
cache->execute_backend_ = empty_cache;
cache->location_ = location;
cache->clear_receiver_data();
return cache;
}

Object* RespondToCache::empty_cache(STATE, CallSite* call_site, CallFrame* call_frame,
Arguments& args) {
RespondToCache* cache = static_cast<RespondToCache*>(call_site);
return cache->fallback_->execute(state, call_frame, args);
}

Object* RespondToCache::check_cache(STATE, CallSite* call_site, CallFrame* call_frame,
Arguments& args) {
RespondToCache* cache = static_cast<RespondToCache*>(call_site);

Class* const recv_class = args.recv()->lookup_begin(state);
Object* const message = args.get_argument(0);
Object* visibility = cFalse;
if(args.total() > 1) {
visibility = args.get_argument(1);
}

register uint64_t recv_data = recv_class->data_id();
if(likely(recv_data == cache->receiver_data() && message == cache->message_ && visibility == cache->visibility_)) {
return cache->responds_;
}

// Invalidate the cache here so we don't see anything else
cache->clear_receiver_data();
atomic::memory_barrier();
return cache->initialize(state, call_frame, args);
}

void RespondToCache::update(STATE, Object* recv, Symbol* msg, Object* priv, Object* res) {
Class* const recv_class = recv->lookup_begin(state);
uint64_t recv_data = recv_class->data_id();
message(state, msg);
visibility(state, priv);
responds(state, res);
atomic::memory_barrier();
set_receiver_data(recv_data);
execute_backend_ = check_cache;
}

void RespondToCache::Info::mark(Object* obj, ObjectMark& mark) {
auto_mark(obj, mark);
}

}

78 changes: 78 additions & 0 deletions vm/builtin/respond_to_cache.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#ifndef RBX_RESPOND_TO_CACHE_HPP
#define RBX_RESPOND_TO_CACHE_HPP

#include "dispatch.hpp"
#include "builtin/object.hpp"
#include "builtin/class.hpp"
#include "builtin/call_site.hpp"
#include "builtin/exception.hpp"
#include "type_info.hpp"

namespace rubinius {
struct CallFrame;
class Arguments;
class Module;

class RespondToCache : public CallSite {
public:
const static object_type type = RespondToCacheType;

private:
ClassData receiver_;
Class* receiver_class_; // slot
Object* message_; // slot
Object* visibility_; // slot
Object* responds_; // slot
CallSite* fallback_; // slot

public:
attr_accessor(receiver_class, Class);
attr_accessor(message, Object);
attr_accessor(visibility, Object);
attr_accessor(responds, Object);
attr_accessor(fallback, CallSite);

void clear_receiver_data() {
receiver_.raw = 0;
}

void set_receiver_data(uint64_t data) {
receiver_.raw = data;
}

uint64_t receiver_data() {
return receiver_.raw;
}

uint32_t receiver_class_id() {
return receiver_.f.class_id;
}

uint32_t receiver_serial_id() {
return receiver_.f.serial_id;
}

public:
static void init(STATE);
static RespondToCache* empty(STATE, CallSite* fallback, CallSite** location);

static Object* empty_cache(STATE, CallSite* call_site, CallFrame* call_frame,
Arguments& args);

static Object* check_cache(STATE, CallSite* call_site, CallFrame* call_frame,
Arguments& args);

void update(STATE, Object* recv, Symbol* msg, Object* priv, Object* res);

public: // Rubinius Type stuff
class Info : public CallSite::Info {
public:
BASIC_TYPEINFO(CallSite::Info)
virtual void mark(Object* t, ObjectMark& mark);
};

};
}

#endif

1 change: 1 addition & 0 deletions vm/llvm/inline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,7 @@ namespace rubinius {
CompiledCode* code, MachineCode* mcode, int hits) {

InlineCache* cache = try_as<InlineCache>(call_site_);
assert(cache);

ctx_->enter_inline();

Expand Down
14 changes: 8 additions & 6 deletions vm/llvm/jit_builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -472,12 +472,14 @@ namespace jit {
}

void check_for_eval(opcode which) {
InlineCache* ic = reinterpret_cast<InlineCache*>(which);
if(ic->name() == s_eval_ ||
ic->name() == s_binding_ ||
ic->name() == s_class_eval_ ||
ic->name() == s_module_eval_) {
calctx_evalish_ = true;
CallSite* call_site = reinterpret_cast<CallSite*>(which);
if(InlineCache* ic = try_as<InlineCache>(call_site)) {
if(ic->name() == s_eval_ ||
ic->name() == s_binding_ ||
ic->name() == s_class_eval_ ||
ic->name() == s_module_eval_) {
calctx_evalish_ = true;
}
}
}

Expand Down
18 changes: 0 additions & 18 deletions vm/llvm/jit_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1224,24 +1224,6 @@ extern "C" {

MachineCode* mcode = call_frame->compiled_code->machine_code();

/*
InlineCache* cache = 0;
if(mcode->opcodes[call_frame->ip()] == InstructionSequence::insn_send_stack) {
cache = reinterpret_cast<InlineCache*>(mcode->opcodes[call_frame->ip() + 1]);
} else if(mcode->opcodes[call_frame->ip()] == InstructionSequence::insn_send_method) {
cache = reinterpret_cast<InlineCache*>(mcode->opcodes[call_frame->ip() + 1]);
} else if(mcode->opcodes[call_frame->ip()] == InstructionSequence::insn_send_stack_with_block) {
cache = reinterpret_cast<InlineCache*>(mcode->opcodes[call_frame->ip() + 1]);
}
if(cache && cache->name()->symbol_p()) {
std::cout << "Uncommon trap for send: " << cache->name()->c_str(state) << "\n";
} else {
std::cout << "Unknown uncommon try reason.\n";
}
*/

if(call_frame->is_inline_frame()) {
// Fix up this inlined block.
if(mcode->parent()) {
Expand Down
1 change: 1 addition & 0 deletions vm/llvm/offset.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ namespace CallSite {
const static int name = 1;
const static int initial = 2;
const static int execute = 3;
const static int location = 4;
}
namespace Class {
const static int header = 0;
Expand Down
2 changes: 1 addition & 1 deletion vm/llvm/types64.ll
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ declare void @output3(%"struct.rubinius::Dispatch"*)
%"struct.rubinius::Symbol"*, ; name
%"struct.rubinius::Object"* (%"struct.rubinius::State"*, %"struct.rubinius::CallSite"*, %"struct.rubinius::CallFrame"*, %"struct.rubinius::Arguments"*)*, ; initial
%"struct.rubinius::Object"* (%"struct.rubinius::State"*, %"struct.rubinius::CallSite"*, %"struct.rubinius::CallFrame"*, %"struct.rubinius::Arguments"*)*, ; execute
%"struct.rubinius::CallSite"**
%"struct.rubinius::CallSite"** ; location
}

declare void @outputCallSite(%"struct.rubinius::CallSite"*)
Expand Down
Loading

0 comments on commit 58f2078

Please sign in to comment.