halyavin created this revision.
Herald added a reviewer: EricWF.
This change removes dependency on Microsoft STL implementation but adds
dependency on VC runtime implementation. We will need to evolve and add
#ifdef's to this code as new versions of Visual Studio runtime will come out.
Tested on x86 and x64, llvm clang 5.0, Visual Studio 2015 runtime.
https://reviews.llvm.org/D40071
Files:
include/exception
src/support/runtime/exception_pointer_msvc.ipp
Index: src/support/runtime/exception_pointer_msvc.ipp
===================================================================
--- src/support/runtime/exception_pointer_msvc.ipp
+++ src/support/runtime/exception_pointer_msvc.ipp
@@ -8,84 +8,237 @@
//
//===----------------------------------------------------------------------===//
-#include <stdio.h>
-#include <stdlib.h>
+#include <atomic>
+#include <cstdint>
+#include <cstring>
+#include <malloc.h>
+#include <windows.h> // For RtlPcToFileHeader function
-#if !defined(_CRTIMP2_PURE)
-#define _CRTIMP2_PURE __declspec(dllimport)
+struct EHCatchableType {
+ uint32_t properties;
+ int32_t type_info;
+ uint32_t non_virtual_adjustment;
+ uint32_t offset_to_virtual_base_ptr;
+ uint32_t virtual_base_table_index;
+ uint32_t size;
+ int32_t copy_function;
+};
+
+struct EHCatchableTypeArray {
+ uint32_t catchable_types;
+ // It is variable size but we only need the first element of this array
+ int32_t array_of_catchable_types[1];
+};
+
+struct EHThrowInfo {
+ uint32_t attributes;
+ int32_t unwind;
+ int32_t forward_compat;
+ int32_t catchable_type_array;
+};
+
+struct EHParameters {
+ uint32_t magic_number;
+ void* exception_object;
+ EHThrowInfo* throw_info;
+#ifdef _M_AMD64
+ uintptr_t throw_image_base;
#endif
+};
-#if !defined(__CLRCALL_PURE_OR_CDECL)
-#define __CLRCALL_PURE_OR_CDECL __cdecl
+struct EHExceptionRecord {
+ uint32_t exception_code;
+ uint32_t exception_flags;
+ void* exception_record;
+ void* exception_address;
+ uint32_t number_of_parameters;
+ EHParameters parameters;
+};
+
+// defined in vcruntime<ver>.dll
+extern "C" EHExceptionRecord** __current_exception();
+
+// This is internal compiler definition for MSVC but not for clang.
+// We use our own EHThrowInfo because _ThrowInfo doesn't match actual
+// compiler-generated structures in 64-bit mode.
+#ifdef __clang__
+struct _ThrowInfo;
+// defined in vcruntime<ver>.dll
+_LIBCPP_NORETURN extern "C" void __stdcall _CxxThrowException(
+ void* __exc, _ThrowInfo* __throw_info);
#endif
-_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL __ExceptionPtrCreate(void*);
-_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL __ExceptionPtrDestroy(void*);
-_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL __ExceptionPtrCopy(void*,
- const void*);
-_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL
-__ExceptionPtrAssign(void*, const void*);
-_CRTIMP2_PURE bool __CLRCALL_PURE_OR_CDECL
-__ExceptionPtrCompare(const void*, const void*);
-_CRTIMP2_PURE bool __CLRCALL_PURE_OR_CDECL
-__ExceptionPtrToBool(const void*);
-_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL __ExceptionPtrSwap(void*, void*);
-_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL
-__ExceptionPtrCurrentException(void*);
-[[noreturn]] _CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL
-__ExceptionPtrRethrow(const void*);
-_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL
-__ExceptionPtrCopyException(void*, const void*, const void*);
+namespace {
+struct ExceptionPtr {
+ void* exception_object;
+ const EHThrowInfo* throw_info;
+ std::atomic<size_t> counter;
+#ifdef _M_AMD64
+ PVOID image_base;
+#endif
+ template <class T>
+ T convert(int32_t offset) {
+#ifdef _M_AMD64
+ uintptr_t value = reinterpret_cast<uintptr_t>(image_base) +
+ static_cast<uintptr_t>(offset);
+#else
+ uintptr_t value = static_cast<uintptr_t>(offset);
+#endif
+ T res;
+ static_assert(
+ sizeof(value) == sizeof(res),
+ "Can only convert to pointers or pointers to member functions");
+ memcpy(&res, &value, sizeof(value));
+ return res;
+ }
-namespace std {
+ void copy(void* dst, const void* src, const EHCatchableType* exc_type) {
+ struct Temp {};
+ constexpr uint32_t virtual_base = 4;
+ if (exc_type->copy_function == 0) {
+ memcpy(dst, src, exc_type->size);
+ } else if (exc_type->properties & virtual_base) {
+ auto copy_constructor =
+ convert<void (Temp::*)(const void*, int)>(exc_type->copy_function);
+ ((Temp*)dst->*copy_constructor)(src, 1);
+ } else {
+ auto copy_constructor =
+ convert<void (Temp::*)(const void*)>(exc_type->copy_function);
+ ((Temp*)dst->*copy_constructor)(src);
+ }
+ }
-exception_ptr::exception_ptr() _NOEXCEPT { __ExceptionPtrCreate(this); }
-exception_ptr::exception_ptr(nullptr_t) _NOEXCEPT { __ExceptionPtrCreate(this); }
+ EHCatchableType* exception_type() {
+ return convert<EHCatchableType*>(
+ convert<EHCatchableTypeArray*>(throw_info->catchable_type_array)
+ ->array_of_catchable_types[0]);
+ }
-exception_ptr::exception_ptr(const exception_ptr& __other) _NOEXCEPT {
- __ExceptionPtrCopy(this, &__other);
-}
-exception_ptr& exception_ptr::operator=(const exception_ptr& __other) _NOEXCEPT {
- __ExceptionPtrAssign(this, &__other);
- return *this;
-}
+ ExceptionPtr(const void* exception_object_, const EHThrowInfo* throw_info_)
+ : exception_object(nullptr), throw_info(throw_info_), counter(1) {
+#ifdef _M_AMD64
+ RtlPcToFileHeader(
+ reinterpret_cast<PVOID>(const_cast<EHThrowInfo*>(throw_info)),
+ &image_base);
+#endif
+ EHCatchableType* exc_type = exception_type();
+ this->exception_object = malloc(exc_type->size);
+ if (this->exception_object == nullptr) {
+ throw std::bad_alloc();
+ }
+ copy(exception_object, exception_object_, exc_type);
+ }
-exception_ptr& exception_ptr::operator=(nullptr_t) _NOEXCEPT {
- exception_ptr dummy;
- __ExceptionPtrAssign(this, &dummy);
- return *this;
-}
+ ~ExceptionPtr() {
+ if (throw_info->unwind && exception_object) {
+ struct Temp {};
+ auto destructor = convert<void (Temp::*)()>(throw_info->unwind);
+ ((Temp*)exception_object->*destructor)();
+ }
+ free(exception_object);
+ }
-exception_ptr::~exception_ptr() _NOEXCEPT { __ExceptionPtrDestroy(this); }
+ static ExceptionPtr bad_alloc;
+ static ExceptionPtr bad_exception;
+};
-exception_ptr::operator bool() const _NOEXCEPT {
- return __ExceptionPtrToBool(this);
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Waddress-of-temporary"
+#endif
+
+ExceptionPtr ExceptionPtr::bad_alloc(
+ &std::bad_alloc(),
+ reinterpret_cast<const EHThrowInfo*>(__GetExceptionInfo(std::bad_alloc())));
+
+ExceptionPtr
+ExceptionPtr::bad_exception(&std::bad_exception(),
+ reinterpret_cast<const EHThrowInfo*>(
+ __GetExceptionInfo(std::bad_exception())));
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+} // namespace
+
+namespace std {
+
+exception_ptr::exception_ptr(const exception_ptr& __other) _NOEXCEPT
+ : __ptr_(__other.__ptr_) {
+ if (__ptr_) {
+ reinterpret_cast<ExceptionPtr*>(__ptr_)->counter.fetch_add(1);
+ }
}
-bool operator==(const exception_ptr& __x, const exception_ptr& __y) _NOEXCEPT {
- return __ExceptionPtrCompare(&__x, &__y);
+exception_ptr& exception_ptr::
+operator=(const exception_ptr& __other) _NOEXCEPT {
+ auto before = __ptr_;
+ __ptr_ = __other.__ptr_;
+ if (__ptr_) {
+ reinterpret_cast<ExceptionPtr*>(__ptr_)->counter.fetch_add(1);
+ }
+ if (before) {
+ if (reinterpret_cast<ExceptionPtr*>(before)->counter.fetch_sub(1) == 1) {
+ delete reinterpret_cast<ExceptionPtr*>(before);
+ }
+ }
+ return *this;
}
-
-void swap(exception_ptr& lhs, exception_ptr& rhs) _NOEXCEPT {
- __ExceptionPtrSwap(&rhs, &lhs);
+exception_ptr::~exception_ptr() _NOEXCEPT {
+ if (__ptr_) {
+ if (reinterpret_cast<ExceptionPtr*>(__ptr_)->counter.fetch_sub(1) == 1) {
+ delete reinterpret_cast<ExceptionPtr*>(__ptr_);
+ }
+ }
}
-exception_ptr __copy_exception_ptr(void* __except, const void* __ptr) {
- exception_ptr __ret = nullptr;
- if (__ptr)
- __ExceptionPtrCopyException(&__ret, __except, __ptr);
- return __ret;
+exception_ptr __copy_exception_ptr(void* exception_object,
+ const void* throw_info) {
+ ExceptionPtr* ptr;
+ try {
+ ptr = new ExceptionPtr(exception_object,
+ reinterpret_cast<const EHThrowInfo*>(throw_info));
+ } catch (const std::bad_alloc&) {
+ ptr = &ExceptionPtr::bad_alloc;
+ ptr->counter.fetch_add(1);
+ } catch (...) {
+ ptr = &ExceptionPtr::bad_exception;
+ ptr->counter.fetch_add(1);
+ }
+ exception_ptr res;
+ memcpy(&res, &ptr, sizeof(ptr));
+ return res;
}
exception_ptr current_exception() _NOEXCEPT {
- exception_ptr __ret;
- __ExceptionPtrCurrentException(&__ret);
- return __ret;
+ EHExceptionRecord** record = __current_exception();
+ if (*record && !std::uncaught_exception()) {
+ return __copy_exception_ptr((*record)->parameters.exception_object,
+ (*record)->parameters.throw_info);
+ }
+ return exception_ptr();
}
_LIBCPP_NORETURN
-void rethrow_exception(exception_ptr p) { __ExceptionPtrRethrow(&p); }
+void rethrow_exception(exception_ptr p) {
+ if (!p) {
+ throw std::bad_exception();
+ }
+ ExceptionPtr* exc_ptr = reinterpret_cast<ExceptionPtr*>(p.__ptr_);
+ EHCatchableType* exc_type = exc_ptr->exception_type();
+ // _CxxThrowException doesn't call free on exception object so we must
+ // allocate it on the stack.
+ void* dst = _alloca(exc_type->size);
+ exc_ptr->copy(dst, exc_ptr->exception_object, exc_type);
+ auto throw_info = reinterpret_cast<_ThrowInfo*>(
+ const_cast<EHThrowInfo*>(exc_ptr->throw_info));
+ // For some reason clang doesn't call p destructor during unwinding.
+ // So we must clear it ourselves.
+ p = nullptr;
+ _CxxThrowException(dst, throw_info);
+}
nested_exception::nested_exception() _NOEXCEPT : __ptr_(current_exception()) {}
Index: include/exception
===================================================================
--- include/exception
+++ include/exception
@@ -134,8 +134,6 @@
_LIBCPP_FUNC_VIS exception_ptr current_exception() _NOEXCEPT;
_LIBCPP_NORETURN _LIBCPP_FUNC_VIS void rethrow_exception(exception_ptr);
-#ifndef _LIBCPP_ABI_MICROSOFT
-
class _LIBCPP_TYPE_VIS exception_ptr
{
void* __ptr_;
@@ -158,10 +156,19 @@
bool operator!=(const exception_ptr& __x, const exception_ptr& __y) _NOEXCEPT
{return !(__x == __y);}
+ friend _LIBCPP_FUNC_VIS void swap(exception_ptr& __x, exception_ptr& __y) _NOEXCEPT
+ {
+ void* __tmp = __x.__ptr_;
+ __x.__ptr_ = __y.__ptr_;
+ __y.__ptr_ = __tmp;
+ }
+
friend _LIBCPP_FUNC_VIS exception_ptr current_exception() _NOEXCEPT;
friend _LIBCPP_FUNC_VIS void rethrow_exception(exception_ptr);
};
+#ifndef _LIBCPP_ABI_MICROSOFT
+
template<class _Ep>
exception_ptr
make_exception_ptr(_Ep __e) _NOEXCEPT
@@ -183,39 +190,7 @@
#else // _LIBCPP_ABI_MICROSOFT
-class _LIBCPP_TYPE_VIS exception_ptr
-{
-#if defined(__clang__)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-private-field"
-#endif
- void* __ptr1_;
- void* __ptr2_;
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#endif
-public:
- exception_ptr() _NOEXCEPT;
- exception_ptr(nullptr_t) _NOEXCEPT;
- exception_ptr(const exception_ptr& __other) _NOEXCEPT;
- exception_ptr& operator=(const exception_ptr& __other) _NOEXCEPT;
- exception_ptr& operator=(nullptr_t) _NOEXCEPT;
- ~exception_ptr() _NOEXCEPT;
- _LIBCPP_EXPLICIT operator bool() const _NOEXCEPT;
-};
-
-_LIBCPP_FUNC_VIS
-bool operator==(const exception_ptr& __x, const exception_ptr& __y) _NOEXCEPT;
-
-inline _LIBCPP_INLINE_VISIBILITY
-bool operator!=(const exception_ptr& __x, const exception_ptr& __y) _NOEXCEPT
- {return !(__x == __y);}
-
-_LIBCPP_FUNC_VIS void swap(exception_ptr&, exception_ptr&) _NOEXCEPT;
-
_LIBCPP_FUNC_VIS exception_ptr __copy_exception_ptr(void *__except, const void* __ptr);
-_LIBCPP_FUNC_VIS exception_ptr current_exception() _NOEXCEPT;
-_LIBCPP_NORETURN _LIBCPP_FUNC_VIS void rethrow_exception(exception_ptr p);
// This is a built-in template function which automagically extracts the required
// information.
@@ -229,6 +204,7 @@
}
#endif // _LIBCPP_ABI_MICROSOFT
+
// nested_exception
class _LIBCPP_EXCEPTION_ABI nested_exception
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits