Pastie now auto-senses if line-wrap is a bad or good idea. Feedback?
## mark a section (Learn more)
This paste will be private.
diff --git a/kernel/core/hash.rb b/kernel/core/hash.rb index 0f23106..746f910 100644 --- a/kernel/core/hash.rb +++ b/kernel/core/hash.rb @@ -445,7 +445,7 @@ class Hash hash = obj.hash hash = hash % HASH_MAX unless hash.kind_of? Fixnum - bin = hash % @bins + bin = hash & (@bins - 1) entry = @values.at bin diff --git a/kernel/core/iseq.rb b/kernel/core/iseq.rb index 12b5076..6c82978 100644 --- a/kernel/core/iseq.rb +++ b/kernel/core/iseq.rb @@ -57,17 +57,17 @@ class InstructionSet {:opcode => :push_local, :args => [:local], :stack => [0,1]}, {:opcode => :push_exception, :args => [], :stack => [0,1]}, {:opcode => :make_array, :args => [:int], :stack => [-110,1], - :vm_flags => [:check_interrupts]}, + :vm_flags => []}, {:opcode => :set_ivar, :args => [:literal], :stack => [1,1], - :vm_flags => [:check_interrupts]}, + :vm_flags => []}, {:opcode => :push_ivar, :args => [:literal], :stack => [0,1]}, {:opcode => :goto_if_defined, :args => [:ip], :stack => [1,0], :flow => :goto}, {:opcode => :push_const, :args => [:literal], :stack => [0,1]}, {:opcode => :set_const, :args => [:literal], :stack => [1,1], - :vm_flags => [:check_interrupts]}, + :vm_flags => []}, {:opcode => :set_const_at, :args => [:literal], :stack => [2,0], - :vm_flags => [:check_interrupts]}, + :vm_flags => []}, {:opcode => :find_const, :args => [:literal], :stack => [1,1]}, {:opcode => :attach_method, :args => [:literal], :stack => [2,1], :vm_flags => [:check_interrupts]}, @@ -82,11 +82,11 @@ class InstructionSet {:opcode => :open_module_under, :args => [:literal], :stack => [1,1], :vm_flags => [:check_interrupts]}, {:opcode => :unshift_tuple, :args => [], :stack => [1,2], - :vm_flags => [:check_interrupts]}, + :vm_flags => []}, {:opcode => :cast_tuple, :args => [], :stack => [1,1], - :vm_flags => [:check_interrupts]}, + :vm_flags => []}, {:opcode => :make_rest, :args => [:int], :stack => [-110,1], - :vm_flags => [:check_interrupts]}, + :vm_flags => []}, {:opcode => :dup_top, :args => [], :stack => [0,1]}, {:opcode => :pop, :args => [], :stack => [1,0]}, {:opcode => :ret, :args => [], :stack => [1,0], :flow => :return, @@ -105,7 +105,7 @@ class InstructionSet :vm_flags => [:terminator]}, {:opcode => :push_array, :args => [], :stack => [1,-999]}, {:opcode => :cast_array, :args => [], :stack => [1,1], - :vm_flags => [:check_interrupts]}, + :vm_flags => []}, {:opcode => :make_hash, :args => [:int], :stack => [-210,1], :vm_flags => [:check_interrupts]}, {:opcode => :raise_exc, :args => [], :stack => [1,0], :flow => :raise, @@ -119,15 +119,15 @@ class InstructionSet :vm_flags => [:terminator]}, {:opcode => :passed_arg, :args => [:int], :stack => [0,1]}, {:opcode => :string_append, :args => [], :stack => [2,1], - :vm_flags => [:check_interrupts]}, + :vm_flags => []}, {:opcode => :string_dup, :args => [], :stack => [1,1], - :vm_flags => [:check_interrupts]}, + :vm_flags => []}, {:opcode => :set_args, :args => [], :stack => [1,0]}, {:opcode => :get_args, :args => [], :stack => [0,1]}, {:opcode => :send_with_arg_register, :args => [:literal], :stack => [-132,1], :flow => :send, :vm_flags => [:check_interrupts]}, {:opcode => :cast_array_for_args, :args => [:int], :stack => [1,1], - :vm_flags => [:check_interrupts]}, + :vm_flags => []}, {:opcode => :send_super_stack_with_block, :args => [:literal, :int], :stack => [-121,1], :flow => :send, :vm_flags => [:check_interrupts]}, {:opcode => :push_my_field, :args => [:field], :stack => [0,1]}, @@ -167,9 +167,9 @@ class InstructionSet {:opcode => :push_local_depth, :args => [:depth, :block_local], :stack => [0,1]}, {:opcode => :set_local_depth, :args => [:depth, :block_local], - :stack => [1,1], :vm_flags => [:check_interrupts]}, + :stack => [1,1], :vm_flags => []}, {:opcode => :create_block, :args => [:int], :stack => [3,1], - :vm_flags => [:check_interrupts]}, + :vm_flags => []}, {:opcode => :send_off_stack, :args => [], :stack => [-133,1], :flow => :send, :vm_flags => [:check_interrupts]}, {:opcode => :locate_method, :args => [], :stack => [3,1]}, @@ -181,7 +181,7 @@ class InstructionSet {:opcode => :from_fp, :args => [:int], :stack => [0,1]}, {:opcode => :set_local_from_fp, :args => [:local, :int], :stack => [0,0]}, {:opcode => :make_rest_fp, :args => [:int], :stack => [0,1], - :vm_flags => [:check_interrupts]}, + :vm_flags => []}, {:opcode => :allocate_stack, :args => [:int], :stack => [0,-110]}, {:opcode => :deallocate_stack, :args => [:int], :stack => [-110,0]}, {:opcode => :set_local_fp, :args => [:int], :stack => [1,1]}, @@ -196,7 +196,7 @@ class InstructionSet {:opcode => :set_literal, :args => [:literal], :stack => [0,0]}, {:opcode => :passed_blockarg, :args => [:int], :stack => [0,1]}, {:opcode => :create_block2, :args => [], :stack => [1,1], - :vm_flags => [:check_interrupts]}, + :vm_flags => []}, {:opcode => :cast_for_single_block_arg, :args => [], :stack => [1,1]}, {:opcode => :cast_for_multi_block_arg, :args => [], :stack => [1,1]}, {:opcode => :set_call_info, :args => [:int, :cache], :stack => [0,0]}, @@ -358,8 +358,14 @@ class InstructionSequence sz = stream.inject(0) { |acc, ele| acc + (ele.size * InstructionSet::InstructionSize) } @iseq = InstructionSequence.new(sz) @offset = 0 - stream.each do |inst| - encode inst + begin + stream.each do |inst| + encode inst + end + rescue Exception => e + STDERR.puts "Unable to encode stream:" + STDERR.puts stream.inspect + raise e end return @iseq diff --git a/lib/compiler/bytecode.rb b/lib/compiler/bytecode.rb index 093df68..bb397e2 100644 --- a/lib/compiler/bytecode.rb +++ b/lib/compiler/bytecode.rb @@ -1733,15 +1733,6 @@ class Node end # TESTED - class MethodCall - def use_plugin(g) - @compiler.call_plugins.find do |plug| - plug.handle(g, self) - end - end - end - - # TESTED class FCall def allow_private? true @@ -1829,7 +1820,7 @@ class Node end def bytecode(g) - return if use_plugin(g) + return if use_plugin(g, :call) emit_args(g) @@ -2101,6 +2092,8 @@ class Node meth.sret meth.close + use_plugin g, :method, desc + return desc end diff --git a/lib/compiler/compiler.rb b/lib/compiler/compiler.rb index d03c2cc..9a2a92b 100644 --- a/lib/compiler/compiler.rb +++ b/lib/compiler/compiler.rb @@ -87,7 +87,7 @@ class Compiler def initialize(gen_class, binding=nil) @variables = {} @generator_class = gen_class - @call_plugins = [] + @plugins = Hash.new { |h,k| h[k]= [] } @file = "(unknown)" @line = 0 @@ -156,7 +156,7 @@ class Compiler end end - attr_reader :call_plugins + attr_reader :plugins attr_accessor :generator_class def set_position(file, line) @@ -182,6 +182,7 @@ class Compiler activate_default :inline activate_default :fastsystem activate_default :fastgeneric + activate_default :auto_primitive end def activate_default(name) @@ -191,7 +192,7 @@ class Compiler def activate(name) cls = Compiler::Plugins.find_plugin(name) raise Error, "Unknown plugin '#{name}'" unless cls - @call_plugins << cls.new(self) + @plugins[cls.kind] << cls.new(self) end def inspect diff --git a/lib/compiler/generator.rb b/lib/compiler/generator.rb index 258e7f4..d7a9046 100644 --- a/lib/compiler/generator.rb +++ b/lib/compiler/generator.rb @@ -37,8 +37,36 @@ class Compiler @exceptions = [] end - attr_reader :ip, :cache_size, :exceptions + attr_reader :ip, :cache_size, :exceptions, :stream, :literals attr_accessor :break, :redo, :next, :retry, :ensure_return + + def ===(pattern) + + return false unless @stream.size == pattern.size + + i = 0 + @stream.each do |part| + pat = pattern[i] + j = 0 + + if pat == :any + j += 1 + next + end + + part.each do |e| + s = pat[j] + next if s == :any + return false unless e == s + + j += 1 + end + + i += 1 + end + + return true + end def run(node) node.bytecode self diff --git a/lib/compiler/nodes.rb b/lib/compiler/nodes.rb index fa82484..27d2758 100644 --- a/lib/compiler/nodes.rb +++ b/lib/compiler/nodes.rb @@ -87,6 +87,12 @@ class Node obj.nil? ? Nil.new(@compiler) : obj end + def use_plugin(g, kind, *args) + @compiler.plugins[kind].find do |plug| + plug.handle(g, self, *args) + end + end + # Start of Node subclasses class ClosedScope < Node diff --git a/lib/compiler/plugins.rb b/lib/compiler/plugins.rb index 0c1cb6a..e0d3869 100644 --- a/lib/compiler/plugins.rb +++ b/lib/compiler/plugins.rb @@ -17,10 +17,15 @@ module Compiler::Plugins @compiler = compiler end - def self.plugin(name) + def self.plugin(name, kind=:call) + @kind = kind Compiler::Plugins.add_plugin name, self end + def self.kind + @kind + end + def call_match(c, const, method) return false unless c.call? return false unless c.method == method @@ -314,4 +319,60 @@ module Compiler::Plugins end end + + class AutoPrimitiveDetection < Plugin + plugin :auto_primitive, :method + + SingleInt = [[:check_argcount, 0, 0], [:push_int, :any], [:sret]] + Literal = [[:check_argcount, 0, 0], [:push_literal, 0], [:sret]] + Self = [[:check_argcount, 0, 0], [:push_self], [:sret]] + Ivar = [[:check_argcount, 0, 0], [:push_ivar, 0], [:sret]] + Field = [[:check_argcount, 0, 0], [:push_my_field, :any], [:sret]] + + def handle(g, obj, meth) + ss = meth.generator.stream + + return true unless ss.size == 3 + + gen = meth.generator + + if gen === SingleInt + meth.generator.literals[0] = ss[1][1] + meth.generator.as_primitive :opt_push_literal + elsif gen === Literal + # The value we want is already in literal 0 + meth.generator.as_primitive :opt_push_literal + elsif gen === Self + meth.generator.as_primitive :opt_push_self + elsif gen === Ivar + meth.generator.as_primitive :opt_push_ivar + elsif gen === Field + meth.generator.literals[0] = ss[1][1] + meth.generator.as_primitive :opt_push_my_field + else + case ss[1].first + when :push_nil + lit = nil + when :push_true + lit = true + when :push_false + lit = false + when :meta_push_0 + lit = 0 + when :meta_push_1 + lit = 1 + when :meta_push_2 + lit = 2 + else + return true + end + + meth.generator.literals[0] = lit + meth.generator.as_primitive :opt_push_literal + end + + true + end + + end end diff --git a/lib/timeout.rb b/lib/timeout.rb index dc92964..ac12c6e 100644 --- a/lib/timeout.rb +++ b/lib/timeout.rb @@ -5,6 +5,7 @@ # # = Copyright # +# Copyright - (C) 2008 Evan Phoenix # Copyright:: (C) 2000 Network Applied Communication Laboratory, Inc. # Copyright:: (C) 2000 Information-technology Promotion Agency, Japan # @@ -27,6 +28,8 @@ # } # +require 'thread' + module Timeout ## @@ -35,6 +38,89 @@ module Timeout class Error<Interrupt end + # A mutex to protect @requests + @mutex = Mutex.new + + # All the outstanding TimeoutRequests + @requests = [] + + # Represents +thr+ asking for it to be timeout at in +secs+ + # seconds. At timeout, raise +exc+. + class TimeoutRequest + def initialize(secs, thr, exc) + @left = secs + @thread = thr + @exception = exc + end + + attr_reader :thread, :left + + # Called because +time+ seconds have gone by. Returns + # true if the request has no more time left to run. + def elapsed(time) + @left -= time + @left <= 0 + end + + # Raise @exception if @thread. + def cancel + if @thread and @thread.alive? + @thread.raise @exception, "execution expired" + end + + @left = 0 + end + + # Abort this request, ie, we don't care about tracking + # the thread anymore. + def abort + @thread = nil + @left = 0 + end + end + + def self.add_timeout(time, exc) + + @controller ||= Thread.new do + while true + if @requests.empty? + sleep + next + end + + min = nil + + @mutex.synchronize do + min = @requests.min { |a,b| a.left <=> b.left } + end + + slept_for = sleep(min.left) + + @mutex.synchronize do + @requests.delete_if do |r| + if r.elapsed(slept_for) + r.cancel + true + else + false + end + end + end + + end + end + + req = TimeoutRequest.new(time, Thread.current, exc) + + @mutex.synchronize do + @requests << req + end + + @controller.run + + return req + end + ## # Executes the method's block. If the block execution terminates before +sec+ # seconds has passed, it returns true. If not, it terminates the execution @@ -43,20 +129,17 @@ module Timeout # Note that this is both a method of module Timeout, so you can 'include # Timeout' into your classes so they have a #timeout method, as well as a # module method, so you can call it directly as Timeout.timeout(). - + def timeout(sec, exception=Error) return yield if sec == nil or sec.zero? raise ThreadError, "timeout within critical session" if Thread.critical + + req = Timeout.add_timeout sec, exception + begin - x = Thread.current - y = Thread.start { - sleep sec - x.raise exception, "execution expired" if x.alive? - } yield sec - # return true ensure - y.kill if y and y.alive? + req.abort end end diff --git a/shotgun/Makefile b/shotgun/Makefile index 3427377..bf54b7c 100644 --- a/shotgun/Makefile +++ b/shotgun/Makefile @@ -127,7 +127,7 @@ dtrace.h: rubinius.d /usr/sbin/dtrace -h -s rubinius.d -o dtrace.h rubinius.bin: $(RBLIB) main.o - $(COMP) -o rubinius.bin main.o $(RBLIB) $(BIN_RPATH) + $(COMP) -o rubinius.bin main.o $(RBLIB) $(BIN_RPATH) $(LDFLAGS) test/test_state: test/test_state.c library $(COMP) -c -o test/test_state.o test/test_state.c $(CFLAGS) diff --git a/shotgun/lib/Makefile b/shotgun/lib/Makefile index 2bdff86..353aeec 100644 --- a/shotgun/lib/Makefile +++ b/shotgun/lib/Makefile @@ -54,7 +54,9 @@ ifeq ($(UNAME),SunOS) LIBS+= -lsocket endif -ifdef DEV +ifdef PROF + OPTIMIZATIONS=-O2 +else ifdef DEV OPTIMIZATIONS= else INLINE_OPTS=-Winline -finline-limit=2000 --param max-inline-insns-single=3500 --param large-function-growth=7000 --param inline-unit-growth=1500 diff --git a/shotgun/lib/cpu.c b/shotgun/lib/cpu.c index 1c7dc07..b892d45 100644 --- a/shotgun/lib/cpu.c +++ b/shotgun/lib/cpu.c @@ -131,7 +131,6 @@ void cpu_add_roots(STATE, cpu c, ptr_array roots) { ar(c->sender); ar(c->self); - ar(c->cache); ar(c->exception); ar(c->enclosing_class); ar(c->main); @@ -142,6 +141,7 @@ void cpu_add_roots(STATE, cpu c, ptr_array roots) { ar(c->debug_channel); ar(c->control_channel); ar(c->current_scope); + ar(c->locals); len = ptr_array_length(c->paths); ptr_array_append(roots, (xpointer)I2N(len)); // printf("Paths: %d\n", len); @@ -190,7 +190,6 @@ void cpu_update_roots(STATE, cpu c, ptr_array roots, int start) { ar(c->sender); ar(c->self); - ar(c->cache); ar(c->exception); ar(c->enclosing_class); ar(c->main); @@ -201,6 +200,7 @@ void cpu_update_roots(STATE, cpu c, ptr_array roots, int start) { ar(c->debug_channel); ar(c->control_channel); ar(c->current_scope); + ar(c->locals); tmp = ptr_array_get_index(roots, start++); len = N2I((OBJECT)tmp); for(i = 0; i < len; start++, i++) { @@ -247,6 +247,9 @@ OBJECT cpu_new_exception2(STATE, cpu c, OBJECT klass, const char *msg, ...) { OBJECT cpu_const_get_in_context(STATE, cpu c, OBJECT sym) { OBJECT cur, klass, start, hsh, val; OBJECT cref, cbase; + int sym_hash; + + sym_hash = object_hash_int(state, sym); /* Look up the lexical scope first */ @@ -263,7 +266,7 @@ OBJECT cpu_const_get_in_context(STATE, cpu c, OBJECT sym) { if(klass == state->global->object) break; hsh = module_get_constants(klass); - val = hash_find_undef(state, hsh, sym); + val = hash_get_undef(state, hsh, sym_hash); if(val != Qundef) return val; cbase = staticscope_get_parent(cbase); @@ -274,7 +277,7 @@ OBJECT cpu_const_get_in_context(STATE, cpu c, OBJECT sym) { while(!NIL_P(cur) && cur != state->global->object) { hsh = module_get_constants(cur); - val = hash_find_undef(state, hsh, sym); + val = hash_get_undef(state, hsh, sym_hash); if(val != Qundef) return val; cur = module_get_superclass(cur); } @@ -282,7 +285,7 @@ OBJECT cpu_const_get_in_context(STATE, cpu c, OBJECT sym) { // As a last rescue, we search in Object's constants hsh = module_get_constants(state->global->object); - val = hash_find_undef(state, hsh, sym); + val = hash_get_undef(state, hsh, sym_hash); if(val != Qundef) return val; stack_push(sym); diff --git a/shotgun/lib/cpu.h b/shotgun/lib/cpu.h index 2664219..db6ea6e 100644 --- a/shotgun/lib/cpu.h +++ b/shotgun/lib/cpu.h @@ -92,7 +92,7 @@ struct cpu_task { struct rubinius_cpu { /* Normal registers are saved and restored per new method call . */ OBJECT self, sender; - OBJECT cache; + OBJECT locals; IP_TYPE *data; unsigned short type; unsigned short argcount; @@ -281,12 +281,16 @@ OBJECT cpu_sampler_disable(STATE); #define cpu_stack_top(state, c) (*(c)->sp_ptr) #define cpu_stack_set_top(state, c, oop) (*(c)->sp_ptr = oop) -typedef int (*prim_func)(STATE, cpu c, struct message *msg); -void cpu_patch_primitive(STATE, struct message *msg, prim_func func); -int cpu_perform_system_primitive(STATE, cpu c, int prim, struct message *msg); +#include "shotgun/lib/sendsite.h" -void cpu_patch_ffi(STATE, struct message *msg); +void cpu_initialize_sendsite(STATE, struct send_site *ss); +typedef int (*prim_func)(STATE, cpu c, const struct message *msg); +void cpu_patch_primitive(STATE, const struct message *msg, prim_func func); +int cpu_perform_system_primitive(STATE, cpu c, int prim, const struct message *msg); + +void cpu_patch_ffi(STATE, const struct message *msg); void ffi_call(STATE, cpu c, OBJECT ptr); +void ffi_autorelease(OBJECT ptr, int ar); OBJECT ffi_new_pointer(STATE, void *ptr); #endif /* RBS_CPU_H */ diff --git a/shotgun/lib/cpu_instructions.c b/shotgun/lib/cpu_instructions.c index 3ea7cbd..17f771e 100644 --- a/shotgun/lib/cpu_instructions.c +++ b/shotgun/lib/cpu_instructions.c @@ -523,7 +523,7 @@ static inline OBJECT _allocate_context(STATE, cpu c, OBJECT meth, int locals) { return ctx; } -static inline OBJECT cpu_create_context(STATE, cpu c, struct message *msg) { +static inline OBJECT cpu_create_context(STATE, cpu c, const struct message *msg) { OBJECT ctx; struct fast_context *fc; @@ -568,12 +568,6 @@ OBJECT cpu_create_block_context(STATE, cpu c, OBJECT env, int sp) { fc->method_module = Qnil; fc->type = FASTCTX_BLOCK; - /* If post send is nil, that means we're not allowed to return directly to - the home context. */ - if(NIL_P(blokenv_get_post_send(env))) { - fc->flags |= CTX_FLAG_NO_LONG_RETURN; - } - return ctx; } @@ -612,7 +606,7 @@ void cpu_raise_primitive_failure(STATE, cpu c, int primitive_idx) { cpu_raise_exception(state, c, primitive_failure); } -static inline int cpu_try_primitive(STATE, cpu c, struct message *msg) { +static inline int cpu_try_primitive(STATE, cpu c, const struct message *msg) { int prim, req; OBJECT prim_obj; @@ -736,12 +730,8 @@ inline void cpu_restore_context_with_home(STATE, cpu c, OBJECT ctx, OBJECT home) c->data = fc->data; c->type = fc->type; - - if(fc->type != FASTCTX_NMC) { - c->cache = fast_fetch(fc->method, CMETHOD_f_CACHE); - } else { - c->cache = Qnil; - } + + c->locals = FASTCTX(home)->locals; c->sender = fc->sender; c->sp = fc->sp; @@ -919,20 +909,6 @@ inline int cpu_return_to_sender(STATE, cpu c, OBJECT val, int consider_block, in printf("CTX: remote return from %p to %p\n", c->active_context, destination); } - /* If the current context is marked as not being allowed to - return long, raise an exception instead. */ - if(FASTCTX(c->active_context)->flags & CTX_FLAG_NO_LONG_RETURN) { - OBJECT exc; - home = rbs_const_get(state, BASIC_CLASS(object), "IllegalLongReturn"); - - exc = cpu_new_exception(state, c, home, "Unable to perform a long return"); - object_set_ivar(state, exc, SYM("@return_value"), val); - - cpu_raise_exception(state, c, exc); - - return TRUE; - } - /* If we're making a non-local return to a stack context... */ if(om_on_stack(state->om, destination)) { /* If we're returning to a reference'd context, reset the @@ -1079,14 +1055,70 @@ static inline void cpu_activate_method(STATE, cpu c, struct message *msg) { cpu_restore_context_with_home(state, c, ctx, ctx); } +static inline void cpu_perform(STATE, cpu c, const struct message *msg) { + OBJECT ctx; + + c->depth++; + if(c->depth == CPU_MAX_DEPTH) { + machine_handle_fire(FIRE_STACK); + } + + ctx = cpu_create_context(state, c, msg); + + /* If it was missing, setup some extra data in the MethodContext for + the method_missing method to check out, to see why it was missing. */ + if(msg->missing && msg->priv) { + methctx_reference(state, ctx); + object_set_ivar(state, ctx, SYM("@send_private"), Qtrue); + } + + cpu_save_registers(state, c, msg->args); + cpu_restore_context_with_home(state, c, ctx, ctx); +} + + +static inline void +cpu_patch_mono(STATE, struct send_site *ss, struct message *msg); + +static inline void +cpu_patch_missing(STATE, struct send_site *ss, struct message *msg); + +static void +_cpu_ss_basic(STATE, cpu c, struct send_site *ss, struct message *msg) { + msg->missing = 0; + + sassert(cpu_locate_method(state, c, msg)); + + /* If it's not method_missing, cache the details of msg in the send_site */ + if(!msg->missing) { + cpu_patch_mono(state, ss, msg); + } else { + cpu_patch_missing(state, ss, msg); + msg->args += 1; + stack_push(msg->name); + } + + if(cpu_try_primitive(state, c, msg)) return; + + cpu_perform(state, c, msg); +} + +void cpu_initialize_sendsite(STATE, struct send_site *ss) { + ss->lookup = _cpu_ss_basic; +} + /* Send Site specialization 1: execute a primitive directly. */ -static int _cpu_ss_mono_prim(STATE, cpu c, struct send_site *ss, struct message *msg) { +static void _cpu_ss_mono_prim(STATE, cpu c, struct send_site *ss, + struct message *msg) { prim_func func; int _orig_sp; OBJECT *_orig_sp_ptr; - if(_real_class(state, msg->recv) != ss->data1) return SEND_SITE_ABORT; + if(_real_class(state, msg->recv) != ss->data1) { + _cpu_ss_basic(state, c, ss, msg); + return; + } _orig_sp_ptr = c->sp_ptr; _orig_sp = c->sp; @@ -1100,15 +1132,13 @@ static int _cpu_ss_mono_prim(STATE, cpu c, struct send_site *ss, struct message c->sp_ptr = _orig_sp_ptr; c->sp = _orig_sp; - return SEND_SITE_BYTECODE; + cpu_perform(state, c, msg); } - - return SEND_SITE_DONE; } /* Called before a primitive is run the slow way, allowing the send_site to be patch * to call the primitive directly. */ -void cpu_patch_primitive(STATE, struct message *msg, prim_func func) { +void cpu_patch_primitive(STATE, const struct message *msg, prim_func func) { struct send_site *ss; if(!REFERENCE_P(msg->send_site)) return; @@ -1124,27 +1154,29 @@ void cpu_patch_primitive(STATE, struct message *msg, prim_func func) { } /* Send Site specialization 2: Run an ffi function directly. */ -static int _cpu_ss_mono_ffi(STATE, cpu c, struct send_site *ss, struct message *msg) { +static void _cpu_ss_mono_ffi(STATE, cpu c, struct send_site *ss, + struct message *msg) { rni_context *ctx; nf_stub_ffi func; func = (nf_stub_ffi)ss->c_data; // printf("mono-ffi: %p (%s)\n", func, _inspect(ss->name)); - if(_real_class(state, msg->recv) != ss->data1) return SEND_SITE_ABORT; + if(_real_class(state, msg->recv) != ss->data1) { + _cpu_ss_basic(state, c, ss, msg); + return; + } ctx = subtend_retrieve_context(); ctx->state = state; ctx->cpu = c; func(); - - return SEND_SITE_DONE; } /* Called before an FFI function is run the slow way, allowing the send_site to be patch * to call the function directly. */ -void cpu_patch_ffi(STATE, struct message *msg) { +void cpu_patch_ffi(STATE, const struct message *msg) { struct send_site *ss; if(!REFERENCE_P(msg->send_site)) return; @@ -1171,25 +1203,61 @@ void cpu_patch_ffi(STATE, struct message *msg) { } /* Send Site specialzitation 3: simple monomorphic last implemenation cache. */ -static int _cpu_ss_mono(STATE, cpu c, struct send_site *ss, struct message *msg) { - if(_real_class(state, msg->recv) != ss->data1) return SEND_SITE_ABORT; +static void _cpu_ss_mono(STATE, cpu c, struct send_site *ss, + struct message *msg) { + + if(_real_class(state, msg->recv) != ss->data1) { + _cpu_ss_basic(state, c, ss, msg); + return; + } + msg->method = ss->data2; msg->module = ss->data3; + + if(cpu_try_primitive(state, c, msg)) return; - return SEND_SITE_RESOLVED; + cpu_perform(state, c, msg); } /* Saves the details of +msg+ in +ss+ and install _cpu_ss_mono in +ss+, so * that the next time +ss+ is used, it will try the cache details. */ static inline void -cpu_initialize_sendsite(STATE, struct send_site *ss, struct message *msg) { +cpu_patch_mono(STATE, struct send_site *ss, struct message *msg) { ss->lookup = _cpu_ss_mono; SET_STRUCT_FIELD(msg->send_site, ss->data1, _real_class(state, msg->recv)); SET_STRUCT_FIELD(msg->send_site, ss->data2, msg->method); SET_STRUCT_FIELD(msg->send_site, ss->data3, msg->module); } -static void _cpu_on_no_method(STATE, cpu c, struct message *msg) { +static void +_cpu_ss_missing(STATE, cpu c, struct send_site *ss, struct message *msg) { + if(_real_class(state, msg->recv) != ss->data1) { + _cpu_ss_basic(state, c, ss, msg); + return; + } + + msg->method = ss->data2; + msg->module = ss->data3; + + msg->args += 1; + stack_push(msg->name); + + if(cpu_try_primitive(state, c, msg)) return; + + cpu_perform(state, c, msg); +} + +/* Saves the details of +msg+ in +ss+ and install _cpu_ss_mono in +ss+, so + * that the next time +ss+ is used, it will try the cache details. */ +static inline void +cpu_patch_missing(STATE, struct send_site *ss, struct message *msg) { + ss->lookup = _cpu_ss_missing; + SET_STRUCT_FIELD(msg->send_site, ss->data1, _real_class(state, msg->recv)); + SET_STRUCT_FIELD(msg->send_site, ss->data2, msg->method); + SET_STRUCT_FIELD(msg->send_site, ss->data3, msg->module); +} + +static void _cpu_on_no_method(STATE, cpu c, const struct message *msg) { char *str; OBJECT exc; @@ -1205,60 +1273,56 @@ static void _cpu_on_no_method(STATE, cpu c, struct message *msg) { /* Layer 4: send. Primary method calling function. */ inline void cpu_send_message(STATE, cpu c, struct message *msg) { - OBJECT ctx; struct send_site *ss; #ifdef TIME_LOOKUP uint64_t start = measure_cpu_time(); #endif -#if ENABLE_DTRACE - if(RUBINIUS_VM_SEND_BEGIN_ENABLED()) { - RUBINIUS_VM_SEND_BEGIN(); - } -#endif + //if(SENDSITE_P(msg->send_site)) { + ss = SENDSITE(msg->send_site); + msg->name = ss->name; + ss->lookup(state, c, ss, msg); + /* + } else { + msg->name = msg->send_site; + msg->send_site = Qnil; - msg->missing = 0; + msg->missing = 0; - ss = SENDSITE(msg->send_site); - msg->name = ss->name; - - /* If the send_site has a custom lookup function, use it. */ - if(ss->lookup) { - switch(ss->lookup(state, c, ss, msg)) { - case SEND_SITE_DONE: /* lookup did all the work, we're done. */ - return; - case SEND_SITE_RESOLVED: /* lookup filled in msg with the details. */ - goto dispatch; - case SEND_SITE_BYTECODE: /* same as resolved, but only run the bytecode */ - goto bytecode; + sassert(cpu_locate_method(state, c, msg)); + + if(msg->missing) { + msg->args += 1; + stack_push(msg->name); } + + if(cpu_try_primitive(state, c, msg)) return; + + cpu_perform(state, c, msg); } + */ - /* We're here if SEND_SITE_ABORT was returned, or if there was no - * ->lookup. locate method fills in msg with the details. */ +#ifdef TIME_LOOKUP + state->lookup_time += (measure_cpu_time() - start); +#endif +} + +void cpu_send_message_external(STATE, cpu c, struct message *msg) { + OBJECT ctx; + if(!cpu_locate_method(state, c, msg)) { - /* Only happens if locate_method can't even find method_missing. */ _cpu_on_no_method(state, c, msg); - goto done; + return; } - - /* If it's not method_missing, cache the details of msg in the send_site */ - if(!msg->missing) cpu_initialize_sendsite(state, ss, msg); - -dispatch: - - /* If using method_missing, put the name of the method on the stack and - * add an argument. */ + if(msg->missing) { msg->args += 1; stack_push(msg->name); } else { - /* Otherwise try and run this method as a primitive. */ - if(cpu_try_primitive(state, c, msg)) goto done; + if(cpu_try_primitive(state, c, msg)) return; } - -bytecode: + c->depth++; if(c->depth == CPU_MAX_DEPTH) { machine_handle_fire(FIRE_STACK); @@ -1275,19 +1339,12 @@ bytecode: cpu_save_registers(state, c, msg->args); cpu_restore_context_with_home(state, c, ctx, ctx); - -done: -#ifdef TIME_LOOKUP - state->lookup_time += (measure_cpu_time() - start); -#endif - - return; } + /* A version used when there is no send_site. */ void cpu_send(STATE, cpu c, OBJECT recv, OBJECT sym, int args, OBJECT block) { struct message msg; - OBJECT ctx; msg.recv = recv; msg.name = sym; @@ -1299,35 +1356,8 @@ void cpu_send(STATE, cpu c, OBJECT recv, OBJECT sym, int args, OBJECT block) { msg.send_site = Qnil; c->call_flags = 0; - - if(!cpu_locate_method(state, c, &msg)) { - _cpu_on_no_method(state, c, &msg); - return; - } - - if(msg.missing) { - msg.args += 1; - stack_push(msg.name); - } else { - if(cpu_try_primitive(state, c, &msg)) return; - } - - c->depth++; - if(c->depth == CPU_MAX_DEPTH) { - machine_handle_fire(FIRE_STACK); - } - - ctx = cpu_create_context(state, c, &msg); - /* If it was missing, setup some extra data in the MethodContext for - the method_missing method to check out, to see why it was missing. */ - if(msg.missing && msg.priv) { - methctx_reference(state, ctx); - object_set_ivar(state, ctx, SYM("@send_private"), Qtrue); - } - - cpu_save_registers(state, c, msg.args); - cpu_restore_context_with_home(state, c, ctx, ctx); + cpu_send_message_external(state, c, &msg); } void cpu_raise_exception(STATE, cpu c, OBJECT exc) { diff --git a/shotgun/lib/cpu_primitives.c b/shotgun/lib/cpu_primitives.c index cf99336..356ca11 100644 --- a/shotgun/lib/cpu_primitives.c +++ b/shotgun/lib/cpu_primitives.c @@ -118,7 +118,7 @@ void ffi_call(STATE, cpu c, OBJECT ptr); #include "shotgun/lib/primitive_implementation.gen" -int cpu_perform_system_primitive(STATE, cpu c, int prim, struct message *msg) { +int cpu_perform_system_primitive(STATE, cpu c, int prim, const struct message *msg) { int _ret = TRUE; int _orig_sp; OBJECT *_orig_sp_ptr; diff --git a/shotgun/lib/ffi_util.c b/shotgun/lib/ffi_util.c index 53ef154..5c4e033 100644 --- a/shotgun/lib/ffi_util.c +++ b/shotgun/lib/ffi_util.c @@ -259,6 +259,22 @@ void *ffi_add_ptr(char *ptr, int offset) { return (void*)(ptr + offset); } +long ffi_major(dev_t n) { +#ifdef major + return major(n); +#else + return -1; +#endif +} + +long ffi_minor(dev_t n) { +#ifdef minor + return minor(n); +#else + return -1; +#endif +} + /* FIXME: Kill these after the next rebuild of the stable RBAs */ int ffi_seek_set() { return SEEK_SET; } int ffi_seek_cur() { return SEEK_CUR; } diff --git a/shotgun/lib/hash.c b/shotgun/lib/hash.c index 96b29f8..5cf3f71 100644 --- a/shotgun/lib/hash.c +++ b/shotgun/lib/hash.c @@ -2,90 +2,22 @@ #include "shotgun/lib/tuple.h" #include "shotgun/lib/hash.h" -/* Adapted from st.c in 1.8.5 */ - -/* -Table of prime numbers 2^n+a, 2<=n<=30. -*/ -static const long primes[] = { - 8 + 3, - 16 + 3, - 23, - 32 + 5, - 43, - 53, - 59, - 64 + 3, - 73, - 83, - 97, - 109, - 128 + 3, - 137, - 149, - 157, - 167, - 179, - 191, - 197, - 211, - 229, - 256 + 27, - 512 + 9, - 1024 + 9, - 2048 + 5, - 4096 + 3, - 8192 + 27, - 16384 + 43, - 32768 + 3, - 65536 + 45, - 131072 + 29, - 262144 + 3, - 524288 + 21, - 1048576 + 7, - 2097152 + 17, - 4194304 + 15, - 8388608 + 9, - 16777216 + 43, - 33554432 + 35, - 67108864 + 15, - 134217728 + 29, - 268435456 + 3, - 536870912 + 11, - 1073741824 + 85, - 0 -}; - #define MINSIZE 8 -static int hash_new_size(int size) { - int i, p; - int newsize; - - for (i = 0, newsize = MINSIZE; - i < sizeof(primes)/sizeof(primes[0]); - i++) - { - p = primes[i]; - if(p > size) return p; - } - /* Ran out of polynomials */ - return -1; /* should raise exception */ -} - -#define MAX_DENSITY 5 - -/* end adaptation. */ +#define MAX_DENSITY 0.75 #define Increments 16 +#define find_bin(hash, total) (hash & (total - 1)) + OBJECT hash_new(STATE) { OBJECT hsh; hsh = hash_allocate(state); - hash_setup(state, hsh, 0); + hash_setup(state, hsh, MINSIZE); return hsh; } +/* size MUST be a power of 2 */ OBJECT hash_new_sized(STATE, int size) { OBJECT hsh; hsh = hash_allocate(state); @@ -95,7 +27,7 @@ OBJECT hash_new_sized(STATE, int size) { void hash_setup(STATE, OBJECT hsh, int size) { int sz; - sz = hash_new_size(size); + sz = size == 0 ? MINSIZE : size; hash_set_keys(hsh, tuple_new(state, sz)); hash_set_values(hsh, tuple_new(state, sz)); hash_set_bins(hsh, I2N(sz)); @@ -147,7 +79,7 @@ static void hash_rehash(STATE, OBJECT hsh, int _ents) { OBJECT tbl, tup, ent, next; old_bins = N2I(hash_get_bins(hsh)); - new_bins = hash_new_size(old_bins + 1); + new_bins = old_bins * 2; tup = tuple_new(state, new_bins); tbl = hash_get_values(hsh); @@ -159,7 +91,7 @@ static void hash_rehash(STATE, OBJECT hsh, int _ents) { next = tuple_at(state, ent, 3); hv = (unsigned int)N2I(tuple_at(state, ent, 0)); - bin = hv % new_bins; + bin = find_bin(hv, new_bins); tuple_put(state, ent, 3, tuple_at(state, tup, (int)bin)); tuple_put(state, tup, (int)bin, ent); @@ -218,7 +150,7 @@ OBJECT hash_find_entry(STATE, OBJECT h, unsigned int hsh) { OBJECT entry, th; bins = (unsigned int)N2I(hash_get_bins(h)); - bin = hsh % bins; + bin = find_bin(hsh, bins); entry = tuple_at(state, hash_get_values(h), bin); // printf("start: %x, %ud, %d, %d\n", entry, hsh, bin, N2I(hash_get_bins(h))); @@ -249,7 +181,7 @@ OBJECT hash_add(STATE, OBJECT h, unsigned int hsh, OBJECT key, OBJECT data) { i = N2I(hash_get_entries(h)); b = N2I(hash_get_bins(h)); - if(i / b > MAX_DENSITY) { + if((double)i / (double)b > MAX_DENSITY) { hash_rehash(state, h, i); } @@ -298,8 +230,8 @@ OBJECT hash_get_undef(STATE, OBJECT hash, unsigned int hsh) { OBJECT hash_delete(STATE, OBJECT self, unsigned int hsh) { unsigned int bin; OBJECT entry, th, lk, val, lst; - - bin = hsh % N2I(hash_get_bins(self)); + + bin = find_bin(hsh, N2I(hash_get_bins(self))); entry = tuple_at(state, hash_get_values(self), bin); lst = Qnil; diff --git a/shotgun/lib/instructions.rb b/shotgun/lib/instructions.rb index 9d3e740..a57dd73 100644 --- a/shotgun/lib/instructions.rb +++ b/shotgun/lib/instructions.rb @@ -237,7 +237,12 @@ CODE end def push_local - "next_int; stack_push(fast_fetch(cpu_current_locals(state, c), _int));" + <<-CODE + next_int; + stack_push(fast_fetch(c->locals, _int)); + CODE + + # "next_int; stack_push(fast_fetch(cpu_current_locals(state, c), _int));" end def push_local_depth @@ -415,7 +420,7 @@ CODE next_int; t1 = stack_pop(); // printf("Set local %d to %s\\n", _int, _inspect(t1)); - t2 = cpu_current_locals(state, c); + t2 = c->locals; if(t2->gc_zone == 0) { sassert(_int < NUM_FIELDS(t2) && "locals tuple sized wrong"); fast_unsafe_set(t2, _int, t1); diff --git a/shotgun/lib/primitive_names.rb b/shotgun/lib/primitive_names.rb index 00921c8..e71477b 100755 --- a/shotgun/lib/primitive_names.rb +++ b/shotgun/lib/primitive_names.rb @@ -194,7 +194,11 @@ module Bytecode :get_ivar, :set_index, :get_index, - :dispatch_as_method + :dispatch_as_method, + :opt_push_literal, + :opt_push_self, + :opt_push_my_field, + :opt_push_ivar ] end end diff --git a/shotgun/lib/primitives.rb b/shotgun/lib/primitives.rb index 99e021b..2a63ed8 100644 --- a/shotgun/lib/primitives.rb +++ b/shotgun/lib/primitives.rb @@ -2,13 +2,21 @@ require File.dirname(__FILE__) + '/primitive_names' class ShotgunPrimitives + OldMap = { + :set_ivar => 1024, + :get_ivar => 1025, + :set_index => 1026, + :get_index => 1027, + :dispatch_as_method => 1028 + } + def generate_select(fd, op="prim") i = 1 order = Bytecode::Compiler::Primitives File.open("primitive_implementation.gen", "w") do |f| order.each do |ins| - f.puts "int cpu_primitive_#{ins}(STATE, cpu c, struct message *msg) {" + f.puts "int cpu_primitive_#{ins}(STATE, cpu c, const struct message *msg) {" f.puts send(ins) f.puts " DONE();\n}" end @@ -30,6 +38,9 @@ class ShotgunPrimitives fd.puts " // NOOP is 0 and signifies a method with no primitive" order.each do |ins| fd.puts "case #{i}: { // #{ins}" + if old = OldMap[ins] + fd.puts "case #{old}:" + end fd.puts " cpu_patch_primitive(state, msg, cpu_primitive_#{ins});" fd.puts " _ret = cpu_primitive_#{ins}(state, c, msg);" fd.puts " break;\n}" @@ -71,6 +82,8 @@ class ShotgunPrimitives for(i = 0; pi[i].name; i++) { if(!strcmp(target, pi[i].name)) return pi[i].index; } + + printf("Unknown primitive %s\\n", target); return -1; } @@ -616,6 +629,7 @@ class ShotgunPrimitives def gettimeofday <<-CODE + OBJECT t1; struct timeval tv; /* don't fill in the 2nd argument here. getting the timezone here @@ -624,11 +638,11 @@ class ShotgunPrimitives gettimeofday(&tv, NULL); /* update Time::TIMEVAL_FIELDS when changing order of fields */ - msg->recv = array_new(state, 2); - array_set(state, msg->recv, 0, ML2N(tv.tv_sec)); - array_set(state, msg->recv, 1, ML2N(tv.tv_usec)); + t1 = array_new(state, 2); + array_set(state, t1, 0, ML2N(tv.tv_sec)); + array_set(state, t1, 1, ML2N(tv.tv_usec)); - RET(msg->recv); + RET(t1); CODE end @@ -1101,7 +1115,7 @@ class ShotgunPrimitives k = bytearray_bytes(state, msg->recv); GUARD( j >= 0 && j < k ); - + indexed = (unsigned char*)bytearray_byte_address(state, msg->recv); indexed += j; t2 = UI2N(*indexed = N2I(t2)); @@ -1482,18 +1496,18 @@ class ShotgunPrimitives } err = tcgetattr(STDOUT_FILENO, &ts); - + if(err == -1) { /* TODO: handle errno */ RET(Qfalse); } - + ts.c_lflag &= ~ICANON; /* -icanon */ ts.c_lflag &= ~ISIG; /* -isig */ ts.c_lflag &= ~ECHO; /* -echo */ ts.c_cc[VMIN] = 1; /* min 1 */ - + err = tcsetattr(STDOUT_FILENO, TCSANOW, &ts); - + if(err == -1) { /* TODO: handle errno */ RET(Qfalse); } @@ -3331,6 +3345,39 @@ class ShotgunPrimitives CODE end + def opt_push_literal + <<-CODE + OBJECT lits; + + lits = cmethod_get_literals(msg->method); + RET(fast_fetch(lits, 0)); + CODE + end + + def opt_push_self + <<-CODE + RET(msg->recv); + CODE + end + + def opt_push_ivar + <<-CODE + OBJECT lits; + + lits = cmethod_get_literals(msg->method); + RET(object_get_ivar(state, msg->recv, fast_fetch(lits, 0))); + CODE + end + + def opt_push_my_field + <<-CODE + OBJECT lits; + + lits = cmethod_get_literals(msg->method); + RET(NTH_FIELD(msg->recv, N2I(fast_fetch(lits, 0)))); + CODE + end + end prim = ShotgunPrimitives.new diff --git a/shotgun/lib/selector.c b/shotgun/lib/selector.c index 8a0b71c..0e621a3 100644 --- a/shotgun/lib/selector.c +++ b/shotgun/lib/selector.c @@ -50,7 +50,7 @@ void selector_clear(STATE, OBJECT self) { for(i = 0; i < sz; i++) { ss = array_get(state, ary, i); - SENDSITE(ss)->lookup = NULL; + cpu_initialize_sendsite(state, SENDSITE(ss)); } } diff --git a/shotgun/lib/sendsite.c b/shotgun/lib/sendsite.c index ab2f737..ed9f602 100644 --- a/shotgun/lib/sendsite.c +++ b/shotgun/lib/sendsite.c @@ -28,6 +28,8 @@ OBJECT send_site_create(STATE, OBJECT name, send_site_lookup func) { ss->data1 = ss->data2 = ss->data3 = Qnil; ss->lookup = func; + cpu_initialize_sendsite(state, ss); + selector_associate(state, ss->selector, ss_obj); return ss_obj; diff --git a/shotgun/lib/sendsite.h b/shotgun/lib/sendsite.h index 492568a..e832cb0 100644 --- a/shotgun/lib/sendsite.h +++ b/shotgun/lib/sendsite.h @@ -1,7 +1,10 @@ +#ifndef RBX_SENDSITE_H +#define RBX_SENDSITE_H + typedef struct send_site _send_site; -typedef int (*send_site_lookup)(STATE, cpu c, _send_site *ss, struct message *msg); +typedef void (*send_site_lookup)(STATE, cpu c, _send_site *ss, struct message *msg); struct send_site { OBJECT name; @@ -19,11 +22,7 @@ struct send_site { #define SEND_SITE_OBJECT_FIELDS 5 -#define SEND_SITE_ABORT 0 -#define SEND_SITE_DONE 1 -#define SEND_SITE_RESOLVED 2 -#define SEND_SITE_BYTECODE 3 - void send_site_init(STATE); OBJECT send_site_create(STATE, OBJECT name, send_site_lookup func); +#endif diff --git a/shotgun/lib/shotgun.h b/shotgun/lib/shotgun.h index d7fcb43..f14d6e8 100644 --- a/shotgun/lib/shotgun.h +++ b/shotgun/lib/shotgun.h @@ -4,7 +4,7 @@ #define INTERNAL_DEBUG 0 #define TRACK_STATS 0 -#define DISABLE_CHECKS 0 +#define DISABLE_CHECKS 1 // #define TIME_LOOKUP 1 #include <stdio.h>
From the Design Piracy series on my blog: