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.
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"