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