/* cpjit.C -- c portable jit in time compiler Copyright (C) 2005 Marc Lehmann This file is part of libcpjit. libcpjit is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with gvpe; if not, write to the Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include "cpjit.h" #include #include #include #include #include #include #include #include #include #include #include #include // for locking #include #include #include #include "md5.h" #include #define STR_(str) #str #define STR(str) STR_(str) namespace cpjit { const char typestr::str[] = "unsigned " STR(TYPE_INT8); const char typestr::str[] = "unsigned " STR(TYPE_INT16); const char typestr::str[] = "unsigned " STR(TYPE_INT32); const char typestr::str[] = "unsigned " STR(TYPE_INT64); const char typestr::str[] = "signed " STR(TYPE_INT8); const char typestr::str[] = STR(TYPE_INT16); const char typestr::str[] = STR(TYPE_INT32); const char typestr::str[] = STR(TYPE_INT64); const char typestr::str[] = "float"; const char typestr::str[] = "double"; const char typestr::str[] = "void" " *"; const char typestr::str[] = "unsigned " STR(TYPE_INT8) " *"; const char typestr::str[] = "unsigned " STR(TYPE_INT16) " *"; const char typestr::str[] = "unsigned " STR(TYPE_INT32) " *"; const char typestr::str[] = "unsigned " STR(TYPE_INT64) " *"; const char typestr::str[] = "signed " STR(TYPE_INT8) " *"; const char typestr::str[] = STR(TYPE_INT16) " *"; const char typestr::str[] = STR(TYPE_INT32) " *"; const char typestr::str[] = STR(TYPE_INT64) " *"; const char typestr::str[] = "float" " *"; const char typestr::str[] = "double" " *"; struct digest { unsigned char data[16]; digest (const std::string &str); operator std::string(); }; digest::digest (const std::string &str) { MD5_CTX ctx; MD5Init (&ctx); MD5Update (&ctx, (const unsigned char *)str.c_str (), str.size ()); MD5Final (&ctx); memcpy (data, ctx.digest, sizeof (data)); } digest::operator std::string () { std::ostringstream s; for (int i = 0; i < 15; i++) s << std::setfill ('0') << std::hex << std::setw (2) << (int)data [i]; return s.str (); } static inline bool is_null (const std::string &s) { return s.empty (); } // error exception builder struct error : std::ostringstream { ~error () { throw std::runtime_error (str ()); } }; #define FATAL(msg) do { error o; o << msg << std::endl; } while (0) #define ERRNO " (" << strerror (errno) << ")" env::env (const std::string &path, bool temporary) : path(path), temporary(temporary) { if (is_null (path)) { this->path = tmpnam (0); if (mkdir (this->path.c_str (), 0777)) FATAL ("error while creating libcpjit temporary directory" << ERRNO); temporary = true; } std::string index = this->path + "/libcpjit.idx"; idxfd = open (index.c_str (), O_RDWR|O_CREAT, 0666); if (idxfd < 0) FATAL (path << ": unable to open index file" << index); lock (); load_index (); unlock (); nextid = 0; } env::~env () { if (temporary) { DIR *dir = opendir (path.c_str ()); if (dir) { struct dirent *de; while ((de = readdir (dir))) { if (de->d_name [0] == '.') continue; std::string f = path + "/" + de->d_name; unlink (f.c_str ()); } closedir (dir); } if (rmdir (path.c_str ())) FATAL (path << ": unable to remove libcpjit temporary directory" << ERRNO); } } void env::dolock (bool lock) { struct flock fl; fl.l_type = lock ? F_WRLCK : F_UNLCK; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 0; while (fcntl (idxfd, F_SETLKW, &fl) < 0) if (errno != EINTR) FATAL (path << ": unable to lock index file" << ERRNO); } void env::lock () { dolock (true); } void env::unlock () { dolock (false); } void env::load_index () { std::ifstream f ((this->path + "/libcpjit.idx").c_str ()); assert (("not a cpjit directory", f)); f.close (); } std::string env::genid () { std::ostringstream os; os << "i" << ++nextid; return os.str (); } fun::fun (env &e, const idstr &id) : e(e), id(id), funptr(0) { } void fun::set_source (const std::string source) { this->source = source; } void *fun::ptr () { if (!funptr) { std::string base = e.path + "/" + (std::string) digest (source); std::string fc = base + ".c"; std::string fo = base + ".o"; std::string fso = base + ".so"; std::string fa = base + ".a"; std::ofstream f (fc.c_str ()); f << source; f.close (); system (("gcc -O6 -c -o " + fo + " " + fc).c_str ()); unlink (fc.c_str ()); system (("gcc -O6 -nostdlib -lgcc -shared -o " + fso + " " + fo).c_str ()); printf ("%s\n", fso.c_str ()); sleep (10); void *so = dlopen (fso.c_str (), RTLD_LAZY); assert (so); funptr = dlsym (so, id.c_str ()); assert (funptr); } return funptr; } funbuild::funbuild (env &e, const idstr &id, const char *rettype, ...) : e(e), id(id) { std::ostringstream hdrs; if (is_null (id)) this->id = e.genid (); hdrs << rettype << " " << this->id << "("; va_list ap; va_start (ap, rettype); int args = 0; while ((rettype = va_arg (ap, const char *))) { if (args++) hdrs << ", "; hdrs << rettype << " a" << args; } va_end (ap); hdrs << ")\n{\n"; hdr = hdrs.str (); } std::string funbuild::finish () { *this << "\n}\n"; return hdr + str (); } }