Index: lib/CodeGen/MIRParser/MIParser.cpp =================================================================== --- lib/CodeGen/MIRParser/MIParser.cpp +++ lib/CodeGen/MIRParser/MIParser.cpp @@ -29,6 +29,18 @@ 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; @@ -88,6 +100,9 @@ bool parseInstruction(unsigned &OpCode); + bool verifyImplicitOperands(ArrayRef Operands, + const MCInstrDesc &MCID); + void initNames2Regs(); /// Try to convert a register name to a register number. Return true if the @@ -137,11 +152,12 @@ // Parse any register operands before '=' // TODO: Allow parsing of multiple operands before '=' MachineOperand MO = MachineOperand::CreateImm(0); - SmallVector Operands; + SmallVector Operands; if (Token.isRegister() || Token.isRegisterFlag()) { + auto Loc = Token.location(); if (parseRegisterOperand(MO, /*IsDef=*/true)) return nullptr; - Operands.push_back(MO); + Operands.push_back(MachineOperandWithLocation(MO, Loc, Token.location())); if (Token.isNot(MIToken::equal)) { error("expected '='"); return nullptr; @@ -157,9 +173,10 @@ // Parse the remaining machine operands. while (Token.isNot(MIToken::Eof)) { + auto Loc = Token.location(); if (parseMachineOperand(MO)) return nullptr; - Operands.push_back(MO); + Operands.push_back(MachineOperandWithLocation(MO, Loc, Token.location())); if (Token.is(MIToken::Eof)) break; if (Token.isNot(MIToken::comma)) { @@ -170,15 +187,80 @@ } const auto &MCID = MF.getSubtarget().getInstrInfo()->get(OpCode); + if (!MCID.isVariadic()) { + if (verifyImplicitOperands(Operands, MCID)) + return nullptr; + } // TODO: Check for extraneous machine operands. - // TODO: Check that this instruction has the implicit register operands. auto *MI = MF.CreateMachineInstr(MCID, DebugLoc(), /*NoImplicit=*/true); for (const auto &Operand : Operands) - MI->addOperand(MF, Operand); + MI->addOperand(MF, Operand.Operand); return MI; } +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 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 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"); Index: test/CodeGen/MIR/X86/expected-different-implicit-operand.mir =================================================================== --- /dev/null +++ test/CodeGen/MIR/X86/expected-different-implicit-operand.mir @@ -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' +... Index: test/CodeGen/MIR/X86/expected-different-implicit-register-flag.mir =================================================================== --- /dev/null +++ test/CodeGen/MIR/X86/expected-different-implicit-register-flag.mir @@ -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' +... Index: test/CodeGen/MIR/X86/expected-number-after-bb.mir =================================================================== --- test/CodeGen/MIR/X86/expected-number-after-bb.mir +++ test/CodeGen/MIR/X86/expected-number-after-bb.mir @@ -23,13 +23,13 @@ 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: Index: test/CodeGen/MIR/X86/global-value-operands.mir =================================================================== --- test/CodeGen/MIR/X86/global-value-operands.mir +++ test/CodeGen/MIR/X86/global-value-operands.mir @@ -31,7 +31,7 @@ # 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 @@ # 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' ... Index: test/CodeGen/MIR/X86/large-index-number-error.mir =================================================================== --- test/CodeGen/MIR/X86/large-index-number-error.mir +++ test/CodeGen/MIR/X86/large-index-number-error.mir @@ -23,12 +23,12 @@ 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' Index: test/CodeGen/MIR/X86/machine-basic-block-operands.mir =================================================================== --- test/CodeGen/MIR/X86/machine-basic-block-operands.mir +++ test/CodeGen/MIR/X86/machine-basic-block-operands.mir @@ -41,13 +41,13 @@ - '%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 @@ - '%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' Index: test/CodeGen/MIR/X86/machine-instructions.mir =================================================================== --- test/CodeGen/MIR/X86/machine-instructions.mir +++ test/CodeGen/MIR/X86/machine-instructions.mir @@ -18,8 +18,8 @@ - id: 0 name: entry instructions: - # CHECK: - IMUL32rri8 + # CHECK: - MOV32rr # CHECK-NEXT: - RETQ - - IMUL32rri8 + - MOV32rr - ' RETQ ' ... Index: test/CodeGen/MIR/X86/missing-implicit-operand.mir =================================================================== --- /dev/null +++ test/CodeGen/MIR/X86/missing-implicit-operand.mir @@ -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' +... Index: test/CodeGen/MIR/X86/named-registers.mir =================================================================== --- test/CodeGen/MIR/X86/named-registers.mir +++ test/CodeGen/MIR/X86/named-registers.mir @@ -18,6 +18,6 @@ instructions: # CHECK: - '%eax = MOV32r0 # CHECK-NEXT: - 'RETQ %eax - - '%eax = MOV32r0' + - '%eax = MOV32r0 implicit-def %eflags' - 'RETQ %eax' ... Index: test/CodeGen/MIR/X86/register-mask-operands.mir =================================================================== --- test/CodeGen/MIR/X86/register-mask-operands.mir +++ test/CodeGen/MIR/X86/register-mask-operands.mir @@ -24,7 +24,7 @@ - id: 0 name: body instructions: - - '%eax = IMUL32rri8 %edi, 11' + - '%eax = IMUL32rri8 %edi, 11, implicit-def %eflags' - 'RETQ %eax' ... --- @@ -36,8 +36,8 @@ 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' ... Index: test/CodeGen/MIR/X86/unknown-machine-basic-block.mir =================================================================== --- test/CodeGen/MIR/X86/unknown-machine-basic-block.mir +++ test/CodeGen/MIR/X86/unknown-machine-basic-block.mir @@ -26,12 +26,12 @@ 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' Index: test/CodeGen/MIR/X86/unknown-named-machine-basic-block.mir =================================================================== --- test/CodeGen/MIR/X86/unknown-named-machine-basic-block.mir +++ test/CodeGen/MIR/X86/unknown-named-machine-basic-block.mir @@ -25,13 +25,13 @@ 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: