[TableGen] Support to set operand custom flag by CustomConstraints.

Summary: [TableGen] Support to set operand custom flag by CustomConstraints.

Test Plan: llvm/test/TableGen/ConstraintChecking8.td

Reviewers: zhoujing

Reviewed By: zhoujing

Differential Revision: http://www.tpt.com/D636
This commit is contained in:
yanming 2023-07-05 11:55:26 +08:00
parent e3ecedc1a5
commit 3685ef10e0
8 changed files with 98 additions and 59 deletions

View File

@ -33,16 +33,10 @@ namespace MCOI {
/// This allows for a maximum of 3 constraints.
enum OperandConstraint {
TIED_TO = 0, // Must be allocated the same register as specified value.
CUSTOM, // Used by target custom flag
EARLY_CLOBBER // If present, operand is an early clobber register.
};
// Define a macro to produce each constraint value.
#define MCOI_TIED_TO(op) \
((1 << MCOI::TIED_TO) | ((op) << (4 + MCOI::TIED_TO * 4)))
#define MCOI_EARLY_CLOBBER \
(1 << MCOI::EARLY_CLOBBER)
/// These are flags set on operands, but should be considered
/// private, all access should go through the MCOperandInfo accessors.
/// See the accessors for a description of what these are.

View File

@ -626,6 +626,8 @@ class Instruction : InstructionEncoding {
string Constraints = ""; // OperandConstraint, e.g. $src = $dst.
string CustomConstraints = ""; // OperandCustomConstraint, e.g. $dst = 3.
/// DisableEncoding - List of operand names (e.g. "$op1,$op2") that should not
/// be encoded into the output machineinstr.
string DisableEncoding = "";

View File

@ -15,12 +15,13 @@ def R0 : TestReg<"R0", 0>;
def R1 : TestReg<"R1", 1>;
def Reg : RegisterClass<"TestTarget", [i32], 32, (sequence "R%d", 0, 1)>;
class TestInstructionWithConstraints<string cstr> : Encoding {
class TestInstructionWithConstraints<string cstr, string ccstr = ""> : Encoding {
dag OutOperandList = (outs Reg:$dest1, Reg:$dest2);
dag InOperandList = (ins Reg:$src1, Reg:$src2);
string AsmString = "mnemonic $dest1, $dest2, $src1, $src2";
string AsmVariantName = "";
let Constraints = cstr;
let CustomConstraints = ccstr;
field bits<1> dest1;
field bits<1> dest2;
field bits<1> src1;

View File

@ -2,5 +2,5 @@
include "ConstraintChecking.inc"
// CHECK: [[FILE]]:[[@LINE+1]]:5: error: Operand '$src1' of 'Foo' cannot have multiple constraints!
// CHECK: [[FILE]]:[[@LINE+1]]:5: error: Operand '$src1' of 'Foo' cannot have multiple tied-to constraints!
def Foo : TestInstructionWithConstraints<"$dest1 = $src1, $dest2 = $src1">;

View File

@ -0,0 +1,6 @@
// RUN: not llvm-tblgen -gen-asm-writer -I %p -I %p/../../include %s 2>&1 | FileCheck %s -DFILE=%s
include "ConstraintChecking.inc"
// CHECK: [[FILE]]:[[@LINE+1]]:5: error: Illegal format custom number in range[0, 15] for custom constraint: '$dest1 = 16'
def Foo : TestInstructionWithConstraints<"", "$dest1 = 16">;

View File

@ -306,12 +306,11 @@ static void ParseConstraint(StringRef CStr, CGIOperandList &Ops,
std::pair<unsigned,unsigned> Op = Ops.ParseOperandName(Name, false);
// Build the string for the operand
if (!Ops[Op.first].Constraints[Op.second].isNone())
if (Ops[Op.first].Constraints[Op.second].isEarlyClobber())
PrintFatalError(
Rec->getLoc(), "Operand '" + Name + "' of '" + Rec->getName() +
"' cannot have multiple constraints!");
Ops[Op.first].Constraints[Op.second] =
CGIOperandList::ConstraintInfo::getEarlyClobber();
"' cannot have multiple @earlyclobber constraints!");
Ops[Op.first].Constraints[Op.second].setConstraint(MCOI::EARLY_CLOBBER);
return;
}
@ -361,28 +360,66 @@ static void ParseConstraint(StringRef CStr, CGIOperandList &Ops,
// The constraint has to go on the operand with higher index, i.e.
// the source one. Check there isn't another constraint there
// already.
if (!Ops[SrcOp.first].Constraints[SrcOp.second].isNone())
if (Ops[SrcOp.first].Constraints[SrcOp.second].isTied())
PrintFatalError(
Rec->getLoc(), "Operand '" + SrcOpName + "' of '" + Rec->getName() +
"' cannot have multiple constraints!");
"' cannot have multiple tied-to constraints!");
unsigned DestFlatOpNo = Ops.getFlattenedOperandNumber(DestOp);
auto NewConstraint = CGIOperandList::ConstraintInfo::getTied(DestFlatOpNo);
// Check that the earlier operand is not the target of another tie
// before making it the target of this one.
for (const CGIOperandList::OperandInfo &Op : Ops) {
for (unsigned i = 0; i < Op.MINumOperands; i++)
if (Op.Constraints[i] == NewConstraint)
if (Op.Constraints[i].getConstraint(MCOI::TIED_TO) == (int)DestFlatOpNo)
PrintFatalError(
Rec->getLoc(), "Operand '" + DestOpName + "' of '" + Rec->getName() +
"' cannot have multiple operands tied to it!");
}
Ops[SrcOp.first].Constraints[SrcOp.second] = NewConstraint;
Ops[SrcOp.first].Constraints[SrcOp.second].setConstraint(MCOI::TIED_TO,
DestFlatOpNo);
}
static void ParseConstraints(StringRef CStr, CGIOperandList &Ops, Record *Rec) {
static void ParseCustomConstraint(StringRef CStr, CGIOperandList &Ops,
Record *Rec) {
// Only custom constraint is "CUSTOM" for now.
StringRef::size_type pos = CStr.find_first_of('=');
if (pos == StringRef::npos)
PrintFatalError(Rec->getLoc(), "Unrecognized custom constraint '" + CStr +
"' in '" + Rec->getName() + "'");
StringRef::size_type start = CStr.find_first_not_of(" \t");
// CUSTOM: $dst = number
StringRef::size_type wpos = CStr.find_first_of(" \t", start);
if (wpos == StringRef::npos || wpos > pos)
PrintFatalError(Rec->getLoc(), "Illegal format for custom constraint in '" +
Rec->getName() + "': '" + CStr + "'");
StringRef Name = CStr.substr(start, wpos - start);
std::pair<unsigned, unsigned> Op = Ops.ParseOperandName(Name, false);
wpos = CStr.find_first_not_of(" \t", pos + 1);
if (wpos == StringRef::npos)
PrintFatalError(Rec->getLoc(),
"Illegal format for custom constraint: '" + CStr + "'");
unsigned CustomNum;
if (CStr.substr(wpos).consumeInteger(0, CustomNum) || CustomNum > 15)
PrintFatalError(Rec->getLoc(), "Illegal format custom number in range[0, "
"15] for custom constraint: '" +
CStr + "'");
// Build the string for the operand
if (Ops[Op.first].Constraints[Op.second].isCustom())
PrintFatalError(Rec->getLoc(),
"Operand '" + Name + "' of '" + Rec->getName() +
"' cannot have multiple custom constraints!");
Ops[Op.first].Constraints[Op.second].setConstraint(MCOI::CUSTOM, CustomNum);
}
static void ParseConstraints(StringRef CStr, CGIOperandList &Ops, Record *Rec,
bool isCustom = false) {
if (CStr.empty()) return;
StringRef delims(",");
@ -394,7 +431,10 @@ static void ParseConstraints(StringRef CStr, CGIOperandList &Ops, Record *Rec) {
if (eidx == StringRef::npos)
eidx = CStr.size();
ParseConstraint(CStr.substr(bidx, eidx - bidx), Ops, Rec);
if (isCustom)
ParseCustomConstraint(CStr.substr(bidx, eidx - bidx), Ops, Rec);
else
ParseConstraint(CStr.substr(bidx, eidx - bidx), Ops, Rec);
bidx = CStr.find_first_not_of(delims, eidx);
}
}
@ -485,6 +525,9 @@ CodeGenInstruction::CodeGenInstruction(Record *R)
// Parse Constraints.
ParseConstraints(R->getValueAsString("Constraints"), Operands, R);
// Parse CustomConstraints.
ParseConstraints(R->getValueAsString("CustomConstraints"), Operands, R, true);
// Parse the DisableEncoding field.
Operands.ProcessDisableEncoding(
R->getValueAsString("DisableEncoding"));

View File

@ -17,6 +17,7 @@
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/MachineValueType.h"
#include "llvm/MC/MCInstrDesc.h"
#include <cassert>
#include <string>
#include <utility>
@ -32,44 +33,43 @@ template <typename T> class ArrayRef;
class CGIOperandList {
public:
class ConstraintInfo {
enum { None, EarlyClobber, Tied } Kind = None;
unsigned OtherTiedOperand = 0;
uint16_t Constraints = 0;
public:
ConstraintInfo() = default;
static ConstraintInfo getEarlyClobber() {
ConstraintInfo I;
I.Kind = EarlyClobber;
I.OtherTiedOperand = 0;
return I;
uint16_t getConstraintsValue() const { return Constraints; }
/// Returns the value of the specified operand constraint if
/// it is present. Returns -1 if it is not present.
int getConstraint(MCOI::OperandConstraint Constraint) const {
if (Constraints & (1 << Constraint)) {
unsigned ValuePos = 4 + Constraint * 4;
return (int)(Constraints >> ValuePos) & 0x0f;
}
return -1;
}
static ConstraintInfo getTied(unsigned Op) {
ConstraintInfo I;
I.Kind = Tied;
I.OtherTiedOperand = Op;
return I;
void setConstraint(MCOI::OperandConstraint Constraint,
unsigned Value = 0) {
assert(Value <= 15 && "Out of range [0, 15] constraint value");
assert(getConstraint(Constraint) &&
"Cannot have multiple same constraints");
// Produce each constraint value.
Constraints |= ((1 << Constraint) | (Value << (4 + Constraint * 4)));
}
bool isNone() const { return Kind == None; }
bool isEarlyClobber() const { return Kind == EarlyClobber; }
bool isTied() const { return Kind == Tied; }
bool isEarlyClobber() const {
return getConstraint(MCOI::EARLY_CLOBBER) != -1;
}
bool isCustom() const { return getConstraint(MCOI::CUSTOM) != -1; }
bool isTied() const { return getConstraint(MCOI::TIED_TO) != -1; }
unsigned getTiedOperand() const {
assert(isTied());
return OtherTiedOperand;
}
bool operator==(const ConstraintInfo &RHS) const {
if (Kind != RHS.Kind)
return false;
if (Kind == Tied && OtherTiedOperand != RHS.OtherTiedOperand)
return false;
return true;
}
bool operator!=(const ConstraintInfo &RHS) const {
return !(*this == RHS);
int TiedOperand = getConstraint(MCOI::TIED_TO);
assert(TiedOperand != -1);
return (unsigned)TiedOperand;
}
};
@ -131,7 +131,9 @@ template <typename T> class ArrayRef;
int getTiedRegister() const {
for (unsigned j = 0, e = Constraints.size(); j != e; ++j) {
const CGIOperandList::ConstraintInfo &CI = Constraints[j];
if (CI.isTied()) return CI.getTiedOperand();
int TiedOperand = CI.getConstraint(MCOI::TIED_TO);
if (TiedOperand != -1)
return TiedOperand;
}
return -1;
}

View File

@ -197,16 +197,7 @@ InstrInfoEmitter::GetOperandInfo(const CodeGenInstruction &Inst) {
// Fill in constraint info.
Res += ", ";
const CGIOperandList::ConstraintInfo &Constraint =
Op.Constraints[j];
if (Constraint.isNone())
Res += "0";
else if (Constraint.isEarlyClobber())
Res += "MCOI_EARLY_CLOBBER";
else {
assert(Constraint.isTied());
Res += "MCOI_TIED_TO(" + utostr(Constraint.getTiedOperand()) + ")";
}
Res += utostr(Op.Constraints[j].getConstraintsValue());
Result.push_back(Res);
}