[hip][cuda] Enable extended lambda support on Windows.

- On Windows, extended lambda has extra issues due to the numbering
  schemes are different between the host compilation (Microsoft C++ ABI)
  and the device compilation (Itanium C++ ABI. Additional device side
  lambda number is required per lambda for the host compilation to
  correctly mangle the device-side lambda name.
- A hybrid numbering context `MSHIPNumberingContext` is introduced to
  number a lambda for both host- and device-compilations.

Reviewed By: rnk

Differential Revision: https://reviews.llvm.org/D69322
This commit is contained in:
Michael Liao 2019-10-11 17:15:26 -04:00
parent ac14bb14e7
commit a2fdf9d4d7
18 changed files with 131 additions and 17 deletions

View File

@ -538,6 +538,9 @@ private:
/// need them (like static local vars).
llvm::MapVector<const NamedDecl *, unsigned> MangleNumbers;
llvm::MapVector<const VarDecl *, unsigned> StaticLocalNumbers;
/// Mapping the associated device lambda mangling number if present.
mutable llvm::DenseMap<const CXXRecordDecl *, unsigned>
DeviceLambdaManglingNumbers;
/// Mapping that stores parameterIndex values for ParmVarDecls when
/// that value exceeds the bitfield size of ParmVarDeclBits.ParameterIndex.

View File

@ -1735,6 +1735,12 @@ public:
getLambdaData().HasKnownInternalLinkage = HasKnownInternalLinkage;
}
/// Set the device side mangling number.
void setDeviceLambdaManglingNumber(unsigned Num) const;
/// Retrieve the device side mangling number.
unsigned getDeviceLambdaManglingNumber() const;
/// Returns the inheritance model used for this record.
MSInheritanceModel getMSInheritanceModel() const;

View File

@ -107,6 +107,9 @@ public:
virtual bool shouldMangleCXXName(const NamedDecl *D) = 0;
virtual bool shouldMangleStringLiteral(const StringLiteral *SL) = 0;
virtual bool isDeviceMangleContext() const { return false; }
virtual void setDeviceMangleContext(bool) {}
// FIXME: consider replacing raw_ostream & with something like SmallString &.
void mangleName(GlobalDecl GD, raw_ostream &);
virtual void mangleCXXName(GlobalDecl GD, raw_ostream &) = 0;

View File

@ -52,6 +52,11 @@ public:
/// this context.
virtual unsigned getManglingNumber(const TagDecl *TD,
unsigned MSLocalManglingNumber) = 0;
/// Retrieve the mangling number of a new lambda expression with the
/// given call operator within the device context. No device number is
/// assigned if there's no device numbering context is associated.
virtual unsigned getDeviceManglingNumber(const CXXMethodDecl *) { return 0; }
};
} // end namespace clang

View File

@ -6558,7 +6558,7 @@ public:
/// Number lambda for linkage purposes if necessary.
void handleLambdaNumbering(
CXXRecordDecl *Class, CXXMethodDecl *Method,
Optional<std::tuple<unsigned, bool, Decl *>> Mangling = None);
Optional<std::tuple<bool, unsigned, unsigned, Decl *>> Mangling = None);
/// Endow the lambda scope info with the relevant properties.
void buildLambdaScope(sema::LambdaScopeInfo *LSI,

View File

@ -2848,6 +2848,8 @@ ExpectedDecl ASTNodeImporter::VisitRecordDecl(RecordDecl *D) {
return CDeclOrErr.takeError();
D2CXX->setLambdaMangling(DCXX->getLambdaManglingNumber(), *CDeclOrErr,
DCXX->hasKnownLambdaInternalLinkage());
D2CXX->setDeviceLambdaManglingNumber(
DCXX->getDeviceLambdaManglingNumber());
} else if (DCXX->isInjectedClassName()) {
// We have to be careful to do a similar dance to the one in
// Sema::ActOnStartCXXMemberDeclarations

View File

@ -22,8 +22,9 @@ class ASTContext;
class CXXConstructorDecl;
class DeclaratorDecl;
class Expr;
class MemberPointerType;
class MangleContext;
class MangleNumberingContext;
class MemberPointerType;
/// Implements C++ ABI-specific semantic analysis functions.
class CXXABI {
@ -75,6 +76,8 @@ public:
/// Creates an instance of a C++ ABI class.
CXXABI *CreateItaniumCXXABI(ASTContext &Ctx);
CXXABI *CreateMicrosoftCXXABI(ASTContext &Ctx);
std::unique_ptr<MangleNumberingContext>
createItaniumNumberingContext(MangleContext *);
}
#endif

View File

@ -1593,6 +1593,20 @@ Decl *CXXRecordDecl::getLambdaContextDecl() const {
return getLambdaData().ContextDecl.get(Source);
}
void CXXRecordDecl::setDeviceLambdaManglingNumber(unsigned Num) const {
assert(isLambda() && "Not a lambda closure type!");
if (Num)
getASTContext().DeviceLambdaManglingNumbers[this] = Num;
}
unsigned CXXRecordDecl::getDeviceLambdaManglingNumber() const {
assert(isLambda() && "Not a lambda closure type!");
auto I = getASTContext().DeviceLambdaManglingNumbers.find(this);
if (I != getASTContext().DeviceLambdaManglingNumbers.end())
return I->second;
return 0;
}
static CanQualType GetConversionType(ASTContext &Context, NamedDecl *Conv) {
QualType T =
cast<CXXConversionDecl>(Conv->getUnderlyingDecl()->getAsFunction())

View File

@ -258,3 +258,9 @@ public:
CXXABI *clang::CreateItaniumCXXABI(ASTContext &Ctx) {
return new ItaniumCXXABI(Ctx);
}
std::unique_ptr<MangleNumberingContext>
clang::createItaniumNumberingContext(MangleContext *Mangler) {
return std::make_unique<ItaniumNumberingContext>(
cast<ItaniumMangleContext>(Mangler));
}

View File

@ -125,6 +125,8 @@ class ItaniumMangleContextImpl : public ItaniumMangleContext {
llvm::DenseMap<DiscriminatorKeyTy, unsigned> Discriminator;
llvm::DenseMap<const NamedDecl*, unsigned> Uniquifier;
bool IsDevCtx = false;
public:
explicit ItaniumMangleContextImpl(ASTContext &Context,
DiagnosticsEngine &Diags)
@ -137,6 +139,10 @@ public:
bool shouldMangleStringLiteral(const StringLiteral *) override {
return false;
}
bool isDeviceMangleContext() const override { return IsDevCtx; }
void setDeviceMangleContext(bool IsDev) override { IsDevCtx = IsDev; }
void mangleCXXName(GlobalDecl GD, raw_ostream &) override;
void mangleThunk(const CXXMethodDecl *MD, const ThunkInfo &Thunk,
raw_ostream &) override;
@ -1876,7 +1882,15 @@ void CXXNameMangler::mangleLambda(const CXXRecordDecl *Lambda) {
// (in lexical order) with that same <lambda-sig> and context.
//
// The AST keeps track of the number for us.
unsigned Number = Lambda->getLambdaManglingNumber();
//
// In CUDA/HIP, to ensure the consistent lamba numbering between the device-
// and host-side compilations, an extra device mangle context may be created
// if the host-side CXX ABI has different numbering for lambda. In such case,
// if the mangle context is that device-side one, use the device-side lambda
// mangling number for this lambda.
unsigned Number = Context.isDeviceMangleContext()
? Lambda->getDeviceLambdaManglingNumber()
: Lambda->getLambdaManglingNumber();
assert(Number > 0 && "Lambda should be mangled as an unnamed class");
if (Number > 1)
mangleNumber(Number - 2);

View File

@ -16,6 +16,7 @@
#include "clang/AST/Attr.h"
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/Mangle.h"
#include "clang/AST/MangleNumberingContext.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/Type.h"
@ -64,6 +65,19 @@ public:
}
};
class MSHIPNumberingContext : public MicrosoftNumberingContext {
std::unique_ptr<MangleNumberingContext> DeviceCtx;
public:
MSHIPNumberingContext(MangleContext *Mangler) {
DeviceCtx = createItaniumNumberingContext(Mangler);
}
unsigned getDeviceManglingNumber(const CXXMethodDecl *CallOperator) override {
return DeviceCtx->getManglingNumber(CallOperator);
}
};
class MicrosoftCXXABI : public CXXABI {
ASTContext &Context;
llvm::SmallDenseMap<CXXRecordDecl *, CXXConstructorDecl *> RecordToCopyCtor;
@ -73,8 +87,19 @@ class MicrosoftCXXABI : public CXXABI {
llvm::SmallDenseMap<TagDecl *, TypedefNameDecl *>
UnnamedTagDeclToTypedefNameDecl;
// MangleContext for device numbering context, which is based on Itanium C++
// ABI.
std::unique_ptr<MangleContext> Mangler;
public:
MicrosoftCXXABI(ASTContext &Ctx) : Context(Ctx) { }
MicrosoftCXXABI(ASTContext &Ctx) : Context(Ctx) {
if (Context.getLangOpts().CUDA) {
assert(Context.getTargetInfo().getCXXABI().isMicrosoft() &&
Context.getAuxTargetInfo()->getCXXABI().isItaniumFamily() &&
"Unexpected combination of C++ ABIs.");
Mangler.reset(Context.createMangleContext(Context.getAuxTargetInfo()));
}
}
MemberPointerInfo
getMemberPointerInfo(const MemberPointerType *MPT) const override;
@ -133,6 +158,8 @@ public:
std::unique_ptr<MangleNumberingContext>
createMangleNumberingContext() const override {
if (Context.getLangOpts().CUDA)
return std::make_unique<MSHIPNumberingContext>(Mangler.get());
return std::make_unique<MicrosoftNumberingContext>();
}
};
@ -266,4 +293,3 @@ CXXABI::MemberPointerInfo MicrosoftCXXABI::getMemberPointerInfo(
CXXABI *clang::CreateMicrosoftCXXABI(ASTContext &Ctx) {
return new MicrosoftCXXABI(Ctx);
}

View File

@ -190,6 +190,12 @@ CGNVCUDARuntime::CGNVCUDARuntime(CodeGenModule &CGM)
CharPtrTy = llvm::PointerType::getUnqual(Types.ConvertType(Ctx.CharTy));
VoidPtrTy = cast<llvm::PointerType>(Types.ConvertType(Ctx.VoidPtrTy));
VoidPtrPtrTy = VoidPtrTy->getPointerTo();
// If the host and device have different C++ ABIs, mark it as the device
// mangle context so that the mangling needs to retrieve the additonal device
// lambda mangling number instead of the regular host one.
DeviceMC->setDeviceMangleContext(
CGM.getContext().getTargetInfo().getCXXABI().isMicrosoft() &&
CGM.getContext().getAuxTargetInfo()->getCXXABI().isItaniumFamily());
}
llvm::FunctionCallee CGNVCUDARuntime::getSetupArgumentFn() const {

View File

@ -429,15 +429,16 @@ CXXMethodDecl *Sema::startLambdaDefinition(CXXRecordDecl *Class,
void Sema::handleLambdaNumbering(
CXXRecordDecl *Class, CXXMethodDecl *Method,
Optional<std::tuple<unsigned, bool, Decl *>> Mangling) {
Optional<std::tuple<bool, unsigned, unsigned, Decl *>> Mangling) {
if (Mangling) {
unsigned ManglingNumber;
bool HasKnownInternalLinkage;
unsigned ManglingNumber, DeviceManglingNumber;
Decl *ManglingContextDecl;
std::tie(ManglingNumber, HasKnownInternalLinkage, ManglingContextDecl) =
Mangling.getValue();
std::tie(HasKnownInternalLinkage, ManglingNumber, DeviceManglingNumber,
ManglingContextDecl) = Mangling.getValue();
Class->setLambdaMangling(ManglingNumber, ManglingContextDecl,
HasKnownInternalLinkage);
Class->setDeviceLambdaManglingNumber(DeviceManglingNumber);
return;
}
@ -473,6 +474,7 @@ void Sema::handleLambdaNumbering(
unsigned ManglingNumber = MCtx->getManglingNumber(Method);
Class->setLambdaMangling(ManglingNumber, ManglingContextDecl,
HasKnownInternalLinkage);
Class->setDeviceLambdaManglingNumber(MCtx->getDeviceManglingNumber(Method));
}
}

View File

@ -12504,10 +12504,11 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
E->getCaptureDefault());
getDerived().transformedLocalDecl(OldClass, {Class});
Optional<std::tuple<unsigned, bool, Decl *>> Mangling;
Optional<std::tuple<bool, unsigned, unsigned, Decl *>> Mangling;
if (getDerived().ReplacingOriginal())
Mangling = std::make_tuple(OldClass->getLambdaManglingNumber(),
OldClass->hasKnownLambdaInternalLinkage(),
Mangling = std::make_tuple(OldClass->hasKnownLambdaInternalLinkage(),
OldClass->getLambdaManglingNumber(),
OldClass->getDeviceLambdaManglingNumber(),
OldClass->getLambdaContextDecl());
// Build the call operator.

View File

@ -1748,6 +1748,7 @@ void ASTDeclReader::ReadCXXDefinitionData(
Lambda.NumExplicitCaptures = Record.readInt();
Lambda.HasKnownInternalLinkage = Record.readInt();
Lambda.ManglingNumber = Record.readInt();
D->setDeviceLambdaManglingNumber(Record.readInt());
Lambda.ContextDecl = readDeclID();
Lambda.Captures = (Capture *)Reader.getContext().Allocate(
sizeof(Capture) * Lambda.NumCaptures);

View File

@ -5663,6 +5663,7 @@ void ASTRecordWriter::AddCXXDefinitionData(const CXXRecordDecl *D) {
Record->push_back(Lambda.NumExplicitCaptures);
Record->push_back(Lambda.HasKnownInternalLinkage);
Record->push_back(Lambda.ManglingNumber);
Record->push_back(D->getDeviceLambdaManglingNumber());
AddDeclRef(D->getLambdaContextDecl());
AddTypeSourceInfo(Lambda.MethodTyInfo);
for (unsigned I = 0, N = Lambda.NumCaptures; I != N; ++I) {

View File

@ -2,12 +2,12 @@
// RUN: -fno-autolink -triple amdgcn-amd-amdhsa \
// RUN: | FileCheck -check-prefix=DEV %s
// RUN: %clang_cc1 -emit-llvm -o - -fms-extensions -x hip %s -triple \
// RUN: x86_64-pc-windows-msvc | FileCheck -check-prefix=HOST %s
// RUN: x86_64-pc-windows-msvc -aux-triple amdgcn | FileCheck -check-prefix=HOST %s
// RUN: %clang_cc1 -emit-llvm -o - -fcuda-is-device -fms-extensions %s \
// RUN: -fno-autolink -triple amdgcn-amd-amdhsa \
// RUN: | FileCheck -check-prefix=DEV %s
// RUN: %clang_cc1 -emit-llvm -o - -fms-extensions %s -triple \
// RUN: x86_64-pc-windows-msvc | FileCheck -check-prefix=HOST %s
// RUN: x86_64-pc-windows-msvc -aux-triple amdgcn | FileCheck -check-prefix=HOST %s
// DEV-NOT: llvm.linker.options
// DEV-NOT: llvm.dependent-libraries

View File

@ -1,12 +1,17 @@
// RUN: %clang_cc1 -std=c++11 -x hip -triple x86_64-linux-gnu -aux-triple amdgcn-amd-amdhsa -emit-llvm %s -o - | FileCheck %s --check-prefix=HOST
// RUN: %clang_cc1 -std=c++11 -x hip -triple x86_64-pc-windows-msvc -aux-triple amdgcn-amd-amdhsa -emit-llvm %s -o - | FileCheck %s --check-prefix=MSVC
// RUN: %clang_cc1 -std=c++11 -x hip -triple amdgcn-amd-amdhsa -fcuda-is-device -emit-llvm %s -o - | FileCheck %s --check-prefix=DEVICE
#include "Inputs/cuda.h"
// HOST: @0 = private unnamed_addr constant [43 x i8] c"_Z2k0IZZ2f1PfENKUlS0_E_clES0_EUlfE_EvS0_T_\00", align 1
// HOST: @1 = private unnamed_addr constant [60 x i8] c"_Z2k1IZ2f1PfEUlfE_Z2f1S0_EUlffE_Z2f1S0_EUlfE0_EvS0_T_T0_T1_\00", align 1
// Check that, on MSVC, the same device kernel mangling name is generated.
// MSVC: @0 = private unnamed_addr constant [43 x i8] c"_Z2k0IZZ2f1PfENKUlS0_E_clES0_EUlfE_EvS0_T_\00", align 1
// MSVC: @1 = private unnamed_addr constant [60 x i8] c"_Z2k1IZ2f1PfEUlfE_Z2f1S0_EUlffE_Z2f1S0_EUlfE0_EvS0_T_T0_T1_\00", align 1
__device__ float d0(float x) {
return [](float x) { return x + 2.f; }(x);
return [](float x) { return x + 1.f; }(x);
}
__device__ float d1(float x) {
@ -14,11 +19,21 @@ __device__ float d1(float x) {
}
// DEVICE: amdgpu_kernel void @_Z2k0IZZ2f1PfENKUlS0_E_clES0_EUlfE_EvS0_T_(
// DEVICE: define internal float @_ZZZ2f1PfENKUlS_E_clES_ENKUlfE_clEf(
template <typename F>
__global__ void k0(float *p, F f) {
p[0] = f(p[0]) + d0(p[1]) + d1(p[2]);
}
// DEVICE: amdgpu_kernel void @_Z2k1IZ2f1PfEUlfE_Z2f1S0_EUlffE_Z2f1S0_EUlfE0_EvS0_T_T0_T1_(
// DEVICE: define internal float @_ZZ2f1PfENKUlfE_clEf(
// DEVICE: define internal float @_ZZ2f1PfENKUlffE_clEff(
// DEVICE: define internal float @_ZZ2f1PfENKUlfE0_clEf(
template <typename F0, typename F1, typename F2>
__global__ void k1(float *p, F0 f0, F1 f1, F2 f2) {
p[0] = f0(p[0]) + f1(p[1], p[2]) + f2(p[3]);
}
void f0(float *p) {
[](float *p) {
*p = 1.f;
@ -29,11 +44,17 @@ void f0(float *p) {
// linkages are still required to keep the original `internal` linkage.
// HOST: define internal void @_ZZ2f1PfENKUlS_E_clES_(
// DEVICE: define internal float @_ZZZ2f1PfENKUlS_E_clES_ENKUlfE_clEf(
void f1(float *p) {
[](float *p) {
k0<<<1,1>>>(p, [] __device__ (float x) { return x + 1.f; });
k0<<<1,1>>>(p, [] __device__ (float x) { return x + 3.f; });
}(p);
k1<<<1,1>>>(p,
[] __device__ (float x) { return x + 4.f; },
[] __device__ (float x, float y) { return x * y; },
[] __device__ (float x) { return x + 5.f; });
}
// HOST: @__hip_register_globals
// HOST: __hipRegisterFunction{{.*}}@_Z17__device_stub__k0IZZ2f1PfENKUlS0_E_clES0_EUlfE_EvS0_T_{{.*}}@0
// HOST: __hipRegisterFunction{{.*}}@_Z17__device_stub__k1IZ2f1PfEUlfE_Z2f1S0_EUlffE_Z2f1S0_EUlfE0_EvS0_T_T0_T1_{{.*}}@1
// MSVC: __hipRegisterFunction{{.*}}@"??$k0@V<lambda_1>@?0???R1?0??f1@@YAXPEAM@Z@QEBA@0@Z@@@YAXPEAMV<lambda_1>@?0???R0?0??f1@@YAX0@Z@QEBA@0@Z@@Z{{.*}}@0
// MSVC: __hipRegisterFunction{{.*}}@"??$k1@V<lambda_2>@?0??f1@@YAXPEAM@Z@V<lambda_3>@?0??2@YAX0@Z@V<lambda_4>@?0??2@YAX0@Z@@@YAXPEAMV<lambda_2>@?0??f1@@YAX0@Z@V<lambda_3>@?0??1@YAX0@Z@V<lambda_4>@?0??1@YAX0@Z@@Z{{.*}}@1