1 require 'compiler/execute'
6 # A namespace for compiler plugins. A plugin emits custom bytecode for an
13 def self.add_plugin(name, cls)
17 def self.find_plugin(name)
22 def initialize(compiler)
26 def self.plugin(name, kind=:call)
28 Plugins.add_plugin name, self
35 def call_match(c, const, method)
36 return false unless c.call?
37 return false unless c.method == method
38 return false unless c.object.kind_of? Node::ConstFind
39 return false unless c.object.name == const
45 # Handles block_given?
47 class BlockGiven < Plugin
53 if call.method == :block_given? or call.method == :iterator?
64 # Handles Ruby.primitive
66 class PrimitiveDeclaration < Plugin
71 return false unless call_match(call, :Ruby, :primitive)
73 prim = call.arguments.first.value
82 # Handles Rubinius.asm
84 class InlineAssembly < Plugin
89 return false unless call_match(call, :Rubinius, :asm)
90 return false unless call.block
92 exc = ExecuteContext.new(g)
96 call.block.arguments.names.each do |name|
97 exc.set_local name, args[i]
101 exc.execute call.block.body
111 class CurrentMethod < Plugin
113 plugin :current_method
116 return false unless call.kind_of? Node::VCall
117 if call.method == :__METHOD__
128 # Handles emitting fast VM instructions for certain math operations.
130 class FastMathOperators < Plugin
135 :+ => :meta_send_op_plus,
136 :- => :meta_send_op_minus,
137 :== => :meta_send_op_equal,
138 :"!=" => :meta_send_op_nequal,
139 :=== => :meta_send_op_tequal,
140 :< => :meta_send_op_lt,
141 :> => :meta_send_op_gt
145 name = MetaMath[call.method]
147 if name and call.argcount == 1
149 call.receiver_bytecode(g)
159 # Handles emitting fast VM instructions for certain methods.
161 class FastGenericMethods < Plugin
166 :call => :meta_send_call
170 # Don't handle send's with a block or non static args.
171 return false if call.block or call.argcount.nil?
173 if name = Methods[call.method]
175 call.receiver_bytecode(g)
176 g.add name, call.argcount
185 # Handles emitting save VM instructions for redifinition of math operators.
187 class SafeMathOperators < Plugin
196 name = MathOps[call.method]
197 if name and call.argcount == 1
199 call.receiver_bytecode(g)
200 g.send name, 1, false
208 # Handles constant folding
210 class ConstantExpressions < Plugin
214 MathOps = [:+, :-, :*, :/, :**]
218 return false unless MathOps.include? op and call.argcount == 1
221 arg = call.arguments.first
222 if call.object.kind_of? Node::NumberLiteral and call.arguments.first.kind_of? Node::NumberLiteral
223 res = obj.value.__send__(op, arg.value)
224 if res.kind_of? Fixnum
236 # Prototype plugin for handling inlining. Not in use.
238 class CompilerInlining < Plugin
242 :times => :fixnum_times
246 return false unless call.block.kind_of? Node::Iter
247 if handler = Methods[call.method]
248 return __send__(handler, g, call)
254 def fixnum_times(g, call)
255 return false unless call.no_args?
256 return false unless call.block.arguments.names.empty?
258 do_call = g.new_label
260 # Since there are no args, this just makes sure that the internals
261 # are setup properly.
264 call.receiver_bytecode(g)
269 g.check_serial :times, 0
274 desc = MethodDescription.new @compiler.generator_class, call.block.locals
275 desc.name = :__inlined_block__
276 desc.required, desc.optional = call.block.argument_info
279 sub.set_line g.line, g.file
281 @compiler.show_errors(sub) do
289 # TODO: break needs to be handled differently. See the specs for
290 # a nested while with a complex break expression in the Integer#times specs.
292 sub.redo = sub.new_label
294 # Get rid of the block args.
298 # Create the loop counter
301 sub.meta_send_op_plus
307 # Descrement the loop counter
311 sub.meta_send_op_minus
313 # Check if the loop counter is 0
319 # To the times logic now, calling block.body
322 call.block.body.bytecode(sub)
345 call.block.bytecode(g)
347 call.block_bytecode(g)
354 # Maps various methods to VM instructions
356 class SystemMethods < Plugin
361 :__kind_of__ => :kind_of,
362 :__instance_of__ => :instance_of,
364 :__equal__ => :equal,
365 :__class__ => :class,
366 :__fixnum__ => :is_fixnum,
367 :__symbol__ => :is_symbol,
371 # How many arguments each method takes.
374 :__instance_of__ => 1,
384 return false if call.block
386 name = Methods[call.method]
388 return false unless name
389 return false unless Args[call.method] == call.argcount
392 call.receiver_bytecode(g)
400 # Detects common simple expressions and simplifies them
402 class AutoPrimitiveDetection < Plugin
403 plugin :auto_primitive, :method
405 SingleInt = [[:check_argcount, 0, 0], [:push_int, :any], [:sret]]
406 Literal = [[:check_argcount, 0, 0], [:push_literal, 0], [:sret]]
407 Self = [[:check_argcount, 0, 0], [:push_self], [:sret]]
408 Ivar = [[:check_argcount, 0, 0], [:push_ivar, 0], [:sret]]
409 Field = [[:check_argcount, 0, 0], [:push_my_field, :any], [:sret]]
411 def handle(g, obj, meth)
412 ss = meth.generator.stream
414 return true unless ss.size == 3
419 meth.generator.literals[0] = ss[1][1]
420 meth.generator.as_primitive :opt_push_literal
421 elsif gen === Literal
422 # The value we want is already in literal 0
423 meth.generator.as_primitive :opt_push_literal
425 meth.generator.as_primitive :opt_push_self
427 meth.generator.as_primitive :opt_push_ivar
429 meth.generator.literals[0] = ss[1][1]
430 meth.generator.as_primitive :opt_push_my_field
449 meth.generator.literals[0] = lit
450 meth.generator.as_primitive :opt_push_literal
459 # Conditional compilation
461 class ConditionalCompilation < Plugin
462 plugin :conditional_compilation, :conditional_compilation
465 # Matches on the special form Rubinius.compile_if.
467 # If the form is not matched, returns nil. If the form does match,
468 # however, then its argument is checked. If the arg evaluates to true, the
469 # contained code should be compiled and if false, it should be omitted. To
470 # achieve this, we throw a symbol in both cases (:iter for former,
471 # :newline for latter.) The symbols are caught at the appropriate spots
472 # further up the processing branch and those nodes then appropriately
473 # slice up the sexp to produce the desired result.
475 # See Node#consume and Iter#consume for those actions.
477 def handle(g, call_node, sexp)
478 # The sexp should look something like
480 # [[:const, :Rubinius], :compile_if, [:array, [<type>, ...]]]
482 # Currently we only check global variables but stuff like strings
483 # to eval or even actual composable method calls are possible.
484 if sexp[1] == :compile_if and sexp[0].kind_of? Array and sexp[0][1] == :Rubinius
485 throw(eval(sexp[2][1][1].to_s) ? :iter : :newline)