[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[dotgnu-pnet-commits] libjit ChangeLog jitruby/README jitruby/generat...
From: |
Aleksey Demakov |
Subject: |
[dotgnu-pnet-commits] libjit ChangeLog jitruby/README jitruby/generat... |
Date: |
Fri, 12 Dec 2008 11:31:06 +0000 |
CVSROOT: /sources/dotgnu-pnet
Module name: libjit
Changes by: Aleksey Demakov <avd> 08/12/12 11:31:05
Modified files:
. : ChangeLog
Added files:
jitruby : README generate_rdoc.rb metaconfig
post-install.rb post-setup.rb
ruby-libjit.gemspec run_tests.rb setup.rb
jitruby/ext : extconf.rb insns.inc.rpp jit.c
method_data.c.rpp method_data.h minimal_node.c
minimal_node.h rubyjit.h rubypp.rb
jitruby/lib : jit.rb
jitruby/lib/jit: array.rb function.rb pointer.rb struct.rb
value.rb
jitruby/sample : fib.rb gcd_benchmark.rb simple.rb
jitruby/test : assertions.rb test_jit_array.rb
test_jit_function.rb test_jit_pointer.rb
test_jit_struct.rb test_jit_value.rb
Log message:
add Paul Brannan's ruby-libjit
CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/libjit/ChangeLog?cvsroot=dotgnu-pnet&r1=1.397&r2=1.398
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/README?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/generate_rdoc.rb?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/metaconfig?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/post-install.rb?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/post-setup.rb?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/ruby-libjit.gemspec?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/run_tests.rb?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/setup.rb?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/ext/extconf.rb?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/ext/insns.inc.rpp?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/ext/jit.c?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/ext/method_data.c.rpp?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/ext/method_data.h?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/ext/minimal_node.c?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/ext/minimal_node.h?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/ext/rubyjit.h?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/ext/rubypp.rb?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/lib/jit.rb?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/lib/jit/array.rb?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/lib/jit/function.rb?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/lib/jit/pointer.rb?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/lib/jit/struct.rb?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/lib/jit/value.rb?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/sample/fib.rb?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/sample/gcd_benchmark.rb?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/sample/simple.rb?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/test/assertions.rb?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/test/test_jit_array.rb?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/test/test_jit_function.rb?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/test/test_jit_pointer.rb?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/test/test_jit_struct.rb?cvsroot=dotgnu-pnet&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/libjit/jitruby/test/test_jit_value.rb?cvsroot=dotgnu-pnet&rev=1.1
Patches:
Index: ChangeLog
===================================================================
RCS file: /sources/dotgnu-pnet/libjit/ChangeLog,v
retrieving revision 1.397
retrieving revision 1.398
diff -u -b -r1.397 -r1.398
--- ChangeLog 10 Dec 2008 20:44:24 -0000 1.397
+++ ChangeLog 12 Dec 2008 11:30:57 -0000 1.398
@@ -1,3 +1,7 @@
+2008-12-12 Aleksey Demakov <address@hidden>
+
+ * jitruby/*: add Paul Brannan's ruby-libjit.
+
2008-12-11 Juan Jesus Garcia de Soria <address@hidden>
* jit/jit-insn.c (jit_insn_call_native): extend small int return
Index: jitruby/README
===================================================================
RCS file: jitruby/README
diff -N jitruby/README
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/README 12 Dec 2008 11:30:57 -0000 1.1
@@ -0,0 +1,50 @@
+Ruby-libjit 0.1.0
+Copyright (C) 2008 Paul Brannan
+
+Ruby-libjit is a wrapper for the libjit library. It provides basic
+functionality for jit-compiling functions, including integrating those
+functions as callable methods from within Ruby. Abstractions are also
+provided so that jit code may be written in a ruby-like manner.
+
+Please see the file COPYING for license information.
+
+A simple example:
+
+ :include: sample/simple.rb
+
+Looping structures and other abstractions are provided to make writing
+jit code easier:
+
+ :include: sample/fib.rb
+
+To build ruby-libjit, you will need to install libjit. If it is not
+available pre-compiled for your platform, you may build the latest
+release like this:
+
+ $ wget ftp://ftp.gnu.org/gnu/dotgnu/pnet/libjit-0.1.0.tar.gz
+ $ tar xvfz libjit-0.1.0.tar.gz
+ $ cd libjit-0.1.0
+ $ ./configure
+ $ make
+ $ sudo make install
+
+Or the latest development version like this:
+
+ $ cvs -z3 -d:pserver:address@hidden:/sources/dotgnu-pnet co libjit
+ $ cd libjit
+ $ ./auto_gen.sh
+ $ ./configure
+ $ make
+ $ sudo make install
+
+To build ruby-libjit, run setup.rb:
+
+ $ ruby setup.rb config
+ $ ruby setup.rb setup
+ $ sudo ruby setup.rb install
+
+For a more complete JIT framework and compiler for Ruby code, please
+take a look at Ludicrous:
+
+ http://rubystuff.org/ludicrous/
+
Index: jitruby/generate_rdoc.rb
===================================================================
RCS file: jitruby/generate_rdoc.rb
diff -N jitruby/generate_rdoc.rb
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/generate_rdoc.rb 12 Dec 2008 11:30:58 -0000 1.1
@@ -0,0 +1,33 @@
+require 'find'
+
+def list_files(dir, pattern)
+ arr = []
+ Find.find(dir) do |filename|
+ if filename =~ pattern then
+ arr.push(filename)
+ end
+ end
+ return arr
+end
+
+def generate_rdoc(*options)
+ begin
+ require 'rdoc/rdoc'
+ rescue LoadError
+ puts "WARNING: RDoc not installed; skipping generation of docs"
+ return
+ end
+
+ r = RDoc::RDoc.new
+ rdoc_files = []
+ rdoc_files.concat [ 'README' ]
+ rdoc_files.concat list_files('lib', /\.rb$/) if File.exist?('lib')
+ rdoc_files.concat list_files('ext', /\.c$/) if File.exist?('ext')
+ rdoc_files.reject! { |file| file =~ %r{^ext/cached/} }
+ r.document(options + rdoc_files)
+end
+
+if __FILE__ == $0 then
+ generate_rdoc(*ARGV)
+end
+
Index: jitruby/metaconfig
===================================================================
RCS file: jitruby/metaconfig
diff -N jitruby/metaconfig
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/metaconfig 12 Dec 2008 11:30:58 -0000 1.1
@@ -0,0 +1,4 @@
+add_bool_config(
+ 'without-tests',
+ false,
+ 'does not run tests')
Index: jitruby/post-install.rb
===================================================================
RCS file: jitruby/post-install.rb
diff -N jitruby/post-install.rb
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/post-install.rb 12 Dec 2008 11:30:58 -0000 1.1
@@ -0,0 +1,4 @@
+require 'generate_rdoc'
+
+generate_rdoc('--ri-site')
+
Index: jitruby/post-setup.rb
===================================================================
RCS file: jitruby/post-setup.rb
diff -N jitruby/post-setup.rb
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/post-setup.rb 12 Dec 2008 11:30:58 -0000 1.1
@@ -0,0 +1,7 @@
+require 'run_tests'
+require 'generate_rdoc'
+
+if config('without-tests') != 'yes' then
+ run_tests()
+end
+
Index: jitruby/ruby-libjit.gemspec
===================================================================
RCS file: jitruby/ruby-libjit.gemspec
diff -N jitruby/ruby-libjit.gemspec
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/ruby-libjit.gemspec 12 Dec 2008 11:30:58 -0000 1.1
@@ -0,0 +1,39 @@
+require 'enumerator'
+
+spec = Gem::Specification.new do |s|
+ s.name = 'ruby-libjit'
+ s.version = '0.1.0'
+ s.summary = 'A wrapper for the libjit library'
+ s.homepage = 'http://ruby-libjit.rubyforge.org'
+ s.rubyforge_project = 'ruby-libjit'
+ s.author = 'Paul Brannan'
+ s.email = 'address@hidden'
+
+ s.description = <<-END
+A wrapper for the libjit library
+ END
+
+
+ patterns = [
+ 'COPYING',
+ 'LGPL',
+ 'LICENSE',
+ 'README',
+ 'lib/*.rb',
+ 'lib/jit/*.rb',
+ 'ext/*.rb',
+ 'ext/*.c',
+ 'ext/*.h',
+ 'ext/*.rpp',
+ 'sample/*.rb',
+ ]
+
+ s.files = patterns.collect { |p| Dir.glob(p) }.flatten
+
+ s.test_files = Dir.glob('test/test_*.rb')
+
+ s.extensions = 'ext/extconf.rb'
+
+ s.has_rdoc = true
+end
+
Index: jitruby/run_tests.rb
===================================================================
RCS file: jitruby/run_tests.rb
diff -N jitruby/run_tests.rb
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/run_tests.rb 12 Dec 2008 11:30:58 -0000 1.1
@@ -0,0 +1,81 @@
+$mini_unit_exit_code = 0
+
+def disable_mini_unit_auto_run
+ MiniTest::Unit.class_eval do
+ alias :run_ :run
+ def run(*args)
+ return $mini_unit_exit_code
+ end
+ end
+end
+
+def run_tests_with_mini_unit
+ begin
+ test = MiniTest::Unit.new
+ args = ARGV.dup
+ args << '-v'
+ $mini_unit_exit_code = test.run(args)
+ exit($mini_unit_exit_code)
+ ensure
+ disable_mini_unit_auto_run
+ end
+end
+
+def run_tests_with_test_unit
+ tests = []
+ ObjectSpace.each_object(Class) do |o|
+ if o < Test::Unit::TestCase then
+ tests << o
+ end
+ end
+
+ suite = Test::Unit::TestSuite.new("RubyInternal")
+ tests.each do |test|
+ test.suite.tests.each do |testcase|
+ suite << testcase
+ end
+ end
+
+ require 'test/unit/ui/console/testrunner'
+ verbose = nil
+ begin
+ verbose = Test::Unit::UI.const_get(:VERBOSE)
+ rescue NameError
+ verbose = Test::Unit::UI::Console::TestRunner.const_get(:VERBOSE)
+ end
+
+ result = Test::Unit::UI::Console::TestRunner.run(
+ suite,
+ verbose)
+ exit(result.error_count + result.failure_count)
+end
+
+def run_tests
+ begin
+ require 'test/unit'
+ rescue LoadError
+ puts "WARNING: Test::Unit not installed; skipping tests"
+ return
+ end
+
+ $:.unshift(File.join(Dir.pwd, 'ext'))
+ $:.unshift(File.join(Dir.pwd, 'lib'))
+ Dir.chdir('test')
+ tests = Dir['test_*.rb']
+ tests.each do |test|
+ load test
+ end
+
+ if defined?(MiniTest) then
+ run_tests_with_mini_unit
+ else
+ run_tests_with_test_unit
+ end
+end
+
+if __FILE__ == $0 then
+ require 'timeout'
+ result = nil
+ timeout(600) { run_tests() }
+end
+
Index: jitruby/setup.rb
===================================================================
RCS file: jitruby/setup.rb
diff -N jitruby/setup.rb
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/setup.rb 12 Dec 2008 11:30:58 -0000 1.1
@@ -0,0 +1,1599 @@
+#
+# setup.rb
+#
+# Copyright (c) 2000-2006 Minero Aoki
+#
+# This program is free software.
+# You can distribute/modify this program under the terms of
+# the GNU LGPL, Lesser General Public License version 2.1.
+#
+
+unless Enumerable.method_defined?(:map) # Ruby 1.4.6
+ module Enumerable
+ alias map collect
+ end
+end
+
+unless File.respond_to?(:read) # Ruby 1.6
+ def File.read(fname)
+ open(fname) {|f|
+ return f.read
+ }
+ end
+end
+
+unless Errno.const_defined?(:ENOTEMPTY) # Windows?
+ module Errno
+ class ENOTEMPTY
+ # We do not raise this exception, implementation is not needed.
+ end
+ end
+end
+
+def File.binread(fname)
+ open(fname, 'rb') {|f|
+ return f.read
+ }
+end
+
+# for corrupted Windows' stat(2)
+def File.dir?(path)
+ File.directory?((path[-1,1] == '/') ? path : path + '/')
+end
+
+
+class ConfigTable
+
+ include Enumerable
+
+ def initialize(rbconfig)
+ @rbconfig = rbconfig
+ @items = []
+ @table = {}
+ # options
+ @install_prefix = nil
+ @config_opt = nil
+ @verbose = true
+ @no_harm = false
+ end
+
+ attr_accessor :install_prefix
+ attr_accessor :config_opt
+
+ attr_writer :verbose
+
+ def verbose?
+ @verbose
+ end
+
+ attr_writer :no_harm
+
+ def no_harm?
+ @no_harm
+ end
+
+ def [](key)
+ lookup(key).resolve(self)
+ end
+
+ def []=(key, val)
+ lookup(key).set val
+ end
+
+ def names
+ @items.map {|i| i.name }
+ end
+
+ def each(&block)
+ @items.each(&block)
+ end
+
+ def key?(name)
+ @table.key?(name)
+ end
+
+ def lookup(name)
+ @table[name] or setup_rb_error "no such config item: #{name}"
+ end
+
+ def add(item)
+ @items.push item
+ @table[item.name] = item
+ end
+
+ def remove(name)
+ item = lookup(name)
+ @items.delete_if {|i| i.name == name }
+ @table.delete_if {|name, i| i.name == name }
+ item
+ end
+
+ def load_script(path, inst = nil)
+ if File.file?(path)
+ MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path
+ end
+ end
+
+ def savefile
+ '.config'
+ end
+
+ def load_savefile
+ begin
+ File.foreach(savefile()) do |line|
+ k, v = *line.split(/=/, 2)
+ self[k] = v.strip
+ end
+ rescue Errno::ENOENT
+ setup_rb_error $!.message + "\n#{File.basename($0)} config first"
+ end
+ end
+
+ def save
+ @items.each {|i| i.value }
+ File.open(savefile(), 'w') {|f|
+ @items.each do |i|
+ f.printf "%s=%s\n", i.name, i.value if i.value? and i.value
+ end
+ }
+ end
+
+ def load_standard_entries
+ standard_entries(@rbconfig).each do |ent|
+ add ent
+ end
+ end
+
+ def standard_entries(rbconfig)
+ c = rbconfig
+
+ rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT'])
+
+ major = c['MAJOR'].to_i
+ minor = c['MINOR'].to_i
+ teeny = c['TEENY'].to_i
+ version = "#{major}.#{minor}"
+
+ # ruby ver. >= 1.4.4?
+ newpath_p = ((major >= 2) or
+ ((major == 1) and
+ ((minor >= 5) or
+ ((minor == 4) and (teeny >= 4)))))
+
+ if c['rubylibdir']
+ # V > 1.6.3
+ libruby = "#{c['prefix']}/lib/ruby"
+ librubyver = c['rubylibdir']
+ librubyverarch = c['archdir']
+ siteruby = c['sitedir']
+ siterubyver = c['sitelibdir']
+ siterubyverarch = c['sitearchdir']
+ elsif newpath_p
+ # 1.4.4 <= V <= 1.6.3
+ libruby = "#{c['prefix']}/lib/ruby"
+ librubyver = "#{c['prefix']}/lib/ruby/#{version}"
+ librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
+ siteruby = c['sitedir']
+ siterubyver = "$siteruby/#{version}"
+ siterubyverarch = "$siterubyver/#{c['arch']}"
+ else
+ # V < 1.4.4
+ libruby = "#{c['prefix']}/lib/ruby"
+ librubyver = "#{c['prefix']}/lib/ruby/#{version}"
+ librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
+ siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby"
+ siterubyver = siteruby
+ siterubyverarch = "$siterubyver/#{c['arch']}"
+ end
+ parameterize = lambda {|path|
+ path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')
+ }
+
+ if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~
arg }
+ makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
+ else
+ makeprog = 'make'
+ end
+
+ [
+ ExecItem.new('installdirs', 'std/site/home',
+ 'std: install under libruby; site: install under site_ruby;
home: install under $HOME')\
+ {|val, table|
+ case val
+ when 'std'
+ table['rbdir'] = '$librubyver'
+ table['sodir'] = '$librubyverarch'
+ when 'site'
+ table['rbdir'] = '$siterubyver'
+ table['sodir'] = '$siterubyverarch'
+ when 'home'
+ setup_rb_error '$HOME was not set' unless ENV['HOME']
+ table['prefix'] = ENV['HOME']
+ table['rbdir'] = '$libdir/ruby'
+ table['sodir'] = '$libdir/ruby'
+ end
+ },
+ PathItem.new('prefix', 'path', c['prefix'],
+ 'path prefix of target environment'),
+ PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
+ 'the directory for commands'),
+ PathItem.new('libdir', 'path', parameterize.call(c['libdir']),
+ 'the directory for libraries'),
+ PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
+ 'the directory for shared data'),
+ PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
+ 'the directory for man pages'),
+ PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
+ 'the directory for system configuration files'),
+ PathItem.new('localstatedir', 'path',
parameterize.call(c['localstatedir']),
+ 'the directory for local state data'),
+ PathItem.new('libruby', 'path', libruby,
+ 'the directory for ruby libraries'),
+ PathItem.new('librubyver', 'path', librubyver,
+ 'the directory for standard ruby libraries'),
+ PathItem.new('librubyverarch', 'path', librubyverarch,
+ 'the directory for standard ruby extensions'),
+ PathItem.new('siteruby', 'path', siteruby,
+ 'the directory for version-independent aux ruby libraries'),
+ PathItem.new('siterubyver', 'path', siterubyver,
+ 'the directory for aux ruby libraries'),
+ PathItem.new('siterubyverarch', 'path', siterubyverarch,
+ 'the directory for aux ruby binaries'),
+ PathItem.new('rbdir', 'path', '$siterubyver',
+ 'the directory for ruby scripts'),
+ PathItem.new('sodir', 'path', '$siterubyverarch',
+ 'the directory for ruby extentions'),
+ PathItem.new('rubypath', 'path', rubypath,
+ 'the path to set to #! line'),
+ ProgramItem.new('rubyprog', 'name', rubypath,
+ 'the ruby program using for installation'),
+ ProgramItem.new('makeprog', 'name', makeprog,
+ 'the make program to compile ruby extentions'),
+ SelectItem.new('shebang', 'all/ruby/never', 'ruby',
+ 'shebang line (#!) editing mode'),
+ BoolItem.new('without-ext', 'yes/no', 'no',
+ 'does not compile/install ruby extentions')
+ ]
+ end
+ private :standard_entries
+
+ def load_multipackage_entries
+ multipackage_entries().each do |ent|
+ add ent
+ end
+ end
+
+ def multipackage_entries
+ [
+ PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
+ 'package names that you want to install'),
+ PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
+ 'package names that you do not want to install')
+ ]
+ end
+ private :multipackage_entries
+
+ ALIASES = {
+ 'std-ruby' => 'librubyver',
+ 'stdruby' => 'librubyver',
+ 'rubylibdir' => 'librubyver',
+ 'archdir' => 'librubyverarch',
+ 'site-ruby-common' => 'siteruby', # For backward compatibility
+ 'site-ruby' => 'siterubyver', # For backward compatibility
+ 'bin-dir' => 'bindir',
+ 'bin-dir' => 'bindir',
+ 'rb-dir' => 'rbdir',
+ 'so-dir' => 'sodir',
+ 'data-dir' => 'datadir',
+ 'ruby-path' => 'rubypath',
+ 'ruby-prog' => 'rubyprog',
+ 'ruby' => 'rubyprog',
+ 'make-prog' => 'makeprog',
+ 'make' => 'makeprog'
+ }
+
+ def fixup
+ ALIASES.each do |ali, name|
+ @table[ali] = @table[name]
+ end
+ end
+
+ def options_re
+ /\A--(address@hidden('|')})(?:=(.*))?\z/
+ end
+
+ def parse_opt(opt)
+ m = options_re().match(opt) or setup_rb_error "config: unknown option
#{opt}"
+ m.to_a[1,2]
+ end
+
+ def dllext
+ @rbconfig['DLEXT']
+ end
+
+ def value_config?(name)
+ lookup(name).value?
+ end
+
+ class Item
+ def initialize(name, template, default, desc)
+ @name = name.freeze
+ @template = template
+ @value = default
+ @default = default
+ @description = desc
+ end
+
+ attr_reader :name
+ attr_reader :description
+
+ attr_accessor :default
+ alias help_default default
+
+ def help_opt
+ "address@hidden@template}"
+ end
+
+ def value?
+ true
+ end
+
+ def value
+ @value
+ end
+
+ def resolve(table)
+ @value.gsub(%r<\$([^/]+)>) { table[$1] }
+ end
+
+ def set(val)
+ @value = check(val)
+ end
+
+ private
+
+ def check(val)
+ setup_rb_error "config: --#{name} requires argument" unless val
+ val
+ end
+ end
+
+ class BoolItem < Item
+ def config_type
+ 'bool'
+ end
+
+ def help_opt
+ "address@hidden"
+ end
+
+ private
+
+ def check(val)
+ return 'yes' unless val
+ case val
+ when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes'
+ when /\An(o)?\z/i, /\Af(alse)\z/i then 'no'
+ else
+ setup_rb_error "config: address@hidden accepts only yes/no for
argument"
+ end
+ end
+ end
+
+ class PathItem < Item
+ def config_type
+ 'path'
+ end
+
+ private
+
+ def check(path)
+ setup_rb_error "config: address@hidden requires argument" unless path
+ path[0,1] == '$' ? path : File.expand_path(path)
+ end
+ end
+
+ class ProgramItem < Item
+ def config_type
+ 'program'
+ end
+ end
+
+ class SelectItem < Item
+ def initialize(name, selection, default, desc)
+ super
+ @ok = selection.split('/')
+ end
+
+ def config_type
+ 'select'
+ end
+
+ private
+
+ def check(val)
+ unless @ok.include?(val.strip)
+ setup_rb_error "config: use address@hidden@template} (#{val})"
+ end
+ val.strip
+ end
+ end
+
+ class ExecItem < Item
+ def initialize(name, selection, desc, &block)
+ super name, selection, nil, desc
+ @ok = selection.split('/')
+ @action = block
+ end
+
+ def config_type
+ 'exec'
+ end
+
+ def value?
+ false
+ end
+
+ def resolve(table)
+ setup_rb_error "$#{name()} wrongly used as option value"
+ end
+
+ undef set
+
+ def evaluate(val, table)
+ v = val.strip.downcase
+ unless @ok.include?(v)
+ setup_rb_error "invalid option address@hidden (use address@hidden)"
+ end
+ @action.call v, table
+ end
+ end
+
+ class PackageSelectionItem < Item
+ def initialize(name, template, default, help_default, desc)
+ super name, template, default, desc
+ @help_default = help_default
+ end
+
+ attr_reader :help_default
+
+ def config_type
+ 'package'
+ end
+
+ private
+
+ def check(val)
+ unless File.dir?("packages/#{val}")
+ setup_rb_error "config: no such package: #{val}"
+ end
+ val
+ end
+ end
+
+ class MetaConfigEnvironment
+ def initialize(config, installer)
+ @config = config
+ @installer = installer
+ end
+
+ def config_names
+ @config.names
+ end
+
+ def config?(name)
+ @config.key?(name)
+ end
+
+ def bool_config?(name)
+ @config.lookup(name).config_type == 'bool'
+ end
+
+ def path_config?(name)
+ @config.lookup(name).config_type == 'path'
+ end
+
+ def value_config?(name)
+ @config.lookup(name).config_type != 'exec'
+ end
+
+ def add_config(item)
+ @config.add item
+ end
+
+ def add_bool_config(name, default, desc)
+ @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
+ end
+
+ def add_path_config(name, default, desc)
+ @config.add PathItem.new(name, 'path', default, desc)
+ end
+
+ def set_config_default(name, default)
+ @config.lookup(name).default = default
+ end
+
+ def remove_config(name)
+ @config.remove(name)
+ end
+
+ # For only multipackage
+ def packages
+ raise '[setup.rb fatal] multi-package metaconfig API packages() called
for single-package; contact application package vendor' unless @installer
+ @installer.packages
+ end
+
+ # For only multipackage
+ def declare_packages(list)
+ raise '[setup.rb fatal] multi-package metaconfig API declare_packages()
called for single-package; contact application package vendor' unless @installer
+ @installer.packages = list
+ end
+ end
+
+end # class ConfigTable
+
+
+# This module requires: #verbose?, #no_harm?
+module FileOperations
+
+ def mkdir_p(dirname, prefix = nil)
+ dirname = prefix + File.expand_path(dirname) if prefix
+ $stderr.puts "mkdir -p #{dirname}" if verbose?
+ return if no_harm?
+
+ # Does not check '/', it's too abnormal.
+ dirs = File.expand_path(dirname).split(%r<(?=/)>)
+ if /\A[a-z]:\z/i =~ dirs[0]
+ disk = dirs.shift
+ dirs[0] = disk + dirs[0]
+ end
+ dirs.each_index do |idx|
+ path = dirs[0..idx].join('')
+ Dir.mkdir path unless File.dir?(path)
+ end
+ end
+
+ def rm_f(path)
+ $stderr.puts "rm -f #{path}" if verbose?
+ return if no_harm?
+ force_remove_file path
+ end
+
+ def rm_rf(path)
+ $stderr.puts "rm -rf #{path}" if verbose?
+ return if no_harm?
+ remove_tree path
+ end
+
+ def remove_tree(path)
+ if File.symlink?(path)
+ remove_file path
+ elsif File.dir?(path)
+ remove_tree0 path
+ else
+ force_remove_file path
+ end
+ end
+
+ def remove_tree0(path)
+ Dir.foreach(path) do |ent|
+ next if ent == '.'
+ next if ent == '..'
+ entpath = "#{path}/#{ent}"
+ if File.symlink?(entpath)
+ remove_file entpath
+ elsif File.dir?(entpath)
+ remove_tree0 entpath
+ else
+ force_remove_file entpath
+ end
+ end
+ begin
+ Dir.rmdir path
+ rescue Errno::ENOTEMPTY
+ # directory may not be empty
+ end
+ end
+
+ def move_file(src, dest)
+ force_remove_file dest
+ begin
+ File.rename src, dest
+ rescue
+ File.open(dest, 'wb') {|f|
+ f.write File.binread(src)
+ }
+ File.chmod File.stat(src).mode, dest
+ File.unlink src
+ end
+ end
+
+ def force_remove_file(path)
+ begin
+ remove_file path
+ rescue
+ end
+ end
+
+ def remove_file(path)
+ File.chmod 0777, path
+ File.unlink path
+ end
+
+ def install(from, dest, mode, prefix = nil)
+ $stderr.puts "install #{from} #{dest}" if verbose?
+ return if no_harm?
+
+ realdest = prefix ? prefix + File.expand_path(dest) : dest
+ realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
+ str = File.binread(from)
+ if diff?(str, realdest)
+ verbose_off {
+ rm_f realdest if File.exist?(realdest)
+ }
+ File.open(realdest, 'wb') {|f|
+ f.write str
+ }
+ File.chmod mode, realdest
+
+ File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
+ if prefix
+ f.puts realdest.sub(prefix, '')
+ else
+ f.puts realdest
+ end
+ }
+ end
+ end
+
+ def diff?(new_content, path)
+ return true unless File.exist?(path)
+ new_content != File.binread(path)
+ end
+
+ def command(*args)
+ $stderr.puts args.join(' ') if verbose?
+ system(*args) or raise RuntimeError,
+ "system(#{args.map{|a| a.inspect }.join(' ')}) failed"
+ end
+
+ def ruby(*args)
+ command config('rubyprog'), *args
+ end
+
+ def make(task = nil)
+ command(*[config('makeprog'), task].compact)
+ end
+
+ def extdir?(dir)
+ File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb")
+ end
+
+ def files_of(dir)
+ Dir.open(dir) {|d|
+ return d.select {|ent| File.file?("#{dir}/#{ent}") }
+ }
+ end
+
+ DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn )
+
+ def directories_of(dir)
+ Dir.open(dir) {|d|
+ return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT
+ }
+ end
+
+end
+
+
+# This module requires: #srcdir_root, #objdir_root, #relpath
+module HookScriptAPI
+
+ def get_config(key)
+ @config[key]
+ end
+
+ alias config get_config
+
+ # obsolete: use metaconfig to change configuration
+ def set_config(key, val)
+ @config[key] = val
+ end
+
+ #
+ # srcdir/objdir (works only in the package directory)
+ #
+
+ def curr_srcdir
+ "#{srcdir_root()}/#{relpath()}"
+ end
+
+ def curr_objdir
+ "#{objdir_root()}/#{relpath()}"
+ end
+
+ def srcfile(path)
+ "#{curr_srcdir()}/#{path}"
+ end
+
+ def srcexist?(path)
+ File.exist?(srcfile(path))
+ end
+
+ def srcdirectory?(path)
+ File.dir?(srcfile(path))
+ end
+
+ def srcfile?(path)
+ File.file?(srcfile(path))
+ end
+
+ def srcentries(path = '.')
+ Dir.open("#{curr_srcdir()}/#{path}") {|d|
+ return d.to_a - %w(. ..)
+ }
+ end
+
+ def srcfiles(path = '.')
+ srcentries(path).select {|fname|
+ File.file?(File.join(curr_srcdir(), path, fname))
+ }
+ end
+
+ def srcdirectories(path = '.')
+ srcentries(path).select {|fname|
+ File.dir?(File.join(curr_srcdir(), path, fname))
+ }
+ end
+
+end
+
+
+class ToplevelInstaller
+
+ Version = '3.4.1'
+ Copyright = 'Copyright (c) 2000-2006 Minero Aoki'
+
+ TASKS = [
+ [ 'all', 'do config, setup, then install' ],
+ [ 'config', 'saves your configurations' ],
+ [ 'show', 'shows current configuration' ],
+ [ 'setup', 'compiles ruby extentions and others' ],
+ [ 'install', 'installs files' ],
+ [ 'test', 'run all tests in test/' ],
+ [ 'clean', "does `make clean' for each extention" ],
+ [ 'distclean',"does `make distclean' for each extention" ]
+ ]
+
+ def ToplevelInstaller.invoke
+ config = ConfigTable.new(load_rbconfig())
+ config.load_standard_entries
+ config.load_multipackage_entries if multipackage?
+ config.fixup
+ klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller)
+ klass.new(File.dirname($0), config).invoke
+ end
+
+ def ToplevelInstaller.multipackage?
+ File.dir?(File.dirname($0) + '/packages')
+ end
+
+ def ToplevelInstaller.load_rbconfig
+ if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
+ ARGV.delete(arg)
+ load File.expand_path(arg.split(/=/, 2)[1])
+ $".push 'rbconfig.rb'
+ else
+ require 'rbconfig'
+ end
+ ::Config::CONFIG
+ end
+
+ def initialize(ardir_root, config)
+ @ardir = File.expand_path(ardir_root)
+ @config = config
+ # cache
+ @valid_task_re = nil
+ end
+
+ def config(key)
+ @config[key]
+ end
+
+ def inspect
+ "#<#{self.class} #{__id__()}>"
+ end
+
+ def invoke
+ run_metaconfigs
+ case task = parsearg_global()
+ when nil, 'all'
+ parsearg_config
+ init_installers
+ exec_config
+ exec_setup
+ exec_install
+ else
+ case task
+ when 'config', 'test'
+ ;
+ when 'clean', 'distclean'
+ @config.load_savefile if File.exist?(@config.savefile)
+ else
+ @config.load_savefile
+ end
+ __send__ "parsearg_#{task}"
+ init_installers
+ __send__ "exec_#{task}"
+ end
+ end
+
+ def run_metaconfigs
+ @config.load_script "address@hidden/metaconfig"
+ end
+
+ def init_installers
+ @installer = Installer.new(@config, @ardir, File.expand_path('.'))
+ end
+
+ #
+ # Hook Script API bases
+ #
+
+ def srcdir_root
+ @ardir
+ end
+
+ def objdir_root
+ '.'
+ end
+
+ def relpath
+ '.'
+ end
+
+ #
+ # Option Parsing
+ #
+
+ def parsearg_global
+ while arg = ARGV.shift
+ case arg
+ when /\A\w+\z/
+ setup_rb_error "invalid task: #{arg}" unless valid_task?(arg)
+ return arg
+ when '-q', '--quiet'
+ @config.verbose = false
+ when '--verbose'
+ @config.verbose = true
+ when '--help'
+ print_usage $stdout
+ exit 0
+ when '--version'
+ puts "#{File.basename($0)} version #{Version}"
+ exit 0
+ when '--copyright'
+ puts Copyright
+ exit 0
+ else
+ setup_rb_error "unknown global option '#{arg}'"
+ end
+ end
+ nil
+ end
+
+ def valid_task?(t)
+ valid_task_re() =~ t
+ end
+
+ def valid_task_re
+ @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/
+ end
+
+ def parsearg_no_options
+ unless ARGV.empty?
+ task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1)
+ setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}"
+ end
+ end
+
+ alias parsearg_show parsearg_no_options
+ alias parsearg_setup parsearg_no_options
+ alias parsearg_test parsearg_no_options
+ alias parsearg_clean parsearg_no_options
+ alias parsearg_distclean parsearg_no_options
+
+ def parsearg_config
+ evalopt = []
+ set = []
+ @config.config_opt = []
+ while i = ARGV.shift
+ if /\A--?\z/ =~ i
+ @config.config_opt = ARGV.dup
+ break
+ end
+ name, value = address@hidden(i)
+ if @config.value_config?(name)
+ @config[name] = value
+ else
+ evalopt.push [name, value]
+ end
+ set.push name
+ end
+ evalopt.each do |name, value|
+ @config.lookup(name).evaluate value, @config
+ end
+ # Check if configuration is valid
+ set.each do |n|
+ @config[n] if @config.value_config?(n)
+ end
+ end
+
+ def parsearg_install
+ @config.no_harm = false
+ @config.install_prefix = ''
+ while a = ARGV.shift
+ case a
+ when '--no-harm'
+ @config.no_harm = true
+ when /\A--prefix=/
+ path = a.split(/=/, 2)[1]
+ path = File.expand_path(path) unless path[0,1] == '/'
+ @config.install_prefix = path
+ else
+ setup_rb_error "install: unknown option #{a}"
+ end
+ end
+ end
+
+ def print_usage(out)
+ out.puts 'Typical Installation Procedure:'
+ out.puts " $ ruby #{File.basename $0} config"
+ out.puts " $ ruby #{File.basename $0} setup"
+ out.puts " # ruby #{File.basename $0} install (may require root
privilege)"
+ out.puts
+ out.puts 'Detailed Usage:'
+ out.puts " ruby #{File.basename $0} <global option>"
+ out.puts " ruby #{File.basename $0} [<global options>] <task> [<task
options>]"
+
+ fmt = " %-24s %s\n"
+ out.puts
+ out.puts 'Global options:'
+ out.printf fmt, '-q,--quiet', 'suppress message outputs'
+ out.printf fmt, ' --verbose', 'output messages verbosely'
+ out.printf fmt, ' --help', 'print this message'
+ out.printf fmt, ' --version', 'print version and quit'
+ out.printf fmt, ' --copyright', 'print copyright and quit'
+ out.puts
+ out.puts 'Tasks:'
+ TASKS.each do |name, desc|
+ out.printf fmt, name, desc
+ end
+
+ fmt = " %-24s %s [%s]\n"
+ out.puts
+ out.puts 'Options for CONFIG or ALL:'
+ @config.each do |item|
+ out.printf fmt, item.help_opt, item.description, item.help_default
+ end
+ out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
+ out.puts
+ out.puts 'Options for INSTALL:'
+ out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
+ out.printf fmt, '--prefix=path', 'install path prefix', ''
+ out.puts
+ end
+
+ #
+ # Task Handlers
+ #
+
+ def exec_config
+ @installer.exec_config
+ @config.save # must be final
+ end
+
+ def exec_setup
+ @installer.exec_setup
+ end
+
+ def exec_install
+ @installer.exec_install
+ end
+
+ def exec_test
+ @installer.exec_test
+ end
+
+ def exec_show
+ @config.each do |i|
+ printf "%-20s %s\n", i.name, i.value if i.value?
+ end
+ end
+
+ def exec_clean
+ @installer.exec_clean
+ end
+
+ def exec_distclean
+ @installer.exec_distclean
+ end
+
+end # class ToplevelInstaller
+
+
+class ToplevelInstallerMulti < ToplevelInstaller
+
+ include FileOperations
+
+ def initialize(ardir_root, config)
+ super
+ @packages = directories_of("address@hidden/packages")
+ raise 'no package exists' if @packages.empty?
+ @root_installer = Installer.new(@config, @ardir, File.expand_path('.'))
+ end
+
+ def run_metaconfigs
+ @config.load_script "address@hidden/metaconfig", self
+ @packages.each do |name|
+ @config.load_script "address@hidden/packages/#{name}/metaconfig"
+ end
+ end
+
+ attr_reader :packages
+
+ def packages=(list)
+ raise 'package list is empty' if list.empty?
+ list.each do |name|
+ raise "directory packages/#{name} does not exist"\
+ unless File.dir?("address@hidden/packages/#{name}")
+ end
+ @packages = list
+ end
+
+ def init_installers
+ @installers = {}
+ @packages.each do |pack|
+ @installers[pack] = Installer.new(@config,
+ "address@hidden/packages/#{pack}",
+ "packages/#{pack}")
+ end
+ with = extract_selection(config('with'))
+ without = extract_selection(config('without'))
+ @selected = @installers.keys.select {|name|
+ (with.empty? or with.include?(name)) \
+ and not without.include?(name)
+ }
+ end
+
+ def extract_selection(list)
+ a = list.split(/,/)
+ a.each do |name|
+ setup_rb_error "no such package: #{name}" unless @installers.key?(name)
+ end
+ a
+ end
+
+ def print_usage(f)
+ super
+ f.puts 'Inluded packages:'
+ f.puts ' ' + @packages.sort.join(' ')
+ f.puts
+ end
+
+ #
+ # Task Handlers
+ #
+
+ def exec_config
+ run_hook 'pre-config'
+ each_selected_installers {|inst| inst.exec_config }
+ run_hook 'post-config'
+ @config.save # must be final
+ end
+
+ def exec_setup
+ run_hook 'pre-setup'
+ each_selected_installers {|inst| inst.exec_setup }
+ run_hook 'post-setup'
+ end
+
+ def exec_install
+ run_hook 'pre-install'
+ each_selected_installers {|inst| inst.exec_install }
+ run_hook 'post-install'
+ end
+
+ def exec_test
+ run_hook 'pre-test'
+ each_selected_installers {|inst| inst.exec_test }
+ run_hook 'post-test'
+ end
+
+ def exec_clean
+ rm_f @config.savefile
+ run_hook 'pre-clean'
+ each_selected_installers {|inst| inst.exec_clean }
+ run_hook 'post-clean'
+ end
+
+ def exec_distclean
+ rm_f @config.savefile
+ run_hook 'pre-distclean'
+ each_selected_installers {|inst| inst.exec_distclean }
+ run_hook 'post-distclean'
+ end
+
+ #
+ # lib
+ #
+
+ def each_selected_installers
+ Dir.mkdir 'packages' unless File.dir?('packages')
+ @selected.each do |pack|
+ $stderr.puts "Processing the package `#{pack}' ..." if verbose?
+ Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
+ Dir.chdir "packages/#{pack}"
+ yield @installers[pack]
+ Dir.chdir '../..'
+ end
+ end
+
+ def run_hook(id)
+ @root_installer.run_hook id
+ end
+
+ # module FileOperations requires this
+ def verbose?
+ @config.verbose?
+ end
+
+ # module FileOperations requires this
+ def no_harm?
+ @config.no_harm?
+ end
+
+end # class ToplevelInstallerMulti
+
+
+class Installer
+
+ FILETYPES = %w( bin lib ext data conf man )
+
+ include FileOperations
+ include HookScriptAPI
+
+ def initialize(config, srcroot, objroot)
+ @config = config
+ @srcdir = File.expand_path(srcroot)
+ @objdir = File.expand_path(objroot)
+ @currdir = '.'
+ end
+
+ def inspect
+ "#<#{self.class} #{File.basename(@srcdir)}>"
+ end
+
+ def noop(rel)
+ end
+
+ #
+ # Hook Script API base methods
+ #
+
+ def srcdir_root
+ @srcdir
+ end
+
+ def objdir_root
+ @objdir
+ end
+
+ def relpath
+ @currdir
+ end
+
+ #
+ # Config Access
+ #
+
+ # module FileOperations requires this
+ def verbose?
+ @config.verbose?
+ end
+
+ # module FileOperations requires this
+ def no_harm?
+ @config.no_harm?
+ end
+
+ def verbose_off
+ begin
+ save, @config.verbose = @config.verbose?, false
+ yield
+ ensure
+ @config.verbose = save
+ end
+ end
+
+ #
+ # TASK config
+ #
+
+ def exec_config
+ exec_task_traverse 'config'
+ end
+
+ alias config_dir_bin noop
+ alias config_dir_lib noop
+
+ def config_dir_ext(rel)
+ extconf if extdir?(curr_srcdir())
+ end
+
+ alias config_dir_data noop
+ alias config_dir_conf noop
+ alias config_dir_man noop
+
+ def extconf
+ ruby "#{curr_srcdir()}/extconf.rb", address@hidden
+ end
+
+ #
+ # TASK setup
+ #
+
+ def exec_setup
+ exec_task_traverse 'setup'
+ end
+
+ def setup_dir_bin(rel)
+ files_of(curr_srcdir()).each do |fname|
+ update_shebang_line "#{curr_srcdir()}/#{fname}"
+ end
+ end
+
+ alias setup_dir_lib noop
+
+ def setup_dir_ext(rel)
+ make if extdir?(curr_srcdir())
+ end
+
+ alias setup_dir_data noop
+ alias setup_dir_conf noop
+ alias setup_dir_man noop
+
+ def update_shebang_line(path)
+ return if no_harm?
+ return if config('shebang') == 'never'
+ old = Shebang.load(path)
+ if old
+ $stderr.puts "warning: #{path}: Shebang line includes too many args. It
is not portable and your program may not work." if old.args.size > 1
+ new = new_shebang(old)
+ return if new.to_s == old.to_s
+ else
+ return unless config('shebang') == 'all'
+ new = Shebang.new(config('rubypath'))
+ end
+ $stderr.puts "updating shebang: #{File.basename(path)}" if verbose?
+ open_atomic_writer(path) {|output|
+ File.open(path, 'rb') {|f|
+ f.gets if old # discard
+ output.puts new.to_s
+ output.print f.read
+ }
+ }
+ end
+
+ def new_shebang(old)
+ if /\Aruby/ =~ File.basename(old.cmd)
+ Shebang.new(config('rubypath'), old.args)
+ elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby'
+ Shebang.new(config('rubypath'), old.args[1..-1])
+ else
+ return old unless config('shebang') == 'all'
+ Shebang.new(config('rubypath'))
+ end
+ end
+
+ def open_atomic_writer(path, &block)
+ tmpfile = File.basename(path) + '.tmp'
+ begin
+ File.open(tmpfile, 'wb', &block)
+ File.rename tmpfile, File.basename(path)
+ ensure
+ File.unlink tmpfile if File.exist?(tmpfile)
+ end
+ end
+
+ class Shebang
+ def Shebang.load(path)
+ line = nil
+ File.open(path) {|f|
+ line = f.gets
+ }
+ return nil unless /\A#!/ =~ line
+ parse(line)
+ end
+
+ def Shebang.parse(line)
+ cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ')
+ new(cmd, args)
+ end
+
+ def initialize(cmd, args = [])
+ @cmd = cmd
+ @args = args
+ end
+
+ attr_reader :cmd
+ attr_reader :args
+
+ def to_s
+ "#! address@hidden" + (@args.empty? ? '' : " address@hidden(' ')}")
+ end
+ end
+
+ #
+ # TASK install
+ #
+
+ def exec_install
+ rm_f 'InstalledFiles'
+ exec_task_traverse 'install'
+ end
+
+ def install_dir_bin(rel)
+ install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755, strip_ext?
+ end
+
+ def strip_ext?
+ /mswin|mingw/ !~ RUBY_PLATFORM
+ end
+
+ def install_dir_lib(rel)
+ install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644
+ end
+
+ def install_dir_ext(rel)
+ return unless extdir?(curr_srcdir())
+ install_files rubyextentions('.'),
+ "#{config('sodir')}/#{File.dirname(rel)}",
+ 0555
+ end
+
+ def install_dir_data(rel)
+ install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644
+ end
+
+ def install_dir_conf(rel)
+ # FIXME: should not remove current config files
+ # (rename previous file to .old/.org)
+ install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644
+ end
+
+ def install_dir_man(rel)
+ install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644
+ end
+
+ def install_files(list, dest, mode, stripext = false)
+ mkdir_p dest, @config.install_prefix
+ list.each do |fname|
+ if stripext
+ install fname, "#{dest}/#{File.basename(fname, '.*')}",
+ mode, @config.install_prefix
+ else
+ install fname, dest, mode, @config.install_prefix
+ end
+ end
+ end
+
+ def libfiles
+ glob_reject(%w(*.y *.output), targetfiles())
+ end
+
+ def rubyextentions(dir)
+ ents = glob_select("address@hidden", targetfiles())
+ if ents.empty?
+ setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
+ end
+ ents
+ end
+
+ def targetfiles
+ mapdir(existfiles() - hookfiles())
+ end
+
+ def mapdir(ents)
+ ents.map {|ent|
+ if File.exist?(ent)
+ then ent # objdir
+ else "#{curr_srcdir()}/#{ent}" # srcdir
+ end
+ }
+ end
+
+ # picked up many entries from cvs-1.11.1/src/ignore.c
+ JUNK_FILES = %w(
+ core RCSLOG tags TAGS .make.state
+ .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
+ *~ *.old *.bak *.BAK *.orig *.rej _$* *$
+
+ *.org *.in .*
+ )
+
+ def existfiles
+ glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.')))
+ end
+
+ def hookfiles
+ %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
+ %w( config setup install clean distclean ).map {|t| sprintf(fmt, t) }
+ }.flatten
+ end
+
+ def glob_select(pat, ents)
+ re = globs2re([pat])
+ ents.select {|ent| re =~ ent }
+ end
+
+ def glob_reject(pats, ents)
+ re = globs2re(pats)
+ ents.reject {|ent| re =~ ent }
+ end
+
+ GLOB2REGEX = {
+ '.' => '\.',
+ '$' => '\$',
+ '#' => '\#',
+ '*' => '.*'
+ }
+
+ def globs2re(pats)
+ /\A(?:#{
+ pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|')
+ })\z/
+ end
+
+ #
+ # TASK test
+ #
+
+ TESTDIR = 'test'
+
+ def exec_test
+ unless File.directory?('test')
+ $stderr.puts 'no test in this package' if verbose?
+ return
+ end
+ $stderr.puts 'Running tests...' if verbose?
+ begin
+ require 'test/unit'
+ rescue LoadError
+ setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to
invoke this task.'
+ end
+ runner = Test::Unit::AutoRunner.new(true)
+ runner.to_run << TESTDIR
+ runner.run
+ end
+
+ #
+ # TASK clean
+ #
+
+ def exec_clean
+ exec_task_traverse 'clean'
+ rm_f @config.savefile
+ rm_f 'InstalledFiles'
+ end
+
+ alias clean_dir_bin noop
+ alias clean_dir_lib noop
+ alias clean_dir_data noop
+ alias clean_dir_conf noop
+ alias clean_dir_man noop
+
+ def clean_dir_ext(rel)
+ return unless extdir?(curr_srcdir())
+ make 'clean' if File.file?('Makefile')
+ end
+
+ #
+ # TASK distclean
+ #
+
+ def exec_distclean
+ exec_task_traverse 'distclean'
+ rm_f @config.savefile
+ rm_f 'InstalledFiles'
+ end
+
+ alias distclean_dir_bin noop
+ alias distclean_dir_lib noop
+
+ def distclean_dir_ext(rel)
+ return unless extdir?(curr_srcdir())
+ make 'distclean' if File.file?('Makefile')
+ end
+
+ alias distclean_dir_data noop
+ alias distclean_dir_conf noop
+ alias distclean_dir_man noop
+
+ #
+ # Traversing
+ #
+
+ def exec_task_traverse(task)
+ run_hook "pre-#{task}"
+ FILETYPES.each do |type|
+ if type == 'ext' and config('without-ext') == 'yes'
+ $stderr.puts 'skipping ext/* by user option' if verbose?
+ next
+ end
+ traverse task, type, "#{task}_dir_#{type}"
+ end
+ run_hook "post-#{task}"
+ end
+
+ def traverse(task, rel, mid)
+ dive_into(rel) {
+ run_hook "pre-#{task}"
+ __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
+ directories_of(curr_srcdir()).each do |d|
+ traverse task, "#{rel}/#{d}", mid
+ end
+ run_hook "post-#{task}"
+ }
+ end
+
+ def dive_into(rel)
+ return unless File.dir?("address@hidden/#{rel}")
+
+ dir = File.basename(rel)
+ Dir.mkdir dir unless File.dir?(dir)
+ prevdir = Dir.pwd
+ Dir.chdir dir
+ $stderr.puts '---> ' + rel if verbose?
+ @currdir = rel
+ yield
+ Dir.chdir prevdir
+ $stderr.puts '<--- ' + rel if verbose?
+ @currdir = File.dirname(rel)
+ end
+
+ def run_hook(id)
+ path = [ "#{curr_srcdir()}/#{id}",
+ "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) }
+ return unless path
+ $stderr.puts "invoking hook script #{path}" if verbose?
+ begin
+ instance_eval File.read(path), path, 1
+ rescue
+ raise if $DEBUG
+ setup_rb_error "hook #{path} failed:\n" + $!.message
+ end
+ end
+
+end # class Installer
+
+
+class SetupError < StandardError; end
+
+def setup_rb_error(msg)
+ raise SetupError, msg
+end
+
+# $DEBUG = true
+
+if $0 == __FILE__
+ ToplevelInstaller.invoke
+ # begin
+ # ToplevelInstaller.invoke
+ # rescue SetupError
+ # raise if $DEBUG
+ # $stderr.puts $!.message
+ # $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
+ # exit 1
+ # end
+end
Index: jitruby/ext/extconf.rb
===================================================================
RCS file: jitruby/ext/extconf.rb
diff -N jitruby/ext/extconf.rb
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/ext/extconf.rb 12 Dec 2008 11:30:58 -0000 1.1
@@ -0,0 +1,108 @@
+require 'mkmf'
+require 'rbconfig'
+
+if Config::CONFIG['host_os'] =~ /cygwin|win32|windows/ then
+ need_windows_h = [ 'windows.h' ]
+ $defs << ' -DNEED_WINDOWS_H'
+else
+ need_windows_h = [ ]
+end
+
+if not have_library('jit', 'jit_init', need_windows_h + ["jit/jit.h"]) then
+ $stderr.puts "libjit not found"
+ exit 1
+end
+
+if not have_macro("SIZEOF_VALUE", "ruby.h") then
+ check_sizeof("VALUE", "ruby.h")
+end
+
+if not have_macro("SIZEOF_ID", "ruby.h") then
+ check_sizeof("ID", "ruby.h")
+end
+
+if have_macro("_GNU_SOURCE", "ruby.h") then
+ $defs.push("-DRUBY_DEFINES_GNU_SOURCE")
+end
+
+have_func("rb_class_boot", "ruby.h")
+have_func("rb_errinfo", "ruby.h")
+have_func('fmemopen')
+have_func("rb_ensure", "ruby.h")
+
+if have_header('ruby/node.h') then
+ # ruby.h defines HAVE_RUBY_NODE_H, even though it is not there
+ $defs.push("-DREALLY_HAVE_RUBY_NODE_H")
+elsif have_header('node.h') then
+ # okay
+else
+ $defs.push("-DNEED_MINIMAL_NODE")
+end
+
+have_header('env.h')
+
+checking_for("whether VALUE is a pointer") do
+ if not try_link(<<"SRC")
+#include <ruby.h>
+int main()
+{
+ VALUE v;
+ v /= 5;
+}
+SRC
+ then
+ $defs.push("-DVALUE_IS_PTR");
+ end
+end
+
+if defined?(RUBY_ENGINE) and RUBY_ENGINE == 'rbx' then
+ $defs.push("-DHAVE_RUBINIUS")
+end
+
+rb_files = Dir['*.rb']
+rpp_files = Dir['*.rpp']
+generated_files = rpp_files.map { |f| f.sub(/\.rpp$/, '') }
+
+srcs = Dir['*.c']
+generated_files.each do |f|
+ if f =~ /\.c$/ then
+ srcs << f
+ end
+end
+srcs.uniq!
+$objs = srcs.map { |f| f.sub(/\.c$/, ".#{$OBJEXT}") }
+
+if Config::CONFIG['CC'] == 'gcc' then
+ # $CFLAGS << ' -Wall -g -pedantic -Wno-long-long'
+ $CFLAGS << ' -Wall -g'
+end
+
+create_makefile("jit")
+
+append_to_makefile = ''
+
+rpp_files.each do |rpp_file|
+dest_file = rpp_file.sub(/\.rpp$/, '')
+append_to_makefile << <<END
+#{dest_file}: #{rpp_file} #{rb_files.join(' ')}
+ $(RUBY) rubypp.rb #{rpp_file} #{dest_file}
+END
+end
+
+generated_headers = generated_files.select { |x| x =~ /\.h$/ || x =~ /\.inc$/ }
+append_to_makefile << <<END
+$(OBJS): #{generated_headers.join(' ')}
+clean: clean_generated_files
+clean_generated_files:
+ @$(RM) #{generated_files.join(' ')}
+
+install: $(includedir)/rubyjit.h
+
+$(includedir)/rubyjit.h: rubyjit.h
+ $(INSTALL_PROG) rubyjit.h $(includedir)
+END
+
+File.open('Makefile', 'a') do |makefile|
+ makefile.puts(append_to_makefile)
+end
+
Index: jitruby/ext/insns.inc.rpp
===================================================================
RCS file: jitruby/ext/insns.inc.rpp
diff -N jitruby/ext/insns.inc.rpp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/ext/insns.inc.rpp 12 Dec 2008 11:31:02 -0000 1.1
@@ -0,0 +1,203 @@
+#ruby <<END
+
+V = :value
+L = :label_ptr
+B = :label
+N = :nint
+T = :type_t
+_ = :void
+
+def incoming_args(arg_types)
+ num_args = arg_types.length
+ return (1..num_args).map { |n| ", VALUE arg#{n}" }.join
+end
+
+def declaration(name, type)
+ case type
+ when V then return "jit_value_t #{name};"
+ when L then return "jit_label_t * #{name};"
+ when B then return "jit_label_t * #{name};"
+ when N then return "jit_nint #{name};"
+ when T then return "jit_type_t #{name};"
+ when :void then nil
+ else raise "Invalid type #{type}"
+ end
+end
+
+def write_declaration(name, type, indent)
+ d = declaration(name, type)
+ if d then
+ puts "#{' '*indent}#{d}"
+ end
+end
+
+def ruby_type(type)
+ case type
+ when V then return "rb_cValue"
+ when L then return "rb_cLabel"
+ when B then return "rb_cLabel"
+ when N then return "rb_cFixnum"
+ when T then return "rb_cType"
+ else raise "Invalid type #{type}"
+ end
+end
+
+def jit_type(type)
+ case type
+ when V then return "struct _jit_value"
+ when L then return "jit_label_t"
+ when B then return "jit_label_t"
+ when T then return "struct _jit_type"
+ else raise "Invalid type #{type}"
+ end
+end
+
+def get_value(type, arg, j_arg)
+ case type
+ when N then
+ return "#{j_arg} = NUM2INT(#{arg})"
+ else
+ return "Data_Get_Struct(#{arg}, #{jit_type(type)}, #{j_arg})"
+ end
+end
+
+def jit_insn_args(arg_types)
+ num_args = arg_types.length
+ arg_list = (1..num_args).map do |n|
+ case arg_types[n-1]
+ when B then ", *j_arg#{n}"
+ else ", j_arg#{n}"
+ end
+ end
+ return arg_list.join
+end
+
+def write_insn(name, retval_type, arg_types)
+ num_args = arg_types.length
+ an = [?a, ?e, ?i, ?o, ?u].include?(name[0])
+ puts "/* Generate a#{an} #{name} instruction (see the libjit documentation
for more"
+ puts " * details. */"
+ puts "static VALUE function_insn_#{name}(VALUE
self#{incoming_args(arg_types)})"
+ puts "{"
+ puts " jit_function_t function;"
+ write_declaration('retval', retval_type, 2)
+ arg_types.each_with_index do |type, n|
+ write_declaration("j_arg#{n+1}", type, 2)
+ end
+ puts
+ arg_types.each_with_index do |type, n|
+ puts " check_type(\"arg#{n+1}\", #{ruby_type(type)}, arg#{n+1});"
+ end
+ puts
+ puts " Data_Get_Struct(self, struct _jit_function, function);"
+ arg_types.each_with_index do |type, n|
+ puts " " + get_value(type, "arg#{n+1}", "j_arg#{n+1}") + ";"
+ end
+ puts
+ if retval_type == V then
+ puts " retval = jit_insn_#{name}(function#{jit_insn_args(arg_types)});"
+ puts " return Data_Wrap_Struct(rb_cValue, 0, 0, retval);"
+ elsif retval_type == :void then
+ puts " jit_insn_#{name}(function#{jit_insn_args(arg_types)});"
+ puts " return Qnil;"
+ else
+ raise "Invalid retval type #{retval_type}"
+ end
+ puts "}"
+end
+
+insns = [
+ [ 'load' , V, V ] ,
+ [ 'store' , _, V, V ] ,
+ [ 'dup' , V, V ] ,
+ [ 'add' , V, V, V ] ,
+ [ 'add_ovf' , V, V, V ] ,
+ [ 'sub' , V, V, V ] ,
+ [ 'sub_ovf' , V, V, V ] ,
+ [ 'mul' , V, V, V ] ,
+ [ 'mul_ovf' , V, V, V ] ,
+ [ 'div' , V, V, V ] ,
+ [ 'rem' , V, V, V ] ,
+ [ 'rem_ieee' , V, V, V ] ,
+ [ 'neg' , V, V ] ,
+ [ 'and' , V, V, V ] ,
+ [ 'or' , V, V, V ] ,
+ [ 'xor' , V, V, V ] ,
+ [ 'not' , V, V ] ,
+ [ 'shl' , V, V, V ] ,
+ [ 'shr' , V, V, V ] ,
+ [ 'ushr' , V, V, V ] ,
+ [ 'sshr' , V, V, V ] ,
+ [ 'eq' , V, V, V ] ,
+ [ 'ne' , V, V, V ] ,
+ [ 'lt' , V, V, V ] ,
+ [ 'le' , V, V, V ] ,
+ [ 'gt' , V, V, V ] ,
+ [ 'ge' , V, V, V ] ,
+ [ 'cmpl' , V, V, V ] ,
+ [ 'cmpg' , V, V, V ] ,
+ [ 'to_bool' , V, V ] ,
+ [ 'to_not_bool' , V, V ] ,
+ [ 'acos' , V, V ] ,
+ [ 'asin' , V, V ] ,
+ [ 'atan' , V, V ] ,
+ [ 'atan2' , V, V, V ] ,
+ [ 'ceil' , V, V ] ,
+ [ 'cos' , V, V ] ,
+ [ 'cosh' , V, V ] ,
+ [ 'exp' , V, V ] ,
+ [ 'floor' , V, V ] ,
+ [ 'log' , V, V ] ,
+ [ 'log10' , V, V ] ,
+ [ 'pow' , V, V, V ] ,
+ [ 'rint' , V, V ] ,
+ [ 'round' , V, V ] ,
+ [ 'sin' , V, V ] ,
+ [ 'sinh' , V, V ] ,
+ [ 'sqrt' , V, V ] ,
+ [ 'tan' , V, V ] ,
+ [ 'tanh' , V, V ] ,
+ [ 'is_nan' , V, V ] ,
+ [ 'is_finite' , V, V ] ,
+ [ 'is_inf' , V, V ] ,
+ [ 'abs' , V, V ] ,
+ [ 'min' , V, V, V ] ,
+ [ 'max' , V, V, V ] ,
+ [ 'sign' , V, V ] ,
+ [ 'branch' , _, L ] ,
+ [ 'branch_if' , _, V, L ] ,
+ [ 'branch_if_not' , _, V, L ] ,
+ [ 'label' , _, L ] ,
+ [ 'address_of' , V, V ] ,
+ [ 'address_of_label' , V, L ] ,
+ [ 'store_relative' , _, V, N, V ] ,
+ [ 'load_relative' , V, V, N, T ] ,
+ [ 'add_relative' , V ,V, N ] ,
+ [ 'memcpy' , _, V, V, V ] ,
+ [ 'memmove' , _, V, V, V ] ,
+ [ 'memset' , _, V, V, V ] ,
+ [ 'alloca' , V, V ] ,
+ [ 'load_elem' , V, V, V, T ] ,
+ [ 'store_elem' , _, V, V, V ] ,
+ [ 'move_blocks_to_end' , _, B, B ] ,
+ [ 'move_blocks_to_start' , _, B, B ] ,
+ [ 'push' , _, V ] ,
+ [ 'push_ptr' , _, V, T ] ,
+ [ 'pop_stack' , _, N ] ,
+]
+
+insns.each do |name, retval_type, *arg_types|
+ write_insn(name, retval_type, arg_types)
+ puts
+end
+
+puts "static void init_insns()"
+puts "{"
+insns.each do |name, retval_type, *arg_types|
+ num_args = arg_types.length
+ puts " rb_define_method(rb_cFunction, \"insn_#{name}\",
function_insn_#{name}, #{num_args});"
+end
+puts "}"
+
+END
+
Index: jitruby/ext/jit.c
===================================================================
RCS file: jitruby/ext/jit.c
diff -N jitruby/ext/jit.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/ext/jit.c 12 Dec 2008 11:31:03 -0000 1.1
@@ -0,0 +1,1552 @@
+#ifndef RUBY_DEFINES_GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#ifdef NEED_WINDOWS_H
+#include <windows.h>
+#endif
+
+#include <ruby.h>
+
+#include <stdio.h>
+
+#include <jit/jit.h>
+#include <jit/jit-dump.h>
+
+#include "rubyjit.h"
+#include "method_data.h"
+
+#ifdef NEED_MINIMAL_NODE
+#include "minimal_node.h"
+#endif
+
+#ifndef RARRAY_LEN
+#define RARRAY_LEN(a) RARRAY(a)->len
+#endif
+
+#ifndef RARRAY_PTR
+#define RARRAY_PTR(a) RARRAY(a)->ptr
+#endif
+
+static VALUE rb_mJIT;
+static VALUE rb_cContext;
+static VALUE rb_cFunction;
+static VALUE rb_cType;
+static VALUE rb_mABI;
+static VALUE rb_cValue;
+static VALUE rb_cLabel;
+static VALUE rb_mCall;
+static VALUE rb_cClosure;
+
+jit_type_t jit_type_VALUE;
+jit_type_t jit_type_ID;
+jit_type_t jit_type_Function_Ptr;
+
+static jit_type_t ruby_vararg_signature;
+
+typedef void (*Void_Function_Ptr)();
+
+struct Closure
+{
+ VALUE function;
+ Void_Function_Ptr function_ptr;
+};
+
+#ifdef VALUE_IS_PTR
+/* Rubinius */
+typedef jit_ptr jit_VALUE;
+#define jit_underlying_type_VALUE jit_type_void_ptr
+#define SET_CONSTANT_VALUE(c, v) (c.un.ptr_value = v)
+
+#elif SIZEOF_VALUE == 4
+/* 32-bit */
+typedef jit_uint jit_VALUE;
+#define jit_underlying_type_VALUE jit_type_uint
+#define SET_CONSTANT_VALUE(c, v) (c.un.uint_value = v)
+
+#elif SIZEOF_VALUE == 8
+/* 64-bit */
+typedef jit_ulong jit_VALUE;
+#define jit_underlying_type_VALUE jit_type_ulong
+#define SET_CONSTANT_VALUE(c, v) (c.un.ulong_value = v)
+
+#else
+#error "Unsupported size for VALUE"
+#endif
+
+#if SIZEOF_ID == 4
+/* 32-bit */
+typedef jit_uint jit_ID;
+#define jit_underlying_type_ID jit_type_uint
+#define SET_CONSTANT_ID(c, v) (c.un.uint_value = v)
+#elif SIZEOF_ID == 8
+/* 64-bit */
+typedef jit_ulong jit_ID;
+#define jit_underlying_type_ID jit_type_ulong
+#define SET_CONSTANT_ID(c, v) (c.un.ulong_value = v)
+#else
+#error "Unsupported size for ID"
+#endif
+
+typedef jit_ptr jit_Function_Ptr;
+#define jit_underlying_type_void_ptr jit_type_void_ptr
+#define SET_FUNCTION_POINTER_VALUE(c, v) (c.un.ptr_value = v)
+
+#ifdef HAVE_RB_ERRINFO
+#define ruby_errinfo rb_errinfo()
+#endif
+
+/* ---------------------------------------------------------------------------
+ * Utility functions
+ * ---------------------------------------------------------------------------
+ */
+
+static VALUE lookup_const(VALUE module, VALUE symbol)
+{
+ if(SYMBOL_P(symbol))
+ {
+ return rb_const_get(module, SYM2ID(symbol));
+ }
+ else
+ {
+ return symbol;
+ }
+}
+
+static void check_type(char const * param_name, VALUE expected_klass, VALUE
val)
+{
+ if(!rb_obj_is_kind_of(val, expected_klass))
+ {
+ rb_raise(
+ rb_eTypeError,
+ "Wrong type for %s; expected %s but got %s",
+ param_name,
+ rb_class2name(expected_klass),
+ rb_class2name(CLASS_OF(val)));
+ }
+}
+
+void raise_memory_error_if_zero(void * v)
+{
+ if(!v)
+ {
+ rb_raise(rb_eNoMemError, "Out of memory");
+ }
+}
+
+/* ---------------------------------------------------------------------------
+ * Context
+ * ---------------------------------------------------------------------------
+ */
+
+static void context_mark(jit_context_t context)
+{
+ VALUE functions = (VALUE)jit_context_get_meta(context, RJT_FUNCTIONS);
+ rb_gc_mark(functions);
+}
+
+/*
+ * call-seq:
+ * context = Context.new
+ *
+ * Create a new context.
+ */
+static VALUE context_s_new(VALUE klass)
+{
+ jit_context_t context = jit_context_create();
+ jit_context_set_meta(context, RJT_FUNCTIONS, (void*)rb_ary_new(), 0);
+ return Data_Wrap_Struct(rb_cContext, context_mark, jit_context_destroy,
context);
+}
+
+/*
+ * call-seq:
+ * context.build { ... }
+ *
+ * Acquire a lock on the context so it can be used to build a function.
+ */
+static VALUE context_build(VALUE self)
+{
+ jit_context_t context;
+ Data_Get_Struct(self, struct _jit_context, context);
+ jit_context_build_start(context);
+#ifdef HAVE_RB_ENSURE
+ return rb_ensure(
+ rb_yield,
+ self,
+ RUBY_METHOD_FUNC(jit_context_build_end),
+ (VALUE)context);
+#else
+ /* Rubinius does not yet have rb_ensure */
+ rb_yield(self);
+ jit_context_build_end(context);
+#endif
+}
+
+/*
+ * call-seq:
+ * Context.build { |context| ... }
+ *
+ * Create a context and acquire a lock on it, then yield the context to
+ * the block.
+ */
+static VALUE context_s_build(VALUE klass)
+{
+ return context_build(context_s_new(klass));
+}
+
+/* ---------------------------------------------------------------------------
+ * Closure
+ * ---------------------------------------------------------------------------
+ */
+
+static void mark_closure(struct Closure * closure)
+{
+ rb_gc_mark(closure->function);
+}
+
+VALUE closure_to_int(VALUE self)
+{
+ struct Closure * closure;
+ Data_Get_Struct(self, struct Closure, closure);
+ VALUE v = ULONG2NUM((unsigned long)closure->function_ptr);
+ return v;
+}
+
+VALUE closure_to_s(VALUE self)
+{
+ struct Closure * closure;
+ VALUE args[4];
+ Data_Get_Struct(self, struct Closure, closure);
+ args[0] = rb_str_new2("#<JIT::Closure:0x%x function=%s function_ptr=0x%x>");
+ args[1] = ULONG2NUM((unsigned long)self);
+ args[2] = rb_any_to_s(closure->function);
+ args[3] = ULONG2NUM((unsigned long)closure->function_ptr);
+ return rb_f_sprintf(sizeof(args)/sizeof(args[0]), args);
+}
+
+VALUE closure_inspect(VALUE self)
+{
+ return closure_to_s(self);
+}
+
+/* ---------------------------------------------------------------------------
+ * Function
+ * ---------------------------------------------------------------------------
+ */
+
+static void mark_function(jit_function_t function)
+{
+ rb_gc_mark((VALUE)jit_function_get_meta(function, RJT_VALUE_OBJECTS));
+ rb_gc_mark((VALUE)jit_function_get_meta(function, RJT_CONTEXT));
+}
+
+static VALUE create_function(int argc, VALUE * argv, VALUE klass)
+{
+ VALUE context_v;
+ VALUE signature_v;
+ VALUE parent_function_v;
+
+ jit_function_t function;
+ jit_function_t parent_function;
+ jit_context_t context;
+ jit_type_t signature;
+ jit_type_t untagged_signature;
+
+ VALUE function_v;
+ VALUE functions;
+
+ int signature_tag;
+
+ rb_scan_args(argc, argv, "21", &context_v, &signature_v, &parent_function_v);
+
+ Data_Get_Struct(context_v, struct _jit_context, context);
+ Data_Get_Struct(signature_v, struct _jit_type, signature);
+
+ signature_tag = jit_type_get_kind(signature);
+
+ /* If this signature was tagged, get the untagged type */
+ if((untagged_signature = jit_type_get_tagged_type(signature)))
+ {
+ signature = untagged_signature;
+ }
+
+ if(RTEST(parent_function_v))
+ {
+ /* If this function has a parent, then it is a nested function */
+ Data_Get_Struct(parent_function_v, struct _jit_function, parent_function);
+ function = jit_function_create_nested(context, signature, parent_function);
+ }
+ else
+ {
+ /* Otherwise, it's a standalone function */
+ function = jit_function_create(context, signature);
+ }
+
+ /* Make sure the function is around as long as the context is */
+ if(!jit_function_set_meta(function, RJT_VALUE_OBJECTS, (void *)rb_ary_new(),
0, 0))
+ {
+ rb_raise(rb_eNoMemError, "Out of memory");
+ }
+
+ /* Remember the signature's tag for later */
+ if(!jit_function_set_meta(function, RJT_TAG_FOR_SIGNATURE, (void
*)signature_tag, 0, 0))
+ {
+ rb_raise(rb_eNoMemError, "Out of memory");
+ }
+
+ /* And remember the function's context for later */
+ if(!jit_function_set_meta(function, RJT_CONTEXT, (void *)context_v, 0, 0))
+ {
+ rb_raise(rb_eNoMemError, "Out of memory");
+ }
+
+ function_v = Data_Wrap_Struct(rb_cFunction, mark_function, 0, function);
+
+ /* Add this function to the context's list of functions */
+ functions = (VALUE)jit_context_get_meta(context, RJT_FUNCTIONS);
+ rb_ary_push(functions, function_v);
+
+ return function_v;
+}
+
+/*
+ * call-seq:
+ * function.compile()
+ *
+ * Begin compiling a function.
+ */
+static VALUE function_compile(VALUE self)
+{
+ jit_function_t function;
+ Data_Get_Struct(self, struct _jit_function, function);
+ if(!jit_function_compile(function))
+ {
+ rb_raise(rb_eRuntimeError, "Unable to compile function");
+ }
+ return self;
+}
+
+/*
+ * call-seq:
+ * function = Function.new(context, signature, [parent])
+ *
+ * Create a new function.
+ */
+static VALUE function_s_new(int argc, VALUE * argv, VALUE klass)
+{
+ if(rb_block_given_p())
+ {
+ rb_raise(rb_eArgError, "Function.new does not take a block");
+ }
+
+ return create_function(argc, argv, klass);
+}
+
+static VALUE function_abandon_if_exception(VALUE function_v)
+{
+ if(ruby_errinfo)
+ {
+ jit_function_t function;
+ Data_Get_Struct(function_v, struct _jit_function, function);
+ jit_function_abandon(function);
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * function = Function.new(context, signature, [parent]) { |function| ... }
+ *
+ * Create a new function, begin compiling it, and pass the function to
+ * the block.
+ */
+static VALUE function_s_compile(int argc, VALUE * argv, VALUE klass)
+{
+ VALUE function = create_function(argc, argv, klass);
+ rb_yield(function);
+ function_compile(function);
+#ifdef HAVE_RB_ENSURE
+ rb_ensure(
+ function_compile,
+ function,
+ function_abandon_if_exception,
+ function);
+#else
+ /* Rubinius does not yet have rb_ensure */
+ function_compile(function);
+ function_abandon_if_exception(function);
+#endif
+ return function;
+}
+
+/*
+ * Get the value that corresponds to a specified function parameter.
+ *
+ * call-seq:
+ * value = function.get_param(index)
+ */
+static VALUE function_get_param(VALUE self, VALUE idx)
+{
+ jit_function_t function;
+ jit_value_t value;
+ Data_Get_Struct(self, struct _jit_function, function);
+ value = jit_value_get_param(function, NUM2INT(idx));
+ raise_memory_error_if_zero(value);
+ return Data_Wrap_Struct(rb_cValue, 0, 0, value);
+}
+
+#include "insns.inc"
+
+static VALUE function_value_klass(VALUE self, VALUE type_v, VALUE klass)
+{
+ jit_function_t function;
+ jit_type_t type;
+ jit_value_t value;
+
+ Data_Get_Struct(self, struct _jit_function, function);
+
+ type_v = lookup_const(rb_cType, type_v);
+ check_type("type", rb_cType, type_v);
+ Data_Get_Struct(type_v, struct _jit_type, type);
+
+ /* TODO: When we wrap a value, we should inject a reference to the
+ * function in the object, so the function stays around as long as the
+ * value does */
+ value = jit_value_create(function, type);
+ return Data_Wrap_Struct(klass, 0, 0, value);
+}
+
+static VALUE coerce_to_jit(VALUE function, VALUE type_v, VALUE value_v);
+
+/*
+ * call-seq:
+ * value = function.value(type)
+ * value = function.value(type, initial_value)
+ *
+ * Create a value (placeholder/variable) with the given type.
+ */
+static VALUE function_value(int argc, VALUE * argv, VALUE self)
+{
+ VALUE type_v = Qnil;
+ VALUE initial_value_v = Qnil;
+
+ VALUE new_value = Qnil;
+
+ rb_scan_args(argc, argv, "11", &type_v, &initial_value_v);
+
+ new_value = function_value_klass(self, type_v, rb_cValue);
+
+ if(argc > 1)
+ {
+ function_insn_store(
+ self,
+ new_value,
+ coerce_to_jit(self, type_v, initial_value_v));
+ }
+
+ return new_value;
+}
+
+static jit_value_t create_const(jit_function_t function, jit_type_t type,
VALUE constant)
+{
+ jit_constant_t c;
+ int kind = jit_type_get_kind(type);
+
+ switch(kind)
+ {
+ case JIT_TYPE_INT:
+ {
+ c.type = type;
+ c.un.int_value = NUM2INT(constant);
+ break;
+ }
+
+ case JIT_TYPE_UINT:
+ {
+ c.type = type;
+ c.un.int_value = NUM2UINT(constant);
+ break;
+ }
+
+ case JIT_TYPE_FLOAT32:
+ {
+ c.type = type;
+ c.un.float32_value = NUM2DBL(constant);
+ break;
+ }
+
+ case JIT_TYPE_FLOAT64:
+ {
+ c.type = type;
+ c.un.float64_value = NUM2DBL(constant);
+ break;
+ }
+
+ case JIT_TYPE_PTR:
+ {
+ c.type = type;
+ c.un.ptr_value = (void *)NUM2ULONG(constant);
+ break;
+ }
+
+ case JIT_TYPE_FIRST_TAGGED + RJT_OBJECT:
+ {
+ VALUE value_objects = (VALUE)jit_function_get_meta(function,
RJT_VALUE_OBJECTS);
+
+ c.type = type;
+ SET_CONSTANT_VALUE(c, constant);
+
+ /* Make sure the object gets marked as long as the function is
+ * around */
+ rb_ary_push(value_objects, constant);
+ break;
+ }
+
+ case JIT_TYPE_FIRST_TAGGED + RJT_ID:
+ {
+ c.type = type;
+ SET_CONSTANT_ID(c, SYM2ID(constant));
+ break;
+ }
+
+ case JIT_TYPE_FIRST_TAGGED + RJT_FUNCTION_PTR:
+ {
+ c.type = type;
+ SET_FUNCTION_POINTER_VALUE(
+ c,
+ (Void_Function_Ptr)NUM2ULONG(rb_to_int(constant)));
+ break;
+ }
+
+ default:
+ rb_raise(rb_eTypeError, "Unsupported type");
+ }
+
+ return jit_value_create_constant(function, &c);
+}
+
+/*
+ * call-seq:
+ * value = function.const(type, constant_value)
+ *
+ * Create a constant value with the given type.
+ */
+static VALUE function_const(VALUE self, VALUE type_v, VALUE constant)
+{
+ jit_function_t function;
+ jit_type_t type;
+ jit_value_t value;
+
+ Data_Get_Struct(self, struct _jit_function, function);
+
+ type_v = lookup_const(rb_cType, type_v);
+ check_type("type", rb_cType, type_v);
+ Data_Get_Struct(type_v, struct _jit_type, type);
+
+ value = create_const(function, type, constant);
+ return Data_Wrap_Struct(rb_cValue, 0, 0, value);
+}
+
+static VALUE coerce_to_jit(VALUE function, VALUE type_v, VALUE value_v)
+{
+ if(rb_obj_is_kind_of(value_v, rb_cValue))
+ {
+ return value_v;
+ }
+ else
+ {
+ return function_const(function, type_v, value_v);
+ }
+}
+
+static void convert_call_args(jit_function_t function, jit_value_t * args,
VALUE args_v, jit_type_t signature)
+{
+ int j;
+
+ for(j = 0; j < RARRAY_LEN(args_v); ++j)
+ {
+ VALUE value = RARRAY_PTR(args_v)[j];
+ jit_value_t arg;
+
+ jit_type_t type = jit_type_get_param(signature, j);
+ if(!type)
+ {
+ rb_raise(rb_eArgError, "Type missing for param %d", j);
+ }
+
+ if(rb_obj_is_kind_of(value, rb_cValue))
+ {
+ Data_Get_Struct(value, struct _jit_value, arg);
+ if(!arg)
+ {
+ rb_raise(rb_eArgError, "Argument %d is invalid", j);
+ }
+ args[j] = arg;
+ }
+ else
+ {
+ args[j] = create_const(function, type, value);
+ }
+ }
+}
+
+/*
+ * call-seq:
+ * value = function.call(name, called_function, flags, [arg1 [, ... ]])
+ *
+ * Generate an instruction to call the specified function.
+ */
+static VALUE function_insn_call(int argc, VALUE * argv, VALUE self)
+{
+ jit_function_t function;
+
+ VALUE name_v;
+ VALUE called_function_v;
+ VALUE args_v;
+ VALUE flags_v = Qnil;
+
+ char const * name;
+ jit_function_t called_function;
+ jit_type_t signature;
+ jit_value_t * args;
+ jit_value_t retval;
+ int flags;
+ size_t num_args;
+
+ rb_scan_args(argc, argv, "3*", &name_v, &called_function_v, &flags_v,
&args_v);
+
+ Data_Get_Struct(self, struct _jit_function, function);
+
+ name = STR2CSTR(name_v);
+
+ check_type("called function", rb_cFunction, called_function_v);
+ Data_Get_Struct(called_function_v, struct _jit_function, called_function);
+
+ num_args = RARRAY_LEN(args_v);
+ args = ALLOCA_N(jit_value_t, num_args);
+
+ signature = jit_function_get_signature(function);
+ convert_call_args(function, args, args_v, signature);
+
+ flags = NUM2INT(flags_v);
+
+ retval = jit_insn_call(
+ function, name, called_function, 0, args, num_args, flags);
+ return Data_Wrap_Struct(rb_cValue, 0, 0, retval);
+}
+
+/*
+ * call-seq:
+ * value = function.call(name, flags, [arg1 [, ... ]])
+ *
+ * Generate an instruction to call a native function.
+ */
+static VALUE function_insn_call_native(int argc, VALUE * argv, VALUE self)
+{
+ jit_function_t function;
+
+ VALUE name_v;
+ VALUE args_v;
+ VALUE function_ptr_v;
+ VALUE signature_v;
+ VALUE flags_v;
+
+ char const * name;
+ jit_value_t * args;
+ jit_value_t retval;
+ void * function_ptr;
+ jit_type_t signature;
+ int flags;
+ size_t num_args;
+
+ rb_scan_args(argc, argv, "4*", &name_v, &function_ptr_v, &signature_v,
&flags_v, &args_v);
+
+ Data_Get_Struct(self, struct _jit_function, function);
+
+ if(SYMBOL_P(name_v))
+ {
+ name = rb_id2name(SYM2ID(name_v));
+ }
+ else
+ {
+ name = StringValuePtr(name_v);
+ }
+
+ function_ptr = (void *)NUM2ULONG(function_ptr_v);
+
+ Data_Get_Struct(signature_v, struct _jit_type, signature);
+
+ num_args = RARRAY_LEN(args_v);
+ args = ALLOCA_N(jit_value_t, num_args);
+
+ if(num_args != jit_type_num_params(signature))
+ {
+ rb_raise(
+ rb_eArgError,
+ "Wrong number of arguments passed for %s (expecting %d but got %d)",
+ name,
+ jit_type_num_params(signature),
+ num_args);
+ }
+
+ convert_call_args(function, args, args_v, signature);
+
+ flags = NUM2INT(flags_v);
+
+ retval = jit_insn_call_native(
+ function, name, function_ptr, signature, args, num_args, flags);
+ return Data_Wrap_Struct(rb_cValue, 0, 0, retval);
+}
+
+/*
+ * call-seq:
+ * function.insn_return()
+ * function.insn_return(value)
+ *
+ * Emit an instruction to return from the function. If value is
+ * specified, return the given value, otherwise return void.
+ */
+static VALUE function_insn_return(int argc, VALUE * argv, VALUE self)
+{
+ jit_function_t function;
+ jit_value_t value = 0;
+ VALUE value_v = Qnil;
+
+ rb_scan_args(argc, argv, "01", &value_v);
+
+ if(value_v != Qnil)
+ {
+ Data_Get_Struct(value_v, struct _jit_value, value);
+ }
+
+ Data_Get_Struct(self, struct _jit_function, function);
+ jit_insn_return(function, value);
+
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * function.apply(arg1 [, arg2 [, ... ]])
+ *
+ * Call a compiled function. Each argument passed in will be converted
+ * to the type specified by the function's signature.
+ *
+ * If the function's signature is Type::RUBY_VARARG_SIGNATURE, then the
+ * arguments will be passed in with the first parameter the count of the
+ * number of arguments, the second parameter a pointer to an array
+ * containing the second through the last argument, and the third
+ * parameter the explicit self (that is, the first argument passed to
+ * apply).
+ */
+static VALUE function_apply(int argc, VALUE * argv, VALUE self)
+{
+ jit_function_t function;
+ jit_type_t signature;
+ int j, n;
+ void * * args;
+ char * arg_data;
+ int signature_tag;
+
+ Data_Get_Struct(self, struct _jit_function, function);
+ signature = jit_function_get_signature(function);
+ n = jit_type_num_params(signature);
+
+ /* void pointers to each of the arguments */
+ args = ALLOCA_N(void *, n);
+
+ /* the actual data */
+ /* TODO: we need to allocate the proper size (but 8 bytes per arg
+ * should be sufficient for now) */
+ arg_data = (char *)ALLOCA_N(char, 8 * n);
+
+ signature_tag = (int)jit_function_get_meta(function, RJT_TAG_FOR_SIGNATURE);
+ if(signature_tag == JIT_TYPE_FIRST_TAGGED + RJT_RUBY_VARARG_SIGNATURE)
+ {
+ jit_VALUE result;
+ int f_argc = argc - 1;
+ VALUE f_self = *(VALUE *)argv;
+ VALUE * f_argv = ((VALUE *)argv) + 1;
+ void * f_args[3];
+ f_args[0] = &f_argc;
+ f_args[1] = &f_argv;
+ f_args[2] = &f_self;
+ jit_function_apply(function, f_args, &result);
+ return result;
+ }
+
+ if(argc != n)
+ {
+ rb_raise(
+ rb_eArgError,
+ "Wrong number of arguments (expected %d but got %d)",
+ n,
+ argc);
+ }
+
+ for(j = 0; j < n; ++j)
+ {
+ jit_type_t arg_type = jit_type_get_param(signature, j);
+ int kind = jit_type_get_kind(arg_type);
+ switch(kind)
+ {
+ case JIT_TYPE_INT:
+ {
+ *(int *)arg_data = NUM2INT(argv[j]);
+ args[j] = (int *)arg_data;
+ arg_data += sizeof(int);
+ break;
+ }
+
+ case JIT_TYPE_UINT:
+ {
+ *(int *)arg_data = NUM2UINT(argv[j]);
+ args[j] = (int *)arg_data;
+ arg_data += sizeof(int);
+ break;
+ }
+
+ case JIT_TYPE_FIRST_TAGGED + RJT_OBJECT:
+ {
+ *(VALUE *)arg_data = argv[j];
+ args[j] = (VALUE *)arg_data;
+ arg_data += sizeof(VALUE);
+ break;
+ }
+
+ case JIT_TYPE_FIRST_TAGGED + RJT_ID:
+ {
+ *(ID *)arg_data = SYM2ID(argv[j]);
+ args[j] = (ID *)arg_data;
+ arg_data += sizeof(ID);
+ break;
+ }
+
+ case JIT_TYPE_FIRST_TAGGED + RJT_FUNCTION_PTR:
+ {
+ *(Void_Function_Ptr *)arg_data =
+ (Void_Function_Ptr)NUM2ULONG(rb_to_int(argv[j]));
+ args[j] = (Void_Function_Ptr *)(arg_data);
+ arg_data += sizeof(Void_Function_Ptr);
+ break;
+ }
+
+ default:
+ rb_raise(rb_eTypeError, "Unsupported type %d", kind);
+ }
+ }
+
+ {
+ jit_type_t return_type = jit_type_get_return(signature);
+ int return_kind = jit_type_get_kind(return_type);
+ switch(return_kind)
+ {
+ case JIT_TYPE_INT:
+ {
+ jit_int result;
+ jit_function_apply(function, args, &result);
+ return INT2NUM(result);
+ }
+
+ case JIT_TYPE_FLOAT32:
+ {
+ jit_float32 result;
+ jit_function_apply(function, args, &result);
+ return rb_float_new(result);
+ }
+
+ case JIT_TYPE_FLOAT64:
+ {
+ jit_float64 result;
+ jit_function_apply(function, args, &result);
+ return rb_float_new(result);
+ }
+
+ case JIT_TYPE_FIRST_TAGGED + RJT_OBJECT:
+ {
+ jit_VALUE result;
+ jit_function_apply(function, args, &result);
+ return result;
+ }
+
+ case JIT_TYPE_FIRST_TAGGED + RJT_ID:
+ {
+ jit_ID result;
+ jit_function_apply(function, args, &result);
+ return ID2SYM(result);
+ }
+
+ default:
+ rb_raise(rb_eTypeError, "Unsupported return type %d", return_kind);
+ }
+ }
+}
+
+/*
+ * call-seq:
+ * level = function.optimization_level()
+ *
+ * Get the optimization level for a function.
+ */
+static VALUE function_optimization_level(VALUE self)
+{
+ jit_function_t function;
+ Data_Get_Struct(self, struct _jit_function, function);
+ return INT2NUM(jit_function_get_optimization_level(function));
+}
+
+/*
+ * call-seq:
+ * function.optimization_level = level
+ *
+ * Set the optimization level for a function.
+ */
+static VALUE function_set_optimization_level(VALUE self, VALUE level)
+{
+ jit_function_t function;
+ Data_Get_Struct(self, struct _jit_function, function);
+ jit_function_set_optimization_level(function, NUM2INT(level));
+ return level;
+}
+
+/*
+ * call-seq:
+ * level = function.max_optimization_level()
+ *
+ * Get the maximum optimization level (which should be the same for any
+ * function).
+ */
+static VALUE function_max_optimization_level(VALUE klass)
+{
+ return INT2NUM(jit_function_get_max_optimization_level());
+}
+
+/*
+ * call-seq:
+ * str = function.dump()
+ *
+ * Dump the instructions in a function to a string.
+ */
+static VALUE function_dump(VALUE self)
+{
+#ifdef HAVE_FMEMOPEN
+ jit_function_t function;
+ char buf[16*1024]; /* TODO: big enough? */
+ FILE * fp = fmemopen(buf, sizeof(buf), "w");
+ Data_Get_Struct(self, struct _jit_function, function);
+ jit_dump_function(fp, function, 0);
+ fclose(fp);
+ return rb_str_new2(buf);
+#else
+ rb_raise(rb_eNotImpError, "Not implemented: missing fmemopen");
+#endif
+}
+
+/*
+ * call-seq:
+ * ptr = function.to_closure()
+ *
+ * Return a pointer to a closure for a function. This pointer can be
+ * passed into other functions as a function pointer.
+ */
+static VALUE function_to_closure(VALUE self)
+{
+ jit_function_t function;
+ struct Closure * closure;
+ VALUE closure_v = Data_Make_Struct(
+ rb_cClosure, struct Closure, mark_closure, free, closure);
+ Data_Get_Struct(self, struct _jit_function, function);
+ closure->function = self;
+ closure->function_ptr =
+ (Void_Function_Ptr)jit_function_to_closure(function);
+ return closure_v;
+}
+
+/*
+ * call-seq:
+ * context = function.context()
+ *
+ * Get a function's context.
+ */
+static VALUE function_get_context(VALUE self)
+{
+ jit_function_t function;
+ Data_Get_Struct(self, struct _jit_function, function);
+ return (VALUE)jit_function_get_meta(function, RJT_CONTEXT);
+}
+
+/*
+ * call-seq:
+ * is_compiled = function.compiled?
+ *
+ * Determine whether a function is compiled.
+ */
+static VALUE function_is_compiled(VALUE self)
+{
+ jit_function_t function;
+ Data_Get_Struct(self, struct _jit_function, function);
+ return jit_function_is_compiled(function) ? Qtrue : Qfalse;
+}
+
+/* ---------------------------------------------------------------------------
+ * Type
+ * ---------------------------------------------------------------------------
+ */
+
+/* This function does not increment the reference count. It is assumed
+ * that the caller will increment the reference count and that the newly
+ * wrapped object will take ownership. */
+static VALUE wrap_type_with_klass(jit_type_t type, VALUE klass)
+{
+ return Data_Wrap_Struct(klass, 0, jit_type_free, type);
+}
+
+static VALUE wrap_type(jit_type_t type)
+{
+ return wrap_type_with_klass(type, rb_cType);
+}
+
+/*
+ * call-seq:
+ * type = Type.create_signature(abi, return_type, array_of_param_types)
+ *
+ * Create a new signature.
+ */
+static VALUE type_s_create_signature(
+ VALUE klass, VALUE abi_v, VALUE return_type_v, VALUE params_v)
+{
+ jit_abi_t abi;
+ jit_type_t return_type;
+ jit_type_t * params;
+ jit_type_t signature;
+ int j;
+ int len;
+
+ return_type_v = lookup_const(rb_cType, return_type_v);
+ check_type("return type", rb_cType, return_type_v);
+ Data_Get_Struct(return_type_v, struct _jit_type, return_type);
+
+ Check_Type(params_v, T_ARRAY);
+ len = RARRAY_LEN(params_v);
+ params = ALLOCA_N(jit_type_t, len);
+ for(j = 0; j < len; ++j)
+ {
+ VALUE param = RARRAY_PTR(params_v)[j];
+ param = lookup_const(rb_cType, param);
+ check_type("param", rb_cType, param);
+ Data_Get_Struct(param, struct _jit_type, params[j]);
+ }
+
+ abi_v = lookup_const(rb_mABI, abi_v);
+ abi = NUM2INT(abi_v);
+
+ signature = jit_type_create_signature(abi, return_type, params, len, 1);
+ return wrap_type(signature);
+}
+
+/*
+ * call-seq:
+ * type = Type.create_struct(array_of_field_types)
+ *
+ * Create a new struct type.
+ */
+static VALUE type_s_create_struct(
+ VALUE klass, VALUE fields_v)
+{
+ jit_type_t * fields;
+ jit_type_t struct_type;
+ int len;
+ int j;
+
+ Check_Type(fields_v, T_ARRAY);
+ len = RARRAY_LEN(fields_v);
+ fields = ALLOCA_N(jit_type_t, len);
+ for(j = 0; j < len; ++j)
+ {
+ VALUE field = RARRAY_PTR(fields_v)[j];
+ check_type("field", rb_cType, field);
+ Data_Get_Struct(field, struct _jit_type, fields[j]);
+ }
+
+ struct_type = jit_type_create_struct(fields, RARRAY_LEN(fields_v), 1);
+ return wrap_type_with_klass(struct_type, klass);
+}
+
+/*
+ * call-seq:
+ * type = Type.create_pointer(pointed_to_type)
+ *
+ * Create a new pointer type, pointing to the given type.
+ */
+static VALUE type_s_create_pointer(
+ VALUE klass, VALUE type_v)
+{
+ jit_type_t type;
+ jit_type_t pointer_type;
+ Data_Get_Struct(type_v, struct _jit_type, type);
+ pointer_type = jit_type_create_pointer(type, 1);
+ return wrap_type_with_klass(pointer_type, klass);
+}
+
+/*
+ * call-seq:
+ * offset = struct_type.get_offset(index)
+ *
+ * Get the offset of the nth field in a struct.
+ */
+static VALUE type_get_offset(VALUE self, VALUE field_index_v)
+{
+ int field_index = NUM2INT(field_index_v);
+ jit_type_t type;
+ Data_Get_Struct(self, struct _jit_type, type);
+ return INT2NUM(jit_type_get_offset(type, field_index));
+}
+
+/*
+ * call-seq:
+ * offset = struct_type.set_offset(index, offset)
+ *
+ * Set the offset of the nth field in a struct.
+ */
+static VALUE type_set_offset(VALUE self, VALUE field_index_v, VALUE offset_v)
+{
+ int field_index = NUM2INT(field_index_v);
+ int offset = NUM2UINT(offset_v);
+ jit_type_t type;
+ Data_Get_Struct(self, struct _jit_type, type);
+ jit_type_set_offset(type, field_index, offset);
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * offset = struct_type.size
+ *
+ * Get the size of a struct or union type.
+ */
+static VALUE type_size(VALUE self)
+{
+ jit_type_t type;
+ Data_Get_Struct(self, struct _jit_type, type);
+ return INT2NUM(jit_type_get_size(type));
+}
+
+/* ---------------------------------------------------------------------------
+ * Value
+ * ---------------------------------------------------------------------------
+ */
+
+static VALUE value_s_new_value(VALUE klass, VALUE function, VALUE type)
+{
+ return function_value_klass(function, type, klass);
+}
+
+/*
+ * call-seq:
+ * str = value.to_s
+ *
+ * Return a string representation of the value.
+ */
+static VALUE value_to_s(VALUE self)
+{
+#ifdef HAVE_FMEMOPEN
+ char buf[1024];
+ FILE * fp = fmemopen(buf, sizeof(buf), "w");
+ jit_value_t value;
+ jit_function_t function;
+ Data_Get_Struct(self, struct _jit_value, value);
+ function = jit_value_get_function(value);
+ jit_dump_value(fp, function, value, 0);
+ fclose(fp);
+ return rb_str_new2(buf);
+#else
+ rb_raise(rb_eNotImpError, "Not implemented: missing fmemopen");
+#endif
+}
+
+/*
+ * call-seq:
+ * str = value.inspect
+ *
+ * Return a string representation of a value with additional
+ * information about the value.
+ */
+static VALUE value_inspect(VALUE self)
+{
+ jit_value_t value;
+ jit_type_t type;
+ char const * cname = rb_obj_classname(self);
+ VALUE args[6];
+ Data_Get_Struct(self, struct _jit_value, value);
+ type = jit_value_get_type(value);
+ args[0] = rb_str_new2("#<%s:0x%x %s ptr=0x%x type=0x%x>");
+ args[1] = rb_str_new2(cname);
+ args[2] = ULONG2NUM((unsigned long)self);
+ args[3] = value_to_s(self);
+ args[4] = ULONG2NUM((unsigned long)value);
+ args[5] = ULONG2NUM((unsigned long)type);
+ return rb_f_sprintf(sizeof(args)/sizeof(args[0]), args);
+}
+
+/*
+ * call-seq:
+ * is_valid = value.valid?
+ *
+ * Determine if a value is valid (non-zero).
+ */
+static VALUE value_is_valid(VALUE self)
+{
+ jit_value_t value;
+ Data_Get_Struct(self, struct _jit_value, value);
+ return (value != 0) ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq:
+ * is_temporary = value.temporary?
+ *
+ * Determine if a value represents a temporary.
+ */
+static VALUE value_is_temporary(VALUE self)
+{
+ jit_value_t value;
+ Data_Get_Struct(self, struct _jit_value, value);
+ return jit_value_is_temporary(value) ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq:
+ * is_local = value.local?
+ *
+ * Determine if a value represents a local.
+ */
+static VALUE value_is_local(VALUE self)
+{
+ jit_value_t value;
+ Data_Get_Struct(self, struct _jit_value, value);
+ return jit_value_is_local(value) ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq:
+ * is_local = value.constant?
+ *
+ * Determine if a value represents a constant.
+ */
+static VALUE value_is_constant(VALUE self)
+{
+ jit_value_t value;
+ Data_Get_Struct(self, struct _jit_value, value);
+ return jit_value_is_constant(value) ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq:
+ * is_volatile = value.volatile?
+ *
+ * Determine if a value is volatile (that is, the contents must be
+ * reloaded from memory each time it is used).
+ */
+static VALUE value_is_volatile(VALUE self)
+{
+ jit_value_t value;
+ Data_Get_Struct(self, struct _jit_value, value);
+ return jit_value_is_volatile(value) ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq:
+ * value.volatile = is_volatile
+ *
+ * Make a value volatile (that is, ensure that its contents are reloaded
+ * from memory each time it is used).
+ */
+static VALUE value_set_volatile(VALUE self)
+{
+ jit_value_t value;
+ Data_Get_Struct(self, struct _jit_value, value);
+ jit_value_set_volatile(value);
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * is_addressable = value.addressable?
+ *
+ * Determine if a value is addressable.
+ */
+static VALUE value_is_addressable(VALUE self)
+{
+ jit_value_t value;
+ Data_Get_Struct(self, struct _jit_value, value);
+ return jit_value_is_addressable(value) ? Qtrue : Qfalse;
+}
+
+/*
+ * call-seq:
+ * value.addressable = is_addressable
+ *
+ * Make a value addressable.
+ */
+static VALUE value_set_addressable(VALUE self)
+{
+ jit_value_t value;
+ Data_Get_Struct(self, struct _jit_value, value);
+ jit_value_set_addressable(value);
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * function = value.function()
+ *
+ * Get a value's function.
+ */
+static VALUE value_function(VALUE self)
+{
+ jit_value_t value;
+ jit_function_t function;
+ Data_Get_Struct(self, struct _jit_value, value);
+ function = jit_value_get_function(value);
+ return Data_Wrap_Struct(rb_cFunction, mark_function, 0, function);
+}
+
+/*
+ * call-seq:
+ * function = value.type()
+ *
+ * Get a value's type.
+ */
+static VALUE value_type(VALUE self)
+{
+ jit_value_t value;
+ jit_type_t type;
+ Data_Get_Struct(self, struct _jit_value, value);
+ type = jit_value_get_type(value);
+ type = jit_type_copy(type);
+ return wrap_type(type);
+}
+
+/*
+ * call-seq:
+ * value1, value2 = value1.coerce(value2)
+ *
+ * If the given value is a JIT::Value, return an +[ self, value ]+,
+ * otherwise coerce the value to the same type as self and return
+ * +[ self, coerced_value ]+.
+ */
+static VALUE value_coerce(VALUE self, VALUE value)
+{
+ return rb_assoc_new(
+ self,
+ coerce_to_jit(
+ value_function(self),
+ value_type(self),
+ value));
+}
+
+/* ---------------------------------------------------------------------------
+ * Label
+ * ---------------------------------------------------------------------------
+ */
+
+/*
+ * call-seq:
+ * label = Label.new
+ *
+ * Create a new label.
+ */
+static VALUE label_s_new(VALUE klass)
+{
+ jit_label_t * label;
+ VALUE labelval = Data_Make_Struct(rb_cLabel, jit_label_t, 0, xfree, label);
+ *label = jit_label_undefined;
+ return labelval;
+}
+
+/* ---------------------------------------------------------------------------
+ * Module
+ * ---------------------------------------------------------------------------
+ */
+
+/*
+ * call-seq:
+ * module.define_jit_method(name, function)
+ *
+ * Use a Function to define an instance method on a module. The
+ * function should have one of two signatures:
+ *
+ * * The first parameter to the function should be an OBJECT that
+ * represents the self parameter and the rest of the parameters, or
+ *
+ * * The function's signature should be Type::RUBY_VARARG_SIGNATURE.
+ */
+static VALUE module_define_jit_method(VALUE klass, VALUE name_v, VALUE
function_v)
+{
+ char const * name;
+ jit_function_t function;
+ jit_type_t signature;
+ int signature_tag;
+ int arity;
+ VALUE closure_v;
+ struct Closure * closure;
+
+ if(SYMBOL_P(name_v))
+ {
+ name = rb_id2name(SYM2ID(name_v));
+ }
+ else
+ {
+ name = STR2CSTR(name_v);
+ }
+
+ Data_Get_Struct(function_v, struct _jit_function, function);
+
+ signature = jit_function_get_signature(function);
+ signature_tag = (int)jit_function_get_meta(function, RJT_TAG_FOR_SIGNATURE);
+ if(signature_tag == JIT_TYPE_FIRST_TAGGED + RJT_RUBY_VARARG_SIGNATURE)
+ {
+ arity = -1;
+ }
+ else
+ {
+ /* TODO: check the types to make sure they are OBJECT */
+ arity = jit_type_num_params(signature) - 1;
+ }
+
+ closure_v = function_to_closure(function_v);
+ Data_Get_Struct(closure_v, struct Closure, closure);
+ define_method_with_data(
+ klass, rb_intern(name), RUBY_METHOD_FUNC(closure->function_ptr),
+ arity, closure_v);
+ return Qnil;
+}
+
+/* ---------------------------------------------------------------------------
+ * Init
+ * ---------------------------------------------------------------------------
+ */
+
+void Init_jit()
+{
+ jit_init();
+
+ rb_mJIT = rb_define_module("JIT");
+
+ rb_cContext = rb_define_class_under(rb_mJIT, "Context", rb_cObject);
+ rb_define_singleton_method(rb_cContext, "new", context_s_new, 0);
+ rb_define_method(rb_cContext, "build", context_build, 0);
+ rb_define_singleton_method(rb_cContext, "build", context_s_build, 0);
+
+ rb_cClosure = rb_define_class_under(rb_mJIT, "Closure", rb_cObject);
+ rb_define_method(rb_cClosure, "to_int", closure_to_int, 0);
+ rb_define_method(rb_cClosure, "to_s", closure_to_s, 0);
+ rb_define_method(rb_cClosure, "inspect", closure_inspect, 0);
+
+ rb_cFunction = rb_define_class_under(rb_mJIT, "Function", rb_cObject);
+ rb_define_singleton_method(rb_cFunction, "new", function_s_new, -1);
+ rb_define_method(rb_cFunction, "compile", function_compile, 0);
+ rb_define_singleton_method(rb_cFunction, "compile", function_s_compile, -1);
+ rb_define_method(rb_cFunction, "get_param", function_get_param, 1);
+ init_insns();
+ rb_define_method(rb_cFunction, "insn_call", function_insn_call, -1);
+ rb_define_method(rb_cFunction, "insn_call_native",
function_insn_call_native, -1);
+ rb_define_method(rb_cFunction, "insn_return", function_insn_return, -1);
+ rb_define_method(rb_cFunction, "apply", function_apply, -1);
+ rb_define_alias(rb_cFunction, "call", "apply");
+ rb_define_method(rb_cFunction, "value", function_value, -1);
+ rb_define_method(rb_cFunction, "const", function_const, 2);
+ rb_define_method(rb_cFunction, "optimization_level",
function_optimization_level, 0);
+ rb_define_method(rb_cFunction, "optimization_level=",
function_set_optimization_level, 1);
+ rb_define_singleton_method(rb_cFunction, "max_optimization_level",
function_max_optimization_level, 0);
+ rb_define_method(rb_cFunction, "dump", function_dump, 0);
+ rb_define_method(rb_cFunction, "to_closure", function_to_closure, 0);
+ rb_define_method(rb_cFunction, "context", function_get_context, 0);
+ rb_define_method(rb_cFunction, "compiled?", function_is_compiled, 0);
+
+ rb_cType = rb_define_class_under(rb_mJIT, "Type", rb_cObject);
+ rb_define_singleton_method(rb_cType, "create_signature",
type_s_create_signature, 3);
+ rb_define_singleton_method(rb_cType, "create_struct", type_s_create_struct,
1);
+ rb_define_singleton_method(rb_cType, "create_pointer",
type_s_create_pointer, 1);
+ rb_define_method(rb_cType, "get_offset", type_get_offset, 1);
+ rb_define_method(rb_cType, "set_offset", type_set_offset, 2);
+ rb_define_method(rb_cType, "size", type_size, 0);
+ rb_define_const(rb_cType, "VOID", wrap_type(jit_type_void));
+ rb_define_const(rb_cType, "SBYTE", wrap_type(jit_type_sbyte));
+ rb_define_const(rb_cType, "UBYTE", wrap_type(jit_type_ubyte));
+ rb_define_const(rb_cType, "SHORT", wrap_type(jit_type_short));
+ rb_define_const(rb_cType, "USHORT", wrap_type(jit_type_ushort));
+ rb_define_const(rb_cType, "INT", wrap_type(jit_type_int));
+ rb_define_const(rb_cType, "UINT", wrap_type(jit_type_uint));
+ rb_define_const(rb_cType, "NINT", wrap_type(jit_type_nint));
+ rb_define_const(rb_cType, "NUINT", wrap_type(jit_type_nuint));
+ rb_define_const(rb_cType, "LONG", wrap_type(jit_type_long));
+ rb_define_const(rb_cType, "ULONG", wrap_type(jit_type_ulong));
+ rb_define_const(rb_cType, "FLOAT32", wrap_type(jit_type_float32));
+ rb_define_const(rb_cType, "FLOAT64", wrap_type(jit_type_float64));
+ rb_define_const(rb_cType, "NFLOAT", wrap_type(jit_type_nfloat));
+ rb_define_const(rb_cType, "VOID_PTR", wrap_type(jit_type_void_ptr));
+
+ jit_type_VALUE = jit_type_create_tagged(jit_underlying_type_VALUE,
RJT_OBJECT, 0, 0, 1);
+ rb_define_const(rb_cType, "OBJECT", wrap_type(jit_type_VALUE));
+
+ jit_type_ID = jit_type_create_tagged(jit_underlying_type_ID, RJT_ID, 0, 0,
1);
+ rb_define_const(rb_cType, "ID", wrap_type(jit_type_ID));
+
+ jit_type_Function_Ptr = jit_type_create_tagged(jit_underlying_type_ID,
RJT_FUNCTION_PTR, 0, 0, 1);
+ rb_define_const(rb_cType, "FUNCTION_PTR", wrap_type(jit_type_Function_Ptr));
+
+ {
+ jit_type_t ruby_vararg_param_types[3];
+ jit_type_t ruby_vararg_signature_untagged;
+ ruby_vararg_param_types[0] = jit_type_int;
+ ruby_vararg_param_types[1] = jit_type_void_ptr;
+ ruby_vararg_param_types[2] = jit_type_VALUE;
+ ruby_vararg_signature_untagged = jit_type_create_signature(
+ jit_abi_cdecl,
+ jit_type_VALUE,
+ ruby_vararg_param_types,
+ 3,
+ 1);
+ ruby_vararg_signature =
jit_type_create_tagged(ruby_vararg_signature_untagged,
RJT_RUBY_VARARG_SIGNATURE, 0, 0, 1);
+ }
+ rb_define_const(rb_cType, "RUBY_VARARG_SIGNATURE",
wrap_type(ruby_vararg_signature));
+
+ rb_mABI = rb_define_module_under(rb_mJIT, "ABI");
+ rb_define_const(rb_mABI, "CDECL", INT2NUM(jit_abi_cdecl));
+ rb_define_const(rb_mABI, "VARARG", INT2NUM(jit_abi_vararg));
+ rb_define_const(rb_mABI, "STDCALL", INT2NUM(jit_abi_stdcall));
+ rb_define_const(rb_mABI, "FASTCALL", INT2NUM(jit_abi_fastcall));
+
+ rb_cValue = rb_define_class_under(rb_mJIT, "Value", rb_cObject);
+ rb_define_singleton_method(rb_cValue, "new_value", value_s_new_value, 2);
+ rb_define_method(rb_cValue, "to_s", value_to_s, 0);
+ rb_define_method(rb_cValue, "inspect", value_inspect, 0);
+ rb_define_method(rb_cValue, "valid?", value_is_valid, 0);
+ rb_define_method(rb_cValue, "temporary?", value_is_temporary, 0);
+ rb_define_method(rb_cValue, "local?", value_is_local, 0);
+ rb_define_method(rb_cValue, "constant?", value_is_constant, 0);
+ rb_define_method(rb_cValue, "volatile?", value_is_volatile, 0);
+ rb_define_method(rb_cValue, "set_volatile", value_set_volatile, 0);
+ rb_define_method(rb_cValue, "addressable?", value_is_addressable, 0);
+ rb_define_method(rb_cValue, "addressable=", value_set_addressable, 0);
+ rb_define_method(rb_cValue, "function", value_function, 0);
+ rb_define_method(rb_cValue, "type", value_type, 0);
+ rb_define_method(rb_cValue, "coerce", value_coerce, 1);
+
+ rb_cLabel = rb_define_class_under(rb_mJIT, "Label", rb_cObject);
+ rb_define_singleton_method(rb_cLabel, "new", label_s_new, 0);
+
+ rb_mCall = rb_define_module_under(rb_mJIT, "Call");
+ rb_define_const(rb_mCall, "NOTHROW", INT2NUM(JIT_CALL_NOTHROW));
+ rb_define_const(rb_mCall, "NORETURN", INT2NUM(JIT_CALL_NORETURN));
+ rb_define_const(rb_mCall, "TAIL", INT2NUM(JIT_CALL_TAIL));
+
+ /* VALUE rb_cModule = rb_define_module(); */
+ rb_define_method(rb_cModule, "define_jit_method", module_define_jit_method,
2);
+
+#ifdef NEED_MINIMAL_NODE
+ Init_minimal_node();
+#endif
+}
+
Index: jitruby/ext/method_data.c.rpp
===================================================================
RCS file: jitruby/ext/method_data.c.rpp
diff -N jitruby/ext/method_data.c.rpp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/ext/method_data.c.rpp 12 Dec 2008 11:31:03 -0000 1.1
@@ -0,0 +1,294 @@
+#include "method_data.h"
+
+#include <ruby.h>
+
+#ifndef HAVE_RUBINIUS
+
+#ruby <<END
+MAX_ARGS = 15
+nil
+END
+
+#if defined(HAVE_NODE_H)
+/* pre-YARV */
+#include <node.h>
+#elif defined(REALLY_HAVE_RUBY_NODE_H)
+/* YARV */
+#include <ruby/node.h>
+#elif defined(RUBY_VM)
+/* YARV without node.h */
+#include "minimal_node.h"
+#else
+/* something else */
+#error "Need node.h"
+#endif
+
+#ifdef HAVE_ENV_H
+/* pre-YARV */
+#include <env.h>
+#endif
+
+#ifdef RUBY_VM
+
+/* YARV */
+
+struct rb_thread_struct
+{
+ VALUE self;
+ void *vm;
+ VALUE *stack;
+ unsigned long stack_size;
+ VALUE *cfp;
+ /* ... */
+};
+
+typedef struct rb_thread_struct rb_thread_t;
+
+#define CFP_DATA_MEMO_NODE_AND_PC cfp[0]
+#define CFP_METHOD_CLASS cfp[11]
+
+/* On YARV, we store the method data on the stack. We don't have to pop
+ * it off the stack, because the stack pointer will be reset to the
+ * previous frame's stack pointer when the function returns.
+ */
+static void fix_frame()
+{
+ do {
+ extern rb_thread_t * ruby_current_thread;
+ VALUE * cfp = ruby_current_thread->cfp;
+ CFP_DATA_MEMO_NODE_AND_PC = RBASIC(CFP_METHOD_CLASS)->klass;
+
+ if(rb_type(CFP_DATA_MEMO_NODE_AND_PC) != T_NODE)
+ {
+ /* This can happen for module functions that are created after
+ * the stub function */
+ rb_raise(
+ rb_eRuntimeError,
+ "Cannot find method data for module function");
+ }
+ else
+ {
+ CFP_METHOD_CLASS = RCLASS_SUPER(CFP_METHOD_CLASS);
+ }
+ } while(0);
+}
+
+#define FIX_FRAME() \
+ fix_frame()
+
+static NODE * data_memo_node()
+{
+ extern rb_thread_t * ruby_current_thread;
+ VALUE * cfp = ruby_current_thread->cfp;
+ return (NODE *)CFP_DATA_MEMO_NODE_AND_PC;
+}
+
+#else
+
+/* pre-YARV */
+
+/* Okay to not pop this temporary frame, since it will be popped by the
+ * caller
+ */
+#define FIX_FRAME() \
+ struct FRAME _frame = *ruby_frame; \
+ _frame.last_class = RCLASS(ruby_frame->last_class)->super; \
+ _frame.prev = ruby_frame; \
+ ruby_frame = &_frame; \
+
+static NODE * data_memo_node()
+{
+ return (NODE *)(RBASIC(ruby_frame->prev->last_class)->klass);
+}
+
+#endif
+
+typedef VALUE (*Method_Func)(ANYARGS);
+
+static Method_Func actual_cfunc()
+{
+ return data_memo_node()->nd_cfnc;
+}
+
+static VALUE data_wrapper_m1(int argc, VALUE * argv, VALUE self)
+{
+ VALUE result;
+ FIX_FRAME();
+ result = (*actual_cfunc())(argc, argv, self);
+ return result;
+}
+
+static VALUE data_wrapper_0(VALUE self)
+{
+ VALUE result;
+ FIX_FRAME();
+ result = (*actual_cfunc())(self);
+ return result;
+}
+
+#ruby <<END
+ (1..MAX_ARGS).each do |i|
+ params = (1..i).map { |j| "VALUE arg#{j}" }.join(', ')
+ args = (1..i).map { |j| "arg#{j}" }.join(', ')
+
+ puts <<-END
+static VALUE data_wrapper_#{i}(VALUE self, #{params})
+{
+ VALUE result;
+ FIX_FRAME();
+ result = (*actual_cfunc())(self, #{args});
+ return result;
+}
+ END
+ end
+
+ nil
+END
+
+/* Define a method and attach data to it.
+ *
+ * The method looks to ruby like a normal aliased CFUNC, with a modified
+ * origin class:
+ *
+ * NODE_FBODY
+ * |- (u1) orig - origin class
+ * | |- basic
+ * | | |- flags - origin class flags + FL_SINGLETON
+ * | | +- klass - NODE_MEMO
+ * | | |- (u1) cfnc - actual C function to call
+ * | | |- (u2) rval - stored data
+ * | | +- (u3) 0
+ * | |- iv_tbl - 0
+ * | |- m_tbl - 0
+ * | +- super - actual origin class
+ * |- (u2) mid - name of the method
+ * +- (u3) head - NODE_CFUNC
+ * |- (u1) cfnc - wrapper function to call
+ * +- (u2) argc - function arity
+ *
+ * Or, on YARV:
+ *
+ * NODE_FBODY
+ * |- (u1) oid - name of the method
+ * +- (u2) body - NODE_METHOD
+ * |- (u1) clss - origin class
+ * | |- basic
+ * | | |- flags - origin class flags + FL_SINGLETON
+ * | | +- klass - NODE_MEMO
+ * | | |- (u1) cfnc - actual C function to call
+ * | | |- (u2) rval - stored data
+ * | | +- (u3) 0
+ * | |- ptr - rb_classext_t
+ * | | |- super - actual origin class
+ * | | +- iv_tbl - 0
+ * | |- m_tbl - 0
+ * | +- iv_index_tbl - 0?
+ * |- (u2) body - NODE_CFUNC
+ * | |- (u1) cfnc - wrapper function to call
+ * | |- (u2) argc - function arity
+ * +- (u3) noex - NOEX_PUBLIC
+ *
+ * When the wrapper function is called, last_class is set to the origin
+ * class found in the FBODY node. So that the method data will be
+ * accessible, and so last_class will point to klass and not to our MEMO
+ * node, it is necessary to "fix" the current frame.
+ *
+ * Pre-YARV, this means we duplicate the current frame and set last_class:
+ *
+ * ruby_frame
+ * |- last_class - klass
+ * |- prev
+ * | |- last_class - NODE_MEMO
+ * | | |- (u1) cfnc - actual C function to call
+ * | | |- (u2) rval - stored data
+ * | | +- (u3) 0
+ * | |- prev - the real previous frame
+ * | +- ...
+ * +- ...
+ *
+ * The method data is then accessible via
+ * ruby_frame->prev->last_class->rval.
+ *
+ * On YARV, the current frame is not duplicated; rather, the method data
+ * is placed on the stack and is referenced by one of the unused members
+ * of the control frame (the program counter):
+ *
+ * ruby_current_thread->cfp
+ * |- pc - NODE_MEMO
+ * | |- (u1) cfnc - actual C function to call
+ * | |- (u2) rval - stored data
+ * | +- (u3) 0
+ * |- method_class - klass
+ * +- ...
+ *
+ */
+void define_method_with_data(
+ VALUE klass, ID id, VALUE (*cfunc)(ANYARGS), int arity, VALUE data)
+{
+ /* TODO: origin should have #to_s and #inspect methods defined */
+#ifdef HAVE_RB_CLASS_BOOT
+ VALUE origin = rb_class_boot(klass);
+#else
+ VALUE origin = rb_class_new(klass);
+#endif
+ NODE * node;
+
+ VALUE (*data_wrapper)(ANYARGS);
+ switch(arity)
+ {
+#ruby <<END
+ (0..MAX_ARGS).each do |i|
+ puts <<-END
+ case #{i}: data_wrapper = RUBY_METHOD_FUNC(data_wrapper_#{i}); break;
+ END
+ end
+ nil
+END
+ case -1: data_wrapper = RUBY_METHOD_FUNC(data_wrapper_m1); break;
+ default: rb_raise(rb_eArgError, "unsupported arity %d", arity);
+ }
+
+ FL_SET(origin, FL_SINGLETON);
+ rb_singleton_class_attached(origin, klass);
+ rb_name_class(origin, SYM2ID(rb_class_name(klass)));
+
+ RBASIC(origin)->klass = (VALUE)NEW_NODE(NODE_MEMO, cfunc, data, 0);
+
+#ifdef RUBY_VM
+ /* YARV */
+ node = NEW_FBODY(
+ NEW_METHOD(
+ NEW_CFUNC(data_wrapper, arity),
+ origin,
+ NOEX_PUBLIC),
+ id);
+ st_insert(RCLASS_M_TBL(klass), id, (st_data_t)node);
+#else
+ /* pre-YARV */
+ node = NEW_FBODY(
+ NEW_CFUNC(data_wrapper, arity),
+ id,
+ origin);
+ rb_add_method(klass, id, node, NOEX_PUBLIC);
+#endif
+}
+
+VALUE get_method_data()
+{
+ return data_memo_node()->nd_rval;
+}
+
+#else /* HAVE_RUBINIUS */
+
+void define_method_with_data(
+ VALUE klass, ID id, VALUE (*cfunc)(ANYARGS), int arity, VALUE data)
+{
+ rb_raise(rb_eNotImpError, "Not implemented: define_method_with_data");
+}
+
+VALUE get_method_data()
+{
+ rb_raise(rb_eNotImpError, "Not implemented: get_method_data");
+}
+
+#endif
Index: jitruby/ext/method_data.h
===================================================================
RCS file: jitruby/ext/method_data.h
diff -N jitruby/ext/method_data.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/ext/method_data.h 12 Dec 2008 11:31:03 -0000 1.1
@@ -0,0 +1,11 @@
+#ifndef METHOD_DATA_H
+#define METHOD_DATA_H
+
+#include <ruby.h>
+
+void define_method_with_data(
+ VALUE klass, ID id, VALUE (*cfunc)(ANYARGS), int arity, VALUE data);
+
+VALUE get_method_data();
+
+#endif // METHOD_DATA_H
Index: jitruby/ext/minimal_node.c
===================================================================
RCS file: jitruby/ext/minimal_node.c
diff -N jitruby/ext/minimal_node.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/ext/minimal_node.c 12 Dec 2008 11:31:03 -0000 1.1
@@ -0,0 +1,35 @@
+#ifdef NEED_MINIMAL_NODE
+
+#include "minimal_node.h"
+#include <string.h>
+
+int NODE_MEMO = 0;
+int NODE_METHOD = 0;
+int NODE_FBODY = 0;
+int NODE_CFUNC = 0;
+
+char const * ruby_node_name(int node);
+
+static int node_value(char const * name)
+{
+ /* TODO: any way to end the block? */
+ int j;
+ for(j = 0; ; ++j)
+ {
+ if(!strcmp(name, ruby_node_name(j)))
+ {
+ return j;
+ }
+ }
+}
+
+void Init_minimal_node()
+{
+ NODE_MEMO = node_value("NODE_MEMO");
+ NODE_METHOD = node_value("NODE_METHOD");
+ NODE_FBODY = node_value("NODE_FBODY");
+ NODE_CFUNC = node_value("NODE_CFUNC");
+}
+
+#endif
+
Index: jitruby/ext/minimal_node.h
===================================================================
RCS file: jitruby/ext/minimal_node.h
diff -N jitruby/ext/minimal_node.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/ext/minimal_node.h 12 Dec 2008 11:31:03 -0000 1.1
@@ -0,0 +1,52 @@
+#ifndef minimal_node_h
+#define minimal_node_h
+
+#ifdef NEED_MINIMAL_NODE
+
+#include "ruby.h"
+
+typedef struct RNode {
+ unsigned long flags;
+ void * reserved;
+ union {
+ struct RNode * node;
+ VALUE (*cfunc)(ANYARGS);
+ } u1;
+ union {
+ struct RNode * node;
+ VALUE value;
+ } u2;
+ union {
+ struct RNode * node;
+ } u3;
+} NODE;
+
+#define nd_cfnc u1.cfunc
+#define nd_rval u2.value
+
+#define NEW_NODE(t,a0,a1,a2)
rb_node_newnode((t),(VALUE)(a0),(VALUE)(a1),(VALUE)(a2))
+
+/* TODO: No way to know the correct size of node_type */
+enum node_type {
+ NODE_FOO,
+};
+
+void rb_add_method(VALUE, ID, NODE *, int);
+NODE *rb_node_newnode(enum node_type, VALUE, VALUE, VALUE);
+
+extern int NODE_MEMO;
+extern int NODE_METHOD;
+extern int NODE_FBODY;
+extern int NODE_CFUNC;
+
+void Init_minimal_node();
+
+#define NOEX_PUBLIC 0x0
+
+#define NEW_METHOD(n,x,v) NEW_NODE(NODE_METHOD,x,n,v)
+#define NEW_FBODY(n,i) NEW_NODE(NODE_FBODY,i,n,0)
+#define NEW_CFUNC(f,c) NEW_NODE(NODE_CFUNC,f,c,0)
+
+#endif
+
+#endif
Index: jitruby/ext/rubyjit.h
===================================================================
RCS file: jitruby/ext/rubyjit.h
diff -N jitruby/ext/rubyjit.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/ext/rubyjit.h 12 Dec 2008 11:31:03 -0000 1.1
@@ -0,0 +1,20 @@
+#ifndef RUBYJIT_H
+#define RUBYJIT_H
+
+enum Ruby_Libjit_Tag
+{
+ RJT_OBJECT,
+ RJT_ID,
+ RJT_FUNCTION_PTR,
+ RJT_RUBY_VARARG_SIGNATURE,
+ RJT_VALUE_OBJECTS,
+ RJT_FUNCTIONS,
+ RJT_CONTEXT,
+ RJT_TAG_FOR_SIGNATURE
+};
+
+extern jit_type_t jit_type_VALUE;
+extern jit_type_t jit_type_ID;
+extern jit_type_t jit_type_Function_Ptr;
+
+#endif
Index: jitruby/ext/rubypp.rb
===================================================================
RCS file: jitruby/ext/rubypp.rb
diff -N jitruby/ext/rubypp.rb
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/ext/rubypp.rb 12 Dec 2008 11:31:03 -0000 1.1
@@ -0,0 +1,97 @@
+class Preprocessor
+ def initialize(input, output, filename)
+ @input = input
+ @output = output
+ @filename = filename
+ @linenum = 1
+ end
+
+ def getline
+ line = @input.gets
+ @linenum += 1 if not line.nil?
+ return line
+ end
+
+ def preprocess
+ success = false
+ begin
+ loop do
+ line = getline
+ break if line.nil?
+ case line
+ when /(.*[^\\]|^)\#\{(.*?)\}(.*)/
+ puts "#{$1}#{evaluate($2, @linenum)}#{$3}"
+ when /^\#ruby\s+<<(.*)/
+ marker = $1
+ str = ''
+ evalstart = @linenum
+ loop do
+ line = getline
+ if line.nil? then
+ raise "End of input without #{marker}"
+ end
+ break if line.chomp == marker
+ str << line
+ end
+ result = evaluate(str, evalstart)
+ puts result if not result.nil?
+ when /^\#ruby\s+(.*)/
+ str = line = $1
+ while line[-1] == ?\\
+ str.chop!
+ line = getline
+ break if line.nil?
+ line.chomp!
+ str << line
+ end
+ result = evaluate(str, @linenum)
+ puts result if not result.nil?
+ else
+ puts line
+ end
+ end
+ success = true
+ ensure
+ if not success then
+ $stderr.puts "Error on line address@hidden:"
+ end
+ end
+ end
+
+ def evaluate(str, linenum)
+ result = eval(str, TOPLEVEL_BINDING, @filename, linenum)
+ success = true
+ return result
+ end
+
+ def puts(line='')
+ @output.puts(line)
+ end
+end
+
+def puts(line='')
+ $preprocessor.puts(line)
+end
+
+def rubypp(input_file, output_file)
+ input = input_file ? File.open(input_file) : $stdin
+ output = output_file ? File.open(output_file, 'w') : $stdout
+
+ success = false
+ begin
+ $preprocessor = Preprocessor.new(input, output, input_file || "(stdin)")
+ $preprocessor.preprocess()
+ success = true
+ ensure
+ if not success then
+ File.unlink(output_file) rescue Errno::ENOENT
+ end
+ end
+end
+
+if __FILE__ == $0 then
+ input_file = ARGV[0]
+ output_file = ARGV[1]
+ rubypp(input_file, output_file)
+end
+
Index: jitruby/lib/jit.rb
===================================================================
RCS file: jitruby/lib/jit.rb
diff -N jitruby/lib/jit.rb
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/lib/jit.rb 12 Dec 2008 11:31:03 -0000 1.1
@@ -0,0 +1,5 @@
+require 'jit.so'
+require 'jit/array'
+require 'jit/function'
+require 'jit/struct'
+require 'jit/value'
Index: jitruby/lib/jit/array.rb
===================================================================
RCS file: jitruby/lib/jit/array.rb
diff -N jitruby/lib/jit/array.rb
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/lib/jit/array.rb 12 Dec 2008 11:31:04 -0000 1.1
@@ -0,0 +1,135 @@
+require 'jit'
+require 'jit/value'
+
+module JIT
+
+ # An abstraction for a fixed-length array type.
+ #
+ # Example usage:
+ #
+ # array_type = JIT::Array.new(JIT::Type::INT, 4)
+ #
+ # JIT::Context.build do |context|
+ # signature = JIT::Type.create_signature(
+ # JIT::ABI::CDECL, JIT::Type::INT, [ ])
+ #
+ # function = JIT::Function.compile(context, signature) do |f|
+ # array_instance = array_type.create(f)
+ # array_instance[0] = f.const(JIT::Type::INT, 42)
+ # f.insn_return(array_instance[0])
+ # end
+ #
+ # end
+ #
+ class Array < JIT::Type
+ attr_reader :type
+ attr_reader :length
+
+ # Create a new JIT array type.
+ #
+ # +type+:: The type of the elements in the array.
+ # +length+:: The number of elements in the array.
+ #
+ def self.new(type, length)
+ array = self.create_struct([ type ] * length)
+ array.instance_eval do
+ @type = type
+ @length = length
+ end
+ return array
+ end
+
+ # Wrap an existing array.
+ #
+ # +ptr+:: A pointer to the first element in the array.
+ #
+ def wrap(ptr)
+ return Instance.wrap(self, ptr)
+ end
+
+ # Create a new array.
+ #
+ # +function+:: The JIT::Function this array will be used in.
+ #
+ def create(function)
+ instance = function.value(self)
+ ptr = function.insn_address_of(instance)
+ return wrap(ptr)
+ end
+
+ # Return the offset (in bytes) of the element at the given +index+.
+ #
+ # +index+:: The index of the desired element.
+ #
+ def offset_of(index)
+ return self.get_offset(index)
+ end
+
+ # Return the type of the element at the given +index+.
+ #
+ # +index+:: The index of the desired element.
+ #
+ def type_of(index)
+ return @type
+ end
+
+ # An abstraction for an instance of a fixed-length array.
+ #
+ class Instance < JIT::Value
+ attr_reader :array_type
+ attr_reader :type
+
+ # A pointer to the first element of the array. Note that this
+ # differs from +address+, which returns the address of a pointer
+ # to the array.
+ attr_reader :ptr
+
+ # TODO: This breaks code below?
+ # attr_reader :function
+
+ # Wrap an existing array.
+ #
+ # +array_type+:: The JIT::Array type to wrap.
+ # +ptr+:: A pointer to the first element in the array.
+ #
+ def self.wrap(array_type, ptr)
+ pointer_type = JIT::Type.create_pointer(array_type)
+ value = self.new_value(ptr.function, pointer_type)
+ value.store(ptr)
+ value.instance_eval do
+ @array_type = array_type
+ @type = array_type.type
+ @function = ptr.function
+ @ptr = ptr
+ end
+ return value
+ end
+
+ # Generate JIT code to retrieve the element at the given +index+.
+ #
+ # +index+:: The index of the desired element. The value of the
+ # index must be known at compile-time.
+ #
+ def [](index)
+ @function.insn_load_relative(
+ @ptr,
+ @array_type.offset_of(index),
+ @array_type.type_of(index))
+ end
+
+ # Generate JIT code to assign to the element at the given +index+.
+ #
+ # +index+:: The index of the desired element. The value of the
+ # index must be known at compile-time.
+ # +value+:: The JIT::Value to assign to the element.
+ #
+ def []=(index, value)
+ @function.insn_store_relative(
+ @ptr,
+ @array_type.offset_of(index),
+ value)
+ end
+ end
+ end
+end
+
Index: jitruby/lib/jit/function.rb
===================================================================
RCS file: jitruby/lib/jit/function.rb
diff -N jitruby/lib/jit/function.rb
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/lib/jit/function.rb 12 Dec 2008 11:31:04 -0000 1.1
@@ -0,0 +1,200 @@
+require 'jit'
+
+module JIT
+ class Function
+ # An abstraction for conditionals.
+ #
+ # Example usage:
+ #
+ # function.if(condition) {
+ # # condition is true
+ # } .elsif(condition2) {
+ # # condition2 is true
+ # } .else {
+ # # condition1 and condition2 are false
+ # } .end
+ #
+ # Caution: if you omit end, then the generated code will have
+ # undefined behavior, but there will be no warning generated.
+ def if(cond, end_label = Label.new, &block)
+ false_label = Label.new
+ insn_branch_if_not(cond, false_label)
+ block.call
+ insn_branch(end_label)
+ insn_label(false_label)
+ return If.new(self, end_label)
+ end
+
+ # An abstraction for an inverted conditional.
+ #
+ # Example usage:
+ #
+ # function.unless(condition) {
+ # # condition is false
+ # } .elsunless(condition2) {
+ # # condition2 is false
+ # } .else {
+ # # condition1 and condition2 are true
+ # } .end
+ #
+ # Caution: if you omit end, then the generated code will have
+ # undefined behavior, but there will be no warning generated.
+ def unless(cond, end_label = Label.new, &block)
+ true_label = Label.new
+ insn_branch_if(cond, true_label)
+ block.call
+ insn_branch(end_label)
+ insn_label(true_label)
+ return If.new(self, end_label)
+ end
+
+ class If # :nodoc:
+ def initialize(function, end_label)
+ @function = function
+ @end_label = end_label
+ end
+
+ def else(&block)
+ block.call
+ return self
+ end
+
+ def elsif(cond, &block)
+ return @function.if(cond, @end_label, &block)
+ end
+
+ def elsunless(cond, &block)
+ return @function.unless(cond, @end_label, &block)
+ end
+
+ def end
+ @function.insn_label(@end_label)
+ end
+ end
+
+ # An abstraction for a multi-way conditional.
+ #
+ # Example usage:
+ #
+ # function.case(value1)
+ # .when(value2) {
+ # # value1 == value2
+ # }.when(value3) {
+ # # value1 == value3
+ # } .else {
+ # # all other cases fell through
+ # } .end
+ #
+ # Caution: if you omit end, then the generated code will have
+ # undefined behavior, but there will be no warning generated.
+ def case(value)
+ return Case.new(self, value)
+ end
+
+ class Case # :nodoc:
+ def initialize(function, value)
+ @function = function
+ @value = value
+ @if = nil
+ end
+
+ def when(value, &block)
+ if not @if then
+ @if = @function.if(value == @value, &block)
+ else
+ @if.elsif(value == @value, &block)
+ end
+ return self
+ end
+
+ def else(&block)
+ @if.else(&block)
+ end
+
+ def end
+ @if.end if @if
+ end
+ end
+
+ # Usage:
+ #
+ # until { <condition> }.do {
+ # # loop body
+ # } .end
+ #
+ def until(&block)
+ start_label = Label.new
+ done_label = Label.new
+ insn_label(start_label)
+ insn_branch_if(block.call, done_label)
+ loop = Loop.new(self, start_label, done_label)
+ return loop
+ end
+
+ # Usage:
+ #
+ # while { <condition> }.do {
+ # # loop body
+ # } .end
+ #
+ def while(&block)
+ start_label = Label.new
+ done_label = Label.new
+ insn_label(start_label)
+ insn_branch_if_not(block.call, done_label)
+ loop = Loop.new(self, start_label, done_label)
+ return loop
+ end
+
+ class Loop # :nodoc:
+ def initialize(function, start_label, done_label)
+ @function = function
+ @start_label = start_label
+ @redo_label = start_label
+ @done_label = done_label
+ end
+
+ def do(&block)
+ block.call(self)
+ return self
+ end
+
+ def end
+ @function.insn_branch(@start_label)
+ @function.insn_label(@done_label)
+ end
+
+ def break
+ @function.insn_branch(@done_label)
+ end
+
+ def redo
+ @function.insn_branch(@redo_label)
+ end
+
+ def redo_from_here
+ @redo_label = JIT::Label.new
+ @function.insn_label(@redo_label)
+ end
+ end
+
+ # An alias for get_param
+ def param(n)
+ self.get_param(n)
+ end
+
+ # An alias for insn_return
+ def return(result)
+ self.insn_return(result)
+ end
+
+ # Create a JIT::Context and compile a new function within that
+ # context.
+ def self.build(*args, &block)
+ JIT::Context.build do |context|
+ JIT::Function.compile(context, *args, &block)
+ end
+ end
+ end
+end
+
Index: jitruby/lib/jit/pointer.rb
===================================================================
RCS file: jitruby/lib/jit/pointer.rb
diff -N jitruby/lib/jit/pointer.rb
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/lib/jit/pointer.rb 12 Dec 2008 11:31:04 -0000 1.1
@@ -0,0 +1,98 @@
+require 'jit'
+require 'jit/value'
+
+module JIT
+
+ # An abstraction for a pointer to a non-void type.
+ #
+ # Example usage:
+ #
+ # TODO
+ #
+ class Pointer < JIT::Type
+ attr_reader :type
+
+ # Create a new JIT pointer type.
+ #
+ # +type+:: The pointed-to type.
+ #
+ def self.new(type)
+ pointer_type = self.create_pointer(type)
+ pointer_type.instance_eval do
+ @type = type
+ end
+ return pointer_type
+ end
+
+ # Wrap an existing void pointer.
+ #
+ # +ptr+:: The pointer to wrap.
+ #
+ def wrap(ptr)
+ return Instance.wrap(self, ptr)
+ end
+
+ # Return the offset (in bytes) of the element at the given +index+.
+ #
+ # +index+:: The index of the desired element.
+ #
+ def offset_of(index)
+ return index * @type.size
+ end
+
+ # Return the type of the element at the given +index+.
+ #
+ # +index+:: The index of the desired element.
+ #
+ def type_of(index)
+ return @type
+ end
+
+ # An abstraction for a pointer object.
+ #
+ class Instance < JIT::Value
+ # Wrap an existing void pointer.
+ #
+ # +array_type+:: The JIT::Array type to wrap.
+ # +ptr+:: A pointer to the first element in the array.
+ #
+ def self.wrap(pointer_type, ptr)
+ value = self.new_value(ptr.function, pointer_type)
+ value.store(ptr)
+ value.instance_eval do
+ @pointer_type = pointer_type
+ @pointed_type = pointer_type.type
+ @function = ptr.function
+ @ptr = ptr
+ end
+ return value
+ end
+
+ # Generate JIT code to retrieve the element at the given +index+.
+ #
+ # +index+:: The index of the desired element. The value of the
+ # index must be known at compile-time.
+ #
+ def [](index)
+ @function.insn_load_relative(
+ @ptr,
+ @pointer_type.offset_of(index),
+ @pointer_type.type_of(index))
+ end
+
+ # Generate JIT code to assign to the element at the given +index+.
+ #
+ # +index+:: The index of the desired element. The value of the
+ # index must be known at compile-time.
+ # +value+:: The JIT::Value to assign to the element.
+ #
+ def []=(index, value)
+ @function.insn_store_relative(
+ @ptr,
+ @pointer_type.offset_of(index),
+ value)
+ end
+ end
+ end
+end
+
Index: jitruby/lib/jit/struct.rb
===================================================================
RCS file: jitruby/lib/jit/struct.rb
diff -N jitruby/lib/jit/struct.rb
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/lib/jit/struct.rb 12 Dec 2008 11:31:04 -0000 1.1
@@ -0,0 +1,158 @@
+require 'jit'
+
+module JIT
+
+ # An abstraction for a record composed of heterogenous fields.
+ #
+ # Example usage:
+ #
+ # point_type = JIT::Struct.new(
+ # [ :x, JIT::Type::INT ],
+ # [ :y, JIT::Type::INT ],
+ # [ :z, JIT::Type::INT ])
+ #
+ # JIT::Context.build do |context|
+ # signature = JIT::Type.create_signature(
+ # JIT::ABI::CDECL, JIT::Type::INT, [ ])
+ #
+ # function = JIT::Function.compile(context, signature) do |f|
+ # point = point_type.create(f)
+ # point.x = function.const(JIT::Type::INT, 1)
+ # point.y = function.const(JIT::Type::INT, 2)
+ # point.z = function.const(JIT::Type::INT, 3)
+ # f.insn_return(point.x)
+ # end
+ #
+ # end
+ #
+ class Struct < JIT::Type
+
+ # Construct a new JIT structure type.
+ #
+ # +members+:: A list of members, where each element in the list is a
+ # two-element array [ :name, type ]
+ #
+ def self.new(*members)
+ member_names = members.map { |m| m[0].to_s.intern }
+ member_types = members.map { |m| m[1] }
+ type = self.create_struct(member_types)
+ type.instance_eval do
+ @members = members
+ @member_names = member_names
+ @member_types = member_types
+ @index = address@hidden((address@hidden).to_a).flatten]
+ end
+ return type
+ end
+
+ # Return the names of the members in the structure.
+ def members
+ return @member_names
+ end
+
+ # Wrap an existing structure.
+ #
+ # +ptr+:: A pointer to the first element in the structure.
+ #
+ def wrap(ptr)
+ return Instance.new(self, ptr)
+ end
+
+ # Create a new structure.
+ #
+ # +function+:: The JIT::Function this structure will be used in.
+ #
+ def create(function)
+ instance = function.value(self)
+ ptr = function.insn_address_of(instance)
+ return wrap(ptr)
+ end
+
+ # Return the offset (in bytes) of the element with the given name.
+ #
+ # +name+:: The name of the desired element.
+ #
+ def offset_of(name)
+ name = (Symbol === name) ? name : name.to_s.intern
+ return self.get_offset(@index[name])
+ end
+
+ # Change the offset of the element with the given name.
+ #
+ # +name+:: The name of the desired element.
+ # +offset+:: The new offset.
+ #
+ def set_offset_of(name, offset)
+ name = (Symbol === name) ? name : name.to_s.intern
+ return self.set_offset(@index[name], offset)
+ end
+
+ # Return the type of the element with the given name.
+ #
+ # +name+:: The name of the desired element.
+ #
+ def type_of(name)
+ name = (Symbol === name) ? name : name.to_s.intern
+ return @address@hidden
+ end
+
+ # An abstraction for an instance of a JIT::Struct.
+ #
+ class Instance
+ attr_reader :ptr
+
+ # Wrap an existing structure.
+ #
+ # +struct+:: The JIT::Struct type to wrap.
+ # +ptr+ A pointer to the first element of the structure.
+ #
+ def initialize(struct, ptr)
+ @struct = struct
+ @function = ptr.function
+ @ptr = ptr
+
+ mod = Module.new do
+ struct.members.each do |name|
+ define_method("#{name}") do
+ self[name] # return
+ end
+
+ define_method("#{name}=") do |value|
+ self[name] = value # return
+ end
+ end
+ end
+
+ extend(mod)
+ end
+
+ # Generate JIT code to retrieve the element with the given name.
+ #
+ # +name+:: The name of the desired element.
+ #
+ def [](name)
+ @function.insn_load_relative(
+ @ptr,
+ @struct.offset_of(name),
+ @struct.type_of(name))
+ end
+
+ # Generate JIT code to assign to the element with the given name.
+ #
+ # +name+:: The name of the desired element.
+ # +value+:: The JIT::Value to assign to the element.
+ #
+ def []=(name, value)
+ @function.insn_store_relative(
+ @ptr,
+ @struct.offset_of(name),
+ value)
+ end
+
+ def members
+ return @struct.members
+ end
+ end
+ end
+end
+
Index: jitruby/lib/jit/value.rb
===================================================================
RCS file: jitruby/lib/jit/value.rb
diff -N jitruby/lib/jit/value.rb
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/lib/jit/value.rb 12 Dec 2008 11:31:04 -0000 1.1
@@ -0,0 +1,199 @@
+require 'jit'
+
+module JIT
+ class Value
+ module UNINITIALIZED; end
+
+ # Create a new JIT::Value. If value is specified, the value will be
+ # variable, otherwise it will be a constant with the given value.
+ #
+ # +function+:: The function to which this value will belong.
+ # +type+:: The type of the new value.
+ # +value+: The value to use, if this is a constant.
+ #
+ def self.new(function, type, value=UNINITIALIZED)
+ # TODO: Not sure if I like this...
+ if value == UNINITIALIZED then
+ return function.value(type)
+ else
+ return function.const(type, value)
+ end
+ end
+
+ # Assign +value+ to this JIT::Value.
+ #
+ # +value+:: The value to assign.
+ #
+ def store(value)
+ lhs, rhs = coerce(value)
+ self.function.insn_store(lhs, rhs)
+ end
+
+ # Return the address of this value.
+ def address
+ return self.function.insn_address_of(self)
+ end
+
+ # Add this value to another and return the result.
+ #
+ # +rhs+:: The right hand side of the addition operator.
+ #
+ def +(rhs)
+ lhs, rhs = coerce(rhs)
+ return self.function.insn_add(lhs, rhs)
+ end
+
+ # Subtract another value from this one and return the result.
+ #
+ # +rhs+:: The right hand side of the subtraction operator.
+ #
+ def -(rhs)
+ lhs, rhs = coerce(rhs)
+ return self.function.insn_sub(lhs, rhs)
+ end
+
+ # Multiply this value by another and return the result.
+ #
+ # +rhs+:: The right hand side of the multiplication operator.
+ #
+ def *(rhs)
+ lhs, rhs = coerce(rhs)
+ return self.function.insn_mul(lhs, rhs)
+ end
+
+ # Divide this value by another and return the quotient.
+ #
+ # +rhs+:: The right hand side of the division operator.
+ #
+ def /(rhs)
+ lhs, rhs = coerce(rhs)
+ return self.function.insn_div(lhs, rhs)
+ end
+
+ # Return the additive inverse (negation) of this value.
+ def -@()
+ rhs, lhs = coerce(0) # inverted, since we are subtracting from 0
+ return lhs - rhs
+ end
+
+ # Divide this value by another and return the remainder.
+ #
+ # +rhs+:: The right hand side of the modulus operator.
+ #
+ def %(rhs)
+ lhs, rhs = coerce(rhs)
+ return self.function.insn_rem(lhs, rhs)
+ end
+
+ # Perform a bitwise and between this value and another.
+ #
+ # +rhs+:: The right hand side of the operator.
+ #
+ def &(rhs)
+ lhs, rhs = coerce(rhs)
+ return self.function.insn_and(lhs, rhs)
+ end
+
+ # Perform a bitwise or between this value and another.
+ #
+ # +rhs+:: The right hand side of the operator.
+ #
+ def |(rhs)
+ lhs, rhs = coerce(rhs)
+ return self.function.insn_or(lhs, rhs)
+ end
+
+ # Perform a bitwise xor between this value and another.
+ #
+ # +rhs+:: The right hand side of the operator.
+ #
+ def ^(rhs)
+ lhs, rhs = coerce(rhs)
+ return self.function.insn_xor(lhs, rhs)
+ end
+
+ # Compare this value to another, returning 1 if the left hand is
+ # less than the right hand side or 0 otherwise.
+ #
+ # +rhs+:: The right hand side of the comparison.
+ #
+ def <(rhs)
+ lhs, rhs = coerce(rhs)
+ return self.function.insn_lt(lhs, rhs)
+ end
+
+ # Compare this value to another, returning 1 if the left hand is
+ # greater than the right hand side or 0 otherwise.
+ #
+ # +rhs+:: The right hand side of the comparison.
+ #
+ def >(rhs)
+ lhs, rhs = coerce(rhs)
+ return self.function.insn_gt(lhs, rhs)
+ end
+
+ # Compare this value to another, returning 1 if the left hand is
+ # equal to the right hand side or 0 otherwise.
+ #
+ # +rhs+:: The right hand side of the comparison.
+ #
+ def ==(rhs)
+ lhs, rhs = coerce(rhs)
+ return self.function.insn_eq(lhs, rhs)
+ end
+
+ # Compare this value to another, returning 1 if the left hand is
+ # not equal to the right hand side or 0 otherwise.
+ #
+ # +rhs+:: The right hand side of the comparison.
+ #
+ def neq(rhs)
+ lhs, rhs = coerce(rhs)
+ return self.function.insn_ne(lhs, rhs)
+ end
+
+ # Compare this value to another, returning 1 if the left hand is
+ # less than or equal to the right hand side or 0 otherwise.
+ #
+ # +rhs+:: The right hand side of the comparison.
+ #
+ def <=(rhs)
+ lhs, rhs = coerce(rhs)
+ return self.function.insn_le(lhs, rhs)
+ end
+
+ # Compare this value to another, returning 1 if the left hand is
+ # greater than or equal to the right hand side or 0 otherwise.
+ #
+ # +rhs+:: The right hand side of the comparison.
+ #
+ def >=(rhs)
+ lhs, rhs = coerce(rhs)
+ return self.function.insn_ge(lhs, rhs)
+ end
+
+ # Shift this value left by the given number of bits.
+ #
+ # +rhs+:: The number of bits to shift by.
+ #
+ def <<(rhs)
+ lhs, rhs = coerce(rhs)
+ return self.function.insn_shl(lhs, rhs)
+ end
+
+ # Shift this value right by the given number of bits.
+ #
+ # +rhs+:: The number of bits to shift by.
+ #
+ def >>(rhs)
+ lhs, rhs = coerce(rhs)
+ return self.function.insn_shr(lhs, rhs)
+ end
+
+ # Return the logical inverse of this value.
+ def ~()
+ return self.function.insn_not(self)
+ end
+ end
+end
+
Index: jitruby/sample/fib.rb
===================================================================
RCS file: jitruby/sample/fib.rb
diff -N jitruby/sample/fib.rb
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/sample/fib.rb 12 Dec 2008 11:31:04 -0000 1.1
@@ -0,0 +1,29 @@
+require 'jit'
+
+fib = nil
+signature = JIT::Type.create_signature(
+ :CDECL,
+ :INT,
+ [ :INT ])
+fib = JIT::Function.build(signature) do |f|
+ n = f.param(0)
+
+ a = f.value(:INT, 0)
+ b = f.value(:INT, 1)
+ c = f.value(:INT, 1)
+
+ i = f.value(:INT, 0)
+
+ f.while{ i < n }.do {
+ c.store(a + b)
+ a.store(b)
+ b.store(c)
+ i.store(i + 1)
+ }.end
+
+ f.return(c)
+end
+
+values = (0...10).collect { |x| fib.apply(x) }
+p values #=> [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
+
Index: jitruby/sample/gcd_benchmark.rb
===================================================================
RCS file: jitruby/sample/gcd_benchmark.rb
diff -N jitruby/sample/gcd_benchmark.rb
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/sample/gcd_benchmark.rb 12 Dec 2008 11:31:04 -0000 1.1
@@ -0,0 +1,117 @@
+require 'jit'
+require 'benchmark'
+
+# GCD, JIT-compiled
+
+jit_gcd = nil
+
+JIT::Context.build do |context|
+ signature = JIT::Type.create_signature(
+ JIT::ABI::CDECL,
+ JIT::Type::INT,
+ [ JIT::Type::INT, JIT::Type::INT ])
+ jit_gcd = JIT::Function.compile(context, signature) do |f|
+ x = f.get_param(0)
+ y = f.get_param(1)
+ temp1 = f.insn_eq(x, y)
+ label1 = JIT::Label.new
+ f.insn_branch_if_not(temp1, label1)
+ f.insn_return(x)
+ f.insn_label(label1)
+ temp2 = f.insn_lt(x, y)
+ label2 = JIT::Label.new
+ f.insn_branch_if_not(temp2, label2)
+ s1 = f.insn_sub(y, x)
+ temp3 = f.insn_call("gcd", f, 0, x, s1)
+ f.insn_return(temp3)
+ f.insn_label(label2)
+ s2 = f.insn_sub(x, y)
+ temp4 = f.insn_call("gcd", f, 0, s2, y)
+ f.insn_return(temp4)
+
+ f.optimization_level = 3
+ end
+end
+
+if jit_gcd.apply(28, 21) != 7 then
+ puts "jit_gcd is broken"
+ exit 1
+end
+
+
+# GCD with tail recursion optimization
+
+jit_gcd_tail = nil
+
+JIT::Context.build do |context|
+ signature = JIT::Type.create_signature(
+ JIT::ABI::CDECL,
+ JIT::Type::INT,
+ [ JIT::Type::INT, JIT::Type::INT ])
+ jit_gcd_tail = JIT::Function.compile(context, signature) do |f|
+ x = f.get_param(0)
+ y = f.get_param(1)
+ temp1 = f.insn_eq(x, y)
+ label1 = JIT::Label.new
+ f.insn_branch_if_not(temp1, label1)
+ f.insn_return(x)
+ f.insn_label(label1)
+ temp2 = f.insn_lt(x, y)
+ label2 = JIT::Label.new
+ f.insn_branch_if_not(temp2, label2)
+ s1 = f.insn_sub(y, x)
+ temp3 = f.insn_call("gcd", f, JIT::Call::TAIL, x, s1)
+ # f.insn_return(temp3)
+ f.insn_label(label2)
+ s2 = f.insn_sub(x, y)
+ temp4 = f.insn_call("gcd", f, JIT::Call::TAIL, s2, x)
+ # f.insn_return(temp4)
+
+ f.optimization_level = 3
+ end
+end
+
+if jit_gcd_tail.apply(28, 21) != 7 then
+ puts "jit_gcd_tail is broken"
+ exit 1
+end
+
+
+# GCD in ruby with recursion
+
+def gcd(x, y)
+ if x == y
+ return x
+ elsif x < y
+ return gcd(x, y - x)
+ else
+ return gcd(x - y, y)
+ end
+end
+
+
+# GCD in ruby without recursion
+
+def gcd2(x, y)
+ while x != y do
+ if x < y
+ y -= x
+ else
+ x -= y
+ end
+ end
+ return x
+end
+
+N = 1000
+
+X = 1000
+Y = 1005
+
+Benchmark.bm(16) do |x|
+ x.report("jit") { N.times { jit_gcd.apply(X, Y) } }
+ x.report("jit tail:") { N.times { jit_gcd_tail.apply(X, Y) } }
+ x.report("ruby recur:") { N.times { gcd(X, Y) } }
+ x.report("ruby iter:") { N.times { gcd2(X, Y) } }
+end
+
Index: jitruby/sample/simple.rb
===================================================================
RCS file: jitruby/sample/simple.rb
diff -N jitruby/sample/simple.rb
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/sample/simple.rb 12 Dec 2008 11:31:04 -0000 1.1
@@ -0,0 +1,15 @@
+require 'jit'
+
+function = nil
+JIT::Context.build do |context|
+ signature = JIT::Type.create_signature(
+ JIT::ABI::CDECL,
+ JIT::Type::INT, # returns an integer
+ [ JIT::Type::INT ]) # and tages an integer as a parameter
+ function = JIT::Function.compile(context, signature) do |f|
+ value = f.get_param(0)
+ f.insn_return(value)
+ end
+end
+
+p function.apply(42) #=> 42
Index: jitruby/test/assertions.rb
===================================================================
RCS file: jitruby/test/assertions.rb
diff -N jitruby/test/assertions.rb
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/test/assertions.rb 12 Dec 2008 11:31:05 -0000 1.1
@@ -0,0 +1,34 @@
+module JitAssertions
+ def assert_function_result(args, &block)
+ result_type = nil
+ expected_result = nil
+ param_types = []
+ params = []
+
+ args.each do |k, v|
+ case k.to_s
+ when /^arg(\d+)$/
+ n = $1.to_i
+ param_types[n] = v[0]
+ params[n] = v[1]
+ when 'result'
+ result_type = v[0]
+ expected_result = v[1]
+ else
+ raise "Bad arg #{arg}"
+ end
+ end
+
+ function = nil
+ JIT::Context.build do |context|
+ signature = JIT::Type.create_signature(
+ JIT::ABI::CDECL,
+ result_type,
+ param_types)
+ function = JIT::Function.compile(context, signature, &block)
+ end
+
+ assert_equal(expected_result, function.apply(*params))
+ end
+end
+
Index: jitruby/test/test_jit_array.rb
===================================================================
RCS file: jitruby/test/test_jit_array.rb
diff -N jitruby/test/test_jit_array.rb
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/test/test_jit_array.rb 12 Dec 2008 11:31:05 -0000 1.1
@@ -0,0 +1,70 @@
+require 'jit/array'
+require 'jit/function'
+require 'test/unit'
+require 'assertions'
+
+class TestJitArray < Test::Unit::TestCase
+ include JitAssertions
+
+ def test_new_array
+ a_type = JIT::Array.new(JIT::Type::INT, 12)
+ assert_equal JIT::Type::INT, a_type.type
+ assert_equal 12, a_type.length
+ end
+
+ # TODO: wrap
+
+ def test_create
+ p = proc { |f|
+ a_type = JIT::Array.new(JIT::Type::INT, 4)
+ a = a_type.create(f)
+ f.return f.const(JIT::Type::INT, 0)
+ }
+ assert_function_result(
+ :result => [ JIT::Type::INT, 0 ],
+ &p)
+ end
+
+ def test_offset_of
+ a_type = JIT::Array.new(JIT::Type::INT, 4)
+ assert_equal 0, a_type.offset_of(0)
+ assert_equal 4, a_type.offset_of(1)
+ assert_equal 8, a_type.offset_of(2)
+ assert_equal 12, a_type.offset_of(3)
+ # TODO: check out of bounds
+ end
+
+ def test_type_of
+ a_type = JIT::Array.new(JIT::Type::INT, 4)
+ assert_equal JIT::Type::INT, a_type.type_of(0)
+ assert_equal JIT::Type::INT, a_type.type_of(1)
+ assert_equal JIT::Type::INT, a_type.type_of(2)
+ assert_equal JIT::Type::INT, a_type.type_of(3)
+ # TODO: check out of bounds
+ end
+
+ def test_instance_bracket
+ p = proc { |f|
+ a_type = JIT::Array.new(JIT::Type::INT, 4)
+ a = a_type.create(f)
+ f.insn_store_relative(a.ptr, 4, f.const(JIT::Type::INT, 42))
+ f.return a[1]
+ }
+ assert_function_result(
+ :result => [ JIT::Type::INT, 42 ],
+ &p)
+ end
+
+ def test_instance_bracket_eq
+ p = proc { |f|
+ a_type = JIT::Array.new(JIT::Type::INT, 4)
+ a = a_type.create(f)
+ a[1] = f.const(JIT::Type::INT, 42)
+ f.return a[1]
+ }
+ assert_function_result(
+ :result => [ JIT::Type::INT, 42 ],
+ &p)
+ end
+end
+
Index: jitruby/test/test_jit_function.rb
===================================================================
RCS file: jitruby/test/test_jit_function.rb
diff -N jitruby/test/test_jit_function.rb
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/test/test_jit_function.rb 12 Dec 2008 11:31:05 -0000 1.1
@@ -0,0 +1,329 @@
+require 'jit/function'
+require 'jit/value'
+require 'test/unit'
+
+class TestJitFunction < Test::Unit::TestCase
+ def test_if_false
+ function = nil
+ JIT::Context.build do |context|
+ signature = JIT::Type.create_signature(
+ JIT::ABI::CDECL,
+ JIT::Type::INT,
+ [ JIT::Type::INT ])
+ function = JIT::Function.compile(context, signature) do |f|
+ result = f.value(JIT::Type::INT)
+ result.store f.const(JIT::Type::INT, 1)
+ f.if(f.get_param(0)) {
+ result.store f.const(JIT::Type::INT, 2)
+ } .end
+ f.insn_return result
+ end
+ end
+
+ assert_equal(1, function.apply(0))
+ end
+
+ def test_if_true
+ function = nil
+ JIT::Context.build do |context|
+ signature = JIT::Type.create_signature(
+ JIT::ABI::CDECL,
+ JIT::Type::INT,
+ [ JIT::Type::INT ])
+ function = JIT::Function.compile(context, signature) do |f|
+ result = f.value(JIT::Type::INT)
+ result.store f.const(JIT::Type::INT, 1)
+ f.if(f.get_param(0)) {
+ result.store f.const(JIT::Type::INT, 2)
+ } .end
+ f.insn_return result
+ end
+ end
+
+ assert_equal(2, function.apply(1))
+ end
+
+ def test_if_false_else
+ function = nil
+ JIT::Context.build do |context|
+ signature = JIT::Type.create_signature(
+ JIT::ABI::CDECL,
+ JIT::Type::INT,
+ [ JIT::Type::INT ])
+ function = JIT::Function.compile(context, signature) do |f|
+ result = f.value(JIT::Type::INT)
+ result.store f.const(JIT::Type::INT, 1)
+ f.if(f.get_param(0)) {
+ result.store f.const(JIT::Type::INT, 2)
+ } .else {
+ result.store f.const(JIT::Type::INT, 3)
+ } .end
+ f.insn_return result
+ end
+ end
+
+ assert_equal(3, function.apply(0))
+ end
+
+ def test_if_true_else
+ function = nil
+ JIT::Context.build do |context|
+ signature = JIT::Type.create_signature(
+ JIT::ABI::CDECL,
+ JIT::Type::INT,
+ [ JIT::Type::INT ])
+ function = JIT::Function.compile(context, signature) do |f|
+ result = f.value(JIT::Type::INT)
+ result.store f.const(JIT::Type::INT, 1)
+ f.if(f.get_param(0)) {
+ result.store f.const(JIT::Type::INT, 2)
+ } .else {
+ result.store f.const(JIT::Type::INT, 3)
+ } .end
+ f.insn_return result
+ end
+ end
+
+ assert_equal(2, function.apply(1))
+ end
+
+ def test_if_false_else_if_true_else
+ function = nil
+ JIT::Context.build do |context|
+ signature = JIT::Type.create_signature(
+ JIT::ABI::CDECL,
+ JIT::Type::INT,
+ [ JIT::Type::INT, JIT::Type::INT ])
+ function = JIT::Function.compile(context, signature) do |f|
+ result = f.value(JIT::Type::INT)
+ result.store f.const(JIT::Type::INT, 1)
+ f.if(f.get_param(0)) {
+ result.store f.const(JIT::Type::INT, 2)
+ } .elsif(f.get_param(1)) {
+ result.store f.const(JIT::Type::INT, 3)
+ } .else {
+ result.store f.const(JIT::Type::INT, 4)
+ } .end
+ f.insn_return result
+ end
+ end
+
+ assert_equal(3, function.apply(0, 1))
+ end
+
+ def test_if_true_else_if_false_else
+ function = nil
+ JIT::Context.build do |context|
+ signature = JIT::Type.create_signature(
+ JIT::ABI::CDECL,
+ JIT::Type::INT,
+ [ JIT::Type::INT, JIT::Type::INT ])
+ function = JIT::Function.compile(context, signature) do |f|
+ result = f.value(JIT::Type::INT)
+ result.store f.const(JIT::Type::INT, 1)
+ f.if(f.get_param(0)) {
+ result.store f.const(JIT::Type::INT, 2)
+ } .elsif(f.get_param(1)) {
+ result.store f.const(JIT::Type::INT, 3)
+ } .else {
+ result.store f.const(JIT::Type::INT, 4)
+ } .end
+ f.insn_return result
+ end
+ end
+
+ assert_equal(2, function.apply(1, 0))
+ end
+
+ def test_if_false_else_if_false_else
+ function = nil
+ JIT::Context.build do |context|
+ signature = JIT::Type.create_signature(
+ JIT::ABI::CDECL,
+ JIT::Type::INT,
+ [ JIT::Type::INT, JIT::Type::INT ])
+ function = JIT::Function.compile(context, signature) do |f|
+ result = f.value(JIT::Type::INT)
+ result.store f.const(JIT::Type::INT, 1)
+ f.if(f.get_param(0)) {
+ result.store f.const(JIT::Type::INT, 2)
+ } .elsif(f.get_param(1)) {
+ result.store f.const(JIT::Type::INT, 3)
+ } .else {
+ result.store f.const(JIT::Type::INT, 4)
+ } .end
+ f.insn_return result
+ end
+ end
+
+ assert_equal(4, function.apply(0, 0))
+ end
+
+ def test_while_true_enters_loop
+ function = nil
+ JIT::Context.build do |context|
+ signature = JIT::Type.create_signature(
+ JIT::ABI::CDECL,
+ JIT::Type::INT,
+ [ ])
+ function = JIT::Function.compile(context, signature) do |f|
+ true_value = f.const(JIT::Type::INT, 1)
+ false_value = f.const(JIT::Type::INT, 0)
+ f.while{ true_value }.do {
+ f.insn_return true_value
+ }.end
+ f.insn_return false_value
+ end
+ end
+
+ assert_equal(1, function.apply)
+ end
+
+ def test_while_true_reenters_loop
+ function = nil
+ JIT::Context.build do |context|
+ signature = JIT::Type.create_signature(
+ JIT::ABI::CDECL,
+ JIT::Type::INT,
+ [ ])
+ function = JIT::Function.compile(context, signature) do |f|
+ value = f.value(JIT::Type::INT)
+ value.store(f.const(JIT::Type::INT, 0))
+ f.while{ value < f.const(JIT::Type::INT, 2) }.do {
+ value.store(value + f.const(JIT::Type::INT, 1))
+ }.end
+ f.insn_return value
+ end
+ end
+
+ assert_equal(2, function.apply)
+ end
+
+ def test_while_false_does_not_enter_loop
+ function = nil
+ JIT::Context.build do |context|
+ signature = JIT::Type.create_signature(
+ JIT::ABI::CDECL,
+ JIT::Type::INT,
+ [ ])
+ function = JIT::Function.compile(context, signature) do |f|
+ true_value = f.const(JIT::Type::INT, 1)
+ false_value = f.const(JIT::Type::INT, 0)
+ f.while{ false_value }.do {
+ f.insn_return true_value
+ }.end
+ f.insn_return false_value
+ end
+ end
+
+ assert_equal(0, function.apply)
+ end
+
+ def test_until_false_enters_loop
+ function = nil
+ JIT::Context.build do |context|
+ signature = JIT::Type.create_signature(
+ JIT::ABI::CDECL,
+ JIT::Type::INT,
+ [ ])
+ function = JIT::Function.compile(context, signature) do |f|
+ true_value = f.const(JIT::Type::INT, 1)
+ false_value = f.const(JIT::Type::INT, 0)
+ f.until{ false_value }.do {
+ f.insn_return true_value
+ }.end
+ f.insn_return false_value
+ end
+ end
+
+ assert_equal(1, function.apply)
+ end
+
+ def test_until_false_reenters_loop
+ function = nil
+ JIT::Context.build do |context|
+ signature = JIT::Type.create_signature(
+ JIT::ABI::CDECL,
+ JIT::Type::INT,
+ [ ])
+ function = JIT::Function.compile(context, signature) do |f|
+ value = f.value(JIT::Type::INT)
+ value.store(f.const(JIT::Type::INT, 0))
+ f.until{ value == f.const(JIT::Type::INT, 2) }.do {
+ value.store(value + f.const(JIT::Type::INT, 1))
+ }.end
+ f.insn_return value
+ end
+ end
+
+ assert_equal(2, function.apply)
+ end
+
+ def test_until_true_does_not_enter_loop
+ function = nil
+ JIT::Context.build do |context|
+ signature = JIT::Type.create_signature(
+ JIT::ABI::CDECL,
+ JIT::Type::INT,
+ [ ])
+ function = JIT::Function.compile(context, signature) do |f|
+ true_value = f.const(JIT::Type::INT, 1)
+ false_value = f.const(JIT::Type::INT, 0)
+ f.until{ true_value }.do {
+ f.insn_return true_value
+ }.end
+ f.insn_return false_value
+ end
+ end
+
+ assert_equal(0, function.apply)
+ end
+
+ # TODO: while/break
+ # TODO: while/redo
+ # TODO: until/break
+ # TODO: until/redo
+ # TODO: unless
+ # TODO: elsunless
+
+ def test_define_jit_method
+ function = nil
+ JIT::Context.build do |context|
+ signature = JIT::Type.create_signature(
+ JIT::ABI::CDECL,
+ JIT::Type::OBJECT,
+ [ JIT::Type::OBJECT ])
+ function = JIT::Function.compile(context, signature) do |f|
+ f.insn_return(f.const(JIT::Type::OBJECT, 42))
+ end
+ end
+
+ c = Class.new
+ c.instance_eval do
+ define_jit_method('foo', function)
+ end
+
+ o = c.new
+ assert_equal 42, o.foo
+ end
+
+ def test_define_jit_method_non_object_param
+ # TODO: should raise an exception
+ end
+
+ # TODO: get_param
+ # TODO: insn_call
+ # TODO: insn_call_native
+ # TODO: insn_return
+ # TODO: apply
+ # TODO: value
+ # TODO: const
+ # TODO: optimization_level
+ # TODO: optimization_level=
+ # TODO: max_optimization_level
+ # TODO: dump
+ # TODO: to_closure
+ # TODO: context
+ # TODO: compiled?
+end
+
Index: jitruby/test/test_jit_pointer.rb
===================================================================
RCS file: jitruby/test/test_jit_pointer.rb
diff -N jitruby/test/test_jit_pointer.rb
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/test/test_jit_pointer.rb 12 Dec 2008 11:31:05 -0000 1.1
@@ -0,0 +1,63 @@
+require 'jit/pointer'
+require 'jit/array'
+require 'jit/function'
+require 'test/unit'
+require 'assertions'
+
+class TestJitArray < Test::Unit::TestCase
+ include JitAssertions
+
+ def test_new_pointer
+ p_type = JIT::Pointer.new(JIT::Type::INT)
+ assert_equal JIT::Type::INT, p_type.type
+ end
+
+ # TODO: wrap
+
+ def test_offset_of
+ p_type = JIT::Pointer.new(JIT::Type::INT)
+ assert_equal 0, p_type.offset_of(0)
+ assert_equal 4, p_type.offset_of(1)
+ assert_equal 8, p_type.offset_of(2)
+ assert_equal 12, p_type.offset_of(3)
+ # TODO: check out of bounds
+ end
+
+ def test_type_of
+ p_type = JIT::Pointer.new(JIT::Type::INT)
+ assert_equal JIT::Type::INT, p_type.type_of(0)
+ assert_equal JIT::Type::INT, p_type.type_of(1)
+ assert_equal JIT::Type::INT, p_type.type_of(2)
+ assert_equal JIT::Type::INT, p_type.type_of(3)
+ # TODO: check out of bounds
+ end
+
+ def test_instance_bracket
+ p = proc { |f|
+ a_type = JIT::Array.new(JIT::Type::INT, 4)
+ p_type = JIT::Pointer.new(JIT::Type::INT)
+ a = a_type.create(f)
+ ptr = p_type.wrap(a.ptr)
+ f.insn_store_relative(a.ptr, 4, f.const(JIT::Type::INT, 42))
+ f.return ptr[1]
+ }
+ assert_function_result(
+ :result => [ JIT::Type::INT, 42 ],
+ &p)
+ end
+
+ def test_instance_bracket_eq
+ p = proc { |f|
+ a_type = JIT::Array.new(JIT::Type::INT, 4)
+ p_type = JIT::Pointer.new(JIT::Type::INT)
+ a = a_type.create(f)
+ ptr = p_type.wrap(a.ptr)
+ ptr[1] = f.const(JIT::Type::INT, 42)
+ f.return a[1]
+ }
+ assert_function_result(
+ :result => [ JIT::Type::INT, 42 ],
+ &p)
+ end
+end
+
Index: jitruby/test/test_jit_struct.rb
===================================================================
RCS file: jitruby/test/test_jit_struct.rb
diff -N jitruby/test/test_jit_struct.rb
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/test/test_jit_struct.rb 12 Dec 2008 11:31:05 -0000 1.1
@@ -0,0 +1,111 @@
+require 'jit/array'
+require 'jit/function'
+require 'test/unit'
+require 'assertions'
+
+class TestJitStruct < Test::Unit::TestCase
+ include JitAssertions
+
+ def test_new_struct
+ s_type = JIT::Struct.new(
+ [ :foo, JIT::Type::INT ],
+ [ :bar, JIT::Type::VOID_PTR ],
+ [ :baz, JIT::Type::FLOAT32 ])
+ assert_equal [ :foo, :bar, :baz ], s_type.members
+ end
+
+ def test_create
+ p = proc { |f|
+ s_type = JIT::Struct.new(
+ [ :foo, JIT::Type::INT ],
+ [ :bar, JIT::Type::VOID_PTR ],
+ [ :baz, JIT::Type::FLOAT32 ])
+ s = s_type.create(f)
+ f.return f.const(JIT::Type::INT, 0)
+ }
+ assert_function_result(
+ :result => [ JIT::Type::INT, 0 ],
+ &p)
+ end
+
+ def test_offset_of
+ s_type = JIT::Struct.new(
+ [ :foo, JIT::Type::INT ],
+ [ :bar, JIT::Type::FLOAT64 ],
+ [ :baz, JIT::Type::VOID_PTR ])
+ assert_equal 0, s_type.offset_of(:foo)
+ assert_equal 4, s_type.offset_of(:bar)
+ assert_equal 12, s_type.offset_of(:baz)
+ end
+
+ def test_type_of
+ s_type = JIT::Struct.new(
+ [ :foo, JIT::Type::INT ],
+ [ :bar, JIT::Type::FLOAT64 ],
+ [ :baz, JIT::Type::VOID_PTR ])
+ assert_equal JIT::Type::INT, s_type.type_of(:foo)
+ assert_equal JIT::Type::FLOAT64, s_type.type_of(:bar)
+ assert_equal JIT::Type::VOID_PTR, s_type.type_of(:baz)
+ end
+
+ def test_instance_bracket
+ p = proc { |f|
+ s_type = JIT::Struct.new(
+ [ :foo, JIT::Type::INT ],
+ [ :bar, JIT::Type::FLOAT64 ],
+ [ :baz, JIT::Type::VOID_PTR ])
+ s = s_type.create(f)
+ f.insn_store_relative(s.ptr, 4, f.const(JIT::Type::FLOAT64, 42.0))
+ f.return s[:bar]
+ }
+ assert_function_result(
+ :result => [ JIT::Type::FLOAT64, 42.0 ],
+ &p)
+ end
+
+ def test_instance_attrget
+ p = proc { |f|
+ s_type = JIT::Struct.new(
+ [ :foo, JIT::Type::INT ],
+ [ :bar, JIT::Type::FLOAT64 ],
+ [ :baz, JIT::Type::VOID_PTR ])
+ s = s_type.create(f)
+ f.insn_store_relative(s.ptr, 4, f.const(JIT::Type::FLOAT64, 42.0))
+ f.return s.bar
+ }
+ assert_function_result(
+ :result => [ JIT::Type::FLOAT64, 42.0 ],
+ &p)
+ end
+
+ def test_instance_bracket_eq
+ p = proc { |f|
+ s_type = JIT::Struct.new(
+ [ :foo, JIT::Type::INT ],
+ [ :bar, JIT::Type::FLOAT64 ],
+ [ :baz, JIT::Type::VOID_PTR ])
+ s = s_type.create(f)
+ s[:bar] = f.const(JIT::Type::FLOAT64, 42.0)
+ f.return s[:bar]
+ }
+ assert_function_result(
+ :result => [ JIT::Type::FLOAT64, 42.0 ],
+ &p)
+ end
+
+ def test_instance_attrset
+ p = proc { |f|
+ s_type = JIT::Struct.new(
+ [ :foo, JIT::Type::INT ],
+ [ :bar, JIT::Type::FLOAT64 ],
+ [ :baz, JIT::Type::VOID_PTR ])
+ s = s_type.create(f)
+ s.bar = f.const(JIT::Type::FLOAT64, 42.0)
+ f.return s.bar
+ }
+ assert_function_result(
+ :result => [ JIT::Type::FLOAT64, 42.0 ],
+ &p)
+ end
+end
+
Index: jitruby/test/test_jit_value.rb
===================================================================
RCS file: jitruby/test/test_jit_value.rb
diff -N jitruby/test/test_jit_value.rb
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ jitruby/test/test_jit_value.rb 12 Dec 2008 11:31:05 -0000 1.1
@@ -0,0 +1,258 @@
+require 'jit/value'
+require 'jit/function'
+require 'test/unit'
+require 'assertions'
+
+class TestJitValue < Test::Unit::TestCase
+ include JitAssertions
+
+ def test_store
+ p = proc { |f|
+ v = f.value(JIT::Type::INT)
+ v.store(f.const(JIT::Type::INT, 42))
+ f.return v
+ }
+ assert_function_result(
+ :result => [ JIT::Type::INT, 42 ],
+ &p)
+ end
+
+ # TODO: address
+
+ def test_int_plus
+ p = proc { |f|
+ v1 = f.get_param(0)
+ v2 = f.get_param(1)
+ f.return v1 + v2
+ }
+ assert_function_result(
+ :arg0 => [ JIT::Type::INT, 1 ],
+ :arg1 => [ JIT::Type::INT, 2 ],
+ :result => [ JIT::Type::INT, 3 ],
+ &p)
+ end
+
+ def test_int_minus
+ p = proc { |f|
+ v1 = f.get_param(0)
+ v2 = f.get_param(1)
+ f.return v1 - v2
+ }
+ assert_function_result(
+ :arg0 => [ JIT::Type::INT, 3 ],
+ :arg1 => [ JIT::Type::INT, 2 ],
+ :result => [ JIT::Type::INT, 1 ],
+ &p)
+ end
+
+ def test_int_mult
+ p = proc { |f|
+ v1 = f.get_param(0)
+ v2 = f.get_param(1)
+ f.return v1 * v2
+ }
+ assert_function_result(
+ :arg0 => [ JIT::Type::INT, 3 ],
+ :arg1 => [ JIT::Type::INT, 2 ],
+ :result => [ JIT::Type::INT, 6 ],
+ &p)
+ end
+
+ def test_int_div
+ p = proc { |f|
+ v1 = f.get_param(0)
+ v2 = f.get_param(1)
+ f.return v1 / v2
+ }
+ assert_function_result(
+ :arg0 => [ JIT::Type::INT, 6 ],
+ :arg1 => [ JIT::Type::INT, 2 ],
+ :result => [ JIT::Type::INT, 3 ],
+ &p)
+ end
+
+ def test_int_mod
+ p = proc { |f|
+ v1 = f.get_param(0)
+ v2 = f.get_param(1)
+ f.return v1 % v2
+ }
+ assert_function_result(
+ :arg0 => [ JIT::Type::INT, 20 ],
+ :arg1 => [ JIT::Type::INT, 6 ],
+ :result => [ JIT::Type::INT, 2 ],
+ &p)
+ end
+
+ def test_int_and
+ p = proc { |f|
+ v1 = f.get_param(0)
+ v2 = f.get_param(1)
+ f.return v1 & v2
+ }
+ assert_function_result(
+ :arg0 => [ JIT::Type::INT, 11 ],
+ :arg1 => [ JIT::Type::INT, 3 ],
+ :result => [ JIT::Type::INT, 3 ],
+ &p)
+ assert_function_result(
+ :arg0 => [ JIT::Type::INT, 8 ],
+ :arg1 => [ JIT::Type::INT, 3 ],
+ :result => [ JIT::Type::INT, 0 ],
+ &p)
+ end
+
+ def test_int_or
+ p = proc { |f|
+ v1 = f.get_param(0)
+ v2 = f.get_param(1)
+ f.return v1 | v2
+ }
+ assert_function_result(
+ :arg0 => [ JIT::Type::INT, 10 ],
+ :arg1 => [ JIT::Type::INT, 3 ],
+ :result => [ JIT::Type::INT, 11 ],
+ &p)
+ end
+
+ def test_int_xor
+ p = proc { |f|
+ v1 = f.get_param(0)
+ v2 = f.get_param(1)
+ f.return v1 ^ v2
+ }
+ assert_function_result(
+ :arg0 => [ JIT::Type::INT, 10 ],
+ :arg1 => [ JIT::Type::INT, 3 ],
+ :result => [ JIT::Type::INT, 9 ],
+ &p)
+ end
+
+ def test_int_lt
+ p = proc { |f|
+ v1 = f.get_param(0)
+ v2 = f.get_param(1)
+ f.return v1 < v2
+ }
+ assert_function_result(
+ :arg0 => [ JIT::Type::INT, 1 ],
+ :arg1 => [ JIT::Type::INT, 2 ],
+ :result => [ JIT::Type::INT, 1 ],
+ &p)
+ assert_function_result(
+ :arg0 => [ JIT::Type::INT, 2 ],
+ :arg1 => [ JIT::Type::INT, 1 ],
+ :result => [ JIT::Type::INT, 0 ],
+ &p)
+ end
+
+ def test_int_gt
+ p = proc { |f|
+ v1 = f.get_param(0)
+ v2 = f.get_param(1)
+ f.return v1 > v2
+ }
+ assert_function_result(
+ :arg0 => [ JIT::Type::INT, 1 ],
+ :arg1 => [ JIT::Type::INT, 2 ],
+ :result => [ JIT::Type::INT, 0 ],
+ &p)
+ assert_function_result(
+ :arg0 => [ JIT::Type::INT, 2 ],
+ :arg1 => [ JIT::Type::INT, 1 ],
+ :result => [ JIT::Type::INT, 1 ],
+ &p)
+ end
+
+ def test_int_eq
+ p = proc { |f|
+ v1 = f.get_param(0)
+ v2 = f.get_param(1)
+ f.return v1 == v2
+ }
+ assert_function_result(
+ :arg0 => [ JIT::Type::INT, 1 ],
+ :arg1 => [ JIT::Type::INT, 2 ],
+ :result => [ JIT::Type::INT, 0 ],
+ &p)
+ assert_function_result(
+ :arg0 => [ JIT::Type::INT, 1 ],
+ :arg1 => [ JIT::Type::INT, 1 ],
+ :result => [ JIT::Type::INT, 1 ],
+ &p)
+ end
+
+ def test_int_le
+ p = proc { |f|
+ v1 = f.get_param(0)
+ v2 = f.get_param(1)
+ f.return v1 <= v2
+ }
+ assert_function_result(
+ :arg0 => [ JIT::Type::INT, 1 ],
+ :arg1 => [ JIT::Type::INT, 2 ],
+ :result => [ JIT::Type::INT, 1 ],
+ &p)
+ assert_function_result(
+ :arg0 => [ JIT::Type::INT, 2 ],
+ :arg1 => [ JIT::Type::INT, 1 ],
+ :result => [ JIT::Type::INT, 0 ],
+ &p)
+ assert_function_result(
+ :arg0 => [ JIT::Type::INT, 1 ],
+ :arg1 => [ JIT::Type::INT, 1 ],
+ :result => [ JIT::Type::INT, 1 ],
+ &p)
+ end
+
+ def test_int_ge
+ p = proc { |f|
+ v1 = f.get_param(0)
+ v2 = f.get_param(1)
+ f.return v1 >= v2
+ }
+ assert_function_result(
+ :arg0 => [ JIT::Type::INT, 1 ],
+ :arg1 => [ JIT::Type::INT, 2 ],
+ :result => [ JIT::Type::INT, 0 ],
+ &p)
+ assert_function_result(
+ :arg0 => [ JIT::Type::INT, 2 ],
+ :arg1 => [ JIT::Type::INT, 1 ],
+ :result => [ JIT::Type::INT, 1 ],
+ &p)
+ assert_function_result(
+ :arg0 => [ JIT::Type::INT, 1 ],
+ :arg1 => [ JIT::Type::INT, 1 ],
+ :result => [ JIT::Type::INT, 1 ],
+ &p)
+ end
+
+ def test_int_lshift
+ p = proc { |f|
+ v1 = f.get_param(0)
+ v2 = f.get_param(1)
+ f.return v1 << v2
+ }
+ assert_function_result(
+ :arg0 => [ JIT::Type::INT, 31 ],
+ :arg1 => [ JIT::Type::INT, 2 ],
+ :result => [ JIT::Type::INT, 124 ],
+ &p)
+ end
+
+ def test_int_rshift
+ p = proc { |f|
+ v1 = f.get_param(0)
+ v2 = f.get_param(1)
+ f.return v1 >> v2
+ }
+ assert_function_result(
+ :arg0 => [ JIT::Type::INT, 31 ],
+ :arg1 => [ JIT::Type::INT, 2 ],
+ :result => [ JIT::Type::INT, 7 ],
+ &p)
+ end
+end
+
+
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [dotgnu-pnet-commits] libjit ChangeLog jitruby/README jitruby/generat...,
Aleksey Demakov <=