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