ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/libcpjit/cpjit.C
Revision: 1.12
Committed: Mon Oct 17 18:27:05 2005 UTC (18 years, 9 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.11: +118 -77 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 /*
2 cpjit.C -- c portable jit in time compiler
3 Copyright (C) 2005 Marc Lehmann <cpjit@schmorp.de>
4
5 This file is part of libcpjit.
6
7 libcpjit is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with gvpe; if not, write to the Free Software
19 Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 #include "config.h"
23
24 #include "cpjit.h"
25
26 #include <cassert>
27 #include <cstdlib>
28 #include <cstdarg>
29 #include <cstdio>
30 #include <cstring>
31 #include <cerrno>
32
33 #include <iostream>
34 #include <sstream>
35 #include <fstream>
36 #include <stdexcept>
37 #include <iomanip>
38
39 #include <unistd.h>
40 #include <fcntl.h> // for locking
41
42 #include <sys/stat.h>
43 #include <sys/types.h>
44 #include <dirent.h>
45 #include <utime.h>
46
47 #include "md5.h"
48
49 #include <dlfcn.h>
50
51 #define STR_(str) #str
52 #define STR(str) STR_(str)
53
54 #define IDX_VERSION 1
55
56 namespace cpjit
57 {
58
59 using namespace std;
60
61 const char typestr<U8 >::str[] = "unsigned " STR(TYPE_INT8);
62 const char typestr<U16 >::str[] = "unsigned " STR(TYPE_INT16);
63 const char typestr<U32 >::str[] = "unsigned " STR(TYPE_INT32);
64 const char typestr<U64 >::str[] = "unsigned " STR(TYPE_INT64);
65 const char typestr<I8 >::str[] = "signed " STR(TYPE_INT8);
66 const char typestr<I16 >::str[] = STR(TYPE_INT16);
67 const char typestr<I32 >::str[] = STR(TYPE_INT32);
68 const char typestr<I64 >::str[] = STR(TYPE_INT64);
69 const char typestr<FLT >::str[] = "float";
70 const char typestr<DBL >::str[] = "double";
71
72 const char typestr<PTR >::str[] = "void" " *";
73
74 const char typestr<U8 *>::str[] = "unsigned " STR(TYPE_INT8) " *";
75 const char typestr<U16 *>::str[] = "unsigned " STR(TYPE_INT16) " *";
76 const char typestr<U32 *>::str[] = "unsigned " STR(TYPE_INT32) " *";
77 const char typestr<U64 *>::str[] = "unsigned " STR(TYPE_INT64) " *";
78 const char typestr<I8 *>::str[] = "signed " STR(TYPE_INT8) " *";
79 const char typestr<I16 *>::str[] = STR(TYPE_INT16) " *";
80 const char typestr<I32 *>::str[] = STR(TYPE_INT32) " *";
81 const char typestr<I64 *>::str[] = STR(TYPE_INT64) " *";
82 const char typestr<FLT *>::str[] = "float" " *";
83 const char typestr<DBL *>::str[] = "double" " *";
84
85 /////////////////////////////////////////////////////////////////////////////
86
87 struct digest : md5_ctx
88 {
89 digest ()
90 {
91 md5_init_ctx (this);
92 }
93
94 digest &operator <<(const string &data)
95 {
96 md5_process_bytes (data.c_str (), data.size (), this);
97 return *this;
98 }
99
100 operator string();
101 };
102
103 digest::operator string ()
104 {
105 unsigned char data[16];
106 std::ostringstream s;
107
108 md5_finish_ctx (this, data);
109
110 for (int i = 0; i < 16; i++)
111 s << std::setfill ('0') << std::hex << std::setw (2) << (int)data [i];
112
113 return s.str ();
114 }
115
116 /////////////////////////////////////////////////////////////////////////////
117
118 static inline bool
119 is_null (const string &s)
120 {
121 return s.empty ();
122 }
123
124 /////////////////////////////////////////////////////////////////////////////
125
126 // error exception builder
127 struct error : std::ostringstream
128 {
129 ~error ()
130 {
131 throw std::runtime_error (str ());
132 }
133 };
134
135 #define FATAL(msg) do { error o; o << msg << std::endl; } while (0)
136 #define ERRNO " (" << strerror (errno) << ")"
137
138 /////////////////////////////////////////////////////////////////////////////
139
140 static long get_stamp (int fd)
141 {
142 struct stat st;
143
144 if (fstat (fd, &st))
145 FATAL ("could not get file stamp");
146
147 return (long)st.st_mtime;
148 }
149
150 static void set_stamp (const string &path, long stamp)
151 {
152 struct utimbuf ut;
153
154 ut.actime = stamp;
155 ut.modtime = stamp;
156
157 if (utime (path.c_str (), &ut))
158 FATAL ("could not set file stamp");
159 }
160
161 /////////////////////////////////////////////////////////////////////////////
162
163 funsrc::funsrc ()
164 {
165 }
166
167 void funsrc::update_id (const char *pfx)
168 {
169 digest dgst;
170
171 for (set<string>::const_iterator i = hdrlist.begin ();
172 i != hdrlist.end ();
173 ++i)
174 dgst << *i;
175
176 dgst << retlist << arglist << body;
177
178 id = pfx + (string) dgst;
179 }
180
181 /////////////////////////////////////////////////////////////////////////////
182
183 funbuild::funbuild (env &e, const char *rettype, ...)
184 : e(e)
185 {
186 retlist = rettype;
187
188 std::ostringstream argl;
189
190 va_list ap;
191 va_start (ap, rettype);
192
193 int args = 0;
194 while ((rettype = va_arg (ap, const char *)))
195 {
196 if (args++)
197 argl << ", ";
198
199 argl << rettype << " a" << args;
200 }
201
202 va_end (ap);
203
204 arglist = argl.str ();
205 }
206
207 void funbuild::header (const std::string &hdr)
208 {
209 hdrlist.insert (hdr);
210 }
211
212 funbuild &funbuild::operator <<(const char *src)
213 {
214 bodystream << src;
215 }
216
217 funbuild &funbuild::operator <<(const string &src)
218 {
219 bodystream << src;
220 }
221
222 fun *funbuild::get ()
223 {
224 body = bodystream.str ();
225
226 update_id ("f_");
227
228 e.register_fragment (*this);
229
230 return new fun (e, id);
231 }
232
233 /////////////////////////////////////////////////////////////////////////////
234
235 fun::fun (env &e, const funid &id)
236 : e(e), id(id), funptr(0)
237 {
238 }
239
240 void *fun::ptr ()
241 {
242 if (!funptr)
243 funptr = e.sym (id, id.c_str ());
244
245 return funptr;
246 }
247
248 /////////////////////////////////////////////////////////////////////////////
249
250 env::env (const string &path, bool temporary)
251 : path(path), temporary(temporary)
252 {
253 bool create;
254 mode_t oflags = O_RDWR | O_EXCL;
255
256 if (is_null (path))
257 {
258 temporary = true;
259 this->path = tmpnam (0);
260 }
261
262 create = mkdir (this->path.c_str (), 0777) == 0;
263
264 if (!create && temporary)
265 FATAL ("error while creating libcpjit temporary directory" << ERRNO);
266
267 idxpath = this->path + "/libcpjit.idx";
268 idxfd = open (idxpath.c_str (), O_RDWR | (create ? O_CREAT | O_EXCL : 0), 0666);
269 if (idxfd < 0)
270 FATAL (path << ": unable to open index file " << idxpath);
271
272 lock ();
273
274 if (create)
275 {
276 idxstamp = get_stamp (idxfd) - 1;
277 nextid = 0;
278 save_index ();
279 }
280
281 load_index ();
282 unlock ();
283 compact ();
284 }
285
286 env::~env ()
287 {
288 if (temporary)
289 clear ();
290 }
291
292 void env::clear ()
293 {
294 DIR *dir = opendir (path.c_str ());
295
296 if (dir)
297 {
298 struct dirent *de;
299 while ((de = readdir (dir)))
300 {
301 if (de->d_name [0] == '.')
302 continue;
303
304 string f = path + "/" + de->d_name;
305 unlink (f.c_str ());
306 }
307
308 closedir (dir);
309 }
310
311 if (rmdir (path.c_str ()))
312 FATAL (path << ": unable to remove libcpjit directory" << ERRNO);
313 }
314
315 void env::dolock (bool lock)
316 {
317 struct flock fl;
318
319 fl.l_type = lock ? F_WRLCK : F_UNLCK;
320 fl.l_whence = SEEK_SET;
321 fl.l_start = 0;
322 fl.l_len = 0;
323
324 while (fcntl (idxfd, F_SETLKW, &fl) < 0)
325 if (errno != EINTR)
326 FATAL (path << ": unable to lock index file" << ERRNO);
327 }
328
329 void env::lock ()
330 {
331 dolock (true);
332 }
333
334 void env::unlock ()
335 {
336 dolock (false);
337 }
338
339 bool
340 env::load_index ()
341 {
342 // caller must lock()
343
344 long stamp = get_stamp (idxfd);
345
346 if (stamp == idxstamp)
347 return false;
348
349 idxstamp = stamp;
350
351 fstream f (idxpath.c_str (), ios::in);
352
353 if (!f)
354 FATAL (idxpath << ": unable to read index" << ERRNO);
355
356 f >> nextid;
357
358 if (nextid != IDX_VERSION)
359 FATAL (idxpath << ": index file corrupted");
360
361 f >> nextid;
362
363 id2file.clear ();
364
365 while (f)
366 {
367 string id; f >> id;
368
369 if (id == "-")
370 break;
371
372 string fileid; f >> fileid;
373
374 id2file [id] = fileid;
375
376 }
377
378 string end; f >> end;
379
380 if (end != "__end__" || !f)
381 FATAL (idxpath << ": index file corrupted");
382
383 f.close ();
384
385 return true;
386 }
387
388 void
389 env::save_index ()
390 {
391 // caller must lock()+load_index()
392
393 std::fstream f (idxpath.c_str ());
394
395 if (!f)
396 FATAL (idxpath << ": unable to write index" << ERRNO);
397
398 f << IDX_VERSION << "\n"
399 << nextid << "\n";
400
401 for (map<string, string>::const_iterator i = id2file.begin ();
402 i != id2file.end ();
403 ++i)
404 f << (*i).first << " " << (*i).second << "\n";
405
406 f << "-\n";
407 f << "__end__\n";
408
409 f.close ();
410
411 set_stamp (idxpath, ++idxstamp);
412 }
413
414 void
415 env::compact ()
416 {
417 lock ();
418 save_index ();
419 unlock ();
420 }
421
422 void env::register_fragment (const funsrc &fragment)
423 {
424 fragments.push_back (fragment);
425 }
426
427 void *env::dlsym (const string &id, const char *symbol)
428 {
429 assert (id2file.find (id) != id2file.end ());
430
431 const string &fileid = id2file [id];
432
433 void *so;
434
435 // load so
436 if (file2so.find (fileid) == file2so.end ())
437 {
438 string sopath = path + "/" + fileid + ".so";
439
440 so = dlopen (sopath.c_str (), RTLD_LAZY | RTLD_GLOBAL);
441 if (!so)
442 FATAL (sopath << ": so file should be there but isn't" << ERRNO);
443 }
444 else
445 so = file2so [fileid];
446
447 return ::dlsym (so, symbol);
448 }
449
450 void *env::sym (const string &id, const char *symbol)
451 {
452 // not yet compiled?
453 if (id2file.find (id) == id2file.end ())
454 {
455 lock ();
456
457 // nobody else compiled it?
458 if (!load_index () || id2file.find (id) == id2file.end ())
459 {
460 ostringstream sfile;
461 sfile << nextid++;
462 string fileid = sfile.str ();
463 string base = path + "/" + fileid;
464
465 string fc = base + ".c";
466 string fo = base + ".o";
467 string fso = base + ".so";
468
469 std::ofstream f (fc.c_str ());
470 set<string> hdrs;
471
472 for (vector<funsrc>::const_iterator i = fragments.begin ();
473 i != fragments.end ();
474 ++i)
475 if (id2file.find (i->id) == id2file.end ())
476 {
477 f << "#line 1 \"" << i->id << "-hdrlist\"\n";
478
479 for (set<string>::const_iterator j = i->hdrlist.begin ();
480 j != i->hdrlist.end ();
481 ++j)
482 f << *j << "\n";
483
484 f << "#line 1 \"" << i->id << "-body\"\n"
485 << i->retlist << " " << i->id << " (" << i->arglist << ")\n"
486 << "{\n"
487 << i->body
488 << "\n}\n";
489
490 id2file [i->id] = fileid;
491 }
492
493 f.close ();
494 fragments.clear ();
495
496 system ((CC " " CFLAGS " -o " + fo + " " + fc).c_str ());
497 unlink (fc.c_str ());
498 system ((LD " " LFLAGS " -o " + fso + " " + fo).c_str ());
499
500 save_index ();
501 }
502
503 unlock ();
504 }
505
506 void *p = dlsym (id, symbol);
507
508 if (!p)
509 {
510 lock ();
511 load_index ();
512 unlock ();
513 p = dlsym (id, symbol);
514 }
515
516 if (!p)
517 FATAL ("symbol " << id << ":" << symbol << " not where it should be");
518
519 return p;
520 }
521
522 }