clang 24.0.0git
IdiomRecognizer.cpp
Go to the documentation of this file.
1//===- IdiomRecognizer.cpp - recognizing and raising idioms to CIR --------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This pass is responsible for recognizing idioms (such as uses of functions
10// and types to the C/C++ standard library) and replacing them with Clang IR
11// operators for later optimization.
12//
13//===----------------------------------------------------------------------===//
14
15#include "PassDetail.h"
19#include "llvm/ADT/StringRef.h"
20
21#include <utility>
22
23using namespace mlir;
24using namespace cir;
25
26namespace mlir {
27#define GEN_PASS_DEF_IDIOMRECOGNIZER
28#include "clang/CIR/Dialect/Passes.h.inc"
29} // namespace mlir
30
31namespace {
32
33// True when the call's no builtin state forbids treating it as `name`. A
34// builtin mark wins over a nobuiltin mark or a nobuiltins list.
35bool isNoBuiltin(CallOp call, llvm::StringRef name) {
36 if (call->hasAttr(cir::CIRDialect::getBuiltinAttrName()))
37 return false;
38 if (call->hasAttr(cir::CIRDialect::getNoBuiltinAttrName()))
39 return true;
40 auto noBuiltins = call->getAttrOfType<mlir::ArrayAttr>(
41 cir::CIRDialect::getNoBuiltinsAttrName());
42 if (!noBuiltins)
43 return false;
44 return noBuiltins.empty() ||
45 llvm::any_of(noBuiltins, [name](mlir::Attribute entry) {
46 auto builtinName = mlir::dyn_cast<mlir::StringAttr>(entry);
47 return builtinName && builtinName.getValue() == name;
48 });
49}
50
51// Raises a direct cir.call to the first candidate in `TargetOps` that matches.
52template <typename... TargetOps> class StdRecognizer {
53 template <typename TargetOp, size_t... Indices>
54 static TargetOp buildCall(cir::CIRBaseBuilderTy &builder, CallOp call,
55 std::index_sequence<Indices...>) {
56 return TargetOp::create(builder, call.getLoc(),
57 call->getResult(0).getType(),
58 call.getOperand(Indices)..., call.getCalleeAttr());
59 }
60
61 template <typename TargetOp>
62 static bool raiseOne(CallOp call, mlir::MLIRContext &context,
63 mlir::SymbolTableCollection &symbolTables) {
64 // A musttail call must stay a call, so it is never raised.
65 if (!call.getCallee() || call.getMusttail() ||
66 !TargetOp::signatureMatches(call->getOperandTypes(),
67 call->getResultTypes()))
68 return false;
69
70 if constexpr (TargetOp::hasKnownFuncKind()) {
71 // Only a free std function with the right name carries the tag, so
72 // members, static members, and operators never match. The shape of the
73 // call is checked here, so a variadic callee never matches.
74 cir::FuncOp callee = call.resolveCalleeInTable(symbolTables);
75 if (!callee || callee.getFunctionType().isVarArg())
76 return false;
77 auto funcIdentity = mlir::dyn_cast_if_present<cir::FuncIdentityAttr>(
78 callee.getFuncInfoAttr());
79 if (!funcIdentity || funcIdentity.getKind() != TargetOp::getFuncKind())
80 return false;
81 } else {
82 // A C library function has no identity tag, so it is matched by callee
83 // symbol, which works because C names are unmangled. The symbol alone is
84 // not enough when builtins are disabled, so the recorded no builtin state
85 // gates the match.
86 if (*call.getCallee() != TargetOp::getFunctionName() ||
87 isNoBuiltin(call, TargetOp::getFunctionName()))
88 return false;
89 // The library function is not variadic, so a variadic callee that only
90 // shares the name is not that function. This lookup runs only after the
91 // name matches.
92 cir::FuncOp callee = call.resolveCalleeInTable(symbolTables);
93 if (callee && callee.getFunctionType().isVarArg())
94 return false;
95 }
96
97 cir::CIRBaseBuilderTy builder(context);
98 builder.setInsertionPointAfter(call.getOperation());
99 constexpr unsigned numArgs = TargetOp::getNumArgs();
100 TargetOp op =
101 buildCall<TargetOp>(builder, call, std::make_index_sequence<numArgs>());
102 // The raised operation keeps every call attribute except the callee,
103 // which it carries as original_fn, so lowering back loses nothing.
104 for (mlir::NamedAttribute attr : call->getAttrs())
105 if (attr.getName() != call.getCalleeAttrName())
106 op->setAttr(attr.getName(), attr.getValue());
107 call.replaceAllUsesWith(op);
108 call.erase();
109 return true;
110 }
111
112public:
113 // Tries each candidate in order and stops at the first that raises.
114 static bool raise(CallOp call, mlir::MLIRContext &context,
115 mlir::SymbolTableCollection &symbolTables) {
116 return (raiseOne<TargetOps>(call, context, symbolTables) || ...);
117 }
118};
119
120// The library calls the recognizer knows how to raise, tried in order.
121using RecognizedStdOps = StdRecognizer<StdFindOp, StrLenOp>;
122
123struct IdiomRecognizerPass
124 : public impl::IdiomRecognizerBase<IdiomRecognizerPass> {
125 IdiomRecognizerPass() = default;
126
127 void runOnOperation() override;
128
129 void recognizeStandardLibraryCall(CallOp call,
130 mlir::SymbolTableCollection &symbolTables);
131};
132} // namespace
133
134void IdiomRecognizerPass::recognizeStandardLibraryCall(
135 CallOp call, mlir::SymbolTableCollection &symbolTables) {
136 RecognizedStdOps::raise(call, getContext(), symbolTables);
137}
138
139void IdiomRecognizerPass::runOnOperation() {
140 // The facts this pass reads live on the operations, so it needs no AST
141 // and also works on parsed CIR assembly.
142 mlir::SymbolTableCollection symbolTables;
143
144 getOperation()->walk([&](CallOp callOp) {
145 // Skip indirect calls.
146 std::optional<llvm::StringRef> callee = callOp.getCallee();
147 if (!callee)
148 return;
149
150 recognizeStandardLibraryCall(callOp, symbolTables);
151 });
152}
153
154std::unique_ptr<Pass> mlir::createIdiomRecognizerPass() {
155 return std::make_unique<IdiomRecognizerPass>();
156}
const internal::VariadicAllOfMatcher< Attr > attr
RangeSelector name(std::string ID)
Given a node with a "name", (like NamedDecl, DeclRefExpr, CxxCtorInitializer, and TypeLoc) selects th...
std::unique_ptr< Pass > createIdiomRecognizerPass()