Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions llvm/test/TableGen/Common/RegClassByHwModeCommon.td
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
include "llvm/Target/Target.td"

class MyReg<string n> : Register<n> {
let Namespace = "MyTarget";
}

class MyClass<int size, list<ValueType> types, dag registers>
: RegisterClass<"MyTarget", types, size, registers> {
let Size = size;
}

def X0 : MyReg<"x0">;
def X1 : MyReg<"x1">;
def X2 : MyReg<"x2">;
def X3 : MyReg<"x3">;
def X4 : MyReg<"x4">;
def X5 : MyReg<"x5">;
def X6 : MyReg<"x6">;
def XRegs : RegisterClass<"MyTarget", [i64], 64, (add X0, X1, X2, X3, X4, X5, X6)>;
def Y0 : MyReg<"y0">;
def Y1 : MyReg<"y1">;
def Y2 : MyReg<"y2">;
def Y3 : MyReg<"y3">;
def Y4 : MyReg<"y4">;
def Y5 : MyReg<"y5">;
def Y6 : MyReg<"y6">;
def YRegs : RegisterClass<"MyTarget", [i64], 64, (add Y0, Y1, Y2, Y3, Y4, Y5, Y6)>;

class TestInstruction : Instruction {
let Size = 2;
let Namespace = "MyTarget";
let hasSideEffects = false;
let hasExtraSrcRegAllocReq = false;
let hasExtraDefRegAllocReq = false;

field bits<16> Inst;
bits<3> dst;
bits<3> src;
bits<3> opcode;

let Inst{2-0} = dst;
let Inst{5-3} = src;
let Inst{7-5} = opcode;
}
64 changes: 7 additions & 57 deletions llvm/test/TableGen/RegClassByHwMode.td
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// RUN: llvm-tblgen -gen-instr-info -I %p/../../include %s -o - | FileCheck -check-prefix=INSTRINFO %s
// RUN: llvm-tblgen -gen-asm-matcher -I %p/../../include %s -o - | FileCheck -check-prefix=ASMMATCHER %s
// RUN: llvm-tblgen -gen-disassembler -I %p/../../include %s -o - | FileCheck -check-prefix=DISASM %s
// RUN: llvm-tblgen -gen-dag-isel -I %p/../../include %s -o - | FileCheck -check-prefix=ISEL-SDAG %s
// RUN: llvm-tblgen -gen-global-isel -I %p/../../include %s -o - | FileCheck -check-prefix=ISEL-GISEL %s
// RUN: llvm-tblgen -gen-instr-info -I %S -I %p/../../include %s -o - | FileCheck -check-prefix=INSTRINFO %s
// RUN: llvm-tblgen -gen-asm-matcher -I %S -I %p/../../include %s -o - | FileCheck -check-prefix=ASMMATCHER %s
// RUN: llvm-tblgen -gen-disassembler -I %S -I %p/../../include %s -o - | FileCheck -check-prefix=DISASM %s
// RUN: llvm-tblgen -gen-dag-isel -I %S -I %p/../../include %s -o - | FileCheck -check-prefix=ISEL-SDAG %s
// RUN: llvm-tblgen -gen-global-isel -I %S -I %p/../../include %s -o - | FileCheck -check-prefix=ISEL-GISEL %s

include "llvm/Target/Target.td"
include "Common/RegClassByHwModeCommon.td"

// INSTRINFO: #ifdef GET_INSTRINFO_ENUM
// INSTRINFO-NEXT: #undef GET_INSTRINFO_ENUM
Expand Down Expand Up @@ -302,8 +302,6 @@ include "llvm/Target/Target.td"
// ISEL-GISEL-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/GIMT_Encode2(MyTarget::MY_STORE),
// ISEL-GISEL-NEXT: GIR_RootConstrainSelectedInstOperands,



def HasAlignedRegisters : Predicate<"Subtarget->hasAlignedRegisters()">;
def HasUnalignedRegisters : Predicate<"Subtarget->hasUnalignedRegisters()">;
def IsPtr64 : Predicate<"Subtarget->isPtr64()">;
Expand All @@ -317,34 +315,6 @@ def EvenMode : HwMode<[HasAlignedRegisters]>;
def OddMode : HwMode<[HasUnalignedRegisters]>;
def Ptr64 : HwMode<[IsPtr64]>;

class MyReg<string n>
: Register<n> {
let Namespace = "MyTarget";
}

class MyClass<int size, list<ValueType> types, dag registers>
: RegisterClass<"MyTarget", types, size, registers> {
let Size = size;
}

def X0 : MyReg<"x0">;
def X1 : MyReg<"x1">;
def X2 : MyReg<"x2">;
def X3 : MyReg<"x3">;
def X4 : MyReg<"x4">;
def X5 : MyReg<"x5">;
def X6 : MyReg<"x6">;

def Y0 : MyReg<"y0">;
def Y1 : MyReg<"y1">;
def Y2 : MyReg<"y2">;
def Y3 : MyReg<"y3">;
def Y4 : MyReg<"y4">;
def Y5 : MyReg<"y5">;
def Y6 : MyReg<"y6">;



def P0_32 : MyReg<"p0">;
def P1_32 : MyReg<"p1">;
def P2_32 : MyReg<"p2">;
Expand All @@ -356,15 +326,12 @@ def P2_64 : MyReg<"p2_64">;
def P3_64 : MyReg<"p3_64">;



def XRegs : RegisterClass<"MyTarget", [i64], 64, (add X0, X1, X2, X3, X4, X5, X6)>;
def XRegs_Odd : RegisterClass<"MyTarget", [i64], 64, (add X1, X3, X5)>;
def XRegs_Even : RegisterClass<"MyTarget", [i64], 64, (add X0, X2, X4, X6)>;

def XRegs_EvenIfRequired : RegClassByHwMode<[DefaultMode, EvenMode, OddMode],
[XRegs, XRegs_Even, XRegs_Odd]>;
[XRegs, XRegs_Even, XRegs_Odd]>;

def YRegs : RegisterClass<"MyTarget", [i64], 64, (add Y0, Y1, Y2, Y3, Y4, Y5, Y6)>;
def YRegs_Even : RegisterClass<"MyTarget", [i64], 64, (add Y0, Y2, Y4, Y6)>;

def YRegs_EvenIfRequired : RegClassByHwMode<[DefaultMode, EvenMode],
Expand All @@ -385,23 +352,6 @@ def CustomDecodeYEvenIfRequired : RegisterOperand<YRegs_EvenIfRequired> {
let DecoderMethod = "YEvenIfRequiredCustomDecoder";
}

class TestInstruction : Instruction {
let Size = 2;
let Namespace = "MyTarget";
let hasSideEffects = false;
let hasExtraSrcRegAllocReq = false;
let hasExtraDefRegAllocReq = false;

field bits<16> Inst;
bits<3> dst;
bits<3> src;
bits<3> opcode;

let Inst{2-0} = dst;
let Inst{5-3} = src;
let Inst{7-5} = opcode;
}

def SpecialOperand : RegisterOperand<XRegs_EvenIfRequired>;

def MY_MOV : TestInstruction {
Expand Down
88 changes: 88 additions & 0 deletions llvm/test/TableGen/RegClassByHwModeErrors.td
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// RUN: rm -rf %t && split-file %s %t
// RUN: not llvm-tblgen --gen-asm-matcher -I %p/../../include -I %t -I %S \
// RUN: %t/inst-alias-bad-reg.td -o /dev/null 2>&1 | FileCheck %t/inst-alias-bad-reg.td --implicit-check-not="error:"
// RUN: not llvm-tblgen --gen-asm-matcher -I %p/../../include -I %t -I %S \
// RUN: %t/inst-alias-static-predicates.td -o /dev/null 2>&1 | FileCheck %t/inst-alias-static-predicates.td --implicit-check-not="error:"
// RUN: not llvm-tblgen --gen-compress-inst-emitter -I %p/../../include -I %t -I %S \
// RUN: %t/compress-regclass-by-hwmode.td -o /dev/null 2>&1 | FileCheck %t/compress-regclass-by-hwmode.td --implicit-check-not="error:"
// RUN: not llvm-tblgen --gen-compress-inst-emitter -I %p/../../include -I %t -I %S \
// RUN: %t/compress-regclass-by-hwmode-2.td -o /dev/null 2>&1 | FileCheck %t/compress-regclass-by-hwmode-2.td --implicit-check-not="error:"

//--- Common.td
include "Common/RegClassByHwModeCommon.td"

def IsPtr64 : Predicate<"Subtarget->isPtr64()">;
def IsPtr32 : Predicate<"!Subtarget->isPtr64()">;
defvar Ptr32 = DefaultMode;
def Ptr64 : HwMode<[IsPtr64]>;
def PtrRC : RegClassByHwMode<[Ptr32, Ptr64], [XRegs, YRegs]>;

def PTR_MOV : TestInstruction {
let OutOperandList = (outs PtrRC:$dst);
let InOperandList = (ins PtrRC:$src);
let AsmString = "ptr_mov $dst, $src";
let opcode = 0;
}


//--- inst-alias-bad-reg.td
include "Common.td"
/// This should fail since X0 is not necessarily part of PtrRC.
def BAD_REG : InstAlias<"ptr_zero $rd", (PTR_MOV PtrRC:$dst, X0)>;
// CHECK: [[#@LINE-1]]:5: error: cannot resolve HwMode for PtrRC
// CHECK: Common.td:7:5: note: PtrRC defined here
def MyTargetISA : InstrInfo;
def MyTarget : Target { let InstructionSet = MyTargetISA; }


//--- inst-alias-static-predicates.td
include "Common.td"
/// In theory we could allow the following code since the predicates statically
/// resolve to the correct register class, but since this is non-trivial, check
// that we get a sensible-ish error instead.
let Predicates = [IsPtr32] in
def MOV_X0 : InstAlias<"mov_x0 $dst", (PTR_MOV PtrRC:$dst, X0)>;
// CHECK: [[#@LINE-1]]:5: error: cannot resolve HwMode for PtrRC
// CHECK: Common.td:7:5: note: PtrRC defined here
let Predicates = [IsPtr64] in
def MOV_Y0 : InstAlias<"mov_y0 $dst", (PTR_MOV PtrRC:$dst, Y0)>;

def MyTargetISA : InstrInfo;
def MyTarget : Target { let InstructionSet = MyTargetISA; }

//--- compress-regclass-by-hwmode.td
include "Common.td"
def PTR_ZERO_SMALL : TestInstruction {
let OutOperandList = (outs PtrRC:$dst);
let InOperandList = (ins);
let AsmString = "ptr_zero $dst";
let opcode = 1;
let Size = 1;
}
/// This should fail since X0 is not necessarily part of PtrRC.
def : CompressPat<(PTR_MOV PtrRC:$dst, X0),
(PTR_ZERO_SMALL PtrRC:$dst)>;
// CHECK: [[#@LINE-2]]:1: error: cannot resolve HwMode for PtrRC
// CHECK: Common.td:7:5: note: PtrRC defined here
def MyTargetISA : InstrInfo;
def MyTarget : Target { let InstructionSet = MyTargetISA; }


//--- compress-regclass-by-hwmode-2.td
include "Common.td"
def X_MOV_BIG : TestInstruction {
let OutOperandList = (outs XRegs:$dst);
let InOperandList = (ins XRegs:$src);
let AsmString = "x_mov $dst, $src";
let opcode = 1;
let Size = 4;
}
/// This should fail since PtrRC is not necessarily part of XRegs.
/// In theory, this could be resolved depending on the Predicates but
/// for not we should just always emit an error.
let Predicates = [IsPtr32] in
def : CompressPat<(X_MOV_BIG XRegs:$dst, XRegs:$src),
(PTR_MOV PtrRC:$dst, PtrRC:$src)>;
// CHECK: [[#@LINE-2]]:1: error: Type mismatch between Input and Output Dag operand 'dst'
def MyTargetISA : InstrInfo;
def MyTarget : Target { let InstructionSet = MyTargetISA; }
21 changes: 12 additions & 9 deletions llvm/utils/TableGen/Common/CodeGenInstAlias.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,9 @@ unsigned CodeGenInstAlias::ResultOperand::getMINumOperands() const {

using ResultOperand = CodeGenInstAlias::ResultOperand;

static Expected<ResultOperand> matchSimpleOperand(const Init *Arg,
const StringInit *ArgName,
const Record *Op,
const CodeGenTarget &T) {
static Expected<ResultOperand>
matchSimpleOperand(const Init *Arg, const StringInit *ArgName, const Record *Op,
const CodeGenTarget &T, ArrayRef<SMLoc> Loc) {
if (const Record *OpRC = T.getAsRegClassLike(Op)) {
if (const auto *ArgDef = dyn_cast<DefInit>(Arg)) {
const Record *ArgRec = ArgDef->getDef();
Expand All @@ -52,7 +51,8 @@ static Expected<ResultOperand> matchSimpleOperand(const Init *Arg,
if (const Record *ArgRC = T.getInitValueAsRegClassLike(Arg)) {
if (ArgRC->isSubClassOf("RegisterClass")) {
if (!OpRC->isSubClassOf("RegisterClass") ||
!T.getRegisterClass(OpRC).hasSubClass(&T.getRegisterClass(ArgRC)))
!T.getRegisterClass(OpRC, Loc).hasSubClass(
&T.getRegisterClass(ArgRC, Loc)))
return createStringError(
"argument register class " + ArgRC->getName() +
" is not a subclass of operand register class " +
Expand All @@ -70,7 +70,8 @@ static Expected<ResultOperand> matchSimpleOperand(const Init *Arg,

// Match 'Reg'.
if (ArgRec->isSubClassOf("Register")) {
if (!T.getRegisterClass(OpRC).contains(T.getRegBank().getReg(ArgRec)))
if (!T.getRegisterClass(OpRC, Loc).contains(
T.getRegBank().getReg(ArgRec)))
return createStringError(
"register argument " + ArgRec->getName() +
" is not a member of operand register class " + OpRC->getName());
Expand Down Expand Up @@ -199,7 +200,8 @@ CodeGenInstAlias::CodeGenInstAlias(const Record *R, const CodeGenTarget &T)
const Record *SubOp =
cast<DefInit>(OpInfo.MIOperandInfo->getArg(SubOpIdx))->getDef();
Expected<ResultOperand> ResOpOrErr = matchSimpleOperand(
ArgDag->getArg(SubOpIdx), ArgDag->getArgName(SubOpIdx), SubOp, T);
ArgDag->getArg(SubOpIdx), ArgDag->getArgName(SubOpIdx), SubOp, T,
R->getLoc());
if (!ResOpOrErr)
PrintFatalError(R, "in argument #" + Twine(ArgIdx) + "." +
Twine(SubOpIdx) + ": " +
Expand All @@ -220,8 +222,9 @@ CodeGenInstAlias::CodeGenInstAlias(const Record *R, const CodeGenTarget &T)
} else {
// Simple operand (RegisterClass, RegisterOperand or Operand with empty
// MIOperandInfo).
Expected<ResultOperand> ResOpOrErr = matchSimpleOperand(
Result->getArg(ArgIdx), Result->getArgName(ArgIdx), Op, T);
Expected<ResultOperand> ResOpOrErr =
matchSimpleOperand(Result->getArg(ArgIdx), Result->getArgName(ArgIdx),
Op, T, R->getLoc());
if (!ResOpOrErr)
PrintFatalError(R, "in argument #" + Twine(ArgIdx) + ": " +
toString(ResOpOrErr.takeError()));
Expand Down
12 changes: 10 additions & 2 deletions llvm/utils/TableGen/Common/CodeGenRegisters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1316,11 +1316,19 @@ CodeGenRegBank::getOrCreateSubClass(const CodeGenRegisterClass *RC,
return {&RegClasses.back(), true};
}

CodeGenRegisterClass *CodeGenRegBank::getRegClass(const Record *Def) const {
CodeGenRegisterClass *CodeGenRegBank::getRegClass(const Record *Def,
ArrayRef<SMLoc> Loc) const {
assert(Def->isSubClassOf("RegisterClassLike"));
if (CodeGenRegisterClass *RC = Def2RC.lookup(Def))
return RC;

PrintFatalError(Def->getLoc(), "Not a known RegisterClass!");
ArrayRef<SMLoc> DiagLoc = Loc.empty() ? Def->getLoc() : Loc;
// TODO: Ideally we should update the API to allow resolving HwMode.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that even possible? HwModes are resolved at runtime

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In some cases (e.g. inside a let Predicates = [...] block), we may be able to know exactly what it resolves to but that will require a lot more refactoring. I can drop the comment if you prefer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe in those cases we should resolve it manually in td?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, that would not be very ergonomic. The point is to have reusable patterns and not have to spam every possible detail through every deeply nested multi class

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a test case for this. If there are no further comments I'll submit this tomorrow.

if (Def->isSubClassOf("RegClassByHwMode"))
PrintError(DiagLoc, "cannot resolve HwMode for " + Def->getName());
else
PrintError(DiagLoc, Def->getName() + " is not a known RegisterClass!");
PrintFatalNote(Def->getLoc(), Def->getName() + " defined here");
}

CodeGenSubRegIndex *
Expand Down
3 changes: 2 additions & 1 deletion llvm/utils/TableGen/Common/CodeGenRegisters.h
Original file line number Diff line number Diff line change
Expand Up @@ -815,7 +815,8 @@ class CodeGenRegBank {
}

// Find a register class from its def.
CodeGenRegisterClass *getRegClass(const Record *) const;
CodeGenRegisterClass *getRegClass(const Record *,
ArrayRef<SMLoc> Loc = {}) const;

/// getRegisterClassForRegister - Find the register class that contains the
/// specified physical register. If the register is not in a register
Expand Down
4 changes: 2 additions & 2 deletions llvm/utils/TableGen/Common/CodeGenTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,8 @@ const CodeGenRegister *CodeGenTarget::getRegisterByName(StringRef Name) const {
}

const CodeGenRegisterClass &
CodeGenTarget::getRegisterClass(const Record *R) const {
return *getRegBank().getRegClass(R);
CodeGenTarget::getRegisterClass(const Record *R, ArrayRef<SMLoc> Loc) const {
return *getRegBank().getRegClass(R, Loc);
}

std::vector<ValueTypeByHwMode>
Expand Down
3 changes: 2 additions & 1 deletion llvm/utils/TableGen/Common/CodeGenTarget.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ class CodeGenTarget {
return RegAltNameIndices;
}

const CodeGenRegisterClass &getRegisterClass(const Record *R) const;
const CodeGenRegisterClass &getRegisterClass(const Record *R,
ArrayRef<SMLoc> Loc = {}) const;

/// Convenience wrapper to avoid hardcoding the name of RegClassByHwMode
/// everywhere. This is here instead of CodeGenRegBank to avoid the fatal
Expand Down
14 changes: 8 additions & 6 deletions llvm/utils/TableGen/CompressInstEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,8 @@ class CompressInstEmitter {
void emitCompressInstEmitter(raw_ostream &OS, EmitterType EType);
bool validateTypes(const Record *DagOpType, const Record *InstOpType,
bool IsSourceInst);
bool validateRegister(const Record *Reg, const Record *RegClass);
bool validateRegister(const Record *Reg, const Record *RegClass,
ArrayRef<SMLoc> Loc);
void checkDagOperandMapping(const Record *Rec,
const StringMap<ArgData> &DestOperands,
const DagInit *SourceDag, const DagInit *DestDag);
Expand All @@ -162,11 +163,12 @@ class CompressInstEmitter {
} // End anonymous namespace.

bool CompressInstEmitter::validateRegister(const Record *Reg,
const Record *RegClass) {
const Record *RegClass,
ArrayRef<SMLoc> Loc) {
assert(Reg->isSubClassOf("Register") && "Reg record should be a Register");
assert(RegClass->isSubClassOf("RegisterClass") &&
"RegClass record should be a RegisterClass");
const CodeGenRegisterClass &RC = Target.getRegisterClass(RegClass);
assert(RegClass->isSubClassOf("RegisterClassLike") &&
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this change related?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without this change we get an assertion failure instead of an error in the second test that I added

"RegClass record should be RegisterClassLike");
const CodeGenRegisterClass &RC = Target.getRegisterClass(RegClass, Loc);
const CodeGenRegister *R = Target.getRegBank().getReg(Reg);
assert(R != nullptr && "Register not defined!!");
return RC.contains(R);
Expand Down Expand Up @@ -255,7 +257,7 @@ void CompressInstEmitter::addDagOperandMapping(const Record *Rec,
if (const auto *DI = dyn_cast<DefInit>(Dag->getArg(DAGOpNo))) {
if (DI->getDef()->isSubClassOf("Register")) {
// Check if the fixed register belongs to the Register class.
if (!validateRegister(DI->getDef(), OpndRec))
if (!validateRegister(DI->getDef(), OpndRec, Rec->getLoc()))
PrintFatalError(Rec->getLoc(),
"Error in Dag '" + Dag->getAsString() +
"'Register: '" + DI->getDef()->getName() +
Expand Down
Loading