Просмотр исходного кода

Added options to toggle exception-handling and RTTI (#1209)

CPP_EXCEPTIONS_ENABLED enables exceptions, CPP_RTTI_ENABLED enables RTTI.
By default they're both off as Jolt doesn't use these features.
In the PerformanceTest this speeds up the simulation by about 5% for MSVC, no difference was measured for clang.
Mikael Hermansson 1 год назад
Родитель
Сommit
760974d733

+ 29 - 3
Build/CMakeLists.txt

@@ -33,6 +33,14 @@ option(INTERPROCEDURAL_OPTIMIZATION "Enable interprocedural optimizations" ON)
 # Note that this currently only works using MSVC. Clang turns Float2 into a SIMD vector sometimes causing floating point exceptions (the option is ignored).
 option(FLOATING_POINT_EXCEPTIONS_ENABLED "Enable floating point exceptions" ON)
 
+# When turning this on, the library will be compiled with C++ exceptions enabled.
+# This adds some overhead and Jolt doesn't use exceptions so by default it is off.
+option(CPP_EXCEPTIONS_ENABLED "Enable C++ exceptions" OFF)
+
+# When turning this on, the library will be compiled with C++ RTTI enabled.
+# This adds some overhead and Jolt doesn't use RTTI so by default it is off.
+option(CPP_RTTI_ENABLED "Enable C++ RTTI" OFF)
+
 # Number of bits to use in ObjectLayer. Can be 16 or 32.
 option(OBJECT_LAYER_BITS "Number of bits in ObjectLayer" 16)
 
@@ -127,10 +135,18 @@ if (MSVC)
 	endif()
 
 	# Set compiler flag for disabling RTTI
-	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR-")
+	if (NOT CPP_RTTI_ENABLED)
+		set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR-")
+	endif()
 
-	if ("${CMAKE_VS_PLATFORM_NAME}" STREQUAL "ARM")
-		# On ARM the exception handling flag is missing which causes warnings
+	if (NOT CPP_EXCEPTIONS_ENABLED)
+		# Remove any existing compiler flag that enables exceptions
+		string(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
+
+		# Disable warning about STL and compiler-generated types using noexcept when exceptions are disabled
+		set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4577")
+	else()
+		# Enable exceptions
 		set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc")
 	endif()
 
@@ -173,6 +189,16 @@ else()
 		set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
 	endif()
 
+	# Set compiler flag for disabling RTTI
+	if (NOT CPP_RTTI_ENABLED)
+		set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
+	endif()
+
+	# Disable exception-handling
+	if (NOT CPP_EXCEPTIONS_ENABLED)
+		set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
+	endif()
+
 	if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
 		# Also disable -Wstringop-overflow or it will generate false positives that can't be disabled from code when link-time optimizations are enabled
 		# Also turn off automatic fused multiply add contractions, there doesn't seem to be a way to do this selectively through the macro JPH_PRECISE_MATH_OFF

+ 6 - 0
Jolt/ConfigurationString.h

@@ -81,6 +81,12 @@ inline const char *GetConfigurationString()
 #endif
 #ifdef JPH_DEBUG
 		"(Debug) "
+#endif
+#if defined(__cpp_rtti) && __cpp_rtti
+		"(C++ RTTI) "
+#endif
+#if defined(__cpp_exceptions) && __cpp_exceptions
+		"(C++ Exceptions) "
 #endif
 		;
 }

+ 3 - 0
Jolt/Core/Core.h

@@ -406,6 +406,9 @@ JPH_SUPPRESS_WARNINGS_STD_BEGIN
 #include <functional>
 #include <algorithm>
 #include <cstdint>
+#ifdef JPH_COMPILER_MSVC
+	#include <malloc.h> // for alloca
+#endif
 #if defined(JPH_USE_SSE)
 	#include <immintrin.h>
 #elif defined(JPH_USE_NEON)

+ 5 - 0
Jolt/Jolt.cmake

@@ -503,6 +503,11 @@ target_include_directories(Jolt PUBLIC
 # Code coverage doesn't work when using precompiled headers
 target_precompile_headers(Jolt PRIVATE "$<$<NOT:$<CONFIG:ReleaseCoverage>>:${JOLT_PHYSICS_ROOT}/Jolt.h>")
 
+if (NOT CPP_EXCEPTIONS_ENABLED)
+	# Disable use of exceptions in MSVC's STL
+	target_compile_definitions(Jolt PUBLIC $<$<BOOL:${MSVC}>:_HAS_EXCEPTIONS=0>)
+endif()
+
 # Set the debug/non-debug build flags
 target_compile_definitions(Jolt PUBLIC "$<$<CONFIG:Debug>:_DEBUG>")
 target_compile_definitions(Jolt PUBLIC "$<$<CONFIG:Release,Distribution,ReleaseASAN,ReleaseUBSAN,ReleaseCoverage>:NDEBUG>")

+ 6 - 1
Jolt/Math/UVec4.inl

@@ -103,7 +103,12 @@ UVec4 UVec4::sGatherInt4(const uint32 *inBase, UVec4Arg inOffsets)
 #ifdef JPH_USE_AVX2
 	return _mm_i32gather_epi32(reinterpret_cast<const int *>(inBase), inOffsets.mValue, Scale);
 #else
-	return Vec4::sGatherFloat4<Scale>(reinterpret_cast<const float *>(inBase), inOffsets).ReinterpretAsInt();
+	const uint8 *base = reinterpret_cast<const uint8 *>(inBase);
+	uint32 x = *reinterpret_cast<const uint32 *>(base + inOffsets.GetX() * Scale);
+	uint32 y = *reinterpret_cast<const uint32 *>(base + inOffsets.GetY() * Scale);
+	uint32 z = *reinterpret_cast<const uint32 *>(base + inOffsets.GetZ() * Scale);
+	uint32 w = *reinterpret_cast<const uint32 *>(base + inOffsets.GetW() * Scale);
+	return UVec4(x, y, z, w);
 #endif
 }
 

+ 2 - 2
UnitTests/Core/ArrayTest.cpp

@@ -439,6 +439,7 @@ TEST_SUITE("ArrayTest")
 		CHECK(NonTriv::sNumDestructors == 0);
 	}
 
+#ifndef JPH_USE_STD_VECTOR // std::vector can choose to not shrink the array when calling shrink_to_fit so we can't test this
 	TEST_CASE("TestShrinkToFit")
 	{
 		Array<int> arr;
@@ -476,13 +477,12 @@ TEST_SUITE("ArrayTest")
 		CHECK(arr.size() == 5);
 		for (int i = 0; i < 5; ++i)
 			CHECK(arr[i].Value() == i);
-#ifndef JPH_USE_STD_VECTOR
 		CHECK(NonTriv::sNumConstructors == 0);
 		CHECK(NonTriv::sNumCopyConstructors == 0);
 		CHECK(NonTriv::sNumMoveConstructors == 5);
 		CHECK(NonTriv::sNumDestructors == 5); // Switched to a new block, all old elements are destroyed after being moved
-#endif // JPH_USE_STD_VECTOR
 	}
+#endif // JPH_USE_STD_VECTOR
 
 	TEST_CASE("TestAssignIterator")
 	{

+ 8 - 4
UnitTests/ObjectStream/ObjectStreamTest.cpp

@@ -209,10 +209,12 @@ TEST_SUITE("ObjectStreamTest")
 		TestSerializable *test = CreateTestObject();
 
 		stringstream stream;
-		REQUIRE(ObjectStreamOut::sWriteObject(stream, ObjectStreamOut::EStreamType::Text, *test));
+		CHECK(ObjectStreamOut::sWriteObject(stream, ObjectStreamOut::EStreamType::Text, *test));
 
 		TestSerializable *test_out = nullptr;
-		REQUIRE(ObjectStreamIn::sReadObject(stream, test_out));
+		CHECK(ObjectStreamIn::sReadObject(stream, test_out));
+		if (test_out == nullptr)
+			return;
 
 		// Check that DynamicCast returns the right offsets
 		CHECK(DynamicCast<TestSerializable>(test_out) == test_out);
@@ -232,10 +234,12 @@ TEST_SUITE("ObjectStreamTest")
 		TestSerializable *test = CreateTestObject();
 
 		stringstream stream;
-		REQUIRE(ObjectStreamOut::sWriteObject(stream, ObjectStreamOut::EStreamType::Binary, *test));
+		CHECK(ObjectStreamOut::sWriteObject(stream, ObjectStreamOut::EStreamType::Binary, *test));
 
 		TestSerializable *test_out = nullptr;
-		REQUIRE(ObjectStreamIn::sReadObject(stream, test_out));
+		CHECK(ObjectStreamIn::sReadObject(stream, test_out));
+		if (test_out == nullptr)
+			return;
 
 		CompareObjects(test, test_out);