llrb 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.gitmodules +4 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +5 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +56 -0
  8. data/README.md +311 -0
  9. data/Rakefile +30 -0
  10. data/bin/bm_app_fib +41 -0
  11. data/bin/bm_empty_method +33 -0
  12. data/bin/bm_loop_while +27 -0
  13. data/bin/bm_plus +33 -0
  14. data/bin/console +14 -0
  15. data/bin/loop_while.rb +5 -0
  16. data/bin/setup +8 -0
  17. data/ext/llrb/cfg.h +124 -0
  18. data/ext/llrb/compiler.c +987 -0
  19. data/ext/llrb/compiler/funcs.h +164 -0
  20. data/ext/llrb/compiler/stack.h +43 -0
  21. data/ext/llrb/cruby.h +42 -0
  22. data/ext/llrb/cruby/ccan/build_assert/build_assert.h +40 -0
  23. data/ext/llrb/cruby/ccan/check_type/check_type.h +63 -0
  24. data/ext/llrb/cruby/ccan/container_of/container_of.h +142 -0
  25. data/ext/llrb/cruby/ccan/list/list.h +773 -0
  26. data/ext/llrb/cruby/ccan/str/str.h +16 -0
  27. data/ext/llrb/cruby/internal.h +1774 -0
  28. data/ext/llrb/cruby/iseq.h +252 -0
  29. data/ext/llrb/cruby/method.h +213 -0
  30. data/ext/llrb/cruby/node.h +520 -0
  31. data/ext/llrb/cruby/probes_helper.h +43 -0
  32. data/ext/llrb/cruby/ruby_assert.h +54 -0
  33. data/ext/llrb/cruby/ruby_atomic.h +233 -0
  34. data/ext/llrb/cruby/thread_pthread.h +54 -0
  35. data/ext/llrb/cruby/vm_core.h +1646 -0
  36. data/ext/llrb/cruby/vm_debug.h +37 -0
  37. data/ext/llrb/cruby/vm_exec.h +182 -0
  38. data/ext/llrb/cruby/vm_opts.h +57 -0
  39. data/ext/llrb/cruby_extra/id.h +220 -0
  40. data/ext/llrb/cruby_extra/insns.inc +113 -0
  41. data/ext/llrb/cruby_extra/insns_info.inc +796 -0
  42. data/ext/llrb/cruby_extra/probes.h +80 -0
  43. data/ext/llrb/extconf.rb +102 -0
  44. data/ext/llrb/llrb.c +148 -0
  45. data/ext/llrb/optimizer.cc +118 -0
  46. data/ext/llrb/parser.c +191 -0
  47. data/ext/llrb/profiler.c +336 -0
  48. data/ext/llrb_insn_checkkeyword.c +20 -0
  49. data/ext/llrb_insn_checkmatch.c +28 -0
  50. data/ext/llrb_insn_concatarray.c +23 -0
  51. data/ext/llrb_insn_concatstrings.c +21 -0
  52. data/ext/llrb_insn_defined.c +9 -0
  53. data/ext/llrb_insn_getclassvariable.c +10 -0
  54. data/ext/llrb_insn_getinstancevariable.c +44 -0
  55. data/ext/llrb_insn_getlocal.c +14 -0
  56. data/ext/llrb_insn_getlocal_level0.c +8 -0
  57. data/ext/llrb_insn_getlocal_level1.c +8 -0
  58. data/ext/llrb_insn_getspecial.c +14 -0
  59. data/ext/llrb_insn_invokeblock.c +39 -0
  60. data/ext/llrb_insn_invokesuper.c +47 -0
  61. data/ext/llrb_insn_opt_aref.c +25 -0
  62. data/ext/llrb_insn_opt_aset.c +28 -0
  63. data/ext/llrb_insn_opt_div.c +32 -0
  64. data/ext/llrb_insn_opt_eq.c +57 -0
  65. data/ext/llrb_insn_opt_ge.c +28 -0
  66. data/ext/llrb_insn_opt_gt.c +38 -0
  67. data/ext/llrb_insn_opt_le.c +29 -0
  68. data/ext/llrb_insn_opt_lt.c +38 -0
  69. data/ext/llrb_insn_opt_ltlt.c +27 -0
  70. data/ext/llrb_insn_opt_minus.c +36 -0
  71. data/ext/llrb_insn_opt_mod.c +32 -0
  72. data/ext/llrb_insn_opt_mult.c +30 -0
  73. data/ext/llrb_insn_opt_neq.c +103 -0
  74. data/ext/llrb_insn_opt_plus.c +48 -0
  75. data/ext/llrb_insn_opt_send_without_block.c +45 -0
  76. data/ext/llrb_insn_opt_str_freeze.c +12 -0
  77. data/ext/llrb_insn_putspecialobject.c +23 -0
  78. data/ext/llrb_insn_send.c +49 -0
  79. data/ext/llrb_insn_setclassvariable.c +19 -0
  80. data/ext/llrb_insn_setconstant.c +23 -0
  81. data/ext/llrb_insn_setinstancevariable.c +48 -0
  82. data/ext/llrb_insn_setlocal.c +16 -0
  83. data/ext/llrb_insn_setlocal_level0.c +9 -0
  84. data/ext/llrb_insn_setlocal_level1.c +10 -0
  85. data/ext/llrb_insn_setspecial.c +15 -0
  86. data/ext/llrb_insn_splatarray.c +13 -0
  87. data/ext/llrb_insn_throw.c +11 -0
  88. data/ext/llrb_insn_trace.c +37 -0
  89. data/ext/llrb_push_result.c +14 -0
  90. data/ext/llrb_self_from_cfp.c +12 -0
  91. data/ext/llrb_set_pc.c +8 -0
  92. data/lib/llrb.rb +2 -0
  93. data/lib/llrb/jit.rb +76 -0
  94. data/lib/llrb/start.rb +2 -0
  95. data/lib/llrb/version.rb +3 -0
  96. data/llrb.gemspec +48 -0
  97. data/wercker.yml +31 -0
  98. 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
+
@@ -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')
@@ -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"