Skip to content

Commit

Permalink
[RISCV] Add basic RV32E definitions and MC layer support
Browse files Browse the repository at this point in the history
The RISC-V ISA defines RV32E as an alternative "base" instruction set
encoding, that differs from RV32I by having only 16 rather than 32 registers.
This patch adds basic definitions for RV32E as well as MC layer support
(assembling, disassembling) and tests. The only supported ABI on RV32E is
ILP32E.

Add a new RISCVFeatures::validate() helper to RISCVUtils which can be called
from codegen or MC layer libraries to validate the combination of TargetTriple
and FeatureBitSet. Other targets have similar checks (e.g. erroring if SPE is
enabled on PPC64 or oddspreg + o32 ABI on Mips), but they either duplicate the
checks (Mips), or fail to check for both codegen and MC codepaths (PPC).

Codegen for the ILP32E ABI support and RV32E codegen are left for a future
patch/patches.

Differential Revision: https://reviews.llvm.org/D59470

llvm-svn: 356744
  • Loading branch information
asb committed Mar 22, 2019
1 parent 91e5cdf commit dab1f6f
Showing 16 changed files with 333 additions and 15 deletions.
13 changes: 9 additions & 4 deletions llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
Original file line number Diff line number Diff line change
@@ -47,6 +47,7 @@ class RISCVAsmParser : public MCTargetAsmParser {

SMLoc getLoc() const { return getParser().getTok().getLoc(); }
bool isRV64() const { return getSTI().hasFeature(RISCV::Feature64Bit); }
bool isRV32E() const { return getSTI().hasFeature(RISCV::FeatureRV32E); }

RISCVTargetStreamer &getTargetStreamer() {
MCTargetStreamer &TS = *getParser().getStreamer().getTargetStreamer();
@@ -910,11 +911,15 @@ bool RISCVAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,

// Attempts to match Name as a register (either using the default name or
// alternative ABI names), setting RegNo to the matching register. Upon
// failure, returns true and sets RegNo to 0.
static bool matchRegisterNameHelper(unsigned &RegNo, StringRef Name) {
// failure, returns true and sets RegNo to 0. If IsRV32E then registers
// x16-x31 will be rejected.
static bool matchRegisterNameHelper(bool IsRV32E, unsigned &RegNo,
StringRef Name) {
RegNo = MatchRegisterName(Name);
if (RegNo == 0)
RegNo = MatchRegisterAltName(Name);
if (IsRV32E && RegNo >= RISCV::X16 && RegNo <= RISCV::X31)
RegNo = 0;
return RegNo == 0;
}

@@ -926,7 +931,7 @@ bool RISCVAsmParser::ParseRegister(unsigned &RegNo, SMLoc &StartLoc,
RegNo = 0;
StringRef Name = getLexer().getTok().getIdentifier();

if (matchRegisterNameHelper(RegNo, Name))
if (matchRegisterNameHelper(isRV32E(), RegNo, Name))
return Error(StartLoc, "invalid register name");

getParser().Lex(); // Eat identifier token.
@@ -954,7 +959,7 @@ OperandMatchResultTy RISCVAsmParser::parseRegister(OperandVector &Operands,
case AsmToken::Identifier:
StringRef Name = getLexer().getTok().getIdentifier();
unsigned RegNo;
matchRegisterNameHelper(RegNo, Name);
matchRegisterNameHelper(isRV32E(), RegNo, Name);

if (RegNo == 0) {
if (HadParens)
8 changes: 7 additions & 1 deletion llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
Original file line number Diff line number Diff line change
@@ -69,7 +69,13 @@ static const unsigned GPRDecoderTable[] = {
static DecodeStatus DecodeGPRRegisterClass(MCInst &Inst, uint64_t RegNo,
uint64_t Address,
const void *Decoder) {
if (RegNo > array_lengthof(GPRDecoderTable))
const FeatureBitset &FeatureBits =
static_cast<const MCDisassembler *>(Decoder)
->getSubtargetInfo()
.getFeatureBits();
bool IsRV32E = FeatureBits[RISCV::FeatureRV32E];

if (RegNo > array_lengthof(GPRDecoderTable) || (IsRV32E && RegNo > 15))
return MCDisassembler::Fail;

// We must define our own mapping from RegNo to register identifier.
1 change: 1 addition & 0 deletions llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.h
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@ class RISCVAsmBackend : public MCAsmBackend {
TargetOptions(Options) {
TargetABI = RISCVABI::computeTargetABI(
STI.getTargetTriple(), STI.getFeatureBits(), Options.getABIName());
RISCVFeatures::validate(STI.getTargetTriple(), STI.getFeatureBits());
}
~RISCVAsmBackend() override {}

6 changes: 6 additions & 0 deletions llvm/lib/Target/RISCV/RISCV.td
Original file line number Diff line number Diff line change
@@ -54,6 +54,12 @@ def IsRV32 : Predicate<"!Subtarget->is64Bit()">,
def RV64 : HwMode<"+64bit">;
def RV32 : HwMode<"-64bit">;

def FeatureRV32E
: SubtargetFeature<"e", "IsRV32E", "true",
"Implements RV32E (provides 16 rather than 32 GPRs)">;
def IsRV32E : Predicate<"Subtarget->isRV32E()">,
AssemblerPredicate<"FeatureRV32E">;

def FeatureRelax
: SubtargetFeature<"relax", "EnableLinkerRelax", "true",
"Enable Linker relaxation.">;
3 changes: 3 additions & 0 deletions llvm/lib/Target/RISCV/RISCVISelLowering.cpp
Original file line number Diff line number Diff line change
@@ -43,6 +43,9 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
const RISCVSubtarget &STI)
: TargetLowering(TM), Subtarget(STI) {

if (Subtarget.isRV32E())
report_fatal_error("Codegen not yet implemented for RV32E");

RISCVABI::ABI ABI = Subtarget.getTargetABI();
assert(ABI != RISCVABI::ABI_Unknown && "Improperly initialised target ABI");

1 change: 1 addition & 0 deletions llvm/lib/Target/RISCV/RISCVSubtarget.cpp
Original file line number Diff line number Diff line change
@@ -39,6 +39,7 @@ RISCVSubtarget &RISCVSubtarget::initializeSubtargetDependencies(
}

TargetABI = RISCVABI::computeTargetABI(TT, getFeatureBits(), ABIName);
RISCVFeatures::validate(TT, getFeatureBits());
return *this;
}

2 changes: 2 additions & 0 deletions llvm/lib/Target/RISCV/RISCVSubtarget.h
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@ class RISCVSubtarget : public RISCVGenSubtargetInfo {
bool HasStdExtD = false;
bool HasStdExtC = false;
bool HasRV64 = false;
bool IsRV32E = false;
bool EnableLinkerRelax = false;
unsigned XLen = 32;
MVT XLenVT = MVT::i32;
@@ -80,6 +81,7 @@ class RISCVSubtarget : public RISCVGenSubtargetInfo {
bool hasStdExtD() const { return HasStdExtD; }
bool hasStdExtC() const { return HasStdExtC; }
bool is64Bit() const { return HasRV64; }
bool isRV32E() const { return IsRV32E; }
bool enableLinkerRelax() const { return EnableLinkerRelax; }
MVT getXLenVT() const { return XLenVT; }
unsigned getXLen() const { return XLen; }
40 changes: 30 additions & 10 deletions llvm/lib/Target/RISCV/Utils/RISCVBaseInfo.cpp
Original file line number Diff line number Diff line change
@@ -22,15 +22,18 @@ ABI computeTargetABI(const Triple &TT, FeatureBitset FeatureBits,
.Case("lp64d", ABI_LP64D)
.Default(ABI_Unknown);

bool IsRV64 = TT.isArch64Bit();
bool IsRV32E = FeatureBits[RISCV::FeatureRV32E];

if (!ABIName.empty() && TargetABI == ABI_Unknown) {
errs()
<< "'" << ABIName
<< "' is not a recognized ABI for this target (ignoring target-abi)\n";
} else if (ABIName.startswith("ilp32") && TT.isArch64Bit()) {
} else if (ABIName.startswith("ilp32") && IsRV64) {
errs() << "32-bit ABIs are not supported for 64-bit targets (ignoring "
"target-abi)\n";
TargetABI = ABI_Unknown;
} else if (ABIName.startswith("lp64") && !TT.isArch64Bit()) {
} else if (ABIName.startswith("lp64") && !IsRV64) {
errs() << "64-bit ABIs are not supported for 32-bit targets (ignoring "
"target-abi)\n";
TargetABI = ABI_Unknown;
@@ -44,17 +47,34 @@ ABI computeTargetABI(const Triple &TT, FeatureBitset FeatureBits,
"doesn't support the D instruction set extension (ignoring "
"target-abi)\n";
TargetABI = ABI_Unknown;
} else if (IsRV32E && TargetABI != ABI_ILP32E && TargetABI != ABI_Unknown) {
errs()
<< "Only the ilp32e ABI is supported for RV32E (ignoring target-abi)\n";
TargetABI = ABI_Unknown;
}

// For now, default to the ilp32/lp64 if no explicit ABI is given or an
// invalid/unrecognised string is given. In the future, it might be worth
// changing this to default to ilp32f/lp64f and ilp32d/lp64d when hardware
// support for floating point is present.
if (TargetABI == ABI_Unknown) {
TargetABI = TT.isArch64Bit() ? ABI_LP64 : ABI_ILP32;
}
if (TargetABI != ABI_Unknown)
return TargetABI;

return TargetABI;
// For now, default to the ilp32/ilp32e/lp64 ABI if no explicit ABI is given
// or an invalid/unrecognised string is given. In the future, it might be
// worth changing this to default to ilp32f/lp64f and ilp32d/lp64d when
// hardware support for floating point is present.
if (IsRV32E)
return ABI_ILP32E;
if (IsRV64)
return ABI_LP64;
return ABI_ILP32;
}
} // namespace RISCVABI

namespace RISCVFeatures {

void validate(const Triple &TT, const FeatureBitset &FeatureBits) {
if (TT.isArch64Bit() && FeatureBits[RISCV::FeatureRV32E])
report_fatal_error("RV32E can't be enabled for an RV64 target");
}

} // namespace RISCVFeatures

} // namespace llvm
8 changes: 8 additions & 0 deletions llvm/lib/Target/RISCV/Utils/RISCVBaseInfo.h
Original file line number Diff line number Diff line change
@@ -172,6 +172,14 @@ ABI computeTargetABI(const Triple &TT, FeatureBitset FeatureBits,

} // namespace RISCVABI

namespace RISCVFeatures {

// Validates if the given combination of features are valid for the target
// triple. Exits with report_fatal_error if not.
void validate(const Triple &TT, const FeatureBitset &FeatureBits);

} // namespace RISCVFeatures

} // namespace llvm

#endif
4 changes: 4 additions & 0 deletions llvm/test/CodeGen/RISCV/mattr-invalid-combination.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
; RUN: not llc -mtriple=riscv64 -mattr=+e < %s 2>&1 \
; RUN: | FileCheck -check-prefix=RV64E %s

; RV64E: LLVM ERROR: RV32E can't be enabled for an RV64 target
7 changes: 7 additions & 0 deletions llvm/test/CodeGen/RISCV/rv32e.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
; RUN: not llc -mtriple=riscv32 -mattr=+e < %s 2>&1 | FileCheck %s

; CHECK: LLVM ERROR: Codegen not yet implemented for RV32E

define void @nothing() nounwind {
ret void
}
7 changes: 7 additions & 0 deletions llvm/test/MC/RISCV/elf-flags.s
Original file line number Diff line number Diff line change
@@ -2,6 +2,9 @@
# RUN: llvm-mc -triple=riscv64 -filetype=obj < %s | llvm-readobj -file-headers - | FileCheck -check-prefixes=CHECK-RVI %s
# RUN: llvm-mc -triple=riscv32 -mattr=+c -filetype=obj < %s | llvm-readobj -file-headers - | FileCheck -check-prefixes=CHECK-RVIC %s
# RUN: llvm-mc -triple=riscv64 -mattr=+c -filetype=obj < %s | llvm-readobj -file-headers - | FileCheck -check-prefixes=CHECK-RVIC %s
# RUN: llvm-mc -triple=riscv32 -mattr=+e -filetype=obj < %s \
# RUN: | llvm-readobj -file-headers - \
# RUN: | FileCheck -check-prefix=CHECK-RVE %s

# CHECK-RVI: Flags [ (0x0)
# CHECK-RVI-NEXT: ]
@@ -10,4 +13,8 @@
# CHECK-RVIC-NEXT: EF_RISCV_RVC (0x1)
# CHECK-RVIC-NEXT: ]

# CHECK-RVE: Flags [ (0x8)
# CHECK-RVE-NEXT: EF_RISCV_RVE (0x8)
# CHECK-RVE-NEXT: ]

nop
4 changes: 4 additions & 0 deletions llvm/test/MC/RISCV/mattr-invalid-combination.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# RUN: not llvm-mc -triple riscv64 -mattr=+e < %s 2>&1 \
# RUN: | FileCheck %s -check-prefix=RV64E

# RV64E: LLVM ERROR: RV32E can't be enabled for an RV64 target
106 changes: 106 additions & 0 deletions llvm/test/MC/RISCV/rv32e-invalid.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# RUN: not llvm-mc -triple riscv32 -mattr=+e < %s 2>&1 | FileCheck %s
# RUN: llvm-mc -filetype=obj -triple=riscv32 < %s \
# RUN: | llvm-objdump -mattr=+e -riscv-no-aliases -d -r - \
# RUN: | FileCheck -check-prefix=CHECK-DIS %s

# Perform a simple sanity check that registers x16-x31 (and the equivalent
# ABI names) are rejected for RV32E, when both assembling and disassembling.


# CHECK-DIS: 37 18 00 00 <unknown>
# CHECK: :[[@LINE+1]]:5: error: invalid operand for instruction
lui x16, 1
# CHECK-DIS: b7 28 00 00 <unknown>
# CHECK: :[[@LINE+1]]:5: error: invalid operand for instruction
lui x17, 2
# CHECK-DIS: 37 39 00 00 <unknown>
# CHECK: :[[@LINE+1]]:5: error: invalid operand for instruction
lui x18, 3
# CHECK-DIS: b7 49 00 00 <unknown>
# CHECK: :[[@LINE+1]]:5: error: invalid operand for instruction
lui x19, 4
# CHECK-DIS: 37 5a 00 00 <unknown>
# CHECK: :[[@LINE+1]]:5: error: invalid operand for instruction
lui x20, 5
# CHECK-DIS: b7 6a 00 00 <unknown>
# CHECK: :[[@LINE+1]]:5: error: invalid operand for instruction
lui x21, 6
# CHECK-DIS: 37 7b 00 00 <unknown>
# CHECK: :[[@LINE+1]]:5: error: invalid operand for instruction
lui x22, 7
# CHECK-DIS: b7 8b 00 00 <unknown>
# CHECK: :[[@LINE+1]]:5: error: invalid operand for instruction
lui x23, 8
# CHECK-DIS: 37 9c 00 00 <unknown>
# CHECK: :[[@LINE+1]]:5: error: invalid operand for instruction
lui x24, 9
# CHECK-DIS: b7 ac 00 00 <unknown>
# CHECK: :[[@LINE+1]]:5: error: invalid operand for instruction
lui x25, 10
# CHECK-DIS: 37 bd 00 00 <unknown>
# CHECK: :[[@LINE+1]]:5: error: invalid operand for instruction
lui x26, 11
# CHECK-DIS: b7 cd 00 00 <unknown>
# CHECK: :[[@LINE+1]]:5: error: invalid operand for instruction
lui x27, 12
# CHECK-DIS: 37 de 00 00 <unknown>
# CHECK: :[[@LINE+1]]:5: error: invalid operand for instruction
lui x28, 13
# CHECK-DIS: b7 ee 00 00 <unknown>
# CHECK: :[[@LINE+1]]:5: error: invalid operand for instruction
lui x29, 14
# CHECK-DIS: 37 ff 00 00 <unknown>
# CHECK: :[[@LINE+1]]:5: error: invalid operand for instruction
lui x30, 15
# CHECK-DIS: b7 0f 01 00 <unknown>
# CHECK: :[[@LINE+1]]:5: error: invalid operand for instruction
lui x31, 16

# CHECK-DIS: 17 18 01 00 <unknown>
# CHECK: :[[@LINE+1]]:7: error: invalid operand for instruction
auipc a6, 17
# CHECK-DIS: 97 28 01 00 <unknown>
# CHECK: :[[@LINE+1]]:7: error: invalid operand for instruction
auipc a7, 18
# CHECK-DIS: 17 39 01 00 <unknown>
# CHECK: :[[@LINE+1]]:7: error: invalid operand for instruction
auipc s2, 19
# CHECK-DIS: 97 49 01 00 <unknown>
# CHECK: :[[@LINE+1]]:7: error: invalid operand for instruction
auipc s3, 20
# CHECK-DIS: 17 5a 01 00 <unknown>
# CHECK: :[[@LINE+1]]:7: error: invalid operand for instruction
auipc s4, 21
# CHECK-DIS: 97 6a 01 00 <unknown>
# CHECK: :[[@LINE+1]]:7: error: invalid operand for instruction
auipc s5, 22
# CHECK-DIS: 17 7b 01 00 <unknown>
# CHECK: :[[@LINE+1]]:7: error: invalid operand for instruction
auipc s6, 23
# CHECK-DIS: 97 8b 01 00 <unknown>
# CHECK: :[[@LINE+1]]:7: error: invalid operand for instruction
auipc s7, 24
# CHECK-DIS: 17 9c 01 00 <unknown>
# CHECK: :[[@LINE+1]]:7: error: invalid operand for instruction
auipc s8, 25
# CHECK-DIS: 97 ac 01 00 <unknown>
# CHECK: :[[@LINE+1]]:7: error: invalid operand for instruction
auipc s9, 26
# CHECK-DIS: 17 bd 01 00 <unknown>
# CHECK: :[[@LINE+1]]:7: error: invalid operand for instruction
auipc s10, 27
# CHECK-DIS: 97 cd 01 00 <unknown>
# CHECK: :[[@LINE+1]]:7: error: invalid operand for instruction
auipc s11, 28
# CHECK-DIS: 17 de 01 00 <unknown>
# CHECK: :[[@LINE+1]]:7: error: invalid operand for instruction
auipc t3, 29
# CHECK-DIS: 97 ee 01 00 <unknown>
# CHECK: :[[@LINE+1]]:7: error: invalid operand for instruction
auipc t4, 30
# CHECK-DIS: 17 ff 01 00 <unknown>
# CHECK: :[[@LINE+1]]:7: error: invalid operand for instruction
auipc t5, 31
# CHECK-DIS: 97 0f 02 00 <unknown>
# CHECK: :[[@LINE+1]]:7: error: invalid operand for instruction
auipc t6, 32
Loading

0 comments on commit dab1f6f

Please sign in to comment.