Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 267 lines (216 sloc) 7.962 kb
f5941b7 @dgl It would be helpful to not ignore the .c files
authored
1 #include "unroll.h"
2 #include <sljitLir.h>
3
4 #ifdef DEBUG
5 void debugf(void *f, ...) {
6 va_list ap;
7 va_start(ap, f);
8 if (getenv("RUNOPS_OPTIMIZED_DEBUG"))
9 vfprintf(stderr, f, ap);
10 va_end(f);
11 }
12 #define DEBUGf(x) debugf x;
13 #else
14 #define DEBUGf(x)
15 #endif
16
17 /*** Local data structures ***/
18
19 struct oplabel {
20 struct sljit_label *label;
21 OP *op;
22 struct oplabel *next;
23 };
24
25 struct needjump {
26 struct sljit_jump *jump;
27 OP *op_to;
28 struct needjump *next;
29 };
30
31 struct needjump* needjumps = NULL;
32 struct oplabel* labels = NULL;
33
34 /*** Static prototypes ***/
35 static void unroll_tree(pTHX_ struct sljit_compiler* compiler, HV* seenops,
36 OP* op, OP* end);
37 static void unroll_branching_op(struct sljit_compiler* compiler, HV* seenops,
38 OP* op, OP* next, OP* other);
39 static void unroll_op(pTHX_ struct sljit_compiler* compiler, OP* op);
40
41 static void emit_jump(struct sljit_compiler* compiler, OP* op);
42 static void emit_label(struct sljit_compiler* compiler, OP* op);
43 static void fixup_jumps(struct sljit_compiler* compiler, struct needjump* jump,
44 struct oplabel* label);
45
46 static const char* format_hex(void* inaddr);
47
48 /******/
49
50 void unroll_this(pTHX_ OP* op) {
51 struct sljit_compiler* compiler = sljit_create_compiler();
52 HV* seenops = newHV();
53
54 #ifdef DEBUG
55 if (getenv("RUNOPS_OPTIMIZED_DEBUG")) {
56 CV *runcv = Perl_find_runcv(NULL);
57 sljit_compiler_verbose(compiler, stderr);
58
59 DEBUGf(("Unroll %s::%s cv=%p, op=%p (%s)\n", HvNAME_get(CvSTASH(runcv)),
60 GvENAME(CvGV(runcv)), runcv, op, sljit_get_platform_name()));
61 }
62 #endif
63
64 sljit_emit_enter(compiler, 0, 2, 1, 0);
65 unroll_tree(compiler, seenops, op, NULL);
66 fixup_jumps(compiler, needjumps, labels);
67 // This is needed for things that drop off the runloop without a
68 // return, e.g. S_sortcv. TODO: Make conditional?
69 sljit_emit_return(compiler, SLJIT_MEM, (sljit_w) &PL_op);
70
71 op->op_ppaddr = sljit_generate_code(compiler);
72 op->op_spare = 3;
73 DEBUGf(("Code at %p\n", op->op_ppaddr));
74
75 labels = NULL;
76 needjumps = NULL;
77 SvREFCNT_dec(seenops);
78 sljit_free_compiler(compiler);
79 }
80
81 static void unroll_tree(pTHX_ struct sljit_compiler* compiler, HV* seenops,
82 OP* op, OP* end)
83 {
84 // XXX: This basically is the algorithm from walk_exec in B::Concise, but I
85 // think it could be done better. (Maybe core will get something like
86 // codegen?).
87
88 while (op && op != end) {
89
90 const char* op_hex = format_hex(op);
91
92 // Have we visited this op already?
93 if (hv_exists(seenops, op_hex, strlen(op_hex))) {
94 DEBUGf((" ;; Already seen %s [0x%s]\n", OP_NAME(op), op_hex));
95 // Insert jump to it
96 emit_jump(compiler, op);
97 // We know we have followed all the next pointers for this chain,
98 // so:
99 break;
100 }
101
102 // Seen op
103 hv_store(seenops, op_hex, 0, &PL_sv_yes, strlen(op_hex));
104
105 if (op->op_type == OP_CUSTOM) {
106 // All bets are off
107 sljit_emit_return(compiler, SLJIT_MEM, (sljit_w) &PL_op);
108 }
109
110 else if (OP_CLASS(op) == OA_LOGOP) {
111 unroll_branching_op(compiler, seenops, op, op->op_next,
112 cLOGOPx(op)->op_other);
113 }
114
115 else if (op->op_type == OP_SUBST
116 && cPMOPx(op)->op_pmstashstartu.op_pmreplstart) {
117 unroll_branching_op(compiler, seenops, op, op->op_next,
118 cPMOPx(op)->op_pmstashstartu.op_pmreplstart);
119 }
120
121 else if (op->op_type == OP_GREPSTART || op->op_type == OP_MAPSTART) {
122 unroll_branching_op(compiler, seenops, op, op->op_next->op_next,
123 cLOGOPx(op->op_next)->op_other);
124 }
125
126 else if (op->op_type == OP_NEXT || op->op_type == OP_LAST
127 || op->op_type == OP_REDO) {
128 sljit_emit_return(compiler, SLJIT_MEM, (sljit_w) &PL_op);
129 //need_patch = 1; XXX
130 }
131
132 else if (op->op_type == OP_FLIP || op->op_type == OP_GOTO) {
133 sljit_emit_return(compiler, SLJIT_MEM, (sljit_w) &PL_op);
134 //need_patch = 1; XXX
135 }
136
137 else if (op->op_type == OP_ENTERSUB) {
138 sljit_emit_return(compiler, SLJIT_MEM, (sljit_w) &PL_op);
139 //need_patch = 1; XXX
140 }
141
142 else if (op->op_type == OP_RETURN || op->op_type == OP_LEAVESUB
143 || op->op_type == OP_REQUIRE) {
144 // XXX: leavesublv?
145 unroll_op(compiler, op);
146 sljit_emit_return(compiler, SLJIT_MEM, (sljit_w) &PL_op);
147 }
148
149 else {
150 unroll_op(compiler, op);
151 #ifdef DEBUG
152 // Ensure returned OP is actually the one we expect
153 struct sljit_jump *jump1 = sljit_emit_cmp(compiler, SLJIT_C_EQUAL,
154 SLJIT_RETURN_REG, 0, SLJIT_IMM, (sljit_w) op->op_next);
155 sljit_emit_op0(compiler, SLJIT_BREAKPOINT);
156 sljit_set_label(jump1, sljit_emit_label(compiler));
157 #endif
158 }
159 op = op->op_next;
160 }
161 }
162
163 static void emit_jump(struct sljit_compiler* compiler, OP* op)
164 {
165 struct sljit_jump* jump = sljit_emit_jump(compiler, SLJIT_JUMP);
166 struct needjump* fixup = sljit_alloc_memory(compiler, sizeof *fixup);
167
168 fixup->jump = jump;
169 fixup->op_to = op;
170
171 fixup->next = needjumps;
172 needjumps = fixup;
173 }
174
175 static void emit_label(struct sljit_compiler* compiler, OP* op)
176 {
177 struct sljit_label* label = sljit_emit_label(compiler);
178 struct oplabel *oplabel = sljit_alloc_memory(compiler, sizeof *oplabel);
179
180 oplabel->label = label;
181 oplabel->op = op;
182
183 oplabel->next = labels;
184 labels = oplabel;
185 }
186
187 static void unroll_branching_op(struct sljit_compiler* compiler, HV* seenops,
188 OP* op, OP* next, OP* other)
189 {
190 OP *i = other;
191 OP *converge = NULL;
192
193 unroll_op(compiler, op);
194
195 // TODO: Generalise emit_jump a bit to save this extra jump
196 struct sljit_jump *jump_other = sljit_emit_cmp(compiler,
197 SLJIT_C_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, (sljit_w) other);
198 emit_jump(compiler, next);
199 sljit_set_label(jump_other, sljit_emit_label(compiler));
200
201 do {
202 OP *j = op;
203
204 do {
205 if (j == i) {
206 converge = i;
207 goto done;
208 }
209 } while ((j = j->op_next));
210 } while ((i = i->op_next));
211
212 done:
213 if (!converge)
214 Perl_croak("Runops::Optimized panic: No idea where these ops converge");
215
216 DEBUGf((" ;; %p converges at %p\n", other, converge));
217
218 unroll_tree(aTHX_ compiler, seenops, other, converge);
219
220 if (next != converge) {
221 DEBUGf((" ;; %p jump needed\n", converge));
222 emit_jump(compiler, converge);
223 }
224 }
225
226 static void unroll_op(pTHX_ struct sljit_compiler* compiler, OP* op)
227 {
228 DEBUGf((" ; %s [%p]\n", OP_NAME(op), op));
229
230 emit_label(compiler, op); // TODO(dgl): Can we avoid doing this for every op?
231 sljit_emit_ijump(compiler, SLJIT_CALL0, SLJIT_IMM, (sljit_w) op->op_ppaddr);
232 sljit_emit_op1(compiler, SLJIT_MOV, SLJIT_MEM, (sljit_w) &PL_op, SLJIT_RETURN_REG, 0);
233 }
234
235 static void fixup_jumps(struct sljit_compiler* compiler, struct needjump* jump,
236 struct oplabel *label)
237 {
238 while (jump) {
239 struct oplabel *l = label;
240
241 while (l) {
242 if (l->op == jump->op_to) {
243 sljit_set_label(jump->jump, l->label);
244 goto done;
245 }
246 l = l->next;
247 }
248 Perl_croak("Unable to resolve jump for %p [%s]", jump->op_to, OP_NAME(jump->op_to));
249 done: jump = jump->next;
250 }
251 }
252
253 // XXX: Hacky, but rather than fixing this consider the whole reuse of HV for
254 // remembering seen ops thing.
255 static const char* format_hex(void* inaddr)
256 {
257 static char ret[16 + 1]; // 64-bits as hex + NUL
258 long addr = (long)inaddr;
259 char *retval = ret + sizeof ret;
260 *--retval = '\0';
261 do {
262 *--retval = PL_hexdigit[addr & 15];
263 } while (addr >>= 4);
264 return retval;
265 }
266
Something went wrong with that request. Please try again.