xref: /llvm-project/llvm/lib/IR/InlineAsm.cpp (revision d7c14c8f976fd291984e0c7eed75dd3331b1ed6d)
1 //===- InlineAsm.cpp - Implement the InlineAsm class ----------------------===//
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 //
9 // This file implements the InlineAsm class.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "llvm/IR/InlineAsm.h"
14 #include "ConstantsContext.h"
15 #include "LLVMContextImpl.h"
16 #include "llvm/ADT/StringRef.h"
17 #include "llvm/IR/DerivedTypes.h"
18 #include "llvm/IR/LLVMContext.h"
19 #include "llvm/IR/Value.h"
20 #include "llvm/Support/Casting.h"
21 #include "llvm/Support/Compiler.h"
22 #include "llvm/Support/Errc.h"
23 #include <algorithm>
24 #include <cassert>
25 #include <cctype>
26 #include <cstdlib>
27 
28 using namespace llvm;
29 
30 InlineAsm::InlineAsm(FunctionType *FTy, const std::string &asmString,
31                      const std::string &constraints, bool hasSideEffects,
32                      bool isAlignStack, AsmDialect asmDialect, bool canThrow)
33     : Value(PointerType::getUnqual(FTy->getContext()), Value::InlineAsmVal),
34       AsmString(asmString), Constraints(constraints), FTy(FTy),
35       HasSideEffects(hasSideEffects), IsAlignStack(isAlignStack),
36       Dialect(asmDialect), CanThrow(canThrow) {
37 #ifndef NDEBUG
38   // Do various checks on the constraint string and type.
39   cantFail(verify(getFunctionType(), constraints));
40 #endif
41 }
42 
43 InlineAsm *InlineAsm::get(FunctionType *FTy, StringRef AsmString,
44                           StringRef Constraints, bool hasSideEffects,
45                           bool isAlignStack, AsmDialect asmDialect,
46                           bool canThrow) {
47   InlineAsmKeyType Key(AsmString, Constraints, FTy, hasSideEffects,
48                        isAlignStack, asmDialect, canThrow);
49   LLVMContextImpl *pImpl = FTy->getContext().pImpl;
50   return pImpl->InlineAsms.getOrCreate(
51       PointerType::getUnqual(FTy->getContext()), Key);
52 }
53 
54 void InlineAsm::destroyConstant() {
55   getType()->getContext().pImpl->InlineAsms.remove(this);
56   delete this;
57 }
58 
59 FunctionType *InlineAsm::getFunctionType() const {
60   return FTy;
61 }
62 
63 void InlineAsm::collectAsmStrs(SmallVectorImpl<StringRef> &AsmStrs) const {
64   StringRef AsmStr(AsmString);
65   AsmStrs.clear();
66 
67   // TODO: 1) Unify delimiter for inline asm, we also meet other delimiters
68   // for example "\0A", ";".
69   // 2) Enhance StringRef. Some of the special delimiter ("\0") can't be
70   // split in StringRef. Also empty StringRef can not call split (will stuck).
71   if (AsmStr.empty())
72     return;
73   AsmStr.split(AsmStrs, "\n\t", -1, false);
74 }
75 
76 /// Parse - Analyze the specified string (e.g. "==&{eax}") and fill in the
77 /// fields in this structure.  If the constraint string is not understood,
78 /// return true, otherwise return false.
79 bool InlineAsm::ConstraintInfo::Parse(StringRef Str,
80                      InlineAsm::ConstraintInfoVector &ConstraintsSoFar) {
81   StringRef::iterator I = Str.begin(), E = Str.end();
82   unsigned multipleAlternativeCount = Str.count('|') + 1;
83   unsigned multipleAlternativeIndex = 0;
84   ConstraintCodeVector *pCodes = &Codes;
85 
86   // Initialize
87   isMultipleAlternative = multipleAlternativeCount > 1;
88   if (isMultipleAlternative) {
89     multipleAlternatives.resize(multipleAlternativeCount);
90     pCodes = &multipleAlternatives[0].Codes;
91   }
92   Type = isInput;
93   isEarlyClobber = false;
94   MatchingInput = -1;
95   isCommutative = false;
96   isIndirect = false;
97   currentAlternativeIndex = 0;
98 
99   // Parse prefixes.
100   if (*I == '~') {
101     Type = isClobber;
102     ++I;
103 
104     // '{' must immediately follow '~'.
105     if (I != E && *I != '{')
106       return true;
107   } else if (*I == '=') {
108     ++I;
109     Type = isOutput;
110   } else if (*I == '!') {
111     ++I;
112     Type = isLabel;
113   }
114 
115   if (*I == '*') {
116     isIndirect = true;
117     ++I;
118   }
119 
120   if (I == E) return true;  // Just a prefix, like "==" or "~".
121 
122   // Parse the modifiers.
123   bool DoneWithModifiers = false;
124   while (!DoneWithModifiers) {
125     switch (*I) {
126     default:
127       DoneWithModifiers = true;
128       break;
129     case '&':     // Early clobber.
130       if (Type != isOutput ||      // Cannot early clobber anything but output.
131           isEarlyClobber)          // Reject &&&&&&
132         return true;
133       isEarlyClobber = true;
134       break;
135     case '%':     // Commutative.
136       if (Type == isClobber ||     // Cannot commute clobbers.
137           isCommutative)           // Reject %%%%%
138         return true;
139       isCommutative = true;
140       break;
141     case '#':     // Comment.
142     case '*':     // Register preferencing.
143       return true;     // Not supported.
144     }
145 
146     if (!DoneWithModifiers) {
147       ++I;
148       if (I == E) return true;   // Just prefixes and modifiers!
149     }
150   }
151 
152   // Parse the various constraints.
153   while (I != E) {
154     if (*I == '{') {   // Physical register reference.
155       // Find the end of the register name.
156       StringRef::iterator ConstraintEnd = std::find(I+1, E, '}');
157       if (ConstraintEnd == E) return true;  // "{foo"
158       pCodes->push_back(std::string(StringRef(I, ConstraintEnd + 1 - I)));
159       I = ConstraintEnd+1;
160     } else if (isdigit(static_cast<unsigned char>(*I))) { // Matching Constraint
161       // Maximal munch numbers.
162       StringRef::iterator NumStart = I;
163       while (I != E && isdigit(static_cast<unsigned char>(*I)))
164         ++I;
165       pCodes->push_back(std::string(StringRef(NumStart, I - NumStart)));
166       unsigned N = atoi(pCodes->back().c_str());
167       // Check that this is a valid matching constraint!
168       if (N >= ConstraintsSoFar.size() || ConstraintsSoFar[N].Type != isOutput||
169           Type != isInput)
170         return true;  // Invalid constraint number.
171 
172       // If Operand N already has a matching input, reject this.  An output
173       // can't be constrained to the same value as multiple inputs.
174       if (isMultipleAlternative) {
175         if (multipleAlternativeIndex >=
176             ConstraintsSoFar[N].multipleAlternatives.size())
177           return true;
178         InlineAsm::SubConstraintInfo &scInfo =
179           ConstraintsSoFar[N].multipleAlternatives[multipleAlternativeIndex];
180         if (scInfo.MatchingInput != -1)
181           return true;
182         // Note that operand #n has a matching input.
183         scInfo.MatchingInput = ConstraintsSoFar.size();
184         assert(scInfo.MatchingInput >= 0);
185       } else {
186         if (ConstraintsSoFar[N].hasMatchingInput() &&
187             (size_t)ConstraintsSoFar[N].MatchingInput !=
188                 ConstraintsSoFar.size())
189           return true;
190         // Note that operand #n has a matching input.
191         ConstraintsSoFar[N].MatchingInput = ConstraintsSoFar.size();
192         assert(ConstraintsSoFar[N].MatchingInput >= 0);
193         }
194     } else if (*I == '|') {
195       multipleAlternativeIndex++;
196       pCodes = &multipleAlternatives[multipleAlternativeIndex].Codes;
197       ++I;
198     } else if (*I == '^') {
199       // Multi-letter constraint
200       // FIXME: For now assuming these are 2-character constraints.
201       pCodes->push_back(std::string(StringRef(I + 1, 2)));
202       I += 3;
203     } else if (*I == '@') {
204       // Multi-letter constraint
205       ++I;
206       unsigned char C = static_cast<unsigned char>(*I);
207       assert(isdigit(C) && "Expected a digit!");
208       int N = C - '0';
209       assert(N > 0 && "Found a zero letter constraint!");
210       ++I;
211       pCodes->push_back(std::string(StringRef(I, N)));
212       I += N;
213     } else {
214       // Single letter constraint.
215       pCodes->push_back(std::string(StringRef(I, 1)));
216       ++I;
217     }
218   }
219 
220   return false;
221 }
222 
223 /// selectAlternative - Point this constraint to the alternative constraint
224 /// indicated by the index.
225 void InlineAsm::ConstraintInfo::selectAlternative(unsigned index) {
226   if (index < multipleAlternatives.size()) {
227     currentAlternativeIndex = index;
228     InlineAsm::SubConstraintInfo &scInfo =
229       multipleAlternatives[currentAlternativeIndex];
230     MatchingInput = scInfo.MatchingInput;
231     Codes = scInfo.Codes;
232   }
233 }
234 
235 InlineAsm::ConstraintInfoVector
236 InlineAsm::ParseConstraints(StringRef Constraints) {
237   ConstraintInfoVector Result;
238 
239   // Scan the constraints string.
240   for (StringRef::iterator I = Constraints.begin(),
241          E = Constraints.end(); I != E; ) {
242     ConstraintInfo Info;
243 
244     // Find the end of this constraint.
245     StringRef::iterator ConstraintEnd = std::find(I, E, ',');
246 
247     if (ConstraintEnd == I ||  // Empty constraint like ",,"
248         Info.Parse(StringRef(I, ConstraintEnd-I), Result)) {
249       Result.clear();          // Erroneous constraint?
250       break;
251     }
252 
253     Result.push_back(Info);
254 
255     // ConstraintEnd may be either the next comma or the end of the string.  In
256     // the former case, we skip the comma.
257     I = ConstraintEnd;
258     if (I != E) {
259       ++I;
260       if (I == E) {
261         Result.clear();
262         break;
263       } // don't allow "xyz,"
264     }
265   }
266 
267   return Result;
268 }
269 
270 static Error makeStringError(const char *Msg) {
271   return createStringError(errc::invalid_argument, Msg);
272 }
273 
274 Error InlineAsm::verify(FunctionType *Ty, StringRef ConstStr) {
275   if (Ty->isVarArg())
276     return makeStringError("inline asm cannot be variadic");
277 
278   ConstraintInfoVector Constraints = ParseConstraints(ConstStr);
279 
280   // Error parsing constraints.
281   if (Constraints.empty() && !ConstStr.empty())
282     return makeStringError("failed to parse constraints");
283 
284   unsigned NumOutputs = 0, NumInputs = 0, NumClobbers = 0;
285   unsigned NumIndirect = 0, NumLabels = 0;
286 
287   for (const ConstraintInfo &Constraint : Constraints) {
288     switch (Constraint.Type) {
289     case InlineAsm::isOutput:
290       if ((NumInputs-NumIndirect) != 0 || NumClobbers != 0 || NumLabels != 0)
291         return makeStringError("output constraint occurs after input, "
292                                "clobber or label constraint");
293 
294       if (!Constraint.isIndirect) {
295         ++NumOutputs;
296         break;
297       }
298       ++NumIndirect;
299       [[fallthrough]]; // We fall through for Indirect Outputs.
300     case InlineAsm::isInput:
301       if (NumClobbers)
302         return makeStringError("input constraint occurs after clobber "
303                                "constraint");
304       ++NumInputs;
305       break;
306     case InlineAsm::isClobber:
307       ++NumClobbers;
308       break;
309     case InlineAsm::isLabel:
310       if (NumClobbers)
311         return makeStringError("label constraint occurs after clobber "
312                                "constraint");
313 
314       ++NumLabels;
315       break;
316     }
317   }
318 
319   switch (NumOutputs) {
320   case 0:
321     if (!Ty->getReturnType()->isVoidTy())
322       return makeStringError("inline asm without outputs must return void");
323     break;
324   case 1:
325     if (Ty->getReturnType()->isStructTy())
326       return makeStringError("inline asm with one output cannot return struct");
327     break;
328   default:
329     StructType *STy = dyn_cast<StructType>(Ty->getReturnType());
330     if (!STy || STy->getNumElements() != NumOutputs)
331       return makeStringError("number of output constraints does not match "
332                              "number of return struct elements");
333     break;
334   }
335 
336   if (Ty->getNumParams() != NumInputs)
337     return makeStringError("number of input constraints does not match number "
338                            "of parameters");
339 
340   // We don't have access to labels here, NumLabels will be checked separately.
341   return Error::success();
342 }
343