1 // Copyright 2020 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "sandboxed_api/tools/clang_generator/generator.h"
16
17 #include <memory>
18 #include <optional>
19 #include <string>
20 #include <utility>
21 #include <vector>
22
23 #include "absl/container/flat_hash_set.h"
24 #include "absl/status/status.h"
25 #include "absl/strings/match.h"
26 #include "absl/strings/str_cat.h"
27 #include "absl/strings/string_view.h"
28 #include "absl/strings/strip.h"
29 #include "clang/AST/ASTContext.h"
30 #include "clang/AST/Decl.h"
31 #include "clang/AST/Type.h"
32 #include "clang/Basic/SourceLocation.h"
33 #include "clang/Basic/SourceManager.h"
34 #include "clang/Lex/PreprocessorOptions.h"
35 #include "sandboxed_api/tools/clang_generator/diagnostics.h"
36 #include "sandboxed_api/tools/clang_generator/emitter.h"
37
38 namespace sapi {
39 namespace {
40
41 // Replaces the file extension of a path name.
ReplaceFileExtension(absl::string_view path,absl::string_view new_extension)42 std::string ReplaceFileExtension(absl::string_view path,
43 absl::string_view new_extension) {
44 auto last_slash = path.rfind('/');
45 auto pos = path.rfind('.', last_slash);
46 if (pos != absl::string_view::npos && last_slash != absl::string_view::npos) {
47 pos += last_slash;
48 }
49 return absl::StrCat(path.substr(0, pos), new_extension);
50 }
51
52 } // namespace
53
GetOutputFilename(absl::string_view source_file)54 std::string GetOutputFilename(absl::string_view source_file) {
55 return ReplaceFileExtension(source_file, ".sapi.h");
56 }
57
VisitTypeDecl(clang::TypeDecl * decl)58 bool GeneratorASTVisitor::VisitTypeDecl(clang::TypeDecl* decl) {
59 collector_.RecordOrderedDecl(decl);
60 return true;
61 }
62
VisitFunctionDecl(clang::FunctionDecl * decl)63 bool GeneratorASTVisitor::VisitFunctionDecl(clang::FunctionDecl* decl) {
64 if (decl->isCXXClassMember() || // Skip classes
65 !decl->isExternC() || // Skip non external functions
66 decl->isTemplated() // Skip function templates
67 ) {
68 return true;
69 }
70
71 // Process either all function or just the requested ones
72 bool all_functions = options_.function_names.empty();
73 if (!all_functions &&
74 !options_.function_names.contains(ToStringView(decl->getName()))) {
75 return true;
76 }
77
78 // Skip Abseil internal functions when all functions are requested. This still
79 // allows them to be specified explicitly.
80 if (all_functions &&
81 absl::StartsWith(decl->getQualifiedNameAsString(), "AbslInternal")) {
82 return true;
83 }
84
85 clang::SourceManager& source_manager =
86 decl->getASTContext().getSourceManager();
87 clang::SourceLocation decl_start = decl->getBeginLoc();
88
89 // Skip functions from system headers when all functions are requested. Like
90 // above, they can still explicitly be specified.
91 if (all_functions && source_manager.isInSystemHeader(decl_start)) {
92 return true;
93 }
94
95 if (all_functions) {
96 const std::string filename(absl::StripPrefix(
97 ToStringView(source_manager.getFilename(decl_start)), "./"));
98 if (options_.limit_scan_depth && !options_.in_files.contains(filename)) {
99 return true;
100 }
101 }
102
103 functions_.push_back(decl);
104
105 collector_.CollectRelatedTypes(decl->getDeclaredReturnType());
106 for (const clang::ParmVarDecl* param : decl->parameters()) {
107 collector_.CollectRelatedTypes(param->getType());
108 }
109
110 return true;
111 }
112
HandleTranslationUnit(clang::ASTContext & context)113 void GeneratorASTConsumer::HandleTranslationUnit(clang::ASTContext& context) {
114 if (!visitor_.TraverseDecl(context.getTranslationUnitDecl())) {
115 ReportFatalError(context.getDiagnostics(),
116 context.getTranslationUnitDecl()->getBeginLoc(),
117 "AST traversal exited early");
118 return;
119 }
120
121 // TODO(cblichmann): Move below to emit all functions after traversing TUs.
122 emitter_.AddTypeDeclarations(visitor_.collector().GetTypeDeclarations());
123 for (clang::FunctionDecl* func : visitor_.functions()) {
124 absl::Status status = emitter_.AddFunction(func);
125 if (!status.ok()) {
126 clang::SourceLocation loc =
127 GetDiagnosticLocationFromStatus(status).value_or(func->getBeginLoc());
128 if (absl::IsCancelled(status)) {
129 ReportWarning(context.getDiagnostics(), loc, status.message());
130 continue;
131 }
132 ReportFatalError(context.getDiagnostics(), loc, status.message());
133 break;
134 }
135 }
136 }
137
runInvocation(std::shared_ptr<clang::CompilerInvocation> invocation,clang::FileManager * files,std::shared_ptr<clang::PCHContainerOperations> pch_container_ops,clang::DiagnosticConsumer * diag_consumer)138 bool GeneratorFactory::runInvocation(
139 std::shared_ptr<clang::CompilerInvocation> invocation,
140 clang::FileManager* files,
141 std::shared_ptr<clang::PCHContainerOperations> pch_container_ops,
142 clang::DiagnosticConsumer* diag_consumer) {
143 auto& options = invocation->getPreprocessorOpts();
144 // Explicitly ask to define __clang_analyzer__ macro.
145 options.SetUpStaticAnalyzer = true;
146 for (const auto& def : {
147 // Enable code to detect whether it is being SAPI-ized
148 "__SAPI__",
149 // TODO(b/222241644): Figure out how to deal with intrinsics properly
150 // Note: The definitions below just need to parse, they don't need to
151 // compile into useful code.
152 // Intel
153 "__builtin_ia32_cvtsbf162ss_32=[](auto)->long long{return 0;}",
154 "__builtin_ia32_paddsb128=",
155 "__builtin_ia32_paddsb256=",
156 "__builtin_ia32_paddsb512=",
157 "__builtin_ia32_paddsw128=",
158 "__builtin_ia32_paddsw256=",
159 "__builtin_ia32_paddsw512=",
160 "__builtin_ia32_paddusb128=",
161 "__builtin_ia32_paddusb256=",
162 "__builtin_ia32_paddusb512=",
163 "__builtin_ia32_paddusw128=",
164 "__builtin_ia32_paddusw256=",
165 "__builtin_ia32_paddusw512=",
166 "__builtin_ia32_psubsb128=",
167 "__builtin_ia32_psubsb256=",
168 "__builtin_ia32_psubsb512=",
169 "__builtin_ia32_psubsw128=",
170 "__builtin_ia32_psubsw256=",
171 "__builtin_ia32_psubsw512=",
172 "__builtin_ia32_psubusb128=",
173 "__builtin_ia32_psubusb256=",
174 "__builtin_ia32_psubusb512=",
175 "__builtin_ia32_psubusw128=",
176 "__builtin_ia32_psubusw256=",
177 "__builtin_ia32_psubusw512=",
178 "__builtin_ia32_reduce_add_d512=[](auto)->long long{return 0;}",
179 "__builtin_ia32_reduce_add_q512=[](auto)->long long{return 0;}",
180 "__builtin_ia32_reduce_mul_d512=[](auto)->long long{return 0;}",
181 "__builtin_ia32_reduce_mul_q512=[](auto)->long long{return 0;}",
182 }) {
183 options.addMacroDef(def);
184 // To avoid code to include header with compiler intrinsics, undefine a few
185 // key pre-defines.
186 for (
187 const auto& undef : {
188 // ARM ISA (see
189 // https://developer.arm.com/documentation/101028/0010/Feature-test-macros)
190 "__ARM_NEON",
191 "__ARM_NEON__",
192 // Intel
193 "__AVX__",
194 "__AVX2__",
195 "__AVX512BW__",
196 "__AVX512CD__",
197 "__AVX512DQ__",
198 "__AVX512F__",
199 "__AVX512VL__",
200 "__SSE__",
201 "__SSE2__",
202 "__SSE2_MATH__",
203 "__SSE3__",
204 "__SSE4_1__",
205 "__SSE4_2__",
206 "__SSE_MATH__",
207 "__SSSE3__",
208 }) {
209 options.addMacroUndef(undef);
210 }
211 }
212 return FrontendActionFactory::runInvocation(std::move(invocation), files,
213 std::move(pch_container_ops),
214 diag_consumer);
215 }
216
217 } // namespace sapi
218