Skip to content
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
a2f523c
[Clang] Add support for the C 'defer' TS
Sirraide Aug 6, 2025
0a756c5
Implement codegen
Sirraide Aug 7, 2025
2505774
Check gotos around defer
Sirraide Aug 7, 2025
58a6c2d
Forbid break/continue/return/__leave out of defer
Sirraide Aug 7, 2025
0a5c8fa
Jumping out of a scope that contains a defer is fine
Sirraide Aug 7, 2025
b9e845d
Add a bunch of tests
Sirraide Aug 7, 2025
2eaecf5
Define __STDC_DEFER_TS25755__
Sirraide Aug 7, 2025
e74537c
Add test for 'defer defer'
Sirraide Aug 7, 2025
11a40d0
Add test for 'main' since the TS mentions it explicitly
Sirraide Aug 7, 2025
bbf4389
Fix AST printer
Sirraide Aug 7, 2025
3350e28
Add AST dump and serialisation test
Sirraide Aug 7, 2025
40b449b
Add VLA test and rename -fdefer-ts -> -fexperimental-defer-ts
Sirraide Aug 7, 2025
9706711
Add a SEH test
Sirraide Aug 7, 2025
d56cfb9
Add tests involving [[noreturn]] functions
Sirraide Aug 7, 2025
e51b68e
Forbid setjmp/longjmp within a defer statement
Sirraide Aug 7, 2025
8948a46
clang-format
Sirraide Aug 7, 2025
b944bde
Test __builtin_setjmp/longjmp as well
Sirraide Aug 7, 2025
fbc4f5a
Remove assertion
Sirraide Aug 7, 2025
65c2b88
Merge branch 'main' into defer
Sirraide Oct 10, 2025
2eb59e3
-fexperimental-defer-ts -> -fdefer-ts
Sirraide Oct 10, 2025
d7fc314
Undo whitespace changes
Sirraide Oct 10, 2025
62261b1
Use ShouldParseIf
Sirraide Oct 10, 2025
7106167
Update test
Sirraide Oct 10, 2025
a0b19b8
Add release note
Sirraide Oct 13, 2025
e8f5982
Address some of Aaron’s comments
Sirraide Oct 13, 2025
191b8f7
Add this horrible test case
Sirraide Oct 13, 2025
48a0433
Add musttail test
Sirraide Oct 13, 2025
f117c4b
Allow attributes as an extension
Sirraide Oct 13, 2025
48fc974
Add `_Defer`
Sirraide Oct 17, 2025
6b041e8
Add stddefer.h and make 'defer' no longer a keyword
Sirraide Nov 7, 2025
45c8113
Update stddefer.h
Sirraide Nov 10, 2025
3a8741e
Merge branch 'main' into defer
Sirraide Nov 10, 2025
004c40e
Move KEYDEFERTS to header
Sirraide Nov 10, 2025
34ec829
I accidentally deleted KEYFIXEDPOINT...
Sirraide Nov 10, 2025
0e238f1
Don't clobber the cleanup slot when emitting nested cleanups
Sirraide Dec 4, 2025
24d7212
Add scope reentry test
Sirraide Dec 4, 2025
c18ac11
Address review feedback
Sirraide Dec 4, 2025
53deeda
Merge branch 'main' into defer
Sirraide Dec 4, 2025
2ace560
Update clang/test/Lexer/defer-keyword.cpp
Sirraide Dec 8, 2025
fd13abb
Save and restore the current cleanup destination
Sirraide Dec 8, 2025
2965221
Merge branch 'main' into defer
Sirraide Dec 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions clang-tools-extra/clang-tidy/bugprone/BranchCloneCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,12 @@ static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1,
return false;
return true;
}
case Stmt::DeferStmtClass: {
const auto *DefStmt1 = cast<DeferStmt>(Stmt1);
const auto *DefStmt2 = cast<DeferStmt>(Stmt2);
return isIdenticalStmt(Ctx, DefStmt1->getBody(), DefStmt2->getBody(),
IgnoreSideEffects);
}
case Stmt::CompoundStmtClass: {
const auto *CompStmt1 = cast<CompoundStmt>(Stmt1);
const auto *CompStmt2 = cast<CompoundStmt>(Stmt2);
Expand Down
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@ Resolutions to C++ Defect Reports
C Language Changes
------------------

- Implemented the ``defer`` Technical Specification (n3589); it is enabled
in C mode by passing ``-fdefer-ts``.

C2y Feature Support
^^^^^^^^^^^^^^^^^^^
- No longer triggering ``-Wstatic-in-inline`` in C2y mode; use of a static
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -2561,6 +2561,7 @@ DEF_TRAVERSE_STMT(DefaultStmt, {})
DEF_TRAVERSE_STMT(DoStmt, {})
DEF_TRAVERSE_STMT(ForStmt, {})
DEF_TRAVERSE_STMT(GotoStmt, {})
DEF_TRAVERSE_STMT(DeferStmt, {})
DEF_TRAVERSE_STMT(IfStmt, {})
DEF_TRAVERSE_STMT(IndirectGotoStmt, {})
DEF_TRAVERSE_STMT(LabelStmt, {})
Expand Down
52 changes: 52 additions & 0 deletions clang/include/clang/AST/Stmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,16 @@ class alignas(void *) Stmt {
SourceLocation KeywordLoc;
};

class DeferStmtBitfields {
friend class DeferStmt;

LLVM_PREFERRED_TYPE(StmtBitfields)
unsigned : NumStmtBits;

/// The location of the "defer".
SourceLocation DeferLoc;
};

//===--- Expression bitfields classes ---===//

class ExprBitfields {
Expand Down Expand Up @@ -1318,6 +1328,7 @@ class alignas(void *) Stmt {
LoopControlStmtBitfields LoopControlStmtBits;
ReturnStmtBitfields ReturnStmtBits;
SwitchCaseBitfields SwitchCaseBits;
DeferStmtBitfields DeferStmtBits;

// Expressions
ExprBitfields ExprBits;
Expand Down Expand Up @@ -3212,6 +3223,47 @@ class ReturnStmt final
}
};

/// DeferStmt - This represents a deferred statement.
class DeferStmt : public Stmt {
friend class ASTStmtReader;

/// The deferred statement.
Stmt *Body;

public:
DeferStmt(SourceLocation DeferLoc, Stmt *Body) : Stmt(DeferStmtClass) {
setDeferLoc(DeferLoc);
setBody(Body);
}

explicit DeferStmt(EmptyShell Empty) : Stmt(DeferStmtClass, Empty) {}

SourceLocation getDeferLoc() const { return DeferStmtBits.DeferLoc; }
void setDeferLoc(SourceLocation DeferLoc) {
DeferStmtBits.DeferLoc = DeferLoc;
}

Stmt *getBody() { return Body; }
const Stmt *getBody() const { return Body; }
void setBody(Stmt *S) {
assert(S && "defer body must not be null");
Body = S;
}

SourceLocation getBeginLoc() const { return getDeferLoc(); }
SourceLocation getEndLoc() const { return Body->getEndLoc(); }

child_range children() { return child_range(&Body, &Body + 1); }

const_child_range children() const {
return const_child_range(&Body, &Body + 1);
}

static bool classof(const Stmt *S) {
return S->getStmtClass() == DeferStmtClass;
}
};

/// AsmStmt is the base class for GCCAsmStmt and MSAsmStmt.
class AsmStmt : public Stmt {
protected:
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,8 @@ def err_address_of_label_outside_fn : Error<
"use of address-of-label extension outside of a function body">;
def err_asm_operand_wide_string_literal : Error<
"cannot use %select{unicode|wide}0 string literal in 'asm'">;
def err_defer_ts_labeled_stmt : Error<
"substatement of defer must not be a label">;

def err_asm_expected_string : Error<
"expected string literal %select{or parenthesized constant expression |}0in 'asm'">;
Expand Down
16 changes: 16 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -6815,6 +6815,7 @@ def note_protected_by_objc_weak_init : Note<
"jump bypasses initialization of __weak variable">;
def note_protected_by_non_trivial_c_struct_init : Note<
"jump bypasses initialization of variable of non-trivial C struct type">;
def note_protected_by_defer_stmt : Note<"jump bypasses defer statement">;
def note_enters_block_captures_cxx_obj : Note<
"jump enters lifetime of block which captures a destructible C++ object">;
def note_enters_block_captures_strong : Note<
Expand All @@ -6828,6 +6829,7 @@ def note_enters_compound_literal_scope : Note<
"jump enters lifetime of a compound literal that is non-trivial to destruct">;
def note_enters_statement_expression : Note<
"jump enters a statement expression">;
def note_enters_defer_stmt : Note<"jump enters a defer statement">;

def note_exits_cleanup : Note<
"jump exits scope of variable with __attribute__((cleanup))">;
Expand Down Expand Up @@ -6873,6 +6875,16 @@ def note_exits_block_captures_non_trivial_c_struct : Note<
"to destroy">;
def note_exits_compound_literal_scope : Note<
"jump exits lifetime of a compound literal that is non-trivial to destruct">;
def note_exits_defer_stmt : Note<"jump exits a defer statement">;
def err_jump_out_of_defer_stmt : Error<
"cannot %enum_select<DeferJumpKind>{"
"%Break{break out of a}|"
"%Continue{continue loop outside of enclosing}|"
"%Return{return from a}|"
"%SEHLeave{__leave a}"
"}0 defer statement">;
def err_defer_invalid_sjlj : Error<
"cannot use %0 inside a defer statement">;

def err_func_returning_qualified_void : ExtWarn<
"function cannot return qualified void type %0">,
Expand Down Expand Up @@ -10964,6 +10976,8 @@ def err_switch_explicit_conversion : Error<
def err_switch_incomplete_class_type : Error<
"switch condition has incomplete class type %0">;

// TODO: It ought to be possible to refactor these to be a single warning that
// uses %enum_select.
def warn_empty_if_body : Warning<
"if statement has empty body">, InGroup<EmptyBody>;
def warn_empty_for_body : Warning<
Expand All @@ -10974,6 +10988,8 @@ def warn_empty_while_body : Warning<
"while loop has empty body">, InGroup<EmptyBody>;
def warn_empty_switch_body : Warning<
"switch statement has empty body">, InGroup<EmptyBody>;
def warn_empty_defer_body : Warning<
"defer statement has empty body">, InGroup<EmptyBody>;
def note_empty_body_on_separate_line : Note<
"put the semicolon on a separate line to silence this warning">;

Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/Basic/IdentifierTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ enum TokenKey : unsigned {
KEYNOZOS = 0x4000000,
KEYHLSL = 0x8000000,
KEYFIXEDPOINT = 0x10000000,
KEYMAX = KEYFIXEDPOINT, // The maximum key
KEYDEFERTS = 0x20000000,
KEYMAX = KEYDEFERTS, // The maximum key
KEYALLCXX = KEYCXX | KEYCXX11 | KEYCXX20,
KEYALL = (KEYMAX | (KEYMAX - 1)) & ~KEYNOMS18 & ~KEYNOOPENCL &
~KEYNOZOS // KEYNOMS18, KEYNOOPENCL, KEYNOZOS are excluded.
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/LangOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ LANGOPT(NoSignedZero , 1, 0, Benign, "Permit Floating Point optimization wi
LANGOPT(AllowRecip , 1, 0, Benign, "Permit Floating Point reciprocal")
LANGOPT(ApproxFunc , 1, 0, Benign, "Permit Floating Point approximation")
LANGOPT(NamedLoops , 1, 0, Benign, "Permit named break/continue")
LANGOPT(DeferTS , 1, 0, Benign, "C 'defer' Technical Specification")

ENUM_LANGOPT(ComplexRange, ComplexRangeKind, 3, CX_None, NotCompatible, "Enable use of range reduction for complex arithmetics.")

Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/StmtNodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def ForStmt : StmtNode<Stmt>;
def GotoStmt : StmtNode<Stmt>;
def IndirectGotoStmt : StmtNode<Stmt>;
def ReturnStmt : StmtNode<Stmt>;
def DeferStmt : StmtNode<Stmt>;
def DeclStmt : StmtNode<Stmt>;
def SwitchCase : StmtNode<Stmt, 1>;
def CaseStmt : StmtNode<SwitchCase>;
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ PUNCTUATOR(greatergreatergreater, ">>>")
// CHAR8SUPPORT - This is a keyword if 'char8_t' is a built-in type
// KEYFIXEDPOINT - This is a keyword according to the N1169 fixed point
// extension.
// KEYDEFERTS - This is a keyword if the C 'defer' TS is enabled
// KEYZOS - This is a keyword in C/C++ on z/OS
//
KEYWORD(auto , KEYALL)
Expand Down Expand Up @@ -441,6 +442,9 @@ KEYWORD(_Float16 , KEYALL)
C23_KEYWORD(typeof , KEYGNU)
C23_KEYWORD(typeof_unqual , 0)

// 'defer' TS
KEYWORD(_Defer , KEYDEFERTS)

// ISO/IEC JTC1 SC22 WG14 N1169 Extension
KEYWORD(_Accum , KEYFIXEDPOINT)
KEYWORD(_Fract , KEYFIXEDPOINT)
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -1656,6 +1656,14 @@ defm named_loops
PosFlag<SetTrue, [], [CC1Option], "Enable support for named loops">,
NegFlag<SetFalse>>;

// C 'defer' TS
defm defer_ts : BoolFOption<"defer-ts",
LangOpts<"DeferTS">, DefaultFalse,
PosFlag<SetTrue, [], [ClangOption, CC1Option],
"Enable support for the C 'defer' Technical Specification">,
NegFlag<SetFalse>>,
ShouldParseIf<!strconcat("!", cplusplus.KeyPath)>;

// C++ Coroutines
defm coroutines : BoolFOption<"coroutines",
LangOpts<"Coroutines">, Default<cpp20.KeyPath>,
Expand Down
10 changes: 10 additions & 0 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -7504,6 +7504,16 @@ class Parser : public CodeCompletionHandler {

StmtResult ParseBreakOrContinueStatement(bool IsContinue);

/// ParseDeferStatement
/// \verbatim
/// defer-statement:
/// 'defer' deferred-block
///
/// deferred-block:
/// unlabeled-statement
/// \endverbatim
StmtResult ParseDeferStatement(SourceLocation *TrailingElseLoc);

StmtResult ParsePragmaLoopHint(StmtVector &Stmts, ParsedStmtContext StmtCtx,
SourceLocation *TrailingElseLoc,
ParsedAttributes &Attrs,
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -10918,6 +10918,10 @@ class Sema final : public SemaBase {
/// Stack of active SEH __finally scopes. Can be empty.
SmallVector<Scope *, 2> CurrentSEHFinally;

/// Stack of 'defer' statements that are currently being parsed, as well
/// as the locations of their 'defer' keywords. Can be empty.
SmallVector<std::pair<Scope *, SourceLocation>, 2> CurrentDefer;

StmtResult ActOnExprStmt(ExprResult Arg, bool DiscardedValue = true);
StmtResult ActOnExprStmtError();

Expand Down Expand Up @@ -11064,6 +11068,10 @@ class Sema final : public SemaBase {
StmtResult ActOnBreakStmt(SourceLocation BreakLoc, Scope *CurScope,
LabelDecl *Label, SourceLocation LabelLoc);

void ActOnStartOfDeferStmt(SourceLocation DeferLoc, Scope *CurScope);
void ActOnDeferStmtError(Scope *CurScope);
StmtResult ActOnEndOfDeferStmt(Stmt *Body, Scope *CurScope);

struct NamedReturnInfo {
const VarDecl *Candidate;

Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Serialization/ASTBitCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -2061,6 +2061,7 @@ enum StmtCode {
// HLSL Constructs
EXPR_HLSL_OUT_ARG,

STMT_DEFER,
};

/// The kinds of designators that can occur in a
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/AST/StmtPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,11 @@ void StmtPrinter::VisitBreakStmt(BreakStmt *Node) {
if (Policy.IncludeNewlines) OS << NL;
}

void StmtPrinter::VisitDeferStmt(DeferStmt *Node) {
Indent() << "_Defer";
PrintControlledStmt(Node->getBody());
}

void StmtPrinter::VisitReturnStmt(ReturnStmt *Node) {
Indent() << "return";
if (Node->getRetValue()) {
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/AST/StmtProfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,8 @@ void StmtProfiler::VisitReturnStmt(const ReturnStmt *S) {
VisitStmt(S);
}

void StmtProfiler::VisitDeferStmt(const DeferStmt *S) { VisitStmt(S); }

void StmtProfiler::VisitGCCAsmStmt(const GCCAsmStmt *S) {
VisitStmt(S);
ID.AddBoolean(S->isVolatile());
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Basic/IdentifierTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ static KeywordStatus getKeywordStatusHelper(const LangOptions &LangOpts,
return KS_Unknown;
case KEYFIXEDPOINT:
return LangOpts.FixedPoint ? KS_Enabled : KS_Disabled;
case KEYDEFERTS:
return LangOpts.DeferTS ? KS_Enabled : KS_Disabled;
default:
llvm_unreachable("Unknown KeywordStatus flag");
}
Expand Down
78 changes: 78 additions & 0 deletions clang/lib/CodeGen/CGStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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.
//
// This situation warrants some explanation: Assume we have a scope that
// 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());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@erichkeane can you have a look at that bit?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CC @efriedma-quic @rjmccall for other codegen opinions as well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I would appreciate it if someone more familiar w/ the cleanups code could confirm that this is enough because it’s rather complicated

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is precedent in the code for this sort of construct. See PerformFinally in CGException.cpp: it loads the destination on entry, and stores it on exit. Not sure that's necessarily better, but I'd prefer to follow the existing pattern unless there's some compelling reason to diverge.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh interesting, I didn’t think there was; I’ll take a look at that

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I did that; that worked. It does mean we get a few more loads and stores even if the _Defer doesn’t use the cleanup slot at all, but the optimiser will just remove those so I don’t think it matters.

}
};
} // 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,
Expand Down
Loading