84 changes: 57 additions & 27 deletions clang/include/clang/ExtractAPI/DeclarationFragments.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/Specifiers.h"
#include "clang/Lex/MacroInfo.h"
#include <iterator>
#include <utility>
#include <vector>

namespace clang {
Expand Down Expand Up @@ -113,28 +115,26 @@ class DeclarationFragments {

ConstFragmentIterator cend() const { return Fragments.cend(); }

// Add a new Fragment at an arbitrary offset.
DeclarationFragments &insert(FragmentIterator It, StringRef Spelling,
FragmentKind Kind,
StringRef PreciseIdentifier = "",
const Decl *Declaration = nullptr) {
Fragments.insert(It,
Fragment(Spelling, Kind, PreciseIdentifier, Declaration));
return *this;
/// Prepend another DeclarationFragments to the beginning.
///
/// \returns a reference to the DeclarationFragments object itself after
/// appending to chain up consecutive operations.
DeclarationFragments &prepend(DeclarationFragments Other) {
return insert(begin(), std::move(Other));
}

DeclarationFragments &insert(FragmentIterator It,
DeclarationFragments &&Other) {
Fragments.insert(It, std::make_move_iterator(Other.Fragments.begin()),
std::make_move_iterator(Other.Fragments.end()));
Other.Fragments.clear();
return *this;
/// Append another DeclarationFragments to the end.
///
/// \returns a reference to the DeclarationFragments object itself after
/// appending to chain up consecutive operations.
DeclarationFragments &append(DeclarationFragments Other) {
return insert(end(), std::move(Other));
}

/// Append a new Fragment to the end of the Fragments.
///
/// \returns a reference to the DeclarationFragments object itself after
/// appending to chain up consecutive appends.
/// appending to chain up consecutive operations.
DeclarationFragments &append(StringRef Spelling, FragmentKind Kind,
StringRef PreciseIdentifier = "",
const Decl *Declaration = nullptr) {
Expand All @@ -149,18 +149,48 @@ class DeclarationFragments {
return *this;
}

/// Append another DeclarationFragments to the end.
///
/// Note: \p Other is moved from and cannot be used after a call to this
/// method.
/// Inserts another DeclarationFragments at \p It.
///
/// \returns a reference to the DeclarationFragments object itself after
/// appending to chain up consecutive appends.
DeclarationFragments &append(DeclarationFragments &&Other) {
Fragments.insert(Fragments.end(),
std::make_move_iterator(Other.Fragments.begin()),
std::make_move_iterator(Other.Fragments.end()));
Other.Fragments.clear();
/// appending to chain up consecutive operations.
DeclarationFragments &insert(FragmentIterator It,
DeclarationFragments Other) {
if (Other.Fragments.empty())
return *this;

if (Fragments.empty()) {
Fragments = std::move(Other.Fragments);
return *this;
}

const auto &OtherFrags = Other.Fragments;
auto ToInsertBegin = std::make_move_iterator(Other.begin());
auto ToInsertEnd = std::make_move_iterator(Other.end());

// If we aren't inserting at the end let's make sure that we merge their
// last fragment with It if both are text fragments.
if (It != end() && It->Kind == FragmentKind::Text &&
OtherFrags.back().Kind == FragmentKind::Text) {
auto &TheirBackSpelling = OtherFrags.back().Spelling;
It->Spelling.reserve(It->Spelling.size() + TheirBackSpelling.size());
It->Spelling.insert(It->Spelling.begin(), TheirBackSpelling.begin(),
TheirBackSpelling.end());
--ToInsertEnd;
}

// If we aren't inserting at the beginning we want to merge their first
// fragment with the fragment before It if both are text fragments.
if (It != begin() && std::prev(It)->Kind == FragmentKind::Text &&
OtherFrags.front().Kind == FragmentKind::Text) {
auto PrevIt = std::prev(It);
auto &TheirFrontSpelling = OtherFrags.front().Spelling;
PrevIt->Spelling.reserve(PrevIt->Spelling.size() +
TheirFrontSpelling.size());
PrevIt->Spelling.append(TheirFrontSpelling);
++ToInsertBegin;
}

Fragments.insert(It, ToInsertBegin, ToInsertEnd);
return *this;
}

Expand All @@ -177,13 +207,13 @@ class DeclarationFragments {
/// Append a text Fragment of a space character.
///
/// \returns a reference to the DeclarationFragments object itself after
/// appending to chain up consecutive appends.
/// appending to chain up consecutive operations.
DeclarationFragments &appendSpace();

/// Append a text Fragment of a semicolon character.
///
/// \returns a reference to the DeclarationFragments object itself after
/// appending to chain up consecutive appends.
/// appending to chain up consecutive operations.
DeclarationFragments &appendSemicolon();

/// Removes a trailing semicolon character if present.
Expand Down
76 changes: 53 additions & 23 deletions clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,29 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> {

return API.createSymbolReference(Name, USR, getOwningModuleName(D));
}

bool isEmbeddedInVarDeclarator(const TagDecl &D) {
return D.getName().empty() && getTypedefName(&D).empty() &&
D.isEmbeddedInDeclarator();
}

void maybeMergeWithAnonymousTag(const DeclaratorDecl &D,
RecordContext *NewRecordContext) {
if (!NewRecordContext)
return;
auto *Tag = D.getType()->getAsTagDecl();
SmallString<128> TagUSR;
clang::index::generateUSRForDecl(Tag, TagUSR);
if (auto *Record = llvm::dyn_cast_if_present<TagRecord>(
API.findRecordForUSR(TagUSR))) {
if (Record->IsEmbeddedInVarDeclarator) {
NewRecordContext->stealRecordChain(*Record);
auto *NewRecord = cast<APIRecord>(NewRecordContext);
if (NewRecord->Comment.empty())
NewRecord->Comment = Record->Comment;
}
}
}
};

template <typename Derived>
Expand Down Expand Up @@ -273,12 +296,18 @@ bool ExtractAPIVisitorBase<Derived>::VisitVarDecl(const VarDecl *Decl) {
USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
AvailabilityInfo::createFromDecl(Decl), Linkage, Comment, Declaration,
SubHeading, Access, isInSystemHeader(Decl));
} else
} else {
// Add the global variable record to the API set.
API.createRecord<GlobalVariableRecord>(
auto *NewRecord = API.createRecord<GlobalVariableRecord>(
USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
AvailabilityInfo::createFromDecl(Decl), Linkage, Comment, Declaration,
SubHeading, isInSystemHeader(Decl));

// If this global variable has a non typedef'd anonymous tag type let's
// pretend the type's child records are under us in the hierarchy.
maybeMergeWithAnonymousTag(*Decl, NewRecord);
}

return true;
}

Expand Down Expand Up @@ -364,7 +393,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitEnumDecl(const EnumDecl *Decl) {
if (Name.empty()) {
llvm::raw_svector_ostream OS(QualifiedNameBuffer);
Decl->printQualifiedName(OS);
Name = QualifiedNameBuffer.str();
Name = QualifiedNameBuffer;
}

SmallString<128> USR;
Expand All @@ -385,7 +414,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitEnumDecl(const EnumDecl *Decl) {
auto *ER = API.createRecord<EnumRecord>(
USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
AvailabilityInfo::createFromDecl(Decl), Comment, Declaration, SubHeading,
isInSystemHeader(Decl));
isInSystemHeader(Decl), isEmbeddedInVarDeclarator(*Decl));

// Now collect information about the enumerators in this enum.
getDerivedExtractAPIVisitor().recordEnumConstants(ER, Decl->enumerators());
Expand Down Expand Up @@ -510,16 +539,10 @@ bool ExtractAPIVisitorBase<Derived>::VisitRecordDecl(const RecordDecl *Decl) {
if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl))
return true;

SmallString<128> QualifiedNameBuffer;
// Collect symbol information.
StringRef Name = Decl->getName();
if (Name.empty())
Name = getTypedefName(Decl);
if (Name.empty()) {
llvm::raw_svector_ostream OS(QualifiedNameBuffer);
Decl->printQualifiedName(OS);
Name = QualifiedNameBuffer.str();
}

SmallString<128> USR;
index::generateUSRForDecl(Decl, USR);
Expand All @@ -541,12 +564,12 @@ bool ExtractAPIVisitorBase<Derived>::VisitRecordDecl(const RecordDecl *Decl) {
API.createRecord<UnionRecord>(
USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
SubHeading, isInSystemHeader(Decl));
SubHeading, isInSystemHeader(Decl), isEmbeddedInVarDeclarator(*Decl));
else
API.createRecord<StructRecord>(
USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
SubHeading, isInSystemHeader(Decl));
SubHeading, isInSystemHeader(Decl), isEmbeddedInVarDeclarator(*Decl));

return true;
}
Expand All @@ -559,6 +582,9 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXRecordDecl(
return true;

StringRef Name = Decl->getName();
if (Name.empty())
Name = getTypedefName(Decl);

SmallString<128> USR;
index::generateUSRForDecl(Decl, USR);
PresumedLoc Loc =
Expand All @@ -585,8 +611,7 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXRecordDecl(
CXXClassRecord *Record;
if (Decl->getDescribedClassTemplate()) {
// Inject template fragments before class fragments.
Declaration.insert(
Declaration.begin(),
Declaration.prepend(
DeclarationFragmentsBuilder::getFragmentsForRedeclarableTemplate(
Decl->getDescribedClassTemplate()));
Record = API.createRecord<ClassTemplateRecord>(
Expand All @@ -598,7 +623,8 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXRecordDecl(
Record = API.createRecord<CXXClassRecord>(
USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
SubHeading, Kind, Access, isInSystemHeader(Decl));
SubHeading, Kind, Access, isInSystemHeader(Decl),
isEmbeddedInVarDeclarator(*Decl));

Record->Bases = getBases(Decl);

Expand Down Expand Up @@ -1075,18 +1101,17 @@ bool ExtractAPIVisitorBase<Derived>::VisitTypedefNameDecl(
// If the underlying type was defined as part of the typedef modify it's
// fragments directly and pretend the typedef doesn't exist.
if (auto *TagDecl = Decl->getUnderlyingType()->getAsTagDecl()) {
if (TagDecl->getName() == Decl->getName() &&
TagDecl->isEmbeddedInDeclarator() && TagDecl->isCompleteDefinition()) {
if (TagDecl->isEmbeddedInDeclarator() && TagDecl->isCompleteDefinition() &&
Decl->getName() == TagDecl->getName()) {
SmallString<128> TagUSR;
index::generateUSRForDecl(TagDecl, TagUSR);
if (auto *Record = API.findRecordForUSR(TagUSR)) {
DeclarationFragments LeadingFragments;
LeadingFragments.append("typedef",
DeclarationFragments::FragmentKind::Keyword, "",
nullptr);
DeclarationFragments::FragmentKind::Keyword);
LeadingFragments.appendSpace();
Record->Declaration.removeTrailingSemicolon()
.insert(Record->Declaration.begin(), std::move(LeadingFragments))
.prepend(std::move(LeadingFragments))
.append(" { ... } ", DeclarationFragments::FragmentKind::Text)
.append(Name, DeclarationFragments::FragmentKind::Identifier)
.appendSemicolon();
Expand Down Expand Up @@ -1221,26 +1246,31 @@ bool ExtractAPIVisitorBase<Derived>::VisitFieldDecl(const FieldDecl *Decl) {
DeclarationFragments SubHeading =
DeclarationFragmentsBuilder::getSubHeading(Decl);

RecordContext *NewRecord = nullptr;
if (isa<CXXRecordDecl>(Decl->getDeclContext())) {
AccessControl Access = DeclarationFragmentsBuilder::getAccessControl(Decl);

API.createRecord<CXXFieldRecord>(
NewRecord = API.createRecord<CXXFieldRecord>(
USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
SubHeading, Access, isInSystemHeader(Decl));
} else if (auto *RD = dyn_cast<RecordDecl>(Decl->getDeclContext())) {
if (RD->isUnion())
API.createRecord<UnionFieldRecord>(
NewRecord = API.createRecord<UnionFieldRecord>(
USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
SubHeading, isInSystemHeader(Decl));
else
API.createRecord<StructFieldRecord>(
NewRecord = API.createRecord<StructFieldRecord>(
USR, Name, createHierarchyInformationForDecl(*Decl), Loc,
AvailabilityInfo::createFromDecl(Decl), Comment, Declaration,
SubHeading, isInSystemHeader(Decl));
}

// If this field has a non typedef'd anonymous tag type let's pretend the
// type's child records are under us in the hierarchy.
maybeMergeWithAnonymousTag(*Decl, NewRecord);

return true;
}

Expand Down
3 changes: 1 addition & 2 deletions clang/lib/Driver/ToolChains/AMDGPU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -617,8 +617,7 @@ void amdgpu::Linker::ConstructJob(Compilation &C, const JobAction &JA,
const InputInfoList &Inputs,
const ArgList &Args,
const char *LinkingOutput) const {

std::string Linker = getToolChain().GetProgramPath(getShortName());
std::string Linker = getToolChain().GetLinkerPath();
ArgStringList CmdArgs;
CmdArgs.push_back("--no-undefined");
CmdArgs.push_back("-shared");
Expand Down
22 changes: 22 additions & 0 deletions clang/lib/ExtractAPI/API.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,28 @@ RecordContext *APIRecord::castToRecordContext(const APIRecord *Record) {
}
}

bool RecordContext::IsWellFormed() const {
// Check that First and Last are both null or both non-null.
return (First == nullptr) == (Last == nullptr);
}

void RecordContext::stealRecordChain(RecordContext &Other) {
assert(IsWellFormed());
// If we don't have an empty chain append Other's chain into ours.
if (First)
Last->NextInContext = Other.First;
else
First = Other.First;

Last = Other.Last;

// Delete Other's chain to ensure we don't accidentally traverse it.
Other.First = nullptr;
Other.Last = nullptr;
}

void RecordContext::addToRecordChain(APIRecord *Record) const {
assert(IsWellFormed());
if (!First) {
First = Record;
Last = Record;
Expand Down Expand Up @@ -95,6 +116,7 @@ SymbolReference APISet::createSymbolReference(StringRef Name, StringRef USR,
}

APIRecord::~APIRecord() {}
TagRecord::~TagRecord() {}
RecordRecord::~RecordRecord() {}
RecordFieldRecord::~RecordFieldRecord() {}
ObjCContainerRecord::~ObjCContainerRecord() {}
Expand Down
17 changes: 13 additions & 4 deletions clang/lib/ExtractAPI/DeclarationFragments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,8 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType(
const TagDecl *Decl = TagTy->getDecl();
// Anonymous decl, skip this fragment.
if (Decl->getName().empty())
return Fragments;
return Fragments.append("{ ... }",
DeclarationFragments::FragmentKind::Text);
SmallString<128> TagUSR;
clang::index::generateUSRForDecl(Decl, TagUSR);
return Fragments.append(Decl->getName(),
Expand Down Expand Up @@ -743,11 +744,16 @@ DeclarationFragmentsBuilder::getFragmentsForEnum(const EnumDecl *EnumDecl) {

QualType IntegerType = EnumDecl->getIntegerType();
if (!IntegerType.isNull())
Fragments.append(": ", DeclarationFragments::FragmentKind::Text)
Fragments.appendSpace()
.append(": ", DeclarationFragments::FragmentKind::Text)
.append(
getFragmentsForType(IntegerType, EnumDecl->getASTContext(), After))
.append(std::move(After));

if (EnumDecl->getName().empty())
Fragments.appendSpace().append("{ ... }",
DeclarationFragments::FragmentKind::Text);

return Fragments.appendSemicolon();
}

Expand Down Expand Up @@ -778,9 +784,12 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForRecordDecl(
else
Fragments.append("struct", DeclarationFragments::FragmentKind::Keyword);

Fragments.appendSpace();
if (!Record->getName().empty())
Fragments.appendSpace().append(
Record->getName(), DeclarationFragments::FragmentKind::Identifier);
Fragments.append(Record->getName(),
DeclarationFragments::FragmentKind::Identifier);
else
Fragments.append("{ ... }", DeclarationFragments::FragmentKind::Text);

return Fragments.appendSemicolon();
}
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,14 @@ bool SymbolGraphSerializer::shouldSkip(const APIRecord *Record) const {
if (Record->Availability.isUnconditionallyUnavailable())
return true;

// Filter out symbols without a name as we can generate correct symbol graphs
// for them. In practice these are anonymous record types that aren't attached
// to a declaration.
if (auto *Tag = dyn_cast<TagRecord>(Record)) {
if (Tag->IsEmbeddedInVarDeclarator)
return true;
}

// Filter out symbols prefixed with an underscored as they are understood to
// be symbols clients should not use.
if (Record->Name.starts_with("_"))
Expand Down
18 changes: 13 additions & 5 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7953,7 +7953,8 @@ void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,
// For variadic functions, we may have more args than parameters.
// For some K&R functions, we may have less args than parameters.
const auto N = std::min<unsigned>(Proto->getNumParams(), Args.size());
bool AnyScalableArgsOrRet = Proto->getReturnType()->isSizelessVectorType();
bool IsScalableRet = Proto->getReturnType()->isSizelessVectorType();
bool IsScalableArg = false;
for (unsigned ArgIdx = 0; ArgIdx < N; ++ArgIdx) {
// Args[ArgIdx] can be null in malformed code.
if (const Expr *Arg = Args[ArgIdx]) {
Expand All @@ -7968,7 +7969,7 @@ void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,

QualType ParamTy = Proto->getParamType(ArgIdx);
if (ParamTy->isSizelessVectorType())
AnyScalableArgsOrRet = true;
IsScalableArg = true;
QualType ArgTy = Arg->getType();
CheckArgAlignment(Arg->getExprLoc(), FDecl, std::to_string(ArgIdx + 1),
ArgTy, ParamTy);
Expand All @@ -7993,7 +7994,8 @@ void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,
// arguments or return values, then warn the user that the streaming and
// non-streaming vector lengths may be different.
const auto *CallerFD = dyn_cast<FunctionDecl>(CurContext);
if (CallerFD && (!FD || !FD->getBuiltinID()) && AnyScalableArgsOrRet) {
if (CallerFD && (!FD || !FD->getBuiltinID()) &&
(IsScalableArg || IsScalableRet)) {
bool IsCalleeStreaming =
ExtInfo.AArch64SMEAttributes & FunctionType::SME_PStateSMEnabledMask;
bool IsCalleeStreamingCompatible =
Expand All @@ -8002,8 +8004,14 @@ void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,
ArmStreamingType CallerFnType = getArmStreamingFnType(CallerFD);
if (!IsCalleeStreamingCompatible &&
(CallerFnType == ArmStreamingCompatible ||
((CallerFnType == ArmStreaming) ^ IsCalleeStreaming)))
Diag(Loc, diag::warn_sme_streaming_pass_return_vl_to_non_streaming);
((CallerFnType == ArmStreaming) ^ IsCalleeStreaming))) {
if (IsScalableArg)
Diag(Loc, diag::warn_sme_streaming_pass_return_vl_to_non_streaming)
<< /*IsArg=*/true;
if (IsScalableRet)
Diag(Loc, diag::warn_sme_streaming_pass_return_vl_to_non_streaming)
<< /*IsArg=*/false;
}
}

FunctionType::ArmStateValue CalleeArmZAState =
Expand Down
10 changes: 7 additions & 3 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12417,12 +12417,16 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
bool UsesZT0 = Attr && Attr->isNewZT0();

if (NewFD->hasAttr<ArmLocallyStreamingAttr>()) {
if (NewFD->getReturnType()->isSizelessVectorType() ||
llvm::any_of(NewFD->parameters(), [](ParmVarDecl *P) {
if (NewFD->getReturnType()->isSizelessVectorType())
Diag(NewFD->getLocation(),
diag::warn_sme_locally_streaming_has_vl_args_returns)
<< /*IsArg=*/false;
if (llvm::any_of(NewFD->parameters(), [](ParmVarDecl *P) {
return P->getOriginalType()->isSizelessVectorType();
}))
Diag(NewFD->getLocation(),
diag::warn_sme_locally_streaming_has_vl_args_returns);
diag::warn_sme_locally_streaming_has_vl_args_returns)
<< /*IsArg=*/true;
}
if (const auto *FPT = NewFD->getType()->getAs<FunctionProtoType>()) {
FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo();
Expand Down
4 changes: 4 additions & 0 deletions clang/test/Driver/amdgpu-toolchain.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@
// RUN: -L. -fconvergent-functions %s 2>&1 | FileCheck -check-prefix=MCPU %s
// LTO: clang{{.*}} "-flto=full"{{.*}}"-fconvergent-functions"
// MCPU: ld.lld{{.*}}"-L."{{.*}}"-plugin-opt=mcpu=gfx906"

// RUN: %clang -### --target=amdgcn-amd-amdhsa -mcpu=gfx906 -nogpulib \
// RUN: -fuse-ld=ld %s 2>&1 | FileCheck -check-prefixes=LD %s
// LD: ld.lld"
565 changes: 165 additions & 400 deletions clang/test/ExtractAPI/anonymous_record_no_typedef.c

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions clang/test/ExtractAPI/enum.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ enum {
},
{
"kind": "text",
"spelling": ": "
"spelling": " : "
},
{
"kind": "typeIdentifier",
Expand Down Expand Up @@ -459,7 +459,7 @@ enum {
},
{
"kind": "text",
"spelling": ": "
"spelling": " : "
},
{
"kind": "typeIdentifier",
Expand Down Expand Up @@ -686,7 +686,7 @@ enum {
},
{
"kind": "text",
"spelling": ": "
"spelling": " : "
},
{
"kind": "typeIdentifier",
Expand All @@ -695,7 +695,7 @@ enum {
},
{
"kind": "text",
"spelling": ";"
"spelling": " { ... };"
}
],
"identifier": {
Expand Down Expand Up @@ -778,7 +778,7 @@ enum {
},
{
"kind": "text",
"spelling": ": "
"spelling": " : "
},
{
"kind": "typeIdentifier",
Expand All @@ -787,7 +787,7 @@ enum {
},
{
"kind": "text",
"spelling": ";"
"spelling": " { ... };"
}
],
"identifier": {
Expand Down
18 changes: 3 additions & 15 deletions clang/test/ExtractAPI/function_noexcepts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,7 @@ void getFooBar() noexcept(false);
},
{
"kind": "text",
"spelling": "()"
},
{
"kind": "text",
"spelling": " "
"spelling": "() "
},
{
"kind": "keyword",
Expand Down Expand Up @@ -139,11 +135,7 @@ void getFooBar() noexcept(false);
},
{
"kind": "text",
"spelling": "()"
},
{
"kind": "text",
"spelling": " "
"spelling": "() "
},
{
"kind": "keyword",
Expand Down Expand Up @@ -223,11 +215,7 @@ void getFooBar() noexcept(false);
},
{
"kind": "text",
"spelling": "()"
},
{
"kind": "text",
"spelling": " "
"spelling": "() "
},
{
"kind": "keyword",
Expand Down
6 changes: 1 addition & 5 deletions clang/test/ExtractAPI/methods.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,7 @@ class Foo {
// SETL-NEXT: },
// SETL-NEXT: {
// SETL-NEXT: "kind": "text",
// SETL-NEXT: "spelling": ")"
// SETL-NEXT: },
// SETL-NEXT: {
// SETL-NEXT: "kind": "text",
// SETL-NEXT: "spelling": " "
// SETL-NEXT: "spelling": ") "
// SETL-NEXT: },
// SETL-NEXT: {
// SETL-NEXT: "kind": "keyword",
Expand Down
48 changes: 8 additions & 40 deletions clang/test/ExtractAPI/objc_block.m
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,7 @@ -(void)methodBlockNoParam:(void (^)())block;
// NOPARAM-NEXT: },
// NOPARAM-NEXT: {
// NOPARAM-NEXT: "kind": "text",
// NOPARAM-NEXT: "spelling": " (^"
// NOPARAM-NEXT: },
// NOPARAM-NEXT: {
// NOPARAM-NEXT: "kind": "text",
// NOPARAM-NEXT: "spelling": ")()) "
// NOPARAM-NEXT: "spelling": " (^)()) "
// NOPARAM-NEXT: },
// NOPARAM-NEXT: {
// NOPARAM-NEXT: "kind": "internalParam",
Expand All @@ -65,11 +61,7 @@ -(void)methodBlockNoParam:(void (^)())block;
// NOPARAM-NEXT: },
// NOPARAM-NEXT: {
// NOPARAM-NEXT: "kind": "text",
// NOPARAM-NEXT: "spelling": " (^"
// NOPARAM-NEXT: },
// NOPARAM-NEXT: {
// NOPARAM-NEXT: "kind": "text",
// NOPARAM-NEXT: "spelling": ")()) "
// NOPARAM-NEXT: "spelling": " (^)()) "
// NOPARAM-NEXT: },
// NOPARAM-NEXT: {
// NOPARAM-NEXT: "kind": "internalParam",
Expand Down Expand Up @@ -120,11 +112,7 @@ -(void)methodBlockWithParam:(int (^)(int foo))block;
// PARAM-NEXT: },
// PARAM-NEXT: {
// PARAM-NEXT: "kind": "text",
// PARAM-NEXT: "spelling": " (^"
// PARAM-NEXT: },
// PARAM-NEXT: {
// PARAM-NEXT: "kind": "text",
// PARAM-NEXT: "spelling": ")("
// PARAM-NEXT: "spelling": " (^)("
// PARAM-NEXT: },
// PARAM-NEXT: {
// PARAM-NEXT: "kind": "typeIdentifier",
Expand Down Expand Up @@ -167,11 +155,7 @@ -(void)methodBlockWithParam:(int (^)(int foo))block;
// PARAM-NEXT: },
// PARAM-NEXT: {
// PARAM-NEXT: "kind": "text",
// PARAM-NEXT: "spelling": " (^"
// PARAM-NEXT: },
// PARAM-NEXT: {
// PARAM-NEXT: "kind": "text",
// PARAM-NEXT: "spelling": ")("
// PARAM-NEXT: "spelling": " (^)("
// PARAM-NEXT: },
// PARAM-NEXT: {
// PARAM-NEXT: "kind": "typeIdentifier",
Expand Down Expand Up @@ -239,11 +223,7 @@ -(void)methodBlockWithMultipleParam:(int (^)(int foo, unsigned baz))block;
// MULTIPARAM-NEXT: },
// MULTIPARAM-NEXT: {
// MULTIPARAM-NEXT: "kind": "text",
// MULTIPARAM-NEXT: "spelling": " (^"
// MULTIPARAM-NEXT: },
// MULTIPARAM-NEXT: {
// MULTIPARAM-NEXT: "kind": "text",
// MULTIPARAM-NEXT: "spelling": ")("
// MULTIPARAM-NEXT: "spelling": " (^)("
// MULTIPARAM-NEXT: },
// MULTIPARAM-NEXT: {
// MULTIPARAM-NEXT: "kind": "typeIdentifier",
Expand Down Expand Up @@ -303,11 +283,7 @@ -(void)methodBlockWithMultipleParam:(int (^)(int foo, unsigned baz))block;
// MULTIPARAM-NEXT: },
// MULTIPARAM-NEXT: {
// MULTIPARAM-NEXT: "kind": "text",
// MULTIPARAM-NEXT: "spelling": " (^"
// MULTIPARAM-NEXT: },
// MULTIPARAM-NEXT: {
// MULTIPARAM-NEXT: "kind": "text",
// MULTIPARAM-NEXT: "spelling": ")("
// MULTIPARAM-NEXT: "spelling": " (^)("
// MULTIPARAM-NEXT: },
// MULTIPARAM-NEXT: {
// MULTIPARAM-NEXT: "kind": "typeIdentifier",
Expand Down Expand Up @@ -392,11 +368,7 @@ -(void)methodBlockVariadic:(int (^)(int foo, ...))block;
// VARIADIC-NEXT: },
// VARIADIC-NEXT: {
// VARIADIC-NEXT: "kind": "text",
// VARIADIC-NEXT: "spelling": " (^"
// VARIADIC-NEXT: },
// VARIADIC-NEXT: {
// VARIADIC-NEXT: "kind": "text",
// VARIADIC-NEXT: "spelling": ")("
// VARIADIC-NEXT: "spelling": " (^)("
// VARIADIC-NEXT: },
// VARIADIC-NEXT: {
// VARIADIC-NEXT: "kind": "typeIdentifier",
Expand Down Expand Up @@ -439,11 +411,7 @@ -(void)methodBlockVariadic:(int (^)(int foo, ...))block;
// VARIADIC-NEXT: },
// VARIADIC-NEXT: {
// VARIADIC-NEXT: "kind": "text",
// VARIADIC-NEXT: "spelling": " (^"
// VARIADIC-NEXT: },
// VARIADIC-NEXT: {
// VARIADIC-NEXT: "kind": "text",
// VARIADIC-NEXT: "spelling": ")("
// VARIADIC-NEXT: "spelling": " (^)("
// VARIADIC-NEXT: },
// VARIADIC-NEXT: {
// VARIADIC-NEXT: "kind": "typeIdentifier",
Expand Down
4 changes: 2 additions & 2 deletions clang/test/ExtractAPI/typedef_anonymous_record.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ typedef struct { } MyStruct;
// MYSTRUCT-NEXT: },
// MYSTRUCT-NEXT: {
// MYSTRUCT-NEXT: "kind": "text",
// MYSTRUCT-NEXT: "spelling": " "
// MYSTRUCT-NEXT: "spelling": " { ... } "
// MYSTRUCT-NEXT: },
// MYSTRUCT-NEXT: {
// MYSTRUCT-NEXT: "kind": "identifier",
Expand Down Expand Up @@ -97,7 +97,7 @@ typedef enum { Case } MyEnum;
// MYENUM-NEXT: },
// MYENUM-NEXT: {
// MYENUM-NEXT: "kind": "text",
// MYENUM-NEXT: "spelling": " "
// MYENUM-NEXT: "spelling": " { ... } "
// MYENUM-NEXT: },
// MYENUM-NEXT: {
// MYENUM-NEXT: "kind": "identifier",
Expand Down
2 changes: 1 addition & 1 deletion clang/test/ExtractAPI/typedef_struct_enum.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ typedef enum Test2 {
// TEST2-NEXT: },
// TEST2-NEXT: {
// TEST2-NEXT: "kind": "text",
// TEST2-NEXT: "spelling": ": "
// TEST2-NEXT: "spelling": " : "
// TEST2-NEXT: },
// TEST2-NEXT: {
// TEST2-NEXT: "kind": "typeIdentifier",
Expand Down
12 changes: 8 additions & 4 deletions clang/test/Sema/aarch64-incompat-sm-builtin-calls.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ svuint32_t incompat_sve_sm(svbool_t pg, svuint32_t a, int16_t b) __arm_streaming
return __builtin_sve_svld1_gather_u32base_index_u32(pg, a, b);
}

// expected-warning@+1 {{passing/returning a VL-dependent argument to/from a __arm_locally_streaming function. The streaming and non-streaming vector lengths may be different}}
// expected-warning@+2 {{returning a VL-dependent argument from a locally streaming function is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
// expected-warning@+1 {{passing a VL-dependent argument to a locally streaming function is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
__arm_locally_streaming svuint32_t incompat_sve_ls(svbool_t pg, svuint32_t a, int64_t b) {
// expected-warning@+1 {{builtin call has undefined behaviour when called from a streaming function}}
return __builtin_sve_svld1_gather_u32base_index_u32(pg, a, b);
Expand All @@ -49,7 +50,8 @@ svuint32_t incompat_sve2_sm(svbool_t pg, svuint32_t a, int64_t b) __arm_streamin
return __builtin_sve_svldnt1_gather_u32base_index_u32(pg, a, b);
}

// expected-warning@+1 {{passing/returning a VL-dependent argument to/from a __arm_locally_streaming function. The streaming and non-streaming vector lengths may be different}}
// expected-warning@+2 {{returning a VL-dependent argument from a locally streaming function is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
// expected-warning@+1 {{passing a VL-dependent argument to a locally streaming function is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
__arm_locally_streaming svuint32_t incompat_sve2_ls(svbool_t pg, svuint32_t a, int64_t b) {
// expected-warning@+1 {{builtin call has undefined behaviour when called from a streaming function}}
return __builtin_sve_svldnt1_gather_u32base_index_u32(pg, a, b);
Expand All @@ -70,7 +72,8 @@ svfloat64_t streaming_caller_sve(svbool_t pg, svfloat64_t a, float64_t b) __arm_
return svadd_n_f64_m(pg, a, b);
}

// expected-warning@+1 {{passing/returning a VL-dependent argument to/from a __arm_locally_streaming function. The streaming and non-streaming vector lengths may be different}}
// expected-warning@+2 {{returning a VL-dependent argument from a locally streaming function is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
// expected-warning@+1 {{passing a VL-dependent argument to a locally streaming function is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
__arm_locally_streaming svfloat64_t locally_streaming_caller_sve(svbool_t pg, svfloat64_t a, float64_t b) {
// expected-no-warning
return svadd_n_f64_m(pg, a, b);
Expand All @@ -86,7 +89,8 @@ svint16_t streaming_caller_sve2(svint16_t op1, svint16_t op2) __arm_streaming {
return svmul_lane_s16(op1, op2, 0);
}

// expected-warning@+1 {{passing/returning a VL-dependent argument to/from a __arm_locally_streaming function. The streaming and non-streaming vector lengths may be different}}
// expected-warning@+2 {{returning a VL-dependent argument from a locally streaming function is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
// expected-warning@+1 {{passing a VL-dependent argument to a locally streaming function is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
__arm_locally_streaming svint16_t locally_streaming_caller_sve2(svint16_t op1, svint16_t op2) {
// expected-no-warning
return svmul_lane_s16(op1, op2, 0);
Expand Down
48 changes: 24 additions & 24 deletions clang/test/Sema/aarch64-sme-func-attrs.c
Original file line number Diff line number Diff line change
Expand Up @@ -509,73 +509,73 @@ void sme_no_streaming_with_vl_arg(__SVInt8_t a) { }

__SVInt8_t sme_no_streaming_returns_vl(void) { __SVInt8_t r; return r; }

// expected-warning@+2 {{passing/returning a VL-dependent argument to/from a __arm_locally_streaming function. The streaming and non-streaming vector lengths may be different}}
// expected-cpp-warning@+1 {{passing/returning a VL-dependent argument to/from a __arm_locally_streaming function. The streaming and non-streaming vector lengths may be different}}
// expected-warning@+2 {{passing a VL-dependent argument to a locally streaming function is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
// expected-cpp-warning@+1 {{passing a VL-dependent argument to a locally streaming function is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
__arm_locally_streaming void sme_locally_streaming_with_vl_arg(__SVInt8_t a) { }

// expected-warning@+2 {{passing/returning a VL-dependent argument to/from a __arm_locally_streaming function. The streaming and non-streaming vector lengths may be different}}
// expected-cpp-warning@+1 {{passing/returning a VL-dependent argument to/from a __arm_locally_streaming function. The streaming and non-streaming vector lengths may be different}}
// expected-warning@+2 {{returning a VL-dependent argument from a locally streaming function is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
// expected-cpp-warning@+1 {{returning a VL-dependent argument from a locally streaming function is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
__arm_locally_streaming __SVInt8_t sme_locally_streaming_returns_vl(void) { __SVInt8_t r; return r; }

void sme_no_streaming_calling_streaming_with_vl_args() {
__SVInt8_t a;
// expected-warning@+2 {{passing a VL-dependent argument to/from a function that has a different streaming-mode. The streaming and non-streaming vector lengths may be different}}
// expected-cpp-warning@+1 {{passing a VL-dependent argument to/from a function that has a different streaming-mode. The streaming and non-streaming vector lengths may be different}}
// expected-warning@+2 {{passing a VL-dependent argument to a function with a different streaming-mode is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
// expected-cpp-warning@+1 {{passing a VL-dependent argument to a function with a different streaming-mode is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
sme_streaming_with_vl_arg(a);
}

void sme_no_streaming_calling_streaming_with_return_vl() {
// expected-warning@+2 {{passing a VL-dependent argument to/from a function that has a different streaming-mode. The streaming and non-streaming vector lengths may be different}}
// expected-cpp-warning@+1 {{passing a VL-dependent argument to/from a function that has a different streaming-mode. The streaming and non-streaming vector lengths may be different}}
// expected-warning@+2 {{returning a VL-dependent argument from a function with a different streaming-mode is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
// expected-cpp-warning@+1 {{returning a VL-dependent argument from a function with a different streaming-mode is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
__SVInt8_t r = sme_streaming_returns_vl();
}

void sme_streaming_calling_non_streaming_with_vl_args(void) __arm_streaming {
__SVInt8_t a;
// expected-warning@+2 {{passing a VL-dependent argument to/from a function that has a different streaming-mode. The streaming and non-streaming vector lengths may be different}}
// expected-cpp-warning@+1 {{passing a VL-dependent argument to/from a function that has a different streaming-mode. The streaming and non-streaming vector lengths may be different}}
// expected-warning@+2 {{passing a VL-dependent argument to a function with a different streaming-mode is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
// expected-cpp-warning@+1 {{passing a VL-dependent argument to a function with a different streaming-mode is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
sme_no_streaming_with_vl_arg(a);
}

void sme_streaming_calling_non_streaming_with_return_vl(void) __arm_streaming {
// expected-warning@+2 {{passing a VL-dependent argument to/from a function that has a different streaming-mode. The streaming and non-streaming vector lengths may be different}}
// expected-cpp-warning@+1 {{passing a VL-dependent argument to/from a function that has a different streaming-mode. The streaming and non-streaming vector lengths may be different}}
// expected-warning@+2 {{returning a VL-dependent argument from a function with a different streaming-mode is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
// expected-cpp-warning@+1 {{returning a VL-dependent argument from a function with a different streaming-mode is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
__SVInt8_t r = sme_no_streaming_returns_vl();
}

void sme_no_streaming_calling_streaming_with_vl_args_param(__SVInt8_t arg, void (*sc)( __SVInt8_t arg) __arm_streaming) {
// expected-warning@+2 {{passing a VL-dependent argument to/from a function that has a different streaming-mode. The streaming and non-streaming vector lengths may be different}}
// expected-cpp-warning@+1 {{passing a VL-dependent argument to/from a function that has a different streaming-mode. The streaming and non-streaming vector lengths may be different}}
// expected-warning@+2 {{passing a VL-dependent argument to a function with a different streaming-mode is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
// expected-cpp-warning@+1 {{passing a VL-dependent argument to a function with a different streaming-mode is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
sc(arg);
}

__SVInt8_t sme_no_streaming_calling_streaming_return_vl_param(__SVInt8_t (*s)(void) __arm_streaming) {
// expected-warning@+2 {{passing a VL-dependent argument to/from a function that has a different streaming-mode. The streaming and non-streaming vector lengths may be different}}
// expected-cpp-warning@+1 {{passing a VL-dependent argument to/from a function that has a different streaming-mode. The streaming and non-streaming vector lengths may be different}}
// expected-warning@+2 {{returning a VL-dependent argument from a function with a different streaming-mode is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
// expected-cpp-warning@+1 {{returning a VL-dependent argument from a function with a different streaming-mode is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
return s();
}

void sme_streaming_compatible_calling_streaming_with_vl_args(__SVInt8_t arg) __arm_streaming_compatible {
// expected-warning@+2 {{passing a VL-dependent argument to/from a function that has a different streaming-mode. The streaming and non-streaming vector lengths may be different}}
// expected-cpp-warning@+1 {{passing a VL-dependent argument to/from a function that has a different streaming-mode. The streaming and non-streaming vector lengths may be different}}
// expected-warning@+2 {{passing a VL-dependent argument to a function with a different streaming-mode is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
// expected-cpp-warning@+1 {{passing a VL-dependent argument to a function with a different streaming-mode is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
sme_streaming_with_vl_arg(arg);
}

void sme_streaming_compatible_calling_sme_streaming_return_vl(void) __arm_streaming_compatible {
// expected-warning@+2 {{passing a VL-dependent argument to/from a function that has a different streaming-mode. The streaming and non-streaming vector lengths may be different}}
// expected-cpp-warning@+1 {{passing a VL-dependent argument to/from a function that has a different streaming-mode. The streaming and non-streaming vector lengths may be different}}
// expected-warning@+2 {{returning a VL-dependent argument from a function with a different streaming-mode is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
// expected-cpp-warning@+1 {{returning a VL-dependent argument from a function with a different streaming-mode is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
__SVInt8_t r = sme_streaming_returns_vl();
}

void sme_streaming_compatible_calling_no_streaming_with_vl_args(__SVInt8_t arg) __arm_streaming_compatible {
// expected-warning@+2 {{passing a VL-dependent argument to/from a function that has a different streaming-mode. The streaming and non-streaming vector lengths may be different}}
// expected-cpp-warning@+1 {{passing a VL-dependent argument to/from a function that has a different streaming-mode. The streaming and non-streaming vector lengths may be different}}
// expected-warning@+2 {{passing a VL-dependent argument to a function with a different streaming-mode is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
// expected-cpp-warning@+1 {{passing a VL-dependent argument to a function with a different streaming-mode is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
sme_no_streaming_with_vl_arg(arg);
}

void sme_streaming_compatible_calling_no_sme_streaming_return_vl(void) __arm_streaming_compatible {
// expected-warning@+2 {{passing a VL-dependent argument to/from a function that has a different streaming-mode. The streaming and non-streaming vector lengths may be different}}
// expected-cpp-warning@+1 {{passing a VL-dependent argument to/from a function that has a different streaming-mode. The streaming and non-streaming vector lengths may be different}}
// expected-warning@+2 {{returning a VL-dependent argument from a function with a different streaming-mode is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
// expected-cpp-warning@+1 {{returning a VL-dependent argument from a function with a different streaming-mode is undefined behaviour when the streaming and non-streaming vector lengths are different at runtime}}
__SVInt8_t r = sme_no_streaming_returns_vl();
}

Expand Down
15 changes: 11 additions & 4 deletions compiler-rt/lib/scudo/standalone/mem_map_fuchsia.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@ static zx_handle_t getPlaceholderVmo() {
return Vmo;
}

// Checks if MAP_ALLOWNOMEM allows the given error code.
static bool IsNoMemError(zx_status_t Status) {
// Note: _zx_vmar_map returns ZX_ERR_NO_RESOURCES if the VMAR does not contain
// a suitable free spot.
return Status == ZX_ERR_NO_MEMORY || Status == ZX_ERR_NO_RESOURCES;
}

MemMapFuchsia::MemMapFuchsia(uptr Base, uptr Capacity)
: MapAddr(Base), WindowBase(Base), WindowSize(Capacity) {
// Create the VMO.
Expand All @@ -101,7 +108,7 @@ bool MemMapFuchsia::mapImpl(UNUSED uptr Addr, uptr Size, const char *Name,
// Create the VMO.
zx_status_t Status = _zx_vmo_create(Size, 0, &Vmo);
if (UNLIKELY(Status != ZX_OK)) {
if (Status != ZX_ERR_NO_MEMORY || !AllowNoMem)
if (!IsNoMemError(Status) || !AllowNoMem)
dieOnError(Status, "zx_vmo_create", Size);
return false;
}
Expand All @@ -116,7 +123,7 @@ bool MemMapFuchsia::mapImpl(UNUSED uptr Addr, uptr Size, const char *Name,
Status =
_zx_vmar_map(_zx_vmar_root_self(), MapFlags, 0, Vmo, 0, Size, &MapAddr);
if (UNLIKELY(Status != ZX_OK)) {
if (Status != ZX_ERR_NO_MEMORY || !AllowNoMem)
if (!IsNoMemError(Status) || !AllowNoMem)
dieOnError(Status, "zx_vmar_map", Size);

Status = _zx_handle_close(Vmo);
Expand Down Expand Up @@ -187,7 +194,7 @@ bool MemMapFuchsia::remapImpl(uptr Addr, uptr Size, const char *Name,
_zx_vmar_map(_zx_vmar_root_self(), MapFlags, Addr - getRootVmarBase(),
Vmo, Addr - MapAddr, Size, &MappedAddr);
if (UNLIKELY(Status != ZX_OK)) {
if (Status != ZX_ERR_NO_MEMORY || !AllowNoMem)
if (!IsNoMemError(Status) || !AllowNoMem)
dieOnError(Status, "zx_vmar_map", Size);
return false;
}
Expand Down Expand Up @@ -227,7 +234,7 @@ bool ReservedMemoryFuchsia::createImpl(UNUSED uptr Addr, uptr Size,
zx_status_t Status = _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_ALLOW_FAULTS, 0,
getPlaceholderVmo(), 0, Size, &Base);
if (UNLIKELY(Status != ZX_OK)) {
if (Status != ZX_ERR_NO_MEMORY || !AllowNoMem)
if (!IsNoMemError(Status) || !AllowNoMem)
dieOnError(Status, "zx_vmar_map", Size);
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12517,7 +12517,7 @@ This instruction requires several arguments:
``llvm::GuaranteedTailCallOpt`` is ``true``, or the calling convention
is ``tailcc``
- `Platform-specific constraints are
met. <CodeGenerator.html#tailcallopt>`_
met. <CodeGenerator.html#tail-call-optimization>`_

#. The optional ``notail`` marker indicates that the optimizers should not add
``tail`` or ``musttail`` markers to the call. It is used to prevent tail
Expand Down
14 changes: 14 additions & 0 deletions llvm/include/llvm/CodeGen/SelectionDAG.h
Original file line number Diff line number Diff line change
Expand Up @@ -2146,18 +2146,32 @@ class SelectionDAG {
const APInt *getValidShiftAmountConstant(SDValue V,
const APInt &DemandedElts) const;

/// If a SHL/SRA/SRL node \p V has a constant or splat constant shift amount
/// that is less than the element bit-width of the shift node, return it.
const APInt *getValidShiftAmountConstant(SDValue V) const;

/// If a SHL/SRA/SRL node \p V has constant shift amounts that are all less
/// than the element bit-width of the shift node, return the minimum value.
const APInt *
getValidMinimumShiftAmountConstant(SDValue V,
const APInt &DemandedElts) const;

/// If a SHL/SRA/SRL node \p V has constant shift amounts that are all less
/// than the element bit-width of the shift node, return the minimum value.
const APInt *
getValidMinimumShiftAmountConstant(SDValue V) const;

/// If a SHL/SRA/SRL node \p V has constant shift amounts that are all less
/// than the element bit-width of the shift node, return the maximum value.
const APInt *
getValidMaximumShiftAmountConstant(SDValue V,
const APInt &DemandedElts) const;

/// If a SHL/SRA/SRL node \p V has constant shift amounts that are all less
/// than the element bit-width of the shift node, return the maximum value.
const APInt *
getValidMaximumShiftAmountConstant(SDValue V) const;

/// Match a binop + shuffle pyramid that represents a horizontal reduction
/// over the elements of a vector starting from the EXTRACT_VECTOR_ELT node /p
/// Extract. The reduction must use one of the opcodes listed in /p
Expand Down
6 changes: 6 additions & 0 deletions llvm/include/llvm/Frontend/OpenMP/OMP.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,15 @@
#include "llvm/Frontend/OpenMP/OMP.h.inc"

#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"

namespace llvm::omp {
ArrayRef<Directive> getLeafConstructs(Directive D);
ArrayRef<Directive> getLeafConstructsOrSelf(Directive D);

ArrayRef<Directive>
getLeafOrCompositeConstructs(Directive D, SmallVectorImpl<Directive> &Output);

Directive getCompoundConstruct(ArrayRef<Directive> Parts);

bool isLeafConstruct(Directive D);
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4764,6 +4764,8 @@ LegalizerHelper::fewerElementsVector(MachineInstr &MI, unsigned TypeIdx,
return fewerElementsVectorMultiEltType(GMI, NumElts, {2 /*pow*/});
case G_BITCAST:
return fewerElementsBitcast(MI, TypeIdx, NarrowTy);
case G_INTRINSIC_FPTRUNC_ROUND:
return fewerElementsVectorMultiEltType(GMI, NumElts, {2});
default:
return UnableToLegalize;
}
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ SDValue VectorLegalizer::LegalizeOp(SDValue Op) {
case ISD::FFLOOR:
case ISD::FP_ROUND:
case ISD::FP_EXTEND:
case ISD::FPTRUNC_ROUND:
case ISD::FMA:
case ISD::SIGN_EXTEND_INREG:
case ISD::ANY_EXTEND_VECTOR_INREG:
Expand Down
24 changes: 24 additions & 0 deletions llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2993,6 +2993,14 @@ SelectionDAG::getValidShiftAmountConstant(SDValue V,
return nullptr;
}

const APInt *SelectionDAG::getValidShiftAmountConstant(SDValue V) const {
EVT VT = V.getValueType();
APInt DemandedElts = VT.isFixedLengthVector()
? APInt::getAllOnes(VT.getVectorNumElements())
: APInt(1, 1);
return getValidShiftAmountConstant(V, DemandedElts);
}

const APInt *SelectionDAG::getValidMinimumShiftAmountConstant(
SDValue V, const APInt &DemandedElts) const {
assert((V.getOpcode() == ISD::SHL || V.getOpcode() == ISD::SRL ||
Expand Down Expand Up @@ -3022,6 +3030,14 @@ const APInt *SelectionDAG::getValidMinimumShiftAmountConstant(
return MinShAmt;
}

const APInt *SelectionDAG::getValidMinimumShiftAmountConstant(SDValue V) const {
EVT VT = V.getValueType();
APInt DemandedElts = VT.isFixedLengthVector()
? APInt::getAllOnes(VT.getVectorNumElements())
: APInt(1, 1);
return getValidMinimumShiftAmountConstant(V, DemandedElts);
}

const APInt *SelectionDAG::getValidMaximumShiftAmountConstant(
SDValue V, const APInt &DemandedElts) const {
assert((V.getOpcode() == ISD::SHL || V.getOpcode() == ISD::SRL ||
Expand Down Expand Up @@ -3051,6 +3067,14 @@ const APInt *SelectionDAG::getValidMaximumShiftAmountConstant(
return MaxShAmt;
}

const APInt *SelectionDAG::getValidMaximumShiftAmountConstant(SDValue V) const {
EVT VT = V.getValueType();
APInt DemandedElts = VT.isFixedLengthVector()
? APInt::getAllOnes(VT.getVectorNumElements())
: APInt(1, 1);
return getValidMaximumShiftAmountConstant(V, DemandedElts);
}

/// Determine which bits of Op are known to be either zero or one and return
/// them in Known. For vectors, the known bits are those that are shared by
/// every vector element.
Expand Down
103 changes: 90 additions & 13 deletions llvm/lib/Frontend/OpenMP/OMP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,54 @@ using namespace llvm::omp;
#define GEN_DIRECTIVES_IMPL
#include "llvm/Frontend/OpenMP/OMP.inc"

static iterator_range<ArrayRef<Directive>::iterator>
getFirstCompositeRange(iterator_range<ArrayRef<Directive>::iterator> Leafs) {
// OpenMP Spec 5.2: [17.3, 8-9]
// If directive-name-A and directive-name-B both correspond to loop-
// associated constructs then directive-name is a composite construct
// otherwise directive-name is a combined construct.
//
// In the list of leaf constructs, find the first loop-associated construct,
// this is the beginning of the returned range. Then, starting from the
// immediately following leaf construct, find the first sequence of adjacent
// loop-associated constructs. The last of those is the last one of the
// range, that is, the end of the range is one past that element.
// If such a sequence of adjacent loop-associated directives does not exist,
// return an empty range.
//
// The end of the returned range (including empty range) is intended to be
// a point from which the search for the next range could resume.
//
// Consequently, this function can't return a range with a single leaf
// construct in it.

auto firstLoopAssociated =
[](iterator_range<ArrayRef<Directive>::iterator> List) {
for (auto It = List.begin(), End = List.end(); It != End; ++It) {
if (getDirectiveAssociation(*It) == Association::Loop)
return It;
}
return List.end();
};

auto Empty = llvm::make_range(Leafs.end(), Leafs.end());

auto Begin = firstLoopAssociated(Leafs);
if (Begin == Leafs.end())
return Empty;

auto End =
firstLoopAssociated(llvm::make_range(std::next(Begin), Leafs.end()));
if (End == Leafs.end())
return Empty;

for (; End != Leafs.end(); ++End) {
if (getDirectiveAssociation(*End) != Association::Loop)
break;
}
return llvm::make_range(Begin, End);
}

namespace llvm::omp {
ArrayRef<Directive> getLeafConstructs(Directive D) {
auto Idx = static_cast<std::size_t>(D);
Expand All @@ -34,6 +82,44 @@ ArrayRef<Directive> getLeafConstructs(Directive D) {
return ArrayRef(&Row[2], static_cast<int>(Row[1]));
}

ArrayRef<Directive> getLeafConstructsOrSelf(Directive D) {
if (auto Leafs = getLeafConstructs(D); !Leafs.empty())
return Leafs;
auto Idx = static_cast<size_t>(D);
assert(Idx < Directive_enumSize && "Invalid directive");
const auto *Row = LeafConstructTable[LeafConstructTableOrdering[Idx]];
// The first entry in the row is the directive itself.
return ArrayRef(&Row[0], &Row[0] + 1);
}

ArrayRef<Directive>
getLeafOrCompositeConstructs(Directive D, SmallVectorImpl<Directive> &Output) {
using ArrayTy = ArrayRef<Directive>;
using IteratorTy = ArrayTy::iterator;
ArrayRef<Directive> Leafs = getLeafConstructsOrSelf(D);

IteratorTy Iter = Leafs.begin();
do {
auto Range = getFirstCompositeRange(llvm::make_range(Iter, Leafs.end()));
// All directives before the range are leaf constructs.
for (; Iter != Range.begin(); ++Iter)
Output.push_back(*Iter);
if (!Range.empty()) {
Directive Comp =
getCompoundConstruct(ArrayTy(Range.begin(), Range.end()));
assert(Comp != OMPD_unknown);
Output.push_back(Comp);
Iter = Range.end();
// As of now, a composite construct must contain all constituent leaf
// constructs from some point until the end of all constituent leaf
// constructs.
assert(Iter == Leafs.end() && "Malformed directive");
}
} while (Iter != Leafs.end());

return Output;
}

Directive getCompoundConstruct(ArrayRef<Directive> Parts) {
if (Parts.empty())
return OMPD_unknown;
Expand Down Expand Up @@ -88,20 +174,11 @@ Directive getCompoundConstruct(ArrayRef<Directive> Parts) {
bool isLeafConstruct(Directive D) { return getLeafConstructs(D).empty(); }

bool isCompositeConstruct(Directive D) {
// OpenMP Spec 5.2: [17.3, 8-9]
// If directive-name-A and directive-name-B both correspond to loop-
// associated constructs then directive-name is a composite construct
llvm::ArrayRef<Directive> Leafs{getLeafConstructs(D)};
if (Leafs.empty())
return false;
if (getDirectiveAssociation(Leafs.front()) != Association::Loop)
ArrayRef<Directive> Leafs = getLeafConstructsOrSelf(D);
if (Leafs.size() <= 1)
return false;

size_t numLoopConstructs =
llvm::count_if(Leafs.drop_front(), [](Directive L) {
return getDirectiveAssociation(L) == Association::Loop;
});
return numLoopConstructs != 0;
auto Range = getFirstCompositeRange(Leafs);
return Range.begin() == Leafs.begin() && Range.end() == Leafs.end();
}

bool isCombinedConstruct(Directive D) {
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Target/AArch64/AArch64InstrInfo.td
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,8 @@ def UseNegativeImmediates

def UseScalarIncVL : Predicate<"Subtarget->useScalarIncVL()">;

def NoUseScalarIncVL : Predicate<"!Subtarget->useScalarIncVL()">;

def UseSVEFPLD1R : Predicate<"!Subtarget->noSVEFPLD1R()">;

def IsNeonAvailable : Predicate<"Subtarget->isNeonAvailable()">;
Expand Down
17 changes: 17 additions & 0 deletions llvm/lib/Target/AArch64/AArch64SVEInstrInfo.td
Original file line number Diff line number Diff line change
Expand Up @@ -2517,6 +2517,23 @@ let Predicates = [HasSVEorSME] in {
def : Pat<(vscale (sve_cntd_imm_neg i32:$imm)), (SUBXrs XZR, (CNTD_XPiI 31, $imm), 0)>;
}

// Add NoUseScalarIncVL to avoid affecting for patterns with UseScalarIncVL
let Predicates = [HasSVEorSME, NoUseScalarIncVL] in {
def : Pat<(add GPR64:$op, (vscale (sve_cnth_imm_neg i32:$imm))),
(SUBXrs GPR64:$op, (CNTH_XPiI 31, $imm), 0)>;
def : Pat<(add GPR64:$op, (vscale (sve_cntw_imm_neg i32:$imm))),
(SUBXrs GPR64:$op, (CNTW_XPiI 31, $imm), 0)>;
def : Pat<(add GPR64:$op, (vscale (sve_cntd_imm_neg i32:$imm))),
(SUBXrs GPR64:$op, (CNTD_XPiI 31, $imm), 0)>;

def : Pat<(add GPR32:$op, (i32 (trunc (vscale (sve_cnth_imm_neg i32:$imm))))),
(SUBSWrr GPR32:$op, (EXTRACT_SUBREG (CNTH_XPiI 31, $imm), sub_32))>;
def : Pat<(add GPR32:$op, (i32 (trunc (vscale (sve_cntw_imm_neg i32:$imm))))),
(SUBSWrr GPR32:$op, (EXTRACT_SUBREG (CNTW_XPiI 31, $imm), sub_32))>;
def : Pat<(add GPR32:$op, (i32 (trunc (vscale (sve_cntd_imm_neg i32:$imm))))),
(SUBSWrr GPR32:$op, (EXTRACT_SUBREG (CNTD_XPiI 31, $imm), sub_32))>;
}

let AddedComplexity = 5 in {
def : Pat<(nxv8i16 (add ZPR:$op, (nxv8i16 (splat_vector (i32 (trunc (vscale (sve_cnth_imm i32:$imm)))))))),
(INCH_ZPiI ZPR:$op, 31, $imm)>;
Expand Down
3 changes: 1 addition & 2 deletions llvm/lib/Target/X86/X86ISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20459,8 +20459,7 @@ static SDValue matchTruncateWithPACK(unsigned &PackOpcode, EVT DstVT,
// the truncation then we can use PACKSS by converting the srl to a sra.
// SimplifyDemandedBits often relaxes sra to srl so we need to reverse it.
if (In.getOpcode() == ISD::SRL && In->hasOneUse())
if (const APInt *ShAmt = DAG.getValidShiftAmountConstant(
In, APInt::getAllOnes(SrcVT.getVectorNumElements()))) {
if (const APInt *ShAmt = DAG.getValidShiftAmountConstant(In)) {
if (*ShAmt == MinSignBits) {
PackOpcode = X86ISD::PACKSS;
return DAG.getNode(ISD::SRA, DL, SrcVT, In->ops());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ define %"class.std::complex" @complex_mul_v2f64(ptr %a, ptr %b) {
; CHECK-NEXT: mov z1.d, #0 // =0x0
; CHECK-NEXT: cntd x9
; CHECK-NEXT: ptrue p1.b
; CHECK-NEXT: neg x9, x9
; CHECK-NEXT: mov w10, #100 // =0x64
; CHECK-NEXT: neg x10, x9
; CHECK-NEXT: mov w11, #100 // =0x64
; CHECK-NEXT: ptrue p0.d
; CHECK-NEXT: mov x8, xzr
; CHECK-NEXT: and x10, x9, x10
; CHECK-NEXT: and x10, x10, x11
; CHECK-NEXT: rdvl x11, #2
; CHECK-NEXT: zip2 z0.d, z1.d, z1.d
; CHECK-NEXT: zip1 z1.d, z1.d, z1.d
Expand All @@ -33,7 +33,7 @@ define %"class.std::complex" @complex_mul_v2f64(ptr %a, ptr %b) {
; CHECK-NEXT: ld1d { z3.d }, p0/z, [x12, #1, mul vl]
; CHECK-NEXT: ld1b { z4.b }, p1/z, [x1, x8]
; CHECK-NEXT: ld1d { z5.d }, p0/z, [x13, #1, mul vl]
; CHECK-NEXT: adds x10, x10, x9
; CHECK-NEXT: subs x10, x10, x9
; CHECK-NEXT: add x8, x8, x11
; CHECK-NEXT: fcmla z1.d, p0/m, z4.d, z2.d, #0
; CHECK-NEXT: fcmla z0.d, p0/m, z5.d, z3.d, #0
Expand Down Expand Up @@ -106,12 +106,12 @@ define %"class.std::complex" @complex_mul_nonzero_init_v2f64(ptr %a, ptr %b) {
; CHECK-NEXT: cntd x9
; CHECK-NEXT: fmov d2, #2.00000000
; CHECK-NEXT: ptrue p0.d, vl1
; CHECK-NEXT: neg x9, x9
; CHECK-NEXT: neg x10, x9
; CHECK-NEXT: ptrue p1.b
; CHECK-NEXT: mov w10, #100 // =0x64
; CHECK-NEXT: mov w11, #100 // =0x64
; CHECK-NEXT: mov x8, xzr
; CHECK-NEXT: sel z3.d, p0, z0.d, z1.d
; CHECK-NEXT: and x10, x9, x10
; CHECK-NEXT: and x10, x10, x11
; CHECK-NEXT: rdvl x11, #2
; CHECK-NEXT: mov z1.d, p0/m, z2.d
; CHECK-NEXT: ptrue p0.d
Expand All @@ -125,7 +125,7 @@ define %"class.std::complex" @complex_mul_nonzero_init_v2f64(ptr %a, ptr %b) {
; CHECK-NEXT: ld1d { z3.d }, p0/z, [x12, #1, mul vl]
; CHECK-NEXT: ld1b { z4.b }, p1/z, [x1, x8]
; CHECK-NEXT: ld1d { z5.d }, p0/z, [x13, #1, mul vl]
; CHECK-NEXT: adds x10, x10, x9
; CHECK-NEXT: subs x10, x10, x9
; CHECK-NEXT: add x8, x8, x11
; CHECK-NEXT: fcmla z1.d, p0/m, z4.d, z2.d, #0
; CHECK-NEXT: fcmla z0.d, p0/m, z5.d, z3.d, #0
Expand Down Expand Up @@ -191,13 +191,13 @@ define %"class.std::complex" @complex_mul_v2f64_unrolled(ptr %a, ptr %b) {
; CHECK: // %bb.0: // %entry
; CHECK-NEXT: mov z1.d, #0 // =0x0
; CHECK-NEXT: cntw x9
; CHECK-NEXT: mov w10, #1000 // =0x3e8
; CHECK-NEXT: neg x9, x9
; CHECK-NEXT: mov w11, #1000 // =0x3e8
; CHECK-NEXT: neg x10, x9
; CHECK-NEXT: rdvl x12, #2
; CHECK-NEXT: ptrue p1.b
; CHECK-NEXT: ptrue p0.d
; CHECK-NEXT: mov x8, xzr
; CHECK-NEXT: and x10, x9, x10
; CHECK-NEXT: and x10, x10, x11
; CHECK-NEXT: zip2 z0.d, z1.d, z1.d
; CHECK-NEXT: zip1 z1.d, z1.d, z1.d
; CHECK-NEXT: add x11, x1, x12
Expand All @@ -219,7 +219,7 @@ define %"class.std::complex" @complex_mul_v2f64_unrolled(ptr %a, ptr %b) {
; CHECK-NEXT: ld1d { z17.d }, p0/z, [x15, #1, mul vl]
; CHECK-NEXT: ld1b { z18.b }, p1/z, [x11, x8]
; CHECK-NEXT: ld1d { z19.d }, p0/z, [x17, #1, mul vl]
; CHECK-NEXT: adds x10, x10, x9
; CHECK-NEXT: subs x10, x10, x9
; CHECK-NEXT: add x8, x8, x13
; CHECK-NEXT: fcmla z1.d, p0/m, z7.d, z4.d, #0
; CHECK-NEXT: fcmla z0.d, p0/m, z16.d, z5.d, #0
Expand Down
26 changes: 10 additions & 16 deletions llvm/test/CodeGen/AArch64/sve-vl-arith.ll
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@ define i64 @incd_scalar_i64(i64 %a) {
define i64 @decb_scalar_i64(i64 %a) {
; NO_SCALAR_INC-LABEL: decb_scalar_i64:
; NO_SCALAR_INC: // %bb.0:
; NO_SCALAR_INC-NEXT: rdvl x8, #-2
; NO_SCALAR_INC-NEXT: add x0, x0, x8
; NO_SCALAR_INC-NEXT: cnth x8, all, mul #4
; NO_SCALAR_INC-NEXT: sub x0, x0, x8
; NO_SCALAR_INC-NEXT: ret
;
; CHECK-LABEL: decb_scalar_i64:
Expand All @@ -204,8 +204,7 @@ define i64 @dech_scalar_i64(i64 %a) {
; NO_SCALAR_INC-LABEL: dech_scalar_i64:
; NO_SCALAR_INC: // %bb.0:
; NO_SCALAR_INC-NEXT: cnth x8, all, mul #3
; NO_SCALAR_INC-NEXT: neg x8, x8
; NO_SCALAR_INC-NEXT: add x0, x0, x8
; NO_SCALAR_INC-NEXT: sub x0, x0, x8
; NO_SCALAR_INC-NEXT: ret
;
; CHECK-LABEL: dech_scalar_i64:
Expand All @@ -222,8 +221,7 @@ define i64 @decw_scalar_i64(i64 %a) {
; NO_SCALAR_INC-LABEL: decw_scalar_i64:
; NO_SCALAR_INC: // %bb.0:
; NO_SCALAR_INC-NEXT: cntw x8, all, mul #3
; NO_SCALAR_INC-NEXT: neg x8, x8
; NO_SCALAR_INC-NEXT: add x0, x0, x8
; NO_SCALAR_INC-NEXT: sub x0, x0, x8
; NO_SCALAR_INC-NEXT: ret
;
; CHECK-LABEL: decw_scalar_i64:
Expand All @@ -240,8 +238,7 @@ define i64 @decd_scalar_i64(i64 %a) {
; NO_SCALAR_INC-LABEL: decd_scalar_i64:
; NO_SCALAR_INC: // %bb.0:
; NO_SCALAR_INC-NEXT: cntd x8, all, mul #3
; NO_SCALAR_INC-NEXT: neg x8, x8
; NO_SCALAR_INC-NEXT: add x0, x0, x8
; NO_SCALAR_INC-NEXT: sub x0, x0, x8
; NO_SCALAR_INC-NEXT: ret
;
; CHECK-LABEL: decd_scalar_i64:
Expand Down Expand Up @@ -345,8 +342,8 @@ define i32 @incd_scalar_i32(i32 %a) {
define i32 @decb_scalar_i32(i32 %a) {
; NO_SCALAR_INC-LABEL: decb_scalar_i32:
; NO_SCALAR_INC: // %bb.0:
; NO_SCALAR_INC-NEXT: rdvl x8, #-4
; NO_SCALAR_INC-NEXT: add w0, w0, w8
; NO_SCALAR_INC-NEXT: cnth x8, all, mul #8
; NO_SCALAR_INC-NEXT: sub w0, w0, w8
; NO_SCALAR_INC-NEXT: ret
;
; CHECK-LABEL: decb_scalar_i32:
Expand All @@ -367,8 +364,7 @@ define i32 @dech_scalar_i32(i32 %a) {
; NO_SCALAR_INC-LABEL: dech_scalar_i32:
; NO_SCALAR_INC: // %bb.0:
; NO_SCALAR_INC-NEXT: cnth x8
; NO_SCALAR_INC-NEXT: neg x8, x8
; NO_SCALAR_INC-NEXT: add w0, w0, w8
; NO_SCALAR_INC-NEXT: sub w0, w0, w8
; NO_SCALAR_INC-NEXT: ret
;
; CHECK-LABEL: dech_scalar_i32:
Expand All @@ -389,8 +385,7 @@ define i32 @decw_scalar_i32(i32 %a) {
; NO_SCALAR_INC-LABEL: decw_scalar_i32:
; NO_SCALAR_INC: // %bb.0:
; NO_SCALAR_INC-NEXT: cntw x8
; NO_SCALAR_INC-NEXT: neg x8, x8
; NO_SCALAR_INC-NEXT: add w0, w0, w8
; NO_SCALAR_INC-NEXT: sub w0, w0, w8
; NO_SCALAR_INC-NEXT: ret
;
; CHECK-LABEL: decw_scalar_i32:
Expand All @@ -411,8 +406,7 @@ define i32 @decd_scalar_i32(i32 %a) {
; NO_SCALAR_INC-LABEL: decd_scalar_i32:
; NO_SCALAR_INC: // %bb.0:
; NO_SCALAR_INC-NEXT: cntd x8
; NO_SCALAR_INC-NEXT: neg x8, x8
; NO_SCALAR_INC-NEXT: add w0, w0, w8
; NO_SCALAR_INC-NEXT: sub w0, w0, w8
; NO_SCALAR_INC-NEXT: ret
;
; CHECK-LABEL: decd_scalar_i32:
Expand Down
10 changes: 5 additions & 5 deletions llvm/test/CodeGen/AArch64/vscale-and-sve-cnt-demandedbits.ll
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ define i32 @vscale_with_multiplier() vscale_range(1,16) {
; CHECK-LABEL: vscale_with_multiplier:
; CHECK: // %bb.0:
; CHECK-NEXT: rdvl x8, #1
; CHECK-NEXT: mov w9, #5
; CHECK-NEXT: mov w9, #5 // =0x5
; CHECK-NEXT: lsr x8, x8, #4
; CHECK-NEXT: mul x8, x8, x9
; CHECK-NEXT: and w9, w8, #0x3f
Expand All @@ -212,7 +212,7 @@ define i32 @vscale_with_negative_multiplier() vscale_range(1,16) {
; CHECK-LABEL: vscale_with_negative_multiplier:
; CHECK: // %bb.0:
; CHECK-NEXT: rdvl x8, #1
; CHECK-NEXT: mov x9, #-5
; CHECK-NEXT: mov x9, #-5 // =0xfffffffffffffffb
; CHECK-NEXT: lsr x8, x8, #4
; CHECK-NEXT: mul x8, x8, x9
; CHECK-NEXT: and w9, w8, #0xffffffc0
Expand All @@ -230,9 +230,9 @@ define i32 @pow2_vscale_with_negative_multiplier() vscale_range(1,16) {
; CHECK-LABEL: pow2_vscale_with_negative_multiplier:
; CHECK: // %bb.0:
; CHECK-NEXT: cntd x8
; CHECK-NEXT: neg x8, x8
; CHECK-NEXT: orr w9, w8, #0xfffffff0
; CHECK-NEXT: add w0, w8, w9
; CHECK-NEXT: neg x9, x8
; CHECK-NEXT: orr w9, w9, #0xfffffff0
; CHECK-NEXT: sub w0, w9, w8
; CHECK-NEXT: ret
%vscale = call i32 @llvm.vscale.i32()
%mul = mul i32 %vscale, -2
Expand Down
408 changes: 387 additions & 21 deletions llvm/test/CodeGen/AMDGPU/llvm.fptrunc.round.ll

Large diffs are not rendered by default.

480 changes: 480 additions & 0 deletions llvm/test/Transforms/LoopUnroll/unroll-loads-cse.ll

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,92 @@ exit:
ret void
}

define void @cse_matching_load_from_previous_unrolled_iteration(i32 %N, ptr %src, ptr noalias %dst) {
; CHECK-LABEL: define void @cse_matching_load_from_previous_unrolled_iteration(
; CHECK-SAME: i32 [[N:%.*]], ptr nocapture readonly [[SRC:%.*]], ptr noalias nocapture writeonly [[DST:%.*]]) local_unnamed_addr #[[ATTR0]] {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[SRC_4:%.*]] = getelementptr i8, ptr [[SRC]], i64 4
; CHECK-NEXT: [[SRC_12:%.*]] = getelementptr i8, ptr [[SRC]], i64 12
; CHECK-NEXT: [[CMP141:%.*]] = icmp sgt i32 [[N]], 0
; CHECK-NEXT: br i1 [[CMP141]], label [[LOOP_LATCH_PREHEADER:%.*]], label [[EXIT:%.*]]
; CHECK: loop.latch.preheader:
; CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext nneg i32 [[N]] to i64
; CHECK-NEXT: [[XTRAITER:%.*]] = and i64 [[WIDE_TRIP_COUNT]], 1
; CHECK-NEXT: [[TMP0:%.*]] = icmp eq i32 [[N]], 1
; CHECK-NEXT: br i1 [[TMP0]], label [[EXIT_LOOPEXIT_UNR_LCSSA:%.*]], label [[LOOP_LATCH_PREHEADER_NEW:%.*]]
; CHECK: loop.latch.preheader.new:
; CHECK-NEXT: [[UNROLL_ITER:%.*]] = and i64 [[WIDE_TRIP_COUNT]], 2147483646
; CHECK-NEXT: br label [[LOOP_LATCH:%.*]]
; CHECK: loop.latch:
; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[LOOP_LATCH_PREHEADER_NEW]] ], [ [[INDVARS_IV_NEXT_1:%.*]], [[LOOP_LATCH]] ]
; CHECK-NEXT: [[NITER:%.*]] = phi i64 [ 0, [[LOOP_LATCH_PREHEADER_NEW]] ], [ [[NITER_NEXT_1:%.*]], [[LOOP_LATCH]] ]
; CHECK-NEXT: [[GEP_SRC_12:%.*]] = getelementptr <2 x i32>, ptr [[SRC_12]], i64 [[INDVARS_IV]]
; CHECK-NEXT: [[L_12:%.*]] = load <2 x i32>, ptr [[GEP_SRC_12]], align 8
; CHECK-NEXT: [[GEP_SRC_4:%.*]] = getelementptr <2 x i32>, ptr [[SRC_4]], i64 [[INDVARS_IV]]
; CHECK-NEXT: [[L_4:%.*]] = load <2 x i32>, ptr [[GEP_SRC_4]], align 8
; CHECK-NEXT: [[MUL:%.*]] = mul <2 x i32> [[L_4]], [[L_12]]
; CHECK-NEXT: [[GEP_DST:%.*]] = getelementptr <2 x i32>, ptr [[DST]], i64 [[INDVARS_IV]]
; CHECK-NEXT: store <2 x i32> [[MUL]], ptr [[GEP_DST]], align 8
; CHECK-NEXT: [[INDVARS_IV_NEXT:%.*]] = or disjoint i64 [[INDVARS_IV]], 1
; CHECK-NEXT: [[GEP_SRC_12_1:%.*]] = getelementptr <2 x i32>, ptr [[SRC_12]], i64 [[INDVARS_IV_NEXT]]
; CHECK-NEXT: [[L_12_1:%.*]] = load <2 x i32>, ptr [[GEP_SRC_12_1]], align 8
; CHECK-NEXT: [[GEP_SRC_4_1:%.*]] = getelementptr <2 x i32>, ptr [[SRC_4]], i64 [[INDVARS_IV_NEXT]]
; CHECK-NEXT: [[L_4_1:%.*]] = load <2 x i32>, ptr [[GEP_SRC_4_1]], align 8
; CHECK-NEXT: [[MUL_1:%.*]] = mul <2 x i32> [[L_4_1]], [[L_12_1]]
; CHECK-NEXT: [[GEP_DST_1:%.*]] = getelementptr <2 x i32>, ptr [[DST]], i64 [[INDVARS_IV_NEXT]]
; CHECK-NEXT: store <2 x i32> [[MUL_1]], ptr [[GEP_DST_1]], align 8
; CHECK-NEXT: [[INDVARS_IV_NEXT_1]] = add nuw nsw i64 [[INDVARS_IV]], 2
; CHECK-NEXT: [[NITER_NEXT_1]] = add i64 [[NITER]], 2
; CHECK-NEXT: [[NITER_NCMP_1:%.*]] = icmp eq i64 [[NITER_NEXT_1]], [[UNROLL_ITER]]
; CHECK-NEXT: br i1 [[NITER_NCMP_1]], label [[EXIT_LOOPEXIT_UNR_LCSSA]], label [[LOOP_LATCH]], !llvm.loop [[LOOP3:![0-9]+]]
; CHECK: exit.loopexit.unr-lcssa:
; CHECK-NEXT: [[INDVARS_IV_UNR:%.*]] = phi i64 [ 0, [[LOOP_LATCH_PREHEADER]] ], [ [[INDVARS_IV_NEXT_1]], [[LOOP_LATCH]] ]
; CHECK-NEXT: [[LCMP_MOD_NOT:%.*]] = icmp eq i64 [[XTRAITER]], 0
; CHECK-NEXT: br i1 [[LCMP_MOD_NOT]], label [[EXIT]], label [[LOOP_LATCH_EPIL:%.*]]
; CHECK: loop.latch.epil:
; CHECK-NEXT: [[GEP_SRC_12_EPIL:%.*]] = getelementptr <2 x i32>, ptr [[SRC_12]], i64 [[INDVARS_IV_UNR]]
; CHECK-NEXT: [[L_12_EPIL:%.*]] = load <2 x i32>, ptr [[GEP_SRC_12_EPIL]], align 8
; CHECK-NEXT: [[GEP_SRC_4_EPIL:%.*]] = getelementptr <2 x i32>, ptr [[SRC_4]], i64 [[INDVARS_IV_UNR]]
; CHECK-NEXT: [[L_4_EPIL:%.*]] = load <2 x i32>, ptr [[GEP_SRC_4_EPIL]], align 8
; CHECK-NEXT: [[MUL_EPIL:%.*]] = mul <2 x i32> [[L_4_EPIL]], [[L_12_EPIL]]
; CHECK-NEXT: [[GEP_DST_EPIL:%.*]] = getelementptr <2 x i32>, ptr [[DST]], i64 [[INDVARS_IV_UNR]]
; CHECK-NEXT: store <2 x i32> [[MUL_EPIL]], ptr [[GEP_DST_EPIL]], align 8
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
entry:
%src.4 = getelementptr i8, ptr %src, i64 4
%src.12 = getelementptr i8, ptr %src, i64 12
br label %loop.header

loop.header:
%iv = phi i32 [ 0, %entry ], [ %iv.next, %loop.latch ]
%cmp14 = icmp slt i32 %iv, %N
br i1 %cmp14, label %loop.latch, label %exit

loop.latch:
%iv.ext = zext i32 %iv to i64
%gep.src.12 = getelementptr <2 x i32>, ptr %src.12, i64 %iv.ext
%l.12 = load <2 x i32>, ptr %gep.src.12, align 8
%gep.src.4 = getelementptr <2 x i32>, ptr %src.4, i64 %iv.ext
%l.4 = load <2 x i32>, ptr %gep.src.4, align 8
%mul = mul <2 x i32> %l.12, %l.4
%gep.dst = getelementptr <2 x i32>, ptr %dst, i64 %iv.ext
store <2 x i32> %mul, ptr %gep.dst
%iv.next = add nuw nsw i32 %iv, 1
br label %loop.header, !llvm.loop !0

exit:
ret void
}

!0 = distinct !{!0, !1, !2}
!1 = !{!"llvm.loop.mustprogress"}
!2 = !{!"llvm.loop.unroll.count", i32 2}
;.
; CHECK: [[LOOP0]] = distinct !{[[LOOP0]], [[META1:![0-9]+]], [[META2:![0-9]+]]}
; CHECK: [[META1]] = !{!"llvm.loop.mustprogress"}
; CHECK: [[META2]] = !{!"llvm.loop.unroll.disable"}
; CHECK: [[LOOP3]] = distinct !{[[LOOP3]], [[META1]], [[META2]]}
;.
32 changes: 32 additions & 0 deletions llvm/unittests/Frontend/OpenMPCompositionTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//

#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Frontend/OpenMP/OMP.h"
#include "gtest/gtest.h"

Expand Down Expand Up @@ -40,6 +41,37 @@ TEST(Composition, GetCompoundConstruct) {
ASSERT_EQ(C7, OMPD_do_simd); // Make sure it's not OMPD_end_do_simd
}

TEST(Composition, GetLeafOrCompositeConstructs) {
SmallVector<Directive> Out1;
auto Ret1 = getLeafOrCompositeConstructs(
OMPD_target_teams_distribute_parallel_for, Out1);
ASSERT_EQ(Ret1, ArrayRef<Directive>(Out1));
ASSERT_EQ((ArrayRef<Directive>(Out1)),
(ArrayRef<Directive>{OMPD_target, OMPD_teams,
OMPD_distribute_parallel_for}));

SmallVector<Directive> Out2;
auto Ret2 =
getLeafOrCompositeConstructs(OMPD_parallel_masked_taskloop_simd, Out2);
ASSERT_EQ(Ret2, ArrayRef<Directive>(Out2));
ASSERT_EQ(
(ArrayRef<Directive>(Out2)),
(ArrayRef<Directive>{OMPD_parallel, OMPD_masked, OMPD_taskloop_simd}));

SmallVector<Directive> Out3;
auto Ret3 =
getLeafOrCompositeConstructs(OMPD_distribute_parallel_do_simd, Out3);
ASSERT_EQ(Ret3, ArrayRef<Directive>(Out3));
ASSERT_EQ((ArrayRef<Directive>(Out3)),
(ArrayRef<Directive>{OMPD_distribute_parallel_do_simd}));

SmallVector<Directive> Out4;
auto Ret4 = getLeafOrCompositeConstructs(OMPD_target_parallel_loop, Out4);
ASSERT_EQ(Ret4, ArrayRef<Directive>(Out4));
ASSERT_EQ((ArrayRef<Directive>(Out4)),
(ArrayRef<Directive>{OMPD_target, OMPD_parallel, OMPD_loop}));
}

TEST(Composition, IsLeafConstruct) {
ASSERT_TRUE(isLeafConstruct(OMPD_loop));
ASSERT_TRUE(isLeafConstruct(OMPD_teams));
Expand Down
20 changes: 19 additions & 1 deletion llvm/utils/gn/secondary/llvm/include/llvm/TargetParser/BUILD.gn
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
import("//llvm/utils/TableGen/tablegen.gni")

tablegen("ARMTargetParserDef") {
visibility = [ ":gen" ]
args = [ "-gen-arm-target-def" ]
td_file = "//llvm/lib/Target/ARM/ARM.td"
tblgen_target = "//llvm/utils/TableGen:llvm-min-tblgen"
}

tablegen("AArch64TargetParserDef") {
visibility = [ ":gen" ]
args = [ "-gen-arm-target-def" ]
td_file = "//llvm/lib/Target/AArch64/AArch64.td"
tblgen_target = "//llvm/utils/TableGen:llvm-min-tblgen"
}

tablegen("RISCVTargetParserDef") {
visibility = [ ":gen" ]
args = [ "-gen-riscv-target-def" ]
Expand All @@ -8,5 +22,9 @@ tablegen("RISCVTargetParserDef") {
}

group("gen") {
deps = [ ":RISCVTargetParserDef" ]
deps = [
":ARMTargetParserDef",
":AArch64TargetParserDef",
":RISCVTargetParserDef",
]
}
1 change: 1 addition & 0 deletions llvm/utils/gn/secondary/llvm/utils/TableGen/BUILD.gn
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
source_set("llvm-min-tblgen-sources") {
sources = [
"ARMTargetDefEmitter.cpp",
"Attributes.cpp",
"DirectiveEmitter.cpp",
"IntrinsicEmitter.cpp",
Expand Down
7 changes: 7 additions & 0 deletions mlir/include/mlir-c/Dialect/LLVM.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ MLIR_DECLARE_CAPI_DIALECT_REGISTRATION(LLVM, llvm);
MLIR_CAPI_EXPORTED MlirType mlirLLVMPointerTypeGet(MlirContext ctx,
unsigned addressSpace);

/// Returns `true` if the type is an LLVM dialect pointer type.
MLIR_CAPI_EXPORTED bool mlirTypeIsALLVMPointerType(MlirType type);

/// Returns address space of llvm.ptr
MLIR_CAPI_EXPORTED unsigned
mlirLLVMPointerTypeGetAddressSpace(MlirType pointerType);

/// Creates an llmv.void type.
MLIR_CAPI_EXPORTED MlirType mlirLLVMVoidTypeGet(MlirContext ctx);

Expand Down
1 change: 1 addition & 0 deletions mlir/include/mlir/Interfaces/MemorySlotInterfaces.td
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ def PromotableMemOpInterface : OpInterface<"PromotableMemOpInterface"> {
"::mlir::Value", "getStored",
(ins "const ::mlir::MemorySlot &":$slot,
"::mlir::RewriterBase &":$rewriter,
"::mlir::Value":$reachingDef,
"const ::mlir::DataLayout &":$dataLayout)
>,
InterfaceMethod<[{
Expand Down
43 changes: 35 additions & 8 deletions mlir/lib/Bindings/Python/DialectLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ using namespace mlir::python;
using namespace mlir::python::adaptors;

void populateDialectLLVMSubmodule(const pybind11::module &m) {

//===--------------------------------------------------------------------===//
// StructType
//===--------------------------------------------------------------------===//

auto llvmStructType =
mlir_type_subclass(m, "StructType", mlirTypeIsALLVMStructType);

Expand All @@ -35,25 +40,24 @@ void populateDialectLLVMSubmodule(const pybind11::module &m) {
}
return cls(type);
},
py::arg("cls"), py::arg("elements"), py::kw_only(),
py::arg("packed") = false, py::arg("loc") = py::none());
"cls"_a, "elements"_a, py::kw_only(), "packed"_a = false,
"loc"_a = py::none());

llvmStructType.def_classmethod(
"get_identified",
[](py::object cls, const std::string &name, MlirContext context) {
return cls(mlirLLVMStructTypeIdentifiedGet(
context, mlirStringRefCreate(name.data(), name.size())));
},
py::arg("cls"), py::arg("name"), py::kw_only(),
py::arg("context") = py::none());
"cls"_a, "name"_a, py::kw_only(), "context"_a = py::none());

llvmStructType.def_classmethod(
"get_opaque",
[](py::object cls, const std::string &name, MlirContext context) {
return cls(mlirLLVMStructTypeOpaqueGet(
context, mlirStringRefCreate(name.data(), name.size())));
},
py::arg("cls"), py::arg("name"), py::arg("context") = py::none());
"cls"_a, "name"_a, "context"_a = py::none());

llvmStructType.def(
"set_body",
Expand All @@ -65,7 +69,7 @@ void populateDialectLLVMSubmodule(const pybind11::module &m) {
"Struct body already set to different content.");
}
},
py::arg("elements"), py::kw_only(), py::arg("packed") = false);
"elements"_a, py::kw_only(), "packed"_a = false);

llvmStructType.def_classmethod(
"new_identified",
Expand All @@ -75,8 +79,8 @@ void populateDialectLLVMSubmodule(const pybind11::module &m) {
ctx, mlirStringRefCreate(name.data(), name.length()),
elements.size(), elements.data(), packed));
},
py::arg("cls"), py::arg("name"), py::arg("elements"), py::kw_only(),
py::arg("packed") = false, py::arg("context") = py::none());
"cls"_a, "name"_a, "elements"_a, py::kw_only(), "packed"_a = false,
"context"_a = py::none());

llvmStructType.def_property_readonly(
"name", [](MlirType type) -> std::optional<std::string> {
Expand Down Expand Up @@ -105,6 +109,29 @@ void populateDialectLLVMSubmodule(const pybind11::module &m) {

llvmStructType.def_property_readonly(
"opaque", [](MlirType type) { return mlirLLVMStructTypeIsOpaque(type); });

//===--------------------------------------------------------------------===//
// PointerType
//===--------------------------------------------------------------------===//

mlir_type_subclass(m, "PointerType", mlirTypeIsALLVMPointerType)
.def_classmethod(
"get",
[](py::object cls, std::optional<unsigned> addressSpace,
MlirContext context) {
CollectDiagnosticsToStringScope scope(context);
MlirType type = mlirLLVMPointerTypeGet(
context, addressSpace.has_value() ? *addressSpace : 0);
if (mlirTypeIsNull(type)) {
throw py::value_error(scope.takeMessage());
}
return cls(type);
},
"cls"_a, "address_space"_a = py::none(), py::kw_only(),
"context"_a = py::none())
.def_property_readonly("address_space", [](MlirType type) {
return mlirLLVMPointerTypeGetAddressSpace(type);
});
}

PYBIND11_MODULE(_mlirDialectsLLVM, m) {
Expand Down
8 changes: 8 additions & 0 deletions mlir/lib/CAPI/Dialect/LLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ MlirType mlirLLVMPointerTypeGet(MlirContext ctx, unsigned addressSpace) {
return wrap(LLVMPointerType::get(unwrap(ctx), addressSpace));
}

bool mlirTypeIsALLVMPointerType(MlirType type) {
return isa<LLVM::LLVMPointerType>(unwrap(type));
}

unsigned mlirLLVMPointerTypeGetAddressSpace(MlirType pointerType) {
return cast<LLVM::LLVMPointerType>(unwrap(pointerType)).getAddressSpace();
}

MlirType mlirLLVMVoidTypeGet(MlirContext ctx) {
return wrap(LLVMVoidType::get(unwrap(ctx)));
}
Expand Down
214 changes: 159 additions & 55 deletions mlir/lib/Dialect/LLVMIR/IR/LLVMMemorySlot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ bool LLVM::LoadOp::loadsFrom(const MemorySlot &slot) {
bool LLVM::LoadOp::storesTo(const MemorySlot &slot) { return false; }

Value LLVM::LoadOp::getStored(const MemorySlot &slot, RewriterBase &rewriter,
const DataLayout &dataLayout) {
Value reachingDef, const DataLayout &dataLayout) {
llvm_unreachable("getStored should not be called on LoadOp");
}

Expand Down Expand Up @@ -142,23 +142,29 @@ static bool isSupportedTypeForConversion(Type type) {
}

/// Checks that `rhs` can be converted to `lhs` by a sequence of casts and
/// truncations.
/// truncations. Checks for narrowing or widening conversion compatibility
/// depending on `narrowingConversion`.
static bool areConversionCompatible(const DataLayout &layout, Type targetType,
Type srcType) {
Type srcType, bool narrowingConversion) {
if (targetType == srcType)
return true;

if (!isSupportedTypeForConversion(targetType) ||
!isSupportedTypeForConversion(srcType))
return false;

uint64_t targetSize = layout.getTypeSize(targetType);
uint64_t srcSize = layout.getTypeSize(srcType);

// Pointer casts will only be sane when the bitsize of both pointer types is
// the same.
if (isa<LLVM::LLVMPointerType>(targetType) &&
isa<LLVM::LLVMPointerType>(srcType))
return layout.getTypeSize(targetType) == layout.getTypeSize(srcType);
return targetSize == srcSize;

return layout.getTypeSize(targetType) <= layout.getTypeSize(srcType);
if (narrowingConversion)
return targetSize <= srcSize;
return targetSize >= srcSize;
}

/// Checks if `dataLayout` describes a little endian layout.
Expand All @@ -167,22 +173,49 @@ static bool isBigEndian(const DataLayout &dataLayout) {
return endiannessStr && endiannessStr == "big";
}

/// The size of a byte in bits.
constexpr const static uint64_t kBitsInByte = 8;
/// Converts a value to an integer type of the same size.
/// Assumes that the type can be converted.
static Value castToSameSizedInt(RewriterBase &rewriter, Location loc, Value val,
const DataLayout &dataLayout) {
Type type = val.getType();
assert(isSupportedTypeForConversion(type) &&
"expected value to have a convertible type");

if (isa<IntegerType>(type))
return val;

uint64_t typeBitSize = dataLayout.getTypeSizeInBits(type);
IntegerType valueSizeInteger = rewriter.getIntegerType(typeBitSize);

if (isa<LLVM::LLVMPointerType>(type))
return rewriter.createOrFold<LLVM::PtrToIntOp>(loc, valueSizeInteger, val);
return rewriter.createOrFold<LLVM::BitcastOp>(loc, valueSizeInteger, val);
}

/// Converts a value with an integer type to `targetType`.
static Value castIntValueToSameSizedType(RewriterBase &rewriter, Location loc,
Value val, Type targetType) {
assert(isa<IntegerType>(val.getType()) &&
"expected value to have an integer type");
assert(isSupportedTypeForConversion(targetType) &&
"expected the target type to be supported for conversions");
if (val.getType() == targetType)
return val;
if (isa<LLVM::LLVMPointerType>(targetType))
return rewriter.createOrFold<LLVM::IntToPtrOp>(loc, targetType, val);
return rewriter.createOrFold<LLVM::BitcastOp>(loc, targetType, val);
}

/// Constructs operations that convert `inputValue` into a new value of type
/// `targetType`. Assumes that this conversion is possible.
static Value createConversionSequence(RewriterBase &rewriter, Location loc,
Value srcValue, Type targetType,
const DataLayout &dataLayout) {
// Get the types of the source and target values.
/// Constructs operations that convert `srcValue` into a new value of type
/// `targetType`. Assumes the types have the same bitsize.
static Value castSameSizedTypes(RewriterBase &rewriter, Location loc,
Value srcValue, Type targetType,
const DataLayout &dataLayout) {
Type srcType = srcValue.getType();
assert(areConversionCompatible(dataLayout, targetType, srcType) &&
assert(areConversionCompatible(dataLayout, targetType, srcType,
/*narrowingConversion=*/true) &&
"expected that the compatibility was checked before");

uint64_t srcTypeSize = dataLayout.getTypeSize(srcType);
uint64_t targetTypeSize = dataLayout.getTypeSize(targetType);

// Nothing has to be done if the types are already the same.
if (srcType == targetType)
return srcValue;
Expand All @@ -196,48 +229,117 @@ static Value createConversionSequence(RewriterBase &rewriter, Location loc,
return rewriter.createOrFold<LLVM::AddrSpaceCastOp>(loc, targetType,
srcValue);

IntegerType valueSizeInteger =
rewriter.getIntegerType(srcTypeSize * kBitsInByte);
Value replacement = srcValue;
// For all other castable types, casting through integers is necessary.
Value replacement = castToSameSizedInt(rewriter, loc, srcValue, dataLayout);
return castIntValueToSameSizedType(rewriter, loc, replacement, targetType);
}

/// Constructs operations that convert `srcValue` into a new value of type
/// `targetType`. Performs bit-level extraction if the source type is larger
/// than the target type. Assumes that this conversion is possible.
static Value createExtractAndCast(RewriterBase &rewriter, Location loc,
Value srcValue, Type targetType,
const DataLayout &dataLayout) {
// Get the types of the source and target values.
Type srcType = srcValue.getType();
assert(areConversionCompatible(dataLayout, targetType, srcType,
/*narrowingConversion=*/true) &&
"expected that the compatibility was checked before");

uint64_t srcTypeSize = dataLayout.getTypeSizeInBits(srcType);
uint64_t targetTypeSize = dataLayout.getTypeSizeInBits(targetType);
if (srcTypeSize == targetTypeSize)
return castSameSizedTypes(rewriter, loc, srcValue, targetType, dataLayout);

// First, cast the value to a same-sized integer type.
if (isa<LLVM::LLVMPointerType>(srcType))
replacement = rewriter.createOrFold<LLVM::PtrToIntOp>(loc, valueSizeInteger,
replacement);
else if (replacement.getType() != valueSizeInteger)
replacement = rewriter.createOrFold<LLVM::BitcastOp>(loc, valueSizeInteger,
replacement);
Value replacement = castToSameSizedInt(rewriter, loc, srcValue, dataLayout);

// Truncate the integer if the size of the target is less than the value.
if (targetTypeSize != srcTypeSize) {
if (isBigEndian(dataLayout)) {
uint64_t shiftAmount = (srcTypeSize - targetTypeSize) * kBitsInByte;
auto shiftConstant = rewriter.create<LLVM::ConstantOp>(
loc, rewriter.getIntegerAttr(srcType, shiftAmount));
replacement =
rewriter.createOrFold<LLVM::LShrOp>(loc, srcValue, shiftConstant);
}

replacement = rewriter.create<LLVM::TruncOp>(
loc, rewriter.getIntegerType(targetTypeSize * kBitsInByte),
replacement);
if (isBigEndian(dataLayout)) {
uint64_t shiftAmount = srcTypeSize - targetTypeSize;
auto shiftConstant = rewriter.create<LLVM::ConstantOp>(
loc, rewriter.getIntegerAttr(srcType, shiftAmount));
replacement =
rewriter.createOrFold<LLVM::LShrOp>(loc, srcValue, shiftConstant);
}

replacement = rewriter.create<LLVM::TruncOp>(
loc, rewriter.getIntegerType(targetTypeSize), replacement);

// Now cast the integer to the actual target type if required.
if (isa<LLVM::LLVMPointerType>(targetType))
replacement =
rewriter.createOrFold<LLVM::IntToPtrOp>(loc, targetType, replacement);
else if (replacement.getType() != targetType)
replacement =
rewriter.createOrFold<LLVM::BitcastOp>(loc, targetType, replacement);
return castIntValueToSameSizedType(rewriter, loc, replacement, targetType);
}

/// Constructs operations that insert the bits of `srcValue` into the
/// "beginning" of `reachingDef` (beginning is endianness dependent).
/// Assumes that this conversion is possible.
static Value createInsertAndCast(RewriterBase &rewriter, Location loc,
Value srcValue, Value reachingDef,
const DataLayout &dataLayout) {

assert(areConversionCompatible(dataLayout, reachingDef.getType(),
srcValue.getType(),
/*narrowingConversion=*/false) &&
"expected that the compatibility was checked before");
uint64_t valueTypeSize = dataLayout.getTypeSizeInBits(srcValue.getType());
uint64_t slotTypeSize = dataLayout.getTypeSizeInBits(reachingDef.getType());
if (slotTypeSize == valueTypeSize)
return castSameSizedTypes(rewriter, loc, srcValue, reachingDef.getType(),
dataLayout);

// In the case where the store only overwrites parts of the memory,
// bit fiddling is required to construct the new value.

// First convert both values to integers of the same size.
Value defAsInt = castToSameSizedInt(rewriter, loc, reachingDef, dataLayout);
Value valueAsInt = castToSameSizedInt(rewriter, loc, srcValue, dataLayout);
// Extend the value to the size of the reaching definition.
valueAsInt =
rewriter.createOrFold<LLVM::ZExtOp>(loc, defAsInt.getType(), valueAsInt);
uint64_t sizeDifference = slotTypeSize - valueTypeSize;
if (isBigEndian(dataLayout)) {
// On big endian systems, a store to the base pointer overwrites the most
// significant bits. To accomodate for this, the stored value needs to be
// shifted into the according position.
Value bigEndianShift = rewriter.create<LLVM::ConstantOp>(
loc, rewriter.getIntegerAttr(defAsInt.getType(), sizeDifference));
valueAsInt =
rewriter.createOrFold<LLVM::ShlOp>(loc, valueAsInt, bigEndianShift);
}

// Construct the mask that is used to erase the bits that are overwritten by
// the store.
APInt maskValue;
if (isBigEndian(dataLayout)) {
// Build a mask that has the most significant bits set to zero.
// Note: This is the same as 2^sizeDifference - 1
maskValue = APInt::getAllOnes(sizeDifference).zext(slotTypeSize);
} else {
// Build a mask that has the least significant bits set to zero.
// Note: This is the same as -(2^valueTypeSize)
maskValue = APInt::getAllOnes(valueTypeSize).zext(slotTypeSize);
maskValue.flipAllBits();
}

// Mask out the affected bits ...
Value mask = rewriter.create<LLVM::ConstantOp>(
loc, rewriter.getIntegerAttr(defAsInt.getType(), maskValue));
Value masked = rewriter.createOrFold<LLVM::AndOp>(loc, defAsInt, mask);

// ... and combine the result with the new value.
Value combined = rewriter.createOrFold<LLVM::OrOp>(loc, masked, valueAsInt);

return replacement;
return castIntValueToSameSizedType(rewriter, loc, combined,
reachingDef.getType());
}

Value LLVM::StoreOp::getStored(const MemorySlot &slot, RewriterBase &rewriter,
Value reachingDef,
const DataLayout &dataLayout) {
return createConversionSequence(rewriter, getLoc(), getValue(), slot.elemType,
dataLayout);
assert(reachingDef && reachingDef.getType() == slot.elemType &&
"expected the reaching definition's type to match the slot's type");
return createInsertAndCast(rewriter, getLoc(), getValue(), reachingDef,
dataLayout);
}

bool LLVM::LoadOp::canUsesBeRemoved(
Expand All @@ -249,11 +351,10 @@ bool LLVM::LoadOp::canUsesBeRemoved(
Value blockingUse = (*blockingUses.begin())->get();
// If the blocking use is the slot ptr itself, there will be enough
// context to reconstruct the result of the load at removal time, so it can
// be removed (provided it loads the exact stored value and is not
// volatile).
// be removed (provided it is not volatile).
return blockingUse == slot.ptr && getAddr() == slot.ptr &&
areConversionCompatible(dataLayout, getResult().getType(),
slot.elemType) &&
slot.elemType, /*narrowingConversion=*/true) &&
!getVolatile_();
}

Expand All @@ -263,9 +364,8 @@ DeletionKind LLVM::LoadOp::removeBlockingUses(
const DataLayout &dataLayout) {
// `canUsesBeRemoved` checked this blocking use must be the loaded slot
// pointer.
Value newResult =
createConversionSequence(rewriter, getLoc(), reachingDefinition,
getResult().getType(), dataLayout);
Value newResult = createExtractAndCast(rewriter, getLoc(), reachingDefinition,
getResult().getType(), dataLayout);
rewriter.replaceAllUsesWith(getResult(), newResult);
return DeletionKind::Delete;
}
Expand All @@ -283,7 +383,8 @@ bool LLVM::StoreOp::canUsesBeRemoved(
return blockingUse == slot.ptr && getAddr() == slot.ptr &&
getValue() != slot.ptr &&
areConversionCompatible(dataLayout, slot.elemType,
getValue().getType()) &&
getValue().getType(),
/*narrowingConversion=*/false) &&
!getVolatile_();
}

Expand Down Expand Up @@ -838,6 +939,7 @@ bool LLVM::MemsetOp::storesTo(const MemorySlot &slot) {
}

Value LLVM::MemsetOp::getStored(const MemorySlot &slot, RewriterBase &rewriter,
Value reachingDef,
const DataLayout &dataLayout) {
// TODO: Support non-integer types.
return TypeSwitch<Type, Value>(slot.elemType)
Expand Down Expand Up @@ -1149,6 +1251,7 @@ bool LLVM::MemcpyOp::storesTo(const MemorySlot &slot) {
}

Value LLVM::MemcpyOp::getStored(const MemorySlot &slot, RewriterBase &rewriter,
Value reachingDef,
const DataLayout &dataLayout) {
return memcpyGetStored(*this, slot, rewriter);
}
Expand Down Expand Up @@ -1199,7 +1302,7 @@ bool LLVM::MemcpyInlineOp::storesTo(const MemorySlot &slot) {
}

Value LLVM::MemcpyInlineOp::getStored(const MemorySlot &slot,
RewriterBase &rewriter,
RewriterBase &rewriter, Value reachingDef,
const DataLayout &dataLayout) {
return memcpyGetStored(*this, slot, rewriter);
}
Expand Down Expand Up @@ -1252,6 +1355,7 @@ bool LLVM::MemmoveOp::storesTo(const MemorySlot &slot) {
}

Value LLVM::MemmoveOp::getStored(const MemorySlot &slot, RewriterBase &rewriter,
Value reachingDef,
const DataLayout &dataLayout) {
return memcpyGetStored(*this, slot, rewriter);
}
Expand Down
2 changes: 2 additions & 0 deletions mlir/lib/Dialect/MemRef/IR/MemRefMemorySlot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ bool memref::LoadOp::loadsFrom(const MemorySlot &slot) {
bool memref::LoadOp::storesTo(const MemorySlot &slot) { return false; }

Value memref::LoadOp::getStored(const MemorySlot &slot, RewriterBase &rewriter,
Value reachingDef,
const DataLayout &dataLayout) {
llvm_unreachable("getStored should not be called on LoadOp");
}
Expand Down Expand Up @@ -242,6 +243,7 @@ bool memref::StoreOp::storesTo(const MemorySlot &slot) {
}

Value memref::StoreOp::getStored(const MemorySlot &slot, RewriterBase &rewriter,
Value reachingDef,
const DataLayout &dataLayout) {
return getValue();
}
Expand Down
21 changes: 11 additions & 10 deletions mlir/lib/Transforms/Mem2Reg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,13 +191,13 @@ class MemorySlotPromoter {

/// Lazily-constructed default value representing the content of the slot when
/// no store has been executed. This function may mutate IR.
Value getLazyDefaultValue();
Value getOrCreateDefaultValue();

MemorySlot slot;
PromotableAllocationOpInterface allocator;
RewriterBase &rewriter;
/// Potentially non-initialized default value. Use `getLazyDefaultValue` to
/// initialize it on demand.
/// Potentially non-initialized default value. Use `getOrCreateDefaultValue`
/// to initialize it on demand.
Value defaultValue;
/// Contains the reaching definition at this operation. Reaching definitions
/// are only computed for promotable memory operations with blocking uses.
Expand Down Expand Up @@ -232,7 +232,7 @@ MemorySlotPromoter::MemorySlotPromoter(
#endif // NDEBUG
}

Value MemorySlotPromoter::getLazyDefaultValue() {
Value MemorySlotPromoter::getOrCreateDefaultValue() {
if (defaultValue)
return defaultValue;

Expand Down Expand Up @@ -438,7 +438,7 @@ Value MemorySlotPromoter::computeReachingDefInBlock(Block *block,

if (memOp.storesTo(slot)) {
rewriter.setInsertionPointAfter(memOp);
Value stored = memOp.getStored(slot, rewriter, dataLayout);
Value stored = memOp.getStored(slot, rewriter, reachingDef, dataLayout);
assert(stored && "a memory operation storing to a slot must provide a "
"new definition of the slot");
reachingDef = stored;
Expand All @@ -452,6 +452,7 @@ Value MemorySlotPromoter::computeReachingDefInBlock(Block *block,

void MemorySlotPromoter::computeReachingDefInRegion(Region *region,
Value reachingDef) {
assert(reachingDef && "expected an initial reaching def to be provided");
if (region->hasOneBlock()) {
computeReachingDefInBlock(&region->front(), reachingDef);
return;
Expand Down Expand Up @@ -508,12 +509,11 @@ void MemorySlotPromoter::computeReachingDefInRegion(Region *region,
}

job.reachingDef = computeReachingDefInBlock(block, job.reachingDef);
assert(job.reachingDef);

if (auto terminator = dyn_cast<BranchOpInterface>(block->getTerminator())) {
for (BlockOperand &blockOperand : terminator->getBlockOperands()) {
if (info.mergePoints.contains(blockOperand.get())) {
if (!job.reachingDef)
job.reachingDef = getLazyDefaultValue();
rewriter.modifyOpInPlace(terminator, [&]() {
terminator.getSuccessorOperands(blockOperand.getOperandNumber())
.append(job.reachingDef);
Expand Down Expand Up @@ -567,7 +567,7 @@ void MemorySlotPromoter::removeBlockingUses() {
// If no reaching definition is known, this use is outside the reach of
// the slot. The default value should thus be used.
if (!reachingDef)
reachingDef = getLazyDefaultValue();
reachingDef = getOrCreateDefaultValue();

rewriter.setInsertionPointAfter(toPromote);
if (toPromoteMemOp.removeBlockingUses(
Expand Down Expand Up @@ -601,7 +601,8 @@ void MemorySlotPromoter::removeBlockingUses() {
}

void MemorySlotPromoter::promoteSlot() {
computeReachingDefInRegion(slot.ptr.getParentRegion(), {});
computeReachingDefInRegion(slot.ptr.getParentRegion(),
getOrCreateDefaultValue());

// Now that reaching definitions are known, remove all users.
removeBlockingUses();
Expand All @@ -617,7 +618,7 @@ void MemorySlotPromoter::promoteSlot() {
succOperands.size() + 1 == mergePoint->getNumArguments());
if (succOperands.size() + 1 == mergePoint->getNumArguments())
rewriter.modifyOpInPlace(
user, [&]() { succOperands.append(getLazyDefaultValue()); });
user, [&]() { succOperands.append(getOrCreateDefaultValue()); });
}
}

Expand Down
1 change: 1 addition & 0 deletions mlir/python/mlir/dialects/LLVMOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
#define PYTHON_BINDINGS_LLVM_OPS

include "mlir/Dialect/LLVMIR/LLVMOps.td"
include "mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td"

#endif
8 changes: 8 additions & 0 deletions mlir/python/mlir/dialects/llvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,11 @@
from ._llvm_ops_gen import *
from ._llvm_enum_gen import *
from .._mlir_libs._mlirDialectsLLVM import *
from ..ir import Value
from ._ods_common import get_op_result_or_op_results as _get_op_result_or_op_results


def mlir_constant(value, *, loc=None, ip=None) -> Value:
return _get_op_result_or_op_results(
ConstantOp(res=value.type, value=value, loc=loc, ip=ip)
)
Loading