Implementing blocks with try-finally semantics

Hi,

I am working on a block annotation which should have a try-finally semantics. For example, given a code like this:

__speculate {
if (x > 2) {
y = 0;
}
}

clang should produce something like

__exec_mode = __begin_spec();
if (__exec_mode == SW) {
if (__read_barrier(&x) > 2) {
__write_barrier(&y, 0);
}
__end_spec();
} else {
if (x > 2) {
y = 0;
}
__end_spec();
}

such that __end_spec() is guaranteed to be called on every exit of the __speculate {} block.

It is my understanding that Clang implements try-finally statements as a Microsoft Specific Extension. However, I would not like to have such restriction, since such extension is only available for a reduced number of targets.

My question is, how should I implement try-finally semantics? Through an IR pass (fixing up every exit with a call to __end_spec()) or it is possible to extend from CXXTryStmt (reusing some CodeGen code)?

Hi,

From your example, it’s not clear to me what you want this feature to do. Do you want the finally block to execute:

  1. When a C++ exception is thrown?
  2. When normal control flow leaves the scope (goto, break, return)?
  3. When a hardware trap occurs (div by zero, access violation, segv)?

3 is not implementable with LLVM today. It’s not implemented properly even for __try / __finally.

1 and 2 are simple and can be accomplished with regular C++ destructors. You can do things like:
struct Cleanup {
Cleanup(std::function<void()> f) : f(f) {}
~Cleanup() { f(); }
std::function<void()> f;
};
void foo() {
Cleanup c(& {
… // any finally code here
});
// any try body code here
}

Reid

Reid,

Thank you for the quick reply.

I am not worried about exceptions, I am more interested in the concept of finally, a block of code which is guaranteed to execute “no matter what” happens inside the try block. But you are right, my example is a little crude.

My goal is to guarantee that __end_spec() is called on every exit of __speculate blocks. Even if __speculate is within a for/while-loop and a there is a break/return inside of it. The __speculate block annotates a block that will be transformed into a two-path code: (i) instrumented with software load/store barriers to detect conflicts and (ii) instrumentation-free that will either execute under mutual exclusion or using hardware support for speculative execution.

I am already generating correct code when the compound statement does not have any statements which cause control flow to “escape” the block within which __speculate resides.

My question was motivated more by my worry to no generate an ugly solution (e.g. fixing up every exit of the block) or (re)coding something that is(might have been) implemented.

Reid,

Thank you for the quick reply.

I am not worried about exceptions, I am more interested in the concept of finally, a block of code which is guaranteed to execute “no matter what” happens inside the try block. But you are right, my example is a little crude.

My goal is to guarantee that __end_spec() is called on every exit of __speculate blocks. Even if __speculate is within a for/while-loop and a there is a break/return inside of it. The __speculate block annotates a block that will be transformed into a two-path code: (i) instrumented with software load/store barriers to detect conflicts and (ii) instrumentation-free that will either execute under mutual exclusion or using hardware support for speculative execution.

I am already generating correct code when the compound statement does not have any statements which cause control flow to “escape” the block within which __speculate resides.

My question was motivated more by my worry to no generate an ugly solution (e.g. fixing up every exit of the block) or (re)coding something that is(might have been) implemented.

You’re hacking the compiler, right? In the IRGen for your new declaration (statement?), push a cleanup.

John.

John,

Thank you very much! Pushing a cleanup via EHStack did the trick!

John,

Thank you very much! Pushing a cleanup via EHStack did the trick!

Note that if you’re interested in making this user-facing, you should probably add something to the JumpDiagnostics checker to make sure you can’t jump into this thing.

I think the analysis part is fairly easy to find examples of how to modify; the more subtle thing is that you need to call getCurFunction()->setHasBranchProtectedScope() to ensure that the analysis is run.

John.