2010-02-02 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / mini / mini-llvm.c
index f16d407a6995daedd0d2d83ce9b4f8dd361c7275..d899d95252baf473e57e1ab8a0dba5e35e4c3495 100644 (file)
@@ -8,8 +8,12 @@
 #include <mono/metadata/debug-helpers.h>
 #include <mono/metadata/mempool-internals.h>
 
+#ifndef __STDC_LIMIT_MACROS
 #define __STDC_LIMIT_MACROS
+#endif
+#ifndef __STDC_CONSTANT_MACROS
 #define __STDC_CONSTANT_MACROS
+#endif
 
 #include "llvm-c/Core.h"
 #include "llvm-c/ExecutionEngine.h"
@@ -68,6 +72,8 @@ typedef struct {
        MonoMethodSignature *sig;
        GSList *builders;
        GHashTable *region_to_handler;
+       LLVMBuilderRef alloca_builder;
+       LLVMValueRef last_alloca;
 
        char temp_name [32];
 } EmitContext;
@@ -458,6 +464,7 @@ op_to_llvm_type (int opcode)
        case OP_IMUL_OVF_UN:
                return LLVMInt32Type ();
        case OP_LADD_OVF:
+       case OP_LADD_OVF_UN:
        case OP_LSUB_OVF:
        case OP_LSUB_OVF_UN:
        case OP_LMUL_OVF:
@@ -582,6 +589,7 @@ static const char*
 simd_op_to_intrins (int opcode)
 {
        switch (opcode) {
+#if defined(TARGET_X86) || defined(TARGET_AMD64)
        case OP_MINPD:
                return "llvm.x86.sse2.min.pd";
        case OP_MINPS:
@@ -602,6 +610,7 @@ simd_op_to_intrins (int opcode)
                return "llvm.x86.sse41.pmaxuw";
        case OP_PMAXB_UN:
                return "llvm.x86.sse41.pmaxub";
+#endif
        default:
                g_assert_not_reached ();
                return NULL;
@@ -961,7 +970,7 @@ emit_call (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, LL
        LLVMBuilderRef builder = *builder_ref;
 
        // FIXME: Nested clauses
-       if (bb->region && MONO_BBLOCK_IS_IN_REGION (bb, MONO_REGION_TRY)) {
+       if (bb->region != -1 && MONO_BBLOCK_IS_IN_REGION (bb, MONO_REGION_TRY)) {
                MonoMethodHeader *header = mono_method_get_header (cfg->method);
                // FIXME: Add a macro for this
                int clause_index = (bb->region >> 8) - 1;
@@ -1041,15 +1050,27 @@ emit_cond_system_exception (EmitContext *ctx, MonoBasicBlock *bb, const char *ex
                        callee = get_plt_entry (ctx, sig, MONO_PATCH_INFO_INTERNAL_METHOD, "mono_arch_throw_corlib_exception");
                } else {
                        callee = LLVMAddFunction (ctx->module, "throw_corlib_exception", sig_to_llvm_sig (ctx, throw_sig, NULL));
+
+#ifdef TARGET_X86 
+                       /* 
+                        * LLVM generated code doesn't push the arguments, so we need another
+                        * throw trampoline.
+                        */
+                       LLVMAddGlobalMapping (ee, callee, resolve_patch (ctx->cfg, MONO_PATCH_INFO_INTERNAL_METHOD, "mono_arch_llvm_throw_corlib_exception"));
+#else
                        LLVMAddGlobalMapping (ee, callee, resolve_patch (ctx->cfg, MONO_PATCH_INFO_INTERNAL_METHOD, "mono_arch_throw_corlib_exception"));
+#endif
                }
 
                mono_memory_barrier ();
                ctx->lmodule->throw_corlib_exception = callee;
        }
 
+#ifdef TARGET_X86
+       args [0] = LLVMConstInt (LLVMInt32Type (), exc_class->type_token - MONO_TOKEN_TYPE_DEF, FALSE);
+#else
        args [0] = LLVMConstInt (LLVMInt32Type (), exc_class->type_token, FALSE);
+#endif
        /*
         * FIXME: The offset is 0, this is not a problem for exception handling
         * in general, because we don't llvm compile methods with handlers, its only
@@ -1178,7 +1199,14 @@ build_alloca (EmitContext *ctx, MonoType *t)
        while (mono_is_power_of_two (align) == -1)
                align ++;
 
-       return mono_llvm_build_alloca (ctx->builder, type_to_llvm_type (ctx, t), NULL, align, "");
+       /*
+        * Have to place all alloca's at the end of the entry bb, since otherwise they would
+        * get executed every time control reaches them.
+        */
+       LLVMPositionBuilder (ctx->alloca_builder, get_bb (ctx, ctx->cfg->bb_entry), ctx->last_alloca);
+
+       ctx->last_alloca = mono_llvm_build_alloca (ctx->alloca_builder, type_to_llvm_type (ctx, t), NULL, align, "");
+       return ctx->last_alloca;
 }
 
 /*
@@ -1212,6 +1240,8 @@ emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder, int *pindexes)
        LLVMCallInfo *linfo = ctx->linfo;
        MonoBasicBlock *bb;
 
+       ctx->alloca_builder = create_builder (ctx);
+
        /*
         * Handle indirect/volatile variables by allocating memory for them
         * using 'alloca', and storing their address in a temporary.
@@ -1258,6 +1288,8 @@ emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder, int *pindexes)
                }
        }
 
+       if (cfg->vret_addr)
+               emit_volatile_store (ctx, cfg->vret_addr->dreg);
        if (sig->hasthis)
                emit_volatile_store (ctx, cfg->args [0]->dreg);
        for (i = 0; i < sig->param_count; ++i)
@@ -1282,8 +1314,12 @@ emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder, int *pindexes)
  FAILURE:
        ;
 }
+
+/* Have to export this for AOT */
+void
+mono_personality (void);
        
-static void
+void
 mono_personality (void)
 {
        /* Not used */
@@ -1566,6 +1602,11 @@ mono_llvm_emit_method (MonoCompile *cfg)
                        LLVMValueRef eh_selector, eh_exception, personality, args [4];
                        MonoInst *exvar;
                        static gint32 mapping_inited;
+                       static int ti_generator;
+                       char ti_name [128];
+                       MonoClass **ti;
+                       LLVMValueRef type_info;
+                       int clause_index;
 
                        if (!bblocks [bb->block_num].invoke_target) {
                                /*
@@ -1577,15 +1618,51 @@ mono_llvm_emit_method (MonoCompile *cfg)
 
                        eh_selector = LLVMGetNamedFunction (module, "llvm.eh.selector");
 
-                       personality = LLVMGetNamedFunction (module, "mono_personality");
-                       if (InterlockedCompareExchange (&mapping_inited, 1, 0) == 0)
-                               LLVMAddGlobalMapping (ee, personality, mono_personality);
+                       if (cfg->compile_aot) {
+                               /* Use a dummy personality function */
+                               personality = LLVMGetNamedFunction (module, "mono_aot_personality");
+                               g_assert (personality);
+                       } else {
+                               personality = LLVMGetNamedFunction (module, "mono_personality");
+                               if (InterlockedCompareExchange (&mapping_inited, 1, 0) == 0)
+                                       LLVMAddGlobalMapping (ee, personality, mono_personality);
+                       }
 
                        i8ptr = LLVMPointerType (LLVMInt8Type (), 0);
+
+                       clause_index = (mono_get_block_region_notry (cfg, bb->region) >> 8) - 1;
+
+                       /*
+                        * Create the type info
+                        */
+                       sprintf (ti_name, "type_info_%d", ti_generator);
+                       ti_generator ++;
+
+                       if (cfg->compile_aot) {
+                               /* decode_eh_frame () in aot-runtime.c will decode this */
+                               type_info = LLVMAddGlobal (module, LLVMInt32Type (), ti_name);
+                               LLVMSetInitializer (type_info, LLVMConstInt (LLVMInt32Type (), clause_index, FALSE));
+
+                               /* 
+                                * FIXME: llc currently generates incorrect data in the LSDA:
+                                *      .byte   0x9B                                        # @TType format (indirect pcrel sdata4)
+                                * and later:
+                                * .quad        type_info_1                                 # TypeInfo
+                                */
+                               LLVM_FAILURE (ctx, "aot+clauses");
+                       } else {
+                               /* exception_cb will decode this */
+                               ti = g_malloc (sizeof (MonoExceptionClause));
+                               memcpy (ti, &mono_method_get_header (cfg->method)->clauses [clause_index], sizeof (MonoExceptionClause));
+
+                               type_info = LLVMAddGlobal (module, i8ptr, ti_name);
+
+                               LLVMAddGlobalMapping (ee, type_info, ti);
+                       }
+
                        args [0] = LLVMConstNull (i8ptr);
                        args [1] = LLVMConstBitCast (personality, i8ptr);
-                       args [2] = LLVMConstNull (i8ptr);
-                       args [3] = LLVMConstNull (LLVMInt32Type ());
+                       args [2] = type_info;
                        LLVMBuildCall (builder, eh_selector, args, 3, "");
 
                        /* Store the exception into the exvar */
@@ -2336,9 +2413,10 @@ mono_llvm_emit_method (MonoCompile *cfg)
                        case OP_LOADU4_MEM:
                        case OP_LOAD_MEM: {
                                int size = 8;
-                               LLVMValueRef index;
+                               LLVMValueRef index, addr;
                                LLVMTypeRef t;
                                gboolean sext = FALSE, zext = FALSE;
+                               gboolean is_volatile = (ins->flags & MONO_INST_FAULT);
 
                                t = load_store_to_llvm_type (ins->opcode, &size, &sext, &zext);
 
@@ -2346,20 +2424,27 @@ mono_llvm_emit_method (MonoCompile *cfg)
                                        dname = (char*)"";
 
                                /* 
-                                * We emit volatile loads because otherwise LLVM will
-                                * generate invalid code when encountering a load from a
+                                * We emit volatile loads for loads which can fault, because otherwise
+                                * LLVM will generate invalid code when encountering a load from a
                                 * NULL address.
-                                * FIXME: Avoid this somehow.
                                 */
-                               g_assert (ins->inst_offset % size == 0);
                                if ((ins->opcode == OP_LOADI8_MEM) || (ins->opcode == OP_LOAD_MEM) || (ins->opcode == OP_LOADI4_MEM) || (ins->opcode == OP_LOADU4_MEM) || (ins->opcode == OP_LOADU1_MEM) || (ins->opcode == OP_LOADU2_MEM)) {
-                                       values [ins->dreg] = mono_llvm_build_volatile_load (builder, convert (ctx, LLVMConstInt (IntPtrType (), ins->inst_imm, FALSE), LLVMPointerType (t, 0)), dname);
+                                       addr = LLVMConstInt (IntPtrType (), ins->inst_imm, FALSE);
                                } else if (ins->inst_offset == 0) {
-                                       values [ins->dreg] = mono_llvm_build_volatile_load (builder, convert (ctx, values [ins->inst_basereg], LLVMPointerType (t, 0)), dname);
+                                       addr = values [ins->inst_basereg];
+                               } else if (ins->inst_offset % size != 0) {
+                                       /* Unaligned load */
+                                       index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset, FALSE);
+                                       addr = LLVMBuildGEP (builder, convert (ctx, values [ins->inst_basereg], LLVMPointerType (LLVMInt8Type (), 0)), &index, 1, "");
                                } else {
                                        index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE);                                
-                                       values [ins->dreg] = mono_llvm_build_volatile_load (builder, LLVMBuildGEP (builder, convert (ctx, values [ins->inst_basereg], LLVMPointerType (t, 0)), &index, 1, ""), dname);
+                                       addr = LLVMBuildGEP (builder, convert (ctx, values [ins->inst_basereg], LLVMPointerType (t, 0)), &index, 1, "");
                                }
+
+                               addr = convert (ctx, addr, LLVMPointerType (t, 0));
+
+                               values [ins->dreg] = mono_llvm_build_load (builder, addr, dname, is_volatile);
+
                                if (sext)
                                        values [ins->dreg] = LLVMBuildSExt (builder, values [ins->dreg], LLVMInt32Type (), dname);
                                else if (zext)
@@ -2408,7 +2493,7 @@ mono_llvm_emit_method (MonoCompile *cfg)
                        }
 
                        case OP_CHECK_THIS:
-                               mono_llvm_build_volatile_load (builder, convert (ctx, values [ins->sreg1], LLVMPointerType (IntPtrType (), 0)), "");
+                               mono_llvm_build_load (builder, convert (ctx, values [ins->sreg1], LLVMPointerType (IntPtrType (), 0)), "", TRUE);
                                break;
                        case OP_OUTARG_VTRETADDR:
                                break;
@@ -2447,10 +2532,6 @@ mono_llvm_emit_method (MonoCompile *cfg)
                                llvm_sig = sig_to_llvm_sig (ctx, sig, cinfo);
                                CHECK_FAILURE (ctx);
 
-                               if (call->rgctx_reg) {
-                                       LLVM_FAILURE (ctx, "rgctx reg");
-                               }
-
                                virtual = (ins->opcode == OP_VOIDCALL_MEMBASE || ins->opcode == OP_CALL_MEMBASE || ins->opcode == OP_VCALL_MEMBASE || ins->opcode == OP_LCALL_MEMBASE || ins->opcode == OP_FCALL_MEMBASE);
                                calli = (ins->opcode == OP_VOIDCALL_REG || ins->opcode == OP_CALL_REG || ins->opcode == OP_VCALL_REG || ins->opcode == OP_LCALL_REG || ins->opcode == OP_FCALL_REG);
 
@@ -2518,12 +2599,13 @@ mono_llvm_emit_method (MonoCompile *cfg)
                                                                MonoJumpInfo *abs_ji = g_hash_table_lookup (cfg->abs_patches, call->fptr);
                                                                if (abs_ji) {
                                                                        /*
-                                                                        * The monitor entry/exit trampolines have their
-                                                                        * own calling convention, and call->signature
-                                                                        * doesn't include the argument.
+                                                                        * The monitor entry/exit trampolines might have
+                                                                        * their own calling convention on some platforms.
                                                                         */
+#ifndef TARGET_AMD64
                                                                        if (abs_ji->type == MONO_PATCH_INFO_MONITOR_ENTER || abs_ji->type == MONO_PATCH_INFO_MONITOR_EXIT)
                                                                                LLVM_FAILURE (ctx, "monitor enter/exit");
+#endif
                                                                        target = mono_resolve_patch_target (cfg->method, cfg->domain, NULL, abs_ji, FALSE);
                                                                        LLVMAddGlobalMapping (ee, callee, target);
                                                                }
@@ -2667,6 +2749,7 @@ mono_llvm_emit_method (MonoCompile *cfg)
                                guint32 got_offset;
                                LLVMValueRef indexes [2];
                                MonoJumpInfo *ji;
+                               LLVMValueRef got_entry_addr;
 
                                /* 
                                 * FIXME: Can't allocate from the cfg mempool since that is freed if
@@ -2686,7 +2769,25 @@ mono_llvm_emit_method (MonoCompile *cfg)
  
                                indexes [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
                                indexes [1] = LLVMConstInt (LLVMInt32Type (), (gssize)got_offset, FALSE);
-                               values [ins->dreg] = LLVMBuildLoad (builder, LLVMBuildGEP (builder, ctx->lmodule->got_var, indexes, 2, ""), dname);
+                               got_entry_addr = LLVMBuildGEP (builder, ctx->lmodule->got_var, indexes, 2, "");
+
+                               // FIXME: This doesn't work right now, because it must be
+                               // paired with an invariant.end, and even then, its only in effect
+                               // inside its basic block
+#if 0
+                               {
+                                       LLVMValueRef args [3];
+                                       LLVMValueRef ptr, val;
+
+                                       ptr = LLVMBuildBitCast (builder, got_entry_addr, LLVMPointerType (LLVMInt8Type (), 0), "ptr");
+
+                                       args [0] = LLVMConstInt (LLVMInt64Type (), sizeof (gpointer), FALSE);
+                                       args [1] = ptr;
+                                       val = LLVMBuildCall (builder, LLVMGetNamedFunction (module, "llvm.invariant.start"), args, 2, "");
+                               }
+#endif
+
+                               values [ins->dreg] = LLVMBuildLoad (builder, got_entry_addr, dname);
                                break;
                        }
                        case OP_NOT_REACHED:
@@ -2731,6 +2832,14 @@ mono_llvm_emit_method (MonoCompile *cfg)
                        }
                                */
 
+                       case OP_ABS: {
+                               LLVMValueRef args [1];
+
+                               args [0] = lhs;
+                               values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (module, "fabs"), args, 1, dname);
+                               break;
+                       }
+
                        case OP_IMIN:
                        case OP_LMIN: {
                                LLVMValueRef v = LLVMBuildICmp (builder, LLVMIntSLE, lhs, rhs, "");
@@ -2864,6 +2973,7 @@ mono_llvm_emit_method (MonoCompile *cfg)
                        case OP_IMUL_OVF_UN:
 #if SIZEOF_VOID_P == 8
                        case OP_LADD_OVF:
+                       case OP_LADD_OVF_UN:
                        case OP_LSUB_OVF:
                        case OP_LSUB_OVF_UN:
                        case OP_LMUL_OVF:
@@ -2982,6 +3092,7 @@ mono_llvm_emit_method (MonoCompile *cfg)
                        /* 
                         * SIMD
                         */
+#if defined(TARGET_X86) || defined(TARGET_AMD64)
                        case OP_XZERO: {
                                values [ins->dreg] = LLVMConstNull (type_to_llvm_type (ctx, &ins->klass->byval_arg));
                                break;
@@ -3136,6 +3247,7 @@ mono_llvm_emit_method (MonoCompile *cfg)
                                values [ins->dreg] = LLVMBuildExtractElement (builder, lhs, LLVMConstInt (LLVMInt32Type (), ins->inst_c0, FALSE), "");
                                break;
                        }
+#endif
 
                        case OP_DUMMY_USE:
                                break;
@@ -3143,6 +3255,11 @@ mono_llvm_emit_method (MonoCompile *cfg)
                        /*
                         * EXCEPTION HANDLING
                         */
+                       case OP_IMPLICIT_EXCEPTION:
+                               /* This marks a place where an implicit exception can happen */
+                               if (bb->region != -1)
+                                       LLVM_FAILURE (ctx, "implicit-exception");
+                               break;
                        case OP_THROW: {
                                MonoMethodSignature *throw_sig;
                                LLVMValueRef callee, arg;
@@ -3155,7 +3272,16 @@ mono_llvm_emit_method (MonoCompile *cfg)
                                                callee = get_plt_entry (ctx, sig_to_llvm_sig (ctx, throw_sig, NULL), MONO_PATCH_INFO_INTERNAL_METHOD, "mono_arch_throw_exception");
                                        } else {
                                                callee = LLVMAddFunction (module, "mono_arch_throw_exception", sig_to_llvm_sig (ctx, throw_sig, NULL));
+
+#ifdef TARGET_X86
+                                               /* 
+                                                * LLVM doesn't push the exception argument, so we need a different
+                                                * trampoline.
+                                                */
+                                               LLVMAddGlobalMapping (ee, callee, resolve_patch (cfg, MONO_PATCH_INFO_INTERNAL_METHOD, "mono_arch_llvm_throw_exception"));
+#else
                                                LLVMAddGlobalMapping (ee, callee, resolve_patch (cfg, MONO_PATCH_INFO_INTERNAL_METHOD, "mono_arch_throw_exception"));
+#endif
                                        }
 
                                        mono_memory_barrier ();
@@ -3269,6 +3395,9 @@ mono_llvm_emit_method (MonoCompile *cfg)
 
                if (bb == cfg->bb_exit && sig->ret->type == MONO_TYPE_VOID)
                        LLVMBuildRetVoid (builder);
+
+               if (bb == cfg->bb_entry)
+                       ctx->last_alloca = LLVMGetLastInstruction (get_bb (ctx, cfg->bb_entry));
        }
 
        /* Add incoming phi values */
@@ -3485,7 +3614,8 @@ exception_cb (void *data)
 {
        MonoCompile *cfg;
        MonoJitExceptionInfo *ei;
-       guint32 ei_len;
+       guint32 ei_len, i;
+       gpointer *type_info;
 
        cfg = TlsGetValue (current_cfg_tls_id);
        g_assert (cfg);
@@ -3496,11 +3626,19 @@ exception_cb (void *data)
         * An alternative would be to save it directly, and modify our unwinder to work
         * with it.
         */
-       cfg->encoded_unwind_ops = mono_unwind_decode_fde ((guint8*)data, &cfg->encoded_unwind_ops_len, NULL, &ei, &ei_len);
+       cfg->encoded_unwind_ops = mono_unwind_decode_fde ((guint8*)data, &cfg->encoded_unwind_ops_len, NULL, &ei, &ei_len, &type_info);
 
        cfg->llvm_ex_info = mono_mempool_alloc0 (cfg->mempool, ei_len * sizeof (MonoJitExceptionInfo));
        cfg->llvm_ex_info_len = ei_len;
        memcpy (cfg->llvm_ex_info, ei, ei_len * sizeof (MonoJitExceptionInfo));
+       /* Fill the rest of the information from the type info */
+       for (i = 0; i < ei_len; ++i) {
+               MonoExceptionClause *clause = type_info [i];
+
+               cfg->llvm_ex_info [i].flags = clause->flags;
+               cfg->llvm_ex_info [i].data.catch_class = clause->data.catch_class;
+       }
+
        g_free (ei);
 }
 
@@ -3526,6 +3664,9 @@ add_intrinsics (LLVMModuleRef module)
                LLVMAddFunction (module, "llvm.sin.f64", LLVMFunctionType (LLVMDoubleType (), params, 1, FALSE));
                LLVMAddFunction (module, "llvm.cos.f64", LLVMFunctionType (LLVMDoubleType (), params, 1, FALSE));
                LLVMAddFunction (module, "llvm.sqrt.f64", LLVMFunctionType (LLVMDoubleType (), params, 1, FALSE));
+
+               /* This isn't an intrinsic, instead llvm seems to special case it by name */
+               LLVMAddFunction (module, "fabs", LLVMFunctionType (LLVMDoubleType (), params, 1, FALSE));
        }
 
        {
@@ -3564,6 +3705,16 @@ add_intrinsics (LLVMModuleRef module)
                LLVMAddFunction (module, "llvm.umul.with.overflow.i64", LLVMFunctionType (LLVMStructType (ovf_res_i64, 2, FALSE), ovf_params_i64, 2, FALSE));
        }
 
+       {
+               LLVMTypeRef struct_ptr = LLVMPointerType (LLVMStructType (NULL, 0, FALSE), 0);
+               LLVMTypeRef invariant_start_params [] = { LLVMInt64Type (), LLVMPointerType (LLVMInt8Type (), 0) };
+               LLVMTypeRef invariant_end_params [] = { struct_ptr, LLVMInt64Type (), LLVMPointerType (LLVMInt8Type (), 0) };
+
+               LLVMAddFunction (module, "llvm.invariant.start", LLVMFunctionType (struct_ptr, invariant_start_params, 2, FALSE));
+
+               LLVMAddFunction (module, "llvm.invariant.end", LLVMFunctionType (LLVMVoidType (), invariant_end_params, 3, FALSE));
+       }
+
        /* EH intrinsics */
        {
                LLVMTypeRef arg_types [2];
@@ -3676,6 +3827,20 @@ mono_llvm_create_aot_module (const char *got_symbol)
                LLVMBuildRetVoid (builder);
        }
 
+       /* Add a dummy personality function */
+       {
+               LLVMBasicBlockRef lbb;
+               LLVMBuilderRef lbuilder;
+               LLVMValueRef personality;
+
+               personality = LLVMAddFunction (aot_module.module, "mono_aot_personality", LLVMFunctionType (LLVMVoidType (), NULL, 0, FALSE));
+               LLVMSetLinkage (personality, LLVMPrivateLinkage);
+               lbb = LLVMAppendBasicBlock (personality, "BB0");
+               lbuilder = LLVMCreateBuilder ();
+               LLVMPositionBuilderAtEnd (lbuilder, lbb);
+               LLVMBuildRetVoid (lbuilder);
+       }
+
        aot_module.llvm_types = g_hash_table_new (NULL, NULL);
        aot_module.plt_entries = g_hash_table_new (g_str_hash, g_str_equal);
 }