632e1eece8365cc3f76d520f02f1ff88e3e545ca
[libfirm] / ir / be / bejavacoal.c
1 /*
2  * Copyright (C) 1995-2007 University of Karlsruhe.  All right reserved.
3  *
4  * This file is part of libFirm.
5  *
6  * This file may be distributed and/or modified under the terms of the
7  * GNU General Public License version 2 as published by the Free Software
8  * Foundation and appearing in the file LICENSE.GPL included in the
9  * packaging of this file.
10  *
11  * Licensees holding valid libFirm Professional Edition licenses may use
12  * this file in accordance with the libFirm Commercial License.
13  * Agreement provided with the Software.
14  *
15  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
16  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE.
18  */
19
20 /**
21  * @file
22  * @brief       Interface for external Java coalescer.
23  * @author      Sebastian Hack
24  * @version     $Id$
25  */
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include <libcore/lc_opts.h>
31 #include <libcore/lc_opts_enum.h>
32
33 #ifdef _WIN32
34 #include <windows.h>
35 #else
36 #include <dlfcn.h>
37 #endif
38
39 #include <signal.h>
40 #include <stdlib.h>
41 #include <assert.h>
42 #include <stdio.h>
43
44 #include "bejavacoal.h"
45 #include "irtools.h"
46 #include "bemodule.h"
47
48 #ifdef WITH_JVM
49
50 /* Path to the jar file. A little OS dependent convenience. */
51 #ifdef _WIN32
52 static char jar_file[512] = "y:\\user\\hack\\public\\coal.jar";
53 #else
54 static char jar_file[512] = "/ben/hack/public/coal.jar";
55 #endif
56
57 static char cls_name[256] = "coalescing/mst/safe/Algo";
58
59 /* Name of the JVM dll/so */
60 static char jvm_lib[512] = { 0 };
61
62 static const lc_opt_table_entry_t options[] = {
63         LC_OPT_ENT_STR      ("jvm",  "absolute path to jvm dll",                    jvm_lib, sizeof(jvm_lib)),
64         LC_OPT_ENT_STR      ("jar",  "jar file of the coalescer",                   jar_file, sizeof(jar_file)),
65         LC_OPT_ENT_STR      ("cls",  "name of the class providing the factory",     cls_name, sizeof(cls_name)),
66         LC_OPT_ENT_NULL
67 };
68
69 void be_init_javacoal(void)
70 {
71         lc_opt_entry_t *be_grp = lc_opt_get_grp(firm_opt_get_root(), "be");
72         lc_opt_entry_t *ra_grp = lc_opt_get_grp(be_grp, "ra");
73         lc_opt_entry_t *chordal_grp = lc_opt_get_grp(ra_grp, "chordal");
74         lc_opt_entry_t *jc_grp = lc_opt_get_grp(chordal_grp, "jc");
75         lc_opt_add_table(jc_grp, options);
76 }
77
78 BE_REGISTER_MODULE_CONSTRUCTOR(be_init_javacoal);
79
80 #include <jni.h>
81
82 typedef struct _jni_env_t {
83         JavaVM *jvm;
84         JNIEnv *jni;
85 } jni_env_t;
86
87 /*
88
89         Ugly code to retrieve the JVM dll/so file.
90
91 */
92
93 #ifdef _WIN32
94 /* Win32 version */
95 static void *find_jvm_symbol(const char *vmlibpath, const char *sym)
96 {
97         HINSTANCE hVM = LoadLibrary(vmlibpath);
98         return hVM ? GetProcAddress(hVM, sym) : NULL;
99 }
100
101 #define JRE_KEY "SOFTWARE\\JavaSoft\\Java Development Kit"
102
103 static char *locate_jvm_lib(char *path, size_t path_len)
104 {
105         char version[32];
106         char buf[256];
107         DWORD version_len = sizeof(version);
108         DWORD dwPathLen = path_len;
109         HKEY hKey;
110
111         RegOpenKeyEx(HKEY_LOCAL_MACHINE, JRE_KEY, 0, KEY_QUERY_VALUE, &hKey);
112         RegQueryValueEx(hKey, "CurrentVersion", NULL, NULL, (LPBYTE) version, &version_len);
113         RegCloseKey(hKey);
114
115         _snprintf(buf, sizeof(buf), JRE_KEY "\\%s", version);
116         RegOpenKeyEx(HKEY_LOCAL_MACHINE, buf, 0, KEY_QUERY_VALUE, &hKey);
117         RegQueryValueEx(hKey, "JavaHome", NULL, NULL, (LPBYTE) path, &dwPathLen);
118         RegCloseKey(hKey);
119
120         strncat(path, "\\jre\\bin\\server\\jvm.dll", path_len);
121         return path;
122 }
123
124 #else /* ! _WIN32 */
125 /* Unix version */
126 static void *find_jvm_symbol(const char *vmlibpath, const char *sym)
127 {
128         void *libVM = dlopen(vmlibpath, RTLD_LAZY);
129         return libVM ? dlsym(libVM, sym) : NULL;
130 }
131
132 static char *locate_jvm_lib(char *path, size_t n)
133 {
134         (void) path;
135         (void) n;
136         return NULL;
137 }
138 #endif /* _WIN32 */
139
140 static int start_vm(jni_env_t *env, int argc, char *argv[])
141 {
142         int i;
143         long ret;
144         JavaVMInitArgs args;
145         JavaVMOption *opts;
146
147         long (JNICALL * create_func)(JavaVM **, void **, void *) = find_jvm_symbol(jvm_lib, "JNI_CreateJavaVM");
148
149         if(!create_func) {
150                 fprintf(stderr, "could not find JVM creation function\n");
151                 exit(1);
152         }
153
154         memset(&args, 0, sizeof(args));
155         opts = malloc(argc * sizeof(opts[0]));
156         for(i = 0; i < argc; ++i) {
157                 opts[i].optionString = argv[i];
158                 opts[i].extraInfo    = NULL;
159         }
160
161         args.version  = JNI_VERSION_1_4;
162         args.nOptions = argc;
163         args.options  = opts;
164         args.ignoreUnrecognized = JNI_FALSE;
165
166         ret = create_func(&env->jvm, (void **) &env->jni, &args);
167         free(opts);
168         if(ret != JNI_OK) {
169                 fprintf(stderr, "JNI_CreateJavaVM returned errrocode %ld\n" , ret);
170                 return 0;
171         }
172
173         return 1;
174 }
175
176 static void stop_vm(jni_env_t *env)
177 {
178         JavaVM *jvm = env->jvm;
179         (*jvm)->DetachCurrentThread(jvm);
180         (*jvm)->DestroyJavaVM(jvm);
181 }
182
183 static int jvm_inited = 0;
184 static jni_env_t env;
185 void (*old_int_handler)(int);
186 void (*old_abrt_handler)(int);
187
188 static void sig_jvm_destroy_at_exit(int signal)
189 {
190         if(jvm_inited)
191                 stop_vm(&env);
192
193         switch(signal) {
194         case SIGABRT:
195                 old_abrt_handler(signal);
196                 break;
197         case SIGINT:
198                 old_int_handler(signal);
199                 break;
200         default:;
201         }
202 }
203
204 static void jvm_destroy_at_exit(void)
205 {
206         sig_jvm_destroy_at_exit(0);
207 }
208
209 static jni_env_t *get_jvm(void)
210 {
211         char cp_param[512];
212         char *args[1];
213
214         if(!jvm_inited) {
215                 /* Find the dll */
216                 if(strlen(jvm_lib) == 0) {
217                         if(!locate_jvm_lib(jvm_lib, sizeof(jvm_lib))) {
218                                 fprintf(stderr, "could not find jvm library\n");
219                                 exit(1);
220                         }
221                 }
222
223                 snprintf(cp_param, sizeof(cp_param), "-Djava.class.path=%s", jar_file);
224                 args[0] = cp_param;
225                 if(!start_vm(&env, sizeof(args) / sizeof(args[0]), args)) {
226                         fprintf(stderr, "Couldn't initialize java VM\n");
227                         abort();
228                 }
229                 jvm_inited = 1;
230                 old_int_handler  = signal(SIGINT,  sig_jvm_destroy_at_exit);
231                 old_abrt_handler = signal(SIGABRT, sig_jvm_destroy_at_exit);
232                 atexit(jvm_destroy_at_exit);
233         }
234
235         return &env;
236 }
237
238
239 static void check(jni_env_t *env, const char *file, int line)
240 {
241         JNIEnv *jni = env->jni;
242         jboolean exc = (*jni)->ExceptionCheck(jni);
243         if(exc) {
244                 fprintf(stderr, "%s:%d: ", file, line);
245                 (*jni)->ExceptionDescribe(jni);
246                 (*jni)->ExceptionClear(jni);
247                 stop_vm(env);
248                 abort();
249         }
250 }
251
252 #define CHECK(env) check(env, __FILE__, __LINE__)
253
254 enum {
255         mth_add_int_edge,
256         mth_add_aff_edge,
257         mth_set_color,
258         //mth_set_debug,
259         mth_get_color,
260         mth_forbid_color,
261         mth_coalesce,
262         mth_dump,
263         mth_finish,
264         mth_last
265 };
266
267 struct _mth_info_t {
268         const char *name;
269         const char *sig;
270 };
271
272 static const struct _mth_info_t mthis[mth_last] = {
273         { "addIntEdge",  "(II)V"                   }, /* public void addIntEdge(int, int); */
274         { "addAffEdge",  "(III)V"                  }, /* public void addAffEdge(int, int, int); */
275         { "setColor",    "(II)V"                   }, /* public void setColor(int, int); */
276         //{ "setDebug",    "(ILjava/lang/String;)V"  }, /* public void setDebug(int, String); */
277         { "getColor",    "(I)I"                    }, /* public int getColor(int); */
278         { "forbidColor", "(II)V"                   }, /* public void forbidColor(int, int); */
279         { "coalesce",    "()V"                     }, /* public void coalesce(); */
280         { "dump",        "(Ljava/lang/String;)V"   }, /* public void dump(String); */
281         { "finish",      "()V"                     }  /* public void finish(); */
282 };
283
284 /* public static coalescing.Extern createExtern(java.lang.String, int, int, int); */
285 static const struct _mth_info_t mthi_factory = {
286         "createExtern", "(Ljava/lang/String;III)Lcoalescing/Extern;"
287 };
288
289 struct _be_java_coal_t {
290         jni_env_t *env;
291         jclass    cls;
292         jobject   obj;
293
294         jmethodID mth_ids[mth_last];
295 };
296
297 static void jc_call_void(be_java_coal_t *c, int mth_index, ...)
298 {
299         JNIEnv *jni   = c->env->jni;
300         jmethodID mid = c->mth_ids[mth_index];
301
302         va_list args;
303
304         va_start(args, mth_index);
305         (*jni)->CallVoidMethodV(jni, c->obj, mid, args);
306         CHECK(c->env);
307         va_end(args);
308 }
309
310 static int jc_call_int(be_java_coal_t *c, int mth_index, ...)
311 {
312         JNIEnv *jni   = c->env->jni;
313         jmethodID mid = c->mth_ids[mth_index];
314
315         int res;
316         va_list args;
317
318         va_start(args, mth_index);
319         res = (*jni)->CallIntMethodV(jni, c->obj, mid, args);
320         CHECK(c->env);
321         va_end(args);
322
323         return res;
324 }
325
326 be_java_coal_t *be_java_coal_init(const char *graph_name, int n_nodes, int n_regs, int dbg_level)
327 {
328         be_java_coal_t *c;
329         jni_env_t *env = get_jvm();
330         JNIEnv *jni = env->jni;
331         jmethodID fact;
332         jclass cls;
333         jstring str;
334         int i;
335
336         c = malloc(sizeof(c[0]));
337         memset(c, 0, sizeof(c[0]));
338         c->env = env;
339
340         /* Find the class we are are looking for. */
341         cls = (*jni)->FindClass(jni, cls_name);
342         CHECK(env);
343
344         /* Get the static factory method. */
345         fact = (*jni)->GetStaticMethodID(jni, cls, mthi_factory.name, mthi_factory.sig);
346         CHECK(env);
347
348         /* Call the factory. */
349         str = (*jni)->NewStringUTF(jni, graph_name);
350         CHECK(env);
351         c->obj = (*jni)->CallStaticObjectMethod(jni, cls, fact, str, n_nodes, n_regs, dbg_level);
352         CHECK(env);
353         c->cls = (*jni)->GetObjectClass(jni, c->obj);
354
355         /* Reference the created object. */
356         c->obj = (*jni)->NewGlobalRef(jni, c->obj);
357         CHECK(env);
358
359         /* Lookup the member methods of the object. */
360         for(i = 0; i < mth_last; ++i) {
361                 c->mth_ids[i] = (*jni)->GetMethodID(jni, c->cls, mthis[i].name, mthis[i].sig);
362                 CHECK(env);
363         }
364
365         return c;
366 }
367
368 void be_java_coal_destroy(be_java_coal_t *c) {
369         JNIEnv *jni = c->env->jni;
370         jc_call_void(c, mth_finish);
371         (*jni)->DeleteGlobalRef(jni, c->obj);
372         free(c);
373 }
374
375 void be_java_coal_add_int_edge(be_java_coal_t *c, int n, int m)
376 {
377         jc_call_void(c, mth_add_int_edge, (jint) n, (jint) m);
378 }
379
380 void be_java_coal_add_aff_edge(be_java_coal_t *c, int n, int m, int weight)
381 {
382         jc_call_void(c, mth_add_aff_edge, (jint) n, (jint) m, (jint) weight);
383 }
384
385 void be_java_coal_set_color(be_java_coal_t *c, int n, int col)
386 {
387         jc_call_void(c, mth_set_color, (jint) n, (jint) col);
388 }
389
390 void be_java_coal_set_debug(be_java_coal_t *c, int n, const char *dbg)
391 {
392         (void) c;
393         (void) n;
394         (void) dbg;
395 #if 0
396         JNIEnv *jni   = c->env->jni;
397         jmethodID mid = c->mth_ids[mth_set_debug];
398         jstring str;
399
400         str = (*jni)->NewStringUTF(jni, dbg);
401         CHECK(c->env);
402         (*jni)->CallVoidMethod(jni, c->obj, mid, (jint) n, str);
403         CHECK(c->env);
404 #endif /* if 0 */
405 }
406
407 void be_java_coal_forbid_color(be_java_coal_t *c, int n, int col)
408 {
409         jc_call_void(c, mth_forbid_color, (jint) n, (jint) col);
410 }
411
412 void be_java_coal_coalesce(be_java_coal_t *c)
413 {
414         jc_call_void(c, mth_coalesce);
415 }
416
417 void be_java_coal_dump(be_java_coal_t *c, const char *fn)
418 {
419         JNIEnv *jni   = c->env->jni;
420         jmethodID mid = c->mth_ids[mth_dump];
421         jstring str;
422
423         str = (*jni)->NewStringUTF(jni, fn);
424         CHECK(c->env);
425         (*jni)->CallVoidMethod(jni, c->obj, mid, str);
426         CHECK(c->env);
427 }
428
429 int be_java_coal_get_color(be_java_coal_t *c, int n)
430 {
431         return jc_call_int(c, mth_get_color, (jint) n);
432 }
433
434 void be_java_coal_start_jvm(void)
435 {
436         get_jvm();
437 }
438
439 #else /* ! WITH_JVM */
440
441 be_java_coal_t *be_java_coal_init(const char *graph_name, int n_nodes, int n_regs, int dbg_level)
442 {
443         assert(0 && "use --enable-jvm");
444         return NULL;
445 }
446
447 void be_java_coal_destroy(be_java_coal_t *c)
448 {
449         assert(0 && "use --enable-jvm");
450 }
451
452
453 void be_java_coal_add_int_edge(be_java_coal_t *c, int n, int m)
454 {
455         assert(0 && "use --enable-jvm");
456 }
457
458 void be_java_coal_add_aff_edge(be_java_coal_t *c, int n, int m, int weight)
459 {
460         assert(0 && "use --enable-jvm");
461 }
462
463 void be_java_coal_set_color(be_java_coal_t *c, int n, int col)
464 {
465         assert(0 && "use --enable-jvm");
466 }
467
468 void be_java_coal_set_debug(be_java_coal_t *c, int n, const char *dbg)
469 {
470         assert(0 && "use --enable-jvm");
471 }
472
473 void be_java_coal_forbid_color(be_java_coal_t *c, int n, int col)
474 {
475         assert(0 && "use --enable-jvm");
476 }
477
478 void be_java_coal_coalesce(be_java_coal_t *c)
479 {
480         assert(0 && "use --enable-jvm");
481 }
482
483 void be_java_coal_dump(be_java_coal_t *c, const char *fn)
484 {
485         assert(0 && "use --enable-jvm");
486 }
487
488 int be_java_coal_get_color(be_java_coal_t *c, int n)
489 {
490         assert(0 && "use --enable-jvm");
491         return -1;
492 }
493
494 void be_java_coal_start_jvm(void)
495 {
496 }
497
498 #endif /* WITH_JVM */