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;
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
// 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();
// 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))
}
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;
}
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");
--- /dev/null
+# 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'
+...
--- /dev/null
+# 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'
+...
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:
# 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'
...
---
# 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'
...
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'
- '%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:
- '%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'
- id: 0
name: entry
instructions:
- # CHECK: - IMUL32rri8
+ # CHECK: - MOV32rr
# CHECK-NEXT: - RETQ
- - IMUL32rri8
+ - MOV32rr
- ' RETQ '
...
--- /dev/null
+# 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'
+...
instructions:
# CHECK: - '%eax = MOV32r0
# CHECK-NEXT: - 'RETQ %eax
- - '%eax = MOV32r0'
+ - '%eax = MOV32r0 implicit-def %eflags'
- 'RETQ %eax'
...
- id: 0
name: body
instructions:
- - '%eax = IMUL32rri8 %edi, 11'
+ - '%eax = IMUL32rri8 %edi, 11, implicit-def %eflags'
- 'RETQ %eax'
...
---
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'
...
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'
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: