@@ -76,6 +76,7 @@ CodeGenIntrinsicTable::CodeGenIntrinsicTable(const RecordKeeper &RC) {
76
76
77
77
CheckDuplicateIntrinsics ();
78
78
CheckTargetIndependentIntrinsics ();
79
+ CheckOverloadSuffixConflicts ();
79
80
}
80
81
81
82
// Check for duplicate intrinsic names.
@@ -124,6 +125,129 @@ void CodeGenIntrinsicTable::CheckTargetIndependentIntrinsics() const {
124
125
}
125
126
}
126
127
128
+ // Return true if the given Suffix looks like a mangled type. Note that this
129
+ // check is conservative, but allows all existing LLVM intrinsic suffixes to be
130
+ // considered as not looking like a mangling suffix.
131
+ static bool doesSuffixLookLikeMangledType (StringRef Suffix) {
132
+ // Try to match against possible mangling suffixes for various types.
133
+ // See getMangledTypeStr() for the mangling suffixes possible. It includes
134
+ // pointer : p[0-9]+
135
+ // array : a[0-9]+.+
136
+ // struct: : s_/sl_.+
137
+ // function : f_.+
138
+ // vector : v/nxv[0-9]+.+
139
+ // target type : t.+
140
+ // integer : i[0-9]+
141
+ // named types : See `NamedTypes` below.
142
+
143
+ // Match anything with an _, so match function and struct types.
144
+ if (Suffix.contains (' _' ))
145
+ return true ;
146
+
147
+ // [av][0-9]+.+, simplified to [av][0-9].+
148
+ if (Suffix.size () >= 2 && is_contained (" av" , Suffix[0 ]) && isDigit (Suffix[1 ]))
149
+ return true ;
150
+
151
+ // nxv[0-9]+.+, simplified to nxv[0-9].+
152
+ if (Suffix.size () >= 4 && Suffix.starts_with (" nxv" ) && isDigit (Suffix[3 ]))
153
+ return true ;
154
+
155
+ // t.+
156
+ if (Suffix.size () > 1 && Suffix.starts_with (' t' ))
157
+ return false ;
158
+
159
+ // [pi][0-9]+
160
+ if (is_contained (" pi" , Suffix[0 ]) && all_of (Suffix.drop_front (), isDigit))
161
+ return true ;
162
+
163
+ // Match one of the named types.
164
+ static constexpr StringLiteral NamedTypes[] = {
165
+ " isVoid" , " Metadata" , " f16" , " f32" , " f64" ,
166
+ " f80" , " f128" , " bf16" , " ppcf128" , " x86amx" };
167
+ return is_contained (NamedTypes, Suffix);
168
+ }
169
+
170
+ // Check for conflicts with overloaded intrinsics. If there exists an overloaded
171
+ // intrinsic with base name `llvm.target.foo`, LLVM will add a mangling suffix
172
+ // to it to encode the overload types. This mangling suffix is 1 or more .
173
+ // prefixed mangled type string as defined in `getMangledTypeStr`. If there
174
+ // exists another intrinsic `llvm.target.foo[.<suffixN>]+`, which has the same
175
+ // prefix as the overloaded intrinsic, its possible that there may be a name
176
+ // conflict with the overloaded intrinsic and either one may interfere with name
177
+ // lookup for the other, leading to wrong intrinsic ID being assigned.
178
+ //
179
+ // The actual name lookup in the intrinsic name table is done by a search
180
+ // on each successive '.' separted component of the intrinsic name (see
181
+ // `lookupLLVMIntrinsicByName`). Consider first the case where there exists a
182
+ // non-overloaded intrinsic `llvm.target.foo[.suffix]+`. For the non-overloaded
183
+ // intrinsics, the name lookup is an exact match, so the presence of the
184
+ // overloaded intrinsic with the same prefix will not interfere with the
185
+ // search. However, a lookup intended to match the overloaded intrinsic might be
186
+ // affected by the presence of another entry in the name table with the same
187
+ // prefix.
188
+ //
189
+ // Since LLVM's name lookup first selects the target specific (or target
190
+ // independent) slice of the name table to look into, intrinsics in 2 different
191
+ // targets cannot conflict with each other. Within a specific target,
192
+ // if we have an overloaded intrinsic with name `llvm.target.foo` and another
193
+ // one with same prefix and one or more suffixes `llvm.target.foo[.<suffixN>]+`,
194
+ // then the name search will try to first match against suffix0, then suffix1
195
+ // etc. If suffix0 can match a mangled type, then the search for an
196
+ // `llvm.target.foo` with a mangling suffix can match against suffix0,
197
+ // preventing a match with `llvm.target.foo`. If suffix0 cannot match a mangled
198
+ // type, then that cannot happen, so we do not need to check for later suffixes.
199
+ //
200
+ // Generalizing, the `llvm.target.foo[.suffixN]+` will cause a conflict if the
201
+ // first suffix (.suffix0) can match a mangled type (and then we do not need to
202
+ // check later suffixes) and will not cause a conflict if it cannot (and then
203
+ // again, we do not need to check for later suffixes).
204
+ void CodeGenIntrinsicTable::CheckOverloadSuffixConflicts () const {
205
+ for (const TargetSet &Set : Targets) {
206
+ const CodeGenIntrinsic *Overloaded = nullptr ;
207
+ for (const CodeGenIntrinsic &Int : (*this )[Set]) {
208
+ // If we do not have an overloaded intrinsic to check against, nothing
209
+ // to do except potentially identifying this as a candidate for checking
210
+ // against in future iteration.
211
+ if (!Overloaded) {
212
+ if (Int.isOverloaded )
213
+ Overloaded = ∬
214
+ continue ;
215
+ }
216
+
217
+ StringRef Name = Int.Name ;
218
+ StringRef OverloadName = Overloaded->Name ;
219
+ // If we have an overloaded intrinsic to check again, check if its name is
220
+ // a proper prefix of this intrinsic.
221
+ if (Name.starts_with (OverloadName) && Name[OverloadName.size ()] == ' .' ) {
222
+ // If yes, verify suffixes and flag an error.
223
+ StringRef Suffixes = Name.drop_front (OverloadName.size () + 1 );
224
+
225
+ // Only need to look at the first suffix.
226
+ StringRef Suffix0 = Suffixes.split (' .' ).first ;
227
+
228
+ if (!doesSuffixLookLikeMangledType (Suffix0))
229
+ continue ;
230
+
231
+ unsigned SuffixSize = OverloadName.size () + 1 + Suffix0.size ();
232
+ // If suffix looks like mangling suffix, flag it as an error.
233
+ PrintError (Int.TheDef ->getLoc (),
234
+ " intrinsic `" + Name + " ` cannot share prefix `" +
235
+ Name.take_front (SuffixSize) +
236
+ " ` with another overloaded intrinsic `" + OverloadName +
237
+ " `" );
238
+ PrintNote (Overloaded->TheDef ->getLoc (),
239
+ " Overloaded intrinsic `" + OverloadName + " ` defined here" );
240
+ continue ;
241
+ }
242
+
243
+ // If we find an intrinsic that is not a proper prefix, any later
244
+ // intrinsic is also not going to be a proper prefix, so invalidate the
245
+ // overloaded to check against.
246
+ Overloaded = nullptr ;
247
+ }
248
+ }
249
+ }
250
+
127
251
const CodeGenIntrinsic &CodeGenIntrinsicMap::operator [](const Record *Record) {
128
252
if (!Record->isSubClassOf (" Intrinsic" ))
129
253
PrintFatalError (" Intrinsic defs should be subclass of 'Intrinsic' class" );
0 commit comments