Skip to content

Commit 2b39393

Browse files
PetroZarytskyivgvassilev
authored andcommitted
Sink FPErrorEstimationModel in ErrorEstimationHandler
``FPErrorEstimationModel`` used to be the base class for user-provided error estimation models. However, since we removed custom models in favor of custom error estimation functions, there is no point in having it as a separate entity from ``ErrorEstimationHandler``.
1 parent ccf72d6 commit 2b39393

8 files changed

Lines changed: 129 additions & 212 deletions

File tree

include/clad/Differentiator/DerivativeBuilder.h

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "clad/Differentiator/DerivedFnCollector.h"
1414
#include "clad/Differentiator/DiffPlanner.h"
1515

16+
#include "clang/AST/Decl.h"
1617
#include "clang/AST/RecursiveASTVisitor.h"
1718
#include "clang/AST/StmtVisitor.h"
1819
#include "clang/Basic/Diagnostic.h"
@@ -23,6 +24,7 @@
2324
#include <memory>
2425
#include <stack>
2526
#include <unordered_map>
27+
#include <utility>
2628

2729
namespace clang {
2830
class ASTContext;
@@ -49,24 +51,23 @@ namespace clad {
4951
} // namespace clad
5052

5153
namespace clad {
52-
class ErrorEstimationHandler;
53-
class FPErrorEstimationModel;
54-
55-
/// A pair of FunctionDecl and potential enclosing context, e.g. a function
56-
/// in nested namespaces.
57-
// This is the type returned by cloneFunction. Using OverloadedDeclWithContext
58-
// instead would lead to unnecessarily returning a nullptr in the overloaded
59-
// FD
60-
using DeclWithContext = std::pair<clang::FunctionDecl*, clang::Decl*>;
61-
/// Stores derivative and the corresponding overload. If no overload exist
62-
/// then `second` data member should be `nullptr`.
63-
struct DerivativeAndOverload {
64-
clang::Decl* derivative = nullptr;
65-
clang::FunctionDecl* overload = nullptr;
66-
DerivativeAndOverload(clang::Decl* p_derivative = nullptr,
67-
clang::FunctionDecl* p_overload = nullptr)
68-
: derivative(p_derivative), overload(p_overload) {}
69-
};
54+
class ErrorEstimationHandler;
55+
56+
/// A pair of FunctionDecl and potential enclosing context, e.g. a function
57+
/// in nested namespaces.
58+
// This is the type returned by cloneFunction. Using OverloadedDeclWithContext
59+
// instead would lead to unnecessarily returning a nullptr in the overloaded
60+
// FD
61+
using DeclWithContext = std::pair<clang::FunctionDecl*, clang::Decl*>;
62+
/// Stores derivative and the corresponding overload. If no overload exist
63+
/// then `second` data member should be `nullptr`.
64+
struct DerivativeAndOverload {
65+
clang::Decl* derivative = nullptr;
66+
clang::FunctionDecl* overload = nullptr;
67+
DerivativeAndOverload(clang::Decl* p_derivative = nullptr,
68+
clang::FunctionDecl* p_overload = nullptr)
69+
: derivative(p_derivative), overload(p_overload) {}
70+
};
7071

7172
static clang::SourceLocation noLoc{};
7273
class VisitorBase;

include/clad/Differentiator/ErrorEstimator.h

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
#ifndef CLAD_ERROR_ESTIMATOR_H
22
#define CLAD_ERROR_ESTIMATOR_H
33

4-
#include "EstimationModel.h"
54
#include "clad/Differentiator/ExternalRMVSource.h"
65
#include "clad/Differentiator/ReverseModeVisitorDirectionKinds.h"
76

87
#include "clang/AST/OperationKinds.h"
98

109
#include <stack>
10+
#include <string>
1111

1212
namespace clang {
1313
class Stmt;
@@ -29,37 +29,44 @@ class ErrorEstimationHandler : public ExternalRMVSource {
2929
// `Stmts` is originally defined in `VisitorBase`.
3030
using Stmts = llvm::SmallVector<clang::Stmt*, 16>;
3131
/// Reference to the return error expression.
32-
clang::Expr* m_RetErrorExpr;
33-
/// An instance of the custom error estimation model to be used.
34-
FPErrorEstimationModel* m_EstModel; // We do not own this.
32+
clang::Expr* m_RetErrorExpr = nullptr;
33+
/// An Expr representing a custom `getErrorVal` function, if any.
34+
clang::Expr* m_CustomErrorFunction = nullptr;
35+
void LookupCustomErrorFunction();
36+
/// The function to build the error expression of a
37+
/// specific estimation model. The error expression is returned in the form
38+
/// of a clang::Expr.
39+
/// \param[in] refExpr The reference of the expression to which the error
40+
/// has to be assigned, this is a StmtDiff type hence one can use getExpr()
41+
/// to get the unmodified expression and getExpr_dx() to get the absolute
42+
/// derivative of the same.
43+
/// \param [in] name Name of the variable being analysed.
44+
///
45+
/// \returns The error expression of the input value.
46+
// Return an expression of the following kind:
47+
// std::abs(dfdx * delta_x * Em)
48+
clang::Expr* AssignError(StmtDiff refExpr, const std::string& name);
3549
/// A set of assignments resulting for declaration statments.
3650
Stmts m_ForwardReplStmts;
3751
/// A vector to keep track of error statements for delayed emission.
3852
Stmts m_ReverseErrorStmts;
3953
/// The index expression for emitting final errors for input param errors.
40-
clang::Expr* m_IdxExpr;
54+
clang::Expr* m_IdxExpr = nullptr;
4155
/// A map from var decls to their size variables (e.g. `var_size`).
4256
std::unordered_map<const clang::VarDecl*, clang::Expr*> m_ArrSizes;
4357
// FIXME: Solve this in a more general way.
4458
/// A flag signaling if the current error comes from a function call.
4559
bool m_ErrorFromFunctionCall = false;
4660

4761
std::stack<bool> m_ShouldEmit;
48-
ReverseModeVisitor* m_RMV;
62+
ReverseModeVisitor* m_RMV = nullptr;
4963
llvm::SmallVectorImpl<clang::ParmVarDecl*>* m_Params = nullptr;
5064

5165
public:
5266
using direction = rmv::direction;
53-
ErrorEstimationHandler()
54-
: m_RetErrorExpr(nullptr), m_EstModel(nullptr), m_IdxExpr(nullptr) {}
67+
ErrorEstimationHandler() = default;
5568
~ErrorEstimationHandler() override = default;
5669

57-
/// Function to set the error estimation model currently in use.
58-
///
59-
/// \param[in] estModel The error estimation model, can be either
60-
/// an in-built one or one provided by the user.
61-
void SetErrorEstimationModel(FPErrorEstimationModel* estModel);
62-
6370
/// Builds a reference to the final error parameter of the function.
6471
clang::DeclRefExpr* BuildFinalErrorExpr();
6572

@@ -122,7 +129,7 @@ class ErrorEstimationHandler : public ExternalRMVSource {
122129
/// \returns The error in the variable 'var'.
123130
clang::Expr* GetError(clang::Expr* var, clang::Expr* varDiff,
124131
const std::string& varName) {
125-
return m_EstModel->AssignError({var, varDiff}, varName);
132+
return AssignError({var, varDiff}, varName);
126133
}
127134

128135
/// This function adds the final error and the other parameter errors to the

include/clad/Differentiator/EstimationModel.h

Lines changed: 0 additions & 59 deletions
This file was deleted.

lib/Differentiator/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ llvm_add_library(cladDifferentiator
5959
DerivedFnInfo.cpp
6060
DiffPlanner.cpp
6161
ErrorEstimator.cpp
62-
EstimationModel.cpp
6362
JacobianModeVisitor.cpp
6463
HessianModeVisitor.cpp
6564
MultiplexExternalRMVSource.cpp

lib/Differentiator/DerivativeBuilder.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -592,13 +592,9 @@ static void registerDerivative(Decl* D, Sema& S, const DiffRequest& R) {
592592
} else if (request.Mode == DiffMode::reverse ||
593593
request.Mode == DiffMode::pullback) {
594594
ErrorEstimationHandler handler;
595-
std::unique_ptr<FPErrorEstimationModel> model;
596595
ReverseModeVisitor V(*this, request);
597-
if (request.EnableErrorEstimation) {
598-
model = std::make_unique<FPErrorEstimationModel>(*this, request);
599-
handler.SetErrorEstimationModel(model.get());
596+
if (request.EnableErrorEstimation)
600597
V.AddExternalSource(handler);
601-
}
602598
result = V.Derive();
603599
} else if (request.Mode == DiffMode::reverse_mode_forward_pass) {
604600
ReverseModeForwPassVisitor V(*this, request);

lib/Differentiator/ErrorEstimator.cpp

Lines changed: 87 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,20 @@
66
#include "clad/Differentiator/ReverseModeVisitor.h"
77

88
#include "clang/AST/Decl.h"
9+
#include "clang/AST/DeclarationName.h"
910
#include "clang/AST/Expr.h"
1011
#include "clang/AST/OperationKinds.h"
12+
#include "clang/AST/Type.h"
1113
#include "clang/Basic/LLVM.h"
14+
#include "clang/Sema/DeclSpec.h"
15+
#include "clang/Sema/Lookup.h"
16+
#include "clang/Sema/TemplateDeduction.h"
1217

13-
#include <llvm/ADT/STLExtras.h>
14-
#include <llvm/ADT/SmallVector.h>
18+
#include "llvm/ADT/STLExtras.h"
19+
#include "llvm/ADT/SmallVector.h"
20+
21+
#include <limits>
22+
#include <string>
1523

1624
using namespace clang;
1725

@@ -26,11 +34,6 @@ QualType getUnderlyingArrayType(QualType baseType, ASTContext& C) {
2634
return baseType;
2735
}
2836

29-
void ErrorEstimationHandler::SetErrorEstimationModel(
30-
FPErrorEstimationModel* estModel) {
31-
m_EstModel = estModel;
32-
}
33-
3437
DeclRefExpr* ErrorEstimationHandler::BuildFinalErrorExpr() {
3538
return m_RMV->BuildDeclRef(m_Params->back());
3639
}
@@ -42,8 +45,7 @@ void ErrorEstimationHandler::BuildReturnErrorStmt() {
4245
auto flitr =
4346
FloatingLiteral::Create(m_RMV->m_Context, llvm::APFloat(1.0), true,
4447
m_RMV->m_Context.DoubleTy, noLoc);
45-
Expr* finExpr =
46-
m_EstModel->AssignError(StmtDiff(m_RetErrorExpr, flitr), "return_expr");
48+
Expr* finExpr = AssignError(StmtDiff(m_RetErrorExpr, flitr), "return_expr");
4749
m_RMV->addToCurrentBlock(
4850
m_RMV->BuildOp(BO_AddAssign, BuildFinalErrorExpr(), finExpr),
4951
direction::forward);
@@ -102,9 +104,9 @@ void ErrorEstimationHandler::EmitNestedFunctionParamError(
102104
// if (utils::IsReferenceOrPointerType(fnDecl->getParamDecl(i)->getType()))
103105
// continue;
104106
auto* derefExpr = m_RMV->BuildOp(UO_Deref, ArgResult[i]);
105-
Expr* errorExpr = m_EstModel->AssignError(
106-
{derivedCallArgs[i], derefExpr},
107-
fnDecl->getNameInfo().getAsString() + "_param_" + std::to_string(i));
107+
Expr* errorExpr = AssignError({derivedCallArgs[i], derefExpr},
108+
fnDecl->getNameInfo().getAsString() +
109+
"_param_" + std::to_string(i));
108110
Expr* FinalError = BuildFinalErrorExpr();
109111
Expr* errorStmt = m_RMV->BuildOp(BO_AddAssign, FinalError, errorExpr);
110112
m_ReverseErrorStmts.push_back(errorStmt);
@@ -265,6 +267,7 @@ void ErrorEstimationHandler::EmitDeclErrorStmts(DeclDiff<VarDecl> VDDiff,
265267

266268
void ErrorEstimationHandler::InitialiseRMV(ReverseModeVisitor& RMV) {
267269
m_RMV = &RMV;
270+
LookupCustomErrorFunction();
268271
}
269272

270273
void ErrorEstimationHandler::ForgetRMV() { m_RMV = nullptr; }
@@ -333,7 +336,7 @@ void ErrorEstimationHandler::ActAfterProcessingArraySubscriptExpr(
333336
m_RMV->m_Sema.ImpCastExprToType(idx, size->getType(), CK_IntegralCast)
334337
.get();
335338
llvm::SmallVector<clang::Expr*, 2> args{size, idx};
336-
Expr* extendedSize = m_EstModel->GetFunctionCall("max", "std", args);
339+
Expr* extendedSize = m_RMV->GetFunctionCall("max", "std", args);
337340
size = m_RMV->Clone(size);
338341
Stmt* updateSize = m_RMV->BuildOp(BO_Assign, size, extendedSize);
339342
m_RMV->addToCurrentBlock(updateSize, direction::reverse);
@@ -450,6 +453,77 @@ void ErrorEstimationHandler::ActBeforeDifferentiatingCallExpr(
450453
pullbackArgs.push_back(BuildFinalErrorExpr());
451454
}
452455

456+
void ErrorEstimationHandler::LookupCustomErrorFunction() {
457+
Sema& S = m_RMV->m_Sema;
458+
ASTContext& C = m_RMV->m_Context;
459+
NamespaceDecl* cladNS = utils::LookupNSD(S, "clad", /*shouldExist=*/true);
460+
IdentifierInfo* II = &C.Idents.get("getErrorVal");
461+
DeclarationNameInfo DNInfo(DeclarationName(II), utils::GetValidSLoc(S));
462+
LookupResult R(S, DNInfo, Sema::LookupOrdinaryName);
463+
S.LookupQualifiedName(R, cladNS);
464+
if (R.empty())
465+
return;
466+
467+
FunctionProtoType::ExtProtoInfo EPI;
468+
QualType ConstCharPtr = C.getPointerType(C.getConstType(C.CharTy));
469+
QualType DoubleTy = C.DoubleTy;
470+
llvm::SmallVector<QualType, 3> FnTypes = {DoubleTy, DoubleTy, ConstCharPtr};
471+
QualType FnTy = C.getFunctionType(DoubleTy, FnTypes, EPI);
472+
TemplateSpecCandidateSet FailedCandidates(utils::GetValidSLoc(S),
473+
/*ForTakingAddress=*/false);
474+
if (utils::MatchOverloadType(S, FnTy, R, FailedCandidates)) {
475+
// FIXME: MatchOverloadType returns an overload expr without the `clad::`
476+
// namespace specifier. Here, we rebuild manually.
477+
CXXScopeSpec SS;
478+
SS.Extend(C, cladNS, noLoc, noLoc);
479+
m_CustomErrorFunction =
480+
S.BuildDeclarationNameExpr(SS, R, /*ADL=*/false).get();
481+
return;
482+
}
483+
484+
// We did not match the found candidates. Warn and offer the user hints.
485+
auto errId = S.Diags.getCustomDiagID(
486+
DiagnosticsEngine::Error,
487+
"user-defined derivative error function was provided but not used; "
488+
"expected signature %0 does not match");
489+
S.Diag(m_RMV->m_DiffReq->getLocation(), errId) << FnTy;
490+
FailedCandidates.NoteCandidates(S, utils::GetValidSLoc(S));
491+
utils::DiagnoseSignatureMismatch(S, FnTy, R);
492+
}
493+
494+
Expr* ErrorEstimationHandler::AssignError(StmtDiff refExpr,
495+
const std::string& varName) {
496+
Sema& S = m_RMV->m_Sema;
497+
ASTContext& C = m_RMV->m_Context;
498+
if (m_CustomErrorFunction) {
499+
llvm::SmallVector<clang::Expr*, 3> callParams{
500+
refExpr.getExpr_dx(), refExpr.getExpr(),
501+
clad::utils::CreateStringLiteral(C, varName)};
502+
return S
503+
.ActOnCallExpr(m_RMV->getCurrentScope(), m_CustomErrorFunction, noLoc,
504+
callParams, noLoc)
505+
.get();
506+
}
507+
// Get the machine epsilon value.
508+
double val = std::numeric_limits<float>::epsilon();
509+
// Convert it into a floating point literal clang::Expr.
510+
Expr* epsExpr =
511+
FloatingLiteral::Create(C, llvm::APFloat(val), true, C.DoubleTy, noLoc);
512+
// Here, we first build a multiplication operation for the following:
513+
// refExpr * <--floating point literal (i.e. machine dependent constant)-->
514+
// Build another multiplication operation with above and the derivative
515+
Expr* errExpr =
516+
m_RMV->BuildOp(BO_Mul, refExpr.getExpr_dx(),
517+
m_RMV->BuildOp(BO_Mul, refExpr.getExpr(), epsExpr));
518+
// Next, build a llvm vector-like container to store the parameters
519+
// of the function call.
520+
llvm::SmallVector<Expr*, 1> params{errExpr};
521+
// Finally, build a call to std::abs
522+
Expr* absExpr = m_RMV->GetFunctionCall("abs", "std", params);
523+
// Return the built error expression.
524+
return absExpr;
525+
}
526+
453527
void ErrorEstimationHandler::ActBeforeFinalizingVisitDeclStmt(
454528
llvm::SmallVectorImpl<Decl*>& decls,
455529
llvm::SmallVectorImpl<Decl*>& declsDiff) {

0 commit comments

Comments
 (0)