343 lines · cpp
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-exception6//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 28using namespace llvm;29 30InlineAsm::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 NDEBUG38 // Do various checks on the constraint string and type.39 cantFail(verify(getFunctionType(), constraints));40#endif41}42 43InlineAsm *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 54void InlineAsm::destroyConstant() {55 getType()->getContext().pImpl->InlineAsms.remove(this);56 delete this;57}58 59FunctionType *InlineAsm::getFunctionType() const {60 return FTy;61}62 63void 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 delimiters68 // for example "\0A", ";".69 // 2) Enhance StringRef. Some of the special delimiter ("\0") can't be70 // 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 the77/// fields in this structure. If the constraint string is not understood,78/// return true, otherwise return false.79bool 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 // Initialize87 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 Constraint161 // 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 output173 // 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 constraint200 // 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 constraint205 ++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 constraint224/// indicated by the index.225void 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 235InlineAsm::ConstraintInfoVector236InlineAsm::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. In256 // 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 270static Error makeStringError(const char *Msg) {271 return createStringError(errc::invalid_argument, Msg);272}273 274Error 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