Index: test/TableGen/validate-operand.td =================================================================== --- test/TableGen/validate-operand.td +++ test/TableGen/validate-operand.td @@ -0,0 +1,35 @@ +// RUN: llvm-tblgen -gen-mc-inst-validator -I %p/../../include %s | FileCheck %s + +// Check that validateMCOperand has the expected code in it. + +include "llvm/Target/Target.td" + +def archInstrInfo : InstrInfo { } + +def arch : Target { + let InstructionSet = archInstrInfo; +} + +def Reg : Register<"reg">; +def RegClass : RegisterClass<"foo", [i32], 0, (add Reg)>; + +def OpA : Operand { + let MCOperandPredicate = [{return MCOp.isImm() && MCOp.getImm() != 0;}]; +} + +def OpB : Operand; + +def InstA : Instruction { + let Size = 1; + let OutOperandList = (outs OpA:$a); + let InOperandList = (ins OpB:$b); + field bits<8> Inst; + field bits<8> SoftFail = 0; + let Namespace = "MyNamespace"; +} + +// CHECK: #ifdef VALIDATE_MC_OPERAND +// CHECK: { 0, -1, }, +// CHECK: case 0: +// CHECK: return MCOp.isImm() && MCOp.getImm() != 0; +// CHECK: #endif //VALIDATE_MC_OPERAND Index: utils/TableGen/CMakeLists.txt =================================================================== --- utils/TableGen/CMakeLists.txt +++ utils/TableGen/CMakeLists.txt @@ -48,5 +48,6 @@ X86RecognizableInstr.cpp WebAssemblyDisassemblerEmitter.cpp CTagsEmitter.cpp + MCInstValidatorEmitter.cpp ) set_target_properties(llvm-tblgen PROPERTIES FOLDER "Tablegenning") Index: utils/TableGen/MCInstValidatorEmitter.cpp =================================================================== --- utils/TableGen/MCInstValidatorEmitter.cpp +++ utils/TableGen/MCInstValidatorEmitter.cpp @@ -0,0 +1,147 @@ +//===- MCInstValidatorEmitter.cpp - Generates an MCInst validator - C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This tablegen backend is responsible for emitting utilities used to to check +// that an MCInst and its operands are valid. +// +//===----------------------------------------------------------------------===// + +#include "CodeGenDAGPatterns.h" +#include "CodeGenInstruction.h" +#include "TableGenBackends.h" +#include "llvm/ADT/CachedHashString.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/TableGenBackend.h" + +using namespace llvm; + +namespace { + +class MCInstValidatorEmitter { + const RecordKeeper &Records; + CodeGenDAGPatterns CDP; + +public: + MCInstValidatorEmitter(RecordKeeper &R) : Records(R), CDP(R) {} + + // run - Output the validator. + void run(raw_ostream &OS); + + // Outputs a function that validates an MCOperand. + void emitMCOperandValidator(raw_ostream &OS); +}; + +} // end anonymous namespace + +void MCInstValidatorEmitter::emitMCOperandValidator(raw_ostream &OS) { + const CodeGenTarget &Target = CDP.getTargetInfo(); + const StringRef Namespace = Target.getName(); + const std::vector Operands = + Records.getAllDerivedDefinitions("Operand"); + const ArrayRef NumberedInstructions = + Target.getInstructionsByEnumValue(); + + // All unique values of MCOperandPredicate, for de-duplication. + SetVector MCOperandPredicates; + // The ID of the predicate for each operand with an MCOperandPredicate. + std::map OperandToMCOperandPredicateID; + for (const Record *Op : Operands) { + if (Op->isValueUnset("MCOperandPredicate")) + continue; + const Init *MCOpPred = Op->getValueInit("MCOperandPredicate"); + if (const CodeInit *SI = dyn_cast(MCOpPred)) { + unsigned MCOperandPredicateID = (unsigned)MCOperandPredicates.size(); + MCOperandPredicates.insert(CachedHashString(SI->getValue())); + OperandToMCOperandPredicateID.emplace(Op, MCOperandPredicateID); + } else + llvm_unreachable("Unexpected MCOperandPredicate field!"); + } + + OS << "#ifdef VALIDATE_MC_OPERAND\n"; + OS << "#undef VALIDATE_MC_OPERAND\n"; + OS << "namespace llvm {\n"; + OS << "namespace " << Namespace << " {\n"; + OS << "LLVM_READONLY\n"; + OS << "bool validateMCOperand(unsigned Opcode, unsigned OpIdx, const " + "MCOperand &MCOp) {\n"; + OS << " static const std::initializer_list MCOperandPredicateIDTable[] " + "= {\n"; + for (const CodeGenInstruction *Inst : NumberedInstructions) { + OS << " { "; + for (const auto &Op : Inst->Operands) { + // Handle aggregate operands and normal operands the same way by expanding + // either case into a list of operands for this op. + std::vector OperandList; + DagInit *MIOI = Op.MIOperandInfo; + if (!MIOI || MIOI->getNumArgs() == 0) { + // Single, anonymous, operand. + OperandList.push_back(Op); + } else { + for (unsigned j = 0, e = Op.MINumOperands; j != e; ++j) { + OperandList.push_back(Op); + + auto *OpR = cast(MIOI->getArg(j))->getDef(); + OperandList.back().Rec = OpR; + } + } + for (unsigned j = 0, e = OperandList.size(); j != e; ++j) { + const Record *OpR = OperandList[j].Rec; + const auto It = OperandToMCOperandPredicateID.find(OpR); + if (It == OperandToMCOperandPredicateID.end()) { + // No predicate specified. + OS << "-1"; + } else + OS << It->second; + OS << ", "; + } + } + OS << "},\n"; + } + OS << " };\n"; + OS << " const int MCOperandPredicateID = " + "MCOperandPredicateIDTable[Opcode].begin()[OpIdx];\n"; + OS << " if (MCOperandPredicateID == -1) {\n"; + OS << " // No predicate specified, so assume any value is ok.\n"; + OS << " return true;\n"; + OS << " }\n"; + OS << " switch (MCOperandPredicateID) {\n"; + OS << " default:\n"; + OS << " llvm_unreachable(\"Unhandled MCOperandPredicateID\");\n"; + for (size_t MCOperandPredicateID = 0; + MCOperandPredicateID < MCOperandPredicates.size(); + MCOperandPredicateID++) { + OS << " case " << MCOperandPredicateID << ":\n"; + // Put the MCOperandPredicate in a lambda to slightly sandbox the code and + // to try to force the user to have to have a return statement. + OS << " return ([](const MCOperand &MCOp) -> bool {\n"; + OS << MCOperandPredicates[MCOperandPredicateID].val(); + OS << "\n })(MCOp);\n"; + } + OS << " }\n"; + OS << " llvm_unreachable(\"Unexpected end of function (Is " + "MCOperandPredicate missing a return?)\");\n"; + OS << "}\n"; + OS << "} // end namespace " << Namespace << "\n"; + OS << "} // end namespace llvm\n"; + OS << "#endif //VALIDATE_MC_OPERAND\n\n"; +} + +void MCInstValidatorEmitter::run(raw_ostream &OS) { + emitSourceFileHeader("Validation for MCInsts", OS); + emitMCOperandValidator(OS); +} + +namespace llvm { + +void EmitMCInstValidator(RecordKeeper &RK, raw_ostream &OS) { + MCInstValidatorEmitter(RK).run(OS); +} + +} // namespace llvm Index: utils/TableGen/TableGen.cpp =================================================================== --- utils/TableGen/TableGen.cpp +++ utils/TableGen/TableGen.cpp @@ -53,6 +53,7 @@ GenX86FoldTables, GenRegisterBank, GenExegesis, + GenMCInstValidator, }; namespace llvm { @@ -125,7 +126,9 @@ clEnumValN(GenRegisterBank, "gen-register-bank", "Generate registers bank descriptions"), clEnumValN(GenExegesis, "gen-exegesis", - "Generate llvm-exegesis tables"))); + "Generate llvm-exegesis tables"), + clEnumValN(GenMCInstValidator, "gen-mc-inst-validator", + "Generate validator for MCInsts"))); cl::OptionCategory PrintEnumsCat("Options for -print-enums"); cl::opt @@ -247,6 +250,9 @@ case GenExegesis: EmitExegesis(Records, OS); break; + case GenMCInstValidator: + EmitMCInstValidator(Records, OS); + break; } return false; Index: utils/TableGen/TableGenBackends.h =================================================================== --- utils/TableGen/TableGenBackends.h +++ utils/TableGen/TableGenBackends.h @@ -89,6 +89,7 @@ void EmitX86FoldTables(RecordKeeper &RK, raw_ostream &OS); void EmitRegisterBank(RecordKeeper &RK, raw_ostream &OS); void EmitExegesis(RecordKeeper &RK, raw_ostream &OS); +void EmitMCInstValidator(RecordKeeper &RK, raw_ostream &OS); } // End llvm namespace