llrb 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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"
|