36 changes: 31 additions & 5 deletions clang/include/clang/Interpreter/Interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "clang/AST/GlobalDecl.h"
#include "clang/Interpreter/PartialTranslationUnit.h"
#include "clang/Interpreter/Value.h"
#include "clang/Sema/Ownership.h"

#include "llvm/ADT/DenseMap.h"
#include "llvm/ExecutionEngine/JITSymbol.h"
Expand Down Expand Up @@ -75,17 +76,26 @@ class IncrementalCompilerBuilder {
llvm::StringRef CudaSDKPath;
};

/// Generate glue code between the Interpreter's built-in runtime and user code.
class RuntimeInterfaceBuilder {
public:
virtual ~RuntimeInterfaceBuilder() = default;

using TransformExprFunction = ExprResult(RuntimeInterfaceBuilder *Builder,
Expr *, ArrayRef<Expr *>);
virtual TransformExprFunction *getPrintValueTransformer() = 0;
};

/// Provides top-level interfaces for incremental compilation and execution.
class Interpreter {
std::unique_ptr<llvm::orc::ThreadSafeContext> TSCtx;
std::unique_ptr<IncrementalParser> IncrParser;
std::unique_ptr<IncrementalExecutor> IncrExecutor;
std::unique_ptr<RuntimeInterfaceBuilder> RuntimeIB;

// An optional parser for CUDA offloading
std::unique_ptr<IncrementalParser> DeviceParser;

Interpreter(std::unique_ptr<CompilerInstance> CI, llvm::Error &Err);

llvm::Error CreateExecutor();
unsigned InitPTUSize = 0;

Expand All @@ -94,8 +104,25 @@ class Interpreter {
// printing happens, it's in an invalid state.
Value LastValue;

// Add a call to an Expr to report its result. We query the function from
// RuntimeInterfaceBuilder once and store it as a function pointer to avoid
// frequent virtual function calls.
RuntimeInterfaceBuilder::TransformExprFunction *AddPrintValueCall = nullptr;

protected:
// Derived classes can make use an extended interface of the Interpreter.
// That's useful for testing and out-of-tree clients.
Interpreter(std::unique_ptr<CompilerInstance> CI, llvm::Error &Err);

// Lazily construct the RuntimeInterfaceBuilder. The provided instance will be
// used for the entire lifetime of the interpreter. The default implementation
// targets the in-process __clang_Interpreter runtime. Override this to use a
// custom runtime.
virtual std::unique_ptr<RuntimeInterfaceBuilder> FindRuntimeInterface();

public:
~Interpreter();
virtual ~Interpreter();

static llvm::Expected<std::unique_ptr<Interpreter>>
create(std::unique_ptr<CompilerInstance> CI);
static llvm::Expected<std::unique_ptr<Interpreter>>
Expand Down Expand Up @@ -142,8 +169,7 @@ class Interpreter {

private:
size_t getEffectivePTUSize() const;

bool FindRuntimeInterface();
void markUserCodeStart();

llvm::DenseMap<CXXRecordDecl *, llvm::orc::ExecutorAddr> Dtors;

Expand Down
12 changes: 2 additions & 10 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -6752,18 +6752,10 @@ class Sema final {
SourceLocation RParenLoc);

//// ActOnCXXThis - Parse 'this' pointer.
///
/// \param ThisRefersToClosureObject Whether to skip the 'this' check for a
/// lambda because 'this' refers to the closure object.
ExprResult ActOnCXXThis(SourceLocation loc,
bool ThisRefersToClosureObject = false);
ExprResult ActOnCXXThis(SourceLocation loc);

/// Build a CXXThisExpr and mark it referenced in the current context.
///
/// \param ThisRefersToClosureObject Whether to skip the 'this' check for a
/// lambda because 'this' refers to the closure object.
Expr *BuildCXXThisExpr(SourceLocation Loc, QualType Type, bool IsImplicit,
bool ThisRefersToClosureObject = false);
Expr *BuildCXXThisExpr(SourceLocation Loc, QualType Type, bool IsImplicit);
void MarkThisReferenced(CXXThisExpr *This);

/// Try to retrieve the type of the 'this' pointer.
Expand Down
7 changes: 0 additions & 7 deletions clang/include/clang/Serialization/ASTReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -2456,13 +2456,6 @@ class BitsUnpacker {
uint32_t Value;
uint32_t CurrentBitsIndex = ~0;
};

inline bool shouldSkipCheckingODR(const Decl *D) {
return D->getOwningModule() &&
D->getASTContext().getLangOpts().SkipODRCheckInGMF &&
D->getOwningModule()->isExplicitGlobalModule();
}

} // namespace clang

#endif // LLVM_CLANG_SERIALIZATION_ASTREADER_H
2 changes: 1 addition & 1 deletion clang/lib/APINotes/APINotesManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ APINotesManager::getCurrentModuleAPINotes(Module *M, bool LookInModule,
llvm::SmallVector<FileEntryRef, 2> APINotes;

// First, look relative to the module itself.
if (LookInModule) {
if (LookInModule && M->Directory) {
// Local function to try loading an API notes file in the given directory.
auto tryAPINotes = [&](DirectoryEntryRef Dir, bool WantPublic) {
if (auto File = findAPINotesFile(Dir, ModuleName, WantPublic)) {
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4496,7 +4496,7 @@ unsigned FunctionDecl::getODRHash() {
}

class ODRHash Hash;
Hash.AddFunctionDecl(this);
Hash.AddFunctionDecl(this, /*SkipBody=*/shouldSkipCheckingODR());
setHasODRHash(true);
ODRHash = Hash.CalculateHash();
return ODRHash;
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/AST/DeclBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1102,6 +1102,11 @@ bool Decl::isInAnotherModuleUnit() const {
return M != getASTContext().getCurrentNamedModule();
}

bool Decl::shouldSkipCheckingODR() const {
return getASTContext().getLangOpts().SkipODRCheckInGMF && getOwningModule() &&
getOwningModule()->isExplicitGlobalModule();
}

static Decl::Kind getKind(const Decl *D) { return D->getKind(); }
static Decl::Kind getKind(const DeclContext *DC) { return DC->getDeclKind(); }

Expand Down
7 changes: 2 additions & 5 deletions clang/lib/AST/Interp/InterpBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,7 @@ static APSInt peekToAPSInt(InterpStack &Stk, PrimType T, size_t Offset = 0) {
Offset = align(primSize(T));

APSInt R;
INT_TYPE_SWITCH(T, {
T Val = Stk.peek<T>(Offset);
R = APSInt(APInt(Val.bitWidth(), static_cast<uint64_t>(Val), T::isSigned()),
!T::isSigned());
});
INT_TYPE_SWITCH(T, R = Stk.peek<T>(Offset).toAPSInt());

return R;
}
Expand Down Expand Up @@ -1052,6 +1048,7 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
case Builtin::BI__builtin_popcount:
case Builtin::BI__builtin_popcountl:
case Builtin::BI__builtin_popcountll:
case Builtin::BI__builtin_popcountg:
case Builtin::BI__popcnt16: // Microsoft variants of popcount
case Builtin::BI__popcnt:
case Builtin::BI__popcnt64:
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2682,6 +2682,8 @@ bool QualType::isTriviallyRelocatableType(const ASTContext &Context) const {
return false;
} else if (const auto *RD = BaseElementType->getAsRecordDecl()) {
return RD->canPassInRegisters();
} else if (BaseElementType.isTriviallyCopyableType(Context)) {
return true;
} else {
switch (isNonTrivialToPrimitiveDestructiveMove()) {
case PCK_Trivial:
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CodeGen/BackendUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,8 @@ static void addSanitizers(const Triple &TargetTriple,
[](FunctionPassManager &FPM, OptimizationLevel Level) {
// RemoveTrapsPass expects trap blocks preceded by conditional
// branches, which usually is not the case without SimplifyCFG.
// TODO: Remove `SimplifyCFGPass` after switching to dedicated
// intrinsic.
FPM.addPass(SimplifyCFGPass());
FPM.addPass(RemoveTrapsPass());
});
Expand Down
161 changes: 150 additions & 11 deletions clang/lib/CodeGen/CGCoroutine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ static bool FunctionCanThrow(const FunctionDecl *D) {
Proto->canThrow() != CT_Cannot;
}

static bool ResumeStmtCanThrow(const Stmt *S) {
static bool StmtCanThrow(const Stmt *S) {
if (const auto *CE = dyn_cast<CallExpr>(S)) {
const auto *Callee = CE->getDirectCallee();
if (!Callee)
Expand All @@ -167,7 +167,7 @@ static bool ResumeStmtCanThrow(const Stmt *S) {
}

for (const auto *child : S->children())
if (ResumeStmtCanThrow(child))
if (StmtCanThrow(child))
return true;

return false;
Expand All @@ -178,18 +178,31 @@ static bool ResumeStmtCanThrow(const Stmt *S) {
// auto && x = CommonExpr();
// if (!x.await_ready()) {
// llvm_coro_save();
// x.await_suspend(...); (*)
// llvm_coro_suspend(); (**)
// llvm_coro_await_suspend(&x, frame, wrapper) (*) (**)
// llvm_coro_suspend(); (***)
// }
// x.await_resume();
//
// where the result of the entire expression is the result of x.await_resume()
//
// (*) If x.await_suspend return type is bool, it allows to veto a suspend:
// (*) llvm_coro_await_suspend_{void, bool, handle} is lowered to
// wrapper(&x, frame) when it's certain not to interfere with
// coroutine transform. await_suspend expression is
// asynchronous to the coroutine body and not all analyses
// and transformations can handle it correctly at the moment.
//
// Wrapper function encapsulates x.await_suspend(...) call and looks like:
//
// auto __await_suspend_wrapper(auto& awaiter, void* frame) {
// std::coroutine_handle<> handle(frame);
// return awaiter.await_suspend(handle);
// }
//
// (**) If x.await_suspend return type is bool, it allows to veto a suspend:
// if (x.await_suspend(...))
// llvm_coro_suspend();
//
// (**) llvm_coro_suspend() encodes three possible continuations as
// (***) llvm_coro_suspend() encodes three possible continuations as
// a switch instruction:
//
// %where-to = call i8 @llvm.coro.suspend(...)
Expand All @@ -212,9 +225,10 @@ static LValueOrRValue emitSuspendExpression(CodeGenFunction &CGF, CGCoroData &Co
bool ignoreResult, bool forLValue) {
auto *E = S.getCommonExpr();

auto Binder =
auto CommonBinder =
CodeGenFunction::OpaqueValueMappingData::bind(CGF, S.getOpaqueValue(), E);
auto UnbindOnExit = llvm::make_scope_exit([&] { Binder.unbind(CGF); });
auto UnbindCommonOnExit =
llvm::make_scope_exit([&] { CommonBinder.unbind(CGF); });

auto Prefix = buildSuspendPrefixStr(Coro, Kind);
BasicBlock *ReadyBlock = CGF.createBasicBlock(Prefix + Twine(".ready"));
Expand All @@ -232,16 +246,73 @@ static LValueOrRValue emitSuspendExpression(CodeGenFunction &CGF, CGCoroData &Co
auto *NullPtr = llvm::ConstantPointerNull::get(CGF.CGM.Int8PtrTy);
auto *SaveCall = Builder.CreateCall(CoroSave, {NullPtr});

auto SuspendWrapper = CodeGenFunction(CGF.CGM).generateAwaitSuspendWrapper(
CGF.CurFn->getName(), Prefix, S);

CGF.CurCoro.InSuspendBlock = true;
auto *SuspendRet = CGF.EmitScalarExpr(S.getSuspendExpr());

assert(CGF.CurCoro.Data && CGF.CurCoro.Data->CoroBegin &&
"expected to be called in coroutine context");

SmallVector<llvm::Value *, 3> SuspendIntrinsicCallArgs;
SuspendIntrinsicCallArgs.push_back(
CGF.getOrCreateOpaqueLValueMapping(S.getOpaqueValue()).getPointer(CGF));

SuspendIntrinsicCallArgs.push_back(CGF.CurCoro.Data->CoroBegin);
SuspendIntrinsicCallArgs.push_back(SuspendWrapper);

const auto SuspendReturnType = S.getSuspendReturnType();
llvm::Intrinsic::ID AwaitSuspendIID;

switch (SuspendReturnType) {
case CoroutineSuspendExpr::SuspendReturnType::SuspendVoid:
AwaitSuspendIID = llvm::Intrinsic::coro_await_suspend_void;
break;
case CoroutineSuspendExpr::SuspendReturnType::SuspendBool:
AwaitSuspendIID = llvm::Intrinsic::coro_await_suspend_bool;
break;
case CoroutineSuspendExpr::SuspendReturnType::SuspendHandle:
AwaitSuspendIID = llvm::Intrinsic::coro_await_suspend_handle;
break;
}

llvm::Function *AwaitSuspendIntrinsic = CGF.CGM.getIntrinsic(AwaitSuspendIID);

const auto AwaitSuspendCanThrow = StmtCanThrow(S.getSuspendExpr());

llvm::CallBase *SuspendRet = nullptr;
// FIXME: add call attributes?
if (AwaitSuspendCanThrow)
SuspendRet =
CGF.EmitCallOrInvoke(AwaitSuspendIntrinsic, SuspendIntrinsicCallArgs);
else
SuspendRet = CGF.EmitNounwindRuntimeCall(AwaitSuspendIntrinsic,
SuspendIntrinsicCallArgs);

assert(SuspendRet);
CGF.CurCoro.InSuspendBlock = false;

if (SuspendRet != nullptr && SuspendRet->getType()->isIntegerTy(1)) {
switch (SuspendReturnType) {
case CoroutineSuspendExpr::SuspendReturnType::SuspendVoid:
assert(SuspendRet->getType()->isVoidTy());
break;
case CoroutineSuspendExpr::SuspendReturnType::SuspendBool: {
assert(SuspendRet->getType()->isIntegerTy());

// Veto suspension if requested by bool returning await_suspend.
BasicBlock *RealSuspendBlock =
CGF.createBasicBlock(Prefix + Twine(".suspend.bool"));
CGF.Builder.CreateCondBr(SuspendRet, RealSuspendBlock, ReadyBlock);
CGF.EmitBlock(RealSuspendBlock);
break;
}
case CoroutineSuspendExpr::SuspendReturnType::SuspendHandle: {
assert(SuspendRet->getType()->isPointerTy());

auto ResumeIntrinsic = CGF.CGM.getIntrinsic(llvm::Intrinsic::coro_resume);
Builder.CreateCall(ResumeIntrinsic, SuspendRet);
break;
}
}

// Emit the suspend point.
Expand All @@ -267,7 +338,7 @@ static LValueOrRValue emitSuspendExpression(CodeGenFunction &CGF, CGCoroData &Co
// is marked as 'noexcept', we avoid generating this additional IR.
CXXTryStmt *TryStmt = nullptr;
if (Coro.ExceptionHandler && Kind == AwaitKind::Init &&
ResumeStmtCanThrow(S.getResumeExpr())) {
StmtCanThrow(S.getResumeExpr())) {
Coro.ResumeEHVar =
CGF.CreateTempAlloca(Builder.getInt1Ty(), Prefix + Twine("resume.eh"));
Builder.CreateFlagStore(true, Coro.ResumeEHVar);
Expand Down Expand Up @@ -338,6 +409,69 @@ static QualType getCoroutineSuspendExprReturnType(const ASTContext &Ctx,
}
#endif

llvm::Function *
CodeGenFunction::generateAwaitSuspendWrapper(Twine const &CoroName,
Twine const &SuspendPointName,
CoroutineSuspendExpr const &S) {
std::string FuncName = "__await_suspend_wrapper_";
FuncName += CoroName.str();
FuncName += '_';
FuncName += SuspendPointName.str();

ASTContext &C = getContext();

FunctionArgList args;

ImplicitParamDecl AwaiterDecl(C, C.VoidPtrTy, ImplicitParamKind::Other);
ImplicitParamDecl FrameDecl(C, C.VoidPtrTy, ImplicitParamKind::Other);
QualType ReturnTy = S.getSuspendExpr()->getType();

args.push_back(&AwaiterDecl);
args.push_back(&FrameDecl);

const CGFunctionInfo &FI =
CGM.getTypes().arrangeBuiltinFunctionDeclaration(ReturnTy, args);

llvm::FunctionType *LTy = CGM.getTypes().GetFunctionType(FI);

llvm::Function *Fn = llvm::Function::Create(
LTy, llvm::GlobalValue::PrivateLinkage, FuncName, &CGM.getModule());

Fn->addParamAttr(0, llvm::Attribute::AttrKind::NonNull);
Fn->addParamAttr(0, llvm::Attribute::AttrKind::NoUndef);

Fn->addParamAttr(1, llvm::Attribute::AttrKind::NoUndef);

Fn->setMustProgress();
Fn->addFnAttr(llvm::Attribute::AttrKind::AlwaysInline);

StartFunction(GlobalDecl(), ReturnTy, Fn, FI, args);

// FIXME: add TBAA metadata to the loads
llvm::Value *AwaiterPtr = Builder.CreateLoad(GetAddrOfLocalVar(&AwaiterDecl));
auto AwaiterLValue =
MakeNaturalAlignAddrLValue(AwaiterPtr, AwaiterDecl.getType());

CurAwaitSuspendWrapper.FramePtr =
Builder.CreateLoad(GetAddrOfLocalVar(&FrameDecl));

auto AwaiterBinder = CodeGenFunction::OpaqueValueMappingData::bind(
*this, S.getOpaqueValue(), AwaiterLValue);

auto *SuspendRet = EmitScalarExpr(S.getSuspendExpr());

auto UnbindCommonOnExit =
llvm::make_scope_exit([&] { AwaiterBinder.unbind(*this); });
if (SuspendRet != nullptr) {
Fn->addRetAttr(llvm::Attribute::AttrKind::NoUndef);
Builder.CreateStore(SuspendRet, ReturnValue);
}

CurAwaitSuspendWrapper.FramePtr = nullptr;
FinishFunction();
return Fn;
}

LValue
CodeGenFunction::EmitCoawaitLValue(const CoawaitExpr *E) {
assert(getCoroutineSuspendExprReturnType(getContext(), E)->isReferenceType() &&
Expand Down Expand Up @@ -834,6 +968,11 @@ RValue CodeGenFunction::EmitCoroutineIntrinsic(const CallExpr *E,
if (CurCoro.Data && CurCoro.Data->CoroBegin) {
return RValue::get(CurCoro.Data->CoroBegin);
}

if (CurAwaitSuspendWrapper.FramePtr) {
return RValue::get(CurAwaitSuspendWrapper.FramePtr);
}

CGM.Error(E->getBeginLoc(), "this builtin expect that __builtin_coro_begin "
"has been used earlier in this function");
auto *NullPtr = llvm::ConstantPointerNull::get(Builder.getPtrTy());
Expand Down
19 changes: 19 additions & 0 deletions clang/lib/CodeGen/CodeGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,25 @@ class CodeGenFunction : public CodeGenTypeCache {
return isCoroutine() && CurCoro.InSuspendBlock;
}

// Holds FramePtr for await_suspend wrapper generation,
// so that __builtin_coro_frame call can be lowered
// directly to value of its second argument
struct AwaitSuspendWrapperInfo {
llvm::Value *FramePtr = nullptr;
};
AwaitSuspendWrapperInfo CurAwaitSuspendWrapper;

// Generates wrapper function for `llvm.coro.await.suspend.*` intrinisics.
// It encapsulates SuspendExpr in a function, to separate it's body
// from the main coroutine to avoid miscompilations. Intrinisic
// is lowered to this function call in CoroSplit pass
// Function signature is:
// <type> __await_suspend_wrapper_<name>(ptr %awaiter, ptr %hdl)
// where type is one of (void, i1, ptr)
llvm::Function *generateAwaitSuspendWrapper(Twine const &CoroName,
Twine const &SuspendPointName,
CoroutineSuspendExpr const &S);

/// CurGD - The GlobalDecl for the current function being compiled.
GlobalDecl CurGD;

Expand Down
114 changes: 54 additions & 60 deletions clang/lib/Headers/emmintrin.h

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions clang/lib/Headers/smmintrin.h
Original file line number Diff line number Diff line change
Expand Up @@ -1188,6 +1188,8 @@ static __inline__ int __DEFAULT_FN_ATTRS _mm_testnzc_si128(__m128i __M,
/// Compares each of the corresponding 64-bit values of the 128-bit
/// integer vectors for equality.
///
/// Each comparison yields 0x0 for false, 0xFFFFFFFFFFFFFFFF for true.
///
/// \headerfile <x86intrin.h>
///
/// This intrinsic corresponds to the <c> VPCMPEQQ / PCMPEQQ </c> instruction.
Expand Down Expand Up @@ -2301,6 +2303,8 @@ static __inline__ __m128i __DEFAULT_FN_ATTRS _mm_minpos_epu16(__m128i __V) {
/// integer vectors to determine if the values in the first operand are
/// greater than those in the second operand.
///
/// Each comparison yields 0x0 for false, 0xFFFFFFFFFFFFFFFF for true.
///
/// \headerfile <x86intrin.h>
///
/// This intrinsic corresponds to the <c> VPCMPGTQ / PCMPGTQ </c> instruction.
Expand Down
202 changes: 119 additions & 83 deletions clang/lib/Headers/xmmintrin.h

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions clang/lib/InstallAPI/Frontend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,9 @@ std::unique_ptr<MemoryBuffer> createInputBuffer(InstallAPIContext &Ctx) {
else
OS << "#import ";
if (H.useIncludeName())
OS << "<" << H.getIncludeName() << ">";
OS << "<" << H.getIncludeName() << ">\n";
else
OS << "\"" << H.getPath() << "\"";
OS << "\"" << H.getPath() << "\"\n";

Ctx.addKnownHeader(H);
}
Expand Down
426 changes: 423 additions & 3 deletions clang/lib/InstallAPI/Visitor.cpp

Large diffs are not rendered by default.

259 changes: 151 additions & 108 deletions clang/lib/Interpreter/Interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,15 +280,14 @@ Interpreter::create(std::unique_ptr<CompilerInstance> CI) {
if (Err)
return std::move(Err);

// Add runtime code and set a marker to hide it from user code. Undo will not
// go through that.
auto PTU = Interp->Parse(Runtimes);
if (!PTU)
return PTU.takeError();
Interp->markUserCodeStart();

Interp->ValuePrintingInfo.resize(4);
// FIXME: This is a ugly hack. Undo command checks its availability by looking
// at the size of the PTU list. However we have parsed something in the
// beginning of the REPL so we have to mark them as 'Irrevocable'.
Interp->InitPTUSize = Interp->IncrParser->getPTUs().size();
return std::move(Interp);
}

Expand Down Expand Up @@ -345,6 +344,11 @@ const ASTContext &Interpreter::getASTContext() const {
return getCompilerInstance()->getASTContext();
}

void Interpreter::markUserCodeStart() {
assert(!InitPTUSize && "We only do this once");
InitPTUSize = IncrParser->getPTUs().size();
}

size_t Interpreter::getEffectivePTUSize() const {
std::list<PartialTranslationUnit> &PTUs = IncrParser->getPTUs();
assert(PTUs.size() >= InitPTUSize && "empty PTU list?");
Expand Down Expand Up @@ -507,9 +511,13 @@ static constexpr llvm::StringRef MagicRuntimeInterface[] = {
"__clang_Interpreter_SetValueWithAlloc",
"__clang_Interpreter_SetValueCopyArr", "__ci_newtag"};

bool Interpreter::FindRuntimeInterface() {
static std::unique_ptr<RuntimeInterfaceBuilder>
createInProcessRuntimeInterfaceBuilder(Interpreter &Interp, ASTContext &Ctx,
Sema &S);

std::unique_ptr<RuntimeInterfaceBuilder> Interpreter::FindRuntimeInterface() {
if (llvm::all_of(ValuePrintingInfo, [](Expr *E) { return E != nullptr; }))
return true;
return nullptr;

Sema &S = getCompilerInstance()->getSema();
ASTContext &Ctx = S.getASTContext();
Expand All @@ -528,120 +536,34 @@ bool Interpreter::FindRuntimeInterface() {

if (!LookupInterface(ValuePrintingInfo[NoAlloc],
MagicRuntimeInterface[NoAlloc]))
return false;
return nullptr;
if (!LookupInterface(ValuePrintingInfo[WithAlloc],
MagicRuntimeInterface[WithAlloc]))
return false;
return nullptr;
if (!LookupInterface(ValuePrintingInfo[CopyArray],
MagicRuntimeInterface[CopyArray]))
return false;
return nullptr;
if (!LookupInterface(ValuePrintingInfo[NewTag],
MagicRuntimeInterface[NewTag]))
return false;
return true;
return nullptr;

return createInProcessRuntimeInterfaceBuilder(*this, Ctx, S);
}

namespace {

class RuntimeInterfaceBuilder
: public TypeVisitor<RuntimeInterfaceBuilder, Interpreter::InterfaceKind> {
clang::Interpreter &Interp;
class InterfaceKindVisitor
: public TypeVisitor<InterfaceKindVisitor, Interpreter::InterfaceKind> {
friend class InProcessRuntimeInterfaceBuilder;

ASTContext &Ctx;
Sema &S;
Expr *E;
llvm::SmallVector<Expr *, 3> Args;

public:
RuntimeInterfaceBuilder(clang::Interpreter &In, ASTContext &C, Sema &SemaRef,
Expr *VE, ArrayRef<Expr *> FixedArgs)
: Interp(In), Ctx(C), S(SemaRef), E(VE) {
// The Interpreter* parameter and the out parameter `OutVal`.
for (Expr *E : FixedArgs)
Args.push_back(E);

// Get rid of ExprWithCleanups.
if (auto *EWC = llvm::dyn_cast_if_present<ExprWithCleanups>(E))
E = EWC->getSubExpr();
}

ExprResult getCall() {
QualType Ty = E->getType();
QualType DesugaredTy = Ty.getDesugaredType(Ctx);

// For lvalue struct, we treat it as a reference.
if (DesugaredTy->isRecordType() && E->isLValue()) {
DesugaredTy = Ctx.getLValueReferenceType(DesugaredTy);
Ty = Ctx.getLValueReferenceType(Ty);
}

Expr *TypeArg =
CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)Ty.getAsOpaquePtr());
// The QualType parameter `OpaqueType`, represented as `void*`.
Args.push_back(TypeArg);

// We push the last parameter based on the type of the Expr. Note we need
// special care for rvalue struct.
Interpreter::InterfaceKind Kind = Visit(&*DesugaredTy);
switch (Kind) {
case Interpreter::InterfaceKind::WithAlloc:
case Interpreter::InterfaceKind::CopyArray: {
// __clang_Interpreter_SetValueWithAlloc.
ExprResult AllocCall = S.ActOnCallExpr(
/*Scope=*/nullptr,
Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::WithAlloc],
E->getBeginLoc(), Args, E->getEndLoc());
assert(!AllocCall.isInvalid() && "Can't create runtime interface call!");

TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation());

// Force CodeGen to emit destructor.
if (auto *RD = Ty->getAsCXXRecordDecl()) {
auto *Dtor = S.LookupDestructor(RD);
Dtor->addAttr(UsedAttr::CreateImplicit(Ctx));
Interp.getCompilerInstance()->getASTConsumer().HandleTopLevelDecl(
DeclGroupRef(Dtor));
}

// __clang_Interpreter_SetValueCopyArr.
if (Kind == Interpreter::InterfaceKind::CopyArray) {
const auto *ConstantArrTy =
cast<ConstantArrayType>(DesugaredTy.getTypePtr());
size_t ArrSize = Ctx.getConstantArrayElementCount(ConstantArrTy);
Expr *ArrSizeExpr = IntegerLiteralExpr(Ctx, ArrSize);
Expr *Args[] = {E, AllocCall.get(), ArrSizeExpr};
return S.ActOnCallExpr(
/*Scope *=*/nullptr,
Interp
.getValuePrintingInfo()[Interpreter::InterfaceKind::CopyArray],
SourceLocation(), Args, SourceLocation());
}
Expr *Args[] = {
AllocCall.get(),
Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NewTag]};
ExprResult CXXNewCall = S.BuildCXXNew(
E->getSourceRange(),
/*UseGlobal=*/true, /*PlacementLParen=*/SourceLocation(), Args,
/*PlacementRParen=*/SourceLocation(),
/*TypeIdParens=*/SourceRange(), TSI->getType(), TSI, std::nullopt,
E->getSourceRange(), E);

assert(!CXXNewCall.isInvalid() &&
"Can't create runtime placement new call!");

return S.ActOnFinishFullExpr(CXXNewCall.get(),
/*DiscardedValue=*/false);
}
// __clang_Interpreter_SetValueNoAlloc.
case Interpreter::InterfaceKind::NoAlloc: {
return S.ActOnCallExpr(
/*Scope=*/nullptr,
Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NoAlloc],
E->getBeginLoc(), Args, E->getEndLoc());
}
default:
llvm_unreachable("Unhandled Interpreter::InterfaceKind");
}
}
InterfaceKindVisitor(ASTContext &Ctx, Sema &S, Expr *E)
: Ctx(Ctx), S(S), E(E) {}

Interpreter::InterfaceKind VisitRecordType(const RecordType *Ty) {
return Interpreter::InterfaceKind::WithAlloc;
Expand Down Expand Up @@ -713,8 +635,124 @@ class RuntimeInterfaceBuilder
Args.push_back(CastedExpr.get());
}
};

class InProcessRuntimeInterfaceBuilder : public RuntimeInterfaceBuilder {
Interpreter &Interp;
ASTContext &Ctx;
Sema &S;

public:
InProcessRuntimeInterfaceBuilder(Interpreter &Interp, ASTContext &C, Sema &S)
: Interp(Interp), Ctx(C), S(S) {}

TransformExprFunction *getPrintValueTransformer() override {
return &transformForValuePrinting;
}

private:
static ExprResult transformForValuePrinting(RuntimeInterfaceBuilder *Builder,
Expr *E,
ArrayRef<Expr *> FixedArgs) {
auto *B = static_cast<InProcessRuntimeInterfaceBuilder *>(Builder);

// Get rid of ExprWithCleanups.
if (auto *EWC = llvm::dyn_cast_if_present<ExprWithCleanups>(E))
E = EWC->getSubExpr();

InterfaceKindVisitor Visitor(B->Ctx, B->S, E);

// The Interpreter* parameter and the out parameter `OutVal`.
for (Expr *E : FixedArgs)
Visitor.Args.push_back(E);

QualType Ty = E->getType();
QualType DesugaredTy = Ty.getDesugaredType(B->Ctx);

// For lvalue struct, we treat it as a reference.
if (DesugaredTy->isRecordType() && E->isLValue()) {
DesugaredTy = B->Ctx.getLValueReferenceType(DesugaredTy);
Ty = B->Ctx.getLValueReferenceType(Ty);
}

Expr *TypeArg = CStyleCastPtrExpr(B->S, B->Ctx.VoidPtrTy,
(uintptr_t)Ty.getAsOpaquePtr());
// The QualType parameter `OpaqueType`, represented as `void*`.
Visitor.Args.push_back(TypeArg);

// We push the last parameter based on the type of the Expr. Note we need
// special care for rvalue struct.
Interpreter::InterfaceKind Kind = Visitor.Visit(&*DesugaredTy);
switch (Kind) {
case Interpreter::InterfaceKind::WithAlloc:
case Interpreter::InterfaceKind::CopyArray: {
// __clang_Interpreter_SetValueWithAlloc.
ExprResult AllocCall = B->S.ActOnCallExpr(
/*Scope=*/nullptr,
B->Interp
.getValuePrintingInfo()[Interpreter::InterfaceKind::WithAlloc],
E->getBeginLoc(), Visitor.Args, E->getEndLoc());
assert(!AllocCall.isInvalid() && "Can't create runtime interface call!");

TypeSourceInfo *TSI =
B->Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation());

// Force CodeGen to emit destructor.
if (auto *RD = Ty->getAsCXXRecordDecl()) {
auto *Dtor = B->S.LookupDestructor(RD);
Dtor->addAttr(UsedAttr::CreateImplicit(B->Ctx));
B->Interp.getCompilerInstance()->getASTConsumer().HandleTopLevelDecl(
DeclGroupRef(Dtor));
}

// __clang_Interpreter_SetValueCopyArr.
if (Kind == Interpreter::InterfaceKind::CopyArray) {
const auto *ConstantArrTy =
cast<ConstantArrayType>(DesugaredTy.getTypePtr());
size_t ArrSize = B->Ctx.getConstantArrayElementCount(ConstantArrTy);
Expr *ArrSizeExpr = IntegerLiteralExpr(B->Ctx, ArrSize);
Expr *Args[] = {E, AllocCall.get(), ArrSizeExpr};
return B->S.ActOnCallExpr(
/*Scope *=*/nullptr,
B->Interp
.getValuePrintingInfo()[Interpreter::InterfaceKind::CopyArray],
SourceLocation(), Args, SourceLocation());
}
Expr *Args[] = {
AllocCall.get(),
B->Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NewTag]};
ExprResult CXXNewCall = B->S.BuildCXXNew(
E->getSourceRange(),
/*UseGlobal=*/true, /*PlacementLParen=*/SourceLocation(), Args,
/*PlacementRParen=*/SourceLocation(),
/*TypeIdParens=*/SourceRange(), TSI->getType(), TSI, std::nullopt,
E->getSourceRange(), E);

assert(!CXXNewCall.isInvalid() &&
"Can't create runtime placement new call!");

return B->S.ActOnFinishFullExpr(CXXNewCall.get(),
/*DiscardedValue=*/false);
}
// __clang_Interpreter_SetValueNoAlloc.
case Interpreter::InterfaceKind::NoAlloc: {
return B->S.ActOnCallExpr(
/*Scope=*/nullptr,
B->Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NoAlloc],
E->getBeginLoc(), Visitor.Args, E->getEndLoc());
}
default:
llvm_unreachable("Unhandled Interpreter::InterfaceKind");
}
}
};
} // namespace

static std::unique_ptr<RuntimeInterfaceBuilder>
createInProcessRuntimeInterfaceBuilder(Interpreter &Interp, ASTContext &Ctx,
Sema &S) {
return std::make_unique<InProcessRuntimeInterfaceBuilder>(Interp, Ctx, S);
}

// This synthesizes a call expression to a speciall
// function that is responsible for generating the Value.
// In general, we transform:
Expand All @@ -733,8 +771,13 @@ Expr *Interpreter::SynthesizeExpr(Expr *E) {
Sema &S = getCompilerInstance()->getSema();
ASTContext &Ctx = S.getASTContext();

if (!FindRuntimeInterface())
llvm_unreachable("We can't find the runtime iterface for pretty print!");
if (!RuntimeIB) {
RuntimeIB = FindRuntimeInterface();
AddPrintValueCall = RuntimeIB->getPrintValueTransformer();
}

assert(AddPrintValueCall &&
"We don't have a runtime interface for pretty print!");

// Create parameter `ThisInterp`.
auto *ThisInterp = CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)this);
Expand All @@ -743,9 +786,9 @@ Expr *Interpreter::SynthesizeExpr(Expr *E) {
auto *OutValue = CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)&LastValue);

// Build `__clang_Interpreter_SetValue*` call.
RuntimeInterfaceBuilder Builder(*this, Ctx, S, E, {ThisInterp, OutValue});
ExprResult Result =
AddPrintValueCall(RuntimeIB.get(), E, {ThisInterp, OutValue});

ExprResult Result = Builder.getCall();
// It could fail, like printing an array type in C. (not supported)
if (Result.isInvalid())
return E;
Expand Down
112 changes: 5 additions & 107 deletions clang/lib/Sema/SemaCoroutine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
#include "clang/Sema/Initialization.h"
#include "clang/Sema/Overload.h"
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaInternal.h"
#include "llvm/ADT/SmallSet.h"

Expand Down Expand Up @@ -348,99 +347,15 @@ static Expr *maybeTailCall(Sema &S, QualType RetType, Expr *E,

Expr *JustAddress = AddressExpr.get();

// FIXME: Without optimizations, the temporary result from `await_suspend()`
// may be put on the coroutine frame since the coroutine frame constructor
// will think the temporary variable will escape from the
// `coroutine_handle<>::address()` call. This is problematic since the
// coroutine should be considered to be suspended after it enters
// `await_suspend` so it shouldn't access/update the coroutine frame after
// that.
//
// See https://github.com/llvm/llvm-project/issues/65054 for the report.
//
// The long term solution may wrap the whole logic about `await-suspend`
// into a standalone function. This is similar to the proposed solution
// in tryMarkAwaitSuspendNoInline. See the comments there for details.
//
// The short term solution here is to mark `coroutine_handle<>::address()`
// function as always-inline so that the coroutine frame constructor won't
// think the temporary result is escaped incorrectly.
if (auto *FD = cast<CallExpr>(JustAddress)->getDirectCallee())
if (!FD->hasAttr<AlwaysInlineAttr>() && !FD->hasAttr<NoInlineAttr>())
FD->addAttr(AlwaysInlineAttr::CreateImplicit(S.getASTContext(),
FD->getLocation()));

// Check that the type of AddressExpr is void*
if (!JustAddress->getType().getTypePtr()->isVoidPointerType())
S.Diag(cast<CallExpr>(JustAddress)->getCalleeDecl()->getLocation(),
diag::warn_coroutine_handle_address_invalid_return_type)
<< JustAddress->getType();

// Clean up temporary objects so that they don't live across suspension points
// unnecessarily. We choose to clean up before the call to
// __builtin_coro_resume so that the cleanup code are not inserted in-between
// the resume call and return instruction, which would interfere with the
// musttail call contract.
JustAddress = S.MaybeCreateExprWithCleanups(JustAddress);
return S.BuildBuiltinCallExpr(Loc, Builtin::BI__builtin_coro_resume,
JustAddress);
}

/// The await_suspend call performed by co_await is essentially asynchronous
/// to the execution of the coroutine. Inlining it normally into an unsplit
/// coroutine can cause miscompilation because the coroutine CFG misrepresents
/// the true control flow of the program: things that happen in the
/// await_suspend are not guaranteed to happen prior to the resumption of the
/// coroutine, and things that happen after the resumption of the coroutine
/// (including its exit and the potential deallocation of the coroutine frame)
/// are not guaranteed to happen only after the end of await_suspend.
///
/// See https://github.com/llvm/llvm-project/issues/56301 and
/// https://reviews.llvm.org/D157070 for the example and the full discussion.
///
/// The short-term solution to this problem is to mark the call as uninlinable.
/// But we don't want to do this if the call is known to be trivial, which is
/// very common.
///
/// The long-term solution may introduce patterns like:
///
/// call @llvm.coro.await_suspend(ptr %awaiter, ptr %handle,
/// ptr @awaitSuspendFn)
///
/// Then it is much easier to perform the safety analysis in the middle end.
/// If it is safe to inline the call to awaitSuspend, we can replace it in the
/// CoroEarly pass. Otherwise we could replace it in the CoroSplit pass.
static void tryMarkAwaitSuspendNoInline(Sema &S, OpaqueValueExpr *Awaiter,
CallExpr *AwaitSuspend) {
// The method here to extract the awaiter decl is not precise.
// This is intentional. Since it is hard to perform the analysis in the
// frontend due to the complexity of C++'s type systems.
// And we prefer to perform such analysis in the middle end since it is
// easier to implement and more powerful.
CXXRecordDecl *AwaiterDecl =
Awaiter->getType().getNonReferenceType()->getAsCXXRecordDecl();

if (AwaiterDecl && AwaiterDecl->field_empty())
return;

FunctionDecl *FD = AwaitSuspend->getDirectCallee();

assert(FD);

// If the `await_suspend()` function is marked as `always_inline` explicitly,
// we should give the user the right to control the codegen.
if (FD->hasAttr<NoInlineAttr>() || FD->hasAttr<AlwaysInlineAttr>())
return;

// This is problematic if the user calls the await_suspend standalone. But on
// the on hand, it is not incorrect semantically since inlining is not part
// of the standard. On the other hand, it is relatively rare to call
// the await_suspend function standalone.
//
// And given we've already had the long-term plan, the current workaround
// looks relatively tolerant.
FD->addAttr(
NoInlineAttr::CreateImplicit(S.getASTContext(), FD->getLocation()));
// Clean up temporary objects, because the resulting expression
// will become the body of await_suspend wrapper.
return S.MaybeCreateExprWithCleanups(JustAddress);
}

/// Build calls to await_ready, await_suspend, and await_resume for a co_await
Expand Down Expand Up @@ -514,10 +429,6 @@ static ReadySuspendResumeResult buildCoawaitCalls(Sema &S, VarDecl *CoroPromise,
// type Z.
QualType RetType = AwaitSuspend->getCallReturnType(S.Context);

// We need to mark await_suspend as noinline temporarily. See the comment
// of tryMarkAwaitSuspendNoInline for details.
tryMarkAwaitSuspendNoInline(S, Operand, AwaitSuspend);

// Support for coroutine_handle returning await_suspend.
if (Expr *TailCallSuspend =
maybeTailCall(S, RetType, AwaitSuspend, Loc))
Expand Down Expand Up @@ -1379,21 +1290,8 @@ bool CoroutineStmtBuilder::makeReturnOnAllocFailure() {
static bool collectPlacementArgs(Sema &S, FunctionDecl &FD, SourceLocation Loc,
SmallVectorImpl<Expr *> &PlacementArgs) {
if (auto *MD = dyn_cast<CXXMethodDecl>(&FD)) {
if (MD->isImplicitObjectMemberFunction()) {
ExprResult ThisExpr{};

if (isLambdaCallOperator(MD) && !MD->isStatic()) {
Qualifiers ThisQuals = MD->getMethodQualifiers();
CXXRecordDecl *Record = MD->getParent();

Sema::CXXThisScopeRAII ThisScope(S, Record, ThisQuals,
Record != nullptr);

ThisExpr = S.ActOnCXXThis(Loc, /*ThisRefersToClosureObject=*/true);
} else {
ThisExpr = S.ActOnCXXThis(Loc);
}

if (MD->isImplicitObjectMemberFunction() && !isLambdaCallOperator(MD)) {
ExprResult ThisExpr = S.ActOnCXXThis(Loc);
if (ThisExpr.isInvalid())
return false;
ThisExpr = S.CreateBuiltinUnaryOp(Loc, UO_Deref, ThisExpr.get());
Expand Down
16 changes: 5 additions & 11 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1414,8 +1414,7 @@ bool Sema::CheckCXXThisCapture(SourceLocation Loc, const bool Explicit,
return false;
}

ExprResult Sema::ActOnCXXThis(SourceLocation Loc,
bool ThisRefersToClosureObject) {
ExprResult Sema::ActOnCXXThis(SourceLocation Loc) {
/// C++ 9.3.2: In the body of a non-static member function, the keyword this
/// is a non-lvalue expression whose value is the address of the object for
/// which the function is called.
Expand All @@ -1435,18 +1434,13 @@ ExprResult Sema::ActOnCXXThis(SourceLocation Loc,
return Diag(Loc, diag::err_invalid_this_use) << 0;
}

return BuildCXXThisExpr(Loc, ThisTy, /*IsImplicit=*/false,
ThisRefersToClosureObject);
return BuildCXXThisExpr(Loc, ThisTy, /*IsImplicit=*/false);
}

Expr *Sema::BuildCXXThisExpr(SourceLocation Loc, QualType Type, bool IsImplicit,
bool ThisRefersToClosureObject) {
Expr *Sema::BuildCXXThisExpr(SourceLocation Loc, QualType Type,
bool IsImplicit) {
auto *This = CXXThisExpr::Create(Context, Loc, Type, IsImplicit);

if (!ThisRefersToClosureObject) {
MarkThisReferenced(This);
}

MarkThisReferenced(This);
return This;
}

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9762,7 +9762,7 @@ void ASTReader::finishPendingActions() {
!NonConstDefn->isLateTemplateParsed() &&
// We only perform ODR checks for decls not in the explicit
// global module fragment.
!shouldSkipCheckingODR(FD) &&
!FD->shouldSkipCheckingODR() &&
FD->getODRHash() != NonConstDefn->getODRHash()) {
if (!isa<CXXMethodDecl>(FD)) {
PendingFunctionOdrMergeFailures[FD].push_back(NonConstDefn);
Expand Down
8 changes: 4 additions & 4 deletions clang/lib/Serialization/ASTReaderDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -832,7 +832,7 @@ void ASTDeclReader::VisitEnumDecl(EnumDecl *ED) {
Reader.mergeDefinitionVisibility(OldDef, ED);
// We don't want to check the ODR hash value for declarations from global
// module fragment.
if (!shouldSkipCheckingODR(ED) &&
if (!ED->shouldSkipCheckingODR() &&
OldDef->getODRHash() != ED->getODRHash())
Reader.PendingEnumOdrMergeFailures[OldDef].push_back(ED);
} else {
Expand Down Expand Up @@ -874,7 +874,7 @@ void ASTDeclReader::VisitRecordDecl(RecordDecl *RD) {
VisitRecordDeclImpl(RD);
// We should only reach here if we're in C/Objective-C. There is no
// global module fragment.
assert(!shouldSkipCheckingODR(RD));
assert(!RD->shouldSkipCheckingODR());
RD->setODRHash(Record.readInt());

// Maintain the invariant of a redeclaration chain containing only
Expand Down Expand Up @@ -2152,7 +2152,7 @@ void ASTDeclReader::MergeDefinitionData(
}

// We don't want to check ODR for decls in the global module fragment.
if (shouldSkipCheckingODR(MergeDD.Definition))
if (MergeDD.Definition->shouldSkipCheckingODR())
return;

if (D->getODRHash() != MergeDD.ODRHash) {
Expand Down Expand Up @@ -3526,7 +3526,7 @@ ASTDeclReader::FindExistingResult ASTDeclReader::findExisting(NamedDecl *D) {
// same template specialization into the same CXXRecordDecl.
auto MergedDCIt = Reader.MergedDeclContexts.find(D->getLexicalDeclContext());
if (MergedDCIt != Reader.MergedDeclContexts.end() &&
!shouldSkipCheckingODR(D) && MergedDCIt->second == D->getDeclContext())
!D->shouldSkipCheckingODR() && MergedDCIt->second == D->getDeclContext())
Reader.PendingOdrMergeChecks.push_back(D);

return FindExistingResult(Reader, D, /*Existing=*/nullptr,
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Serialization/ASTWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6060,7 +6060,7 @@ void ASTRecordWriter::AddCXXDefinitionData(const CXXRecordDecl *D) {

BitsPacker DefinitionBits;

bool ShouldSkipCheckingODR = shouldSkipCheckingODR(D);
bool ShouldSkipCheckingODR = D->shouldSkipCheckingODR();
DefinitionBits.addBit(ShouldSkipCheckingODR);

#define FIELD(Name, Width, Merge) \
Expand Down
8 changes: 4 additions & 4 deletions clang/lib/Serialization/ASTWriterDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,7 @@ void ASTDeclWriter::VisitEnumDecl(EnumDecl *D) {
BitsPacker EnumDeclBits;
EnumDeclBits.addBits(D->getNumPositiveBits(), /*BitWidth=*/8);
EnumDeclBits.addBits(D->getNumNegativeBits(), /*BitWidth=*/8);
bool ShouldSkipCheckingODR = shouldSkipCheckingODR(D);
bool ShouldSkipCheckingODR = D->shouldSkipCheckingODR();
EnumDeclBits.addBit(ShouldSkipCheckingODR);
EnumDeclBits.addBit(D->isScoped());
EnumDeclBits.addBit(D->isScopedUsingClassTag());
Expand All @@ -545,7 +545,7 @@ void ASTDeclWriter::VisitEnumDecl(EnumDecl *D) {
!D->isTopLevelDeclInObjCContainer() &&
!CXXRecordDecl::classofKind(D->getKind()) &&
!D->getIntegerTypeSourceInfo() && !D->getMemberSpecializationInfo() &&
!needsAnonymousDeclarationNumber(D) && !shouldSkipCheckingODR(D) &&
!needsAnonymousDeclarationNumber(D) && !D->shouldSkipCheckingODR() &&
D->getDeclName().getNameKind() == DeclarationName::Identifier)
AbbrevToUse = Writer.getDeclEnumAbbrev();

Expand Down Expand Up @@ -711,7 +711,7 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
// FIXME: stable encoding
FunctionDeclBits.addBits(llvm::to_underlying(D->getLinkageInternal()), 3);
FunctionDeclBits.addBits((uint32_t)D->getStorageClass(), /*BitWidth=*/3);
bool ShouldSkipCheckingODR = shouldSkipCheckingODR(D);
bool ShouldSkipCheckingODR = D->shouldSkipCheckingODR();
FunctionDeclBits.addBit(ShouldSkipCheckingODR);
FunctionDeclBits.addBit(D->isInlineSpecified());
FunctionDeclBits.addBit(D->isInlined());
Expand Down Expand Up @@ -1545,7 +1545,7 @@ void ASTDeclWriter::VisitCXXMethodDecl(CXXMethodDecl *D) {
D->getFirstDecl() == D->getMostRecentDecl() && !D->isInvalidDecl() &&
!D->hasAttrs() && !D->isTopLevelDeclInObjCContainer() &&
D->getDeclName().getNameKind() == DeclarationName::Identifier &&
!shouldSkipCheckingODR(D) && !D->hasExtInfo() &&
!D->shouldSkipCheckingODR() && !D->hasExtInfo() &&
!D->isExplicitlyDefaulted()) {
if (D->getTemplatedKind() == FunctionDecl::TK_NonTemplate ||
D->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate ||
Expand Down
29 changes: 29 additions & 0 deletions clang/test/AST/Interp/builtin-functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,24 @@ namespace popcount {
static_assert(__builtin_popcountl(0) == 0, "");
static_assert(__builtin_popcountll(~0ull) == __CHAR_BIT__ * sizeof(unsigned long long), "");
static_assert(__builtin_popcountll(0) == 0, "");
static_assert(__builtin_popcountg((unsigned char)~0) == __CHAR_BIT__ * sizeof(unsigned char), "");
static_assert(__builtin_popcountg((unsigned char)0) == 0, "");
static_assert(__builtin_popcountg((unsigned short)~0) == __CHAR_BIT__ * sizeof(unsigned short), "");
static_assert(__builtin_popcountg((unsigned short)0) == 0, "");
static_assert(__builtin_popcountg(~0u) == __CHAR_BIT__ * sizeof(unsigned int), "");
static_assert(__builtin_popcountg(0u) == 0, "");
static_assert(__builtin_popcountg(~0ul) == __CHAR_BIT__ * sizeof(unsigned long), "");
static_assert(__builtin_popcountg(0ul) == 0, "");
static_assert(__builtin_popcountg(~0ull) == __CHAR_BIT__ * sizeof(unsigned long long), "");
static_assert(__builtin_popcountg(0ull) == 0, "");
#ifdef __SIZEOF_INT128__
static_assert(__builtin_popcountg(~(unsigned __int128)0) == __CHAR_BIT__ * sizeof(unsigned __int128), "");
static_assert(__builtin_popcountg((unsigned __int128)0) == 0, "");
#endif
#ifndef __AVR__
static_assert(__builtin_popcountg(~(unsigned _BitInt(128))0) == __CHAR_BIT__ * sizeof(unsigned _BitInt(128)), "");
static_assert(__builtin_popcountg((unsigned _BitInt(128))0) == 0, "");
#endif

/// From test/Sema/constant-builtins-2.c
char popcount1[__builtin_popcount(0) == 0 ? 1 : -1];
Expand All @@ -280,6 +298,17 @@ namespace popcount {
char popcount8[__builtin_popcountll(0LL) == 0 ? 1 : -1];
char popcount9[__builtin_popcountll(0xF0F0LL) == 8 ? 1 : -1];
char popcount10[__builtin_popcountll(~0LL) == BITSIZE(long long) ? 1 : -1];
char popcount11[__builtin_popcountg(0U) == 0 ? 1 : -1];
char popcount12[__builtin_popcountg(0xF0F0U) == 8 ? 1 : -1];
char popcount13[__builtin_popcountg(~0U) == BITSIZE(int) ? 1 : -1];
char popcount14[__builtin_popcountg(~0UL) == BITSIZE(long) ? 1 : -1];
char popcount15[__builtin_popcountg(~0ULL) == BITSIZE(long long) ? 1 : -1];
#ifdef __SIZEOF_INT128__
char popcount16[__builtin_popcountg(~(unsigned __int128)0) == BITSIZE(__int128) ? 1 : -1];
#endif
#ifndef __AVR__
char popcount17[__builtin_popcountg(~(unsigned _BitInt(128))0) == BITSIZE(_BitInt(128)) ? 1 : -1];
#endif
}

namespace parity {
Expand Down
10 changes: 2 additions & 8 deletions clang/test/AST/coroutine-locals-cleanup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,7 @@ Task bar() {
// CHECK: ExprWithCleanups {{.*}} 'bool'
// CHECK-NEXT: CXXMemberCallExpr {{.*}} 'bool'
// CHECK-NEXT: MemberExpr {{.*}} .await_ready
// CHECK: CallExpr {{.*}} 'void'
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(void *)'
// CHECK-NEXT: DeclRefExpr {{.*}} '__builtin_coro_resume' 'void (void *)'
// CHECK-NEXT: ExprWithCleanups {{.*}} 'void *'
// CHECK: ExprWithCleanups {{.*}} 'void *'

// CHECK: CaseStmt
// CHECK: ExprWithCleanups {{.*}} 'void'
Expand All @@ -103,7 +100,4 @@ Task bar() {
// CHECK: ExprWithCleanups {{.*}} 'bool'
// CHECK-NEXT: CXXMemberCallExpr {{.*}} 'bool'
// CHECK-NEXT: MemberExpr {{.*}} .await_ready
// CHECK: CallExpr {{.*}} 'void'
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(void *)'
// CHECK-NEXT: DeclRefExpr {{.*}} '__builtin_coro_resume' 'void (void *)'
// CHECK-NEXT: ExprWithCleanups {{.*}} 'void *'
// CHECK: ExprWithCleanups {{.*}} 'void *'
2 changes: 1 addition & 1 deletion clang/test/CodeGenCoroutines/coro-always-inline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ struct coroutine_traits {
// CHECK-LABEL: @_Z3foov
// CHECK-LABEL: entry:
// CHECK: %ref.tmp.reload.addr = getelementptr
// CHECK: %ref.tmp4.reload.addr = getelementptr
// CHECK: %ref.tmp3.reload.addr = getelementptr
void foo() { co_return; }

// Check that bar is not inlined even it's marked as always_inline.
Expand Down
79 changes: 62 additions & 17 deletions clang/test/CodeGenCoroutines/coro-await.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,13 @@ extern "C" void f0() {
// CHECK: [[SUSPEND_BB]]:
// CHECK: %[[SUSPEND_ID:.+]] = call token @llvm.coro.save(
// ---------------------------
// Build the coroutine handle and pass it to await_suspend
// Call coro.await.suspend
// ---------------------------
// CHECK: call ptr @_ZNSt16coroutine_handleINSt16coroutine_traitsIJvEE12promise_typeEE12from_addressEPv(ptr %[[FRAME]])
// ... many lines of code to coerce coroutine_handle into an ptr scalar
// CHECK: %[[CH:.+]] = load ptr, ptr %{{.+}}
// CHECK: call void @_ZN14suspend_always13await_suspendESt16coroutine_handleIvE(ptr {{[^,]*}} %[[AWAITABLE]], ptr %[[CH]])
// CHECK-NEXT: call void @llvm.coro.await.suspend.void(ptr %[[AWAITABLE]], ptr %[[FRAME]], ptr @__await_suspend_wrapper_f0_await)
// -------------------------
// Generate a suspend point:
// -------------------------
// CHECK: %[[OUTCOME:.+]] = call i8 @llvm.coro.suspend(token %[[SUSPEND_ID]], i1 false)
// CHECK-NEXT: %[[OUTCOME:.+]] = call i8 @llvm.coro.suspend(token %[[SUSPEND_ID]], i1 false)
// CHECK: switch i8 %[[OUTCOME]], label %[[RET_BB:.+]] [
// CHECK: i8 0, label %[[READY_BB]]
// CHECK: i8 1, label %[[CLEANUP_BB:.+]]
Expand All @@ -101,6 +98,17 @@ extern "C" void f0() {
// CHECK-NEXT: call zeroext i1 @_ZN10final_susp11await_readyEv(ptr
// CHECK: %[[FINALSP_ID:.+]] = call token @llvm.coro.save(
// CHECK: call i8 @llvm.coro.suspend(token %[[FINALSP_ID]], i1 true)

// Await suspend wrapper
// CHECK: define{{.*}} @__await_suspend_wrapper_f0_await(ptr {{[^,]*}} %[[AWAITABLE_ARG:.+]], ptr {{[^,]*}} %[[FRAME_ARG:.+]])
// CHECK: store ptr %[[AWAITABLE_ARG]], ptr %[[AWAITABLE_TMP:.+]],
// CHECK: store ptr %[[FRAME_ARG]], ptr %[[FRAME_TMP:.+]],
// CHECK: %[[AWAITABLE:.+]] = load ptr, ptr %[[AWAITABLE_TMP]]
// CHECK: %[[FRAME:.+]] = load ptr, ptr %[[FRAME_TMP]]
// CHECK: call ptr @_ZNSt16coroutine_handleINSt16coroutine_traitsIJvEE12promise_typeEE12from_addressEPv(ptr %[[FRAME]])
// ... many lines of code to coerce coroutine_handle into an ptr scalar
// CHECK: %[[CH:.+]] = load ptr, ptr %{{.+}}
// CHECK: call void @_ZN14suspend_always13await_suspendESt16coroutine_handleIvE(ptr {{[^,]*}} %[[AWAITABLE]], ptr %[[CH]])
}

struct suspend_maybe {
Expand Down Expand Up @@ -131,20 +139,17 @@ extern "C" void f1(int) {

// See if we need to suspend:
// --------------------------
// CHECK: %[[READY:.+]] = call zeroext i1 @_ZN13suspend_maybe11await_readyEv(ptr {{[^,]*}} %[[AWAITABLE]])
// CHECK: %[[READY:.+]] = call zeroext i1 @_ZN13suspend_maybe11await_readyEv(ptr {{[^,]*}} %[[AWAITABLE:.+]])
// CHECK: br i1 %[[READY]], label %[[READY_BB:.+]], label %[[SUSPEND_BB:.+]]

// If we are suspending:
// ---------------------
// CHECK: [[SUSPEND_BB]]:
// CHECK: %[[SUSPEND_ID:.+]] = call token @llvm.coro.save(
// ---------------------------
// Build the coroutine handle and pass it to await_suspend
// Call coro.await.suspend
// ---------------------------
// CHECK: call ptr @_ZNSt16coroutine_handleINSt16coroutine_traitsIJviEE12promise_typeEE12from_addressEPv(ptr %[[FRAME]])
// ... many lines of code to coerce coroutine_handle into an ptr scalar
// CHECK: %[[CH:.+]] = load ptr, ptr %{{.+}}
// CHECK: %[[YES:.+]] = call zeroext i1 @_ZN13suspend_maybe13await_suspendESt16coroutine_handleIvE(ptr {{[^,]*}} %[[AWAITABLE]], ptr %[[CH]])
// CHECK-NEXT: %[[YES:.+]] = call i1 @llvm.coro.await.suspend.bool(ptr %[[AWAITABLE]], ptr %[[FRAME]], ptr @__await_suspend_wrapper_f1_yield)
// -------------------------------------------
// See if await_suspend decided not to suspend
// -------------------------------------------
Expand All @@ -155,6 +160,18 @@ extern "C" void f1(int) {

// CHECK: [[READY_BB]]:
// CHECK: call void @_ZN13suspend_maybe12await_resumeEv(ptr {{[^,]*}} %[[AWAITABLE]])

// Await suspend wrapper
// CHECK: define {{.*}} i1 @__await_suspend_wrapper_f1_yield(ptr {{[^,]*}} %[[AWAITABLE_ARG:.+]], ptr {{[^,]*}} %[[FRAME_ARG:.+]])
// CHECK: store ptr %[[AWAITABLE_ARG]], ptr %[[AWAITABLE_TMP:.+]],
// CHECK: store ptr %[[FRAME_ARG]], ptr %[[FRAME_TMP:.+]],
// CHECK: %[[AWAITABLE:.+]] = load ptr, ptr %[[AWAITABLE_TMP]]
// CHECK: %[[FRAME:.+]] = load ptr, ptr %[[FRAME_TMP]]
// CHECK: call ptr @_ZNSt16coroutine_handleINSt16coroutine_traitsIJviEE12promise_typeEE12from_addressEPv(ptr %[[FRAME]])
// ... many lines of code to coerce coroutine_handle into an ptr scalar
// CHECK: %[[CH:.+]] = load ptr, ptr %{{.+}}
// CHECK: %[[YES:.+]] = call zeroext i1 @_ZN13suspend_maybe13await_suspendESt16coroutine_handleIvE(ptr {{[^,]*}} %[[AWAITABLE]], ptr %[[CH]])
// CHECK-NEXT: ret i1 %[[YES]]
}

struct ComplexAwaiter {
Expand Down Expand Up @@ -340,11 +357,39 @@ struct TailCallAwait {

// CHECK-LABEL: @TestTailcall(
extern "C" void TestTailcall() {
// CHECK: %[[PROMISE:.+]] = alloca %"struct.std::coroutine_traits<void>::promise_type"
// CHECK: %[[FRAME:.+]] = call ptr @llvm.coro.begin(
co_await TailCallAwait{};
// CHECK: %[[READY:.+]] = call zeroext i1 @_ZN13TailCallAwait11await_readyEv(ptr {{[^,]*}} %[[AWAITABLE:.+]])
// CHECK: br i1 %[[READY]], label %[[READY_BB:.+]], label %[[SUSPEND_BB:.+]]

// CHECK: %[[RESULT:.+]] = call ptr @_ZN13TailCallAwait13await_suspendESt16coroutine_handleIvE(ptr
// CHECK: %[[COERCE:.+]] = getelementptr inbounds %"struct.std::coroutine_handle", ptr %[[TMP:.+]], i32 0, i32 0
// CHECK: store ptr %[[RESULT]], ptr %[[COERCE]]
// CHECK: %[[ADDR:.+]] = call ptr @_ZNSt16coroutine_handleIvE7addressEv(ptr {{[^,]*}} %[[TMP]])
// CHECK: call void @llvm.coro.resume(ptr %[[ADDR]])
// If we are suspending:
// ---------------------
// CHECK: [[SUSPEND_BB]]:
// CHECK: %[[SUSPEND_ID:.+]] = call token @llvm.coro.save(
// ---------------------------
// Call coro.await.suspend
// ---------------------------
// CHECK-NEXT: %[[RESUMED:.+]] = call ptr @llvm.coro.await.suspend.handle(ptr %[[AWAITABLE]], ptr %[[FRAME]], ptr @__await_suspend_wrapper_TestTailcall_await)
// CHECK-NEXT: call void @llvm.coro.resume(ptr %[[RESUMED]])
// CHECK-NEXT: %[[OUTCOME:.+]] = call i8 @llvm.coro.suspend(token %[[SUSPEND_ID]], i1 false)
// CHECK-NEXT: switch i8 %[[OUTCOME]], label %[[RET_BB:.+]] [
// CHECK-NEXT: i8 0, label %[[READY_BB]]
// CHECK-NEXT: i8 1, label %{{.+}}
// CHECK-NEXT: ]

// Await suspend wrapper
// CHECK: define {{.*}} ptr @__await_suspend_wrapper_TestTailcall_await(ptr {{[^,]*}} %[[AWAITABLE_ARG:.+]], ptr {{[^,]*}} %[[FRAME_ARG:.+]])
// CHECK: store ptr %[[AWAITABLE_ARG]], ptr %[[AWAITABLE_TMP:.+]],
// CHECK: store ptr %[[FRAME_ARG]], ptr %[[FRAME_TMP:.+]],
// CHECK: %[[AWAITABLE:.+]] = load ptr, ptr %[[AWAITABLE_TMP]]
// CHECK: %[[FRAME:.+]] = load ptr, ptr %[[FRAME_TMP]]
// CHECK: call ptr @_ZNSt16coroutine_handleINSt16coroutine_traitsIJvEE12promise_typeEE12from_addressEPv(ptr %[[FRAME]])
// ... many lines of code to coerce coroutine_handle into an ptr scalar
// CHECK: %[[CH:.+]] = load ptr, ptr %{{.+}}
// CHECK-NEXT: %[[RESULT:.+]] = call ptr @_ZN13TailCallAwait13await_suspendESt16coroutine_handleIvE(ptr {{[^,]*}} %[[AWAITABLE]], ptr %[[CH]])
// CHECK-NEXT: %[[COERCE:.+]] = getelementptr inbounds %"struct.std::coroutine_handle", ptr %[[TMP:.+]], i32 0, i32 0
// CHECK-NEXT: store ptr %[[RESULT]], ptr %[[COERCE]]
// CHECK-NEXT: %[[ADDR:.+]] = call ptr @_ZNSt16coroutine_handleIvE7addressEv(ptr {{[^,]*}} %[[TMP]])
// CHECK-NEXT: ret ptr %[[ADDR]]
}
168 changes: 0 additions & 168 deletions clang/test/CodeGenCoroutines/coro-awaiter-noinline-suspend.cpp

This file was deleted.

12 changes: 12 additions & 0 deletions clang/test/CodeGenCoroutines/coro-dwarf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,15 @@ void f_coro(int val, MoveOnly moParam, MoveAndCopy mcParam) {
// CHECK: !{{[0-9]+}} = !DILocalVariable(name: "moParam", arg: 2, scope: ![[SP]], file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
// CHECK: !{{[0-9]+}} = !DILocalVariable(name: "mcParam", arg: 3, scope: ![[SP]], file: !{{[0-9]+}}, line: {{[0-9]+}}, type: !{{[0-9]+}})
// CHECK: !{{[0-9]+}} = !DILocalVariable(name: "__promise",

// CHECK: !{{[0-9]+}} = distinct !DISubprogram(linkageName: "__await_suspend_wrapper__Z6f_coroi8MoveOnly11MoveAndCopy_init"
// CHECK-NEXT: !{{[0-9]+}} = !DIFile
// CHECK-NEXT: !{{[0-9]+}} = !DISubroutineType
// CHECK-NEXT: !{{[0-9]+}} = !DILocalVariable(arg: 1,
// CHECK-NEXT: !{{[0-9]+}} = !DILocation
// CHECK-NEXT: !{{[0-9]+}} = !DILocalVariable(arg: 2,

// CHECK: !{{[0-9]+}} = distinct !DISubprogram(linkageName: "__await_suspend_wrapper__Z6f_coroi8MoveOnly11MoveAndCopy_final"
// CHECK-NEXT: !{{[0-9]+}} = !DILocalVariable(arg: 1,
// CHECK-NEXT: !{{[0-9]+}} = !DILocation
// CHECK-NEXT: !{{[0-9]+}} = !DILocalVariable(arg: 2,
2 changes: 1 addition & 1 deletion clang/test/CodeGenCoroutines/coro-function-try-block.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ task f() try {
}

// CHECK-LABEL: define{{.*}} void @_Z1fv(
// CHECK: call void @_ZNSt13suspend_never13await_suspendESt16coroutine_handleIvE(
// CHECK: call void @llvm.coro.await.suspend.void(
// CHECK: call void @_ZN4task12promise_type11return_voidEv(
7 changes: 1 addition & 6 deletions clang/test/CodeGenCoroutines/coro-symmetric-transfer-01.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,5 @@ detached_task foo() {
// check that the lifetime of the coroutine handle used to obtain the address is contained within single basic block, and hence does not live across suspension points.
// CHECK-LABEL: final.suspend:
// CHECK: %{{.+}} = call token @llvm.coro.save(ptr null)
// CHECK: call void @llvm.lifetime.start.p0(i64 8, ptr %[[HDL:.+]])
// CHECK: %[[CALL:.+]] = call ptr @_ZN13detached_task12promise_type13final_awaiter13await_suspendESt16coroutine_handleIS0_E(
// CHECK: %[[HDL_CAST2:.+]] = getelementptr inbounds %"struct.std::coroutine_handle.0", ptr %[[HDL]], i32 0, i32 0
// CHECK: store ptr %[[CALL]], ptr %[[HDL_CAST2]], align 8
// CHECK: %[[HDL_TRANSFER:.+]] = call noundef ptr @_ZNKSt16coroutine_handleIvE7addressEv(ptr noundef {{.*}}%[[HDL]])
// CHECK: call void @llvm.lifetime.end.p0(i64 8, ptr %[[HDL]])
// CHECK: %[[HDL_TRANSFER:.+]] = call ptr @llvm.coro.await.suspend.handle
// CHECK: call void @llvm.coro.resume(ptr %[[HDL_TRANSFER]])
12 changes: 4 additions & 8 deletions clang/test/CodeGenCoroutines/coro-symmetric-transfer-02.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,8 @@ Task bar() {
// CHECK: br i1 %{{.+}}, label %[[CASE1_AWAIT_READY:.+]], label %[[CASE1_AWAIT_SUSPEND:.+]]
// CHECK: [[CASE1_AWAIT_SUSPEND]]:
// CHECK-NEXT: %{{.+}} = call token @llvm.coro.save(ptr null)
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr %[[TMP1:.+]])

// CHECK: call void @llvm.lifetime.end.p0(i64 8, ptr %[[TMP1]])
// CHECK-NEXT: call void @llvm.coro.resume
// CHECK-NEXT: %[[HANDLE1_PTR:.+]] = call ptr @llvm.coro.await.suspend.handle
// CHECK-NEXT: call void @llvm.coro.resume(ptr %[[HANDLE1_PTR]])
// CHECK-NEXT: %{{.+}} = call i8 @llvm.coro.suspend
// CHECK-NEXT: switch i8 %{{.+}}, label %coro.ret [
// CHECK-NEXT: i8 0, label %[[CASE1_AWAIT_READY]]
Expand All @@ -106,10 +104,8 @@ Task bar() {
// CHECK: br i1 %{{.+}}, label %[[CASE2_AWAIT_READY:.+]], label %[[CASE2_AWAIT_SUSPEND:.+]]
// CHECK: [[CASE2_AWAIT_SUSPEND]]:
// CHECK-NEXT: %{{.+}} = call token @llvm.coro.save(ptr null)
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr %[[TMP2:.+]])

// CHECK: call void @llvm.lifetime.end.p0(i64 8, ptr %[[TMP2]])
// CHECK-NEXT: call void @llvm.coro.resume
// CHECK-NEXT: %[[HANDLE2_PTR:.+]] = call ptr @llvm.coro.await.suspend.handle
// CHECK-NEXT: call void @llvm.coro.resume(ptr %[[HANDLE2_PTR]])
// CHECK-NEXT: %{{.+}} = call i8 @llvm.coro.suspend
// CHECK-NEXT: switch i8 %{{.+}}, label %coro.ret [
// CHECK-NEXT: i8 0, label %[[CASE2_AWAIT_READY]]
Expand Down
4 changes: 4 additions & 0 deletions clang/test/CodeGenCoroutines/pr56329.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,5 +115,9 @@ Task Outer() {
// CHECK-NOT: _exit
// CHECK: musttail call
// CHECK: musttail call
// CHECK: musttail call
// CHECK-NEXT: ret void
// CHECK-EMPTY:
// CHECK-NEXT: unreachable:
// CHECK-NEXT: unreachable
// CHECK-NEXT: }
7 changes: 3 additions & 4 deletions clang/test/CodeGenCoroutines/pr59181.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,8 @@ void foo() {
// CHECK-NEXT: load i8
// CHECK-NEXT: trunc
// CHECK-NEXT: store i1 false
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[REF:%ref.tmp[0-9]+]])

// CHECK: await.suspend:{{.*}}
// CHECK-NOT: call void @llvm.lifetime.start.p0(i64 8, ptr [[REF]])
// CHECK: call void @_ZZN4Task12promise_type15await_transformES_EN10Suspension13await_suspendESt16coroutine_handleIvE
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[REF]])
// CHECK-NOT: call void @llvm.lifetime
// CHECK: call void @llvm.coro.await.suspend.void(
// CHECK-NEXT: %{{[0-9]+}} = call i8 @llvm.coro.suspend(
9 changes: 1 addition & 8 deletions clang/test/CodeGenCoroutines/pr65054.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 \
// RUN: -O0 -disable-llvm-passes -emit-llvm %s -o - \
// RUN: | FileCheck %s --check-prefix=FRONTEND

// The output of O0 is highly redundant and hard to test. Also it is not good
// limit the output of O0. So we test the optimized output from O0. The idea
// is the optimizations shouldn't change the semantics of the program.
Expand Down Expand Up @@ -51,10 +47,7 @@ MyTask FooBar() {
}
}

// FRONTEND: define{{.*}}@_ZNKSt16coroutine_handleIvE7addressEv{{.*}}#[[address_attr:[0-9]+]]
// FRONTEND: attributes #[[address_attr]] = {{.*}}alwaysinline

// CHECK-O0: define{{.*}}@_Z6FooBarv.resume
// CHECK-O0: call{{.*}}@_ZN7Awaiter13await_suspendESt16coroutine_handleIvE
// CHECK-O0: call{{.*}}@__await_suspend_wrapper__Z6FooBarv_await(
// CHECK-O0-NOT: store
// CHECK-O0: ret void
530 changes: 530 additions & 0 deletions clang/test/InstallAPI/cpp.test

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions clang/test/Misc/warning-wall.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ CHECK-NEXT: -Wreorder-ctor
CHECK-NEXT: -Wreorder-init-list
CHECK-NEXT: -Wreturn-type
CHECK-NEXT: -Wreturn-type-c-linkage
CHECK-NEXT: -Wreturn-mismatch
CHECK-NEXT: -Wself-assign
CHECK-NEXT: -Wself-assign-overloaded
CHECK-NEXT: -Wself-assign-field
Expand Down
67 changes: 67 additions & 0 deletions clang/test/Modules/hashing-decls-in-exprs-from-gmf.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// RUN: rm -rf %t
// RUN: mkdir -p %t
// RUN: split-file %s %t
//
// RUN: %clang_cc1 -std=c++20 -fskip-odr-check-in-gmf %t/A.cppm -emit-module-interface -o %t/A.pcm
// RUN: %clang_cc1 -std=c++20 -fskip-odr-check-in-gmf %t/B.cppm -emit-module-interface -o %t/B.pcm
// RUN: %clang_cc1 -std=c++20 -fskip-odr-check-in-gmf %t/test.cpp -fprebuilt-module-path=%t -fsyntax-only -verify

//--- header.h
#pragma once
template <class _Tp>
class Optional {};

template <class _Tp>
concept C = requires(const _Tp& __t) {
[]<class _Up>(const Optional<_Up>&) {}(__t);
};

//--- func.h
#include "header.h"
template <C T>
void func() {}

//--- duplicated_func.h
#include "header.h"
template <C T>
void duplicated_func() {}

//--- test_func.h
#include "func.h"

void test_func() {
func<Optional<int>>();
}

//--- test_duplicated_func.h
#include "duplicated_func.h"

void test_duplicated_func() {
duplicated_func<Optional<int>>();
}

//--- A.cppm
module;
#include "header.h"
#include "test_duplicated_func.h"
export module A;
export using ::test_duplicated_func;

//--- B.cppm
module;
#include "header.h"
#include "test_func.h"
#include "test_duplicated_func.h"
export module B;
export using ::test_func;
export using ::test_duplicated_func;

//--- test.cpp
// expected-no-diagnostics
import A;
import B;

void test() {
test_func();
test_duplicated_func();
}
36 changes: 36 additions & 0 deletions clang/test/Sema/return-type-mismatch.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// RUN: %clang_cc1 -Wreturn-type -Wno-return-mismatch -fsyntax-only -verify=return-type %s
// RUN: %clang_cc1 -Wno-return-type -Wreturn-mismatch -fsyntax-only -verify=return-mismatch %s

int foo(void) __attribute__((noreturn));
int bar(void);

void test1(void) {
return 1; // return-mismatch-warning{{void function 'test1' should not return a value}}
}

int test2(void) {
return; // return-mismatch-warning{{non-void function 'test2' should return a value}}
}

int test3(void) {
// return-type-warning@+1 {{non-void function does not return a value}}
}

int test4(void) {
(void)(bar() || foo()); // return-type-warning@+1 {{non-void function does not return a value in all control paths}}
}

void test5(void) {
} // no-warning

int test6(void) {
return 0; // no-warning
}

int test7(void) {
foo(); // no warning
}

int test8(void) {
bar(); // return-type-warning@+1 {{non-void function does not return a value}}
}
79 changes: 0 additions & 79 deletions clang/test/SemaCXX/gh84064-1.cpp

This file was deleted.

53 changes: 0 additions & 53 deletions clang/test/SemaCXX/gh84064-2.cpp

This file was deleted.

181 changes: 106 additions & 75 deletions clang/test/SemaCXX/type-traits.cpp

Large diffs are not rendered by default.

33 changes: 32 additions & 1 deletion clang/tools/clang-installapi/Options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,33 @@ bool Options::processLinkerOptions(InputArgList &Args) {
return true;
}

bool Options::processFrontendOptions(InputArgList &Args) {
// Do not claim any arguments, as they will be passed along for CC1
// invocations.
if (auto *A = Args.getLastArgNoClaim(OPT_x)) {
FEOpts.LangMode = llvm::StringSwitch<clang::Language>(A->getValue())
.Case("c", clang::Language::C)
.Case("c++", clang::Language::CXX)
.Case("objective-c", clang::Language::ObjC)
.Case("objective-c++", clang::Language::ObjCXX)
.Default(clang::Language::Unknown);

if (FEOpts.LangMode == clang::Language::Unknown) {
Diags->Report(clang::diag::err_drv_invalid_value)
<< A->getAsString(Args) << A->getValue();
return false;
}
}
for (auto *A : Args.filtered(OPT_ObjC, OPT_ObjCXX)) {
if (A->getOption().matches(OPT_ObjC))
FEOpts.LangMode = clang::Language::ObjC;
else
FEOpts.LangMode = clang::Language::ObjCXX;
}

return true;
}

Options::Options(DiagnosticsEngine &Diag, FileManager *FM,
InputArgList &ArgList)
: Diags(&Diag), FM(FM) {
Expand All @@ -108,7 +135,10 @@ Options::Options(DiagnosticsEngine &Diag, FileManager *FM,
if (!processLinkerOptions(ArgList))
return;

/// Any remaining arguments should be handled by invoking the clang frontend.
if (!processFrontendOptions(ArgList))
return;

/// Any unclaimed arguments should be handled by invoking the clang frontend.
for (const Arg *A : ArgList) {
if (A->isClaimed())
continue;
Expand All @@ -132,6 +162,7 @@ InstallAPIContext Options::createContext() {
Ctx.BA.AppExtensionSafe = LinkerOpts.AppExtensionSafe;
Ctx.FT = DriverOpts.OutFT;
Ctx.OutputLoc = DriverOpts.OutputPath;
Ctx.LangMode = FEOpts.LangMode;

// Process inputs.
for (const std::string &ListPath : DriverOpts.FileLists) {
Expand Down
7 changes: 7 additions & 0 deletions clang/tools/clang-installapi/Options.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,22 @@ struct LinkerOptions {
bool IsDylib = false;
};

struct FrontendOptions {
/// \brief The language mode to parse headers in.
Language LangMode = Language::ObjC;
};

class Options {
private:
bool processDriverOptions(llvm::opt::InputArgList &Args);
bool processLinkerOptions(llvm::opt::InputArgList &Args);
bool processFrontendOptions(llvm::opt::InputArgList &Args);

public:
/// The various options grouped together.
DriverOptions DriverOpts;
LinkerOptions LinkerOpts;
FrontendOptions FEOpts;

Options() = delete;

Expand Down
1 change: 1 addition & 0 deletions clang/unittests/Interpreter/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ add_clang_unittest(ClangReplInterpreterTests
IncrementalCompilerBuilderTest.cpp
IncrementalProcessingTest.cpp
InterpreterTest.cpp
InterpreterExtensionsTest.cpp
CodeCompletionTest.cpp
)
target_link_libraries(ClangReplInterpreterTests PUBLIC
Expand Down
79 changes: 79 additions & 0 deletions clang/unittests/Interpreter/InterpreterExtensionsTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//===- unittests/Interpreter/InterpreterExtensionsTest.cpp ----------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Unit tests for Clang's Interpreter library.
//
//===----------------------------------------------------------------------===//

#include "clang/Interpreter/Interpreter.h"

#include "clang/AST/Expr.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Sema.h"

#include "llvm/Support/Error.h"
#include "llvm/Testing/Support/Error.h"

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <system_error>

using namespace clang;
namespace {

class RecordRuntimeIBMetrics : public Interpreter {
struct NoopRuntimeInterfaceBuilder : public RuntimeInterfaceBuilder {
NoopRuntimeInterfaceBuilder(Sema &S) : S(S) {}

TransformExprFunction *getPrintValueTransformer() override {
TransformerQueries += 1;
return &noop;
}

static ExprResult noop(RuntimeInterfaceBuilder *Builder, Expr *E,
ArrayRef<Expr *> FixedArgs) {
auto *B = static_cast<NoopRuntimeInterfaceBuilder *>(Builder);
B->TransformedExprs += 1;
return B->S.ActOnFinishFullExpr(E, /*DiscardedValue=*/false);
}

Sema &S;
size_t TransformedExprs = 0;
size_t TransformerQueries = 0;
};

public:
// Inherit with using wouldn't make it public
RecordRuntimeIBMetrics(std::unique_ptr<CompilerInstance> CI, llvm::Error &Err)
: Interpreter(std::move(CI), Err) {}

std::unique_ptr<RuntimeInterfaceBuilder> FindRuntimeInterface() override {
assert(RuntimeIBPtr == nullptr && "We create the builder only once");
Sema &S = getCompilerInstance()->getSema();
auto RuntimeIB = std::make_unique<NoopRuntimeInterfaceBuilder>(S);
RuntimeIBPtr = RuntimeIB.get();
return RuntimeIB;
}

NoopRuntimeInterfaceBuilder *RuntimeIBPtr = nullptr;
};

TEST(InterpreterExtensionsTest, FindRuntimeInterface) {
clang::IncrementalCompilerBuilder CB;
llvm::Error ErrOut = llvm::Error::success();
RecordRuntimeIBMetrics Interp(cantFail(CB.CreateCpp()), ErrOut);
cantFail(std::move(ErrOut));
cantFail(Interp.Parse("int a = 1; a"));
cantFail(Interp.Parse("int b = 2; b"));
cantFail(Interp.Parse("int c = 3; c"));
EXPECT_EQ(3U, Interp.RuntimeIBPtr->TransformedExprs);
EXPECT_EQ(1U, Interp.RuntimeIBPtr->TransformerQueries);
}

} // end anonymous namespace
15 changes: 15 additions & 0 deletions cmake/Modules/LLVMVersion.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# The LLVM Version number information

if(NOT DEFINED LLVM_VERSION_MAJOR)
set(LLVM_VERSION_MAJOR 19)
endif()
if(NOT DEFINED LLVM_VERSION_MINOR)
set(LLVM_VERSION_MINOR 0)
endif()
if(NOT DEFINED LLVM_VERSION_PATCH)
set(LLVM_VERSION_PATCH 0)
endif()
if(NOT DEFINED LLVM_VERSION_SUFFIX)
set(LLVM_VERSION_SUFFIX git)
endif()

5 changes: 4 additions & 1 deletion compiler-rt/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,10 @@ append_list_if(MINGW -fms-extensions SANITIZER_COMMON_CFLAGS)
#
# Note that this type of issue was discovered with lsan, but can apply to other
# sanitizers.
append_list_if(COMPILER_RT_HAS_TRIVIAL_AUTO_INIT -ftrivial-auto-var-init=pattern SANITIZER_COMMON_CFLAGS)
# Disable PowerPC because of https://github.com/llvm/llvm-project/issues/84654.
if(NOT "${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "powerpc")
append_list_if(COMPILER_RT_HAS_TRIVIAL_AUTO_INIT -ftrivial-auto-var-init=pattern SANITIZER_COMMON_CFLAGS)
endif()

# Set common link flags.
# TODO: We should consider using the same model as libc++, that is use either
Expand Down
1 change: 0 additions & 1 deletion compiler-rt/lib/lsan/lsan_common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,6 @@ static void ProcessRootRegions(Frontier *frontier) {
MemoryMappingLayout proc_maps(/*cache_enabled*/ true);
MemoryMappedSegment segment;
InternalMmapVector<Region> mapped_regions;
CHECK_EQ(mapped_regions.size(), 0ull);
while (proc_maps.Next(&segment))
if (segment.IsReadable())
mapped_regions.push_back({segment.start, segment.end});
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/lib/profile/InstrProfilingPlatformWindows.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ ValueProfNode *__llvm_profile_end_vnodes(void) { return &VNodesEnd; }
ValueProfNode *CurrentVNode = &VNodesStart + 1;
ValueProfNode *EndVNode = &VNodesEnd;

/* lld-link provides __buildid symbol which ponits to the 16 bytes build id when
/* lld-link provides __buildid symbol which points to the 16 bytes build id when
* using /build-id flag. https://lld.llvm.org/windows_support.html#lld-flags */
#define BUILD_ID_LEN 16
COMPILER_RT_WEAK uint8_t __buildid[BUILD_ID_LEN] = {0};
Expand Down
83 changes: 46 additions & 37 deletions compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions compiler-rt/test/tsan/pthread_atfork_deadlock3.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,17 @@ void *worker(void *main) {
}

void atfork() {
write(2, "in atfork\n", strlen("in atfork\n"));
barrier_wait(&barrier);
barrier_wait(&barrier);
write(2, "in atfork\n", strlen("in atfork\n"));
static volatile long a;
__atomic_fetch_add(&a, 1, __ATOMIC_RELEASE);
}

void afterfork() {
write(2, "in afterfork\n", strlen("in afterfork\n"));
barrier_wait(&barrier);
barrier_wait(&barrier);
write(2, "in afterfork\n", strlen("in afterfork\n"));
static volatile long a;
__atomic_fetch_add(&a, 1, __ATOMIC_RELEASE);
}
Expand Down
59 changes: 59 additions & 0 deletions compiler-rt/test/tsan/signal_in_read.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s

#include "test.h"

#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

static int SignalPipeFd[] = {-1, -1};
static int BlockingPipeFd[] = {-1, -1};

static void Handler(int _) { assert(write(SignalPipeFd[1], ".", 1) == 1); }

static void *ThreadFunc(void *_) {
char C;
assert(read(BlockingPipeFd[0], &C, sizeof(C)) == 1);
assert(C == '.');
return 0;
}

int main() {
alarm(60); // Kill the test if it hangs.

assert(pipe(SignalPipeFd) == 0);
assert(pipe(BlockingPipeFd) == 0);

struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_RESTART;
act.sa_handler = Handler;
assert(sigaction(SIGUSR1, &act, 0) == 0);

pthread_t Thr;
assert(pthread_create(&Thr, 0, ThreadFunc, 0) == 0);

// Give the thread enough time to block in the read call.
usleep(1000000);

// Signal the thread, this should run the signal handler and unblock the read
// below.
pthread_kill(Thr, SIGUSR1);
char C;
assert(read(SignalPipeFd[0], &C, 1) == 1);

// Unblock the thread and join it.
assert(write(BlockingPipeFd[1], &C, 1) == 1);
void *_ = 0;
assert(pthread_join(Thr, &_) == 0);

fprintf(stderr, "PASS\n");
return 0;
}

// CHECK-NOT: WARNING: ThreadSanitizer:
// CHECK: PASS
8 changes: 8 additions & 0 deletions flang/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,14 @@ if (LLVM_COMPILER_IS_GCC_COMPATIBLE)

endif()

# Clang on Darwin enables non-POSIX extensions by default, which allows the
# macro HUGE to leak out of <math.h> even when it is never directly included,
# conflicting with Flang's HUGE symbols.
# Set _POSIX_C_SOURCE to avoid including these extensions.
if (APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_POSIX_C_SOURCE=200809")
endif()

list(REMOVE_DUPLICATES CMAKE_CXX_FLAGS)

# Determine HOST_LINK_VERSION on Darwin.
Expand Down
4 changes: 0 additions & 4 deletions flang/include/flang/Evaluate/integer.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@
#include <string>
#include <type_traits>

// Some environments, viz. clang on Darwin, allow the macro HUGE
// to leak out of <math.h> even when it is never directly included.
#undef HUGE

namespace Fortran::evaluate::value {

// Implements an integer as an assembly of smaller host integer parts
Expand Down
4 changes: 0 additions & 4 deletions flang/include/flang/Evaluate/real.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@
#include <limits>
#include <string>

// Some environments, viz. clang on Darwin, allow the macro HUGE
// to leak out of <math.h> even when it is never directly included.
#undef HUGE

namespace llvm {
class raw_ostream;
}
Expand Down
3 changes: 2 additions & 1 deletion flang/include/flang/Optimizer/Builder/HLFIRTools.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,8 @@ translateToExtendedValue(mlir::Location loc, fir::FirOpBuilder &builder,
/// on the IR.
fir::ExtendedValue
translateToExtendedValue(mlir::Location loc, fir::FirOpBuilder &builder,
fir::FortranVariableOpInterface fortranVariable);
fir::FortranVariableOpInterface fortranVariable,
bool forceHlfirBase = false);

/// Generate declaration for a fir::ExtendedValue in memory.
fir::FortranVariableOpInterface
Expand Down
4 changes: 0 additions & 4 deletions flang/lib/Evaluate/fold-implementation.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,6 @@
#include <type_traits>
#include <variant>

// Some environments, viz. clang on Darwin, allow the macro HUGE
// to leak out of <math.h> even when it is never directly included.
#undef HUGE

namespace Fortran::evaluate {

// Utilities
Expand Down
4 changes: 2 additions & 2 deletions flang/lib/Evaluate/intrinsics-library.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -299,8 +299,8 @@ struct HostRuntimeLibrary<std::complex<HostT>, LibraryVersion::Libm> {
/// Define libm extensions
/// Bessel functions are defined in POSIX.1-2001.

// Remove float bessel functions for AIX as they are not supported
#ifndef _AIX
// Remove float bessel functions for AIX and Darwin as they are not supported
#if !defined(_AIX) && !defined(__APPLE__)
template <> struct HostRuntimeLibrary<float, LibraryVersion::LibmExtensions> {
using F = FuncPointer<float, float>;
using FN = FuncPointer<float, int, float>;
Expand Down
13 changes: 8 additions & 5 deletions flang/lib/Lower/Bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,8 @@ class FirConverter : public Fortran::lower::AbstractConverter {
assert(details && "No host-association found");
const Fortran::semantics::Symbol &hsym = details->symbol();
mlir::Type hSymType = genType(hsym);
Fortran::lower::SymbolBox hsb = lookupSymbol(hsym);
Fortran::lower::SymbolBox hsb =
lookupSymbol(hsym, /*symMap=*/nullptr, /*forceHlfirBase=*/true);

auto allocate = [&](llvm::ArrayRef<mlir::Value> shape,
llvm::ArrayRef<mlir::Value> typeParams) -> mlir::Value {
Expand Down Expand Up @@ -727,7 +728,8 @@ class FirConverter : public Fortran::lower::AbstractConverter {
void createHostAssociateVarCloneDealloc(
const Fortran::semantics::Symbol &sym) override final {
mlir::Location loc = genLocation(sym.name());
Fortran::lower::SymbolBox hsb = lookupSymbol(sym);
Fortran::lower::SymbolBox hsb =
lookupSymbol(sym, /*symMap=*/nullptr, /*forceHlfirBase=*/true);

fir::ExtendedValue hexv = symBoxToExtendedValue(hsb);
hexv.match(
Expand Down Expand Up @@ -960,13 +962,14 @@ class FirConverter : public Fortran::lower::AbstractConverter {
/// Find the symbol in the local map or return null.
Fortran::lower::SymbolBox
lookupSymbol(const Fortran::semantics::Symbol &sym,
Fortran::lower::SymMap *symMap = nullptr) {
Fortran::lower::SymMap *symMap = nullptr,
bool forceHlfirBase = false) {
symMap = symMap ? symMap : &localSymbols;
if (lowerToHighLevelFIR()) {
if (std::optional<fir::FortranVariableOpInterface> var =
symMap->lookupVariableDefinition(sym)) {
auto exv =
hlfir::translateToExtendedValue(toLocation(), *builder, *var);
auto exv = hlfir::translateToExtendedValue(toLocation(), *builder, *var,
forceHlfirBase);
return exv.match(
[](mlir::Value x) -> Fortran::lower::SymbolBox {
return Fortran::lower::SymbolBox::Intrinsic{x};
Expand Down
31 changes: 17 additions & 14 deletions flang/lib/Optimizer/Builder/HLFIRTools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -848,36 +848,38 @@ hlfir::LoopNest hlfir::genLoopNest(mlir::Location loc,

static fir::ExtendedValue
translateVariableToExtendedValue(mlir::Location loc, fir::FirOpBuilder &builder,
hlfir::Entity variable) {
hlfir::Entity variable,
bool forceHlfirBase = false) {
assert(variable.isVariable() && "must be a variable");
/// When going towards FIR, use the original base value to avoid
/// introducing descriptors at runtime when they are not required.
mlir::Value firBase = variable.getFirBase();
mlir::Value base =
forceHlfirBase ? variable.getBase() : variable.getFirBase();
if (variable.isMutableBox())
return fir::MutableBoxValue(firBase, getExplicitTypeParams(variable),
return fir::MutableBoxValue(base, getExplicitTypeParams(variable),
fir::MutableProperties{});

if (firBase.getType().isa<fir::BaseBoxType>()) {
if (base.getType().isa<fir::BaseBoxType>()) {
if (!variable.isSimplyContiguous() || variable.isPolymorphic() ||
variable.isDerivedWithLengthParameters() || variable.isOptional()) {
llvm::SmallVector<mlir::Value> nonDefaultLbounds =
getNonDefaultLowerBounds(loc, builder, variable);
return fir::BoxValue(firBase, nonDefaultLbounds,
return fir::BoxValue(base, nonDefaultLbounds,
getExplicitTypeParams(variable));
}
// Otherwise, the variable can be represented in a fir::ExtendedValue
// without the overhead of a fir.box.
firBase = genVariableRawAddress(loc, builder, variable);
base = genVariableRawAddress(loc, builder, variable);
}

if (variable.isScalar()) {
if (variable.isCharacter()) {
if (firBase.getType().isa<fir::BoxCharType>())
return genUnboxChar(loc, builder, firBase);
if (base.getType().isa<fir::BoxCharType>())
return genUnboxChar(loc, builder, base);
mlir::Value len = genCharacterVariableLength(loc, builder, variable);
return fir::CharBoxValue{firBase, len};
return fir::CharBoxValue{base, len};
}
return firBase;
return base;
}
llvm::SmallVector<mlir::Value> extents;
llvm::SmallVector<mlir::Value> nonDefaultLbounds;
Expand All @@ -893,15 +895,16 @@ translateVariableToExtendedValue(mlir::Location loc, fir::FirOpBuilder &builder,
}
if (variable.isCharacter())
return fir::CharArrayBoxValue{
firBase, genCharacterVariableLength(loc, builder, variable), extents,
base, genCharacterVariableLength(loc, builder, variable), extents,
nonDefaultLbounds};
return fir::ArrayBoxValue{firBase, extents, nonDefaultLbounds};
return fir::ArrayBoxValue{base, extents, nonDefaultLbounds};
}

fir::ExtendedValue
hlfir::translateToExtendedValue(mlir::Location loc, fir::FirOpBuilder &builder,
fir::FortranVariableOpInterface var) {
return translateVariableToExtendedValue(loc, builder, var);
fir::FortranVariableOpInterface var,
bool forceHlfirBase) {
return translateVariableToExtendedValue(loc, builder, var, forceHlfirBase);
}

std::pair<fir::ExtendedValue, std::optional<hlfir::CleanupFunction>>
Expand Down
11 changes: 10 additions & 1 deletion flang/lib/Optimizer/CodeGen/BoxedProcedure.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ class BoxedProcedurePass
BoxprocTypeRewriter typeConverter(mlir::UnknownLoc::get(context));
mlir::Dialect *firDialect = context->getLoadedDialect("fir");
getModule().walk([&](mlir::Operation *op) {
bool opIsValid = true;
typeConverter.setLocation(op->getLoc());
if (auto addr = mlir::dyn_cast<BoxAddrOp>(op)) {
mlir::Type ty = addr.getVal().getType();
Expand All @@ -220,6 +221,7 @@ class BoxedProcedurePass
rewriter.setInsertionPoint(addr);
rewriter.replaceOpWithNewOp<ConvertOp>(
addr, typeConverter.convertType(addr.getType()), addr.getVal());
opIsValid = false;
} else if (typeConverter.needsConversion(resTy)) {
rewriter.startOpModification(op);
op->getResult(0).setType(typeConverter.convertType(resTy));
Expand Down Expand Up @@ -271,10 +273,12 @@ class BoxedProcedurePass
llvm::ArrayRef<mlir::Value>{tramp});
rewriter.replaceOpWithNewOp<ConvertOp>(embox, toTy,
adjustCall.getResult(0));
opIsValid = false;
} else {
// Just forward the function as a pointer.
rewriter.replaceOpWithNewOp<ConvertOp>(embox, toTy,
embox.getFunc());
opIsValid = false;
}
} else if (auto global = mlir::dyn_cast<GlobalOp>(op)) {
auto ty = global.getType();
Expand All @@ -297,6 +301,7 @@ class BoxedProcedurePass
rewriter.replaceOpWithNewOp<AllocaOp>(
mem, toTy, uniqName, bindcName, isPinned, mem.getTypeparams(),
mem.getShape());
opIsValid = false;
}
} else if (auto mem = mlir::dyn_cast<AllocMemOp>(op)) {
auto ty = mem.getType();
Expand All @@ -310,6 +315,7 @@ class BoxedProcedurePass
rewriter.replaceOpWithNewOp<AllocMemOp>(
mem, toTy, uniqName, bindcName, mem.getTypeparams(),
mem.getShape());
opIsValid = false;
}
} else if (auto coor = mlir::dyn_cast<CoordinateOp>(op)) {
auto ty = coor.getType();
Expand All @@ -321,6 +327,7 @@ class BoxedProcedurePass
auto toBaseTy = typeConverter.convertType(baseTy);
rewriter.replaceOpWithNewOp<CoordinateOp>(coor, toTy, coor.getRef(),
coor.getCoor(), toBaseTy);
opIsValid = false;
}
} else if (auto index = mlir::dyn_cast<FieldIndexOp>(op)) {
auto ty = index.getType();
Expand All @@ -332,6 +339,7 @@ class BoxedProcedurePass
auto toOnTy = typeConverter.convertType(onTy);
rewriter.replaceOpWithNewOp<FieldIndexOp>(
index, toTy, index.getFieldId(), toOnTy, index.getTypeparams());
opIsValid = false;
}
} else if (auto index = mlir::dyn_cast<LenParamIndexOp>(op)) {
auto ty = index.getType();
Expand All @@ -343,6 +351,7 @@ class BoxedProcedurePass
auto toOnTy = typeConverter.convertType(onTy);
rewriter.replaceOpWithNewOp<LenParamIndexOp>(
index, toTy, index.getFieldId(), toOnTy, index.getTypeparams());
opIsValid = false;
}
} else if (op->getDialect() == firDialect) {
rewriter.startOpModification(op);
Expand All @@ -354,7 +363,7 @@ class BoxedProcedurePass
rewriter.finalizeOpModification(op);
}
// Ensure block arguments are updated if needed.
if (op->getNumRegions() != 0) {
if (opIsValid && op->getNumRegions() != 0) {
rewriter.startOpModification(op);
for (mlir::Region &region : op->getRegions())
for (mlir::Block &block : region.getBlocks())
Expand Down
10 changes: 5 additions & 5 deletions flang/test/Lower/OpenMP/parallel-private-clause-str.f90
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@
!CHECK: %[[C_DECL:.*]]:2 = hlfir.declare %[[C_BOX_REF]] typeparams %{{.*}} {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFtest_allocatable_stringEc"} : (!fir.ref<!fir.box<!fir.heap<!fir.char<1,?>>>>, i32) -> (!fir.ref<!fir.box<!fir.heap<!fir.char<1,?>>>>, !fir.ref<!fir.box<!fir.heap<!fir.char<1,?>>>>)
!CHECK: omp.parallel {
!CHECK: %[[C_PVT_BOX_REF:.*]] = fir.alloca !fir.box<!fir.heap<!fir.char<1,?>>> {bindc_name = "c", pinned, uniq_name = "_QFtest_allocatable_stringEc"}
!CHECK: %[[C_BOX:.*]] = fir.load %[[C_DECL]]#1 : !fir.ref<!fir.box<!fir.heap<!fir.char<1,?>>>>
!CHECK: %[[C_BOX:.*]] = fir.load %[[C_DECL]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.char<1,?>>>>
!CHECK: fir.if %{{.*}} {
!CHECK: %[[C_PVT_MEM:.*]] = fir.allocmem !fir.char<1,?>(%{{.*}} : index) {fir.must_be_heap = true, uniq_name = "_QFtest_allocatable_stringEc.alloc"}
!CHECK: %[[C_PVT_BOX:.*]] = fir.embox %[[C_PVT_MEM]] typeparams %{{.*}} : (!fir.heap<!fir.char<1,?>>, index) -> !fir.box<!fir.heap<!fir.char<1,?>>>
!CHECK: fir.store %[[C_PVT_BOX]] to %[[C_PVT_BOX_REF]] : !fir.ref<!fir.box<!fir.heap<!fir.char<1,?>>>>
!CHECK: }
!CHECK: %[[C_PVT_DECL:.*]]:2 = hlfir.declare %[[C_PVT_BOX_REF]] {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFtest_allocatable_stringEc"} : (!fir.ref<!fir.box<!fir.heap<!fir.char<1,?>>>>) -> (!fir.ref<!fir.box<!fir.heap<!fir.char<1,?>>>>, !fir.ref<!fir.box<!fir.heap<!fir.char<1,?>>>>)
!CHECK: fir.if %{{.*}} {
!CHECK: %[[C_PVT_BOX:.*]] = fir.load %[[C_PVT_DECL]]#1 : !fir.ref<!fir.box<!fir.heap<!fir.char<1,?>>>>
!CHECK: %[[C_PVT_BOX:.*]] = fir.load %[[C_PVT_DECL]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.char<1,?>>>>
!CHECK: %[[C_PVT_BOX_ADDR:.*]] = fir.box_addr %[[C_PVT_BOX]] : (!fir.box<!fir.heap<!fir.char<1,?>>>) -> !fir.heap<!fir.char<1,?>>
!CHECK: fir.freemem %[[C_PVT_BOX_ADDR]] : !fir.heap<!fir.char<1,?>>
!CHECK: }
Expand All @@ -38,16 +38,16 @@ subroutine test_allocatable_string(n)
!CHECK: %[[C_DECL:.*]]:2 = hlfir.declare %[[C_BOX_REF]] typeparams %{{.*}} {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFtest_allocatable_string_arrayEc"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>, i32) -> (!fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>)
!CHECK: omp.parallel {
!CHECK: %[[C_PVT_BOX_REF:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>> {bindc_name = "c", pinned, uniq_name = "_QFtest_allocatable_string_arrayEc"}
!CHECK: %{{.*}} = fir.load %[[C_DECL]]#1 : !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>
!CHECK: %{{.*}} = fir.load %[[C_DECL]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>
!CHECK: fir.if %{{.*}} {
!CHECK: %[[C_PVT_ALLOC:.*]] = fir.allocmem !fir.array<?x!fir.char<1,?>>(%{{.*}} : index), %{{.*}} {fir.must_be_heap = true, uniq_name = "_QFtest_allocatable_string_arrayEc.alloc"}
!CHECK: %[[C_PVT_BOX:.*]] = fir.embox %[[C_PVT_ALLOC]](%{{.*}}) typeparams %{{.*}} : (!fir.heap<!fir.array<?x!fir.char<1,?>>>, !fir.shapeshift<1>, index) -> !fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>
!CHECK: fir.store %[[C_PVT_BOX]] to %[[C_PVT_BOX_REF]] : !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>
!CHECK: }
!CHECK: %[[C_PVT_DECL:.*]]:2 = hlfir.declare %[[C_PVT_BOX_REF]] {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFtest_allocatable_string_arrayEc"} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>) -> (!fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>)
!CHECK: %{{.*}} = fir.load %[[C_PVT_DECL]]#1 : !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>
!CHECK: %{{.*}} = fir.load %[[C_PVT_DECL]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>
!CHECK: fir.if %{{.*}} {
!CHECK: %[[C_PVT_BOX:.*]] = fir.load %[[C_PVT_DECL]]#1 : !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>
!CHECK: %[[C_PVT_BOX:.*]] = fir.load %[[C_PVT_DECL]]#0 : !fir.ref<!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>>
!CHECK: %[[C_PVT_ADDR:.*]] = fir.box_addr %[[C_PVT_BOX]] : (!fir.box<!fir.heap<!fir.array<?x!fir.char<1,?>>>>) -> !fir.heap<!fir.array<?x!fir.char<1,?>>>
!CHECK: fir.freemem %[[C_PVT_ADDR]] : !fir.heap<!fir.array<?x!fir.char<1,?>>>
!CHECK: }
Expand Down
Loading