================
@@ -0,0 +1,1258 @@
+=============================================
+ClangIR Cleanup and Exception Handling Design
+=============================================
+
+.. contents::
+   :local:
+
+Overview
+========
+
+This document describes the proposed new design for C++ cleanups and exception
+handling representation and lowering in the CIR dialect. The initial CIR
+generation will follow the general structure of the cleanup and exception
+handling code in Clang's LLVM IR generation. In particular, we will continue
+to use the ``EHScopeStack`` with pushing and popping of
+``EHScopeStack::Cleanup`` objects to drive the creation of cleanup scopes 
within
+CIR.
+
+However, the LLVM IR generated by Clang is fundamentally unstructured and
+therefore isn't well suited to the goals of CIR. Therefore, we are proposing
+a high-level representation that follows MLIR's structured control flow model.
+
+The ``cir::LowerCFG`` pass will lower this high-level representation to a
+different form where control flow is block-based and explicit. This form will
+more closely resemble the LLVM IR used when Clang is generating LLVM IR
+directly. However, this form will still be ABI-agnostic.
+
+An additional pass will be introduced to lower the flattened form to an
+ABI-specific representation. This ABI-specific form will have a direct
+correspondence to the LLVM IR exception handling representation for a given
+target.
+
+High-level CIR representation
+==============================
+
+Normal and EH cleanups
+----------------------
+Scopes that require normal or EH cleanup will be represented using a new
+operation, ``cir.cleanup.scope``.
+
+.. code-block::
+
+  cir.cleanup.scope {
+    // body region
+  } cleanup [eh_only] {
+    // cleanup instructions
+  }
+
+Execution begins with the first operation in the body region and continues
+according to normal control flow semantics until a terminating operation
+(``cir.yield``, ``cir.break``, ``cir.return``) is encountered or an exception 
is
+thrown.
+
+If the cleanup region is marked as ``eh_only``, normal control flow exits from
+the body region skip the cleanup region and continue to their normal 
destination
+according to the semantics of the operation. If the cleanup region is not
+marked as ``eh_only``, normal control flow exits from the body region must
+execute the cleanup region before control is transferred to the destination
+implied by the operation.
+
+When an exception is thrown from within a cleanup scope, the cleanup region
+must be executed before handling of the exception continues. If the cleanup
+scope is nested within another cleanup scope, the cleanup region of the inner
+scope is executed, followed by the cleanup region of the outer scope, and
+handling continues according to these rules. If the cleanup scope is nested
+within a try operation, the cleanup region is executed before control is
+transferred to the catch handlers. If an exception is thrown from within a
+cleanup region that is not nested within either another cleanup region or a
+try operation, the cleanup region is executed and then exception unwinding
+continues as if a ``cir.resume`` operation had been executed.
+
+Note that this design eliminates the need for synthetic try operations, such
+as were used to represent calls within a cleanup scope in the ClangIR
+incubator project.
+
+Implementation notes
+^^^^^^^^^^^^^^^^^^^^
+
+The ``cir.cleanup.scope`` must be created when we call ``pushCleanup``. We will
+need to set the insertion point at that time. When each cleanup block is 
popped,
+we will need to set the insertion point to immediately following the cleanup
+scope operation. If ``forceCleanups()`` is called, it will pop cleanup blocks,
+which is good.
+
+Example: Automatic storage object cleanup
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+**C++**
+
+.. code-block:: c++
+
+  void someFunc() {
+    SomeClass c;
+    c.doSomething();
+  }
+
+**CIR**
+
+.. code-block::
+
+  cir.func @someFunc() {
+    %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
+    cir.call @_ZN9SomeClassC1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+    cir.cleanup.scope {
+      cir.call @_ZN9SomeClass11doSomethingEv(%0) : (!cir.ptr<!rec_SomeClass>) 
-> ()
+    } cleanup {
+      cir.call @_ZN9SomeClassD1Ev(%0) : (!cir.ptr<!rec_SomeClass>) -> ()
+    }
+    cir.return
+  }
+
+In this example, we create an instance of ``SomeClass`` which has a constructor
+and a destructor. If an exception occurs within the constructor call, it
+unwinds without any handling in this function. The cleanup scope is not
+entered in that case. Once the object has been constructed, we enter a cleanup
+scope which continues until the object goes out of scope, in this case for the
+remainder of the function.
+
+If an exception is thrown from within the ``doSomething()`` function, we 
execute
+the cleanup region, calling the ``SomeClass`` destructor before continuing to
+unwind the exception. If the call to ``doSomething()`` completes successfully,
+the object goes out of scope and we execute the cleanup region, calling the
+destructor, before continuing to the return operation.
+
+Example: Multiple automatic objects
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+**C++**
+
+.. code-block:: c++
+
+  void someFunc() {
+    SomeClass c;
+    SomeClass c2;
+    c.doSomething();
+    SomeClass c3;
+    c3.doSomething();
+  }
+
+**CIR**
+
+.. code-block::
+
+  cir.func @someFunc() {
+    %0 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c", init]
+    %1 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c2", init]
+    %2 = cir.alloca !rec_SomeClass, !cir.ptr<!rec_SomeClass>, ["c3", init]
----------------
erichkeane wrote:

Presumably this is AFTER hoisting?  I would expect these allocas to have ended 
up in the 'scope' during normal generation, correct?

https://github.com/llvm/llvm-project/pull/177625
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to