Skip to content

Commit

Permalink
[InlineAsm] Improve error messages for invalid constraint strings
Browse files Browse the repository at this point in the history
InlineAsm constraint string verification can fail for many reasons,
but used to always print a generic "invalid type for inline asm
constraint string" message -- which is especially confusing if
the actual error is unrelated to the type, e.g. a failure to parse
the constraint string.

Change the verify API to return an Error with a more specific
error message, and print that in the IR parser.
  • Loading branch information
nikic committed Jul 12, 2022
1 parent 4d26faa commit 00797b8
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 31 deletions.
9 changes: 4 additions & 5 deletions llvm/include/llvm/IR/InlineAsm.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

namespace llvm {

class Error;
class FunctionType;
class PointerType;
template <class ConstantClass> class ConstantUniqueMap;
Expand Down Expand Up @@ -83,11 +84,9 @@ class InlineAsm final : public Value {
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 {
Expand Down
9 changes: 8 additions & 1 deletion llvm/lib/AsmParser/LLParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5383,8 +5383,15 @@ bool LLParser::convertValIDToValue(Type *Ty, ValID &ID, Value *&V,
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);
Expand Down
43 changes: 29 additions & 14 deletions llvm/lib/IR/InlineAsm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#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>
Expand All @@ -33,9 +34,10 @@ InlineAsm::InlineAsm(FunctionType *FTy, const std::string &asmString,
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,
Expand Down Expand Up @@ -248,15 +250,19 @@ InlineAsm::ParseConstraints(StringRef Constraints) {
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;
Expand All @@ -265,15 +271,19 @@ bool InlineAsm::Verify(FunctionType *Ty, StringRef ConstStr) {
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:
Expand All @@ -284,18 +294,23 @@ bool InlineAsm::Verify(FunctionType *Ty, StringRef ConstStr) {

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();
}
10 changes: 0 additions & 10 deletions llvm/test/Assembler/inline-asm-clobber.ll

This file was deleted.

58 changes: 58 additions & 0 deletions llvm/test/Assembler/inline-asm-constraint-error.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
; 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
}
2 changes: 1 addition & 1 deletion llvm/test/Assembler/invalid-inline-constraint.ll
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
; 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}"()ounwindret

0 comments on commit 00797b8

Please sign in to comment.