kristina updated this revision to Diff 173503.
kristina set the repository for this revision to rC Clang.
kristina added a comment.

Revised, I think I worked out what was happening, there's an explanation in the 
comment in the test. This is a relatively new attribute so the coverage for it 
did not test this specific corner case, I've managed to manually narrow it down 
and write a reliable regression test. The core issue boils down to having a 
non-trivially destructible member in a superclass that lacks a destructor and a 
subclass that lacks a destructor, in which case, a different path was taken to 
balance out the static storage duration class' initializer call and the 
`__cxa_atexit` registration. This adds an explicit check for the attribute and 
avoids balancing out the constructor as intended by the attribute.

The new test successfully replicates the assertion failure and should fail for 
the above cases in assertion builds. Without assertions the generated code 
produces undefined behavior if the above conditions are met. There is a 
separate test for this attribute that provides the coverage for its 
functionality, this is a much more narrower test for a very specific scenario 
in which it was possible to cause an improperly balanced constructor call 
followed by a emission of a call to a destructor that would never be emitted 
due to the attribute, thus tripping the assert. Now no attempt to call the 
destructor is made if the declaration is marked as `no_destroy`.

`Test without patch:`

  => ninja check-clang-semacxx
  
  FAIL: Clang :: SemaCXX/attr-no-destroy-d54344.cpp (164 of 818)
  ******************** TEST 'Clang :: SemaCXX/attr-no-destroy-d54344.cpp' 
FAILED ********************
  
  (--- stack trace and the expected assertion failure ---)
  
  
/q/org.llvm.caches/llvm-8.0/4141/tools/clang/test/SemaCXX/Output/attr-no-destroy-d54344.cpp.script:
 line 1: 31356 Aborted (core dumped)
  
  --
  
  ********************
  Testing Time: 5.03s
  ********************
  Failing Tests (1):
      Clang :: SemaCXX/attr-no-destroy-d54344.cpp
  
    Expected Passes    : 816
    Expected Failures  : 1
    Unexpected Failures: 1
  FAILED: tools/clang/test/CMakeFiles/check-clang-semacxx
  
  ninja: build stopped: subcommand failed.

`Patch applied:`

  => ninja check-clang-semacxx
  
  Testing Time: 6.28s
    Expected Passes    : 817
    Expected Failures  : 1


Repository:
  rC Clang

https://reviews.llvm.org/D54344

Files:
  lib/CodeGen/CGDeclCXX.cpp
  test/SemaCXX/attr-no-destroy-d54344.cpp

Index: test/SemaCXX/attr-no-destroy-d54344.cpp
===================================================================
--- test/SemaCXX/attr-no-destroy-d54344.cpp
+++ test/SemaCXX/attr-no-destroy-d54344.cpp
@@ -0,0 +1,92 @@
+// RUN: %clang_cc1 -std=c++2a -emit-llvm -S -O0 %s -o - > /dev/null
+// REQUIRES: asserts
+
+// Regression test for D54344. Class with no user-defined destructor
+// that has an inherited member that has a non-trivial destructor
+// and a non-default constructor will attempt to emit a destructor
+// despite being marked as __attribute((no_destroy)) in which case
+// it would trigger an assertion due to an incorrect assumption.
+// This test requires asserts to reliably work as without the crash
+// it generates working but semantically incorrect code.
+
+namespace util {
+	template <typename T>
+	class test_vector
+	{
+	public:
+		test_vector(unsigned c)
+			: Used(0), TotalSize(sizeof(T) * c)
+		{
+			// Intentional
+			Storage = (T*)(new T[TotalSize]);
+		}
+		~test_vector() {
+			delete[] Storage;
+		}
+		char* data() {
+			return Storage;
+		}
+		unsigned size() {
+			return TotalSize;
+		}
+		void push_back(T one_thing) {
+			Storage[Used++] = one_thing;
+		}
+	private:
+		unsigned Used;
+		unsigned TotalSize;
+		T*       Storage;
+	};
+}
+
+volatile int do_not_optimize_me = 0;
+
+namespace os {
+	using char_vec_t = util::test_vector<char>;
+	
+	class logger_base
+	{
+	public:
+		constexpr logger_base() = default;
+	protected:
+		char_vec_t* NamePtr = nullptr;
+		char_vec_t  Name    = char_vec_t(10);
+	};
+	
+	class logger : public logger_base
+	{
+	public:
+		void stuff(const char* fmt, int something) {
+			do_not_optimize_me = something + fmt[0];
+		}
+		logger()
+		{
+			Name = char_vec_t(8);
+			Name.push_back('a');
+		}
+		
+		logger(const char* name)
+		{
+			Name = util::test_vector<char>(__builtin_strlen(name));
+			Name.push_back(name[0]);
+			Name.push_back(name[1]);
+		}
+	};
+}
+
+#define TOPLEVEL_LOGGER(_var_name, _category_const)               \
+	namespace { constexpr char $__##_var_name[] = _category_const; \
+	__attribute((no_destroy)) os::logger _var_name ($__##_var_name) ; }
+
+TOPLEVEL_LOGGER(s_log, "something");
+
+class some_class {
+public:
+	some_class(int some_value) {
+		do_not_optimize_me = some_value;
+		s_log.stuff("wawawa", 5 + do_not_optimize_me);
+	}
+	~some_class() = default;
+};
+
+static some_class s_ctor(1);
\ No newline at end of file
Index: lib/CodeGen/CGDeclCXX.cpp
===================================================================
--- lib/CodeGen/CGDeclCXX.cpp
+++ lib/CodeGen/CGDeclCXX.cpp
@@ -64,6 +64,14 @@
 /// static storage duration.
 static void EmitDeclDestroy(CodeGenFunction &CGF, const VarDecl &D,
                             ConstantAddress Addr) {
+  // Honor __attribute__((no_destroy)) and bail instead of attempting
+  // to emit a reference to a possibly non-existant destructor, which
+  // in turn can cause a crash. This will result in a global constructor
+  // that isn't balanced out by a destructor call as intended by the
+  // attribute (D54344).
+  if (D.hasAttr<NoDestroyAttr>())
+    return;
+  
   CodeGenModule &CGM = CGF.CGM;
 
   // FIXME:  __attribute__((cleanup)) ?
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to