Pārlūkot izejas kodu

Fixes NaN comparisons

Brian Fiete 9 mēneši atpakaļ
vecāks
revīzija
55298ee884

+ 106 - 29
IDEHelper/Backend/BeMCContext.cpp

@@ -2106,6 +2106,8 @@ String BeMCContext::ToString(const BeMCOperand& operand)
 		if (cmpResult.mResultVRegIdx != -1)
 			result += StrFormat("<vreg%d>", cmpResult.mResultVRegIdx);
 		result += " ";
+ 		if (cmpResult.mInverted)
+ 			result += "!";
 		result += BeDumpContext::ToString(cmpResult.mCmpKind);
 		return result;
 	}
@@ -3519,7 +3521,16 @@ void BeMCContext::CreateCondBr(BeMCBlock* mcBlock, BeMCOperand& testVal, const B
 		// Beef-specific: assuming CMP results aren't stomped
 		auto& cmpResult = mCmpResults[testVal.mCmpResultIdx];
 		AllocInst(BeMCInstKind_CondBr, trueBlock, BeMCOperand::FromCmpKind(cmpResult.mCmpKind));
-		AllocInst(BeMCInstKind_Br, falseBlock);
+		if (cmpResult.mInverted)
+		{
+			AllocInst(BeMCInstKind_CondBr, falseBlock, BeMCOperand::FromCmpKind(cmpResult.mCmpKind));
+			AllocInst(BeMCInstKind_Br, trueBlock);
+		}
+		else
+		{
+			AllocInst(BeMCInstKind_CondBr, trueBlock, BeMCOperand::FromCmpKind(cmpResult.mCmpKind));
+			AllocInst(BeMCInstKind_Br, falseBlock);
+		}
 	}
 	else if (testVal.mKind == BeMCOperandKind_Phi)
 	{
@@ -6135,27 +6146,35 @@ uint8 BeMCContext::GetJumpOpCode(BeCmpKind cmpKind, bool isLong)
 		case BeCmpKind_SLT: // JL
 			return 0x8C;
 		case BeCmpKind_ULT: // JB
+		case BeCmpKind_OLT: // JB
 			return 0x82;
 		case BeCmpKind_SLE: // JLE
 			return 0x8E;
 		case BeCmpKind_ULE: // JBE
+		case BeCmpKind_OLE: // JBE
 			return 0x86;
 		case BeCmpKind_EQ: // JE
 			return 0x84;
 		case BeCmpKind_NE: // JNE
 			return 0x85;
+		case BeCmpKind_OEQ: // JNP
+			return 0x8B;
+		case BeCmpKind_UNE: // JP
+			return 0x8A;
 		case BeCmpKind_SGT: // JG
 			return 0x8F;
 		case BeCmpKind_UGT: // JA
+		case BeCmpKind_OGT: // JA
 			return 0x87;
 		case BeCmpKind_SGE: // JGE
 			return 0x8D;
 		case BeCmpKind_UGE: // JAE
+		case BeCmpKind_OGE: // JAE
 			return 0x83;
 		case BeCmpKind_NB: // JNB
 			return 0x83;
 		case BeCmpKind_NO: // JNO
-			return 0x81;
+			return 0x81;		
 		}
 	}
 	else
@@ -6167,22 +6186,30 @@ uint8 BeMCContext::GetJumpOpCode(BeCmpKind cmpKind, bool isLong)
 		case BeCmpKind_SLT: // JL
 			return 0x7C;
 		case BeCmpKind_ULT: // JB
+		case BeCmpKind_OLT: // JB
 			return 0x72;
 		case BeCmpKind_SLE: // JLE
 			return 0x7E;
 		case BeCmpKind_ULE: // JBE
+		case BeCmpKind_OLE: // JBE
 			return 0x76;
 		case BeCmpKind_EQ: // JE
 			return 0x74;
 		case BeCmpKind_NE: // JNE
 			return 0x75;
+		case BeCmpKind_OEQ: // JNP
+			return 0x7B;
+		case BeCmpKind_UNE: // JP
+			return 0x7A;
 		case BeCmpKind_SGT: // JG
 			return 0x7F;
 		case BeCmpKind_UGT: // JA
+		case BeCmpKind_OGT: // JA
 			return 0x77;
 		case BeCmpKind_SGE: // JGE
 			return 0x7D;
 		case BeCmpKind_UGE: // JAE
+		case BeCmpKind_OGE: // JAE
 			return 0x73;
 		case BeCmpKind_NB: // JNB
 			return 0x73;
@@ -6669,6 +6696,8 @@ void BeMCContext::InitializedPassHelper(BeMCBlock* mcBlock, BeVTrackingGenContex
 			{
 				auto cmpToBoolInst = AllocInst(BeMCInstKind_CmpToBool, BeMCOperand::FromCmpKind(cmpResult.mCmpKind), BeMCOperand(), instIdx + 1);
 				cmpToBoolInst->mResult = BeMCOperand::FromVReg(cmpResult.mResultVRegIdx);
+				if (cmpResult.mInverted)
+					AllocInst(BeMCInstKind_Not, cmpToBoolInst->mResult, instIdx + 2);
 			}
 
 			inst->mResult = BeMCOperand();
@@ -10384,6 +10413,14 @@ bool BeMCContext::DoLegalization()
 
 					if (arg0Type->IsFloat())
 					{
+						if (!arg0.IsNativeReg())
+						{
+							// We need an <xmm> for reg0. We're not allowed to reorder SwapCmpSides due to NaN handling
+							ReplaceWithNewVReg(inst->mArg0, instIdx, true, true);
+							isFinalRun = false;
+							break;
+						}
+
 						// Cmp <r/m>, <xmm> is not valid, only Cmp <xmm>, <r/m>
 						if ((!arg0.IsNativeReg()) && (arg1.IsNativeReg()))
 						{
@@ -11347,7 +11384,8 @@ bool BeMCContext::DoJumpRemovePass()
 
 						auto nextNextInst = mcBlock->mInstructions[nextNextIdx];
 						if ((nextInst->mKind == BeMCInstKind_Br) &&
-							(nextNextInst->mKind == BeMCInstKind_Label) && (inst->mArg0 == nextNextInst->mArg0))
+							(nextNextInst->mKind == BeMCInstKind_Label) && (inst->mArg0 == nextNextInst->mArg0) &&
+							(!BeModule::IsCmpOrdered(inst->mArg1.mCmpKind)))
 						{
 							didWork = true;
 							inst->mArg0 = nextInst->mArg0;
@@ -14815,17 +14853,17 @@ void BeMCContext::DoCodeEmission()
 					{
 					case BeMCInstForm_XMM32_FRM32:
 					case BeMCInstForm_XMM32_IMM:
-						// COMISS
+						// UCOMISS
 						EmitREX(inst->mArg0, inst->mArg1, false);
-						Emit(0x0F); Emit(0x2F);
+						Emit(0x0F); Emit(0x2E);
 						EmitModRM(inst->mArg0, inst->mArg1);
 						break;
 					case BeMCInstForm_XMM64_FRM64:
 					case BeMCInstForm_XMM64_IMM:
-						// COMISD
+						// UCOMISD
 						Emit(0x66);
 						EmitREX(inst->mArg0, inst->mArg1, false);
-						Emit(0x0F); Emit(0x2F);
+						Emit(0x0F); Emit(0x2E);
 						EmitModRM(inst->mArg0, inst->mArg1);
 						break;
 					default:
@@ -15103,24 +15141,50 @@ void BeMCContext::DoCodeEmission()
 				break;
 			case BeMCInstKind_CondBr:
 				{
-					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);
+					for (int pass = 0; pass < 2; pass++)
+					{						
+						if (inst->mArg0.mKind == BeMCOperandKind_Immediate_i64)
+						{
+							if (pass == 1)
+								break;
 
-						mOut.Write(GetJumpOpCode(jump.mCmpKind, false));
-						mOut.Write((uint8)0);
+							mOut.Write(GetJumpOpCode(inst->mArg1.mCmpKind, false));
+							mOut.Write((uint8)inst->mArg0.mImmediate);
+						}
+						else
+						{
+							BeCmpKind cmpKind = inst->mArg1.mCmpKind;
+							if (pass == 1)
+							{
+								switch (cmpKind)
+								{
+								case BeCmpKind_OEQ:
+									cmpKind = BeCmpKind_EQ;
+									break;
+								case BeCmpKind_UNE:
+									cmpKind = BeCmpKind_NE;
+									break;
+								default:
+									cmpKind = BeCmpKind_None;
+								}
+
+								if (cmpKind == BeCmpKind_None)
+									break;
+							}
+
+							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 = cmpKind;;
+							deferredJumps.push_back(jump);
+
+							mOut.Write(GetJumpOpCode(jump.mCmpKind, false));
+							mOut.Write((uint8)0);
+							funcCodePos += 2;
+						}
 					}
 				}
 				break;
@@ -16687,19 +16751,31 @@ void BeMCContext::Generate(BeFunction* function)
 
 					if (valType->IsFloat())
 					{
+						// These operations are set up to properly handle NaN comparisons
 						switch (cmpResult.mCmpKind)
 						{
 						case BeCmpKind_SLT:
-							cmpResult.mCmpKind = BeCmpKind_ULT;
+							cmpResult.mCmpKind = BeCmpKind_OLE;
+							cmpResult.mInverted = true;
+							BF_SWAP(mcInst->mArg0, mcInst->mArg1);
 							break;
 						case BeCmpKind_SLE:
-							cmpResult.mCmpKind = BeCmpKind_ULE;
+							cmpResult.mCmpKind = BeCmpKind_OLT;
+							cmpResult.mInverted = true;
+							BF_SWAP(mcInst->mArg0, mcInst->mArg1);
 							break;
 						case BeCmpKind_SGT:
-							cmpResult.mCmpKind = BeCmpKind_UGT;
+							cmpResult.mCmpKind = BeCmpKind_OGT;
 							break;
 						case BeCmpKind_SGE:
-							cmpResult.mCmpKind = BeCmpKind_UGE;
+							cmpResult.mCmpKind = BeCmpKind_OGE;
+							break;
+						case BeCmpKind_EQ:
+							cmpResult.mCmpKind = BeCmpKind_UNE;
+							cmpResult.mInverted = true;
+							break;
+						case BeCmpKind_NE:
+							cmpResult.mCmpKind = BeCmpKind_UNE;							
 							break;
 						}
 					}
@@ -16710,6 +16786,7 @@ void BeMCContext::Generate(BeFunction* function)
 					result.mCmpResultIdx = cmpResultIdx;
 
 					mcInst->mResult = result;
+					break;
 				}
 				break;
 			case BeObjectAccessCheckInst::TypeId:
@@ -18083,7 +18160,7 @@ void BeMCContext::Generate(BeFunction* function)
 	BEMC_ASSERT(retCount == 1);
 
 	bool wantDebug = mDebugging;
-	wantDebug |= function->mName == "?Test@Program@BeefTest@bf@@SAXXZ";
+	wantDebug |= function->mName == "?GetVal@TestProgram@BeefTest@bf@@SATint@@M@Z";
 	//wantDebug |= function->mName == "?Testos@Fartso@@SAHPEA1@HH@Z";
 	//wantDebug |= function->mName == "?GetYoopA@Fartso@@QEAAUYoop@@XZ";
 		//"?TestVals@Fartso@@QEAATint@@XZ";

+ 2 - 0
IDEHelper/Backend/BeMCContext.h

@@ -166,11 +166,13 @@ struct BeCmpResult
 {
 	BeCmpKind mCmpKind;
 	int mResultVRegIdx;
+	bool mInverted;
 
 	BeCmpResult()
 	{
 		mCmpKind = BeCmpKind_None;
 		mResultVRegIdx = -1;
+		mInverted = false;
 	}
 };
 

+ 39 - 0
IDEHelper/Backend/BeModule.cpp

@@ -1726,14 +1726,18 @@ void BeDumpContext::ToString(StringImpl& str, BeCmpKind cmpKind)
 	{
 	case BeCmpKind_SLT: str += "slt"; return;
 	case BeCmpKind_ULT: str += "ult"; return;
+	case BeCmpKind_OLT: str += "olt"; return;
 	case BeCmpKind_SLE: str += "sle"; return;
 	case BeCmpKind_ULE: str += "ule"; return;
+	case BeCmpKind_OLE: str += "ole"; return;
 	case BeCmpKind_EQ: str += "eq"; return;
 	case BeCmpKind_NE: str += "ne"; return;
 	case BeCmpKind_SGT: str += "sgt"; return;
 	case BeCmpKind_UGT: str += "ugt"; return;
+	case BeCmpKind_OGT: str += "ogt"; return;
 	case BeCmpKind_SGE: str += "sge"; return;
 	case BeCmpKind_UGE: str += "uge"; return;
+	case BeCmpKind_OGE: str += "oge"; return;
 	case BeCmpKind_NB: str += "nb"; return;
 	case BeCmpKind_NO: str += "no"; return;
 	default:
@@ -3017,22 +3021,34 @@ BeCmpKind BeModule::InvertCmp(BeCmpKind cmpKind)
 		return BeCmpKind_SGE;
 	case BeCmpKind_ULT:
 		return BeCmpKind_UGE;
+	case BeCmpKind_OLT:
+		return BeCmpKind_OGE;
 	case BeCmpKind_SLE:
 		return BeCmpKind_SGT;
 	case BeCmpKind_ULE:
 		return BeCmpKind_UGT;
+	case BeCmpKind_OLE:
+		return BeCmpKind_OGT;
 	case BeCmpKind_EQ:
 		return BeCmpKind_NE;
+	case BeCmpKind_OEQ:
+		return BeCmpKind_UNE;
 	case BeCmpKind_NE:
 		return BeCmpKind_EQ;
+	case BeCmpKind_UNE:
+		return BeCmpKind_OEQ;
 	case BeCmpKind_SGT:
 		return BeCmpKind_SLE;
 	case BeCmpKind_UGT:
 		return BeCmpKind_ULE;
+	case BeCmpKind_OGT:
+		return BeCmpKind_OLE;
 	case BeCmpKind_SGE:
 		return BeCmpKind_SLT;
 	case BeCmpKind_UGE:
 		return BeCmpKind_ULT;
+	case BeCmpKind_OGE:
+		return BeCmpKind_OLT;
 	}
 	return cmpKind;
 }
@@ -3045,10 +3061,14 @@ BeCmpKind BeModule::SwapCmpSides(BeCmpKind cmpKind)
 		return BeCmpKind_SGT;
 	case BeCmpKind_ULT:
 		return BeCmpKind_UGT;
+	case BeCmpKind_OLT:
+		return BeCmpKind_OGT;
 	case BeCmpKind_SLE:
 		return BeCmpKind_SGE;
 	case BeCmpKind_ULE:
 		return BeCmpKind_UGE;
+	case BeCmpKind_OLE:
+		return BeCmpKind_OGE;
 	case BeCmpKind_EQ:
 		return BeCmpKind_EQ;
 	case BeCmpKind_NE:
@@ -3057,14 +3077,33 @@ BeCmpKind BeModule::SwapCmpSides(BeCmpKind cmpKind)
 		return BeCmpKind_SLT;
 	case BeCmpKind_UGT:
 		return BeCmpKind_ULT;
+	case BeCmpKind_OGT:
+		return BeCmpKind_OLT;
 	case BeCmpKind_SGE:
 		return BeCmpKind_SLE;
 	case BeCmpKind_UGE:
 		return BeCmpKind_ULE;
+	case BeCmpKind_OGE:
+		return BeCmpKind_OLE;
 	}
 	return cmpKind;
 }
 
+bool BeModule::IsCmpOrdered(BeCmpKind cmpKind)
+{
+	switch (cmpKind)
+	{
+	case BeCmpKind_OLT:
+	case BeCmpKind_OLE:
+	case BeCmpKind_OGT:
+	case BeCmpKind_OGE:
+	case BeCmpKind_OEQ:
+	case BeCmpKind_UNE:
+		return true;
+	}
+	return false;
+}
+
 void BeModule::AddInst(BeInst* inst)
 {
 	inst->mDbgLoc = mCurDbgLoc;

+ 7 - 0
IDEHelper/Backend/BeModule.h

@@ -858,14 +858,20 @@ enum BeCmpKind
 
 	BeCmpKind_SLT,
 	BeCmpKind_ULT,
+	BeCmpKind_OLT,
 	BeCmpKind_SLE,
 	BeCmpKind_ULE,
+	BeCmpKind_OLE,
 	BeCmpKind_EQ,
+	BeCmpKind_OEQ,
 	BeCmpKind_NE,
+	BeCmpKind_UNE,
 	BeCmpKind_SGT,
 	BeCmpKind_UGT,
+	BeCmpKind_OGT,
 	BeCmpKind_SGE,
 	BeCmpKind_UGE,
+	BeCmpKind_OGE,
 	BeCmpKind_NB,
 	BeCmpKind_NO,
 };
@@ -2371,6 +2377,7 @@ public:
 
 	static BeCmpKind InvertCmp(BeCmpKind cmpKind);
 	static BeCmpKind SwapCmpSides(BeCmpKind cmpKind);
+	static bool IsCmpOrdered(BeCmpKind cmpKind);
 	void SetActiveFunction(BeFunction* function);
 	BeArgument* GetArgument(int arg);
 	BeBlock* CreateBlock(const StringImpl& name);

+ 29 - 6
IDEHelper/Compiler/BfIRBuilder.cpp

@@ -297,12 +297,13 @@ String BfIRConstHolder::ToString(BfIRValue irValue)
 		}
 		else if (constant->mTypeCode == BfTypeCode_NullPtr)
 		{
-			String ret = "null";
+			String ret;
 			if (constant->mIRType)
 			{
-				ret += "\n";
 				ret += ToString(constant->mIRType);
+				ret += " ";
 			}
+			ret += "null";
 			return ret;
 		}
 		else if (constant->mTypeCode == BfTypeCode_Boolean)
@@ -333,14 +334,14 @@ String BfIRConstHolder::ToString(BfIRValue irValue)
 		else if (constant->mConstType == BfConstType_BitCast)
 		{
 			auto bitcast = (BfConstantBitCast*)constant;
-			BfIRValue targetConst(BfIRValueFlags_Const, bitcast->mTarget);
-			return ToString(targetConst) + " BitCast to " + ToString(bitcast->mToType);
+			BfIRValue targetConst(BfIRValueFlags_Const, bitcast->mTarget);			
+			return ToString(bitcast->mToType) += " bitcast " + ToString(targetConst);
 		}
 		else if (constant->mConstType == BfConstType_Box)
 		{
 			auto box = (BfConstantBox*)constant;
 			BfIRValue targetConst(BfIRValueFlags_Const, box->mTarget);
-			return ToString(targetConst) + " box to " + ToString(box->mToType);
+			return ToString(box->mToType) + " box " + ToString(targetConst);
 		}
 		else if (constant->mConstType == BfConstType_GEP32_1)
 		{
@@ -376,7 +377,7 @@ String BfIRConstHolder::ToString(BfIRValue irValue)
 		{
 			auto constAgg = (BfConstantAgg*)constant;
 			String str = ToString(constAgg->mType);
-			str += "(";
+			str += " (";
 
 			for (int i = 0; i < (int)constAgg->mValues.size(); i++)
 			{
@@ -4778,6 +4779,12 @@ BfIRValue BfIRBuilder::CreateInBoundsGEP(BfIRValue val, int idx0, int idx1)
 {
 	if (val.IsConst())
 	{
+#ifdef _DEBUG
+		auto targetConstant = GetConstant(val);
+		BF_ASSERT((mBfIRCodeGen == NULL) || 
+			((targetConstant->mTypeCode != BfTypeCode_NullPtr) && (targetConstant->mConstType != BfConstType_BitCastNull)));
+#endif
+
 		auto constGEP = mTempAlloc.Alloc<BfConstantGEP32_2>();
 		constGEP->mConstType = BfConstType_GEP32_2;
 		constGEP->mTarget = val.mId;
@@ -4804,6 +4811,10 @@ BfIRValue BfIRBuilder::CreateInBoundsGEP(BfIRValue val, BfIRValue idx0)
 	auto constant = GetConstant(val);
 	if (constant != NULL)
 	{
+#ifdef _DEBUG		
+		//BF_ASSERT((constant->mTypeCode != BfTypeCode_NullPtr) && (constant->mConstType != BfConstType_BitCastNull));
+#endif
+
 		if (constant->mConstType == BfConstType_IntToPtr)
 		{
 			auto fromPtrToInt = (BfConstantIntToPtr*)constant;
@@ -5051,6 +5062,12 @@ void BfIRBuilder::CreateValueScopeHardEnd(BfIRValue scopeStart)
 
 BfIRValue BfIRBuilder::CreateLoad(BfIRValue val, bool isVolatile)
 {
+#ifdef _DEBUG
+// 	auto targetConstant = GetConstant(val);
+// 	if (targetConstant != NULL)
+// 		BF_ASSERT((targetConstant->mTypeCode != BfTypeCode_NullPtr) && (targetConstant->mConstType != BfConstType_BitCastNull));
+#endif
+
 	BfIRValue retVal = WriteCmd(BfIRCmd_Load, val, isVolatile);
 	NEW_CMD_INSERTED_IRVALUE;
 	return retVal;
@@ -5058,6 +5075,12 @@ BfIRValue BfIRBuilder::CreateLoad(BfIRValue val, bool isVolatile)
 
 BfIRValue BfIRBuilder::CreateAlignedLoad(BfIRValue val, int align, bool isVolatile)
 {
+#ifdef _DEBUG
+// 	auto targetConstant = GetConstant(val);
+// 	if (targetConstant != NULL)
+// 		BF_ASSERT((targetConstant->mTypeCode != BfTypeCode_NullPtr) && (targetConstant->mConstType != BfConstType_BitCastNull));
+#endif
+
 	BfIRValue retVal = WriteCmd(BfIRCmd_AlignedLoad, val, align, isVolatile);
 	NEW_CMD_INSERTED_IRVALUE;
 	return retVal;

+ 2 - 2
IDEHelper/Compiler/BfIRCodeGen.cpp

@@ -2400,7 +2400,7 @@ void BfIRCodeGen::HandleNextCmd()
 			CMD_PARAM(llvm::Value*, lhs);
 			CMD_PARAM(llvm::Value*, rhs);
 			if (lhs->getType()->isFloatingPointTy())
-				SetResult(curId, mIRBuilder->CreateFCmpONE(lhs, rhs));
+				SetResult(curId, mIRBuilder->CreateFCmpUNE(lhs, rhs));
 			else
 				SetResult(curId, mIRBuilder->CreateICmpNE(lhs, rhs));
 		}
@@ -2450,7 +2450,7 @@ void BfIRCodeGen::HandleNextCmd()
 			CMD_PARAM(llvm::Value*, lhs);
 			CMD_PARAM(llvm::Value*, rhs);
 			if (lhs->getType()->isFloatingPointTy())
-				SetResult(curId, mIRBuilder->CreateFCmpUGT(lhs, rhs));
+				SetResult(curId, mIRBuilder->CreateFCmpOGT(lhs, rhs));
 			else
 				SetResult(curId, mIRBuilder->CreateICmpSGT(lhs, rhs));
 		}

+ 11 - 1
IDEHelper/Compiler/BfModule.cpp

@@ -10126,7 +10126,17 @@ BfIRValue BfModule::AllocFromType(BfType* type, const BfAllocTarget& allocTarget
 		{
 			if ((mBfIRBuilder->mIgnoreWrites) ||
 				((mCompiler->mIsResolveOnly) && (!mIsComptimeModule)))
-				return GetDefaultValue(typeInstance);
+			{
+				if (mBfIRBuilder->mIgnoreWrites)
+				{
+					return GetDefaultValue(typeInstance);
+				}
+				else
+				{
+					// Fake with alloca
+					return mBfIRBuilder->CreateAlloca(allocType);
+				}
+			}
 
 			auto classVDataType = ResolveTypeDef(mCompiler->mClassVDataTypeDef);
 			auto vData = mBfIRBuilder->CreateBitCast(vDataRef, mBfIRBuilder->MapTypeInstPtr(classVDataType->ToTypeInstance()));

+ 60 - 0
IDEHelper/Tests/src/Floats.bf

@@ -51,6 +51,66 @@ namespace Tests
 			FloatParseErrTest("6e+");
 		}
 
+		[Test]
+		public static void TestCmp()
+		{
+			float fNeg = -1;
+			float fNan = float.NaN;
+
+			if (fNeg < 0)
+			{
+			}
+			else
+			{
+				Test.FatalError();
+			}
+			if (fNeg > 0)
+				Test.FatalError();
+
+			if (fNan < 0)
+				Test.FatalError();
+			if (fNan <= 0)
+				Test.FatalError();
+			if (fNan > 0)
+				Test.FatalError();
+			if (fNan >= 0)
+				Test.FatalError();
+			if (fNan == 0)
+				Test.FatalError();
+			if (fNan != 0)
+			{
+
+			}
+			else
+			{
+				Test.FatalError();
+			}
+
+			if (fNan == fNan)
+				Test.FatalError();
+
+			if (fNan != fNan)
+			{
+
+			}
+			else
+			{
+				Test.FatalError();
+			}
+
+			bool b0 = fNan < 0;
+			bool b1 = fNan > 0;
+			bool b2 = fNan == fNan;
+			bool b3 = fNan != fNan;
+			bool b4 = fNan != 0;
+
+			Test.Assert(!b0);
+			Test.Assert(!b1);
+			Test.Assert(!b2);
+			Test.Assert(b3);
+			Test.Assert(b4);
+		}
+
 		public static void MinMaxTest<T>(T expectedMinValue, T expectedMaxValue)
 		where T : IMinMaxValue<T>
 		where int : operator T <=> T