namespace llvm {
+class Error;
class FunctionType;
class PointerType;
template <class ConstantClass> class ConstantUniqueMap;
const std::string &getAsmString() const { return AsmString; }
const std::string &getConstraintString() const { return Constraints; }
- /// Verify - This static method can be used by the parser to check to see if
- /// the specified constraint string is legal for the type. This returns true
- /// if legal, false if not.
- ///
- static bool Verify(FunctionType *Ty, StringRef Constraints);
+ /// This static method can be used by the parser to check to see if the
+ /// specified constraint string is legal for the type.
+ static Error verify(FunctionType *Ty, StringRef Constraints);
// Constraint String Parsing
enum ConstraintPrefix {
V = PFS->getVal(ID.StrVal, Ty, ID.Loc);
return V == nullptr;
case ValID::t_InlineAsm: {
- if (!ID.FTy || !InlineAsm::Verify(ID.FTy, ID.StrVal2))
+ if (!ID.FTy)
return error(ID.Loc, "invalid type for inline asm constraint string");
+ if (Error Err = InlineAsm::verify(ID.FTy, ID.StrVal2)) {
+ std::string Str;
+ raw_string_ostream OS(Str);
+ OS << Err;
+ consumeError(std::move(Err));
+ return error(ID.Loc, Str.c_str());
+ }
V = InlineAsm::get(
ID.FTy, ID.StrVal, ID.StrVal2, ID.UIntVal & 1, (ID.UIntVal >> 1) & 1,
InlineAsm::AsmDialect((ID.UIntVal >> 2) & 1), (ID.UIntVal >> 3) & 1);
#include "llvm/IR/Value.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Compiler.h"
+#include "llvm/Support/Errc.h"
#include <algorithm>
#include <cassert>
#include <cctype>
AsmString(asmString), Constraints(constraints), FTy(FTy),
HasSideEffects(hasSideEffects), IsAlignStack(isAlignStack),
Dialect(asmDialect), CanThrow(canThrow) {
+#ifndef NDEBUG
// Do various checks on the constraint string and type.
- assert(Verify(getFunctionType(), constraints) &&
- "Function type not legal for constraints!");
+ cantFail(verify(getFunctionType(), constraints));
+#endif
}
InlineAsm *InlineAsm::get(FunctionType *FTy, StringRef AsmString,
return Result;
}
-/// Verify - Verify that the specified constraint string is reasonable for the
-/// specified function type, and otherwise validate the constraint string.
-bool InlineAsm::Verify(FunctionType *Ty, StringRef ConstStr) {
- if (Ty->isVarArg()) return false;
+static Error makeStringError(const char *Msg) {
+ return createStringError(errc::invalid_argument, Msg);
+}
+
+Error InlineAsm::verify(FunctionType *Ty, StringRef ConstStr) {
+ if (Ty->isVarArg())
+ return makeStringError("inline asm cannot be variadic");
ConstraintInfoVector Constraints = ParseConstraints(ConstStr);
// Error parsing constraints.
- if (Constraints.empty() && !ConstStr.empty()) return false;
+ if (Constraints.empty() && !ConstStr.empty())
+ return makeStringError("failed to parse constraints");
unsigned NumOutputs = 0, NumInputs = 0, NumClobbers = 0;
unsigned NumIndirect = 0;
switch (Constraint.Type) {
case InlineAsm::isOutput:
if ((NumInputs-NumIndirect) != 0 || NumClobbers != 0)
- return false; // outputs before inputs and clobbers.
+ return makeStringError("output constraint occurs after input "
+ "or clobber constraint");
+
if (!Constraint.isIndirect) {
++NumOutputs;
break;
++NumIndirect;
LLVM_FALLTHROUGH; // We fall through for Indirect Outputs.
case InlineAsm::isInput:
- if (NumClobbers) return false; // inputs before clobbers.
+ if (NumClobbers)
+ return makeStringError("input constraint occurs after clobber "
+ "constraint");
++NumInputs;
break;
case InlineAsm::isClobber:
switch (NumOutputs) {
case 0:
- if (!Ty->getReturnType()->isVoidTy()) return false;
+ if (!Ty->getReturnType()->isVoidTy())
+ return makeStringError("inline asm without outputs must return void");
break;
case 1:
- if (Ty->getReturnType()->isStructTy()) return false;
+ if (Ty->getReturnType()->isStructTy())
+ return makeStringError("inline asm with one output cannot return struct");
break;
default:
StructType *STy = dyn_cast<StructType>(Ty->getReturnType());
if (!STy || STy->getNumElements() != NumOutputs)
- return false;
+ return makeStringError("number of output constraints does not match "
+ "number of return struct elements");
break;
}
- if (Ty->getNumParams() != NumInputs) return false;
- return true;
+ if (Ty->getNumParams() != NumInputs)
+ return makeStringError("number of input constraints does not match number "
+ "of parameters");
+ return Error::success();
}
+++ /dev/null
-; RUN: not llvm-as <%s 2>&1 | FileCheck %s
-
-; "~x{21}" is not a valid clobber constraint.
-
-; CHECK: invalid type for inline asm constraint string
-
-define void @foo() nounwind {
- call void asm sideeffect "mov x0, #42", "~{x0},~{x19},~x{21}"() nounwind
- ret void
-}
--- /dev/null
+; RUN: split-file %s %t
+; RUN: not llvm-as < %t/parse-fail.ll 2>&1 | FileCheck %s --check-prefix=CHECK-PARSE-FAIL
+; RUN: not llvm-as < %t/input-before-output.ll 2>&1 | FileCheck %s --check-prefix=CHECK-INPUT-BEFORE-OUTPUT
+; RUN: not llvm-as < %t/input-after-clobber.ll 2>&1 | FileCheck %s --check-prefix=CHECK-INPUT-AFTER-CLOBBER
+; RUN: not llvm-as < %t/must-return-void.ll 2>&1 | FileCheck %s --check-prefix=CHECK-MUST-RETURN-VOID
+; RUN: not llvm-as < %t/cannot-be-struct.ll 2>&1 | FileCheck %s --check-prefix=CHECK-CANNOT-BE-STRUCT
+; RUN: not llvm-as < %t/incorrect-struct-elements.ll 2>&1 | FileCheck %s --check-prefix=CHECK-INCORRECT-STRUCT-ELEMENTS
+; RUN: not llvm-as < %t/incorrect-arg-num.ll 2>&1 | FileCheck %s --check-prefix=CHECK-INCORRECT-ARG-NUM
+
+;--- parse-fail.ll
+; CHECK-PARSE-FAIL: failed to parse constraints
+define void @foo() {
+ ; "~x{21}" is not a valid clobber constraint.
+ call void asm sideeffect "mov x0, #42", "~{x0},~{x19},~x{21}"()
+ ret void
+}
+
+;--- input-before-output.ll
+; CHECK-INPUT-BEFORE-OUTPUT: output constraint occurs after input or clobber constraint
+define void @foo() {
+ call void asm sideeffect "mov x0, #42", "r,=r"()
+ ret void
+}
+
+;--- input-after-clobber.ll
+; CHECK-INPUT-AFTER-CLOBBER: input constraint occurs after clobber constraint
+define void @foo() {
+ call void asm sideeffect "mov x0, #42", "~{x0},r"()
+ ret void
+}
+
+;--- must-return-void.ll
+; CHECK-MUST-RETURN-VOID: inline asm without outputs must return void
+define void @foo() {
+ call i32 asm sideeffect "mov x0, #42", ""()
+ ret void
+}
+
+;--- cannot-be-struct.ll
+; CHECK-CANNOT-BE-STRUCT: inline asm with one output cannot return struct
+define void @foo() {
+ call { i32 } asm sideeffect "mov x0, #42", "=r"()
+ ret void
+}
+
+;--- incorrect-struct-elements.ll
+; CHECK-INCORRECT-STRUCT-ELEMENTS: number of output constraints does not match number of return struct elements
+define void @foo() {
+ call { i32 } asm sideeffect "mov x0, #42", "=r,=r"()
+ ret void
+}
+
+;--- incorrect-arg-num.ll
+; CHECK-INCORRECT-ARG-NUM: number of input constraints does not match number of parameters
+define void @foo() {
+ call void asm sideeffect "mov x0, #42", "r"()
+ ret void
+}
; RUN: not llvm-as < %s 2>&1 | FileCheck %s
; Tests bug: https://llvm.org/bugs/show_bug.cgi?id=24646
-; CHECK: error: invalid type for inline asm constraint string
+; CHECK: error: failed to parse constraints
define void @foo() nounwind {
call void asm sideeffect "mov x0, #42","=~{x0},~{x19},mov |0,{x19},mov x0, #4~x{21}"()ounwi #4~x{21}"()ounwind\1cret