ソースを参照

[llvm] Use explicit null checks with LLVM. (#16954)

* [llvm] Use explicit null checks with LLVM.

When the LLVM backend is used for a method, explicit null checks will
unconditionally be used at the mini IR level. We had previously used
implicit null checks at the mini IR level combined with llvm-volatile
loads and stores to avoid mis-optimization of the generated code.

During AOT compilation, LLVM's `ImplicitNullChecks` pass is enabled.
This conservatively elides these explicit null checks. This pass only
modifies branches tagged with `make.implicit` metadata.

Null check branches are now tagged with `make.implicit` metadata, with
three exceptions:

1. For platforms with backends that do not support zero page fault
handling, null check branches are not tagged with `make.implicit`.

2. This commit also adds a `MONO_DEBUG` option:
`llvm-disable-implicit-null-checks`. When it is enabled, null check
branches are not tagged with `make.implicit`.

3. This commit alters the behavior of the `explicit-null-checks`
`MONO_DEBUG` option. Enabling `explicit-null-checks` also implies
`llvm-disable-implicit-null-checks`, because this option is documented
(e.g. in https://www.mono-project.com/docs/debug+profile/debug/) to
completely disable signal/seh-based null checks.

* Add llvm-disable-implicit-null-checks to the MONO_DEBUG help string.

* Add llvm-disable-implicit-null-checks to mono.1.

* Don't tag null checks in EH regions with `make.implicit`.

This relocates the logic formerly used to guard individual LLVM-level
loads and stores with explicit null checks inside EH regions.

* Use atomic/volatile loads and stores for the terimation flag in thread-suspend-suspended.

LLVM was hoisting the load of and branch against `finished` out of
thread t1's loop, resulting in an infinite spin.

* Use ImplicitNullChecks with LLVM JIT.

* Suppress emission of the .llvm_faultmaps section.

Bump external/llvm to release_60
(7a8dc89adbe7e123220e070a527e096ee91e66d5).

* [ci] use LLVM built from repository
imhameed 6 年 前
コミット
fe0f824db5

+ 6 - 0
man/mono.1

@@ -1763,6 +1763,12 @@ Automatically generates sequence points where the
 IL stack is empty.  These are places where the debugger can set a
 IL stack is empty.  These are places where the debugger can set a
 breakpoint.
 breakpoint.
 .TP
 .TP
+\fBllvm-disable-implicit-null-checks\fR
+Makes the LLVM backend use explicit NULL checks on variable dereferences
+instead of depending on operating system support for signals or traps when
+an invalid memory location is accessed. Unconditionally enabled by
+explicit-null-checks.
+.TP
 \fBno-compact-seq-points\fR
 \fBno-compact-seq-points\fR
 Unless the option is used, the runtime generates sequence points data that
 Unless the option is used, the runtime generates sequence points data that
 maps native offsets to IL offsets. Sequence point data is used to
 maps native offsets to IL offsets. Sequence point data is used to

+ 1 - 0
mono/mini/aot-compiler.c

@@ -1125,6 +1125,7 @@ arch_init (MonoAotCompile *acfg)
 	/* NOP */
 	/* NOP */
 	acfg->align_pad_value = 0x90;
 	acfg->align_pad_value = 0x90;
 #endif
 #endif
+	g_string_append (acfg->llc_args, " -enable-implicit-null-checks -disable-fault-maps");
 
 
 	if (mono_use_fast_math) {
 	if (mono_use_fast_math) {
 		// same parameters are passed to opt and LLVM JIT
 		// same parameters are passed to opt and LLVM JIT

+ 1 - 1
mono/mini/llvm-jit.cpp

@@ -288,7 +288,7 @@ public:
 		// FIXME: find optimal mono specific order of passes
 		// FIXME: find optimal mono specific order of passes
 		// see https://llvm.org/docs/Frontend/PerformanceTips.html#pass-ordering
 		// see https://llvm.org/docs/Frontend/PerformanceTips.html#pass-ordering
 		// the following order is based on a stripped version of "OPT -O2"
 		// the following order is based on a stripped version of "OPT -O2"
-		const char *default_opts = " -simplifycfg -sroa -lower-expect -instcombine -licm -simplifycfg -lcssa -indvars -loop-deletion -gvn -memcpyopt -sccp -bdce -instcombine -dse -simplifycfg";
+		const char *default_opts = " -simplifycfg -sroa -lower-expect -instcombine -licm -simplifycfg -lcssa -indvars -loop-deletion -gvn -memcpyopt -sccp -bdce -instcombine -dse -simplifycfg -enable-implicit-null-checks";
 		const char *opts = g_getenv ("MONO_LLVM_OPT");
 		const char *opts = g_getenv ("MONO_LLVM_OPT");
 		if (opts == NULL)
 		if (opts == NULL)
 			opts = default_opts;
 			opts = default_opts;

+ 41 - 48
mono/mini/mini-llvm.c

@@ -405,7 +405,7 @@ static void emit_dbg_loc (EmitContext *ctx, LLVMBuilderRef builder, const unsign
 static void emit_default_dbg_loc (EmitContext *ctx, LLVMBuilderRef builder);
 static void emit_default_dbg_loc (EmitContext *ctx, LLVMBuilderRef builder);
 static LLVMValueRef emit_dbg_subprogram (EmitContext *ctx, MonoCompile *cfg, LLVMValueRef method, const char *name);
 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);
 static void emit_dbg_info (MonoLLVMModule *module, const char *filename, const char *cu_name);
-static void emit_cond_system_exception (EmitContext *ctx, MonoBasicBlock *bb, const char *exc_type, LLVMValueRef cmp);
+static void emit_cond_system_exception (EmitContext *ctx, MonoBasicBlock *bb, const char *exc_type, LLVMValueRef cmp, gboolean force_explicit);
 static LLVMValueRef get_intrins_by_name (EmitContext *ctx, const char *name);
 static LLVMValueRef get_intrins_by_name (EmitContext *ctx, const char *name);
 static LLVMValueRef get_intrins (EmitContext *ctx, int id);
 static LLVMValueRef get_intrins (EmitContext *ctx, int id);
 static LLVMValueRef get_intrins_from_module (LLVMModuleRef lmodule, int id);
 static LLVMValueRef get_intrins_from_module (LLVMModuleRef lmodule, int id);
@@ -2299,58 +2299,36 @@ emit_call (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, LL
 }
 }
 
 
 static LLVMValueRef
 static LLVMValueRef
-emit_load (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, int size, LLVMValueRef addr, LLVMValueRef base, const char *name, gboolean is_faulting, BarrierKind barrier)
+emit_load (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, int size, LLVMValueRef addr, LLVMValueRef base, const char *name, gboolean is_faulting, gboolean is_volatile, BarrierKind barrier)
 {
 {
 	LLVMValueRef res;
 	LLVMValueRef res;
 
 
-	if (is_faulting && bb->region != -1 && !ctx->cfg->llvm_only) {
-		/* The llvm.mono.load/store intrinsics are not supported by this llvm version, emit an explicit null check instead */
-		LLVMValueRef cmp;
-
-		cmp = LLVMBuildICmp (*builder_ref, LLVMIntEQ, base, LLVMConstNull (LLVMTypeOf (base)), "");
-		emit_cond_system_exception (ctx, bb, "NullReferenceException", cmp);
-		*builder_ref = ctx->builder;
-	}
-
 	/* 
 	/* 
 	 * We emit volatile loads for loads which can fault, because otherwise
 	 * We emit volatile loads for loads which can fault, because otherwise
 	 * LLVM will generate invalid code when encountering a load from a
 	 * LLVM will generate invalid code when encountering a load from a
 	 * NULL address.
 	 * NULL address.
 	 */
 	 */
 	if (barrier != LLVM_BARRIER_NONE)
 	if (barrier != LLVM_BARRIER_NONE)
-		res = mono_llvm_build_atomic_load (*builder_ref, addr, name, is_faulting, size, barrier);
+		res = mono_llvm_build_atomic_load (*builder_ref, addr, name, is_volatile, size, barrier);
 	else
 	else
-		res = mono_llvm_build_load (*builder_ref, addr, name, is_faulting);
-
-	/* Mark it with a custom metadata */
-	/*
-	  if (is_faulting)
-	  set_metadata_flag (res, "mono.faulting.load");
-	*/
+		res = mono_llvm_build_load (*builder_ref, addr, name, is_volatile);
 
 
 	return res;
 	return res;
 }
 }
 
 
 static void
 static void
-emit_store_general (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, int size, LLVMValueRef value, LLVMValueRef addr, LLVMValueRef base, gboolean is_faulting, BarrierKind barrier)
+emit_store_general (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, int size, LLVMValueRef value, LLVMValueRef addr, LLVMValueRef base, gboolean is_faulting, gboolean is_volatile, BarrierKind barrier)
 {
 {
-	if (is_faulting && bb->region != -1 && !ctx->cfg->llvm_only) {
-		/* The llvm.mono.load/store intrinsics are not supported by this llvm version, emit an explicit null check instead */
-		LLVMValueRef cmp = LLVMBuildICmp (*builder_ref, LLVMIntEQ, base, LLVMConstNull (LLVMTypeOf (base)), "");
-		emit_cond_system_exception (ctx, bb, "NullReferenceException", cmp);
-		*builder_ref = ctx->builder;
-	}
-
 	if (barrier != LLVM_BARRIER_NONE)
 	if (barrier != LLVM_BARRIER_NONE)
 		mono_llvm_build_aligned_store (*builder_ref, value, addr, barrier, size);
 		mono_llvm_build_aligned_store (*builder_ref, value, addr, barrier, size);
 	else
 	else
-		mono_llvm_build_store (*builder_ref, value, addr, is_faulting, barrier);
+		mono_llvm_build_store (*builder_ref, value, addr, is_volatile, barrier);
 }
 }
 
 
 static void
 static void
-emit_store (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, int size, LLVMValueRef value, LLVMValueRef addr, LLVMValueRef base, gboolean is_faulting)
+emit_store (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, int size, LLVMValueRef value, LLVMValueRef addr, LLVMValueRef base, gboolean is_faulting, gboolean is_volatile)
 {
 {
-	emit_store_general (ctx, bb, builder_ref, size, value, addr, base, is_faulting, LLVM_BARRIER_NONE);
+	emit_store_general (ctx, bb, builder_ref, size, value, addr, base, is_faulting, is_volatile, LLVM_BARRIER_NONE);
 }
 }
 
 
 /*
 /*
@@ -2360,7 +2338,7 @@ emit_store (EmitContext *ctx, MonoBasicBlock *bb, LLVMBuilderRef *builder_ref, i
  * Might set the ctx exception.
  * Might set the ctx exception.
  */
  */
 static void
 static void
-emit_cond_system_exception (EmitContext *ctx, MonoBasicBlock *bb, const char *exc_type, LLVMValueRef cmp)
+emit_cond_system_exception (EmitContext *ctx, MonoBasicBlock *bb, const char *exc_type, LLVMValueRef cmp, gboolean force_explicit)
 {
 {
 	LLVMBasicBlockRef ex_bb, ex2_bb = NULL, noex_bb;
 	LLVMBasicBlockRef ex_bb, ex2_bb = NULL, noex_bb;
 	LLVMBuilderRef builder;
 	LLVMBuilderRef builder;
@@ -2384,7 +2362,10 @@ emit_cond_system_exception (EmitContext *ctx, MonoBasicBlock *bb, const char *ex
 		ex2_bb = gen_bb (ctx, "EX2_BB");
 		ex2_bb = gen_bb (ctx, "EX2_BB");
 	noex_bb = gen_bb (ctx, "NOEX_BB");
 	noex_bb = gen_bb (ctx, "NOEX_BB");
 
 
-	LLVMBuildCondBr (ctx->builder, cmp, ex_bb, noex_bb);
+	LLVMValueRef branch = LLVMBuildCondBr (ctx->builder, cmp, ex_bb, noex_bb);
+	if (exc_id == MONO_EXC_NULL_REF && !ctx->cfg->disable_llvm_implicit_null_checks && !force_explicit) {
+		mono_llvm_set_implicit_branch (ctx->builder, branch);
+	}
 
 
 	/* Emit exception throwing code */
 	/* Emit exception throwing code */
 	ctx->builder = builder = create_builder (ctx);
 	ctx->builder = builder = create_builder (ctx);
@@ -3284,7 +3265,7 @@ emit_div_check (EmitContext *ctx, LLVMBuilderRef builder, MonoBasicBlock *bb, Mo
 							  ins->opcode == OP_IDIV_IMM || ins->opcode == OP_LDIV_IMM || ins->opcode == OP_IREM_IMM || ins->opcode == OP_LREM_IMM);
 							  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), "");
 		cmp = LLVMBuildICmp (builder, LLVMIntEQ, rhs, LLVMConstInt (LLVMTypeOf (rhs), 0, FALSE), "");
-		emit_cond_system_exception (ctx, bb, "DivideByZeroException", cmp);
+		emit_cond_system_exception (ctx, bb, "DivideByZeroException", cmp, FALSE);
 		if (!ctx_ok (ctx))
 		if (!ctx_ok (ctx))
 			break;
 			break;
 		builder = ctx->builder;
 		builder = ctx->builder;
@@ -3296,7 +3277,7 @@ emit_div_check (EmitContext *ctx, LLVMBuilderRef builder, MonoBasicBlock *bb, Mo
 			LLVMValueRef cond2 = LLVMBuildICmp (builder, LLVMIntEQ, lhs, c, "");
 			LLVMValueRef cond2 = LLVMBuildICmp (builder, LLVMIntEQ, lhs, c, "");
 
 
 			cmp = LLVMBuildICmp (builder, LLVMIntEQ, LLVMBuildAnd (builder, cond1, cond2, ""), LLVMConstInt (LLVMInt1Type (), 1, FALSE), "");
 			cmp = LLVMBuildICmp (builder, LLVMIntEQ, LLVMBuildAnd (builder, cond1, cond2, ""), LLVMConstInt (LLVMInt1Type (), 1, FALSE), "");
-			emit_cond_system_exception (ctx, bb, "OverflowException", cmp);
+			emit_cond_system_exception (ctx, bb, "OverflowException", cmp, FALSE);
 			if (!ctx_ok (ctx))
 			if (!ctx_ok (ctx))
 				break;
 				break;
 			builder = ctx->builder;
 			builder = ctx->builder;
@@ -5211,7 +5192,14 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
 				/* Add stores for volatile variables */
 				/* Add stores for volatile variables */
 				emit_volatile_store (ctx, ins->next->dreg);
 				emit_volatile_store (ctx, ins->next->dreg);
 			} else if (MONO_IS_COND_EXC (ins->next)) {
 			} else if (MONO_IS_COND_EXC (ins->next)) {
-				emit_cond_system_exception (ctx, bb, (const char*)ins->next->inst_p1, cmp);
+				gboolean force_explicit_branch = FALSE;
+				if (bb->region != -1) {
+					/* Don't tag null check branches in exception-handling
+					 * regions with `make.implicit`.
+					 */
+					force_explicit_branch = TRUE;
+				}
+				emit_cond_system_exception (ctx, bb, (const char*)ins->next->inst_p1, cmp, force_explicit_branch);
 				if (!ctx_ok (ctx))
 				if (!ctx_ok (ctx))
 					break;
 					break;
 				builder = ctx->builder;
 				builder = ctx->builder;
@@ -5758,7 +5746,8 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
 			LLVMValueRef base, index, addr;
 			LLVMValueRef base, index, addr;
 			LLVMTypeRef t;
 			LLVMTypeRef t;
 			gboolean sext = FALSE, zext = FALSE;
 			gboolean sext = FALSE, zext = FALSE;
-			gboolean is_volatile = (ins->flags & (MONO_INST_FAULT | MONO_INST_VOLATILE)) != 0;
+			gboolean is_faulting = (ins->flags & MONO_INST_FAULT) != 0;
+			gboolean is_volatile = (ins->flags & MONO_INST_VOLATILE) != 0;
 
 
 			t = load_store_to_llvm_type (ins->opcode, &size, &sext, &zext);
 			t = load_store_to_llvm_type (ins->opcode, &size, &sext, &zext);
 
 
@@ -5786,9 +5775,9 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
 
 
 			addr = convert (ctx, addr, LLVMPointerType (t, 0));
 			addr = convert (ctx, addr, LLVMPointerType (t, 0));
 
 
-			values [ins->dreg] = emit_load (ctx, bb, &builder, size, addr, base, dname, is_volatile, LLVM_BARRIER_NONE);
+			values [ins->dreg] = emit_load (ctx, bb, &builder, size, addr, base, dname, is_faulting, is_volatile, LLVM_BARRIER_NONE);
 
 
-			if (!is_volatile && (ins->flags & MONO_INST_INVARIANT_LOAD)) {
+			if (!(is_faulting || is_volatile) && (ins->flags & MONO_INST_INVARIANT_LOAD)) {
 				/*
 				/*
 				 * These will signal LLVM that these loads do not alias any stores, and
 				 * 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.
 				 * they can't fail, allowing them to be hoisted out of loops.
@@ -5816,7 +5805,8 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
 			LLVMValueRef index, addr, base;
 			LLVMValueRef index, addr, base;
 			LLVMTypeRef t;
 			LLVMTypeRef t;
 			gboolean sext = FALSE, zext = FALSE;
 			gboolean sext = FALSE, zext = FALSE;
-			gboolean is_volatile = (ins->flags & (MONO_INST_FAULT | MONO_INST_VOLATILE)) != 0;
+			gboolean is_faulting = (ins->flags & MONO_INST_FAULT) != 0;
+			gboolean is_volatile = (ins->flags & MONO_INST_VOLATILE) != 0;
 
 
 			if (!values [ins->inst_destbasereg]) {
 			if (!values [ins->inst_destbasereg]) {
 				set_failure (ctx, "inst_destbasereg");
 				set_failure (ctx, "inst_destbasereg");
@@ -5837,7 +5827,7 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
 			if (is_volatile && LLVMGetInstructionOpcode (base) == LLVMAlloca && !(ins->flags & MONO_INST_VOLATILE))
 			if (is_volatile && LLVMGetInstructionOpcode (base) == LLVMAlloca && !(ins->flags & MONO_INST_VOLATILE))
 				/* Storing to an alloca cannot fail */
 				/* Storing to an alloca cannot fail */
 				is_volatile = FALSE;
 				is_volatile = FALSE;
-			emit_store (ctx, bb, &builder, size, convert (ctx, values [ins->sreg1], t), convert (ctx, addr, LLVMPointerType (t, 0)), base, is_volatile);
+			emit_store (ctx, bb, &builder, size, convert (ctx, values [ins->sreg1], t), convert (ctx, addr, LLVMPointerType (t, 0)), base, is_faulting, is_volatile);
 			break;
 			break;
 		}
 		}
 
 
@@ -5850,7 +5840,8 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
 			LLVMValueRef index, addr, base;
 			LLVMValueRef index, addr, base;
 			LLVMTypeRef t;
 			LLVMTypeRef t;
 			gboolean sext = FALSE, zext = FALSE;
 			gboolean sext = FALSE, zext = FALSE;
-			gboolean is_volatile = (ins->flags & (MONO_INST_FAULT | MONO_INST_VOLATILE)) != 0;
+			gboolean is_faulting = (ins->flags & MONO_INST_FAULT) != 0;
+			gboolean is_volatile = (ins->flags & MONO_INST_VOLATILE) != 0;
 
 
 			t = load_store_to_llvm_type (ins->opcode, &size, &sext, &zext);
 			t = load_store_to_llvm_type (ins->opcode, &size, &sext, &zext);
 
 
@@ -5863,12 +5854,12 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
 				index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE);				
 				index = LLVMConstInt (LLVMInt32Type (), ins->inst_offset / size, FALSE);				
 				addr = LLVMBuildGEP (builder, convert (ctx, base, LLVMPointerType (t, 0)), &index, 1, "");
 				addr = LLVMBuildGEP (builder, convert (ctx, base, 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)), base, is_volatile);
+			emit_store (ctx, bb, &builder, size, convert (ctx, LLVMConstInt (IntPtrType (), ins->inst_imm, FALSE), t), convert (ctx, addr, LLVMPointerType (t, 0)), base, is_faulting, is_volatile);
 			break;
 			break;
 		}
 		}
 
 
 		case OP_CHECK_THIS:
 		case OP_CHECK_THIS:
-			emit_load (ctx, bb, &builder, TARGET_SIZEOF_VOID_P, convert (ctx, lhs, LLVMPointerType (IntPtrType (), 0)), lhs, "", TRUE, LLVM_BARRIER_NONE);
+			emit_load (ctx, bb, &builder, TARGET_SIZEOF_VOID_P, convert (ctx, lhs, LLVMPointerType (IntPtrType (), 0)), lhs, "", TRUE, FALSE, LLVM_BARRIER_NONE);
 			break;
 			break;
 		case OP_OUTARG_VTRETADDR:
 		case OP_OUTARG_VTRETADDR:
 			break;
 			break;
@@ -6344,7 +6335,8 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
 			int size;
 			int size;
 			gboolean sext, zext;
 			gboolean sext, zext;
 			LLVMTypeRef t;
 			LLVMTypeRef t;
-			gboolean is_volatile = (ins->flags & (MONO_INST_FAULT | MONO_INST_VOLATILE)) != 0;
+			gboolean is_faulting = (ins->flags & MONO_INST_FAULT) != 0;
+			gboolean is_volatile = (ins->flags & MONO_INST_VOLATILE) != 0;
 			BarrierKind barrier = (BarrierKind) ins->backend.memory_barrier_kind;
 			BarrierKind barrier = (BarrierKind) ins->backend.memory_barrier_kind;
 			LLVMValueRef index, addr;
 			LLVMValueRef index, addr;
 
 
@@ -6363,7 +6355,7 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
 			addr = convert (ctx, addr, LLVMPointerType (t, 0));
 			addr = convert (ctx, addr, LLVMPointerType (t, 0));
 
 
 			ARM64_ATOMIC_FENCE_FIX;
 			ARM64_ATOMIC_FENCE_FIX;
-			values [ins->dreg] = emit_load (ctx, bb, &builder, size, addr, lhs, dname, is_volatile, barrier);
+			values [ins->dreg] = emit_load (ctx, bb, &builder, size, addr, lhs, dname, is_faulting, is_volatile, barrier);
 			ARM64_ATOMIC_FENCE_FIX;
 			ARM64_ATOMIC_FENCE_FIX;
 
 
 			if (sext)
 			if (sext)
@@ -6385,7 +6377,8 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
 			int size;
 			int size;
 			gboolean sext, zext;
 			gboolean sext, zext;
 			LLVMTypeRef t;
 			LLVMTypeRef t;
-			gboolean is_volatile = (ins->flags & (MONO_INST_FAULT | MONO_INST_VOLATILE)) != 0;
+			gboolean is_faulting = (ins->flags & MONO_INST_FAULT) != 0;
+			gboolean is_volatile = (ins->flags & MONO_INST_VOLATILE) != 0;
 			BarrierKind barrier = (BarrierKind) ins->backend.memory_barrier_kind;
 			BarrierKind barrier = (BarrierKind) ins->backend.memory_barrier_kind;
 			LLVMValueRef index, addr, value, base;
 			LLVMValueRef index, addr, value, base;
 
 
@@ -6402,7 +6395,7 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
 			value = convert (ctx, values [ins->sreg1], t);
 			value = convert (ctx, values [ins->sreg1], t);
 
 
 			ARM64_ATOMIC_FENCE_FIX;
 			ARM64_ATOMIC_FENCE_FIX;
-			emit_store_general (ctx, bb, &builder, size, value, addr, base, is_volatile, barrier);
+			emit_store_general (ctx, bb, &builder, size, value, addr, base, is_faulting, is_volatile, barrier);
 			ARM64_ATOMIC_FENCE_FIX;
 			ARM64_ATOMIC_FENCE_FIX;
 			break;
 			break;
 		}
 		}
@@ -6520,7 +6513,7 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb)
 				val = LLVMBuildCall (builder, func, args, 2, "");
 				val = LLVMBuildCall (builder, func, args, 2, "");
 				values [ins->dreg] = LLVMBuildExtractValue (builder, val, 0, dname);
 				values [ins->dreg] = LLVMBuildExtractValue (builder, val, 0, dname);
 				ovf = LLVMBuildExtractValue (builder, val, 1, "");
 				ovf = LLVMBuildExtractValue (builder, val, 1, "");
-				emit_cond_system_exception (ctx, bb, "OverflowException", ovf);
+				emit_cond_system_exception (ctx, bb, "OverflowException", ovf, FALSE);
 				if (!ctx_ok (ctx))
 				if (!ctx_ok (ctx))
 					break;
 					break;
 				builder = ctx->builder;
 				builder = ctx->builder;

+ 3 - 1
mono/mini/mini-runtime.c

@@ -3694,6 +3694,8 @@ mini_parse_debug_option (const char *option)
 		mini_debug_options.llvm_disable_self_init = TRUE;
 		mini_debug_options.llvm_disable_self_init = TRUE;
 	else if (!strcmp (option, "llvm-disable-inlining"))
 	else if (!strcmp (option, "llvm-disable-inlining"))
 		mini_debug_options.llvm_disable_inlining = TRUE;
 		mini_debug_options.llvm_disable_inlining = TRUE;
+	else if (!strcmp (option, "llvm-disable-implicit-null-checks"))
+		mini_debug_options.llvm_disable_implicit_null_checks = TRUE;
 	else if (!strcmp (option, "explicit-null-checks"))
 	else if (!strcmp (option, "explicit-null-checks"))
 		mini_debug_options.explicit_null_checks = TRUE;
 		mini_debug_options.explicit_null_checks = TRUE;
 	else if (!strcmp (option, "gen-seq-points"))
 	else if (!strcmp (option, "gen-seq-points"))
@@ -3767,7 +3769,7 @@ mini_parse_debug_options (void)
 			// test-tailcall-require is also accepted but not documented.
 			// test-tailcall-require is also accepted but not documented.
 			// empty string is also accepted and ignored as a consequence
 			// empty string is also accepted and ignored as a consequence
 			// of appending ",foo" without checking for empty.
 			// of appending ",foo" without checking for empty.
-			fprintf (stderr, "Available options: 'handle-sigint', 'keep-delegates', 'reverse-pinvoke-exceptions', 'collect-pagefault-stats', 'break-on-unverified', 'no-gdb-backtrace', 'suspend-on-native-crash', 'suspend-on-sigsegv', 'suspend-on-exception', 'suspend-on-unhandled', 'dont-free-domains', 'dyn-runtime-invoke', 'gdb', 'explicit-null-checks', 'gen-seq-points', 'no-compact-seq-points', 'single-imm-size', 'init-stacks', 'casts', 'soft-breakpoints', 'check-pinvoke-callconv', 'use-fallback-tls', 'debug-domain-unload', 'partial-sharing', 'align-small-structs', 'native-debugger-break', 'thread-dump-dir=DIR', 'no-verbose-gdb', 'llvm_disable_inlining', 'llvm-disable-self-init', 'weak-memory-model'.\n");
+			fprintf (stderr, "Available options: 'handle-sigint', 'keep-delegates', 'reverse-pinvoke-exceptions', 'collect-pagefault-stats', 'break-on-unverified', 'no-gdb-backtrace', 'suspend-on-native-crash', 'suspend-on-sigsegv', 'suspend-on-exception', 'suspend-on-unhandled', 'dont-free-domains', 'dyn-runtime-invoke', 'gdb', 'explicit-null-checks', 'gen-seq-points', 'no-compact-seq-points', 'single-imm-size', 'init-stacks', 'casts', 'soft-breakpoints', 'check-pinvoke-callconv', 'use-fallback-tls', 'debug-domain-unload', 'partial-sharing', 'align-small-structs', 'native-debugger-break', 'thread-dump-dir=DIR', 'no-verbose-gdb', 'llvm_disable_inlining', 'llvm-disable-self-init', 'llvm-disable-implicit-null-checks', 'weak-memory-model'.\n");
 			exit (1);
 			exit (1);
 		}
 		}
 	}
 	}

+ 1 - 0
mono/mini/mini-runtime.h

@@ -189,6 +189,7 @@ typedef struct MonoDebugOptions {
 	 * Prevent LLVM from inlining any methods
 	 * Prevent LLVM from inlining any methods
 	 */
 	 */
 	gboolean llvm_disable_inlining;
 	gboolean llvm_disable_inlining;
+	gboolean llvm_disable_implicit_null_checks;
 	gboolean use_fallback_tls;
 	gboolean use_fallback_tls;
 	/*
 	/*
 	 * Whenever data such as next sequence points and flags is required.
 	 * Whenever data such as next sequence points and flags is required.

+ 7 - 2
mono/mini/mini.c

@@ -3173,11 +3173,13 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, JitFl
 	/* coop requires loop detection to happen */
 	/* coop requires loop detection to happen */
 	if (mini_safepoints_enabled ())
 	if (mini_safepoints_enabled ())
 		cfg->opt |= MONO_OPT_LOOP;
 		cfg->opt |= MONO_OPT_LOOP;
-	if (cfg->backend->explicit_null_checks) {
+	cfg->disable_llvm_implicit_null_checks = mini_debug_options.llvm_disable_implicit_null_checks;
+	if (cfg->backend->explicit_null_checks || mini_debug_options.explicit_null_checks) {
 		/* some platforms have null pages, so we can't SIGSEGV */
 		/* some platforms have null pages, so we can't SIGSEGV */
 		cfg->explicit_null_checks = TRUE;
 		cfg->explicit_null_checks = TRUE;
+		cfg->disable_llvm_implicit_null_checks = TRUE;
 	} else {
 	} else {
-		cfg->explicit_null_checks = mini_debug_options.explicit_null_checks || (flags & JIT_FLAG_EXPLICIT_NULL_CHECKS);
+		cfg->explicit_null_checks = flags & JIT_FLAG_EXPLICIT_NULL_CHECKS;
 	}
 	}
 	cfg->soft_breakpoints = mini_debug_options.soft_breakpoints;
 	cfg->soft_breakpoints = mini_debug_options.soft_breakpoints;
 	cfg->check_pinvoke_callconv = mini_debug_options.check_pinvoke_callconv;
 	cfg->check_pinvoke_callconv = mini_debug_options.check_pinvoke_callconv;
@@ -3190,6 +3192,9 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, JitFl
 	if (cfg->compile_aot)
 	if (cfg->compile_aot)
 		cfg->method_index = aot_method_index;
 		cfg->method_index = aot_method_index;
 
 
+	if (cfg->compile_llvm)
+		cfg->explicit_null_checks = TRUE;
+
 	/*
 	/*
 	if (!mono_debug_count ())
 	if (!mono_debug_count ())
 		cfg->opt &= ~MONO_OPT_FLOAT32;
 		cfg->opt &= ~MONO_OPT_FLOAT32;

+ 1 - 0
mono/mini/mini.h

@@ -1382,6 +1382,7 @@ typedef struct {
 	guint            ret_var_set : 1;
 	guint            ret_var_set : 1;
 	guint            unverifiable : 1;
 	guint            unverifiable : 1;
 	guint            skip_visibility : 1;
 	guint            skip_visibility : 1;
+	guint            disable_llvm_implicit_null_checks : 1;
 	guint            disable_reuse_registers : 1;
 	guint            disable_reuse_registers : 1;
 	guint            disable_reuse_stack_slots : 1;
 	guint            disable_reuse_stack_slots : 1;
 	guint            disable_reuse_ref_stack_slots : 1;
 	guint            disable_reuse_ref_stack_slots : 1;

+ 3 - 3
mono/tests/thread-suspend-suspended.cs

@@ -13,11 +13,11 @@ class Driver
 		AutoResetEvent finished_gc = new AutoResetEvent (false);
 		AutoResetEvent finished_gc = new AutoResetEvent (false);
 
 
 		Thread t1 = new Thread (() => {
 		Thread t1 = new Thread (() => {
-			while (!finished) {}
+			while (!Volatile.Read(ref finished)) {}
 		});
 		});
 
 
 		Thread t2 = new Thread (() => {
 		Thread t2 = new Thread (() => {
-			while (!finished) {
+			while (!Volatile.Read(ref finished)) {
 				if (start_gc.WaitOne (0)) {
 				if (start_gc.WaitOne (0)) {
 					GC.Collect ();
 					GC.Collect ();
 					finished_gc.Set ();
 					finished_gc.Set ();
@@ -44,7 +44,7 @@ class Driver
 				Console.WriteLine ();
 				Console.WriteLine ();
 		}
 		}
 
 
-		finished = true;
+		Volatile.Write(ref finished, true);
 
 
 		t1.Join ();
 		t1.Join ();
 		t2.Join ();
 		t2.Join ();

+ 1 - 1
scripts/ci/run-test-make-install.sh

@@ -11,5 +11,5 @@ export PATH=$MONO_PREFIX/bin:$PATH
 ${TESTCMD} --label=check-prefix-mcs --timeout=1m mcs /out:${MONO_REPO_ROOT}/tmp/hello.exe ${MONO_REPO_ROOT}/tmp/hello.cs 
 ${TESTCMD} --label=check-prefix-mcs --timeout=1m mcs /out:${MONO_REPO_ROOT}/tmp/hello.exe ${MONO_REPO_ROOT}/tmp/hello.cs 
 ${TESTCMD} --label=check-prefix-roslyn --timeout=1m csc /out:${MONO_REPO_ROOT}/tmp/hello.exe ${MONO_REPO_ROOT}/tmp/hello.cs
 ${TESTCMD} --label=check-prefix-roslyn --timeout=1m csc /out:${MONO_REPO_ROOT}/tmp/hello.exe ${MONO_REPO_ROOT}/tmp/hello.cs
 ${TESTCMD} --label=check-prefix-aot --timeout=1m mono --aot ${MONO_REPO_ROOT}/tmp/hello.exe
 ${TESTCMD} --label=check-prefix-aot --timeout=1m mono --aot ${MONO_REPO_ROOT}/tmp/hello.exe
-${TESTCMD} --label=check-prefix-llvmaot --timeout=1m mono --aot=llvm,llvm-path=/usr/lib/mono/llvm/bin ${MONO_REPO_ROOT}/tmp/hello.exe
+${TESTCMD} --label=check-prefix-llvmaot --timeout=1m mono --aot=llvm,llvm-path=${MONO_REPO_ROOT}/llvm/usr/bin ${MONO_REPO_ROOT}/tmp/hello.exe
 ${TESTCMD} --label=check-prefix-llvmjit --timeout=1m mono --llvm ${MONO_REPO_ROOT}/tmp/hello.exe
 ${TESTCMD} --label=check-prefix-llvmjit --timeout=1m mono --llvm ${MONO_REPO_ROOT}/tmp/hello.exe