@@ -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,132 @@ void CodeGenIntrinsicTable::CheckTargetIndependentIntrinsics() const {
124125 }
125126}
126127
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+
127254CodeGenIntrinsic &CodeGenIntrinsicMap::operator [](const Record *Record) {
128255 if (!Record->isSubClassOf (" Intrinsic" ))
129256 PrintFatalError (" Intrinsic defs should be subclass of 'Intrinsic' class" );
0 commit comments