From 56afb333c36d6373fe0905351a73d9b63bb5a3a8 Mon Sep 17 00:00:00 2001 From: Vassil Vassilev Date: Mon, 27 Apr 2026 14:13:27 +0000 Subject: [PATCH] [test] Disable OOP fixture under sanitizers and Emscripten. Two configurations the `OutOfProcessJIT` typed-test fixture can't survive: - Sanitizer builds (ASan/MSan/TSan): upstream LLVM ORC asserts on `Resolving symbol with incorrect flags` (`llvm/lib/ExecutionEngine/Orc/Core.cpp::OL_notifyResolved`). Sanitizer-instrumented common symbols carry flags the host process never declared; the EPC boundary surfaces the mismatch. Earlier per-test skips landed in `EnumReflection_GetEnums` and `FunctionReflection_InstantiateTemplateFunctionFromString`, but a wider repro shows the failure is structural -- nearly every test that JITs nontrivial code crashes. - Emscripten: the OOP path requires fork/exec and a separate `llvm-jitlink-executor` binary; the wasm runtime provides neither. Move both gates from per-test sites into the typed-test config in `unittests/CppInterOp/Utils.h` via a single `CPPINTEROP_OOP_DISABLED` macro that combines `__has_feature(...sanitizer)` / `__SANITIZE_ADDRESS__` / `__SANITIZE_THREAD__` / `__EMSCRIPTEN__`. Both the `OutOfProcessJITConfig` struct and the `CppInterOpTestTypes` typedef key off the same macro, so under any disabled configuration only `InProcessJIT` registers and no per-test boilerplate is needed. Drops the inline sanitizer skip blocks the previous round added to `EnumReflection_GetEnums` and `FunctionReflection_InstantiateTemplateFunctionFromString` -- the fixture-level gate covers them. The CI fixture-registration assertion in `.github/actions/Build_and_Test_CppInterOp/action.yml` already allows `OutOfProcessJIT` to be missing on `clang-runtime >= 22` rows (transitional permissiveness for cache rows without compiler-rt). Sanitizer and emscripten rows piggyback on that permissiveness; no CI change needed. --- unittests/CppInterOp/EnumReflectionTest.cpp | 14 -------- .../CppInterOp/FunctionReflectionTest.cpp | 14 -------- unittests/CppInterOp/Utils.h | 32 +++++++++++++++++-- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/unittests/CppInterOp/EnumReflectionTest.cpp b/unittests/CppInterOp/EnumReflectionTest.cpp index 5069e0aa0..19b58f3e7 100644 --- a/unittests/CppInterOp/EnumReflectionTest.cpp +++ b/unittests/CppInterOp/EnumReflectionTest.cpp @@ -254,20 +254,6 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, EnumReflection_GetEnumConstantValue) { } TYPED_TEST(CPPINTEROP_TEST_MODE, EnumReflection_GetEnums) { - // Skip under OOP+sanitizer: upstream LLVM ORC asserts on - // `Resolving symbol with incorrect flags` - // (`llvm/lib/ExecutionEngine/Orc/Core.cpp:2800`) when - // sanitizer-instrumented common symbols cross the EPC boundary -- - // the host's JITSymbolFlags don't match what the executor reports. -#if (defined(__has_feature) && \ - (__has_feature(address_sanitizer) || \ - __has_feature(memory_sanitizer) || \ - __has_feature(thread_sanitizer))) || \ - defined(__SANITIZE_ADDRESS__) || defined(__SANITIZE_THREAD__) - if (this->IsOutOfProcess()) - GTEST_SKIP() << "OOP+sanitizer trips LLVM ORC symbol-flag assertion"; -#endif - std::string code = R"( enum Color { Red, diff --git a/unittests/CppInterOp/FunctionReflectionTest.cpp b/unittests/CppInterOp/FunctionReflectionTest.cpp index b575d6a48..a01e019ac 100644 --- a/unittests/CppInterOp/FunctionReflectionTest.cpp +++ b/unittests/CppInterOp/FunctionReflectionTest.cpp @@ -702,20 +702,6 @@ TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_ExistsFunctionTemplate) { TYPED_TEST(CPPINTEROP_TEST_MODE, FunctionReflection_InstantiateTemplateFunctionFromString) { - // Skip under OOP+sanitizer: same upstream LLVM ORC - // `Resolving symbol with incorrect flags` issue as - // EnumReflection_GetEnums (Core.cpp:2800) -- sanitizer-instrumented - // common symbols cross the EPC boundary with mismatched - // JITSymbolFlags. -#if (defined(__has_feature) && \ - (__has_feature(address_sanitizer) || \ - __has_feature(memory_sanitizer) || \ - __has_feature(thread_sanitizer))) || \ - defined(__SANITIZE_ADDRESS__) || defined(__SANITIZE_THREAD__) - if (this->IsOutOfProcess()) - GTEST_SKIP() << "OOP+sanitizer trips LLVM ORC symbol-flag assertion"; -#endif - std::vector interpreter_args = { "-include", "new" }; TestFixture::CreateInterpreter(interpreter_args); std::string code = R"(#include )"; diff --git a/unittests/CppInterOp/Utils.h b/unittests/CppInterOp/Utils.h index 7710ddd64..6fb46d939 100644 --- a/unittests/CppInterOp/Utils.h +++ b/unittests/CppInterOp/Utils.h @@ -57,13 +57,40 @@ CXScope make_scope(const clang::Decl* D, const CXInterpreter I); bool IsTargetX86(); +// OOP-JIT is incompatible with two configurations and is excluded +// from the typed-test matrix wholesale (rather than per-test) when +// either applies: +// * Any sanitizer (ASan/MSan/TSan): upstream LLVM ORC trips +// `Resolving symbol with incorrect flags` +// (`llvm/lib/ExecutionEngine/Orc/Core.cpp`, the JITSymbolFlags +// compare under `OL_notifyResolved`) because +// sanitizer-instrumented common symbols carry flags the host +// process didn't declare; the EPC boundary surfaces the +// mismatch. In-process JIT is unaffected. +// * Emscripten: the OOP path requires fork/exec + a separate +// executor binary, which the wasm runtime doesn't provide. +#if defined(__has_feature) +# if __has_feature(address_sanitizer) || \ + __has_feature(memory_sanitizer) || \ + __has_feature(thread_sanitizer) +# define CPPINTEROP_OOP_DISABLED 1 +# endif +#endif +#if defined(__SANITIZE_ADDRESS__) || defined(__SANITIZE_THREAD__) +# define CPPINTEROP_OOP_DISABLED 1 +#endif +#if defined(__EMSCRIPTEN__) +# define CPPINTEROP_OOP_DISABLED 1 +#endif + // Define type tags for each configuration struct InProcessJITConfig { static constexpr bool isOutOfProcess = false; static constexpr const char* name = "InProcessJIT"; }; -#if LLVM_VERSION_MAJOR > 21 && !defined(_WIN32) +#if LLVM_VERSION_MAJOR > 21 && !defined(_WIN32) && \ + !defined(CPPINTEROP_OOP_DISABLED) struct OutOfProcessJITConfig { static constexpr bool isOutOfProcess = true; static constexpr const char* name = "OutOfProcessJIT"; @@ -97,7 +124,8 @@ struct JITConfigNameGenerator { } }; -#if LLVM_VERSION_MAJOR > 21 && !defined(_WIN32) +#if LLVM_VERSION_MAJOR > 21 && !defined(_WIN32) && \ + !defined(CPPINTEROP_OOP_DISABLED) using CppInterOpTestTypes = ::testing::Types; #else using CppInterOpTestTypes = ::testing::Types;