Skip to content

Commit b05101b

Browse files
resistorjrtc27
andauthored
[TableGen, CodeGen, CHERI] Add support for the cPTR wildcard value type. (#158426)
cPTR is a wildcard CHERI capability value type, used analogously to iPTR. This allows TableGen patterns to abstract over CHERI capability widths. Co-authored-by: Jessica Clarke <[email protected]>
1 parent 0c1087b commit b05101b

File tree

8 files changed

+150
-8
lines changed

8 files changed

+150
-8
lines changed

llvm/include/llvm/CodeGen/ValueTypes.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,10 @@ def aarch64mfp8 : ValueType<8, 253>; // 8-bit value in FPR (AArch64)
367367
def c64 : VTCheriCapability<64, 254>; // 64-bit CHERI capability value
368368
def c128 : VTCheriCapability<128, 255>; // 128-bit CHERI capability value
369369

370+
// Pseudo valuetype mapped to the current CHERI capability pointer size.
371+
// Should only be used in TableGen.
372+
def cPTR : VTAny<503>;
373+
370374
let isNormalValueType = false in {
371375
def token : ValueType<0, 504>; // TokenTy
372376
def MetadataVT : ValueType<0, 505> { // Metadata

llvm/include/llvm/CodeGenTypes/MachineValueType.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,12 @@ namespace llvm {
582582
MVT::LAST_FP_SCALABLE_VECTOR_VALUETYPE,
583583
force_iteration_on_noniterable_enum);
584584
}
585+
586+
static auto cheri_capability_valuetypes() {
587+
return enum_seq_inclusive(MVT::FIRST_CHERI_CAPABILITY_VALUETYPE,
588+
MVT::LAST_CHERI_CAPABILITY_VALUETYPE,
589+
force_iteration_on_noniterable_enum);
590+
}
585591
/// @}
586592
};
587593

llvm/test/TableGen/CPtrWildcard.td

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// RUN: llvm-tblgen -gen-dag-isel -I %p/../../include %s -o - | FileCheck %s
2+
3+
// Create an intrinsic that uses cPTR to overload on capability pointer types,
4+
// and verify that we can match it correct in SelectionDAG.
5+
6+
// CHECK: static const unsigned char MatcherTable[] = {
7+
// CHECK-NEXT: /* 0*/ OPC_CheckOpcode, TARGET_VAL(ISD::INTRINSIC_WO_CHAIN),
8+
// CHECK-NEXT:/* 3*/ OPC_CheckChild0Integer, 42,
9+
// CHECK-NEXT:/* 5*/ OPC_RecordChild1, // #0 = $src
10+
// CHECK-NEXT:/* 6*/ OPC_Scope, 9, /*->17*/ // 2 children in Scope
11+
// CHECK-NEXT:/* 8*/ OPC_CheckChild1Type, /*MVT::c64*/126|128,1/*254*/,
12+
// CHECK-NEXT:/* 11*/ OPC_MorphNodeTo1None, TARGET_VAL(MyTarget::C64_TO_I64),
13+
// CHECK-NEXT: /*MVT::i64*/8, 1/*#Ops*/, 0,
14+
// CHECK-NEXT: // Src: (intrinsic_wo_chain:{ *:[i64] } 21:{ *:[iPTR] }, c64:{ *:[c64] }:$src) - Complexity = 8
15+
// CHECK-NEXT: // Dst: (C64_TO_I64:{ *:[i64] } ?:{ *:[c64] }:$src)
16+
// CHECK-NEXT:/* 17*/ /*Scope*/ 9, /*->27*/
17+
// CHECK-NEXT:/* 18*/ OPC_CheckChild1Type, /*MVT::c128*/127|128,1/*255*/,
18+
// CHECK-NEXT:/* 21*/ OPC_MorphNodeTo1None, TARGET_VAL(MyTarget::C128_TO_I64),
19+
// CHECK-NEXT: /*MVT::i64*/8, 1/*#Ops*/, 0,
20+
// CHECK-NEXT: // Src: (intrinsic_wo_chain:{ *:[i64] } 21:{ *:[iPTR] }, c128:{ *:[c128] }:$src) - Complexity = 8
21+
// CHECK-NEXT: // Dst: (C128_TO_I64:{ *:[i64] } ?:{ *:[c128] }:$src)
22+
// CHECK-NEXT:/* 27*/ 0, /*End of Scope*/
23+
// CHECK-NEXT: 0
24+
// CHECK-NEXT: }; // Total Array size is 29 bytes
25+
26+
include "llvm/Target/Target.td"
27+
28+
def my_cap_ty : LLVMQualPointerType<200> {
29+
let VT = cPTR;
30+
}
31+
32+
def int_cap_get_length :
33+
Intrinsic<[llvm_i64_ty],
34+
[my_cap_ty],
35+
[IntrNoMem, IntrWillReturn]>;
36+
37+
class CapReg<string n> : Register<n> {
38+
let Namespace = "MyTarget";
39+
}
40+
41+
def C64 : CapReg<"c0">;
42+
def C64s
43+
: RegisterClass<"MyTarget", [i64, c64], 64,
44+
(add C64)>;
45+
46+
def C128 : CapReg<"c0">;
47+
def C128s
48+
: RegisterClass<"MyTarget", [c128], 64,
49+
(add C128)>;
50+
51+
def C64_TO_I64 : Instruction {
52+
let Namespace = "MyTarget";
53+
let OutOperandList = (outs C64s:$dst);
54+
let InOperandList = (ins C64s:$src);
55+
}
56+
57+
def C128_TO_I64 : Instruction {
58+
let Namespace = "MyTarget";
59+
let OutOperandList = (outs C64s:$dst);
60+
let InOperandList = (ins C128s:$src);
61+
}
62+
63+
def : Pat<
64+
(int_cap_get_length c64:$src),
65+
(C64_TO_I64 $src)
66+
>;
67+
68+
def : Pat<
69+
(int_cap_get_length c128:$src),
70+
(C128_TO_I64 $src)
71+
>;
72+
73+
def MyTargetISA : InstrInfo;
74+
def MyTarget : Target { let InstructionSet = MyTargetISA; }

llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,8 @@ bool TypeSetByHwMode::intersect(SetType &Out, const SetType &In) {
335335
using WildPartT = std::pair<MVT, std::function<bool(MVT)>>;
336336
static const WildPartT WildParts[] = {
337337
{MVT::iPTR, [](MVT T) { return T.isScalarInteger() || T == MVT::iPTR; }},
338+
{MVT::cPTR,
339+
[](MVT T) { return T.isCheriCapability() || T == MVT::cPTR; }},
338340
};
339341

340342
bool Changed = false;
@@ -816,6 +818,10 @@ void TypeInfer::expandOverloads(TypeSetByHwMode::SetType &Out,
816818
if (Out.count(MVT::pAny)) {
817819
Out.erase(MVT::pAny);
818820
Out.insert(MVT::iPTR);
821+
for (MVT T : MVT::cheri_capability_valuetypes()) {
822+
if (Legal.count(T))
823+
Out.insert(MVT::cPTR);
824+
}
819825
} else if (Out.count(MVT::iAny)) {
820826
Out.erase(MVT::iAny);
821827
for (MVT T : MVT::integer_valuetypes())
@@ -1647,9 +1653,11 @@ bool SDTypeConstraint::ApplyTypeConstraint(TreePatternNode &N,
16471653
case SDTCisVT:
16481654
// Operand must be a particular type.
16491655
return NodeToApply.UpdateNodeType(ResNo, VVT, TP);
1650-
case SDTCisPtrTy:
1651-
// Operand must be same as target pointer type.
1652-
return NodeToApply.UpdateNodeType(ResNo, MVT::iPTR, TP);
1656+
case SDTCisPtrTy: {
1657+
// Operand must be a legal pointer (iPTR, or possibly cPTR) type.
1658+
const TypeSetByHwMode &PtrTys = TP.getDAGPatterns().getLegalPtrTypes();
1659+
return NodeToApply.UpdateNodeType(ResNo, PtrTys, TP);
1660+
}
16531661
case SDTCisInt:
16541662
// Require it to be one of the legal integer VTs.
16551663
return TI.EnforceInteger(NodeToApply.getExtType(ResNo));
@@ -3293,6 +3301,7 @@ CodeGenDAGPatterns::CodeGenDAGPatterns(const RecordKeeper &R,
32933301
PatternRewriterFn PatternRewriter)
32943302
: Records(R), Target(R), Intrinsics(R),
32953303
LegalVTS(Target.getLegalValueTypes()),
3304+
LegalPtrVTS(ComputeLegalPtrTypes()),
32963305
PatternRewriter(std::move(PatternRewriter)) {
32973306
ParseNodeInfo();
32983307
ParseNodeTransforms();
@@ -3328,6 +3337,36 @@ const Record *CodeGenDAGPatterns::getSDNodeNamed(StringRef Name) const {
33283337
return N;
33293338
}
33303339

3340+
// Compute the subset of iPTR and cPTR legal for each mode, coalescing into the
3341+
// default mode where possible to avoid predicate explosion.
3342+
TypeSetByHwMode CodeGenDAGPatterns::ComputeLegalPtrTypes() const {
3343+
auto LegalPtrsForSet = [](const MachineValueTypeSet &In) {
3344+
MachineValueTypeSet Out;
3345+
Out.insert(MVT::iPTR);
3346+
for (MVT T : MVT::cheri_capability_valuetypes()) {
3347+
if (In.count(T)) {
3348+
Out.insert(MVT::cPTR);
3349+
break;
3350+
}
3351+
}
3352+
return Out;
3353+
};
3354+
3355+
const TypeSetByHwMode &LegalTypes = getLegalTypes();
3356+
MachineValueTypeSet LegalPtrsDefault =
3357+
LegalPtrsForSet(LegalTypes.get(DefaultMode));
3358+
3359+
TypeSetByHwMode LegalPtrTypes;
3360+
for (const auto &I : LegalTypes) {
3361+
MachineValueTypeSet S = LegalPtrsForSet(I.second);
3362+
if (I.first != DefaultMode && S == LegalPtrsDefault)
3363+
continue;
3364+
LegalPtrTypes.getOrCreate(I.first).insert(S);
3365+
}
3366+
3367+
return LegalPtrTypes;
3368+
}
3369+
33313370
// Parse all of the SDNode definitions for the target, populating SDNodes.
33323371
void CodeGenDAGPatterns::ParseNodeInfo() {
33333372
const CodeGenHwModes &CGH = getTargetInfo().getHwModes();

llvm/utils/TableGen/Common/CodeGenDAGPatterns.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,6 +1135,7 @@ class CodeGenDAGPatterns {
11351135
std::vector<PatternToMatch> PatternsToMatch;
11361136

11371137
TypeSetByHwMode LegalVTS;
1138+
TypeSetByHwMode LegalPtrVTS;
11381139

11391140
using PatternRewriterFn = std::function<void(TreePattern *)>;
11401141
PatternRewriterFn PatternRewriter;
@@ -1148,6 +1149,7 @@ class CodeGenDAGPatterns {
11481149
CodeGenTarget &getTargetInfo() { return Target; }
11491150
const CodeGenTarget &getTargetInfo() const { return Target; }
11501151
const TypeSetByHwMode &getLegalTypes() const { return LegalVTS; }
1152+
const TypeSetByHwMode &getLegalPtrTypes() const { return LegalPtrVTS; }
11511153

11521154
const Record *getSDNodeNamed(StringRef Name) const;
11531155

@@ -1249,6 +1251,7 @@ class CodeGenDAGPatterns {
12491251
}
12501252

12511253
private:
1254+
TypeSetByHwMode ComputeLegalPtrTypes() const;
12521255
void ParseNodeInfo();
12531256
void ParseNodeTransforms();
12541257
void ParseComplexPatterns();

llvm/utils/TableGen/Common/DAGISelMatcher.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,14 @@ static bool TypesAreContradictory(MVT::SimpleValueType T1,
328328
if (T1 == T2)
329329
return false;
330330

331+
if (T1 == MVT::pAny)
332+
return TypesAreContradictory(MVT::iPTR, T2) &&
333+
TypesAreContradictory(MVT::cPTR, T2);
334+
335+
if (T2 == MVT::pAny)
336+
return TypesAreContradictory(T1, MVT::iPTR) &&
337+
TypesAreContradictory(T1, MVT::cPTR);
338+
331339
// If either type is about iPtr, then they don't conflict unless the other
332340
// one is not a scalar integer type.
333341
if (T1 == MVT::iPTR)
@@ -336,7 +344,13 @@ static bool TypesAreContradictory(MVT::SimpleValueType T1,
336344
if (T2 == MVT::iPTR)
337345
return !MVT(T1).isInteger() || MVT(T1).isVector();
338346

339-
// Otherwise, they are two different non-iPTR types, they conflict.
347+
if (T1 == MVT::cPTR)
348+
return !MVT(T2).isCheriCapability() || MVT(T2).isVector();
349+
350+
if (T2 == MVT::cPTR)
351+
return !MVT(T1).isCheriCapability() || MVT(T1).isVector();
352+
353+
// Otherwise, they are two different non-iPTR/cPTR types, they conflict.
340354
return true;
341355
}
342356

llvm/utils/TableGen/Common/GlobalISel/GlobalISelMatchTable.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1467,7 +1467,9 @@ Error OperandMatcher::addTypeCheckPredicate(const TypeSetByHwMode &VTy,
14671467
if (!VTy.isMachineValueType())
14681468
return failUnsupported("unsupported typeset");
14691469

1470-
if (VTy.getMachineValueType() == MVT::iPTR && OperandIsAPointer) {
1470+
if ((VTy.getMachineValueType() == MVT::iPTR ||
1471+
VTy.getMachineValueType() == MVT::cPTR) &&
1472+
OperandIsAPointer) {
14711473
addPredicate<PointerToAnyOperandMatcher>(0);
14721474
return Error::success();
14731475
}

llvm/utils/TableGen/DAGISelMatcherOpt.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -519,9 +519,9 @@ static void FactorScope(std::unique_ptr<Matcher> &MatcherPtr) {
519519
CheckTypeMatcher *CTM = cast_or_null<CheckTypeMatcher>(
520520
FindNodeWithKind(Optn, Matcher::CheckType));
521521
if (!CTM ||
522-
// iPTR checks could alias any other case without us knowing, don't
523-
// bother with them.
524-
CTM->getType() == MVT::iPTR ||
522+
// iPTR/cPTR checks could alias any other case without us knowing,
523+
// don't bother with them.
524+
CTM->getType() == MVT::iPTR || CTM->getType() == MVT::cPTR ||
525525
// SwitchType only works for result #0.
526526
CTM->getResNo() != 0 ||
527527
// If the CheckType isn't at the start of the list, see if we can move

0 commit comments

Comments
 (0)