Skip to content

Commit 00797b8

Browse files
committedJul 12, 2022
[InlineAsm] Improve error messages for invalid constraint strings
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.
1 parent 4d26faa commit 00797b8

File tree

6 files changed

+100
-31
lines changed

6 files changed

+100
-31
lines changed
 

‎llvm/include/llvm/IR/InlineAsm.h

+4-5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
namespace llvm {
2626

27+
class Error;
2728
class FunctionType;
2829
class PointerType;
2930
template <class ConstantClass> class ConstantUniqueMap;
@@ -83,11 +84,9 @@ class InlineAsm final : public Value {
8384
const std::string &getAsmString() const { return AsmString; }
8485
const std::string &getConstraintString() const { return Constraints; }
8586

86-
/// Verify - This static method can be used by the parser to check to see if
87-
/// the specified constraint string is legal for the type. This returns true
88-
/// if legal, false if not.
89-
///
90-
static bool Verify(FunctionType *Ty, StringRef Constraints);
87+
/// This static method can be used by the parser to check to see if the
88+
/// specified constraint string is legal for the type.
89+
static Error verify(FunctionType *Ty, StringRef Constraints);
9190

9291
// Constraint String Parsing
9392
enum ConstraintPrefix {

‎llvm/lib/AsmParser/LLParser.cpp

+8-1
Original file line numberDiff line numberDiff line change
@@ -5383,8 +5383,15 @@ bool LLParser::convertValIDToValue(Type *Ty, ValID &ID, Value *&V,
53835383
V = PFS->getVal(ID.StrVal, Ty, ID.Loc);
53845384
return V == nullptr;
53855385
case ValID::t_InlineAsm: {
5386-
if (!ID.FTy || !InlineAsm::Verify(ID.FTy, ID.StrVal2))
5386+
if (!ID.FTy)
53875387
return error(ID.Loc, "invalid type for inline asm constraint string");
5388+
if (Error Err = InlineAsm::verify(ID.FTy, ID.StrVal2)) {
5389+
std::string Str;
5390+
raw_string_ostream OS(Str);
5391+
OS << Err;
5392+
consumeError(std::move(Err));
5393+
return error(ID.Loc, Str.c_str());
5394+
}
53885395
V = InlineAsm::get(
53895396
ID.FTy, ID.StrVal, ID.StrVal2, ID.UIntVal & 1, (ID.UIntVal >> 1) & 1,
53905397
InlineAsm::AsmDialect((ID.UIntVal >> 2) & 1), (ID.UIntVal >> 3) & 1);

‎llvm/lib/IR/InlineAsm.cpp

+29-14
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "llvm/IR/Value.h"
2020
#include "llvm/Support/Casting.h"
2121
#include "llvm/Support/Compiler.h"
22+
#include "llvm/Support/Errc.h"
2223
#include <algorithm>
2324
#include <cassert>
2425
#include <cctype>
@@ -33,9 +34,10 @@ InlineAsm::InlineAsm(FunctionType *FTy, const std::string &asmString,
3334
AsmString(asmString), Constraints(constraints), FTy(FTy),
3435
HasSideEffects(hasSideEffects), IsAlignStack(isAlignStack),
3536
Dialect(asmDialect), CanThrow(canThrow) {
37+
#ifndef NDEBUG
3638
// Do various checks on the constraint string and type.
37-
assert(Verify(getFunctionType(), constraints) &&
38-
"Function type not legal for constraints!");
39+
cantFail(verify(getFunctionType(), constraints));
40+
#endif
3941
}
4042

4143
InlineAsm *InlineAsm::get(FunctionType *FTy, StringRef AsmString,
@@ -248,15 +250,19 @@ InlineAsm::ParseConstraints(StringRef Constraints) {
248250
return Result;
249251
}
250252

251-
/// Verify - Verify that the specified constraint string is reasonable for the
252-
/// specified function type, and otherwise validate the constraint string.
253-
bool InlineAsm::Verify(FunctionType *Ty, StringRef ConstStr) {
254-
if (Ty->isVarArg()) return false;
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");
255260

256261
ConstraintInfoVector Constraints = ParseConstraints(ConstStr);
257262

258263
// Error parsing constraints.
259-
if (Constraints.empty() && !ConstStr.empty()) return false;
264+
if (Constraints.empty() && !ConstStr.empty())
265+
return makeStringError("failed to parse constraints");
260266

261267
unsigned NumOutputs = 0, NumInputs = 0, NumClobbers = 0;
262268
unsigned NumIndirect = 0;
@@ -265,15 +271,19 @@ bool InlineAsm::Verify(FunctionType *Ty, StringRef ConstStr) {
265271
switch (Constraint.Type) {
266272
case InlineAsm::isOutput:
267273
if ((NumInputs-NumIndirect) != 0 || NumClobbers != 0)
268-
return false; // outputs before inputs and clobbers.
274+
return makeStringError("output constraint occurs after input "
275+
"or clobber constraint");
276+
269277
if (!Constraint.isIndirect) {
270278
++NumOutputs;
271279
break;
272280
}
273281
++NumIndirect;
274282
LLVM_FALLTHROUGH; // We fall through for Indirect Outputs.
275283
case InlineAsm::isInput:
276-
if (NumClobbers) return false; // inputs before clobbers.
284+
if (NumClobbers)
285+
return makeStringError("input constraint occurs after clobber "
286+
"constraint");
277287
++NumInputs;
278288
break;
279289
case InlineAsm::isClobber:
@@ -284,18 +294,23 @@ bool InlineAsm::Verify(FunctionType *Ty, StringRef ConstStr) {
284294

285295
switch (NumOutputs) {
286296
case 0:
287-
if (!Ty->getReturnType()->isVoidTy()) return false;
297+
if (!Ty->getReturnType()->isVoidTy())
298+
return makeStringError("inline asm without outputs must return void");
288299
break;
289300
case 1:
290-
if (Ty->getReturnType()->isStructTy()) return false;
301+
if (Ty->getReturnType()->isStructTy())
302+
return makeStringError("inline asm with one output cannot return struct");
291303
break;
292304
default:
293305
StructType *STy = dyn_cast<StructType>(Ty->getReturnType());
294306
if (!STy || STy->getNumElements() != NumOutputs)
295-
return false;
307+
return makeStringError("number of output constraints does not match "
308+
"number of return struct elements");
296309
break;
297310
}
298311

299-
if (Ty->getNumParams() != NumInputs) return false;
300-
return true;
312+
if (Ty->getNumParams() != NumInputs)
313+
return makeStringError("number of input constraints does not match number "
314+
"of parameters");
315+
return Error::success();
301316
}

‎llvm/test/Assembler/inline-asm-clobber.ll

-10
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
; RUN: split-file %s %t
2+
; RUN: not llvm-as < %t/parse-fail.ll 2>&1 | FileCheck %s --check-prefix=CHECK-PARSE-FAIL
3+
; RUN: not llvm-as < %t/input-before-output.ll 2>&1 | FileCheck %s --check-prefix=CHECK-INPUT-BEFORE-OUTPUT
4+
; RUN: not llvm-as < %t/input-after-clobber.ll 2>&1 | FileCheck %s --check-prefix=CHECK-INPUT-AFTER-CLOBBER
5+
; RUN: not llvm-as < %t/must-return-void.ll 2>&1 | FileCheck %s --check-prefix=CHECK-MUST-RETURN-VOID
6+
; RUN: not llvm-as < %t/cannot-be-struct.ll 2>&1 | FileCheck %s --check-prefix=CHECK-CANNOT-BE-STRUCT
7+
; RUN: not llvm-as < %t/incorrect-struct-elements.ll 2>&1 | FileCheck %s --check-prefix=CHECK-INCORRECT-STRUCT-ELEMENTS
8+
; RUN: not llvm-as < %t/incorrect-arg-num.ll 2>&1 | FileCheck %s --check-prefix=CHECK-INCORRECT-ARG-NUM
9+
10+
;--- parse-fail.ll
11+
; CHECK-PARSE-FAIL: failed to parse constraints
12+
define void @foo() {
13+
; "~x{21}" is not a valid clobber constraint.
14+
call void asm sideeffect "mov x0, #42", "~{x0},~{x19},~x{21}"()
15+
ret void
16+
}
17+
18+
;--- input-before-output.ll
19+
; CHECK-INPUT-BEFORE-OUTPUT: output constraint occurs after input or clobber constraint
20+
define void @foo() {
21+
call void asm sideeffect "mov x0, #42", "r,=r"()
22+
ret void
23+
}
24+
25+
;--- input-after-clobber.ll
26+
; CHECK-INPUT-AFTER-CLOBBER: input constraint occurs after clobber constraint
27+
define void @foo() {
28+
call void asm sideeffect "mov x0, #42", "~{x0},r"()
29+
ret void
30+
}
31+
32+
;--- must-return-void.ll
33+
; CHECK-MUST-RETURN-VOID: inline asm without outputs must return void
34+
define void @foo() {
35+
call i32 asm sideeffect "mov x0, #42", ""()
36+
ret void
37+
}
38+
39+
;--- cannot-be-struct.ll
40+
; CHECK-CANNOT-BE-STRUCT: inline asm with one output cannot return struct
41+
define void @foo() {
42+
call { i32 } asm sideeffect "mov x0, #42", "=r"()
43+
ret void
44+
}
45+
46+
;--- incorrect-struct-elements.ll
47+
; CHECK-INCORRECT-STRUCT-ELEMENTS: number of output constraints does not match number of return struct elements
48+
define void @foo() {
49+
call { i32 } asm sideeffect "mov x0, #42", "=r,=r"()
50+
ret void
51+
}
52+
53+
;--- incorrect-arg-num.ll
54+
; CHECK-INCORRECT-ARG-NUM: number of input constraints does not match number of parameters
55+
define void @foo() {
56+
call void asm sideeffect "mov x0, #42", "r"()
57+
ret void
58+
}
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
; RUN: not llvm-as < %s 2>&1 | FileCheck %s
22

33
; Tests bug: https://llvm.org/bugs/show_bug.cgi?id=24646
4-
; CHECK: error: invalid type for inline asm constraint string
4+
; CHECK: error: failed to parse constraints
55

66
define void @foo() nounwind {
77
call void asm sideeffect "mov x0, #42","=~{x0},~{x19},mov |0,{x19},mov x0, #4~x{21}"()ounwi #4~x{21}"()ounwindret

0 commit comments

Comments
 (0)
Please sign in to comment.