Skip to content

Commit

Permalink
[AArch64] Add target feature "all"
Browse files Browse the repository at this point in the history
This is used by disassemblers: `llvm-mc -disassemble -mattr=` and `llvm-objdump --mattr=`.
The main use case is for llvm-objdump to disassemble all known instructions
(D128030).

In user-facing tools, "all" is intentionally not supported in producers:
integrated assembler (`.arch_extension all`), clang -march (`-march=armv9.3a+all`).
Due to the code structure, `llvm-mc -mattr=+all` `llc -mattr=+all` are not
rejected (they are internal tool). Add `llvm/test/CodeGen/AArch64/mattr-all.ll`
to catch behavior changes.

AArch64SysReg::SysReg::haveFeatures: check `FeatureAll` to print
`AArch64SysReg::SysReg::AltName` for some system registers (e.g. `ERRIDR_EL1, RNDR`).

AArch64.td: add `AssemblerPredicateWithAll` to additionally test `FeatureAll`.
Change all `AssemblerPredicate` (except `UseNegativeImmediates`) to `AssemblerPredicateWithAll`.

utils/TableGen/{DecoderEmitter,SubtargetFeatureInfo}.cpp: support arbitrarily
nested all_of, any_of, and not.

Note: A predicate supports all_of, any_of, and not. For a target (though
currently not for AArch64) an encoding may be disassembled differently with
different target features.
Note: AArch64MCCodeEmitter::computeAvailableFeatures is not available to
the disassembler.

Reviewed By: peter.smith, lenary

Differential Revision: https://reviews.llvm.org/D128029
  • Loading branch information
MaskRay committed Jun 30, 2022
1 parent 3a743a5 commit 45f3a5a
Show file tree
Hide file tree
Showing 12 changed files with 251 additions and 152 deletions.
7 changes: 7 additions & 0 deletions llvm/lib/Target/AArch64/AArch64.td
Expand Up @@ -556,6 +556,13 @@ def HasV8_0rOps : SubtargetFeature<
FeatureSpecRestrict
]>;

// Only intended to be used by disassemblers.
def FeatureAll
: SubtargetFeature<"all", "IsAll", "true", "Enable all instructions", []>;

class AssemblerPredicateWithAll<dag cond, string name="">
: AssemblerPredicate<(any_of FeatureAll, cond), name>;

//===----------------------------------------------------------------------===//
// Register File Description
//===----------------------------------------------------------------------===//
Expand Down
160 changes: 80 additions & 80 deletions llvm/lib/Target/AArch64/AArch64InstrInfo.td

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions llvm/lib/Target/AArch64/AArch64SystemOperands.td
Expand Up @@ -18,23 +18,23 @@ include "llvm/TableGen/SearchableTable.td"
//===----------------------------------------------------------------------===//

def HasCCPP : Predicate<"Subtarget->hasCCPP()">,
AssemblerPredicate<(all_of FeatureCCPP), "ccpp">;
AssemblerPredicateWithAll<(all_of FeatureCCPP), "ccpp">;

def HasPAN : Predicate<"Subtarget->hasPAN()">,
AssemblerPredicate<(all_of FeaturePAN),
AssemblerPredicateWithAll<(all_of FeaturePAN),
"ARM v8.1 Privileged Access-Never extension">;

def HasPsUAO : Predicate<"Subtarget->hasPsUAO()">,
AssemblerPredicate<(all_of FeaturePsUAO),
AssemblerPredicateWithAll<(all_of FeaturePsUAO),
"ARM v8.2 UAO PState extension (psuao)">;

def HasPAN_RWV : Predicate<"Subtarget->hasPAN_RWV()">,
AssemblerPredicate<(all_of FeaturePAN_RWV),
AssemblerPredicateWithAll<(all_of FeaturePAN_RWV),
"ARM v8.2 PAN AT S1E1R and AT S1E1W Variation">;

def HasCONTEXTIDREL2
: Predicate<"Subtarget->hasCONTEXTIDREL2()">,
AssemblerPredicate<(all_of FeatureCONTEXTIDREL2),
AssemblerPredicateWithAll<(all_of FeatureCONTEXTIDREL2),
"Target contains CONTEXTIDR_EL2 RW operand">;

//===----------------------------------------------------------------------===//
Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h
Expand Up @@ -634,7 +634,8 @@ namespace AArch64SysReg {
FeatureBitset FeaturesRequired;

bool haveFeatures(FeatureBitset ActiveFeatures) const {
return (FeaturesRequired & ActiveFeatures) == FeaturesRequired;
return ActiveFeatures[llvm::AArch64::FeatureAll] ||
(FeaturesRequired & ActiveFeatures) == FeaturesRequired;
}
};

Expand Down
24 changes: 24 additions & 0 deletions llvm/test/CodeGen/AArch64/mattr-all.ll
@@ -0,0 +1,24 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
;; -mattr=+all is not intended to be used for code emitters. Nevertheless,
;; llc does not reject it. This test intends to catch behavior changes.
; RUN: llc -mtriple=aarch64 -mattr=+all < %s | FileCheck %s

define half @bf16() nounwind {
; CHECK-LABEL: bf16:
; CHECK: // %bb.0:
; CHECK-NEXT: adrp x8, .LCPI0_0
; CHECK-NEXT: ldr h0, [x8, :lo12:.LCPI0_0]
; CHECK-NEXT: ret
ret half 0xH0000
}

define i64 @perfmon() nounwind {
; CHECK-LABEL: perfmon:
; CHECK: // %bb.0:
; CHECK-NEXT: mov x0, xzr
; CHECK-NEXT: ret
%tmp0 = call i64 @llvm.readcyclecounter()
ret i64 %tmp0
}

declare i64 @llvm.readcyclecounter()
3 changes: 3 additions & 0 deletions llvm/test/MC/AArch64/alias-addsubimm.s
Expand Up @@ -2,6 +2,9 @@
// RUN: llvm-mc -triple=aarch64 -M no-aliases < %s | FileCheck %s --check-prefixes=CHECK,NOALIAS
// RUN: not llvm-mc -mattr=+no-neg-immediates -triple=aarch64-none-linux-gnu < %s 2>&1 | FileCheck %s --check-prefix=CHECK-NO-NEG-IMM

/// +all does not imply +no-neg-immediates.
// RUN: llvm-mc -triple=aarch64 -mattr=+all -M no-aliases %s | FileCheck %s --check-prefixes=CHECK,NOALIAS

add w0, w2, #4096
sub w0, w2, #4096
// CHECK: add w0, w2, #1, lsl #12
Expand Down
1 change: 1 addition & 0 deletions llvm/test/MC/AArch64/armv8.6a-bf16.s
@@ -1,5 +1,6 @@
// RUN: llvm-mc -triple aarch64 -show-encoding -mattr=+bf16 < %s | FileCheck %s
// RUN: llvm-mc -triple aarch64 -show-encoding -mattr=+v8.6a < %s | FileCheck %s
// RUN: llvm-mc -triple aarch64 -show-encoding -mattr=+all %s | FileCheck %s
// RUN: not llvm-mc -triple aarch64 -show-encoding -mattr=-bf16 < %s 2>&1 | FileCheck %s --check-prefix=NOBF16
// RUN: not llvm-mc -triple aarch64 -show-encoding < %s 2>&1 | FileCheck %s --check-prefix=NOBF16

Expand Down
37 changes: 37 additions & 0 deletions llvm/test/MC/Disassembler/AArch64/mattr-all.txt
@@ -0,0 +1,37 @@
# RUN: llvm-mc -triple=aarch64 -mattr=+all -disassemble %s 2>&1 | FileCheck %s

## aes
# CHECK: aese v0.16b, v1.16b
[0x20,0x48,0x28,0x4e]

## ete
# CHECK: mrs x0, TRCRSR
[0x00,0x0a,0x31,0xd5]

## fp16fml
# CHECK: fmlal v0.2s, v1.2h, v2.2h
[0x20,0xec,0x22,0x0e]

## armv8.2a ras
# CHECK: mrs x0, ERRIDR_EL1
[0x00,0x53,0x38,0xd5]

## armv8.5a mte
# CHECK: irg x0, x1
[0x20,0x10,0xdf,0x9a]

## armv8.5a rand
# CHECK: mrs x0, RNDR
[0x00,0x24,0x3b,0xd5]

## armv8.6a matmul
# CHECK: smmla v1.4s, v16.16b, v31.16b
[0x01,0xa6,0x9f,0x4e]

## armv8.8a-hbc
# CHECK: bc.eq #4
[0x30,0x00,0x00,0x54]

## armv9a rme
# CHECK: mrs x0, MFAR_EL3
[0xa0,0x60,0x3e,0xd5]
10 changes: 7 additions & 3 deletions llvm/test/TableGen/AsmPredicateCombining.td
Expand Up @@ -47,16 +47,20 @@ def AsmCond2a: SubtargetFeature<"cond2a", "cond2a", "true", "">;
def AsmCond2b: SubtargetFeature<"cond2b", "cond2b", "true", "">;
def AsmCond3a: SubtargetFeature<"cond3a", "cond3a", "true", "">;
def AsmCond3b: SubtargetFeature<"cond3b", "cond3b", "true", "">;
def AsmCond4 : SubtargetFeature<"cond4", "cond4", "true", "">;

def AsmPred1 : Predicate<"Pred1">, AssemblerPredicate<(all_of AsmCond1)>;
def AsmPred2 : Predicate<"Pred2">, AssemblerPredicate<(all_of AsmCond2a, AsmCond2b)>;
def AsmPred3 : Predicate<"Pred3">, AssemblerPredicate<(any_of AsmCond3a, AsmCond3b)>;
def AsmPred4 : Predicate<"Pred4">, AssemblerPredicate<(all_of AsmCond4, (not (any_of AsmCond3a, AsmCond3b)))>;
// MATCHER: if (FB[arch::AsmCond1])
// MATCHER-NEXT: Features.set(Feature_AsmPred1Bit);
// MATCHER-NEXT: if (FB[arch::AsmCond2a] && FB[arch::AsmCond2b])
// MATCHER-NEXT: Features.set(Feature_AsmPred2Bit);
// MATCHER-NEXT: if ((FB[arch::AsmCond3a] || FB[arch::AsmCond3b]))
// MATCHER-NEXT: if (FB[arch::AsmCond3a] || FB[arch::AsmCond3b])
// MATCHER-NEXT: Features.set(Feature_AsmPred3Bit);
// MATCHER-NEXT: if (FB[arch::AsmCond4] && !(FB[arch::AsmCond3a] || FB[arch::AsmCond3b]))
// MATCHER-NEXT: Features.set(Feature_AsmPred4Bit);

def insn1 : TestInsn<1, [AsmPred1]>;
// DISASS: return (Bits[arch::AsmCond1]);
Expand All @@ -65,10 +69,10 @@ def insn2 : TestInsn<2, [AsmPred2]>;
// DISASS: return (Bits[arch::AsmCond2a] && Bits[arch::AsmCond2b])

def insn3 : TestInsn<3, [AsmPred3]>;
// DISASS: return ((Bits[arch::AsmCond3a] || Bits[arch::AsmCond3b]))
// DISASS: return (Bits[arch::AsmCond3a] || Bits[arch::AsmCond3b])

def insn4 : TestInsn<4, [AsmPred1, AsmPred2]>;
// DISASS: return (Bits[arch::AsmCond1] && Bits[arch::AsmCond2a] && Bits[arch::AsmCond2b])
// DISASS: return (Bits[arch::AsmCond1] && (Bits[arch::AsmCond2a] && Bits[arch::AsmCond2b]))

def insn5 : TestInsn<5, [AsmPred1, AsmPred3]>;
// DISASS: return (Bits[arch::AsmCond1] && (Bits[arch::AsmCond3a] || Bits[arch::AsmCond3b]))
Expand Down
11 changes: 11 additions & 0 deletions llvm/utils/TableGen/AsmWriterEmitter.cpp
Expand Up @@ -999,6 +999,17 @@ void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) {
if (D->getNumArgs() == 0)
PrintFatalError(R->getLoc(), "Invalid AssemblerCondDag!");
bool IsOr = CombineType == "any_of";
// Change (any_of FeatureAll, (any_of ...)) to (any_of FeatureAll, ...).
if (IsOr && D->getNumArgs() == 2 && isa<DagInit>(D->getArg(1))) {
DagInit *RHS = dyn_cast<DagInit>(D->getArg(1));
SmallVector<Init *> Args{D->getArg(0)};
SmallVector<StringInit *> ArgNames{D->getArgName(0)};
for (unsigned i = 0, e = RHS->getNumArgs(); i != e; ++i) {
Args.push_back(RHS->getArg(i));
ArgNames.push_back(RHS->getArgName(i));
}
D = DagInit::get(D->getOperator(), nullptr, Args, ArgNames);
}

for (auto *Arg : D->getArgs()) {
bool IsNeg = false;
Expand Down
71 changes: 39 additions & 32 deletions llvm/utils/TableGen/DecoderEmitter.cpp
Expand Up @@ -509,6 +509,8 @@ class FilterChooser {
// Returns true if predicate matches were emitted, false otherwise.
bool emitPredicateMatch(raw_ostream &o, unsigned &Indentation,
unsigned Opc) const;
bool emitPredicateMatchAux(const Init &Val, bool ParenIfBinOp,
raw_ostream &OS) const;

bool doesOpcodeNeedPredicate(unsigned Opc) const;
unsigned getPredicateIndex(DecoderTableInfo &TableInfo, StringRef P) const;
Expand Down Expand Up @@ -1226,6 +1228,40 @@ unsigned FilterChooser::getDecoderIndex(DecoderSet &Decoders,
return (unsigned)(P - Decoders.begin());
}

// If ParenIfBinOp is true, print a surrounding () if Val uses && or ||.
bool FilterChooser::emitPredicateMatchAux(const Init &Val, bool ParenIfBinOp,
raw_ostream &OS) const {
if (auto *D = dyn_cast<DefInit>(&Val)) {
if (!D->getDef()->isSubClassOf("SubtargetFeature"))
return true;
OS << "Bits[" << Emitter->PredicateNamespace << "::" << D->getAsString()
<< "]";
return false;
}
if (auto *D = dyn_cast<DagInit>(&Val)) {
std::string Op = D->getOperator()->getAsString();
if (Op == "not" && D->getNumArgs() == 1) {
OS << '!';
return emitPredicateMatchAux(*D->getArg(0), true, OS);
}
if ((Op == "any_of" || Op == "all_of") && D->getNumArgs() > 0) {
bool Paren = D->getNumArgs() > 1 && std::exchange(ParenIfBinOp, true);
if (Paren)
OS << '(';
ListSeparator LS(Op == "any_of" ? " || " : " && ");
for (auto *Arg : D->getArgs()) {
OS << LS;
if (emitPredicateMatchAux(*Arg, ParenIfBinOp, OS))
return true;
}
if (Paren)
OS << ')';
return false;
}
}
return true;
}

bool FilterChooser::emitPredicateMatch(raw_ostream &o, unsigned &Indentation,
unsigned Opc) const {
ListInit *Predicates =
Expand All @@ -1239,40 +1275,11 @@ bool FilterChooser::emitPredicateMatch(raw_ostream &o, unsigned &Indentation,
if (!isa<DagInit>(Pred->getValue("AssemblerCondDag")->getValue()))
continue;

const DagInit *D = Pred->getValueAsDag("AssemblerCondDag");
std::string CombineType = D->getOperator()->getAsString();
if (CombineType != "any_of" && CombineType != "all_of")
PrintFatalError(Pred->getLoc(), "Invalid AssemblerCondDag!");
if (D->getNumArgs() == 0)
PrintFatalError(Pred->getLoc(), "Invalid AssemblerCondDag!");
bool IsOr = CombineType == "any_of";

if (!IsFirstEmission)
o << " && ";

if (IsOr)
o << "(";

ListSeparator LS(IsOr ? " || " : " && ");
for (auto *Arg : D->getArgs()) {
o << LS;
if (auto *NotArg = dyn_cast<DagInit>(Arg)) {
if (NotArg->getOperator()->getAsString() != "not" ||
NotArg->getNumArgs() != 1)
PrintFatalError(Pred->getLoc(), "Invalid AssemblerCondDag!");
Arg = NotArg->getArg(0);
o << "!";
}
if (!isa<DefInit>(Arg) ||
!cast<DefInit>(Arg)->getDef()->isSubClassOf("SubtargetFeature"))
PrintFatalError(Pred->getLoc(), "Invalid AssemblerCondDag!");
o << "Bits[" << Emitter->PredicateNamespace << "::" << Arg->getAsString()
<< "]";
}

if (IsOr)
o << ")";

if (emitPredicateMatchAux(*Pred->getValueAsDag("AssemblerCondDag"),
Predicates->size() > 1, o))
PrintFatalError(Pred->getLoc(), "Invalid AssemblerCondDag!");
IsFirstEmission = false;
}
return !Predicates->empty();
Expand Down
66 changes: 35 additions & 31 deletions llvm/utils/TableGen/SubtargetFeatureInfo.cpp
Expand Up @@ -108,6 +108,39 @@ void SubtargetFeatureInfo::emitComputeAvailableFeatures(
OS << "}\n\n";
}

// If ParenIfBinOp is true, print a surrounding () if Val uses && or ||.
static bool emitFeaturesAux(StringRef TargetName, const Init &Val,
bool ParenIfBinOp, raw_ostream &OS) {
if (auto *D = dyn_cast<DefInit>(&Val)) {
if (!D->getDef()->isSubClassOf("SubtargetFeature"))
return true;
OS << "FB[" << TargetName << "::" << D->getAsString() << "]";
return false;
}
if (auto *D = dyn_cast<DagInit>(&Val)) {
std::string Op = D->getOperator()->getAsString();
if (Op == "not" && D->getNumArgs() == 1) {
OS << '!';
return emitFeaturesAux(TargetName, *D->getArg(0), true, OS);
}
if ((Op == "any_of" || Op == "all_of") && D->getNumArgs() > 0) {
bool Paren = D->getNumArgs() > 1 && std::exchange(ParenIfBinOp, true);
if (Paren)
OS << '(';
ListSeparator LS(Op == "any_of" ? " || " : " && ");
for (auto *Arg : D->getArgs()) {
OS << LS;
if (emitFeaturesAux(TargetName, *Arg, ParenIfBinOp, OS))
return true;
}
if (Paren)
OS << ')';
return false;
}
}
return true;
}

void SubtargetFeatureInfo::emitComputeAssemblerAvailableFeatures(
StringRef TargetName, StringRef ClassName, StringRef FuncName,
SubtargetFeatureInfoMap &SubtargetFeatures, raw_ostream &OS) {
Expand All @@ -118,37 +151,8 @@ void SubtargetFeatureInfo::emitComputeAssemblerAvailableFeatures(
const SubtargetFeatureInfo &SFI = SF.second;

OS << " if (";

const DagInit *D = SFI.TheDef->getValueAsDag("AssemblerCondDag");
std::string CombineType = D->getOperator()->getAsString();
if (CombineType != "any_of" && CombineType != "all_of")
PrintFatalError(SFI.TheDef->getLoc(), "Invalid AssemblerCondDag!");
if (D->getNumArgs() == 0)
PrintFatalError(SFI.TheDef->getLoc(), "Invalid AssemblerCondDag!");
bool IsOr = CombineType == "any_of";

if (IsOr)
OS << "(";

ListSeparator LS(IsOr ? " || " : " && ");
for (auto *Arg : D->getArgs()) {
OS << LS;
if (auto *NotArg = dyn_cast<DagInit>(Arg)) {
if (NotArg->getOperator()->getAsString() != "not" ||
NotArg->getNumArgs() != 1)
PrintFatalError(SFI.TheDef->getLoc(), "Invalid AssemblerCondDag!");
Arg = NotArg->getArg(0);
OS << "!";
}
if (!isa<DefInit>(Arg) ||
!cast<DefInit>(Arg)->getDef()->isSubClassOf("SubtargetFeature"))
PrintFatalError(SFI.TheDef->getLoc(), "Invalid AssemblerCondDag!");
OS << "FB[" << TargetName << "::" << Arg->getAsString() << "]";
}

if (IsOr)
OS << ")";

emitFeaturesAux(TargetName, *SFI.TheDef->getValueAsDag("AssemblerCondDag"),
/*ParenIfBinOp=*/false, OS);
OS << ")\n";
OS << " Features.set(" << SFI.getEnumBitName() << ");\n";
}
Expand Down

0 comments on commit 45f3a5a

Please sign in to comment.