MIR Parser: Verify the implicit machine register operands.

This commit verifies that the parsed machine instructions contain the implicit
register operands as specified by the MCInstrDesc. Variadic and call
instructions aren't verified.

Reviewers: Duncan P. N. Exon Smith

Differential Revision: http://reviews.llvm.org/D10781

llvm-svn: 241537
This commit is contained in:
Alex Lorenz 2015-07-07 02:08:46 +00:00
parent 9622cdf4b9
commit 36962cd925
13 changed files with 230 additions and 31 deletions

View File

@ -29,6 +29,18 @@ using namespace llvm;
namespace {
/// A wrapper struct around the 'MachineOperand' struct that includes a source
/// range.
struct MachineOperandWithLocation {
MachineOperand Operand;
StringRef::iterator Begin;
StringRef::iterator End;
MachineOperandWithLocation(const MachineOperand &Operand,
StringRef::iterator Begin, StringRef::iterator End)
: Operand(Operand), Begin(Begin), End(End) {}
};
class MIParser {
SourceMgr &SM;
MachineFunction &MF;
@ -90,6 +102,9 @@ private:
bool parseInstruction(unsigned &OpCode);
bool verifyImplicitOperands(ArrayRef<MachineOperandWithLocation> Operands,
const MCInstrDesc &MCID);
void initNames2Regs();
/// Try to convert a register name to a register number. Return true if the
@ -139,11 +154,12 @@ bool MIParser::parse(MachineInstr *&MI) {
// Parse any register operands before '='
// TODO: Allow parsing of multiple operands before '='
MachineOperand MO = MachineOperand::CreateImm(0);
SmallVector<MachineOperand, 8> Operands;
SmallVector<MachineOperandWithLocation, 8> Operands;
if (Token.isRegister() || Token.isRegisterFlag()) {
auto Loc = Token.location();
if (parseRegisterOperand(MO, /*IsDef=*/true))
return true;
Operands.push_back(MO);
Operands.push_back(MachineOperandWithLocation(MO, Loc, Token.location()));
if (Token.isNot(MIToken::equal))
return error("expected '='");
lex();
@ -157,9 +173,10 @@ bool MIParser::parse(MachineInstr *&MI) {
// Parse the remaining machine operands.
while (Token.isNot(MIToken::Eof)) {
auto Loc = Token.location();
if (parseMachineOperand(MO))
return true;
Operands.push_back(MO);
Operands.push_back(MachineOperandWithLocation(MO, Loc, Token.location()));
if (Token.is(MIToken::Eof))
break;
if (Token.isNot(MIToken::comma))
@ -168,12 +185,16 @@ bool MIParser::parse(MachineInstr *&MI) {
}
const auto &MCID = MF.getSubtarget().getInstrInfo()->get(OpCode);
if (!MCID.isVariadic()) {
// FIXME: Move the implicit operand verification to the machine verifier.
if (verifyImplicitOperands(Operands, MCID))
return true;
}
// TODO: Check for extraneous machine operands.
// TODO: Check that this instruction has the implicit register operands.
MI = MF.CreateMachineInstr(MCID, DebugLoc(), /*NoImplicit=*/true);
for (const auto &Operand : Operands)
MI->addOperand(MF, Operand);
MI->addOperand(MF, Operand.Operand);
return false;
}
@ -190,6 +211,68 @@ bool MIParser::parseMBB(MachineBasicBlock *&MBB) {
return false;
}
static const char *printImplicitRegisterFlag(const MachineOperand &MO) {
assert(MO.isImplicit());
return MO.isDef() ? "implicit-def" : "implicit";
}
static std::string getRegisterName(const TargetRegisterInfo *TRI,
unsigned Reg) {
assert(TargetRegisterInfo::isPhysicalRegister(Reg) && "expected phys reg");
return StringRef(TRI->getName(Reg)).lower();
}
bool MIParser::verifyImplicitOperands(
ArrayRef<MachineOperandWithLocation> Operands, const MCInstrDesc &MCID) {
if (MCID.isCall())
// We can't verify call instructions as they can contain arbitrary implicit
// register and register mask operands.
return false;
// Gather all the expected implicit operands.
SmallVector<MachineOperand, 4> ImplicitOperands;
if (MCID.ImplicitDefs)
for (const uint16_t *ImpDefs = MCID.getImplicitDefs(); *ImpDefs; ++ImpDefs)
ImplicitOperands.push_back(
MachineOperand::CreateReg(*ImpDefs, true, true));
if (MCID.ImplicitUses)
for (const uint16_t *ImpUses = MCID.getImplicitUses(); *ImpUses; ++ImpUses)
ImplicitOperands.push_back(
MachineOperand::CreateReg(*ImpUses, false, true));
const auto *TRI = MF.getSubtarget().getRegisterInfo();
assert(TRI && "Expected target register info");
size_t I = ImplicitOperands.size(), J = Operands.size();
while (I) {
--I;
if (J) {
--J;
const auto &ImplicitOperand = ImplicitOperands[I];
const auto &Operand = Operands[J].Operand;
if (ImplicitOperand.isIdenticalTo(Operand))
continue;
if (Operand.isReg() && Operand.isImplicit()) {
return error(Operands[J].Begin,
Twine("expected an implicit register operand '") +
printImplicitRegisterFlag(ImplicitOperand) + " %" +
getRegisterName(TRI, ImplicitOperand.getReg()) + "'");
}
}
// TODO: Fix source location when Operands[J].end is right before '=', i.e:
// insead of reporting an error at this location:
// %eax = MOV32r0
// ^
// report the error at the following location:
// %eax = MOV32r0
// ^
return error(J < Operands.size() ? Operands[J].End : Token.location(),
Twine("missing implicit register operand '") +
printImplicitRegisterFlag(ImplicitOperands[I]) + " %" +
getRegisterName(TRI, ImplicitOperands[I].getReg()) + "'");
}
return false;
}
bool MIParser::parseInstruction(unsigned &OpCode) {
if (Token.isNot(MIToken::Identifier))
return error("expected a machine instruction");

View File

@ -0,0 +1,38 @@
# RUN: not llc -march=x86-64 -start-after branch-folder -stop-after branch-folder -o /dev/null %s 2>&1 | FileCheck %s
--- |
define i32 @foo(i32* %p) {
entry:
%a = load i32, i32* %p
%0 = icmp sle i32 %a, 10
br i1 %0, label %less, label %exit
less:
ret i32 0
exit:
ret i32 %a
}
...
---
name: foo
body:
- id: 0
name: entry
instructions:
- '%eax = MOV32rm %rdi, 1, _, 0, _'
- 'CMP32ri8 %eax, 10, implicit-def %eflags'
# CHECK: [[@LINE+1]]:26: expected an implicit register operand 'implicit %eflags'
- 'JG_1 %bb.2.exit, implicit %eax'
- id: 1
name: less
instructions:
- '%eax = MOV32r0 implicit-def %eflags'
- id: 2
name: exit
instructions:
- 'RETQ %eax'
...

View File

@ -0,0 +1,38 @@
# RUN: not llc -march=x86-64 -start-after branch-folder -stop-after branch-folder -o /dev/null %s 2>&1 | FileCheck %s
--- |
define i32 @foo(i32* %p) {
entry:
%a = load i32, i32* %p
%0 = icmp sle i32 %a, 10
br i1 %0, label %less, label %exit
less:
ret i32 0
exit:
ret i32 %a
}
...
---
name: foo
body:
- id: 0
name: entry
instructions:
- '%eax = MOV32rm %rdi, 1, _, 0, _'
- 'CMP32ri8 %eax, 10, implicit-def %eflags'
# CHECK: [[@LINE+1]]:26: expected an implicit register operand 'implicit %eflags'
- 'JG_1 %bb.2.exit, implicit-def %eflags'
- id: 1
name: less
instructions:
- '%eax = MOV32r0 implicit-def %eflags'
- id: 2
name: exit
instructions:
- 'RETQ %eax'
...

View File

@ -23,13 +23,13 @@ body:
name: entry
instructions:
- '%eax = MOV32rm %rdi, 1, _, 0, _'
- 'CMP32ri8 %eax, 10'
- 'CMP32ri8 %eax, 10, implicit-def %eflags'
# CHECK: [[@LINE+1]]:18: expected a number after '%bb.'
- 'JG_1 %bb.nah'
- 'JG_1 %bb.nah, implicit %eflags'
- id: 1
name: yes
instructions:
- '%eax = MOV32r0'
- '%eax = MOV32r0 implicit-def %eflags'
- id: 2
name: nah
instructions:

View File

@ -31,7 +31,7 @@ body:
# CHECK: - '%rax = MOV64rm %rip, 1, _, @G, _'
- '%rax = MOV64rm %rip, 1, _, @G, _'
- '%eax = MOV32rm %rax, 1, _, 0, _'
- '%eax = INC32r %eax'
- '%eax = INC32r %eax, implicit-def %eflags'
- 'RETQ %eax'
...
---
@ -44,6 +44,6 @@ body:
# CHECK: - '%rax = MOV64rm %rip, 1, _, @0, _'
- '%rax = MOV64rm %rip, 1, _, @0, _'
- '%eax = MOV32rm %rax, 1, _, 0, _'
- '%eax = INC32r %eax'
- '%eax = INC32r %eax, implicit-def %eflags'
- 'RETQ %eax'
...

View File

@ -23,12 +23,12 @@ body:
name: entry
instructions:
- '%eax = MOV32rm %rdi, 1, _, 0, _'
- 'CMP32ri8 %eax, 10'
- 'CMP32ri8 %eax, 10, implicit-def %eflags'
# CHECK: [[@LINE+1]]:14: expected 32-bit integer (too large)
- 'JG_1 %bb.123456789123456'
- 'JG_1 %bb.123456789123456, implicit %eflags'
- id: 1
instructions:
- '%eax = MOV32r0'
- '%eax = MOV32r0 implicit-def %eflags'
- id: 2
instructions:
- 'RETQ %eax'

View File

@ -41,13 +41,13 @@ body:
- '%eax = MOV32rm %rdi, 1, _, 0, _'
# CHECK: - 'CMP32ri8 %eax, 10
# CHECK-NEXT: - 'JG_1 %bb.2.exit
- 'CMP32ri8 %eax, 10'
- 'JG_1 %bb.2.exit'
- 'CMP32ri8 %eax, 10, implicit-def %eflags'
- 'JG_1 %bb.2.exit, implicit %eflags'
# CHECK: name: less
- id: 1
name: less
instructions:
- '%eax = MOV32r0'
- '%eax = MOV32r0 implicit-def %eflags'
- id: 2
name: exit
instructions:
@ -64,11 +64,11 @@ body:
- '%eax = MOV32rm %rdi, 1, _, 0, _'
# CHECK: - 'CMP32ri8 %eax, 10
# CHECK-NEXT: - 'JG_1 %bb.2
- 'CMP32ri8 %eax, 10'
- 'JG_1 %bb.3'
- 'CMP32ri8 %eax, 10, implicit-def %eflags'
- 'JG_1 %bb.3, implicit %eflags'
- id: 1
instructions:
- '%eax = MOV32r0'
- '%eax = MOV32r0 implicit-def %eflags'
- id: 3
instructions:
- 'RETQ %eax'

View File

@ -18,8 +18,8 @@ body:
- id: 0
name: entry
instructions:
# CHECK: - IMUL32rri8
# CHECK: - MOV32rr
# CHECK-NEXT: - RETQ
- IMUL32rri8
- MOV32rr
- ' RETQ '
...

View File

@ -0,0 +1,40 @@
# RUN: not llc -march=x86-64 -start-after branch-folder -stop-after branch-folder -o /dev/null %s 2>&1 | FileCheck %s
# This test ensures that the MIR parser reports an error when an instruction
# is missing one of its implicit register operands.
--- |
define i32 @foo(i32* %p) {
entry:
%a = load i32, i32* %p
%0 = icmp sle i32 %a, 10
br i1 %0, label %less, label %exit
less:
ret i32 0
exit:
ret i32 %a
}
...
---
name: foo
body:
- id: 0
name: entry
instructions:
- '%eax = MOV32rm %rdi, 1, _, 0, _'
- 'CMP32ri8 %eax, 10, implicit-def %eflags'
# CHECK: [[@LINE+1]]:24: missing implicit register operand 'implicit %eflags'
- 'JG_1 %bb.2.exit'
- id: 1
name: less
instructions:
- '%eax = MOV32r0 implicit-def %eflags'
- id: 2
name: exit
instructions:
- 'RETQ %eax'
...

View File

@ -18,6 +18,6 @@ body:
instructions:
# CHECK: - '%eax = MOV32r0
# CHECK-NEXT: - 'RETQ %eax
- '%eax = MOV32r0'
- '%eax = MOV32r0 implicit-def %eflags'
- 'RETQ %eax'
...

View File

@ -24,7 +24,7 @@ body:
- id: 0
name: body
instructions:
- '%eax = IMUL32rri8 %edi, 11'
- '%eax = IMUL32rri8 %edi, 11, implicit-def %eflags'
- 'RETQ %eax'
...
---
@ -36,8 +36,8 @@ body:
instructions:
# CHECK: - 'PUSH64r %rax
# CHECK-NEXT: - 'CALL64pcrel32 @compute, csr_64, implicit %rsp, implicit %edi, implicit-def %rsp, implicit-def %eax'
- 'PUSH64r %rax'
- 'PUSH64r %rax, implicit-def %rsp, implicit %rsp'
- 'CALL64pcrel32 @compute, csr_64, implicit %rsp, implicit %edi, implicit-def %rsp, implicit-def %eax'
- '%rdx = POP64r'
- '%rdx = POP64r implicit-def %rsp, implicit %rsp'
- 'RETQ %eax'
...

View File

@ -26,12 +26,12 @@ body:
name: entry
instructions:
- '%eax = MOV32rm %rdi, 1, _, 0, _'
- 'CMP32ri8 %eax, 10'
- 'CMP32ri8 %eax, 10, implicit-def %eflags'
# CHECK: [[@LINE+1]]:14: use of undefined machine basic block #4
- 'JG_1 %bb.4'
- 'JG_1 %bb.4, implicit %eflags'
- id: 1
instructions:
- '%eax = MOV32r0'
- '%eax = MOV32r0 implicit-def %eflags'
- id: 2
instructions:
- 'RETQ %eax'

View File

@ -25,13 +25,13 @@ body:
name: entry
instructions:
- '%eax = MOV32rm %rdi, 1, _, 0, _'
- 'CMP32ri8 %eax, 10'
- 'CMP32ri8 %eax, 10, implicit-def %eflags'
# CHECK: [[@LINE+1]]:14: the name of machine basic block #2 isn't 'hit'
- 'JG_1 %bb.2.hit'
- 'JG_1 %bb.2.hit, implicit %eflags'
- id: 1
name: less
instructions:
- '%eax = MOV32r0'
- '%eax = MOV32r0 implicit-def %eflags'
- id: 2
name: exit
instructions: