Skip to content

Commit

Permalink
[RISCV] CodeGen of RVE and ilp32e/lp64e ABIs (#76777)
Browse files Browse the repository at this point in the history
This commit includes the necessary changes to clang and LLVM to support
codegen of `RVE` and the `ilp32e`/`lp64e` ABIs.

The differences between `RVE` and `RVI` are:
* `RVE` reduces the integer register count to 16(x0-x16).
* The ABI should be `ilp32e` for 32 bits and `lp64e` for 64 bits.

`RVE` can be combined with all current standard extensions.

The central changes in ilp32e/lp64e ABI, compared to ilp32/lp64 are:
* Only 6 integer argument registers (rather than 8).
* Only 2 callee-saved registers (rather than 12).
* A Stack Alignment of 32bits (rather than 128bits).
* ilp32e isn't compatible with D ISA extension.

If `ilp32e` or `lp64` is used with an ISA that has any of the registers
x16-x31 and f0-f31, then these registers are considered temporaries.

To be compatible with the implementation of ilp32e in GCC, we don't use
aligned registers to pass variadic arguments and set stack alignment\
to 4-bytes for types with length of 2*XLEN.

FastCC is also supported on RVE, while GHC isn't since there is only one
avaiable register.

Differential Revision: https://reviews.llvm.org/D70401
  • Loading branch information
wangpc-pp committed Jan 16, 2024
1 parent e2bb47c commit 3ac9fe6
Show file tree
Hide file tree
Showing 45 changed files with 7,889 additions and 234 deletions.
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Expand Up @@ -1060,6 +1060,8 @@ RISC-V Support
^^^^^^^^^^^^^^
- Unaligned memory accesses can be toggled by ``-m[no-]unaligned-access`` or the
aliases ``-m[no-]strict-align``.
- CodeGen of RV32E/RV64E was supported experimentally.
- CodeGen of ilp32e/lp64e was supported experimentally.

- Default ABI with F but without D was changed to ilp32f for RV32 and to lp64f
for RV64.
Expand Down
14 changes: 13 additions & 1 deletion clang/lib/Basic/Targets/RISCV.cpp
Expand Up @@ -154,7 +154,7 @@ void RISCVTargetInfo::getTargetDefines(const LangOptions &Opts,
else
Builder.defineMacro("__riscv_float_abi_soft");

if (ABIName == "ilp32e")
if (ABIName == "ilp32e" || ABIName == "lp64e")
Builder.defineMacro("__riscv_abi_rve");

Builder.defineMacro("__riscv_arch_test");
Expand Down Expand Up @@ -214,6 +214,13 @@ void RISCVTargetInfo::getTargetDefines(const LangOptions &Opts,
Builder.defineMacro("__riscv_misaligned_fast");
else
Builder.defineMacro("__riscv_misaligned_avoid");

if (ISAInfo->hasExtension("e")) {
if (Is64Bit)
Builder.defineMacro("__riscv_64e");
else
Builder.defineMacro("__riscv_32e");
}
}

static constexpr Builtin::Info BuiltinInfo[] = {
Expand Down Expand Up @@ -378,6 +385,11 @@ bool RISCVTargetInfo::handleTargetFeatures(std::vector<std::string> &Features,
if (llvm::is_contained(Features, "+experimental"))
HasExperimental = true;

if (ABI == "ilp32e" && ISAInfo->hasExtension("d")) {
Diags.Report(diag::err_invalid_feature_combination)
<< "ILP32E cannot be used with the D ISA extension";
return false;
}
return true;
}

Expand Down
12 changes: 12 additions & 0 deletions clang/lib/Basic/Targets/RISCV.h
Expand Up @@ -132,6 +132,12 @@ class LLVM_LIBRARY_VISIBILITY RISCV32TargetInfo : public RISCVTargetInfo {
}

bool setABI(const std::string &Name) override {
if (Name == "ilp32e") {
ABI = Name;
resetDataLayout("e-m:e-p:32:32-i64:64-n32-S32");
return true;
}

if (Name == "ilp32" || Name == "ilp32f" || Name == "ilp32d") {
ABI = Name;
return true;
Expand All @@ -156,6 +162,12 @@ class LLVM_LIBRARY_VISIBILITY RISCV64TargetInfo : public RISCVTargetInfo {
}

bool setABI(const std::string &Name) override {
if (Name == "lp64e") {
ABI = Name;
resetDataLayout("e-m:e-p:64:64-i64:64-i128:128-n32:64-S64");
return true;
}

if (Name == "lp64" || Name == "lp64f" || Name == "lp64d") {
ABI = Name;
return true;
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/CodeGen/CodeGenModule.cpp
Expand Up @@ -229,7 +229,8 @@ createTargetCodeGenInfo(CodeGenModule &CGM) {
ABIFLen = 32;
else if (ABIStr.ends_with("d"))
ABIFLen = 64;
return createRISCVTargetCodeGenInfo(CGM, XLen, ABIFLen);
bool EABI = ABIStr.ends_with("e");
return createRISCVTargetCodeGenInfo(CGM, XLen, ABIFLen, EABI);
}

case llvm::Triple::systemz: {
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/CodeGen/TargetInfo.h
Expand Up @@ -496,7 +496,8 @@ createPPC64_SVR4_TargetCodeGenInfo(CodeGenModule &CGM, PPC64_SVR4_ABIKind Kind,
bool SoftFloatABI);

std::unique_ptr<TargetCodeGenInfo>
createRISCVTargetCodeGenInfo(CodeGenModule &CGM, unsigned XLen, unsigned FLen);
createRISCVTargetCodeGenInfo(CodeGenModule &CGM, unsigned XLen, unsigned FLen,
bool EABI);

std::unique_ptr<TargetCodeGenInfo>
createCommonSPIRTargetCodeGenInfo(CodeGenModule &CGM);
Expand Down
35 changes: 25 additions & 10 deletions clang/lib/CodeGen/Targets/RISCV.cpp
Expand Up @@ -25,17 +25,20 @@ class RISCVABIInfo : public DefaultABIInfo {
// ISA might have a wider FLen than the selected ABI (e.g. an RV32IF target
// with soft float ABI has FLen==0).
unsigned FLen;
static const int NumArgGPRs = 8;
static const int NumArgFPRs = 8;
const int NumArgGPRs;
const int NumArgFPRs;
const bool EABI;
bool detectFPCCEligibleStructHelper(QualType Ty, CharUnits CurOff,
llvm::Type *&Field1Ty,
CharUnits &Field1Off,
llvm::Type *&Field2Ty,
CharUnits &Field2Off) const;

public:
RISCVABIInfo(CodeGen::CodeGenTypes &CGT, unsigned XLen, unsigned FLen)
: DefaultABIInfo(CGT), XLen(XLen), FLen(FLen) {}
RISCVABIInfo(CodeGen::CodeGenTypes &CGT, unsigned XLen, unsigned FLen,
bool EABI)
: DefaultABIInfo(CGT), XLen(XLen), FLen(FLen), NumArgGPRs(EABI ? 6 : 8),
NumArgFPRs(FLen != 0 ? 8 : 0), EABI(EABI) {}

// DefaultABIInfo's classifyReturnType and classifyArgumentType are
// non-virtual, but computeInfo is virtual, so we overload it.
Expand Down Expand Up @@ -86,7 +89,7 @@ void RISCVABIInfo::computeInfo(CGFunctionInfo &FI) const {
}

int ArgGPRsLeft = IsRetIndirect ? NumArgGPRs - 1 : NumArgGPRs;
int ArgFPRsLeft = FLen ? NumArgFPRs : 0;
int ArgFPRsLeft = NumArgFPRs;
int NumFixedArgs = FI.getNumRequiredArgs();

int ArgNum = 0;
Expand Down Expand Up @@ -396,9 +399,12 @@ ABIArgInfo RISCVABIInfo::classifyArgumentType(QualType Ty, bool IsFixed,
// Determine the number of GPRs needed to pass the current argument
// according to the ABI. 2*XLen-aligned varargs are passed in "aligned"
// register pairs, so may consume 3 registers.
// TODO: To be compatible with GCC's behaviors, we don't align registers
// currently if we are using ILP32E calling convention. This behavior may be
// changed when RV32E/ILP32E is ratified.
int NeededArgGPRs = 1;
if (!IsFixed && NeededAlign == 2 * XLen)
NeededArgGPRs = 2 + (ArgGPRsLeft % 2);
NeededArgGPRs = 2 + (EABI && XLen == 32 ? 0 : (ArgGPRsLeft % 2));
else if (Size > XLen && Size <= 2 * XLen)
NeededArgGPRs = 2;

Expand Down Expand Up @@ -480,6 +486,13 @@ Address RISCVABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,

auto TInfo = getContext().getTypeInfoInChars(Ty);

// TODO: To be compatible with GCC's behaviors, we force arguments with
// 2×XLEN-bit alignment and size at most 2×XLEN bits like `long long`,
// `unsigned long long` and `double` to have 4-byte alignment. This
// behavior may be changed when RV32E/ILP32E is ratified.
if (EABI && XLen == 32)
TInfo.Align = std::min(TInfo.Align, CharUnits::fromQuantity(4));

// Arguments bigger than 2*Xlen bytes are passed indirectly.
bool IsIndirect = TInfo.Width > 2 * SlotSize;

Expand All @@ -499,8 +512,9 @@ namespace {
class RISCVTargetCodeGenInfo : public TargetCodeGenInfo {
public:
RISCVTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT, unsigned XLen,
unsigned FLen)
: TargetCodeGenInfo(std::make_unique<RISCVABIInfo>(CGT, XLen, FLen)) {}
unsigned FLen, bool EABI)
: TargetCodeGenInfo(
std::make_unique<RISCVABIInfo>(CGT, XLen, FLen, EABI)) {}

void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV,
CodeGen::CodeGenModule &CGM) const override {
Expand All @@ -526,6 +540,7 @@ class RISCVTargetCodeGenInfo : public TargetCodeGenInfo {

std::unique_ptr<TargetCodeGenInfo>
CodeGen::createRISCVTargetCodeGenInfo(CodeGenModule &CGM, unsigned XLen,
unsigned FLen) {
return std::make_unique<RISCVTargetCodeGenInfo>(CGM.getTypes(), XLen, FLen);
unsigned FLen, bool EABI) {
return std::make_unique<RISCVTargetCodeGenInfo>(CGM.getTypes(), XLen, FLen,
EABI);
}
4 changes: 4 additions & 0 deletions clang/lib/Driver/ToolChains/Arch/RISCV.cpp
Expand Up @@ -210,6 +210,7 @@ StringRef riscv::getRISCVABI(const ArgList &Args, const llvm::Triple &Triple) {
// rv32e -> ilp32e
// rv32* -> ilp32
// rv64g | rv64*d -> lp64d
// rv64e -> lp64e
// rv64* -> lp64
StringRef Arch = getRISCVArch(Args, Triple);

Expand Down Expand Up @@ -285,13 +286,16 @@ StringRef riscv::getRISCVArch(const llvm::opt::ArgList &Args,
// 3. Choose a default based on `-mabi=`
//
// ilp32e -> rv32e
// lp64e -> rv64e
// ilp32 | ilp32f | ilp32d -> rv32imafdc
// lp64 | lp64f | lp64d -> rv64imafdc
if (const Arg *A = Args.getLastArg(options::OPT_mabi_EQ)) {
StringRef MABI = A->getValue();

if (MABI.equals_insensitive("ilp32e"))
return "rv32e";
else if (MABI.equals_insensitive("lp64e"))
return "rv64e";
else if (MABI.starts_with_insensitive("ilp32"))
return "rv32imafdc";
else if (MABI.starts_with_insensitive("lp64")) {
Expand Down
3 changes: 3 additions & 0 deletions clang/test/CodeGen/RISCV/riscv32-abi.c
Expand Up @@ -5,6 +5,8 @@
// RUN: | FileCheck -check-prefixes=ILP32-ILP32F-ILP32D,ILP32F-ILP32D,ILP32-ILP32F,ILP32F %s
// RUN: %clang_cc1 -triple riscv32 -target-feature +f -target-feature +d -target-abi ilp32d -emit-llvm %s -o - \
// RUN: | FileCheck -check-prefixes=ILP32-ILP32F-ILP32D,ILP32F-ILP32D,ILP32D %s
// RUN: %clang_cc1 -triple riscv32 -emit-llvm -target-abi ilp32e %s -o - \
// RUN: | FileCheck -check-prefixes=ILP32-ILP32F-ILP32D,ILP32-ILP32F,ILP32,ILP32E %s

#include <stddef.h>
#include <stdint.h>
Expand Down Expand Up @@ -2064,4 +2066,5 @@ union float16_u f_ret_float16_u(void) {
}

//// NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
// ILP32E: {{.*}}
// ILP32F: {{.*}}
4 changes: 4 additions & 0 deletions clang/test/CodeGen/RISCV/riscv32-ilp32e-error.c
@@ -0,0 +1,4 @@
// RUN: not %clang_cc1 -triple riscv32 -target-feature +d -emit-llvm -target-abi ilp32e %s 2>&1 \
// RUN: | FileCheck -check-prefix=ILP32E-WITH-FD %s

// ILP32E-WITH-FD: error: invalid feature combination: ILP32E cannot be used with the D ISA extension

0 comments on commit 3ac9fe6

Please sign in to comment.