-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[Clang] Add support for the C defer TS
#162848
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 35 commits
a2f523c
0a756c5
2505774
58a6c2d
0a5c8fa
b9e845d
2eaecf5
e74537c
11a40d0
bbf4389
3350e28
40b449b
9706711
d56cfb9
e51b68e
8948a46
b944bde
fbc4f5a
65c2b88
2eb59e3
d7fc314
62261b1
7106167
a0b19b8
e8f5982
191b8f7
48a0433
f117c4b
48fc974
6b041e8
45c8113
3a8741e
004c40e
34ec829
0e238f1
24d7212
c18ac11
53deeda
2ace560
fd13abb
2965221
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -114,6 +114,7 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef<const Attr *> Attrs) { | |
| case Stmt::ContinueStmtClass: | ||
| case Stmt::DefaultStmtClass: | ||
| case Stmt::CaseStmtClass: | ||
| case Stmt::DeferStmtClass: | ||
| case Stmt::SEHLeaveStmtClass: | ||
| case Stmt::SYCLKernelCallStmtClass: | ||
| llvm_unreachable("should have emitted these statements as simple"); | ||
|
|
@@ -539,6 +540,9 @@ bool CodeGenFunction::EmitSimpleStmt(const Stmt *S, | |
| case Stmt::CaseStmtClass: | ||
| EmitCaseStmt(cast<CaseStmt>(*S), Attrs); | ||
| break; | ||
| case Stmt::DeferStmtClass: | ||
| EmitDeferStmt(cast<DeferStmt>(*S)); | ||
| break; | ||
| case Stmt::SEHLeaveStmtClass: | ||
| EmitSEHLeaveStmt(cast<SEHLeaveStmt>(*S)); | ||
| break; | ||
|
|
@@ -2000,6 +2004,80 @@ void CodeGenFunction::EmitDefaultStmt(const DefaultStmt &S, | |
| EmitStmt(S.getSubStmt()); | ||
| } | ||
|
|
||
| namespace { | ||
| struct EmitDeferredStatement final : EHScopeStack::Cleanup { | ||
| const DeferStmt &Stmt; | ||
| EmitDeferredStatement(const DeferStmt *Stmt) : Stmt(*Stmt) {} | ||
|
|
||
| void Emit(CodeGenFunction &CGF, Flags) override { | ||
| // Take care that any cleanups pushed by the body of the 'defer' don't | ||
| // clobber the current cleanup slot value. | ||
Sirraide marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| // | ||
| // This situation warrants some explanation: Assume we have a scope that | ||
Sirraide marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| // pushes a cleanup; when that scope is exited, we need to run that cleanup; | ||
| // this is accomplished by emitting the cleanup into a separate block and | ||
| // then branching to that block at scope exit. | ||
| // | ||
| // Where this gets complicated is if we exit the scope in multiple different | ||
| // ways; e.g. in a 'for' loop, we may exit the scope of its body by falling | ||
| // off the end (in which case we need to run the cleanup and then branch to | ||
| // the increment), or by 'break'ing out of the loop (in which case we need | ||
| // to run the cleanup and then branch to the loop exit block); in both cases | ||
| // we first branch to the cleanup block to run the cleanup, but the block we | ||
| // need to jump to *after* running the cleanup is different. | ||
| // | ||
| // This is accomplished using a local integer variable called the 'cleanup | ||
| // slot': before branching to the cleanup block, we store a value into that | ||
| // slot. Then, in the cleanup block, after running the cleanup, we load the | ||
| // value of that variable and 'switch' on it to branch to the appropriate | ||
| // continuation block. | ||
| // | ||
| // The problem that arises once 'defer' statements are involved is that the | ||
| // body of a 'defer' is an arbitrary statement which itself can create more | ||
| // cleanups. This means we may end up overwriting the cleanup slot before we | ||
| // ever have a chance to 'switch' on it, which means that once we *do* get | ||
| // to the 'switch', we end up in whatever block the cleanup code happened to | ||
| // pick as the default 'switch' exit label! | ||
| // | ||
| // That is, what is normally supposed to happen is something like: | ||
| // | ||
| // 1. Store 'X' to cleanup slot. | ||
| // 2. Branch to cleanup block. | ||
| // 3. Execute cleanup. | ||
| // 4. Read value from cleanup slot. | ||
| // 5. Branch to the block associated with 'X'. | ||
| // | ||
| // But if we encounter a 'defer' statement that contains a cleanup, then | ||
| // what might instead happen is: | ||
| // | ||
| // 1. Store 'X' to cleanup slot. | ||
| // 2. Branch to cleanup block. | ||
| // 3. Execute cleanup; this ends up pushing another cleanup, so: | ||
| // 3a. Store 'Y' to cleanup slot. | ||
| // 3b. Run steps 2–5 recursively. | ||
| // 4. Read value from cleanup slot, which is now 'Y' instead of 'X'. | ||
| // 5. Branch to the block associated with 'Y'... which doesn't even | ||
| // exist because the value 'Y' is only meaningful for the inner | ||
| // cleanup. The result is we just branch 'somewhere random'. | ||
| // | ||
| // The rest of the cleanup code simply isn't prepared to handle this case | ||
| // because there are no other cleanups that can push more cleanups, and | ||
| // thus, emitting any other cleanup cannot clobber the cleanup slot. | ||
| // | ||
| // To prevent this from happening, simply force the allocation of a new | ||
| // cleanup slot for the body of the defer statement by temporarily clearing | ||
| // out any existing slot. | ||
| SaveAndRestore PreserveCleanupSlot{CGF.NormalCleanupDest, | ||
| RawAddress::invalid()}; | ||
| CGF.EmitStmt(Stmt.getBody()); | ||
|
||
| } | ||
| }; | ||
| } // namespace | ||
|
|
||
| void CodeGenFunction::EmitDeferStmt(const DeferStmt &S) { | ||
| EHStack.pushCleanup<EmitDeferredStatement>(NormalAndEHCleanup, &S); | ||
| } | ||
|
|
||
| /// CollectStatementsForCase - Given the body of a 'switch' statement and a | ||
| /// constant value that is being switched on, see if we can dead code eliminate | ||
| /// the body of the switch to a simple series of statements to emit. Basically, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.