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

# User Rev Content
1 root 1.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 root 1.2 #include <cassert>
27     #include <cstdlib>
28 root 1.1 #include <cstdarg>
29 root 1.2 #include <cstdio>
30 root 1.4 #include <cstring>
31     #include <cerrno>
32    
33 root 1.10 #include <iostream>
34 root 1.1 #include <sstream>
35 root 1.2 #include <fstream>
36 root 1.4 #include <stdexcept>
37     #include <iomanip>
38    
39     #include <unistd.h>
40     #include <fcntl.h> // for locking
41 root 1.2
42     #include <sys/stat.h>
43     #include <sys/types.h>
44     #include <dirent.h>
45 root 1.8 #include <utime.h>
46 root 1.2
47 root 1.4 #include "md5.h"
48    
49 root 1.2 #include <dlfcn.h>
50 root 1.1
51     #define STR_(str) #str
52     #define STR(str) STR_(str)
53    
54 root 1.10 #define IDX_VERSION 1
55    
56 root 1.1 namespace cpjit
57     {
58    
59 root 1.10 using namespace std;
60    
61 root 1.1 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 root 1.2 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 root 1.4 const char typestr<FLT >::str[] = "float";
70     const char typestr<DBL >::str[] = "double";
71 root 1.1
72 root 1.2 const char typestr<PTR >::str[] = "void" " *";
73    
74 root 1.1 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 root 1.2 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 root 1.4 const char typestr<FLT *>::str[] = "float" " *";
83     const char typestr<DBL *>::str[] = "double" " *";
84    
85 root 1.8 /////////////////////////////////////////////////////////////////////////////
86    
87 root 1.5 struct digest : md5_ctx
88 root 1.4 {
89 root 1.12 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 root 1.10 operator string();
101 root 1.4 };
102    
103 root 1.10 digest::operator string ()
104 root 1.4 {
105 root 1.5 unsigned char data[16];
106 root 1.4 std::ostringstream s;
107    
108 root 1.5 md5_finish_ctx (this, data);
109    
110     for (int i = 0; i < 16; i++)
111 root 1.4 s << std::setfill ('0') << std::hex << std::setw (2) << (int)data [i];
112    
113     return s.str ();
114     }
115 root 1.1
116 root 1.8 /////////////////////////////////////////////////////////////////////////////
117    
118 root 1.2 static inline bool
119 root 1.10 is_null (const string &s)
120 root 1.2 {
121     return s.empty ();
122     }
123    
124 root 1.8 /////////////////////////////////////////////////////////////////////////////
125    
126 root 1.4 // 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 root 1.8 /////////////////////////////////////////////////////////////////////////////
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 root 1.10 static void set_stamp (const string &path, long stamp)
151 root 1.8 {
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 root 1.12 /////////////////////////////////////////////////////////////////////////////
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 root 1.10 env::env (const string &path, bool temporary)
251 root 1.2 : path(path), temporary(temporary)
252     {
253 root 1.10 bool create;
254 root 1.6 mode_t oflags = O_RDWR | O_EXCL;
255    
256 root 1.2 if (is_null (path))
257     {
258 root 1.10 temporary = true;
259 root 1.2 this->path = tmpnam (0);
260 root 1.10 }
261 root 1.2
262 root 1.10 create = mkdir (this->path.c_str (), 0777) == 0;
263 root 1.2
264 root 1.10 if (!create && temporary)
265     FATAL ("error while creating libcpjit temporary directory" << ERRNO);
266 root 1.6
267 root 1.8 idxpath = this->path + "/libcpjit.idx";
268 root 1.10 idxfd = open (idxpath.c_str (), O_RDWR | (create ? O_CREAT | O_EXCL : 0), 0666);
269 root 1.4 if (idxfd < 0)
270 root 1.10 FATAL (path << ": unable to open index file " << idxpath);
271 root 1.4
272     lock ();
273 root 1.10
274     if (create)
275     {
276     idxstamp = get_stamp (idxfd) - 1;
277     nextid = 0;
278     save_index ();
279     }
280    
281 root 1.4 load_index ();
282 root 1.10 unlock ();
283 root 1.9 compact ();
284 root 1.2 }
285    
286     env::~env ()
287     {
288     if (temporary)
289 root 1.5 clear ();
290     }
291    
292     void env::clear ()
293     {
294     DIR *dir = opendir (path.c_str ());
295    
296     if (dir)
297 root 1.2 {
298 root 1.5 struct dirent *de;
299     while ((de = readdir (dir)))
300 root 1.2 {
301 root 1.5 if (de->d_name [0] == '.')
302     continue;
303 root 1.2
304 root 1.10 string f = path + "/" + de->d_name;
305 root 1.5 unlink (f.c_str ());
306 root 1.2 }
307    
308 root 1.5 closedir (dir);
309 root 1.2 }
310 root 1.5
311     if (rmdir (path.c_str ()))
312     FATAL (path << ": unable to remove libcpjit directory" << ERRNO);
313 root 1.2 }
314    
315 root 1.4 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 root 1.10 bool
340 root 1.4 env::load_index ()
341     {
342 root 1.8 // caller must lock()
343    
344     long stamp = get_stamp (idxfd);
345    
346     if (stamp == idxstamp)
347 root 1.10 return false;
348 root 1.8
349     idxstamp = stamp;
350    
351 root 1.10 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 root 1.4 }
387    
388 root 1.5 void
389     env::save_index ()
390 root 1.2 {
391 root 1.8 // caller must lock()+load_index()
392    
393 root 1.10 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 root 1.8 set_stamp (idxpath, ++idxstamp);
412 root 1.2 }
413    
414 root 1.9 void
415     env::compact ()
416     {
417 root 1.10 lock ();
418     save_index ();
419     unlock ();
420 root 1.9 }
421    
422 root 1.12 void env::register_fragment (const funsrc &fragment)
423 root 1.10 {
424 root 1.12 fragments.push_back (fragment);
425 root 1.10 }
426 root 1.8
427 root 1.10 void *env::dlsym (const string &id, const char *symbol)
428 root 1.3 {
429 root 1.10 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 root 1.3 }
449    
450 root 1.10 void *env::sym (const string &id, const char *symbol)
451 root 1.1 {
452 root 1.10 // not yet compiled?
453     if (id2file.find (id) == id2file.end ())
454 root 1.2 {
455 root 1.10 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 root 1.12 set<string> hdrs;
471 root 1.5
472 root 1.12 for (vector<funsrc>::const_iterator i = fragments.begin ();
473 root 1.10 i != fragments.end ();
474     ++i)
475 root 1.12 if (id2file.find (i->id) == id2file.end ())
476 root 1.10 {
477 root 1.12 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 root 1.4
490 root 1.12 id2file [i->id] = fileid;
491 root 1.10 }
492    
493     f.close ();
494     fragments.clear ();
495 root 1.2
496 root 1.11 system ((CC " " CFLAGS " -o " + fo + " " + fc).c_str ());
497     unlink (fc.c_str ());
498     system ((LD " " LFLAGS " -o " + fso + " " + fo).c_str ());
499 root 1.5
500 root 1.10 save_index ();
501     }
502    
503     unlock ();
504 root 1.2 }
505    
506 root 1.10 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 root 1.1 }