text: /*
   * mini-llvm.c: llvm "Backend" for the mono JIT
   *
   * Copyright 2009-2011 Novell Inc (http://www.novell.com)
   * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
   */
  
  #include "mini.h"
  #include <mono/metadata/debug-helpers.h>
  #include <mono/metadata/debug-mono-symfile.h>
  #include <mono/metadata/mempool-internals.h>
  #include <mono/metadata/environment.h>
  #include <mono/metadata/object-internals.h>
  #include <mono/utils/mono-tls.h>
  #include <mono/utils/mono-dl.h>
  #include <mono/utils/mono-time.h>
  #include <mono/utils/freebsd-dwarf.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"
  #include "llvm-c/BitWriter.h"
  #include "llvm-c/Analysis.h"
  
  #include "mini-llvm-cpp.h"
  #include "aot-compiler.h"
  #include "mini-llvm.h"
  
  #ifdef __MINGW32__
  
  #include <stddef.h>
  extern void *memset(void *, int, size_t);
  void bzero (void *to, size_t count) { memset (to, 0, count); }
  
  #endif
  
  #if LLVM_API_VERSION < 4
  #error "The version of the mono llvm repository is too old."
  #endif
  
   /*
    * Information associated by mono with LLVM modules.
    */
  typedef struct {
  	LLVMModuleRef lmodule;
  	LLVMValueRef throw_icall, rethrow, match_exc, throw_corlib_exception, resume_eh;
  	GHashTable *llvm_types;
  	LLVMValueRef got_var;
  	const char *got_symbol;
  	const char *get_method_symbol;
  	const char *get_unbox_tramp_symbol;
  	GHashTable *plt_entries;
  	GHashTable *plt_entries_ji;
  	GHashTable *method_to_lmethod;
  	GHashTable *direct_callables;
  	char **bb_names;
  	int bb_names_len;
  	GPtrArray *used;
  	LLVMTypeRef ptr_type;
  	GPtrArray *subprogram_mds;
  	MonoEERef *mono_ee;
  	LLVMExecutionEngineRef ee;
  	gboolean external_symbols;
  	gboolean emit_dwarf;
  	int max_got_offset;
  
  	/* For AOT */
  	MonoAssembly *assembly;
  	char *global_prefix;
  	MonoAotFileInfo aot_info;
  	const char *jit_got_symbol;
  	const char *eh_frame_symbol;
  	LLVMValueRef get_method, get_unbox_tramp;
  	LLVMValueRef init_method, init_method_gshared_rgctx, init_method_gshared_this;
  	LLVMValueRef code_start, code_end;
  	LLVMValueRef inited_var;
  	int max_inited_idx, max_method_idx;
  	gboolean has_jitted_code;
  	gboolean static_link;
  	gboolean llvm_only;
  	GHashTable *idx_to_lmethod;
  	GHashTable *idx_to_unbox_tramp;
  	/* Maps a MonoMethod to LLVM instructions representing it */
  	GHashTable *method_to_callers;
  	LLVMContextRef context;
  	LLVMValueRef sentinel_exception;
  } MonoLLVMModule;
  
  /*
   * Information associated by the backend with mono basic blocks.
   */
  typedef struct {
  	LLVMBasicBlockRef bblock, end_bblock;
  	LLVMValueRef finally_ind;
  	gboolean added, invoke_target;
  	/* 
  	 * If this bblock is the start of a finally clause, this is a list of bblocks it
  	 * needs to branch to in ENDFINALLY.
  	 */
  	GSList *call_handler_return_bbs;
  	/*
  	 * If this bblock is the start of a finally clause, this is the bblock that
  	 * CALL_HANDLER needs to branch to.
  	 */
  	LLVMBasicBlockRef call_handler_target_bb;
  	/* The list of switch statements generated by ENDFINALLY instructions */
  	GSList *endfinally_switch_ins_list;
  	GSList *phi_nodes;
  } BBInfo;
  
  /*
   * Structure containing emit state
   */
  typedef struct {
  	MonoMemPool *mempool;
  
  	/* Maps method names to the corresponding LLVMValueRef */
  	GHashTable *emitted_method_decls;
  
  	MonoCompile *cfg;
  	LLVMValueRef lmethod;
  	MonoLLVMModule *module;
  	LLVMModuleRef lmodule;
  	BBInfo *bblocks;
  	int sindex, default_index, ex_index;
  	LLVMBuilderRef builder;
  	LLVMValueRef *values, *addresses;
  	MonoType **vreg_cli_types;
  	LLVMCallInfo *linfo;
  	MonoMethodSignature *sig;
  	GSList *builders;
  	GHashTable *region_to_handler;
  	GHashTable *clause_to_handler;
  	LLVMBuilderRef alloca_builder;
  	LLVMValueRef last_alloca;
  	LLVMValueRef rgctx_arg;
  	LLVMValueRef this_arg;
  	LLVMTypeRef *vreg_types;
  	LLVMBasicBlockRef init_bb, inited_bb;
  	gboolean *is_dead;
  	gboolean *unreachable;
  	gboolean llvm_only;
  	gboolean has_got_access;
  	int this_arg_pindex, rgctx_arg_pindex;
  	LLVMValueRef imt_rgctx_loc;
  	GHashTable *llvm_types;
  	LLVMValueRef dbg_md;
  	MonoDebugMethodInfo *minfo;
  	char temp_name [32];
  	/* For every clause, the clauses it is nested in */
  	GSList **nested_in;
  	LLVMValueRef ex_var;
  	GHashTable *exc_meta;
  	GHashTable *method_to_callers;
  } EmitContext;
  
  typedef struct {
  	MonoBasicBlock *bb;
  	MonoInst *phi;
  	MonoBasicBlock *in_bb;
  	int sreg;
  } PhiNode;
  
  /*
   * Instruction metadata
   * This is the same as ins_info, but LREG != IREG.
   */
  #ifdef MINI_OP
  #undef MINI_OP
  #endif
  #ifdef MINI_OP3
  #undef MINI_OP3
  #endif
  #define MINI_OP(a,b,dest,src1,src2) dest, src1, src2, ' ',
  #define MINI_OP3(a,b,dest,src1,src2,src3) dest, src1, src2, src3,
  #define NONE ' '
  #define IREG 'i'
  #define FREG 'f'
  #define VREG 'v'
  #define XREG 'x'
  #define LREG 'l'
  /* keep in sync with the enum in mini.h */
  const char
  llvm_ins_info[] = {
  #include "mini-ops.h"
  };
  #undef MINI_OP
  #undef MINI_OP3
  
  #if SIZEOF_VOID_P == 4
  #define GET_LONG_IMM(ins) (((guint64)(ins)->inst_ms_word << 32) | (guint64)(guint32)(ins)->inst_ls_word)
  #else
  #define GET_LONG_IMM(ins) ((ins)->inst_imm)
  #endif
  
  #define LLVM_INS_INFO(opcode) (&llvm_ins_info [((opcode) - OP_START - 1) * 4])
  
  #if 0
  #define TRACE_FAILURE(msg) do { printf ("%s\n", msg); } while (0)
  #else
  #define TRACE_FAILURE(msg)
  #endif
  
  #ifdef TARGET_X86
  #define IS_TARGET_X86 1
  #else
  #define IS_TARGET_X86 0
  #endif
  
  #ifdef TARGET_AMD64
  #define IS_TARGET_AMD64 1
  #else
  #define IS_TARGET_AMD64 0
  #endif
  
  #define LLVM_FAILURE(ctx, reason) do { \
  	TRACE_FAILURE (reason); \
  	(ctx)->cfg->exception_message = g_strdup (reason); \
  	(ctx)->cfg->disable_llvm = TRUE; \
  	goto FAILURE; \
  } while (0)
  
  #define CHECK_FAILURE(ctx) do { \
      if ((ctx)->cfg->disable_llvm) \
  		goto FAILURE; \
  } while (0)
  
  static LLVMIntPredicate cond_to_llvm_cond [] = {
  	LLVMIntEQ,
  	LLVMIntNE,
  	LLVMIntSLE,
  	LLVMIntSGE,
  	LLVMIntSLT,
  	LLVMIntSGT,
  	LLVMIntULE,
  	LLVMIntUGE,
  	LLVMIntULT,
  	LLVMIntUGT,
  };
  
  static LLVMRealPredicate fpcond_to_llvm_cond [] = {
  	LLVMRealOEQ,
  	LLVMRealUNE,
  	LLVMRealOLE,
  	LLVMRealOGE,
  	LLVMRealOLT,
  	LLVMRealOGT,
  	LLVMRealULE,
  	LLVMRealUGE,
  	LLVMRealULT,
  	LLVMRealUGT,
  };
  
  static MonoNativeTlsKey current_cfg_tls_id;
  
  static MonoLLVMModule aot_module;
  static int memset_param_count, memcpy_param_count;
  static const char *memset_func_name;
  static const char *memcpy_func_name;
  
  static void init_jit_module (MonoDomain *domain);
  
  static void emit_dbg_loc (EmitContext *ctx, LLVMBuilderRef builder, const unsigned char *cil_code);
  static LLVMValueRef emit_dbg_subprogram (EmitContext *ctx, MonoCompile *cfg, LLVMValueRef method, const char *name);
  static void emit_dbg_info (MonoLLVMModule *module, const char *filename, const char *cu_name);
  
  /*
   * IntPtrType:
   *
   *   The LLVM type with width == sizeof (gpointer)
   */
  static LLVMTypeRef
  IntPtrType (void)
  {
  	return sizeof (gpointer) == 8 ? LLVMInt64Type () : LLVMInt32Type ();
  }
  
  static LLVMTypeRef
  ObjRefType (void)
  {
  	return sizeof (gpointer) == 8 ? LLVMPointerType (LLVMInt64Type (), 0) : LLVMPointerType (LLVMInt32Type (), 0);
  }
  
  static LLVMTypeRef
  ThisType (void)
  {
  	return sizeof (gpointer) == 8 ? LLVMPointerType (LLVMInt64Type (), 0) : LLVMPointerType (LLVMInt32Type (), 0);
  }
  
  /*
   * get_vtype_size:
   *
   *   Return the size of the LLVM representation of the vtype T.
   */
  static guint32
  get_vtype_size (MonoType *t)
  {
  	int size;
  
  	size = mono_class_value_size (mono_class_from_mono_type (t), NULL);
  
  	/* LLVMArgAsIArgs depends on this since it stores whole words */
  	while (size < 2 * sizeof (gpointer) && mono_is_power_of_two (size) == -1)
  		size ++;
  
  	return size;
  }
  
  /*
   * simd_class_to_llvm_type:
   *
   *   Return the LLVM type corresponding to the Mono.SIMD class KLASS
   */
  static LLVMTypeRef
  simd_class_to_llvm_type (EmitContext *ctx, MonoClass *klass)
  {
  	if (!strcmp (klass->name, "Vector2d")) {
  		return LLVMVectorType (LLVMDoubleType (), 2);
  	} else if (!strcmp (klass->name, "Vector2l")) {
  		return LLVMVectorType (LLVMInt64Type (), 2);
  	} else if (!strcmp (klass->name, "Vector2ul")) {
  		return LLVMVectorType (LLVMInt64Type (), 2);
  	} else if (!strcmp (klass->name, "Vector4i")) {
  		return LLVMVectorType (LLVMInt32Type (), 4);
  	} else if (!strcmp (klass->name, "Vector4ui")) {
  		return LLVMVectorType (LLVMInt32Type (), 4);
  	} else if (!strcmp (klass->name, "Vector4f")) {
  		return LLVMVectorType (LLVMFloatType (), 4);
  	} else if (!strcmp (klass->name, "Vector8s")) {
  		return LLVMVectorType (LLVMInt16Type (), 8);
  	} else if (!strcmp (klass->name, "Vector8us")) {
  		return LLVMVectorType (LLVMInt16Type (), 8);
  	} else if (!strcmp (klass->name, "Vector16sb")) {
  		return LLVMVectorType (LLVMInt8Type (), 16);
  	} else if (!strcmp (klass->name, "Vector16b")) {
  		return LLVMVectorType (LLVMInt8Type (), 16);
  	} else {
  		printf ("%s\n", klass->name);
  		NOT_IMPLEMENTED;
  		return NULL;
  	}
  }
  
  /* Return the 128 bit SIMD type corresponding to the mono type TYPE */
  static inline G_GNUC_UNUSED LLVMTypeRef
  type_to_simd_type (int type)
  {
  	switch (type) {
  	case MONO_TYPE_I1:
  		return LLVMVectorType (LLVMInt8Type (), 16);
  	case MONO_TYPE_I2:
  		return LLVMVectorType (LLVMInt16Type (), 8);
  	case MONO_TYPE_I4:
  		return LLVMVectorType (LLVMInt32Type (), 4);
  	case MONO_TYPE_I8:
  		return LLVMVectorType (LLVMInt64Type (), 2);
  	case MONO_TYPE_R8:
  		return LLVMVectorType (LLVMDoubleType (), 2);
  	case MONO_TYPE_R4:
  		return LLVMVectorType (LLVMFloatType (), 4);
  	default:
  		g_assert_not_reached ();
  		return NULL;
  	}
  }
  
  static LLVMTypeRef
  create_llvm_type_for_type (MonoLLVMModule *module, MonoClass *klass)
  {
  	int i, size, nfields, esize;
  	LLVMTypeRef *eltypes;
  	char *name;
  	MonoType *t;
  	LLVMTypeRef ltype;
  
  	t = &klass->byval_arg;
  
  	if (mini_type_is_hfa (t, &nfields, &esize)) {
  		/*
  		 * This is needed on arm64 where HFAs are returned in
  		 * registers.
  		 */
  		size = nfields;
  		eltypes = g_new (LLVMTypeRef, size);
  		for (i = 0; i < size; ++i)
  			eltypes [i] = esize == 4 ? LLVMFloatType () : LLVMDoubleType ();
  	} else {
  		size = get_vtype_size (t);
  
  		eltypes = g_new (LLVMTypeRef, size);
  		for (i = 0; i < size; ++i)
  			eltypes [i] = LLVMInt8Type ();
  	}
  
  	name = mono_type_full_name (&klass->byval_arg);
  	ltype = LLVMStructCreateNamed (module->context, name);
  	LLVMStructSetBody (ltype, eltypes, size, FALSE);
  	g_free (eltypes);
  	g_free (name);
  
  	return ltype;
  }
  
  /*
   * type_to_llvm_type:
   *
   *   Return the LLVM type corresponding to T.
   */
  static LLVMTypeRef
  type_to_llvm_type (EmitContext *ctx, MonoType *t)
  {
  	t = mini_get_underlying_type (t);
  
  	switch (t->type) {
  	case MONO_TYPE_VOID:
  		return LLVMVoidType ();
  	case MONO_TYPE_I1:
  		return LLVMInt8Type ();
  	case MONO_TYPE_I2:
  		return LLVMInt16Type ();
  	case MONO_TYPE_I4:
  		return LLVMInt32Type ();
  	case MONO_TYPE_U1:
  		return LLVMInt8Type ();
  	case MONO_TYPE_U2:
  		return LLVMInt16Type ();
  	case MONO_TYPE_U4:
  		return LLVMInt32Type ();
  	case MONO_TYPE_BOOLEAN:
  		return LLVMInt8Type ();
  	case MONO_TYPE_I8:
  	case MONO_TYPE_U8:
  		return LLVMInt64Type ();
  	case MONO_TYPE_CHAR:
  		return LLVMInt16Type ();
  	case MONO_TYPE_R4:
  		return LLVMFloatType ();
  	case MONO_TYPE_R8:
  		return LLVMDoubleType ();
  	case MONO_TYPE_I:
  	case MONO_TYPE_U:
  		return IntPtrType ();
  	case MONO_TYPE_OBJECT:
  	case MONO_TYPE_CLASS:
  	case MONO_TYPE_ARRAY:
  	case MONO_TYPE_SZARRAY:
  	case MONO_TYPE_STRING:
  	case MONO_TYPE_PTR:
  		return ObjRefType ();
  	case MONO_TYPE_VAR:
  	case MONO_TYPE_MVAR:
  		/* Because of generic sharing */
  		return ObjRefType ();
  	case MONO_TYPE_GENERICINST:
  		if (!mono_type_generic_inst_is_valuetype (t))
  			return ObjRefType ();
  		/* Fall through */
  	case MONO_TYPE_VALUETYPE:
  	case MONO_TYPE_TYPEDBYREF: {
  		MonoClass *klass;
  		LLVMTypeRef ltype;
  
  		klass = mono_class_from_mono_type (t);
  
  		if (MONO_CLASS_IS_SIMD (ctx->cfg, klass))
  			return simd_class_to_llvm_type (ctx, klass);
  
  		if (klass->enumtype)
  			return type_to_llvm_type (ctx, mono_class_enum_basetype (klass));
  
  		ltype = (LLVMTypeRef)g_hash_table_lookup (ctx->module->llvm_types, klass);
  		if (!ltype) {
  			ltype = create_llvm_type_for_type (ctx->module, klass);
  			g_hash_table_insert (ctx->module->llvm_types, klass, ltype);
  		}
  		return ltype;
  	}
  
  	default:
  		printf ("X: %d\n", t->type);
  		ctx->cfg->exception_message = g_strdup_printf ("type %s", mono_type_full_name (t));
  		ctx->cfg->disable_llvm = TRUE;
  		return NULL;
  	}
  }
  
  /*
   * type_is_unsigned:
   *
   *   Return whenever T is an unsigned int type.
   */
  static gboolean
  type_is_unsigned (EmitContext *ctx, MonoType *t)
  {
  	if (t->byref)
  		return FALSE;
  	switch (t->type) {
  	case MONO_TYPE_U1:
  	case MONO_TYPE_U2:
  	case MONO_TYPE_CHAR:
  	case MONO_TYPE_U4:
  	case MONO_TYPE_U8:
  		return TRUE;
  	default:
  		return FALSE;
  	}
  }
  
  /*
   * type_to_llvm_arg_type:
   *
   *   Same as type_to_llvm_type, but treat i8/i16 as i32.
   */
  static LLVMTypeRef
  type_to_llvm_arg_type (EmitContext *ctx, MonoType *t)
  {
  	LLVMTypeRef ptype = type_to_llvm_type (ctx, t);
  
  	/*
  	 * This works on all abis except arm64/ios which passes multiple
  	 * arguments in one stack slot.
  	 */
  #ifndef TARGET_ARM64
  	if (ptype == LLVMInt8Type () || ptype == LLVMInt16Type ()) {
  		/* 
  		 * LLVM generates code which only sets the lower bits, while JITted
  		 * code expects all the bits to be set.
  		 */
  		ptype = LLVMInt32Type ();
  	}
  #endif
  
  	return ptype;
  }
  
  /*
   * llvm_type_to_stack_type:
   *
   *   Return the LLVM type which needs to be used when a value of type TYPE is pushed
   * on the IL stack.
   */
  static G_GNUC_UNUSED LLVMTypeRef
  llvm_type_to_stack_type (MonoCompile *cfg, LLVMTypeRef type)
  {
  	if (type == NULL)
  		return NULL;
  	if (type == LLVMInt8Type ())
  		return LLVMInt32Type ();
  	else if (type == LLVMInt16Type ())
  		return LLVMInt32Type ();
  	else if (!cfg->r4fp && type == LLVMFloatType ())
  		return LLVMDoubleType ();
  	else
  		return type;
  }
  
  /*
   * regtype_to_llvm_type:
   *
   *   Return the LLVM type corresponding to the regtype C used in instruction 
   * descriptions.
   */
  static LLVMTypeRef
  regtype_to_llvm_type (char c)
  {
  	switch (c) {
  	case 'i':
  		return LLVMInt32Type ();
  	case 'l':
  		return LLVMInt64Type ();
  	case 'f':
  		return LLVMDoubleType ();
  	default:
  		return NULL;
  	}
  }
  
  /*
   * op_to_llvm_type:
   *
   *   Return the LLVM type corresponding to the unary/binary opcode OPCODE.
   */
  static LLVMTypeRef
  op_to_llvm_type (int opcode)
  {
  	switch (opcode) {
  	case OP_ICONV_TO_I1:
  	case OP_LCONV_TO_I1:
  		return LLVMInt8Type ();
  	case OP_ICONV_TO_U1:
  	case OP_LCONV_TO_U1:
  		return LLVMInt8Type ();
  	case OP_ICONV_TO_I2:
  	case OP_LCONV_TO_I2:
  		return LLVMInt16Type ();
  	case OP_ICONV_TO_U2:
  	case OP_LCONV_TO_U2:
  		return LLVMInt16Type ();
  	case OP_ICONV_TO_I4:
  	case OP_LCONV_TO_I4:
  		return LLVMInt32Type ();
  	case OP_ICONV_TO_U4:
  	case OP_LCONV_TO_U4:
  		return LLVMInt32Type ();
  	case OP_ICONV_TO_I8:
  		return LLVMInt64Type ();
  	case OP_ICONV_TO_R4:
  		return LLVMFloatType ();
  	case OP_ICONV_TO_R8:
  		return LLVMDoubleType ();
  	case OP_ICONV_TO_U8:
  		return LLVMInt64Type ();
  	case OP_FCONV_TO_I4:
  		return LLVMInt32Type ();
  	case OP_FCONV_TO_I8:
  		return LLVMInt64Type ();
  	case OP_FCONV_TO_I1:
  	case OP_FCONV_TO_U1:
  	case OP_RCONV_TO_I1:
  	case OP_RCONV_TO_U1:
  		return LLVMInt8Type ();
  	case OP_FCONV_TO_I2:
  	case OP_FCONV_TO_U2:
  	case OP_RCONV_TO_I2:
  	case OP_RCONV_TO_U2:
  		return LLVMInt16Type ();
  	case OP_FCONV_TO_I:
  	case OP_FCONV_TO_U:
  		return sizeof (gpointer) == 8 ? LLVMInt64Type () : LLVMInt32Type ();
  	case OP_IADD_OVF:
  	case OP_IADD_OVF_UN:
  	case OP_ISUB_OVF:
  	case OP_ISUB_OVF_UN:
  	case OP_IMUL_OVF:
  	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:
  	case OP_LMUL_OVF_UN:
  		return LLVMInt64Type ();
  	default:
  		printf ("%s\n", mono_inst_name (opcode));
  		g_assert_not_reached ();
  		return NULL;
  	}
  }		
  
  #define CLAUSE_START(clause) ((clause)->try_offset)
  #define CLAUSE_END(clause) (((clause))->try_offset + ((clause))->try_len)
  
  /*
   * load_store_to_llvm_type:
   *
   *   Return the size/sign/zero extension corresponding to the load/store opcode
   * OPCODE.
   */
  static LLVMTypeRef
  load_store_to_llvm_type (int opcode, int *size, gboolean *sext, gboolean *zext)
  {
  	*sext = FALSE;
  	*zext = FALSE;
  
  	switch (opcode) {
  	case OP_LOADI1_MEMBASE:
  	case OP_STOREI1_MEMBASE_REG:
  	case OP_STOREI1_MEMBASE_IMM:
  	case OP_ATOMIC_LOAD_I1:
  	case OP_ATOMIC_STORE_I1:
  		*size = 1;
  		*sext = TRUE;
  		return LLVMInt8Type ();
  	case OP_LOADU1_MEMBASE:
  	case OP_LOADU1_MEM:
  	case OP_ATOMIC_LOAD_U1:
  	case OP_ATOMIC_STORE_U1:
  		*size = 1;
  		*zext = TRUE;
  		return LLVMInt8Type ();
  	case OP_LOADI2_MEMBASE:
  	case OP_STOREI2_MEMBASE_REG:
  	case OP_STOREI2_MEMBASE_IMM:
  	case OP_ATOMIC_LOAD_I2:
  	case OP_ATOMIC_STORE_I2:
  		*size = 2;
  		*sext = TRUE;
  		return LLVMInt16Type ();
  	case OP_LOADU2_MEMBASE:
  	case OP_LOADU2_MEM:
  	case OP_ATOMIC_LOAD_U2:
  	case OP_ATOMIC_STORE_U2:
  		*size = 2;
  		*zext = TRUE;
  		return LLVMInt16Type ();
  	case OP_LOADI4_MEMBASE:
  	case OP_LOADU4_MEMBASE:
  	case OP_LOADI4_MEM:
  	case OP_LOADU4_MEM:
  	case OP_STOREI4_MEMBASE_REG:
  	case OP_STOREI4_MEMBASE_IMM:
  	case OP_ATOMIC_LOAD_I4:
  	case OP_ATOMIC_STORE_I4:
  	case OP_ATOMIC_LOAD_U4:
  	case OP_ATOMIC_STORE_U4:
  		*size = 4;
  		return LLVMInt32Type ();
  	case OP_LOADI8_MEMBASE:
  	case OP_LOADI8_MEM:
  	case OP_STOREI8_MEMBASE_REG:
  	case OP_STOREI8_MEMBASE_IMM:
  	case OP_ATOMIC_LOAD_I8:
  	case OP_ATOMIC_STORE_I8:
  	case OP_ATOMIC_LOAD_U8:
  	case OP_ATOMIC_STORE_U8:
  		*size = 8;
  		return LLVMInt64Type ();
  	case OP_LOADR4_MEMBASE:
  	case OP_STORER4_MEMBASE_REG:
  	case OP_ATOMIC_LOAD_R4:
  	case OP_ATOMIC_STORE_R4:
  		*size = 4;
  		return LLVMFloatType ();
  	case OP_LOADR8_MEMBASE:
  	case OP_STORER8_MEMBASE_REG:
  	case OP_ATOMIC_LOAD_R8:
  	case OP_ATOMIC_STORE_R8:
  		*size = 8;
  		return LLVMDoubleType ();
  	case OP_LOAD_MEMBASE:
  	case OP_LOAD_MEM:
  	case OP_STORE_MEMBASE_REG:
  	case OP_STORE_MEMBASE_IMM:
  		*size = sizeof (gpointer);
  		return IntPtrType ();
  	default:
  		g_assert_not_reached ();
  		return NULL;
  	}
  }
  
  /*
   * ovf_op_to_intrins:
   *
   *   Return the LLVM intrinsics corresponding to the overflow opcode OPCODE.
   */
  static const char*
  ovf_op_to_intrins (int opcode)
  {
  	switch (opcode) {
  	case OP_IADD_OVF:
  		return "llvm.sadd.with.overflow.i32";
  	case OP_IADD_OVF_UN:
  		return "llvm.uadd.with.overflow.i32";
  	case OP_ISUB_OVF:
  		return "llvm.ssub.with.overflow.i32";
  	case OP_ISUB_OVF_UN:
  		return "llvm.usub.with.overflow.i32";
  	case OP_IMUL_OVF:
  		return "llvm.smul.with.overflow.i32";
  	case OP_IMUL_OVF_UN:
  		return "llvm.umul.with.overflow.i32";
  	case OP_LADD_OVF:
  		return "llvm.sadd.with.overflow.i64";
  	case OP_LADD_OVF_UN:
  		return "llvm.uadd.with.overflow.i64";
  	case OP_LSUB_OVF:
  		return "llvm.ssub.with.overflow.i64";
  	case OP_LSUB_OVF_UN:
  		return "llvm.usub.with.overflow.i64";
  	case OP_LMUL_OVF:
  		return "llvm.smul.with.overflow.i64";
  	case OP_LMUL_OVF_UN:
  		return "llvm.umul.with.overflow.i64";
  	default:
  		g_assert_not_reached ();
  		return NULL;
  	}
  }
  
  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:
  		return "llvm.x86.sse.min.ps";
  	case OP_PMIND_UN:
  		return "llvm.x86.sse41.pminud";
  	case OP_PMINW_UN:
  		return "llvm.x86.sse41.pminuw";
  	case OP_PMINB_UN:
  		return "llvm.x86.sse2.pminu.b";
  	case OP_PMINW:
  		return "llvm.x86.sse2.pmins.w";
  	case OP_MAXPD:
  		return "llvm.x86.sse2.max.pd";
  	case OP_MAXPS:
  		return "llvm.x86.sse.max.ps";
  	case OP_HADDPD:
  		return "llvm.x86.sse3.hadd.pd";
  	case OP_HADDPS:
  		return "llvm.x86.sse3.hadd.ps";
  	case OP_HSUBPD:
  		return "llvm.x86.sse3.hsub.pd";
  	case OP_HSUBPS:
  		return "llvm.x86.sse3.hsub.ps";
  	case OP_PMAXD_UN:
  		return "llvm.x86.sse41.pmaxud";
  	case OP_PMAXW_UN:
  		return "llvm.x86.sse41.pmaxuw";
  	case OP_PMAXB_UN:
  		return "llvm.x86.sse2.pmaxu.b";
  	case OP_ADDSUBPS:
  		return "llvm.x86.sse3.addsub.ps";
  	case OP_ADDSUBPD:
  		return "llvm.x86.sse3.addsub.pd";
  	case OP_EXTRACT_MASK:
  		return "llvm.x86.sse2.pmovmskb.128";
  	case OP_PSHRW:
  	case OP_PSHRW_REG:
  		return "llvm.x86.sse2.psrli.w";
  	case OP_PSHRD:
  	case OP_PSHRD_REG:
  		return "llvm.x86.sse2.psrli.d";
  	case OP_PSHRQ:
  	case OP_PSHRQ_REG:
  		return "llvm.x86.sse2.psrli.q";
  	case OP_PSHLW:
  	case OP_PSHLW_REG:
  		return "llvm.x86.sse2.pslli.w";
  	case OP_PSHLD:
  	case OP_PSHLD_REG:
  		return "llvm.x86.sse2.pslli.d";
  	case OP_PSHLQ:
  	case OP_PSHLQ_REG:
  		return "llvm.x86.sse2.pslli.q";
  	case OP_PSARW:
  	case OP_PSARW_REG:
  		return "llvm.x86.sse2.psrai.w";
  	case OP_PSARD:
  	case OP_PSARD_REG:
  		return "llvm.x86.sse2.psrai.d";
  	case OP_PADDB_SAT:
  		return "llvm.x86.sse2.padds.b";
  	case OP_PADDW_SAT:
  		return "llvm.x86.sse2.padds.w";
  	case OP_PSUBB_SAT:
  		return "llvm.x86.sse2.psubs.b";
  	case OP_PSUBW_SAT:
  		return "llvm.x86.sse2.psubs.w";
  	case OP_PADDB_SAT_UN:
  		return "llvm.x86.sse2.paddus.b";
  	case OP_PADDW_SAT_UN:
  		return "llvm.x86.sse2.paddus.w";
  	case OP_PSUBB_SAT_UN:
  		return "llvm.x86.sse2.psubus.b";
  	case OP_PSUBW_SAT_UN:
  		return "llvm.x86.sse2.psubus.w";
  	case OP_PAVGB_UN:
  		return "llvm.x86.sse2.pavg.b";
  	case OP_PAVGW_UN:
  		return "llvm.x86.sse2.pavg.w";
  	case OP_SQRTPS:
  		return "llvm.x86.sse.sqrt.ps";
  	case OP_SQRTPD:
  		return "llvm.x86.sse2.sqrt.pd";
  	case OP_RSQRTPS:
  		return "llvm.x86.sse.rsqrt.ps";
  	case OP_RCPPS:
  		return "llvm.x86.sse.rcp.ps";
  	case OP_CVTDQ2PD:
  		return "llvm.x86.sse2.cvtdq2pd";
  	case OP_CVTDQ2PS:
  		return "llvm.x86.sse2.cvtdq2ps";
  	case OP_CVTPD2DQ:
  		return "llvm.x86.sse2.cvtpd2dq";
  	case OP_CVTPS2DQ:
  		return "llvm.x86.sse2.cvtps2dq";
  	case OP_CVTPD2PS:
  		return "llvm.x86.sse2.cvtpd2ps";
  	case OP_CVTPS2PD:
  		return "llvm.x86.sse2.cvtps2pd";
  	case OP_CVTTPD2DQ:
  		return "llvm.x86.sse2.cvttpd2dq";
  	case OP_CVTTPS2DQ:
  		return "llvm.x86.sse2.cvttps2dq";
  	case OP_COMPPS:
  		return "llvm.x86.sse.cmp.ps";
  	case OP_COMPPD:
  		return "llvm.x86.sse2.cmp.pd";
  	case OP_PACKW:
  		return "llvm.x86.sse2.packsswb.128";
  	case OP_PACKD:
  		return "llvm.x86.sse2.packssdw.128";
  	case OP_PACKW_UN:
  		return "llvm.x86.sse2.packuswb.128";
  	case OP_PACKD_UN:
  		return "llvm.x86.sse41.packusdw";
  	case OP_PMULW_HIGH:
  		return "llvm.x86.sse2.pmulh.w";
  	case OP_PMULW_HIGH_UN:
  		return "llvm.x86.sse2.pmulhu.w";
  #endif
  	default:
  		g_assert_not_reached ();
  		return NULL;
  	}
  }
  
  static LLVMTypeRef
  simd_op_to_llvm_type (int opcode)
  {
  #if defined(TARGET_X86) || defined(TARGET_AMD64)
  	switch (opcode) {
  	case OP_EXTRACT_R8:
  	case OP_EXPAND_R8:
  		return type_to_simd_type (MONO_TYPE_R8);
  	case OP_EXTRACT_I8:
  	case OP_EXPAND_I8:
  		return type_to_simd_type (MONO_TYPE_I8);
  	case OP_EXTRACT_I4:
  	case OP_EXPAND_I4:
  		return type_to_simd_type (MONO_TYPE_I4);
  	case OP_EXTRACT_I2:
  	case OP_EXTRACT_U2:
  	case OP_EXTRACTX_U2:
  	case OP_EXPAND_I2:
  		return type_to_simd_type (MONO_TYPE_I2);
  	case OP_EXTRACT_I1:
  	case OP_EXTRACT_U1:
  	case OP_EXPAND_I1:
  		return type_to_simd_type (MONO_TYPE_I1);
  	case OP_EXPAND_R4:
  		return type_to_simd_type (MONO_TYPE_R4);
  	case OP_CVTDQ2PD:
  	case OP_CVTDQ2PS:
  		return type_to_simd_type (MONO_TYPE_I4);
  	case OP_CVTPD2DQ:
  	case OP_CVTPD2PS:
  	case OP_CVTTPD2DQ:
  		return type_to_simd_type (MONO_TYPE_R8);
  	case OP_CVTPS2DQ:
  	case OP_CVTPS2PD:
  	case OP_CVTTPS2DQ:
  		return type_to_simd_type (MONO_TYPE_R4);
  	case OP_EXTRACT_MASK:
  		return type_to_simd_type (MONO_TYPE_I1);
  	case OP_SQRTPS:
  	case OP_RSQRTPS:
  	case OP_RCPPS:
  	case OP_DUPPS_LOW:
  	case OP_DUPPS_HIGH:
  		return type_to_simd_type (MONO_TYPE_R4);
  	case OP_SQRTPD:
  	case OP_DUPPD:
  		return type_to_simd_type (MONO_TYPE_R8);
  	default:
  		g_assert_not_reached ();
  		return NULL;
  	}
  #else
  	return NULL;
  #endif
  }
  
  /*
   * get_bb:
   *
   *   Return the LLVM basic block corresponding to BB.
   */
  static LLVMBasicBlockRef
  get_bb (EmitContext *ctx, MonoBasicBlock *bb)
  {
  	char bb_name_buf [128];
  	char *bb_name;
  
  	if (ctx->bblocks [bb->block_num].bblock == NULL) {
  		if (bb->flags & BB_EXCEPTION_HANDLER) {
  			int clause_index = (mono_get_block_region_notry (ctx->cfg, bb->region) >> 8) - 1;
  			sprintf (bb_name_buf, "EH_CLAUSE%d_BB%d", clause_index, bb->block_num);
  			bb_name = bb_name_buf;
  		} else if (bb->block_num < 256) {
  			if (!ctx->module->bb_names) {
  				ctx->module->bb_names_len = 256;
  				ctx->module->bb_names = g_new0 (char*, ctx->module->bb_names_len);
  			}
  			if (!ctx->module->bb_names [bb->block_num]) {
  				char *n;
  
  				n = g_strdup_printf ("BB%d", bb->block_num);
  				mono_memory_barrier ();
  				ctx->module->bb_names [bb->block_num] = n;
  			}
  			bb_name = ctx->module->bb_names [bb->block_num];
  		} else {
  			sprintf (bb_name_buf, "BB%d", bb->block_num);
  			bb_name = bb_name_buf;
  		}
  
  		ctx->bblocks [bb->block_num].bblock = LLVMAppendBasicBlock (ctx->lmethod, bb_name);
  		ctx->bblocks [bb->block_num].end_bblock = ctx->bblocks [bb->block_num].bblock;
  	}
  
  	return ctx->bblocks [bb->block_num].bblock;
  }
  
  /* 
   * get_end_bb:
   *
   *   Return the last LLVM bblock corresponding to BB.
   * This might not be equal to the bb returned by get_bb () since we need to generate
   * multiple LLVM bblocks for a mono bblock to handle throwing exceptions.
   */
  static LLVMBasicBlockRef
  get_end_bb (EmitContext *ctx, MonoBasicBlock *bb)
  {
  	get_bb (ctx, bb);
  	return ctx->bblocks [bb->block_num].end_bblock;
  }
  
  static LLVMBasicBlockRef
  gen_bb (EmitContext *ctx, const char *prefix)
  {
  	char bb_name [128];
  
  	sprintf (bb_name, "%s%d", prefix, ++ ctx->ex_index);
  	return LLVMAppendBasicBlock (ctx->lmethod, bb_name);
  }
  
  /*
   * resolve_patch:
   *
   *   Return the target of the patch identified by TYPE and TARGET.
   */
  static gpointer
  resolve_patch (MonoCompile *cfg, MonoJumpInfoType type, gconstpointer target)
  {
  	MonoJumpInfo ji;
  
  	memset (&ji, 0, sizeof (ji));
  	ji.type = type;
  	ji.data.target = target;
  
  	return mono_resolve_patch_target (cfg->method, cfg->domain, NULL, &ji, FALSE);
  }
  
  /*
   * convert_full:
   *
   *   Emit code to convert the LLVM value V to DTYPE.
   */
  static LLVMValueRef
  convert_full (EmitContext *ctx, LLVMValueRef v, LLVMTypeRef dtype, gboolean is_unsigned)
  {
  	LLVMTypeRef stype = LLVMTypeOf (v);
  
  	if (stype != dtype) {
  		gboolean ext = FALSE;
  
  		/* Extend */
  		if (dtype == LLVMInt64Type () && (stype == LLVMInt32Type () || stype == LLVMInt16Type () || stype == LLVMInt8Type ()))
  			ext = TRUE;
  		else if (dtype == LLVMInt32Type () && (stype == LLVMInt16Type () || stype == LLVMInt8Type ()))
  			ext = TRUE;
  		else if (dtype == LLVMInt16Type () && (stype == LLVMInt8Type ()))
  			ext = TRUE;
  
  		if (ext)
  			return is_unsigned ? LLVMBuildZExt (ctx->builder, v, dtype, "") : LLVMBuildSExt (ctx->builder, v, dtype, "");
  
  		if (dtype == LLVMDoubleType () && stype == LLVMFloatType ())
  			return LLVMBuildFPExt (ctx->builder, v, dtype, "");
  
  		/* Trunc */
  		if (stype == LLVMInt64Type () && (dtype == LLVMInt32Type () || dtype == LLVMInt16Type () || dtype == LLVMInt8Type ()))
  			return LLVMBuildTrunc (ctx->builder, v, dtype, "");
  		if (stype == LLVMInt32Type () && (dtype == LLVMInt16Type () || dtype == LLVMInt8Type ()))
  			return LLVMBuildTrunc (ctx->builder, v, dtype, "");
  		if (stype == LLVMInt16Type () && dtype == LLVMInt8Type ())
  			return LLVMBuildTrunc (ctx->builder, v, dtype, "");
  		if (stype == LLVMDoubleType () && dtype == LLVMFloatType ())
  			return LLVMBuildFPTrunc (ctx->builder, v, dtype, "");
  
  		if (LLVMGetTypeKind (stype) == LLVMPointerTypeKind && LLVMGetTypeKind (dtype) == LLVMPointerTypeKind)
  			return LLVMBuildBitCast (ctx->builder, v, dtype, "");
  		if (LLVMGetTypeKind (dtype) == LLVMPointerTypeKind)
  			return LLVMBuildIntToPtr (ctx->builder, v, dtype, "");
  		if (LLVMGetTypeKind (stype) == LLVMPointerTypeKind)
  			return LLVMBuildPtrToInt (ctx->builder, v, dtype, "");
  
  		if (mono_arch_is_soft_float ()) {
  			if (stype == LLVMInt32Type () && dtype == LLVMFloatType ())
  				return LLVMBuildBitCast (ctx->builder, v, dtype, "");
  			if (stype == LLVMInt32Type () && dtype == LLVMDoubleType ())
  				return LLVMBuildBitCast (ctx->builder, LLVMBuildZExt (ctx->builder, v, LLVMInt64Type (), ""), dtype, "");
  		}
  
  		if (LLVMGetTypeKind (stype) == LLVMVectorTypeKind && LLVMGetTypeKind (dtype) == LLVMVectorTypeKind)
  			return LLVMBuildBitCast (ctx->builder, v, dtype, "");
  
  		LLVMDumpValue (v);
  		LLVMDumpValue (LLVMConstNull (dtype));
  		g_assert_not_reached ();
  		return NULL;
  	} else {
  		return v;
  	}
  }
  
  static LLVMValueRef
  convert (EmitContext *ctx, LLVMValueRef v, LLVMTypeRef dtype)
  {
  	return convert_full (ctx, v, dtype, FALSE);
  }
  
  /*
   * emit_volatile_load:
   *
   *   If vreg is volatile, emit a load from its address.
   */
  static LLVMValueRef
  emit_volatile_load (EmitContext *ctx, int vreg)
  {
  	MonoType *t;
  
  	LLVMValueRef v = LLVMBuildLoad (ctx->builder, ctx->addresses [vreg], "");
  	t = ctx->vreg_cli_types [vreg];
  	if (t && !t->byref) {
  		/* 
  		 * Might have to zero extend since llvm doesn't have 
  		 * unsigned types.
  		 */
  		if (t->type == MONO_TYPE_U1 || t->type == MONO_TYPE_U2 || t->type == MONO_TYPE_CHAR || t->type == MONO_TYPE_BOOLEAN)
  			v = LLVMBuildZExt (ctx->builder, v, LLVMInt32Type (), "");
  		else if (t->type == MONO_TYPE_I1 || t->type == MONO_TYPE_I2)
  			v = LLVMBuildSExt (ctx->builder, v, LLVMInt32Type (), "");
  		else if (t->type == MONO_TYPE_U8)
  			v = LLVMBuildZExt (ctx->builder, v, LLVMInt64Type (), "");
  	}
  
  	return v;
  }
  
  /*
   * emit_volatile_store:
   *
   *   If VREG is volatile, emit a store from its value to its address.
   */
  static void
  emit_volatile_store (EmitContext *ctx, int vreg)
  {
  	MonoInst *var = get_vreg_to_inst (ctx->cfg, vreg);
  
  	if (var && var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)) {
  		g_assert (ctx->addresses [vreg]);
  		LLVMBuildStore (ctx->builder, convert (ctx, ctx->values [vreg], type_to_llvm_type (ctx, var->inst_vtype)), ctx->addresses [vreg]);
  	}
  }
  
  static LLVMTypeRef
  sig_to_llvm_sig_no_cinfo (EmitContext *ctx, MonoMethodSignature *sig)
  {
  	LLVMTypeRef ret_type;
  	LLVMTypeRef *param_types = NULL;
  	LLVMTypeRef res;
  	int i, pindex;
  	MonoType *rtype;
  
  	rtype = mini_get_underlying_type (sig->ret);
  	ret_type = type_to_llvm_type (ctx, rtype);
  	CHECK_FAILURE (ctx);
  
  	param_types = g_new0 (LLVMTypeRef, (sig->param_count * 8) + 3);
  	pindex = 0;
  
  	if (sig->hasthis)
  		param_types [pindex ++] = ThisType ();
  	for (i = 0; i < sig->param_count; ++i)
  		param_types [pindex ++] = type_to_llvm_arg_type (ctx, sig->params [i]);
  
  	CHECK_FAILURE (ctx);
  
  	res = LLVMFunctionType (ret_type, param_types, pindex, FALSE);
  	g_free (param_types);
  
  	return res;
  
   FAILURE:
  	g_free (param_types);
  
  	return NULL;
  }
  
  /*
   * sig_to_llvm_sig_full:
   *
   *   Return the LLVM signature corresponding to the mono signature SIG using the
   * calling convention information in CINFO. Fill out the parameter mapping information in CINFO.
   */
  static LLVMTypeRef
  sig_to_llvm_sig_full (EmitContext *ctx, MonoMethodSignature *sig, LLVMCallInfo *cinfo)
  {
  	LLVMTypeRef ret_type;
  	LLVMTypeRef *param_types = NULL;
  	LLVMTypeRef res;
  	int i, j, pindex, vret_arg_pindex = 0;
  	gboolean vretaddr = FALSE;
  	MonoType *rtype;
  
  	if (!cinfo)
  		return sig_to_llvm_sig_no_cinfo (ctx, sig);
  
  	rtype = mini_get_underlying_type (sig->ret);
  	ret_type = type_to_llvm_type (ctx, rtype);
  	CHECK_FAILURE (ctx);
  
  	switch (cinfo->ret.storage) {
  	case LLVMArgVtypeInReg:
  		/* LLVM models this by returning an aggregate value */
  		if (cinfo->ret.pair_storage [0] == LLVMArgInIReg && cinfo->ret.pair_storage [1] == LLVMArgNone) {
  			LLVMTypeRef members [2];
  
  			members [0] = IntPtrType ();
  			ret_type = LLVMStructType (members, 1, FALSE);
  		} else if (cinfo->ret.pair_storage [0] == LLVMArgNone && cinfo->ret.pair_storage [1] == LLVMArgNone) {
  			/* Empty struct */
  			ret_type = LLVMVoidType ();
  		} else if (cinfo->ret.pair_storage [0] == LLVMArgInIReg && cinfo->ret.pair_storage [1] == LLVMArgInIReg) {
  			LLVMTypeRef members [2];
  
  			members [0] = IntPtrType ();
  			members [1] = IntPtrType ();
  			ret_type = LLVMStructType (members, 2, FALSE);
  		} else {
  			g_assert_not_reached ();
  		}
  		break;
  	case LLVMArgVtypeByVal:
  		/* Vtype returned normally by val */
  		break;
  	case LLVMArgVtypeAsScalar:
  		/* LLVM models this by returning an int */
  		g_assert (cinfo->ret.nslots == 1 || cinfo->ret.nslots == 2);
  		ret_type = LLVMIntType (cinfo->ret.nslots * sizeof (mgreg_t) * 8);
  		break;
  	case LLVMArgFpStruct: {
  		/* Vtype returned as a fp struct */
  		LLVMTypeRef members [16];
  
  		/* Have to create our own structure since we don't map fp structures to LLVM fp structures yet */
  		for (i = 0; i < cinfo->ret.nslots; ++i)
  			members [i] = cinfo->ret.esize == 8 ? LLVMDoubleType () : LLVMFloatType ();
  		ret_type = LLVMStructType (members, cinfo->ret.nslots, FALSE);
  		break;
  	}
  	case LLVMArgVtypeByRef:
  		/* Vtype returned using a hidden argument */
  		ret_type = LLVMVoidType ();
  		break;
  	case LLVMArgVtypeRetAddr:
  	case LLVMArgScalarRetAddr:
  		vretaddr = TRUE;
  		ret_type = LLVMVoidType ();
  		break;
  	default:
  		break;
  	}
  
  	param_types = g_new0 (LLVMTypeRef, (sig->param_count * 8) + 3);
  	pindex = 0;
  	if (cinfo->ret.storage == LLVMArgVtypeByRef) {
  		/*
  		 * Has to be the first argument because of the sret argument attribute
  		 * FIXME: This might conflict with passing 'this' as the first argument, but
  		 * this is only used on arm64 which has a dedicated struct return register.
  		 */
  		cinfo->vret_arg_pindex = pindex;
  		param_types [pindex] = type_to_llvm_arg_type (ctx, sig->ret);
  		CHECK_FAILURE (ctx);
  		param_types [pindex] = LLVMPointerType (param_types [pindex], 0);
  		pindex ++;
  	}
  	if (!ctx->llvm_only && cinfo->rgctx_arg) {
  		cinfo->rgctx_arg_pindex = pindex;
  		param_types [pindex] = ctx->module->ptr_type;
  		pindex ++;
  	}
  	if (cinfo->imt_arg) {
  		cinfo->imt_arg_pindex = pindex;
  		param_types [pindex] = ctx->module->ptr_type;
  		pindex ++;
  	}
  	if (vretaddr) {
  		/* Compute the index in the LLVM signature where the vret arg needs to be passed */
  		vret_arg_pindex = pindex;
  		if (cinfo->vret_arg_index == 1) {
  			/* Add the slots consumed by the first argument */
  			LLVMArgInfo *ainfo = &cinfo->args [0];
  			switch (ainfo->storage) {
  			case LLVMArgVtypeInReg:
  				for (j = 0; j < 2; ++j) {
  					if (ainfo->pair_storage [j] == LLVMArgInIReg)
  						vret_arg_pindex ++;
  				}
  				break;
  			default:
  				vret_arg_pindex ++;
  			}
  		}
  
  		cinfo->vret_arg_pindex = vret_arg_pindex;
  	}				
  
  	if (vretaddr && vret_arg_pindex == pindex)
  		param_types [pindex ++] = IntPtrType ();
  	if (sig->hasthis) {
  		cinfo->this_arg_pindex = pindex;
  		param_types [pindex ++] = ThisType ();
  	}
  	if (vretaddr && vret_arg_pindex == pindex)
  		param_types [pindex ++] = IntPtrType ();
  	for (i = 0; i < sig->param_count; ++i) {
  		LLVMArgInfo *ainfo = &cinfo->args [i + sig->hasthis];
  
  		if (vretaddr && vret_arg_pindex == pindex)
  			param_types [pindex ++] = IntPtrType ();
  		ainfo->pindex = pindex;
  
  		switch (ainfo->storage) {
  		case LLVMArgVtypeInReg:
  			for (j = 0; j < 2; ++j) {
  				switch (ainfo->pair_storage [j]) {
  				case LLVMArgInIReg:
  					param_types [pindex ++] = LLVMIntType (sizeof (gpointer) * 8);
  					break;
  				case LLVMArgNone:
  					break;
  				default:
  					g_assert_not_reached ();
  				}
  			}
  			break;
  		case LLVMArgVtypeByVal:
  			param_types [pindex] = type_to_llvm_arg_type (ctx, ainfo->type);
  			CHECK_FAILURE (ctx);
  			param_types [pindex] = LLVMPointerType (param_types [pindex], 0);
  			pindex ++;
  			break;
  		case LLVMArgAsIArgs:
  			param_types [pindex] = LLVMArrayType (IntPtrType (), ainfo->nslots);
  			pindex ++;
  			break;
  		case LLVMArgVtypeByRef:
  		case LLVMArgScalarByRef:
  			param_types [pindex] = type_to_llvm_arg_type (ctx, ainfo->type);
  			CHECK_FAILURE (ctx);
  			param_types [pindex] = LLVMPointerType (param_types [pindex], 0);
  			pindex ++;
  			break;
  		case LLVMArgAsFpArgs: {
  			int j;
  
  			for (j = 0; j < ainfo->nslots; ++j)
  				param_types [pindex + j] = ainfo->esize == 8 ? LLVMDoubleType () : LLVMFloatType ();
  			pindex += ainfo->nslots;
  			break;
  		}
  		case LLVMArgVtypeAsScalar:
  			g_assert_not_reached ();
  			break;
  		default:
  			param_types [pindex ++] = type_to_llvm_arg_type (ctx, ainfo->type);
  			break;
  		}
  	}
  	if (vretaddr && vret_arg_pindex == pindex)
  		param_types [pindex ++] = IntPtrType ();
  	if (ctx->llvm_only && cinfo->rgctx_arg) {
  		/* Pass the rgctx as the last argument */
  		cinfo->rgctx_arg_pindex = pindex;
  		param_types [pindex] = ctx->module->ptr_type;
  		pindex ++;
  	}
  
  	CHECK_FAILURE (ctx);
  
  	res = LLVMFunctionType (ret_type, param_types, pindex, FALSE);
  	g_free (param_types);
  
  	return res;
  
   FAILURE:
  	g_free (param_types);
  
  	return NULL;
  }
  
  static LLVMTypeRef
  sig_to_llvm_sig (EmitContext *ctx, MonoMethodSignature *sig)
  {
  	return sig_to_llvm_sig_full (ctx, sig, NULL);
  }
  
  /*
   * LLVMFunctionType1:
   *
   *   Create an LLVM function type from the arguments.
   */
  static G_GNUC_UNUSED LLVMTypeRef
  LLVMFunctionType0 (LLVMTypeRef ReturnType,
  				   int IsVarArg)
  {
  	return LLVMFunctionType (ReturnType, NULL, 0, IsVarArg);
  }
  
  /*
   * LLVMFunctionType1:
   *
   *   Create an LLVM function type from the arguments.
   */
  static G_GNUC_UNUSED LLVMTypeRef 
  LLVMFunctionType1 (LLVMTypeRef ReturnType,
  				   LLVMTypeRef ParamType1,
  				   int IsVarArg)
  {
  	LLVMTypeRef param_types [1];
  
  	param_types [0] = ParamType1;
  
  	return LLVMFunctionType (ReturnType, param_types, 1, IsVarArg);
  }
  
  /*
   * LLVMFunctionType2:
   *
   *   Create an LLVM function type from the arguments.
   */
  static G_GNUC_UNUSED LLVMTypeRef
  LLVMFunctionType2 (LLVMTypeRef ReturnType,
  				   LLVMTypeRef ParamType1,
  				   LLVMTypeRef ParamType2,
  				   int IsVarArg)
  {
  	LLVMTypeRef param_types [2];
  
  	param_types [0] = ParamType1;
  	param_types [1] = ParamType2;
  
  	return LLVMFunctionType (ReturnType, param_types, 2, IsVarArg);
  }
  
  /*
   * LLVMFunctionType3:
   *
   *   Create an LLVM function type from the arguments.
   */
  static G_GNUC_UNUSED LLVMTypeRef
  LLVMFunctionType3 (LLVMTypeRef ReturnType,
  				   LLVMTypeRef ParamType1,
  				   LLVMTypeRef ParamType2,
  				   LLVMTypeRef ParamType3,
  				   int IsVarArg)
  {
  	LLVMTypeRef param_types [3];
  
  	param_types [0] = ParamType1;
  	param_types [1] = ParamType2;
  	param_types [2] = ParamType3;
  
  	return LLVMFunctionType (ReturnType, param_types, 3, IsVarArg);
  }
  
  /*
   * create_builder:
   *
   *   Create an LLVM builder and remember it so it can be freed later.
   */
  static LLVMBuilderRef
  create_builder (EmitContext *ctx)
  {
  	LLVMBuilderRef builder = LLVMCreateBuilder ();
  
  	ctx->builders = g_slist_prepend_mempool (ctx->cfg->mempool, ctx->builders, builder);
  
  	return builder;
  }
  
  static char*
  get_aotconst_name (MonoJumpInfoType type, gconstpointer data, int got_offset)
  {
  	char *name;
  
  	switch (type) {
  	case MONO_PATCH_INFO_INTERNAL_METHOD:
  		name = g_strdup_printf ("jit_icall_%s", data);
  		break;
  	default:
  		name = g_strdup_printf ("%s_%d", mono_ji_type_to_string (type), got_offset);
  		break;
  	}
  
  	return name;
  }
  
  static LLVMValueRef
  get_aotconst (EmitContext *ctx, MonoJumpInfoType type, gconstpointer data)
  {
  	MonoCompile *cfg;
  	guint32 got_offset;
  	LLVMValueRef indexes [2];
  	MonoJumpInfo *ji;
  	LLVMValueRef got_entry_addr, load;
  	LLVMBuilderRef builder = ctx->builder;
  	char *name = NULL;
  
  	cfg = ctx->cfg;
  
  	ji = g_new0 (MonoJumpInfo, 1);
  	ji->type = type;
  	ji->data.target = data;
  
  	ji = mono_aot_patch_info_dup (ji);
  
  	ji->next = cfg->patch_info;
  	cfg->patch_info = ji;
  
  	got_offset = mono_aot_get_got_offset (cfg->patch_info);
  	ctx->module->max_got_offset = MAX (ctx->module->max_got_offset, got_offset);
  	/* 
  	 * If the got slot is shared, it means its initialized when the aot image is loaded, so we don't need to
  	 * explicitly initialize it.
  	 */
  	if (!mono_aot_is_shared_got_offset (got_offset)) {
  		//mono_print_ji (ji);
  		//printf ("\n");
  		ctx->has_got_access = TRUE;
  	}
  
  	indexes [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
  	indexes [1] = LLVMConstInt (LLVMInt32Type (), (gssize)got_offset, FALSE);
  	got_entry_addr = LLVMBuildGEP (builder, ctx->module->got_var, indexes, 2, "");
  
  	name = get_aotconst_name (type, data, got_offset);
  	load = LLVMBuildLoad (builder, got_entry_addr, name ? name : "");
  	g_free (name);
  	//set_invariant_load_flag (load);
  
  	return load;
  }
  
  static LLVMValueRef
  get_callee (EmitContext *ctx, LLVMTypeRef llvm_sig, MonoJumpInfoType type, gconstpointer data)
  {
  	LLVMValueRef callee;
  	char *callee_name;
  	if (ctx->llvm_only) {
  		LLVMValueRef load;
  
  		callee_name = mono_aot_get_direct_call_symbol (type, data);
  		if (callee_name) {
  			/* Directly callable */
  			// FIXME: Locking
  			callee = (LLVMValueRef)g_hash_table_lookup (ctx->module->direct_callables, callee_name);
  			if (!callee) {
  				callee = LLVMAddFunction (ctx->lmodule, callee_name, llvm_sig);
  
  				LLVMSetVisibility (callee, LLVMHiddenVisibility);
  
  				g_hash_table_insert (ctx->module->direct_callables, (char*)callee_name, callee);
  			} else {
  				g_free (callee_name);
  			}
  			return callee;
  		}
  
  		/*
  		 * Calls are made through the GOT.
  		 */
  		load = get_aotconst (ctx, type, data);
  
  		return convert (ctx, load, LLVMPointerType (llvm_sig, 0));
  	} else {
  		MonoJumpInfo *ji = NULL;
  
  		callee_name = mono_aot_get_plt_symbol (type, data);
  		if (!callee_name)
  			return NULL;
  
  		if (ctx->cfg->compile_aot)
  			/* Add a patch so referenced wrappers can be compiled in full aot mode */
  			mono_add_patch_info (ctx->cfg, 0, type, data);
  
  		// FIXME: Locking
  		callee = (LLVMValueRef)g_hash_table_lookup (ctx->module->plt_entries, callee_name);
  		if (!callee) {
  			callee = LLVMAddFunction (ctx->lmodule, callee_name, llvm_sig);
  
  			LLVMSetVisibility (callee, LLVMHiddenVisibility);
  
  			g_hash_table_insert (ctx->module->plt_entries, (char*)callee_name, callee);
  		}
  
  		if (ctx->cfg->compile_aot) {
  			ji = g_new0 (MonoJumpInfo, 1);
  			ji->type = type;
  			ji->data.target = data;
  
  			g_hash_table_insert (ctx->module->plt_entries_ji, ji, callee);
  		}
  
  		return callee;
  	}
  }
  
  static int
  get_handler_clause (MonoCompile *cfg, MonoBasicBlock *bb)
  {
  	MonoMethodHeader *header = cfg->header;
  	MonoExceptionClause *clause;
  	int i;
  
  	/* Directly */
  	if (bb->region != -1 && MONO_BBLOCK_IS_IN_REGION (bb, MONO_REGION_TRY))
  		return (bb->region >> 8) - 1;
  
  	/* Indirectly */
  	for (i = 0; i < header->num_clauses; ++i) {
  		clause = &header->clauses [i];
  			   
  		if (MONO_OFFSET_IN_CLAUSE (clause, bb->real_offset) && clause->flags == MONO_EXCEPTION_CLAUSE_NONE)
  			return i;
  	}
  
  	return -1;
  }
  
  static MonoExceptionClause *
  get_most_deep_clause (MonoCompile *cfg, EmitContext *ctx, MonoBasicBlock *bb)
  {
  	// Since they're sorted by nesting we just need
  	// the first one that the bb is a member of
  	MonoExceptionClause *last = NULL;
  
  	for (int i = 0; i < cfg->header->num_clauses; i++) {
  		MonoExceptionClause *curr = &cfg->header->clauses [i];
  
  		if (MONO_OFFSET_IN_CLAUSE (curr, bb->real_offset))
  			return curr;
  		/*
  		if (MONO_OFFSET_IN_CLAUSE (curr, bb->real_offset)) {
  			if (last && CLAUSE_END(last) > CLAUSE_END(curr))
  				last = curr;
  			else
  				last = curr;
  		} else if(last) {
  			break;
  		}
  		*/
  	}
  
  	return last;
  }
  	
  static void
  set_metadata_flag (LLVMValueRef v, const char *flag_name)
  {
  	LLVMValueRef md_arg;
  	int md_kind;
  
  	md_kind = LLVMGetMDKindID (flag_name, strlen (flag_name));
  	md_arg = LLVMMDString ("mono", 4);
  	LLVMSetMetadata (v, md_kind, LLVMMDNode (&md_arg, 1));
  }
  
  static void
  set_invariant_load_flag (LLVMValueRef v)
  {
  	LLVMValueRef md_arg;
  	int md_kind;
  	const char *flag_name;
  
  	// FIXME: Cache this
  	flag_name = "invariant.load";
  	md_kind = LLVMGetMDKindID (flag_name, strlen (flag_name));
  	md_arg = LLVMMDString ("<index>", strlen ("<index>"));
  	LLVMSetMetadata (v, md_kind, LLVMMDNode (&md_arg, 1));
  }
  
  /*
   * emit_call:
   *
   *   Emit an LLVM call or invoke instruction depending on whenever the call is inside
   * a try region.
   */
  static LLVMValueRef
  emit_call (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, LLVMValueRef callee, LLVMValueRef *args, int pindex)
  {
  	MonoCompile *cfg = ctx->cfg;
  	LLVMValueRef lcall = NULL;
  	LLVMBuilderRef builder = *builder_ref;
  	MonoExceptionClause *clause;
  
  	if (ctx->llvm_only) {
  		clause = get_most_deep_clause (cfg, ctx, bb);
  
  		if (clause) {
  			g_assert (clause->flags == MONO_EXCEPTION_CLAUSE_NONE || clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY);
  
  			/*
  			 * Have to use an invoke instead of a call, branching to the
  			 * handler bblock of the clause containing this bblock.
  			 */
  			intptr_t key = CLAUSE_END(clause);
  
  			LLVMBasicBlockRef lpad_bb = (LLVMBasicBlockRef)g_hash_table_lookup (ctx->exc_meta, (gconstpointer)key);
  
  			// FIXME: Find the one that has the lowest end bound for the right start address
  			// FIXME: Finally + nesting
  
  			if (lpad_bb) {
  				LLVMBasicBlockRef noex_bb = gen_bb (ctx, "CALL_NOEX_BB");
  
  				/* Use an invoke */
  				lcall = LLVMBuildInvoke (builder, callee, args, pindex, noex_bb, lpad_bb, "");
  
  				builder = ctx->builder = create_builder (ctx);
  				LLVMPositionBuilderAtEnd (ctx->builder, noex_bb);
  
  				ctx->bblocks [bb->block_num].end_bblock = noex_bb;
  			}
  		}
  	} else {
  		int clause_index = get_handler_clause (cfg, bb);
  
  		if (clause_index != -1) {
  			MonoMethodHeader *header = cfg->header;
  			MonoExceptionClause *ec = &header->clauses [clause_index];
  			MonoBasicBlock *tblock;
  			LLVMBasicBlockRef ex_bb, noex_bb;
  
  			/*
  			 * Have to use an invoke instead of a call, branching to the
  			 * handler bblock of the clause containing this bblock.
  			 */
  
  			g_assert (ec->flags == MONO_EXCEPTION_CLAUSE_NONE || ec->flags == MONO_EXCEPTION_CLAUSE_FINALLY);
  
  			tblock = cfg->cil_offset_to_bb [ec->handler_offset];
  			g_assert (tblock);
  
  			ctx->bblocks [tblock->block_num].invoke_target = TRUE;
  
  			ex_bb = get_bb (ctx, tblock);
  
  			noex_bb = gen_bb (ctx, "NOEX_BB");
  
  			/* Use an invoke */
  			lcall = LLVMBuildInvoke (builder, callee, args, pindex, noex_bb, ex_bb, "");
  
  			builder = ctx->builder = create_builder (ctx);
  			LLVMPositionBuilderAtEnd (ctx->builder, noex_bb);
  
  			ctx->bblocks [bb->block_num].end_bblock = noex_bb;
  		}
  	}
  	
  	if (!lcall) {
  		lcall = LLVMBuildCall (builder, callee, args, pindex, "");
  		ctx->builder = builder;
  	}
  
  	if (builder_ref)
  		*builder_ref = ctx->builder;
  
  	return lcall;
  }
  
  static LLVMValueRef
  emit_load_general (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, int size, LLVMValueRef addr, const char *name, gboolean is_faulting, BarrierKind barrier)
  {
  	const char *intrins_name;
  	LLVMValueRef args [16], res;
  	LLVMTypeRef addr_type;
  
  	if (is_faulting && bb->region != -1 && !ctx->cfg->llvm_only) {
  		LLVMAtomicOrdering ordering;
  
  		switch (barrier) {
  		case LLVM_BARRIER_NONE:
  			ordering = LLVMAtomicOrderingNotAtomic;
  			break;
  		case LLVM_BARRIER_ACQ:
  			ordering = LLVMAtomicOrderingAcquire;
  			break;
  		case LLVM_BARRIER_SEQ:
  			ordering = LLVMAtomicOrderingSequentiallyConsistent;
  			break;
  		default:
  			g_assert_not_reached ();
  			break;
  		}
  
  		/*
  		 * We handle loads which can fault by calling a mono specific intrinsic
  		 * using an invoke, so they are handled properly inside try blocks.
  		 * We can't use this outside clauses, since LLVM optimizes intrinsics which
  		 * are marked with IntrReadArgMem.
  		 */
  		switch (size) {
  		case 1:
  			intrins_name = "llvm.mono.load.i8.p0i8";
  			break;
  		case 2:
  			intrins_name = "llvm.mono.load.i16.p0i16";
  			break;
  		case 4:
  			intrins_name = "llvm.mono.load.i32.p0i32";
  			break;
  		case 8:
  			intrins_name = "llvm.mono.load.i64.p0i64";
  			break;
  		default:
  			g_assert_not_reached ();
  		}
  
  		addr_type = LLVMTypeOf (addr);
  		if (addr_type == LLVMPointerType (LLVMDoubleType (), 0) || addr_type == LLVMPointerType (LLVMFloatType (), 0))
  			addr = LLVMBuildBitCast (*builder_ref, addr, LLVMPointerType (LLVMIntType (size * 8), 0), "");
  
  		args [0] = addr;
  		args [1] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
  		args [2] = LLVMConstInt (LLVMInt1Type (), TRUE, FALSE);
  		args [3] = LLVMConstInt (LLVMInt32Type (), ordering, FALSE);
  		res = emit_call (ctx, bb, builder_ref, LLVMGetNamedFunction (ctx->lmodule, intrins_name), args, 4);
  
  		if (addr_type == LLVMPointerType (LLVMDoubleType (), 0))
  			res = LLVMBuildBitCast (*builder_ref, res, LLVMDoubleType (), "");
  		else if (addr_type == LLVMPointerType (LLVMFloatType (), 0))
  			res = LLVMBuildBitCast (*builder_ref, res, LLVMFloatType (), "");
  		
  		return res;
  	} else {
  		LLVMValueRef res;
  
  		/* 
  		 * We emit volatile loads for loads which can fault, because otherwise
  		 * LLVM will generate invalid code when encountering a load from a
  		 * NULL address.
  		 */
  		 res = mono_llvm_build_load (*builder_ref, addr, name, is_faulting, barrier);
  
  		 /* Mark it with a custom metadata */
  		 /*
  		 if (is_faulting)
  			 set_metadata_flag (res, "mono.faulting.load");
  		 */
  
  		 return res;
  	}
  }
  
  static LLVMValueRef
  emit_load (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, int size, LLVMValueRef addr, const char *name, gboolean is_faulting)
  {
  	return emit_load_general (ctx, bb, builder_ref, size, addr, name, is_faulting, LLVM_BARRIER_NONE);
  }
  
  static void
  emit_store_general (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, int size, LLVMValueRef value, LLVMValueRef addr, gboolean is_faulting, BarrierKind barrier)
  {
  	const char *intrins_name;
  	LLVMValueRef args [16];
  
  	if (is_faulting && bb->region != -1 && !ctx->cfg->llvm_only) {
  		LLVMAtomicOrdering ordering;
  
  		switch (barrier) {
  		case LLVM_BARRIER_NONE:
  			ordering = LLVMAtomicOrderingNotAtomic;
  			break;
  		case LLVM_BARRIER_REL:
  			ordering = LLVMAtomicOrderingRelease;
  			break;
  		case LLVM_BARRIER_SEQ:
  			ordering = LLVMAtomicOrderingSequentiallyConsistent;
  			break;
  		default:
  			g_assert_not_reached ();
  			break;
  		}
  
  		switch (size) {
  		case 1:
  			intrins_name = "llvm.mono.store.i8.p0i8";
  			break;
  		case 2:
  			intrins_name = "llvm.mono.store.i16.p0i16";
  			break;
  		case 4:
  			intrins_name = "llvm.mono.store.i32.p0i32";
  			break;
  		case 8:
  			intrins_name = "llvm.mono.store.i64.p0i64";
  			break;
  		default:
  			g_assert_not_reached ();
  		}
  
  		if (LLVMTypeOf (value) == LLVMDoubleType () || LLVMTypeOf (value) == LLVMFloatType ()) {
  			value = LLVMBuildBitCast (*builder_ref, value, LLVMIntType (size * 8), "");
  			addr = LLVMBuildBitCast (*builder_ref, addr, LLVMPointerType (LLVMIntType (size * 8), 0), "");
  		}
  
  		args [0] = value;
  		args [1] = addr;
  		args [2] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
  		args [3] = LLVMConstInt (LLVMInt1Type (), TRUE, FALSE);
  		args [4] = LLVMConstInt (LLVMInt32Type (), ordering, FALSE);
  		emit_call (ctx, bb, builder_ref, LLVMGetNamedFunction (ctx->lmodule, intrins_name), args, 5);
  	} else {
  		mono_llvm_build_store (*builder_ref, value, addr, is_faulting, barrier);
  	}
  }
  
  static void
  emit_store (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, int size, LLVMValueRef value, LLVMValueRef addr, gboolean is_faulting)
  {
  	emit_store_general (ctx, bb, builder_ref, size, value, addr, is_faulting, LLVM_BARRIER_NONE);
  }
  
  /*
   * emit_cond_system_exception:
   *
   *   Emit code to throw the exception EXC_TYPE if the condition CMP is false.
   * Might set the ctx exception.
   */
  static void
  emit_cond_system_exception (EmitContext *ctx, MonoBasicBlock *bb, const char *exc_type, LLVMValueRef cmp)
  {
  	LLVMBasicBlockRef ex_bb, ex2_bb = NULL, noex_bb;
  	LLVMBuilderRef builder;
  	MonoClass *exc_class;
  	LLVMValueRef args [2];
  	LLVMValueRef callee;
  	
  	ex_bb = gen_bb (ctx, "EX_BB");
  	if (ctx->llvm_only)
  		ex2_bb = gen_bb (ctx, "EX2_BB");
  	noex_bb = gen_bb (ctx, "NOEX_BB");
  
  	LLVMBuildCondBr (ctx->builder, cmp, ex_bb, noex_bb);
  
  	exc_class = mono_class_from_name (mono_get_corlib (), "System", exc_type);
  	g_assert (exc_class);
  
  	/* Emit exception throwing code */
  	ctx->builder = builder = create_builder (ctx);
  	LLVMPositionBuilderAtEnd (builder, ex_bb);
  
  	if (ctx->cfg->llvm_only) {
  		static LLVMTypeRef sig;
  
  		if (!sig)
  			sig = LLVMFunctionType1 (LLVMVoidType (), LLVMInt32Type (), FALSE);
  		callee = get_callee (ctx, sig, MONO_PATCH_INFO_INTERNAL_METHOD, "mono_llvm_throw_corlib_exception");
  
  		LLVMBuildBr (builder, ex2_bb);
  
  		ctx->builder = builder = create_builder (ctx);
  		LLVMPositionBuilderAtEnd (ctx->builder, ex2_bb);
  
  		args [0] = LLVMConstInt (LLVMInt32Type (), exc_class->type_token - MONO_TOKEN_TYPE_DEF, FALSE);
  		emit_call (ctx, bb, &builder, callee, args, 1);
  		LLVMBuildUnreachable (builder);
  
  		ctx->builder = builder = create_builder (ctx);
  		LLVMPositionBuilderAtEnd (ctx->builder, noex_bb);
  
  		ctx->bblocks [bb->block_num].end_bblock = noex_bb;
  
  		ctx->ex_index ++;
  		return;
  	}
  
  	callee = ctx->module->throw_corlib_exception;
  	if (!callee) {
  		LLVMTypeRef sig;
  		const char *icall_name;
  
  		sig = LLVMFunctionType2 (LLVMVoidType (), LLVMInt32Type (), LLVMPointerType (LLVMInt8Type (), 0), FALSE);
  		icall_name = "llvm_throw_corlib_exception_abs_trampoline";
  
  		if (ctx->cfg->compile_aot) {
  			callee = get_callee (ctx, sig, MONO_PATCH_INFO_INTERNAL_METHOD, icall_name);
  		} else {
  			callee = LLVMAddFunction (ctx->lmodule, "llvm_throw_corlib_exception_trampoline", sig);
  
  			/*
  			 * Differences between the LLVM/non-LLVM throw corlib exception trampoline:
  			 * - On x86, LLVM generated code doesn't push the arguments
  			 * - The trampoline takes the throw address as an arguments, not a pc offset.
  			 */
  			LLVMAddGlobalMapping (ctx->module->ee, callee, resolve_patch (ctx->cfg, MONO_PATCH_INFO_INTERNAL_METHOD, icall_name));
  
  			mono_memory_barrier ();
  			ctx->module->throw_corlib_exception = callee;
  		}
  	}
  
  	if (IS_TARGET_X86 || IS_TARGET_AMD64)
  		args [0] = LLVMConstInt (LLVMInt32Type (), exc_class->type_token - MONO_TOKEN_TYPE_DEF, FALSE);
  	else
  		args [0] = LLVMConstInt (LLVMInt32Type (), exc_class->type_token, FALSE);
  
  	/*
  	 * The LLVM mono branch contains changes so a block address can be passed as an
  	 * argument to a call.
  	 */
  	args [1] = LLVMBlockAddress (ctx->lmethod, ex_bb);
  	emit_call (ctx, bb, &builder, callee, args, 2);
  
  	LLVMBuildUnreachable (builder);
  
  	ctx->builder = builder = create_builder (ctx);
  	LLVMPositionBuilderAtEnd (ctx->builder, noex_bb);
  
  	ctx->bblocks [bb->block_num].end_bblock = noex_bb;
  
  	ctx->ex_index ++;
  	return;
  }
  
  /*
   * emit_args_to_vtype:
   *
   *   Emit code to store the vtype in the arguments args to the address ADDRESS.
   */
  static void
  emit_args_to_vtype (EmitContext *ctx, LLVMBuilderRef builder, MonoType *t, LLVMValueRef address, LLVMArgInfo *ainfo, LLVMValueRef *args)
  {
  	int j, size, nslots;
  
  	size = get_vtype_size (t);
  
  	if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (t))) {
  		address = LLVMBuildBitCast (ctx->builder, address, LLVMPointerType (LLVMInt8Type (), 0), "");
  	}
  
  	if (ainfo->storage == LLVMArgAsFpArgs)
  		nslots = ainfo->nslots;
  	else
  		nslots = 2;
  
  	for (j = 0; j < nslots; ++j) {
  		LLVMValueRef index [2], addr, daddr;
  		int part_size = size > sizeof (gpointer) ? sizeof (gpointer) : size;
  		LLVMTypeRef part_type;
  
  		if (ainfo->pair_storage [j] == LLVMArgNone)
  			continue;
  
  		switch (ainfo->pair_storage [j]) {
  		case LLVMArgInIReg: {
  			part_type = LLVMIntType (part_size * 8);
  			if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (t))) {
  				index [0] = LLVMConstInt (LLVMInt32Type (), j * sizeof (gpointer), FALSE);
  				addr = LLVMBuildGEP (builder, address, index, 1, "");
  			} else {
  				daddr = LLVMBuildBitCast (ctx->builder, address, LLVMPointerType (IntPtrType (), 0), "");
  				index [0] = LLVMConstInt (LLVMInt32Type (), j, FALSE);
  				addr = LLVMBuildGEP (builder, daddr, index, 1, "");
  			}
  			LLVMBuildStore (builder, convert (ctx, args [j], part_type), LLVMBuildBitCast (ctx->builder, addr, LLVMPointerType (part_type, 0), ""));
  			break;
  		}
  		case LLVMArgInFPReg: {
  			LLVMTypeRef arg_type;
  
  			if (ainfo->esize == 8)
  				arg_type = LLVMDoubleType ();
  			else
  				arg_type = LLVMFloatType ();
  
  			index [0] = LLVMConstInt (LLVMInt32Type (), j, FALSE);
  			daddr = LLVMBuildBitCast (ctx->builder, address, LLVMPointerType (arg_type, 0), "");
  			addr = LLVMBuildGEP (builder, daddr, index, 1, "");
  			LLVMBuildStore (builder, args [j], addr);
  			break;
  		}
  		case LLVMArgNone:
  			break;
  		default:
  			g_assert_not_reached ();
  		}
  
  		size -= sizeof (gpointer);
  	}
  }
  
  /*
   * emit_vtype_to_args:
   *
   *   Emit code to load a vtype at address ADDRESS into scalar arguments. Store the arguments
   * into ARGS, and the number of arguments into NARGS.
   */
  static void
  emit_vtype_to_args (EmitContext *ctx, LLVMBuilderRef builder, MonoType *t, LLVMValueRef address, LLVMArgInfo *ainfo, LLVMValueRef *args, guint32 *nargs)
  {
  	int pindex = 0;
  	int j, size, nslots;
  	LLVMTypeRef arg_type;
  
  	size = get_vtype_size (t);
  
  	if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (t)))
  		address = LLVMBuildBitCast (ctx->builder, address, LLVMPointerType (LLVMInt8Type (), 0), "");
  
  	if (ainfo->storage == LLVMArgAsFpArgs)
  		nslots = ainfo->nslots;
  	else
  		nslots = 2;
  	for (j = 0; j < nslots; ++j) {
  		LLVMValueRef index [2], addr, daddr;
  		int partsize = size > sizeof (gpointer) ? sizeof (gpointer) : size;
  
  		if (ainfo->pair_storage [j] == LLVMArgNone)
  			continue;
  
  		switch (ainfo->pair_storage [j]) {
  		case LLVMArgInIReg:
  			if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (t))) {
  				index [0] = LLVMConstInt (LLVMInt32Type (), j * sizeof (gpointer), FALSE);
  				addr = LLVMBuildGEP (builder, address, index, 1, "");
  			} else {
  				daddr = LLVMBuildBitCast (ctx->builder, address, LLVMPointerType (IntPtrType (), 0), "");
  				index [0] = LLVMConstInt (LLVMInt32Type (), j, FALSE);
  				addr = LLVMBuildGEP (builder, daddr, index, 1, "");
  			}
  			args [pindex ++] = convert (ctx, LLVMBuildLoad (builder, LLVMBuildBitCast (ctx->builder, addr, LLVMPointerType (LLVMIntType (partsize * 8), 0), ""), ""), IntPtrType ());
  			break;
  		case LLVMArgInFPReg:
  			if (ainfo->esize == 8)
  				arg_type = LLVMDoubleType ();
  			else
  				arg_type = LLVMFloatType ();
  			daddr = LLVMBuildBitCast (ctx->builder, address, LLVMPointerType (arg_type, 0), "");
  			index [0] = LLVMConstInt (LLVMInt32Type (), j, FALSE);
  			addr = LLVMBuildGEP (builder, daddr, index, 1, "");
  			args [pindex ++] = LLVMBuildLoad (builder, addr, "");
  			break;
  		case LLVMArgNone:
  			break;
  		default:
  			g_assert_not_reached ();
  		}
  		size -= sizeof (gpointer);
  	}
  
  	*nargs = pindex;
  }
  
  static LLVMValueRef
  build_alloca_llvm_type_name (EmitContext *ctx, LLVMTypeRef t, int align, const char *name)
  {
  	/*
  	 * 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, t, NULL, align, name);
  	return ctx->last_alloca;
  }
  
  static LLVMValueRef
  build_alloca_llvm_type (EmitContext *ctx, LLVMTypeRef t, int align)
  {
  	return build_alloca_llvm_type_name (ctx, t, align, "");
  }
  
  static LLVMValueRef
  build_alloca (EmitContext *ctx, MonoType *t)
  {
  	MonoClass *k = mono_class_from_mono_type (t);
  	int align;
  
  	if (MONO_CLASS_IS_SIMD (ctx->cfg, k))
  		align = 16;
  	else
  		align = mono_class_min_align (k);
  
  	/* Sometimes align is not a power of 2 */
  	while (mono_is_power_of_two (align) == -1)
  		align ++;
  
  	return build_alloca_llvm_type (ctx, type_to_llvm_type (ctx, t), align);
  }
  
  /*
   * Put the global into the 'llvm.used' array to prevent it from being optimized away.
   */
  static void
  mark_as_used (MonoLLVMModule *module, LLVMValueRef global)
  {
  	if (!module->used)
  		module->used = g_ptr_array_sized_new (16);
  	g_ptr_array_add (module->used, global);
  }
  
  static void
  emit_llvm_used (MonoLLVMModule *module)
  {
  	LLVMModuleRef lmodule = module->lmodule;
  	LLVMTypeRef used_type;
  	LLVMValueRef used, *used_elem;
  	int i;
  		
  	if (!module->used)
  		return;
  
  	used_type = LLVMArrayType (LLVMPointerType (LLVMInt8Type (), 0), module->used->len);
  	used = LLVMAddGlobal (lmodule, used_type, "llvm.used");
  	used_elem = g_new0 (LLVMValueRef, module->used->len);
  	for (i = 0; i < module->used->len; ++i)
  		used_elem [i] = LLVMConstBitCast ((LLVMValueRef)g_ptr_array_index (module->used, i), LLVMPointerType (LLVMInt8Type (), 0));
  	LLVMSetInitializer (used, LLVMConstArray (LLVMPointerType (LLVMInt8Type (), 0), used_elem, module->used->len));
  	LLVMSetLinkage (used, LLVMAppendingLinkage);
  	LLVMSetSection (used, "llvm.metadata");
  }
  
  /*
   * emit_get_method:
   *
   *   Emit a function mapping method indexes to their code
   */
  static void
  emit_get_method (MonoLLVMModule *module)
  {
  	LLVMModuleRef lmodule = module->lmodule;
  	LLVMValueRef func, switch_ins, m;
  	LLVMBasicBlockRef entry_bb, fail_bb, bb, code_start_bb, code_end_bb;
  	LLVMBasicBlockRef *bbs;
  	LLVMTypeRef rtype;
  	LLVMBuilderRef builder;
  	char *name;
  	int i;
  
  	/*
  	 * Emit a switch statement. Emitting a table of function addresses is smaller/faster,
  	 * but generating code seems safer.
  	 */
  	rtype = LLVMPointerType (LLVMInt8Type (), 0);
  	func = LLVMAddFunction (lmodule, module->get_method_symbol, LLVMFunctionType1 (rtype, LLVMInt32Type (), FALSE));
  	LLVMSetLinkage (func, LLVMExternalLinkage);
  	LLVMSetVisibility (func, LLVMHiddenVisibility);
  	LLVMAddFunctionAttr (func, LLVMNoUnwindAttribute);
  	module->get_method = func;
  
  	entry_bb = LLVMAppendBasicBlock (func, "ENTRY");
  
  	/*
  	 * Return llvm_code_start/llvm_code_end when called with -1/-2.
  	 * Hopefully, the toolchain doesn't reorder these functions. If it does,
  	 * then we will have to find another solution.
  	 */
  
  	name = g_strdup_printf ("BB_CODE_START");
  	code_start_bb = LLVMAppendBasicBlock (func, name);
  	g_free (name);
  	builder = LLVMCreateBuilder ();
  	LLVMPositionBuilderAtEnd (builder, code_start_bb);
  	LLVMBuildRet (builder, LLVMBuildBitCast (builder, module->code_start, rtype, ""));
  
  	name = g_strdup_printf ("BB_CODE_END");
  	code_end_bb = LLVMAppendBasicBlock (func, name);
  	g_free (name);
  	builder = LLVMCreateBuilder ();
  	LLVMPositionBuilderAtEnd (builder, code_end_bb);
  	LLVMBuildRet (builder, LLVMBuildBitCast (builder, module->code_end, rtype, ""));
  
  	bbs = g_new0 (LLVMBasicBlockRef, module->max_method_idx + 1);
  	for (i = 0; i < module->max_method_idx + 1; ++i) {
  		name = g_strdup_printf ("BB_%d", i);
  		bb = LLVMAppendBasicBlock (func, name);
  		g_free (name);
  		bbs [i] = bb;
  
  		builder = LLVMCreateBuilder ();
  		LLVMPositionBuilderAtEnd (builder, bb);
  
  		m = (LLVMValueRef)g_hash_table_lookup (module->idx_to_lmethod, GINT_TO_POINTER (i));
  		if (m)
  			LLVMBuildRet (builder, LLVMBuildBitCast (builder, m, rtype, ""));
  		else
  			LLVMBuildRet (builder, LLVMConstNull (rtype));
  	}
  
  	fail_bb = LLVMAppendBasicBlock (func, "FAIL");
  	builder = LLVMCreateBuilder ();
  	LLVMPositionBuilderAtEnd (builder, fail_bb);
  	LLVMBuildRet (builder, LLVMConstNull (rtype));
  
  	builder = LLVMCreateBuilder ();
  	LLVMPositionBuilderAtEnd (builder, entry_bb);
  
  	switch_ins = LLVMBuildSwitch (builder, LLVMGetParam (func, 0), fail_bb, 0);
  	LLVMAddCase (switch_ins, LLVMConstInt (LLVMInt32Type (), -1, FALSE), code_start_bb);
  	LLVMAddCase (switch_ins, LLVMConstInt (LLVMInt32Type (), -2, FALSE), code_end_bb);
  	for (i = 0; i < module->max_method_idx + 1; ++i) {
  		LLVMAddCase (switch_ins, LLVMConstInt (LLVMInt32Type (), i, FALSE), bbs [i]);
  	}
  
  	mark_as_used (module, func);
  }
  
  /*
   * emit_get_unbox_tramp:
   *
   *   Emit a function mapping method indexes to their unbox trampoline
   */
  static void
  emit_get_unbox_tramp (MonoLLVMModule *module)
  {
  	LLVMModuleRef lmodule = module->lmodule;
  	LLVMValueRef func, switch_ins, m;
  	LLVMBasicBlockRef entry_bb, fail_bb, bb;
  	LLVMBasicBlockRef *bbs;
  	LLVMTypeRef rtype;
  	LLVMBuilderRef builder;
  	char *name;
  	int i;
  
  	/* Similar to emit_get_method () */
  
  	rtype = LLVMPointerType (LLVMInt8Type (), 0);
  	func = LLVMAddFunction (lmodule, module->get_unbox_tramp_symbol, LLVMFunctionType1 (rtype, LLVMInt32Type (), FALSE));
  	LLVMSetLinkage (func, LLVMExternalLinkage);
  	LLVMSetVisibility (func, LLVMHiddenVisibility);
  	LLVMAddFunctionAttr (func, LLVMNoUnwindAttribute);
  	module->get_unbox_tramp = func;
  
  	entry_bb = LLVMAppendBasicBlock (func, "ENTRY");
  
  	bbs = g_new0 (LLVMBasicBlockRef, module->max_method_idx + 1);
  	for (i = 0; i < module->max_method_idx + 1; ++i) {
  		m = (LLVMValueRef)g_hash_table_lookup (module->idx_to_unbox_tramp, GINT_TO_POINTER (i));
  		if (!m)
  			continue;
  
  		name = g_strdup_printf ("BB_%d", i);
  		bb = LLVMAppendBasicBlock (func, name);
  		g_free (name);
  		bbs [i] = bb;
  
  		builder = LLVMCreateBuilder ();
  		LLVMPositionBuilderAtEnd (builder, bb);
  
  		LLVMBuildRet (builder, LLVMBuildBitCast (builder, m, rtype, ""));
  	}
  
  	fail_bb = LLVMAppendBasicBlock (func, "FAIL");
  	builder = LLVMCreateBuilder ();
  	LLVMPositionBuilderAtEnd (builder, fail_bb);
  	LLVMBuildRet (builder, LLVMConstNull (rtype));
  
  	builder = LLVMCreateBuilder ();
  	LLVMPositionBuilderAtEnd (builder, entry_bb);
  
  	switch_ins = LLVMBuildSwitch (builder, LLVMGetParam (func, 0), fail_bb, 0);
  	for (i = 0; i < module->max_method_idx + 1; ++i) {
  		m = (LLVMValueRef)g_hash_table_lookup (module->idx_to_unbox_tramp, GINT_TO_POINTER (i));
  		if (!m)
  			continue;
  
  		LLVMAddCase (switch_ins, LLVMConstInt (LLVMInt32Type (), i, FALSE), bbs [i]);
  	}
  
  	mark_as_used (module, func);
  }
  
  /* Add a function to mark the beginning of LLVM code */
  static void
  emit_llvm_code_start (MonoLLVMModule *module)
  {
  	LLVMModuleRef lmodule = module->lmodule;
  	LLVMValueRef func;
  	LLVMBasicBlockRef entry_bb;
  	LLVMBuilderRef builder;
  
  	func = LLVMAddFunction (lmodule, "llvm_code_start", LLVMFunctionType (LLVMVoidType (), NULL, 0, FALSE));
  	LLVMSetLinkage (func, LLVMInternalLinkage);
  	LLVMAddFunctionAttr (func, LLVMNoUnwindAttribute);
  	module->code_start = func;
  	entry_bb = LLVMAppendBasicBlock (func, "ENTRY");
  	builder = LLVMCreateBuilder ();
  	LLVMPositionBuilderAtEnd (builder, entry_bb);
  	LLVMBuildRetVoid (builder);
  }
  
  static LLVMValueRef
  emit_init_icall_wrapper (MonoLLVMModule *module, const char *name, const char *icall_name, int subtype)
  {
  	LLVMModuleRef lmodule = module->lmodule;
  	LLVMValueRef func, indexes [2], got_entry_addr, args [16], callee;
  	LLVMBasicBlockRef entry_bb;
  	LLVMBuilderRef builder;
  	LLVMTypeRef sig;
  	MonoJumpInfo *ji;
  	int got_offset;
  
  	switch (subtype) {
  	case 0:
  		func = LLVMAddFunction (lmodule, name, LLVMFunctionType1 (LLVMVoidType (), LLVMInt32Type (), FALSE));
  		sig = LLVMFunctionType2 (LLVMVoidType (), IntPtrType (), LLVMInt32Type (), FALSE);
  		break;
  	case 1:
  		func = LLVMAddFunction (lmodule, name, LLVMFunctionType2 (LLVMVoidType (), LLVMInt32Type (), IntPtrType (), FALSE));
  		sig = LLVMFunctionType3 (LLVMVoidType (), IntPtrType (), LLVMInt32Type (), IntPtrType (), FALSE);
  		break;
  	case 2:
  		func = LLVMAddFunction (lmodule, name, LLVMFunctionType2 (LLVMVoidType (), LLVMInt32Type (), ObjRefType (), FALSE));
  		sig = LLVMFunctionType3 (LLVMVoidType (), IntPtrType (), LLVMInt32Type (), ObjRefType (), FALSE);
  		break;
  	default:
  		g_assert_not_reached ();
  	}
  	LLVMSetLinkage (func, LLVMInternalLinkage);
  	LLVMAddFunctionAttr (func, LLVMNoInlineAttribute);
  	mono_llvm_set_preserveall_cc (func);
  	entry_bb = LLVMAppendBasicBlock (func, "ENTRY");
  	builder = LLVMCreateBuilder ();
  	LLVMPositionBuilderAtEnd (builder, entry_bb);
  
  	/* get_aotconst */
  	ji = g_new0 (MonoJumpInfo, 1);
  	ji->type = MONO_PATCH_INFO_AOT_MODULE;
  	ji = mono_aot_patch_info_dup (ji);
  	got_offset = mono_aot_get_got_offset (ji);
  	module->max_got_offset = MAX (module->max_got_offset, got_offset);
  	indexes [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
  	indexes [1] = LLVMConstInt (LLVMInt32Type (), got_offset, FALSE);
  	got_entry_addr = LLVMBuildGEP (builder, module->got_var, indexes, 2, "");
  	args [0] = LLVMBuildPtrToInt (builder, LLVMBuildLoad (builder, got_entry_addr, ""), IntPtrType (), "");
  	args [1] = LLVMGetParam (func, 0);
  	if (subtype)
  		args [2] = LLVMGetParam (func, 1);
  
  	ji = g_new0 (MonoJumpInfo, 1);
  	ji->type = MONO_PATCH_INFO_INTERNAL_METHOD;
  	ji->data.name = icall_name;
  	ji = mono_aot_patch_info_dup (ji);
  	got_offset = mono_aot_get_got_offset (ji);
  	module->max_got_offset = MAX (module->max_got_offset, got_offset);
  	indexes [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
  	indexes [1] = LLVMConstInt (LLVMInt32Type (), got_offset, FALSE);
  	got_entry_addr = LLVMBuildGEP (builder, module->got_var, indexes, 2, "");
  	callee = LLVMBuildLoad (builder, got_entry_addr, "");
  	callee = LLVMBuildBitCast (builder, callee, LLVMPointerType (sig, 0), "");
  	LLVMBuildCall (builder, callee, args, LLVMCountParamTypes (sig), "");
  
  	// Set the inited flag
  	indexes [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
  	indexes [1] = LLVMGetParam (func, 0);
  	LLVMBuildStore (builder, LLVMConstInt (LLVMInt8Type (), 1, FALSE), LLVMBuildGEP (builder, module->inited_var, indexes, 2, ""));
  
  	LLVMBuildRetVoid (builder);
  
  	LLVMVerifyFunction(func, 0);
  	return func;
  }
  
  /*
   * Emit wrappers around the C icalls used to initialize llvm methods, to
   * make the calling code smaller and to enable usage of the llvm
   * PreserveAll calling convention.
   */
  static void
  emit_init_icall_wrappers (MonoLLVMModule *module)
  {
  	module->init_method = emit_init_icall_wrapper (module, "init_method", "mono_aot_init_llvm_method", 0);
  	module->init_method_gshared_rgctx = emit_init_icall_wrapper (module, "init_method_gshared_rgctx", "mono_aot_init_gshared_method_rgctx", 1);
  	module->init_method_gshared_this = emit_init_icall_wrapper (module, "init_method_gshared_this", "mono_aot_init_gshared_method_this", 2);
  }
  
  static void
  emit_llvm_code_end (MonoLLVMModule *module)
  {
  	LLVMModuleRef lmodule = module->lmodule;
  	LLVMValueRef func;
  	LLVMBasicBlockRef entry_bb;
  	LLVMBuilderRef builder;
  
  	func = LLVMAddFunction (lmodule, "llvm_code_end", LLVMFunctionType (LLVMVoidType (), NULL, 0, FALSE));
  	LLVMSetLinkage (func, LLVMInternalLinkage);
  	LLVMAddFunctionAttr (func, LLVMNoUnwindAttribute);
  	module->code_end = func;
  	entry_bb = LLVMAppendBasicBlock (func, "ENTRY");
  	builder = LLVMCreateBuilder ();
  	LLVMPositionBuilderAtEnd (builder, entry_bb);
  	LLVMBuildRetVoid (builder);
  }
  
  static void
  emit_div_check (EmitContext *ctx, LLVMBuilderRef builder, MonoBasicBlock *bb, MonoInst *ins, LLVMValueRef lhs, LLVMValueRef rhs)
  {
  	gboolean need_div_check = ctx->cfg->backend->need_div_check;
  
  	if (bb->region)
  		/* LLVM doesn't know that these can throw an exception since they are not called through an intrinsic */
  		need_div_check = TRUE;
  
  	if (!need_div_check)
  		return;
  
  	switch (ins->opcode) {
  	case OP_IDIV:
  	case OP_LDIV:
  	case OP_IREM:
  	case OP_LREM:
  	case OP_IDIV_UN:
  	case OP_LDIV_UN:
  	case OP_IREM_UN:
  	case OP_LREM_UN:
  	case OP_IDIV_IMM:
  	case OP_LDIV_IMM:
  	case OP_IREM_IMM:
  	case OP_LREM_IMM:
  	case OP_IDIV_UN_IMM:
  	case OP_LDIV_UN_IMM:
  	case OP_IREM_UN_IMM:
  	case OP_LREM_UN_IMM: {
  		LLVMValueRef cmp;
  		gboolean is_signed = (ins->opcode == OP_IDIV || ins->opcode == OP_LDIV || ins->opcode == OP_IREM || ins->opcode == OP_LREM ||
  							  ins->opcode == OP_IDIV_IMM || ins->opcode == OP_LDIV_IMM || ins->opcode == OP_IREM_IMM || ins->opcode == OP_LREM_IMM);
  
  		cmp = LLVMBuildICmp (builder, LLVMIntEQ, rhs, LLVMConstInt (LLVMTypeOf (rhs), 0, FALSE), "");
  		emit_cond_system_exception (ctx, bb, "DivideByZeroException", cmp);
  		CHECK_FAILURE (ctx);
  		builder = ctx->builder;
  
  		/* b == -1 && a == 0x80000000 */
  		if (is_signed) {
  			LLVMValueRef c = (LLVMTypeOf (lhs) == LLVMInt32Type ()) ? LLVMConstInt (LLVMTypeOf (lhs), 0x80000000, FALSE) : LLVMConstInt (LLVMTypeOf (lhs), 0x8000000000000000LL, FALSE);
  			LLVMValueRef cond1 = LLVMBuildICmp (builder, LLVMIntEQ, rhs, LLVMConstInt (LLVMTypeOf (rhs), -1, FALSE), "");
  			LLVMValueRef cond2 = LLVMBuildICmp (builder, LLVMIntEQ, lhs, c, "");
  
  			cmp = LLVMBuildICmp (builder, LLVMIntEQ, LLVMBuildAnd (builder, cond1, cond2, ""), LLVMConstInt (LLVMInt1Type (), 1, FALSE), "");
  			emit_cond_system_exception (ctx, bb, "OverflowException", cmp);
  			CHECK_FAILURE (ctx);
  			builder = ctx->builder;
  		}
  		break;
  	}
  	default:
  		break;
  	}
  
   FAILURE:
  	return;
  }
  
  /*
   * emit_init_method:
   *
   *   Emit code to initialize the GOT slots used by the method.
   */
  static void
  emit_init_method (EmitContext *ctx)
  {
  	LLVMValueRef indexes [16], args [16], callee;
  	LLVMValueRef inited_var, cmp, call;
  	LLVMBasicBlockRef inited_bb, notinited_bb;
  	LLVMBuilderRef builder = ctx->builder;
  	MonoCompile *cfg = ctx->cfg;
  
  	ctx->module->max_inited_idx = MAX (ctx->module->max_inited_idx, cfg->method_index);
  	ctx->module->max_method_idx = MAX (ctx->module->max_method_idx, cfg->method_index);
  
  	indexes [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
  	indexes [1] = LLVMConstInt (LLVMInt32Type (), cfg->method_index, FALSE);
  	inited_var = LLVMBuildLoad (builder, LLVMBuildGEP (builder, ctx->module->inited_var, indexes, 2, ""), "is_inited");
  
  	args [0] = inited_var;
  	args [1] = LLVMConstInt (LLVMInt8Type (), 1, FALSE);
  	inited_var = LLVMBuildCall (ctx->builder, LLVMGetNamedFunction (ctx->lmodule, "llvm.expect.i8"), args, 2, "");
  
  	cmp = LLVMBuildICmp (builder, LLVMIntEQ, inited_var, LLVMConstInt (LLVMTypeOf (inited_var), 0, FALSE), "");
  
  	inited_bb = ctx->inited_bb;
  	notinited_bb = gen_bb (ctx, "NOTINITED_BB");
  
  	LLVMBuildCondBr (ctx->builder, cmp, notinited_bb, inited_bb);
  
  	builder = ctx->builder = create_builder (ctx);
  	LLVMPositionBuilderAtEnd (ctx->builder, notinited_bb);
  
  	// FIXME: Cache
  	if (ctx->rgctx_arg) {
  		args [0] = LLVMConstInt (LLVMInt32Type (), cfg->method_index, 0);
  		args [1] = convert (ctx, ctx->rgctx_arg, IntPtrType ());
  		callee = ctx->module->init_method_gshared_rgctx;
  		call = LLVMBuildCall (builder, callee, args, 2, "");
  	} else if (cfg->gshared) {
  		args [0] = LLVMConstInt (LLVMInt32Type (), cfg->method_index, 0);
  		args [1] = convert (ctx, ctx->this_arg, ObjRefType ());
  		callee = ctx->module->init_method_gshared_this;
  		call = LLVMBuildCall (builder, callee, args, 2, "");
  	} else {
  		args [0] = LLVMConstInt (LLVMInt32Type (), cfg->method_index, 0);
  		callee = ctx->module->init_method;
  		call = LLVMBuildCall (builder, callee, args, 1, "");
  	}
  
  	/*
  	 * This enables llvm to keep arguments in their original registers/
  	 * scratch registers, since the call will not clobber them.
  	 */
  	mono_llvm_set_call_preserveall_cc (call);
  
  	LLVMBuildBr (builder, inited_bb);
  	ctx->bblocks [cfg->bb_entry->block_num].end_bblock = inited_bb;
  
  	builder = ctx->builder = create_builder (ctx);
  	LLVMPositionBuilderAtEnd (ctx->builder, inited_bb);
  }
  
  static void
  emit_unbox_tramp (EmitContext *ctx, const char *method_name, LLVMTypeRef method_type, LLVMValueRef method, int method_index)
  {
  	/*
  	 * Emit unbox trampoline using a tail call
  	 */
  	LLVMValueRef tramp, call, *args;
  	LLVMBuilderRef builder;
  	LLVMBasicBlockRef lbb;
  	char *tramp_name;
  	int i, nargs;
  
  	tramp_name = g_strdup_printf ("ut_%s", method_name);
  	tramp = LLVMAddFunction (ctx->module->lmodule, tramp_name, method_type);
  	LLVMSetLinkage (tramp, LLVMInternalLinkage);
  	LLVMAddFunctionAttr (tramp, LLVMNoUnwindAttribute);
  	if (!ctx->llvm_only && ctx->rgctx_arg_pindex != -1)
  		LLVMAddAttribute (LLVMGetParam (tramp, ctx->rgctx_arg_pindex), LLVMInRegAttribute);
  
  	lbb = LLVMAppendBasicBlock (tramp, "");
  	builder = LLVMCreateBuilder ();
  	LLVMPositionBuilderAtEnd (builder, lbb);
  
  	nargs = LLVMCountParamTypes (method_type);
  	args = g_new0 (LLVMValueRef, nargs);
  	for (i = 0; i < nargs; ++i) {
  		args [i] = LLVMGetParam (tramp, i);
  		if (i == ctx->this_arg_pindex) {
  			LLVMTypeRef arg_type = LLVMTypeOf (args [i]);
  
  			args [i] = LLVMBuildPtrToInt (builder, args [i], IntPtrType (), "");
  			args [i] = LLVMBuildAdd (builder, args [i], LLVMConstInt (IntPtrType (), sizeof (MonoObject), FALSE), "");
  			args [i] = LLVMBuildIntToPtr (builder, args [i], arg_type, "");
  		}
  	}
  	call = LLVMBuildCall (builder, method, args, nargs, "");
  	if (!ctx->llvm_only && ctx->rgctx_arg_pindex != -1)
  		LLVMAddInstrAttribute (call, 1 + ctx->rgctx_arg_pindex, LLVMInRegAttribute);
  	mono_llvm_set_must_tail (call);
  	if (LLVMGetReturnType (method_type) == LLVMVoidType ())
  		LLVMBuildRetVoid (builder);
  	else
  		LLVMBuildRet (builder, call);
  
  	g_hash_table_insert (ctx->module->idx_to_unbox_tramp, GINT_TO_POINTER (method_index), tramp);
  }
  
  /*
   * emit_entry_bb:
   *
   *   Emit code to load/convert arguments.
   */
  static void
  emit_entry_bb (EmitContext *ctx, LLVMBuilderRef builder)
  {
  	int i, j, pindex;
  	MonoCompile *cfg = ctx->cfg;
  	MonoMethodSignature *sig = ctx->sig;
  	LLVMCallInfo *linfo = ctx->linfo;
  	MonoBasicBlock *bb;
  	char **names;
  
  	LLVMBuilderRef old_builder = ctx->builder;
  	ctx->builder = builder;
  
  	ctx->alloca_builder = create_builder (ctx);
  
  	/*
  	 * Handle indirect/volatile variables by allocating memory for them
  	 * using 'alloca', and storing their address in a temporary.
  	 */
  	for (i = 0; i < cfg->num_varinfo; ++i) {
  		MonoInst *var = cfg->varinfo [i];
  		LLVMTypeRef vtype;
  
  		if (var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT) || (mini_type_is_vtype (var->inst_vtype) && !MONO_CLASS_IS_SIMD (ctx->cfg, var->klass))) {
  			vtype = type_to_llvm_type (ctx, var->inst_vtype);
  			CHECK_FAILURE (ctx);
  			/* Could be already created by an OP_VPHI */
  			if (!ctx->addresses [var->dreg])
  				ctx->addresses [var->dreg] = build_alloca (ctx, var->inst_vtype);
  			ctx->vreg_cli_types [var->dreg] = var->inst_vtype;
  		}
  	}
  
  	names = g_new (char *, sig->param_count);
  	mono_method_get_param_names (cfg->method, (const char **) names);
  
  	for (i = 0; i < sig->param_count; ++i) {
  		LLVMArgInfo *ainfo = &linfo->args [i + sig->hasthis];
  		int reg = cfg->args [i + sig->hasthis]->dreg;
  		char *name;
  
  		pindex = ainfo->pindex;
  
  		switch (ainfo->storage) {
  		case LLVMArgVtypeInReg:
  		case LLVMArgAsFpArgs: {
  			LLVMValueRef args [8];
  			int j;
  
  			/* The argument is received as a set of int/fp arguments, store them into the real argument */
  			memset (args, 0, sizeof (args));
  			if (ainfo->storage == LLVMArgVtypeInReg) {
  				args [0] = LLVMGetParam (ctx->lmethod, pindex);
  				if (ainfo->pair_storage [1] != LLVMArgNone)
  					args [1] = LLVMGetParam (ctx->lmethod, pindex + 1);
  			} else {
  				g_assert (ainfo->nslots <= 8);
  				for (j = 0; j < ainfo->nslots; ++j)
  					args [j] = LLVMGetParam (ctx->lmethod, pindex + j);
  			}
  			ctx->addresses [reg] = build_alloca (ctx, ainfo->type);
  
  			emit_args_to_vtype (ctx, builder, ainfo->type, ctx->addresses [reg], ainfo, args);
  
  			if (ainfo->storage == LLVMArgVtypeInReg && MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (ainfo->type))) {
  				/* Treat these as normal values */
  				ctx->values [reg] = LLVMBuildLoad (builder, ctx->addresses [reg], "");
  			}
  			break;
  		}
  		case LLVMArgVtypeByVal: {
  			ctx->addresses [reg] = LLVMGetParam (ctx->lmethod, pindex);
  
  			if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (ainfo->type))) {
  				/* Treat these as normal values */
  				ctx->values [reg] = LLVMBuildLoad (builder, ctx->addresses [reg], "");
  			}
  			break;
  		}
  		case LLVMArgVtypeByRef: {
  			/* The argument is passed by ref */
  			ctx->addresses [reg] = LLVMGetParam (ctx->lmethod, pindex);
  			break;
  		}
  		case LLVMArgScalarByRef: {
  			if (names [i])
  				name = g_strdup_printf ("arg_%s", names [i]);
  			else
  				name = g_strdup_printf ("arg_%d", i);
  			ctx->values [reg] = LLVMBuildLoad (builder, LLVMGetParam (ctx->lmethod, pindex), name);
  			g_free (name);
  			break;
  		}
  		case LLVMArgAsIArgs: {
  			LLVMValueRef arg = LLVMGetParam (ctx->lmethod, pindex);
  
  			ctx->addresses [reg] = build_alloca (ctx, ainfo->type);
  
  			/* The argument is received as an array of ints, store it into the real argument */
  			LLVMBuildStore (ctx->builder, arg, convert (ctx, ctx->addresses [reg], LLVMPointerType (LLVMTypeOf (arg), 0)));
  			break;
  		}
  		case LLVMArgVtypeAsScalar:
  			g_assert_not_reached ();
  			break;
  		default:
  			ctx->values [reg] = convert_full (ctx, ctx->values [reg], llvm_type_to_stack_type (cfg, type_to_llvm_type (ctx, ainfo->type)), type_is_unsigned (ctx, ainfo->type));
  			break;
  		}
  	}
  	g_free (names);
  
  	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)
  		if (!mini_type_is_vtype (sig->params [i]))
  			emit_volatile_store (ctx, cfg->args [i + sig->hasthis]->dreg);
  
  	if (sig->hasthis && !cfg->rgctx_var && cfg->gshared) {
  		LLVMValueRef this_alloc;
  
  		/*
  		 * The exception handling code needs the location where the this argument was
  		 * stored for gshared methods. We create a separate alloca to hold it, and mark it
  		 * with the "mono.this" custom metadata to tell llvm that it needs to save its
  		 * location into the LSDA.
  		 */
  		this_alloc = mono_llvm_build_alloca (builder, ThisType (), LLVMConstInt (LLVMInt32Type (), 1, FALSE), 0, "");
  		/* This volatile store will keep the alloca alive */
  		mono_llvm_build_store (builder, ctx->values [cfg->args [0]->dreg], this_alloc, TRUE, LLVM_BARRIER_NONE);
  
  		set_metadata_flag (this_alloc, "mono.this");
  	}
  
  	if (cfg->rgctx_var) {
  		LLVMValueRef rgctx_alloc, store;
  
  		/*
  		 * We handle the rgctx arg similarly to the this pointer.
  		 */
  		g_assert (ctx->addresses [cfg->rgctx_var->dreg]);
  		rgctx_alloc = ctx->addresses [cfg->rgctx_var->dreg];
  		/* This volatile store will keep the alloca alive */
  		store = mono_llvm_build_store (builder, convert (ctx, ctx->rgctx_arg, IntPtrType ()), rgctx_alloc, TRUE, LLVM_BARRIER_NONE);
  
  		set_metadata_flag (rgctx_alloc, "mono.this");
  	}
  
  	/* Initialize the method if needed */
  	if (cfg->compile_aot && ctx->llvm_only) {
  		/* Emit a location for the initialization code */
  		ctx->init_bb = gen_bb (ctx, "INIT_BB");
  		ctx->inited_bb = gen_bb (ctx, "INITED_BB");
  
  		LLVMBuildBr (ctx->builder, ctx->init_bb);
  		builder = ctx->builder = create_builder (ctx);
  		LLVMPositionBuilderAtEnd (ctx->builder, ctx->inited_bb);
  		ctx->bblocks [cfg->bb_entry->block_num].end_bblock = ctx->inited_bb;
  	}
  
  	/* Compute nesting between clauses */
  	ctx->nested_in = mono_mempool_alloc0 (cfg->mempool, sizeof (GSList*) * cfg->header->num_clauses);
  	for (i = 0; i < cfg->header->num_clauses; ++i) {
  		for (j = 0; j < cfg->header->num_clauses; ++j) {
  			MonoExceptionClause *clause1 = &cfg->header->clauses [i];
  			MonoExceptionClause *clause2 = &cfg->header->clauses [j];
  
  			if (i != j && clause1->try_offset >= clause2->try_offset && clause1->handler_offset <= clause2->handler_offset)
  				ctx->nested_in [i] = g_slist_prepend_mempool (cfg->mempool, ctx->nested_in [i], GINT_TO_POINTER (j));
  		}
  	}
  
  	/*
  	 * For finally clauses, create an indicator variable telling OP_ENDFINALLY whenever
  	 * it needs to continue normally, or return back to the exception handling system.
  	 */
  	for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
  		int clause_index;
  		char name [128];
  
  		if (!(bb->region != -1 && (bb->flags & BB_EXCEPTION_HANDLER)))
  			continue;
  
  		clause_index = MONO_REGION_CLAUSE_INDEX (bb->region);
  		g_hash_table_insert (ctx->region_to_handler, GUINT_TO_POINTER (mono_get_block_region_notry (cfg, bb->region)), bb);
  		g_hash_table_insert (ctx->clause_to_handler, GINT_TO_POINTER (clause_index), bb);
  
  		if (bb->in_scount == 0) {
  			LLVMValueRef val;
  
  			sprintf (name, "finally_ind_bb%d", bb->block_num);
  			val = LLVMBuildAlloca (builder, LLVMInt32Type (), name);
  			LLVMBuildStore (builder, LLVMConstInt (LLVMInt32Type (), 0, FALSE), val);
  
  			ctx->bblocks [bb->block_num].finally_ind = val;
  		} else {
  			/* Create a variable to hold the exception var */
  			if (!ctx->ex_var)
  				ctx->ex_var = LLVMBuildAlloca (builder, ObjRefType (), "exvar");
  		}
  
  		/*
  		 * Create a new bblock which CALL_HANDLER/landing pads can branch to, because branching to the
  		 * LLVM bblock containing a landing pad causes problems for the
  		 * LLVM optimizer passes.
  		 */
  		sprintf (name, "BB%d_CALL_HANDLER_TARGET", bb->block_num);
  		ctx->bblocks [bb->block_num].call_handler_target_bb = LLVMAppendBasicBlock (ctx->lmethod, name);
  	}
  
  	FAILURE:
  		goto CLEANUP;
  
  	CLEANUP:
  		ctx->builder = old_builder;
  }
  
  static void
  process_call (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, MonoInst *ins)
  {
  	MonoCompile *cfg = ctx->cfg;
  	LLVMModuleRef lmodule = ctx->lmodule;
  	LLVMValueRef *values = ctx->values;
  	LLVMValueRef *addresses = ctx->addresses;
  	MonoCallInst *call = (MonoCallInst*)ins;
  	MonoMethodSignature *sig = call->signature;
  	LLVMValueRef callee = NULL, lcall;
  	LLVMValueRef *args;
  	LLVMCallInfo *cinfo;
  	GSList *l;
  	int i, len, nargs;
  	gboolean vretaddr;
  	LLVMTypeRef llvm_sig;
  	gpointer target;
  	gboolean is_virtual, calli;
  	LLVMBuilderRef builder = *builder_ref;
  
  	if (call->signature->call_convention != MONO_CALL_DEFAULT)
  		LLVM_FAILURE (ctx, "non-default callconv");
  
  	cinfo = call->cinfo;
  	g_assert (cinfo);
  	if (call->rgctx_arg_reg)
  		cinfo->rgctx_arg = TRUE;
  	if (call->imt_arg_reg)
  		cinfo->imt_arg = TRUE;
  
  	vretaddr = (cinfo->ret.storage == LLVMArgVtypeRetAddr || cinfo->ret.storage == LLVMArgVtypeByRef || cinfo->ret.storage == LLVMArgScalarRetAddr);
  
  	llvm_sig = sig_to_llvm_sig_full (ctx, sig, cinfo);
  	CHECK_FAILURE (ctx);
  
  	is_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 || ins->opcode == OP_RCALL_MEMBASE);
  	calli = !call->fptr_is_patch && (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 || ins->opcode == OP_RCALL_REG);
  
  	/* FIXME: Avoid creating duplicate methods */
  
  	if (ins->flags & MONO_INST_HAS_METHOD) {
  		if (is_virtual) {
  			callee = NULL;
  		} else {
  			if (cfg->compile_aot) {
  				callee = get_callee (ctx, llvm_sig, MONO_PATCH_INFO_METHOD, call->method);
  				if (!callee)
  					LLVM_FAILURE (ctx, "can't encode patch");
  
  				if (cfg->llvm_only && call->method->klass->image->assembly == ctx->module->assembly) {
  					/*
  					 * Collect instructions representing the callee into a hash so they can be replaced
  					 * by the llvm method for the callee if the callee turns out to be direct
  					 * callable. Currently this only requires it to not fail llvm compilation.
  					 */
  					GSList *l = g_hash_table_lookup (ctx->method_to_callers, call->method);
  					l = g_slist_prepend (l, callee);
  					g_hash_table_insert (ctx->method_to_callers, call->method, l);
  				}
  			} else {
  				callee = LLVMAddFunction (lmodule, "", llvm_sig);
   
  				target =
  					mono_create_jit_trampoline_in_domain (mono_domain_get (),
  														  call->method);
  				LLVMAddGlobalMapping (ctx->module->ee, callee, target);
  			}
  		}
  
  		if (call->method && strstr (call->method->klass->name, "AsyncVoidMethodBuilder"))
  			/* LLVM miscompiles async methods */
  			LLVM_FAILURE (ctx, "#13734");
  	} else if (calli) {
  	} else {
  		MonoJitICallInfo *info = mono_find_jit_icall_by_addr (call->fptr);
  
  		if (info) {
  			/*
  			  MonoJumpInfo ji;
  
  			  memset (&ji, 0, sizeof (ji));
  			  ji.type = MONO_PATCH_INFO_JIT_ICALL_ADDR;
  			  ji.data.target = info->name;
  
  			  target = mono_resolve_patch_target (cfg->method, cfg->domain, NULL, &ji, FALSE);
  			*/
  			if (cfg->compile_aot) {
  				callee = get_callee (ctx, llvm_sig, MONO_PATCH_INFO_INTERNAL_METHOD, (char*)info->name);
  				if (!callee)
  					LLVM_FAILURE (ctx, "can't encode patch");
  			} else {
  				callee = LLVMAddFunction (lmodule, "", llvm_sig);
  				target = (gpointer)mono_icall_get_wrapper (info);
  				LLVMAddGlobalMapping (ctx->module->ee, callee, target);
  			}
  		} else {
  			if (cfg->compile_aot) {
  				callee = NULL;
  				if (cfg->abs_patches) {
  					MonoJumpInfo *abs_ji = (MonoJumpInfo*)g_hash_table_lookup (cfg->abs_patches, call->fptr);
  					if (abs_ji) {
  						callee = get_callee (ctx, llvm_sig, abs_ji->type, abs_ji->data.target);
  						if (!callee)
  							LLVM_FAILURE (ctx, "can't encode patch");
  					}
  				}
  				if (!callee)
  					LLVM_FAILURE (ctx, "aot");
  			} else {
  				callee = LLVMAddFunction (lmodule, "", llvm_sig);
  				target = NULL;
  				if (cfg->abs_patches) {
  					MonoJumpInfo *abs_ji = (MonoJumpInfo*)g_hash_table_lookup (cfg->abs_patches, call->fptr);
  					if (abs_ji) {
  						/*
  						 * FIXME: Some 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_ENTER_V4 ||
  								abs_ji->type == MONO_PATCH_INFO_MONITOR_EXIT)
  							LLVM_FAILURE (ctx, "trampoline with own cconv");
  #endif
  						target = mono_resolve_patch_target (cfg->method, cfg->domain, NULL, abs_ji, FALSE);
  						LLVMAddGlobalMapping (ctx->module->ee, callee, target);
  					}
  				}
  				if (!target)
  					LLVMAddGlobalMapping (ctx->module->ee, callee, (gpointer)call->fptr);
  			}
  		}
  	}
  
  	if (is_virtual) {
  		int size = sizeof (gpointer);
  		LLVMValueRef index;
  
  		g_assert (ins->inst_offset % size == 0);
  		index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE);
  
  		callee = convert (ctx, LLVMBuildLoad (builder, LLVMBuildGEP (builder, convert (ctx, values [ins->inst_basereg], LLVMPointerType (LLVMPointerType (IntPtrType (), 0), 0)), &index, 1, ""), ""), LLVMPointerType (llvm_sig, 0));
  	} else if (calli) {
  		callee = convert (ctx, values [ins->sreg1], LLVMPointerType (llvm_sig, 0));
  	} else {
  		if (ins->flags & MONO_INST_HAS_METHOD) {
  		}
  	}
  
  	/* 
  	 * Collect and convert arguments
  	 */
  	nargs = (sig->param_count * 16) + sig->hasthis + vretaddr + call->rgctx_reg + call->imt_arg_reg;
  	len = sizeof (LLVMValueRef) * nargs;
  	args = (LLVMValueRef*)alloca (len);
  	memset (args, 0, len);
  	l = call->out_ireg_args;
  
  	if (call->rgctx_arg_reg) {
  		g_assert (values [call->rgctx_arg_reg]);
  		g_assert (cinfo->rgctx_arg_pindex < nargs);
  		/*
  		 * On ARM, the imt/rgctx argument is passed in a caller save register, but some of our trampolines etc. clobber it, leading to
  		 * problems is LLVM moves the arg assignment earlier. To work around this, save the argument into a stack slot and load
  		 * it using a volatile load.
  		 */
  #ifdef TARGET_ARM
  		if (!ctx->imt_rgctx_loc)
  			ctx->imt_rgctx_loc = build_alloca_llvm_type (ctx, ctx->module->ptr_type, sizeof (gpointer));
  		LLVMBuildStore (builder, convert (ctx, ctx->values [call->rgctx_arg_reg], ctx->module->ptr_type), ctx->imt_rgctx_loc);
  		args [cinfo->rgctx_arg_pindex] = mono_llvm_build_load (builder, ctx->imt_rgctx_loc, "", TRUE, LLVM_BARRIER_NONE);
  #else
  		args [cinfo->rgctx_arg_pindex] = convert (ctx, values [call->rgctx_arg_reg], ctx->module->ptr_type);
  #endif
  	}
  	if (call->imt_arg_reg) {
  		g_assert (!ctx->llvm_only);
  		g_assert (values [call->imt_arg_reg]);
  		g_assert (cinfo->imt_arg_pindex < nargs);
  #ifdef TARGET_ARM
  		if (!ctx->imt_rgctx_loc)
  			ctx->imt_rgctx_loc = build_alloca_llvm_type (ctx, ctx->module->ptr_type, sizeof (gpointer));
  		LLVMBuildStore (builder, convert (ctx, ctx->values [call->imt_arg_reg], ctx->module->ptr_type), ctx->imt_rgctx_loc);
  		args [cinfo->imt_arg_pindex] = mono_llvm_build_load (builder, ctx->imt_rgctx_loc, "", TRUE, LLVM_BARRIER_NONE);
  #else
  		args [cinfo->imt_arg_pindex] = convert (ctx, values [call->imt_arg_reg], ctx->module->ptr_type);
  #endif
  	}
  	if (vretaddr) {
  		if (!addresses [call->inst.dreg])
  			addresses [call->inst.dreg] = build_alloca (ctx, sig->ret);
  		g_assert (cinfo->vret_arg_pindex < nargs);
  		if (cinfo->ret.storage == LLVMArgVtypeByRef)
  			args [cinfo->vret_arg_pindex] = addresses [call->inst.dreg];
  		else
  			args [cinfo->vret_arg_pindex] = LLVMBuildPtrToInt (builder, addresses [call->inst.dreg], IntPtrType (), "");
  	}
  
  	for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
  		guint32 regpair;
  		int reg, pindex;
  		LLVMArgInfo *ainfo = &call->cinfo->args [i];
  
  		pindex = ainfo->pindex;
  
  		regpair = (guint32)(gssize)(l->data);
  		reg = regpair & 0xffffff;
  		args [pindex] = values [reg];
  		switch (ainfo->storage) {
  		case LLVMArgVtypeInReg:
  		case LLVMArgAsFpArgs: {
  			guint32 nargs;
  
  			g_assert (addresses [reg]);
  			emit_vtype_to_args (ctx, builder, ainfo->type, addresses [reg], ainfo, args + pindex, &nargs);
  			pindex += nargs;
  
  			// FIXME: alignment
  			// FIXME: Get rid of the VMOVE
  			break;
  		}
  		case LLVMArgVtypeByVal:
  			g_assert (addresses [reg]);
  			args [pindex] = addresses [reg];
  			break;
  		case LLVMArgVtypeByRef:
  		case LLVMArgScalarByRef: {
  			g_assert (addresses [reg]);
  			args [pindex] = convert (ctx, addresses [reg], LLVMPointerType (type_to_llvm_arg_type (ctx, ainfo->type), 0));
  			break;
  		}
  		case LLVMArgAsIArgs:
  			g_assert (addresses [reg]);
  			args [pindex] = LLVMBuildLoad (ctx->builder, convert (ctx, addresses [reg], LLVMPointerType (LLVMArrayType (IntPtrType (), ainfo->nslots), 0)), "");
  			break;
  		case LLVMArgVtypeAsScalar:
  			g_assert_not_reached ();
  			break;
  		default:
  			g_assert (args [pindex]);
  			if (i == 0 && sig->hasthis)
  				args [pindex] = convert (ctx, args [pindex], ThisType ());
  			else
  				args [pindex] = convert (ctx, args [pindex], type_to_llvm_arg_type (ctx, ainfo->type));
  			break;
  		}
  		g_assert (pindex <= nargs);
  
  		l = l->next;
  	}
  
  	// FIXME: Align call sites
  
  	/*
  	 * Emit the call
  	 */
  
  	lcall = emit_call (ctx, bb, &builder, callee, args, LLVMCountParamTypes (llvm_sig));
  
  	/*
  	 * Modify cconv and parameter attributes to pass rgctx/imt correctly.
  	 */
  #if defined(MONO_ARCH_IMT_REG) && defined(MONO_ARCH_RGCTX_REG)
  	g_assert (MONO_ARCH_IMT_REG == MONO_ARCH_RGCTX_REG);
  #endif
  	/* The two can't be used together, so use only one LLVM calling conv to pass them */
  	g_assert (!(call->rgctx_arg_reg && call->imt_arg_reg));
  	if (!sig->pinvoke && !cfg->llvm_only)
  		LLVMSetInstructionCallConv (lcall, LLVMMono1CallConv);
  
  	if (cinfo->ret.storage == LLVMArgVtypeByRef)
  		LLVMAddInstrAttribute (lcall, 1 + cinfo->vret_arg_pindex, LLVMStructRetAttribute);
  	if (!ctx->llvm_only && call->rgctx_arg_reg)
  		LLVMAddInstrAttribute (lcall, 1 + cinfo->rgctx_arg_pindex, LLVMInRegAttribute);
  	if (call->imt_arg_reg)
  		LLVMAddInstrAttribute (lcall, 1 + cinfo->imt_arg_pindex, LLVMInRegAttribute);
  
  	/* Add byval attributes if needed */
  	for (i = 0; i < sig->param_count; ++i) {
  		LLVMArgInfo *ainfo = &call->cinfo->args [i + sig->hasthis];
  
  		if (ainfo && ainfo->storage == LLVMArgVtypeByVal)
  			LLVMAddInstrAttribute (lcall, 1 + ainfo->pindex, LLVMByValAttribute);
  	}
  
  	/*
  	 * Convert the result
  	 */
  	switch (cinfo->ret.storage) {
  	case LLVMArgVtypeInReg: {
  		LLVMValueRef regs [2];
  
  		if (LLVMTypeOf (lcall) == LLVMVoidType ())
  			/* Empty struct */
  			break;
  
  		if (!addresses [ins->dreg])
  			addresses [ins->dreg] = build_alloca (ctx, sig->ret);
  
  		regs [0] = LLVMBuildExtractValue (builder, lcall, 0, "");
  		if (cinfo->ret.pair_storage [1] != LLVMArgNone)
  			regs [1] = LLVMBuildExtractValue (builder, lcall, 1, "");
  		emit_args_to_vtype (ctx, builder, sig->ret, addresses [ins->dreg], &cinfo->ret, regs);
  		break;
  	}
  	case LLVMArgVtypeByVal:
  		if (!addresses [call->inst.dreg])
  			addresses [call->inst.dreg] = build_alloca (ctx, sig->ret);
  		LLVMBuildStore (builder, lcall, addresses [call->inst.dreg]);
  		break;
  	case LLVMArgFpStruct:
  		if (!addresses [call->inst.dreg])
  			addresses [call->inst.dreg] = build_alloca (ctx, sig->ret);
  		LLVMBuildStore (builder, lcall, convert_full (ctx, addresses [call->inst.dreg], LLVMPointerType (LLVMTypeOf (lcall), 0), FALSE));
  		break;
  	case LLVMArgVtypeAsScalar:
  		if (!addresses [call->inst.dreg])
  			addresses [call->inst.dreg] = build_alloca (ctx, sig->ret);
  		LLVMBuildStore (builder, lcall, convert_full (ctx, addresses [call->inst.dreg], LLVMPointerType (LLVMTypeOf (lcall), 0), FALSE));
  		break;
  	case LLVMArgVtypeRetAddr:
  	case LLVMArgVtypeByRef:
  		break;
  	case LLVMArgScalarRetAddr:
  		/* Normal scalar returned using a vtype return argument */
  		values [ins->dreg] = LLVMBuildLoad (builder, convert_full (ctx, addresses [call->inst.dreg], LLVMPointerType (type_to_llvm_type (ctx, sig->ret), 0), FALSE), "");
  		break;
  	default:
  		if (sig->ret->type != MONO_TYPE_VOID)
  			/* If the method returns an unsigned value, need to zext it */
  			values [ins->dreg] = convert_full (ctx, lcall, llvm_type_to_stack_type (cfg, type_to_llvm_type (ctx, sig->ret)), type_is_unsigned (ctx, sig->ret));
  		break;
  	}
  
  	*builder_ref = ctx->builder;
  
  	return;
   FAILURE:
  	return;
  }
  
  static void
  emit_llvmonly_throw (EmitContext *ctx, MonoBasicBlock *bb, gboolean rethrow, LLVMValueRef exc)
  {
  	const char *icall_name = rethrow ? "mono_llvm_rethrow_exception" : "mono_llvm_throw_exception";
  	LLVMValueRef callee = rethrow ? ctx->module->rethrow : ctx->module->throw_icall;
  
  	LLVMTypeRef exc_type = type_to_llvm_type (ctx, &mono_get_exception_class ()->byval_arg);
  
  	if (!callee) {
  		LLVMTypeRef fun_sig = LLVMFunctionType1 (LLVMVoidType (), exc_type, FALSE);
  
  		if (ctx->cfg->compile_aot) {
  			callee = get_callee (ctx, fun_sig, MONO_PATCH_INFO_INTERNAL_METHOD, icall_name);
  		} else {
  			callee = LLVMAddFunction (ctx->lmodule, icall_name, fun_sig);
  			LLVMAddGlobalMapping (ctx->module->ee, callee, resolve_patch (ctx->cfg, MONO_PATCH_INFO_INTERNAL_METHOD, icall_name));
  			mono_memory_barrier ();
  
  			if (rethrow)
  				ctx->module->rethrow = callee;
  			else
  				ctx->module->throw_icall = callee;
  		}
  	}
  
  	LLVMValueRef args [2];
  
  	args [0] = convert (ctx, exc, exc_type);
  	emit_call (ctx, bb, &ctx->builder, callee, args, 1);
  
  	LLVMBuildUnreachable (ctx->builder);
  
  	ctx->builder = create_builder (ctx);
  }
  
  static void
  emit_throw (EmitContext *ctx, MonoBasicBlock *bb, gboolean rethrow, LLVMValueRef exc)
  {
  	MonoMethodSignature *throw_sig;
  	LLVMValueRef callee, arg;
  	const char *icall_name;
  				
  	callee = rethrow ? ctx->module->rethrow : ctx->module->throw_icall;
  	icall_name = rethrow ? "mono_arch_rethrow_exception" : "mono_arch_throw_exception";
  
  	if (!callee) {
  		throw_sig = mono_metadata_signature_alloc (mono_get_corlib (), 1);
  		throw_sig->ret = &mono_get_void_class ()->byval_arg;
  		throw_sig->params [0] = &mono_get_object_class ()->byval_arg;
  		if (ctx->cfg->compile_aot) {
  			callee = get_callee (ctx, sig_to_llvm_sig (ctx, throw_sig), MONO_PATCH_INFO_INTERNAL_METHOD, icall_name);
  		} else {
  			callee = LLVMAddFunction (ctx->lmodule, icall_name, sig_to_llvm_sig (ctx, throw_sig));
  
  #ifdef TARGET_X86
  			/* 
  			 * LLVM doesn't push the exception argument, so we need a different
  			 * trampoline.
  			 */
  			LLVMAddGlobalMapping (ctx->module->ee, callee, resolve_patch (ctx->cfg, MONO_PATCH_INFO_INTERNAL_METHOD, rethrow ? "llvm_rethrow_exception_trampoline" : "llvm_throw_exception_trampoline"));
  #else
  			LLVMAddGlobalMapping (ctx->module->ee, callee, resolve_patch (ctx->cfg, MONO_PATCH_INFO_INTERNAL_METHOD, icall_name));
  #endif
  		}
  
  		mono_memory_barrier ();
  		if (rethrow)
  			ctx->module->rethrow = callee;
  		else
  			ctx->module->throw_icall = callee;
  	}
  	arg = convert (ctx, exc, type_to_llvm_type (ctx, &mono_get_object_class ()->byval_arg));
  	emit_call (ctx, bb, &ctx->builder, callee, &arg, 1);
  }
  
  static void
  emit_resume_eh (EmitContext *ctx, MonoBasicBlock *bb)
  {
  	const char *icall_name = "mono_llvm_resume_exception";
  	LLVMValueRef callee = ctx->module->resume_eh;
  
  	LLVMTypeRef fun_sig = LLVMFunctionType0 (LLVMVoidType (), FALSE);
  
  	if (!callee) {
  		if (ctx->cfg->compile_aot) {
  			callee = get_callee (ctx, fun_sig, MONO_PATCH_INFO_INTERNAL_METHOD, icall_name);
  		} else {
  			callee = LLVMAddFunction (ctx->lmodule, icall_name, fun_sig);
  			LLVMAddGlobalMapping (ctx->module->ee, callee, resolve_patch (ctx->cfg, MONO_PATCH_INFO_INTERNAL_METHOD, icall_name));
  			mono_memory_barrier ();
  
  			ctx->module->resume_eh = callee;
  		}
  	}
  
  	emit_call (ctx, bb, &ctx->builder, callee, NULL, 0);
  
  	LLVMBuildUnreachable (ctx->builder);
  
  	ctx->builder = create_builder (ctx);
  }
  
  static LLVMValueRef
  mono_llvm_emit_clear_exception_call (EmitContext *ctx, LLVMBuilderRef builder)
  {
  	const char *icall_name = "mono_llvm_clear_exception";
  
  	LLVMTypeRef call_sig = LLVMFunctionType (LLVMVoidType (), NULL, 0, FALSE);
  	LLVMValueRef callee = NULL;
  
  	if (!callee) {
  		if (ctx->cfg->compile_aot) {
  			callee = get_callee (ctx, call_sig, MONO_PATCH_INFO_INTERNAL_METHOD, icall_name);
  		} else {
  			// FIXME: This is broken.
  			callee = LLVMAddFunction (ctx->lmodule, icall_name, call_sig);
  		}
  	}
  
  	g_assert (builder && callee);
  
  	return LLVMBuildCall (builder, callee, NULL, 0, "");
  }
  
  static LLVMValueRef
  mono_llvm_emit_load_exception_call (EmitContext *ctx, LLVMBuilderRef builder)
  {
  	const char *icall_name = "mono_llvm_load_exception";
  
  	LLVMTypeRef call_sig = LLVMFunctionType (ObjRefType (), NULL, 0, FALSE);
  	LLVMValueRef callee = NULL;
  
  	if (!callee) {
  		if (ctx->cfg->compile_aot) {
  			callee = get_callee (ctx, call_sig, MONO_PATCH_INFO_INTERNAL_METHOD, icall_name);
  		} else {
  			// FIXME: This is broken.
  			callee = LLVMAddFunction (ctx->lmodule, icall_name, call_sig);
  		}
  	}
  
  	g_assert (builder && callee);
  
  	return LLVMBuildCall (builder, callee, NULL, 0, icall_name);
  }
  
  
  static LLVMValueRef
  mono_llvm_emit_match_exception_call (EmitContext *ctx, LLVMBuilderRef builder, gint32 region_start, gint32 region_end)
  {
  	const char *icall_name = "mono_llvm_match_exception";
  
  	ctx->builder = builder;
  
  	const int num_args = 3;
  	LLVMValueRef args [num_args];
  	args [0] = convert (ctx, get_aotconst (ctx, MONO_PATCH_INFO_AOT_JIT_INFO, GINT_TO_POINTER (ctx->cfg->method_index)), IntPtrType ());
  	args [1] = LLVMConstInt (LLVMInt32Type (), region_start, 0);
  	args [2] = LLVMConstInt (LLVMInt32Type (), region_end, 0);
  
  	LLVMTypeRef match_sig = LLVMFunctionType3 (LLVMInt32Type (), IntPtrType (), LLVMInt32Type (), LLVMInt32Type (), FALSE);
  	LLVMValueRef callee = ctx->module->match_exc;
  
  	if (!callee) {
  		if (ctx->cfg->compile_aot) {
  			ctx->builder = builder;
  			// get_callee expects ctx->builder to be the emitting builder
  			callee = get_callee (ctx, match_sig, MONO_PATCH_INFO_INTERNAL_METHOD, icall_name);
  		} else {
  			callee = ctx->module->match_exc = LLVMAddFunction (ctx->lmodule, icall_name, match_sig);
  			LLVMAddGlobalMapping (ctx->module->ee, ctx->module->match_exc, resolve_patch (ctx->cfg, MONO_PATCH_INFO_INTERNAL_METHOD, icall_name));
  			ctx->module->match_exc = callee;
  			mono_memory_barrier ();
  		}
  	}
  
  	g_assert (builder && callee);
  
  	g_assert (ctx->ex_var);
  
  	return LLVMBuildCall (builder, callee, args, num_args, icall_name);
  }
  
  // FIXME: This won't work because the code-finding makes this
  // not a constant.
  /*#define MONO_PERSONALITY_DEBUG*/
  
  #ifdef MONO_PERSONALITY_DEBUG
  static const gboolean use_debug_personality = TRUE;
  static const char *default_personality_name = "mono_debug_personality";
  #else
  static const gboolean use_debug_personality = FALSE;
  static const char *default_personality_name = "__gxx_personality_v0";
  #endif
  
  static LLVMTypeRef
  default_cpp_lpad_exc_signature (void)
  {
  	static gboolean inited = FALSE;
  	static LLVMTypeRef sig;
  
  	if (!sig) {
  		LLVMTypeRef signature [2];
  		signature [0] = LLVMPointerType (LLVMInt8Type (), 0);
  		signature [1] = LLVMInt32Type ();
  		sig = LLVMStructType (signature, 2, FALSE);
  		inited = TRUE;
  	}
  
  	return sig;
  }
  
  static LLVMValueRef
  get_mono_personality (EmitContext *ctx)
  {
  	LLVMValueRef personality = NULL;
  	static gint32 mapping_inited = FALSE;
  	LLVMTypeRef personality_type = LLVMFunctionType (LLVMInt32Type (), NULL, 0, TRUE);
  
  	if (!use_debug_personality) {
  		if (ctx->cfg->compile_aot) {
  				personality = LLVMGetNamedFunction (ctx->lmodule, default_personality_name);
  		} else if (InterlockedCompareExchange (&mapping_inited, 1, 0) == 0) {
  				personality = LLVMAddFunction (ctx->lmodule, default_personality_name, personality_type);
  				LLVMAddGlobalMapping (ctx->module->ee, personality, personality);
  		}
  	} else {
  		if (ctx->cfg->compile_aot) {
  			personality = get_callee (ctx, personality_type, MONO_PATCH_INFO_INTERNAL_METHOD, default_personality_name);
  		} else {
  			personality = LLVMAddFunction (ctx->lmodule, default_personality_name, personality_type);
  			LLVMAddGlobalMapping (ctx->module->ee, personality, resolve_patch (ctx->cfg, MONO_PATCH_INFO_INTERNAL_METHOD, default_personality_name));
  			mono_memory_barrier ();
  		}
  	}
  
  	g_assert (personality);
  	return personality;
  }
  
  static LLVMBasicBlockRef
  emit_landing_pad (EmitContext *ctx, int group_index, int group_size)
  {
  	MonoCompile *cfg = ctx->cfg;
  	LLVMBuilderRef old_builder = ctx->builder;
  	MonoExceptionClause *group_start = cfg->header->clauses + group_index;
  
  	LLVMBuilderRef lpadBuilder = create_builder (ctx);
  	ctx->builder = lpadBuilder;
  
  	MonoBasicBlock *handler_bb = cfg->cil_offset_to_bb [CLAUSE_START (group_start)];
  	g_assert (handler_bb);
  
  	// <resultval> = landingpad <somety> personality <type> <pers_fn> <clause>+
  	LLVMValueRef personality = get_mono_personality (ctx);
  	g_assert (personality);
  
  	char *bb_name = g_strdup_printf ("LPAD%d_BB", group_index);
  	LLVMBasicBlockRef lpad_bb = gen_bb (ctx, bb_name);
  	g_free (bb_name);
  	LLVMPositionBuilderAtEnd (lpadBuilder, lpad_bb);
  	LLVMValueRef landing_pad = LLVMBuildLandingPad (lpadBuilder, default_cpp_lpad_exc_signature (), personality, 0, "");
  	g_assert (landing_pad);
  
  	LLVMValueRef cast = LLVMBuildBitCast (lpadBuilder, ctx->module->sentinel_exception, LLVMPointerType (LLVMInt8Type (), 0), "int8TypeInfo");
  	LLVMAddClause (landing_pad, cast);
  
  	LLVMBasicBlockRef resume_bb = gen_bb (ctx, "RESUME_BB");
  	LLVMBuilderRef resume_builder = create_builder (ctx);
  	ctx->builder = resume_builder;
  	LLVMPositionBuilderAtEnd (resume_builder, resume_bb);
  
  	emit_resume_eh (ctx, handler_bb);
  
  	// Build match
  	ctx->builder = lpadBuilder;
  	LLVMPositionBuilderAtEnd (lpadBuilder, lpad_bb);
  
  	gboolean finally_only = TRUE;
  
  	MonoExceptionClause *group_cursor = group_start;
  
  	for (int i = 0; i < group_size; i ++) {
  		if (!(group_cursor->flags & MONO_EXCEPTION_CLAUSE_FINALLY))
  			finally_only = FALSE;
  
  		group_cursor++;
  	}
  
  	// FIXME:
  	// Handle landing pad inlining
  
  	if (!finally_only) {
  		// So at each level of the exception stack we will match the exception again.
  		// During that match, we need to compare against the handler types for the current
  		// protected region. We send the try start and end so that we can only check against
  		// handlers for this lexical protected region.
  		LLVMValueRef match = mono_llvm_emit_match_exception_call (ctx, lpadBuilder, group_start->try_offset, group_start->try_offset + group_start->try_len);
  
  		// if returns -1, resume
  		LLVMValueRef switch_ins = LLVMBuildSwitch (lpadBuilder, match, resume_bb, group_size);
  
  		// else move to that target bb
  		for (int i=0; i < group_size; i++) {
  			MonoExceptionClause *clause = group_start + i;
  			int clause_index = clause - cfg->header->clauses;
  			MonoBasicBlock *handler_bb = (MonoBasicBlock*)g_hash_table_lookup (ctx->clause_to_handler, GINT_TO_POINTER (clause_index));
  			g_assert (handler_bb);
  			g_assert (ctx->bblocks [handler_bb->block_num].call_handler_target_bb);
  			LLVMAddCase (switch_ins, LLVMConstInt (LLVMInt32Type (), clause_index, FALSE), ctx->bblocks [handler_bb->block_num].call_handler_target_bb);
  		}
  	} else {
  		int clause_index = group_start - cfg->header->clauses;
  		MonoBasicBlock *finally_bb = (MonoBasicBlock*)g_hash_table_lookup (ctx->clause_to_handler, GINT_TO_POINTER (clause_index));
  		g_assert (finally_bb);
  
  		LLVMBuildBr (ctx->builder, ctx->bblocks [finally_bb->block_num].call_handler_target_bb);
  	}
  
  	ctx->builder = old_builder;
  
  	return lpad_bb;
  }
  
  
  static void
  emit_llvmonly_handler_start (EmitContext *ctx, MonoBasicBlock *bb, LLVMBasicBlockRef cbb)
  {
  	int clause_index = MONO_REGION_CLAUSE_INDEX (bb->region);
  	MonoExceptionClause *clause = &ctx->cfg->header->clauses [clause_index];
  
  	// Make exception available to catch blocks
  	if (!(clause->flags & MONO_EXCEPTION_CLAUSE_FINALLY)) {
  		LLVMValueRef mono_exc = mono_llvm_emit_load_exception_call (ctx, ctx->builder);
  
  		g_assert (ctx->ex_var);
  		LLVMBuildStore (ctx->builder, LLVMBuildBitCast (ctx->builder, mono_exc, ObjRefType (), ""), ctx->ex_var);
  
  		if (bb->in_scount == 1) {
  			MonoInst *exvar = bb->in_stack [0];
  			g_assert (!ctx->values [exvar->dreg]);
  			g_assert (ctx->ex_var);
  			ctx->values [exvar->dreg] = LLVMBuildLoad (ctx->builder, ctx->ex_var, "save_exception");
  			emit_volatile_store (ctx, exvar->dreg);
  		}
  
  		mono_llvm_emit_clear_exception_call (ctx, ctx->builder);
  	}
  
  	LLVMBuilderRef handler_builder = create_builder (ctx);
  	LLVMBasicBlockRef target_bb = ctx->bblocks [bb->block_num].call_handler_target_bb;
  	LLVMPositionBuilderAtEnd (handler_builder, target_bb);
  
  	// Make the handler code end with a jump to cbb
  	LLVMBuildBr (handler_builder, cbb);
  }
  
  static void
  emit_handler_start (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef builder)
  {
  	MonoCompile *cfg = ctx->cfg;
  	LLVMValueRef *values = ctx->values;
  	LLVMModuleRef lmodule = ctx->lmodule;
  	BBInfo *bblocks = ctx->bblocks;
  	LLVMTypeRef i8ptr;
  	LLVMValueRef personality;
  	LLVMValueRef landing_pad;
  	LLVMBasicBlockRef target_bb;
  	MonoInst *exvar;
  	static gint32 mapping_inited;
  	static int ti_generator;
  	char ti_name [128];
  	MonoClass **ti;
  	LLVMValueRef type_info;
  	int clause_index;
  	GSList *l;
  
  	// <resultval> = landingpad <somety> personality <type> <pers_fn> <clause>+
  
  	if (cfg->compile_aot) {
  		/* Use a dummy personality function */
  		personality = LLVMGetNamedFunction (lmodule, "mono_personality");
  		g_assert (personality);
  	} else {
  		personality = LLVMGetNamedFunction (lmodule, "mono_personality");
  		if (InterlockedCompareExchange (&mapping_inited, 1, 0) == 0)
  			LLVMAddGlobalMapping (ctx->module->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 (lmodule, LLVMInt32Type (), ti_name);
  		LLVMSetInitializer (type_info, LLVMConstInt (LLVMInt32Type (), clause_index, FALSE));
  
  		/*
  		 * These symbols are not really used, the clause_index is embedded into the EH tables generated by DwarfMonoException in LLVM.
  		 */
  		LLVMSetLinkage (type_info, LLVMInternalLinkage);
  	} else {
  		/*
  		 * After the cfg mempool is freed, the type info will point to stale memory,
  		 * but this is not a problem, since we decode it once in exception_cb during
  		 * compilation.
  		 */
  		ti = mono_mempool_alloc (cfg->mempool, sizeof (gint32));
  		*(gint32*)ti = clause_index;
  
  		type_info = LLVMAddGlobal (lmodule, i8ptr, ti_name);
  
  		LLVMAddGlobalMapping (ctx->module->ee, type_info, ti);
  	}
  
  	{
  		LLVMTypeRef members [2], ret_type;
  
  		members [0] = i8ptr;
  		members [1] = LLVMInt32Type ();
  		ret_type = LLVMStructType (members, 2, FALSE);
  
  		landing_pad = LLVMBuildLandingPad (builder, ret_type, personality, 1, "");
  		LLVMAddClause (landing_pad, type_info);
  
  		/* Store the exception into the exvar */
  		if (ctx->ex_var)
  			LLVMBuildStore (builder, convert (ctx, LLVMBuildExtractValue (builder, landing_pad, 0, "ex_obj"), ObjRefType ()), ctx->ex_var);
  	}
  
  	/*
  	 * LLVM throw sites are associated with a one landing pad, and LLVM generated
  	 * code expects control to be transferred to this landing pad even in the
  	 * presence of nested clauses. The landing pad needs to branch to the landing
  	 * pads belonging to nested clauses based on the selector value returned by
  	 * the landing pad instruction, which is passed to the landing pad in a
  	 * register by the EH code.
  	 */
  	target_bb = bblocks [bb->block_num].call_handler_target_bb;
  	g_assert (target_bb);
  
  	/*
  	 * Branch to the correct landing pad
  	 */
  	LLVMValueRef ex_selector = LLVMBuildExtractValue (builder, landing_pad, 1, "ex_selector");
  	LLVMValueRef switch_ins = LLVMBuildSwitch (builder, ex_selector, target_bb, 0);
  
  	for (l = ctx->nested_in [clause_index]; l; l = l->next) {
  		int nesting_clause_index = GPOINTER_TO_INT (l->data);
  		MonoBasicBlock *handler_bb;
  
  		handler_bb = g_hash_table_lookup (ctx->clause_to_handler, GINT_TO_POINTER (nesting_clause_index));
  		g_assert (handler_bb);
  
  		g_assert (ctx->bblocks [handler_bb->block_num].call_handler_target_bb);
  		LLVMAddCase (switch_ins, LLVMConstInt (LLVMInt32Type (), nesting_clause_index, FALSE), ctx->bblocks [handler_bb->block_num].call_handler_target_bb);
  	}
  
  	/* Start a new bblock which CALL_HANDLER can branch to */
  	target_bb = bblocks [bb->block_num].call_handler_target_bb;
  	if (target_bb) {
  		ctx->builder = builder = create_builder (ctx);
  		LLVMPositionBuilderAtEnd (ctx->builder, target_bb);
  
  		ctx->bblocks [bb->block_num].end_bblock = target_bb;
  
  		/* Store the exception into the IL level exvar */
  		if (bb->in_scount == 1) {
  			g_assert (bb->in_scount == 1);
  			exvar = bb->in_stack [0];
  
  			// FIXME: This is shared with filter clauses ?
  			g_assert (!values [exvar->dreg]);
  
  			g_assert (ctx->ex_var);
  			values [exvar->dreg] = LLVMBuildLoad (builder, ctx->ex_var, "");
  			emit_volatile_store (ctx, exvar->dreg);
  		}
  	}
  }
  
  static void
  process_bb (EmitContext *ctx, MonoBasicBlock *bb)
  {
  	MonoCompile *cfg = ctx->cfg;
  	MonoMethodSignature *sig = ctx->sig;
  	LLVMValueRef method = ctx->lmethod;
  	LLVMValueRef *values = ctx->values;
  	LLVMValueRef *addresses = ctx->addresses;
  	LLVMCallInfo *linfo = ctx->linfo;
  	LLVMModuleRef lmodule = ctx->lmodule;
  	BBInfo *bblocks = ctx->bblocks;
  	MonoInst *ins;
  	LLVMBasicBlockRef cbb;
  	LLVMBuilderRef builder, starting_builder;
  	gboolean has_terminator;
  	LLVMValueRef v;
  	LLVMValueRef lhs, rhs;
  	int nins = 0;
  
  	cbb = get_end_bb (ctx, bb);
  
  	builder = create_builder (ctx);
  	ctx->builder = builder;
  	LLVMPositionBuilderAtEnd (builder, cbb);
  
  	CHECK_FAILURE (ctx);
  
  	if (bb->flags & BB_EXCEPTION_HANDLER) {
  		if (!ctx->llvm_only && !bblocks [bb->block_num].invoke_target) {
  			LLVM_FAILURE (ctx, "handler without invokes");
  		}
  
  		if (ctx->llvm_only)
  			emit_llvmonly_handler_start (ctx, bb, cbb);
  		else
  			emit_handler_start (ctx, bb, builder);
  		CHECK_FAILURE (ctx);
  		builder = ctx->builder;
  	}
  
  	has_terminator = FALSE;
  	starting_builder = builder;
  	for (ins = bb->code; ins; ins = ins->next) {
  		const char *spec = LLVM_INS_INFO (ins->opcode);
  		char *dname = NULL;
  		char dname_buf [128];
  
  		emit_dbg_loc (ctx, builder, ins->cil_code);
  
  		nins ++;
  		if (nins > 3000 && builder == starting_builder) {
  			/* some steps in llc are non-linear in the size of basic blocks, see #5714 */
  			LLVM_FAILURE (ctx, "basic block too long");
  		}
  
  		if (has_terminator)
  			/* There could be instructions after a terminator, skip them */
  			break;
  
  		if (spec [MONO_INST_DEST] != ' ' && !MONO_IS_STORE_MEMBASE (ins)) {
  			sprintf (dname_buf, "t%d", ins->dreg);
  			dname = dname_buf;
  		}
  
  		if (spec [MONO_INST_SRC1] != ' ' && spec [MONO_INST_SRC1] != 'v') {
  			MonoInst *var = get_vreg_to_inst (cfg, ins->sreg1);
  
  			if (var && var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)) {
  				lhs = emit_volatile_load (ctx, ins->sreg1);
  			} else {
  				/* It is ok for SETRET to have an uninitialized argument */
  				if (!values [ins->sreg1] && ins->opcode != OP_SETRET)
  					LLVM_FAILURE (ctx, "sreg1");
  				lhs = values [ins->sreg1];
  			}
  		} else {
  			lhs = NULL;
  		}
  
  		if (spec [MONO_INST_SRC2] != ' ' && spec [MONO_INST_SRC2] != ' ') {
  			MonoInst *var = get_vreg_to_inst (cfg, ins->sreg2);
  			if (var && var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)) {
  				rhs = emit_volatile_load (ctx, ins->sreg2);
  			} else {
  				if (!values [ins->sreg2])
  					LLVM_FAILURE (ctx, "sreg2");
  				rhs = values [ins->sreg2];
  			}
  		} else {
  			rhs = NULL;
  		}
  
  		//mono_print_ins (ins);
  		switch (ins->opcode) {
  		case OP_NOP:
  		case OP_NOT_NULL:
  		case OP_LIVERANGE_START:
  		case OP_LIVERANGE_END:
  			break;
  		case OP_ICONST:
  			values [ins->dreg] = LLVMConstInt (LLVMInt32Type (), ins->inst_c0, FALSE);
  			break;
  		case OP_I8CONST:
  #if SIZEOF_VOID_P == 4
  			values [ins->dreg] = LLVMConstInt (LLVMInt64Type (), GET_LONG_IMM (ins), FALSE);
  #else
  			values [ins->dreg] = LLVMConstInt (LLVMInt64Type (), (gint64)ins->inst_c0, FALSE);
  #endif
  			break;
  		case OP_R8CONST:
  			values [ins->dreg] = LLVMConstReal (LLVMDoubleType (), *(double*)ins->inst_p0);
  			break;
  		case OP_R4CONST:
  			if (cfg->r4fp)
  				values [ins->dreg] = LLVMConstReal (LLVMFloatType (), *(float*)ins->inst_p0);
  			else
  				values [ins->dreg] = LLVMConstFPExt (LLVMConstReal (LLVMFloatType (), *(float*)ins->inst_p0), LLVMDoubleType ());
  			break;
  		case OP_DUMMY_ICONST:
  			values [ins->dreg] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
  			break;
  		case OP_DUMMY_I8CONST:
  			values [ins->dreg] = LLVMConstInt (LLVMInt64Type (), 0, FALSE);
  			break;
  		case OP_DUMMY_R8CONST:
  			values [ins->dreg] = LLVMConstReal (LLVMDoubleType (), 0.0f);
  			break;
  		case OP_BR: {
  			LLVMBasicBlockRef target_bb = get_bb (ctx, ins->inst_target_bb);
  			LLVMBuildBr (builder, target_bb);
  			has_terminator = TRUE;
  			break;
  		}
  		case OP_SWITCH: {
  			int i;
  			LLVMValueRef v;
  			char bb_name [128];
  			LLVMBasicBlockRef new_bb;
  			LLVMBuilderRef new_builder;
  
  			// The default branch is already handled
  			// FIXME: Handle it here
  
  			/* Start new bblock */
  			sprintf (bb_name, "SWITCH_DEFAULT_BB%d", ctx->default_index ++);
  			new_bb = LLVMAppendBasicBlock (ctx->lmethod, bb_name);
  
  			lhs = convert (ctx, lhs, LLVMInt32Type ());
  			v = LLVMBuildSwitch (builder, lhs, new_bb, GPOINTER_TO_UINT (ins->klass));
  			for (i = 0; i < GPOINTER_TO_UINT (ins->klass); ++i) {
  				MonoBasicBlock *target_bb = ins->inst_many_bb [i];
  
  				LLVMAddCase (v, LLVMConstInt (LLVMInt32Type (), i, FALSE), get_bb (ctx, target_bb));
  			}
  
  			new_builder = create_builder (ctx);
  			LLVMPositionBuilderAtEnd (new_builder, new_bb);
  			LLVMBuildUnreachable (new_builder);
  
  			has_terminator = TRUE;
  			g_assert (!ins->next);
  				
  			break;
  		}
  
  		case OP_SETRET:
  			switch (linfo->ret.storage) {
  			case LLVMArgVtypeInReg: {
  				LLVMTypeRef ret_type = LLVMGetReturnType (LLVMGetElementType (LLVMTypeOf (method)));
  				LLVMValueRef val, addr, retval;
  				int i;
  
  				retval = LLVMGetUndef (ret_type);
  
  				if (!addresses [ins->sreg1]) {
  					/*
  					 * The return type is an LLVM vector type, have to convert between it and the
  					 * real return type which is a struct type.
  					 */
  					g_assert (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type (sig->ret)));
  					/* Convert to 2xi64 first */
  					val = LLVMBuildBitCast (builder, values [ins->sreg1], LLVMVectorType (IntPtrType (), 2), "");
  
  					for (i = 0; i < 2; ++i) {
  						if (linfo->ret.pair_storage [i] == LLVMArgInIReg) {
  							retval = LLVMBuildInsertValue (builder, retval, LLVMBuildExtractElement (builder, val, LLVMConstInt (LLVMInt32Type (), i, FALSE), ""), i, "");
  						} else {
  							g_assert (linfo->ret.pair_storage [i] == LLVMArgNone);
  						}
  					}
  				} else {
  					addr = LLVMBuildBitCast (builder, addresses [ins->sreg1], LLVMPointerType (ret_type, 0), "");
  					for (i = 0; i < 2; ++i) {
  						if (linfo->ret.pair_storage [i] == LLVMArgInIReg) {
  							LLVMValueRef indexes [2], part_addr;
  
  							indexes [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
  							indexes [1] = LLVMConstInt (LLVMInt32Type (), i, FALSE);
  							part_addr = LLVMBuildGEP (builder, addr, indexes, 2, "");
  
  							retval = LLVMBuildInsertValue (builder, retval, LLVMBuildLoad (builder, part_addr, ""), i, "");
  						} else {
  							g_assert (linfo->ret.pair_storage [i] == LLVMArgNone);
  						}
  					}
  				}
  				LLVMBuildRet (builder, retval);
  				break;
  			}
  			case LLVMArgVtypeAsScalar: {
  				LLVMTypeRef ret_type = LLVMGetReturnType (LLVMGetElementType (LLVMTypeOf (method)));
  				LLVMValueRef retval;
  				int size;
  
  				size = get_vtype_size (sig->ret);
  
  				g_assert (addresses [ins->sreg1]);
  
  				retval = LLVMBuildLoad (builder, LLVMBuildBitCast (builder, addresses [ins->sreg1], LLVMPointerType (ret_type, 0), ""), "");
  				LLVMBuildRet (builder, retval);
  				break;
  			}
  			case LLVMArgVtypeByVal: {
  				LLVMValueRef retval;
  
  				g_assert (addresses [ins->sreg1]);
  				retval = LLVMBuildLoad (builder, addresses [ins->sreg1], "");
  				LLVMBuildRet (builder, retval);
  				break;
  			}
  			case LLVMArgVtypeByRef: {
  				LLVMBuildRetVoid (builder);
  				break;
  			}
  			case LLVMArgVtypeRetAddr: {
  				LLVMBuildRetVoid (builder);
  				break;
  			}
  			case LLVMArgScalarRetAddr: {
  				LLVMTypeRef ret_type = type_to_llvm_type (ctx, sig->ret);
  				LLVMValueRef param = LLVMGetParam (method, linfo->vret_arg_pindex);
  
  				/* sreg1 might not be set */
  				if (lhs)
  					LLVMBuildStore (builder, convert (ctx, lhs, ret_type), convert (ctx, param, LLVMPointerType (ret_type, 0)));
  				LLVMBuildRetVoid (builder);
  				break;
  			}
  			case LLVMArgFpStruct: {
  				LLVMTypeRef ret_type = LLVMGetReturnType (LLVMGetElementType (LLVMTypeOf (method)));
  				LLVMValueRef retval;
  
  				g_assert (addresses [ins->sreg1]);
  				retval = LLVMBuildLoad (builder, convert (ctx, addresses [ins->sreg1], LLVMPointerType (ret_type, 0)), "");
  				LLVMBuildRet (builder, retval);
  				break;
  			}
  			case LLVMArgNone:
  			case LLVMArgNormal: {
  				if (!lhs || ctx->is_dead [ins->sreg1]) {
  					/*
  					 * The method did not set its return value, probably because it
  					 * ends with a throw.
  					 */
  					if (cfg->vret_addr)
  						LLVMBuildRetVoid (builder);
  					else
  						LLVMBuildRet (builder, LLVMConstNull (type_to_llvm_type (ctx, sig->ret)));
  				} else {
  					LLVMBuildRet (builder, convert (ctx, lhs, type_to_llvm_type (ctx, sig->ret)));
  				}
  				has_terminator = TRUE;
  				break;
  			}
  			default:
  				g_assert_not_reached ();
  				break;
  			}
  			break;
  		case OP_ICOMPARE:
  		case OP_FCOMPARE:
  		case OP_RCOMPARE:
  		case OP_LCOMPARE:
  		case OP_COMPARE:
  		case OP_ICOMPARE_IMM:
  		case OP_LCOMPARE_IMM:
  		case OP_COMPARE_IMM: {
  			CompRelation rel;
  			LLVMValueRef cmp;
  
  			if (ins->next->opcode == OP_NOP)
  				break;
  
  			if (ins->next->opcode == OP_BR)
  				/* The comparison result is not needed */
  				continue;
  
  			rel = mono_opcode_to_cond (ins->next->opcode);
  
  			if (ins->opcode == OP_ICOMPARE_IMM) {
  				lhs = convert (ctx, lhs, LLVMInt32Type ());
  				rhs = LLVMConstInt (LLVMInt32Type (), ins->inst_imm, FALSE);
  			}
  			if (ins->opcode == OP_LCOMPARE_IMM) {
  				lhs = convert (ctx, lhs, LLVMInt64Type ());
  				rhs = LLVMConstInt (LLVMInt64Type (), GET_LONG_IMM (ins), FALSE);
  			}
  			if (ins->opcode == OP_LCOMPARE) {
  				lhs = convert (ctx, lhs, LLVMInt64Type ());
  				rhs = convert (ctx, rhs, LLVMInt64Type ());
  			}
  			if (ins->opcode == OP_ICOMPARE) {
  				lhs = convert (ctx, lhs, LLVMInt32Type ());
  				rhs = convert (ctx, rhs, LLVMInt32Type ());
  			}
  
  			if (lhs && rhs) {
  				if (LLVMGetTypeKind (LLVMTypeOf (lhs)) == LLVMPointerTypeKind)
  					rhs = convert (ctx, rhs, LLVMTypeOf (lhs));
  				else if (LLVMGetTypeKind (LLVMTypeOf (rhs)) == LLVMPointerTypeKind)
  					lhs = convert (ctx, lhs, LLVMTypeOf (rhs));
  			}
  
  			/* We use COMPARE+SETcc/Bcc, llvm uses SETcc+br cond */
  			if (ins->opcode == OP_FCOMPARE) {
  				cmp = LLVMBuildFCmp (builder, fpcond_to_llvm_cond [rel], convert (ctx, lhs, LLVMDoubleType ()), convert (ctx, rhs, LLVMDoubleType ()), "");
  			} else if (ins->opcode == OP_RCOMPARE) {
  				cmp = LLVMBuildFCmp (builder, fpcond_to_llvm_cond [rel], convert (ctx, lhs, LLVMFloatType ()), convert (ctx, rhs, LLVMFloatType ()), "");
  			} else if (ins->opcode == OP_COMPARE_IMM) {
  				if (LLVMGetTypeKind (LLVMTypeOf (lhs)) == LLVMPointerTypeKind && ins->inst_imm == 0)
  					cmp = LLVMBuildICmp (builder, cond_to_llvm_cond [rel], lhs, LLVMConstNull (LLVMTypeOf (lhs)), "");
  				else
  					cmp = LLVMBuildICmp (builder, cond_to_llvm_cond [rel], convert (ctx, lhs, IntPtrType ()), LLVMConstInt (IntPtrType (), ins->inst_imm, FALSE), "");
  			} else if (ins->opcode == OP_LCOMPARE_IMM) {
  				if (SIZEOF_REGISTER == 4 && COMPILE_LLVM (cfg))  {
  					/* The immediate is encoded in two fields */
  					guint64 l = ((guint64)(guint32)ins->inst_offset << 32) | ((guint32)ins->inst_imm);
  					cmp = LLVMBuildICmp (builder, cond_to_llvm_cond [rel], convert (ctx, lhs, LLVMInt64Type ()), LLVMConstInt (LLVMInt64Type (), l, FALSE), "");
  				} else {
  					cmp = LLVMBuildICmp (builder, cond_to_llvm_cond [rel], convert (ctx, lhs, LLVMInt64Type ()), LLVMConstInt (LLVMInt64Type (), ins->inst_imm, FALSE), "");
  				}
  			}
  			else if (ins->opcode == OP_COMPARE) {
  				if (LLVMGetTypeKind (LLVMTypeOf (lhs)) == LLVMPointerTypeKind && LLVMTypeOf (lhs) == LLVMTypeOf (rhs))
  					cmp = LLVMBuildICmp (builder, cond_to_llvm_cond [rel], lhs, rhs, "");
  				else
  					cmp = LLVMBuildICmp (builder, cond_to_llvm_cond [rel], convert (ctx, lhs, IntPtrType ()), convert (ctx, rhs, IntPtrType ()), "");
  			} else
  				cmp = LLVMBuildICmp (builder, cond_to_llvm_cond [rel], lhs, rhs, "");
  
  			if (MONO_IS_COND_BRANCH_OP (ins->next)) {
  				if (ins->next->inst_true_bb == ins->next->inst_false_bb) {
  					/*
  					 * If the target bb contains PHI instructions, LLVM requires
  					 * two PHI entries for this bblock, while we only generate one.
  					 * So convert this to an unconditional bblock. (bxc #171).
  					 */
  					LLVMBuildBr (builder, get_bb (ctx, ins->next->inst_true_bb));
  				} else {
  					LLVMBuildCondBr (builder, cmp, get_bb (ctx, ins->next->inst_true_bb), get_bb (ctx, ins->next->inst_false_bb));
  				}
  				has_terminator = TRUE;
  			} else if (MONO_IS_SETCC (ins->next)) {
  				sprintf (dname_buf, "t%d", ins->next->dreg);
  				dname = dname_buf;
  				values [ins->next->dreg] = LLVMBuildZExt (builder, cmp, LLVMInt32Type (), dname);
  
  				/* Add stores for volatile variables */
  				emit_volatile_store (ctx, ins->next->dreg);
  			} else if (MONO_IS_COND_EXC (ins->next)) {
  				emit_cond_system_exception (ctx, bb, (const char*)ins->next->inst_p1, cmp);
  				CHECK_FAILURE (ctx);
  				builder = ctx->builder;
  			} else {
  				LLVM_FAILURE (ctx, "next");
  			}
  
  			ins = ins->next;
  			break;
  		}
  		case OP_FCEQ:
  		case OP_FCLT:
  		case OP_FCLT_UN:
  		case OP_FCGT:
  		case OP_FCGT_UN: {
  			CompRelation rel;
  			LLVMValueRef cmp;
  
  			rel = mono_opcode_to_cond (ins->opcode);
  
  			cmp = LLVMBuildFCmp (builder, fpcond_to_llvm_cond [rel], convert (ctx, lhs, LLVMDoubleType ()), convert (ctx, rhs, LLVMDoubleType ()), "");
  			values [ins->dreg] = LLVMBuildZExt (builder, cmp, LLVMInt32Type (), dname);
  			break;
  		}
  		case OP_RCEQ:
  		case OP_RCLT:
  		case OP_RCLT_UN:
  		case OP_RCGT:
  		case OP_RCGT_UN: {
  			CompRelation rel;
  			LLVMValueRef cmp;
  
  			rel = mono_opcode_to_cond (ins->opcode);
  
  			cmp = LLVMBuildFCmp (builder, fpcond_to_llvm_cond [rel], convert (ctx, lhs, LLVMFloatType ()), convert (ctx, rhs, LLVMFloatType ()), "");
  			values [ins->dreg] = LLVMBuildZExt (builder, cmp, LLVMInt32Type (), dname);
  			break;
  		}
  		case OP_PHI:
  		case OP_FPHI:
  		case OP_VPHI:
  		case OP_XPHI: {
  			int i;
  			gboolean empty = TRUE;
  
  			/* Check that all input bblocks really branch to us */
  			for (i = 0; i < bb->in_count; ++i) {
  				if (bb->in_bb [i]->last_ins && bb->in_bb [i]->last_ins->opcode == OP_NOT_REACHED)
  					ins->inst_phi_args [i + 1] = -1;
  				else
  					empty = FALSE;
  			}
  
  			if (empty) {
  				/* LLVM doesn't like phi instructions with zero operands */
  				ctx->is_dead [ins->dreg] = TRUE;
  				break;
  			}					
  
  			/* Created earlier, insert it now */
  			LLVMInsertIntoBuilder (builder, values [ins->dreg]);
  
  			for (i = 0; i < ins->inst_phi_args [0]; i++) {
  				int sreg1 = ins->inst_phi_args [i + 1];
  				int count, j;
  
  				/* 
  				 * Count the number of times the incoming bblock branches to us,
  				 * since llvm requires a separate entry for each.
  				 */
  				if (bb->in_bb [i]->last_ins && bb->in_bb [i]->last_ins->opcode == OP_SWITCH) {
  					MonoInst *switch_ins = bb->in_bb [i]->last_ins;
  
  					count = 0;
  					for (j = 0; j < GPOINTER_TO_UINT (switch_ins->klass); ++j) {
  						if (switch_ins->inst_many_bb [j] == bb)
  							count ++;
  					}
  				} else {
  					count = 1;
  				}
  
  				/* Remember for later */
  				for (j = 0; j < count; ++j) {
  					PhiNode *node = (PhiNode*)mono_mempool_alloc0 (ctx->mempool, sizeof (PhiNode));
  					node->bb = bb;
  					node->phi = ins;
  					node->in_bb = bb->in_bb [i];
  					node->sreg = sreg1;
  					bblocks [bb->in_bb [i]->block_num].phi_nodes = g_slist_prepend_mempool (ctx->mempool, bblocks [bb->in_bb [i]->block_num].phi_nodes, node);
  				}
  			}
  			break;
  		}
  		case OP_MOVE:
  		case OP_LMOVE:
  		case OP_XMOVE:
  		case OP_SETFRET:
  			g_assert (lhs);
  			values [ins->dreg] = lhs;
  			break;
  		case OP_FMOVE:
  		case OP_RMOVE: {
  			MonoInst *var = get_vreg_to_inst (cfg, ins->dreg);
  				
  			g_assert (lhs);
  			values [ins->dreg] = lhs;
  
  			if (var && var->klass->byval_arg.type == MONO_TYPE_R4) {
  				/* 
  				 * This is added by the spilling pass in case of the JIT,
  				 * but we have to do it ourselves.
  				 */
  				values [ins->dreg] = convert (ctx, values [ins->dreg], LLVMFloatType ());
  			}
  			break;
  		}
  		case OP_MOVE_F_TO_I4: {
  			values [ins->dreg] = LLVMBuildBitCast (builder, LLVMBuildFPTrunc (builder, lhs, LLVMFloatType (), ""), LLVMInt32Type (), "");
  			break;
  		}
  		case OP_MOVE_I4_TO_F: {
  			values [ins->dreg] = LLVMBuildFPExt (builder, LLVMBuildBitCast (builder, lhs, LLVMFloatType (), ""), LLVMDoubleType (), "");
  			break;
  		}
  		case OP_MOVE_F_TO_I8: {
  			values [ins->dreg] = LLVMBuildBitCast (builder, lhs, LLVMInt64Type (), "");
  			break;
  		}
  		case OP_MOVE_I8_TO_F: {
  			values [ins->dreg] = LLVMBuildBitCast (builder, lhs, LLVMDoubleType (), "");
  			break;
  		}
  		case OP_IADD:
  		case OP_ISUB:
  		case OP_IAND:
  		case OP_IMUL:
  		case OP_IDIV:
  		case OP_IDIV_UN:
  		case OP_IREM:
  		case OP_IREM_UN:
  		case OP_IOR:
  		case OP_IXOR:
  		case OP_ISHL:
  		case OP_ISHR:
  		case OP_ISHR_UN:
  		case OP_FADD:
  		case OP_FSUB:
  		case OP_FMUL:
  		case OP_FDIV:
  		case OP_LADD:
  		case OP_LSUB:
  		case OP_LMUL:
  		case OP_LDIV:
  		case OP_LDIV_UN:
  		case OP_LREM:
  		case OP_LREM_UN:
  		case OP_LAND:
  		case OP_LOR:
  		case OP_LXOR:
  		case OP_LSHL:
  		case OP_LSHR:
  		case OP_LSHR_UN:
  			lhs = convert (ctx, lhs, regtype_to_llvm_type (spec [MONO_INST_DEST]));
  			rhs = convert (ctx, rhs, regtype_to_llvm_type (spec [MONO_INST_DEST]));
  
  			emit_div_check (ctx, builder, bb, ins, lhs, rhs);
  			CHECK_FAILURE (ctx);
  			builder = ctx->builder;
  
  			switch (ins->opcode) {
  			case OP_IADD:
  			case OP_LADD:
  				values [ins->dreg] = LLVMBuildAdd (builder, lhs, rhs, dname);
  				break;
  			case OP_ISUB:
  			case OP_LSUB:
  				values [ins->dreg] = LLVMBuildSub (builder, lhs, rhs, dname);
  				break;
  			case OP_IMUL:
  			case OP_LMUL:
  				values [ins->dreg] = LLVMBuildMul (builder, lhs, rhs, dname);
  				break;
  			case OP_IREM:
  			case OP_LREM:
  				values [ins->dreg] = LLVMBuildSRem (builder, lhs, rhs, dname);
  				break;
  			case OP_IREM_UN:
  			case OP_LREM_UN:
  				values [ins->dreg] = LLVMBuildURem (builder, lhs, rhs, dname);
  				break;
  			case OP_IDIV:
  			case OP_LDIV:
  				values [ins->dreg] = LLVMBuildSDiv (builder, lhs, rhs, dname);
  				break;
  			case OP_IDIV_UN:
  			case OP_LDIV_UN:
  				values [ins->dreg] = LLVMBuildUDiv (builder, lhs, rhs, dname);
  				break;
  			case OP_FDIV:
  			case OP_RDIV:
  				values [ins->dreg] = LLVMBuildFDiv (builder, lhs, rhs, dname);
  				break;
  			case OP_IAND:
  			case OP_LAND:
  				values [ins->dreg] = LLVMBuildAnd (builder, lhs, rhs, dname);
  				break;
  			case OP_IOR:
  			case OP_LOR:
  				values [ins->dreg] = LLVMBuildOr (builder, lhs, rhs, dname);
  				break;
  			case OP_IXOR:
  			case OP_LXOR:
  				values [ins->dreg] = LLVMBuildXor (builder, lhs, rhs, dname);
  				break;
  			case OP_ISHL:
  			case OP_LSHL:
  				values [ins->dreg] = LLVMBuildShl (builder, lhs, rhs, dname);
  				break;
  			case OP_ISHR:
  			case OP_LSHR:
  				values [ins->dreg] = LLVMBuildAShr (builder, lhs, rhs, dname);
  				break;
  			case OP_ISHR_UN:
  			case OP_LSHR_UN:
  				values [ins->dreg] = LLVMBuildLShr (builder, lhs, rhs, dname);
  				break;
  
  			case OP_FADD:
  				values [ins->dreg] = LLVMBuildFAdd (builder, lhs, rhs, dname);
  				break;
  			case OP_FSUB:
  				values [ins->dreg] = LLVMBuildFSub (builder, lhs, rhs, dname);
  				break;
  			case OP_FMUL:
  				values [ins->dreg] = LLVMBuildFMul (builder, lhs, rhs, dname);
  				break;
  
  			default:
  				g_assert_not_reached ();
  			}
  			break;
  		case OP_RADD:
  		case OP_RSUB:
  		case OP_RMUL:
  		case OP_RDIV: {
  			lhs = convert (ctx, lhs, LLVMFloatType ());
  			rhs = convert (ctx, rhs, LLVMFloatType ());
  			switch (ins->opcode) {
  			case OP_RADD:
  				values [ins->dreg] = LLVMBuildFAdd (builder, lhs, rhs, dname);
  				break;
  			case OP_RSUB:
  				values [ins->dreg] = LLVMBuildFSub (builder, lhs, rhs, dname);
  				break;
  			case OP_RMUL:
  				values [ins->dreg] = LLVMBuildFMul (builder, lhs, rhs, dname);
  				break;
  			case OP_RDIV:
  				values [ins->dreg] = LLVMBuildFDiv (builder, lhs, rhs, dname);
  				break;
  			default:
  				g_assert_not_reached ();
  				break;
  			}
  			break;
  		}
  		case OP_IADD_IMM:
  		case OP_ISUB_IMM:
  		case OP_IMUL_IMM:
  		case OP_IREM_IMM:
  		case OP_IREM_UN_IMM:
  		case OP_IDIV_IMM:
  		case OP_IDIV_UN_IMM:
  		case OP_IAND_IMM:
  		case OP_IOR_IMM:
  		case OP_IXOR_IMM:
  		case OP_ISHL_IMM:
  		case OP_ISHR_IMM:
  		case OP_ISHR_UN_IMM:
  		case OP_LADD_IMM:
  		case OP_LSUB_IMM:
  		case OP_LREM_IMM:
  		case OP_LAND_IMM:
  		case OP_LOR_IMM:
  		case OP_LXOR_IMM:
  		case OP_LSHL_IMM:
  		case OP_LSHR_IMM:
  		case OP_LSHR_UN_IMM:
  		case OP_ADD_IMM:
  		case OP_AND_IMM:
  		case OP_MUL_IMM:
  		case OP_SHL_IMM:
  		case OP_SHR_IMM:
  		case OP_SHR_UN_IMM: {
  			LLVMValueRef imm;
  
  			if (spec [MONO_INST_SRC1] == 'l') {
  				imm = LLVMConstInt (LLVMInt64Type (), GET_LONG_IMM (ins), FALSE);
  			} else {
  				imm = LLVMConstInt (LLVMInt32Type (), ins->inst_imm, FALSE);
  			}
  
  			emit_div_check (ctx, builder, bb, ins, lhs, imm);
  			CHECK_FAILURE (ctx);
  			builder = ctx->builder;
  
  #if SIZEOF_VOID_P == 4
  			if (ins->opcode == OP_LSHL_IMM || ins->opcode == OP_LSHR_IMM || ins->opcode == OP_LSHR_UN_IMM)
  				imm = LLVMConstInt (LLVMInt32Type (), ins->inst_imm, FALSE);
  #endif
  
  			if (LLVMGetTypeKind (LLVMTypeOf (lhs)) == LLVMPointerTypeKind)
  				lhs = convert (ctx, lhs, IntPtrType ());
  			imm = convert (ctx, imm, LLVMTypeOf (lhs));
  			switch (ins->opcode) {
  			case OP_IADD_IMM:
  			case OP_LADD_IMM:
  			case OP_ADD_IMM:
  				values [ins->dreg] = LLVMBuildAdd (builder, lhs, imm, dname);
  				break;
  			case OP_ISUB_IMM:
  			case OP_LSUB_IMM:
  				values [ins->dreg] = LLVMBuildSub (builder, lhs, imm, dname);
  				break;
  			case OP_IMUL_IMM:
  			case OP_MUL_IMM:
  				values [ins->dreg] = LLVMBuildMul (builder, lhs, imm, dname);
  				break;
  			case OP_IDIV_IMM:
  			case OP_LDIV_IMM:
  				values [ins->dreg] = LLVMBuildSDiv (builder, lhs, imm, dname);
  				break;
  			case OP_IDIV_UN_IMM:
  			case OP_LDIV_UN_IMM:
  				values [ins->dreg] = LLVMBuildUDiv (builder, lhs, imm, dname);
  				break;
  			case OP_IREM_IMM:
  			case OP_LREM_IMM:
  				values [ins->dreg] = LLVMBuildSRem (builder, lhs, imm, dname);
  				break;
  			case OP_IREM_UN_IMM:
  				values [ins->dreg] = LLVMBuildURem (builder, lhs, imm, dname);
  				break;
  			case OP_IAND_IMM:
  			case OP_LAND_IMM:
  			case OP_AND_IMM:
  				values [ins->dreg] = LLVMBuildAnd (builder, lhs, imm, dname);
  				break;
  			case OP_IOR_IMM:
  			case OP_LOR_IMM:
  				values [ins->dreg] = LLVMBuildOr (builder, lhs, imm, dname);
  				break;
  			case OP_IXOR_IMM:
  			case OP_LXOR_IMM:
  				values [ins->dreg] = LLVMBuildXor (builder, lhs, imm, dname);
  				break;
  			case OP_ISHL_IMM:
  			case OP_LSHL_IMM:
  			case OP_SHL_IMM:
  				values [ins->dreg] = LLVMBuildShl (builder, lhs, imm, dname);
  				break;
  			case OP_ISHR_IMM:
  			case OP_LSHR_IMM:
  			case OP_SHR_IMM:
  				values [ins->dreg] = LLVMBuildAShr (builder, lhs, imm, dname);
  				break;
  			case OP_ISHR_UN_IMM:
  				/* This is used to implement conv.u4, so the lhs could be an i8 */
  				lhs = convert (ctx, lhs, LLVMInt32Type ());
  				imm = convert (ctx, imm, LLVMInt32Type ());
  				values [ins->dreg] = LLVMBuildLShr (builder, lhs, imm, dname);
  				break;
  			case OP_LSHR_UN_IMM:
  			case OP_SHR_UN_IMM:
  				values [ins->dreg] = LLVMBuildLShr (builder, lhs, imm, dname);
  				break;
  			default:
  				g_assert_not_reached ();
  			}
  			break;
  		}
  		case OP_INEG:
  			values [ins->dreg] = LLVMBuildSub (builder, LLVMConstInt (LLVMInt32Type (), 0, FALSE), convert (ctx, lhs, LLVMInt32Type ()), dname);
  			break;
  		case OP_LNEG:
  			values [ins->dreg] = LLVMBuildSub (builder, LLVMConstInt (LLVMInt64Type (), 0, FALSE), lhs, dname);
  			break;
  		case OP_FNEG:
  			lhs = convert (ctx, lhs, LLVMDoubleType ());
  			values [ins->dreg] = LLVMBuildFSub (builder, LLVMConstReal (LLVMDoubleType (), 0.0), lhs, dname);
  			break;
  		case OP_RNEG:
  			lhs = convert (ctx, lhs, LLVMFloatType ());
  			values [ins->dreg] = LLVMBuildFSub (builder, LLVMConstReal (LLVMFloatType (), 0.0), lhs, dname);
  			break;
  		case OP_INOT: {
  			guint32 v = 0xffffffff;
  			values [ins->dreg] = LLVMBuildXor (builder, LLVMConstInt (LLVMInt32Type (), v, FALSE), convert (ctx, lhs, LLVMInt32Type ()), dname);
  			break;
  		}
  		case OP_LNOT: {
  			guint64 v = 0xffffffffffffffffLL;
  			values [ins->dreg] = LLVMBuildXor (builder, LLVMConstInt (LLVMInt64Type (), v, FALSE), lhs, dname);
  			break;
  		}
  #if defined(TARGET_X86) || defined(TARGET_AMD64)
  		case OP_X86_LEA: {
  			LLVMValueRef v1, v2;
  
  			v1 = LLVMBuildMul (builder, convert (ctx, rhs, IntPtrType ()), LLVMConstInt (IntPtrType (), (1 << ins->backend.shift_amount), FALSE), "");
  			v2 = LLVMBuildAdd (builder, convert (ctx, lhs, IntPtrType ()), v1, "");
  			values [ins->dreg] = LLVMBuildAdd (builder, v2, LLVMConstInt (IntPtrType (), ins->inst_imm, FALSE), dname);
  			break;
  		}
  #endif
  
  		case OP_ICONV_TO_I1:
  		case OP_ICONV_TO_I2:
  		case OP_ICONV_TO_I4:
  		case OP_ICONV_TO_U1:
  		case OP_ICONV_TO_U2:
  		case OP_ICONV_TO_U4:
  		case OP_LCONV_TO_I1:
  		case OP_LCONV_TO_I2:
  		case OP_LCONV_TO_U1:
  		case OP_LCONV_TO_U2:
  		case OP_LCONV_TO_U4: {
  			gboolean sign;
  
  			sign = (ins->opcode == OP_ICONV_TO_I1) || (ins->opcode == OP_ICONV_TO_I2) || (ins->opcode == OP_ICONV_TO_I4) || (ins->opcode == OP_LCONV_TO_I1) || (ins->opcode == OP_LCONV_TO_I2);
  
  			/* Have to do two casts since our vregs have type int */
  			v = LLVMBuildTrunc (builder, lhs, op_to_llvm_type (ins->opcode), "");
  			if (sign)
  				values [ins->dreg] = LLVMBuildSExt (builder, v, LLVMInt32Type (), dname);
  			else
  				values [ins->dreg] = LLVMBuildZExt (builder, v, LLVMInt32Type (), dname);
  			break;
  		}
  		case OP_ICONV_TO_I8:
  			values [ins->dreg] = LLVMBuildSExt (builder, lhs, LLVMInt64Type (), dname);
  			break;
  		case OP_ICONV_TO_U8:
  			values [ins->dreg] = LLVMBuildZExt (builder, lhs, LLVMInt64Type (), dname);
  			break;
  		case OP_FCONV_TO_I4:
  		case OP_RCONV_TO_I4:
  			values [ins->dreg] = LLVMBuildFPToSI (builder, lhs, LLVMInt32Type (), dname);
  			break;
  		case OP_FCONV_TO_I1:
  		case OP_RCONV_TO_I1:
  			values [ins->dreg] = LLVMBuildSExt (builder, LLVMBuildFPToSI (builder, lhs, LLVMInt8Type (), dname), LLVMInt32Type (), "");
  			break;
  		case OP_FCONV_TO_U1:
  		case OP_RCONV_TO_U1:
  			values [ins->dreg] = LLVMBuildZExt (builder, LLVMBuildFPToUI (builder, lhs, LLVMInt8Type (), dname), LLVMInt32Type (), "");
  			break;
  		case OP_FCONV_TO_I2:
  		case OP_RCONV_TO_I2:
  			values [ins->dreg] = LLVMBuildSExt (builder, LLVMBuildFPToSI (builder, lhs, LLVMInt16Type (), dname), LLVMInt32Type (), "");
  			break;
  		case OP_FCONV_TO_U2:
  		case OP_RCONV_TO_U2:
  			values [ins->dreg] = LLVMBuildZExt (builder, LLVMBuildFPToUI (builder, lhs, LLVMInt16Type (), dname), LLVMInt32Type (), "");
  			break;
  		case OP_FCONV_TO_I8:
  		case OP_RCONV_TO_I8:
  			values [ins->dreg] = LLVMBuildFPToSI (builder, lhs, LLVMInt64Type (), dname);
  			break;
  		case OP_FCONV_TO_I:
  			values [ins->dreg] = LLVMBuildFPToSI (builder, lhs, IntPtrType (), dname);
  			break;
  		case OP_ICONV_TO_R8:
  		case OP_LCONV_TO_R8:
  			values [ins->dreg] = LLVMBuildSIToFP (builder, lhs, LLVMDoubleType (), dname);
  			break;
  		case OP_ICONV_TO_R_UN:
  		case OP_LCONV_TO_R_UN:
  			values [ins->dreg] = LLVMBuildUIToFP (builder, lhs, LLVMDoubleType (), dname);
  			break;
  #if SIZEOF_VOID_P == 4
  		case OP_LCONV_TO_U:
  #endif
  		case OP_LCONV_TO_I4:
  			values [ins->dreg] = LLVMBuildTrunc (builder, lhs, LLVMInt32Type (), dname);
  			break;
  		case OP_ICONV_TO_R4:
  		case OP_LCONV_TO_R4:
  			v = LLVMBuildSIToFP (builder, lhs, LLVMFloatType (), "");
  			if (cfg->r4fp)
  				values [ins->dreg] = v;
  			else
  				values [ins->dreg] = LLVMBuildFPExt (builder, v, LLVMDoubleType (), dname);
  			break;
  		case OP_FCONV_TO_R4:
  			v = LLVMBuildFPTrunc (builder, lhs, LLVMFloatType (), "");
  			if (cfg->r4fp)
  				values [ins->dreg] = v;
  			else
  				values [ins->dreg] = LLVMBuildFPExt (builder, v, LLVMDoubleType (), dname);
  			break;
  		case OP_RCONV_TO_R8:
  			values [ins->dreg] = LLVMBuildFPExt (builder, lhs, LLVMDoubleType (), dname);
  			break;
  		case OP_RCONV_TO_R4:
  			values [ins->dreg] = lhs;
  			break;
  		case OP_SEXT_I4:
  			values [ins->dreg] = LLVMBuildSExt (builder, convert (ctx, lhs, LLVMInt32Type ()), LLVMInt64Type (), dname);
  			break;
  		case OP_ZEXT_I4:
  			values [ins->dreg] = LLVMBuildZExt (builder, convert (ctx, lhs, LLVMInt32Type ()), LLVMInt64Type (), dname);
  			break;
  		case OP_TRUNC_I4:
  			values [ins->dreg] = LLVMBuildTrunc (builder, lhs, LLVMInt32Type (), dname);
  			break;
  		case OP_LOCALLOC_IMM: {
  			LLVMValueRef v;
  
  			guint32 size = ins->inst_imm;
  			size = (size + (MONO_ARCH_FRAME_ALIGNMENT - 1)) & ~ (MONO_ARCH_FRAME_ALIGNMENT - 1);
  
  			v = mono_llvm_build_alloca (builder, LLVMInt8Type (), LLVMConstInt (LLVMInt32Type (), size, FALSE), MONO_ARCH_FRAME_ALIGNMENT, "");
  
  			if (ins->flags & MONO_INST_INIT) {
  				LLVMValueRef args [5];
  
  				args [0] = v;
  				args [1] = LLVMConstInt (LLVMInt8Type (), 0, FALSE);
  				args [2] = LLVMConstInt (LLVMInt32Type (), size, FALSE);
  				args [3] = LLVMConstInt (LLVMInt32Type (), MONO_ARCH_FRAME_ALIGNMENT, FALSE);
  				args [4] = LLVMConstInt (LLVMInt1Type (), 0, FALSE);
  				LLVMBuildCall (builder, LLVMGetNamedFunction (lmodule, memset_func_name), args, memset_param_count, "");
  			}
  
  			values [ins->dreg] = v;
  			break;
  		}
  		case OP_LOCALLOC: {
  			LLVMValueRef v, size;
  				
  			size = LLVMBuildAnd (builder, LLVMBuildAdd (builder, convert (ctx, lhs, LLVMInt32Type ()), LLVMConstInt (LLVMInt32Type (), MONO_ARCH_FRAME_ALIGNMENT - 1, FALSE), ""), LLVMConstInt (LLVMInt32Type (), ~ (MONO_ARCH_FRAME_ALIGNMENT - 1), FALSE), "");
  
  			v = mono_llvm_build_alloca (builder, LLVMInt8Type (), size, MONO_ARCH_FRAME_ALIGNMENT, "");
  
  			if (ins->flags & MONO_INST_INIT) {
  				LLVMValueRef args [5];
  
  				args [0] = v;
  				args [1] = LLVMConstInt (LLVMInt8Type (), 0, FALSE);
  				args [2] = size;
  				args [3] = LLVMConstInt (LLVMInt32Type (), MONO_ARCH_FRAME_ALIGNMENT, FALSE);
  				args [4] = LLVMConstInt (LLVMInt1Type (), 0, FALSE);
  				LLVMBuildCall (builder, LLVMGetNamedFunction (lmodule, memset_func_name), args, memset_param_count, "");
  			}
  			values [ins->dreg] = v;
  			break;
  		}
  
  		case OP_LOADI1_MEMBASE:
  		case OP_LOADU1_MEMBASE:
  		case OP_LOADI2_MEMBASE:
  		case OP_LOADU2_MEMBASE:
  		case OP_LOADI4_MEMBASE:
  		case OP_LOADU4_MEMBASE:
  		case OP_LOADI8_MEMBASE:
  		case OP_LOADR4_MEMBASE:
  		case OP_LOADR8_MEMBASE:
  		case OP_LOAD_MEMBASE:
  		case OP_LOADI8_MEM:
  		case OP_LOADU1_MEM:
  		case OP_LOADU2_MEM:
  		case OP_LOADI4_MEM:
  		case OP_LOADU4_MEM:
  		case OP_LOAD_MEM: {
  			int size = 8;
  			LLVMValueRef base, 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);
  
  			if (sext || zext)
  				dname = (char*)"";
  
  			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)) {
  				addr = LLVMConstInt (IntPtrType (), ins->inst_imm, FALSE);
  			} else {
  				/* _MEMBASE */
  				base = lhs;
  
  				if (ins->inst_offset == 0) {
  					addr = base;
  				} else if (ins->inst_offset % size != 0) {
  					/* Unaligned load */
  					index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset, FALSE);
  					addr = LLVMBuildGEP (builder, convert (ctx, base, LLVMPointerType (LLVMInt8Type (), 0)), &index, 1, "");
  				} else {
  					index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE);
  					addr = LLVMBuildGEP (builder, convert (ctx, base, LLVMPointerType (t, 0)), &index, 1, "");
  				}
  			}
  
  			addr = convert (ctx, addr, LLVMPointerType (t, 0));
  
  			values [ins->dreg] = emit_load (ctx, bb, &builder, size, addr, dname, is_volatile);
  
  			if (!is_volatile && (ins->flags & MONO_INST_INVARIANT_LOAD)) {
  				/*
  				 * These will signal LLVM that these loads do not alias any stores, and
  				 * they can't fail, allowing them to be hoisted out of loops.
  				 */
  				set_invariant_load_flag (values [ins->dreg]);
  				set_metadata_flag (values [ins->dreg], "mono.nofail.load");
  			}
  
  			if (sext)
  				values [ins->dreg] = LLVMBuildSExt (builder, values [ins->dreg], LLVMInt32Type (), dname);
  			else if (zext)
  				values [ins->dreg] = LLVMBuildZExt (builder, values [ins->dreg], LLVMInt32Type (), dname);
  			else if (!cfg->r4fp && ins->opcode == OP_LOADR4_MEMBASE)
  				values [ins->dreg] = LLVMBuildFPExt (builder, values [ins->dreg], LLVMDoubleType (), dname);
  			break;
  		}
  				
  		case OP_STOREI1_MEMBASE_REG:
  		case OP_STOREI2_MEMBASE_REG:
  		case OP_STOREI4_MEMBASE_REG:
  		case OP_STOREI8_MEMBASE_REG:
  		case OP_STORER4_MEMBASE_REG:
  		case OP_STORER8_MEMBASE_REG:
  		case OP_STORE_MEMBASE_REG: {
  			int size = 8;
  			LLVMValueRef index, addr;
  			LLVMTypeRef t;
  			gboolean sext = FALSE, zext = FALSE;
  			gboolean is_volatile = (ins->flags & MONO_INST_FAULT);
  
  			if (!values [ins->inst_destbasereg])
  				LLVM_FAILURE (ctx, "inst_destbasereg");
  
  			t = load_store_to_llvm_type (ins->opcode, &size, &sext, &zext);
  
  			if (ins->inst_offset % size != 0) {
  				/* Unaligned store */
  				index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset, FALSE);
  				addr = LLVMBuildGEP (builder, convert (ctx, values [ins->inst_destbasereg], LLVMPointerType (LLVMInt8Type (), 0)), &index, 1, "");
  			} else {
  				index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE);				
  				addr = LLVMBuildGEP (builder, convert (ctx, values [ins->inst_destbasereg], LLVMPointerType (t, 0)), &index, 1, "");
  			}
  			emit_store (ctx, bb, &builder, size, convert (ctx, values [ins->sreg1], t), convert (ctx, addr, LLVMPointerType (t, 0)), is_volatile);
  			break;
  		}
  
  		case OP_STOREI1_MEMBASE_IMM:
  		case OP_STOREI2_MEMBASE_IMM:
  		case OP_STOREI4_MEMBASE_IMM:
  		case OP_STOREI8_MEMBASE_IMM:
  		case OP_STORE_MEMBASE_IMM: {
  			int size = 8;
  			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);
  
  			if (ins->inst_offset % size != 0) {
  				/* Unaligned store */
  				index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset, FALSE);
  				addr = LLVMBuildGEP (builder, convert (ctx, values [ins->inst_destbasereg], LLVMPointerType (LLVMInt8Type (), 0)), &index, 1, "");
  			} else {
  				index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE);				
  				addr = LLVMBuildGEP (builder, convert (ctx, values [ins->inst_destbasereg], LLVMPointerType (t, 0)), &index, 1, "");
  			}
  			emit_store (ctx, bb, &builder, size, convert (ctx, LLVMConstInt (IntPtrType (), ins->inst_imm, FALSE), t), convert (ctx, addr, LLVMPointerType (t, 0)), is_volatile);
  			break;
  		}
  
  		case OP_CHECK_THIS:
  			emit_load (ctx, bb, &builder, sizeof (gpointer), convert (ctx, lhs, LLVMPointerType (IntPtrType (), 0)), "", TRUE);
  			break;
  		case OP_OUTARG_VTRETADDR:
  			break;
  		case OP_VOIDCALL:
  		case OP_CALL:
  		case OP_LCALL:
  		case OP_FCALL:
  		case OP_RCALL:
  		case OP_VCALL:
  		case OP_VOIDCALL_MEMBASE:
  		case OP_CALL_MEMBASE:
  		case OP_LCALL_MEMBASE:
  		case OP_FCALL_MEMBASE:
  		case OP_RCALL_MEMBASE:
  		case OP_VCALL_MEMBASE:
  		case OP_VOIDCALL_REG:
  		case OP_CALL_REG:
  		case OP_LCALL_REG:
  		case OP_FCALL_REG:
  		case OP_RCALL_REG:
  		case OP_VCALL_REG: {
  			process_call (ctx, bb, &builder, ins);
  			CHECK_FAILURE (ctx);
  			break;
  		}
  		case OP_AOTCONST: {
  			guint32 got_offset;
  			LLVMValueRef indexes [2];
  			MonoJumpInfo *tmp_ji, *ji;
  			LLVMValueRef got_entry_addr;
  			char *name;
  
  			/* 
  			 * FIXME: Can't allocate from the cfg mempool since that is freed if
  			 * the LLVM compile fails.
  			 */
  			tmp_ji = g_new0 (MonoJumpInfo, 1);
  			tmp_ji->type = (MonoJumpInfoType)ins->inst_c1;
  			tmp_ji->data.target = ins->inst_p0;
  
  			ji = mono_aot_patch_info_dup (tmp_ji);
  			g_free (tmp_ji);
  
  			ji->next = cfg->patch_info;
  			cfg->patch_info = ji;
  				   
  			//mono_add_patch_info (cfg, 0, (MonoJumpInfoType)ins->inst_i1, ins->inst_p0);
  			got_offset = mono_aot_get_got_offset (cfg->patch_info);
  			ctx->module->max_got_offset = MAX (ctx->module->max_got_offset, got_offset);
  			if (!mono_aot_is_shared_got_offset (got_offset)) {
  				//mono_print_ji (ji);
  				//printf ("\n");
  				ctx->has_got_access = TRUE;
  			}
   
  			indexes [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
  			indexes [1] = LLVMConstInt (LLVMInt32Type (), (gssize)got_offset, FALSE);
  			got_entry_addr = LLVMBuildGEP (builder, ctx->module->got_var, indexes, 2, "");
  
  			name = get_aotconst_name (ji->type, ji->data.target, got_offset);
  			values [ins->dreg] = LLVMBuildLoad (builder, got_entry_addr, name);
  			g_free (name);
  			set_invariant_load_flag (values [ins->dreg]);
  			break;
  		}
  		case OP_NOT_REACHED:
  			LLVMBuildUnreachable (builder);
  			has_terminator = TRUE;
  			g_assert (bb->block_num < cfg->max_block_num);
  			ctx->unreachable [bb->block_num] = TRUE;
  			/* Might have instructions after this */
  			while (ins->next) {
  				MonoInst *next = ins->next;
  				/* 
  				 * FIXME: If later code uses the regs defined by these instructions,
  				 * compilation will fail.
  				 */
  				MONO_DELETE_INS (bb, next);
  			}				
  			break;
  		case OP_LDADDR: {
  			MonoInst *var = ins->inst_i0;
  
  			if (var->opcode == OP_VTARG_ADDR) {
  				/* The variable contains the vtype address */
  				values [ins->dreg] = values [var->dreg];
  			} else {
  				values [ins->dreg] = addresses [var->dreg];
  			}
  			break;
  		}
  		case OP_SIN: {
  			LLVMValueRef args [1];
  
  			args [0] = convert (ctx, lhs, LLVMDoubleType ());
  			values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (lmodule, "llvm.sin.f64"), args, 1, dname);
  			break;
  		}
  		case OP_COS: {
  			LLVMValueRef args [1];
  
  			args [0] = convert (ctx, lhs, LLVMDoubleType ());
  			values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (lmodule, "llvm.cos.f64"), args, 1, dname);
  			break;
  		}
  		case OP_SQRT: {
  			LLVMValueRef args [1];
  
  #if 0
  			/* This no longer seems to happen */
  			/*
  			 * LLVM optimizes sqrt(nan) into undefined in
  			 * lib/Analysis/ConstantFolding.cpp
  			 * Also, sqrt(NegativeInfinity) is optimized into 0.
  			 */
  			LLVM_FAILURE (ctx, "sqrt");
  #endif
  			args [0] = convert (ctx, lhs, LLVMDoubleType ());
  			values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (lmodule, "llvm.sqrt.f64"), args, 1, dname);
  			break;
  		}
  		case OP_ABS: {
  			LLVMValueRef args [1];
  
  			args [0] = convert (ctx, lhs, LLVMDoubleType ());
  			values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (lmodule, "fabs"), args, 1, dname);
  			break;
  		}
  
  		case OP_IMIN:
  		case OP_LMIN:
  		case OP_IMAX:
  		case OP_LMAX:
  		case OP_IMIN_UN:
  		case OP_LMIN_UN:
  		case OP_IMAX_UN:
  		case OP_LMAX_UN: {
  			LLVMValueRef v;
  
  			lhs = convert (ctx, lhs, regtype_to_llvm_type (spec [MONO_INST_DEST]));
  			rhs = convert (ctx, rhs, regtype_to_llvm_type (spec [MONO_INST_DEST]));
  
  			switch (ins->opcode) {
  			case OP_IMIN:
  			case OP_LMIN:
  				v = LLVMBuildICmp (builder, LLVMIntSLE, lhs, rhs, "");
  				break;
  			case OP_IMAX:
  			case OP_LMAX:
  				v = LLVMBuildICmp (builder, LLVMIntSGE, lhs, rhs, "");
  				break;
  			case OP_IMIN_UN:
  			case OP_LMIN_UN:
  				v = LLVMBuildICmp (builder, LLVMIntULE, lhs, rhs, "");
  				break;
  			case OP_IMAX_UN:
  			case OP_LMAX_UN:
  				v = LLVMBuildICmp (builder, LLVMIntUGE, lhs, rhs, "");
  				break;
  			default:
  				g_assert_not_reached ();
  				break;
  			}
  			values [ins->dreg] = LLVMBuildSelect (builder, v, lhs, rhs, dname);
  			break;
  		}
  		case OP_ATOMIC_EXCHANGE_I4:
  		case OP_ATOMIC_EXCHANGE_I8: {
  			LLVMValueRef args [2];
  			LLVMTypeRef t;
  				
  			if (ins->opcode == OP_ATOMIC_EXCHANGE_I4)
  				t = LLVMInt32Type ();
  			else
  				t = LLVMInt64Type ();
  
  			g_assert (ins->inst_offset == 0);
  
  			args [0] = convert (ctx, lhs, LLVMPointerType (t, 0));
  			args [1] = convert (ctx, rhs, t);
  
  			values [ins->dreg] = mono_llvm_build_atomic_rmw (builder, LLVM_ATOMICRMW_OP_XCHG, args [0], args [1]);
  			break;
  		}
  		case OP_ATOMIC_ADD_I4:
  		case OP_ATOMIC_ADD_I8: {
  			LLVMValueRef args [2];
  			LLVMTypeRef t;
  				
  			if (ins->opcode == OP_ATOMIC_ADD_I4)
  				t = LLVMInt32Type ();
  			else
  				t = LLVMInt64Type ();
  
  			g_assert (ins->inst_offset == 0);
  
  			args [0] = convert (ctx, lhs, LLVMPointerType (t, 0));
  			args [1] = convert (ctx, rhs, t);
  			values [ins->dreg] = LLVMBuildAdd (builder, mono_llvm_build_atomic_rmw (builder, LLVM_ATOMICRMW_OP_ADD, args [0], args [1]), args [1], dname);
  			break;
  		}
  		case OP_ATOMIC_CAS_I4:
  		case OP_ATOMIC_CAS_I8: {
  			LLVMValueRef args [3], val;
  			LLVMTypeRef t;
  				
  			if (ins->opcode == OP_ATOMIC_CAS_I4)
  				t = LLVMInt32Type ();
  			else
  				t = LLVMInt64Type ();
  
  			args [0] = convert (ctx, lhs, LLVMPointerType (t, 0));
  			/* comparand */
  			args [1] = convert (ctx, values [ins->sreg3], t);
  			/* new value */
  			args [2] = convert (ctx, values [ins->sreg2], t);
  			val = mono_llvm_build_cmpxchg (builder, args [0], args [1], args [2]);
  			/* cmpxchg returns a pair */
  			values [ins->dreg] = LLVMBuildExtractValue (builder, val, 0, "");
  			break;
  		}
  		case OP_MEMORY_BARRIER: {
  			mono_llvm_build_fence (builder, (BarrierKind) ins->backend.memory_barrier_kind);
  			break;
  		}
  		case OP_ATOMIC_LOAD_I1:
  		case OP_ATOMIC_LOAD_I2:
  		case OP_ATOMIC_LOAD_I4:
  		case OP_ATOMIC_LOAD_I8:
  		case OP_ATOMIC_LOAD_U1:
  		case OP_ATOMIC_LOAD_U2:
  		case OP_ATOMIC_LOAD_U4:
  		case OP_ATOMIC_LOAD_U8:
  		case OP_ATOMIC_LOAD_R4:
  		case OP_ATOMIC_LOAD_R8: {
  			LLVM_FAILURE (ctx, "atomic mono.load intrinsic");
  
  			int size;
  			gboolean sext, zext;
  			LLVMTypeRef t;
  			gboolean is_volatile = (ins->flags & MONO_INST_FAULT);
  			BarrierKind barrier = (BarrierKind) ins->backend.memory_barrier_kind;
  			LLVMValueRef index, addr;
  
  			t = load_store_to_llvm_type (ins->opcode, &size, &sext, &zext);
  
  			if (sext || zext)
  				dname = (char *)"";
  
  			if (ins->inst_offset != 0) {
  				index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE);
  				addr = LLVMBuildGEP (builder, convert (ctx, lhs, LLVMPointerType (t, 0)), &index, 1, "");
  			} else {
  				addr = lhs;
  			}
  
  			addr = convert (ctx, addr, LLVMPointerType (t, 0));
  
  			values [ins->dreg] = emit_load_general (ctx, bb, &builder, size, addr, dname, is_volatile, barrier);
  
  			if (sext)
  				values [ins->dreg] = LLVMBuildSExt (builder, values [ins->dreg], LLVMInt32Type (), dname);
  			else if (zext)
  				values [ins->dreg] = LLVMBuildZExt (builder, values [ins->dreg], LLVMInt32Type (), dname);
  			break;
  		}
  		case OP_ATOMIC_STORE_I1:
  		case OP_ATOMIC_STORE_I2:
  		case OP_ATOMIC_STORE_I4:
  		case OP_ATOMIC_STORE_I8:
  		case OP_ATOMIC_STORE_U1:
  		case OP_ATOMIC_STORE_U2:
  		case OP_ATOMIC_STORE_U4:
  		case OP_ATOMIC_STORE_U8:
  		case OP_ATOMIC_STORE_R4:
  		case OP_ATOMIC_STORE_R8: {
  			LLVM_FAILURE (ctx, "atomic mono.store intrinsic");
  
  			int size;
  			gboolean sext, zext;
  			LLVMTypeRef t;
  			gboolean is_volatile = (ins->flags & MONO_INST_FAULT);
  			BarrierKind barrier = (BarrierKind) ins->backend.memory_barrier_kind;
  			LLVMValueRef index, addr, value;
  
  			if (!values [ins->inst_destbasereg])
  				LLVM_FAILURE (ctx, "inst_destbasereg");
  
  			t = load_store_to_llvm_type (ins->opcode, &size, &sext, &zext);
  
  			index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE);
  			addr = LLVMBuildGEP (builder, convert (ctx, values [ins->inst_destbasereg], LLVMPointerType (t, 0)), &index, 1, "");
  			value = convert (ctx, values [ins->sreg1], t);
  
  			emit_store_general (ctx, bb, &builder, size, value, addr, is_volatile, barrier);
  			break;
  		}
  		case OP_RELAXED_NOP: {
  #if defined(TARGET_AMD64) || defined(TARGET_X86)
  			emit_call (ctx, bb, &builder, LLVMGetNamedFunction (ctx->lmodule, "llvm.x86.sse2.pause"), NULL, 0);
  			break;
  #else
  			break;
  #endif
  		}
  		case OP_TLS_GET: {
  #if (defined(TARGET_AMD64) || defined(TARGET_X86)) && defined(__linux__)
  #ifdef TARGET_AMD64
  			// 257 == FS segment register
  			LLVMTypeRef ptrtype = LLVMPointerType (IntPtrType (), 257);
  #else
  			// 256 == GS segment register
  			LLVMTypeRef ptrtype = LLVMPointerType (IntPtrType (), 256);
  #endif
  			// FIXME: XEN
  			values [ins->dreg] = LLVMBuildLoad (builder, LLVMBuildIntToPtr (builder, LLVMConstInt (IntPtrType (), ins->inst_offset, TRUE), ptrtype, ""), "");
  #elif defined(TARGET_AMD64) && defined(TARGET_OSX)
  			/* See mono_amd64_emit_tls_get () */
  			int offset = mono_amd64_get_tls_gs_offset () + (ins->inst_offset * 8);
  
  			// 256 == GS segment register
  			LLVMTypeRef ptrtype = LLVMPointerType (IntPtrType (), 256);
  			values [ins->dreg] = LLVMBuildLoad (builder, LLVMBuildIntToPtr (builder, LLVMConstInt (IntPtrType (), offset, TRUE), ptrtype, ""), "");
  #else
  			LLVM_FAILURE (ctx, "opcode tls-get");
  #endif
  
  			break;
  		}
  		case OP_TLS_GET_REG: {
  #if defined(TARGET_AMD64) && defined(TARGET_OSX)
  			/* See emit_tls_get_reg () */
  			// 256 == GS segment register
  			LLVMTypeRef ptrtype = LLVMPointerType (IntPtrType (), 256);
  			values [ins->dreg] = LLVMBuildLoad (builder, LLVMBuildIntToPtr (builder, convert (ctx, lhs, LLVMInt32Type ()), ptrtype, ""), "");
  #else
  			LLVM_FAILURE (ctx, "opcode tls-get");
  #endif
  			break;
  		}
  
  		case OP_TLS_SET_REG: {
  #if defined(TARGET_AMD64) && defined(TARGET_OSX)
  			/* See emit_tls_get_reg () */
  			// 256 == GS segment register
  			LLVMTypeRef ptrtype = LLVMPointerType (IntPtrType (), 256);
  			LLVMBuildStore (builder, convert (ctx, lhs, IntPtrType ()), LLVMBuildIntToPtr (builder, convert (ctx, rhs, LLVMInt32Type ()), ptrtype, ""));
  #else
  			LLVM_FAILURE (ctx, "opcode tls-set-reg");
  #endif
  			break;
  		}
  
  			/*
  			 * Overflow opcodes.
  			 */
  		case OP_IADD_OVF:
  		case OP_IADD_OVF_UN:
  		case OP_ISUB_OVF:
  		case OP_ISUB_OVF_UN:
  		case OP_IMUL_OVF:
  		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:
  		case OP_LMUL_OVF_UN:
  #endif
  			{
  				LLVMValueRef args [2], val, ovf, func;
  
  				args [0] = convert (ctx, lhs, op_to_llvm_type (ins->opcode));
  				args [1] = convert (ctx, rhs, op_to_llvm_type (ins->opcode));
  				func = LLVMGetNamedFunction (lmodule, ovf_op_to_intrins (ins->opcode));
  				g_assert (func);
  				val = LLVMBuildCall (builder, func, args, 2, "");
  				values [ins->dreg] = LLVMBuildExtractValue (builder, val, 0, dname);
  				ovf = LLVMBuildExtractValue (builder, val, 1, "");
  				emit_cond_system_exception (ctx, bb, "OverflowException", ovf);
  				CHECK_FAILURE (ctx);
  				builder = ctx->builder;
  				break;
  			}
  
  			/* 
  			 * Valuetypes.
  			 *   We currently model them using arrays. Promotion to local vregs is 
  			 * disabled for them in mono_handle_global_vregs () in the LLVM case, 
  			 * so we always have an entry in cfg->varinfo for them.
  			 * FIXME: Is this needed ?
  			 */
  		case OP_VZERO: {
  			MonoClass *klass = ins->klass;
  			LLVMValueRef args [5];
  
  			if (!klass) {
  				// FIXME:
  				LLVM_FAILURE (ctx, "!klass");
  				break;
  			}
  
  			if (!addresses [ins->dreg])
  				addresses [ins->dreg] = build_alloca (ctx, &klass->byval_arg);
  			args [0] = LLVMBuildBitCast (builder, addresses [ins->dreg], LLVMPointerType (LLVMInt8Type (), 0), "");
  			args [1] = LLVMConstInt (LLVMInt8Type (), 0, FALSE);
  			args [2] = LLVMConstInt (LLVMInt32Type (), mono_class_value_size (klass, NULL), FALSE);
  			// FIXME: Alignment
  			args [3] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
  			args [4] = LLVMConstInt (LLVMInt1Type (), 0, FALSE);
  			LLVMBuildCall (builder, LLVMGetNamedFunction (lmodule, memset_func_name), args, memset_param_count, "");
  			break;
  		}
  		case OP_DUMMY_VZERO:
  			break;
  
  		case OP_STOREV_MEMBASE:
  		case OP_LOADV_MEMBASE:
  		case OP_VMOVE: {
  			MonoClass *klass = ins->klass;
  			LLVMValueRef src = NULL, dst, args [5];
  			gboolean done = FALSE;
  
  			if (!klass) {
  				// FIXME:
  				LLVM_FAILURE (ctx, "!klass");
  				break;
  			}
  
  			if (mini_is_gsharedvt_klass (klass)) {
  				// FIXME:
  				LLVM_FAILURE (ctx, "gsharedvt");
  				break;
  			}
  
  			switch (ins->opcode) {
  			case OP_STOREV_MEMBASE:
  				if (cfg->gen_write_barriers && klass->has_references && ins->inst_destbasereg != cfg->frame_reg &&
  					LLVMGetInstructionOpcode (values [ins->inst_destbasereg]) != LLVMAlloca) {
  					/* Decomposed earlier */
  					g_assert_not_reached ();
  					break;
  				}
  				if (!addresses [ins->sreg1]) {
  					/* SIMD */
  					g_assert (values [ins->sreg1]);
  					dst = convert (ctx, LLVMBuildAdd (builder, convert (ctx, values [ins->inst_destbasereg], IntPtrType ()), LLVMConstInt (IntPtrType (), ins->inst_offset, FALSE), ""), LLVMPointerType (type_to_llvm_type (ctx, &klass->byval_arg), 0));
  					LLVMBuildStore (builder, values [ins->sreg1], dst);
  					done = TRUE;
  				} else {
  					src = LLVMBuildBitCast (builder, addresses [ins->sreg1], LLVMPointerType (LLVMInt8Type (), 0), "");
  					dst = convert (ctx, LLVMBuildAdd (builder, convert (ctx, values [ins->inst_destbasereg], IntPtrType ()), LLVMConstInt (IntPtrType (), ins->inst_offset, FALSE), ""), LLVMPointerType (LLVMInt8Type (), 0));
  				}
  				break;
  			case OP_LOADV_MEMBASE:
  				if (!addresses [ins->dreg])
  					addresses [ins->dreg] = build_alloca (ctx, &klass->byval_arg);
  				src = convert (ctx, LLVMBuildAdd (builder, convert (ctx, values [ins->inst_basereg], IntPtrType ()), LLVMConstInt (IntPtrType (), ins->inst_offset, FALSE), ""), LLVMPointerType (LLVMInt8Type (), 0));
  				dst = LLVMBuildBitCast (builder, addresses [ins->dreg], LLVMPointerType (LLVMInt8Type (), 0), "");
  				break;
  			case OP_VMOVE:
  				if (!addresses [ins->sreg1])
  					addresses [ins->sreg1] = build_alloca (ctx, &klass->byval_arg);
  				if (!addresses [ins->dreg])
  					addresses [ins->dreg] = build_alloca (ctx, &klass->byval_arg);
  				src = LLVMBuildBitCast (builder, addresses [ins->sreg1], LLVMPointerType (LLVMInt8Type (), 0), "");
  				dst = LLVMBuildBitCast (builder, addresses [ins->dreg], LLVMPointerType (LLVMInt8Type (), 0), "");
  				break;
  			default:
  				g_assert_not_reached ();
  			}
  			CHECK_FAILURE (ctx);
  
  			if (done)
  				break;
  
  			args [0] = dst;
  			args [1] = src;
  			args [2] = LLVMConstInt (LLVMInt32Type (), mono_class_value_size (klass, NULL), FALSE);
  			args [3] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
  			// FIXME: Alignment
  			args [3] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
  			args [4] = LLVMConstInt (LLVMInt1Type (), 0, FALSE);
  			LLVMBuildCall (builder, LLVMGetNamedFunction (lmodule, memcpy_func_name), args, memcpy_param_count, "");
  			break;
  		}
  		case OP_LLVM_OUTARG_VT: {
  			LLVMArgInfo *ainfo = (LLVMArgInfo*)ins->inst_p0;
  			MonoType *t = mini_get_underlying_type (ins->inst_vtype);
  
  			if (ainfo->storage == LLVMArgScalarByRef) {
  				LLVMTypeRef argtype;
  				LLVMValueRef loc, v;
  
  				argtype = type_to_llvm_arg_type (ctx, t);
  				loc = build_alloca_llvm_type (ctx, argtype, 0);
  				v = convert (ctx, values [ins->sreg1], argtype);
  				LLVMBuildStore (ctx->builder, v, loc);
  				addresses [ins->dreg] = loc;
  			} else {
  				if (!addresses [ins->sreg1]) {
  					addresses [ins->sreg1] = build_alloca (ctx, t);
  					g_assert (values [ins->sreg1]);
  					LLVMBuildStore (builder, values [ins->sreg1], addresses [ins->sreg1]);
  				}
  				addresses [ins->dreg] = addresses [ins->sreg1];
  			}
  			break;
  		}
  
  			/* 
  			 * 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;
  		}
  		case OP_LOADX_MEMBASE: {
  			LLVMTypeRef t = type_to_llvm_type (ctx, &ins->klass->byval_arg);
  			LLVMValueRef src;
  
  			src = convert (ctx, LLVMBuildAdd (builder, convert (ctx, values [ins->inst_basereg], IntPtrType ()), LLVMConstInt (IntPtrType (), ins->inst_offset, FALSE), ""), LLVMPointerType (t, 0));
  			values [ins->dreg] = mono_llvm_build_aligned_load (builder, src, "", FALSE, 1);
  			break;
  		}
  		case OP_STOREX_MEMBASE: {
  			LLVMTypeRef t = LLVMTypeOf (values [ins->sreg1]);
  			LLVMValueRef dest;
  
  			dest = convert (ctx, LLVMBuildAdd (builder, convert (ctx, values [ins->inst_destbasereg], IntPtrType ()), LLVMConstInt (IntPtrType (), ins->inst_offset, FALSE), ""), LLVMPointerType (t, 0));
  			mono_llvm_build_aligned_store (builder, values [ins->sreg1], dest, FALSE, 1);
  			break;
  		}
  		case OP_PADDB:
  		case OP_PADDW:
  		case OP_PADDD:
  		case OP_PADDQ:
  			values [ins->dreg] = LLVMBuildAdd (builder, lhs, rhs, "");
  			break;
  		case OP_ADDPD:
  		case OP_ADDPS:
  			values [ins->dreg] = LLVMBuildFAdd (builder, lhs, rhs, "");
  			break;
  		case OP_PSUBB:
  		case OP_PSUBW:
  		case OP_PSUBD:
  		case OP_PSUBQ:
  			values [ins->dreg] = LLVMBuildSub (builder, lhs, rhs, "");
  			break;
  		case OP_SUBPD:
  		case OP_SUBPS:
  			values [ins->dreg] = LLVMBuildFSub (builder, lhs, rhs, "");
  			break;
  		case OP_MULPD:
  		case OP_MULPS:
  			values [ins->dreg] = LLVMBuildFMul (builder, lhs, rhs, "");
  			break;
  		case OP_DIVPD:
  		case OP_DIVPS:
  			values [ins->dreg] = LLVMBuildFDiv (builder, lhs, rhs, "");
  			break;
  		case OP_PAND:
  			values [ins->dreg] = LLVMBuildAnd (builder, lhs, rhs, "");
  			break;
  		case OP_POR:
  			values [ins->dreg] = LLVMBuildOr (builder, lhs, rhs, "");
  			break;
  		case OP_PXOR:
  			values [ins->dreg] = LLVMBuildXor (builder, lhs, rhs, "");
  			break;
  		case OP_PMULW:
  		case OP_PMULD:
  			values [ins->dreg] = LLVMBuildMul (builder, lhs, rhs, "");
  			break;
  		case OP_ANDPS:
  		case OP_ANDNPS:
  		case OP_ORPS:
  		case OP_XORPS:
  		case OP_ANDPD:
  		case OP_ANDNPD:
  		case OP_ORPD:
  		case OP_XORPD: {
  			LLVMTypeRef t, rt;
  			LLVMValueRef v = NULL;
  
  			switch (ins->opcode) {
  			case OP_ANDPS:
  			case OP_ANDNPS:
  			case OP_ORPS:
  			case OP_XORPS:
  				t = LLVMVectorType (LLVMInt32Type (), 4);
  				rt = LLVMVectorType (LLVMFloatType (), 4);
  				break;
  			case OP_ANDPD:
  			case OP_ANDNPD:
  			case OP_ORPD:
  			case OP_XORPD:
  				t = LLVMVectorType (LLVMInt64Type (), 2);
  				rt = LLVMVectorType (LLVMDoubleType (), 2);
  				break;
  			default:
  				t = LLVMInt32Type ();
  				rt = LLVMInt32Type ();
  				g_assert_not_reached ();
  			}
  
  			lhs = LLVMBuildBitCast (builder, lhs, t, "");
  			rhs = LLVMBuildBitCast (builder, rhs, t, "");
  			switch (ins->opcode) {
  			case OP_ANDPS:
  			case OP_ANDPD:
  				v = LLVMBuildAnd (builder, lhs, rhs, "");
  				break;
  			case OP_ORPS:
  			case OP_ORPD:
  				v = LLVMBuildOr (builder, lhs, rhs, "");
  				break;
  			case OP_XORPS:
  			case OP_XORPD:
  				v = LLVMBuildXor (builder, lhs, rhs, "");
  				break;
  			case OP_ANDNPS:
  			case OP_ANDNPD:
  				v = LLVMBuildAnd (builder, rhs, LLVMBuildNot (builder, lhs, ""), "");
  				break;
  			}
  			values [ins->dreg] = LLVMBuildBitCast (builder, v, rt, "");
  			break;
  		}
  		case OP_MINPD:
  		case OP_MINPS:
  		case OP_MAXPD:
  		case OP_MAXPS:
  		case OP_ADDSUBPD:
  		case OP_ADDSUBPS:
  		case OP_PMIND_UN:
  		case OP_PMINW_UN:
  		case OP_PMINB_UN:
  		case OP_PMINW:
  		case OP_PMAXD_UN:
  		case OP_PMAXW_UN:
  		case OP_PMAXB_UN:
  		case OP_HADDPD:
  		case OP_HADDPS:
  		case OP_HSUBPD:
  		case OP_HSUBPS:
  		case OP_PADDB_SAT:
  		case OP_PADDW_SAT:
  		case OP_PSUBB_SAT:
  		case OP_PSUBW_SAT:
  		case OP_PADDB_SAT_UN:
  		case OP_PADDW_SAT_UN:
  		case OP_PSUBB_SAT_UN:
  		case OP_PSUBW_SAT_UN:
  		case OP_PAVGB_UN:
  		case OP_PAVGW_UN:
  		case OP_PACKW:
  		case OP_PACKD:
  		case OP_PACKW_UN:
  		case OP_PACKD_UN:
  		case OP_PMULW_HIGH:
  		case OP_PMULW_HIGH_UN: {
  			LLVMValueRef args [2];
  
  			args [0] = lhs;
  			args [1] = rhs;
  
  			values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (lmodule, simd_op_to_intrins (ins->opcode)), args, 2, dname);
  			break;
  		}
  		case OP_PCMPEQB:
  		case OP_PCMPEQW:
  		case OP_PCMPEQD:
  		case OP_PCMPEQQ: {
  			values [ins->dreg] = LLVMBuildSExt (builder, LLVMBuildICmp (builder, LLVMIntEQ, lhs, rhs, ""), LLVMTypeOf (lhs), "");
  			break;
  		}
  		case OP_PCMPGTB: {
  			values [ins->dreg] = LLVMBuildSExt (builder, LLVMBuildICmp (builder, LLVMIntSGT, lhs, rhs, ""), LLVMTypeOf (lhs), "");
  			break;
  		}
  		case OP_EXTRACT_R8:
  		case OP_EXTRACT_I8:
  		case OP_EXTRACT_I4:
  		case OP_EXTRACT_I2:
  		case OP_EXTRACT_U2:
  		case OP_EXTRACTX_U2:
  		case OP_EXTRACT_I1:
  		case OP_EXTRACT_U1: {
  			LLVMTypeRef t;
  			gboolean zext = FALSE;
  
  			t = simd_op_to_llvm_type (ins->opcode);
  
  			switch (ins->opcode) {
  			case OP_EXTRACT_R8:
  			case OP_EXTRACT_I8:
  			case OP_EXTRACT_I4:
  			case OP_EXTRACT_I2:
  			case OP_EXTRACT_I1:
  				break;
  			case OP_EXTRACT_U2:
  			case OP_EXTRACTX_U2:
  			case OP_EXTRACT_U1:
  				zext = TRUE;
  				break;
  			default:
  				t = LLVMInt32Type ();
  				g_assert_not_reached ();
  			}
  
  			lhs = LLVMBuildBitCast (builder, lhs, t, "");
  			values [ins->dreg] = LLVMBuildExtractElement (builder, lhs, LLVMConstInt (LLVMInt32Type (), ins->inst_c0, FALSE), "");
  			if (zext)
  				values [ins->dreg] = LLVMBuildZExt (builder, values [ins->dreg], LLVMInt32Type (), "");
  			break;
  		}
  
  		case OP_EXPAND_I1:
  		case OP_EXPAND_I2:
  		case OP_EXPAND_I4:
  		case OP_EXPAND_I8:
  		case OP_EXPAND_R4:
  		case OP_EXPAND_R8: {
  			LLVMTypeRef t = simd_op_to_llvm_type (ins->opcode);
  			LLVMValueRef mask [16], v;
  			int i;
  
  			for (i = 0; i < 16; ++i)
  				mask [i] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
  
  			v = convert (ctx, values [ins->sreg1], LLVMGetElementType (t));
  
  			values [ins->dreg] = LLVMBuildInsertElement (builder, LLVMConstNull (t), v, LLVMConstInt (LLVMInt32Type (), 0, FALSE), "");
  			values [ins->dreg] = LLVMBuildShuffleVector (builder, values [ins->dreg], LLVMGetUndef (t), LLVMConstVector (mask, LLVMGetVectorSize (t)), "");
  			break;
  		}
  
  		case OP_INSERT_I1:
  			values [ins->dreg] = LLVMBuildInsertElement (builder, values [ins->sreg1], convert (ctx, values [ins->sreg2], LLVMInt8Type ()), LLVMConstInt (LLVMInt32Type (), ins->inst_c0, FALSE), dname);
  			break;
  		case OP_INSERT_I2:
  			values [ins->dreg] = LLVMBuildInsertElement (builder, values [ins->sreg1], convert (ctx, values [ins->sreg2], LLVMInt16Type ()), LLVMConstInt (LLVMInt32Type (), ins->inst_c0, FALSE), dname);
  			break;
  		case OP_INSERT_I4:
  			values [ins->dreg] = LLVMBuildInsertElement (builder, values [ins->sreg1], convert (ctx, values [ins->sreg2], LLVMInt32Type ()), LLVMConstInt (LLVMInt32Type (), ins->inst_c0, FALSE), dname);
  			break;
  		case OP_INSERT_I8:
  			values [ins->dreg] = LLVMBuildInsertElement (builder, values [ins->sreg1], convert (ctx, values [ins->sreg2], LLVMInt64Type ()), LLVMConstInt (LLVMInt32Type (), ins->inst_c0, FALSE), dname);
  			break;
  		case OP_INSERT_R4:
  			values [ins->dreg] = LLVMBuildInsertElement (builder, values [ins->sreg1], convert (ctx, values [ins->sreg2], LLVMFloatType ()), LLVMConstInt (LLVMInt32Type (), ins->inst_c0, FALSE), dname);
  			break;
  		case OP_INSERT_R8:
  			values [ins->dreg] = LLVMBuildInsertElement (builder, values [ins->sreg1], convert (ctx, values [ins->sreg2], LLVMDoubleType ()), LLVMConstInt (LLVMInt32Type (), ins->inst_c0, FALSE), dname);
  			break;
  
  		case OP_CVTDQ2PD:
  		case OP_CVTDQ2PS:
  		case OP_CVTPD2DQ:
  		case OP_CVTPS2DQ:
  		case OP_CVTPD2PS:
  		case OP_CVTPS2PD:
  		case OP_CVTTPD2DQ:
  		case OP_CVTTPS2DQ:
  		case OP_EXTRACT_MASK:
  		case OP_SQRTPS:
  		case OP_SQRTPD:
  		case OP_RSQRTPS:
  		case OP_RCPPS: {
  			LLVMValueRef v;
  
  			v = convert (ctx, values [ins->sreg1], simd_op_to_llvm_type (ins->opcode));
  
  			values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (lmodule, simd_op_to_intrins (ins->opcode)), &v, 1, dname);
  			break;
  		}
  
  		case OP_COMPPS:
  		case OP_COMPPD: {
  			LLVMValueRef args [3];
  
  			args [0] = lhs;
  			args [1] = rhs;
  			args [2] = LLVMConstInt (LLVMInt8Type (), ins->inst_c0, FALSE);
  
  			values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (lmodule, simd_op_to_intrins (ins->opcode)), args, 3, dname);
  			break;
  		}
  
  		case OP_ICONV_TO_X:
  			/* This is only used for implementing shifts by non-immediate */
  			values [ins->dreg] = lhs;
  			break;
  
  		case OP_PSHRW:
  		case OP_PSHRD:
  		case OP_PSHRQ:
  		case OP_PSARW:
  		case OP_PSARD:
  		case OP_PSHLW:
  		case OP_PSHLD:
  		case OP_PSHLQ: {
  			LLVMValueRef args [3];
  
  			args [0] = lhs;
  			args [1] = LLVMConstInt (LLVMInt32Type (), ins->inst_imm, FALSE);
  
  			values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (lmodule, simd_op_to_intrins (ins->opcode)), args, 2, dname);
  			break;
  		}
  
  		case OP_PSHRW_REG:
  		case OP_PSHRD_REG:
  		case OP_PSHRQ_REG:
  		case OP_PSARW_REG:
  		case OP_PSARD_REG:
  		case OP_PSHLW_REG:
  		case OP_PSHLD_REG:
  		case OP_PSHLQ_REG: {
  			LLVMValueRef args [3];
  
  			args [0] = lhs;
  			args [1] = values [ins->sreg2];
  
  			values [ins->dreg] = LLVMBuildCall (builder, LLVMGetNamedFunction (lmodule, simd_op_to_intrins (ins->opcode)), args, 2, dname);
  			break;
  		}
  
  		case OP_SHUFPS:
  		case OP_SHUFPD:
  		case OP_PSHUFLED:
  		case OP_PSHUFLEW_LOW:
  		case OP_PSHUFLEW_HIGH: {
  			int mask [16];
  			LLVMValueRef v1 = NULL, v2 = NULL, mask_values [16];
  			int i, mask_size = 0;
  			int imask = ins->inst_c0;
  	
  			/* Convert the x86 shuffle mask to LLVM's */
  			switch (ins->opcode) {
  			case OP_SHUFPS:
  				mask_size = 4;
  				mask [0] = ((imask >> 0) & 3);
  				mask [1] = ((imask >> 2) & 3);
  				mask [2] = ((imask >> 4) & 3) + 4;
  				mask [3] = ((imask >> 6) & 3) + 4;
  				v1 = values [ins->sreg1];
  				v2 = values [ins->sreg2];
  				break;
  			case OP_SHUFPD:
  				mask_size = 2;
  				mask [0] = ((imask >> 0) & 1);
  				mask [1] = ((imask >> 1) & 1) + 2;
  				v1 = values [ins->sreg1];
  				v2 = values [ins->sreg2];
  				break;
  			case OP_PSHUFLEW_LOW:
  				mask_size = 8;
  				mask [0] = ((imask >> 0) & 3);
  				mask [1] = ((imask >> 2) & 3);
  				mask [2] = ((imask >> 4) & 3);
  				mask [3] = ((imask >> 6) & 3);
  				mask [4] = 4 + 0;
  				mask [5] = 4 + 1;
  				mask [6] = 4 + 2;
  				mask [7] = 4 + 3;
  				v1 = values [ins->sreg1];
  				v2 = LLVMGetUndef (LLVMTypeOf (v1));
  				break;
  			case OP_PSHUFLEW_HIGH:
  				mask_size = 8;
  				mask [0] = 0;
  				mask [1] = 1;
  				mask [2] = 2;
  				mask [3] = 3;
  				mask [4] = 4 + ((imask >> 0) & 3);
  				mask [5] = 4 + ((imask >> 2) & 3);
  				mask [6] = 4 + ((imask >> 4) & 3);
  				mask [7] = 4 + ((imask >> 6) & 3);
  				v1 = values [ins->sreg1];
  				v2 = LLVMGetUndef (LLVMTypeOf (v1));
  				break;
  			case OP_PSHUFLED:
  				mask_size = 4;
  				mask [0] = ((imask >> 0) & 3);
  				mask [1] = ((imask >> 2) & 3);
  				mask [2] = ((imask >> 4) & 3);
  				mask [3] = ((imask >> 6) & 3);
  				v1 = values [ins->sreg1];
  				v2 = LLVMGetUndef (LLVMTypeOf (v1));
  				break;
  			default:
  				g_assert_not_reached ();
  			}
  			for (i = 0; i < mask_size; ++i)
  				mask_values [i] = LLVMConstInt (LLVMInt32Type (), mask [i], FALSE);
  
  			values [ins->dreg] =
  				LLVMBuildShuffleVector (builder, v1, v2,
  										LLVMConstVector (mask_values, mask_size), dname);
  			break;
  		}
  
  		case OP_UNPACK_LOWB:
  		case OP_UNPACK_LOWW:
  		case OP_UNPACK_LOWD:
  		case OP_UNPACK_LOWQ:
  		case OP_UNPACK_LOWPS:
  		case OP_UNPACK_LOWPD:
  		case OP_UNPACK_HIGHB:
  		case OP_UNPACK_HIGHW:
  		case OP_UNPACK_HIGHD:
  		case OP_UNPACK_HIGHQ:
  		case OP_UNPACK_HIGHPS:
  		case OP_UNPACK_HIGHPD: {
  			int mask [16];
  			LLVMValueRef mask_values [16];
  			int i, mask_size = 0;
  			gboolean low = FALSE;
  
  			switch (ins->opcode) {
  			case OP_UNPACK_LOWB:
  				mask_size = 16;
  				low = TRUE;
  				break;
  			case OP_UNPACK_LOWW:
  				mask_size = 8;
  				low = TRUE;
  				break;
  			case OP_UNPACK_LOWD:
  			case OP_UNPACK_LOWPS:
  				mask_size = 4;
  				low = TRUE;
  				break;
  			case OP_UNPACK_LOWQ:
  			case OP_UNPACK_LOWPD:
  				mask_size = 2;
  				low = TRUE;
  				break;
  			case OP_UNPACK_HIGHB:
  				mask_size = 16;
  				break;
  			case OP_UNPACK_HIGHW:
  				mask_size = 8;
  				break;
  			case OP_UNPACK_HIGHD:
  			case OP_UNPACK_HIGHPS:
  				mask_size = 4;
  				break;
  			case OP_UNPACK_HIGHQ:
  			case OP_UNPACK_HIGHPD:
  				mask_size = 2;
  				break;
  			default:
  				g_assert_not_reached ();
  			}
  
  			if (low) {
  				for (i = 0; i < (mask_size / 2); ++i) {
  					mask [(i * 2)] = i;
  					mask [(i * 2) + 1] = mask_size + i;
  				}
  			} else {
  				for (i = 0; i < (mask_size / 2); ++i) {
  					mask [(i * 2)] = (mask_size / 2) + i;
  					mask [(i * 2) + 1] = mask_size + (mask_size / 2) + i;
  				}
  			}
  
  			for (i = 0; i < mask_size; ++i)
  				mask_values [i] = LLVMConstInt (LLVMInt32Type (), mask [i], FALSE);
  			
  			values [ins->dreg] =
  				LLVMBuildShuffleVector (builder, values [ins->sreg1], values [ins->sreg2],
  										LLVMConstVector (mask_values, mask_size), dname);
  			break;
  		}
  
  		case OP_DUPPD: {
  			LLVMTypeRef t = simd_op_to_llvm_type (ins->opcode);
  			LLVMValueRef v, val;
  
  			v = LLVMBuildExtractElement (builder, lhs, LLVMConstInt (LLVMInt32Type (), 0, FALSE), "");
  			val = LLVMConstNull (t);
  			val = LLVMBuildInsertElement (builder, val, v, LLVMConstInt (LLVMInt32Type (), 0, FALSE), "");
  			val = LLVMBuildInsertElement (builder, val, v, LLVMConstInt (LLVMInt32Type (), 1, FALSE), dname);
  
  			values [ins->dreg] = val;
  			break;
  		}
  		case OP_DUPPS_LOW:
  		case OP_DUPPS_HIGH: {
  			LLVMTypeRef t = simd_op_to_llvm_type (ins->opcode);
  			LLVMValueRef v1, v2, val;
  			
  
  			if (ins->opcode == OP_DUPPS_LOW) {
  				v1 = LLVMBuildExtractElement (builder, lhs, LLVMConstInt (LLVMInt32Type (), 0, FALSE), "");
  				v2 = LLVMBuildExtractElement (builder, lhs, LLVMConstInt (LLVMInt32Type (), 2, FALSE), "");
  			} else {
  				v1 = LLVMBuildExtractElement (builder, lhs, LLVMConstInt (LLVMInt32Type (), 1, FALSE), "");
  				v2 = LLVMBuildExtractElement (builder, lhs, LLVMConstInt (LLVMInt32Type (), 3, FALSE), "");
  			}
  			val = LLVMConstNull (t);
  			val = LLVMBuildInsertElement (builder, val, v1, LLVMConstInt (LLVMInt32Type (), 0, FALSE), "");
  			val = LLVMBuildInsertElement (builder, val, v1, LLVMConstInt (LLVMInt32Type (), 1, FALSE), "");
  			val = LLVMBuildInsertElement (builder, val, v2, LLVMConstInt (LLVMInt32Type (), 2, FALSE), "");
  			val = LLVMBuildInsertElement (builder, val, v2, LLVMConstInt (LLVMInt32Type (), 3, FALSE), "");
  			
  			values [ins->dreg] = val;
  			break;
  		}
  
  #endif /* SIMD */
  
  		case OP_DUMMY_USE:
  			break;
  
  			/*
  			 * 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:
  		case OP_RETHROW: {
  			gboolean rethrow = (ins->opcode == OP_RETHROW);
  			if (ctx->llvm_only) {
  				emit_llvmonly_throw (ctx, bb, rethrow, lhs);
  				has_terminator = TRUE;
  				ctx->unreachable [bb->block_num] = TRUE;
  			} else {
  				emit_throw (ctx, bb, rethrow, lhs);
  				builder = ctx->builder;
  			}
  			break;
  		}
  		case OP_CALL_HANDLER: {
  			/* 
  			 * We don't 'call' handlers, but instead simply branch to them.
  			 * The code generated by ENDFINALLY will branch back to us.
  			 */
  			LLVMBasicBlockRef noex_bb;
  			GSList *bb_list;
  			BBInfo *info = &bblocks [ins->inst_target_bb->block_num];
  
  			bb_list = info->call_handler_return_bbs;
  
  			/* 
  			 * Set the indicator variable for the finally clause.
  			 */
  			lhs = info->finally_ind;
  			g_assert (lhs);
  			LLVMBuildStore (builder, LLVMConstInt (LLVMInt32Type (), g_slist_length (bb_list) + 1, FALSE), lhs);
  				
  			/* Branch to the finally clause */
  			LLVMBuildBr (builder, info->call_handler_target_bb);
  
  			noex_bb = gen_bb (ctx, "CALL_HANDLER_CONT_BB");
  			info->call_handler_return_bbs = g_slist_append_mempool (cfg->mempool, info->call_handler_return_bbs, noex_bb);
  
  			builder = ctx->builder = create_builder (ctx);
  			LLVMPositionBuilderAtEnd (ctx->builder, noex_bb);
  
  			bblocks [bb->block_num].end_bblock = noex_bb;
  			break;
  		}
  		case OP_START_HANDLER: {
  			break;
  		}
  		case OP_ENDFINALLY: {
  			LLVMBasicBlockRef resume_bb;
  			MonoBasicBlock *handler_bb;
  			LLVMValueRef val, switch_ins, callee;
  			GSList *bb_list;
  			BBInfo *info;
  
  			handler_bb = (MonoBasicBlock*)g_hash_table_lookup (ctx->region_to_handler, GUINT_TO_POINTER (mono_get_block_region_notry (cfg, bb->region)));
  			g_assert (handler_bb);
  			info = &bblocks [handler_bb->block_num];
  			lhs = info->finally_ind;
  			g_assert (lhs);
  
  			bb_list = info->call_handler_return_bbs;
  
  			resume_bb = gen_bb (ctx, "ENDFINALLY_RESUME_BB");
  
  			/* Load the finally variable */
  			val = LLVMBuildLoad (builder, lhs, "");
  
  			/* Reset the variable */
  			LLVMBuildStore (builder, LLVMConstInt (LLVMInt32Type (), 0, FALSE), lhs);
  
  			/* Branch to either resume_bb, or to the bblocks in bb_list */
  			switch_ins = LLVMBuildSwitch (builder, val, resume_bb, g_slist_length (bb_list));
  			/* 
  			 * The other targets are added at the end to handle OP_CALL_HANDLER
  			 * opcodes processed later.
  			 */
  			info->endfinally_switch_ins_list = g_slist_append_mempool (cfg->mempool, info->endfinally_switch_ins_list, switch_ins);
  
  			builder = ctx->builder = create_builder (ctx);
  			LLVMPositionBuilderAtEnd (ctx->builder, resume_bb);
  
  			if (ctx->llvm_only) {
  				emit_resume_eh (ctx, bb);
  			} else {
  				if (ctx->cfg->compile_aot) {
  					callee = get_callee (ctx, LLVMFunctionType (LLVMVoidType (), NULL, 0, FALSE), MONO_PATCH_INFO_INTERNAL_METHOD, "llvm_resume_unwind_trampoline");
  				} else {
  					callee = LLVMGetNamedFunction (lmodule, "llvm_resume_unwind_trampoline");
  				}
  				LLVMBuildCall (builder, callee, NULL, 0, "");
  				LLVMBuildUnreachable (builder);
  			}
  
  			has_terminator = TRUE;
  			break;
  		}
  		default: {
  			char reason [128];
  
  			sprintf (reason, "opcode %s", mono_inst_name (ins->opcode));
  			LLVM_FAILURE (ctx, reason);
  			break;
  		}
  		}
  
  		/* Convert the value to the type required by phi nodes */
  		if (spec [MONO_INST_DEST] != ' ' && !MONO_IS_STORE_MEMBASE (ins) && ctx->vreg_types [ins->dreg]) {
  			if (!values [ins->dreg])
  				/* vtypes */
  				values [ins->dreg] = addresses [ins->dreg];
  			else
  				values [ins->dreg] = convert (ctx, values [ins->dreg], ctx->vreg_types [ins->dreg]);
  		}
  
  		/* Add stores for volatile variables */
  		if (spec [MONO_INST_DEST] != ' ' && spec [MONO_INST_DEST] != 'v' && !MONO_IS_STORE_MEMBASE (ins))
  			emit_volatile_store (ctx, ins->dreg);
  	}
  
  	if (!has_terminator && bb->next_bb && (bb == cfg->bb_entry || bb->in_count > 0)) {
  		LLVMBuildBr (builder, get_bb (ctx, bb->next_bb));
  	}
  
  	if (bb == cfg->bb_exit && sig->ret->type == MONO_TYPE_VOID) {
  		emit_dbg_loc (ctx, builder, cfg->header->code + cfg->header->code_size - 1);
  		LLVMBuildRetVoid (builder);
  	}
  
  	if (bb == cfg->bb_entry)
  		ctx->last_alloca = LLVMGetLastInstruction (get_bb (ctx, cfg->bb_entry));
  
  	return;
  
   FAILURE:
  	return;
  }
  
  /*
   * mono_llvm_check_method_supported:
   *
   *   Do some quick checks to decide whenever cfg->method can be compiled by LLVM, to avoid
   * compiling a method twice.
   */
  void
  mono_llvm_check_method_supported (MonoCompile *cfg)
  {
  	int i, j;
  
  	if (cfg->llvm_only)
  		return;
  
  	if (cfg->method->save_lmf) {
  		cfg->exception_message = g_strdup ("lmf");
  		cfg->disable_llvm = TRUE;
  	}
  	if (cfg->disable_llvm)
  		return;
  
  	/*
  	 * Nested clauses where one of the clauses is a finally clause is
  	 * not supported, because LLVM can't figure out the control flow,
  	 * probably because we resume exception handling by calling our
  	 * own function instead of using the 'resume' llvm instruction.
  	 */
  	for (i = 0; i < cfg->header->num_clauses; ++i) {
  		for (j = 0; j < cfg->header->num_clauses; ++j) {
  			MonoExceptionClause *clause1 = &cfg->header->clauses [i];
  			MonoExceptionClause *clause2 = &cfg->header->clauses [j];
  
  			if (i != j && clause1->try_offset >= clause2->try_offset && clause1->handler_offset <= clause2->handler_offset &&
  				(clause1->flags == MONO_EXCEPTION_CLAUSE_FINALLY || clause2->flags == MONO_EXCEPTION_CLAUSE_FINALLY)) {
  				cfg->exception_message = g_strdup ("nested clauses");
  				cfg->disable_llvm = TRUE;
  				break;
  			}
  		}
  	}
  	if (cfg->disable_llvm)
  		return;
  
  	/* FIXME: */
  	if (cfg->method->dynamic) {
  		cfg->exception_message = g_strdup ("dynamic.");
  		cfg->disable_llvm = TRUE;
  	}
  	if (cfg->disable_llvm)
  		return;
  }
  
  static LLVMCallInfo*
  get_llvm_call_info (MonoCompile *cfg, MonoMethodSignature *sig)
  {
  	LLVMCallInfo *linfo;
  	int i;
  
  	linfo = mono_arch_get_llvm_call_info (cfg, sig);
  	for (i = 0; i < sig->param_count; ++i)
  		linfo->args [i + sig->hasthis].type = sig->params [i];
  
  	return linfo;
  }
  
  /*
   * mono_llvm_emit_method:
   *
   *   Emit LLVM IL from the mono IL, and compile it to native code using LLVM.
   */
  void
  mono_llvm_emit_method (MonoCompile *cfg)
  {
  	EmitContext *ctx;
  	MonoMethodSignature *sig;
  	MonoBasicBlock *bb;
  	LLVMTypeRef method_type;
  	LLVMValueRef method = NULL;
  	char *method_name;
  	LLVMValueRef *values;
  	int i, max_block_num, bb_index;
  	gboolean last = FALSE;
  	GPtrArray *phi_values;
  	LLVMCallInfo *linfo;
  	GSList *l;
  	LLVMModuleRef lmodule;
  	BBInfo *bblocks;
  	GPtrArray *bblock_list;
  	MonoMethodHeader *header;
  	MonoExceptionClause *clause;
  	char **names;
  
  	/* The code below might acquire the loader lock, so use it for global locking */
  	mono_loader_lock ();
  
  	/* Used to communicate with the callbacks */
  	mono_native_tls_set_value (current_cfg_tls_id, cfg);
  
  	ctx = g_new0 (EmitContext, 1);
  	ctx->cfg = cfg;
  	ctx->mempool = cfg->mempool;
  
  	/*
  	 * This maps vregs to the LLVM instruction defining them
  	 */
  	values = g_new0 (LLVMValueRef, cfg->next_vreg);
  	/*
  	 * This maps vregs for volatile variables to the LLVM instruction defining their
  	 * address.
  	 */
  	ctx->addresses = g_new0 (LLVMValueRef, cfg->next_vreg);
  	ctx->vreg_types = g_new0 (LLVMTypeRef, cfg->next_vreg);
  	ctx->vreg_cli_types = g_new0 (MonoType*, cfg->next_vreg);
  	phi_values = g_ptr_array_sized_new (256);
  	/* 
  	 * This signals whenever the vreg was defined by a phi node with no input vars
  	 * (i.e. all its input bblocks end with NOT_REACHABLE).
  	 */
  	ctx->is_dead = g_new0 (gboolean, cfg->next_vreg);
  	/* Whenever the bblock is unreachable */
  	ctx->unreachable = g_new0 (gboolean, cfg->max_block_num);
  	bblock_list = g_ptr_array_sized_new (256);
  
  	ctx->values = values;
  	ctx->region_to_handler = g_hash_table_new (NULL, NULL);
  	ctx->clause_to_handler = g_hash_table_new (NULL, NULL);
  	ctx->method_to_callers = g_hash_table_new (NULL, NULL);
   
  	if (cfg->compile_aot) {
  		ctx->module = &aot_module;
  		method_name = mono_aot_get_method_name (cfg);
  		cfg->llvm_method_name = g_strdup (method_name);
  	} else {
  		init_jit_module (cfg->domain);
  		ctx->module = (MonoLLVMModule*)domain_jit_info (cfg->domain)->llvm_module;
  		method_name = mono_method_full_name (cfg->method, TRUE);
  	}
  
  	lmodule = ctx->lmodule = ctx->module->lmodule;
  	ctx->llvm_only = ctx->module->llvm_only;
  
  	if (cfg->gsharedvt)
  		LLVM_FAILURE (ctx, "gsharedvt");
  
  #if 1
  	{
  		static int count = 0;
  		count ++;
  
  		if (g_getenv ("LLVM_COUNT")) {
  			if (count == atoi (g_getenv ("LLVM_COUNT"))) {
  				printf ("LAST: %s\n", mono_method_full_name (cfg->method, TRUE));
  				fflush (stdout);
  				last = TRUE;
  			}
  			if (count > atoi (g_getenv ("LLVM_COUNT")))
  				LLVM_FAILURE (ctx, "");
  		}
  	}
  #endif
  
  	sig = mono_method_signature (cfg->method);
  	ctx->sig = sig;
  
  	linfo = get_llvm_call_info (cfg, sig);
  	ctx->linfo = linfo;
  	CHECK_FAILURE (ctx);
  
  	if (cfg->rgctx_var)
  		linfo->rgctx_arg = TRUE;
  	method_type = sig_to_llvm_sig_full (ctx, sig, linfo);
  	CHECK_FAILURE (ctx);
  
  	method = LLVMAddFunction (lmodule, method_name, method_type);
  	ctx->lmethod = method;
  
  	if (!cfg->llvm_only)
  		LLVMSetFunctionCallConv (method, LLVMMono1CallConv);
  	LLVMSetLinkage (method, LLVMPrivateLinkage);
  
  	LLVMAddFunctionAttr (method, LLVMUWTable);
  
  	if (cfg->compile_aot) {
  		LLVMSetLinkage (method, LLVMInternalLinkage);
  		if (ctx->module->external_symbols) {
  			LLVMSetLinkage (method, LLVMExternalLinkage);
  			LLVMSetVisibility (method, LLVMHiddenVisibility);
  		}
  	} else {
  		LLVMSetLinkage (method, LLVMPrivateLinkage);
  	}
  
  	if (cfg->method->save_lmf && !cfg->llvm_only)
  		LLVM_FAILURE (ctx, "lmf");
  
  	if (sig->pinvoke && cfg->method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE && !cfg->llvm_only)
  		LLVM_FAILURE (ctx, "pinvoke signature");
  
  	header = cfg->header;
  	for (i = 0; i < header->num_clauses; ++i) {
  		clause = &header->clauses [i];
  		if (clause->flags != MONO_EXCEPTION_CLAUSE_FINALLY && clause->flags != MONO_EXCEPTION_CLAUSE_NONE)
  			LLVM_FAILURE (ctx, "non-finally/catch clause.");
  	}
  	if (header->num_clauses || (cfg->method->iflags & METHOD_IMPL_ATTRIBUTE_NOINLINING))
  		/* We can't handle inlined methods with clauses */
  		LLVMAddFunctionAttr (method, LLVMNoInlineAttribute);
  
  	if (linfo->rgctx_arg) {
  		ctx->rgctx_arg = LLVMGetParam (method, linfo->rgctx_arg_pindex);
  		ctx->rgctx_arg_pindex = linfo->rgctx_arg_pindex;
  		/*
  		 * We mark the rgctx parameter with the inreg attribute, which is mapped to
  		 * MONO_ARCH_RGCTX_REG in the Mono calling convention in llvm, i.e.
  		 * CC_X86_64_Mono in X86CallingConv.td.
  		 */
  		if (!ctx->llvm_only)
  			LLVMAddAttribute (ctx->rgctx_arg, LLVMInRegAttribute);
  		LLVMSetValueName (ctx->rgctx_arg, "rgctx");
  	} else {
  		ctx->rgctx_arg_pindex = -1;
  	}
  	if (cfg->vret_addr) {
  		values [cfg->vret_addr->dreg] = LLVMGetParam (method, linfo->vret_arg_pindex);
  		LLVMSetValueName (values [cfg->vret_addr->dreg], "vret");
  		if (linfo->ret.storage == LLVMArgVtypeByRef) {
  			LLVMAddAttribute (LLVMGetParam (method, linfo->vret_arg_pindex), LLVMStructRetAttribute);
  			LLVMAddAttribute (LLVMGetParam (method, linfo->vret_arg_pindex), LLVMNoAliasAttribute);
  		}
  	} else if (linfo->ret.storage == LLVMArgScalarRetAddr) {
  		LLVMValueRef param = LLVMGetParam (method, linfo->vret_arg_pindex);
  		LLVMSetValueName (param, "vret");
  	}
  
  	if (sig->hasthis) {
  		ctx->this_arg_pindex = linfo->this_arg_pindex;
  		ctx->this_arg = LLVMGetParam (method, linfo->this_arg_pindex);
  		values [cfg->args [0]->dreg] = ctx->this_arg;
  		LLVMSetValueName (values [cfg->args [0]->dreg], "this");
  	}
  
  	names = g_new (char *, sig->param_count);
  	mono_method_get_param_names (cfg->method, (const char **) names);
  
  	for (i = 0; i < sig->param_count; ++i) {
  		LLVMArgInfo *ainfo = &linfo->args [i + sig->hasthis];
  		char *name;
  
  		values [cfg->args [i + sig->hasthis]->dreg] = LLVMGetParam (method, ainfo->pindex);
  		if (ainfo->storage == LLVMArgScalarByRef) {
  			if (names [i] && names [i][0] != '\0')
  				name = g_strdup_printf ("p_arg_%s", names [i]);
  			else
  				name = g_strdup_printf ("p_arg_%d", i);
  		} else {
  			if (names [i] && names [i][0] != '\0')
  				name = g_strdup_printf ("arg_%s", names [i]);
  			else
  				name = g_strdup_printf ("arg_%d", i);
  		}
  		LLVMSetValueName (values [cfg->args [i + sig->hasthis]->dreg], name);
  		g_free (name);
  		if (ainfo->storage == LLVMArgVtypeByVal)
  			LLVMAddAttribute (LLVMGetParam (method, ainfo->pindex), LLVMByValAttribute);
  
  		if (ainfo->storage == LLVMArgVtypeByRef) {
  			/* For OP_LDADDR */
  			cfg->args [i + sig->hasthis]->opcode = OP_VTARG_ADDR;
  		}
  	}
  	g_free (names);
  
  	if (ctx->module->emit_dwarf && cfg->compile_aot && mono_debug_enabled ()) {
  		ctx->minfo = mono_debug_lookup_method (cfg->method);
  		ctx->dbg_md = emit_dbg_subprogram (ctx, cfg, method, method_name);
  	}
  
  	max_block_num = 0;
  	for (bb = cfg->bb_entry; bb; bb = bb->next_bb)
  		max_block_num = MAX (max_block_num, bb->block_num);
  	ctx->bblocks = bblocks = g_new0 (BBInfo, max_block_num + 1);
  
  	/* Add branches between non-consecutive bblocks */
  	for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
  		if (bb->last_ins && MONO_IS_COND_BRANCH_OP (bb->last_ins) &&
  			bb->next_bb != bb->last_ins->inst_false_bb) {
  			
  			MonoInst *inst = (MonoInst*)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst));
  			inst->opcode = OP_BR;
  			inst->inst_target_bb = bb->last_ins->inst_false_bb;
  			mono_bblock_add_inst (bb, inst);
  		}
  	}
  
  	/*
  	 * The INDIRECT flag added by OP_LDADDR inhibits optimizations, even if the LDADDR
  	 * was later optimized away, so clear these flags, and add them back for the still
  	 * present OP_LDADDR instructions.
  	 */
  	for (i = 0; i < cfg->next_vreg; ++i) {
  		MonoInst *ins;
  
  		ins = get_vreg_to_inst (cfg, i);
  		if (ins && ins != cfg->rgctx_var)
  			ins->flags &= ~MONO_INST_INDIRECT;
  	}
  
  	/*
  	 * Make a first pass over the code to precreate PHI nodes/set INDIRECT flags.
  	 */
  	for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
  		MonoInst *ins;
  		LLVMBuilderRef builder;
  		char *dname;
  		char dname_buf[128];
  
  		builder = create_builder (ctx);
  
  		for (ins = bb->code; ins; ins = ins->next) {
  			switch (ins->opcode) {
  			case OP_PHI:
  			case OP_FPHI:
  			case OP_VPHI:
  			case OP_XPHI: {
  				LLVMTypeRef phi_type = llvm_type_to_stack_type (cfg, type_to_llvm_type (ctx, &ins->klass->byval_arg));
  
  				CHECK_FAILURE (ctx);
  
  				if (ins->opcode == OP_VPHI) {
  					/* Treat valuetype PHI nodes as operating on the address itself */
  					g_assert (ins->klass);
  					phi_type = LLVMPointerType (type_to_llvm_type (ctx, &ins->klass->byval_arg), 0);
  				}
  
  				/* 
  				 * Have to precreate these, as they can be referenced by
  				 * earlier instructions.
  				 */
  				sprintf (dname_buf, "t%d", ins->dreg);
  				dname = dname_buf;
  				values [ins->dreg] = LLVMBuildPhi (builder, phi_type, dname);
  
  				if (ins->opcode == OP_VPHI)
  					ctx->addresses [ins->dreg] = values [ins->dreg];
  
  				g_ptr_array_add (phi_values, values [ins->dreg]);
  
  				/* 
  				 * Set the expected type of the incoming arguments since these have
  				 * to have the same type.
  				 */
  				for (i = 0; i < ins->inst_phi_args [0]; i++) {
  					int sreg1 = ins->inst_phi_args [i + 1];
  					
  					if (sreg1 != -1)
  						ctx->vreg_types [sreg1] = phi_type;
  				}
  				break;
  				}
  			case OP_LDADDR:
  				((MonoInst*)ins->inst_p0)->flags |= MONO_INST_INDIRECT;
  				break;
  			default:
  				break;
  			}
  		}
  	}
  
  	/* 
  	 * Create an ordering for bblocks, use the depth first order first, then
  	 * put the exception handling bblocks last.
  	 */
  	for (bb_index = 0; bb_index < cfg->num_bblocks; ++bb_index) {
  		bb = cfg->bblocks [bb_index];
  		if (!(bb->region != -1 && !MONO_BBLOCK_IS_IN_REGION (bb, MONO_REGION_TRY))) {
  			g_ptr_array_add (bblock_list, bb);
  			bblocks [bb->block_num].added = TRUE;
  		}
  	}
  
  	for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
  		if (!bblocks [bb->block_num].added)
  			g_ptr_array_add (bblock_list, bb);
  	}
  
  	/*
  	 * Second pass: generate code.
  	 */
  	// Emit entry point
  	LLVMBuilderRef entry_builder = create_builder (ctx);
  	LLVMBasicBlockRef entry_bb = get_bb (ctx, cfg->bb_entry);
  	LLVMPositionBuilderAtEnd (entry_builder, entry_bb);
  	emit_entry_bb (ctx, entry_builder);
  
  	// Make landing pads first
  	ctx->exc_meta = g_hash_table_new_full (NULL, NULL, NULL, NULL);
  
  	if (ctx->llvm_only) {
  		size_t group_index = 0;
  		while (group_index < cfg->header->num_clauses) {
  			int count = 0;
  			size_t cursor = group_index;
  			while (cursor < cfg->header->num_clauses &&
  				   CLAUSE_START (&cfg->header->clauses [cursor]) == CLAUSE_START (&cfg->header->clauses [group_index]) &&
  				   CLAUSE_END (&cfg->header->clauses [cursor]) == CLAUSE_END (&cfg->header->clauses [group_index])) {
  				count++;
  				cursor++;
  			}
  
  			LLVMBasicBlockRef lpad_bb = emit_landing_pad (ctx, group_index, count);
  			intptr_t key = CLAUSE_END (&cfg->header->clauses [group_index]);
  			g_hash_table_insert (ctx->exc_meta, (gpointer)key, lpad_bb);
  
  			group_index = cursor;
  		}
  	}
  
  	for (bb_index = 0; bb_index < bblock_list->len; ++bb_index) {
  		bb = (MonoBasicBlock*)g_ptr_array_index (bblock_list, bb_index);
  
  		// Prune unreachable mono BBs.
  		if (!(bb == cfg->bb_entry || bb->in_count > 0))
  			continue;
  
  		process_bb (ctx, bb);
  		CHECK_FAILURE (ctx);
  	}
  	g_hash_table_destroy (ctx->exc_meta);
  
  	mono_memory_barrier ();
  
  	/* Add incoming phi values */
  	for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
  		GSList *l, *ins_list;
  
  		ins_list = bblocks [bb->block_num].phi_nodes;
  
  		for (l = ins_list; l; l = l->next) {
  			PhiNode *node = (PhiNode*)l->data;
  			MonoInst *phi = node->phi;
  			int sreg1 = node->sreg;
  			LLVMBasicBlockRef in_bb;
  
  			if (sreg1 == -1)
  				continue;
  
  			in_bb = get_end_bb (ctx, node->in_bb);
  
  			if (ctx->unreachable [node->in_bb->block_num])
  				continue;
  
  			if (!values [sreg1])
  				/* Can happen with values in EH clauses */
  				LLVM_FAILURE (ctx, "incoming phi sreg1");
  
  			if (phi->opcode == OP_VPHI) {
  				g_assert (LLVMTypeOf (ctx->addresses [sreg1]) == LLVMTypeOf (values [phi->dreg]));
  				LLVMAddIncoming (values [phi->dreg], &ctx->addresses [sreg1], &in_bb, 1);
  			} else {
  				if (LLVMTypeOf (values [sreg1]) != LLVMTypeOf (values [phi->dreg]))
  					// FIXME:
  					LLVM_FAILURE (ctx, "incoming phi arg type mismatch");
  				g_assert (LLVMTypeOf (values [sreg1]) == LLVMTypeOf (values [phi->dreg]));
  				LLVMAddIncoming (values [phi->dreg], &values [sreg1], &in_bb, 1);
  			}
  		}
  	}
  
  	/* Nullify empty phi instructions */
  	for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
  		GSList *l, *ins_list;
  
  		ins_list = bblocks [bb->block_num].phi_nodes;
  
  		for (l = ins_list; l; l = l->next) {
  			PhiNode *node = (PhiNode*)l->data;
  			MonoInst *phi = node->phi;
  			LLVMValueRef phi_ins = values [phi->dreg];
  
  			if (!phi_ins)
  				/* Already removed */
  				continue;
  
  			if (LLVMCountIncoming (phi_ins) == 0) {
  				mono_llvm_replace_uses_of (phi_ins, LLVMConstNull (LLVMTypeOf (phi_ins)));
  				LLVMInstructionEraseFromParent (phi_ins);
  				values [phi->dreg] = NULL;
  			}
  		}
  	}
  
  	/* Create the SWITCH statements for ENDFINALLY instructions */
  	for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
  		BBInfo *info = &bblocks [bb->block_num];
  		GSList *l;
  		for (l = info->endfinally_switch_ins_list; l; l = l->next) {
  			LLVMValueRef switch_ins = (LLVMValueRef)l->data;
  			GSList *bb_list = info->call_handler_return_bbs;
  
  			for (i = 0; i < g_slist_length (bb_list); ++i)
  				LLVMAddCase (switch_ins, LLVMConstInt (LLVMInt32Type (), i + 1, FALSE), g_slist_nth (bb_list, i)->data);
  		}
  	}
  
  	/* Initialize the method if needed */
  	if (cfg->compile_aot && ctx->llvm_only) {
  		// FIXME: Add more shared got entries
  		ctx->builder = create_builder (ctx);
  		LLVMPositionBuilderAtEnd (ctx->builder, ctx->init_bb);
  
  		// FIXME: beforefieldinit
  		if (ctx->has_got_access || mono_class_get_cctor (cfg->method->klass)) {
  			emit_init_method (ctx);
  		} else {
  			LLVMBuildBr (ctx->builder, ctx->inited_bb);
  		}
  	}
  
  	if (cfg->llvm_only) {
  		GHashTableIter iter;
  		MonoMethod *method;
  		GSList *callers, *l, *l2;
  
  		/*
  		 * Add the contents of ctx->method_to_callers to module->method_to_callers.
  		 * We can't do this earlier, as it contains llvm instructions which can be
  		 * freed if compilation fails.
  		 * FIXME: Get rid of this when all methods can be llvm compiled.
  		 */
  		g_hash_table_iter_init (&iter, ctx->method_to_callers);
  		while (g_hash_table_iter_next (&iter, (void**)&method, (void**)&callers)) {
  			for (l = callers; l; l = l->next) {
  				l2 = g_hash_table_lookup (ctx->module->method_to_callers, method);
  				l2 = g_slist_prepend (l2, l->data);
  				g_hash_table_insert (ctx->module->method_to_callers, method, l2);
  			}
  		}
  	}
  
  	if (cfg->verbose_level > 1)
  		mono_llvm_dump_value (method);
  
  	if (cfg->compile_aot && !cfg->llvm_only)
  		mark_as_used (ctx->module, method);
  
  	if (cfg->compile_aot) {
  		LLVMValueRef md_args [16];
  		LLVMValueRef md_node;
  		int method_index;
  
  		method_index = mono_aot_get_method_index (cfg->orig_method);
  		md_args [0] = LLVMMDString (method_name, strlen (method_name));
  		md_args [1] = LLVMConstInt (LLVMInt32Type (), method_index, FALSE);
  		md_node = LLVMMDNode (md_args, 2);
  		LLVMAddNamedMetadataOperand (lmodule, "mono.function_indexes", md_node);
  		//LLVMSetMetadata (method, md_kind, LLVMMDNode (&md_arg, 1));
  	}
  
  	if (cfg->compile_aot) {
  		/* Don't generate native code, keep the LLVM IR */
  		if (cfg->verbose_level)
  			printf ("%s emitted as %s\n", mono_method_full_name (cfg->method, TRUE), method_name);
  
  		int err = LLVMVerifyFunction(method,   LLVMPrintMessageAction);
  		g_assert (err == 0);
  	} else {
  		LLVMVerifyFunction(method, 0);
  		mono_llvm_optimize_method (ctx->module->mono_ee, method);
  
  		if (cfg->verbose_level > 1)
  			mono_llvm_dump_value (method);
  
  		cfg->native_code = LLVMGetPointerToGlobal (ctx->module->ee, method);
  
  		/* Set by emit_cb */
  		g_assert (cfg->code_len);
  
  		/* FIXME: Free the LLVM IL for the function */
  	}
  
  	if (ctx->module->method_to_lmethod)
  		g_hash_table_insert (ctx->module->method_to_lmethod, cfg->method, method);
  	if (ctx->module->idx_to_lmethod)
  		g_hash_table_insert (ctx->module->idx_to_lmethod, GINT_TO_POINTER (cfg->method_index), method);
  
  	if (ctx->llvm_only && cfg->orig_method->klass->valuetype && !(cfg->orig_method->flags & METHOD_ATTRIBUTE_STATIC))
  		emit_unbox_tramp (ctx, method_name, method_type, method, cfg->method_index);
  
  	goto CLEANUP;
  
   FAILURE:
  
  	if (method) {
  		/* Need to add unused phi nodes as they can be referenced by other values */
  		LLVMBasicBlockRef phi_bb = LLVMAppendBasicBlock (method, "PHI_BB");
  		LLVMBuilderRef builder;
  
  		builder = create_builder (ctx);
  		LLVMPositionBuilderAtEnd (builder, phi_bb);
  
  		for (i = 0; i < phi_values->len; ++i) {
  			LLVMValueRef v = g_ptr_array_index (phi_values, i);
  			if (LLVMGetInstructionParent (v) == NULL)
  				LLVMInsertIntoBuilder (builder, v);
  		}
  		
  		LLVMDeleteFunction (method);
  	}
  
   CLEANUP:
  	g_free (values);
  	g_free (ctx->addresses);
  	g_free (ctx->vreg_types);
  	g_free (ctx->vreg_cli_types);
  	g_free (ctx->is_dead);
  	g_free (ctx->unreachable);
  	g_ptr_array_free (phi_values, TRUE);
  	g_free (ctx->bblocks);
  	g_hash_table_destroy (ctx->region_to_handler);
  	g_hash_table_destroy (ctx->clause_to_handler);
  	g_free (method_name);
  	g_ptr_array_free (bblock_list, TRUE);
  
  	for (l = ctx->builders; l; l = l->next) {
  		LLVMBuilderRef builder = l->data;
  		LLVMDisposeBuilder (builder);
  	}
  
  	g_free (ctx);
  
  	mono_native_tls_set_value (current_cfg_tls_id, NULL);
  
  	mono_loader_unlock ();
  }
  
  /*
   * mono_llvm_emit_call:
   *
   *   Same as mono_arch_emit_call () for LLVM.
   */
  void
  mono_llvm_emit_call (MonoCompile *cfg, MonoCallInst *call)
  {
  	MonoInst *in;
  	MonoMethodSignature *sig;
  	int i, n, stack_size;
  	LLVMArgInfo *ainfo;
  
  	stack_size = 0;
  
  	sig = call->signature;
  	n = sig->param_count + sig->hasthis;
  
  	call->cinfo = get_llvm_call_info (cfg, sig);
  
  	if (cfg->disable_llvm)
  		return;
  
  	if (sig->call_convention == MONO_CALL_VARARG) {
  		cfg->exception_message = g_strdup ("varargs");
  		cfg->disable_llvm = TRUE;
  	}
  
  	for (i = 0; i < n; ++i) {
  		MonoInst *ins;
  
  		ainfo = call->cinfo->args + i;
  
  		in = call->args [i];
  			
  		/* Simply remember the arguments */
  		switch (ainfo->storage) {
  		case LLVMArgNormal: {
  			MonoType *t = (sig->hasthis && i == 0) ? &mono_get_intptr_class ()->byval_arg : ainfo->type;
  			int opcode;
  
  			opcode = mono_type_to_regmove (cfg, t);
  			if (opcode == OP_FMOVE) {
  				MONO_INST_NEW (cfg, ins, OP_FMOVE);
  				ins->dreg = mono_alloc_freg (cfg);
  			} else if (opcode == OP_LMOVE) {
  				MONO_INST_NEW (cfg, ins, OP_LMOVE);
  				ins->dreg = mono_alloc_lreg (cfg);
  			} else if (opcode == OP_RMOVE) {
  				MONO_INST_NEW (cfg, ins, OP_RMOVE);
  				ins->dreg = mono_alloc_freg (cfg);
  			} else {
  				MONO_INST_NEW (cfg, ins, OP_MOVE);
  				ins->dreg = mono_alloc_ireg (cfg);
  			}
  			ins->sreg1 = in->dreg;
  			break;
  		}
  		case LLVMArgVtypeByVal:
  		case LLVMArgVtypeByRef:
  		case LLVMArgVtypeInReg:
  		case LLVMArgVtypeAsScalar:
  		case LLVMArgScalarByRef:
  		case LLVMArgAsIArgs:
  		case LLVMArgAsFpArgs:
  			MONO_INST_NEW (cfg, ins, OP_LLVM_OUTARG_VT);
  			ins->dreg = mono_alloc_ireg (cfg);
  			ins->sreg1 = in->dreg;
  			ins->inst_p0 = mono_mempool_alloc0 (cfg->mempool, sizeof (LLVMArgInfo));
  			memcpy (ins->inst_p0, ainfo, sizeof (LLVMArgInfo));
  			ins->inst_vtype = ainfo->type;
  			ins->klass = mono_class_from_mono_type (ainfo->type);
  			break;
  		default:
  			cfg->exception_message = g_strdup ("ainfo->storage");
  			cfg->disable_llvm = TRUE;
  			return;
  		}
  
  		if (!cfg->disable_llvm) {
  			MONO_ADD_INS (cfg->cbb, ins);
  			mono_call_inst_add_outarg_reg (cfg, call, ins->dreg, 0, FALSE);
  		}
  	}
  }
  
  static unsigned char*
  alloc_cb (LLVMValueRef function, int size)
  {
  	MonoCompile *cfg;
  
  	cfg = mono_native_tls_get_value (current_cfg_tls_id);
  
  	if (cfg) {
  		// FIXME: dynamic
  		return mono_domain_code_reserve (cfg->domain, size);
  	} else {
  		return mono_domain_code_reserve (mono_domain_get (), size);
  	}
  }
  
  static void
  emitted_cb (LLVMValueRef function, void *start, void *end)
  {
  	MonoCompile *cfg;
  
  	cfg = mono_native_tls_get_value (current_cfg_tls_id);
  	g_assert (cfg);
  	cfg->code_len = (guint8*)end - (guint8*)start;
  }
  
  static void
  exception_cb (void *data)
  {
  	MonoCompile *cfg;
  	MonoJitExceptionInfo *ei;
  	guint32 ei_len, i, j, nested_len, nindex;
  	gpointer *type_info;
  	int this_reg, this_offset;
  
  	cfg = mono_native_tls_get_value (current_cfg_tls_id);
  	g_assert (cfg);
  
  	/*
  	 * data points to a DWARF FDE structure, convert it to our unwind format and
  	 * save it.
  	 * 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, &type_info, &this_reg, &this_offset);
  	if (cfg->verbose_level > 1)
  		mono_print_unwind_info (cfg->encoded_unwind_ops, cfg->encoded_unwind_ops_len);
  
  	/* Count nested clauses */
  	nested_len = 0;
  	for (i = 0; i < ei_len; ++i) {
  		gint32 cindex1 = *(gint32*)type_info [i];
  		MonoExceptionClause *clause1 = &cfg->header->clauses [cindex1];
  
  		for (j = 0; j < cfg->header->num_clauses; ++j) {
  			int cindex2 = j;
  			MonoExceptionClause *clause2 = &cfg->header->clauses [cindex2];
  
  			if (cindex1 != cindex2 && clause1->try_offset >= clause2->try_offset && clause1->handler_offset <= clause2->handler_offset) {
  				nested_len ++;
  			}
  		}
  	}
  
  	cfg->llvm_ex_info = mono_mempool_alloc0 (cfg->mempool, (ei_len + nested_len) * sizeof (MonoJitExceptionInfo));
  	cfg->llvm_ex_info_len = ei_len + nested_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) {
  		gint32 clause_index = *(gint32*)type_info [i];
  		MonoExceptionClause *clause = &cfg->header->clauses [clause_index];
  
  		cfg->llvm_ex_info [i].flags = clause->flags;
  		cfg->llvm_ex_info [i].data.catch_class = clause->data.catch_class;
  		cfg->llvm_ex_info [i].clause_index = clause_index;
  	}
  
  	/*
  	 * For nested clauses, the LLVM produced exception info associates the try interval with
  	 * the innermost handler, while mono expects it to be associated with all nesting clauses.
  	 * So add new clauses which use the IL info (catch class etc.) from the nesting clause,
  	 * and everything else from the nested clause.
  	 */
  	nindex = ei_len;
  	for (i = 0; i < ei_len; ++i) {
  		gint32 cindex1 = *(gint32*)type_info [i];
  		MonoExceptionClause *clause1 = &cfg->header->clauses [cindex1];
  
  		for (j = 0; j < cfg->header->num_clauses; ++j) {
  			int cindex2 = j;
  			MonoExceptionClause *clause2 = &cfg->header->clauses [cindex2];
  			MonoJitExceptionInfo *nesting_ei, *nested_ei;
  
  			if (cindex1 != cindex2 && clause1->try_offset >= clause2->try_offset && clause1->handler_offset <= clause2->handler_offset) {
  				/* clause1 is the nested clause */
  				nested_ei = &cfg->llvm_ex_info [i];
  				nesting_ei = &cfg->llvm_ex_info [nindex];
  				nindex ++;
  
  				memcpy (nesting_ei, nested_ei, sizeof (MonoJitExceptionInfo));
  
  				nesting_ei->flags = clause2->flags;
  				nesting_ei->data.catch_class = clause2->data.catch_class;
  				nesting_ei->clause_index = cindex2;
  			}
  		}
  	}
  	g_assert (nindex == ei_len + nested_len);
  	cfg->llvm_this_reg = this_reg;
  	cfg->llvm_this_offset = this_offset;
  
  	/* type_info [i] is cfg mempool allocated, no need to free it */
  
  	g_free (ei);
  	g_free (type_info);
  }
  
  static char*
  dlsym_cb (const char *name, void **symbol)
  {
  	MonoDl *current;
  	char *err;
  
  	err = NULL;
  	if (!strcmp (name, "__bzero")) {
  		*symbol = (void*)bzero;
  	} else {
  		current = mono_dl_open (NULL, 0, NULL);
  		g_assert (current);
  
  		err = mono_dl_symbol (current, name, symbol);
  
  		mono_dl_close (current);
  	}
  #ifdef MONO_ARCH_HAVE_CREATE_LLVM_NATIVE_THUNK
  	*symbol = (char*)mono_arch_create_llvm_native_thunk (mono_domain_get (), (guint8*)(*symbol));
  #endif
  	return err;
  }
  
  static inline void
  AddFunc (LLVMModuleRef module, const char *name, LLVMTypeRef ret_type, LLVMTypeRef *param_types, int nparams)
  {
  	LLVMAddFunction (module, name, LLVMFunctionType (ret_type, param_types, nparams, FALSE));
  }
  
  static inline void
  AddFunc2 (LLVMModuleRef module, const char *name, LLVMTypeRef ret_type, LLVMTypeRef param_type1, LLVMTypeRef param_type2)
  {
  	LLVMTypeRef param_types [4];
  
  	param_types [0] = param_type1;
  	param_types [1] = param_type2;
  
  	AddFunc (module, name, ret_type, param_types, 2);
  }
  
  static void
  add_intrinsics (LLVMModuleRef module)
  {
  	/* Emit declarations of instrinsics */
  	/*
  	 * It would be nicer to emit only the intrinsics actually used, but LLVM's Module
  	 * type doesn't seem to do any locking.
  	 */
  	{
  		LLVMTypeRef params [] = { LLVMPointerType (LLVMInt8Type (), 0), LLVMInt8Type (), LLVMInt32Type (), LLVMInt32Type (), LLVMInt1Type () };
  
  		memset_param_count = 5;
  		memset_func_name = "llvm.memset.p0i8.i32";
  
  		AddFunc (module, memset_func_name, LLVMVoidType (), params, memset_param_count);
  	}
  
  	{
  		LLVMTypeRef params [] = { LLVMPointerType (LLVMInt8Type (), 0), LLVMPointerType (LLVMInt8Type (), 0), LLVMInt32Type (), LLVMInt32Type (), LLVMInt1Type () };
  
  		memcpy_param_count = 5;
  		memcpy_func_name = "llvm.memcpy.p0i8.p0i8.i32";
  
  		AddFunc (module, memcpy_func_name, LLVMVoidType (), params, memcpy_param_count);
  	}
  
  	{
  		LLVMTypeRef params [] = { LLVMDoubleType () };
  
  		AddFunc (module, "llvm.sin.f64", LLVMDoubleType (), params, 1);
  		AddFunc (module, "llvm.cos.f64", LLVMDoubleType (), params, 1);
  		AddFunc (module, "llvm.sqrt.f64", LLVMDoubleType (), params, 1);
  
  		/* This isn't an intrinsic, instead llvm seems to special case it by name */
  		AddFunc (module, "fabs", LLVMDoubleType (), params, 1);
  	}
  
  	{
  		LLVMTypeRef ovf_res_i32 [] = { LLVMInt32Type (), LLVMInt1Type () };
  		LLVMTypeRef params [] = { LLVMInt32Type (), LLVMInt32Type () };
  		LLVMTypeRef ret_type = LLVMStructType (ovf_res_i32, 2, FALSE);
  
  		AddFunc (module, "llvm.sadd.with.overflow.i32", ret_type, params, 2);
  		AddFunc (module, "llvm.sadd.with.overflow.i32", ret_type, params, 2);
  		AddFunc (module, "llvm.uadd.with.overflow.i32", ret_type, params, 2);
  		AddFunc (module, "llvm.ssub.with.overflow.i32", ret_type, params, 2);
  		AddFunc (module, "llvm.usub.with.overflow.i32", ret_type, params, 2);
  		AddFunc (module, "llvm.smul.with.overflow.i32", ret_type, params, 2);
  		AddFunc (module, "llvm.umul.with.overflow.i32", ret_type, params, 2);
  	}
  
  	{
  		LLVMTypeRef ovf_res_i64 [] = { LLVMInt64Type (), LLVMInt1Type () };
  		LLVMTypeRef params [] = { LLVMInt64Type (), LLVMInt64Type () };
  		LLVMTypeRef ret_type = LLVMStructType (ovf_res_i64, 2, FALSE);
  
  		AddFunc (module, "llvm.sadd.with.overflow.i64", ret_type, params, 2);
  		AddFunc (module, "llvm.uadd.with.overflow.i64", ret_type, params, 2);
  		AddFunc (module, "llvm.ssub.with.overflow.i64", ret_type, params, 2);
  		AddFunc (module, "llvm.usub.with.overflow.i64", ret_type, params, 2);
  		AddFunc (module, "llvm.smul.with.overflow.i64", ret_type, params, 2);
  		AddFunc (module, "llvm.umul.with.overflow.i64", ret_type, params, 2);
  	}
  
  	AddFunc2 (module, "llvm.expect.i8", LLVMInt8Type (), LLVMInt8Type (), LLVMInt8Type ());
  
  	/* EH intrinsics */
  	{
  		AddFunc (module, "mono_personality", LLVMVoidType (), NULL, 0);
  
  		AddFunc (module, "llvm_resume_unwind_trampoline", LLVMVoidType (), NULL, 0);
  	}
  
  	/* SSE intrinsics */
  #if defined(TARGET_X86) || defined(TARGET_AMD64)
  	{
  		LLVMTypeRef ret_type, arg_types [16];
  
  		/* Binary ops */
  		ret_type = type_to_simd_type (MONO_TYPE_I4);
  		arg_types [0] = ret_type;
  		arg_types [1] = ret_type;
  		AddFunc (module, "llvm.x86.sse41.pminud", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse41.pmaxud", ret_type, arg_types, 2);
  
  		ret_type = type_to_simd_type (MONO_TYPE_I2);
  		arg_types [0] = ret_type;
  		arg_types [1] = ret_type;
  		AddFunc (module, "llvm.x86.sse41.pminuw", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse2.pmins.w", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse41.pmaxuw", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse2.padds.w", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse2.psubs.w", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse2.paddus.w", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse2.psubus.w", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse2.pavg.w", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse2.pmulh.w", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse2.pmulhu.w", ret_type, arg_types, 2);
  
  		ret_type = type_to_simd_type (MONO_TYPE_I1);
  		arg_types [0] = ret_type;
  		arg_types [1] = ret_type;
  		AddFunc (module, "llvm.x86.sse2.pminu.b", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse2.pmaxu.b", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse2.padds.b", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse2.psubs.b", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse2.paddus.b", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse2.psubus.b", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse2.pavg.b", ret_type, arg_types, 2);
  
  		ret_type = type_to_simd_type (MONO_TYPE_R8);
  		arg_types [0] = ret_type;
  		arg_types [1] = ret_type;
  		AddFunc (module, "llvm.x86.sse2.min.pd", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse2.max.pd", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse3.hadd.pd", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse3.hsub.pd", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse3.addsub.pd", ret_type, arg_types, 2);
  
  		ret_type = type_to_simd_type (MONO_TYPE_R4);
  		arg_types [0] = ret_type;
  		arg_types [1] = ret_type;
  		AddFunc (module, "llvm.x86.sse.min.ps", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse.max.ps", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse3.hadd.ps", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse3.hsub.ps", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse3.addsub.ps", ret_type, arg_types, 2);
  
  		/* pack */
  		ret_type = type_to_simd_type (MONO_TYPE_I1);
  		arg_types [0] = type_to_simd_type (MONO_TYPE_I2);
  		arg_types [1] = type_to_simd_type (MONO_TYPE_I2);
  		AddFunc (module, "llvm.x86.sse2.packsswb.128", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse2.packuswb.128", ret_type, arg_types, 2);
  		ret_type = type_to_simd_type (MONO_TYPE_I2);
  		arg_types [0] = type_to_simd_type (MONO_TYPE_I4);
  		arg_types [1] = type_to_simd_type (MONO_TYPE_I4);
  		AddFunc (module, "llvm.x86.sse2.packssdw.128", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse41.packusdw", ret_type, arg_types, 2);
  
  		/* cmp pd/ps */
  		ret_type = type_to_simd_type (MONO_TYPE_R8);
  		arg_types [0] = ret_type;
  		arg_types [1] = ret_type;
  		arg_types [2] = LLVMInt8Type ();
  		AddFunc (module, "llvm.x86.sse2.cmp.pd", ret_type, arg_types, 3);
  		ret_type = type_to_simd_type (MONO_TYPE_R4);
  		arg_types [0] = ret_type;
  		arg_types [1] = ret_type;
  		arg_types [2] = LLVMInt8Type ();
  		AddFunc (module, "llvm.x86.sse.cmp.ps", ret_type, arg_types, 3);
  
  		/* Conversion ops */
  		ret_type = type_to_simd_type (MONO_TYPE_R8);
  		arg_types [0] = type_to_simd_type (MONO_TYPE_I4);
  		AddFunc (module, "llvm.x86.sse2.cvtdq2pd", ret_type, arg_types, 1);
  		ret_type = type_to_simd_type (MONO_TYPE_R4);
  		arg_types [0] = type_to_simd_type (MONO_TYPE_I4);
  		AddFunc (module, "llvm.x86.sse2.cvtdq2ps", ret_type, arg_types, 1);
  		ret_type = type_to_simd_type (MONO_TYPE_I4);
  		arg_types [0] = type_to_simd_type (MONO_TYPE_R8);
  		AddFunc (module, "llvm.x86.sse2.cvtpd2dq", ret_type, arg_types, 1);
  		ret_type = type_to_simd_type (MONO_TYPE_I4);
  		arg_types [0] = type_to_simd_type (MONO_TYPE_R4);
  		AddFunc (module, "llvm.x86.sse2.cvtps2dq", ret_type, arg_types, 1);
  		ret_type = type_to_simd_type (MONO_TYPE_R4);
  		arg_types [0] = type_to_simd_type (MONO_TYPE_R8);
  		AddFunc (module, "llvm.x86.sse2.cvtpd2ps", ret_type, arg_types, 1);
  		ret_type = type_to_simd_type (MONO_TYPE_R8);
  		arg_types [0] = type_to_simd_type (MONO_TYPE_R4);
  		AddFunc (module, "llvm.x86.sse2.cvtps2pd", ret_type, arg_types, 1);
  
  		ret_type = type_to_simd_type (MONO_TYPE_I4);
  		arg_types [0] = type_to_simd_type (MONO_TYPE_R8);
  		AddFunc (module, "llvm.x86.sse2.cvttpd2dq", ret_type, arg_types, 1);
  		ret_type = type_to_simd_type (MONO_TYPE_I4);
  		arg_types [0] = type_to_simd_type (MONO_TYPE_R4);
  		AddFunc (module, "llvm.x86.sse2.cvttps2dq", ret_type, arg_types, 1);
  
  		/* Unary ops */
  		ret_type = type_to_simd_type (MONO_TYPE_R8);
  		arg_types [0] = ret_type;
  		AddFunc (module, "llvm.x86.sse2.sqrt.pd", ret_type, arg_types, 1);
  		ret_type = type_to_simd_type (MONO_TYPE_R4);
  		arg_types [0] = ret_type;
  		AddFunc (module, "llvm.x86.sse.sqrt.ps", ret_type, arg_types, 1);
  		ret_type = type_to_simd_type (MONO_TYPE_R4);
  		arg_types [0] = ret_type;
  		AddFunc (module, "llvm.x86.sse.rsqrt.ps", ret_type, arg_types, 1);
  		ret_type = type_to_simd_type (MONO_TYPE_R4);
  		arg_types [0] = ret_type;
  		AddFunc (module, "llvm.x86.sse.rcp.ps", ret_type, arg_types, 1);
  
  		/* shifts */
  		ret_type = type_to_simd_type (MONO_TYPE_I2);
  		arg_types [0] = ret_type;
  		arg_types [1] = LLVMInt32Type ();
  		AddFunc (module, "llvm.x86.sse2.psrli.w", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse2.psrai.w", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse2.pslli.w", ret_type, arg_types, 2);
  		ret_type = type_to_simd_type (MONO_TYPE_I4);
  		arg_types [0] = ret_type;
  		arg_types [1] = LLVMInt32Type ();
  		AddFunc (module, "llvm.x86.sse2.psrli.d", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse2.psrai.d", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse2.pslli.d", ret_type, arg_types, 2);
  		ret_type = type_to_simd_type (MONO_TYPE_I8);
  		arg_types [0] = ret_type;
  		arg_types [1] = LLVMInt32Type ();
  		AddFunc (module, "llvm.x86.sse2.psrli.q", ret_type, arg_types, 2);
  		AddFunc (module, "llvm.x86.sse2.pslli.q", ret_type, arg_types, 2);
  
  		/* pmovmskb */
  		ret_type = LLVMInt32Type ();
  		arg_types [0] = type_to_simd_type (MONO_TYPE_I1);
  		AddFunc (module, "llvm.x86.sse2.pmovmskb.128", ret_type, arg_types, 1);
  	}
  
  	AddFunc (module, "llvm.x86.sse2.pause", LLVMVoidType (), NULL, 0);
  #endif
  
  	/* Load/Store intrinsics */
  	{
  		LLVMTypeRef arg_types [5];
  		int i;
  		char name [128];
  
  		for (i = 1; i <= 8; i *= 2) {
  			arg_types [0] = LLVMPointerType (LLVMIntType (i * 8), 0);
  			arg_types [1] = LLVMInt32Type ();
  			arg_types [2] = LLVMInt1Type ();
  			arg_types [3] = LLVMInt32Type ();
  			sprintf (name, "llvm.mono.load.i%d.p0i%d", i * 8, i * 8);
  			AddFunc (module, name, LLVMIntType (i * 8), arg_types, 4);
  
  			arg_types [0] = LLVMIntType (i * 8);
  			arg_types [1] = LLVMPointerType (LLVMIntType (i * 8), 0);
  			arg_types [2] = LLVMInt32Type ();
  			arg_types [3] = LLVMInt1Type ();
  			arg_types [4] = LLVMInt32Type ();
  			sprintf (name, "llvm.mono.store.i%d.p0i%d", i * 8, i * 8);
  			AddFunc (module, name, LLVMVoidType (), arg_types, 5);
  		}
  	}
  }
  
  static void
  add_types (MonoLLVMModule *module)
  {
  	module->ptr_type = LLVMPointerType (sizeof (gpointer) == 8 ? LLVMInt64Type () : LLVMInt32Type (), 0);
  }
  
  void
  mono_llvm_init (void)
  {
  	mono_native_tls_alloc (&current_cfg_tls_id, NULL);
  }
  
  static void
  init_jit_module (MonoDomain *domain)
  {
  	MonoJitICallInfo *info;
  	MonoJitDomainInfo *dinfo;
  	MonoLLVMModule *module;
  	char *name;
  
  	dinfo = domain_jit_info (domain);
  	if (dinfo->llvm_module)
  		return;
  
  	mono_loader_lock ();
  
  	if (dinfo->llvm_module) {
  		mono_loader_unlock ();
  		return;
  	}
  
  	module = g_new0 (MonoLLVMModule, 1);
  
  	name = g_strdup_printf ("mono-%s", domain->friendly_name);
  	module->lmodule = LLVMModuleCreateWithName (name);
  	module->context = LLVMGetGlobalContext ();
  
  	module->mono_ee = mono_llvm_create_ee (LLVMCreateModuleProviderForExistingModule (module->lmodule), alloc_cb, emitted_cb, exception_cb, dlsym_cb, &module->ee);
  
  	add_intrinsics (module->lmodule);
  	add_types (module);
  
  	module->llvm_types = g_hash_table_new (NULL, NULL);
  
  	info = mono_find_jit_icall_by_name ("llvm_resume_unwind_trampoline");
  	g_assert (info);
  	LLVMAddGlobalMapping (module->ee, LLVMGetNamedFunction (module->lmodule, "llvm_resume_unwind_trampoline"), (void*)info->func);
  
  	mono_memory_barrier ();
  
  	dinfo->llvm_module = module;
  
  	mono_loader_unlock ();
  }
  
  void
  mono_llvm_cleanup (void)
  {
  	MonoLLVMModule *module = &aot_module;
  
  	if (module->lmodule)
  		LLVMDisposeModule (module->lmodule);
  
  	if (module->context)
  		LLVMContextDispose (module->context);
  }
  
  void
  mono_llvm_free_domain_info (MonoDomain *domain)
  {
  	MonoJitDomainInfo *info = domain_jit_info (domain);
  	MonoLLVMModule *module = info->llvm_module;
  	int i;
  
  	if (!module)
  		return;
  
  	if (module->llvm_types)
  		g_hash_table_destroy (module->llvm_types);
  
  	mono_llvm_dispose_ee (module->mono_ee);
  
  	if (module->bb_names) {
  		for (i = 0; i < module->bb_names_len; ++i)
  			g_free (module->bb_names [i]);
  		g_free (module->bb_names);
  	}
  	//LLVMDisposeModule (module->module);
  
  	g_free (module);
  
  	info->llvm_module = NULL;
  }
  
  void
  mono_llvm_create_aot_module (MonoAssembly *assembly, const char *global_prefix, gboolean emit_dwarf, gboolean static_link, gboolean llvm_only)
  {
  	MonoLLVMModule *module = &aot_module;
  
  	/* Delete previous module */
  	if (module->plt_entries)
  		g_hash_table_destroy (module->plt_entries);
  	if (module->lmodule)
  		LLVMDisposeModule (module->lmodule);
  
  	memset (module, 0, sizeof (aot_module));
  
  	module->lmodule = LLVMModuleCreateWithName ("aot");
  	module->assembly = assembly;
  	module->global_prefix = g_strdup (global_prefix);
  	module->got_symbol = g_strdup_printf ("%s_llvm_got", global_prefix);
  	module->eh_frame_symbol = g_strdup_printf ("%s_eh_frame", global_prefix);
  	module->get_method_symbol = g_strdup_printf ("%s_get_method", global_prefix);
  	module->get_unbox_tramp_symbol = g_strdup_printf ("%s_get_unbox_tramp", global_prefix);
  	module->external_symbols = TRUE;
  	module->emit_dwarf = emit_dwarf;
  	module->static_link = static_link;
  	module->llvm_only = llvm_only;
  	/* The first few entries are reserved */
  	module->max_got_offset = 16;
  	module->context = LLVMContextCreate ();
  
  	add_intrinsics (module->lmodule);
  	add_types (module);
  
  	/* Add GOT */
  	/*
  	 * We couldn't compute the type of the LLVM global representing the got because
  	 * its size is only known after all the methods have been emitted. So create
  	 * a dummy variable, and replace all uses it with the real got variable when
  	 * its size is known in mono_llvm_emit_aot_module ().
  	 */
  	{
  		LLVMTypeRef got_type = LLVMArrayType (module->ptr_type, 0);
  
  		module->got_var = LLVMAddGlobal (module->lmodule, got_type, "mono_dummy_got");
  		LLVMSetInitializer (module->got_var, LLVMConstNull (got_type));
  	}
  
  	/* Add initialization array */
  	if (llvm_only) {
  		LLVMTypeRef inited_type = LLVMArrayType (LLVMInt8Type (), 0);
  
  		module->inited_var = LLVMAddGlobal (aot_module.lmodule, inited_type, "mono_inited_tmp");
  		LLVMSetInitializer (module->inited_var, LLVMConstNull (inited_type));
  	}
  
  	if (llvm_only)
  		emit_init_icall_wrappers (module);
  
  	emit_llvm_code_start (module);
  
  	/* Add a dummy personality function */
  	if (!use_debug_personality) {
  		LLVMValueRef personality = LLVMAddFunction (module->lmodule, default_personality_name, LLVMFunctionType (LLVMInt32Type (), NULL, 0, TRUE));
  		LLVMSetLinkage (personality, LLVMExternalLinkage);
  		mark_as_used (module, personality);
  	}
  
  	/* Add a reference to the c++ exception we throw/catch */
  	{
  		LLVMTypeRef exc = LLVMPointerType (LLVMInt8Type (), 0);
  		module->sentinel_exception = LLVMAddGlobal (module->lmodule, exc, "_ZTIPi");
  		LLVMSetLinkage (module->sentinel_exception, LLVMExternalLinkage);
  		mono_llvm_set_is_constant (module->sentinel_exception);
  	}
  
  	module->llvm_types = g_hash_table_new (NULL, NULL);
  	module->plt_entries = g_hash_table_new (g_str_hash, g_str_equal);
  	module->plt_entries_ji = g_hash_table_new (NULL, NULL);
  	module->direct_callables = g_hash_table_new (g_str_hash, g_str_equal);
  	module->method_to_lmethod = g_hash_table_new (NULL, NULL);
  	module->idx_to_lmethod = g_hash_table_new (NULL, NULL);
  	module->idx_to_unbox_tramp = g_hash_table_new (NULL, NULL);
  	module->method_to_callers = g_hash_table_new (NULL, NULL);
  }
  
  static LLVMValueRef
  llvm_array_from_uints (LLVMTypeRef el_type, guint32 *values, int nvalues)
  {
  	int i;
  	LLVMValueRef res, *vals;
  
  	vals = g_new0 (LLVMValueRef, nvalues);
  	for (i = 0; i < nvalues; ++i)
  		vals [i] = LLVMConstInt (LLVMInt32Type (), values [i], FALSE);
  	res = LLVMConstArray (LLVMInt32Type (), vals, nvalues);
  	g_free (vals);
  	return res;
  }
  
  /*
   * mono_llvm_emit_aot_file_info:
   *
   *   Emit the MonoAotFileInfo structure.
   * Same as emit_aot_file_info () in aot-compiler.c.
   */
  void
  mono_llvm_emit_aot_file_info (MonoAotFileInfo *info, gboolean has_jitted_code)
  {
  	MonoLLVMModule *module = &aot_module;
  
  	/* Save these for later */
  	memcpy (&module->aot_info, info, sizeof (MonoAotFileInfo));
  	module->has_jitted_code = has_jitted_code;
  }
  
  /*
   * mono_llvm_emit_aot_data:
   *
   *   Emit the binary data DATA pointed to by symbol SYMBOL.
   */
  void
  mono_llvm_emit_aot_data (const char *symbol, guint8 *data, int data_len)
  {
  	MonoLLVMModule *module = &aot_module;
  	LLVMTypeRef type;
  	LLVMValueRef d;
  
  	type = LLVMArrayType (LLVMInt8Type (), data_len);
  	d = LLVMAddGlobal (module->lmodule, type, symbol);
  	LLVMSetVisibility (d, LLVMHiddenVisibility);
  	LLVMSetLinkage (d, LLVMInternalLinkage);
  	LLVMSetInitializer (d, mono_llvm_create_constant_data_array (data, data_len));
  	mono_llvm_set_is_constant (d);
  }
  
  /* Add a reference to a global defined in JITted code */
  static LLVMValueRef
  AddJitGlobal (MonoLLVMModule *module, LLVMTypeRef type, const char *name)
  {
  	char *s;
  	LLVMValueRef v;
  
  	s = g_strdup_printf ("%s%s", module->global_prefix, name);
  	v = LLVMAddGlobal (module->lmodule, LLVMInt8Type (), s);
  	g_free (s);
  	return v;
  }
  
  static void
  emit_aot_file_info (MonoLLVMModule *module)
  {
  	LLVMTypeRef file_info_type;
  	LLVMTypeRef *eltypes, eltype;
  	LLVMValueRef info_var;
  	LLVMValueRef *fields;
  	int i, nfields, tindex;
  	MonoAotFileInfo *info;
  	LLVMModuleRef lmodule = module->lmodule;
  
  	info = &module->aot_info;
  
  	/* Create an LLVM type to represent MonoAotFileInfo */
  	nfields = 2 + MONO_AOT_FILE_INFO_NUM_SYMBOLS + 14 + 4;
  	eltypes = g_new (LLVMTypeRef, nfields);
  	tindex = 0;
  	eltypes [tindex ++] = LLVMInt32Type ();
  	eltypes [tindex ++] = LLVMInt32Type ();
  	/* Symbols */
  	for (i = 0; i < MONO_AOT_FILE_INFO_NUM_SYMBOLS; ++i)
  		eltypes [tindex ++] = LLVMPointerType (LLVMInt8Type (), 0);
  	/* Scalars */
  	for (i = 0; i < 14; ++i)
  		eltypes [tindex ++] = LLVMInt32Type ();
  	/* Arrays */
  	for (i = 0; i < 4; ++i)
  		eltypes [tindex ++] = LLVMArrayType (LLVMInt32Type (), MONO_AOT_TRAMP_NUM);
  	g_assert (tindex == nfields);
  	file_info_type = LLVMStructCreateNamed (module->context, "MonoAotFileInfo");
  	LLVMStructSetBody (file_info_type, eltypes, nfields, FALSE);
  
  	info_var = LLVMAddGlobal (lmodule, file_info_type, "mono_aot_file_info");
  	if (module->static_link) {
  		LLVMSetVisibility (info_var, LLVMHiddenVisibility);
  		LLVMSetLinkage (info_var, LLVMInternalLinkage);
  	}
  	fields = g_new (LLVMValueRef, nfields);
  	tindex = 0;
  	fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->version, FALSE);
  	fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->dummy, FALSE);
  
  	/* Symbols */
  	/*
  	 * We use LLVMGetNamedGlobal () for symbol which are defined in LLVM code, and LLVMAddGlobal ()
  	 * for symbols defined in the .s file emitted by the aot compiler.
  	 */
  	eltype = eltypes [tindex];
  	if (module->llvm_only)
  		fields [tindex ++] = LLVMConstNull (eltype);
  	else
  		fields [tindex ++] = AddJitGlobal (module, eltype, "jit_got");
  	fields [tindex ++] = module->got_var;
  	/* llc defines this directly */
  	if (!module->llvm_only) {
  		fields [tindex ++] = LLVMAddGlobal (lmodule, eltype, module->eh_frame_symbol);
  		fields [tindex ++] = LLVMConstNull (eltype);
  		fields [tindex ++] = LLVMConstNull (eltype);
  	} else {
  		fields [tindex ++] = LLVMConstNull (eltype);
  		fields [tindex ++] = module->get_method;
  		fields [tindex ++] = module->get_unbox_tramp;
  	}
  	if (module->has_jitted_code) {
  		fields [tindex ++] = AddJitGlobal (module, eltype, "jit_code_start");
  		fields [tindex ++] = AddJitGlobal (module, eltype, "jit_code_end");
  	} else {
  		fields [tindex ++] = LLVMConstNull (eltype);
  		fields [tindex ++] = LLVMConstNull (eltype);
  	}
  	if (!module->llvm_only)
  		fields [tindex ++] = AddJitGlobal (module, eltype, "method_addresses");
  	else
  		fields [tindex ++] = LLVMConstNull (eltype);
  	fields [tindex ++] = LLVMGetNamedGlobal (lmodule, "blob");
  	fields [tindex ++] = LLVMGetNamedGlobal (lmodule, "class_name_table");
  	fields [tindex ++] = LLVMGetNamedGlobal (lmodule, "class_info_offsets");
  	fields [tindex ++] = LLVMGetNamedGlobal (lmodule, "method_info_offsets");
  	fields [tindex ++] = LLVMGetNamedGlobal (lmodule, "ex_info_offsets");
  	fields [tindex ++] = LLVMGetNamedGlobal (lmodule, "extra_method_info_offsets");
  	fields [tindex ++] = LLVMGetNamedGlobal (lmodule, "extra_method_table");
  	fields [tindex ++] = LLVMGetNamedGlobal (lmodule, "got_info_offsets");
  	fields [tindex ++] = LLVMGetNamedGlobal (lmodule, "llvm_got_info_offsets");
  	/* Not needed (mem_end) */
  	fields [tindex ++] = LLVMConstNull (eltype);
  	fields [tindex ++] = LLVMGetNamedGlobal (lmodule, "image_table");
  	fields [tindex ++] = LLVMGetNamedGlobal (lmodule, "assembly_guid");
  	fields [tindex ++] = LLVMGetNamedGlobal (lmodule, "runtime_version");
  	if (info->trampoline_size [0]) {
  		fields [tindex ++] = AddJitGlobal (module, eltype, "specific_trampolines");
  		fields [tindex ++] = AddJitGlobal (module, eltype, "static_rgctx_trampolines");
  		fields [tindex ++] = AddJitGlobal (module, eltype, "imt_thunks");
  		fields [tindex ++] = AddJitGlobal (module, eltype, "gsharedvt_arg_trampolines");
  	} else {
  		fields [tindex ++] = LLVMConstNull (eltype);
  		fields [tindex ++] = LLVMConstNull (eltype);
  		fields [tindex ++] = LLVMConstNull (eltype);
  		fields [tindex ++] = LLVMConstNull (eltype);
  	}
  	if (module->static_link)
  		fields [tindex ++] = AddJitGlobal (module, eltype, "globals");
  	else
  		fields [tindex ++] = LLVMConstNull (eltype);
  	fields [tindex ++] = LLVMGetNamedGlobal (lmodule, "assembly_name");
  	if (!module->llvm_only) {
  		fields [tindex ++] = AddJitGlobal (module, eltype, "plt");
  		fields [tindex ++] = AddJitGlobal (module, eltype, "plt_end");
  		fields [tindex ++] = AddJitGlobal (module, eltype, "unwind_info");
  		fields [tindex ++] = AddJitGlobal (module, eltype, "unbox_trampolines");
  		fields [tindex ++] = AddJitGlobal (module, eltype, "unbox_trampolines_end");
  		fields [tindex ++] = AddJitGlobal (module, eltype, "unbox_trampoline_addresses");
  	} else {
  		fields [tindex ++] = LLVMConstNull (eltype);
  		fields [tindex ++] = LLVMConstNull (eltype);
  		fields [tindex ++] = LLVMConstNull (eltype);
  		fields [tindex ++] = LLVMConstNull (eltype);
  		fields [tindex ++] = LLVMConstNull (eltype);
  		fields [tindex ++] = LLVMConstNull (eltype);
  	}
  
  	for (i = 0; i < MONO_AOT_FILE_INFO_NUM_SYMBOLS; ++i)
  		fields [2 + i] = LLVMConstBitCast (fields [2 + i], eltype);
  
  	/* Scalars */
  	fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->plt_got_offset_base, FALSE);
  	fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->got_size, FALSE);
  	fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->plt_size, FALSE);
  	fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->nmethods, FALSE);
  	fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->flags, FALSE);
  	fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->opts, FALSE);
  	fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->simd_opts, FALSE);
  	fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->gc_name_index, FALSE);
  	fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->num_rgctx_fetch_trampolines, FALSE);
  	fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->double_align, FALSE);
  	fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->long_align, FALSE);
  	fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->generic_tramp_num, FALSE);
  	fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->tramp_page_size, FALSE);
  	fields [tindex ++] = LLVMConstInt (LLVMInt32Type (), info->nshared_got_entries, FALSE);
  	/* Arrays */
  	fields [tindex ++] = llvm_array_from_uints (LLVMInt32Type (), info->num_trampolines, MONO_AOT_TRAMP_NUM);
  	fields [tindex ++] = llvm_array_from_uints (LLVMInt32Type (), info->trampoline_got_offset_base, MONO_AOT_TRAMP_NUM);
  	fields [tindex ++] = llvm_array_from_uints (LLVMInt32Type (), info->trampoline_size, MONO_AOT_TRAMP_NUM);
  	fields [tindex ++] = llvm_array_from_uints (LLVMInt32Type (), info->tramp_page_code_offsets, MONO_AOT_TRAMP_NUM);
  	g_assert (tindex == nfields);
  
  	LLVMSetInitializer (info_var, LLVMConstNamedStruct (file_info_type, fields, nfields));
  
  	if (module->static_link) {
  		char *s, *p;
  		LLVMValueRef var;
  
  		s = g_strdup_printf ("mono_aot_module_%s_info", module->assembly->aname.name);
  		/* Get rid of characters which cannot occur in symbols */
  		p = s;
  		for (p = s; *p; ++p) {
  			if (!(isalnum (*p) || *p == '_'))
  				*p = '_';
  		}
  		var = LLVMAddGlobal (module->lmodule, LLVMPointerType (LLVMInt8Type (), 0), s);
  		g_free (s);
  		LLVMSetInitializer (var, LLVMConstBitCast (LLVMGetNamedGlobal (module->lmodule, "mono_aot_file_info"), LLVMPointerType (LLVMInt8Type (), 0)));
  		LLVMSetLinkage (var, LLVMExternalLinkage);
  	}
  }
  
  /*
   * Emit the aot module into the LLVM bitcode file FILENAME.
   */
  void
  mono_llvm_emit_aot_module (const char *filename, const char *cu_name)
  {
  	LLVMTypeRef got_type, inited_type;
  	LLVMValueRef real_got, real_inited;
  	MonoLLVMModule *module = &aot_module;
  
  	emit_llvm_code_end (module);
  
  	/* 
  	 * Create the real got variable and replace all uses of the dummy variable with
  	 * the real one.
  	 */
  	got_type = LLVMArrayType (module->ptr_type, module->max_got_offset + 1);
  	real_got = LLVMAddGlobal (module->lmodule, got_type, module->got_symbol);
  	LLVMSetInitializer (real_got, LLVMConstNull (got_type));
  	if (module->external_symbols) {
  		LLVMSetLinkage (real_got, LLVMExternalLinkage);
  		LLVMSetVisibility (real_got, LLVMHiddenVisibility);
  	} else {
  		LLVMSetLinkage (real_got, LLVMInternalLinkage);
  	}
  	mono_llvm_replace_uses_of (module->got_var, real_got);
  
  	mark_as_used (&aot_module, real_got);
  
  	/* Delete the dummy got so it doesn't become a global */
  	LLVMDeleteGlobal (module->got_var);
  	module->got_var = real_got;
  
  	/*
  	 * Same for the init_var
  	 */
  	if (module->llvm_only) {
  		inited_type = LLVMArrayType (LLVMInt8Type (), module->max_inited_idx + 1);
  		real_inited = LLVMAddGlobal (module->lmodule, inited_type, "mono_inited");
  		LLVMSetInitializer (real_inited, LLVMConstNull (inited_type));
  		LLVMSetLinkage (real_inited, LLVMInternalLinkage);
  		mono_llvm_replace_uses_of (module->inited_var, real_inited);
  		LLVMDeleteGlobal (module->inited_var);
  	}
  
  	if (module->llvm_only) {
  		emit_get_method (&aot_module);
  		emit_get_unbox_tramp (&aot_module);
  	}
  
  	emit_llvm_used (&aot_module);
  	emit_dbg_info (&aot_module, filename, cu_name);
  	emit_aot_file_info (&aot_module);
  
  	/*
  	 * Replace GOT entries for directly callable methods with the methods themselves.
  	 * It would be easier to implement this by predefining all methods before compiling
  	 * their bodies, but that couldn't handle the case when a method fails to compile
  	 * with llvm.
  	 */
  	if (module->llvm_only) {
  		GHashTableIter iter;
  		MonoMethod *method;
  		GSList *callers, *l;
  
  		g_hash_table_iter_init (&iter, module->method_to_callers);
  		while (g_hash_table_iter_next (&iter, (void**)&method, (void**)&callers)) {
  			LLVMValueRef lmethod;
  
  			lmethod = g_hash_table_lookup (module->method_to_lmethod, method);
  			if (lmethod) {
  				for (l = callers; l; l = l->next) {
  					LLVMValueRef caller = l->data;
  
  					mono_llvm_replace_uses_of (caller, lmethod);
  				}
  			}
  		}
  	}
  
  	/* Replace PLT entries for directly callable methods with the methods themselves */
  	{
  		GHashTableIter iter;
  		MonoJumpInfo *ji;
  		LLVMValueRef callee;
  
  		g_hash_table_iter_init (&iter, module->plt_entries_ji);
  		while (g_hash_table_iter_next (&iter, (void**)&ji, (void**)&callee)) {
  			if (mono_aot_is_direct_callable (ji)) {
  				LLVMValueRef lmethod;
  
  				lmethod = g_hash_table_lookup (module->method_to_lmethod, ji->data.method);
  				/* The types might not match because the caller might pass an rgctx */
  				if (lmethod && LLVMTypeOf (callee) == LLVMTypeOf (lmethod)) {
  					mono_llvm_replace_uses_of (callee, lmethod);
  					mono_aot_mark_unused_llvm_plt_entry (ji);
  				}
  			}
  		}
  	}
  
  #if 0
  	{
  		char *verifier_err;
  
  		if (LLVMVerifyModule (module->module, LLVMReturnStatusAction, &verifier_err)) {
  			g_assert_not_reached ();
  		}
  	}
  #endif
  
  	LLVMWriteBitcodeToFile (module->lmodule, filename);
  }
  
  
  static LLVMValueRef
  md_string (const char *s)
  {
  	return LLVMMDString (s, strlen (s));
  }
  
  /* Debugging support */
  
  static void
  emit_dbg_info (MonoLLVMModule *module, const char *filename, const char *cu_name)
  {
  	LLVMModuleRef lmodule = module->lmodule;
  	LLVMValueRef args [16], cu_args [16], cu, ver;
  	int n_cuargs;
  	char *build_info, *s, *dir;
  
  	/*
  	 * This can only be enabled when LLVM code is emitted into a separate object
  	 * file, since the AOT compiler also emits dwarf info,
  	 * and the abbrev indexes will not be correct since llvm has added its own
  	 * abbrevs.
  	 */
  	if (!module->emit_dwarf)
  		return;
  
  	/*
  	 * Emit dwarf info in the form of LLVM metadata. There is some
  	 * out-of-date documentation at:
  	 * http://llvm.org/docs/SourceLevelDebugging.html
  	 * but most of this was gathered from the llvm and
  	 * clang sources.
  	 */
  
  	n_cuargs = 0;
  	cu_args [n_cuargs ++] = LLVMConstInt (LLVMInt32Type (), DW_TAG_compile_unit, FALSE);
  	/* CU name/compilation dir */
  	dir = g_path_get_dirname (filename);
  	args [0] = LLVMMDString (cu_name, strlen (cu_name));
  	args [1] = LLVMMDString (dir, strlen (dir));
  	cu_args [n_cuargs ++] = LLVMMDNode (args, 2);
  	g_free (dir);
  	/* Language */
  	cu_args [n_cuargs ++] = LLVMConstInt (LLVMInt32Type (), DW_LANG_C99, FALSE);
  	/* Producer */
  	build_info = mono_get_runtime_build_info ();
  	s = g_strdup_printf ("Mono AOT Compiler %s (LLVM)", build_info);
  	cu_args [n_cuargs ++] = LLVMMDString (s, strlen (s));
  	g_free (build_info);
  	/* Optimized */
  	cu_args [n_cuargs ++] = LLVMConstInt (LLVMInt32Type (), 1, FALSE);
  	/* Flags */
  	cu_args [n_cuargs ++] = LLVMMDString ("", strlen (""));
  	/* Runtime version */
  	cu_args [n_cuargs ++] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
  	/* Enums */
  	cu_args [n_cuargs ++] = LLVMMDNode (args, 0);
  	cu_args [n_cuargs ++] = LLVMMDNode (args, 0);
  	/* Subprograms */
  	if (module->subprogram_mds) {
  		LLVMValueRef *mds;
  		int i;
  
  		mds = g_new0 (LLVMValueRef, module->subprogram_mds->len);
  		for (i = 0; i < module->subprogram_mds->len; ++i)
  			mds [i] = g_ptr_array_index (module->subprogram_mds, i);
  		cu_args [n_cuargs ++] = LLVMMDNode (mds, module->subprogram_mds->len);
  	} else {
  		cu_args [n_cuargs ++] = LLVMMDNode (args, 0);
  	}
  	/* GVs */
  	cu_args [n_cuargs ++] = LLVMMDNode (args, 0);
  	/* Imported modules */
  	cu_args [n_cuargs ++] = LLVMMDNode (args, 0);
  	/* SplitName */
  	cu_args [n_cuargs ++] = LLVMMDString ("", strlen (""));
  	/* DebugEmissionKind = FullDebug */
  	cu_args [n_cuargs ++] = LLVMConstInt (LLVMInt32Type (), 1, FALSE);
  	cu = LLVMMDNode (cu_args, n_cuargs);
  	LLVMAddNamedMetadataOperand (lmodule, "llvm.dbg.cu", cu);
  
  	args [0] = LLVMConstInt (LLVMInt32Type (), 1, FALSE);
  	args [1] = LLVMMDString ("Dwarf Version", strlen ("Dwarf Version"));
  	args [2] = LLVMConstInt (LLVMInt32Type (), 2, FALSE);
  	ver = LLVMMDNode (args, 3);
  	LLVMAddNamedMetadataOperand (lmodule, "llvm.module.flags", ver);
  
  	args [0] = LLVMConstInt (LLVMInt32Type (), 1, FALSE);
  	args [1] = LLVMMDString ("Debug Info Version", strlen ("Debug Info Version"));
  	args [2] = LLVMConstInt (LLVMInt32Type (), 1, FALSE);
  	ver = LLVMMDNode (args, 3);
  	LLVMAddNamedMetadataOperand (lmodule, "llvm.module.flags", ver);
  }
  
  static LLVMValueRef
  emit_dbg_subprogram (EmitContext *ctx, MonoCompile *cfg, LLVMValueRef method, const char *name)
  {
  	MonoLLVMModule *module = ctx->module;
  	MonoDebugMethodInfo *minfo = ctx->minfo;
  	char *source_file, *dir, *filename;
  	LLVMValueRef md, args [16], ctx_args [16], md_args [64], type_args [16], ctx_md, type_md;
  	MonoSymSeqPoint *sym_seq_points;
  	int n_seq_points;
  
  	if (!minfo)
  		return NULL;
  
  	mono_debug_symfile_get_seq_points (minfo, &source_file, NULL, NULL, &sym_seq_points, &n_seq_points);
  	if (!source_file)
  		source_file = g_strdup ("<unknown>");
  	dir = g_path_get_dirname (source_file);
  	filename = g_path_get_basename (source_file);
  
  	ctx_args [0] = LLVMConstInt (LLVMInt32Type (), 0x29, FALSE);
  	args [0] = md_string (filename);
  	args [1] = md_string (dir);
  	ctx_args [1] = LLVMMDNode (args, 2);
  	ctx_md = LLVMMDNode (ctx_args, 2);
  
  	type_args [0] = LLVMConstInt (LLVMInt32Type (), DW_TAG_subroutine_type, FALSE);
  	type_args [1] = NULL;
  	type_args [2] = NULL;
  	type_args [3] = LLVMMDString ("", 0);
  	type_args [4] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
  	type_args [5] = LLVMConstInt (LLVMInt64Type (), 0, FALSE);
  	type_args [6] = LLVMConstInt (LLVMInt64Type (), 0, FALSE);
  	type_args [7] = LLVMConstInt (LLVMInt64Type (), 0, FALSE);
  	type_args [8] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
  	type_args [9] = NULL;
  	type_args [10] = NULL;
  	type_args [11] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
  	type_args [12] = NULL;
  	type_args [13] = NULL;
  	type_args [14] = NULL;
  	type_md = LLVMMDNode (type_args, 14);
  
  	/* http://llvm.org/docs/SourceLevelDebugging.html#subprogram-descriptors */
  	md_args [0] = LLVMConstInt (LLVMInt32Type (), DW_TAG_subprogram, FALSE);
  	/* Source directory + file pair */
  	args [0] = md_string (filename);
  	args [1] = md_string (dir);
  	md_args [1] = LLVMMDNode (args ,2);
  	md_args [2] = ctx_md;
  	md_args [3] = md_string (cfg->method->name);
  	md_args [4] = md_string (name);
  	md_args [5] = md_string (name);
  	/* Line number */
  	if (n_seq_points)
  		md_args [6] = LLVMConstInt (LLVMInt32Type (), sym_seq_points [0].line, FALSE);
  	else
  		md_args [6] = LLVMConstInt (LLVMInt32Type (), 1, FALSE);
  	/* Type */
  	md_args [7] = type_md;
  	/* static */
  	md_args [8] = LLVMConstInt (LLVMInt1Type (), 0, FALSE);
  	/* not extern */
  	md_args [9] = LLVMConstInt (LLVMInt1Type (), 1, FALSE);
  	/* Virtuality */
  	md_args [10] = LLVMConstInt (LLVMInt32Type (), 0, FALSE);
  	/* Index into a virtual function */
  	md_args [11] = NULL;
  	md_args [12] = NULL;
  	/* Flags */
  	md_args [13] = LLVMConstInt (LLVMInt1Type (), 0, FALSE);
  	/* isOptimized */
  	md_args [14] = LLVMConstInt (LLVMInt1Type (), 1, FALSE);
  	/* Pointer to LLVM function */
  	md_args [15] = method;
  	/* Function template parameter */
  	md_args [16] = NULL;
  	/* Function declaration descriptor */
  	md_args [17] = NULL;
  	/* List of function variables */
  	md_args [18] = LLVMMDNode (args, 0);
  	/* Line number */
  	md_args [19] = LLVMConstInt (LLVMInt32Type (), 1, FALSE);
  	md = LLVMMDNode (md_args, 20);
  
  	if (!module->subprogram_mds)
  		module->subprogram_mds = g_ptr_array_new ();
  	g_ptr_array_add (module->subprogram_mds, md);
  
  	g_free (dir);
  	g_free (filename);
  	g_free (source_file);
  	g_free (sym_seq_points);
  
  	return md;
  }
  
  static void
  emit_dbg_loc (EmitContext *ctx, LLVMBuilderRef builder, const unsigned char *cil_code)
  {
  	MonoCompile *cfg = ctx->cfg;
  
  	if (ctx->minfo && cil_code && cil_code >= cfg->header->code && cil_code < cfg->header->code + cfg->header->code_size) {
  		MonoDebugSourceLocation *loc;
  		LLVMValueRef loc_md, md_args [16];
  		int nmd_args;
  
  		loc = mono_debug_symfile_lookup_location (ctx->minfo, cil_code - cfg->header->code);
  
  		if (loc) {
  			nmd_args = 0;
  			md_args [nmd_args ++] = LLVMConstInt (LLVMInt32Type (), loc->row, FALSE);
  			md_args [nmd_args ++] = LLVMConstInt (LLVMInt32Type (), loc->column, FALSE);
  			md_args [nmd_args ++] = ctx->dbg_md;
  			md_args [nmd_args ++] = NULL;
  			loc_md = LLVMMDNode (md_args, nmd_args);
  			LLVMSetCurrentDebugLocation (builder, loc_md);
  			mono_debug_symfile_free_location (loc);
  		}
  	}
  }
  
  void
  default_mono_llvm_unhandled_exception (void)
  {
  	MonoJitTlsData *jit_tls = mono_get_jit_tls ();
  	MonoObject *target = mono_gchandle_get_target (jit_tls->thrown_exc);
  
  	mono_unhandled_exception (target);
  	exit (mono_environment_exitcode_get ());
  }
  
  /*
    DESIGN:
    - Emit LLVM IR from the mono IR using the LLVM C API.
    - The original arch specific code remains, so we can fall back to it if we run
      into something we can't handle.
  */
  
  /*  
    A partial list of issues:
    - Handling of opcodes which can throw exceptions.
  
        In the mono JIT, these are implemented using code like this:
  	  method:
        <compare>
  	  throw_pos:
  	  b<cond> ex_label
  	  <rest of code>
        ex_label:
  	  push throw_pos - method
  	  call <exception trampoline>
  
  	  The problematic part is push throw_pos - method, which cannot be represented
        in the LLVM IR, since it does not support label values.
  	  -> this can be implemented in AOT mode using inline asm + labels, but cannot
  	  be implemented in JIT mode ?
  	  -> a possible but slower implementation would use the normal exception 
        throwing code but it would need to control the placement of the throw code
        (it needs to be exactly after the compare+branch).
  	  -> perhaps add a PC offset intrinsics ?
  
    - efficient implementation of .ovf opcodes.
  
  	  These are currently implemented as:
  	  <ins which sets the condition codes>
  	  b<cond> ex_label
  
  	  Some overflow opcodes are now supported by LLVM SVN.
  
    - exception handling, unwinding.
      - SSA is disabled for methods with exception handlers    
  	- How to obtain unwind info for LLVM compiled methods ?
  	  -> this is now solved by converting the unwind info generated by LLVM
  	     into our format.
  	- LLVM uses the c++ exception handling framework, while we use our home grown
        code, and couldn't use the c++ one:
        - its not supported under VC++, other exotic platforms.
  	  - it might be impossible to support filter clauses with it.
  
    - trampolines.
    
      The trampolines need a predictable call sequence, since they need to disasm
      the calling code to obtain register numbers / offsets.
  
      LLVM currently generates this code in non-JIT mode:
  	   mov    -0x98(%rax),%eax
  	   callq  *%rax
      Here, the vtable pointer is lost. 
      -> solution: use one vtable trampoline per class.
  
    - passing/receiving the IMT pointer/RGCTX.
      -> solution: pass them as normal arguments ?
  
    - argument passing.
    
  	  LLVM does not allow the specification of argument registers etc. This means
        that all calls are made according to the platform ABI.
  
    - passing/receiving vtypes.
  
        Vtypes passed/received in registers are handled by the front end by using
  	  a signature with scalar arguments, and loading the parts of the vtype into those
  	  arguments.
  
  	  Vtypes passed on the stack are handled using the 'byval' attribute.
  
    - ldaddr.
  
      Supported though alloca, we need to emit the load/store code.
  
    - types.
  
      The mono JIT uses pointer sized iregs/double fregs, while LLVM uses precisely
      typed registers, so we have to keep track of the precise LLVM type of each vreg.
      This is made easier because the IR is already in SSA form.
      An additional problem is that our IR is not consistent with types, i.e. i32/ia64 
  	types are frequently used incorrectly.
  */
  
  /*
    AOT SUPPORT:
    Emit LLVM bytecode into a .bc file, compile it using llc into a .s file, then link
    it with the file containing the methods emitted by the JIT and the AOT data
    structures.
  */
  
  /* FIXME: Normalize some aspects of the mono IR to allow easier translation, like:
   *   - each bblock should end with a branch
   *   - setting the return value, making cfg->ret non-volatile
   * - avoid some transformations in the JIT which make it harder for us to generate
   *   code.
   * - use pointer types to help optimizations.
   */
  