From eb375362a147d119606d35139db46c242c5e48e0 Mon Sep 17 00:00:00 2001 From: Brian Fiete Date: Tue, 11 Jan 2022 08:17:09 -0500 Subject: [PATCH] Arithmetic overflow checks --- IDE/src/BuildOptions.bf | 6 + IDE/src/Compiler/BfCompiler.bf | 2 + IDE/src/Compiler/BfSystem.bf | 23 ++-- IDE/src/Workspace.bf | 6 + IDE/src/ui/BuildPropertiesDialog.bf | 4 + IDE/src/ui/WorkspaceProperties.bf | 2 + IDEHelper/Backend/BeIRCodeGen.cpp | 19 ++- IDEHelper/Backend/BeIRCodeGen.h | 1 + IDEHelper/Backend/BeMCContext.cpp | 159 ++++++++++++++++++++++--- IDEHelper/Backend/BeMCContext.h | 9 +- IDEHelper/Backend/BeModule.cpp | 5 +- IDEHelper/Backend/BeModule.h | 8 +- IDEHelper/Compiler/BfCompiler.cpp | 4 +- IDEHelper/Compiler/BfCompiler.h | 4 +- IDEHelper/Compiler/BfContext.cpp | 1 + IDEHelper/Compiler/BfExprEvaluator.cpp | 28 ++++- IDEHelper/Compiler/BfIRBuilder.cpp | 35 ++++-- IDEHelper/Compiler/BfIRBuilder.h | 17 ++- IDEHelper/Compiler/BfIRCodeGen.cpp | 104 ++++++++++++++-- IDEHelper/Compiler/BfIRCodeGen.h | 7 +- IDEHelper/Compiler/BfSystem.h | 22 ++-- IDEHelper/DebugCommon.h | 7 ++ IDEHelper/DebugTarget.cpp | 6 +- IDEHelper/DebugTarget.h | 2 +- IDEHelper/WinDebugger.cpp | 9 +- IDEHelper/X64.cpp | 44 ++++++- IDEHelper/X64.h | 2 +- IDEHelper/X86.cpp | 52 ++++++-- IDEHelper/X86.h | 2 +- 29 files changed, 503 insertions(+), 87 deletions(-) diff --git a/IDE/src/BuildOptions.bf b/IDE/src/BuildOptions.bf index a753bfbb..829efe9a 100644 --- a/IDE/src/BuildOptions.bf +++ b/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); diff --git a/IDE/src/Compiler/BfCompiler.bf b/IDE/src/Compiler/BfCompiler.bf index b8a54fd0..67dba204 100644 --- a/IDE/src/Compiler/BfCompiler.bf +++ b/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); diff --git a/IDE/src/Compiler/BfSystem.bf b/IDE/src/Compiler/BfSystem.bf index cd1b057e..f0a9b9ad 100644 --- a/IDE/src/Compiler/BfSystem.bf +++ b/IDE/src/Compiler/BfSystem.bf @@ -15,18 +15,19 @@ namespace IDE.Compiler InitLocalVariables = 2, EmitDynamicCastCheck = 4, EmitObjectAccessCheck = 8, + ArithmeticCheck = 0x10, - ReflectAlwaysIncludeType = 0x10, - ReflectAlwaysIncludeAll = 0x20, - ReflectAssumeInstantiated = 0x40, - ReflectBoxing = 0x80, - ReflectStaticFields = 0x100, - ReflectNonStaticFields = 0x200, - ReflectStaticMethods = 0x400, - ReflectNonStaticMethods = 0x800, - ReflectConstructors = 0x1000, + ReflectAlwaysIncludeType = 0x20, + ReflectAlwaysIncludeAll = 0x40, + ReflectAssumeInstantiated = 0x80, + ReflectBoxing = 0x100, + ReflectStaticFields = 0x200, + ReflectNonStaticFields = 0x400, + ReflectStaticMethods = 0x800, + ReflectNonStaticMethods = 0x1000, + ReflectConstructors = 0x2000, - All = 0x1FFF + 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); } diff --git a/IDE/src/Workspace.bf b/IDE/src/Workspace.bf index 2bc48597..6e3fce46 100644 --- a/IDE/src/Workspace.bf +++ b/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); diff --git a/IDE/src/ui/BuildPropertiesDialog.bf b/IDE/src/ui/BuildPropertiesDialog.bf index c8a0134d..a7a79393 100644 --- a/IDE/src/ui/BuildPropertiesDialog.bf +++ b/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); diff --git a/IDE/src/ui/WorkspaceProperties.bf b/IDE/src/ui/WorkspaceProperties.bf index 63f96cc1..ce878d46 100644 --- a/IDE/src/ui/WorkspaceProperties.bf +++ b/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", diff --git a/IDEHelper/Backend/BeIRCodeGen.cpp b/IDEHelper/Backend/BeIRCodeGen.cpp index 84ec7d73..39f519a5 100644 --- a/IDEHelper/Backend/BeIRCodeGen.cpp +++ b/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); diff --git a/IDEHelper/Backend/BeIRCodeGen.h b/IDEHelper/Backend/BeIRCodeGen.h index debf8ee6..e289a78d 100644 --- a/IDEHelper/Backend/BeIRCodeGen.h +++ b/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); diff --git a/IDEHelper/Backend/BeMCContext.cpp b/IDEHelper/Backend/BeMCContext.cpp index b5184dcb..2d7e9096 100644 --- a/IDEHelper/Backend/BeMCContext.cpp +++ b/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); + 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); + 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; diff --git a/IDEHelper/Backend/BeMCContext.h b/IDEHelper/Backend/BeMCContext.h index aa43f01e..8d3c9610 100644 --- a/IDEHelper/Backend/BeMCContext.h +++ b/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& args, BeType* retType = NULL, BfIRCallingConv callingConv = BfIRCallingConv_CDecl, bool structRet = false, bool noReturn = false, bool isVarArg = false); BeMCOperand CreateCall(const BeMCOperand& func, const SizedArrayImpl& args, BeType* retType = NULL, BfIRCallingConv callingConv = BfIRCallingConv_CDecl, bool structRet = false, bool noReturn = false, bool isVarArg = false); diff --git a/IDEHelper/Backend/BeModule.cpp b/IDEHelper/Backend/BeModule.cpp index db13739b..a91f7b32 100644 --- a/IDEHelper/Backend/BeModule.cpp +++ b/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; } diff --git a/IDEHelper/Backend/BeModule.h b/IDEHelper/Backend/BeModule.h index 8b6d2b27..6398cf36 100644 --- a/IDEHelper/Backend/BeModule.h +++ b/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); diff --git a/IDEHelper/Compiler/BfCompiler.cpp b/IDEHelper/Compiler/BfCompiler.cpp index 5a13291f..7dda4fba 100644 --- a/IDEHelper/Compiler/BfCompiler.cpp +++ b/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; } diff --git a/IDEHelper/Compiler/BfCompiler.h b/IDEHelper/Compiler/BfCompiler.h index cf91c4b2..566e6d9c 100644 --- a/IDEHelper/Compiler/BfCompiler.h +++ b/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; diff --git a/IDEHelper/Compiler/BfContext.cpp b/IDEHelper/Compiler/BfContext.cpp index 17af0431..5f79169b 100644 --- a/IDEHelper/Compiler/BfContext.cpp +++ b/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); diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index a17a2309..97856791 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/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; diff --git a/IDEHelper/Compiler/BfIRBuilder.cpp b/IDEHelper/Compiler/BfIRBuilder.cpp index 8e0c8fda..2be4f74d 100644 --- a/IDEHelper/Compiler/BfIRBuilder.cpp +++ b/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) diff --git a/IDEHelper/Compiler/BfIRBuilder.h b/IDEHelper/Compiler/BfIRBuilder.h index e09a9b64..b70b6ad5 100644 --- a/IDEHelper/Compiler/BfIRBuilder.h +++ b/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 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); diff --git a/IDEHelper/Compiler/BfIRCodeGen.cpp b/IDEHelper/Compiler/BfIRCodeGen.cpp index 0e1da5ea..393d992a 100644 --- a/IDEHelper/Compiler/BfIRCodeGen.cpp +++ b/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 useParams; + useParams.push_back(lhs->getType()); + auto func = llvm::Intrinsic::getDeclaration(mLLVMModule, intrin, useParams); + + CmdParamVec args; + args.push_back(lhs); + args.push_back(rhs); + llvm::FunctionType* funcType = NULL; + if (auto ptrType = llvm::dyn_cast(func->getType())) + funcType = llvm::dyn_cast(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 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 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(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 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 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()); diff --git a/IDEHelper/Compiler/BfIRCodeGen.h b/IDEHelper/Compiler/BfIRCodeGen.h index 30285c51..7cd3868f 100644 --- a/IDEHelper/Compiler/BfIRCodeGen.h +++ b/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 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); diff --git a/IDEHelper/Compiler/BfSystem.h b/IDEHelper/Compiler/BfSystem.h index 0d64aff4..31db1ed4 100644 --- a/IDEHelper/Compiler/BfSystem.h +++ b/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_ArithmeticCheck = 0x10, - 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_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 }; diff --git a/IDEHelper/DebugCommon.h b/IDEHelper/DebugCommon.h index 601c0a14..efc37832 100644 --- a/IDEHelper/DebugCommon.h +++ b/IDEHelper/DebugCommon.h @@ -61,4 +61,11 @@ enum DbgAddrType : uint8 DbgAddrType_Alias }; +enum DbgBreakKind +{ + DbgBreakKind_None, + DbgBreakKind_ObjectAccess, + DbgBreakKind_ArithmeticOverflow +}; + NS_BF_END \ No newline at end of file diff --git a/IDEHelper/DebugTarget.cpp b/IDEHelper/DebugTarget.cpp index efe0055a..3db0fd1c 100644 --- a/IDEHelper/DebugTarget.cpp +++ b/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; } diff --git a/IDEHelper/DebugTarget.h b/IDEHelper/DebugTarget.h index 65d68058..bc4adf85 100644 --- a/IDEHelper/DebugTarget.h +++ b/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); diff --git a/IDEHelper/WinDebugger.cpp b/IDEHelper/WinDebugger.cpp index 3139724a..ee043644 100644 --- a/IDEHelper/WinDebugger.cpp +++ b/IDEHelper/WinDebugger.cpp @@ -4866,7 +4866,8 @@ void WinDebugger::CheckNonDebuggerBreak() } intptr_target objAddr; - if (mDebugTarget->IsObjectAccessBreak(pcAddress, ®isters, &objAddr)) + auto dbgBreakKind = mDebugTarget->GetDbgBreakKind(pcAddress, ®isters, &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; diff --git a/IDEHelper/X64.cpp b/IDEHelper/X64.cpp index b2a10cb5..66817941 100644 --- a/IDEHelper/X64.cpp +++ b/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 [], -0x80 // if 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 [], 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& outOpcodes) diff --git a/IDEHelper/X64.h b/IDEHelper/X64.h index a230b8fe..68085a93 100644 --- a/IDEHelper/X64.h +++ b/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& outOpcodes); void GetClobbersForMnemonic(const StringImpl& mnemonic, int argCount, Array& outImplicitClobberRegNums, int& outClobberArgCount, bool& outMayClobberMem); diff --git a/IDEHelper/X86.cpp b/IDEHelper/X86.cpp index 0c9cccc3..0881bdac 100644 --- a/IDEHelper/X86.cpp +++ b/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 [], -0x80 // if 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 [], 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& outOpcodes) diff --git a/IDEHelper/X86.h b/IDEHelper/X86.h index 0f4e5e77..76af5d72 100644 --- a/IDEHelper/X86.h +++ b/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& outOpcodes); void GetClobbersForMnemonic(const StringImpl& mnemonic, int argCount, Array& outImplicitClobberRegNums, int& outClobberArgCount, bool& outMayClobberMem);