Explorar el Código

Arithmetic overflow checks

Brian Fiete hace 3 años
padre
commit
eb375362a1

+ 6 - 0
IDE/src/BuildOptions.bf

@@ -102,6 +102,8 @@ namespace IDE
 		[Reflect]
 		public bool? mEmitObjectAccessCheck; // Only valid with mObjectHasDebugFlags
 		[Reflect]
+		public bool? mArithmeticCheck;
+		[Reflect]
 		public int32? mAllocStackTraceDepth;
 		[Reflect]
 		public BuildOptions.AlwaysIncludeKind mReflectAlwaysInclude;
@@ -135,6 +137,7 @@ namespace IDE
 			newVal.mInitLocalVariables = mInitLocalVariables;
 			newVal.mEmitDynamicCastCheck = mEmitDynamicCastCheck;
 			newVal.mEmitObjectAccessCheck = mEmitObjectAccessCheck;
+			newVal.mArithmeticCheck = mArithmeticCheck;
 			newVal.mAllocStackTraceDepth = mAllocStackTraceDepth;
 			newVal.mReflectAlwaysInclude = mReflectAlwaysInclude;
 			newVal.mReflectBoxing = mReflectBoxing;
@@ -164,6 +167,8 @@ namespace IDE
 				mEmitDynamicCastCheck = data.GetBool("EmitDynamicCastCheck");
 			if (data.Contains("EmitObjectAccessCheck"))
 				mEmitObjectAccessCheck = data.GetBool("EmitObjectAccessCheck");
+			if (data.Contains("ArithmeticCheck"))
+				mArithmeticCheck = data.GetBool("ArithmeticCheck");
 			if (data.Contains("AllocStackTraceDepth"))
 				mAllocStackTraceDepth = data.GetInt("AllocStackTraceDepth");
 
@@ -194,6 +199,7 @@ namespace IDE
 			data.ConditionalAdd("InitLocalVariables", mInitLocalVariables);
 			data.ConditionalAdd("EmitDynamicCastCheck", mEmitDynamicCastCheck);
 			data.ConditionalAdd("EmitObjectAccessCheck", mEmitObjectAccessCheck);
+			data.ConditionalAdd("ArithmeticCheck", mArithmeticCheck);
 			data.ConditionalAdd("AllocStackTraceDepth", mAllocStackTraceDepth);
 			data.ConditionalAdd("ReflectAlwaysInclude", mReflectAlwaysInclude);
 			data.ConditionalAdd("ReflectBoxing", mReflectBoxing);

+ 2 - 0
IDE/src/Compiler/BfCompiler.bf

@@ -38,6 +38,7 @@ namespace IDE.Compiler
 			DebugAlloc = 0x8000,
 			OmitDebugHelpers = 0x10000,
 			NoFramePointerElim = 0x20000,
+			ArithmeticChecks = 0x40000
 		}
 
         [CallingConvention(.Stdcall), CLink]
@@ -663,6 +664,7 @@ namespace IDE.Compiler
 			SetOpt(options.mEmitDynamicCastCheck, .EmitDynamicCastCheck);
 			SetOpt(enableObjectDebugFlags, .EnableObjectDebugFlags);
 			SetOpt(emitObjectAccessCheck, .EmitObjectAccessCheck);
+			SetOpt(options.mArithmeticCheck, .ArithmeticChecks);
 
 			if (options.LeakCheckingEnabled)
 				SetOpt(options.mEnableRealtimeLeakCheck, .EnableRealtimeLeakCheck);

+ 15 - 12
IDE/src/Compiler/BfSystem.bf

@@ -15,18 +15,19 @@ namespace IDE.Compiler
 			InitLocalVariables			= 2,
 			EmitDynamicCastCheck		= 4,
 			EmitObjectAccessCheck		= 8,
-
-			ReflectAlwaysIncludeType	= 0x10,
-			ReflectAlwaysIncludeAll		= 0x20,
-			ReflectAssumeInstantiated	= 0x40,
-			ReflectBoxing				= 0x80,
-			ReflectStaticFields			= 0x100,
-			ReflectNonStaticFields		= 0x200,
-			ReflectStaticMethods		= 0x400,
-			ReflectNonStaticMethods		= 0x800,
-			ReflectConstructors			= 0x1000,
-
-			All							= 0x1FFF
+			ArithmeticCheck				= 0x10,
+
+			ReflectAlwaysIncludeType	= 0x20,
+			ReflectAlwaysIncludeAll		= 0x40,
+			ReflectAssumeInstantiated	= 0x80,
+			ReflectBoxing				= 0x100,
+			ReflectStaticFields			= 0x200,
+			ReflectNonStaticFields		= 0x400,
+			ReflectStaticMethods		= 0x800,
+			ReflectNonStaticMethods		= 0x1000,
+			ReflectConstructors			= 0x2000,
+
+			All							= 0x3FFF
 		};
 
 		[CallingConvention(.Stdcall), CLink]
@@ -408,6 +409,8 @@ namespace IDE.Compiler
 			SetFlag(typeOption.mReflectStaticMethods, .ReflectStaticMethods);
 			SetFlag(typeOption.mReflectNonStaticMethods, .ReflectNonStaticMethods);
 			SetFlag(typeOption.mReflectConstructors, .ReflectConstructors);
+			SetFlag(typeOption.mEmitObjectAccessCheck, .EmitObjectAccessCheck);
+			SetFlag(typeOption.mArithmeticCheck, .ArithmeticCheck);
 
 			AddTypeOptions(typeOption.mFilter, typeOption.mBfSIMDSetting, typeOption.mBfOptimizationLevel, typeOption.mEmitDebugInfo, andFlags, orFlags, typeOption.mAllocStackTraceDepth, typeOption.mReflectMethodFilter);
 		}

+ 6 - 0
IDE/src/Workspace.bf

@@ -240,6 +240,8 @@ namespace IDE
 			[Reflect]
             public bool mEmitObjectAccessCheck; // Only valid with mObjectHasDebugFlags
 			[Reflect]
+			public bool mArithmeticCheck;
+			[Reflect]
             public bool mEnableRealtimeLeakCheck;
 			[Reflect]
             public bool mEnableSideStack;
@@ -297,6 +299,7 @@ namespace IDE
 				mEmitDynamicCastCheck = prev.mEmitDynamicCastCheck;
 				mEnableObjectDebugFlags = prev.mEnableObjectDebugFlags;
 				mEmitObjectAccessCheck = prev.mEmitObjectAccessCheck;
+				mArithmeticCheck = prev.mArithmeticCheck;
 				mEnableRealtimeLeakCheck = prev.mEnableRealtimeLeakCheck;
 				mEnableSideStack = prev.mEnableSideStack;
 				mAllowHotSwapping = prev.mAllowHotSwapping;
@@ -694,6 +697,7 @@ namespace IDE
                                 data.ConditionalAdd("EmitDynamicCastCheck", options.mEmitDynamicCastCheck, !isRelease);
                                 data.ConditionalAdd("EnableObjectDebugFlags", options.mEnableObjectDebugFlags, !isRelease);
                                 data.ConditionalAdd("EmitObjectAccessCheck", options.mEmitObjectAccessCheck, !isRelease);
+								data.ConditionalAdd("ArithmeticCheck", options.mArithmeticCheck, false);
                                 data.ConditionalAdd("EnableRealtimeLeakCheck", options.mEnableRealtimeLeakCheck, (platformType == .Windows) && !isRelease);
                                 data.ConditionalAdd("EnableSideStack", options.mEnableSideStack, (platformType == .Windows) && isParanoid);
 								data.ConditionalAdd("AllowHotSwapping", options.mAllowHotSwapping, (platformType == .Windows) && !isRelease);
@@ -888,6 +892,7 @@ namespace IDE
 			options.mEmitDynamicCastCheck = !isRelease;
 			options.mEnableObjectDebugFlags = !isRelease;
 			options.mEmitObjectAccessCheck = !isRelease;
+			options.mArithmeticCheck = false;
 
 			if (platformType == .Windows)
 			{
@@ -994,6 +999,7 @@ namespace IDE
                     options.mEmitDynamicCastCheck = data.GetBool("EmitDynamicCastCheck", !isRelease);
                     options.mEnableObjectDebugFlags = data.GetBool("EnableObjectDebugFlags", !isRelease);
                     options.mEmitObjectAccessCheck = data.GetBool("EmitObjectAccessCheck", !isRelease);
+					options.mArithmeticCheck = data.GetBool("ArithmeticCheck", false);
                     options.mEnableRealtimeLeakCheck = data.GetBool("EnableRealtimeLeakCheck", (platformType == .Windows) && !isRelease);
                     options.mEnableSideStack = data.GetBool("EnableSideStack", (platformType == .Windows) && isParanoid);
 					options.mAllowHotSwapping = data.GetBool("AllowHotSwapping", (platformType == .Windows) && !isRelease);

+ 4 - 0
IDE/src/ui/BuildPropertiesDialog.bf

@@ -143,6 +143,10 @@ namespace IDE.ui
 			AddPropertiesItem(category, "Object Access Check", typeName,
 				scope String[] ( "No", "Yes" ));
 
+			typeName.Clear(); typeName.Append(optionsName, "mArithmeticCheck");
+			AddPropertiesItem(category, "Arithmetic Check", typeName,
+				scope String[] ( "No", "Yes" ));
+
 			typeName.Clear(); typeName.Append(optionsName, "mAllocStackTraceDepth");
 			AddPropertiesItem(category, "Alloc Stack Trace Depth", typeName);
 

+ 2 - 0
IDE/src/ui/WorkspaceProperties.bf

@@ -796,6 +796,8 @@ namespace IDE.ui
                 scope String[] ( "No", "Yes" ));
             AddPropertiesItem(category, "Object Access Check", "mEmitObjectAccessCheck",
                 scope String[] ( "No", "Yes" ));
+			AddPropertiesItem(category, "Arithmetic Check", "mArithmeticCheck",
+				scope String[] ( "No", "Yes" ));
             AddPropertiesItem(category, "Realtime Leak Check", "mEnableRealtimeLeakCheck",
                 scope String[] ( "No", "Yes" ));
 			AddPropertiesItem(category, "Enable Hot Compilation", "mAllowHotSwapping",

+ 16 - 3
IDEHelper/Backend/BeIRCodeGen.cpp

@@ -590,6 +590,11 @@ void BeIRCodeGen::Read(bool& val)
 	BE_MEM_END("bool");
 }
 
+void BeIRCodeGen::Read(int8& val)
+{
+	val = mStream->Read();
+}
+
 void BeIRCodeGen::Read(BeIRTypeEntry*& type)
 {
 	BE_MEM_START;
@@ -1432,21 +1437,24 @@ void BeIRCodeGen::HandleNextCmd()
 		{
 			CMD_PARAM(BeValue*, lhs);
 			CMD_PARAM(BeValue*, rhs);			
-			SetResult(curId, mBeModule->CreateBinaryOp(BeBinaryOpKind_Add, lhs, rhs));
+			CMD_PARAM(int8, overflowCheckKind);
+			SetResult(curId, mBeModule->CreateBinaryOp(BeBinaryOpKind_Add, lhs, rhs, (BfOverflowCheckKind)overflowCheckKind));
 		}
 		break;
 	case BfIRCmd_Sub:
 		{
 			CMD_PARAM(BeValue*, lhs);
 			CMD_PARAM(BeValue*, rhs);
-			SetResult(curId, mBeModule->CreateBinaryOp(BeBinaryOpKind_Subtract, lhs, rhs));
+			CMD_PARAM(int8, overflowCheckKind);
+			SetResult(curId, mBeModule->CreateBinaryOp(BeBinaryOpKind_Subtract, lhs, rhs, (BfOverflowCheckKind)overflowCheckKind));
 		}
 		break;
 	case BfIRCmd_Mul:
 		{
 			CMD_PARAM(BeValue*, lhs);
 			CMD_PARAM(BeValue*, rhs);
-			SetResult(curId, mBeModule->CreateBinaryOp(BeBinaryOpKind_Multiply, lhs, rhs));
+			CMD_PARAM(int8, overflowCheckKind);
+			SetResult(curId, mBeModule->CreateBinaryOp(BeBinaryOpKind_Multiply, lhs, rhs, (BfOverflowCheckKind)overflowCheckKind));
 		}
 		break;
 	case BfIRCmd_SDiv:
@@ -2100,6 +2108,11 @@ void BeIRCodeGen::HandleNextCmd()
 			mBeModule->RemoveBlock(mActiveFunction, fromBlock);
 		}
 		break;	
+	case BfIRCmd_GetInsertBlock:
+		{
+			SetResult(curId, mBeModule->mActiveBlock);
+		}
+		break;
 	case BfIRCmd_SetInsertPoint:
 		{			
 			CMD_PARAM(BeBlock*, block);

+ 1 - 0
IDEHelper/Backend/BeIRCodeGen.h

@@ -119,6 +119,7 @@ public:
 	void Read(int64& i);
 	void Read(Val128& i);
 	void Read(bool& val);
+	void Read(int8& val);
 	void Read(BeIRTypeEntry*& type);
 	void Read(BeType*& beType);
 	void Read(BeFunctionType*& beType);

+ 143 - 18
IDEHelper/Backend/BeMCContext.cpp

@@ -6123,6 +6123,10 @@ uint8 BeMCContext::GetJumpOpCode(BeCmpKind cmpKind, bool isLong)
 			return 0x8D;
 		case BeCmpKind_UGE: // JAE
 			return 0x83;
+		case BeCmpKind_NB: // JNB
+			return 0x83;
+		case BeCmpKind_NO: // JNO
+			return 0x81;
 		}
 	}
 	else
@@ -6151,6 +6155,10 @@ uint8 BeMCContext::GetJumpOpCode(BeCmpKind cmpKind, bool isLong)
 			return 0x7D;
 		case BeCmpKind_UGE: // JAE
 			return 0x73;
+		case BeCmpKind_NB: // JNB
+			return 0x73;
+		case BeCmpKind_NO: // JNO
+			return 0x71;
 		}
 	}
 
@@ -10116,8 +10124,10 @@ bool BeMCContext::DoLegalization()
 				break;
 			case BeMCInstKind_Mul:
 			case BeMCInstKind_IMul:
-				{					
-					if (arg0Type->mSize == 1)
+				{
+					bool handled = false;
+
+					if ((arg0Type->mSize == 1) && (arg0Type->IsIntable()))
 					{
 						if ((!arg0.IsNativeReg()) || (arg0.mReg != X64Reg_AL) || (inst->mResult))
 						{
@@ -10152,8 +10162,54 @@ bool BeMCContext::DoLegalization()
 						}
 
 						BF_ASSERT(!inst->mResult);
+						handled = true;
 					}
-					else
+					else if ((inst->mKind == BeMCInstKind_Mul) && (arg0Type->IsIntable()))
+					{
+						auto wantReg0 = ResizeRegister(X64Reg_RAX, arg0Type->mSize);
+
+						if ((!arg0.IsNativeReg()) || (arg0.mReg != wantReg0) || (inst->mResult))
+						{
+							auto srcVRegInfo = GetVRegInfo(inst->mArg0);
+							// unsigned multiplies can only be done on AX/EAX/RAX
+							AllocInst(BeMCInstKind_PreserveVolatiles, BeMCOperand::FromReg(X64Reg_RAX), instIdx++);
+							AllocInst(BeMCInstKind_PreserveVolatiles, BeMCOperand::FromReg(X64Reg_RDX), instIdx++);
+
+							auto vregInfo0 = GetVRegInfo(inst->mArg0);
+							if (vregInfo0 != NULL)
+							{
+								vregInfo0->mDisableRAX = true;
+								vregInfo0->mDisableRDX = true;
+							}
+
+							auto vregInfo1 = GetVRegInfo(inst->mArg1);
+							if (vregInfo1 != NULL)
+							{
+								vregInfo1->mDisableRAX = true;
+								vregInfo1->mDisableRDX = true;
+							}
+							
+							AllocInst(BeMCInstKind_Mov, BeMCOperand::FromReg(wantReg0), inst->mArg0, instIdx++);
+							AllocInst(BeMCInstKind_Mov, inst->mResult ? inst->mResult : inst->mArg0, BeMCOperand::FromReg(wantReg0), instIdx++ + 1);
+							inst->mArg0 = BeMCOperand::FromReg(wantReg0);
+							inst->mResult = BeMCOperand();
+							AllocInst(BeMCInstKind_RestoreVolatiles, BeMCOperand::FromReg(X64Reg_RDX), instIdx++ + 1);
+							AllocInst(BeMCInstKind_RestoreVolatiles, BeMCOperand::FromReg(X64Reg_RAX), instIdx++ + 1);
+
+							isFinalRun = false;							
+							break;
+						}
+
+						if (inst->mArg1.IsImmediateInt())
+						{
+							ReplaceWithNewVReg(inst->mArg1, instIdx, true, false);
+						}
+
+						BF_ASSERT(!inst->mResult);
+						handled = true;
+					}
+					
+					if (handled)
 					{
 						if (inst->mResult)
 						{
@@ -14404,6 +14460,51 @@ void BeMCContext::DoCodeEmission()
 				}
 				break;
 			case BeMCInstKind_Mul:
+				{
+					if (arg0Type->IsIntable())
+					{
+						bool isValid = true;
+						auto typeCode = GetType(inst->mArg1)->mTypeCode;
+						switch (typeCode)
+						{
+						case BeTypeCode_Int8:
+							isValid = inst->mArg0 == BeMCOperand::FromReg(X64Reg_AL);
+							break;
+						case BeTypeCode_Int16:
+							isValid = inst->mArg0 == BeMCOperand::FromReg(X64Reg_AX);
+							break;
+						case BeTypeCode_Int32:
+							isValid = inst->mArg0 == BeMCOperand::FromReg(X64Reg_EAX);
+							break;
+						case BeTypeCode_Int64:
+							isValid = inst->mArg0 == BeMCOperand::FromReg(X64Reg_RAX);
+							break;
+						default:
+							isValid = false;
+						}
+						if (!isValid)
+							SoftFail("Invalid mul arguments");
+
+						switch (typeCode)
+						{
+						case BeTypeCode_Int8:
+							EmitREX(BeMCOperand(), inst->mArg1, false);
+							Emit(0xF6);
+							EmitModRM(4, inst->mArg1);
+							break;
+						case BeTypeCode_Int16: Emit(0x66); // Fallthrough
+						case BeTypeCode_Int32:
+						case BeTypeCode_Int64:
+							EmitREX(BeMCOperand(), inst->mArg1, typeCode == BeTypeCode_Int64);
+							Emit(0xF7); EmitModRM(4, inst->mArg1);
+							break;
+						default:
+							NotImpl();
+						}
+						break;
+					}
+				}
+				//Fallthrough
 			case BeMCInstKind_IMul:
 				{	
 					if (instForm == BeMCInstForm_XMM128_RM128)
@@ -14932,17 +15033,25 @@ void BeMCContext::DoCodeEmission()
 				break;
 			case BeMCInstKind_CondBr:
 				{
-					BF_ASSERT(inst->mArg0.mKind == BeMCOperandKind_Label);
-					BeMCJump jump;
-					jump.mCodeOffset = funcCodePos;
-					jump.mLabelIdx = inst->mArg0.mLabelIdx;
-					// Speculative make it a short jump
-					jump.mJumpKind = 0;
-					jump.mCmpKind = inst->mArg1.mCmpKind;
-					deferredJumps.push_back(jump);
-
-					mOut.Write(GetJumpOpCode(jump.mCmpKind, false));
-					mOut.Write((uint8)0);
+					if (inst->mArg0.mKind == BeMCOperandKind_Immediate_i64)
+					{
+						mOut.Write(GetJumpOpCode(inst->mArg1.mCmpKind, false));
+						mOut.Write((uint8)inst->mArg0.mImmediate);
+					}
+					else
+					{
+						BF_ASSERT(inst->mArg0.mKind == BeMCOperandKind_Label);
+						BeMCJump jump;
+						jump.mCodeOffset = funcCodePos;
+						jump.mLabelIdx = inst->mArg0.mLabelIdx;
+						// Speculative make it a short jump
+						jump.mJumpKind = 0;
+						jump.mCmpKind = inst->mArg1.mCmpKind;
+						deferredJumps.push_back(jump);
+
+						mOut.Write(GetJumpOpCode(jump.mCmpKind, false));
+						mOut.Write((uint8)0);
+					}
 				}
 				break;
 			case BeMCInstKind_Br:
@@ -15857,7 +15966,7 @@ void BeMCContext::Print(bool showVRegFlags, bool showVRegDetails)
 	OutputDebugStr(ToString(showVRegFlags, showVRegDetails));
 }
 
-BeMCOperand BeMCContext::AllocBinaryOp(BeMCInstKind instKind, const BeMCOperand& lhs, const BeMCOperand& rhs, BeMCBinIdentityKind identityKind)
+BeMCOperand BeMCContext::AllocBinaryOp(BeMCInstKind instKind, const BeMCOperand& lhs, const BeMCOperand& rhs, BeMCBinIdentityKind identityKind, BeMCOverflowCheckKind overflowCheckKind)
 {
 	if ((lhs.IsImmediate()) && (lhs.mKind == rhs.mKind))
 	{
@@ -15918,6 +16027,13 @@ BeMCOperand BeMCContext::AllocBinaryOp(BeMCInstKind instKind, const BeMCOperand&
 
 	auto mcInst = AllocInst(instKind, lhs, rhs);
 	mcInst->mResult = result;
+
+	if (overflowCheckKind != BeMCOverflowCheckKind_None)
+	{
+		AllocInst(BeMCInstKind_CondBr, BeMCOperand::FromImmediate(1), BeMCOperand::FromCmpKind((overflowCheckKind == BeMCOverflowCheckKind_B) ? BeCmpKind_NB : BeCmpKind_NO));
+		AllocInst(BeMCInstKind_DbgBreak);
+	}
+
 	return result;
 }
 
@@ -16399,9 +16515,18 @@ void BeMCContext::Generate(BeFunction* function)
 
 					switch (castedInst->mOpKind)
 					{
-					case BeBinaryOpKind_Add: result = AllocBinaryOp(BeMCInstKind_Add, mcLHS, mcRHS, BeMCBinIdentityKind_Any_IsZero); break;
-					case BeBinaryOpKind_Subtract: result = AllocBinaryOp(BeMCInstKind_Sub, mcLHS, mcRHS, BeMCBinIdentityKind_Right_IsZero); break;
-					case BeBinaryOpKind_Multiply: result = AllocBinaryOp(BeMCInstKind_IMul, mcLHS, mcRHS, BeMCBinIdentityKind_Any_IsOne); break;
+					case BeBinaryOpKind_Add: result = AllocBinaryOp(BeMCInstKind_Add, mcLHS, mcRHS, BeMCBinIdentityKind_Any_IsZero, 
+						((castedInst->mOverflowCheckKind & BfOverflowCheckKind_Signed) != 0) ? BeMCOverflowCheckKind_O : 
+						((castedInst->mOverflowCheckKind & BfOverflowCheckKind_Unsigned) != 0) ? BeMCOverflowCheckKind_B : BeMCOverflowCheckKind_None); 
+						break;
+					case BeBinaryOpKind_Subtract: result = AllocBinaryOp(BeMCInstKind_Sub, mcLHS, mcRHS, BeMCBinIdentityKind_Right_IsZero,
+						((castedInst->mOverflowCheckKind & BfOverflowCheckKind_Signed) != 0) ? BeMCOverflowCheckKind_O :
+						((castedInst->mOverflowCheckKind & BfOverflowCheckKind_Unsigned) != 0) ? BeMCOverflowCheckKind_B : BeMCOverflowCheckKind_None);
+						break;
+					case BeBinaryOpKind_Multiply: result = AllocBinaryOp(((castedInst->mOverflowCheckKind & BfOverflowCheckKind_Unsigned) != 0) ? BeMCInstKind_Mul : BeMCInstKind_IMul, mcLHS, mcRHS, BeMCBinIdentityKind_Any_IsOne,
+						((castedInst->mOverflowCheckKind & BfOverflowCheckKind_Signed) != 0) ? BeMCOverflowCheckKind_O :
+						((castedInst->mOverflowCheckKind & BfOverflowCheckKind_Unsigned) != 0) ? BeMCOverflowCheckKind_O : BeMCOverflowCheckKind_None);
+						break;
 					case BeBinaryOpKind_SDivide: result = AllocBinaryOp(BeMCInstKind_IDiv, mcLHS, mcRHS, BeMCBinIdentityKind_Right_IsOne); break;
 					case BeBinaryOpKind_UDivide: result = AllocBinaryOp(BeMCInstKind_Div, mcLHS, mcRHS, BeMCBinIdentityKind_Right_IsOne); break;
 					case BeBinaryOpKind_SModulus: result = AllocBinaryOp(BeMCInstKind_IRem, mcLHS, mcRHS, type->IsFloat() ? BeMCBinIdentityKind_None : BeMCBinIdentityKind_Right_IsOne_Result_Zero); break;

+ 8 - 1
IDEHelper/Backend/BeMCContext.h

@@ -1293,6 +1293,13 @@ struct BeRMParamsInfo
 	}
 };
 
+enum BeMCOverflowCheckKind
+{
+	BeMCOverflowCheckKind_None,
+	BeMCOverflowCheckKind_B,
+	BeMCOverflowCheckKind_O
+};
+
 // This class only processes one function per instantiation
 class BeMCContext
 {
@@ -1367,7 +1374,7 @@ public:
 	BeMCInst* AllocInst(BeMCInstKind instKind, const BeMCOperand& arg0, const BeMCOperand& arg1, int insertIdx = -1);
 	void MergeInstFlags(BeMCInst* prevInst, BeMCInst* inst, BeMCInst* nextInst);
 	void RemoveInst(BeMCBlock* block, int instIdx, bool needChangesMerged = true, bool removeFromList = true);
-	BeMCOperand AllocBinaryOp(BeMCInstKind instKind, const BeMCOperand & lhs, const BeMCOperand & rhs, BeMCBinIdentityKind identityKind);
+	BeMCOperand AllocBinaryOp(BeMCInstKind instKind, const BeMCOperand & lhs, const BeMCOperand & rhs, BeMCBinIdentityKind identityKind, BeMCOverflowCheckKind overflowCheckKind = BeMCOverflowCheckKind_None);
 	BeMCOperand GetCallArgVReg(int argIdx, BeTypeCode typeCode);
 	BeMCOperand CreateCall(const BeMCOperand& func, const SizedArrayImpl<BeMCOperand>& args, BeType* retType = NULL, BfIRCallingConv callingConv = BfIRCallingConv_CDecl, bool structRet = false, bool noReturn = false, bool isVarArg = false);
 	BeMCOperand CreateCall(const BeMCOperand& func, const SizedArrayImpl<BeValue*>& args, BeType* retType = NULL, BfIRCallingConv callingConv = BfIRCallingConv_CDecl, bool structRet = false, bool noReturn = false, bool isVarArg = false);

+ 4 - 1
IDEHelper/Backend/BeModule.cpp

@@ -1683,6 +1683,8 @@ void BeDumpContext::ToString(StringImpl& str, BeCmpKind cmpKind)
 	case BeCmpKind_UGT: str += "ugt"; return;
 	case BeCmpKind_SGE: str += "sge"; return;
 	case BeCmpKind_UGE: str += "uge"; return;
+	case BeCmpKind_NB: str += "nb"; return;
+	case BeCmpKind_NO: str += "no"; return;
 	default:
 		str += "???";
 	}		
@@ -3292,7 +3294,7 @@ BeCmpInst* BeModule::CreateCmp(BeCmpKind cmpKind, BeValue* lhs, BeValue* rhs)
 	return inst;
 }
 
-BeBinaryOpInst* BeModule::CreateBinaryOp(BeBinaryOpKind opKind, BeValue* lhs, BeValue* rhs)
+BeBinaryOpInst* BeModule::CreateBinaryOp(BeBinaryOpKind opKind, BeValue* lhs, BeValue* rhs, BfOverflowCheckKind overflowCheckKind)
 {
 #ifdef _DEBUG
 	auto leftType = lhs->GetType();
@@ -3303,6 +3305,7 @@ BeBinaryOpInst* BeModule::CreateBinaryOp(BeBinaryOpKind opKind, BeValue* lhs, Be
 	inst->mOpKind = opKind;
 	inst->mLHS = lhs;
 	inst->mRHS = rhs;
+	inst->mOverflowCheckKind = overflowCheckKind;
 	AddInst(inst);
 	return inst;
 }

+ 6 - 2
IDEHelper/Backend/BeModule.h

@@ -820,6 +820,7 @@ public:
 	BE_VALUE_TYPE(BeBinaryOpInst, BeInst);
 
 	BeBinaryOpKind mOpKind;
+	BfOverflowCheckKind mOverflowCheckKind;
 	BeValue* mLHS;
 	BeValue* mRHS;
 
@@ -829,6 +830,7 @@ public:
 	{
 		hashCtx.Mixin(TypeId);
 		hashCtx.Mixin(mOpKind);
+		hashCtx.Mixin(mOverflowCheckKind);
 		mLHS->HashReference(hashCtx);
 		mRHS->HashReference(hashCtx);
 	}
@@ -847,7 +849,9 @@ enum BeCmpKind
 	BeCmpKind_SGT,
 	BeCmpKind_UGT,
 	BeCmpKind_SGE,
-	BeCmpKind_UGE
+	BeCmpKind_UGE,
+	BeCmpKind_NB,
+	BeCmpKind_NO,
 };
 
 class BeCmpInst : public BeInst
@@ -2338,7 +2342,7 @@ public:
 	BeNumericCastInst* CreateNumericCast(BeValue* value, BeType* toType, bool valSigned, bool toSigned);	
 	BeBitCastInst* CreateBitCast(BeValue* value, BeType* toType);;
 	BeCmpInst* CreateCmp(BeCmpKind cmpKind, BeValue* lhs, BeValue* rhs);	
-	BeBinaryOpInst* CreateBinaryOp(BeBinaryOpKind opKind, BeValue* lhs, BeValue* rhs);
+	BeBinaryOpInst* CreateBinaryOp(BeBinaryOpKind opKind, BeValue* lhs, BeValue* rhs, BfOverflowCheckKind overflowCheckKind = BfOverflowCheckKind_None);
 
 	BeAllocaInst* CreateAlloca(BeType* type);
 	BeLoadInst* CreateLoad(BeValue* value, bool isVolatile);

+ 3 - 1
IDEHelper/Compiler/BfCompiler.cpp

@@ -9729,7 +9729,8 @@ BF_EXPORT void BF_CALLTYPE BfCompiler_SetOptions(BfCompiler* bfCompiler, BfProje
 // 		}
 		BF_ASSERT(!options->mEnableRealtimeLeakCheck);		
 #endif
-		options->mEmitObjectAccessCheck = (optionFlags & BfCompilerOptionFlag_EmitDebugInfo) != 0;
+		options->mEmitObjectAccessCheck = (optionFlags & BfCompilerOptionFlag_EmitObjectAccessCheck) != 0;
+		options->mArithmeticChecks = (optionFlags & BfCompilerOptionFlag_ArithmeticChecks) != 0;
 		options->mAllocStackCount = allocStackCount;
 		
 		if (hotProject != NULL)
@@ -9770,6 +9771,7 @@ BF_EXPORT void BF_CALLTYPE BfCompiler_SetOptions(BfCompiler* bfCompiler, BfProje
 		options->mObjectHasDebugFlags = false;
 		options->mEnableRealtimeLeakCheck = false;
 		options->mEmitObjectAccessCheck = false;		
+		options->mArithmeticChecks = false;
 		options->mEmitDynamicCastCheck = false;
 		options->mRuntimeChecks = (optionFlags & BfCompilerOptionFlag_RuntimeChecks) != 0;
 	}	

+ 3 - 1
IDEHelper/Compiler/BfCompiler.h

@@ -117,7 +117,8 @@ public:
 		bool mAllowHotSwapping;
 		bool mObjectHasDebugFlags;		
 		bool mEnableRealtimeLeakCheck;
-		bool mEmitObjectAccessCheck; // Only valid with mObjectHasDebugFlags		
+		bool mEmitObjectAccessCheck; // Only valid with mObjectHasDebugFlags
+		bool mArithmeticChecks;
 		bool mEnableCustodian;
 		bool mEnableSideStack;
 		bool mHasVDataExtender; 
@@ -162,6 +163,7 @@ public:
 			mEmitDynamicCastCheck = true;
 			mAllowHotSwapping = false;
 			mEmitObjectAccessCheck = false;
+			mArithmeticChecks = false;
 			mObjectHasDebugFlags = false;			
 			mEnableRealtimeLeakCheck = false;
 			mWriteIR = false;

+ 1 - 0
IDEHelper/Compiler/BfContext.cpp

@@ -2200,6 +2200,7 @@ void BfContext::UpdateRevisedTypes()
 		workspaceConfigHashCtx.Mixin(options->mObjectHasDebugFlags);
 		workspaceConfigHashCtx.Mixin(options->mEnableRealtimeLeakCheck);
 		workspaceConfigHashCtx.Mixin(options->mEmitObjectAccessCheck);
+		workspaceConfigHashCtx.Mixin(options->mArithmeticChecks);
 		workspaceConfigHashCtx.Mixin(options->mEnableCustodian);
 		workspaceConfigHashCtx.Mixin(options->mEnableSideStack);
 		workspaceConfigHashCtx.Mixin(options->mHasVDataExtender);

+ 24 - 4
IDEHelper/Compiler/BfExprEvaluator.cpp

@@ -19769,7 +19769,7 @@ void BfExprEvaluator::Visit(BfIndexerExpression* indexerExpr)
 
 	bool wantsChecks = checkedKind == BfCheckedKind_Checked;
 	if (checkedKind == BfCheckedKind_NotSet)
-		wantsChecks = mModule->GetDefaultCheckedKind() == BfCheckedKind_Checked;	
+		wantsChecks = mModule->GetDefaultCheckedKind() == BfCheckedKind_Checked;
 
 	//target.mType = mModule->ResolveGenericType(target.mType);
 	if (target.mType->IsVar())
@@ -22665,23 +22665,43 @@ void BfExprEvaluator::PerformBinaryOperation(BfType* resultType, BfIRValue convL
 		return;
 	}
 
+	auto _GetOverflowKind = [&](bool wantOverflow)
+	{
+		if (!wantOverflow)
+			return BfOverflowCheckKind_None;
+		if (mModule->GetDefaultCheckedKind() != BfCheckedKind_Checked)
+			return BfOverflowCheckKind_None;
+
+		bool arithmeticChecks = mModule->mCompiler->mOptions.mArithmeticChecks;
+		auto typeOptions = mModule->GetTypeOptions();
+		if (typeOptions != NULL)
+			arithmeticChecks = typeOptions->Apply(arithmeticChecks, BfOptionFlags_ArithmeticCheck);
+		if (!arithmeticChecks)
+			return BfOverflowCheckKind_None;
+
+		BfOverflowCheckKind overflowCheckKind = (resultType->IsSigned()) ? BfOverflowCheckKind_Signed : BfOverflowCheckKind_Unsigned;
+		if (!mModule->IsOptimized())
+			overflowCheckKind = (BfOverflowCheckKind)(overflowCheckKind | BfOverflowCheckKind_Flag_UseAsm);
+		return overflowCheckKind;
+	};
+
 	switch (binaryOp)
 	{
 	case BfBinaryOp_Add:
 	case BfBinaryOp_OverflowAdd:
-		mResult = BfTypedValue(mModule->mBfIRBuilder->CreateAdd(convLeftValue, convRightValue), resultType);		
+		mResult = BfTypedValue(mModule->mBfIRBuilder->CreateAdd(convLeftValue, convRightValue, _GetOverflowKind(binaryOp == BfBinaryOp_Add)), resultType);
 		if (binaryOp != BfBinaryOp_OverflowAdd)
 			mModule->CheckRangeError(resultType, opToken);
 		break;
 	case BfBinaryOp_Subtract:
 	case BfBinaryOp_OverflowSubtract:
-		mResult = BfTypedValue(mModule->mBfIRBuilder->CreateSub(convLeftValue, convRightValue), resultType);
+		mResult = BfTypedValue(mModule->mBfIRBuilder->CreateSub(convLeftValue, convRightValue, _GetOverflowKind(binaryOp == BfBinaryOp_Subtract)), resultType);
 		if (binaryOp != BfBinaryOp_OverflowSubtract)
 			mModule->CheckRangeError(resultType, opToken);
 		break;
 	case BfBinaryOp_Multiply:
 	case BfBinaryOp_OverflowMultiply:
-		mResult = BfTypedValue(mModule->mBfIRBuilder->CreateMul(convLeftValue, convRightValue), resultType);
+		mResult = BfTypedValue(mModule->mBfIRBuilder->CreateMul(convLeftValue, convRightValue, _GetOverflowKind(binaryOp == BfBinaryOp_Multiply)), resultType);
 		if (binaryOp != BfBinaryOp_OverflowMultiply)
 			mModule->CheckRangeError(resultType, opToken);
 		break;

+ 28 - 7
IDEHelper/Compiler/BfIRBuilder.cpp

@@ -4248,7 +4248,7 @@ BfIRValue BfIRBuilder::CreateCmpGTE(BfIRValue lhs, BfIRValue rhs, bool isSigned)
 	return retVal;
 }
 
-BfIRValue BfIRBuilder::CreateAdd(BfIRValue lhs, BfIRValue rhs)
+BfIRValue BfIRBuilder::CreateAdd(BfIRValue lhs, BfIRValue rhs, BfOverflowCheckKind overflowCheckKind)
 {
 	mOpFailed = false;
 	if ((lhs.IsConst()) && (rhs.IsConst()))
@@ -4260,12 +4260,19 @@ BfIRValue BfIRBuilder::CreateAdd(BfIRValue lhs, BfIRValue rhs)
 		}
 	}
 	 
-	auto retVal = WriteCmd(BfIRCmd_Add, lhs, rhs);
+	auto retVal = WriteCmd(BfIRCmd_Add, lhs, rhs, overflowCheckKind);
 	NEW_CMD_INSERTED_IRVALUE;
+
+	if ((overflowCheckKind != BfOverflowCheckKind_None) && (!mIgnoreWrites))
+	{
+		mInsertBlock = mActualInsertBlock = WriteCmd(BfIRCmd_GetInsertBlock);
+		NEW_CMD_INSERTED_IRVALUE;
+	}
+
 	return retVal;
 }
 
-BfIRValue BfIRBuilder::CreateSub(BfIRValue lhs, BfIRValue rhs)
+BfIRValue BfIRBuilder::CreateSub(BfIRValue lhs, BfIRValue rhs, BfOverflowCheckKind overflowCheckKind)
 {
 	mOpFailed = false;
 	if ((lhs.IsConst()) && (rhs.IsConst()))
@@ -4273,12 +4280,19 @@ BfIRValue BfIRBuilder::CreateSub(BfIRValue lhs, BfIRValue rhs)
 		BINOPFUNC_APPLY(lhs, rhs, CheckedSub);
 	}
 
-	auto retVal = WriteCmd(BfIRCmd_Sub, lhs, rhs);
+	auto retVal = WriteCmd(BfIRCmd_Sub, lhs, rhs, overflowCheckKind);
 	NEW_CMD_INSERTED;
+
+	if ((overflowCheckKind != BfOverflowCheckKind_None) && (!mIgnoreWrites))
+	{
+		mInsertBlock = mActualInsertBlock = WriteCmd(BfIRCmd_GetInsertBlock);
+		NEW_CMD_INSERTED_IRVALUE;
+	}
+
 	return retVal;
 }
 
-BfIRValue BfIRBuilder::CreateMul(BfIRValue lhs, BfIRValue rhs)
+BfIRValue BfIRBuilder::CreateMul(BfIRValue lhs, BfIRValue rhs, BfOverflowCheckKind overflowCheckKind)
 {
 	mOpFailed = false;
 	if ((lhs.IsConst()) && (rhs.IsConst()))
@@ -4286,8 +4300,15 @@ BfIRValue BfIRBuilder::CreateMul(BfIRValue lhs, BfIRValue rhs)
 		BINOPFUNC_APPLY(lhs, rhs, CheckedMul);
 	}
 
-	auto retVal = WriteCmd(BfIRCmd_Mul, lhs, rhs);
+	auto retVal = WriteCmd(BfIRCmd_Mul, lhs, rhs, overflowCheckKind);
 	NEW_CMD_INSERTED_IRVALUE;
+
+	if ((overflowCheckKind != BfOverflowCheckKind_None) && (!mIgnoreWrites))
+	{
+		mInsertBlock = mActualInsertBlock = WriteCmd(BfIRCmd_GetInsertBlock);
+		NEW_CMD_INSERTED_IRVALUE;
+	}
+
 	return retVal;
 }
 
@@ -5394,7 +5415,7 @@ void BfIRBuilder::CreateStatementStart()
 }
 
 void BfIRBuilder::CreateObjectAccessCheck(BfIRValue value, bool useAsm)
-{	
+{
 	auto retBlock = WriteCmd(BfIRCmd_ObjectAccessCheck, value, useAsm);	
 	NEW_CMD_INSERTED_IRBLOCK;
 	if (!mIgnoreWrites)

+ 13 - 4
IDEHelper/Compiler/BfIRBuilder.h

@@ -174,7 +174,7 @@ enum BfIRCmd : uint8
 
 	BfIRCmd_SetName,
 	BfIRCmd_CreateUndefValue,
-	BfIRCmd_NumericCast,	
+	BfIRCmd_NumericCast,
 	BfIRCmd_CmpEQ,
 	BfIRCmd_CmpNE,
 	BfIRCmd_CmpSLT,
@@ -247,6 +247,7 @@ enum BfIRCmd : uint8
 	BfIRCmd_AddBlock,	
 	BfIRCmd_DropBlocks,
 	BfIRCmd_MergeBlockDown,	
+	BfIRCmd_GetInsertBlock,
 	BfIRCmd_SetInsertPoint,
 	BfIRCmd_SetInsertPointAtStart,
 	BfIRCmd_EraseFromParent,
@@ -964,6 +965,14 @@ struct BfIRState
 	Array<BfFilePosition> mSavedDebugLocs;
 };
 
+enum BfOverflowCheckKind : int8
+{
+	BfOverflowCheckKind_None = 0,
+	BfOverflowCheckKind_Signed = 1,
+	BfOverflowCheckKind_Unsigned = 2,
+	BfOverflowCheckKind_Flag_UseAsm = 4
+};
+
 class BfIRBuilder : public BfIRConstHolder
 {
 public:	
@@ -1182,9 +1191,9 @@ public:
 	BfIRValue CreateCmpLTE(BfIRValue lhs, BfIRValue rhs, bool isSigned);
 	BfIRValue CreateCmpGT(BfIRValue lhs, BfIRValue rhs, bool isSigned);
 	BfIRValue CreateCmpGTE(BfIRValue lhs, BfIRValue rhs, bool isSigned);
-	BfIRValue CreateAdd(BfIRValue lhs, BfIRValue rhs);
-	BfIRValue CreateSub(BfIRValue lhs, BfIRValue rhs);
-	BfIRValue CreateMul(BfIRValue lhs, BfIRValue rhs);
+	BfIRValue CreateAdd(BfIRValue lhs, BfIRValue rhs, BfOverflowCheckKind overflowCheckKind = BfOverflowCheckKind_None);
+	BfIRValue CreateSub(BfIRValue lhs, BfIRValue rhs, BfOverflowCheckKind overflowCheckKind = BfOverflowCheckKind_None);
+	BfIRValue CreateMul(BfIRValue lhs, BfIRValue rhs, BfOverflowCheckKind overflowCheckKind = BfOverflowCheckKind_None);
 	BfIRValue CreateDiv(BfIRValue lhs, BfIRValue rhs, bool isSigned);
 	BfIRValue CreateRem(BfIRValue lhs, BfIRValue rhs, bool isSigned);
 	BfIRValue CreateAnd(BfIRValue lhs, BfIRValue rhs);

+ 97 - 7
IDEHelper/Compiler/BfIRCodeGen.cpp

@@ -340,7 +340,8 @@ BfIRCodeGen::BfIRCodeGen()
 	mLLVMTargetMachine = NULL;
 
 	mNopInlineAsm = NULL;
-	mAsmObjectCheckAsm = NULL;
+	mObjectCheckAsm = NULL;
+	mOverflowCheckAsm = NULL;
 	mHasDebugLoc = false;
 	mAttrSet = NULL;
 	mIRBuilder = NULL;
@@ -730,6 +731,11 @@ void BfIRCodeGen::Read(bool& val)
 	val = mStream->Read() != 0;
 }
 
+void BfIRCodeGen::Read(int8& val)
+{
+	val = mStream->Read();
+}
+
 void BfIRCodeGen::Read(BfIRTypeEntry*& type)
 {
 	int typeId = (int)ReadSLEB128();
@@ -1447,6 +1453,72 @@ llvm::Value* BfIRCodeGen::FixGEP(llvm::Value* fromValue, llvm::Value* result)
 	return result;
 }
 
+llvm::Value* BfIRCodeGen::DoCheckedIntrinsic(llvm::Intrinsic::ID intrin, llvm::Value* lhs, llvm::Value* rhs, bool useAsm)
+{	
+	if ((mTargetTriple.GetMachineType() != BfMachineType_x86) && (mTargetTriple.GetMachineType() != BfMachineType_x64))
+		useAsm = false;
+
+	CmdParamVec<llvm::Type*> useParams;
+	useParams.push_back(lhs->getType());
+	auto func = llvm::Intrinsic::getDeclaration(mLLVMModule, intrin, useParams);
+
+	CmdParamVec<llvm::Value*> args;
+	args.push_back(lhs);
+	args.push_back(rhs);
+	llvm::FunctionType* funcType = NULL;
+	if (auto ptrType = llvm::dyn_cast<llvm::PointerType>(func->getType()))
+		funcType = llvm::dyn_cast<llvm::FunctionType>(ptrType->getElementType());
+	auto aggResult = mIRBuilder->CreateCall(funcType, func, args);
+	auto valResult = mIRBuilder->CreateExtractValue(aggResult, 0);
+	auto failResult = mIRBuilder->CreateExtractValue(aggResult, 1);
+
+	if (!useAsm)
+	{
+		mLockedBlocks.Add(mIRBuilder->GetInsertBlock());
+
+		auto failBB = llvm::BasicBlock::Create(*mLLVMContext, "access.fail");
+		auto passBB = llvm::BasicBlock::Create(*mLLVMContext, "access.pass");
+
+		mIRBuilder->CreateCondBr(failResult, failBB, passBB);
+
+		mActiveFunction->getBasicBlockList().push_back(failBB);
+		mIRBuilder->SetInsertPoint(failBB);
+
+		auto trapDecl = llvm::Intrinsic::getDeclaration(mLLVMModule, llvm::Intrinsic::trap);
+		auto callInst = mIRBuilder->CreateCall(trapDecl);
+		callInst->addAttribute(llvm::AttributeList::FunctionIndex, llvm::Attribute::NoReturn);
+		mIRBuilder->CreateBr(passBB);
+
+		mActiveFunction->getBasicBlockList().push_back(passBB);
+		mIRBuilder->SetInsertPoint(passBB);
+	}
+	else
+	{
+		if (mOverflowCheckAsm == NULL)
+		{
+			std::vector<llvm::Type*> paramTypes;
+			paramTypes.push_back(llvm::Type::getInt8Ty(*mLLVMContext));
+			auto funcType = llvm::FunctionType::get(llvm::Type::getVoidTy(*mLLVMContext), paramTypes, false);
+
+			String asmStr =
+				"testb $$1, $0\n"
+				"jz 1f\n"
+				"int $$3\n"
+				"1:";
+
+			mOverflowCheckAsm = llvm::InlineAsm::get(funcType,
+				asmStr.c_str(), "r,~{dirflag},~{fpsr},~{flags}", true,
+				false, llvm::InlineAsm::AD_ATT);
+		}
+
+		llvm::SmallVector<llvm::Value*, 1> llvmArgs;
+		llvmArgs.push_back(mIRBuilder->CreateIntCast(failResult, llvm::Type::getInt8Ty(*mLLVMContext), false));
+		llvm::CallInst* callInst = mIRBuilder->CreateCall(mOverflowCheckAsm, llvmArgs);
+		callInst->addAttribute(llvm::AttributeList::FunctionIndex, llvm::Attribute::NoUnwind);
+	}
+	return valResult;
+}
+
 void BfIRCodeGen::CreateMemSet(llvm::Value* addr, llvm::Value* val, llvm::Value* size, int alignment, bool isVolatile)
 {
 	auto sizeConst = llvm::dyn_cast<llvm::ConstantInt>(size);
@@ -2040,22 +2112,31 @@ void BfIRCodeGen::HandleNextCmd()
 				SetResult(curId, mIRBuilder->CreateICmpUGE(lhs, rhs));
 		}
 		break;
+	
 	case BfIRCmd_Add:
 		{
 			CMD_PARAM(llvm::Value*, lhs);
 			CMD_PARAM(llvm::Value*, rhs);
+			CMD_PARAM(int8, overflowCheckKind);
 			if (lhs->getType()->isFloatingPointTy())
 				SetResult(curId, mIRBuilder->CreateFAdd(lhs, rhs));
+			else if ((overflowCheckKind & (BfOverflowCheckKind_Signed | BfOverflowCheckKind_Unsigned)) != 0)
+				SetResult(curId, DoCheckedIntrinsic(((overflowCheckKind & BfOverflowCheckKind_Signed) != 0) ? llvm::Intrinsic::sadd_with_overflow : llvm::Intrinsic::uadd_with_overflow,
+					lhs, rhs, (overflowCheckKind & BfOverflowCheckKind_Flag_UseAsm) != 0));
 			else
-				SetResult(curId, mIRBuilder->CreateAdd(lhs, rhs));
+				SetResult(curId, mIRBuilder->CreateAdd(lhs, rhs));			
 		}
 		break;
 	case BfIRCmd_Sub:
 		{
 			CMD_PARAM(llvm::Value*, lhs);
 			CMD_PARAM(llvm::Value*, rhs);
+			CMD_PARAM(int8, overflowCheckKind);
 			if (lhs->getType()->isFloatingPointTy())
 				SetResult(curId, mIRBuilder->CreateFSub(lhs, rhs));
+			else if ((overflowCheckKind & (BfOverflowCheckKind_Signed | BfOverflowCheckKind_Unsigned)) != 0)
+				SetResult(curId, DoCheckedIntrinsic(((overflowCheckKind & BfOverflowCheckKind_Signed) != 0) ? llvm::Intrinsic::ssub_with_overflow : llvm::Intrinsic::usub_with_overflow,
+					lhs, rhs, (overflowCheckKind & BfOverflowCheckKind_Flag_UseAsm) != 0));
 			else
 				SetResult(curId, mIRBuilder->CreateSub(lhs, rhs));
 		}
@@ -2064,8 +2145,12 @@ void BfIRCodeGen::HandleNextCmd()
 		{
 			CMD_PARAM(llvm::Value*, lhs);
 			CMD_PARAM(llvm::Value*, rhs);
+			CMD_PARAM(int8, overflowCheckKind);
 			if (lhs->getType()->isFloatingPointTy())
 				SetResult(curId, mIRBuilder->CreateFMul(lhs, rhs));
+			else if ((overflowCheckKind & (BfOverflowCheckKind_Signed | BfOverflowCheckKind_Unsigned)) != 0)
+				SetResult(curId, DoCheckedIntrinsic(((overflowCheckKind & BfOverflowCheckKind_Signed) != 0) ? llvm::Intrinsic::smul_with_overflow : llvm::Intrinsic::umul_with_overflow,
+					lhs, rhs, (overflowCheckKind & BfOverflowCheckKind_Flag_UseAsm) != 0));
 			else
 				SetResult(curId, mIRBuilder->CreateMul(lhs, rhs));
 		}
@@ -2528,7 +2613,12 @@ void BfIRCodeGen::HandleNextCmd()
 			intoInstList.splice(intoInstList.begin(), fromInstList, fromInstList.begin(), fromInstList.end());
 			fromBlock->eraseFromParent();
 		}
-		break;	
+		break;
+	case BfIRCmd_GetInsertBlock:
+		{
+			SetResult(curId, mIRBuilder->GetInsertBlock());
+		}
+		break;
 	case BfIRCmd_SetInsertPoint:
 		{			
 			CMD_PARAM(llvm::BasicBlock*, block);
@@ -2542,7 +2632,7 @@ void BfIRCodeGen::HandleNextCmd()
 			CMD_PARAM(llvm::BasicBlock*, block);
 			mIRBuilder->SetInsertPoint(block, block->begin());
 		}
-		break;
+		break;		
 	case BfIRCmd_EraseFromParent:
 		{
 			CMD_PARAM(llvm::BasicBlock*, block);
@@ -3906,7 +3996,7 @@ void BfIRCodeGen::HandleNextCmd()
 			else
 			{
 				llvm::Type* voidPtrType = llvm::Type::getInt8PtrTy(*mLLVMContext);
-				if (mAsmObjectCheckAsm == NULL)
+				if (mObjectCheckAsm == NULL)
 				{
 					std::vector<llvm::Type*> paramTypes;
 					paramTypes.push_back(voidPtrType);
@@ -3918,7 +4008,7 @@ void BfIRCodeGen::HandleNextCmd()
 						"int $$3\n"
 						"1:";
 
-					mAsmObjectCheckAsm = llvm::InlineAsm::get(funcType,
+					mObjectCheckAsm = llvm::InlineAsm::get(funcType,
 						asmStr.c_str(), "r,~{dirflag},~{fpsr},~{flags}", true,
 						false, llvm::InlineAsm::AD_ATT);
 				}
@@ -3926,7 +4016,7 @@ void BfIRCodeGen::HandleNextCmd()
 				llvm::SmallVector<llvm::Value*, 1> llvmArgs;
 				llvmArgs.push_back(mIRBuilder->CreateBitCast(val, voidPtrType));
 
-				llvm::CallInst* callInst = irBuilder->CreateCall(mAsmObjectCheckAsm, llvmArgs);
+				llvm::CallInst* callInst = irBuilder->CreateCall(mObjectCheckAsm, llvmArgs);
 				callInst->addAttribute(llvm::AttributeList::FunctionIndex, llvm::Attribute::NoUnwind);
 
 				SetResult(curId, mIRBuilder->GetInsertBlock());

+ 5 - 2
IDEHelper/Compiler/BfIRCodeGen.h

@@ -20,7 +20,7 @@ namespace llvm
 	class AttributeList;
 	class Module;
 	class LLVMContext;
-	class TargetMachine;
+	class TargetMachine;	
 };
 
 NS_BF_BEGIN
@@ -100,7 +100,8 @@ public:
 	llvm::TargetMachine* mLLVMTargetMachine;
 	Array<llvm::DebugLoc> mSavedDebugLocs;
 	llvm::InlineAsm* mNopInlineAsm;
-	llvm::InlineAsm* mAsmObjectCheckAsm;	
+	llvm::InlineAsm* mObjectCheckAsm;
+	llvm::InlineAsm* mOverflowCheckAsm;
 	llvm::DebugLoc mDebugLoc;
 	BfCodeGenOptions mCodeGenOptions;
 	bool mHasDebugLoc;	
@@ -140,6 +141,7 @@ public:
 	llvm::Type* GetSizeAlignedType(BfIRTypeEntry* typeEntry);
 	llvm::Value* GetAlignedPtr(llvm::Value* val);
 	llvm::Value* FixGEP(llvm::Value* fromValue, llvm::Value* result);
+	llvm::Value* DoCheckedIntrinsic(llvm::Intrinsic::ID intrin, llvm::Value* lhs, llvm::Value* rhs, bool useAsm);
 
 public:
 	BfIRCodeGen();
@@ -158,6 +160,7 @@ public:
 	void Read(int64& i);
 	void Read(Val128& i);
 	void Read(bool& val);
+	void Read(int8& val);
 	void Read(BfIRTypeEntry*& type);
 	void Read(llvm::Type*& llvmType, BfIRTypeEntry** outTypeEntry = NULL);
 	void Read(llvm::FunctionType*& llvmType);

+ 13 - 11
IDEHelper/Compiler/BfSystem.h

@@ -160,6 +160,7 @@ enum BfCompilerOptionFlags
 	BfCompilerOptionFlag_DebugAlloc         = 0x8000,
 	BfCompilerOptionFlag_OmitDebugHelpers   = 0x10000,
 	BfCompilerOptionFlag_NoFramePointerElim = 0x20000,
+	BfCompilerOptionFlag_ArithmeticChecks = 0x40000,
 };
 
 enum BfTypeFlags
@@ -1441,19 +1442,20 @@ enum BfOptionFlags
 	BfOptionFlags_InitLocalVariables		= 2,
 	BfOptionFlags_EmitDynamicCastCheck		= 4,
 	BfOptionFlags_EmitObjectAccessCheck		= 8,
-
-	BfOptionFlags_ReflectAlwaysIncludeType	= 0x10,
-	BfOptionFlags_ReflectAlwaysIncludeAll	= 0x20,
-	BfOptionFlags_ReflectAssumeInstantiated	= 0x40,
-	BfOptionFlags_ReflectBoxing				= 0x80,
-	BfOptionFlags_ReflectStaticFields		= 0x100,
-	BfOptionFlags_ReflectNonStaticFields	= 0x200,
-	BfOptionFlags_ReflectStaticMethods		= 0x400,
-	BfOptionFlags_ReflectNonStaticMethods	= 0x800,
-	BfOptionFlags_ReflectConstructors		= 0x1000,
+	BfOptionFlags_ArithmeticCheck			= 0x10,
+
+	BfOptionFlags_ReflectAlwaysIncludeType	= 0x20,
+	BfOptionFlags_ReflectAlwaysIncludeAll	= 0x40,
+	BfOptionFlags_ReflectAssumeInstantiated	= 0x80,
+	BfOptionFlags_ReflectBoxing				= 0x100,
+	BfOptionFlags_ReflectStaticFields		= 0x200,
+	BfOptionFlags_ReflectNonStaticFields	= 0x400,
+	BfOptionFlags_ReflectStaticMethods		= 0x800,
+	BfOptionFlags_ReflectNonStaticMethods	= 0x1000,
+	BfOptionFlags_ReflectConstructors		= 0x2000,
 
 	BfOptionFlags_Reflect_MethodMask		= BfOptionFlags_ReflectStaticMethods | BfOptionFlags_ReflectNonStaticMethods | BfOptionFlags_ReflectConstructors,
-	BfOptionFlags_Mask = 0xFFF
+	BfOptionFlags_Mask = 0x3FFF
 
 };
 

+ 7 - 0
IDEHelper/DebugCommon.h

@@ -61,4 +61,11 @@ enum DbgAddrType : uint8
 	DbgAddrType_Alias
 };
 
+enum DbgBreakKind
+{
+	DbgBreakKind_None,
+	DbgBreakKind_ObjectAccess,
+	DbgBreakKind_ArithmeticOverflow
+};
+
 NS_BF_END

+ 3 - 3
IDEHelper/DebugTarget.cpp

@@ -2449,18 +2449,18 @@ bool DebugTarget::DecodeInstruction(addr_target address, CPUInst* inst)
 	
 }
 
-bool DebugTarget::IsObjectAccessBreak(addr_target address, CPURegisters* registers, intptr_target* objAddr)
+DbgBreakKind DebugTarget::GetDbgBreakKind(addr_target address, CPURegisters* registers, intptr_target* objAddr)
 {
 	for (auto dwarf : mDbgModules)
 	{
 		if ((address >= dwarf->mImageBase) && (address < dwarf->mImageBase + dwarf->mImageSize) && (dwarf->mOrigImageData != NULL))
 		{			
-			bool result = mDebugger->mCPU->IsObjectAccessBreak(address, dwarf->mOrigImageData, registers->mIntRegsArray, objAddr);			
+			auto result = mDebugger->mCPU->GetDbgBreakKind(address, dwarf->mOrigImageData, registers->mIntRegsArray, objAddr);
 			return result;			
 		}
 	}
 
-	return false;
+	return DbgBreakKind_None;
 
 }
 

+ 1 - 1
IDEHelper/DebugTarget.h

@@ -111,7 +111,7 @@ public:
 
 	const DbgMemoryFlags ReadOrigImageData(addr_target address, uint8* data, int size);
 	bool DecodeInstruction(addr_target address, CPUInst* inst);
-	bool IsObjectAccessBreak(addr_target address, CPURegisters* regs, intptr_target* objAddr);
+	DbgBreakKind GetDbgBreakKind(addr_target address, CPURegisters* regs, intptr_target* objAddr);
 	DbgModule* FindDbgModuleForAddress(addr_target address);
 	DbgModule* GetMainDbgModule();
 	void ReportMemory(MemReporter* memReporter);

+ 8 - 1
IDEHelper/WinDebugger.cpp

@@ -4866,7 +4866,8 @@ void WinDebugger::CheckNonDebuggerBreak()
 	}
 
 	intptr_target objAddr;
-	if (mDebugTarget->IsObjectAccessBreak(pcAddress, &registers, &objAddr))
+	auto dbgBreakKind = mDebugTarget->GetDbgBreakKind(pcAddress, &registers, &objAddr);
+	if (dbgBreakKind == DbgBreakKind_ObjectAccess)
 	{
 		String errorStr = "error Attempted to access deleted object";
 		String objectAddr = EncodeDataPtr((addr_target)objAddr, true);
@@ -4874,6 +4875,12 @@ void WinDebugger::CheckNonDebuggerBreak()
 		mDebugManager->mOutMessages.push_back(errorStr);
 		return;
 	}
+	else if (dbgBreakKind == DbgBreakKind_ArithmeticOverflow)
+	{
+		String errorStr = "error Arithmetic overflow detected";
+		mDebugManager->mOutMessages.push_back(errorStr);
+		return;
+	}
 
 	bool showMainThread = false;
 

+ 41 - 3
IDEHelper/X64.cpp

@@ -985,7 +985,7 @@ String X64CPU::InstructionToString(X64Instr* inst, uint64 addr)
 	return result;
 }
 
-bool X64CPU::IsObjectAccessBreak(uint64 address, DbgModuleMemoryCache* memoryCache, int64* regs, int64* outObjectPtr)
+DbgBreakKind X64CPU::GetDbgBreakKind(uint64 address, DbgModuleMemoryCache* memoryCache, int64* regs, int64* outObjectPtr)
 {	
 	// We've looking for a CMP BYTE PTR [<reg>], -0x80
 	//  if <reg> is R12 then encoding takes an extra 2 bytes
@@ -1005,6 +1005,19 @@ bool X64CPU::IsObjectAccessBreak(uint64 address, DbgModuleMemoryCache* memoryCac
 			continue;
 
 		auto immediateType = (instDesc.TSFlags & llvm::X86II::ImmMask);
+		if ((immediateType == llvm::X86II::Imm8) && (inst.mMCInst.getNumOperands() == 2))
+		{
+			// We're checking for a TEST [<reg>], 1
+			if (inst.mMCInst.getOpcode() != llvm::X86::TEST8ri)
+				continue;
+			auto immOp = inst.mMCInst.getOperand(1);
+			if (!immOp.isImm())
+				continue;
+			if (immOp.getImm() != 1)
+				continue;
+			return DbgBreakKind_ArithmeticOverflow;
+		}
+
 		if ((immediateType == 0) || (inst.mMCInst.getNumOperands() < 6))
 			continue;
 
@@ -1027,10 +1040,35 @@ bool X64CPU::IsObjectAccessBreak(uint64 address, DbgModuleMemoryCache* memoryCac
 
 		*outObjectPtr = (uint64)regs[regNum];
 
-		return true;
+		return DbgBreakKind_ObjectAccess;
 	}
 
-	return false;
+	// check jno/jnb
+	for (int offset = 3; offset <= 3; offset++)
+	{
+		if (!Decode(address - offset, memoryCache, &inst))
+			continue;	
+
+		if (inst.GetLength() != 2)
+			continue;
+
+		const MCInstrDesc &instDesc = mInstrInfo->get(inst.mMCInst.getOpcode());		
+		if (!instDesc.isBranch())
+			continue;
+
+		auto immediateType = (instDesc.TSFlags & llvm::X86II::ImmMask);
+		if ((immediateType == llvm::X86II::Imm8PCRel) && (inst.mMCInst.getNumOperands() == 2))
+		{			
+			auto immOp = inst.mMCInst.getOperand(1);
+			if (!immOp.isImm())
+				continue;
+			if ((immOp.getImm() != 1) && (immOp.getImm() != 3))
+				continue;
+			return DbgBreakKind_ArithmeticOverflow;
+		}
+	}
+
+	return DbgBreakKind_None;
 }
 
 int X64CPU::GetOpcodesForMnemonic(const StringImpl& mnemonic, Array<int>& outOpcodes)

+ 1 - 1
IDEHelper/X64.h

@@ -508,7 +508,7 @@ public:
 	String InstructionToString(X64Instr* inst, uint64 addr);
 
 	void GetNextPC(uint64 baseAddress, const uint8* dataBase, int dataLength, const uint8* dataPtr, uint32* regs, uint64 nextPCs[2]);
-	bool IsObjectAccessBreak(uint64 address, DbgModuleMemoryCache* memoryCache, int64* regs, int64* outObjectPtr);
+	DbgBreakKind GetDbgBreakKind(uint64 address, DbgModuleMemoryCache* memoryCache, int64* regs, int64* outObjectPtr);
 
 	int GetOpcodesForMnemonic(const StringImpl& mnemonic, Array<int>& outOpcodes);
 	void GetClobbersForMnemonic(const StringImpl& mnemonic, int argCount, Array<int>& outImplicitClobberRegNums, int& outClobberArgCount, bool& outMayClobberMem);

+ 45 - 7
IDEHelper/X86.cpp

@@ -575,26 +575,39 @@ String X86CPU::InstructionToString(X86Instr* inst, uint32 addr)
 	return result;
 }
 
-bool X86CPU::IsObjectAccessBreak(uint32 address, DbgModuleMemoryCache* memoryCache, int32* regs, int32* outObjectPtr)
+DbgBreakKind X86CPU::GetDbgBreakKind(uint32 address, DbgModuleMemoryCache* memoryCache, int32* regs, int32* outObjectPtr)
 {
 	// We've looking for a CMP BYTE PTR [<reg>], -0x80
 	//  if <reg> is R12 then encoding takes an extra 2 bytes
-	X86Instr inst;	
+	X86Instr inst;
 	for (int checkLen = 5; checkLen >= 3; checkLen--)
-	{		
+	{
 		int offset = -3 - checkLen;
 
 		if (!Decode(address + offset, memoryCache, &inst))
-			continue;	
+			continue;
 
 		if (inst.GetLength() != checkLen)
 			continue;
 
-		const MCInstrDesc &instDesc = mInstrInfo->get(inst.mMCInst.getOpcode());		
+		const MCInstrDesc& instDesc = mInstrInfo->get(inst.mMCInst.getOpcode());
 		if (!instDesc.isCompare())
 			continue;
 
 		auto immediateType = (instDesc.TSFlags & llvm::X86II::ImmMask);
+		if ((immediateType == llvm::X86II::Imm8) && (inst.mMCInst.getNumOperands() == 2))
+		{
+			// We're checking for a TEST [<reg>], 1
+			if (inst.mMCInst.getOpcode() != llvm::X86::TEST8ri)
+				continue;
+			auto immOp = inst.mMCInst.getOperand(1);
+			if (!immOp.isImm())
+				continue;
+			if (immOp.getImm() != 1)
+				continue;
+			return DbgBreakKind_ArithmeticOverflow;
+		}
+
 		if ((immediateType == 0) || (inst.mMCInst.getNumOperands() < 6))
 			continue;
 
@@ -617,10 +630,35 @@ bool X86CPU::IsObjectAccessBreak(uint32 address, DbgModuleMemoryCache* memoryCac
 
 		*outObjectPtr = (uint64)regs[regNum];
 
-		return true;
+		return DbgBreakKind_ObjectAccess;
 	}
 
-	return false;
+	// check jno/jnb
+	for (int offset = 3; offset <= 3; offset++)
+	{
+		if (!Decode(address - offset, memoryCache, &inst))
+			continue;
+
+		if (inst.GetLength() != 2)
+			continue;
+
+		const MCInstrDesc& instDesc = mInstrInfo->get(inst.mMCInst.getOpcode());
+		if (!instDesc.isBranch())
+			continue;
+
+		auto immediateType = (instDesc.TSFlags & llvm::X86II::ImmMask);
+		if ((immediateType == llvm::X86II::Imm8PCRel) && (inst.mMCInst.getNumOperands() == 2))
+		{
+			auto immOp = inst.mMCInst.getOperand(1);
+			if (!immOp.isImm())
+				continue;
+			if ((immOp.getImm() != 1) && (immOp.getImm() != 3))
+				continue;
+			return DbgBreakKind_ArithmeticOverflow;
+		}
+	}
+
+	return DbgBreakKind_None;
 }
 
 int X86CPU::GetOpcodesForMnemonic(const StringImpl& mnemonic, Array<int>& outOpcodes)

+ 1 - 1
IDEHelper/X86.h

@@ -344,7 +344,7 @@ public:
 	String InstructionToString(X86Instr* inst, uint32 addr);
 
 	void GetNextPC(uint32 baseAddress, const uint8* dataBase, int dataLength, const uint8* dataPtr, uint32* regs, uint32 nextPCs[2]);
-	bool IsObjectAccessBreak(uint32 address, DbgModuleMemoryCache* memoryCache, int32* regs, int32* outObjectPtr);
+	DbgBreakKind GetDbgBreakKind(uint32 address, DbgModuleMemoryCache* memoryCache, int32* regs, int32* outObjectPtr);
 
 	int GetOpcodesForMnemonic(const StringImpl& mnemonic, Array<int>& outOpcodes);
 	void GetClobbersForMnemonic(const StringImpl& mnemonic, int argCount, Array<int>& outImplicitClobberRegNums, int& outClobberArgCount, bool& outMayClobberMem);