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