From b6d553b71b5f1de8c9100aa093cd6a1021154b97 Mon Sep 17 00:00:00 2001 From: Vassil Vassilev Date: Tue, 21 Jul 2026 14:08:14 +0000 Subject: [PATCH] Reverse switches on the stored condition instead of a control-flow tape Reverse-mode differentiation of a switch statement recorded, in a second control-flow tape, which case a `break` exited from, then re-dispatched the reverse sweep by popping that tape. The information is redundant: the switch condition is already stored (`_cond`), and every case guard already compares against it (`if (v == _cond) break`). The extra tape -- and the BreakContStmtHandler state backing it -- only duplicated what the condition carries. Drop the control-flow tape for switches and re-switch on the stored condition directly. Each fall-through group's reverse entry is now labelled with its original case values rather than a synthesized counter, and the trailing group (closed by the switch end rather than a break) is labelled in VisitSwitchStmt. The per-case guards are unchanged. Loops keep their control-flow tape, where a break's iteration genuinely cannot be recovered from a condition. This is behaviour-preserving: all Switch.C/SwitchInit.C execution results are unchanged; only the generated code -- forward (the counter pushes are gone) and reverse -- and its FileCheck baselines change, with one fewer tape. A switch whose cases return rather than break is added to Switch.C to cover the returning-case shape. SwitchInit.C no longer needs its Valgrind XFAIL: the control-flow tape it tripped on under memcheck is gone. --- .../clad/Differentiator/ReverseModeVisitor.h | 41 +- lib/Differentiator/ReverseModeVisitor.cpp | 78 +++- test/Gradient/Switch.C | 415 ++++++++++-------- test/Gradient/SwitchInit.C | 26 +- 4 files changed, 343 insertions(+), 217 deletions(-) diff --git a/include/clad/Differentiator/ReverseModeVisitor.h b/include/clad/Differentiator/ReverseModeVisitor.h index f9454a321..17c338ee6 100644 --- a/include/clad/Differentiator/ReverseModeVisitor.h +++ b/include/clad/Differentiator/ReverseModeVisitor.h @@ -703,17 +703,18 @@ namespace clad { StmtDiff DifferentiateCanonicalLoop(const clang::ForStmt* S); - /// This class modifies forward and reverse blocks of the loop/switch - /// body so that `break` and `continue` statements are correctly - /// handled. `break` and `continue` statements are handled by - /// enclosing entire reverse block loop body in a switch statement - /// and only executing the statements, with the help of case labels, - /// that were executed in the associated forward iteration. This is - /// determined by keeping track of which `break`/`continue` statement - /// was hit in which iteration and that in turn helps to determine which - /// case label should be selected. - /// - /// Class usage: + /// Handles `break`/`continue` inside a differentiated loop. It owns a + /// control-flow tape recording which one fired in which iteration, so the + /// reverse loop body -- wrapped in a switch over that tape -- replays + /// exactly the statements the forward iteration executed. The members below + /// serve this tape. + /// + /// One handler is pushed per enclosing loop or switch, so the top of the + /// stack is the innermost construct a `break` binds to. A switch sets + /// m_IsInvokedBySwitchStmt and leaves the tape unused -- its reverse is + /// rebuilt from the stored condition instead (VisitSwitchStmt). + /// + /// Loop usage: /// /// ```cpp /// auto activeBreakContStmtHandler = PushBreakContStmtHandler(); @@ -760,6 +761,9 @@ namespace clad { clang::Expr* CreateCFTapePushExpr(std::size_t value); public: + /// True when the innermost breakable construct is a source-level switch, + /// whose `break`s are handled in VisitSwitchStmt rather than by the + /// control-flow tape. Lets VisitBreakStmt pick the right handling. bool m_IsInvokedBySwitchStmt = false; BreakContStmtHandler(ReverseModeVisitor& RMV, bool forSwitchStmt = false) @@ -835,9 +839,18 @@ namespace clad { /// Stores data required for differentiating a switch statement. struct SwitchStmtInfo { + /// The forward-pass case/default labels, in source order. llvm::SmallVector cases; + /// The stored switch condition (`_cond`), reused both as the reverse + /// switch discriminator and by every case guard. clang::Expr* switchStmtCond = nullptr; clang::IfStmt* defaultIfBreakExpr = nullptr; + /// Index into `cases` of the first label of the fall-through group not + /// yet closed by a `break`. + std::size_t groupStart = 0; + /// The reverse switch's entry labels, built from the original case values + /// rather than a control-flow tape counter. + llvm::SmallVector reverseEntryCases; }; /// Maintains a stack of `SwitchStmtInfo`. @@ -854,6 +867,12 @@ namespace clad { void PopSwitchStmtInfo() { m_SwitchStmtsData.pop_back(); } + /// Closes the currently open fall-through group `cases[groupStart..)`: + /// builds its reverse-switch entry from the group's original case labels, + /// registers them in `reverseEntryCases`, advances `groupStart`, and + /// returns the label chain to prepend before the group's adjoint replay. + clang::Stmt* CloseReverseSwitchCaseGroup(SwitchStmtInfo& SSData); + private: // When differentiating ArrayInitLoopExpr, we need to replace // ArrayInitIndexExpr with real indices. We need to both add and pop them in diff --git a/lib/Differentiator/ReverseModeVisitor.cpp b/lib/Differentiator/ReverseModeVisitor.cpp index df71ff7d6..7f0af1841 100644 --- a/lib/Differentiator/ReverseModeVisitor.cpp +++ b/lib/Differentiator/ReverseModeVisitor.cpp @@ -4412,6 +4412,8 @@ Expr* ReverseModeVisitor::getStdInitListSizeExpr(const Expr* E) { // statement body will be processed in both the forward and the reverse // pass. Thus, we do not need to add them in the differentiated function. if (!(SSData->cases.empty())) { + // The forward sweep is a clone of the original switch: control flow is + // recorded implicitly by the stored condition, so no tape is needed. Sema::ConditionResult condRes = m_Sema.ActOnCondition(getCurrentScope(), noLoc, CloneNode(condExpr), Sema::ConditionKind::Switch); @@ -4421,18 +4423,40 @@ Expr* ReverseModeVisitor::getStdInitListSizeExpr(const Expr* E) { /*LParenLoc=*/noLoc, nullptr, condRes, /*RParenLoc=*/noLoc) .getAs(); - activeBreakContHandler->UpdateForwAndRevBlocks(bodyDiff); - - // Registers all the cases to the switch statement. for (auto* SC : SSData->cases) forwardSS->addSwitchCase(SC); - forwardSS = m_Sema.ActOnFinishSwitchStmt(noLoc, forwardSS, bodyDiff.getStmt()) .getAs(); + // The reverse sweep re-switches on the stored condition. Each + // fall-through group's adjoint replay is entered through the original + // case values (the per-case `if (v == _cond) break` guards emitted by + // VisitCaseStmt peel off the cases that did not run). The trailing group + // is closed by the switch end rather than a break, so label it here; it + // is the topmost group in the bottom-up reverse block. + Stmt* revBody = bodyDiff.getStmt_dx(); + if (SSData->groupStart < SSData->cases.size()) { + Stmt* finalEntry = CloseReverseSwitchCaseGroup(*SSData); + revBody = + utils::PrependAndCreateCompoundStmt(m_Context, revBody, finalEntry); + } + Sema::ConditionResult revCondRes = + m_Sema.ActOnCondition(getCurrentScope(), noLoc, CloneNode(condExpr), + Sema::ConditionKind::Switch); + SwitchStmt* reverseSS = + m_Sema + .ActOnStartOfSwitchStmt(/*SwitchLoc=*/noLoc, + /*LParenLoc=*/noLoc, nullptr, revCondRes, + /*RParenLoc=*/noLoc) + .getAs(); + for (auto* SC : SSData->reverseEntryCases) + reverseSS->addSwitchCase(SC); + reverseSS = m_Sema.ActOnFinishSwitchStmt(noLoc, reverseSS, revBody) + .getAs(); + addToCurrentBlock(forwardSS, direction::forward); - addToCurrentBlock(bodyDiff.getStmt_dx(), direction::reverse); + addToCurrentBlock(reverseSS, direction::reverse); } PopBreakContStmtHandler(); @@ -4492,6 +4516,36 @@ Expr* ReverseModeVisitor::getStdInitListSizeExpr(const Expr* E) { return {endBlock(direction::forward), endBlock(direction::reverse)}; } + Stmt* + ReverseModeVisitor::CloseReverseSwitchCaseGroup(SwitchStmtInfo& SSData) { + // Build `case v1: case v2: ... [default:] ;` for the still-open group's + // original labels, innermost first. The order of the labels is irrelevant + // (they all fall into the same reverse replay); the shared null + // substatement lets the group's adjoints follow as siblings in the switch + // body. + Stmt* inner = m_Sema.ActOnNullStmt(noLoc).get(); + for (std::size_t i = SSData.groupStart, e = SSData.cases.size(); i != e; + ++i) { + SwitchCase* rev = nullptr; + if (isa(SSData.cases[i])) { + rev = new (m_Context) DefaultStmt(noLoc, noLoc, inner); + } else { + auto* fwd = cast(SSData.cases[i]); + // Clone the range high end too, matching the forward case, so a GNU + // `case a ... b:` keeps its extent in the reverse switch. + Expr* rhs = fwd->getRHS() ? CloneNode(fwd->getRHS()) : nullptr; + auto* caseStmt = CaseStmt::Create(m_Context, CloneNode(fwd->getLHS()), + rhs, noLoc, noLoc, noLoc); + caseStmt->setSubStmt(inner); + rev = caseStmt; + } + SSData.reverseEntryCases.push_back(rev); + inner = rev; + } + SSData.groupStart = SSData.cases.size(); + return inner; + } + static bool hasCheckpointingPragma(ASTContext& C, SourceLocation loopLoc, const DiffRequest& request) { if (!loopLoc.isValid()) @@ -4640,10 +4694,18 @@ Expr* ReverseModeVisitor::getStdInitListSizeExpr(const Expr* E) { Stmt* newBS = clad_compat::ActOnBreakStmt(m_Sema, noLoc, getCurrentScope()).get(); auto* activeBreakContHandler = GetActiveBreakContStmtHandler(); + // A break in a source-level switch only closes the current fall-through + // group (VisitSwitchStmt turns each group into a reverse-switch entry). + // Unlike the loop path below, it records nothing on a control-flow tape. + if (activeBreakContHandler->m_IsInvokedBySwitchStmt) { + addToCurrentBlock(newBS); + Stmt* revEntry = CloseReverseSwitchCaseGroup(*GetActiveSwitchStmtInfo()); + return {endBlock(direction::forward), revEntry}; + } Stmt* CFCaseStmt = activeBreakContHandler->GetNextCFCaseStmt(); Stmt* pushExprToCurrentCase = activeBreakContHandler ->CreateCFTapePushExprToCurrentCase(); - if (isInsideLoop && !activeBreakContHandler->m_IsInvokedBySwitchStmt) { + if (isInsideLoop) { Expr* tapeBackExprForCurrentCase = activeBreakContHandler->CreateCFTapeBackExprForCurrentCase(); if (m_CurrentBreakFlagExpr) { @@ -4733,7 +4795,9 @@ Expr* ReverseModeVisitor::getStdInitListSizeExpr(const Expr* E) { void ReverseModeVisitor::BreakContStmtHandler::UpdateForwAndRevBlocks( StmtDiff& bodyDiff) { - if (m_SwitchCases.empty() && !m_IsInvokedBySwitchStmt) + // Only loops reach here; a loop with no break/continue needs no + // control-flow switch in its reverse body. + if (m_SwitchCases.empty()) return; // Add case statement in the beginning of the reverse block diff --git a/test/Gradient/Switch.C b/test/Gradient/Switch.C index 655f9a742..376c3d893 100644 --- a/test/Gradient/Switch.C +++ b/test/Gradient/Switch.C @@ -23,10 +23,9 @@ double fn1(double i, double j) { // CHECK: void fn1_grad(double i, double j, double *_d_i, double *_d_j) { // CHECK-NEXT: int _cond0; // CHECK-NEXT: double _t0; -// CHECK-NEXT: clad::tape _t1 = {}; +// CHECK-NEXT: double _t1; // CHECK-NEXT: double _t2; // CHECK-NEXT: double _t3; -// CHECK-NEXT: double _t4; // CHECK-NEXT: double _d_res = 0.; // CHECK-NEXT: double res = 0; // CHECK-NEXT: int _d_count = 0; @@ -40,37 +39,37 @@ double fn1(double i, double j) { // CHECK-NEXT: _t0 = res; // CHECK-NEXT: } // CHECK-NEXT: { -// CHECK-NEXT: clad::push(_t1, {{1U|1UL|1ULL}}); // CHECK-NEXT: break; // CHECK-NEXT: } // CHECK-NEXT: { // CHECK-NEXT: case 1: // CHECK-NEXT: res += i * i; -// CHECK-NEXT: _t2 = res; +// CHECK-NEXT: _t1 = res; // CHECK-NEXT: } // CHECK-NEXT: { // CHECK-NEXT: { // CHECK-NEXT: case 2: // CHECK-NEXT: res += j * j; -// CHECK-NEXT: _t3 = res; +// CHECK-NEXT: _t2 = res; // CHECK-NEXT: } // CHECK-NEXT: } // CHECK-NEXT: { // CHECK-NEXT: default: // CHECK-NEXT: res += i * i * j * j; -// CHECK-NEXT: _t4 = res; +// CHECK-NEXT: _t3 = res; // CHECK-NEXT: } -// CHECK-NEXT: clad::push(_t1, {{2U|2UL|2ULL}}); // CHECK-NEXT: } // CHECK-NEXT: } // CHECK-NEXT: _d_res += 1; // CHECK-NEXT: { -// CHECK-NEXT: switch (clad::pop(_t1)) { -// CHECK-NEXT: case {{2U|2UL|2ULL}}: +// CHECK-NEXT: switch (_cond0) { +// CHECK-NEXT: default: +// CHECK-NEXT: case 2: +// CHECK-NEXT: case 1: // CHECK-NEXT: ; // CHECK-NEXT: { // CHECK-NEXT: { -// CHECK-NEXT: res = _t4; +// CHECK-NEXT: res = _t3; // CHECK-NEXT: *_d_i += _d_res * j * j * i; // CHECK-NEXT: *_d_i += i * _d_res * j * j; // CHECK-NEXT: *_d_j += i * i * _d_res * j; @@ -82,7 +81,7 @@ double fn1(double i, double j) { // CHECK-NEXT: { // CHECK-NEXT: { // CHECK-NEXT: { -// CHECK-NEXT: res = _t3; +// CHECK-NEXT: res = _t2; // CHECK-NEXT: *_d_j += _d_res * j; // CHECK-NEXT: *_d_j += j * _d_res; // CHECK-NEXT: } @@ -92,14 +91,14 @@ double fn1(double i, double j) { // CHECK-NEXT: } // CHECK-NEXT: { // CHECK-NEXT: { -// CHECK-NEXT: res = _t2; +// CHECK-NEXT: res = _t1; // CHECK-NEXT: *_d_i += _d_res * i; // CHECK-NEXT: *_d_i += i * _d_res; // CHECK-NEXT: } // CHECK-NEXT: if (1 == _cond0) // CHECK-NEXT: break; // CHECK-NEXT: } -// CHECK-NEXT: case {{1U|1UL|1ULL}}: +// CHECK-NEXT: case 0: // CHECK-NEXT: ; // CHECK-NEXT: { // CHECK-NEXT: { @@ -134,10 +133,9 @@ double fn2(double i, double j) { // CHECK-NEXT: double _t0; // CHECK-NEXT: double _t1; // CHECK-NEXT: double _t2; -// CHECK-NEXT: clad::tape _t3 = {}; +// CHECK-NEXT: double _t3; // CHECK-NEXT: double _t4; // CHECK-NEXT: double _t5; -// CHECK-NEXT: double _t6; // CHECK-NEXT: double _d_res = 0.; // CHECK-NEXT: double res = 0; // CHECK-NEXT: { @@ -154,50 +152,48 @@ double fn2(double i, double j) { // CHECK-NEXT: _t2 = res; // CHECK-NEXT: } // CHECK-NEXT: { -// CHECK-NEXT: clad::push(_t3, {{1U|1UL|1ULL}}); // CHECK-NEXT: break; // CHECK-NEXT: } // CHECK-NEXT: { // CHECK-NEXT: case 1: // CHECK-NEXT: res += j; -// CHECK-NEXT: _t4 = res; +// CHECK-NEXT: _t3 = res; // CHECK-NEXT: } // CHECK-NEXT: { // CHECK-NEXT: case 2: // CHECK-NEXT: res += i * j; -// CHECK-NEXT: _t5 = res; +// CHECK-NEXT: _t4 = res; // CHECK-NEXT: } // CHECK-NEXT: { -// CHECK-NEXT: clad::push(_t3, {{2U|2UL|2ULL}}); // CHECK-NEXT: break; // CHECK-NEXT: } // CHECK-NEXT: { // CHECK-NEXT: default: // CHECK-NEXT: res += i + j; -// CHECK-NEXT: _t6 = res; +// CHECK-NEXT: _t5 = res; // CHECK-NEXT: } -// CHECK-NEXT: clad::push(_t3, {{3U|3UL|3ULL}}); // CHECK-NEXT: } // CHECK-NEXT: } // CHECK-NEXT: _d_res += 1; // CHECK-NEXT: { -// CHECK-NEXT: switch (clad::pop(_t3)) { -// CHECK-NEXT: case {{3U|3UL|3ULL}}: +// CHECK-NEXT: switch (_cond0) { +// CHECK-NEXT: default: // CHECK-NEXT: ; // CHECK-NEXT: { // CHECK-NEXT: { -// CHECK-NEXT: res = _t6; +// CHECK-NEXT: res = _t5; // CHECK-NEXT: *_d_i += _d_res; // CHECK-NEXT: *_d_j += _d_res; // CHECK-NEXT: } // CHECK-NEXT: if (_cond0 != 0 && _cond0 != 1 && _cond0 != 2) // CHECK-NEXT: break; // CHECK-NEXT: } -// CHECK-NEXT: case {{2U|2UL|2ULL}}: +// CHECK-NEXT: case 2: +// CHECK-NEXT: case 1: // CHECK-NEXT: ; // CHECK-NEXT: { // CHECK-NEXT: { -// CHECK-NEXT: res = _t5; +// CHECK-NEXT: res = _t4; // CHECK-NEXT: *_d_i += _d_res * j; // CHECK-NEXT: *_d_j += i * _d_res; // CHECK-NEXT: } @@ -206,13 +202,13 @@ double fn2(double i, double j) { // CHECK-NEXT: } // CHECK-NEXT: { // CHECK-NEXT: { -// CHECK-NEXT: res = _t4; +// CHECK-NEXT: res = _t3; // CHECK-NEXT: *_d_j += _d_res; // CHECK-NEXT: } // CHECK-NEXT: if (1 == _cond0) // CHECK-NEXT: break; // CHECK-NEXT: } -// CHECK-NEXT: case {{1U|1UL|1ULL}}: +// CHECK-NEXT: case 0: // CHECK-NEXT: ; // CHECK-NEXT: { // CHECK-NEXT: { @@ -257,9 +253,8 @@ double fn3(double i, double j) { // CHECK-NEXT: clad::tape _cond0 = {}; // CHECK-NEXT: clad::tape _t1 = {}; // CHECK-NEXT: clad::tape _t2 = {}; -// CHECK-NEXT: clad::tape _t3 = {}; +// CHECK-NEXT: clad::tape _t3 = {}; // CHECK-NEXT: clad::tape _t4 = {}; -// CHECK-NEXT: clad::tape _t5 = {}; // CHECK-NEXT: double _d_res = 0.; // CHECK-NEXT: double res = 0; // CHECK-NEXT: int _d_counter = 0; @@ -284,20 +279,18 @@ double fn3(double i, double j) { // CHECK-NEXT: } // CHECK-NEXT: } // CHECK-NEXT: { -// CHECK-NEXT: clad::push(_t3, {{1U|1UL|1ULL}}); // CHECK-NEXT: break; // CHECK-NEXT: } // CHECK-NEXT: { // CHECK-NEXT: case 2: // CHECK-NEXT: res += j * j; -// CHECK-NEXT: clad::push(_t4, res); +// CHECK-NEXT: clad::push(_t3, res); // CHECK-NEXT: } // CHECK-NEXT: { // CHECK-NEXT: default: // CHECK-NEXT: res += i + j; -// CHECK-NEXT: clad::push(_t5, res); +// CHECK-NEXT: clad::push(_t4, res); // CHECK-NEXT: } -// CHECK-NEXT: clad::push(_t3, {{2U|2UL|2ULL}}); // CHECK-NEXT: } // CHECK-NEXT: } // CHECK-NEXT: } @@ -306,12 +299,13 @@ double fn3(double i, double j) { // CHECK-NEXT: { // CHECK-NEXT: { // CHECK-NEXT: { -// CHECK-NEXT: switch (clad::pop(_t3)) { -// CHECK-NEXT: case {{2U|2UL|2ULL}}: +// CHECK-NEXT: switch (clad::back(_cond0)) { +// CHECK-NEXT: default: +// CHECK-NEXT: case 2: // CHECK-NEXT: ; // CHECK-NEXT: { // CHECK-NEXT: { -// CHECK-NEXT: res = clad::pop(_t5); +// CHECK-NEXT: res = clad::pop(_t4); // CHECK-NEXT: *_d_i += _d_res; // CHECK-NEXT: *_d_j += _d_res; // CHECK-NEXT: } @@ -320,14 +314,15 @@ double fn3(double i, double j) { // CHECK-NEXT: } // CHECK-NEXT: { // CHECK-NEXT: { -// CHECK-NEXT: res = clad::pop(_t4); +// CHECK-NEXT: res = clad::pop(_t3); // CHECK-NEXT: *_d_j += _d_res * j; // CHECK-NEXT: *_d_j += j * _d_res; // CHECK-NEXT: } // CHECK-NEXT: if (2 == clad::back(_cond0)) // CHECK-NEXT: break; // CHECK-NEXT: } -// CHECK-NEXT: case {{1U|1UL|1ULL}}: +// CHECK-NEXT: case 1: +// CHECK-NEXT: case 0: // CHECK-NEXT: ; // CHECK-NEXT: { // CHECK-NEXT: { @@ -375,11 +370,10 @@ double fn4(double i, double j) { // CHECK: void fn4_grad(double i, double j, double *_d_i, double *_d_j) { // CHECK-NEXT: double _t0; -// CHECK-NEXT: clad::tape _t1 = {}; // CHECK-NEXT: int _d_counter = 0; // CHECK-NEXT: int counter = 0; -// CHECK-NEXT: unsigned {{int|long|long long}} _t2; -// CHECK-NEXT: clad::tape _t3 = {}; +// CHECK-NEXT: unsigned {{int|long|long long}} _t1; +// CHECK-NEXT: clad::tape _t2 = {}; // CHECK-NEXT: double _d_res = 0.; // CHECK-NEXT: double res = 0; // CHECK-NEXT: { @@ -390,50 +384,45 @@ double fn4(double i, double j) { // CHECK-NEXT: _t0 = res; // CHECK-NEXT: } // CHECK-NEXT: { -// CHECK-NEXT: clad::push(_t1, {{1U|1UL|1ULL}}); // CHECK-NEXT: break; // CHECK-NEXT: } // CHECK-NEXT: { // CHECK-NEXT: case 1: // CHECK-NEXT: counter = 2; // CHECK-NEXT: } -// CHECK-NEXT: _t2 = 0; +// CHECK-NEXT: _t1 = 0; // CHECK-NEXT: while (counter--) // CHECK-NEXT: { -// CHECK-NEXT: _t2++; -// CHECK-NEXT: clad::push(_t3, res); +// CHECK-NEXT: _t1++; +// CHECK-NEXT: clad::push(_t2, res); // CHECK-NEXT: res += i * j; // CHECK-NEXT: } // CHECK-NEXT: { -// CHECK-NEXT: clad::push(_t1, {{2U|2UL|2ULL}}); // CHECK-NEXT: break; // CHECK-NEXT: } -// CHECK-NEXT: clad::push(_t1, {{3U|3UL|3ULL}}); // CHECK-NEXT: } // CHECK-NEXT: } // CHECK-NEXT: _d_res += 1; // CHECK-NEXT: { -// CHECK-NEXT: switch (clad::pop(_t1)) { -// CHECK-NEXT: case {{3U|3UL|3ULL}}: -// CHECK-NEXT: ; -// CHECK-NEXT: case {{2U|2UL|2ULL}}: +// CHECK-NEXT: switch (1) { +// CHECK-NEXT: case 1: // CHECK-NEXT: ; -// CHECK-NEXT: while (_t2) +// CHECK-NEXT: while (_t1) // CHECK-NEXT: { // CHECK-NEXT: { // CHECK-NEXT: { -// CHECK-NEXT: res = clad::pop(_t3); +// CHECK-NEXT: res = clad::pop(_t2); // CHECK-NEXT: *_d_i += _d_res * j; // CHECK-NEXT: *_d_j += i * _d_res; // CHECK-NEXT: } // CHECK-NEXT: } -// CHECK-NEXT: _t2--; +// CHECK-NEXT: _t1--; // CHECK-NEXT: } // CHECK-NEXT: { // CHECK-NEXT: if (1 == 1) // CHECK-NEXT: break; // CHECK-NEXT: } -// CHECK-NEXT: case {{1U|1UL|1ULL}}: +// CHECK-NEXT: case 0: // CHECK-NEXT: ; // CHECK-NEXT: { // CHECK-NEXT: { @@ -463,7 +452,6 @@ double fn5(double i, double j) { // CHECK-NEXT: int count = 0; // CHECK-NEXT: int _cond0; // CHECK-NEXT: double _t0; -// CHECK-NEXT: clad::tape _t1 = {}; // CHECK-NEXT: double _d_res = 0.; // CHECK-NEXT: double res = 0; // CHECK-NEXT: { @@ -473,13 +461,12 @@ double fn5(double i, double j) { // CHECK-NEXT: case 1: // CHECK-NEXT: res += i * j; // CHECK-NEXT: _t0 = res; -// CHECK-NEXT: clad::push(_t1, {{1U|1UL|1ULL}}); // CHECK-NEXT: } // CHECK-NEXT: } // CHECK-NEXT: _d_res += 1; // CHECK-NEXT: { -// CHECK-NEXT: switch (clad::pop(_t1)) { -// CHECK-NEXT: case {{1U|1UL|1ULL}}: +// CHECK-NEXT: switch (_cond0) { +// CHECK-NEXT: case 1: // CHECK-NEXT: ; // CHECK-NEXT: { // CHECK-NEXT: res = _t0; @@ -506,7 +493,6 @@ double fn6(double u, double v) { // CHECK-NEXT: int _t0; // CHECK-NEXT: int _cond0; // CHECK-NEXT: double _t1; -// CHECK-NEXT: clad::tape _t2 = {}; // CHECK-NEXT: int _d_res = 0; // CHECK-NEXT: int res = 0; // CHECK-NEXT: double _d_temp = 0.; @@ -521,13 +507,12 @@ double fn6(double u, double v) { // CHECK-NEXT: temp = 1; // CHECK-NEXT: _t1 = temp; // CHECK-NEXT: } -// CHECK-NEXT: clad::push(_t2, {{1U|1UL|1ULL}}); // CHECK-NEXT: } // CHECK-NEXT: } // CHECK-NEXT: _d_res += 1; // CHECK-NEXT: { -// CHECK-NEXT: switch (clad::pop(_t2)) { -// CHECK-NEXT: case {{1U|1UL|1ULL}}: +// CHECK-NEXT: switch (_cond0) { +// CHECK-NEXT: default: // CHECK-NEXT: ; // CHECK-NEXT: { // CHECK-NEXT: { @@ -570,15 +555,14 @@ double fn7(double u, double v) { // CHECK-NEXT: int i = 0; // CHECK-NEXT: clad::tape _cond0 = {}; // CHECK-NEXT: clad::tape _t1 = {}; -// CHECK-NEXT: clad::tape _t2 = {}; -// CHECK-NEXT: clad::tape _t3 = {}; +// CHECK-NEXT: clad::tape _t2 = {}; // CHECK-NEXT: double _d_res = 0.; // CHECK-NEXT: double res = 0; // CHECK-NEXT: unsigned {{int|long|long long}} _t0 = 0; // CHECK-NEXT: for (i = 0; i < 5; ++i) { // CHECK-NEXT: _t0++; // CHECK-NEXT: { -// CHECK-NEXT: clad::push(_cond0, i) +// CHECK-NEXT: clad::push(_cond0, i); // CHECK-NEXT: switch (clad::back(_cond0)) { // CHECK-NEXT: { // CHECK-NEXT: case 0: @@ -592,7 +576,6 @@ double fn7(double u, double v) { // CHECK-NEXT: } // CHECK-NEXT: } // CHECK-NEXT: { -// CHECK-NEXT: clad::push(_t2, {{1U|1UL|1ULL}}); // CHECK-NEXT: break; // CHECK-NEXT: } // CHECK-NEXT: { @@ -600,14 +583,12 @@ double fn7(double u, double v) { // CHECK-NEXT: { // CHECK-NEXT: default: // CHECK-NEXT: res += v; -// CHECK-NEXT: clad::push(_t3, res); +// CHECK-NEXT: clad::push(_t2, res); // CHECK-NEXT: } // CHECK-NEXT: } // CHECK-NEXT: { -// CHECK-NEXT: clad::push(_t2, {{2U|2UL|2ULL}}); // CHECK-NEXT: break; // CHECK-NEXT: } -// CHECK-NEXT: clad::push(_t2, {{3U|3UL|3ULL}}); // CHECK-NEXT: } // CHECK-NEXT: } // CHECK-NEXT: } @@ -615,15 +596,14 @@ double fn7(double u, double v) { // CHECK-NEXT: for (; _t0; _t0--) { // CHECK-NEXT: --i; // CHECK-NEXT: { -// CHECK-NEXT: switch (clad::pop(_t2)) { -// CHECK-NEXT: case {{3U|3UL|3ULL}}: -// CHECK-NEXT: ; -// CHECK-NEXT: case {{2U|2UL|2ULL}}: +// CHECK-NEXT: switch (clad::back(_cond0)) { +// CHECK-NEXT: default: +// CHECK-NEXT: case 3: // CHECK-NEXT: ; // CHECK-NEXT: { // CHECK-NEXT: { // CHECK-NEXT: { -// CHECK-NEXT: res = clad::pop(_t3); +// CHECK-NEXT: res = clad::pop(_t2); // CHECK-NEXT: *_d_v += _d_res; // CHECK-NEXT: } // CHECK-NEXT: if (clad::back(_cond0) != 0 && clad::back(_cond0) != 1 && clad::back(_cond0) != 2 && clad::back(_cond0) != 3) @@ -632,7 +612,9 @@ double fn7(double u, double v) { // CHECK-NEXT: if (3 == clad::back(_cond0)) // CHECK-NEXT: break; // CHECK-NEXT: } -// CHECK-NEXT: case {{1U|1UL|1ULL}}: +// CHECK-NEXT: case 2: +// CHECK-NEXT: case 1: +// CHECK-NEXT: case 0: // CHECK-NEXT: ; // CHECK-NEXT: { // CHECK-NEXT: { @@ -683,115 +665,174 @@ double fn24(double x, double y, Op op) { } // CHECK: void fn24_grad_0_1(double x, double y, Op op, double *_d_x, double *_d_y) { -// CHECK-NEXT: {{.*}} _d_op = {{.*}}; -// CHECK-NEXT: Op _cond0; -// CHECK-NEXT: double _t0; -// CHECK-NEXT: clad::tape _t1 = {}; -// CHECK-NEXT: double _t2; -// CHECK-NEXT: double _t3; -// CHECK-NEXT: double _t4; -// CHECK-NEXT: double _d_res = 0.; -// CHECK-NEXT: double res = 0; -// CHECK-NEXT: { -// CHECK-NEXT: _cond0 = op; -// CHECK-NEXT: switch (_cond0) { -// CHECK-NEXT: { -// CHECK-NEXT: case Add: -// CHECK-NEXT: res = x + y; -// CHECK-NEXT: _t0 = res; -// CHECK-NEXT: } -// CHECK-NEXT: { -// CHECK-NEXT: clad::push(_t1, {{1U|1UL}}); -// CHECK-NEXT: break; -// CHECK-NEXT: } -// CHECK-NEXT: { -// CHECK-NEXT: case Sub: -// CHECK-NEXT: res = x - y; -// CHECK-NEXT: _t2 = res; -// CHECK-NEXT: } -// CHECK-NEXT: { -// CHECK-NEXT: clad::push(_t1, {{2U|2UL}}); -// CHECK-NEXT: break; -// CHECK-NEXT: } -// CHECK-NEXT: { -// CHECK-NEXT: case Mul: -// CHECK-NEXT: res = x * y; -// CHECK-NEXT: _t3 = res; -// CHECK-NEXT: } -// CHECK-NEXT: { -// CHECK-NEXT: clad::push(_t1, {{3U|3UL}}); -// CHECK-NEXT: break; -// CHECK-NEXT: } -// CHECK-NEXT: { -// CHECK-NEXT: case Div: -// CHECK-NEXT: res = x / y; -// CHECK-NEXT: _t4 = res; -// CHECK-NEXT: } -// CHECK-NEXT: { -// CHECK-NEXT: clad::push(_t1, {{4U|4UL}}); -// CHECK-NEXT: break; -// CHECK-NEXT: } -// CHECK-NEXT: clad::push(_t1, {{5U|5UL}}); -// CHECK-NEXT: } -// CHECK-NEXT: } -// CHECK-NEXT: _d_res += 1; -// CHECK-NEXT: { -// CHECK-NEXT: switch (clad::pop(_t1)) { -// CHECK-NEXT: case {{5U|5UL}}: -// CHECK-NEXT: ; -// CHECK-NEXT: case {{4U|4UL}}: -// CHECK-NEXT: ; -// CHECK-NEXT: { -// CHECK-NEXT: { -// CHECK-NEXT: res = _t4; -// CHECK-NEXT: *_d_x += _d_res / y; -// CHECK-NEXT: double _r0 = _d_res * -(x / (y * y)); -// CHECK-NEXT: _d_y += _r0; -// CHECK-NEXT: _d_res = 0.; -// CHECK-NEXT: } -// CHECK-NEXT: if (Div == _cond0) -// CHECK-NEXT: break; -// CHECK-NEXT: } -// CHECK-NEXT: case {{3U|3UL}}: -// CHECK-NEXT: ; -// CHECK-NEXT: { -// CHECK-NEXT: { -// CHECK-NEXT: res = _t3; -// CHECK-NEXT: *_d_x += _d_res * y; -// CHECK-NEXT: _d_y += x * _d_res; -// CHECK-NEXT: _d_res = 0.; -// CHECK-NEXT: } -// CHECK-NEXT: if (Mul == _cond0) -// CHECK-NEXT: break; -// CHECK-NEXT: } -// CHECK-NEXT: case {{2U|2UL}}: -// CHECK-NEXT: ; -// CHECK-NEXT: { -// CHECK-NEXT: { -// CHECK-NEXT: res = _t2; -// CHECK-NEXT: *_d_x += _d_res; -// CHECK-NEXT: _d_y += -_d_res; -// CHECK-NEXT: _d_res = 0.; -// CHECK-NEXT: } -// CHECK-NEXT: if (Sub == _cond0) -// CHECK-NEXT: break; -// CHECK-NEXT: } -// CHECK-NEXT: case {{1U|1UL}}: -// CHECK-NEXT: ; -// CHECK-NEXT: { -// CHECK-NEXT: { -// CHECK-NEXT: res = _t0; -// CHECK-NEXT: *_d_x += _d_res; -// CHECK-NEXT: _d_y += _d_res; -// CHECK-NEXT: _d_res = 0.; -// CHECK-NEXT: } -// CHECK-NEXT: if (Add == _cond0) -// CHECK-NEXT: break; -// CHECK-NEXT: } -// CHECK-NEXT: } -// CHECK-NEXT: } -// CHECK-NEXT:} +// CHECK-NEXT: {{.*}} _d_op = {{.*}}; +// CHECK-NEXT: Op _cond0; +// CHECK-NEXT: double _t0; +// CHECK-NEXT: double _t1; +// CHECK-NEXT: double _t2; +// CHECK-NEXT: double _t3; +// CHECK-NEXT: double _d_res = 0.; +// CHECK-NEXT: double res = 0; +// CHECK-NEXT: { +// CHECK-NEXT: _cond0 = op; +// CHECK-NEXT: switch (_cond0) { +// CHECK-NEXT: { +// CHECK-NEXT: case Add: +// CHECK-NEXT: res = x + y; +// CHECK-NEXT: _t0 = res; +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: break; +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: case Sub: +// CHECK-NEXT: res = x - y; +// CHECK-NEXT: _t1 = res; +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: break; +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: case Mul: +// CHECK-NEXT: res = x * y; +// CHECK-NEXT: _t2 = res; +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: break; +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: case Div: +// CHECK-NEXT: res = x / y; +// CHECK-NEXT: _t3 = res; +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: break; +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: _d_res += 1; +// CHECK-NEXT: { +// CHECK-NEXT: switch (_cond0) { +// CHECK-NEXT: case Div: +// CHECK-NEXT: ; +// CHECK-NEXT: { +// CHECK-NEXT: { +// CHECK-NEXT: res = _t3; +// CHECK-NEXT: *_d_x += _d_res / y; +// CHECK-NEXT: double _r0 = _d_res * -(x / (y * y)); +// CHECK-NEXT: *_d_y += _r0; +// CHECK-NEXT: _d_res = 0.; +// CHECK-NEXT: } +// CHECK-NEXT: if (Div == _cond0) +// CHECK-NEXT: break; +// CHECK-NEXT: } +// CHECK-NEXT: case Mul: +// CHECK-NEXT: ; +// CHECK-NEXT: { +// CHECK-NEXT: { +// CHECK-NEXT: res = _t2; +// CHECK-NEXT: *_d_x += _d_res * y; +// CHECK-NEXT: *_d_y += x * _d_res; +// CHECK-NEXT: _d_res = 0.; +// CHECK-NEXT: } +// CHECK-NEXT: if (Mul == _cond0) +// CHECK-NEXT: break; +// CHECK-NEXT: } +// CHECK-NEXT: case Sub: +// CHECK-NEXT: ; +// CHECK-NEXT: { +// CHECK-NEXT: { +// CHECK-NEXT: res = _t1; +// CHECK-NEXT: *_d_x += _d_res; +// CHECK-NEXT: *_d_y += -_d_res; +// CHECK-NEXT: _d_res = 0.; +// CHECK-NEXT: } +// CHECK-NEXT: if (Sub == _cond0) +// CHECK-NEXT: break; +// CHECK-NEXT: } +// CHECK-NEXT: case Add: +// CHECK-NEXT: ; +// CHECK-NEXT: { +// CHECK-NEXT: { +// CHECK-NEXT: res = _t0; +// CHECK-NEXT: *_d_x += _d_res; +// CHECK-NEXT: *_d_y += _d_res; +// CHECK-NEXT: _d_res = 0.; +// CHECK-NEXT: } +// CHECK-NEXT: if (Add == _cond0) +// CHECK-NEXT: break; +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } + +// Each case returns rather than breaking; the reverse sweep still enters only +// the taken case through the stored condition. +double switchReturn(double x, double y, int k) { + switch (k) { + case 0: return x * x; + case 1: return x * y; + default: return y * y; + } +} + +// CHECK: void switchReturn_grad_0_1(double x, double y, int k, double *_d_x, double *_d_y) { +// CHECK-NEXT: int _d_k = 0; +// CHECK-NEXT: int _cond0; +// CHECK-NEXT: { +// CHECK-NEXT: _cond0 = k; +// CHECK-NEXT: switch (_cond0) { +// CHECK-NEXT: { +// CHECK-NEXT: case 0: +// CHECK-NEXT: goto _label0; +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: case 1: +// CHECK-NEXT: goto _label1; +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: default: +// CHECK-NEXT: goto _label2; +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: switch (_cond0) { +// CHECK-NEXT: default: +// CHECK-NEXT: case 1: +// CHECK-NEXT: case 0: +// CHECK-NEXT: ; +// CHECK-NEXT: { +// CHECK-NEXT: _label2: +// CHECK-NEXT: { +// CHECK-NEXT: *_d_y += 1 * y; +// CHECK-NEXT: *_d_y += y * 1; +// CHECK-NEXT: } +// CHECK-NEXT: if (_cond0 != 0 && _cond0 != 1) +// CHECK-NEXT: break; +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: _label1: +// CHECK-NEXT: { +// CHECK-NEXT: *_d_x += 1 * y; +// CHECK-NEXT: *_d_y += x * 1; +// CHECK-NEXT: } +// CHECK-NEXT: if (1 == _cond0) +// CHECK-NEXT: break; +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: _label0: +// CHECK-NEXT: { +// CHECK-NEXT: *_d_x += 1 * x; +// CHECK-NEXT: *_d_x += x * 1; +// CHECK-NEXT: } +// CHECK-NEXT: if (0 == _cond0) +// CHECK-NEXT: break; +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: } #define TEST_2(F, x, y) \ { \ @@ -828,4 +869,8 @@ int main() { TEST_2_Op(fn24, 3, 5, Sub); // CHECK-EXEC: {1.00, -1.00} TEST_2_Op(fn24, 3, 5, Mul); // CHECK-EXEC: {5.00, 3.00} TEST_2_Op(fn24, 3, 5, Div); // CHECK-EXEC: {0.20, -0.12} + + TEST_2_Op(switchReturn, 3, 5, 0); // CHECK-EXEC: {6.00, 0.00} + TEST_2_Op(switchReturn, 3, 5, 1); // CHECK-EXEC: {5.00, 3.00} + TEST_2_Op(switchReturn, 3, 5, 2); // CHECK-EXEC: {0.00, 10.00} } diff --git a/test/Gradient/SwitchInit.C b/test/Gradient/SwitchInit.C index 5eb77dc6f..a73fcc80d 100644 --- a/test/Gradient/SwitchInit.C +++ b/test/Gradient/SwitchInit.C @@ -1,6 +1,5 @@ // RUN: %cladclang -Xclang -plugin-arg-clad -Xclang -disable-tbr %s -I%S/../../include -oSwitchInit.out 2>&1 | %filecheck %s // RUN: ./SwitchInit.out | %filecheck_exec %s -// XFAIL: valgrind #include "clad/Differentiator/Differentiator.h" @@ -21,10 +20,9 @@ double fn1(double i, double j) { // CHECK-NEXT: int count = 0; // CHECK-NEXT: int _cond0; // CHECK-NEXT: double _t0; -// CHECK-NEXT: clad::tape _t1 = {}; +// CHECK-NEXT: double _t1; // CHECK-NEXT: double _t2; // CHECK-NEXT: double _t3; -// CHECK-NEXT: double _t4; // CHECK-NEXT: double _d_res = 0.; // CHECK-NEXT: double res = 0; // CHECK-NEXT: { @@ -37,37 +35,37 @@ double fn1(double i, double j) { // CHECK-NEXT: _t0 = res; // CHECK-NEXT: } // CHECK-NEXT: { -// CHECK-NEXT: clad::push(_t1, {{1U|1UL|1ULL}}); // CHECK-NEXT: break; // CHECK-NEXT: } // CHECK-NEXT: { // CHECK-NEXT: case 1: // CHECK-NEXT: res += i * i; -// CHECK-NEXT: _t2 = res; +// CHECK-NEXT: _t1 = res; // CHECK-NEXT: } // CHECK-NEXT: { // CHECK-NEXT: { // CHECK-NEXT: case 2: // CHECK-NEXT: res += j * j; -// CHECK-NEXT: _t3 = res; +// CHECK-NEXT: _t2 = res; // CHECK-NEXT: } // CHECK-NEXT: } // CHECK-NEXT: { // CHECK-NEXT: default: // CHECK-NEXT: res += i * i * j * j; -// CHECK-NEXT: _t4 = res; +// CHECK-NEXT: _t3 = res; // CHECK-NEXT: } -// CHECK-NEXT: clad::push(_t1, {{2U|2UL|2ULL}}); // CHECK-NEXT: } // CHECK-NEXT: } // CHECK-NEXT: _d_res += 1; // CHECK-NEXT: { -// CHECK-NEXT: switch (clad::pop(_t1)) { -// CHECK-NEXT: case {{2U|2UL|2ULL}}: +// CHECK-NEXT: switch (_cond0) { +// CHECK-NEXT: default: +// CHECK-NEXT: case 2: +// CHECK-NEXT: case 1: // CHECK-NEXT: ; // CHECK-NEXT: { // CHECK-NEXT: { -// CHECK-NEXT: res = _t4; +// CHECK-NEXT: res = _t3; // CHECK-NEXT: *_d_i += _d_res * j * j * i; // CHECK-NEXT: *_d_i += i * _d_res * j * j; // CHECK-NEXT: *_d_j += i * i * _d_res * j; @@ -79,7 +77,7 @@ double fn1(double i, double j) { // CHECK-NEXT: { // CHECK-NEXT: { // CHECK-NEXT: { -// CHECK-NEXT: res = _t3; +// CHECK-NEXT: res = _t2; // CHECK-NEXT: *_d_j += _d_res * j; // CHECK-NEXT: *_d_j += j * _d_res; // CHECK-NEXT: } @@ -89,14 +87,14 @@ double fn1(double i, double j) { // CHECK-NEXT: } // CHECK-NEXT: { // CHECK-NEXT: { -// CHECK-NEXT: res = _t2; +// CHECK-NEXT: res = _t1; // CHECK-NEXT: *_d_i += _d_res * i; // CHECK-NEXT: *_d_i += i * _d_res; // CHECK-NEXT: } // CHECK-NEXT: if (1 == _cond0) // CHECK-NEXT: break; // CHECK-NEXT: } -// CHECK-NEXT: case {{1U|1UL|1ULL}}: +// CHECK-NEXT: case 0: // CHECK-NEXT: ; // CHECK-NEXT: { // CHECK-NEXT: {