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
data/bin/bm_app_fib
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
require 'benchmark/ips'
|
|
5
|
+
require 'optparse'
|
|
6
|
+
require 'llrb'
|
|
7
|
+
|
|
8
|
+
preview = false
|
|
9
|
+
opt = OptionParser.new
|
|
10
|
+
opt.on('-p') { preview = true }
|
|
11
|
+
opt.parse!(ARGV)
|
|
12
|
+
|
|
13
|
+
ruby = Class.new
|
|
14
|
+
def ruby.script(n)
|
|
15
|
+
if n < 3
|
|
16
|
+
1
|
|
17
|
+
else
|
|
18
|
+
script(n-1) + script(n-2)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
llrb = Class.new
|
|
23
|
+
def llrb.script(n)
|
|
24
|
+
if n < 3
|
|
25
|
+
1
|
|
26
|
+
else
|
|
27
|
+
script(n-1) + script(n-2)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
LLRB::JIT.preview(llrb, :script) and return if preview
|
|
31
|
+
|
|
32
|
+
started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_microsecond)
|
|
33
|
+
LLRB::JIT.compile(llrb, :script)
|
|
34
|
+
finished_at = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_microsecond)
|
|
35
|
+
puts "Compile Time: #{ "%.2fms" % ((finished_at - started_at) / 1000)}"
|
|
36
|
+
|
|
37
|
+
Benchmark.ips do |x|
|
|
38
|
+
x.report('Ruby') { ruby.script(34) }
|
|
39
|
+
x.report('LLRB') { llrb.script(34) }
|
|
40
|
+
x.compare!
|
|
41
|
+
end
|
data/bin/bm_empty_method
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
require 'benchmark/ips'
|
|
5
|
+
require 'optparse'
|
|
6
|
+
require 'llrb'
|
|
7
|
+
|
|
8
|
+
preview = false
|
|
9
|
+
opt = OptionParser.new
|
|
10
|
+
opt.on('-p') { preview = true }
|
|
11
|
+
opt.parse!(ARGV)
|
|
12
|
+
|
|
13
|
+
ruby = Class.new
|
|
14
|
+
def ruby.script
|
|
15
|
+
1
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
llrb = Class.new
|
|
19
|
+
def llrb.script
|
|
20
|
+
1
|
|
21
|
+
end
|
|
22
|
+
LLRB::JIT.preview(llrb, :script) and return if preview
|
|
23
|
+
|
|
24
|
+
started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_microsecond)
|
|
25
|
+
LLRB::JIT.compile(llrb, :script)
|
|
26
|
+
finished_at = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_microsecond)
|
|
27
|
+
puts "Compile Time: #{ "%.2fms" % ((finished_at - started_at) / 1000)}"
|
|
28
|
+
|
|
29
|
+
Benchmark.ips do |x|
|
|
30
|
+
x.report('Ruby') { ruby.script }
|
|
31
|
+
x.report('LLRB') { llrb.script }
|
|
32
|
+
x.compare!
|
|
33
|
+
end
|
data/bin/bm_loop_while
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
require 'llrb'
|
|
3
|
+
require 'benchmark/ips'
|
|
4
|
+
|
|
5
|
+
ruby = Class.new
|
|
6
|
+
def ruby.script
|
|
7
|
+
i = 0
|
|
8
|
+
while i< 30_000_000
|
|
9
|
+
i += 1
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
llrb = Class.new
|
|
14
|
+
def llrb.script
|
|
15
|
+
i = 0
|
|
16
|
+
while i< 30_000_000
|
|
17
|
+
i += 1
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
LLRB::JIT.compile(llrb, :script)
|
|
22
|
+
|
|
23
|
+
Benchmark.ips do |x|
|
|
24
|
+
x.report('Ruby') { ruby.script }
|
|
25
|
+
x.report('LLRB') { llrb.script }
|
|
26
|
+
x.compare!
|
|
27
|
+
end
|
data/bin/bm_plus
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
require 'benchmark/ips'
|
|
5
|
+
require 'optparse'
|
|
6
|
+
require 'llrb'
|
|
7
|
+
|
|
8
|
+
preview = false
|
|
9
|
+
opt = OptionParser.new
|
|
10
|
+
opt.on('-p') { preview = true }
|
|
11
|
+
opt.parse!(ARGV)
|
|
12
|
+
|
|
13
|
+
ruby = Class.new
|
|
14
|
+
def ruby.script
|
|
15
|
+
1 + 2 + 3 + 4 + 5
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
llrb = Class.new
|
|
19
|
+
def llrb.script
|
|
20
|
+
1 + 2 + 3 + 4 + 5
|
|
21
|
+
end
|
|
22
|
+
LLRB::JIT.preview(llrb, :script) and return if preview
|
|
23
|
+
|
|
24
|
+
started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_microsecond)
|
|
25
|
+
LLRB::JIT.compile(llrb, :script)
|
|
26
|
+
finished_at = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_microsecond)
|
|
27
|
+
puts "Compile Time: #{ "%.2fms" % ((finished_at - started_at) / 1000)}"
|
|
28
|
+
|
|
29
|
+
Benchmark.ips do |x|
|
|
30
|
+
x.report('Ruby') { ruby.script }
|
|
31
|
+
x.report('LLRB') { llrb.script }
|
|
32
|
+
x.compare!
|
|
33
|
+
end
|
data/bin/console
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "bundler/setup"
|
|
4
|
+
require "llrb"
|
|
5
|
+
|
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
8
|
+
|
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
10
|
+
# require "pry"
|
|
11
|
+
# Pry.start
|
|
12
|
+
|
|
13
|
+
require "irb"
|
|
14
|
+
IRB.start(__FILE__)
|
data/bin/loop_while.rb
ADDED
data/bin/setup
ADDED
data/ext/llrb/cfg.h
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* cfg.h: Has a Control Flow Graph struct definition shared by parser.c and compiler.c.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
#ifndef LLRB_CFG_H
|
|
6
|
+
#define LLRB_CFG_H
|
|
7
|
+
|
|
8
|
+
#include <stdbool.h>
|
|
9
|
+
#include "llvm-c/Core.h"
|
|
10
|
+
|
|
11
|
+
struct llrb_basic_block {
|
|
12
|
+
// Fields set by parser:
|
|
13
|
+
unsigned int start; // Start index of ISeq body's iseq_encoded.
|
|
14
|
+
unsigned int end; // End index of ISeq body's iseq_encoded.
|
|
15
|
+
unsigned int incoming_size; // Size of incoming_starts.
|
|
16
|
+
unsigned int *incoming_starts; // Start indices of incoming basic blocks. This buffer is freed by llrb_destruct_cfg.
|
|
17
|
+
bool traversed; // Prevents infinite loop in `llrb_set_incoming_blocks_by` and used by compiler to judge reachable or not.
|
|
18
|
+
|
|
19
|
+
// Fields set by compiler:
|
|
20
|
+
LLVMBasicBlockRef ref; // LLVM's actual BasicBlock reference. This value is always available after `llrb_init_cfg_for_compile` is called.
|
|
21
|
+
LLVMValueRef phi; // Phi node to collect incoming values. This will be created if incoming_size > 1 and compiled time's stack size > 0.
|
|
22
|
+
bool compiled; // Prevents infinite loop in `llrb_compile_basic_block`.
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Holds Control-Flow-Graph-like data structure. Actually it's a buffer of graph nodes.
|
|
26
|
+
struct llrb_cfg {
|
|
27
|
+
struct llrb_basic_block* blocks; // This buffer is freed by llrb_destruct_cfg.
|
|
28
|
+
unsigned int size;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// Used by llrb_dump_cfg.
|
|
32
|
+
#include "cruby.h"
|
|
33
|
+
#include "cruby_extra/insns.inc"
|
|
34
|
+
#include "cruby_extra/insns_info.inc"
|
|
35
|
+
|
|
36
|
+
// Not using `rb_iseq_original_iseq` to avoid unnecessary memory allocation.
|
|
37
|
+
extern int rb_vm_insn_addr2insn(const void *addr);
|
|
38
|
+
|
|
39
|
+
static void
|
|
40
|
+
llrb_disasm_insns(const struct rb_iseq_constant_body *body, unsigned int start, unsigned int end)
|
|
41
|
+
{
|
|
42
|
+
for (unsigned int i = start; i <= end;) {
|
|
43
|
+
int insn = rb_vm_insn_addr2insn((void *)body->iseq_encoded[i]);
|
|
44
|
+
fprintf(stderr, " %04d %-27s [%-4s] ", i, insn_name(insn), insn_op_types(insn));
|
|
45
|
+
|
|
46
|
+
for (int j = 1; j < insn_len(insn); j++) {
|
|
47
|
+
VALUE op = body->iseq_encoded[i+j];
|
|
48
|
+
switch (insn_op_type(insn, j-1)) {
|
|
49
|
+
case TS_NUM:
|
|
50
|
+
fprintf(stderr, "%-4ld ", (rb_num_t)op);
|
|
51
|
+
break;
|
|
52
|
+
case TS_OFFSET:
|
|
53
|
+
fprintf(stderr, "%"PRIdVALUE" ", (VALUE)(i + j + op + 1));
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
fprintf(stderr, "\n");
|
|
58
|
+
i += insn_len(insn);
|
|
59
|
+
}
|
|
60
|
+
fprintf(stderr, "\n");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
static void
|
|
64
|
+
llrb_dump_catch_table(const struct iseq_catch_table *ct)
|
|
65
|
+
{
|
|
66
|
+
if (!ct) return;
|
|
67
|
+
|
|
68
|
+
fprintf(stderr, "-- LLRB: catch table (size=%d)----------------\n", ct->size);
|
|
69
|
+
for (unsigned int i = 0; i < ct->size; i++) {
|
|
70
|
+
const struct iseq_catch_table_entry *entry = &ct->entries[i];
|
|
71
|
+
|
|
72
|
+
switch (entry->type) {
|
|
73
|
+
case CATCH_TYPE_RESCUE:
|
|
74
|
+
fprintf(stderr, "CATCH_TYPE_RESCUE");
|
|
75
|
+
break;
|
|
76
|
+
case CATCH_TYPE_ENSURE:
|
|
77
|
+
fprintf(stderr, "CATCH_TYPE_ENSURE");
|
|
78
|
+
break;
|
|
79
|
+
case CATCH_TYPE_RETRY:
|
|
80
|
+
fprintf(stderr, "CATCH_TYPE_RETRY");
|
|
81
|
+
break;
|
|
82
|
+
case CATCH_TYPE_BREAK:
|
|
83
|
+
fprintf(stderr, "CATCH_TYPE_BREAK");
|
|
84
|
+
break;
|
|
85
|
+
case CATCH_TYPE_REDO:
|
|
86
|
+
fprintf(stderr, "CATCH_TYPE_REDO");
|
|
87
|
+
break;
|
|
88
|
+
case CATCH_TYPE_NEXT:
|
|
89
|
+
fprintf(stderr, "CATCH_TYPE_NEXT");
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
fprintf(stderr, ": start=%d, end=%d, cont=%d, sp=%d, iseq=%lx\n",
|
|
94
|
+
entry->start, entry->end, entry->cont, entry->sp, (VALUE)entry->iseq);
|
|
95
|
+
}
|
|
96
|
+
fprintf(stderr, "\n");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// NOTE: insns(|_info).inc has some static functions, and llrb_dump_cfg uses all those functions.
|
|
100
|
+
// Thus we must call this function somewhere in all included files to pass compilation. `if (0)` is placed for such a purpose.
|
|
101
|
+
static void
|
|
102
|
+
llrb_dump_cfg(const struct rb_iseq_constant_body *body, const struct llrb_cfg *cfg)
|
|
103
|
+
{
|
|
104
|
+
fprintf(stderr, "\n== LLRB: cfg ================================\n");
|
|
105
|
+
for (unsigned int i = 0; i < cfg->size; i++) {
|
|
106
|
+
struct llrb_basic_block *block = cfg->blocks + i;
|
|
107
|
+
fprintf(stderr, "BasicBlock[%d-%d]", block->start, block->end);
|
|
108
|
+
|
|
109
|
+
if (block->incoming_size > 0) fprintf(stderr, " <- ");
|
|
110
|
+
if (!block->traversed) fprintf(stderr, " UNREACHABLE");
|
|
111
|
+
for (unsigned int j = 0; j < block->incoming_size; j++) {
|
|
112
|
+
fprintf(stderr, "%d", block->incoming_starts[j]);
|
|
113
|
+
if (j != block->incoming_size-1) {
|
|
114
|
+
fprintf(stderr, ", ");
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
fprintf(stderr, "\n");
|
|
119
|
+
llrb_disasm_insns(body, block->start, block->end);
|
|
120
|
+
}
|
|
121
|
+
llrb_dump_catch_table(body->catch_table);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
#endif // LLRB_CFG_H
|
data/ext/llrb/compiler.c
ADDED
|
@@ -0,0 +1,987 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* compiler.c: Compiles encoded YARV instructions structured as Control Flow Graph to LLVM IR.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
#include <stdarg.h>
|
|
6
|
+
#include <stdbool.h>
|
|
7
|
+
#include <string.h>
|
|
8
|
+
#include "llvm-c/BitReader.h"
|
|
9
|
+
#include "llvm-c/Core.h"
|
|
10
|
+
#include "cfg.h"
|
|
11
|
+
#include "cruby.h"
|
|
12
|
+
|
|
13
|
+
static VALUE rb_eCompileError;
|
|
14
|
+
#include "compiler/funcs.h"
|
|
15
|
+
#include "compiler/stack.h"
|
|
16
|
+
|
|
17
|
+
// Store compiler's internal state and shared variables
|
|
18
|
+
struct llrb_compiler {
|
|
19
|
+
const struct rb_iseq_constant_body *body;
|
|
20
|
+
const VALUE *new_iseq_encoded; // program counter's base address after this compilation.
|
|
21
|
+
struct llrb_cfg *cfg;
|
|
22
|
+
LLVMValueRef func;
|
|
23
|
+
LLVMBuilderRef builder;
|
|
24
|
+
LLVMModuleRef mod;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
static inline LLVMValueRef
|
|
28
|
+
llrb_value(VALUE value)
|
|
29
|
+
{
|
|
30
|
+
return LLVMConstInt(LLVMInt64Type(), value, false); // TODO: support 32bit for VALUE type
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static inline LLVMValueRef
|
|
34
|
+
llrb_get_thread(const struct llrb_compiler *c)
|
|
35
|
+
{
|
|
36
|
+
return LLVMGetParam(c->func, 0);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static inline LLVMValueRef
|
|
40
|
+
llrb_get_cfp(const struct llrb_compiler *c)
|
|
41
|
+
{
|
|
42
|
+
return LLVMGetParam(c->func, 1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// In base 2, RTEST is: (v != Qfalse && v != Qnil) -> (v != 0000 && v != 1000) -> (v & 0111) != 0000 -> (v & ~Qnil) != 0
|
|
46
|
+
static LLVMValueRef
|
|
47
|
+
llrb_build_rtest(LLVMBuilderRef builder, LLVMValueRef value)
|
|
48
|
+
{
|
|
49
|
+
LLVMValueRef masked = LLVMBuildAnd(builder, value, llrb_value(~Qnil), "RTEST_mask");
|
|
50
|
+
return LLVMBuildICmp(builder, LLVMIntNE, masked, llrb_value(0), "RTEST");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
static LLVMValueRef
|
|
54
|
+
llrb_call_func(const struct llrb_compiler *c, const char *funcname, unsigned argc, ...)
|
|
55
|
+
{
|
|
56
|
+
LLVMValueRef *args = ALLOC_N(LLVMValueRef, argc); // `xfree`d in the end of this function.
|
|
57
|
+
|
|
58
|
+
va_list ar;
|
|
59
|
+
va_start(ar, argc);
|
|
60
|
+
for (unsigned i = 0; i < argc; i++) {
|
|
61
|
+
args[i] = va_arg(ar, LLVMValueRef);
|
|
62
|
+
}
|
|
63
|
+
va_end(ar);
|
|
64
|
+
|
|
65
|
+
LLVMValueRef ret = LLVMBuildCall(c->builder, llrb_get_function(c->mod, funcname), args, argc, "");
|
|
66
|
+
xfree(args);
|
|
67
|
+
return ret;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static inline LLVMValueRef
|
|
71
|
+
llrb_get_self(const struct llrb_compiler *c)
|
|
72
|
+
{
|
|
73
|
+
return llrb_call_func(c, "llrb_self_from_cfp", 1, llrb_get_cfp(c));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
static LLVMValueRef
|
|
77
|
+
llrb_compile_funcall(const struct llrb_compiler *c, struct llrb_stack *stack, ID mid, int argc)
|
|
78
|
+
{
|
|
79
|
+
LLVMValueRef func = llrb_get_function(c->mod, "rb_funcall");
|
|
80
|
+
LLVMValueRef *args = ALLOC_N(LLVMValueRef, 3+argc); // 3 is recv, mid, n
|
|
81
|
+
|
|
82
|
+
for (int i = argc-1; 0 <= i; i--) {
|
|
83
|
+
args[3+i] = llrb_stack_pop(stack); // 3 is recv, mid, n
|
|
84
|
+
}
|
|
85
|
+
args[0] = llrb_stack_pop(stack);
|
|
86
|
+
args[1] = llrb_value(mid);
|
|
87
|
+
args[2] = LLVMConstInt(LLVMInt32Type(), argc, false);
|
|
88
|
+
|
|
89
|
+
return LLVMBuildCall(c->builder, func, args, 3+argc, "rb_funcall");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Must call `llrb_destruct_stack` after usage of return value.
|
|
93
|
+
// TODO: Using `memcpy` would be faster.
|
|
94
|
+
static struct llrb_stack *
|
|
95
|
+
llrb_copy_stack(const struct llrb_stack *stack)
|
|
96
|
+
{
|
|
97
|
+
struct llrb_stack *ret = ALLOC_N(struct llrb_stack, 1); // `xfree`d by `llrb_destruct_stack`.
|
|
98
|
+
ret->size = stack->size;
|
|
99
|
+
ret->max = stack->max;
|
|
100
|
+
ret->body = ALLOC_N(LLVMValueRef, ret->max); // `xfree`d by `llrb_destruct_stack`.
|
|
101
|
+
for (unsigned int i = 0; i < stack->size; i++) {
|
|
102
|
+
ret->body[i] = stack->body[i];
|
|
103
|
+
}
|
|
104
|
+
return ret;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
static void
|
|
108
|
+
llrb_destruct_stack(struct llrb_stack *stack)
|
|
109
|
+
{
|
|
110
|
+
xfree(stack->body);
|
|
111
|
+
xfree(stack);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
static struct llrb_basic_block *
|
|
115
|
+
llrb_find_block(struct llrb_cfg *cfg, unsigned int start)
|
|
116
|
+
{
|
|
117
|
+
for (unsigned int i = 0; i < cfg->size; i++) {
|
|
118
|
+
struct llrb_basic_block *block = cfg->blocks + i;
|
|
119
|
+
if (block->start == start) return block;
|
|
120
|
+
}
|
|
121
|
+
rb_raise(rb_eCompileError, "BasicBlock (start = %d) was not found in llrb_find_block", start);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
static void
|
|
125
|
+
llrb_push_incoming_things(const struct llrb_compiler *c, struct llrb_basic_block *block, LLVMBasicBlockRef current_ref, LLVMValueRef incoming_value)
|
|
126
|
+
{
|
|
127
|
+
// Ensure phi node existence here.
|
|
128
|
+
if (block->phi == 0) {
|
|
129
|
+
// Phi node should be created before its basic block's compilation...
|
|
130
|
+
if (block->compiled) {
|
|
131
|
+
rb_raise(rb_eCompileError, "Already compiled BasicBlock (start = %d) didn't have phi and was requested to push incoming value", block->start);
|
|
132
|
+
}
|
|
133
|
+
LLVMPositionBuilderAtEnd(c->builder, block->ref);
|
|
134
|
+
block->phi = LLVMBuildPhi(c->builder, LLVMInt64Type(), ""); // TODO: Support 32bit
|
|
135
|
+
LLVMPositionBuilderAtEnd(c->builder, current_ref);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
LLVMValueRef values[] = { incoming_value };
|
|
139
|
+
LLVMBasicBlockRef blocks[] = { current_ref };
|
|
140
|
+
LLVMAddIncoming(block->phi, values, blocks, 1);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
static LLVMValueRef
|
|
144
|
+
llrb_compile_newarray(const struct llrb_compiler *c, struct llrb_stack *stack, long num)
|
|
145
|
+
{
|
|
146
|
+
LLVMValueRef *args = ALLOC_N(LLVMValueRef, num+1); // `xfree`d in the end of this function.
|
|
147
|
+
args[0] = LLVMConstInt(LLVMInt64Type(), num, true); // TODO: support 32bit
|
|
148
|
+
for (long i = num; 1 <= i; i--) {
|
|
149
|
+
args[i] = llrb_stack_pop(stack);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
LLVMValueRef func = llrb_get_function(c->mod, "rb_ary_new_from_args");
|
|
153
|
+
LLVMValueRef ret = LLVMBuildCall(c->builder, func, args, num+1, "newarray");
|
|
154
|
+
xfree(args);
|
|
155
|
+
return ret;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// If insn can call any method, it is throwable and needs to change program counter. Or it may rb_raise.
|
|
159
|
+
static bool
|
|
160
|
+
llrb_pc_change_required(const int insn)
|
|
161
|
+
{
|
|
162
|
+
switch (insn) {
|
|
163
|
+
case YARVINSN_tostring:
|
|
164
|
+
case YARVINSN_freezestring:
|
|
165
|
+
case YARVINSN_checkmatch:
|
|
166
|
+
case YARVINSN_send:
|
|
167
|
+
case YARVINSN_opt_str_freeze:
|
|
168
|
+
case YARVINSN_opt_newarray_max:
|
|
169
|
+
case YARVINSN_opt_newarray_min:
|
|
170
|
+
case YARVINSN_opt_send_without_block:
|
|
171
|
+
case YARVINSN_invokesuper:
|
|
172
|
+
case YARVINSN_invokeblock:
|
|
173
|
+
case YARVINSN_leave:
|
|
174
|
+
case YARVINSN_throw:
|
|
175
|
+
case YARVINSN_opt_plus:
|
|
176
|
+
case YARVINSN_opt_minus:
|
|
177
|
+
case YARVINSN_opt_mult:
|
|
178
|
+
case YARVINSN_opt_div:
|
|
179
|
+
case YARVINSN_opt_mod:
|
|
180
|
+
case YARVINSN_opt_eq:
|
|
181
|
+
case YARVINSN_opt_neq:
|
|
182
|
+
case YARVINSN_opt_lt:
|
|
183
|
+
case YARVINSN_opt_le:
|
|
184
|
+
case YARVINSN_opt_gt:
|
|
185
|
+
case YARVINSN_opt_ge:
|
|
186
|
+
case YARVINSN_opt_ltlt:
|
|
187
|
+
case YARVINSN_opt_aref:
|
|
188
|
+
case YARVINSN_opt_aset:
|
|
189
|
+
case YARVINSN_opt_aset_with:
|
|
190
|
+
case YARVINSN_opt_aref_with:
|
|
191
|
+
case YARVINSN_opt_length:
|
|
192
|
+
case YARVINSN_opt_size:
|
|
193
|
+
case YARVINSN_opt_empty_p:
|
|
194
|
+
case YARVINSN_opt_succ:
|
|
195
|
+
case YARVINSN_opt_not:
|
|
196
|
+
case YARVINSN_opt_regexpmatch1:
|
|
197
|
+
case YARVINSN_opt_regexpmatch2:
|
|
198
|
+
case YARVINSN_opt_call_c_function:
|
|
199
|
+
return true;
|
|
200
|
+
default:
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Catch table checks program counter to decide catch it or not. So we need to set program counter before method call or throw insn.
|
|
206
|
+
static void
|
|
207
|
+
llrb_increment_pc(const struct llrb_compiler *c, const unsigned int pos, const int insn)
|
|
208
|
+
{
|
|
209
|
+
if (pos == 0) return; // Skip. 0 would be opt_call_c_function and there's no need to change.
|
|
210
|
+
|
|
211
|
+
if (llrb_pc_change_required(insn)) {
|
|
212
|
+
// This case should be rejected to compile by `llrb_check_not_compilable`.
|
|
213
|
+
if (pos == 1) rb_raise(rb_eCompileError, "program counter is set to 1 from iseq_encoded");
|
|
214
|
+
|
|
215
|
+
// `pos` MUST NOT be 0 (it causes stack level too deep) or 1 (funcptr is considered as insn and it's invalid as insn).
|
|
216
|
+
const VALUE *pc = c->new_iseq_encoded + pos; // This must be `new_iseq_encoded` to get proper `epc` in `vm_exec`.
|
|
217
|
+
llrb_call_func(c, "llrb_set_pc", 2, llrb_get_cfp(c), llrb_value((VALUE)pc));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
static void
|
|
222
|
+
llrb_compile_opt_insn(const struct llrb_compiler *c, struct llrb_stack *stack, const char *name, int argc)
|
|
223
|
+
{
|
|
224
|
+
LLVMValueRef *args = ALLOC_N(LLVMValueRef, argc); // `xfree`d in this function.
|
|
225
|
+
for (int i = argc-1; i >= 0; i--) {
|
|
226
|
+
args[i] = llrb_stack_pop(stack);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
char *funcname = ZALLOC_N(char, strlen("llrb_insn_") + strlen(name) + 1); // `xfree`d in this function.
|
|
230
|
+
strcat(funcname, "llrb_insn_");
|
|
231
|
+
strcat(funcname, name);
|
|
232
|
+
llrb_stack_push(stack, LLVMBuildCall(c->builder, llrb_get_function(c->mod, funcname), args, argc, name));
|
|
233
|
+
|
|
234
|
+
xfree(funcname);
|
|
235
|
+
xfree(args);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
static void llrb_compile_basic_block(const struct llrb_compiler *c, struct llrb_basic_block *block, struct llrb_stack *stack);
|
|
239
|
+
|
|
240
|
+
// opt TODO:
|
|
241
|
+
// YARVINSN_opt_newarray_max:
|
|
242
|
+
// YARVINSN_opt_newarray_min:
|
|
243
|
+
// YARVINSN_opt_aset:
|
|
244
|
+
// YARVINSN_opt_aset_with:
|
|
245
|
+
// YARVINSN_opt_aref_with:
|
|
246
|
+
// YARVINSN_opt_length:
|
|
247
|
+
// YARVINSN_opt_size:
|
|
248
|
+
// YARVINSN_opt_empty_p:
|
|
249
|
+
// YARVINSN_opt_succ:
|
|
250
|
+
// YARVINSN_opt_not:
|
|
251
|
+
// YARVINSN_opt_regexpmatch1:
|
|
252
|
+
// YARVINSN_opt_regexpmatch2:
|
|
253
|
+
|
|
254
|
+
// @param created_br is set true if conditional branch is created. In that case, br for next block isn't created in `llrb_compile_basic_block`.
|
|
255
|
+
// @return true if the IR compiled from given insn includes `ret` instruction. In that case, next block won't be compiled in `llrb_compile_basic_block`.
|
|
256
|
+
static bool
|
|
257
|
+
llrb_compile_insn(const struct llrb_compiler *c, struct llrb_stack *stack, const unsigned int pos, const int insn, const VALUE *operands, bool *created_br)
|
|
258
|
+
{
|
|
259
|
+
llrb_increment_pc(c, pos, insn);
|
|
260
|
+
|
|
261
|
+
//fprintf(stderr, " [DEBUG] llrb_compile_insn: %04d before %-27s (stack size: %d)\n", pos, insn_name(insn), stack->size);
|
|
262
|
+
*created_br = false;
|
|
263
|
+
switch (insn) {
|
|
264
|
+
case YARVINSN_nop:
|
|
265
|
+
break; // nop
|
|
266
|
+
case YARVINSN_getlocal: {
|
|
267
|
+
llrb_stack_push(stack, llrb_call_func(c, "llrb_insn_getlocal", 3, llrb_get_cfp(c),
|
|
268
|
+
llrb_value((lindex_t)operands[0]), llrb_value((rb_num_t)operands[1])));
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
case YARVINSN_setlocal: {
|
|
272
|
+
llrb_call_func(c, "llrb_insn_setlocal", 4, llrb_get_cfp(c),
|
|
273
|
+
llrb_value((lindex_t)operands[0]), llrb_value((rb_num_t)operands[1]), llrb_stack_pop(stack));
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
case YARVINSN_getspecial: {
|
|
277
|
+
llrb_stack_push(stack, llrb_call_func(c, "llrb_insn_getspecial", 2, llrb_value(operands[0]), llrb_value(operands[1])));
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
case YARVINSN_setspecial: {
|
|
281
|
+
llrb_call_func(c, "llrb_insn_setspecial", 2, llrb_value(operands[0]), llrb_stack_pop(stack));
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
case YARVINSN_getinstancevariable:
|
|
285
|
+
llrb_stack_push(stack, llrb_call_func(c, "llrb_insn_getinstancevariable", 3,
|
|
286
|
+
llrb_get_self(c), llrb_value(operands[0]), llrb_value(operands[1])));
|
|
287
|
+
break;
|
|
288
|
+
case YARVINSN_setinstancevariable:
|
|
289
|
+
llrb_call_func(c, "llrb_insn_setinstancevariable", 4, llrb_get_self(c),
|
|
290
|
+
llrb_value(operands[0]), llrb_stack_pop(stack), llrb_value(operands[1]));
|
|
291
|
+
break;
|
|
292
|
+
case YARVINSN_getclassvariable: {
|
|
293
|
+
llrb_stack_push(stack, llrb_call_func(c, "llrb_insn_getclassvariable", 2, llrb_get_cfp(c), llrb_value(operands[0])));
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
case YARVINSN_setclassvariable: {
|
|
297
|
+
llrb_call_func(c, "llrb_insn_setclassvariable", 3, llrb_get_cfp(c), llrb_value(operands[0]), llrb_stack_pop(stack));
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
case YARVINSN_getconstant: {
|
|
301
|
+
llrb_stack_push(stack, llrb_call_func(c, "vm_get_ev_const", 4, llrb_get_thread(c),
|
|
302
|
+
llrb_stack_pop(stack), llrb_value(operands[0]), LLVMConstInt(LLVMInt32Type(), 0, true)));
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
case YARVINSN_setconstant: {
|
|
306
|
+
LLVMValueRef cbase = llrb_stack_pop(stack);
|
|
307
|
+
LLVMValueRef args[] = { llrb_get_self(c), cbase, llrb_value(operands[0]), llrb_stack_pop(stack) };
|
|
308
|
+
LLVMBuildCall(c->builder, llrb_get_function(c->mod, "llrb_insn_setconstant"), args, 4, "");
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
case YARVINSN_getglobal:
|
|
312
|
+
llrb_stack_push(stack, llrb_call_func(c, "rb_gvar_get", 1, llrb_value(operands[0])));
|
|
313
|
+
break;
|
|
314
|
+
case YARVINSN_setglobal:
|
|
315
|
+
llrb_call_func(c, "rb_gvar_set", 2, llrb_value(operands[0]), llrb_stack_pop(stack));
|
|
316
|
+
break;
|
|
317
|
+
case YARVINSN_putnil:
|
|
318
|
+
llrb_stack_push(stack, llrb_value(Qnil));
|
|
319
|
+
break;
|
|
320
|
+
case YARVINSN_putself: {
|
|
321
|
+
llrb_stack_push(stack, llrb_get_self(c));
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
case YARVINSN_putobject:
|
|
325
|
+
llrb_stack_push(stack, llrb_value(operands[0]));
|
|
326
|
+
break;
|
|
327
|
+
case YARVINSN_putspecialobject: {
|
|
328
|
+
llrb_stack_push(stack, llrb_call_func(c, "llrb_insn_putspecialobject", 1, llrb_value(operands[0])));
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
case YARVINSN_putiseq:
|
|
332
|
+
llrb_stack_push(stack, llrb_value(operands[0]));
|
|
333
|
+
break;
|
|
334
|
+
case YARVINSN_putstring: {
|
|
335
|
+
llrb_stack_push(stack, llrb_call_func(c, "rb_str_resurrect", 1, llrb_value(operands[0])));
|
|
336
|
+
break;
|
|
337
|
+
}
|
|
338
|
+
case YARVINSN_concatstrings: {
|
|
339
|
+
LLVMValueRef *args = ALLOC_N(LLVMValueRef, operands[0] + 1); // `xfree`d in this block.
|
|
340
|
+
args[0] = llrb_value(operands[0]); // function is in size_t. correct?
|
|
341
|
+
for (long i = (long)operands[0]-1; 0 <= i; i--) {
|
|
342
|
+
args[1+i] = llrb_stack_pop(stack);
|
|
343
|
+
}
|
|
344
|
+
llrb_stack_push(stack, LLVMBuildCall(c->builder, llrb_get_function(c->mod, "llrb_insn_concatstrings"), args, operands[0] + 1, "concatstrings"));
|
|
345
|
+
xfree(args);
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
case YARVINSN_tostring: {
|
|
349
|
+
llrb_stack_push(stack, llrb_call_func(c, "rb_obj_as_string", 1, llrb_stack_pop(stack)));
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
case YARVINSN_freezestring: {
|
|
353
|
+
VALUE debug_info = operands[0];
|
|
354
|
+
LLVMValueRef str = llrb_stack_pop(stack);
|
|
355
|
+
if (!NIL_P(debug_info)) {
|
|
356
|
+
llrb_call_func(c, "rb_ivar_set", 3, str, llrb_value(id_debug_created_info), llrb_value(debug_info));
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
llrb_call_func(c, "rb_str_freeze", 1, str); // TODO: inline
|
|
360
|
+
llrb_stack_push(stack, str);
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
case YARVINSN_toregexp: {
|
|
364
|
+
rb_num_t cnt = operands[1];
|
|
365
|
+
LLVMValueRef *args1 = ALLOC_N(LLVMValueRef, cnt+1); // `xfree`d in this block.
|
|
366
|
+
args1[0] = LLVMConstInt(LLVMInt64Type(), (long)cnt, true);
|
|
367
|
+
for (rb_num_t i = 0; i < cnt; i++) {
|
|
368
|
+
args1[1+i] = llrb_stack_pop(stack);
|
|
369
|
+
}
|
|
370
|
+
LLVMValueRef ary = LLVMBuildCall(c->builder, llrb_get_function(c->mod, "rb_ary_new_from_args"), args1, 1+cnt, "toregexp");
|
|
371
|
+
xfree(args1);
|
|
372
|
+
|
|
373
|
+
llrb_stack_push(stack, llrb_call_func(c, "rb_reg_new_ary", 2, ary, LLVMConstInt(LLVMInt32Type(), (int)operands[0], true)));
|
|
374
|
+
|
|
375
|
+
llrb_call_func(c, "rb_ary_clear", 1, ary);
|
|
376
|
+
break;
|
|
377
|
+
}
|
|
378
|
+
case YARVINSN_newarray:
|
|
379
|
+
llrb_stack_push(stack, llrb_compile_newarray(c, stack, (long)operands[0]));
|
|
380
|
+
break;
|
|
381
|
+
case YARVINSN_duparray: {
|
|
382
|
+
llrb_stack_push(stack, llrb_call_func(c, "rb_ary_resurrect", 1, llrb_value(operands[0]))); // TODO: inline rb_ary_resurrect?
|
|
383
|
+
break;
|
|
384
|
+
}
|
|
385
|
+
//case YARVINSN_expandarray: {
|
|
386
|
+
// rb_num_t flag = (rb_num_t)operands[1];
|
|
387
|
+
// if (flag & 0x02) { // for postarg
|
|
388
|
+
// } else {
|
|
389
|
+
// }
|
|
390
|
+
// break;
|
|
391
|
+
//}
|
|
392
|
+
case YARVINSN_concatarray: {
|
|
393
|
+
LLVMValueRef ary2st = llrb_stack_pop(stack);
|
|
394
|
+
LLVMValueRef ary1 = llrb_stack_pop(stack);
|
|
395
|
+
llrb_stack_push(stack, llrb_call_func(c, "llrb_insn_concatarray", 2, ary1, ary2st));
|
|
396
|
+
break;
|
|
397
|
+
}
|
|
398
|
+
case YARVINSN_splatarray: {
|
|
399
|
+
llrb_stack_push(stack, llrb_call_func(c, "llrb_insn_splatarray", 2, llrb_stack_pop(stack), llrb_value(operands[0])));
|
|
400
|
+
break;
|
|
401
|
+
}
|
|
402
|
+
case YARVINSN_newhash: {
|
|
403
|
+
LLVMValueRef *values = ALLOC_N(LLVMValueRef, operands[0] / 2);
|
|
404
|
+
LLVMValueRef *keys = ALLOC_N(LLVMValueRef, operands[0] / 2);
|
|
405
|
+
for (int i = 0; i < (int)operands[0] / 2; i++) {
|
|
406
|
+
values[i] = llrb_stack_pop(stack);
|
|
407
|
+
keys[i] = llrb_stack_pop(stack);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
LLVMValueRef result = LLVMBuildCall(c->builder, llrb_get_function(c->mod, "rb_hash_new"), 0, 0, "newhash");
|
|
411
|
+
// reverse set
|
|
412
|
+
for (int i = (int)operands[0] / 2 - 1; 0 <= i; i--) {
|
|
413
|
+
LLVMValueRef args[] = { result, keys[i], values[i] };
|
|
414
|
+
LLVMBuildCall(c->builder, llrb_get_function(c->mod, "rb_hash_aset"), args, 3, "newhash_aset");
|
|
415
|
+
}
|
|
416
|
+
llrb_stack_push(stack, result);
|
|
417
|
+
break;
|
|
418
|
+
}
|
|
419
|
+
case YARVINSN_newrange: {
|
|
420
|
+
LLVMValueRef high = llrb_stack_pop(stack);
|
|
421
|
+
LLVMValueRef low = llrb_stack_pop(stack);
|
|
422
|
+
LLVMValueRef flag = LLVMConstInt(LLVMInt64Type(), operands[0], false);
|
|
423
|
+
llrb_stack_push(stack, llrb_call_func(c, "rb_range_new", 3, low, high, flag));
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
426
|
+
case YARVINSN_pop:
|
|
427
|
+
llrb_stack_pop(stack);
|
|
428
|
+
break;
|
|
429
|
+
case YARVINSN_dup: {
|
|
430
|
+
LLVMValueRef value = llrb_stack_pop(stack);
|
|
431
|
+
llrb_stack_push(stack, value);
|
|
432
|
+
llrb_stack_push(stack, value);
|
|
433
|
+
break;
|
|
434
|
+
}
|
|
435
|
+
case YARVINSN_dupn: {
|
|
436
|
+
LLVMValueRef *values = ALLOC_N(LLVMValueRef, operands[0]); // `xfree`d in this block.
|
|
437
|
+
for (rb_num_t i = 0; i < (rb_num_t)operands[0]; i++) {
|
|
438
|
+
values[i] = llrb_stack_pop(stack); // TODO: obviously no need to pop
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
for (rb_num_t i = 0; i < (rb_num_t)operands[0]; i++) {
|
|
442
|
+
llrb_stack_push(stack, values[operands[0] - 1 - i]);
|
|
443
|
+
}
|
|
444
|
+
for (rb_num_t i = 0; i < (rb_num_t)operands[0]; i++) {
|
|
445
|
+
llrb_stack_push(stack, values[operands[0] - 1 - i]);
|
|
446
|
+
}
|
|
447
|
+
xfree(values);
|
|
448
|
+
break;
|
|
449
|
+
}
|
|
450
|
+
case YARVINSN_swap: {
|
|
451
|
+
LLVMValueRef first = llrb_stack_pop(stack);
|
|
452
|
+
LLVMValueRef second = llrb_stack_pop(stack);
|
|
453
|
+
llrb_stack_push(stack, first);
|
|
454
|
+
llrb_stack_push(stack, second);
|
|
455
|
+
break;
|
|
456
|
+
}
|
|
457
|
+
//case YARVINSN_reverse: {
|
|
458
|
+
// rb_num_t n = (rb_num_t)operands[0];
|
|
459
|
+
// unsigned int last = stack->size - 1;
|
|
460
|
+
// unsigned int top_i = last - n;
|
|
461
|
+
|
|
462
|
+
// for (rb_num_t i = 0; i < n/2; i++) {
|
|
463
|
+
// LLVMValueRef v0 = stack->body[top_i+i];
|
|
464
|
+
// LLVMValueRef v1 = stack->body[last-i];
|
|
465
|
+
// stack->body[top_i+i] = v1;
|
|
466
|
+
// stack->body[last-i] = v0;
|
|
467
|
+
// }
|
|
468
|
+
// break;
|
|
469
|
+
//}
|
|
470
|
+
//case YARVINSN_reput:
|
|
471
|
+
// break; // none
|
|
472
|
+
case YARVINSN_topn: {
|
|
473
|
+
llrb_stack_push(stack, llrb_stack_topn(stack, (unsigned int)operands[0]));
|
|
474
|
+
break;
|
|
475
|
+
}
|
|
476
|
+
case YARVINSN_setn: {
|
|
477
|
+
rb_num_t last = (rb_num_t)stack->size - 1;
|
|
478
|
+
stack->body[last - (rb_num_t)operands[0]] = stack->body[last];
|
|
479
|
+
break;
|
|
480
|
+
}
|
|
481
|
+
case YARVINSN_adjuststack: {
|
|
482
|
+
for (rb_num_t i = 0; i < (rb_num_t)operands[0]; i++) {
|
|
483
|
+
llrb_stack_pop(stack);
|
|
484
|
+
}
|
|
485
|
+
break;
|
|
486
|
+
}
|
|
487
|
+
case YARVINSN_defined: {
|
|
488
|
+
llrb_stack_push(stack, llrb_call_func(c, "llrb_insn_defined", 4, llrb_value(operands[0]),
|
|
489
|
+
llrb_value(operands[1]), llrb_value(operands[2]), llrb_stack_pop(stack)));
|
|
490
|
+
break;
|
|
491
|
+
}
|
|
492
|
+
case YARVINSN_checkmatch: {
|
|
493
|
+
LLVMValueRef pattern = llrb_stack_pop(stack);
|
|
494
|
+
LLVMValueRef target =llrb_stack_pop(stack);
|
|
495
|
+
LLVMValueRef flag = LLVMConstInt(LLVMInt64Type(), operands[0], false);
|
|
496
|
+
llrb_stack_push(stack, llrb_call_func(c, "llrb_insn_checkmatch", 3, target, pattern, flag));
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
case YARVINSN_checkkeyword: {
|
|
500
|
+
llrb_stack_push(stack, llrb_call_func(c, "llrb_insn_checkkeyword", 3, llrb_get_cfp(c),
|
|
501
|
+
llrb_value((lindex_t)operands[0]), llrb_value((rb_num_t)operands[1])));
|
|
502
|
+
break;
|
|
503
|
+
}
|
|
504
|
+
case YARVINSN_trace: {
|
|
505
|
+
rb_event_flag_t flag = (rb_event_flag_t)((rb_num_t)operands[0]);
|
|
506
|
+
LLVMValueRef val = (flag & (RUBY_EVENT_RETURN | RUBY_EVENT_B_RETURN)) ? stack->body[stack->size-1] : llrb_value(Qundef);
|
|
507
|
+
llrb_call_func(c, "llrb_insn_trace", 4, llrb_get_thread(c), llrb_get_cfp(c), LLVMConstInt(LLVMInt32Type(), flag, false), val);
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
//case YARVINSN_defineclass: {
|
|
511
|
+
// ;
|
|
512
|
+
// break;
|
|
513
|
+
//}
|
|
514
|
+
case YARVINSN_send: {
|
|
515
|
+
CALL_INFO ci = (CALL_INFO)operands[0];
|
|
516
|
+
unsigned int stack_size = ci->orig_argc + 1;
|
|
517
|
+
if (ci->flag & VM_CALL_ARGS_BLOCKARG) stack_size++; // push `&block`
|
|
518
|
+
|
|
519
|
+
unsigned int arg_size = 6 + stack_size;
|
|
520
|
+
LLVMValueRef *args = ALLOC_N(LLVMValueRef, arg_size);
|
|
521
|
+
args[0] = llrb_get_thread(c);
|
|
522
|
+
args[1] = llrb_get_cfp(c);
|
|
523
|
+
args[2] = llrb_value((VALUE)ci);
|
|
524
|
+
args[3] = llrb_value((VALUE)((CALL_CACHE)operands[1]));
|
|
525
|
+
args[4] = llrb_value((VALUE)((ISEQ)operands[2]));
|
|
526
|
+
args[5] = LLVMConstInt(LLVMInt32Type(), stack_size, false);
|
|
527
|
+
for (int i = (int)stack_size - 1; 0 <= i; i--) { // recv + argc
|
|
528
|
+
args[6 + i] = llrb_stack_pop(stack);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
llrb_stack_push(stack, LLVMBuildCall(c->builder, llrb_get_function(c->mod, "llrb_insn_send"), args, arg_size, "send"));
|
|
532
|
+
break;
|
|
533
|
+
}
|
|
534
|
+
case YARVINSN_opt_str_freeze: { // TODO: optimize
|
|
535
|
+
llrb_stack_push(stack, llrb_value(operands[0]));
|
|
536
|
+
llrb_compile_opt_insn(c, stack, "opt_str_freeze", 1);
|
|
537
|
+
break;
|
|
538
|
+
}
|
|
539
|
+
case YARVINSN_opt_newarray_max: // TODO: optimize
|
|
540
|
+
llrb_stack_push(stack, llrb_compile_newarray(c, stack, (long)operands[0]));
|
|
541
|
+
llrb_stack_push(stack, llrb_compile_funcall(c, stack, rb_intern("max"), 0));
|
|
542
|
+
break;
|
|
543
|
+
case YARVINSN_opt_newarray_min: // TODO: optimize
|
|
544
|
+
llrb_stack_push(stack, llrb_compile_newarray(c, stack, (long)operands[0]));
|
|
545
|
+
llrb_stack_push(stack, llrb_compile_funcall(c, stack, rb_intern("min"), 0));
|
|
546
|
+
break;
|
|
547
|
+
case YARVINSN_opt_send_without_block: {
|
|
548
|
+
CALL_INFO ci = (CALL_INFO)operands[0];
|
|
549
|
+
unsigned int stack_size = ci->orig_argc + 1;
|
|
550
|
+
|
|
551
|
+
LLVMValueRef *args = ALLOC_N(LLVMValueRef, 5 + stack_size); // `xfree`d in this block.
|
|
552
|
+
args[0] = llrb_get_thread(c);
|
|
553
|
+
args[1] = llrb_get_cfp(c);
|
|
554
|
+
args[2] = llrb_value((VALUE)ci);
|
|
555
|
+
args[3] = llrb_value((VALUE)((CALL_CACHE)operands[1]));
|
|
556
|
+
args[4] = LLVMConstInt(LLVMInt32Type(), stack_size, false);
|
|
557
|
+
for (int i = (int)stack_size - 1; 0 <= i; i--) { // recv + argc
|
|
558
|
+
args[5 + i] = llrb_stack_pop(stack);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
LLVMValueRef func = llrb_get_function(c->mod, "llrb_insn_opt_send_without_block");
|
|
562
|
+
llrb_stack_push(stack, LLVMBuildCall(c->builder, func, args, 5 + stack_size, "opt_send_without_block"));
|
|
563
|
+
xfree(args);
|
|
564
|
+
break;
|
|
565
|
+
}
|
|
566
|
+
case YARVINSN_invokesuper: { // TODO: refactor with opt_send_without_block
|
|
567
|
+
CALL_INFO ci = (CALL_INFO)operands[0];
|
|
568
|
+
unsigned int stack_size = ci->orig_argc + 1;
|
|
569
|
+
|
|
570
|
+
LLVMValueRef *args = ALLOC_N(LLVMValueRef, 5 + stack_size);
|
|
571
|
+
args[0] = llrb_get_thread(c);
|
|
572
|
+
args[1] = llrb_get_cfp(c);
|
|
573
|
+
args[2] = llrb_value((VALUE)ci);
|
|
574
|
+
args[3] = llrb_value((VALUE)((CALL_CACHE)operands[1]));
|
|
575
|
+
args[4] = llrb_value((VALUE)((ISEQ)operands[2]));
|
|
576
|
+
args[5] = LLVMConstInt(LLVMInt32Type(), stack_size, false);
|
|
577
|
+
for (int i = (int)stack_size - 1; 0 <= i; i--) { // recv + argc
|
|
578
|
+
args[6 + i] = llrb_stack_pop(stack);
|
|
579
|
+
}
|
|
580
|
+
llrb_stack_push(stack, LLVMBuildCall(c->builder, llrb_get_function(c->mod, "llrb_insn_invokesuper"), args, 6 + stack_size, "invokesuper"));
|
|
581
|
+
break;
|
|
582
|
+
}
|
|
583
|
+
case YARVINSN_invokeblock: {
|
|
584
|
+
CALL_INFO ci = (CALL_INFO)operands[0];
|
|
585
|
+
unsigned int stack_size = ci->orig_argc;
|
|
586
|
+
|
|
587
|
+
LLVMValueRef *args = ALLOC_N(LLVMValueRef, 4 + stack_size);
|
|
588
|
+
args[0] = llrb_get_thread(c);
|
|
589
|
+
args[1] = llrb_get_cfp(c);
|
|
590
|
+
args[2] = llrb_value((VALUE)ci);
|
|
591
|
+
args[3] = LLVMConstInt(LLVMInt32Type(), stack_size, false);
|
|
592
|
+
for (int i = (int)stack_size - 1; 0 <= i; i--) { // recv + argc
|
|
593
|
+
args[4 + i] = llrb_stack_pop(stack);
|
|
594
|
+
}
|
|
595
|
+
llrb_stack_push(stack, LLVMBuildCall(c->builder, llrb_get_function(c->mod, "llrb_insn_invokeblock"), args, 4 + stack_size, "invokeblock"));
|
|
596
|
+
break;
|
|
597
|
+
}
|
|
598
|
+
case YARVINSN_leave:
|
|
599
|
+
if (stack->size != 1) {
|
|
600
|
+
llrb_dump_cfg(c->body, c->cfg);
|
|
601
|
+
rb_raise(rb_eCompileError, "unexpected stack size at leave: %d", stack->size);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
llrb_call_func(c, "llrb_push_result", 2, llrb_get_cfp(c), llrb_stack_pop(stack));
|
|
605
|
+
LLVMBuildRet(c->builder, llrb_get_cfp(c));
|
|
606
|
+
return true;
|
|
607
|
+
case YARVINSN_throw: {
|
|
608
|
+
llrb_call_func(c, "llrb_insn_throw", 4, llrb_get_thread(c), llrb_get_cfp(c),
|
|
609
|
+
llrb_value((rb_num_t)operands[0]), llrb_stack_pop(stack));
|
|
610
|
+
|
|
611
|
+
// In opt_call_c_function, if we return 0, we can throw error fron th->errinfo.
|
|
612
|
+
// https://github.com/ruby/ruby/blob/v2_4_1/insns.def#L2147-L2151
|
|
613
|
+
LLVMBuildRet(c->builder, llrb_value(0));
|
|
614
|
+
return true;
|
|
615
|
+
}
|
|
616
|
+
case YARVINSN_jump: {
|
|
617
|
+
unsigned dest = pos + (unsigned)insn_len(insn) + operands[0];
|
|
618
|
+
struct llrb_basic_block *next_block = llrb_find_block(c->cfg, dest);
|
|
619
|
+
|
|
620
|
+
LLVMBuildBr(c->builder, next_block->ref);
|
|
621
|
+
*created_br = true;
|
|
622
|
+
|
|
623
|
+
if (next_block->incoming_size > 1 && stack->size > 0) {
|
|
624
|
+
llrb_push_incoming_things(c, next_block, LLVMGetInsertBlock(c->builder), llrb_stack_pop(stack));
|
|
625
|
+
}
|
|
626
|
+
llrb_compile_basic_block(c, next_block, stack);
|
|
627
|
+
return true;
|
|
628
|
+
}
|
|
629
|
+
case YARVINSN_branchif: { // TODO: refactor with other branch insns
|
|
630
|
+
unsigned branch_dest = pos + (unsigned)insn_len(insn) + operands[0];
|
|
631
|
+
unsigned fallthrough = pos + (unsigned)insn_len(insn);
|
|
632
|
+
struct llrb_basic_block *branch_dest_block = llrb_find_block(c->cfg, branch_dest);
|
|
633
|
+
struct llrb_basic_block *fallthrough_block = llrb_find_block(c->cfg, fallthrough);
|
|
634
|
+
|
|
635
|
+
LLVMValueRef cond = llrb_stack_pop(stack);
|
|
636
|
+
LLVMBuildCondBr(c->builder, llrb_build_rtest(c->builder, cond), branch_dest_block->ref, fallthrough_block->ref);
|
|
637
|
+
*created_br = true;
|
|
638
|
+
|
|
639
|
+
struct llrb_stack *branch_dest_stack = llrb_copy_stack(stack); // `llrb_destruct_stack`ed in this block.
|
|
640
|
+
if (branch_dest_block->incoming_size > 1 && branch_dest_stack->size > 0) {
|
|
641
|
+
llrb_push_incoming_things(c, branch_dest_block,
|
|
642
|
+
LLVMGetInsertBlock(c->builder), llrb_stack_pop(branch_dest_stack));
|
|
643
|
+
}
|
|
644
|
+
llrb_compile_basic_block(c, branch_dest_block, branch_dest_stack);
|
|
645
|
+
llrb_destruct_stack(branch_dest_stack);
|
|
646
|
+
break; // caller `compile_basic_block` compiles fallthrough_block and pushes incoming things to its phi node.
|
|
647
|
+
}
|
|
648
|
+
case YARVINSN_branchunless: { // TODO: refactor with other branch insns
|
|
649
|
+
unsigned branch_dest = pos + (unsigned)insn_len(insn) + operands[0];
|
|
650
|
+
unsigned fallthrough = pos + (unsigned)insn_len(insn);
|
|
651
|
+
struct llrb_basic_block *branch_dest_block = llrb_find_block(c->cfg, branch_dest);
|
|
652
|
+
struct llrb_basic_block *fallthrough_block = llrb_find_block(c->cfg, fallthrough);
|
|
653
|
+
|
|
654
|
+
LLVMValueRef cond = llrb_stack_pop(stack);
|
|
655
|
+
LLVMBuildCondBr(c->builder, llrb_build_rtest(c->builder, cond), fallthrough_block->ref, branch_dest_block->ref);
|
|
656
|
+
*created_br = true;
|
|
657
|
+
|
|
658
|
+
struct llrb_stack *branch_dest_stack = llrb_copy_stack(stack); // `llrb_destruct_stack`ed in this block.
|
|
659
|
+
if (branch_dest_block->incoming_size > 1 && branch_dest_stack->size > 0) {
|
|
660
|
+
llrb_push_incoming_things(c, branch_dest_block,
|
|
661
|
+
LLVMGetInsertBlock(c->builder), llrb_stack_pop(branch_dest_stack));
|
|
662
|
+
}
|
|
663
|
+
llrb_compile_basic_block(c, branch_dest_block, branch_dest_stack);
|
|
664
|
+
llrb_destruct_stack(branch_dest_stack);
|
|
665
|
+
break; // caller `compile_basic_block` compiles fallthrough_block and pushes incoming things to its phi node.
|
|
666
|
+
}
|
|
667
|
+
case YARVINSN_branchnil: { // TODO: refactor with other branch insns
|
|
668
|
+
unsigned branch_dest = pos + (unsigned)insn_len(insn) + operands[0];
|
|
669
|
+
unsigned fallthrough = pos + (unsigned)insn_len(insn);
|
|
670
|
+
struct llrb_basic_block *branch_dest_block = llrb_find_block(c->cfg, branch_dest);
|
|
671
|
+
struct llrb_basic_block *fallthrough_block = llrb_find_block(c->cfg, fallthrough);
|
|
672
|
+
|
|
673
|
+
LLVMValueRef cond = llrb_stack_pop(stack);
|
|
674
|
+
LLVMBuildCondBr(c->builder,
|
|
675
|
+
LLVMBuildICmp(c->builder, LLVMIntNE, cond, llrb_value(Qnil), "NIL_P"),
|
|
676
|
+
fallthrough_block->ref, branch_dest_block->ref);
|
|
677
|
+
*created_br = true;
|
|
678
|
+
|
|
679
|
+
struct llrb_stack *branch_dest_stack = llrb_copy_stack(stack); // `llrb_destruct_stack`ed in this block.
|
|
680
|
+
if (branch_dest_block->incoming_size > 1 && branch_dest_stack->size > 0) {
|
|
681
|
+
llrb_push_incoming_things(c, branch_dest_block,
|
|
682
|
+
LLVMGetInsertBlock(c->builder), llrb_stack_pop(branch_dest_stack));
|
|
683
|
+
}
|
|
684
|
+
llrb_compile_basic_block(c, branch_dest_block, branch_dest_stack);
|
|
685
|
+
llrb_destruct_stack(branch_dest_stack);
|
|
686
|
+
break; // caller `compile_basic_block` compiles fallthrough_block and pushes incoming things to its phi node.
|
|
687
|
+
}
|
|
688
|
+
case YARVINSN_getinlinecache:
|
|
689
|
+
llrb_stack_push(stack, llrb_value(Qnil)); // TODO: implement
|
|
690
|
+
break;
|
|
691
|
+
case YARVINSN_setinlinecache:
|
|
692
|
+
break; // TODO: implement
|
|
693
|
+
//case YARVINSN_once:
|
|
694
|
+
case YARVINSN_opt_case_dispatch: // Use `switch` instruction
|
|
695
|
+
llrb_stack_pop(stack); // TODO: implement
|
|
696
|
+
break;
|
|
697
|
+
case YARVINSN_opt_plus:
|
|
698
|
+
llrb_compile_opt_insn(c, stack, "opt_plus", 2);
|
|
699
|
+
break;
|
|
700
|
+
case YARVINSN_opt_minus:
|
|
701
|
+
llrb_compile_opt_insn(c, stack, "opt_minus", 2);
|
|
702
|
+
break;
|
|
703
|
+
case YARVINSN_opt_mult:
|
|
704
|
+
llrb_compile_opt_insn(c, stack, "opt_mult", 2);
|
|
705
|
+
break;
|
|
706
|
+
case YARVINSN_opt_div:
|
|
707
|
+
llrb_compile_opt_insn(c, stack, "opt_div", 2);
|
|
708
|
+
break;
|
|
709
|
+
case YARVINSN_opt_mod:
|
|
710
|
+
llrb_compile_opt_insn(c, stack, "opt_mod", 2);
|
|
711
|
+
break;
|
|
712
|
+
case YARVINSN_opt_eq:
|
|
713
|
+
llrb_compile_opt_insn(c, stack, "opt_eq", 2);
|
|
714
|
+
break;
|
|
715
|
+
case YARVINSN_opt_neq: {
|
|
716
|
+
LLVMValueRef *args = ALLOC_N(LLVMValueRef, 6); // `xfree`d in this block.
|
|
717
|
+
args[1] = llrb_stack_pop(stack);
|
|
718
|
+
args[0] = llrb_stack_pop(stack);
|
|
719
|
+
args[2] = llrb_value(operands[0]);
|
|
720
|
+
args[3] = llrb_value(operands[1]);
|
|
721
|
+
args[4] = llrb_value(operands[2]);
|
|
722
|
+
args[5] = llrb_value(operands[3]);
|
|
723
|
+
llrb_stack_push(stack, LLVMBuildCall(c->builder, llrb_get_function(c->mod, "llrb_insn_opt_neq"), args, 6, ""));
|
|
724
|
+
xfree(args);
|
|
725
|
+
break;
|
|
726
|
+
}
|
|
727
|
+
case YARVINSN_opt_lt:
|
|
728
|
+
llrb_compile_opt_insn(c, stack, "opt_lt", 2);
|
|
729
|
+
break;
|
|
730
|
+
case YARVINSN_opt_le:
|
|
731
|
+
llrb_compile_opt_insn(c, stack, "opt_le", 2);
|
|
732
|
+
break;
|
|
733
|
+
case YARVINSN_opt_gt:
|
|
734
|
+
llrb_compile_opt_insn(c, stack, "opt_gt", 2);
|
|
735
|
+
break;
|
|
736
|
+
case YARVINSN_opt_ge:
|
|
737
|
+
llrb_compile_opt_insn(c, stack, "opt_ge", 2);
|
|
738
|
+
break;
|
|
739
|
+
case YARVINSN_opt_ltlt:
|
|
740
|
+
llrb_compile_opt_insn(c, stack, "opt_ltlt", 2);
|
|
741
|
+
break;
|
|
742
|
+
case YARVINSN_opt_aref:
|
|
743
|
+
llrb_compile_opt_insn(c, stack, "opt_aref", 2);
|
|
744
|
+
break;
|
|
745
|
+
case YARVINSN_opt_aset:
|
|
746
|
+
llrb_compile_opt_insn(c, stack, "opt_aset", 3);
|
|
747
|
+
break;
|
|
748
|
+
case YARVINSN_opt_aset_with: {
|
|
749
|
+
LLVMValueRef value = llrb_stack_pop(stack);
|
|
750
|
+
LLVMValueRef recv = llrb_stack_pop(stack);
|
|
751
|
+
LLVMValueRef str = llrb_call_func(c, "rb_str_resurrect", 1, llrb_value(operands[2]));
|
|
752
|
+
|
|
753
|
+
// Not using llrb_compile_funcall to prevent stack overflow
|
|
754
|
+
llrb_stack_push(stack, llrb_call_func(c, "rb_funcall", 5, recv,
|
|
755
|
+
llrb_value(rb_intern("[]=")), LLVMConstInt(LLVMInt32Type(), 2, true), str, value));
|
|
756
|
+
break;
|
|
757
|
+
}
|
|
758
|
+
case YARVINSN_opt_aref_with: {
|
|
759
|
+
LLVMValueRef str = llrb_call_func(c, "rb_str_resurrect", 1, llrb_value(operands[2]));
|
|
760
|
+
|
|
761
|
+
// Not using llrb_compile_funcall to prevent stack overflow
|
|
762
|
+
llrb_stack_push(stack, llrb_call_func(c, "rb_funcall", 4, llrb_stack_pop(stack),
|
|
763
|
+
llrb_value(rb_intern("[]")), LLVMConstInt(LLVMInt32Type(), 1, true), str));
|
|
764
|
+
break;
|
|
765
|
+
}
|
|
766
|
+
case YARVINSN_opt_length:
|
|
767
|
+
llrb_stack_push(stack, llrb_compile_funcall(c, stack, rb_intern("length"), 0));
|
|
768
|
+
break;
|
|
769
|
+
case YARVINSN_opt_size:
|
|
770
|
+
llrb_stack_push(stack, llrb_compile_funcall(c, stack, rb_intern("size"), 0));
|
|
771
|
+
break;
|
|
772
|
+
case YARVINSN_opt_empty_p:
|
|
773
|
+
llrb_stack_push(stack, llrb_compile_funcall(c, stack, rb_intern("empty?"), 0));
|
|
774
|
+
break;
|
|
775
|
+
case YARVINSN_opt_succ:
|
|
776
|
+
llrb_stack_push(stack, llrb_compile_funcall(c, stack, rb_intern("succ"), 0));
|
|
777
|
+
break;
|
|
778
|
+
case YARVINSN_opt_not:
|
|
779
|
+
llrb_stack_push(stack, llrb_compile_funcall(c, stack, '!', 0));
|
|
780
|
+
break;
|
|
781
|
+
case YARVINSN_opt_regexpmatch1: {
|
|
782
|
+
// Not using llrb_compile_funcall to prevent stack overflow
|
|
783
|
+
llrb_stack_push(stack, llrb_call_func(c, "rb_funcall", 4, llrb_stack_pop(stack),
|
|
784
|
+
llrb_value(rb_intern("=~")), LLVMConstInt(LLVMInt32Type(), 1, true), llrb_value(operands[0])));
|
|
785
|
+
break;
|
|
786
|
+
}
|
|
787
|
+
case YARVINSN_opt_regexpmatch2: {
|
|
788
|
+
llrb_stack_push(stack, llrb_compile_funcall(c, stack, rb_intern("=~"), 1));
|
|
789
|
+
break;
|
|
790
|
+
}
|
|
791
|
+
//case YARVINSN_opt_call_c_function:
|
|
792
|
+
case YARVINSN_getlocal_OP__WC__0: {
|
|
793
|
+
llrb_stack_push(stack, llrb_call_func(c, "llrb_insn_getlocal_level0", 2, llrb_get_cfp(c), llrb_value((lindex_t)operands[0])));
|
|
794
|
+
break;
|
|
795
|
+
}
|
|
796
|
+
case YARVINSN_getlocal_OP__WC__1: {
|
|
797
|
+
llrb_stack_push(stack, llrb_call_func(c, "llrb_insn_getlocal_level1", 2, llrb_get_cfp(c), llrb_value((lindex_t)operands[0])));
|
|
798
|
+
break;
|
|
799
|
+
}
|
|
800
|
+
case YARVINSN_setlocal_OP__WC__0: {
|
|
801
|
+
LLVMValueRef idx = llrb_value((lindex_t)operands[0]);
|
|
802
|
+
llrb_call_func(c, "llrb_insn_setlocal_level0", 3, llrb_get_cfp(c), idx, llrb_stack_pop(stack));
|
|
803
|
+
break;
|
|
804
|
+
}
|
|
805
|
+
case YARVINSN_setlocal_OP__WC__1: {
|
|
806
|
+
LLVMValueRef idx = llrb_value((lindex_t)operands[0]);
|
|
807
|
+
llrb_call_func(c, "llrb_insn_setlocal_level1", 3, llrb_get_cfp(c), idx, llrb_stack_pop(stack));
|
|
808
|
+
break;
|
|
809
|
+
}
|
|
810
|
+
case YARVINSN_putobject_OP_INT2FIX_O_0_C_:
|
|
811
|
+
llrb_stack_push(stack, llrb_value(INT2FIX(0)));
|
|
812
|
+
break;
|
|
813
|
+
case YARVINSN_putobject_OP_INT2FIX_O_1_C_:
|
|
814
|
+
llrb_stack_push(stack, llrb_value(INT2FIX(1)));
|
|
815
|
+
break;
|
|
816
|
+
default:
|
|
817
|
+
llrb_dump_cfg(c->body, c->cfg);
|
|
818
|
+
rb_raise(rb_eCompileError, "Unhandled insn at llrb_compile_insn: %s", insn_name(insn));
|
|
819
|
+
break;
|
|
820
|
+
}
|
|
821
|
+
//fprintf(stderr, " [DEBUG] llrb_compile_insn: %04d after %-27s (stack size: %d)\n", pos, insn_name(insn), stack->size);
|
|
822
|
+
return false;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
static void
|
|
826
|
+
llrb_compile_basic_block(const struct llrb_compiler *c, struct llrb_basic_block *block, struct llrb_stack *stack)
|
|
827
|
+
{
|
|
828
|
+
if (block->compiled) return;
|
|
829
|
+
block->compiled = true;
|
|
830
|
+
|
|
831
|
+
// If phi node is created for this block, push it to stack.
|
|
832
|
+
if (block->phi) {
|
|
833
|
+
llrb_stack_push(stack, block->phi);
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
// Here is the actual compilation of block specified in arguments.
|
|
837
|
+
bool returned = false, created_br = false;
|
|
838
|
+
unsigned int pos = block->start;
|
|
839
|
+
while (pos <= block->end) {
|
|
840
|
+
LLVMPositionBuilderAtEnd(c->builder, block->ref); // Reset everytime to allow recursive compilation.
|
|
841
|
+
int insn = rb_vm_insn_addr2insn((void *)c->body->iseq_encoded[pos]);
|
|
842
|
+
returned = llrb_compile_insn(c, stack, pos, insn, c->body->iseq_encoded + (pos+1), &created_br);
|
|
843
|
+
pos += insn_len(insn);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// If the function is not returned yet, compiles next block.
|
|
847
|
+
if (!returned) {
|
|
848
|
+
// In the end of the function, it must be returned...
|
|
849
|
+
if (pos >= c->body->iseq_size) {
|
|
850
|
+
rb_raise(rb_eCompileError, "Compiler compiled the end of function but the function was not returned");
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
struct llrb_basic_block *next_block = llrb_find_block(c->cfg, pos);
|
|
854
|
+
LLVMPositionBuilderAtEnd(c->builder, block->ref); // Reset to allow recursive compilation.
|
|
855
|
+
if (!created_br) LLVMBuildBr(c->builder, next_block->ref);
|
|
856
|
+
|
|
857
|
+
if (next_block->incoming_size > 1 && stack->size > 0) {
|
|
858
|
+
llrb_push_incoming_things(c, next_block, block->ref, llrb_stack_pop(stack));
|
|
859
|
+
}
|
|
860
|
+
llrb_compile_basic_block(c, next_block, stack);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
static LLVMBasicBlockRef
|
|
865
|
+
llrb_build_basic_block_ref(const struct llrb_compiler *c, const struct llrb_basic_block *block)
|
|
866
|
+
{
|
|
867
|
+
VALUE label = rb_str_new_cstr("label_"); // `rb_str_free`d in the end of this function.
|
|
868
|
+
rb_str_catf(label, "%d", block->start);
|
|
869
|
+
|
|
870
|
+
LLVMBasicBlockRef ref = LLVMAppendBasicBlock(c->func, RSTRING_PTR(label));
|
|
871
|
+
rb_str_free(label);
|
|
872
|
+
return ref;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
static void
|
|
876
|
+
llrb_init_cfg_for_compile(const struct llrb_compiler *c, struct llrb_cfg *cfg)
|
|
877
|
+
{
|
|
878
|
+
for (unsigned int i = 0; i < cfg->size; i++) {
|
|
879
|
+
struct llrb_basic_block* block = cfg->blocks + i;
|
|
880
|
+
block->compiled = false;
|
|
881
|
+
if (!block->traversed) continue;
|
|
882
|
+
|
|
883
|
+
block->ref = llrb_build_basic_block_ref(c, block);
|
|
884
|
+
block->phi = 0;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
// Compiles Control Flow Graph having encoded YARV instructions to LLVM IR.
|
|
889
|
+
static LLVMValueRef
|
|
890
|
+
llrb_compile_cfg(LLVMModuleRef mod, const struct rb_iseq_constant_body *body, const VALUE *new_iseq_encoded, struct llrb_cfg *cfg, const char* funcname)
|
|
891
|
+
{
|
|
892
|
+
LLVMTypeRef args[] = { LLVMInt64Type(), LLVMInt64Type() };
|
|
893
|
+
LLVMValueRef func = LLVMAddFunction(mod, funcname,
|
|
894
|
+
LLVMFunctionType(LLVMInt64Type(), args, 2, false));
|
|
895
|
+
|
|
896
|
+
const struct llrb_compiler compiler = (struct llrb_compiler){
|
|
897
|
+
.body = body,
|
|
898
|
+
.new_iseq_encoded = new_iseq_encoded,
|
|
899
|
+
.cfg = cfg,
|
|
900
|
+
.func = func,
|
|
901
|
+
.builder = LLVMCreateBuilder(),
|
|
902
|
+
.mod = mod,
|
|
903
|
+
};
|
|
904
|
+
llrb_init_cfg_for_compile(&compiler, cfg);
|
|
905
|
+
|
|
906
|
+
// To simulate YARV stack, we need to traverse CFG again here instead of loop from start to end.
|
|
907
|
+
struct llrb_stack stack = (struct llrb_stack){
|
|
908
|
+
.body = ALLOC_N(LLVMValueRef, body->stack_max), // `xfree`d in the end of this function.
|
|
909
|
+
.size = 0,
|
|
910
|
+
.max = body->stack_max,
|
|
911
|
+
};
|
|
912
|
+
llrb_compile_basic_block(&compiler, cfg->blocks, &stack);
|
|
913
|
+
|
|
914
|
+
xfree(stack.body);
|
|
915
|
+
return func;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
// This sweaps memory `xmalloc`ed by llrb_create_basic_blocks.
|
|
919
|
+
static void
|
|
920
|
+
llrb_destruct_cfg(struct llrb_cfg *cfg)
|
|
921
|
+
{
|
|
922
|
+
for (unsigned int i = 0; i < cfg->size; i++) {
|
|
923
|
+
struct llrb_basic_block *block = cfg->blocks + i;
|
|
924
|
+
xfree(block->incoming_starts);
|
|
925
|
+
}
|
|
926
|
+
xfree(cfg->blocks);
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
static bool
|
|
930
|
+
llrb_includes_unsupported_insn(const rb_iseq_t *iseq)
|
|
931
|
+
{
|
|
932
|
+
unsigned int i = 0;
|
|
933
|
+
while (i < iseq->body->iseq_size) {
|
|
934
|
+
int insn = rb_vm_insn_addr2insn((void *)iseq->body->iseq_encoded[i]);
|
|
935
|
+
switch (insn) {
|
|
936
|
+
case YARVINSN_expandarray:
|
|
937
|
+
case YARVINSN_reverse:
|
|
938
|
+
case YARVINSN_reput:
|
|
939
|
+
case YARVINSN_defineclass:
|
|
940
|
+
case YARVINSN_once:
|
|
941
|
+
case YARVINSN_opt_call_c_function:
|
|
942
|
+
return true;
|
|
943
|
+
default:
|
|
944
|
+
break;
|
|
945
|
+
}
|
|
946
|
+
i += insn_len(insn);
|
|
947
|
+
}
|
|
948
|
+
return false;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
bool
|
|
952
|
+
llrb_check_not_compilable(const rb_iseq_t *iseq)
|
|
953
|
+
{
|
|
954
|
+
// At least 3 is needed: opt_call_c_function + funcptr + leave
|
|
955
|
+
return iseq->body->iseq_size < 3
|
|
956
|
+
// We don't want to set pc to index 1. It will be funcptr. So we don't compile for such case.
|
|
957
|
+
|| (insn_len(rb_vm_insn_addr2insn((void *)iseq->body->iseq_encoded[0])) == 1 &&
|
|
958
|
+
llrb_pc_change_required(rb_vm_insn_addr2insn((void *)iseq->body->iseq_encoded[1])))
|
|
959
|
+
|| llrb_includes_unsupported_insn(iseq);
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// llrb_create_native_func() uses a LLVM function named as `funcname` defined in returned LLVM module.
|
|
963
|
+
LLVMModuleRef
|
|
964
|
+
llrb_compile_iseq(const struct rb_iseq_constant_body *body, const VALUE *new_iseq_encoded, const char* funcname)
|
|
965
|
+
{
|
|
966
|
+
extern void llrb_parse_iseq(const struct rb_iseq_constant_body *body, struct llrb_cfg *result);
|
|
967
|
+
struct llrb_cfg cfg;
|
|
968
|
+
llrb_parse_iseq(body, &cfg);
|
|
969
|
+
|
|
970
|
+
LLVMModuleRef mod = LLVMModuleCreateWithName("llrb");
|
|
971
|
+
LLVMValueRef func = llrb_compile_cfg(mod, body, new_iseq_encoded, &cfg, funcname);
|
|
972
|
+
|
|
973
|
+
extern void llrb_optimize_function(LLVMModuleRef cmod, LLVMValueRef cfunc);
|
|
974
|
+
if (1) llrb_optimize_function(mod, func);
|
|
975
|
+
|
|
976
|
+
if (0) llrb_dump_cfg(body, &cfg);
|
|
977
|
+
if (0) LLVMDumpModule(mod);
|
|
978
|
+
|
|
979
|
+
llrb_destruct_cfg(&cfg);
|
|
980
|
+
return mod;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
void
|
|
984
|
+
Init_compiler(VALUE rb_mJIT)
|
|
985
|
+
{
|
|
986
|
+
rb_eCompileError = rb_define_class_under(rb_mJIT, "CompileError", rb_eStandardError);
|
|
987
|
+
}
|