127 changes: 82 additions & 45 deletions clang/lib/Analysis/UnsafeBufferUsage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,12 @@ class MatchDescendantVisitor
return VisitorBase::TraverseCXXTypeidExpr(Node);
}

bool TraverseCXXDefaultInitExpr(CXXDefaultInitExpr *Node) {
if (!TraverseStmt(Node->getExpr()))
return false;
return VisitorBase::TraverseCXXDefaultInitExpr(Node);
}

bool TraverseStmt(Stmt *Node, DataRecursionQueue *Queue = nullptr) {
if (!Node)
return true;
Expand Down Expand Up @@ -1972,14 +1978,18 @@ class DerefSimplePtrArithFixableGadget : public FixableGadget {
};

/// Scan the function and return a list of gadgets found with provided kits.
static std::tuple<FixableGadgetList, WarningGadgetList, DeclUseTracker>
findGadgets(const Decl *D, const UnsafeBufferUsageHandler &Handler,
bool EmitSuggestions) {
static void findGadgets(const Stmt *S, ASTContext &Ctx,
const UnsafeBufferUsageHandler &Handler,
bool EmitSuggestions, FixableGadgetList &FixableGadgets,
WarningGadgetList &WarningGadgets,
DeclUseTracker &Tracker) {

struct GadgetFinderCallback : MatchFinder::MatchCallback {
FixableGadgetList FixableGadgets;
WarningGadgetList WarningGadgets;
DeclUseTracker Tracker;
GadgetFinderCallback(FixableGadgetList &FixableGadgets,
WarningGadgetList &WarningGadgets,
DeclUseTracker &Tracker)
: FixableGadgets(FixableGadgets), WarningGadgets(WarningGadgets),
Tracker(Tracker) {}

void run(const MatchFinder::MatchResult &Result) override {
// In debug mode, assert that we've found exactly one gadget.
Expand Down Expand Up @@ -2020,10 +2030,14 @@ findGadgets(const Decl *D, const UnsafeBufferUsageHandler &Handler,
assert(numFound >= 1 && "Gadgets not found in match result!");
assert(numFound <= 1 && "Conflicting bind tags in gadgets!");
}

FixableGadgetList &FixableGadgets;
WarningGadgetList &WarningGadgets;
DeclUseTracker &Tracker;
};

MatchFinder M;
GadgetFinderCallback CB;
GadgetFinderCallback CB{FixableGadgets, WarningGadgets, Tracker};

// clang-format off
M.addMatcher(
Expand Down Expand Up @@ -2068,9 +2082,7 @@ findGadgets(const Decl *D, const UnsafeBufferUsageHandler &Handler,
// clang-format on
}

M.match(*D->getBody(), D->getASTContext());
return {std::move(CB.FixableGadgets), std::move(CB.WarningGadgets),
std::move(CB.Tracker)};
M.match(*S, Ctx);
}

// Compares AST nodes by source locations.
Expand Down Expand Up @@ -3614,39 +3626,9 @@ class VariableGroupsManagerImpl : public VariableGroupsManager {
}
};

void clang::checkUnsafeBufferUsage(const Decl *D,
UnsafeBufferUsageHandler &Handler,
bool EmitSuggestions) {
#ifndef NDEBUG
Handler.clearDebugNotes();
#endif

assert(D && D->getBody());
// We do not want to visit a Lambda expression defined inside a method
// independently. Instead, it should be visited along with the outer method.
// FIXME: do we want to do the same thing for `BlockDecl`s?
if (const auto *fd = dyn_cast<CXXMethodDecl>(D)) {
if (fd->getParent()->isLambda() && fd->getParent()->isLocalClass())
return;
}

// Do not emit fixit suggestions for functions declared in an
// extern "C" block.
if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
for (FunctionDecl *FReDecl : FD->redecls()) {
if (FReDecl->isExternC()) {
EmitSuggestions = false;
break;
}
}
}

WarningGadgetSets UnsafeOps;
FixableGadgetSets FixablesForAllVars;

auto [FixableGadgets, WarningGadgets, Tracker] =
findGadgets(D, Handler, EmitSuggestions);

void applyGadgets(const Decl *D, FixableGadgetList FixableGadgets,
WarningGadgetList WarningGadgets, DeclUseTracker Tracker,
UnsafeBufferUsageHandler &Handler, bool EmitSuggestions) {
if (!EmitSuggestions) {
// Our job is very easy without suggestions. Just warn about
// every problematic operation and consider it done. No need to deal
Expand Down Expand Up @@ -3690,8 +3672,10 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
if (WarningGadgets.empty())
return;

UnsafeOps = groupWarningGadgetsByVar(std::move(WarningGadgets));
FixablesForAllVars = groupFixablesByVar(std::move(FixableGadgets));
WarningGadgetSets UnsafeOps =
groupWarningGadgetsByVar(std::move(WarningGadgets));
FixableGadgetSets FixablesForAllVars =
groupFixablesByVar(std::move(FixableGadgets));

std::map<const VarDecl *, FixItList> FixItsForVariableGroup;

Expand Down Expand Up @@ -3912,3 +3896,56 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
}
}
}

void clang::checkUnsafeBufferUsage(const Decl *D,
UnsafeBufferUsageHandler &Handler,
bool EmitSuggestions) {
#ifndef NDEBUG
Handler.clearDebugNotes();
#endif

assert(D);

SmallVector<Stmt *> Stmts;

if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
// We do not want to visit a Lambda expression defined inside a method
// independently. Instead, it should be visited along with the outer method.
// FIXME: do we want to do the same thing for `BlockDecl`s?
if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
if (MD->getParent()->isLambda() && MD->getParent()->isLocalClass())
return;
}

for (FunctionDecl *FReDecl : FD->redecls()) {
if (FReDecl->isExternC()) {
// Do not emit fixit suggestions for functions declared in an
// extern "C" block.
EmitSuggestions = false;
break;
}
}

Stmts.push_back(FD->getBody());

if (const auto *ID = dyn_cast<CXXConstructorDecl>(D)) {
for (const CXXCtorInitializer *CI : ID->inits()) {
Stmts.push_back(CI->getInit());
}
}
} else if (isa<BlockDecl>(D) || isa<ObjCMethodDecl>(D)) {
Stmts.push_back(D->getBody());
}

assert(!Stmts.empty());

FixableGadgetList FixableGadgets;
WarningGadgetList WarningGadgets;
DeclUseTracker Tracker;
for (Stmt *S : Stmts) {
findGadgets(S, D->getASTContext(), Handler, EmitSuggestions, FixableGadgets,
WarningGadgets, Tracker);
}
applyGadgets(D, std::move(FixableGadgets), std::move(WarningGadgets),
std::move(Tracker), Handler, EmitSuggestions);
}
8 changes: 3 additions & 5 deletions clang/lib/CodeGen/CGBlocks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2590,10 +2590,6 @@ const BlockByrefInfo &CodeGenFunction::getBlockByrefInfo(const VarDecl *D) {
if (it != BlockByrefInfos.end())
return it->second;

llvm::StructType *byrefType =
llvm::StructType::create(getLLVMContext(),
"struct.__block_byref_" + D->getNameAsString());

QualType Ty = D->getType();

CharUnits size;
Expand Down Expand Up @@ -2658,7 +2654,9 @@ const BlockByrefInfo &CodeGenFunction::getBlockByrefInfo(const VarDecl *D) {
}
types.push_back(varTy);

byrefType->setBody(types, packed);
llvm::StructType *byrefType = llvm::StructType::create(
getLLVMContext(), types, "struct.__block_byref_" + D->getNameAsString(),
packed);

BlockByrefInfo info;
info.Type = byrefType;
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/CodeGen/CGHLSLRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,16 @@ void CGHLSLRuntime::annotateHLSLResource(const VarDecl *D, GlobalVariable *GV) {
continue;

llvm::hlsl::ResourceClass RC = AttrResType->getAttrs().ResourceClass;
if (RC == llvm::hlsl::ResourceClass::UAV ||
RC == llvm::hlsl::ResourceClass::SRV)
// UAVs and SRVs have already been converted to use LLVM target types,
// we can disable generating of these resource annotations. This will
// enable progress on structured buffers with user defined types this
// resource annotations code does not handle and it crashes.
// This whole function is going to be removed as soon as cbuffers are
// converted to target types (llvm/llvm-project #114126).
return;

bool IsROV = AttrResType->getAttrs().IsROV;
llvm::hlsl::ResourceKind RK = HLSLResAttr->getResourceKind();
llvm::hlsl::ElementType ET = calculateElementType(CGM.getContext(), Ty);
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/CodeGen/CGObjCGNU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1509,8 +1509,8 @@ class CGObjCGNUstep2 : public CGObjCGNUstep {
GetSectionBounds(StringRef Section) {
if (CGM.getTriple().isOSBinFormatCOFF()) {
if (emptyStruct == nullptr) {
emptyStruct = llvm::StructType::create(VMContext, ".objc_section_sentinel");
emptyStruct->setBody({}, /*isPacked*/true);
emptyStruct = llvm::StructType::create(
VMContext, {}, ".objc_section_sentinel", /*isPacked=*/true);
}
auto ZeroInit = llvm::Constant::getNullValue(emptyStruct);
auto Sym = [&](StringRef Prefix, StringRef SecSuffix) {
Expand Down
57 changes: 27 additions & 30 deletions clang/lib/CodeGen/CGObjCMac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5835,15 +5835,7 @@ ObjCTypesHelper::ObjCTypesHelper(CodeGen::CodeGenModule &cgm)
// struct _objc_protocol_extension *
ProtocolExtensionPtrTy = llvm::PointerType::getUnqual(ProtocolExtensionTy);

// Handle recursive construction of Protocol and ProtocolList types

ProtocolTy =
llvm::StructType::create(VMContext, "struct._objc_protocol");

ProtocolListTy =
llvm::StructType::create(VMContext, "struct._objc_protocol_list");
ProtocolListTy->setBody(llvm::PointerType::getUnqual(ProtocolListTy), LongTy,
llvm::ArrayType::get(ProtocolTy, 0));
// Handle construction of Protocol and ProtocolList types

// struct _objc_protocol {
// struct _objc_protocol_extension *isa;
Expand All @@ -5852,9 +5844,16 @@ ObjCTypesHelper::ObjCTypesHelper(CodeGen::CodeGenModule &cgm)
// struct _objc_method_description_list *instance_methods;
// struct _objc_method_description_list *class_methods;
// }
ProtocolTy->setBody(ProtocolExtensionPtrTy, Int8PtrTy,
llvm::PointerType::getUnqual(ProtocolListTy),
MethodDescriptionListPtrTy, MethodDescriptionListPtrTy);
ProtocolTy = llvm::StructType::create(
{ProtocolExtensionPtrTy, Int8PtrTy,
llvm::PointerType::getUnqual(VMContext), MethodDescriptionListPtrTy,
MethodDescriptionListPtrTy},
"struct._objc_protocol");

ProtocolListTy =
llvm::StructType::create({llvm::PointerType::getUnqual(VMContext), LongTy,
llvm::ArrayType::get(ProtocolTy, 0)},
"struct._objc_protocol_list");

// struct _objc_protocol_list *
ProtocolListPtrTy = llvm::PointerType::getUnqual(ProtocolListTy);
Expand Down Expand Up @@ -5886,8 +5885,6 @@ ObjCTypesHelper::ObjCTypesHelper(CodeGen::CodeGenModule &cgm)
"struct._objc_class_extension", IntTy, Int8PtrTy, PropertyListPtrTy);
ClassExtensionPtrTy = llvm::PointerType::getUnqual(ClassExtensionTy);

ClassTy = llvm::StructType::create(VMContext, "struct._objc_class");

// struct _objc_class {
// Class isa;
// Class super_class;
Expand All @@ -5902,10 +5899,12 @@ ObjCTypesHelper::ObjCTypesHelper(CodeGen::CodeGenModule &cgm)
// char *ivar_layout;
// struct _objc_class_ext *ext;
// };
ClassTy->setBody(llvm::PointerType::getUnqual(ClassTy),
llvm::PointerType::getUnqual(ClassTy), Int8PtrTy, LongTy,
LongTy, LongTy, IvarListPtrTy, MethodListPtrTy, CachePtrTy,
ProtocolListPtrTy, Int8PtrTy, ClassExtensionPtrTy);
ClassTy = llvm::StructType::create(
{llvm::PointerType::getUnqual(VMContext),
llvm::PointerType::getUnqual(VMContext), Int8PtrTy, LongTy, LongTy,
LongTy, IvarListPtrTy, MethodListPtrTy, CachePtrTy, ProtocolListPtrTy,
Int8PtrTy, ClassExtensionPtrTy},
"struct._objc_class");

ClassPtrTy = llvm::PointerType::getUnqual(ClassTy);

Expand Down Expand Up @@ -5988,13 +5987,9 @@ ObjCNonFragileABITypesHelper::ObjCNonFragileABITypesHelper(CodeGen::CodeGenModul
// const struct _prop_list_t * class_properties;
// }

// Holder for struct _protocol_list_t *
ProtocolListnfABITy =
llvm::StructType::create(VMContext, "struct._objc_protocol_list");

ProtocolnfABITy = llvm::StructType::create(
"struct._protocol_t", ObjectPtrTy, Int8PtrTy,
llvm::PointerType::getUnqual(ProtocolListnfABITy), MethodListnfABIPtrTy,
llvm::PointerType::getUnqual(VMContext), MethodListnfABIPtrTy,
MethodListnfABIPtrTy, MethodListnfABIPtrTy, MethodListnfABIPtrTy,
PropertyListPtrTy, IntTy, IntTy, Int8PtrPtrTy, Int8PtrTy,
PropertyListPtrTy);
Expand All @@ -6006,8 +6001,9 @@ ObjCNonFragileABITypesHelper::ObjCNonFragileABITypesHelper(CodeGen::CodeGenModul
// long protocol_count; // Note, this is 32/64 bit
// struct _protocol_t *[protocol_count];
// }
ProtocolListnfABITy->setBody(LongTy,
llvm::ArrayType::get(ProtocolnfABIPtrTy, 0));
ProtocolListnfABITy = llvm::StructType::create(
{LongTy, llvm::ArrayType::get(ProtocolnfABIPtrTy, 0)},
"struct._objc_protocol_list");

// struct _objc_protocol_list*
ProtocolListnfABIPtrTy = llvm::PointerType::getUnqual(ProtocolListnfABITy);
Expand Down Expand Up @@ -6067,11 +6063,12 @@ ObjCNonFragileABITypesHelper::ObjCNonFragileABITypesHelper(CodeGen::CodeGenModul
// struct class_ro_t *ro;
// }

ClassnfABITy = llvm::StructType::create(VMContext, "struct._class_t");
ClassnfABITy->setBody(llvm::PointerType::getUnqual(ClassnfABITy),
llvm::PointerType::getUnqual(ClassnfABITy), CachePtrTy,
llvm::PointerType::getUnqual(ImpnfABITy),
llvm::PointerType::getUnqual(ClassRonfABITy));
ClassnfABITy = llvm::StructType::create(
{llvm::PointerType::getUnqual(VMContext),
llvm::PointerType::getUnqual(VMContext), CachePtrTy,
llvm::PointerType::getUnqual(ImpnfABITy),
llvm::PointerType::getUnqual(ClassRonfABITy)},
"struct._class_t");

// LLVM for struct _class_t *
ClassnfABIPtrTy = llvm::PointerType::getUnqual(ClassnfABITy);
Expand Down
12 changes: 5 additions & 7 deletions clang/lib/CodeGen/MicrosoftCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -529,31 +529,29 @@ class MicrosoftCXXABI : public CGCXXABI {
if (ClassHierarchyDescriptorType)
return ClassHierarchyDescriptorType;
// Forward-declare RTTIClassHierarchyDescriptor to break a cycle.
ClassHierarchyDescriptorType = llvm::StructType::create(
CGM.getLLVMContext(), "rtti.ClassHierarchyDescriptor");
llvm::Type *FieldTypes[] = {CGM.IntTy, CGM.IntTy, CGM.IntTy,
getImageRelativeType(CGM.UnqualPtrTy)};
ClassHierarchyDescriptorType->setBody(FieldTypes);
ClassHierarchyDescriptorType =
llvm::StructType::create(FieldTypes, "rtti.ClassHierarchyDescriptor");
return ClassHierarchyDescriptorType;
}

llvm::StructType *getCompleteObjectLocatorType() {
if (CompleteObjectLocatorType)
return CompleteObjectLocatorType;
CompleteObjectLocatorType = llvm::StructType::create(
CGM.getLLVMContext(), "rtti.CompleteObjectLocator");
llvm::Type *FieldTypes[] = {
CGM.IntTy,
CGM.IntTy,
CGM.IntTy,
getImageRelativeType(CGM.Int8PtrTy),
getImageRelativeType(CGM.UnqualPtrTy),
getImageRelativeType(CompleteObjectLocatorType),
getImageRelativeType(CGM.VoidTy),
};
llvm::ArrayRef<llvm::Type *> FieldTypesRef(FieldTypes);
if (!isImageRelative())
FieldTypesRef = FieldTypesRef.drop_back();
CompleteObjectLocatorType->setBody(FieldTypesRef);
CompleteObjectLocatorType =
llvm::StructType::create(FieldTypesRef, "rtti.CompleteObjectLocator");
return CompleteObjectLocatorType;
}

Expand Down
6 changes: 4 additions & 2 deletions clang/lib/Driver/ToolChain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ ToolChain::ToolChain(const Driver &D, const llvm::Triple &T,
llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>>
ToolChain::executeToolChainProgram(StringRef Executable) const {
llvm::SmallString<64> OutputFile;
llvm::sys::fs::createTemporaryFile("toolchain-program", "txt", OutputFile);
llvm::sys::fs::createTemporaryFile("toolchain-program", "txt", OutputFile,
llvm::sys::fs::OF_Text);
llvm::FileRemover OutputRemover(OutputFile.c_str());
std::optional<llvm::StringRef> Redirects[] = {
{""},
Expand All @@ -128,7 +129,8 @@ ToolChain::executeToolChainProgram(StringRef Executable) const {
*Str + "'");
SecondsToWait = std::max(SecondsToWait, 0); // infinite
}
if (llvm::sys::ExecuteAndWait(Executable, {}, {}, Redirects, SecondsToWait,
if (llvm::sys::ExecuteAndWait(Executable, {Executable}, {}, Redirects,
SecondsToWait,
/*MemoryLimit=*/0, &ErrorMessage))
return llvm::createStringError(std::error_code(),
Executable + ": " + ErrorMessage);
Expand Down
24 changes: 0 additions & 24 deletions clang/test/CodeGenHLSL/builtins/RWBuffer-annotations.hlsl

This file was deleted.

14 changes: 0 additions & 14 deletions clang/test/CodeGenHLSL/builtins/RWBuffer-elementtype.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,3 @@ void main(int GI : SV_GroupIndex) {
BufF16x2[GI] = 0;
BufF32x3[GI] = 0;
}

// CHECK: !{{[0-9]+}} = !{ptr @BufI16, i32 10, i32 2,
// CHECK: !{{[0-9]+}} = !{ptr @BufU16, i32 10, i32 3,
// CHECK: !{{[0-9]+}} = !{ptr @BufI32, i32 10, i32 4,
// CHECK: !{{[0-9]+}} = !{ptr @BufU32, i32 10, i32 5,
// CHECK: !{{[0-9]+}} = !{ptr @BufI64, i32 10, i32 6,
// CHECK: !{{[0-9]+}} = !{ptr @BufU64, i32 10, i32 7,
// CHECK: !{{[0-9]+}} = !{ptr @BufF16, i32 10, i32 8,
// CHECK: !{{[0-9]+}} = !{ptr @BufF32, i32 10, i32 9,
// CHECK: !{{[0-9]+}} = !{ptr @BufF64, i32 10, i32 10,
// CHECK: !{{[0-9]+}} = !{ptr @BufI16x4, i32 10, i32 2,
// CHECK: !{{[0-9]+}} = !{ptr @BufU32x3, i32 10, i32 5,
// CHECK: !{{[0-9]+}} = !{ptr @BufF16x2, i32 10, i32 8,
// CHECK: !{{[0-9]+}} = !{ptr @BufF32x3, i32 10, i32 9,
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,3 @@ void main(int GI : SV_GroupIndex) {
BufF16x2[GI] = 0;
BufF32x3[GI] = 0;
}

// CHECK: !{{[0-9]+}} = !{ptr @BufI16, i32 10, i32 2,
// CHECK: !{{[0-9]+}} = !{ptr @BufU16, i32 10, i32 3,
// CHECK: !{{[0-9]+}} = !{ptr @BufI32, i32 10, i32 4,
// CHECK: !{{[0-9]+}} = !{ptr @BufU32, i32 10, i32 5,
// CHECK: !{{[0-9]+}} = !{ptr @BufI64, i32 10, i32 6,
// CHECK: !{{[0-9]+}} = !{ptr @BufU64, i32 10, i32 7,
// CHECK: !{{[0-9]+}} = !{ptr @BufF16, i32 10, i32 8,
// CHECK: !{{[0-9]+}} = !{ptr @BufF32, i32 10, i32 9,
// CHECK: !{{[0-9]+}} = !{ptr @BufF64, i32 10, i32 10,
// CHECK: !{{[0-9]+}} = !{ptr @BufI16x4, i32 10, i32 2,
// CHECK: !{{[0-9]+}} = !{ptr @BufU32x3, i32 10, i32 5,
// CHECK: !{{[0-9]+}} = !{ptr @BufF16x2, i32 10, i32 8,
// CHECK: !{{[0-9]+}} = !{ptr @BufF32x3, i32 10, i32 9,

This file was deleted.

22 changes: 0 additions & 22 deletions clang/test/CodeGenHLSL/builtins/StructuredBuffer-annotations.hlsl

This file was deleted.

14 changes: 0 additions & 14 deletions clang/test/CodeGenHLSL/builtins/StructuredBuffer-elementtype.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,3 @@ void main(int GI : SV_GroupIndex) {
half2 v12 = BufF16x2[GI];
float3 v13 = BufF32x3[GI];
}

// CHECK: !{{[0-9]+}} = !{ptr @BufI16, i32 10, i32 2,
// CHECK: !{{[0-9]+}} = !{ptr @BufU16, i32 10, i32 3,
// CHECK: !{{[0-9]+}} = !{ptr @BufI32, i32 10, i32 4,
// CHECK: !{{[0-9]+}} = !{ptr @BufU32, i32 10, i32 5,
// CHECK: !{{[0-9]+}} = !{ptr @BufI64, i32 10, i32 6,
// CHECK: !{{[0-9]+}} = !{ptr @BufU64, i32 10, i32 7,
// CHECK: !{{[0-9]+}} = !{ptr @BufF16, i32 10, i32 8,
// CHECK: !{{[0-9]+}} = !{ptr @BufF32, i32 10, i32 9,
// CHECK: !{{[0-9]+}} = !{ptr @BufF64, i32 10, i32 10,
// CHECK: !{{[0-9]+}} = !{ptr @BufI16x4, i32 10, i32 2,
// CHECK: !{{[0-9]+}} = !{ptr @BufU32x3, i32 10, i32 5,
// CHECK: !{{[0-9]+}} = !{ptr @BufF16x2, i32 10, i32 8,
// CHECK: !{{[0-9]+}} = !{ptr @BufF32x3, i32 10, i32 9,
2 changes: 0 additions & 2 deletions clang/test/CodeGenHLSL/cbuf.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,4 @@ float foo() {
}

// CHECK: !hlsl.cbufs = !{![[CBMD:[0-9]+]]}
// CHECK: !hlsl.srvs = !{![[TBMD:[0-9]+]]}
// CHECK: ![[CBMD]] = !{ptr @[[CB]], i32 13, i32 0, i1 false, i32 0, i32 2}
// CHECK: ![[TBMD]] = !{ptr @[[TB]], i32 15, i32 0, i1 false, i32 2, i32 1}
122 changes: 122 additions & 0 deletions clang/test/SemaCXX/warn-unsafe-buffer-usage-function-attr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,37 @@ int testFoldExpression(Vs&&... v) {
return (... + v); // expected-warning{{function introduces unsafe buffer manipulation}}
}

struct HoldsUnsafeMembers {
HoldsUnsafeMembers()
: FromCtor(3), // expected-warning{{function introduces unsafe buffer manipulation}}
FromCtor2{3} // expected-warning{{function introduces unsafe buffer manipulation}}
{}

[[clang::unsafe_buffer_usage]]
HoldsUnsafeMembers(int i)
: FromCtor(i), // expected-warning{{function introduces unsafe buffer manipulation}}
FromCtor2{i} // expected-warning{{function introduces unsafe buffer manipulation}}
{}

HoldsUnsafeMembers(float f)
: HoldsUnsafeMembers(0) {} // expected-warning{{function introduces unsafe buffer manipulation}}

UnsafeMembers FromCtor;
UnsafeMembers FromCtor2;
UnsafeMembers FromField{3}; // expected-warning 2{{function introduces unsafe buffer manipulation}}
};

struct SubclassUnsafeMembers : public UnsafeMembers {
SubclassUnsafeMembers()
: UnsafeMembers(3) // expected-warning{{function introduces unsafe buffer manipulation}}
{}

[[clang::unsafe_buffer_usage]]
SubclassUnsafeMembers(int i)
: UnsafeMembers(i) // expected-warning{{function introduces unsafe buffer manipulation}}
{}
};

// https://github.com/llvm/llvm-project/issues/80482
void testClassMembers() {
UnsafeMembers(3); // expected-warning{{function introduces unsafe buffer manipulation}}
Expand All @@ -122,4 +153,95 @@ void testClassMembers() {
UnsafeMembers()(); // expected-warning{{function introduces unsafe buffer manipulation}}

testFoldExpression(UnsafeMembers(), UnsafeMembers());

HoldsUnsafeMembers();
HoldsUnsafeMembers(3); // expected-warning{{function introduces unsafe buffer manipulation}}

SubclassUnsafeMembers();
SubclassUnsafeMembers(3); // expected-warning{{function introduces unsafe buffer manipulation}}
}

// Not an aggregate, so its constructor is not implicit code and will be
// visited/checked for warnings.
struct NotCalledHoldsUnsafeMembers {
NotCalledHoldsUnsafeMembers()
: FromCtor(3), // expected-warning{{function introduces unsafe buffer manipulation}}
FromCtor2{3} // expected-warning{{function introduces unsafe buffer manipulation}}
{}

UnsafeMembers FromCtor;
UnsafeMembers FromCtor2;
UnsafeMembers FromField{3}; // expected-warning{{function introduces unsafe buffer manipulation}}
};

// An aggregate, so its constructor is implicit code. Since it's not called, it
// is never generated.
struct AggregateUnused {
UnsafeMembers f1;
// While this field would trigger the warning during initialization, since
// it's unused, there's no code generated that does the initialization, so
// no warning.
UnsafeMembers f2{3};
};

struct AggregateExplicitlyInitializedSafe {
UnsafeMembers f1;
// The warning is not fired as the field is always explicltly initialized
// elsewhere. This initializer is never used.
UnsafeMembers f2{3};
};

void testAggregateExplicitlyInitializedSafe() {
AggregateExplicitlyInitializedSafe A{
.f2 = UnsafeMembers(), // A safe constructor.
};
}

struct AggregateExplicitlyInitializedUnsafe {
UnsafeMembers f1;
// The warning is not fired as the field is always explicltly initialized
// elsewhere. This initializer is never used.
UnsafeMembers f2{3};
};

void testAggregateExplicitlyInitializedUnsafe() {
AggregateExplicitlyInitializedUnsafe A{
.f2 = UnsafeMembers(3), // expected-warning{{function introduces unsafe buffer manipulation}}
};
}

struct AggregateViaAggregateInit {
UnsafeMembers f1;
// FIXME: A construction of this class does initialize the field through
// this initializer, so it should warn. Ideally it should also point to
// where the site of the construction is in testAggregateViaAggregateInit().
UnsafeMembers f2{3};
};

void testAggregateViaAggregateInit() {
AggregateViaAggregateInit A{};
};

struct AggregateViaValueInit {
UnsafeMembers f1;
// FIXME: A construction of this class does initialize the field through
// this initializer, so it should warn. Ideally it should also point to
// where the site of the construction is in testAggregateViaValueInit().
UnsafeMembers f2{3};
};

void testAggregateViaValueInit() {
auto A = AggregateViaValueInit();
};

struct AggregateViaDefaultInit {
UnsafeMembers f1;
// FIXME: A construction of this class does initialize the field through
// this initializer, so it should warn. Ideally it should also point to
// where the site of the construction is in testAggregateViaValueInit().
UnsafeMembers f2{3};
};

void testAggregateViaDefaultInit() {
AggregateViaDefaultInit A;
};
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,23 @@ namespace test_flag {

}
} //namespace test_flag

struct HoldsStdSpanAndInitializedInCtor {
char* Ptr;
unsigned Size;
std::span<char> Span{Ptr, Size}; // no-warning (this code is unreachable)

HoldsStdSpanAndInitializedInCtor(char* P, unsigned S)
: Span(P, S) // expected-warning{{the two-parameter std::span construction is unsafe as it can introduce mismatch between buffer size and the bound information}}
{}
};

struct HoldsStdSpanAndNotInitializedInCtor {
char* Ptr;
unsigned Size;
std::span<char> Span{Ptr, Size}; // expected-warning{{the two-parameter std::span construction is unsafe as it can introduce mismatch between buffer size and the bound information}}

HoldsStdSpanAndNotInitializedInCtor(char* P, unsigned S)
: Ptr(P), Size(S)
{}
};
45 changes: 0 additions & 45 deletions clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,33 +216,6 @@ void printCommands(ArrayRef<StringRef> CmdArgs) {
exit(EXIT_FAILURE);
}

/// Create an extra user-specified \p OffloadFile.
/// TODO: We should find a way to wrap these as libraries instead.
Expected<OffloadFile> getInputBitcodeLibrary(StringRef Input) {
auto [Device, Path] = StringRef(Input).split('=');
auto [String, Arch] = Device.rsplit('-');
auto [Kind, Triple] = String.split('-');

llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> ImageOrError =
llvm::MemoryBuffer::getFileOrSTDIN(Path);
if (std::error_code EC = ImageOrError.getError())
return createFileError(Path, EC);

OffloadingImage Image{};
Image.TheImageKind = IMG_Bitcode;
Image.TheOffloadKind = getOffloadKind(Kind);
Image.StringData["triple"] = Triple;
Image.StringData["arch"] = Arch;
Image.Image = std::move(*ImageOrError);

std::unique_ptr<MemoryBuffer> Binary =
MemoryBuffer::getMemBufferCopy(OffloadBinary::write(Image));
auto NewBinaryOrErr = OffloadBinary::create(*Binary);
if (!NewBinaryOrErr)
return NewBinaryOrErr.takeError();
return OffloadFile(std::move(*NewBinaryOrErr), std::move(Binary));
}

std::string getMainExecutable(const char *Name) {
void *Ptr = (void *)(intptr_t)&getMainExecutable;
auto COWPath = sys::fs::getMainExecutable(Name, Ptr);
Expand Down Expand Up @@ -600,17 +573,6 @@ Expected<StringRef> clang(ArrayRef<StringRef> InputFiles, const ArgList &Args) {
for (StringRef Arg : Args.getAllArgValues(OPT_compiler_arg_EQ))
CmdArgs.push_back(Args.MakeArgString(Arg));

for (StringRef Arg : Args.getAllArgValues(OPT_builtin_bitcode_EQ)) {
if (llvm::Triple(Arg.split('=').first) == Triple)
CmdArgs.append({"-Xclang", "-mlink-builtin-bitcode", "-Xclang",
Args.MakeArgString(Arg.split('=').second)});
}

// The OpenMPOpt pass can introduce new calls and is expensive, we do
// not want this when running CodeGen through clang.
if (Args.hasArg(OPT_clang_backend) || Args.hasArg(OPT_builtin_bitcode_EQ))
CmdArgs.append({"-mllvm", "-openmp-opt-disable"});

if (Error Err = executeCommands(*ClangPath, CmdArgs))
return std::move(Err);

Expand Down Expand Up @@ -1362,13 +1324,6 @@ getDeviceInput(const ArgList &Args) {
}
}

for (StringRef Library : Args.getAllArgValues(OPT_bitcode_library_EQ)) {
auto FileOrErr = getInputBitcodeLibrary(Library);
if (!FileOrErr)
return FileOrErr.takeError();
InputFiles[*FileOrErr].push_back(std::move(*FileOrErr));
}

SmallVector<SmallVector<OffloadFile>> InputsForTarget;
for (auto &[ID, Input] : InputFiles)
InputsForTarget.emplace_back(std::move(Input));
Expand Down
10 changes: 0 additions & 10 deletions clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,12 @@ def host_triple_EQ : Joined<["--"], "host-triple=">,
def opt_level : Joined<["--"], "opt-level=">,
Flags<[WrapperOnlyOption]>, MetaVarName<"<O0, O1, O2, or O3>">,
HelpText<"Optimization level for LTO">;
def bitcode_library_EQ : Joined<["--"], "bitcode-library=">,
Flags<[WrapperOnlyOption]>, MetaVarName<"<kind>-<triple>-<arch>=<path>">,
HelpText<"Extra bitcode library to link">;
def builtin_bitcode_EQ : Joined<["--"], "builtin-bitcode=">,
Flags<[WrapperOnlyOption]>, MetaVarName<"<triple>=<path>">,
HelpText<"Perform a special internalizing link on the bitcode file. "
"This is necessary for some vendor libraries to be linked correctly">;
def device_linker_args_EQ : Joined<["--"], "device-linker=">,
Flags<[WrapperOnlyOption]>, MetaVarName<"<value> or <triple>=<value>">,
HelpText<"Arguments to pass to the device linker invocation">;
def device_compiler_args_EQ : Joined<["--"], "device-compiler=">,
Flags<[WrapperOnlyOption]>, MetaVarName<"<value> or <triple>=<value>">,
HelpText<"Arguments to pass to the device compiler invocation">;
def clang_backend : Flag<["--"], "clang-backend">,
Flags<[WrapperOnlyOption]>,
HelpText<"Run the backend using clang rather than the LTO backend">;
def dry_run : Flag<["--"], "dry-run">,
Flags<[WrapperOnlyOption]>,
HelpText<"Print program arguments without running">;
Expand Down
39 changes: 39 additions & 0 deletions clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3342,6 +3342,45 @@ TEST_P(ASTMatchersTest,
declStmt(isInTemplateInstantiation())));
}

TEST_P(ASTMatchersTest, IsInstantiated_MatchesVariableInstantiation) {
if (!GetParam().isCXX14OrLater()) {
return;
}

EXPECT_TRUE(matches("template<typename T> int V = 10; void x() { V<int>; }",
varDecl(isInstantiated())));
}

TEST_P(ASTMatchersTest, IsInstantiated_NotMatchesVariableDefinition) {
if (!GetParam().isCXX14OrLater()) {
return;
}

EXPECT_TRUE(notMatches("template<typename T> int V = 10;",
varDecl(isInstantiated())));
}

TEST_P(ASTMatchersTest,
IsInTemplateInstantiation_MatchesVariableInstantiationStmt) {
if (!GetParam().isCXX14OrLater()) {
return;
}

EXPECT_TRUE(matches(
"template<typename T> auto V = []() { T i; }; void x() { V<int>(); }",
declStmt(isInTemplateInstantiation())));
}

TEST_P(ASTMatchersTest,
IsInTemplateInstantiation_NotMatchesVariableDefinitionStmt) {
if (!GetParam().isCXX14OrLater()) {
return;
}

EXPECT_TRUE(notMatches("template<typename T> auto V = []() { T i; };",
declStmt(isInTemplateInstantiation())));
}

TEST_P(ASTMatchersTest, IsInTemplateInstantiation_Sharing) {
if (!GetParam().isCXX()) {
return;
Expand Down
9 changes: 4 additions & 5 deletions flang/include/flang/Runtime/CUDA/memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,18 @@ void RTDECL(CUFDataTransferPtrPtr)(void *dst, void *src, std::size_t bytes,
unsigned mode, const char *sourceFile = nullptr, int sourceLine = 0);

/// Data transfer from a pointer to a descriptor.
void RTDECL(CUFDataTransferDescPtr)(const Descriptor &dst, void *src,
void RTDECL(CUFDataTransferDescPtr)(Descriptor *dst, void *src,
std::size_t bytes, unsigned mode, const char *sourceFile = nullptr,
int sourceLine = 0);

/// Data transfer from a descriptor to a pointer.
void RTDECL(CUFDataTransferPtrDesc)(void *dst, const Descriptor &src,
void RTDECL(CUFDataTransferPtrDesc)(void *dst, Descriptor *src,
std::size_t bytes, unsigned mode, const char *sourceFile = nullptr,
int sourceLine = 0);

/// Data transfer from a descriptor to a descriptor.
void RTDECL(CUFDataTransferDescDesc)(const Descriptor &dst,
const Descriptor &src, unsigned mode, const char *sourceFile = nullptr,
int sourceLine = 0);
void RTDECL(CUFDataTransferDescDesc)(Descriptor *dst, Descriptor *src,
unsigned mode, const char *sourceFile = nullptr, int sourceLine = 0);

} // extern "C"
} // namespace Fortran::runtime::cuda
Expand Down
59 changes: 20 additions & 39 deletions flang/lib/Optimizer/CodeGen/CodeGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2949,9 +2949,10 @@ struct LoadOpConversion : public fir::FIROpConversion<fir::LoadOp> {
llvm::LogicalResult
matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const override {

mlir::Type llvmLoadTy = convertObjectType(load.getType());
if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(load.getType())) {
// fir.box is a special case because it is considered as an ssa values in
// fir.box is a special case because it is considered an ssa value in
// fir, but it is lowered as a pointer to a descriptor. So
// fir.ref<fir.box> and fir.box end up being the same llvm types and
// loading a fir.ref<fir.box> is implemented as taking a snapshot of the
Expand All @@ -2960,30 +2961,17 @@ struct LoadOpConversion : public fir::FIROpConversion<fir::LoadOp> {
mlir::Location loc = load.getLoc();
auto newBoxStorage =
genAllocaAndAddrCastWithType(loc, llvmLoadTy, defaultAlign, rewriter);
// TODO: always generate llvm.memcpy, LLVM is better at optimizing it than
// aggregate loads + stores.
if (boxTy.isAssumedRank()) {

TypePair boxTypePair{boxTy, llvmLoadTy};
mlir::Value boxSize =
computeBoxSize(loc, boxTypePair, inputBoxStorage, rewriter);
auto memcpy = rewriter.create<mlir::LLVM::MemcpyOp>(
loc, newBoxStorage, inputBoxStorage, boxSize, /*isVolatile=*/false);
if (std::optional<mlir::ArrayAttr> optionalTag = load.getTbaa())
memcpy.setTBAATags(*optionalTag);
else
attachTBAATag(memcpy, boxTy, boxTy, nullptr);
} else {
auto boxValue = rewriter.create<mlir::LLVM::LoadOp>(loc, llvmLoadTy,
inputBoxStorage);
if (std::optional<mlir::ArrayAttr> optionalTag = load.getTbaa())
boxValue.setTBAATags(*optionalTag);
else
attachTBAATag(boxValue, boxTy, boxTy, nullptr);
auto storeOp =
rewriter.create<mlir::LLVM::StoreOp>(loc, boxValue, newBoxStorage);
attachTBAATag(storeOp, boxTy, boxTy, nullptr);
}

TypePair boxTypePair{boxTy, llvmLoadTy};
mlir::Value boxSize =
computeBoxSize(loc, boxTypePair, inputBoxStorage, rewriter);
auto memcpy = rewriter.create<mlir::LLVM::MemcpyOp>(
loc, newBoxStorage, inputBoxStorage, boxSize, /*isVolatile=*/false);

if (std::optional<mlir::ArrayAttr> optionalTag = load.getTbaa())
memcpy.setTBAATags(*optionalTag);
else
attachTBAATag(memcpy, boxTy, boxTy, nullptr);
rewriter.replaceOp(load, newBoxStorage);
} else {
auto loadOp = rewriter.create<mlir::LLVM::LoadOp>(
Expand Down Expand Up @@ -3227,20 +3215,13 @@ struct StoreOpConversion : public fir::FIROpConversion<fir::StoreOp> {
mlir::LLVM::AliasAnalysisOpInterface newOp;
if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(storeTy)) {
mlir::Type llvmBoxTy = lowerTy().convertBoxTypeAsStruct(boxTy);
// fir.box value is actually in memory, load it first before storing it,
// or do a memcopy for assumed-rank descriptors.
if (boxTy.isAssumedRank()) {
TypePair boxTypePair{boxTy, llvmBoxTy};
mlir::Value boxSize =
computeBoxSize(loc, boxTypePair, llvmValue, rewriter);
newOp = rewriter.create<mlir::LLVM::MemcpyOp>(
loc, llvmMemref, llvmValue, boxSize, /*isVolatile=*/false);
} else {
auto val =
rewriter.create<mlir::LLVM::LoadOp>(loc, llvmBoxTy, llvmValue);
attachTBAATag(val, boxTy, boxTy, nullptr);
newOp = rewriter.create<mlir::LLVM::StoreOp>(loc, val, llvmMemref);
}
// Always use memcpy because LLVM is not as effective at optimizing
// aggregate loads/stores as it is optimizing memcpy.
TypePair boxTypePair{boxTy, llvmBoxTy};
mlir::Value boxSize =
computeBoxSize(loc, boxTypePair, llvmValue, rewriter);
newOp = rewriter.create<mlir::LLVM::MemcpyOp>(
loc, llvmMemref, llvmValue, boxSize, /*isVolatile=*/false);
} else {
newOp = rewriter.create<mlir::LLVM::StoreOp>(loc, llvmValue, llvmMemref);
}
Expand Down
11 changes: 4 additions & 7 deletions flang/lib/Optimizer/Transforms/CUFOpConversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -529,8 +529,8 @@ struct CUFDataTransferOpConversion
mlir::Value sourceFile = fir::factory::locationToFilename(builder, loc);
mlir::Value sourceLine =
fir::factory::locationToLineNo(builder, loc, fTy.getInput(4));
mlir::Value dst = builder.loadIfRef(loc, op.getDst());
mlir::Value src = builder.loadIfRef(loc, op.getSrc());
mlir::Value dst = op.getDst();
mlir::Value src = op.getSrc();
llvm::SmallVector<mlir::Value> args{fir::runtime::createArguments(
builder, loc, fTy, dst, src, modeValue, sourceFile, sourceLine)};
builder.create<fir::CallOp>(loc, func, args);
Expand Down Expand Up @@ -603,11 +603,8 @@ struct CUFDataTransferOpConversion
mlir::Value sourceFile = fir::factory::locationToFilename(builder, loc);
mlir::Value sourceLine =
fir::factory::locationToLineNo(builder, loc, fTy.getInput(5));
mlir::Value dst =
dstIsDesc ? builder.loadIfRef(loc, op.getDst()) : op.getDst();
mlir::Value src = mlir::isa<fir::BaseBoxType>(srcTy)
? builder.loadIfRef(loc, op.getSrc())
: op.getSrc();
mlir::Value dst = op.getDst();
mlir::Value src = op.getSrc();
llvm::SmallVector<mlir::Value> args{
fir::runtime::createArguments(builder, loc, fTy, dst, src, bytes,
modeValue, sourceFile, sourceLine)};
Expand Down
9 changes: 4 additions & 5 deletions flang/runtime/CUDA/memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,23 +73,22 @@ void RTDEF(CUFDataTransferPtrPtr)(void *dst, void *src, std::size_t bytes,
CUDA_REPORT_IF_ERROR(cudaMemcpy(dst, src, bytes, kind));
}

void RTDEF(CUFDataTransferDescPtr)(const Descriptor &desc, void *addr,
void RTDEF(CUFDataTransferDescPtr)(Descriptor *desc, void *addr,
std::size_t bytes, unsigned mode, const char *sourceFile, int sourceLine) {
Terminator terminator{sourceFile, sourceLine};
terminator.Crash(
"not yet implemented: CUDA data transfer from a pointer to a descriptor");
}

void RTDEF(CUFDataTransferPtrDesc)(void *addr, const Descriptor &desc,
void RTDEF(CUFDataTransferPtrDesc)(void *addr, Descriptor *desc,
std::size_t bytes, unsigned mode, const char *sourceFile, int sourceLine) {
Terminator terminator{sourceFile, sourceLine};
terminator.Crash(
"not yet implemented: CUDA data transfer from a descriptor to a pointer");
}

void RTDECL(CUFDataTransferDescDesc)(const Descriptor &dstDesc,
const Descriptor &srcDesc, unsigned mode, const char *sourceFile,
int sourceLine) {
void RTDECL(CUFDataTransferDescDesc)(Descriptor *dstDesc, Descriptor *srcDesc,
unsigned mode, const char *sourceFile, int sourceLine) {
Terminator terminator{sourceFile, sourceLine};
terminator.Crash(
"not yet implemented: CUDA data transfer between two descriptors");
Expand Down
28 changes: 11 additions & 17 deletions flang/test/Fir/CUDA/cuda-data-transfer.fir
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,9 @@ func.func @_QPsub1() {
// CHECK-LABEL: func.func @_QPsub1()
// CHECK: %[[ADEV:.*]]:2 = hlfir.declare %{{.*}} {data_attr = #cuf.cuda<device>, fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFsub1Eadev"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>) -> (!fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>)
// CHECK: %[[AHOST:.*]]:2 = hlfir.declare %{{.*}} {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFsub1Eahost"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>) -> (!fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>)
// CHECK: %[[AHOST_LOAD:.*]] = fir.load %[[AHOST]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
// CHECK: %[[ADEV_LOAD:.*]] = fir.load %[[ADEV]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
// CHECK: %[[AHOST_BOX:.*]] = fir.convert %[[AHOST_LOAD]] : (!fir.box<!fir.heap<!fir.array<?xi32>>>) -> !fir.box<none>
// CHECK: %[[ADEV_BOX:.*]] = fir.convert %[[ADEV_LOAD]] : (!fir.box<!fir.heap<!fir.array<?xi32>>>) -> !fir.box<none>
// CHECK: fir.call @_FortranACUFDataTransferDescDesc(%[[AHOST_BOX]], %[[ADEV_BOX]], %c1{{.*}}, %{{.*}}, %{{.*}}) : (!fir.box<none>, !fir.box<none>, i32, !fir.ref<i8>, i32) -> none
// CHECK: %[[AHOST_BOX:.*]] = fir.convert %[[AHOST]]#0 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>) -> !fir.ref<!fir.box<none>>
// CHECK: %[[ADEV_BOX:.*]] = fir.convert %[[ADEV]]#0 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>) -> !fir.ref<!fir.box<none>>
// CHECK: fir.call @_FortranACUFDataTransferDescDesc(%[[AHOST_BOX]], %[[ADEV_BOX]], %c1{{.*}}, %{{.*}}, %{{.*}}) : (!fir.ref<!fir.box<none>>, !fir.ref<!fir.box<none>>, i32, !fir.ref<i8>, i32) -> none

func.func @_QPsub2() {
%0 = cuf.alloc !fir.box<!fir.heap<!fir.array<?xi32>>> {bindc_name = "adev", data_attr = #cuf.cuda<device>, uniq_name = "_QFsub2Eadev"} -> !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
Expand Down Expand Up @@ -76,19 +74,17 @@ func.func @_QPsub4() {
// CHECK: %[[NBELEM:.*]] = arith.constant 10 : index
// CHECK: %[[WIDTH:.*]] = arith.constant 4 : index
// CHECK: %[[BYTES:.*]] = arith.muli %[[NBELEM]], %[[WIDTH]] : index
// CHECK: %[[ADEV_LOAD:.*]] = fir.load %[[ADEV]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
// CHECK: %[[ADEV_BOX:.*]] = fir.convert %[[ADEV_LOAD]] : (!fir.box<!fir.heap<!fir.array<?xi32>>>) -> !fir.box<none>
// CHECK: %[[ADEV_BOX:.*]] = fir.convert %[[ADEV]]#0 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>) -> !fir.ref<!fir.box<none>>
// CHECK: %[[AHOST_PTR:.*]] = fir.convert %[[AHOST]]#0 : (!fir.ref<!fir.array<10xi32>>) -> !fir.llvm_ptr<i8>
// CHECK: %[[BYTES_CONV:.*]] = fir.convert %[[BYTES]] : (index) -> i64
// CHECK: fir.call @_FortranACUFDataTransferDescPtr(%[[ADEV_BOX]], %[[AHOST_PTR]], %[[BYTES_CONV]], %c0{{.*}}, %{{.*}}, %{{.*}}) : (!fir.box<none>, !fir.llvm_ptr<i8>, i64, i32, !fir.ref<i8>, i32) -> none
// CHECK: fir.call @_FortranACUFDataTransferDescPtr(%[[ADEV_BOX]], %[[AHOST_PTR]], %[[BYTES_CONV]], %c0{{.*}}, %{{.*}}, %{{.*}}) : (!fir.ref<!fir.box<none>>, !fir.llvm_ptr<i8>, i64, i32, !fir.ref<i8>, i32) -> none
// CHECK: %[[NBELEM:.*]] = arith.constant 10 : index
// CHECK: %[[WIDTH:.*]] = arith.constant 4 : index
// CHECK: %[[BYTES:.*]] = arith.muli %[[NBELEM]], %[[WIDTH]] : index
// CHECK: %[[ADEV_LOAD:.*]] = fir.load %[[ADEV]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
// CHECK: %[[AHOST_PTR:.*]] = fir.convert %[[AHOST]]#0 : (!fir.ref<!fir.array<10xi32>>) -> !fir.llvm_ptr<i8>
// CHECK: %[[ADEV_BOX:.*]] = fir.convert %[[ADEV_LOAD]] : (!fir.box<!fir.heap<!fir.array<?xi32>>>) -> !fir.box<none>
// CHECK: %[[ADEV_BOX:.*]] = fir.convert %[[ADEV]]#0 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>) -> !fir.ref<!fir.box<none>>
// CHECK: %[[BYTES_CONV:.*]] = fir.convert %[[BYTES]] : (index) -> i64
// CHECK: fir.call @_FortranACUFDataTransferPtrDesc(%[[AHOST_PTR]], %[[ADEV_BOX]], %[[BYTES_CONV]], %c1{{.*}}, %{{.*}}, %{{.*}}) : (!fir.llvm_ptr<i8>, !fir.box<none>, i64, i32, !fir.ref<i8>, i32) -> none
// CHECK: fir.call @_FortranACUFDataTransferPtrDesc(%[[AHOST_PTR]], %[[ADEV_BOX]], %[[BYTES_CONV]], %c1{{.*}}, %{{.*}}, %{{.*}}) : (!fir.llvm_ptr<i8>, !fir.ref<!fir.box<none>>, i64, i32, !fir.ref<i8>, i32) -> none

func.func @_QPsub5(%arg0: !fir.ref<i32> {fir.bindc_name = "n"}) {
%0 = fir.dummy_scope : !fir.dscope
Expand Down Expand Up @@ -122,19 +118,17 @@ func.func @_QPsub5(%arg0: !fir.ref<i32> {fir.bindc_name = "n"}) {
// CHECK: %[[NBELEM:.*]] = arith.muli %[[I1]], %[[I2]] : index
// CHECK: %[[WIDTH:.*]] = arith.constant 4 : index
// CHECK: %[[BYTES:.*]] = arith.muli %[[NBELEM]], %[[WIDTH]] : index
// CHECK: %[[ADEV_LOAD:.*]] = fir.load %[[ADEV]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?x?xi32>>>>
// CHECK: %[[ADEV_BOX:.*]] = fir.convert %[[ADEV_LOAD]] : (!fir.box<!fir.heap<!fir.array<?x?xi32>>>) -> !fir.box<none>
// CHECK: %[[ADEV_BOX:.*]] = fir.convert %[[ADEV]]#0 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?x?xi32>>>>) -> !fir.ref<!fir.box<none>>
// CHECK: %[[AHOST_PTR:.*]] = fir.convert %[[AHOST]]#1 : (!fir.ref<!fir.array<?x?xi32>>) -> !fir.llvm_ptr<i8>
// CHECK: %[[BYTES_CONV:.*]] = fir.convert %[[BYTES]] : (index) -> i64
// CHECK: fir.call @_FortranACUFDataTransferDescPtr(%[[ADEV_BOX]], %[[AHOST_PTR]], %[[BYTES_CONV]], %c0{{.*}}, %{{.*}}, %{{.*}}) : (!fir.box<none>, !fir.llvm_ptr<i8>, i64, i32, !fir.ref<i8>, i32) -> none
// CHECK: fir.call @_FortranACUFDataTransferDescPtr(%[[ADEV_BOX]], %[[AHOST_PTR]], %[[BYTES_CONV]], %c0{{.*}}, %{{.*}}, %{{.*}}) : (!fir.ref<!fir.box<none>>, !fir.llvm_ptr<i8>, i64, i32, !fir.ref<i8>, i32) -> none
// CHECK: %[[NBELEM:.*]] = arith.muli %[[I1]], %[[I2]] : index
// CHECK: %[[WIDTH:.*]] = arith.constant 4 : index
// CHECK: %[[BYTES:.*]] = arith.muli %[[NBELEM]], %[[WIDTH]] : index
// CHECK: %[[ADEV_LOAD:.*]] = fir.load %[[ADEV]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?x?xi32>>>>
// CHECK: %[[AHOST_PTR:.*]] = fir.convert %[[AHOST]]#1 : (!fir.ref<!fir.array<?x?xi32>>) -> !fir.llvm_ptr<i8>
// CHECK: %[[ADEV_BOX:.*]] = fir.convert %[[ADEV_LOAD]] : (!fir.box<!fir.heap<!fir.array<?x?xi32>>>) -> !fir.box<none>
// CHECK: %[[ADEV_BOX:.*]] = fir.convert %[[ADEV]]#0 : (!fir.ref<!fir.box<!fir.heap<!fir.array<?x?xi32>>>>) -> !fir.ref<!fir.box<none>>
// CHECK: %[[BYTES_CONV:.*]] = fir.convert %[[BYTES]] : (index) -> i64
// CHECK: fir.call @_FortranACUFDataTransferPtrDesc(%[[AHOST_PTR]], %[[ADEV_BOX]], %[[BYTES_CONV]], %c1{{.*}}, %{{.*}}, %{{.*}}) : (!fir.llvm_ptr<i8>, !fir.box<none>, i64, i32, !fir.ref<i8>, i32) -> none
// CHECK: fir.call @_FortranACUFDataTransferPtrDesc(%[[AHOST_PTR]], %[[ADEV_BOX]], %[[BYTES_CONV]], %c1{{.*}}, %{{.*}}, %{{.*}}) : (!fir.llvm_ptr<i8>, !fir.ref<!fir.box<none>>, i64, i32, !fir.ref<i8>, i32) -> none

func.func @_QPsub6() {
%0 = cuf.alloc i32 {bindc_name = "idev", data_attr = #cuf.cuda<device>, uniq_name = "_QFsub6Eidev"} -> !fir.ref<i32>
Expand Down
19 changes: 13 additions & 6 deletions flang/test/Fir/box.fir
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,14 @@ func.func @fa(%a : !fir.ref<!fir.array<100xf32>>) {
// CHECK-LABEL: define void @b1(
// CHECK-SAME: ptr %[[res:.*]], ptr %[[arg0:.*]], i64 %[[arg1:.*]])
func.func @b1(%arg0 : !fir.ref<!fir.char<1,?>>, %arg1 : index) -> !fir.box<!fir.char<1,?>> {
// CHECK: %[[alloca:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8 }
// CHECK: %[[size:.*]] = mul i64 ptrtoint (ptr getelementptr (i8, ptr null, i32 1) to i64), %[[arg1]]
// CHECK: insertvalue {{.*}} undef, i64 %[[size]], 1
// CHECK: insertvalue {{.*}} i32 20240719, 2
// CHECK: insertvalue {{.*}} ptr %[[arg0]], 0
%x = fir.embox %arg0 typeparams %arg1 : (!fir.ref<!fir.char<1,?>>, index) -> !fir.box<!fir.char<1,?>>
// CHECK: store {{.*}}, ptr %[[res]]
// CHECK: store {{.*}}, ptr %[[alloca]]
// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr %[[res]], ptr %[[alloca]], i32 24, i1 false)
return %x : !fir.box<!fir.char<1,?>>
}

Expand All @@ -71,11 +73,13 @@ func.func @b1(%arg0 : !fir.ref<!fir.char<1,?>>, %arg1 : index) -> !fir.box<!fir.
// CHECK-SAME: ptr %[[arg0:.*]], i64 %[[arg1:.*]])
func.func @b2(%arg0 : !fir.ref<!fir.array<?x!fir.char<1,5>>>, %arg1 : index) -> !fir.box<!fir.array<?x!fir.char<1,5>>> {
%1 = fir.shape %arg1 : (index) -> !fir.shape<1>
// CHECK: %[[alloca:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }
// CHECK: insertvalue {{.*}} { ptr undef, i64 ptrtoint (ptr getelementptr ([5 x i8], ptr null, i32 1) to i64), i32 20240719, i8 1, i8 40, i8 0, i8 0, {{.*}} }, i64 %[[arg1]], 7, 0, 1
// CHECK: insertvalue {{.*}} %{{.*}}, i64 ptrtoint (ptr getelementptr ([5 x i8], ptr null, i32 1) to i64), 7, 0, 2
// CHECK: insertvalue {{.*}} ptr %[[arg0]], 0
%2 = fir.embox %arg0(%1) : (!fir.ref<!fir.array<?x!fir.char<1,5>>>, !fir.shape<1>) -> !fir.box<!fir.array<?x!fir.char<1,5>>>
// CHECK: store {{.*}}, ptr %[[res]]
// CHECK: store {{.*}}, ptr %[[alloca]]
// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr %[[res]], ptr %[[alloca]], i32 48, i1 false)
return %2 : !fir.box<!fir.array<?x!fir.char<1,5>>>
}

Expand All @@ -84,14 +88,16 @@ func.func @b2(%arg0 : !fir.ref<!fir.array<?x!fir.char<1,5>>>, %arg1 : index) ->
// CHECK-SAME: ptr %[[res:.*]], ptr %[[arg0:.*]], i64 %[[arg1:.*]], i64 %[[arg2:.*]])
func.func @b3(%arg0 : !fir.ref<!fir.array<?x!fir.char<1,?>>>, %arg1 : index, %arg2 : index) -> !fir.box<!fir.array<?x!fir.char<1,?>>> {
%1 = fir.shape %arg2 : (index) -> !fir.shape<1>
// CHECK: %[[alloca:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }
// CHECK: %[[size:.*]] = mul i64 ptrtoint (ptr getelementptr (i8, ptr null, i32 1) to i64), %[[arg1]]
// CHECK: insertvalue {{.*}} i64 %[[size]], 1
// CHECK: insertvalue {{.*}} i32 20240719, 2
// CHECK: insertvalue {{.*}} i64 %[[arg2]], 7, 0, 1
// CHECK: insertvalue {{.*}} i64 %[[size]], 7, 0, 2
// CHECK: insertvalue {{.*}} ptr %[[arg0]], 0
%2 = fir.embox %arg0(%1) typeparams %arg1 : (!fir.ref<!fir.array<?x!fir.char<1,?>>>, !fir.shape<1>, index) -> !fir.box<!fir.array<?x!fir.char<1,?>>>
// CHECK: store {{.*}}, ptr %[[res]]
// CHECK: store {{.*}}, ptr %[[alloca]]
// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr %[[res]], ptr %[[alloca]], i32 48, i1 false)
return %2 : !fir.box<!fir.array<?x!fir.char<1,?>>>
}

Expand All @@ -101,14 +107,16 @@ func.func @b3(%arg0 : !fir.ref<!fir.array<?x!fir.char<1,?>>>, %arg1 : index, %ar
func.func @b4(%arg0 : !fir.ref<!fir.array<7x!fir.char<1,?>>>, %arg1 : index) -> !fir.box<!fir.array<7x!fir.char<1,?>>> {
%c_7 = arith.constant 7 : index
%1 = fir.shape %c_7 : (index) -> !fir.shape<1>
// CHECK: %[[alloca:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }
// CHECK: %[[size:.*]] = mul i64 ptrtoint (ptr getelementptr (i8, ptr null, i32 1) to i64), %[[arg1]]
// CHECK: insertvalue {{.*}} i64 %[[size]], 1
// CHECK: insertvalue {{.*}} i32 20240719, 2
// CHECK: insertvalue {{.*}} i64 7, 7, 0, 1
// CHECK: insertvalue {{.*}} i64 %[[size]], 7, 0, 2
// CHECK: insertvalue {{.*}} ptr %[[arg0]], 0
%x = fir.embox %arg0(%1) typeparams %arg1 : (!fir.ref<!fir.array<7x!fir.char<1,?>>>, !fir.shape<1>, index) -> !fir.box<!fir.array<7x!fir.char<1,?>>>
// CHECK: store {{.*}}, ptr %[[res]]
// CHECK: store {{.*}}, ptr %[[alloca]]
// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr %[[res]], ptr %[[alloca]], i32 48, i1 false)
return %x : !fir.box<!fir.array<7x!fir.char<1,?>>>
}

Expand All @@ -117,8 +125,7 @@ func.func @b4(%arg0 : !fir.ref<!fir.array<7x!fir.char<1,?>>>, %arg1 : index) ->
// CHECK-SAME: ptr %[[arg0:.*]], ptr %[[arg1:.*]])
func.func @b5(%arg0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?x?xf32>>>>, %arg1 : !fir.box<!fir.heap<!fir.array<?x?xf32>>>) {
fir.store %arg1 to %arg0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?x?xf32>>>>
// CHECK: %[[boxLoad:.*]] = load { ptr, i64, i32, i8, i8, i8, i8, [2 x [3 x i64]] }, ptr %[[arg1]]
// CHECK: store { ptr, i64, i32, i8, i8, i8, i8, [2 x [3 x i64]] } %[[boxLoad]], ptr %[[arg0]]
// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr %0, ptr %1, i32 72, i1 false)
return
}

Expand Down
4 changes: 2 additions & 2 deletions flang/test/Fir/convert-to-llvm-openmp-and-fir.fir
Original file line number Diff line number Diff line change
Expand Up @@ -799,8 +799,8 @@ func.func @_QPs(%arg0: !fir.ref<complex<f32>> {fir.bindc_name = "x"}) {
//CHECK: omp.parallel {
//CHECK: %[[CONST_1:.*]] = llvm.mlir.constant(1 : i32) : i32
//CHECK: %[[ALLOCA_1:.*]] = llvm.alloca %[[CONST_1:.*]] x !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8)> {alignment = 8 : i64} : (i32) -> !llvm.ptr
//CHECK: %[[LOAD:.*]] = llvm.load %[[ALLOCA]] : !llvm.ptr -> !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8)>
//CHECK: llvm.store %[[LOAD]], %[[ALLOCA_1]] : !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8)>, !llvm.ptr
//CHECK: %[[SIZE:.*]] = llvm.mlir.constant(24 : i32) : i32
//CHECK: "llvm.intr.memcpy"(%[[ALLOCA_1]], %[[ALLOCA]], %[[SIZE]]) <{isVolatile = false}> : (!llvm.ptr, !llvm.ptr, i32) -> ()
//CHECK: %[[GEP:.*]] = llvm.getelementptr %[[ALLOCA_1]][0, 0] : (!llvm.ptr) -> !llvm.ptr
//CHECK: %[[LOAD_2:.*]] = llvm.load %[[GEP]] : !llvm.ptr -> !llvm.ptr
//CHECK: omp.terminator
Expand Down
28 changes: 15 additions & 13 deletions flang/test/Fir/convert-to-llvm.fir
Original file line number Diff line number Diff line change
Expand Up @@ -862,8 +862,8 @@ func.func @test_store_box(%array : !fir.ref<!fir.box<!fir.array<?x?xf32>>>, %box
// CHECK-LABEL: llvm.func @test_store_box
// CHECK-SAME: (%[[arg0:.*]]: !llvm.ptr,
// CHECK-SAME: %[[arg1:.*]]: !llvm.ptr) {
// CHECK-NEXT: %[[box_to_store:.*]] = llvm.load %arg1 : !llvm.ptr -> !llvm.struct<(ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, array<2 x array<3 x i{{.*}}>>)>
// CHECK-NEXT: llvm.store %[[box_to_store]], %[[arg0]] : !llvm.struct<(ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, array<2 x array<3 x i{{.*}}>>)>, !llvm.ptr
// CHECK-NEXT: %[[size:.*]] = llvm.mlir.constant(72 : i32) : i32
// CHECK-NEXT: "llvm.intr.memcpy"(%[[arg0]], %[[arg1]], %[[size]]) <{isVolatile = false}> : (!llvm.ptr, !llvm.ptr, i32) -> ()
// CHECK-NEXT: llvm.return
// CHECK-NEXT: }

Expand All @@ -875,15 +875,17 @@ func.func @store_unlimited_polymorphic_box(%arg0 : !fir.class<none>, %arg1 : !fi
fir.store %arg3 to %arg3r : !fir.ref<!fir.box<!fir.array<?xnone>>>
return
}
// CHECK-LABEL: llvm.func @store_unlimited_polymorphic_box(
// CHECK: %[[VAL_8:.*]] = llvm.load %{{.*}} : !llvm.ptr -> !llvm.struct<(ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, ptr, array<1 x i{{.*}}>)>
// CHECK: llvm.store %[[VAL_8]], %{{.*}} : !llvm.struct<(ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, ptr, array<1 x i{{.*}}>)>, !llvm.ptr
// CHECK: %[[VAL_9:.*]] = llvm.load %{{.*}} : !llvm.ptr -> !llvm.struct<(ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, array<1 x array<3 x i{{.*}}>>, ptr, array<1 x i{{.*}}>)>
// CHECK: llvm.store %[[VAL_9]], %{{.*}} : !llvm.struct<(ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, array<1 x array<3 x i{{.*}}>>, ptr, array<1 x i{{.*}}>)>, !llvm.ptr
// CHECK: %[[VAL_10:.*]] = llvm.load %{{.*}} : !llvm.ptr -> !llvm.struct<(ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, ptr, array<1 x i{{.*}}>)>
// CHECK: llvm.store %[[VAL_10]], %{{.*}} : !llvm.struct<(ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, ptr, array<1 x i{{.*}}>)>, !llvm.ptr
// CHECK: %[[VAL_11:.*]] = llvm.load %{{.*}}: !llvm.ptr
// CHECK: llvm.store %[[VAL_11]], %{{.*}} : !llvm.struct<(ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, array<1 x array<3 x i{{.*}}>>, ptr, array<1 x i{{.*}}>)>, !llvm.ptr
// CHECK: llvm.func @store_unlimited_polymorphic_box(%[[VAL_0:.*]]: !llvm.ptr, %[[VAL_1:.*]]: !llvm.ptr, %[[VAL_2:.*]]: !llvm.ptr, %[[VAL_3:.*]]: !llvm.ptr, %[[VAL_4:.*]]: !llvm.ptr, %[[VAL_5:.*]]: !llvm.ptr, %[[VAL_6:.*]]: !llvm.ptr, %[[VAL_7:.*]]: !llvm.ptr) {
// CHECK: %[[VAL_8:.*]] = llvm.mlir.constant(40 : i32) : i32
// CHECK: "llvm.intr.memcpy"(%[[VAL_4]], %[[VAL_0]], %[[VAL_8]]) <{isVolatile = false}> : (!llvm.ptr, !llvm.ptr, i32) -> ()
// CHECK: %[[VAL_9:.*]] = llvm.mlir.constant(64 : i32) : i32
// CHECK: "llvm.intr.memcpy"(%[[VAL_5]], %[[VAL_1]], %[[VAL_9]]) <{isVolatile = false}> : (!llvm.ptr, !llvm.ptr, i32) -> ()
// CHECK: %[[VAL_10:.*]] = llvm.mlir.constant(40 : i32) : i32
// CHECK: "llvm.intr.memcpy"(%[[VAL_6]], %[[VAL_2]], %[[VAL_10]]) <{isVolatile = false}> : (!llvm.ptr, !llvm.ptr, i32) -> ()
// CHECK: %[[VAL_11:.*]] = llvm.mlir.constant(64 : i32) : i32
// CHECK: "llvm.intr.memcpy"(%[[VAL_7]], %[[VAL_3]], %[[VAL_11]]) <{isVolatile = false}> : (!llvm.ptr, !llvm.ptr, i32) -> ()
// CHECK: llvm.return
// CHECK: }


// -----
Expand Down Expand Up @@ -935,8 +937,8 @@ func.func @test_load_box(%addr : !fir.ref<!fir.box<!fir.array<10xf32>>>) {
// GENERIC-NEXT: %[[box_copy:.*]] = llvm.alloca %[[c1]] x !llvm.struct<([[DESC_TYPE:.*]])>
// AMDGPU-NEXT: %[[alloca_box_copy:.*]] = llvm.alloca %[[c1]] x !llvm.struct<([[DESC_TYPE:.*]])>{{.*}} : (i32) -> !llvm.ptr<5>
// AMDGPU-NEXT: %[[box_copy:.*]] = llvm.addrspacecast %[[alloca_box_copy]] : !llvm.ptr<5> to !llvm.ptr
// CHECK-NEXT: %[[box_val:.*]] = llvm.load %[[arg0]] : !llvm.ptr -> !llvm.struct<([[DESC_TYPE]])>
// CHECK-NEXT: llvm.store %[[box_val]], %[[box_copy]] : !llvm.struct<([[DESC_TYPE]])>, !llvm.ptr
// CHECK-NEXT: %[[size:.*]] = llvm.mlir.constant(48 : i32) : i32
// CHECK-NEXT: "llvm.intr.memcpy"(%[[box_copy]], %[[arg0]], %[[size]]) <{isVolatile = false}> : (!llvm.ptr, !llvm.ptr, i32) -> ()
// CHECK-NEXT: llvm.call @takes_box(%[[box_copy]]) : (!llvm.ptr) -> ()
// CHECK-NEXT: llvm.return
// CHECK-NEXT: }
Expand Down
239 changes: 121 additions & 118 deletions flang/test/Fir/embox-char.fir

Large diffs are not rendered by default.

12 changes: 4 additions & 8 deletions flang/test/Fir/polymorphic.fir
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ func.func @_QMpolymorphic_testPtest_allocate_unlimited_polymorphic_non_derived()
// CHECK: %[[MEM:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }
// CHECK: %[[DESC:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }, i64 1
// CHECK: store { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] } { ptr null, i64 0, i32 20240719, i8 0, i8 -1, i8 1, i8 1, ptr null, [1 x i64] zeroinitializer }, ptr %[[MEM]]
// CHECK: %[[LOADED:.*]] = load { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }, ptr %[[MEM]], align 8
// CHECK: store { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] } %[[LOADED]], ptr %[[DESC]]
// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr %[[DESC]], ptr %[[MEM]], i32 40, i1 false)
// CHECK: ret void
// CHECK: }

Expand Down Expand Up @@ -66,8 +65,7 @@ func.func @_QMpolymorphic_testPtest_embox() {
// CHECK-LABEL: @_QMpolymorphic_testPtest_embox()
// CHECK: %[[ALLOCA_DESC:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }
// CHECK: store { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] } { ptr @_QFEy, i64 ptrtoint (ptr getelementptr (i32, ptr null, i32 1) to i64), i32 20240719, i8 1, i8 9, {{.*}}, ptr %[[ALLOCA_DESC]]
// CHECK: %[[LOADED_DESC:.*]] = load { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, ptr %[[ALLOCA_DESC]], align 8
// CHECK: store { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] } %[[LOADED_DESC]], ptr @_QFEx, align 8
// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr @_QFEx, ptr %[[ALLOCA_DESC]], i32 64, i1 false)

// Test emboxing of an array element from an unlimited polymorphic array.

Expand Down Expand Up @@ -158,8 +156,7 @@ func.func @_QQmain() {
// CHECK: %[[CLASS_NONE:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }
// CHECK: %[[DESC:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }, i64 1
// CHECK: store { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] } { ptr @_QMmod1Ea, i64 ptrtoint (ptr getelementptr (%_QMmod1TtK2, ptr null, i32 1) to i64), i32 20240719, i8 0, i8 42, i8 1, i8 1, ptr @_QMmod1EXdtXtX2, [1 x i64] zeroinitializer }, ptr %[[CLASS_NONE]], align 8
// CHECK: %[[LOAD:.*]] = load { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }, ptr %[[CLASS_NONE]]
// CHECK: store { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] } %[[LOAD]], ptr %[[DESC]]
// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr %[[DESC]], ptr %[[CLASS_NONE]], i32 40, i1 false)
// CHECK: call void @_QMmod1Psub1(ptr %[[DESC]])

fir.global @_QMmod2Ep : !fir.class<!fir.ptr<none>> {
Expand All @@ -180,8 +177,7 @@ func.func private @_FortranAPointerAssociate(!fir.ref<!fir.box<none>>, !fir.box<
// CHECK-LABEL: define void @_QMmod2Pinitp(
// CHECK-SAME: ptr %[[ARG0:.*]]){{.*}}{
// CHECK: %[[ALLOCA_CLASS_NONE:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }
// CHECK: %[[LOAD:.*]] = load { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }, ptr %[[ARG0]]
// CHECK: store { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] } %[[LOAD]], ptr %[[ALLOCA_CLASS_NONE]]
// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr %[[ALLOCA_CLASS_NONE]], ptr %[[ARG0]], i32 40, i1 false)
// CHECK: %{{.*}} = call {} @_FortranAPointerAssociate(ptr @_QMmod2Ep, ptr %[[ALLOCA_CLASS_NONE]])
// CHECK: ret void

Expand Down
4 changes: 2 additions & 2 deletions flang/test/Fir/tbaa.fir
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ module {
// CHECK: %[[VAL_7:.*]] = llvm.mlir.addressof @_QFEx : !llvm.ptr
// CHECK: %[[VAL_8:.*]] = llvm.mlir.addressof @_QQclX2E2F64756D6D792E66393000 : !llvm.ptr
// CHECK: %[[VAL_10:.*]] = llvm.call @_FortranAioBeginExternalListOutput(%[[VAL_6]], %[[VAL_8]], %[[VAL_5]]) {fastmathFlags = #llvm.fastmath<contract>} : (i32, !llvm.ptr, i32) -> !llvm.ptr
// CHECK: %[[VAL_11:.*]] = llvm.load %[[VAL_7]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr -> !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>
// CHECK: llvm.store %[[VAL_11]], %[[VAL_3]] {tbaa = [#[[$BOXT]]]} : !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>, !llvm.ptr
// CHECK: %[[VAL_11:.*]] = llvm.mlir.constant(64 : i32) : i32
// CHECK: "llvm.intr.memcpy"(%[[VAL_3]], %[[VAL_7]], %[[VAL_11]]) <{isVolatile = false, tbaa = [#[[$BOXT]]]}>
// CHECK: %[[VAL_12:.*]] = llvm.getelementptr %[[VAL_3]][0, 7, %[[VAL_4]], 0] : (!llvm.ptr, i64) -> !llvm.ptr, !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>
// CHECK: %[[VAL_13:.*]] = llvm.load %[[VAL_12]] {tbaa = [#[[$BOXT]]]} : !llvm.ptr -> i64
// CHECK: %[[VAL_14:.*]] = llvm.getelementptr %[[VAL_3]][0, 7, %[[VAL_4]], 1] : (!llvm.ptr, i64) -> !llvm.ptr, !llvm.struct<(ptr, i64, i32, i8, i8, i8, i8, array<1 x array<3 x i64>>, ptr, array<1 x i64>)>
Expand Down
5 changes: 3 additions & 2 deletions flang/test/Integration/OpenMP/private-global.f90
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ program bug
! CHECK: %[[TABLE_BOX_ADDR2:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, i64 1, align 8
! CHECK: %[[TABLE_BOX_VAL:.*]] = insertvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } { ptr undef, i64 ptrtoint (ptr getelementptr (i32, ptr null, i32 1) to i64), i32 20240719, i8 1, i8 9, i8 0, i8 0, [1 x [3 x i64]] {{\[\[}}3 x i64] [i64 1, i64 10, i64 ptrtoint (ptr getelementptr (i32, ptr null, i32 1) to i64)]] }, ptr %[[PRIV_TABLE]], 0
! CHECK: store { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %[[TABLE_BOX_VAL]], ptr %[[TABLE_BOX_ADDR]], align 8
! CHECK: %[[TABLE_BOX_VAL2:.*]] = load { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, ptr %[[TABLE_BOX_ADDR]], align 8
! CHECK: store { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %[[TABLE_BOX_VAL2]], ptr %[[TABLE_BOX_ADDR2]], align 8
! CHECK : %[[TABLE_BOX_VAL2:.*]] = load { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, ptr %[[TABLE_BOX_ADDR]], align 8
! CHECK : store { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %[[TABLE_BOX_VAL2]], ptr %[[TABLE_BOX_ADDR2]], align 8
! CHECK: call void @llvm.memcpy.p0.p0.i32(ptr %[[TABLE_BOX_ADDR2]], ptr %[[TABLE_BOX_ADDR]], i32 48, i1 false)
! CHECK: %[[VAL_26:.*]] = call {} @_FortranAAssign(ptr %[[TABLE_BOX_ADDR2]], ptr %[[BOXED_FIFTY]], ptr @{{.*}}, i32 9)
! ...
! check that we use the private copy of table for table/=50
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,4 @@ end program compilation_to_obj
! LLVM: @[[GLOB_VAR:[^[:space:]]+]]t = internal global

! LLVM: define internal void @_QQmain..omp_par
! LLVM: %[[GLOB_VAL:.*]] = load { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, ptr @[[GLOB_VAR]]t, align 8
! LLVM-NEXT: store { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %[[GLOB_VAL]], ptr %{{.*}}, align 8
! LLVM: call void @llvm.memcpy.p0.p0.i32(ptr %{{.+}}, ptr @[[GLOB_VAR]]t, i32 48, i1 false)
2 changes: 1 addition & 1 deletion flang/test/Lower/OpenMP/parallel-reduction-mixed.f90
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ subroutine proc
end subroutine proc

!CHECK-LABEL: define void @proc_()
!CHECK: call void
!CHECK: call void (ptr, i32, ptr, ...)
!CHECK-SAME: @__kmpc_fork_call(ptr {{.*}}, i32 1, ptr @[[OMP_PAR:.*]], {{.*}})

!CHECK: define internal void @[[OMP_PAR]](ptr {{.*}} %[[TID_ADDR:.*]], ptr noalias
Expand Down
18 changes: 6 additions & 12 deletions flang/test/Lower/allocatable-polymorphic.f90
Original file line number Diff line number Diff line change
Expand Up @@ -603,10 +603,9 @@ program test_alloc
! LLVM: %{{.*}} = call {} @_FortranAAllocatableInitDerivedForAllocate(ptr %{{.*}}, ptr @_QMpolyEXdtXp2, i32 1, i32 0)
! LLVM: %{{.*}} = call {} @_FortranAAllocatableSetBounds(ptr %{{.*}}, i32 0, i64 1, i64 20)
! LLVM: %{{.*}} = call i32 @_FortranAAllocatableAllocate(ptr %{{.*}}, i1 false, ptr null, ptr @_QQclX{{.*}}, i32 {{.*}})
! LLVM-COUNT-2: call void %{{.*}}()
! LLVM-COUNT-2: call void %{{[0-9]*}}()

! LLVM: %[[C1_LOAD:.*]] = load { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }, ptr %{{.*}}
! LLVM: store { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] } %[[C1_LOAD]], ptr %{{.*}}
! LLVM: call void @llvm.memcpy.p0.p0.i32
! LLVM: %[[GEP_TDESC_C1:.*]] = getelementptr { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }, ptr %{{.*}}, i32 0, i32 7
! LLVM: %[[TDESC_C1:.*]] = load ptr, ptr %[[GEP_TDESC_C1]]
! LLVM: %[[ELEM_SIZE_GEP:.*]] = getelementptr { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }, ptr %{{.*}}, i32 0, i32 1
Expand All @@ -620,8 +619,7 @@ program test_alloc
! LLVM: store { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] } %{{.*}}, ptr %[[TMP:.*]]
! LLVM: call void %{{.*}}(ptr %{{.*}})

! LLVM: %[[LOAD_C2:.*]] = load { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }, ptr %{{.*}}
! LLVM: store { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] } %[[LOAD_C2]], ptr %{{.*}}
! LLVM: call void @llvm.memcpy.p0.p0.i32
! LLVM: %[[GEP_TDESC_C2:.*]] = getelementptr { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }, ptr %{{.*}}, i32 0, i32 7
! LLVM: %[[TDESC_C2:.*]] = load ptr, ptr %[[GEP_TDESC_C2]]
! LLVM: %[[ELEM_SIZE_GEP:.*]] = getelementptr { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }, ptr %{{.*}}, i32 0, i32 1
Expand All @@ -635,9 +633,7 @@ program test_alloc
! LLVM: store { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] } %{{.*}}, ptr %{{.*}}
! LLVM: call void %{{.*}}(ptr %{{.*}})

! LLVM: %[[C3_LOAD:.*]] = load { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, ptr %{{.*}}
! LLVM: store { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] } %[[C3_LOAD]], ptr %{{.*}}

! LLVM: call void @llvm.memcpy.p0.p0.i32
! LLVM: %[[GEP_TDESC_C3:.*]] = getelementptr { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, ptr %{{.*}}, i32 0, i32 8
! LLVM: %[[TDESC_C3:.*]] = load ptr, ptr %[[GEP_TDESC_C3]]
! LLVM: %[[ELE_SIZE_GEP:.*]] = getelementptr { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, ptr %{{.*}}, i32 0, i32 1
Expand All @@ -658,8 +654,7 @@ program test_alloc
! LLVM: store { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] } %[[BOX7]], ptr %{{.*}}
! LLVM: call void %{{.*}}(ptr %{{.*}})

! LLVM: %[[C4_LOAD:.*]] = load { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, ptr %{{.*}}
! LLVM: store { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] } %[[C4_LOAD]], ptr %{{.*}}
! LLVM: call void @llvm.memcpy.p0.p0.i32
! LLVM: %[[GEP_TDESC_C4:.*]] = getelementptr { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, ptr %{{.*}}, i32 0, i32 8
! LLVM: %[[TDESC_C4:.*]] = load ptr, ptr %[[GEP_TDESC_C4]]
! LLVM: %[[ELE_SIZE_GEP:.*]] = getelementptr { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]], ptr, [1 x i64] }, ptr %{{.*}}, i32 0, i32 1
Expand All @@ -686,8 +681,7 @@ program test_alloc

! LLVM-LABEL: define void @_QMpolyPtest_deallocate()
! LLVM: store { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] } { ptr null, i64 ptrtoint (ptr getelementptr (%_QMpolyTp1, ptr null, i32 1) to i64), i32 20240719, i8 0, i8 42, i8 2, i8 1, ptr @_QMpolyEXdtXp1, [1 x i64] zeroinitializer }, ptr %[[ALLOCA1:[0-9]*]]
! LLVM: %[[LOAD:.*]] = load { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] }, ptr %[[ALLOCA1]]
! LLVM: store { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] } %[[LOAD]], ptr %[[ALLOCA2:[0-9]*]]
! LLVM: call void @llvm.memcpy.p0.p0.i32(ptr %[[ALLOCA2:[0-9]+]], ptr %[[ALLOCA1]], i32 40, i1 false)
! LLVM: %{{.*}} = call {} @_FortranAAllocatableInitDerivedForAllocate(ptr %[[ALLOCA2]], ptr @_QMpolyEXdtXp1, i32 0, i32 0)
! LLVM: %{{.*}} = call i32 @_FortranAAllocatableAllocate(ptr %[[ALLOCA2]], i1 false, ptr null, ptr @_QQclX{{.*}}, i32 {{.*}})
! LLVM: %{{.*}} = call i32 @_FortranAAllocatableDeallocatePolymorphic(ptr %[[ALLOCA2]], ptr {{.*}}, i1 false, ptr null, ptr @_QQclX{{.*}}, i32 {{.*}})
5 changes: 1 addition & 4 deletions libc/include/llvm-libc-macros/linux/signal-macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,12 @@
#define SS_ONSTACK 0x1
#define SS_DISABLE 0x2

#ifdef __x86_64__
#if defined(__x86_64__) || defined(__i386__) || defined(__riscv)
#define MINSIGSTKSZ 2048
#define SIGSTKSZ 8192
#elif defined(__aarch64__)
#define MINSIGSTKSZ 5120
#define SIGSTKSZ 16384
#elif defined(__riscv)
#define MINSIGSTKSZ 2048
#define SIGSTKSZ 8192
#else
#error "Signal stack sizes not defined for your platform."
#endif
Expand Down
6 changes: 5 additions & 1 deletion libc/include/llvm-libc-types/fexcept_t.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
#ifndef LLVM_LIBC_TYPES_FEXCEPT_T_H
#define LLVM_LIBC_TYPES_FEXCEPT_T_H

typedef int fexcept_t;
#if defined(__x86_64__) || defined(__i386__)
typedef unsigned short int fexcept_t;
#else
typedef unsigned int fexcept_t;
#endif

#endif // LLVM_LIBC_TYPES_FEXCEPT_T_H
7 changes: 7 additions & 0 deletions libc/include/llvm-libc-types/jmp_buf.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ typedef struct {
__UINT64_TYPE__ r15;
__UINTPTR_TYPE__ rsp;
__UINTPTR_TYPE__ rip;
#elif defined(__i386__)
long ebx;
long esi;
long edi;
long ebp;
long esp;
long eip;
#elif defined(__riscv)
/* Program counter. */
long int __pc;
Expand Down
25 changes: 24 additions & 1 deletion libc/src/setjmp/x86_64/longjmp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,34 @@
#include "src/__support/common.h"
#include "src/__support/macros/config.h"

#if !defined(LIBC_TARGET_ARCH_IS_X86_64)
#if !defined(LIBC_TARGET_ARCH_IS_X86)
#error "Invalid file include"
#endif

namespace LIBC_NAMESPACE_DECL {

#ifdef __i386__
[[gnu::naked]]
LLVM_LIBC_FUNCTION(void, longjmp, (jmp_buf, int)) {
asm(R"(
mov 0x4(%%esp), %%ecx
mov 0x8(%%esp), %%eax
cmpl $0x1, %%eax
adcl $0x0, %%eax

mov %c[ebx](%%ecx), %%ebx
mov %c[esi](%%ecx), %%esi
mov %c[edi](%%ecx), %%edi
mov %c[ebp](%%ecx), %%ebp
mov %c[esp](%%ecx), %%esp

jmp *%c[eip](%%ecx)
)" ::[ebx] "i"(offsetof(__jmp_buf, ebx)),
[esi] "i"(offsetof(__jmp_buf, esi)), [edi] "i"(offsetof(__jmp_buf, edi)),
[ebp] "i"(offsetof(__jmp_buf, ebp)), [esp] "i"(offsetof(__jmp_buf, esp)),
[eip] "i"(offsetof(__jmp_buf, eip)));
}
#else
[[gnu::naked]]
LLVM_LIBC_FUNCTION(void, longjmp, (jmp_buf, int)) {
asm(R"(
Expand All @@ -38,5 +60,6 @@ LLVM_LIBC_FUNCTION(void, longjmp, (jmp_buf, int)) {
[r15] "i"(offsetof(__jmp_buf, r15)), [rsp] "i"(offsetof(__jmp_buf, rsp)),
[rip] "i"(offsetof(__jmp_buf, rip)));
}
#endif

} // namespace LIBC_NAMESPACE_DECL
28 changes: 27 additions & 1 deletion libc/src/setjmp/x86_64/setjmp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,37 @@
#include "src/__support/macros/config.h"
#include "src/setjmp/setjmp_impl.h"

#if !defined(LIBC_TARGET_ARCH_IS_X86_64)
#if !defined(LIBC_TARGET_ARCH_IS_X86)
#error "Invalid file include"
#endif

namespace LIBC_NAMESPACE_DECL {

#ifdef __i386__
[[gnu::naked]]
LLVM_LIBC_FUNCTION(int, setjmp, (jmp_buf buf)) {
asm(R"(
mov 4(%%esp), %%eax

mov %%ebx, %c[ebx](%%eax)
mov %%esi, %c[esi](%%eax)
mov %%edi, %c[edi](%%eax)
mov %%ebp, %c[ebp](%%eax)

lea 4(%%esp), %%ecx
mov %%ecx, %c[esp](%%eax)

mov (%%esp), %%ecx
mov %%ecx, %c[eip](%%eax)

xorl %%eax, %%eax
retl)" ::[ebx] "i"(offsetof(__jmp_buf, ebx)),
[esi] "i"(offsetof(__jmp_buf, esi)), [edi] "i"(offsetof(__jmp_buf, edi)),
[ebp] "i"(offsetof(__jmp_buf, ebp)), [esp] "i"(offsetof(__jmp_buf, esp)),
[eip] "i"(offsetof(__jmp_buf, eip))
: "eax", "ecx");
}
#else
[[gnu::naked]]
LLVM_LIBC_FUNCTION(int, setjmp, (jmp_buf buf)) {
asm(R"(
Expand All @@ -41,5 +66,6 @@ LLVM_LIBC_FUNCTION(int, setjmp, (jmp_buf buf)) {
[rip] "i"(offsetof(__jmp_buf, rip))
: "rax");
}
#endif

} // namespace LIBC_NAMESPACE_DECL
8 changes: 5 additions & 3 deletions libc/src/string/string_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ LIBC_INLINE size_t strlcpy(char *__restrict dst, const char *__restrict src,
return len;
size_t n = len < size - 1 ? len : size - 1;
inline_memcpy(dst, src, n);
inline_bzero(dst + n, size - n);
dst[n] = '\0';
return len;
}

Expand All @@ -239,11 +239,13 @@ LIBC_INLINE constexpr static char *strrchr_implementation(const char *src,
int c) {
char ch = static_cast<char>(c);
char *last_occurrence = nullptr;
for (; *src; ++src) {
while (true) {
if (*src == ch)
last_occurrence = const_cast<char *>(src);
if (!*src)
return last_occurrence;
++src;
}
return last_occurrence;
}

} // namespace internal
Expand Down
8 changes: 8 additions & 0 deletions libc/test/UnitTest/LibcTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,14 @@ class Test {
(unsigned long long)RHS, LHSStr, RHSStr, Loc);
}

// Helper to allow macro invocations like `ASSERT_EQ(foo, nullptr)`.
template <typename ValType,
cpp::enable_if_t<cpp::is_pointer_v<ValType>, ValType> = nullptr>
bool test(TestCond Cond, ValType LHS, cpp::nullptr_t, const char *LHSStr,
const char *RHSStr, internal::Location Loc) {
return test(Cond, LHS, static_cast<ValType>(nullptr), LHSStr, RHSStr, Loc);
}

template <
typename ValType,
cpp::enable_if_t<
Expand Down
36 changes: 22 additions & 14 deletions libc/test/src/string/StrchrTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,16 @@ template <auto Func> struct StrchrTest : public LIBC_NAMESPACE::testing::Test {
const char *src = "abcde";

// Should return null terminator.
ASSERT_STREQ(Func(src, '\0'), "");
const char *nul_terminator = Func(src, '\0');
ASSERT_NE(nul_terminator, nullptr);
ASSERT_STREQ(nul_terminator, "");
// Source string should not change.
ASSERT_STREQ(src, "abcde");
}

void characterNotWithinStringShouldReturnNullptr() {
// Since 'z' is not within the string, should return nullptr.
ASSERT_STREQ(Func("123?", 'z'), nullptr);
ASSERT_EQ(Func("123?", 'z'), nullptr);
}

void theSourceShouldNotChange() {
Expand All @@ -74,11 +76,13 @@ template <auto Func> struct StrchrTest : public LIBC_NAMESPACE::testing::Test {

void emptyStringShouldOnlyMatchNullTerminator() {
// Null terminator should match.
ASSERT_STREQ(Func("", '\0'), "");
const char empty_string[] = "";
ASSERT_EQ(static_cast<const char *>(Func(empty_string, '\0')),
empty_string);
// All other characters should not match.
ASSERT_STREQ(Func("", 'Z'), nullptr);
ASSERT_STREQ(Func("", '3'), nullptr);
ASSERT_STREQ(Func("", '*'), nullptr);
ASSERT_EQ(Func("", 'Z'), nullptr);
ASSERT_EQ(Func("", '3'), nullptr);
ASSERT_EQ(Func("", '*'), nullptr);
}
};

Expand Down Expand Up @@ -114,25 +118,27 @@ template <auto Func> struct StrrchrTest : public LIBC_NAMESPACE::testing::Test {
const char *src = "abcde";

// Should return null terminator.
ASSERT_STREQ(Func(src, '\0'), "");
const char *nul_terminator = Func(src, '\0');
ASSERT_NE(nul_terminator, nullptr);
ASSERT_STREQ(nul_terminator, "");
// Source string should not change.
ASSERT_STREQ(src, "abcde");
}

void findsLastBehindFirstNullTerminator() {
static const char src[6] = {'a', 'a', '\0', 'b', '\0', 'c'};
// 'b' is behind a null terminator, so should not be found.
ASSERT_STREQ(Func(src, 'b'), nullptr);
ASSERT_EQ(Func(src, 'b'), nullptr);
// Same goes for 'c'.
ASSERT_STREQ(Func(src, 'c'), nullptr);
ASSERT_EQ(Func(src, 'c'), nullptr);

// Should find the second of the two a's.
ASSERT_STREQ(Func(src, 'a'), "a");
}

void characterNotWithinStringShouldReturnNullptr() {
// Since 'z' is not within the string, should return nullptr.
ASSERT_STREQ(Func("123?", 'z'), nullptr);
ASSERT_EQ(Func("123?", 'z'), nullptr);
}

void shouldFindLastOfDuplicates() {
Expand All @@ -146,11 +152,13 @@ template <auto Func> struct StrrchrTest : public LIBC_NAMESPACE::testing::Test {

void emptyStringShouldOnlyMatchNullTerminator() {
// Null terminator should match.
ASSERT_STREQ(Func("", '\0'), "");
const char empty_string[] = "";
ASSERT_EQ(static_cast<const char *>(Func(empty_string, '\0')),
empty_string);
// All other characters should not match.
ASSERT_STREQ(Func("", 'A'), nullptr);
ASSERT_STREQ(Func("", '2'), nullptr);
ASSERT_STREQ(Func("", '*'), nullptr);
ASSERT_EQ(Func("", 'A'), nullptr);
ASSERT_EQ(Func("", '2'), nullptr);
ASSERT_EQ(Func("", '*'), nullptr);
}
};

Expand Down
9 changes: 9 additions & 0 deletions libc/test/src/string/strlcat_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ TEST(LlvmLibcStrlcatTest, Smaller) {
EXPECT_STREQ(buf, "abcd");
}

TEST(LlvmLibcStrlcatTest, SmallerNoOverwriteAfter0) {
const char *str = "cd";
char buf[8]{"ab\0\0efg"};

EXPECT_EQ(LIBC_NAMESPACE::strlcat(buf, str, 8), size_t(4));
EXPECT_STREQ(buf, "abcd");
EXPECT_STREQ(buf + 5, "fg");
}

TEST(LlvmLibcStrlcatTest, No0) {
const char *str = "cd";
char buf[7]{"ab"};
Expand Down
3 changes: 1 addition & 2 deletions libc/test/src/string/strlcpy_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,5 @@ TEST(LlvmLibcStrlcpyTest, Smaller) {

EXPECT_EQ(LIBC_NAMESPACE::strlcpy(buf, str, 7), size_t(3));
EXPECT_STREQ(buf, "abc");
for (const char *p = buf + 3; p < buf + 7; p++)
EXPECT_EQ(*p, '\0');
EXPECT_STREQ(buf + 4, "11");
}
6 changes: 4 additions & 2 deletions libc/test/src/sys/statvfs/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ add_libc_unittest(
statvfs_test.cpp
DEPENDS
libc.src.errno.errno
libc.src.sys.statvfs.linux.statfs_utils
libc.src.sys.statvfs.statvfs
libc.src.sys.stat.mkdirat
libc.src.sys.stat.rmdir
libc.test.UnitTest.ErrnoSetterMatcher
)

Expand All @@ -21,8 +22,9 @@ add_libc_unittest(
fstatvfs_test.cpp
DEPENDS
libc.src.errno.errno
libc.src.sys.statvfs.linux.statfs_utils
libc.src.sys.statvfs.fstatvfs
libc.src.sys.stat.mkdirat
libc.src.sys.stat.rmdir
libc.src.fcntl.open
libc.src.unistd.close
libc.test.UnitTest.ErrnoSetterMatcher
Expand Down
81 changes: 44 additions & 37 deletions libc/test/src/sys/statvfs/linux/fstatvfs_test.cpp
Original file line number Diff line number Diff line change
@@ -1,49 +1,56 @@
//===-- Unittests for fstatvfs --------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "hdr/fcntl_macros.h"
#include "src/__support/macros/config.h"
#include "src/fcntl/open.h"
#include "src/sys/stat/mkdirat.h"
#include "src/sys/statvfs/fstatvfs.h"
#include "src/sys/statvfs/linux/statfs_utils.h"
#include "src/unistd/close.h"
#include "src/unistd/rmdir.h"
#include "test/UnitTest/ErrnoSetterMatcher.h"
#include "test/UnitTest/LibcTest.h"
#include <linux/magic.h>
#include "test/UnitTest/Test.h"

using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;

#ifdef SYS_statfs64
using StatFs = statfs64;
#else
using StatFs = statfs;
#endif

namespace LIBC_NAMESPACE_DECL {
static int fstatfs(int fd, StatFs *buf) {
using namespace statfs_utils;
if (cpp::optional<StatFs> result = linux_fstatfs(fd)) {
*buf = *result;
return 0;
}
return -1;
}
} // namespace LIBC_NAMESPACE_DECL

struct PathFD {
int fd;
explicit PathFD(const char *path)
: fd(LIBC_NAMESPACE::open(path, O_CLOEXEC | O_PATH)) {}
~PathFD() { LIBC_NAMESPACE::close(fd); }
operator int() const { return fd; }
};

TEST(LlvmLibcSysStatvfsTest, FstatfsBasic) {
StatFs buf;
ASSERT_THAT(LIBC_NAMESPACE::fstatfs(PathFD("/"), &buf), Succeeds());
ASSERT_THAT(LIBC_NAMESPACE::fstatfs(PathFD("/proc"), &buf), Succeeds());
ASSERT_EQ(buf.f_type, static_cast<decltype(buf.f_type)>(PROC_SUPER_MAGIC));
ASSERT_THAT(LIBC_NAMESPACE::fstatfs(PathFD("/sys"), &buf), Succeeds());
ASSERT_EQ(buf.f_type, static_cast<decltype(buf.f_type)>(SYSFS_MAGIC));
TEST(LlvmLibcSysFStatvfsTest, FStatvfsBasic) {
struct statvfs buf;

int fd = LIBC_NAMESPACE::open("/", O_PATH);
ASSERT_ERRNO_SUCCESS();
ASSERT_GT(fd, 0);

// The root of the file directory must always exist
ASSERT_THAT(LIBC_NAMESPACE::fstatvfs(fd, &buf), Succeeds());
ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
}

TEST(LlvmLibcSysStatvfsTest, FstatvfsInvalidFD) {
TEST(LlvmLibcSysFStatvfsTest, FStatvfsInvalidPath) {
struct statvfs buf;
ASSERT_THAT(LIBC_NAMESPACE::fstatvfs(-1, &buf), Fails(EBADF));

constexpr const char *FILENAME = "testdata/statvfs.testdir";
auto TEST_DIR = libc_make_test_file_path(FILENAME);

ASSERT_THAT(LIBC_NAMESPACE::mkdirat(AT_FDCWD, TEST_DIR, S_IRWXU),
Succeeds(0));

int fd = LIBC_NAMESPACE::open(TEST_DIR, O_PATH);
ASSERT_ERRNO_SUCCESS();
ASSERT_GT(fd, 0);

// create the file, assert it exists, then delete it and assert it doesn't
// exist anymore.

ASSERT_THAT(LIBC_NAMESPACE::fstatvfs(fd, &buf), Succeeds());

ASSERT_THAT(LIBC_NAMESPACE::rmdir(TEST_DIR), Succeeds(0));

ASSERT_THAT(LIBC_NAMESPACE::fstatvfs(fd, &buf), Fails(ENOENT));
ASSERT_THAT(LIBC_NAMESPACE::close(fd), Succeeds(0));
ASSERT_THAT(LIBC_NAMESPACE::fstatvfs(fd, &buf), Fails(ENOENT));
}
75 changes: 32 additions & 43 deletions libc/test/src/sys/statvfs/linux/statvfs_test.cpp
Original file line number Diff line number Diff line change
@@ -1,54 +1,43 @@
//===-- Unittests for statvfs ---------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "hdr/fcntl_macros.h"
#include "src/__support/macros/config.h"
#include "src/sys/statvfs/linux/statfs_utils.h"
#include "src/sys/stat/mkdirat.h"
#include "src/sys/statvfs/statvfs.h"
#include "src/unistd/rmdir.h"
#include "test/UnitTest/ErrnoSetterMatcher.h"
#include "test/UnitTest/LibcTest.h"
#include <linux/magic.h>
#include "test/UnitTest/Test.h"

using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher;

#ifdef SYS_statfs64
using StatFs = statfs64;
#else
using StatFs = statfs;
#endif

namespace LIBC_NAMESPACE_DECL {
static int statfs(const char *path, StatFs *buf) {
using namespace statfs_utils;
if (cpp::optional<LinuxStatFs> result = linux_statfs(path)) {
*buf = *result;
return 0;
}
return -1;
}
} // namespace LIBC_NAMESPACE_DECL

TEST(LlvmLibcSysStatfsTest, StatfsBasic) {
StatFs buf;
ASSERT_THAT(LIBC_NAMESPACE::statfs("/", &buf), Succeeds());
ASSERT_THAT(LIBC_NAMESPACE::statfs("/proc", &buf), Succeeds());
ASSERT_EQ(buf.f_type, static_cast<decltype(buf.f_type)>(PROC_SUPER_MAGIC));
ASSERT_THAT(LIBC_NAMESPACE::statfs("/sys", &buf), Succeeds());
ASSERT_EQ(buf.f_type, static_cast<decltype(buf.f_type)>(SYSFS_MAGIC));
TEST(LlvmLibcSysStatvfsTest, StatvfsBasic) {
struct statvfs buf;
// The root of the file directory must always exist
ASSERT_THAT(LIBC_NAMESPACE::statvfs("/", &buf), Succeeds());
}

TEST(LlvmLibcSysStatfsTest, StatvfsInvalidPath) {
TEST(LlvmLibcSysStatvfsTest, StatvfsInvalidPath) {
struct statvfs buf;

ASSERT_THAT(LIBC_NAMESPACE::statvfs("", &buf), Fails(ENOENT));
ASSERT_THAT(LIBC_NAMESPACE::statvfs("/nonexistent", &buf), Fails(ENOENT));
ASSERT_THAT(LIBC_NAMESPACE::statvfs("/dev/null/whatever", &buf),
Fails(ENOTDIR));
ASSERT_THAT(LIBC_NAMESPACE::statvfs(nullptr, &buf), Fails(EFAULT));
}

TEST(LlvmLibcSysStatfsTest, StatvfsNameTooLong) {
struct statvfs buf;
ASSERT_THAT(LIBC_NAMESPACE::statvfs("/", &buf), Succeeds());
char *name = static_cast<char *>(__builtin_alloca(buf.f_namemax + 3));
name[0] = '/';
name[buf.f_namemax + 2] = '\0';
for (unsigned i = 1; i < buf.f_namemax + 2; ++i) {
name[i] = 'a';
}
ASSERT_THAT(LIBC_NAMESPACE::statvfs(name, &buf), Fails(ENAMETOOLONG));
// create the file, assert it exists, then delete it and assert it doesn't
// exist anymore.
constexpr const char *FILENAME = "testdata/statvfs.testdir";
auto TEST_DIR = libc_make_test_file_path(FILENAME);

ASSERT_THAT(LIBC_NAMESPACE::mkdirat(AT_FDCWD, TEST_DIR, S_IRWXU),
Succeeds(0));

ASSERT_THAT(LIBC_NAMESPACE::statvfs(TEST_DIR, &buf), Succeeds());

ASSERT_THAT(LIBC_NAMESPACE::rmdir(TEST_DIR), Succeeds(0));

ASSERT_THAT(LIBC_NAMESPACE::statvfs(TEST_DIR, &buf), Fails(ENOENT));
}
1 change: 1 addition & 0 deletions lldb/docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ interesting areas to contribute to lldb.

Source Code <https://github.com/llvm/llvm-project>
Releases <https://github.com/llvm/llvm-project/releases>
Discord <https://discord.com/channels/636084430946959380/636732809708306432>
Discussion Forums <https://discourse.llvm.org/c/subprojects/lldb/8>
Developer Policy <https://llvm.org/docs/DeveloperPolicy.html>
Bug Reports <https://github.com/llvm/llvm-project/issues?q=is%3Aissue+label%3Alldb+is%3Aopen>
Expand Down
36 changes: 36 additions & 0 deletions lldb/include/lldb/Breakpoint/BreakpointLocation.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@

#include <memory>
#include <mutex>
#include <optional>

#include "lldb/Breakpoint/BreakpointOptions.h"
#include "lldb/Breakpoint/StoppointHitCounter.h"
#include "lldb/Core/Address.h"
#include "lldb/Symbol/LineEntry.h"
#include "lldb/Utility/UserID.h"
#include "lldb/lldb-private.h"

Expand Down Expand Up @@ -282,6 +284,25 @@ class BreakpointLocation
/// Returns the breakpoint location ID.
lldb::break_id_t GetID() const { return m_loc_id; }

/// Set the line entry that should be shown to users for this location.
/// It is up to the caller to verify that this is a valid entry to show.
/// The current use of this is to distinguish among line entries from a
/// virtual inlined call stack that all share the same address.
/// The line entry must have the same start address as the address for this
/// location.
bool SetPreferredLineEntry(const LineEntry &line_entry) {
if (m_address == line_entry.range.GetBaseAddress()) {
m_preferred_line_entry = line_entry;
return true;
}
assert(0 && "Tried to set a preferred line entry with a different address");
return false;
}

const std::optional<LineEntry> GetPreferredLineEntry() {
return m_preferred_line_entry;
}

protected:
friend class BreakpointSite;
friend class BreakpointLocationList;
Expand All @@ -306,6 +327,16 @@ class BreakpointLocation
/// If it returns false we should continue, otherwise stop.
bool IgnoreCountShouldStop();

/// If this location knows that the virtual stack frame it represents is
/// not frame 0, return the suggested stack frame instead. This will happen
/// when the location's address contains a "virtual inlined call stack" and
/// the breakpoint was set on a file & line that are not at the bottom of that
/// stack. For now we key off the "preferred line entry" - looking for that
/// in the blocks that start with the stop PC.
/// This version of the API doesn't take an "inlined" parameter because it
/// only changes frames in the inline stack.
std::optional<uint32_t> GetSuggestedStackFrameIndex();

private:
void SwapLocation(lldb::BreakpointLocationSP swap_from);

Expand Down Expand Up @@ -369,6 +400,11 @@ class BreakpointLocation
lldb::break_id_t m_loc_id; ///< Breakpoint location ID.
StoppointHitCounter m_hit_counter; ///< Number of times this breakpoint
/// location has been hit.
/// If this exists, use it to print the stop description rather than the
/// LineEntry m_address resolves to directly. Use this for instance when the
/// location was given somewhere in the virtual inlined call stack since the
/// Address always resolves to the lowest entry in the stack.
std::optional<LineEntry> m_preferred_line_entry;

void SetShouldResolveIndirectFunctions(bool do_resolve) {
m_should_resolve_indirect_functions = do_resolve;
Expand Down
5 changes: 5 additions & 0 deletions lldb/include/lldb/Breakpoint/BreakpointSite.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,11 @@ class BreakpointSite : public std::enable_shared_from_this<BreakpointSite>,
/// \see lldb::DescriptionLevel
void GetDescription(Stream *s, lldb::DescriptionLevel level);

// This runs through all the breakpoint locations owning this site and returns
// the greatest of their suggested stack frame indexes. This only handles
// inlined stack changes.
std::optional<uint32_t> GetSuggestedStackFrameIndex();

/// Tell whether a breakpoint has a location at this site.
///
/// \param[in] bp_id
Expand Down
6 changes: 5 additions & 1 deletion lldb/include/lldb/Core/Declaration.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,14 @@ class Declaration {
/// \param[in] declaration
/// The const Declaration object to compare with.
///
/// \param[in] full
/// Same meaning as Full in FileSpec::Equal. True means an empty
/// directory is not equal to a specified one, false means it is equal.
///
/// \return
/// Returns \b true if \b declaration is at the same file and
/// line, \b false otherwise.
bool FileAndLineEqual(const Declaration &declaration) const;
bool FileAndLineEqual(const Declaration &declaration, bool full) const;

/// Dump a description of this object to a Stream.
///
Expand Down
25 changes: 0 additions & 25 deletions lldb/include/lldb/Host/Editline.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@

#include "lldb/Host/Config.h"

#if LLDB_EDITLINE_USE_WCHAR
#include <codecvt>
#endif
#include <locale>
#include <sstream>
#include <vector>
Expand All @@ -57,23 +54,6 @@

#include "llvm/ADT/FunctionExtras.h"

#if defined(__clang__) && defined(__has_warning)
#if __has_warning("-Wdeprecated-declarations")
#define LLDB_DEPRECATED_WARNING_DISABLE \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")
#define LLDB_DEPRECATED_WARNING_RESTORE _Pragma("clang diagnostic pop")
#endif
#elif defined(__GNUC__) && __GNUC__ > 6
#define LLDB_DEPRECATED_WARNING_DISABLE \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
#define LLDB_DEPRECATED_WARNING_RESTORE _Pragma("GCC diagnostic pop")
#else
#define LLDB_DEPRECATED_WARNING_DISABLE
#define LLDB_DEPRECATED_WARNING_RESTORE
#endif

namespace lldb_private {
namespace line_editor {

Expand Down Expand Up @@ -383,11 +363,6 @@ class Editline {
void SetEditLinePromptCallback(EditlinePromptCallbackType callbackFn);
void SetGetCharacterFunction(EditlineGetCharCallbackType callbackFn);

#if LLDB_EDITLINE_USE_WCHAR
LLDB_DEPRECATED_WARNING_DISABLE
std::wstring_convert<std::codecvt_utf8<wchar_t>> m_utf8conv;
LLDB_DEPRECATED_WARNING_RESTORE
#endif
::EditLine *m_editline = nullptr;
EditlineHistorySP m_history_sp;
bool m_in_history = false;
Expand Down
12 changes: 12 additions & 0 deletions lldb/include/lldb/Target/StopInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,18 @@ class StopInfo : public std::enable_shared_from_this<StopInfo> {
m_description.clear();
}

/// This gives the StopInfo a chance to suggest a stack frame to select.
/// Passing true for inlined_stack will request changes to the inlined
/// call stack. Passing false will request changes to the real stack
/// frame. The inlined stack gets adjusted before we call into the thread
/// plans so they can reason based on the correct values. The real stack
/// adjustment is handled after the frame recognizers get a chance to adjust
/// the frame.
virtual std::optional<uint32_t>
GetSuggestedStackFrameIndex(bool inlined_stack) {
return {};
}

virtual bool IsValidForOperatingSystemThread(Thread &thread) { return true; }

/// A Continue operation can result in a false stop event
Expand Down
4 changes: 2 additions & 2 deletions lldb/include/lldb/Target/ThreadPlanStepInRange.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ class ThreadPlanStepInRange : public ThreadPlanStepRange,
bool m_step_past_prologue; // FIXME: For now hard-coded to true, we could put
// a switch in for this if there's
// demand for that.
bool m_virtual_step; // true if we've just done a "virtual step", i.e. just
// moved the inline stack depth.
LazyBool m_virtual_step; // true if we've just done a "virtual step", i.e.
// just moved the inline stack depth.
ConstString m_step_into_target;
ThreadPlanStepInRange(const ThreadPlanStepInRange &) = delete;
const ThreadPlanStepInRange &
Expand Down
4 changes: 4 additions & 0 deletions lldb/packages/Python/lldbsuite/test/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@
make_path = None

# The overriden dwarf verison.
# Don't use this to test the current compiler's
# DWARF version, as this won't be set if the
# version isn't overridden.
# Use lldbplatformutils.getDwarfVersion() instead.
dwarf_version = 0

# Any overridden settings.
Expand Down
63 changes: 61 additions & 2 deletions lldb/source/Breakpoint/BreakpointLocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -508,8 +508,20 @@ void BreakpointLocation::GetDescription(Stream *s,
s->PutCString("re-exported target = ");
else
s->PutCString("where = ");

// If there's a preferred line entry for printing, use that.
bool show_function_info = true;
if (auto preferred = GetPreferredLineEntry()) {
sc.line_entry = *preferred;
// FIXME: We're going to get the function name wrong when the preferred
// line entry is not the lowest one. For now, just leave the function
// out in this case, but we really should also figure out how to easily
// fake the function name here.
show_function_info = false;
}
sc.DumpStopContext(s, m_owner.GetTarget().GetProcessSP().get(), m_address,
false, true, false, true, true, true);
false, true, false, show_function_info,
show_function_info, show_function_info);
} else {
if (sc.module_sp) {
s->EOL();
Expand Down Expand Up @@ -537,7 +549,10 @@ void BreakpointLocation::GetDescription(Stream *s,
if (sc.line_entry.line > 0) {
s->EOL();
s->Indent("location = ");
sc.line_entry.DumpStopContext(s, true);
if (auto preferred = GetPreferredLineEntry())
preferred->DumpStopContext(s, true);
else
sc.line_entry.DumpStopContext(s, true);
}

} else {
Expand Down Expand Up @@ -656,6 +671,50 @@ void BreakpointLocation::SendBreakpointLocationChangedEvent(
}
}

std::optional<uint32_t> BreakpointLocation::GetSuggestedStackFrameIndex() {
auto preferred_opt = GetPreferredLineEntry();
if (!preferred_opt)
return {};
LineEntry preferred = *preferred_opt;
SymbolContext sc;
if (!m_address.CalculateSymbolContext(&sc))
return {};
// Don't return anything special if frame 0 is the preferred line entry.
// We not really telling the stack frame list to do anything special in that
// case.
if (!LineEntry::Compare(sc.line_entry, preferred))
return {};

if (!sc.block)
return {};

// Blocks have their line info in Declaration form, so make one here:
Declaration preferred_decl(preferred.GetFile(), preferred.line,
preferred.column);

uint32_t depth = 0;
Block *inlined_block = sc.block->GetContainingInlinedBlock();
while (inlined_block) {
// If we've moved to a block that this isn't the start of, that's not
// our inlining info or call site, so we can stop here.
Address start_address;
if (!inlined_block->GetStartAddress(start_address) ||
start_address != m_address)
return {};

const InlineFunctionInfo *info = inlined_block->GetInlinedFunctionInfo();
if (info) {
if (preferred_decl == info->GetDeclaration())
return depth;
if (preferred_decl == info->GetCallSite())
return depth + 1;
}
inlined_block = inlined_block->GetInlinedParent();
depth++;
}
return {};
}

void BreakpointLocation::SwapLocation(BreakpointLocationSP swap_from) {
m_address = swap_from->m_address;
m_should_resolve_indirect_functions =
Expand Down
15 changes: 15 additions & 0 deletions lldb/source/Breakpoint/BreakpointResolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,21 @@ void BreakpointResolver::AddLocation(SearchFilter &filter,
}

BreakpointLocationSP bp_loc_sp(AddLocation(line_start));
// If the address that we resolved the location to returns a different
// LineEntry from the one in the incoming SC, we're probably dealing with an
// inlined call site, so set that as the preferred LineEntry:
LineEntry resolved_entry;
if (!skipped_prologue && bp_loc_sp &&
line_start.CalculateSymbolContextLineEntry(resolved_entry) &&
LineEntry::Compare(resolved_entry, sc.line_entry)) {
// FIXME: The function name will also be wrong here. Do we need to record
// that as well, or can we figure that out again when we report this
// breakpoint location.
if (!bp_loc_sp->SetPreferredLineEntry(sc.line_entry)) {
LLDB_LOG(log, "Tried to add a preferred line entry that didn't have the "
"same address as this location's address.");
}
}
if (log && bp_loc_sp && !GetBreakpoint()->IsInternal()) {
StreamString s;
bp_loc_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose);
Expand Down
17 changes: 17 additions & 0 deletions lldb/source/Breakpoint/BreakpointSite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,23 @@ void BreakpointSite::GetDescription(Stream *s, lldb::DescriptionLevel level) {
m_constituents.GetDescription(s, level);
}

std::optional<uint32_t> BreakpointSite::GetSuggestedStackFrameIndex() {

std::optional<uint32_t> result;
std::lock_guard<std::recursive_mutex> guard(m_constituents_mutex);
for (BreakpointLocationSP loc_sp : m_constituents.BreakpointLocations()) {
std::optional<uint32_t> loc_frame_index =
loc_sp->GetSuggestedStackFrameIndex();
if (loc_frame_index) {
if (result)
result = std::max(*loc_frame_index, *result);
else
result = loc_frame_index;
}
}
return result;
}

bool BreakpointSite::IsInternal() const { return m_constituents.IsInternal(); }

uint8_t *BreakpointSite::GetTrapOpcodeBytes() { return &m_trap_opcode[0]; }
Expand Down
2 changes: 2 additions & 0 deletions lldb/source/Commands/CommandObjectType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2649,6 +2649,8 @@ class CommandObjectTypeLookup : public CommandObjectRaw {
return false;
LanguageType lt1 = lang1->GetLanguageType();
LanguageType lt2 = lang2->GetLanguageType();
if (lt1 == lt2)
return false;
if (lt1 == guessed_language)
return true; // make the selected frame's language come first
if (lt2 == guessed_language)
Expand Down
5 changes: 3 additions & 2 deletions lldb/source/Core/Declaration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,9 @@ int Declaration::Compare(const Declaration &a, const Declaration &b) {
return 0;
}

bool Declaration::FileAndLineEqual(const Declaration &declaration) const {
int file_compare = FileSpec::Compare(this->m_file, declaration.m_file, true);
bool Declaration::FileAndLineEqual(const Declaration &declaration,
bool full) const {
int file_compare = FileSpec::Compare(this->m_file, declaration.m_file, full);
return file_compare == 0 && this->m_line == declaration.m_line;
}

Expand Down
Loading