llrb 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.gitmodules +4 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +56 -0
- data/README.md +311 -0
- data/Rakefile +30 -0
- data/bin/bm_app_fib +41 -0
- data/bin/bm_empty_method +33 -0
- data/bin/bm_loop_while +27 -0
- data/bin/bm_plus +33 -0
- data/bin/console +14 -0
- data/bin/loop_while.rb +5 -0
- data/bin/setup +8 -0
- data/ext/llrb/cfg.h +124 -0
- data/ext/llrb/compiler.c +987 -0
- data/ext/llrb/compiler/funcs.h +164 -0
- data/ext/llrb/compiler/stack.h +43 -0
- data/ext/llrb/cruby.h +42 -0
- data/ext/llrb/cruby/ccan/build_assert/build_assert.h +40 -0
- data/ext/llrb/cruby/ccan/check_type/check_type.h +63 -0
- data/ext/llrb/cruby/ccan/container_of/container_of.h +142 -0
- data/ext/llrb/cruby/ccan/list/list.h +773 -0
- data/ext/llrb/cruby/ccan/str/str.h +16 -0
- data/ext/llrb/cruby/internal.h +1774 -0
- data/ext/llrb/cruby/iseq.h +252 -0
- data/ext/llrb/cruby/method.h +213 -0
- data/ext/llrb/cruby/node.h +520 -0
- data/ext/llrb/cruby/probes_helper.h +43 -0
- data/ext/llrb/cruby/ruby_assert.h +54 -0
- data/ext/llrb/cruby/ruby_atomic.h +233 -0
- data/ext/llrb/cruby/thread_pthread.h +54 -0
- data/ext/llrb/cruby/vm_core.h +1646 -0
- data/ext/llrb/cruby/vm_debug.h +37 -0
- data/ext/llrb/cruby/vm_exec.h +182 -0
- data/ext/llrb/cruby/vm_opts.h +57 -0
- data/ext/llrb/cruby_extra/id.h +220 -0
- data/ext/llrb/cruby_extra/insns.inc +113 -0
- data/ext/llrb/cruby_extra/insns_info.inc +796 -0
- data/ext/llrb/cruby_extra/probes.h +80 -0
- data/ext/llrb/extconf.rb +102 -0
- data/ext/llrb/llrb.c +148 -0
- data/ext/llrb/optimizer.cc +118 -0
- data/ext/llrb/parser.c +191 -0
- data/ext/llrb/profiler.c +336 -0
- data/ext/llrb_insn_checkkeyword.c +20 -0
- data/ext/llrb_insn_checkmatch.c +28 -0
- data/ext/llrb_insn_concatarray.c +23 -0
- data/ext/llrb_insn_concatstrings.c +21 -0
- data/ext/llrb_insn_defined.c +9 -0
- data/ext/llrb_insn_getclassvariable.c +10 -0
- data/ext/llrb_insn_getinstancevariable.c +44 -0
- data/ext/llrb_insn_getlocal.c +14 -0
- data/ext/llrb_insn_getlocal_level0.c +8 -0
- data/ext/llrb_insn_getlocal_level1.c +8 -0
- data/ext/llrb_insn_getspecial.c +14 -0
- data/ext/llrb_insn_invokeblock.c +39 -0
- data/ext/llrb_insn_invokesuper.c +47 -0
- data/ext/llrb_insn_opt_aref.c +25 -0
- data/ext/llrb_insn_opt_aset.c +28 -0
- data/ext/llrb_insn_opt_div.c +32 -0
- data/ext/llrb_insn_opt_eq.c +57 -0
- data/ext/llrb_insn_opt_ge.c +28 -0
- data/ext/llrb_insn_opt_gt.c +38 -0
- data/ext/llrb_insn_opt_le.c +29 -0
- data/ext/llrb_insn_opt_lt.c +38 -0
- data/ext/llrb_insn_opt_ltlt.c +27 -0
- data/ext/llrb_insn_opt_minus.c +36 -0
- data/ext/llrb_insn_opt_mod.c +32 -0
- data/ext/llrb_insn_opt_mult.c +30 -0
- data/ext/llrb_insn_opt_neq.c +103 -0
- data/ext/llrb_insn_opt_plus.c +48 -0
- data/ext/llrb_insn_opt_send_without_block.c +45 -0
- data/ext/llrb_insn_opt_str_freeze.c +12 -0
- data/ext/llrb_insn_putspecialobject.c +23 -0
- data/ext/llrb_insn_send.c +49 -0
- data/ext/llrb_insn_setclassvariable.c +19 -0
- data/ext/llrb_insn_setconstant.c +23 -0
- data/ext/llrb_insn_setinstancevariable.c +48 -0
- data/ext/llrb_insn_setlocal.c +16 -0
- data/ext/llrb_insn_setlocal_level0.c +9 -0
- data/ext/llrb_insn_setlocal_level1.c +10 -0
- data/ext/llrb_insn_setspecial.c +15 -0
- data/ext/llrb_insn_splatarray.c +13 -0
- data/ext/llrb_insn_throw.c +11 -0
- data/ext/llrb_insn_trace.c +37 -0
- data/ext/llrb_push_result.c +14 -0
- data/ext/llrb_self_from_cfp.c +12 -0
- data/ext/llrb_set_pc.c +8 -0
- data/lib/llrb.rb +2 -0
- data/lib/llrb/jit.rb +76 -0
- data/lib/llrb/start.rb +2 -0
- data/lib/llrb/version.rb +3 -0
- data/llrb.gemspec +48 -0
- data/wercker.yml +31 -0
- metadata +227 -0
@@ -0,0 +1,80 @@
|
|
1
|
+
/* -*- c -*- */
|
2
|
+
|
3
|
+
#ifndef _PROBES_H
|
4
|
+
#define _PROBES_H
|
5
|
+
#define DTRACE_PROBES_DISABLED 1
|
6
|
+
|
7
|
+
#define RUBY_DTRACE_METHOD_ENTRY_ENABLED() 0
|
8
|
+
#define RUBY_DTRACE_METHOD_ENTRY(classname, methodname, filename, lineno) do {} while (0)
|
9
|
+
#define RUBY_DTRACE_METHOD_RETURN_ENABLED() 0
|
10
|
+
#define RUBY_DTRACE_METHOD_RETURN(classname, methodname, filename, lineno) do {} while (0)
|
11
|
+
|
12
|
+
#define RUBY_DTRACE_CMETHOD_ENTRY_ENABLED() 0
|
13
|
+
#define RUBY_DTRACE_CMETHOD_ENTRY(classname, methodname, filename, lineno) do {} while (0)
|
14
|
+
#define RUBY_DTRACE_CMETHOD_RETURN_ENABLED() 0
|
15
|
+
#define RUBY_DTRACE_CMETHOD_RETURN(classname, methodname, filename, lineno) do {} while (0)
|
16
|
+
|
17
|
+
#define RUBY_DTRACE_REQUIRE_ENTRY_ENABLED() 0
|
18
|
+
#define RUBY_DTRACE_REQUIRE_ENTRY(rquiredfile, filename, lineno) do {} while (0)
|
19
|
+
|
20
|
+
#define RUBY_DTRACE_REQUIRE_RETURN_ENABLED() 0
|
21
|
+
#define RUBY_DTRACE_REQUIRE_RETURN(requiredfile, filename, lineno) do {} while (0)
|
22
|
+
|
23
|
+
#define RUBY_DTRACE_FIND_REQUIRE_ENTRY_ENABLED() 0
|
24
|
+
#define RUBY_DTRACE_FIND_REQUIRE_ENTRY(requiredfile, filename, lineno) do {} while (0)
|
25
|
+
|
26
|
+
#define RUBY_DTRACE_FIND_REQUIRE_RETURN_ENABLED() 0
|
27
|
+
#define RUBY_DTRACE_FIND_REQUIRE_RETURN(requiredfile, filename, lineno) do {} while (0)
|
28
|
+
|
29
|
+
#define RUBY_DTRACE_LOAD_ENTRY_ENABLED() 0
|
30
|
+
#define RUBY_DTRACE_LOAD_ENTRY(loadedfile, filename, lineno) do {} while (0)
|
31
|
+
|
32
|
+
#define RUBY_DTRACE_LOAD_RETURN_ENABLED() 0
|
33
|
+
#define RUBY_DTRACE_LOAD_RETURN(loadedfile, filename, lineno) do {} while (0)
|
34
|
+
|
35
|
+
#define RUBY_DTRACE_RAISE_ENABLED() 0
|
36
|
+
#define RUBY_DTRACE_RAISE(classname, filename, lineno) do {} while (0)
|
37
|
+
|
38
|
+
#define RUBY_DTRACE_OBJECT_CREATE_ENABLED() 0
|
39
|
+
#define RUBY_DTRACE_OBJECT_CREATE(classname, filename, lineno) do {} while (0)
|
40
|
+
|
41
|
+
#define RUBY_DTRACE_ARRAY_CREATE_ENABLED() 0
|
42
|
+
#define RUBY_DTRACE_ARRAY_CREATE(length, filename, lineno) do {} while (0)
|
43
|
+
|
44
|
+
#define RUBY_DTRACE_HASH_CREATE_ENABLED() 0
|
45
|
+
#define RUBY_DTRACE_HASH_CREATE(length, filename, lineno) do {} while (0)
|
46
|
+
|
47
|
+
#define RUBY_DTRACE_STRING_CREATE_ENABLED() 0
|
48
|
+
#define RUBY_DTRACE_STRING_CREATE(length, filename, lineno) do {} while (0)
|
49
|
+
|
50
|
+
#define RUBY_DTRACE_SYMBOL_CREATE_ENABLED() 0
|
51
|
+
#define RUBY_DTRACE_SYMBOL_CREATE(str, filename, lineno) do {} while (0)
|
52
|
+
|
53
|
+
#define RUBY_DTRACE_PARSE_BEGIN_ENABLED() 0
|
54
|
+
#define RUBY_DTRACE_PARSE_BEGIN(sourcefile, lineno) do {} while (0)
|
55
|
+
|
56
|
+
#define RUBY_DTRACE_PARSE_END_ENABLED() 0
|
57
|
+
#define RUBY_DTRACE_PARSE_END(sourcefile, lineno) do {} while (0)
|
58
|
+
|
59
|
+
#define RUBY_DTRACE_INSN_ENABLED() 0
|
60
|
+
#define RUBY_DTRACE_INSN(insns_name) do {} while (0)
|
61
|
+
#define RUBY_DTRACE_INSN_OPERAND_ENABLED() 0
|
62
|
+
#define RUBY_DTRACE_INSN_OPERAND(val, insns_name) do {} while (0)
|
63
|
+
|
64
|
+
#define RUBY_DTRACE_GC_MARK_BEGIN_ENABLED() 0
|
65
|
+
#define RUBY_DTRACE_GC_MARK_BEGIN() do {} while (0)
|
66
|
+
|
67
|
+
#define RUBY_DTRACE_GC_MARK_END_ENABLED() 0
|
68
|
+
#define RUBY_DTRACE_GC_MARK_END() do {} while (0)
|
69
|
+
|
70
|
+
#define RUBY_DTRACE_GC_SWEEP_BEGIN_ENABLED() 0
|
71
|
+
#define RUBY_DTRACE_GC_SWEEP_BEGIN() do {} while (0)
|
72
|
+
|
73
|
+
#define RUBY_DTRACE_GC_SWEEP_END_ENABLED() 0
|
74
|
+
#define RUBY_DTRACE_GC_SWEEP_END() do {} while (0)
|
75
|
+
|
76
|
+
#define RUBY_DTRACE_METHOD_CACHE_CLEAR_ENABLED() 0
|
77
|
+
#define RUBY_DTRACE_METHOD_CACHE_CLEAR(class, filename, lineno) do {} while (0)
|
78
|
+
|
79
|
+
#endif /* _PROBES_H */
|
80
|
+
|
data/ext/llrb/extconf.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
|
3
|
+
module LLRBExtconf
|
4
|
+
class << self
|
5
|
+
def configure
|
6
|
+
remove_invalid_warnflags
|
7
|
+
add_cflags
|
8
|
+
link_llvm
|
9
|
+
end
|
10
|
+
|
11
|
+
def compile_bitcodes
|
12
|
+
Dir.chdir(extdir) { Dir.glob('*.c') }.each do |c_file|
|
13
|
+
compile_bitcode(c_file, c_file.sub(/\.c\z/, '.bc'))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def remove_invalid_warnflags
|
20
|
+
# cc1plus: warning: command line option ‘-Wimplicit-int’ is valid for C/ObjC but not for C++
|
21
|
+
# cc1plus: warning: command line option ‘-Wdeclaration-after-statement’ is valid for C/ObjC but not for C++
|
22
|
+
# cc1plus: warning: command line option ‘-Wimplicit-function-declaration’ is valid for C/ObjC but not for C++
|
23
|
+
# cc1plus: warning: unrecognized command line option ‘-Wno-self-assign’
|
24
|
+
# cc1plus: warning: unrecognized command line option ‘-Wno-constant-logical-operand’
|
25
|
+
# cc1plus: warning: unrecognized command line option ‘-Wno-parentheses-equality’
|
26
|
+
# cc1plus: warning: unrecognized command line option ‘-Wno-tautological-compare’
|
27
|
+
%w[
|
28
|
+
-Wimplicit-int
|
29
|
+
-Wdeclaration-after-statement
|
30
|
+
-Wimplicit-function-declaration
|
31
|
+
-Wno-self-assign
|
32
|
+
-Wno-constant-logical-operand
|
33
|
+
-Wno-parentheses-equality
|
34
|
+
-Wno-tautological-compare
|
35
|
+
].each do |flag|
|
36
|
+
CONFIG['warnflags'].gsub!(flag, '')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def add_cflags
|
41
|
+
# To include ccan/*, add ext/llrb/cruby under include path. "cruby_extra" dir has CRuby's dynamic headers.
|
42
|
+
$INCFLAGS = "#{$INCFLAGS} -I$(srcdir)/cruby -I$(srcdir)/cruby_extra"
|
43
|
+
|
44
|
+
bitcode_dir = File.expand_path("#{__dir__}/..")
|
45
|
+
$CFLAGS = "#{$CFLAGS} -DLLRB_BITCODE_DIR='\"#{bitcode_dir}\"' -Wall -Werror -W" # remove -Werror later
|
46
|
+
$CXXFLAGS = "#{$CXXFLAGS} -Wall -Werror -W" # remove -Werror later
|
47
|
+
end
|
48
|
+
|
49
|
+
def link_llvm
|
50
|
+
unless system('which llvm-config 2>&1 >/dev/null')
|
51
|
+
raise "llvm-config(1) must be available!\nNot found in PATH='#{ENV['PATH']}'"
|
52
|
+
end
|
53
|
+
|
54
|
+
$CFLAGS = "#{$CFLAGS} #{`llvm-config --cflags`.rstrip}"
|
55
|
+
$CXXFLAGS = "#{$CXXFLAGS} #{`llvm-config --cxxflags`.rstrip}"
|
56
|
+
$LDFLAGS = "#{$LDFLAGS} #{`llvm-config --ldflags`.rstrip} #{`llvm-config --libs core engine passes`}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def compile_bitcode(c_file, bc_file)
|
60
|
+
unless bc_file.end_with?('.bc')
|
61
|
+
raise ArgumentError, "bitcode file should end with .bc but got '#{bc_file}'"
|
62
|
+
end
|
63
|
+
ll_file = bc_file.sub(/\.bc\z/, '.ll')
|
64
|
+
|
65
|
+
incflags = [
|
66
|
+
__dir__,
|
67
|
+
"#{__dir__}/cruby",
|
68
|
+
"#{__dir__}/cruby_extra",
|
69
|
+
RbConfig::CONFIG['rubyhdrdir'],
|
70
|
+
RbConfig::CONFIG['rubyarchhdrdir'],
|
71
|
+
].map {|d| "-I#{d}" }.join(' ')
|
72
|
+
|
73
|
+
ruby_cflags = RbConfig::CONFIG['cflags'].dup.tap do |flags|
|
74
|
+
%w[
|
75
|
+
-Wno-packed-bitfield-compat
|
76
|
+
-Wsuggest-attribute=noreturn
|
77
|
+
-Wsuggest-attribute=format
|
78
|
+
-Wno-maybe-uninitialized
|
79
|
+
].each { |f| flags.sub!(f, '') } # maybe caused by compiling ruby with gcc and this with clang.
|
80
|
+
end
|
81
|
+
|
82
|
+
debug_flags = "-Xclang -print-stats" if false
|
83
|
+
|
84
|
+
sh "clang #{incflags} #{ruby_cflags} #{debug_flags} -Werror -O2 -S -emit-llvm -o #{extdir}/#{ll_file} #{extdir}/#{c_file}" # remove -Werror later
|
85
|
+
sh "llvm-as -o #{extdir}/#{bc_file} #{extdir}/#{ll_file}"
|
86
|
+
end
|
87
|
+
|
88
|
+
def sh(command)
|
89
|
+
$stderr.puts command
|
90
|
+
system(command) || raise("Failed to execute '#{command}'")
|
91
|
+
end
|
92
|
+
|
93
|
+
def extdir
|
94
|
+
File.expand_path("#{__dir__}/..")
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
LLRBExtconf.compile_bitcodes
|
100
|
+
LLRBExtconf.configure
|
101
|
+
|
102
|
+
create_makefile('llrb/llrb')
|
data/ext/llrb/llrb.c
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
/*
|
2
|
+
* llrb.c: Has Ruby interface and native code generation.
|
3
|
+
*
|
4
|
+
* LLRB's internal design:
|
5
|
+
* parser.c: llrb_parse_iseq() # ISeq -> Control Flow Graph
|
6
|
+
* compiler.c: llrb_compile_cfg() # Control Flow Graph -> LLVM IR
|
7
|
+
* optimizer.cc: llrb_optimize_function() # LLVM IR -> optimized LLVM IR
|
8
|
+
* llrb.c: llrb_create_native_func() # optimized LLVM IR -> Native code
|
9
|
+
*/
|
10
|
+
#include <stdbool.h>
|
11
|
+
#include "llvm-c/Core.h"
|
12
|
+
#include "llvm-c/ExecutionEngine.h"
|
13
|
+
#include "cruby.h"
|
14
|
+
#include "cruby_extra/insns.inc"
|
15
|
+
|
16
|
+
static const char *llrb_funcname = "llrb_exec";
|
17
|
+
|
18
|
+
LLVMModuleRef llrb_compile_iseq(const struct rb_iseq_constant_body *body, const VALUE *new_iseq_encoded, const char* funcname);
|
19
|
+
const rb_iseq_t *rb_iseqw_to_iseq(VALUE iseqw);
|
20
|
+
|
21
|
+
static uint64_t
|
22
|
+
llrb_create_native_func(LLVMModuleRef mod, const char *funcname)
|
23
|
+
{
|
24
|
+
LLVMExecutionEngineRef engine;
|
25
|
+
char *error;
|
26
|
+
if (LLVMCreateJITCompilerForModule(&engine, mod, LLVMCodeGenLevelAggressive, &error) != 0) {
|
27
|
+
fprintf(stderr, "Failed to create JIT compiler...\n");
|
28
|
+
|
29
|
+
if (error) {
|
30
|
+
fprintf(stderr, "LLVMCreateJITCompilerForModule: %s\n", error);
|
31
|
+
LLVMDisposeMessage(error);
|
32
|
+
return 0;
|
33
|
+
}
|
34
|
+
}
|
35
|
+
return LLVMGetFunctionAddress(engine, funcname);
|
36
|
+
}
|
37
|
+
|
38
|
+
static void
|
39
|
+
llrb_replace_iseq_with_cfunc(const rb_iseq_t *iseq, VALUE *new_iseq_encoded, rb_insn_func_t funcptr)
|
40
|
+
{
|
41
|
+
new_iseq_encoded[0] = (VALUE)rb_vm_get_insns_address_table()[YARVINSN_opt_call_c_function];
|
42
|
+
new_iseq_encoded[1] = (VALUE)funcptr;
|
43
|
+
|
44
|
+
// JIT code may change program counter to address after `new_iseq_encoded[2]` to get `catch_table` work.
|
45
|
+
// So filling "leave" insns is necessary here.
|
46
|
+
for (unsigned int i = 2; i < iseq->body->iseq_size; i++) {
|
47
|
+
// Is there a case that last insn is not "leave"?
|
48
|
+
new_iseq_encoded[i] = (VALUE)rb_vm_get_insns_address_table()[YARVINSN_leave];
|
49
|
+
}
|
50
|
+
|
51
|
+
// Changing iseq->body->iseq_encoded will not break threads executing old iseq_encoded
|
52
|
+
// because program counter will still point to old iseq's address. This operation is considered safe.
|
53
|
+
// But in the same time it's hard to free memory of old iseq_encoded.
|
54
|
+
iseq->body->iseq_encoded = new_iseq_encoded;
|
55
|
+
}
|
56
|
+
|
57
|
+
static bool
|
58
|
+
llrb_check_already_compiled(const rb_iseq_t *iseq)
|
59
|
+
{
|
60
|
+
return iseq->body->iseq_size >= 3
|
61
|
+
&& iseq->body->iseq_encoded[0] == (VALUE)rb_vm_get_insns_address_table()[YARVINSN_opt_call_c_function]
|
62
|
+
&& iseq->body->iseq_encoded[2] == (VALUE)rb_vm_get_insns_address_table()[YARVINSN_leave];
|
63
|
+
}
|
64
|
+
|
65
|
+
static bool
|
66
|
+
llrb_should_not_compile(const rb_iseq_t *iseq)
|
67
|
+
{
|
68
|
+
extern bool llrb_check_not_compilable(const rb_iseq_t *iseq);
|
69
|
+
return llrb_check_already_compiled(iseq) || llrb_check_not_compilable(iseq);
|
70
|
+
}
|
71
|
+
|
72
|
+
// LLRB::JIT.preview_iseq
|
73
|
+
// @param [Array] iseqw - RubyVM::InstructionSequence instance
|
74
|
+
// @return [Boolean] return true if compiled
|
75
|
+
static VALUE
|
76
|
+
rb_jit_preview_iseq(RB_UNUSED_VAR(VALUE self), VALUE iseqw)
|
77
|
+
{
|
78
|
+
const rb_iseq_t *iseq = rb_iseqw_to_iseq(iseqw);
|
79
|
+
if (llrb_should_not_compile(iseq)) return Qfalse;
|
80
|
+
|
81
|
+
LLVMModuleRef mod = llrb_compile_iseq(iseq->body, iseq->body->iseq_encoded, llrb_funcname);
|
82
|
+
LLVMDumpModule(mod);
|
83
|
+
LLVMDisposeModule(mod);
|
84
|
+
return Qtrue;
|
85
|
+
}
|
86
|
+
|
87
|
+
// Used by profiler.c too
|
88
|
+
VALUE
|
89
|
+
llrb_compile_iseq_to_method(const rb_iseq_t *iseq)
|
90
|
+
{
|
91
|
+
if (llrb_should_not_compile(iseq)) return Qfalse;
|
92
|
+
|
93
|
+
// Creating new_iseq_encoded before compilation to calculate program counter.
|
94
|
+
VALUE *new_iseq_encoded = ALLOC_N(VALUE, iseq->body->iseq_size); // Never freed.
|
95
|
+
LLVMModuleRef mod = llrb_compile_iseq(iseq->body, new_iseq_encoded, llrb_funcname);
|
96
|
+
|
97
|
+
uint64_t func = llrb_create_native_func(mod, llrb_funcname);
|
98
|
+
//LLVMDisposeModule(mod); // This causes SEGV: "corrupted double-linked list".
|
99
|
+
if (!func) {
|
100
|
+
fprintf(stderr, "Failed to create native function...\n");
|
101
|
+
return Qfalse;
|
102
|
+
}
|
103
|
+
|
104
|
+
llrb_replace_iseq_with_cfunc(iseq, new_iseq_encoded, (rb_insn_func_t)func);
|
105
|
+
return Qtrue;
|
106
|
+
}
|
107
|
+
|
108
|
+
// LLRB::JIT.compile_iseq
|
109
|
+
// @param [Array] iseqw - RubyVM::InstructionSequence instance
|
110
|
+
// @return [Boolean] return true if compiled
|
111
|
+
static VALUE
|
112
|
+
rb_jit_compile_iseq(RB_UNUSED_VAR(VALUE self), VALUE iseqw)
|
113
|
+
{
|
114
|
+
const rb_iseq_t *iseq = rb_iseqw_to_iseq(iseqw);
|
115
|
+
return llrb_compile_iseq_to_method(iseq);
|
116
|
+
}
|
117
|
+
|
118
|
+
static VALUE
|
119
|
+
rb_jit_is_compiled(RB_UNUSED_VAR(VALUE self), VALUE iseqw)
|
120
|
+
{
|
121
|
+
const rb_iseq_t *iseq = rb_iseqw_to_iseq(iseqw);
|
122
|
+
return llrb_check_already_compiled(iseq) ? Qtrue : Qfalse;
|
123
|
+
}
|
124
|
+
|
125
|
+
void
|
126
|
+
Init_llrb(void)
|
127
|
+
{
|
128
|
+
// Required to generate native code.
|
129
|
+
LLVMInitializeNativeTarget();
|
130
|
+
LLVMInitializeNativeAsmPrinter();
|
131
|
+
LLVMInitializeNativeAsmParser();
|
132
|
+
LLVMLinkInMCJIT();
|
133
|
+
|
134
|
+
VALUE rb_mLLRB = rb_define_module("LLRB");
|
135
|
+
VALUE rb_mJIT = rb_define_module_under(rb_mLLRB, "JIT");
|
136
|
+
rb_define_singleton_method(rb_mJIT, "preview_iseq", RUBY_METHOD_FUNC(rb_jit_preview_iseq), 1);
|
137
|
+
rb_define_singleton_method(rb_mJIT, "compile_iseq", RUBY_METHOD_FUNC(rb_jit_compile_iseq), 1);
|
138
|
+
rb_define_singleton_method(rb_mJIT, "is_compiled", RUBY_METHOD_FUNC(rb_jit_is_compiled), 1);
|
139
|
+
|
140
|
+
extern void Init_profiler(VALUE rb_mJIT);
|
141
|
+
Init_profiler(rb_mJIT);
|
142
|
+
|
143
|
+
extern void Init_parser(VALUE rb_mJIT);
|
144
|
+
Init_parser(rb_mJIT);
|
145
|
+
|
146
|
+
extern void Init_compiler(VALUE rb_mJIT);
|
147
|
+
Init_compiler(rb_mJIT);
|
148
|
+
}
|
@@ -0,0 +1,118 @@
|
|
1
|
+
/*
|
2
|
+
* optimizer.cc: Optimizes LLVM IR using LLVM Passes.
|
3
|
+
*
|
4
|
+
* This file is created as C++ file for following 2 reasons:
|
5
|
+
*
|
6
|
+
* 1. LLVM needs to be linked by C++ linker, even if we use LLVM-C API.
|
7
|
+
* C extension is linked using C++ linker if we have *.cc file.
|
8
|
+
* We should find a better way to do that.
|
9
|
+
*
|
10
|
+
* 2. Just to make it easy to port opt(1)'s code. Sometimes LLVM-C APIs
|
11
|
+
* don't include C++'s ones. But it looks current implementation
|
12
|
+
* doesn't depend on such APIs. So this reason can be fixed.
|
13
|
+
*
|
14
|
+
*/
|
15
|
+
#include "llvm/ExecutionEngine/ExecutionEngine.h"
|
16
|
+
#include "llvm/IR/LegacyPassManager.h"
|
17
|
+
#include "llvm/IR/Module.h"
|
18
|
+
#include "llvm/IR/Verifier.h"
|
19
|
+
#include "llvm/MC/SubtargetFeature.h"
|
20
|
+
#include "llvm/Support/CBindingWrapping.h"
|
21
|
+
#include "llvm/Support/Host.h"
|
22
|
+
#include "llvm/Transforms/IPO.h"
|
23
|
+
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
|
24
|
+
#include "llvm/Transforms/Scalar.h"
|
25
|
+
|
26
|
+
namespace llrb {
|
27
|
+
|
28
|
+
static inline std::string GetFeaturesStr()
|
29
|
+
{
|
30
|
+
llvm::SubtargetFeatures ret;
|
31
|
+
llvm::StringMap<bool> features;
|
32
|
+
|
33
|
+
if (llvm::sys::getHostCPUFeatures(features)) {
|
34
|
+
for (auto &feature : features) {
|
35
|
+
ret.AddFeature(feature.first(), feature.second);
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
return ret.getString();
|
40
|
+
}
|
41
|
+
|
42
|
+
// Setting function attributes is required for function inlining.
|
43
|
+
static void
|
44
|
+
SetFunctionAttributes(llvm::Module *mod)
|
45
|
+
{
|
46
|
+
std::string cpu = llvm::sys::getHostCPUName();
|
47
|
+
std::string features = GetFeaturesStr();
|
48
|
+
|
49
|
+
for (auto &func : *mod) {
|
50
|
+
auto &ctx = func.getContext();
|
51
|
+
llvm::AttributeSet attrs = func.getAttributes(), newAttrs;
|
52
|
+
|
53
|
+
if (!cpu.empty()) {
|
54
|
+
newAttrs = newAttrs.addAttribute(ctx, llvm::AttributeSet::FunctionIndex, "target-cpu", cpu);
|
55
|
+
}
|
56
|
+
if (!features.empty()) {
|
57
|
+
newAttrs = newAttrs.addAttribute(ctx, llvm::AttributeSet::FunctionIndex, "target-features", features);
|
58
|
+
}
|
59
|
+
|
60
|
+
newAttrs = attrs.addAttributes(ctx, llvm::AttributeSet::FunctionIndex, newAttrs);
|
61
|
+
func.setAttributes(newAttrs);
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
static void
|
66
|
+
RunFunctionPasses(llvm::Module *mod, llvm::Function *func)
|
67
|
+
{
|
68
|
+
std::unique_ptr<llvm::legacy::FunctionPassManager> fpm;
|
69
|
+
fpm.reset(new llvm::legacy::FunctionPassManager(mod));
|
70
|
+
|
71
|
+
fpm->add(llvm::createVerifierPass());
|
72
|
+
|
73
|
+
llvm::PassManagerBuilder builder;
|
74
|
+
builder.OptLevel = 3;
|
75
|
+
builder.SizeLevel = 0;
|
76
|
+
builder.populateFunctionPassManager(*fpm);
|
77
|
+
|
78
|
+
fpm->doInitialization();
|
79
|
+
for (llvm::Function &f : *mod) {
|
80
|
+
fpm->run(f);
|
81
|
+
}
|
82
|
+
fpm->doFinalization();
|
83
|
+
}
|
84
|
+
|
85
|
+
static void
|
86
|
+
RunModulePasses(llvm::Module *mod)
|
87
|
+
{
|
88
|
+
llvm::legacy::PassManager mpm;
|
89
|
+
|
90
|
+
llvm::PassManagerBuilder builder;
|
91
|
+
builder.OptLevel = 3;
|
92
|
+
builder.SizeLevel = 0;
|
93
|
+
builder.Inliner = llvm::createFunctionInliningPass(builder.OptLevel, builder.SizeLevel);
|
94
|
+
builder.populateModulePassManager(mpm);
|
95
|
+
|
96
|
+
mpm.add(llvm::createVerifierPass());
|
97
|
+
mpm.run(*mod);
|
98
|
+
}
|
99
|
+
|
100
|
+
static void
|
101
|
+
OptimizeFunction(llvm::Module *mod, llvm::Function *func)
|
102
|
+
{
|
103
|
+
SetFunctionAttributes(mod);
|
104
|
+
RunFunctionPasses(mod, func);
|
105
|
+
RunModulePasses(mod);
|
106
|
+
}
|
107
|
+
|
108
|
+
} // namespace llrb
|
109
|
+
|
110
|
+
extern "C" {
|
111
|
+
void
|
112
|
+
llrb_optimize_function(LLVMModuleRef cmod, LLVMValueRef cfunc)
|
113
|
+
{
|
114
|
+
llvm::Module *mod = llvm::unwrap(cmod);
|
115
|
+
llvm::Function *func = llvm::unwrap<llvm::Function>(cfunc);
|
116
|
+
llrb::OptimizeFunction(mod, func);
|
117
|
+
}
|
118
|
+
} // extern "C"
|