forked from OSchip/llvm-project
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:
parent
9622cdf4b9
commit
36962cd925
|
@ -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");
|
||||
|
|
|
@ -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'
|
||||
...
|
|
@ -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'
|
||||
...
|
|
@ -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:
|
||||
|
|
|
@ -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'
|
||||
...
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -18,8 +18,8 @@ body:
|
|||
- id: 0
|
||||
name: entry
|
||||
instructions:
|
||||
# CHECK: - IMUL32rri8
|
||||
# CHECK: - MOV32rr
|
||||
# CHECK-NEXT: - RETQ
|
||||
- IMUL32rri8
|
||||
- MOV32rr
|
||||
- ' RETQ '
|
||||
...
|
||||
|
|
|
@ -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'
|
||||
...
|
|
@ -18,6 +18,6 @@ body:
|
|||
instructions:
|
||||
# CHECK: - '%eax = MOV32r0
|
||||
# CHECK-NEXT: - 'RETQ %eax
|
||||
- '%eax = MOV32r0'
|
||||
- '%eax = MOV32r0 implicit-def %eflags'
|
||||
- 'RETQ %eax'
|
||||
...
|
||||
|
|
|
@ -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'
|
||||
...
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue