@@ -76,6 +76,7 @@ CodeGenIntrinsicTable::CodeGenIntrinsicTable(const RecordKeeper &RC) {
7676
7777 CheckDuplicateIntrinsics ();
7878 CheckTargetIndependentIntrinsics ();
79+ CheckOverloadSuffixConflicts ();
7980}
8081
8182// Check for duplicate intrinsic names.
@@ -124,6 +125,129 @@ void CodeGenIntrinsicTable::CheckTargetIndependentIntrinsics() const {
124125 }
125126}
126127
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+
127251const CodeGenIntrinsic &CodeGenIntrinsicMap::operator [](const Record *Record) {
128252 if (!Record->isSubClassOf (" Intrinsic" ))
129253 PrintFatalError (" Intrinsic defs should be subclass of 'Intrinsic' class" );
0 commit comments