Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions external/llvh/include/llvh/ADT/ilist.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,16 @@ template <typename NodeTy> struct ilist_callback_traits {
/// node related operations.
///
/// TODO: Remove this layer of indirection. It's not necessary.
///
/// `LLVM_DECLARE_EMPTY_BASES` is required so MSVC collapses the two empty
/// helper bases via Empty Base Optimization. Without it `sizeof` jumps from
/// 1 to 2, which then prevents EBO when `ilist_node_traits` is itself used
/// as a base of `iplist_impl` — `iplist`/`ilist` end up carrying needless
/// trait padding on Windows. See the symmetric fix on `simple_ilist` and
/// `external/llvh/patches/simple-ilist-msvc-empty-bases.patch`.
template <typename NodeTy>
struct ilist_node_traits : ilist_alloc_traits<NodeTy>,
ilist_callback_traits<NodeTy> {};
struct LLVM_DECLARE_EMPTY_BASES ilist_node_traits
: ilist_alloc_traits<NodeTy>, ilist_callback_traits<NodeTy> {};

/// Template traits for intrusive list.
///
Expand Down
26 changes: 25 additions & 1 deletion external/llvh/include/llvh/ADT/simple_ilist.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,15 @@ namespace llvh {
/// equivalent to the last.
///
/// See \a is_valid_option for steps on adding a new option.
///
/// `simple_ilist` inherits from two empty helpers (`list_base_type` and
/// `SpecificNodeAccess`). Without `LLVM_DECLARE_EMPTY_BASES` MSVC fails to
/// collapse them and shifts the embedded `Sentinel` from offset 0 to offset
/// 8, breaking callers that rely on `&list == &Sentinel` — most visibly FFI
/// consumers and any code comparing an iterator's stored node pointer
/// against `&list` rather than against an iterator from `list.end()`.
template <typename T, class... Options>
class simple_ilist
class LLVM_DECLARE_EMPTY_BASES simple_ilist
: ilist_detail::compute_node_options<T, Options...>::type::list_base_type,
ilist_detail::SpecificNodeAccess<
typename ilist_detail::compute_node_options<T, Options...>::type> {
Expand Down Expand Up @@ -346,6 +353,23 @@ void simple_ilist<T, Options...>::sort(Compare comp) {
merge(RHS, comp);
}

namespace ilist_detail {
// EBO compile-time invariant for simple_ilist. Its two empty bases
// (the configured list_base_type and SpecificNodeAccess) must collapse via
// Empty Base Optimization, otherwise the embedded Sentinel shifts away from
// offset 0 and FFI consumers that rely on `&list == &Sentinel` break.
// On MSVC this requires LLVM_DECLARE_EMPTY_BASES applied to the class
// template (defined in llvh/Support/Compiler.h). Catches accidental removal
// of the macro, ABI-flag drift, or future MSVC layout changes.
struct ebo_check_node : ilist_node<ebo_check_node> {};
static_assert(
sizeof(simple_ilist<ebo_check_node>) ==
sizeof(ilist_sentinel<compute_node_options<ebo_check_node>::type>),
"simple_ilist must use Empty Base Optimization. If this fires on MSVC, "
"ensure LLVM_DECLARE_EMPTY_BASES is applied to the class template "
"(see external/llvh/patches/simple-ilist-msvc-empty-bases.patch).");
} // end namespace ilist_detail

} // end namespace llvh

#endif // LLVM_ADT_SIMPLE_ILIST_H
12 changes: 12 additions & 0 deletions external/llvh/include/llvh/Support/Compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,18 @@
#define LLVM_LIBRARY_VISIBILITY
#endif

/// LLVM_DECLARE_EMPTY_BASES - Force MSVC to apply Empty Base Optimization
/// across multiple empty bases. By default MSVC pads each empty base with
/// one byte and pointer-aligns the result, which shifts member offsets and
/// breaks any code that relies on a derived object's address coinciding
/// with the address of its first member (e.g. layout-sensitive containers
/// and FFI consumers).
#ifdef _MSC_VER
#define LLVM_DECLARE_EMPTY_BASES __declspec(empty_bases)
#else
#define LLVM_DECLARE_EMPTY_BASES
#endif

#if defined(__GNUC__)
#define LLVM_PREFETCH(addr, rw, locality) __builtin_prefetch(addr, rw, locality)
#else
Expand Down
23 changes: 23 additions & 0 deletions external/llvh/patches/ilist-node-traits-msvc-empty-bases.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
diff --git a/include/llvh/ADT/ilist.h b/include/llvh/ADT/ilist.h
index 95edfb8b..518f3ab6 100644
--- a/include/llvh/ADT/ilist.h
+++ b/include/llvh/ADT/ilist.h
@@ -80,9 +80,16 @@ template <typename NodeTy> struct ilist_callback_traits {
/// node related operations.
///
/// TODO: Remove this layer of indirection. It's not necessary.
+///
+/// `LLVM_DECLARE_EMPTY_BASES` is required so MSVC collapses the two empty
+/// helper bases via Empty Base Optimization. Without it `sizeof` jumps from
+/// 1 to 2, which then prevents EBO when `ilist_node_traits` is itself used
+/// as a base of `iplist_impl` — `iplist`/`ilist` end up carrying needless
+/// trait padding on Windows. See the symmetric fix on `simple_ilist` and
+/// `external/llvh/patches/simple-ilist-msvc-empty-bases.patch`.
template <typename NodeTy>
-struct ilist_node_traits : ilist_alloc_traits<NodeTy>,
- ilist_callback_traits<NodeTy> {};
+struct LLVM_DECLARE_EMPTY_BASES ilist_node_traits
+ : ilist_alloc_traits<NodeTy>, ilist_callback_traits<NodeTy> {};

/// Template traits for intrusive list.
///
68 changes: 68 additions & 0 deletions external/llvh/patches/simple-ilist-msvc-empty-bases.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
diff --git a/include/llvh/ADT/simple_ilist.h b/include/llvh/ADT/simple_ilist.h
index 6a59ddc7..8c1bc68d 100644
--- a/include/llvh/ADT/simple_ilist.h
+++ b/include/llvh/ADT/simple_ilist.h
@@ -75,8 +75,15 @@ namespace llvh {
/// equivalent to the last.
///
/// See \a is_valid_option for steps on adding a new option.
+///
+/// `simple_ilist` inherits from two empty helpers (`list_base_type` and
+/// `SpecificNodeAccess`). Without `LLVM_DECLARE_EMPTY_BASES` MSVC fails to
+/// collapse them and shifts the embedded `Sentinel` from offset 0 to offset
+/// 8, breaking callers that rely on `&list == &Sentinel` — most visibly FFI
+/// consumers and any code comparing an iterator's stored node pointer
+/// against `&list` rather than against an iterator from `list.end()`.
template <typename T, class... Options>
-class simple_ilist
+class LLVM_DECLARE_EMPTY_BASES simple_ilist
: ilist_detail::compute_node_options<T, Options...>::type::list_base_type,
ilist_detail::SpecificNodeAccess<
typename ilist_detail::compute_node_options<T, Options...>::type> {
@@ -346,6 +353,23 @@ void simple_ilist<T, Options...>::sort(Compare comp) {
merge(RHS, comp);
}

+namespace ilist_detail {
+// EBO compile-time invariant for simple_ilist. Its two empty bases
+// (the configured list_base_type and SpecificNodeAccess) must collapse via
+// Empty Base Optimization, otherwise the embedded Sentinel shifts away from
+// offset 0 and FFI consumers that rely on `&list == &Sentinel` break.
+// On MSVC this requires LLVM_DECLARE_EMPTY_BASES applied to the class
+// template (defined in llvh/Support/Compiler.h). Catches accidental removal
+// of the macro, ABI-flag drift, or future MSVC layout changes.
+struct ebo_check_node : ilist_node<ebo_check_node> {};
+static_assert(
+ sizeof(simple_ilist<ebo_check_node>) ==
+ sizeof(ilist_sentinel<compute_node_options<ebo_check_node>::type>),
+ "simple_ilist must use Empty Base Optimization. If this fires on MSVC, "
+ "ensure LLVM_DECLARE_EMPTY_BASES is applied to the class template "
+ "(see external/llvh/patches/simple-ilist-msvc-empty-bases.patch).");
+} // end namespace ilist_detail
+
} // end namespace llvh

#endif // LLVM_ADT_SIMPLE_ILIST_H
diff --git a/include/llvh/Support/Compiler.h b/include/llvh/Support/Compiler.h
index 34173110..1dc671e5 100644
--- a/include/llvh/Support/Compiler.h
+++ b/include/llvh/Support/Compiler.h
@@ -108,6 +108,18 @@
#define LLVM_LIBRARY_VISIBILITY
#endif

+/// LLVM_DECLARE_EMPTY_BASES - Force MSVC to apply Empty Base Optimization
+/// across multiple empty bases. By default MSVC pads each empty base with
+/// one byte and pointer-aligns the result, which shifts member offsets and
+/// breaks any code that relies on a derived object's address coinciding
+/// with the address of its first member (e.g. layout-sensitive containers
+/// and FFI consumers).
+#ifdef _MSC_VER
+#define LLVM_DECLARE_EMPTY_BASES __declspec(empty_bases)
+#else
+#define LLVM_DECLARE_EMPTY_BASES
+#endif
+
#if defined(__GNUC__)
#define LLVM_PREFETCH(addr, rw, locality) __builtin_prefetch(addr, rw, locality)
#else