Skip to content

Commit

Permalink
[AArch64] Assembly Support for FEAT_GCS/FEAT_CHK
Browse files Browse the repository at this point in the history
This implements support for two new 2022 A-profile extensions:
- FEAT_CHK - Check Feature Status Extension
- FEAT_GCS - Guarded Control Stacks

FEAT_CHK is mandatory from armv8.0-a, but is in the hint space so
there's no clang command-line flag for it, and we only print the hint as
`chkfeat x16` at v8.9a and above, to be compatible when using a
non-integrated assembler that might not yet know about the extension.

FEAT_GCS is optional from armv9.4-a onwards. It is enabled using `+gcs`
in a clang `-march=` or `-mcpu=` option string, or using a
`.arch_extension gcs` assembly directive.

This patch includes changes by Ties Stuij, Tomas Matheson, and Keith
Walker.

Differential Revision: https://reviews.llvm.org/D145563
  • Loading branch information
lenary committed Mar 15, 2023
1 parent 536f35e commit cb7fb73
Show file tree
Hide file tree
Showing 14 changed files with 387 additions and 69 deletions.
2 changes: 2 additions & 0 deletions clang/lib/Basic/Targets/AArch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,8 @@ bool AArch64TargetInfo::handleTargetFeatures(std::vector<std::string> &Features,
HasMOPS = true;
if (Feature == "+d128")
HasD128 = true;
if (Feature == "+gcs")
HasGCS = true;
}

// Check features that are manually disabled by command line options.
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Basic/Targets/AArch64.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class LLVM_LIBRARY_VISIBILITY AArch64TargetInfo : public TargetInfo {
bool HasNoNeon = false;
bool HasNoSVE = false;
bool HasFMV = true;
bool HasGCS = false;

const llvm::AArch64::ArchInfo *ArchInfo = &llvm::AArch64::ARMV8A;

Expand Down
2 changes: 2 additions & 0 deletions llvm/include/llvm/TargetParser/AArch64TargetParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ enum ArchExtKind : uint64_t {
AEK_SPECRES2 = 1ULL << 53, // FEAT_SPECRES2
AEK_RASv2 = 1ULL << 54, // FEAT_RASv2
AEK_ITE = 1ULL << 55, // FEAT_ITE
AEK_GCS = 1ULL << 56, // FEAT_GCS
};
// clang-format on

Expand Down Expand Up @@ -256,6 +257,7 @@ inline constexpr ExtensionInfo Extensions[] = {
{"the", AArch64::AEK_THE, "+the", "-the", FEAT_MAX, "", 0},
{"tme", AArch64::AEK_TME, "+tme", "-tme", FEAT_MAX, "", 0},
{"wfxt", AArch64::AEK_NONE, {}, {}, FEAT_WFXT, "+wfxt", 550},
{"gcs", AArch64::AEK_GCS, "+gcs", "-gcs", FEAT_MAX, "", 0},
// Special cases
{"none", AArch64::AEK_NONE, {}, {}, FEAT_MAX, "", ExtensionInfo::MaxFMVPriority},
};
Expand Down
8 changes: 7 additions & 1 deletion llvm/lib/Target/AArch64/AArch64.td
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,12 @@ def FeatureNoBTIAtReturnTwice : SubtargetFeature<"no-bti-at-return-twice",
"Don't place a BTI instruction "
"after a return-twice">;

def FeatureCHK : SubtargetFeature<"chk", "HasCHK",
"true", "Enable Armv8.0-A Check Feature Status Extension (FEAT_CHK)">;

def FeatureGCS : SubtargetFeature<"gcs", "HasGCS",
"true", "Enable Armv9.4-A Guarded Call Stack Extension", [FeatureCHK]>;

def FeatureCLRBHB : SubtargetFeature<"clrbhb", "HasCLRBHB",
"true", "Enable Clear BHB instruction (FEAT_CLRBHB)">;

Expand Down Expand Up @@ -603,7 +609,7 @@ def HasV8_8aOps : SubtargetFeature<
def HasV8_9aOps : SubtargetFeature<
"v8.9a", "HasV8_9aOps", "true", "Support ARM v8.9a instructions",
[HasV8_8aOps, FeatureCLRBHB, FeaturePRFM_SLC, FeatureSPECRES2,
FeatureCSSC, FeatureRASv2]>;
FeatureCSSC, FeatureRASv2, FeatureCHK]>;

def HasV9_0aOps : SubtargetFeature<
"v9a", "HasV9_0aOps", "true", "Support ARM v9a instructions",
Expand Down
69 changes: 69 additions & 0 deletions llvm/lib/Target/AArch64/AArch64InstrInfo.td
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,10 @@ def HasLSE128 : Predicate<"Subtarget->hasLSE128()">,
AssemblerPredicateWithAll<(all_of FeatureLSE128), "lse128">;
def HasD128 : Predicate<"Subtarget->hasD128()">,
AssemblerPredicateWithAll<(all_of FeatureD128), "d128">;
def HasCHK : Predicate<"Subtarget->hasCHK()">,
AssemblerPredicateWithAll<(all_of FeatureCHK), "chk">;
def HasGCS : Predicate<"Subtarget->hasGCS()">,
AssemblerPredicateWithAll<(all_of FeatureGCS), "gcs">;
def IsLE : Predicate<"Subtarget->isLittleEndian()">;
def IsBE : Predicate<"!Subtarget->isLittleEndian()">;
def IsWindows : Predicate<"Subtarget->isTargetWindows()">;
Expand Down Expand Up @@ -1050,6 +1054,71 @@ def BRB_INJ: BRBEI<0b101, "\tinj">;
def : TokenAlias<"INJ", "inj">;
def : TokenAlias<"IALL", "iall">;


// ARMv9.4-A Guarded Control Stack
class GCSNoOp<bits<3> op2, string mnemonic>
: SimpleSystemI<0, (ins), mnemonic, "">, Sched<[]> {
let Inst{20-8} = 0b0100001110111;
let Inst{7-5} = op2;
let Predicates = [HasGCS];
}
def GCSPUSHX : GCSNoOp<0b100, "gcspushx">;
def GCSPOPCX : GCSNoOp<0b101, "gcspopcx">;
def GCSPOPX : GCSNoOp<0b110, "gcspopx">;

class GCSRtIn<bits<3> op1, bits<3> op2, string mnemonic,
list<dag> pattern = []>
: RtSystemI<0, (outs), (ins GPR64:$Rt), mnemonic, "\t$Rt", pattern> {
let Inst{20-19} = 0b01;
let Inst{18-16} = op1;
let Inst{15-8} = 0b01110111;
let Inst{7-5} = op2;
let Predicates = [HasGCS];
}

def GCSSS1 : GCSRtIn<0b011, 0b010, "gcsss1">;
def GCSPUSHM : GCSRtIn<0b011, 0b000, "gcspushm">;

class GCSRtOut<bits<3> op1, bits<3> op2, string mnemonic,
list<dag> pattern = []>
: RtSystemI<1, (outs GPR64:$Rt), (ins), mnemonic, "\t$Rt", pattern> {
let Inst{20-19} = 0b01;
let Inst{18-16} = op1;
let Inst{15-8} = 0b01110111;
let Inst{7-5} = op2;
let Predicates = [HasGCS];
}

def GCSSS2 : GCSRtOut<0b011, 0b011, "gcsss2">;
def GCSPOPM : GCSRtOut<0b011, 0b001, "gcspopm">;
def GCSPOPM_NoOp : InstAlias<"gcspopm", (GCSPOPM XZR)>, Requires<[HasGCS]>; // Rt defaults to XZR if absent

def GCSB_DSYNC_disable : InstAlias<"gcsb\tdsync", (HINT 19), 0>;
def GCSB_DSYNC : InstAlias<"gcsb\tdsync", (HINT 19), 1>, Requires<[HasGCS]>;

def : TokenAlias<"DSYNC", "dsync">;

let Uses = [X16], Defs = [X16], CRm = 0b0101 in {
def CHKFEAT : SystemNoOperands<0b000, "hint\t#40">;
}
def : InstAlias<"chkfeat\tx16", (CHKFEAT), 0>;
def : InstAlias<"chkfeat\tx16", (CHKFEAT), 1>, Requires<[HasCHK]>;

class GCSSt<string mnemonic, bits<3> op>
: I<(outs), (ins GPR64:$Rt, GPR64sp:$Rn), mnemonic, "\t$Rt, $Rn", "", []>, Sched<[]> {
bits<5> Rt;
bits<5> Rn;
let Inst{31-15} = 0b11011001000111110;
let Inst{14-12} = op;
let Inst{11-10} = 0b11;
let Inst{9-5} = Rn;
let Inst{4-0} = Rt;
let Predicates = [HasGCS];
}
def GCSSTR : GCSSt<"gcsstr", 0b000>;
def GCSSTTR : GCSSt<"gcssttr", 0b001>;


// ARMv8.2-A Dot Product
let Predicates = [HasDotProd] in {
defm SDOT : SIMDThreeSameVectorDot<0, 0, "sdot", AArch64sdot>;
Expand Down
13 changes: 13 additions & 0 deletions llvm/lib/Target/AArch64/AArch64SystemOperands.td
Original file line number Diff line number Diff line change
Expand Up @@ -1761,6 +1761,19 @@ let Requires = [{ {AArch64::FeatureNMI} }] in {
def : ROSysReg<"ICC_NMIAR1_EL1", 0b11, 0b000, 0b1100, 0b1001, 0b101>; // FEAT_GICv3_NMI
}

// v9.4a Guarded Control Stack Extension (GCS)
// Op0 Op1 CRn CRm Op2
def : RWSysReg<"GCSCR_EL1", 0b11, 0b000, 0b0010, 0b0101, 0b000>;
def : RWSysReg<"GCSPR_EL1", 0b11, 0b000, 0b0010, 0b0101, 0b001>;
def : RWSysReg<"GCSCRE0_EL1", 0b11, 0b000, 0b0010, 0b0101, 0b010>;
def : RWSysReg<"GCSPR_EL0", 0b11, 0b011, 0b0010, 0b0101, 0b001>;
def : RWSysReg<"GCSCR_EL2", 0b11, 0b100, 0b0010, 0b0101, 0b000>;
def : RWSysReg<"GCSPR_EL2", 0b11, 0b100, 0b0010, 0b0101, 0b001>;
def : RWSysReg<"GCSCR_EL12", 0b11, 0b101, 0b0010, 0b0101, 0b000>;
def : RWSysReg<"GCSPR_EL12", 0b11, 0b101, 0b0010, 0b0101, 0b001>;
def : RWSysReg<"GCSCR_EL3", 0b11, 0b110, 0b0010, 0b0101, 0b000>;
def : RWSysReg<"GCSPR_EL3", 0b11, 0b110, 0b0010, 0b0101, 0b001>;

// v8.9a/v9.4a Memory Attribute Index Enhancement (FEAT_AIE)
// Op0 Op1 CRn CRm Op2
def : RWSysReg<"AMAIR2_EL1", 0b11, 0b000, 0b1010, 0b0011, 0b001>;
Expand Down
110 changes: 44 additions & 66 deletions llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3677,6 +3677,7 @@ static const struct Extension {
{"ite", {AArch64::FeatureITE}},
{"cssc", {AArch64::FeatureCSSC}},
{"rcpc3", {AArch64::FeatureRCPC3}},
{"gcs", {AArch64::FeatureGCS}},
// FIXME: Unsupported extensions
{"lor", {}},
{"rdma", {}},
Expand Down Expand Up @@ -4841,11 +4842,6 @@ bool AArch64AsmParser::parseOperand(OperandVector &Operands, bool isCondCode,
if (!parseOptionalMulOperand(Operands))
return false;

// If this is an "smstart" or "smstop" instruction, parse its special
// keyword operand as an identifier.
if (Mnemonic == "smstart" || Mnemonic == "smstop")
return parseKeywordOperand(Operands);

// This could be an optional "shift" or "extend" operand.
OperandMatchResultTy GotShift = tryParseOptionalShiftExtend(Operands);
// We can only continue if no tokens were eaten.
Expand All @@ -4854,7 +4850,8 @@ bool AArch64AsmParser::parseOperand(OperandVector &Operands, bool isCondCode,

// If this is a two-word mnemonic, parse its special keyword
// operand as an identifier.
if (Mnemonic == "brb")
if (Mnemonic == "brb" || Mnemonic == "smstart" || Mnemonic == "smstop" ||
Mnemonic == "gcsb")
return parseKeywordOperand(Operands);

// This was not a register so parse other operands that start with an
Expand Down Expand Up @@ -7621,61 +7618,21 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeAArch64AsmParser() {
unsigned AArch64AsmParser::validateTargetOperandClass(MCParsedAsmOperand &AsmOp,
unsigned Kind) {
AArch64Operand &Op = static_cast<AArch64Operand &>(AsmOp);
// If the kind is a token for a literal immediate, check if our asm
// operand matches. This is for InstAliases which have a fixed-value
// immediate in the syntax.
int64_t ExpectedVal;

auto MatchesOpImmediate = [&](int64_t ExpectedVal) -> MatchResultTy {
if (!Op.isImm())
return Match_InvalidOperand;
const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(Op.getImm());
if (!CE)
return Match_InvalidOperand;
if (CE->getValue() == ExpectedVal)
return Match_Success;
return Match_InvalidOperand;
};

switch (Kind) {
default:
return Match_InvalidOperand;
case MCK__HASH_0:
ExpectedVal = 0;
break;
case MCK__HASH_1:
ExpectedVal = 1;
break;
case MCK__HASH_12:
ExpectedVal = 12;
break;
case MCK__HASH_16:
ExpectedVal = 16;
break;
case MCK__HASH_2:
ExpectedVal = 2;
break;
case MCK__HASH_24:
ExpectedVal = 24;
break;
case MCK__HASH_3:
ExpectedVal = 3;
break;
case MCK__HASH_32:
ExpectedVal = 32;
break;
case MCK__HASH_4:
ExpectedVal = 4;
break;
case MCK__HASH_48:
ExpectedVal = 48;
break;
case MCK__HASH_6:
ExpectedVal = 6;
break;
case MCK__HASH_64:
ExpectedVal = 64;
break;
case MCK__HASH_8:
ExpectedVal = 8;
break;
case MCK__HASH__MINUS_4:
ExpectedVal = -4;
break;
case MCK__HASH__MINUS_8:
ExpectedVal = -8;
break;
case MCK__HASH__MINUS_16:
ExpectedVal = -16;
break;
case MCK_MPR:
// If the Kind is a token for the MPR register class which has the "za"
// register (SME accumulator array), check if the asm is a literal "za"
Expand All @@ -7684,15 +7641,36 @@ unsigned AArch64AsmParser::validateTargetOperandClass(MCParsedAsmOperand &AsmOp,
if (Op.isTokenEqual("za"))
return Match_Success;
return Match_InvalidOperand;

// If the kind is a token for a literal immediate, check if our asm
// operand matches. This is for InstAliases which have a fixed-value
// immediate in the syntax.
#define MATCH_HASH(N) \
case MCK__HASH_##N: \
return MatchesOpImmediate(N);
MATCH_HASH(0)
MATCH_HASH(1)
MATCH_HASH(2)
MATCH_HASH(3)
MATCH_HASH(4)
MATCH_HASH(6)
MATCH_HASH(8)
MATCH_HASH(12)
MATCH_HASH(16)
MATCH_HASH(24)
MATCH_HASH(32)
MATCH_HASH(40)
MATCH_HASH(48)
MATCH_HASH(64)
#undef MATCH_HASH
#define MATCH_HASH_MINUS(N) \
case MCK__HASH__MINUS_##N: \
return MatchesOpImmediate(-N);
MATCH_HASH_MINUS(4)
MATCH_HASH_MINUS(8)
MATCH_HASH_MINUS(16)
#undef MATCH_HASH_MINUS
}
if (!Op.isImm())
return Match_InvalidOperand;
const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(Op.getImm());
if (!CE)
return Match_InvalidOperand;
if (CE->getValue() == ExpectedVal)
return Match_Success;
return Match_InvalidOperand;
}

OperandMatchResultTy
Expand Down
21 changes: 21 additions & 0 deletions llvm/test/MC/AArch64/armv9.4a-chk.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// RUN: llvm-mc -triple aarch64 -mattr=+chk -show-encoding %s | FileCheck %s
// RUN: llvm-mc -triple aarch64 -mattr=+v8.9a -show-encoding %s | FileCheck %s
// RUN: llvm-mc -triple aarch64 -mattr=+v9.4a -show-encoding %s | FileCheck %s
// RUN: llvm-mc -triple aarch64 -mattr=+v8a -show-encoding %s | FileCheck %s --check-prefix=NO-CHK

// FEAT_CHK is mandatory from v8.0-a, but a clang user may not be using the LLVM
// integrated assembler, so we cannot just print `chkfeat x16` in all
// circumstances. Thankfully, we can always print `hint #40` when we cannot
// print `chkfeat x16`.
//
// So, in this case, we only print `chkfeat x16` from v8.9-a onwards, as an
// assembler that understands v8.9-a will understand `chkfeat x16`, and those
// that understand previous versions may not.

chkfeat x16
// CHECK: chkfeat x16 // encoding: [0x1f,0x25,0x03,0xd5]
// NO-CHK: hint #40 // encoding: [0x1f,0x25,0x03,0xd5]

hint #40
// CHECK: chkfeat x16 // encoding: [0x1f,0x25,0x03,0xd5]
// NO-CHK: hint #40 // encoding: [0x1f,0x25,0x03,0xd5]

0 comments on commit cb7fb73

Please sign in to comment.