clang 24.0.0git
RecordTypeConverter.cpp
Go to the documentation of this file.
1//===- RecordTypeConverter.cpp - Record-rebuilding type converter ---------===//
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
10
11#include "llvm/ADT/STLExtras.h"
12#include "llvm/ADT/ScopeExit.h"
13#include "llvm/Support/Threading.h"
14
15#include <mutex>
16#include <shared_mutex>
17
18using namespace cir;
19
21 mlir::MLIRContext &context)
22 : context(context) {
23 addConversion([&](mlir::Type type) -> mlir::Type { return type; });
24 // This is necessary in order to convert CIR pointer types that are pointing
25 // to CIR types that are being converted.
26 addConversion([&](cir::PointerType type) -> mlir::Type {
27 mlir::Type loweredPointeeType = convertType(type.getPointee());
28 if (!loweredPointeeType)
29 return {};
30 return cir::PointerType::get(type.getContext(), loweredPointeeType,
31 type.getAddrSpace());
32 });
33 addConversion([&](cir::ArrayType type) -> mlir::Type {
34 mlir::Type loweredElementType = convertType(type.getElementType());
35 if (!loweredElementType)
36 return {};
37 return cir::ArrayType::get(loweredElementType, type.getSize());
38 });
39 // This is necessary in order to convert CIR function types that have
40 // argument or return types that use CIR types that are being converted.
41 addConversion([&](cir::FuncType type) -> mlir::Type {
42 llvm::SmallVector<mlir::Type> loweredInputTypes;
43 loweredInputTypes.reserve(type.getNumInputs());
44 if (mlir::failed(convertTypes(type.getInputs(), loweredInputTypes)))
45 return {};
46
47 mlir::Type loweredReturnType = convertType(type.getReturnType());
48 if (!loweredReturnType)
49 return {};
50
51 return cir::FuncType::get(loweredInputTypes, loweredReturnType,
52 /*isVarArg=*/type.getVarArg());
53 });
54 addConversion([&](cir::StructType type) -> mlir::Type {
55 return convertRecordType(type);
56 });
57 addConversion([&](cir::UnionType type) -> mlir::Type {
58 return convertRecordType(type);
59 });
60}
61
63 std::unique_lock<decltype(recordTypeMutex)> lock(recordTypeMutex);
64
65 for (auto rt : convertedRecordTypes)
66 rt.removeABIConversionNamePrefix();
67}
68
69// This provides a stack for the RecordTypes being processed on the current
70// thread, which lets us solve recursive conversions. This implementation is
71// cribbed from the LLVMTypeConverter which solves a similar but not identical
72// problem.
74RecordRewritingTypeConverter::getCurrentThreadRecursiveStack() {
75 {
76 // Most of the time, the entry already exists in the map.
77 std::shared_lock<decltype(callStackMutex)> lock(callStackMutex,
78 std::defer_lock);
79 if (context.isMultithreadingEnabled())
80 lock.lock();
81 auto recursiveStack = conversionCallStack.find(llvm::get_threadid());
82 if (recursiveStack != conversionCallStack.end())
83 return *recursiveStack->second;
84 }
85
86 // First time this thread gets here, we have to get an exclusive access to
87 // insert in the map
88 std::unique_lock<decltype(callStackMutex)> lock(callStackMutex);
89 auto recursiveStackInserted = conversionCallStack.insert(
90 std::make_pair(llvm::get_threadid(),
91 std::make_unique<llvm::SmallVector<cir::RecordType>>()));
92 return *recursiveStackInserted.first->second;
93}
94
95void RecordRewritingTypeConverter::addConvertedRecordType(cir::RecordType rt) {
96 std::unique_lock<decltype(recordTypeMutex)> lock(recordTypeMutex);
97 convertedRecordTypes.push_back(rt);
98}
99
100llvm::SmallVector<mlir::Type>
101RecordRewritingTypeConverter::convertRecordMemberTypes(cir::RecordType type) {
102 llvm::SmallVector<mlir::Type> loweredMemberTypes;
103 loweredMemberTypes.reserve(type.getNumElements());
104
105 if (mlir::failed(convertTypes(type.getMembers(), loweredMemberTypes)))
106 return {};
107
108 return loweredMemberTypes;
109}
110
111bool RecordRewritingTypeConverter::reachesChangingType(
112 mlir::Type type, llvm::SmallPtrSetImpl<mlir::Type> &visiting) const {
113 if (typeMayChange(type))
114 return true;
115
116 // The sub-element walk skips record members. Stop on cycles.
117 if (auto rt = mlir::dyn_cast<cir::RecordType>(type)) {
118 if (!visiting.insert(type).second)
119 return false;
120 for (mlir::Type member : rt.getMembers())
121 if (reachesChangingType(member, visiting))
122 return true;
123 if (auto u = mlir::dyn_cast<cir::UnionType>(type))
124 if (mlir::Type pad = u.getPadding())
125 return reachesChangingType(pad, visiting);
126 return false;
127 }
128
129 bool found = false;
130 type.walkImmediateSubElements([](mlir::Attribute) {},
131 [&](mlir::Type sub) {
132 found = found ||
133 reachesChangingType(sub, visiting);
134 });
135 return found;
136}
137
138bool RecordRewritingTypeConverter::recordNeedsConversion(cir::RecordType type) {
139 {
140 std::shared_lock<decltype(recordNeedsConversionMutex)> lock(
141 recordNeedsConversionMutex);
142 auto it = recordNeedsConversionCache.find(type);
143 if (it != recordNeedsConversionCache.end())
144 return it->second;
145 }
146
147 // Nested results may be partial inside a cycle, so cache only this one.
148 llvm::SmallPtrSet<mlir::Type, 8> visiting;
149 bool result = reachesChangingType(type, visiting);
150
151 std::unique_lock<decltype(recordNeedsConversionMutex)> lock(
152 recordNeedsConversionMutex);
153 recordNeedsConversionCache[type] = result;
154 return result;
155}
156
157cir::RecordType
158RecordRewritingTypeConverter::convertRecordType(cir::RecordType type) {
159 // Keep records that reach no changing type, including through nested ones.
160 if (!recordNeedsConversion(type))
161 return type;
162
163 // Unnamed record types can't be referred to recursively, so we can just
164 // convert this one. It also doesn't have uniqueness problems, so we can
165 // just do a conversion on it.
166 if (!type.getName()) {
167 llvm::SmallVector<mlir::Type> converted = convertRecordMemberTypes(type);
168 assert(converted.size() == type.getNumElements() &&
169 "member conversion must be one type in, one type out for the "
170 "kinds to carry over by index");
171 if (auto u = mlir::dyn_cast<cir::UnionType>(type)) {
172 mlir::Type loweredPadding;
173 if (mlir::Type pad = u.getPadding())
174 loweredPadding = convertType(pad);
175 return cir::UnionType::get(type.getContext(), converted, type.getPacked(),
176 loweredPadding, u.getMemberKinds());
177 }
178 auto s = mlir::cast<cir::StructType>(type);
179 return cir::StructType::get(type.getContext(), converted, type.getPacked(),
180 s.getIsClass(), s.getMemberKinds());
181 }
182
183 assert(!type.isIncomplete() || type.getMembers().empty());
184
185 // If the type has already been converted, we can just return, since there
186 // is nothing to do. Also, if it is incomplete, it can't have invalid
187 // members! So we can skip transforming it.
188 if (type.isIncomplete() || type.isABIConvertedRecord())
189 return type;
190
191 llvm::SmallVectorImpl<cir::RecordType> &recursiveStack =
192 getCurrentThreadRecursiveStack();
193
194 cir::RecordType convertedType;
195 if (mlir::isa<cir::UnionType>(type))
196 convertedType =
197 cir::UnionType::get(type.getContext(), type.getABIConvertedName());
198 else
199 convertedType =
200 cir::StructType::get(type.getContext(), type.getABIConvertedName(),
201 mlir::cast<cir::StructType>(type).getIsClass());
202
203 // This type has already been converted, just return it.
204 if (convertedType.isComplete())
205 return convertedType;
206
207 // We put the existing 'type' into the vector if we're in the process of
208 // converting it (and pop it when we're done). To prevent recursion,
209 // just return the 'incomplete' version, and the 'top level' version of this
210 // call will call 'complete' on it.
211 if (llvm::is_contained(recursiveStack, type))
212 return convertedType;
213
214 recursiveStack.push_back(type);
215 llvm::scope_exit popConvertingType(
216 [&recursiveStack]() { recursiveStack.pop_back(); });
217
218 llvm::SmallVector<mlir::Type> convertedMembers =
219 convertRecordMemberTypes(type);
220 assert(convertedMembers.size() == type.getNumElements() &&
221 "member conversion must be one type in, one type out for the kinds "
222 "to carry over by index");
223
224 mlir::Type loweredPadding;
225 if (auto u = mlir::dyn_cast<cir::UnionType>(type))
226 if (mlir::Type pad = u.getPadding())
227 loweredPadding = convertType(pad);
228 convertedType.complete(convertedMembers, type.getPacked(), loweredPadding,
229 type.getMemberKinds());
230 addConvertedRecordType(convertedType);
231 return convertedType;
232}
void restoreRecordTypeNames()
Remove the temporary name of every record rebuilt by this converter.
virtual bool typeMayChange(mlir::Type type) const
Whether type itself, ignoring nested types, may change.
RecordRewritingTypeConverter(mlir::MLIRContext &context)
bool isComplete() const
Definition CIRTypes.h:169
llvm::ArrayRef< mlir::Type > getMembers() const
Definition CIRTypes.cpp:639
void complete(llvm::ArrayRef< mlir::Type > members, bool packed, mlir::Type padding, llvm::ArrayRef< RecordMemberKind > memberKinds)
padding is union-only.
Definition CIRTypes.cpp:687
const internal::VariadicAllOfMatcher< Type > type
Matches Types in the clang AST.