<p>I've been looking into using SSA with basic block arguments for an IR.
However, I have not found a good explanation of how exceptions should be modeled.
This is complicated further because for runtimes like .NET or Java, at least in theory, almost any instruction could throw an exception.
Additionally, is it possible to lower finally blocks to SSA without duplicating the finally block for success and failure cases?</p>
<p>Is this the right use case for this representation? Or would it be better to only use SSA when targeting native code.</p>

<intermediate-representation><static-single-assignment>
Exception Handling within SSA/Basic Block Arguments form
<p>The <a href="https://llvm.org/docs/ExceptionHandling.html" rel="nofollow noreferrer">LLVM model</a> for exception handling represents it in IR by saying that each exception handler is a basic block in its own right, and requiring your IR to include the exception handlers as well as the &quot;main&quot; flow of control.</p>
<p>If an exception is thrown, then instead of continuing as normal, you jump to the exception handler basic block, and let it deal with the exception; it can use a phi node to determine where to return control to, based on where you were when the block was called (so a &quot;finally&quot; block can use a phi node to distinguish &quot;coming from a catch handler&quot; from &quot;end of the try block branched here&quot;).</p>

CC BY-SA 4.0
2024-08-14T13:25:15.797Z
2024-08-14T13:25:15.797Z
Simon Farnsworth
<p>The current approach in the Tyr Intermediate Representation (TIR) is to hold a handler pointer in each basic block. Entering and leaving such blocks is done using dedicated branching instructions to simplify IR handling and construction. Throw is an instruction and there are instructions to get the caught exception from the cxa world.</p>
<p>The boundary conditions are</p>
<ul>
<li>TIR is eventually taken and compiled to LLVM IR</li>
<li>TIR needs an understanding of the kind of handler because some operations can change handlers</li>
<li>Being able to optimize local exception handling is a requirement for Tyr</li>
<li>Tyr has a full lattice type theory with must throw types as one bottom type.</li>
<li>TIR is only meant to serve Tyr; this one is particularly important if you compare approaches or design an SSA for yourself</li>
<li>Exceptions are values like all other values</li>
<li>Exception propagation is done using cxa functions and a single C++ RTTI object wrapping tyr.lang.exceptions.Throwable.</li>
</ul>
<p>My observations so far are that the difference between the current approach and LLVM's approach of having throwing calls branch to two targets is about a factor of two in pointers. Usually, the catch target stays the same and splitting the block there has no benefit for the transformations currently performed on TIR.</p>

CC BY-SA 4.0
2024-08-14T15:50:38.717Z
2024-08-14T15:50:38.717Z
feldentm
# Exception Handling within SSA/Basic Block Arguments form
Posted by: username_0 on 2024-08-14
I've been looking into using SSA with basic block arguments for an IR.
However, I have not found a good explanation of how exceptions should be modeled.
This is complicated further because for runtimes like .NET or Java, at least in theory, almost any instruction could throw an exception.
Additionally, is it possible to lower finally blocks to SSA without duplicating the finally block for success and failure cases?

Is this the right use case for this representation? Or would it be better to only use SSA when targeting native code.
**Tags:** intermediate-representation static-single-assignment
---
## 2 answers
### Answer by username_1 on 2024-08-14. Score: 1
The [LLVM model](https://llvm.org/docs/ExceptionHandling.html) for exception handling represents it in IR by saying that each exception handler is a basic block in its own right, and requiring your IR to include the exception handlers as well as the "main" flow of control.

If an exception is thrown, then instead of continuing as normal, you jump to the exception handler basic block, and let it deal with the exception; it can use a phi node to determine where to return control to, based on where you were when the block was called (so a "finally" block can use a phi node to distinguish "coming from a catch handler" from "end of the try block branched here").
---
### Answer by username_2 on 2024-08-14. Score: 0
The current approach in the Tyr Intermediate Representation (TIR) is to hold a handler pointer in each basic block. Entering and leaving such blocks is done using dedicated branching instructions to simplify IR handling and construction. Throw is an instruction and there are instructions to get the caught exception from the cxa world.

The boundary conditions are

* TIR is eventually taken and compiled to LLVM IR
* TIR needs an understanding of the kind of handler because some operations can change handlers
* Being able to optimize local exception handling is a requirement for Tyr
* Tyr has a full lattice type theory with must throw types as one bottom type.
* TIR is only meant to serve Tyr; this one is particularly important if you compare approaches or design an SSA for yourself
* Exceptions are values like all other values
* Exception propagation is done using cxa functions and a single C++ RTTI object wrapping tyr.lang.exceptions.Throwable.

My observations so far are that the difference between the current approach and LLVM's approach of having throwing calls branch to two targets is about a factor of two in pointers. Usually, the catch target stays the same and splitting the block there has no benefit for the transformations currently performed on TIR.
---
