12 changes: 12 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,14 @@ Attribute Changes in Clang
- Introduced a new attribute ``[[clang::coro_await_elidable_argument]]`` on function parameters
to propagate safe elide context to arguments if such function is also under a safe elide context.

- The documentation of the ``[[clang::musttail]]`` attribute was updated to
note that the lifetimes of all local variables end before the call. This does
not change the behaviour of the compiler, as this was true for previous
versions.

- Fix a bug where clang doesn't automatically apply the ``[[gsl::Owner]]`` or
``[[gsl::Pointer]]`` to STL explicit template specialization decls. (#GH109442)

Improvements to Clang's diagnostics
-----------------------------------

Expand Down Expand Up @@ -324,6 +332,10 @@ Improvements to Clang's diagnostics

- Don't emit bogus dangling diagnostics when ``[[gsl::Owner]]`` and `[[clang::lifetimebound]]` are used together (#GH108272).

- The ``-Wreturn-stack-address`` warning now also warns about addresses of
local variables passed to function calls using the ``[[clang::musttail]]``
attribute.

Improvements to Clang's time-trace
----------------------------------

Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,12 @@ return value must be trivially destructible. The calling convention of the
caller and callee must match, and they must not be variadic functions or have
old style K&R C function declarations.

The lifetimes of all local variables and function parameters end immediately
before the call to the function. This means that it is undefined behaviour to
pass a pointer or reference to a local variable to the called function, which
is not the case without the attribute. Clang will emit a warning in common
cases where this happens.

``clang::musttail`` provides assurances that the tail call can be optimized on
all targets, not just one.
}];
Expand Down
8 changes: 0 additions & 8 deletions clang/include/clang/Basic/BuiltinsNVPTX.def
Original file line number Diff line number Diff line change
Expand Up @@ -599,14 +599,6 @@ TARGET_BUILTIN(__nvvm_e4m3x2_to_f16x2_rn_relu, "V2hs", "", AND(SM_89,PTX81))
TARGET_BUILTIN(__nvvm_e5m2x2_to_f16x2_rn, "V2hs", "", AND(SM_89,PTX81))
TARGET_BUILTIN(__nvvm_e5m2x2_to_f16x2_rn_relu, "V2hs", "", AND(SM_89,PTX81))

// Bitcast

BUILTIN(__nvvm_bitcast_f2i, "if", "")
BUILTIN(__nvvm_bitcast_i2f, "fi", "")

BUILTIN(__nvvm_bitcast_ll2d, "dLLi", "")
BUILTIN(__nvvm_bitcast_d2ll, "LLid", "")

// FNS
TARGET_BUILTIN(__nvvm_fns, "UiUiUii", "n", PTX60)

Expand Down
6 changes: 5 additions & 1 deletion clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -10101,11 +10101,15 @@ def err_lifetimebound_ctor_dtor : Error<
// CHECK: returning address/reference of stack memory
def warn_ret_stack_addr_ref : Warning<
"%select{address of|reference to}0 stack memory associated with "
"%select{local variable|parameter|compound literal}2 %1 returned">,
"%select{local variable|parameter|compound literal}2 %1 "
"%select{returned|passed to musttail function}3">,
InGroup<ReturnStackAddress>;
def warn_ret_local_temp_addr_ref : Warning<
"returning %select{address of|reference to}0 local temporary object">,
InGroup<ReturnStackAddress>;
def warn_musttail_local_temp_addr_ref : Warning<
"passing %select{address of|reference to}0 local temporary object to musttail function">,
InGroup<ReturnStackAddress>;
def err_ret_local_temp_ref : Error<
"returning reference to local temporary object">;
def warn_ret_addr_label : Warning<
Expand Down
6 changes: 4 additions & 2 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -6352,8 +6352,10 @@ def mcx16 : Flag<["-"], "mcx16">, Group<m_x86_Features_Group>;
def mno_cx16 : Flag<["-"], "mno-cx16">, Group<m_x86_Features_Group>;
def menqcmd : Flag<["-"], "menqcmd">, Group<m_x86_Features_Group>;
def mno_enqcmd : Flag<["-"], "mno-enqcmd">, Group<m_x86_Features_Group>;
def mevex512 : Flag<["-"], "mevex512">, Group<m_x86_Features_Group>;
def mno_evex512 : Flag<["-"], "mno-evex512">, Group<m_x86_Features_Group>;
def mevex512 : Flag<["-"], "mevex512">, Group<m_x86_Features_Group>,
Visibility<[ClangOption, CLOption, FlangOption]>;
def mno_evex512 : Flag<["-"], "mno-evex512">, Group<m_x86_Features_Group>,
Visibility<[ClangOption, CLOption, FlangOption]>;
def mf16c : Flag<["-"], "mf16c">, Group<m_x86_Features_Group>;
def mno_f16c : Flag<["-"], "mno-f16c">, Group<m_x86_Features_Group>;
def mfma : Flag<["-"], "mfma">, Group<m_x86_Features_Group>;
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/Frontend/MultiplexConsumer.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class MultiplexConsumer : public SemaConsumer {
public:
// Takes ownership of the pointers in C.
MultiplexConsumer(std::vector<std::unique_ptr<ASTConsumer>> C);
MultiplexConsumer(std::unique_ptr<ASTConsumer> C);
~MultiplexConsumer() override;

// ASTConsumer
Expand Down Expand Up @@ -80,7 +81,7 @@ class MultiplexConsumer : public SemaConsumer {
void InitializeSema(Sema &S) override;
void ForgetSema() override;

private:
protected:
std::vector<std::unique_ptr<ASTConsumer>> Consumers; // Owns these.
std::unique_ptr<MultiplexASTMutationListener> MutationListener;
std::unique_ptr<MultiplexASTDeserializationListener> DeserializationListener;
Expand Down
58 changes: 30 additions & 28 deletions clang/include/clang/Interpreter/Interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,9 @@
#ifndef LLVM_CLANG_INTERPRETER_INTERPRETER_H
#define LLVM_CLANG_INTERPRETER_INTERPRETER_H

#include "clang/AST/Decl.h"
#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 All @@ -38,6 +36,9 @@ class ThreadSafeContext;
namespace clang {

class CompilerInstance;
class CodeGenerator;
class CXXRecordDecl;
class Decl;
class IncrementalExecutor;
class IncrementalParser;

Expand Down Expand Up @@ -77,42 +78,45 @@ 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;
};
class IncrementalAction;
class InProcessPrintingASTConsumer;

/// Provides top-level interfaces for incremental compilation and execution.
class Interpreter {
friend class Value;
friend InProcessPrintingASTConsumer;

std::unique_ptr<llvm::orc::ThreadSafeContext> TSCtx;
/// Long-lived, incremental parsing action.
std::unique_ptr<IncrementalAction> Act;
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;

/// List containing information about each incrementally parsed piece of code.
std::list<PartialTranslationUnit> PTUs;

unsigned InitPTUSize = 0;

// This member holds the last result of the value printing. It's a class
// member because we might want to access it after more inputs. If no value
// 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;
/// When CodeGen is created the first llvm::Module gets cached in many places
/// and we must keep it alive.
std::unique_ptr<llvm::Module> CachedInCodeGenModule;

/// Compiler instance performing the incremental compilation.
std::unique_ptr<CompilerInstance> CI;

protected:
// Derived classes can use an extended interface of the Interpreter.
Interpreter(std::unique_ptr<CompilerInstance> CI, llvm::Error &Err,
std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder = nullptr);
Interpreter(std::unique_ptr<CompilerInstance> Instance, llvm::Error &Err,
std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder = nullptr,
std::unique_ptr<clang::ASTConsumer> Consumer = nullptr);

// Create the internal IncrementalExecutor, or re-create it after calling
// ResetExecutor().
Expand All @@ -122,15 +126,8 @@ class Interpreter {
// JIT engine. In particular, it doesn't run cleanup or destructors.
void ResetExecutor();

// 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:
virtual ~Interpreter();

static llvm::Expected<std::unique_ptr<Interpreter>>
create(std::unique_ptr<CompilerInstance> CI);
static llvm::Expected<std::unique_ptr<Interpreter>>
Expand All @@ -145,7 +142,6 @@ class Interpreter {
llvm::Expected<PartialTranslationUnit &> Parse(llvm::StringRef Code);
llvm::Error Execute(PartialTranslationUnit &T);
llvm::Error ParseAndExecute(llvm::StringRef Code, Value *V = nullptr);
llvm::Expected<llvm::orc::ExecutorAddr> CompileDtorCall(CXXRecordDecl *CXXRD);

/// Undo N previous incremental inputs.
llvm::Error Undo(unsigned N = 1);
Expand All @@ -167,8 +163,6 @@ class Interpreter {
llvm::Expected<llvm::orc::ExecutorAddr>
getSymbolAddressFromLinkerName(llvm::StringRef LinkerName) const;

enum InterfaceKind { NoAlloc, WithAlloc, CopyArray, NewTag };

const llvm::SmallVectorImpl<Expr *> &getValuePrintingInfo() const {
return ValuePrintingInfo;
}
Expand All @@ -178,7 +172,15 @@ class Interpreter {
private:
size_t getEffectivePTUSize() const;
void markUserCodeStart();
llvm::Expected<Expr *> ExtractValueFromExpr(Expr *E);
llvm::Expected<llvm::orc::ExecutorAddr> CompileDtorCall(CXXRecordDecl *CXXRD);

CodeGenerator *getCodeGen() const;
std::unique_ptr<llvm::Module> GenModule();
PartialTranslationUnit &RegisterPTU(TranslationUnitDecl *TU);

// A cache for the compiled destructors used to for de-allocation of managed
// clang::Values.
llvm::DenseMap<CXXRecordDecl *, llvm::orc::ExecutorAddr> Dtors;

llvm::SmallVector<Expr *, 4> ValuePrintingInfo;
Expand Down
3 changes: 1 addition & 2 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12587,8 +12587,7 @@ void ASTContext::forEachMultiversionedFunctionVersion(
FD->getDeclContext()->getRedeclContext()->lookup(FD->getDeclName())) {
FunctionDecl *CurFD = CurDecl->getAsFunction()->getMostRecentDecl();
if (CurFD && hasSameType(CurFD->getType(), FD->getType()) &&
!SeenDecls.contains(CurFD)) {
SeenDecls.insert(CurFD);
SeenDecls.insert(CurFD).second) {
Pred(CurFD);
}
}
Expand Down
98 changes: 67 additions & 31 deletions clang/lib/AST/ByteCode/Compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3097,12 +3097,11 @@ bool Compiler<Emitter>::VisitCXXNewExpr(const CXXNewExpr *E) {
QualType ElementType = E->getAllocatedType();
std::optional<PrimType> ElemT = classify(ElementType);
unsigned PlacementArgs = E->getNumPlacementArgs();
const FunctionDecl *OperatorNew = E->getOperatorNew();
const Expr *PlacementDest = nullptr;
bool IsNoThrow = false;

// FIXME: Better diagnostic. diag::note_constexpr_new_placement
if (PlacementArgs != 0) {
// The only new-placement list we support is of the form (std::nothrow).
//
// FIXME: There is no restriction on this, but it's not clear that any
// other form makes any sense. We get here for cases such as:
//
Expand All @@ -3111,27 +3110,43 @@ bool Compiler<Emitter>::VisitCXXNewExpr(const CXXNewExpr *E) {
// (which should presumably be valid only if N is a multiple of
// alignof(int), and in any case can't be deallocated unless N is
// alignof(X) and X has new-extended alignment).
if (PlacementArgs != 1 || !E->getPlacementArg(0)->getType()->isNothrowT())
return this->emitInvalid(E);
if (PlacementArgs == 1) {
const Expr *Arg1 = E->getPlacementArg(0);
if (Arg1->getType()->isNothrowT()) {
if (!this->discard(Arg1))
return false;
IsNoThrow = true;
} else if (Ctx.getLangOpts().CPlusPlus26 &&
OperatorNew->isReservedGlobalPlacementOperator()) {
// If we have a placement-new destination, we'll later use that instead
// of allocating.
PlacementDest = Arg1;
} else {
return this->emitInvalidNewDeleteExpr(E, E);
}

if (!this->discard(E->getPlacementArg(0)))
return false;
IsNoThrow = true;
} else {
return this->emitInvalid(E);
}
} else if (!OperatorNew->isReplaceableGlobalAllocationFunction()) {
return this->emitInvalidNewDeleteExpr(E, E);
}

const Descriptor *Desc;
if (ElemT) {
if (E->isArray())
Desc = nullptr; // We're not going to use it in this case.
else
Desc = P.createDescriptor(E, *ElemT, Descriptor::InlineDescMD,
/*IsConst=*/false, /*IsTemporary=*/false,
/*IsMutable=*/false);
} else {
Desc = P.createDescriptor(
E, ElementType.getTypePtr(),
E->isArray() ? std::nullopt : Descriptor::InlineDescMD,
/*IsConst=*/false, /*IsTemporary=*/false, /*IsMutable=*/false, Init);
if (!PlacementDest) {
if (ElemT) {
if (E->isArray())
Desc = nullptr; // We're not going to use it in this case.
else
Desc = P.createDescriptor(E, *ElemT, Descriptor::InlineDescMD,
/*IsConst=*/false, /*IsTemporary=*/false,
/*IsMutable=*/false);
} else {
Desc = P.createDescriptor(
E, ElementType.getTypePtr(),
E->isArray() ? std::nullopt : Descriptor::InlineDescMD,
/*IsConst=*/false, /*IsTemporary=*/false, /*IsMutable=*/false, Init);
}
}

if (E->isArray()) {
Expand All @@ -3148,26 +3163,42 @@ bool Compiler<Emitter>::VisitCXXNewExpr(const CXXNewExpr *E) {

PrimType SizeT = classifyPrim(Stripped->getType());

if (!this->visit(Stripped))
return false;

if (ElemT) {
// N primitive elements.
if (!this->emitAllocN(SizeT, *ElemT, E, IsNoThrow, E))
if (PlacementDest) {
if (!this->visit(PlacementDest))
return false;
if (!this->visit(Stripped))
return false;
if (!this->emitCheckNewTypeMismatchArray(SizeT, E, E))
return false;
} else {
// N Composite elements.
if (!this->emitAllocCN(SizeT, Desc, IsNoThrow, E))
if (!this->visit(Stripped))
return false;

if (ElemT) {
// N primitive elements.
if (!this->emitAllocN(SizeT, *ElemT, E, IsNoThrow, E))
return false;
} else {
// N Composite elements.
if (!this->emitAllocCN(SizeT, Desc, IsNoThrow, E))
return false;
}
}

if (Init && !this->visitInitializer(Init))
return false;

} else {
// Allocate just one element.
if (!this->emitAlloc(Desc, E))
return false;
if (PlacementDest) {
if (!this->visit(PlacementDest))
return false;
if (!this->emitCheckNewTypeMismatch(E, E))
return false;
} else {
// Allocate just one element.
if (!this->emitAlloc(Desc, E))
return false;
}

if (Init) {
if (ElemT) {
Expand All @@ -3194,6 +3225,11 @@ template <class Emitter>
bool Compiler<Emitter>::VisitCXXDeleteExpr(const CXXDeleteExpr *E) {
const Expr *Arg = E->getArgument();

const FunctionDecl *OperatorDelete = E->getOperatorDelete();

if (!OperatorDelete->isReplaceableGlobalAllocationFunction())
return this->emitInvalidNewDeleteExpr(E, E);

// Arg must be an lvalue.
if (!this->visit(Arg))
return false;
Expand Down
74 changes: 74 additions & 0 deletions clang/lib/AST/ByteCode/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1286,6 +1286,80 @@ bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize,
return Call(S, OpPC, F, VarArgSize);
}

bool CheckNewTypeMismatch(InterpState &S, CodePtr OpPC, const Expr *E,
std::optional<uint64_t> ArraySize) {
const Pointer &Ptr = S.Stk.peek<Pointer>();

if (!CheckStore(S, OpPC, Ptr))
return false;

const auto *NewExpr = cast<CXXNewExpr>(E);
QualType StorageType = Ptr.getType();

if (isa_and_nonnull<CXXNewExpr>(Ptr.getFieldDesc()->asExpr())) {
// FIXME: Are there other cases where this is a problem?
StorageType = StorageType->getPointeeType();
}

const ASTContext &ASTCtx = S.getASTContext();
QualType AllocType;
if (ArraySize) {
AllocType = ASTCtx.getConstantArrayType(
NewExpr->getAllocatedType(),
APInt(64, static_cast<uint64_t>(*ArraySize), false), nullptr,
ArraySizeModifier::Normal, 0);
} else {
AllocType = NewExpr->getAllocatedType();
}

unsigned StorageSize = 1;
unsigned AllocSize = 1;
if (const auto *CAT = dyn_cast<ConstantArrayType>(AllocType))
AllocSize = CAT->getZExtSize();
if (const auto *CAT = dyn_cast<ConstantArrayType>(StorageType))
StorageSize = CAT->getZExtSize();

if (AllocSize > StorageSize ||
!ASTCtx.hasSimilarType(ASTCtx.getBaseElementType(AllocType),
ASTCtx.getBaseElementType(StorageType))) {
S.FFDiag(S.Current->getLocation(OpPC),
diag::note_constexpr_placement_new_wrong_type)
<< StorageType << AllocType;
return false;
}
return true;
}

bool InvalidNewDeleteExpr(InterpState &S, CodePtr OpPC, const Expr *E) {
assert(E);
const auto &Loc = S.Current->getSource(OpPC);

if (const auto *NewExpr = dyn_cast<CXXNewExpr>(E)) {
const FunctionDecl *OperatorNew = NewExpr->getOperatorNew();

if (!S.getLangOpts().CPlusPlus26 && NewExpr->getNumPlacementArgs() > 0) {
S.FFDiag(Loc, diag::note_constexpr_new_placement)
<< /*C++26 feature*/ 1 << E->getSourceRange();
} else if (NewExpr->getNumPlacementArgs() == 1 &&
!OperatorNew->isReservedGlobalPlacementOperator()) {
S.FFDiag(Loc, diag::note_constexpr_new_placement)
<< /*Unsupported*/ 0 << E->getSourceRange();
} else if (!OperatorNew->isReplaceableGlobalAllocationFunction()) {
S.FFDiag(Loc, diag::note_constexpr_new_non_replaceable)
<< isa<CXXMethodDecl>(OperatorNew) << OperatorNew;
}
} else {
const auto *DeleteExpr = cast<CXXDeleteExpr>(E);
const FunctionDecl *OperatorDelete = DeleteExpr->getOperatorDelete();
if (!OperatorDelete->isReplaceableGlobalAllocationFunction()) {
S.FFDiag(Loc, diag::note_constexpr_new_non_replaceable)
<< isa<CXXMethodDecl>(OperatorDelete) << OperatorDelete;
}
}

return false;
}

bool Interpret(InterpState &S, APValue &Result) {
// The current stack frame when we started Interpret().
// This is being used by the ops to determine wheter
Expand Down
11 changes: 11 additions & 0 deletions clang/lib/AST/ByteCode/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -2947,6 +2947,17 @@ static inline bool IsConstantContext(InterpState &S, CodePtr OpPC) {
return true;
}

/// Check if the initializer and storage types of a placement-new expression
/// match.
bool CheckNewTypeMismatch(InterpState &S, CodePtr OpPC, const Expr *E,
std::optional<uint64_t> ArraySize = std::nullopt);

template <PrimType Name, class T = typename PrimConv<Name>::T>
bool CheckNewTypeMismatchArray(InterpState &S, CodePtr OpPC, const Expr *E) {
const auto &Size = S.Stk.pop<T>();
return CheckNewTypeMismatch(S, OpPC, E, static_cast<uint64_t>(Size));
}
bool InvalidNewDeleteExpr(InterpState &S, CodePtr OpPC, const Expr *E);
//===----------------------------------------------------------------------===//
// Read opcode arguments
//===----------------------------------------------------------------------===//
Expand Down
11 changes: 10 additions & 1 deletion clang/lib/AST/ByteCode/InterpBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1306,7 +1306,16 @@ static bool interp__builtin_operator_new(InterpState &S, CodePtr OpPC,
return false;
}

// FIXME: CheckArraySize for NumElems?
// NB: The same check we're using in CheckArraySize()
if (NumElems.getActiveBits() >
ConstantArrayType::getMaxSizeBits(S.getASTContext()) ||
NumElems.ugt(Descriptor::MaxArrayElemBytes / ElemSize.getQuantity())) {
// FIXME: NoThrow check?
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.FFDiag(Loc, diag::note_constexpr_new_too_large)
<< NumElems.getZExtValue();
return false;
}

std::optional<PrimType> ElemT = S.getContext().classify(ElemType);
DynamicAllocator &Allocator = S.getAllocator();
Expand Down
16 changes: 14 additions & 2 deletions clang/lib/AST/ByteCode/InterpFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,26 @@ static void print(llvm::raw_ostream &OS, const T &V, ASTContext &ASTCtx,
V.toAPValue(ASTCtx).printPretty(OS, ASTCtx, Ty);
}

static bool shouldSkipInBacktrace(const Function *F) {
if (F->isBuiltin())
return true;
if (F->isLambdaStaticInvoker())
return true;

const FunctionDecl *FD = F->getDecl();
if (FD->getDeclName().getCXXOverloadedOperator() == OO_New ||
FD->getDeclName().getCXXOverloadedOperator() == OO_Array_New)
return true;
return false;
}

void InterpFrame::describe(llvm::raw_ostream &OS) const {
// We create frames for builtin functions as well, but we can't reliably
// diagnose them. The 'in call to' diagnostics for them add no value to the
// user _and_ it doesn't generally work since the argument types don't always
// match the function prototype. Just ignore them.
// Similarly, for lambda static invokers, we would just print __invoke().
if (const auto *F = getFunction();
F && (F->isBuiltin() || F->isLambdaStaticInvoker()))
if (const auto *F = getFunction(); F && shouldSkipInBacktrace(F))
return;

const Expr *CallExpr = Caller->getExpr(getRetPC());
Expand Down
14 changes: 14 additions & 0 deletions clang/lib/AST/ByteCode/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -787,4 +787,18 @@ def Free : Opcode {
let Args = [ArgBool];
}

def CheckNewTypeMismatch : Opcode {
let Args = [ArgExpr];
}

def InvalidNewDeleteExpr : Opcode {
let Args = [ArgExpr];
}

def CheckNewTypeMismatchArray : Opcode {
let Types = [IntegerTypeClass];
let Args = [ArgExpr];
let HasGroup = 1;
}

def IsConstantContext: Opcode;
12 changes: 12 additions & 0 deletions clang/lib/Basic/Targets/RISCV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -486,3 +486,15 @@ bool RISCVTargetInfo::validateCpuSupports(StringRef Feature) const {
bool RISCVTargetInfo::isValidFeatureName(StringRef Name) const {
return llvm::RISCVISAInfo::isSupportedExtensionFeature(Name);
}

bool RISCVTargetInfo::validateGlobalRegisterVariable(
StringRef RegName, unsigned RegSize, bool &HasSizeMismatch) const {
if (RegName == "ra" || RegName == "sp" || RegName == "gp" ||
RegName == "tp" || RegName.starts_with("x") || RegName.starts_with("a") ||
RegName.starts_with("s") || RegName.starts_with("t")) {
unsigned XLen = getTriple().isArch64Bit() ? 64 : 32;
HasSizeMismatch = RegSize != XLen;
return true;
}
return false;
}
3 changes: 3 additions & 0 deletions clang/lib/Basic/Targets/RISCV.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ class RISCVTargetInfo : public TargetInfo {
bool supportsCpuInit() const override { return getTriple().isOSLinux(); }
bool validateCpuSupports(StringRef Feature) const override;
bool isValidFeatureName(StringRef Name) const override;

bool validateGlobalRegisterVariable(StringRef RegName, unsigned RegSize,
bool &HasSizeMismatch) const override;
};
class LLVM_LIBRARY_VISIBILITY RISCV32TargetInfo : public RISCVTargetInfo {
public:
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13649,7 +13649,7 @@ Value *CodeGenFunction::EmitBPFBuiltinExpr(unsigned BuiltinID,
else
InitValStr = std::to_string(InitVal.getZExtValue());
std::string EnumStr = Enumerator->getNameAsString() + ":" + InitValStr;
Value *EnumStrVal = Builder.CreateGlobalStringPtr(EnumStr);
Value *EnumStrVal = Builder.CreateGlobalString(EnumStr);

ConstantInt *Flag = cast<ConstantInt>(EmitScalarExpr(E->getArg(1)));
Value *FlagValue = ConstantInt::get(Int64Ty, Flag->getSExtValue());
Expand Down Expand Up @@ -18175,7 +18175,7 @@ Value *CodeGenFunction::EmitPPCBuiltinExpr(unsigned BuiltinID,
CallOps.push_back(Ops[i]);
llvm::Function *F = CGM.getIntrinsic(ID);
Value *Call = Builder.CreateCall(F, CallOps);
return Builder.CreateAlignedStore(Call, Ops[0], MaybeAlign(64));
return Builder.CreateAlignedStore(Call, Ops[0], MaybeAlign());
}

case PPC::BI__builtin_ppc_compare_and_swap:
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CodeGen/CGExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1807,7 +1807,7 @@ ScalarExprEmitter::VisitSYCLUniqueStableNameExpr(SYCLUniqueStableNameExpr *E) {
ASTContext &Context = CGF.getContext();
unsigned AddrSpace =
Context.getTargetAddressSpace(CGF.CGM.GetGlobalConstantAddressSpace());
llvm::Constant *GlobalConstStr = Builder.CreateGlobalStringPtr(
llvm::Constant *GlobalConstStr = Builder.CreateGlobalString(
E->ComputeName(Context), "__usn_str", AddrSpace);

llvm::Type *ExprTy = ConvertType(E->getType());
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CGStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,7 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
// C99 6.8.4.1: The first substatement is executed if the expression compares
// unequal to 0. The condition must be a scalar type.
LexicalScope ConditionScope(*this, S.getCond()->getSourceRange());
ApplyDebugLocation DL(*this, S.getCond());

if (S.getInit())
EmitStmt(S.getInit());
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/Frontend/MultiplexConsumer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,13 @@ MultiplexConsumer::MultiplexConsumer(
}
}

MultiplexConsumer::MultiplexConsumer(std::unique_ptr<ASTConsumer> C)
: MultiplexConsumer([](std::unique_ptr<ASTConsumer> Consumer) {
std::vector<std::unique_ptr<ASTConsumer>> Consumers;
Consumers.push_back(std::move(Consumer));
return Consumers;
}(std::move(C))) {}

MultiplexConsumer::~MultiplexConsumer() {}

void MultiplexConsumer::Initialize(ASTContext &Context) {
Expand Down
8 changes: 4 additions & 4 deletions clang/lib/Frontend/Rewrite/RewriteObjC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4358,21 +4358,21 @@ Stmt *RewriteObjC::SynthBlockInitExpr(BlockExpr *Exp,
for (unsigned i = 0; i < InnerBlockDeclRefs.size(); i++) {
DeclRefExpr *Exp = InnerBlockDeclRefs[i];
ValueDecl *VD = Exp->getDecl();
if (!VD->hasAttr<BlocksAttr>() && !BlockByCopyDeclsPtrSet.count(VD)) {
if (!VD->hasAttr<BlocksAttr>() &&
BlockByCopyDeclsPtrSet.insert(VD).second) {
// We need to save the copied-in variables in nested
// blocks because it is needed at the end for some of the API
// generations. See SynthesizeBlockLiterals routine.
InnerDeclRefs.push_back(Exp);
countOfInnerDecls++;
BlockDeclRefs.push_back(Exp);
BlockByCopyDeclsPtrSet.insert(VD);
BlockByCopyDecls.push_back(VD);
}
if (VD->hasAttr<BlocksAttr>() && !BlockByRefDeclsPtrSet.count(VD)) {
if (VD->hasAttr<BlocksAttr>() &&
BlockByRefDeclsPtrSet.insert(VD).second) {
InnerDeclRefs.push_back(Exp);
countOfInnerDecls++;
BlockDeclRefs.push_back(Exp);
BlockByRefDeclsPtrSet.insert(VD);
BlockByRefDecls.push_back(VD);
}
}
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Interpreter/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ add_clang_library(clangInterpreter
IncrementalExecutor.cpp
IncrementalParser.cpp
Interpreter.cpp
InterpreterValuePrinter.cpp
InterpreterUtils.cpp
Value.cpp
${WASM_SRC}
Expand Down
21 changes: 12 additions & 9 deletions clang/lib/Interpreter/DeviceOffload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "clang/Basic/TargetOptions.h"
#include "clang/CodeGen/ModuleBuilder.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Interpreter/PartialTranslationUnit.h"

#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
Expand All @@ -24,15 +25,17 @@
namespace clang {

IncrementalCUDADeviceParser::IncrementalCUDADeviceParser(
Interpreter &Interp, std::unique_ptr<CompilerInstance> Instance,
IncrementalParser &HostParser, llvm::LLVMContext &LLVMCtx,
std::unique_ptr<CompilerInstance> DeviceInstance,
CompilerInstance &HostInstance,
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS,
llvm::Error &Err)
: IncrementalParser(Interp, std::move(Instance), LLVMCtx, Err),
HostParser(HostParser), VFS(FS) {
llvm::Error &Err, const std::list<PartialTranslationUnit> &PTUs)
: IncrementalParser(*DeviceInstance, Err), PTUs(PTUs), VFS(FS),
CodeGenOpts(HostInstance.getCodeGenOpts()),
TargetOpts(HostInstance.getTargetOpts()) {
if (Err)
return;
StringRef Arch = CI->getTargetOpts().CPU;
DeviceCI = std::move(DeviceInstance);
StringRef Arch = TargetOpts.CPU;
if (!Arch.starts_with("sm_") || Arch.substr(3).getAsInteger(10, SMVersion)) {
Err = llvm::joinErrors(std::move(Err), llvm::make_error<llvm::StringError>(
"Invalid CUDA architecture",
Expand All @@ -41,7 +44,7 @@ IncrementalCUDADeviceParser::IncrementalCUDADeviceParser(
}
}

llvm::Expected<PartialTranslationUnit &>
llvm::Expected<TranslationUnitDecl *>
IncrementalCUDADeviceParser::Parse(llvm::StringRef Input) {
auto PTU = IncrementalParser::Parse(Input);
if (!PTU)
Expand All @@ -62,7 +65,7 @@ IncrementalCUDADeviceParser::Parse(llvm::StringRef Input) {
llvm::StringRef(FatbinContent.data(), FatbinContent.size()),
"", false));

HostParser.getCI()->getCodeGenOpts().CudaGpuBinaryFileName = FatbinFileName;
CodeGenOpts.CudaGpuBinaryFileName = FatbinFileName;

FatbinContent.clear();

Expand All @@ -80,7 +83,7 @@ llvm::Expected<llvm::StringRef> IncrementalCUDADeviceParser::GeneratePTX() {
std::error_code());
llvm::TargetOptions TO = llvm::TargetOptions();
llvm::TargetMachine *TargetMachine = Target->createTargetMachine(
PTU.TheModule->getTargetTriple(), getCI()->getTargetOpts().CPU, "", TO,
PTU.TheModule->getTargetTriple(), TargetOpts.CPU, "", TO,
llvm::Reloc::Model::PIC_);
PTU.TheModule->setDataLayout(TargetMachine->createDataLayout());

Expand Down
21 changes: 14 additions & 7 deletions clang/lib/Interpreter/DeviceOffload.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,24 @@
#include "llvm/Support/VirtualFileSystem.h"

namespace clang {
struct PartialTranslationUnit;
class CompilerInstance;
class CodeGenOptions;
class TargetOptions;

class IncrementalCUDADeviceParser : public IncrementalParser {
const std::list<PartialTranslationUnit> &PTUs;

public:
IncrementalCUDADeviceParser(
Interpreter &Interp, std::unique_ptr<CompilerInstance> Instance,
IncrementalParser &HostParser, llvm::LLVMContext &LLVMCtx,
std::unique_ptr<CompilerInstance> DeviceInstance,
CompilerInstance &HostInstance,
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS,
llvm::Error &Err);
llvm::Error &Err, const std::list<PartialTranslationUnit> &PTUs);

llvm::Expected<PartialTranslationUnit &>
Parse(llvm::StringRef Input) override;
llvm::Expected<TranslationUnitDecl *> Parse(llvm::StringRef Input) override;

// Generate PTX for the last PTU
// Generate PTX for the last PTU.
llvm::Expected<llvm::StringRef> GeneratePTX();

// Generate fatbinary contents in memory
Expand All @@ -39,11 +44,13 @@ class IncrementalCUDADeviceParser : public IncrementalParser {
~IncrementalCUDADeviceParser();

protected:
IncrementalParser &HostParser;
std::unique_ptr<CompilerInstance> DeviceCI;
int SMVersion;
llvm::SmallString<1024> PTXCode;
llvm::SmallVector<char, 1024> FatbinContent;
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS;
CodeGenOptions &CodeGenOpts; // Intentionally a reference.
const TargetOptions &TargetOpts;
};

} // namespace clang
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Interpreter/IncrementalExecutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,4 @@ IncrementalExecutor::getSymbolAddress(llvm::StringRef Name,
return SymOrErr->getAddress();
}

} // end namespace clang
} // namespace clang
272 changes: 16 additions & 256 deletions clang/lib/Interpreter/IncrementalParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,246 +13,40 @@
#include "IncrementalParser.h"

#include "clang/AST/DeclContextInternals.h"
#include "clang/CodeGen/BackendUtil.h"
#include "clang/CodeGen/CodeGenAction.h"
#include "clang/CodeGen/ModuleBuilder.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/FrontendTool/Utils.h"
#include "clang/Interpreter/Interpreter.h"
#include "clang/Interpreter/PartialTranslationUnit.h"
#include "clang/Parse/Parser.h"
#include "clang/Sema/Sema.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Support/CrashRecoveryContext.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Timer.h"

#include <sstream>

namespace clang {

class IncrementalASTConsumer final : public ASTConsumer {
Interpreter &Interp;
std::unique_ptr<ASTConsumer> Consumer;
// IncrementalParser::IncrementalParser() {}

public:
IncrementalASTConsumer(Interpreter &InterpRef, std::unique_ptr<ASTConsumer> C)
: Interp(InterpRef), Consumer(std::move(C)) {}

bool HandleTopLevelDecl(DeclGroupRef DGR) override final {
if (DGR.isNull())
return true;
if (!Consumer)
return true;

for (Decl *D : DGR)
if (auto *TSD = llvm::dyn_cast<TopLevelStmtDecl>(D);
TSD && TSD->isSemiMissing())
TSD->setStmt(Interp.SynthesizeExpr(cast<Expr>(TSD->getStmt())));

return Consumer->HandleTopLevelDecl(DGR);
}
void HandleTranslationUnit(ASTContext &Ctx) override final {
Consumer->HandleTranslationUnit(Ctx);
}
void HandleInlineFunctionDefinition(FunctionDecl *D) override final {
Consumer->HandleInlineFunctionDefinition(D);
}
void HandleInterestingDecl(DeclGroupRef D) override final {
Consumer->HandleInterestingDecl(D);
}
void HandleTagDeclDefinition(TagDecl *D) override final {
Consumer->HandleTagDeclDefinition(D);
}
void HandleTagDeclRequiredDefinition(const TagDecl *D) override final {
Consumer->HandleTagDeclRequiredDefinition(D);
}
void HandleCXXImplicitFunctionInstantiation(FunctionDecl *D) override final {
Consumer->HandleCXXImplicitFunctionInstantiation(D);
}
void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override final {
Consumer->HandleTopLevelDeclInObjCContainer(D);
}
void HandleImplicitImportDecl(ImportDecl *D) override final {
Consumer->HandleImplicitImportDecl(D);
}
void CompleteTentativeDefinition(VarDecl *D) override final {
Consumer->CompleteTentativeDefinition(D);
}
void CompleteExternalDeclaration(DeclaratorDecl *D) override final {
Consumer->CompleteExternalDeclaration(D);
}
void AssignInheritanceModel(CXXRecordDecl *RD) override final {
Consumer->AssignInheritanceModel(RD);
}
void HandleCXXStaticMemberVarInstantiation(VarDecl *D) override final {
Consumer->HandleCXXStaticMemberVarInstantiation(D);
}
void HandleVTable(CXXRecordDecl *RD) override final {
Consumer->HandleVTable(RD);
}
ASTMutationListener *GetASTMutationListener() override final {
return Consumer->GetASTMutationListener();
}
ASTDeserializationListener *GetASTDeserializationListener() override final {
return Consumer->GetASTDeserializationListener();
}
void PrintStats() override final { Consumer->PrintStats(); }
bool shouldSkipFunctionBody(Decl *D) override final {
return Consumer->shouldSkipFunctionBody(D);
}
static bool classof(const clang::ASTConsumer *) { return true; }
};

/// A custom action enabling the incremental processing functionality.
///
/// The usual \p FrontendAction expects one call to ExecuteAction and once it
/// sees a call to \p EndSourceFile it deletes some of the important objects
/// such as \p Preprocessor and \p Sema assuming no further input will come.
///
/// \p IncrementalAction ensures it keep its underlying action's objects alive
/// as long as the \p IncrementalParser needs them.
///
class IncrementalAction : public WrapperFrontendAction {
private:
bool IsTerminating = false;

public:
IncrementalAction(CompilerInstance &CI, llvm::LLVMContext &LLVMCtx,
llvm::Error &Err)
: WrapperFrontendAction([&]() {
llvm::ErrorAsOutParameter EAO(&Err);
std::unique_ptr<FrontendAction> Act;
switch (CI.getFrontendOpts().ProgramAction) {
default:
Err = llvm::createStringError(
std::errc::state_not_recoverable,
"Driver initialization failed. "
"Incremental mode for action %d is not supported",
CI.getFrontendOpts().ProgramAction);
return Act;
case frontend::ASTDump:
[[fallthrough]];
case frontend::ASTPrint:
[[fallthrough]];
case frontend::ParseSyntaxOnly:
Act = CreateFrontendAction(CI);
break;
case frontend::PluginAction:
[[fallthrough]];
case frontend::EmitAssembly:
[[fallthrough]];
case frontend::EmitBC:
[[fallthrough]];
case frontend::EmitObj:
[[fallthrough]];
case frontend::PrintPreprocessedInput:
[[fallthrough]];
case frontend::EmitLLVMOnly:
Act.reset(new EmitLLVMOnlyAction(&LLVMCtx));
break;
}
return Act;
}()) {}
FrontendAction *getWrapped() const { return WrappedAction.get(); }
TranslationUnitKind getTranslationUnitKind() override {
return TU_Incremental;
}

void ExecuteAction() override {
CompilerInstance &CI = getCompilerInstance();
assert(CI.hasPreprocessor() && "No PP!");

// Use a code completion consumer?
CodeCompleteConsumer *CompletionConsumer = nullptr;
if (CI.hasCodeCompletionConsumer())
CompletionConsumer = &CI.getCodeCompletionConsumer();

Preprocessor &PP = CI.getPreprocessor();
PP.EnterMainSourceFile();

if (!CI.hasSema())
CI.createSema(getTranslationUnitKind(), CompletionConsumer);
}

// Do not terminate after processing the input. This allows us to keep various
// clang objects alive and to incrementally grow the current TU.
void EndSourceFile() override {
// The WrappedAction can be nullptr if we issued an error in the ctor.
if (IsTerminating && getWrapped())
WrapperFrontendAction::EndSourceFile();
}

void FinalizeAction() {
assert(!IsTerminating && "Already finalized!");
IsTerminating = true;
EndSourceFile();
}
};

CodeGenerator *IncrementalParser::getCodeGen() const {
FrontendAction *WrappedAct = Act->getWrapped();
if (!WrappedAct->hasIRSupport())
return nullptr;
return static_cast<CodeGenAction *>(WrappedAct)->getCodeGenerator();
}

IncrementalParser::IncrementalParser() {}

IncrementalParser::IncrementalParser(Interpreter &Interp,
std::unique_ptr<CompilerInstance> Instance,
llvm::LLVMContext &LLVMCtx,
IncrementalParser::IncrementalParser(CompilerInstance &Instance,
llvm::Error &Err)
: CI(std::move(Instance)) {
: S(Instance.getSema()) {
llvm::ErrorAsOutParameter EAO(&Err);
Act = std::make_unique<IncrementalAction>(*CI, LLVMCtx, Err);
if (Err)
return;
CI->ExecuteAction(*Act);

if (getCodeGen())
CachedInCodeGenModule = GenModule();

std::unique_ptr<ASTConsumer> IncrConsumer =
std::make_unique<IncrementalASTConsumer>(Interp, CI->takeASTConsumer());
CI->setASTConsumer(std::move(IncrConsumer));
Consumer = &CI->getASTConsumer();
P.reset(
new Parser(CI->getPreprocessor(), CI->getSema(), /*SkipBodies=*/false));
Consumer = &S.getASTConsumer();
P.reset(new Parser(S.getPreprocessor(), S, /*SkipBodies=*/false));
P->Initialize();

// An initial PTU is needed as CUDA includes some headers automatically
auto PTU = ParseOrWrapTopLevelDecl();
if (auto E = PTU.takeError()) {
consumeError(std::move(E)); // FIXME
return; // PTU.takeError();
}

if (getCodeGen()) {
PTU->TheModule = GenModule();
assert(PTU->TheModule && "Failed to create initial PTU");
}
}

IncrementalParser::~IncrementalParser() {
P.reset();
Act->FinalizeAction();
}
IncrementalParser::~IncrementalParser() { P.reset(); }

llvm::Expected<PartialTranslationUnit &>
llvm::Expected<TranslationUnitDecl *>
IncrementalParser::ParseOrWrapTopLevelDecl() {
// Recover resources if we crash before exiting this method.
Sema &S = CI->getSema();
llvm::CrashRecoveryContextCleanupRegistrar<Sema> CleanupSema(&S);
Sema::GlobalEagerInstantiationScope GlobalInstantiations(S, /*Enabled=*/true);
Sema::LocalEagerInstantiationScope LocalInstantiations(S);

PTUs.emplace_back(PartialTranslationUnit());
PartialTranslationUnit &LastPTU = PTUs.back();
// Add a new PTU.
ASTContext &C = S.getASTContext();
C.addTranslationUnitDecl();
LastPTU.TUPart = C.getTranslationUnitDecl();

// Skip previous eof due to last incremental input.
if (P->getCurToken().is(tok::annot_repl_input_end)) {
Expand All @@ -276,11 +70,9 @@ IncrementalParser::ParseOrWrapTopLevelDecl() {
std::error_code());
}

DiagnosticsEngine &Diags = getCI()->getDiagnostics();
DiagnosticsEngine &Diags = S.getDiagnostics();
if (Diags.hasErrorOccurred()) {
PartialTranslationUnit MostRecentPTU = {C.getTranslationUnitDecl(),
nullptr};
CleanUpPTU(MostRecentPTU);
CleanUpPTU(C.getTranslationUnitDecl());

Diags.Reset(/*soft=*/true);
Diags.getClient()->clear();
Expand All @@ -299,12 +91,12 @@ IncrementalParser::ParseOrWrapTopLevelDecl() {

Consumer->HandleTranslationUnit(C);

return LastPTU;
return C.getTranslationUnitDecl();
}

llvm::Expected<PartialTranslationUnit &>
llvm::Expected<TranslationUnitDecl *>
IncrementalParser::Parse(llvm::StringRef input) {
Preprocessor &PP = CI->getPreprocessor();
Preprocessor &PP = S.getPreprocessor();
assert(PP.isIncrementalProcessingEnabled() && "Not in incremental mode!?");

std::ostringstream SourceName;
Expand All @@ -320,7 +112,7 @@ IncrementalParser::Parse(llvm::StringRef input) {
memcpy(MBStart, input.data(), InputSize);
MBStart[InputSize] = '\n';

SourceManager &SM = CI->getSourceManager();
SourceManager &SM = S.getSourceManager();

// FIXME: Create SourceLocation, which will allow clang to order the overload
// candidates for example
Expand Down Expand Up @@ -356,37 +148,10 @@ IncrementalParser::Parse(llvm::StringRef input) {
"Lexer must be EOF when starting incremental parse!");
}

if (std::unique_ptr<llvm::Module> M = GenModule())
PTU->TheModule = std::move(M);

return PTU;
}

std::unique_ptr<llvm::Module> IncrementalParser::GenModule() {
static unsigned ID = 0;
if (CodeGenerator *CG = getCodeGen()) {
// Clang's CodeGen is designed to work with a single llvm::Module. In many
// cases for convenience various CodeGen parts have a reference to the
// llvm::Module (TheModule or Module) which does not change when a new
// module is pushed. However, the execution engine wants to take ownership
// of the module which does not map well to CodeGen's design. To work this
// around we created an empty module to make CodeGen happy. We should make
// sure it always stays empty.
assert((!CachedInCodeGenModule ||
(CachedInCodeGenModule->empty() &&
CachedInCodeGenModule->global_empty() &&
CachedInCodeGenModule->alias_empty() &&
CachedInCodeGenModule->ifunc_empty())) &&
"CodeGen wrote to a readonly module");
std::unique_ptr<llvm::Module> M(CG->ReleaseModule());
CG->StartModule("incr_module_" + std::to_string(ID++), M->getContext());
return M;
}
return nullptr;
}

void IncrementalParser::CleanUpPTU(PartialTranslationUnit &PTU) {
TranslationUnitDecl *MostRecentTU = PTU.TUPart;
void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) {
if (StoredDeclsMap *Map = MostRecentTU->getPrimaryContext()->getLookupPtr()) {
for (auto &&[Key, List] : *Map) {
DeclContextLookupResult R = List.getLookupResult();
Expand Down Expand Up @@ -415,13 +180,8 @@ void IncrementalParser::CleanUpPTU(PartialTranslationUnit &PTU) {
// Check if we need to clean up the IdResolver chain.
if (ND->getDeclName().getFETokenInfo() && !D->getLangOpts().ObjC &&
!D->getLangOpts().CPlusPlus)
getCI()->getSema().IdResolver.RemoveDecl(ND);
S.IdResolver.RemoveDecl(ND);
}
}

llvm::StringRef IncrementalParser::GetMangledName(GlobalDecl GD) const {
CodeGenerator *CG = getCodeGen();
assert(CG);
return CG->GetMangledName(GD);
}
} // end namespace clang
53 changes: 11 additions & 42 deletions clang/lib/Interpreter/IncrementalParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,37 +13,27 @@
#ifndef LLVM_CLANG_LIB_INTERPRETER_INCREMENTALPARSER_H
#define LLVM_CLANG_LIB_INTERPRETER_INCREMENTALPARSER_H

#include "clang/AST/GlobalDecl.h"
#include "clang/Interpreter/PartialTranslationUnit.h"

#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"

#include <list>
#include <memory>
namespace llvm {
class LLVMContext;
class Module;
} // namespace llvm

namespace clang {
class ASTConsumer;
class CodeGenerator;
class CompilerInstance;
class IncrementalAction;
class Interpreter;
class Parser;
class Sema;
class TranslationUnitDecl;

/// Provides support for incremental compilation. Keeps track of the state
/// changes between the subsequent incremental input.
///
class IncrementalParser {
protected:
/// Long-lived, incremental parsing action.
std::unique_ptr<IncrementalAction> Act;

/// Compiler instance performing the incremental compilation.
std::unique_ptr<CompilerInstance> CI;
/// The Sema performing the incremental compilation.
Sema &S;

/// Parser.
std::unique_ptr<Parser> P;
Expand All @@ -54,42 +44,21 @@ class IncrementalParser {
/// Counts the number of direct user input lines that have been parsed.
unsigned InputCount = 0;

/// List containing every information about every incrementally parsed piece
/// of code.
std::list<PartialTranslationUnit> PTUs;

/// When CodeGen is created the first llvm::Module gets cached in many places
/// and we must keep it alive.
std::unique_ptr<llvm::Module> CachedInCodeGenModule;

IncrementalParser();
// IncrementalParser();

public:
IncrementalParser(Interpreter &Interp,
std::unique_ptr<CompilerInstance> Instance,
llvm::LLVMContext &LLVMCtx, llvm::Error &Err);
IncrementalParser(CompilerInstance &Instance, llvm::Error &Err);
virtual ~IncrementalParser();

CompilerInstance *getCI() { return CI.get(); }
CodeGenerator *getCodeGen() const;

/// Parses incremental input by creating an in-memory file.
///\returns a \c PartialTranslationUnit which holds information about the
/// \c TranslationUnitDecl and \c llvm::Module corresponding to the input.
virtual llvm::Expected<PartialTranslationUnit &> Parse(llvm::StringRef Input);

/// Uses the CodeGenModule mangled name cache and avoids recomputing.
///\returns the mangled name of a \c GD.
llvm::StringRef GetMangledName(GlobalDecl GD) const;

void CleanUpPTU(PartialTranslationUnit &PTU);

std::list<PartialTranslationUnit> &getPTUs() { return PTUs; }
/// \c TranslationUnitDecl.
virtual llvm::Expected<TranslationUnitDecl *> Parse(llvm::StringRef Input);

std::unique_ptr<llvm::Module> GenModule();
void CleanUpPTU(TranslationUnitDecl *MostRecentTU);

private:
llvm::Expected<PartialTranslationUnit &> ParseOrWrapTopLevelDecl();
llvm::Expected<TranslationUnitDecl *> ParseOrWrapTopLevelDecl();
};
} // end namespace clang

Expand Down
650 changes: 214 additions & 436 deletions clang/lib/Interpreter/Interpreter.cpp

Large diffs are not rendered by default.

400 changes: 400 additions & 0 deletions clang/lib/Interpreter/InterpreterValuePrinter.cpp

Large diffs are not rendered by default.

19 changes: 17 additions & 2 deletions clang/lib/Sema/CheckExprLifetime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ enum LifetimeKind {
/// the entity is a return object.
LK_Return,

/// The lifetime of a temporary bound to this entity ends too soon, because
/// the entity passed to a musttail function call.
LK_MustTail,

/// The lifetime of a temporary bound to this entity ends too soon, because
/// the entity is the result of a statement expression.
LK_StmtExprResult,
Expand Down Expand Up @@ -1150,6 +1154,7 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
break;

case LK_Return:
case LK_MustTail:
case LK_StmtExprResult:
if (auto *DRE = dyn_cast<DeclRefExpr>(L)) {
// We can't determine if the local variable outlives the statement
Expand All @@ -1158,7 +1163,8 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
return false;
SemaRef.Diag(DiagLoc, diag::warn_ret_stack_addr_ref)
<< InitEntity->getType()->isReferenceType() << DRE->getDecl()
<< isa<ParmVarDecl>(DRE->getDecl()) << DiagRange;
<< isa<ParmVarDecl>(DRE->getDecl()) << (LK == LK_MustTail)
<< DiagRange;
} else if (isa<BlockExpr>(L)) {
SemaRef.Diag(DiagLoc, diag::err_ret_local_block) << DiagRange;
} else if (isa<AddrLabelExpr>(L)) {
Expand All @@ -1170,7 +1176,7 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
} else if (auto *CLE = dyn_cast<CompoundLiteralExpr>(L)) {
SemaRef.Diag(DiagLoc, diag::warn_ret_stack_addr_ref)
<< InitEntity->getType()->isReferenceType() << CLE->getInitializer()
<< 2 << DiagRange;
<< 2 << (LK == LK_MustTail) << DiagRange;
} else {
// P2748R5: Disallow Binding a Returned Glvalue to a Temporary.
// [stmt.return]/p6: In a function whose return type is a reference,
Expand All @@ -1181,6 +1187,9 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
InitEntity->getType()->isReferenceType())
SemaRef.Diag(DiagLoc, diag::err_ret_local_temp_ref)
<< InitEntity->getType()->isReferenceType() << DiagRange;
else if (LK == LK_MustTail)
SemaRef.Diag(DiagLoc, diag::warn_musttail_local_temp_addr_ref)
<< InitEntity->getType()->isReferenceType() << DiagRange;
else
SemaRef.Diag(DiagLoc, diag::warn_ret_local_temp_addr_ref)
<< InitEntity->getType()->isReferenceType() << DiagRange;
Expand Down Expand Up @@ -1265,6 +1274,12 @@ void checkExprLifetime(Sema &SemaRef, const InitializedEntity &Entity,
/*AEntity*/ nullptr, Init);
}

void checkExprLifetimeMustTailArg(Sema &SemaRef,
const InitializedEntity &Entity, Expr *Init) {
checkExprLifetimeImpl(SemaRef, &Entity, nullptr, LK_MustTail,
/*AEntity*/ nullptr, Init);
}

void checkExprLifetime(Sema &SemaRef, const AssignedEntity &Entity,
Expr *Init) {
bool EnableDanglingPointerAssignment = !SemaRef.getDiagnostics().isIgnored(
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Sema/CheckExprLifetime.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ void checkExprLifetime(Sema &SemaRef, const InitializedEntity &Entity,
/// sufficient for assigning to the entity.
void checkExprLifetime(Sema &SemaRef, const AssignedEntity &Entity, Expr *Init);

/// Check that the lifetime of the given expr (and its subobjects) is
/// sufficient, assuming that it is passed as an argument to a musttail
/// function.
void checkExprLifetimeMustTailArg(Sema &SemaRef,
const InitializedEntity &Entity, Expr *Init);

} // namespace clang::sema

#endif // LLVM_CLANG_SEMA_CHECK_EXPR_LIFETIME_H
10 changes: 10 additions & 0 deletions clang/lib/Sema/SemaStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//

#include "CheckExprLifetime.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTDiagnostic.h"
#include "clang/AST/ASTLambda.h"
Expand Down Expand Up @@ -889,6 +890,15 @@ bool Sema::checkMustTailAttr(const Stmt *St, const Attr &MTA) {
return false;
}

// The lifetimes of locals and incoming function parameters must end before
// the call, because we can't have a stack frame to store them, so diagnose
// any pointers or references to them passed into the musttail call.
for (auto ArgExpr : CE->arguments()) {
InitializedEntity Entity = InitializedEntity::InitializeParameter(
Context, ArgExpr->getType(), false);
checkExprLifetimeMustTailArg(*this, Entity, const_cast<Expr *>(ArgExpr));
}

return true;
}

Expand Down
1 change: 1 addition & 0 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8631,6 +8631,7 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
return SkipBody->Previous;

Specialization->setInvalidDecl(Invalid);
inferGslOwnerPointerAttribute(Specialization);
return Specialization;
}

Expand Down
4 changes: 4 additions & 0 deletions clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,10 @@ static std::optional<std::string> printReferrer(const MemRegion *Referrer) {
// warn_bind_ref_member_to_parameter or
// warn_init_ptr_member_to_parameter_addr
return std::nullopt;
} else if (isa<AllocaRegion>(Referrer)) {
// Skip alloca() regions, they indicate advanced memory management
// and higher likelihood of CSA false positives.
return std::nullopt;
} else {
assert(false && "Unexpected referrer region type.");
return std::nullopt;
Expand Down
2 changes: 0 additions & 2 deletions clang/test/AST/ByteCode/cxx20.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -596,8 +596,6 @@ namespace ImplicitFunction {
// both-note {{in call to 'callMe()'}}
}

/// FIXME: Unfortunately, the similar tests in test/SemaCXX/{compare-cxx2a.cpp use member pointers,
/// which we don't support yet.
namespace std {
class strong_ordering {
public:
Expand Down
63 changes: 47 additions & 16 deletions clang/test/AST/ByteCode/new-delete.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,12 +241,10 @@ namespace std {



/// FIXME: The new interpreter produces the wrong diagnostic.
namespace PlacementNew {
constexpr int foo() { // both-error {{never produces a constant expression}}
char c[sizeof(int)];
new (c) int{12}; // ref-note {{this placement new expression is not supported in constant expressions before C++2c}} \
// expected-note {{subexpression not valid in a constant expression}}
new (c) int{12}; // both-note {{this placement new expression is not supported in constant expressions before C++2c}}
return 0;
}
}
Expand Down Expand Up @@ -305,41 +303,38 @@ namespace placement_new_delete {
}
static_assert(ok());

/// FIXME: Diagnosting placement new.
constexpr bool bad(int which) {
switch (which) {
case 0:
delete new (placement_new_arg{}) int; // ref-note {{this placement new expression is not supported in constant expressions}} \
// expected-note {{subexpression not valid in a constant expression}}
delete new (placement_new_arg{}) int; // both-note {{this placement new expression is not supported in constant expressions}}
break;

case 1:
delete new ClassSpecificNew; // ref-note {{call to class-specific 'operator new'}}
delete new ClassSpecificNew; // both-note {{call to class-specific 'operator new'}}
break;

case 2:
delete new ClassSpecificDelete; // ref-note {{call to class-specific 'operator delete'}}
delete new ClassSpecificDelete; // both-note {{call to class-specific 'operator delete'}}
break;

case 3:
delete new DestroyingDelete; // ref-note {{call to class-specific 'operator delete'}}
delete new DestroyingDelete; // both-note {{call to class-specific 'operator delete'}}
break;

case 4:
// FIXME: This technically follows the standard's rules, but it seems
// unreasonable to expect implementations to support this.
delete new (std::align_val_t{64}) Overaligned; // ref-note {{this placement new expression is not supported in constant expressions}} \
// expected-note {{subexpression not valid in a constant expression}}
delete new (std::align_val_t{64}) Overaligned; // both-note {{this placement new expression is not supported in constant expressions}}
break;
}

return true;
}
static_assert(bad(0)); // both-error {{constant expression}} \
// both-note {{in call}}
static_assert(bad(1)); // ref-error {{constant expression}} ref-note {{in call}}
static_assert(bad(2)); // ref-error {{constant expression}} ref-note {{in call}}
static_assert(bad(3)); // ref-error {{constant expression}} ref-note {{in call}}
static_assert(bad(1)); // both-error {{constant expression}} both-note {{in call}}
static_assert(bad(2)); // both-error {{constant expression}} both-note {{in call}}
static_assert(bad(3)); // both-error {{constant expression}} both-note {{in call}}
static_assert(bad(4)); // both-error {{constant expression}} \
// both-note {{in call}}
}
Expand Down Expand Up @@ -586,13 +581,13 @@ constexpr void use_after_free_2() { // both-error {{never produces a constant ex
p->f(); // both-note {{member call on heap allocated object that has been deleted}}
}


/// std::allocator definition
namespace std {
using size_t = decltype(sizeof(0));
template<typename T> struct allocator {
constexpr T *allocate(size_t N) {
return (T*)__builtin_operator_new(sizeof(T) * N); // both-note 2{{allocation performed here}}
return (T*)__builtin_operator_new(sizeof(T) * N); // both-note 2{{allocation performed here}} \
// #alloc
}
constexpr void deallocate(void *p) {
__builtin_operator_delete(p); // both-note 2{{std::allocator<...>::deallocate' used to delete pointer to object allocated with 'new'}} \
Expand Down Expand Up @@ -731,6 +726,42 @@ namespace Limits {
return n;
}
static_assert(dynarray<char>(5, 0) == 'f');


#if __LP64__
template <typename T>
struct S {
constexpr S(unsigned long long N)
: data(nullptr){
data = alloc.allocate(N); // both-note {{in call to 'this->alloc.allocate(18446744073709551615)}}
}
constexpr T operator[](std::size_t i) const {
return data[i];
}

constexpr ~S() {
alloc.deallocate(data);
}
std::allocator<T> alloc;
T* data;
};

constexpr std::size_t s = S<std::size_t>(~0UL)[42]; // both-error {{constexpr variable 's' must be initialized by a constant expression}} \
// both-note@#alloc {{cannot allocate array; evaluated array bound 2305843009213693951 is too large}} \
// both-note {{in call to}}
#endif
}

/// Just test that we reject placement-new expressions before C++2c.
/// Tests for successful expressions are in placement-new.cpp
namespace Placement {
consteval auto ok1() { // both-error {{never produces a constant expression}}
bool b;
new (&b) bool(true); // both-note 2{{this placement new expression is not supported in constant expressions before C++2c}}
return b;
}
static_assert(ok1()); // both-error {{not an integral constant expression}} \
// both-note {{in call to}}
}

#else
Expand Down
219 changes: 219 additions & 0 deletions clang/test/AST/ByteCode/placement-new.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
// RUN: %clang_cc1 -std=c++2c -fcxx-exceptions -fexperimental-new-constant-interpreter -verify=expected,both %s
// RUN: %clang_cc1 -std=c++2c -fcxx-exceptions -verify=ref,both %s

namespace std {
using size_t = decltype(sizeof(0));
}

void *operator new(std::size_t, void *p) { return p; }
void* operator new[] (std::size_t, void* p) {return p;}


consteval auto ok1() {
bool b;
new (&b) bool(true);
return b;
}
static_assert(ok1());

consteval auto ok2() {
int b;
new (&b) int(12);
return b;
}
static_assert(ok2() == 12);


consteval auto ok3() {
float b;
new (&b) float(12.0);
return b;
}
static_assert(ok3() == 12.0);


consteval auto ok4() {
_BitInt(11) b;
new (&b) _BitInt(11)(37);
return b;
}
static_assert(ok4() == 37);

/// FIXME: Broken in both interpreters.
#if 0
consteval int ok5() {
int i;
new (&i) int[1]{1}; // expected-note {{assignment to dereferenced one-past-the-end pointer}}
return i;
}
static_assert(ok5() == 1); // expected-error {{not an integral constant expression}} \
// expected-note {{in call to}}
#endif

/// FIXME: Crashes the current interpreter.
#if 0
consteval int ok6() {
int i[2];
new (&i) int(100);
return i[0];
}
static_assert(ok6() == 100);
#endif

consteval int ok6() {
int i[2];
new (i) int(100);
new (i + 1) int(200);
return i[0] + i[1];
}
static_assert(ok6() == 300);


consteval auto fail1() {
int b;
new (&b) float(1.0); // both-note {{placement new would change type of storage from 'int' to 'float'}}
return b;
}
static_assert(fail1() == 0); // both-error {{not an integral constant expression}} \
// both-note {{in call to}}

consteval int fail2() {
int i;
new (static_cast<void*>(&i)) float(0); // both-note {{placement new would change type of storage from 'int' to 'float'}}
return 0;
}
static_assert(fail2() == 0); // both-error {{not an integral constant expression}} \
// both-note {{in call to}}

consteval int indeterminate() {
int * indeterminate;
new (indeterminate) int(0); // both-note {{read of uninitialized object is not allowed in a constant expression}}
return 0;
}
static_assert(indeterminate() == 0); // both-error {{not an integral constant expression}} \
// both-note {{in call to}}

consteval int array1() {
int i[2];
new (&i) int[]{1,2};
return i[0] + i[1];
}
static_assert(array1() == 3);

consteval int array2() {
int i[2];
new (static_cast<void*>(&i)) int[]{1,2};
return i[0] + i[1];
}
static_assert(array2() == 3);

consteval int array3() {
int i[1];
new (&i) int[2]; // both-note {{placement new would change type of storage from 'int[1]' to 'int[2]'}}
return 0;
}
static_assert(array3() == 0); // both-error {{not an integral constant expression}} \
// both-note {{in call to}}

consteval int array4() {
int i[2];
new (&i) int[]{12};
return i[0];
}
static_assert(array4() == 12);

constexpr int *intptr() {
return new int;
}
constexpr bool yay() {
int *ptr = new (intptr()) int(42);
bool ret = *ptr == 42;
delete ptr;
return ret;
}
static_assert(yay());


constexpr bool blah() {
int *ptr = new (intptr()) int[3]{ 1, 2, 3 }; // both-note {{placement new would change type of storage from 'int' to 'int[3]'}}
bool ret = ptr[0] == 1 && ptr[1] == 2 && ptr[2] == 3;
delete [] ptr;
return ret;
}
static_assert(blah()); // both-error {{not an integral constant expression}} \
// both-note {{in call to 'blah()'}}


constexpr int *get_indeterminate() {
int *evil;
return evil; // both-note {{read of uninitialized object is not allowed in a constant expression}}
}

constexpr bool bleh() {
int *ptr = new (get_indeterminate()) int; // both-note {{in call to 'get_indeterminate()'}}
return true;
}
static_assert(bleh()); // both-error {{not an integral constant expression}} \
// both-note {{in call to 'bleh()'}}

namespace records {
class S {
public:
float f;
};

constexpr bool record1() {
S s(13);
new (&s) S(42);
return s.f == 42;
}
static_assert(record1());

S GlobalS;
constexpr bool record2() {
new (&GlobalS) S(42); // both-note {{a constant expression cannot modify an object that is visible outside that expression}}
return GlobalS.f == 42;
}
static_assert(record2()); // both-error {{not an integral constant expression}} \
// both-note {{in call to}}


constexpr bool record3() {
S ss[3];

new (&ss) S[]{{1}, {2}, {3}};

return ss[0].f == 1 && ss[1].f == 2 && ss[2].f == 3;
}
static_assert(record3());

struct F {
float f;
};
struct R {
F f;
int a;
};
constexpr bool record4() {
R r;
new (&r.f) F{42.0};
new (&r.a) int(12);

return r.f.f == 42.0 && r.a == 12;
}
static_assert(record4());

/// Destructor is NOT called.
struct A {
bool b;
constexpr ~A() { if (b) throw; }
};

constexpr int foo() {
A a;
new (&a) A(true);
new (&a) A(false);
return 0;
}
static_assert(foo() == 0);
}
33 changes: 32 additions & 1 deletion clang/test/Analysis/stack-addr-ps.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s -Wno-undefined-bool-conversion
// RUN: %clang_analyze_cc1 \
// RUN: -analyzer-checker=core,debug.ExprInspection \
// RUN: -verify %s \
// RUN: -Wno-undefined-bool-conversion
// RUN: %clang_analyze_cc1 \
// RUN: -analyzer-checker=core,debug.ExprInspection,unix.Malloc \
// RUN: -verify %s \
// RUN: -Wno-undefined-bool-conversion
// unix.Malloc is necessary to model __builtin_alloca,
// which could trigger an "unexpected region" bug in StackAddrEscapeChecker.

typedef __INTPTR_TYPE__ intptr_t;

template <typename T>
void clang_analyzer_dump(T x);

using size_t = decltype(sizeof(int));
void * malloc(size_t size);
void free(void*);

const int& g() {
int s;
return s; // expected-warning{{Address of stack memory associated with local variable 's' returned}} expected-warning{{reference to stack memory associated with local variable 's' returned}}
Expand Down Expand Up @@ -846,3 +859,21 @@ void top(char **p) {
foo(); // no-warning FIXME: p binding is reclaimed before the function end
}
} // namespace early_reclaim_dead_limitation

namespace alloca_region_pointer {
void callee(char **pptr) {
char local;
*pptr = &local;
} // no crash

void top_alloca_no_crash_fn() {
char **pptr = (char**)__builtin_alloca(sizeof(char*));
callee(pptr);
}

void top_malloc_no_crash_fn() {
char **pptr = (char**)malloc(sizeof(char*));
callee(pptr);
free(pptr);
}
} // namespace alloca_region_pointer
2 changes: 1 addition & 1 deletion clang/test/CodeGen/PowerPC/builtins-ppc-build-pair-mma.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ void test1(unsigned char *vqp, unsigned char *vpp, vector unsigned char vc1, vec
// CHECK-LE-NOOPT-NEXT: [[TMP4:%.*]] = load <16 x i8>, ptr [[VC1_ADDR]], align 16
// CHECK-LE-NOOPT-NEXT: [[TMP5:%.*]] = load <16 x i8>, ptr [[VC2_ADDR]], align 16
// CHECK-LE-NOOPT-NEXT: [[TMP6:%.*]] = call <256 x i1> @llvm.ppc.vsx.assemble.pair(<16 x i8> [[TMP5]], <16 x i8> [[TMP4]])
// CHECK-LE-NOOPT-NEXT: store <256 x i1> [[TMP6]], ptr [[RES]], align 64
// CHECK-LE-NOOPT-NEXT: store <256 x i1> [[TMP6]], ptr [[RES]], align 32
// CHECK-LE-NOOPT-NEXT: [[TMP7:%.*]] = load <256 x i1>, ptr [[RES]], align 32
// CHECK-LE-NOOPT-NEXT: [[TMP8:%.*]] = load ptr, ptr [[RESP_ADDR]], align 8
// CHECK-LE-NOOPT-NEXT: store <256 x i1> [[TMP7]], ptr [[TMP8]], align 32
Expand Down
8 changes: 4 additions & 4 deletions clang/test/CodeGen/PowerPC/builtins-ppc-pair-mma-types.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,11 @@ void testVQLocal(int *ptr, vector unsigned char vc) {
// CHECK-NEXT: [[TMP3:%.*]] = load <16 x i8>, ptr [[VC_ADDR]], align 16
// CHECK-NEXT: [[TMP4:%.*]] = load <16 x i8>, ptr [[VC_ADDR]], align 16
// CHECK-NEXT: [[TMP5:%.*]] = call <256 x i1> @llvm.ppc.vsx.assemble.pair(<16 x i8> [[TMP3]], <16 x i8> [[TMP4]])
// CHECK-NEXT: store <256 x i1> [[TMP5]], ptr [[VP2]], align 64
// CHECK-NEXT: store <256 x i1> [[TMP5]], ptr [[VP2]], align 32
// CHECK-NEXT: [[TMP6:%.*]] = load <16 x i8>, ptr [[VC_ADDR]], align 16
// CHECK-NEXT: [[TMP7:%.*]] = load <16 x i8>, ptr [[VC_ADDR]], align 16
// CHECK-NEXT: [[TMP8:%.*]] = call <256 x i1> @llvm.ppc.vsx.assemble.pair(<16 x i8> [[TMP7]], <16 x i8> [[TMP6]])
// CHECK-NEXT: store <256 x i1> [[TMP8]], ptr [[VP2]], align 64
// CHECK-NEXT: store <256 x i1> [[TMP8]], ptr [[VP2]], align 32
// CHECK-NEXT: [[TMP9:%.*]] = load <256 x i1>, ptr [[VP3]], align 32
// CHECK-NEXT: [[TMP10:%.*]] = load <16 x i8>, ptr [[VC_ADDR]], align 16
// CHECK-NEXT: [[TMP11:%.*]] = call <512 x i1> @llvm.ppc.mma.xvf64ger(<256 x i1> [[TMP9]], <16 x i8> [[TMP10]])
Expand Down Expand Up @@ -118,11 +118,11 @@ void testVQLocal(int *ptr, vector unsigned char vc) {
// CHECK-BE-NEXT: [[TMP3:%.*]] = load <16 x i8>, ptr [[VC_ADDR]], align 16
// CHECK-BE-NEXT: [[TMP4:%.*]] = load <16 x i8>, ptr [[VC_ADDR]], align 16
// CHECK-BE-NEXT: [[TMP5:%.*]] = call <256 x i1> @llvm.ppc.vsx.assemble.pair(<16 x i8> [[TMP3]], <16 x i8> [[TMP4]])
// CHECK-BE-NEXT: store <256 x i1> [[TMP5]], ptr [[VP2]], align 64
// CHECK-BE-NEXT: store <256 x i1> [[TMP5]], ptr [[VP2]], align 32
// CHECK-BE-NEXT: [[TMP6:%.*]] = load <16 x i8>, ptr [[VC_ADDR]], align 16
// CHECK-BE-NEXT: [[TMP7:%.*]] = load <16 x i8>, ptr [[VC_ADDR]], align 16
// CHECK-BE-NEXT: [[TMP8:%.*]] = call <256 x i1> @llvm.ppc.vsx.assemble.pair(<16 x i8> [[TMP6]], <16 x i8> [[TMP7]])
// CHECK-BE-NEXT: store <256 x i1> [[TMP8]], ptr [[VP2]], align 64
// CHECK-BE-NEXT: store <256 x i1> [[TMP8]], ptr [[VP2]], align 32
// CHECK-BE-NEXT: [[TMP9:%.*]] = load <256 x i1>, ptr [[VP3]], align 32
// CHECK-BE-NEXT: [[TMP10:%.*]] = load <16 x i8>, ptr [[VC_ADDR]], align 16
// CHECK-BE-NEXT: [[TMP11:%.*]] = call <512 x i1> @llvm.ppc.mma.xvf64ger(<256 x i1> [[TMP9]], <16 x i8> [[TMP10]])
Expand Down
45 changes: 45 additions & 0 deletions clang/test/CodeGenCXX/debug-info-line-if-2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// RUN: %clang_cc1 -debug-info-kind=limited -gno-column-info -triple=x86_64-pc-linux -emit-llvm %s -o - | FileCheck %s

// The important thing is that the compare and the conditional branch have
// locs with the same scope (the lexical block for the 'if'). By turning off
// column info, they end up with the same !dbg record, which halves the number
// of checks to verify the scope.

int c = 2;

int f() {
#line 100
if (int a = 5; a > c)
return 1;
return 0;
}
// CHECK-LABEL: define {{.*}} @_Z1fv()
// CHECK: = icmp {{.*}} !dbg [[F_CMP:![0-9]+]]
// CHECK-NEXT: br i1 {{.*}} !dbg [[F_CMP]]

int g() {
#line 200
if (int a = f())
return 2;
return 3;
}
// CHECK-LABEL: define {{.*}} @_Z1gv()
// CHECK: = icmp {{.*}} !dbg [[G_CMP:![0-9]+]]
// CHECK-NEXT: br i1 {{.*}} !dbg [[G_CMP]]

int h() {
#line 300
if (c > 3)
return 4;
return 5;
}
// CHECK-LABEL: define {{.*}} @_Z1hv()
// CHECK: = icmp {{.*}} !dbg [[H_CMP:![0-9]+]]
// CHECK-NEXT: br i1 {{.*}} !dbg [[H_CMP]]

// CHECK-DAG: [[F_CMP]] = !DILocation(line: 100, scope: [[F_SCOPE:![0-9]+]]
// CHECK-DAG: [[F_SCOPE]] = distinct !DILexicalBlock({{.*}} line: 100)
// CHECK-DAG: [[G_CMP]] = !DILocation(line: 200, scope: [[G_SCOPE:![0-9]+]]
// CHECK-DAG: [[G_SCOPE]] = distinct !DILexicalBlock({{.*}} line: 200)
// CHECK-DAG: [[H_CMP]] = !DILocation(line: 300, scope: [[H_SCOPE:![0-9]+]]
// CHECK-DAG: [[H_SCOPE]] = distinct !DILexicalBlock({{.*}} line: 300)
6 changes: 1 addition & 5 deletions clang/test/CodeGenOpenCL/builtins-amdgcn.cl
Original file line number Diff line number Diff line change
Expand Up @@ -638,11 +638,7 @@ void test_get_workgroup_size(int d, global int *out)

// CHECK-LABEL: @test_get_grid_size(
// CHECK: {{.*}}call align 4 dereferenceable(64){{.*}} ptr addrspace(4) @llvm.amdgcn.dispatch.ptr()
// CHECK: getelementptr inbounds i8, ptr addrspace(4) %{{.*}}, i64 12
// CHECK: load i32, ptr addrspace(4) %{{.*}}, align 4, !invariant.load
// CHECK: getelementptr inbounds i8, ptr addrspace(4) %{{.*}}, i64 16
// CHECK: load i32, ptr addrspace(4) %{{.*}}, align 4, !invariant.load
// CHECK: getelementptr inbounds i8, ptr addrspace(4) %{{.*}}, i64 20
// CHECK: getelementptr inbounds i8, ptr addrspace(4) %{{.*}}, i64 %.sink
// CHECK: load i32, ptr addrspace(4) %{{.*}}, align 4, !invariant.load
void test_get_grid_size(int d, global int *out)
{
Expand Down
17 changes: 15 additions & 2 deletions clang/test/Sema/riscv-asm.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// RUN: %clang_cc1 %s -triple riscv32 -verify -fsyntax-only
// RUN: %clang_cc1 %s -triple riscv64 -verify -fsyntax-only

// expected-no-diagnostics

void i (void) {
asm volatile ("" ::: "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7");
asm volatile ("" ::: "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15");
Expand All @@ -26,3 +24,18 @@ void f (void) {
asm volatile ("" ::: "fa6", "fa7", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7");
asm volatile ("" ::: "fs8", "fs9", "fs10", "fs11", "ft8", "ft9", "ft10", "ft11");
}

register char i1 __asm__ ("x1"); // expected-error {{size of register 'x1' does not match variable size}}
#if __riscv_xlen == 32
register long long ll2 __asm__ ("x2"); // expected-error {{size of register 'x2' does not match variable size}}
register int i2 __asm__ ("x3");
#endif
register long l3 __asm__ ("x4");
register long ra __asm__ ("ra");
register long sp __asm__ ("sp");
register int *gp __asm__ ("gp");
register char *tp __asm__ ("tp");
register long a7 __asm__ ("a7");
register long s11 __asm__ ("s11");
register long t5 __asm__ ("t5");
register long* f1 __asm__ ("f1"); // expected-error {{register 'f1' unsuitable for global register variables on this target}}
17 changes: 17 additions & 0 deletions clang/test/SemaCXX/attr-gsl-owner-pointer-std.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ class vector {
static_assert(sizeof(vector<int>), ""); // Force instantiation.
static_assert(sizeof(vector<int>::iterator), ""); // Force instantiation.

template <>
class vector<bool> {};
// CHECK: ClassTemplateSpecializationDecl {{.*}} vector
// CHECK: OwnerAttr {{.*}}

// If std::container::iterator is a using declaration, attributes are inferred
// for the underlying class.
template <typename T>
Expand Down Expand Up @@ -173,6 +178,18 @@ class reference_wrapper;
class some_unknown_type;
// CHECK: CXXRecordDecl {{.*}} some_unknown_type

using size_t = unsigned;
inline constexpr size_t dynamic_extent = -1;
template <typename _Tp, size_t _Extent = dynamic_extent>
class span;
// CHECK: CXXRecordDecl {{.*}} span
// CHECK: PointerAttr {{.*}}


template <typename _Tp>
struct span<_Tp, dynamic_extent> {};
// CHECK: ClassTemplatePartialSpecializationDecl {{.*}} span
// CHECK: PointerAttr {{.*}}
} // namespace std

namespace user {
Expand Down
31 changes: 31 additions & 0 deletions clang/test/SemaCXX/attr-musttail.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,3 +267,34 @@ namespace ns {}
void TestCallNonValue() {
[[clang::musttail]] return ns; // expected-error {{unexpected namespace name 'ns': expected expression}}
}

// Test diagnostics for lifetimes of local variables, which end earlier for a
// musttail call than for a nowmal one.

void TakesIntAndPtr(int, int *);
void PassAddressOfLocal(int a, int *b) {
int c;
[[clang::musttail]] return TakesIntAndPtr(0, &c); // expected-warning {{address of stack memory associated with local variable 'c' passed to musttail function}}
}
void PassAddressOfParam(int a, int *b) {
[[clang::musttail]] return TakesIntAndPtr(0, &a); // expected-warning {{address of stack memory associated with parameter 'a' passed to musttail function}}
}
void PassValues(int a, int *b) {
[[clang::musttail]] return TakesIntAndPtr(a, b);
}

void TakesIntAndRef(int, const int &);
void PassRefOfLocal(int a, const int &b) {
int c;
[[clang::musttail]] return TakesIntAndRef(0, c); // expected-warning {{address of stack memory associated with local variable 'c' passed to musttail function}}
}
void PassRefOfParam(int a, const int &b) {
[[clang::musttail]] return TakesIntAndRef(0, a); // expected-warning {{address of stack memory associated with parameter 'a' passed to musttail function}}
}
int ReturnInt();
void PassRefOfTemporary(int a, const int &b) {
[[clang::musttail]] return TakesIntAndRef(0, ReturnInt()); // expected-warning {{passing address of local temporary object to musttail function}}
}
void PassValuesRef(int a, const int &b) {
[[clang::musttail]] return TakesIntAndRef(a, b);
}
2 changes: 1 addition & 1 deletion clang/tools/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ add_clang_subdirectory(c-index-test)
add_clang_subdirectory(clang-refactor)
# For MinGW we only enable shared library if LLVM_LINK_LLVM_DYLIB=ON.
# Without that option resulting library is too close to 2^16 DLL exports limit.
if(UNIX OR (MINGW AND LLVM_LINK_LLVM_DYLIB))
if(UNIX OR (MSVC AND LLVM_BUILD_LLVM_DYLIB_VIS) OR (MINGW AND LLVM_LINK_LLVM_DYLIB))
add_clang_subdirectory(clang-shlib)
endif()

Expand Down
2 changes: 1 addition & 1 deletion clang/unittests/Interpreter/CodeCompletionTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ auto CB = clang::IncrementalCompilerBuilder();

class CodeCompletionTest : public InterpreterTestBase {
public:
std::unique_ptr<Interpreter> Interp;
std::unique_ptr<clang::Interpreter> Interp;

void SetUp() override {
if (!HostSupportsJIT())
Expand Down
64 changes: 23 additions & 41 deletions clang/unittests/Interpreter/InterpreterExtensionsTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,41 +65,13 @@ class InterpreterExtensionsTest : public InterpreterTestBase {
}
};

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;
struct OutOfProcInterpreter : public Interpreter {
OutOfProcInterpreter(
std::unique_ptr<CompilerInstance> CI, llvm::Error &ErrOut,
std::unique_ptr<clang::ASTConsumer> Consumer,
std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder = nullptr)
: Interpreter(std::move(CI), ErrOut, std::move(JITBuilder),
std::move(Consumer)) {}
};

TEST_F(InterpreterExtensionsTest, FindRuntimeInterface) {
Expand All @@ -108,13 +80,23 @@ TEST_F(InterpreterExtensionsTest, FindRuntimeInterface) {

clang::IncrementalCompilerBuilder CB;
llvm::Error ErrOut = llvm::Error::success();
RecordRuntimeIBMetrics Interp(cantFail(CB.CreateCpp()), ErrOut);
auto CI = cantFail(CB.CreateCpp());
// Do not attach the default consumer which is specialized for in-process.
class NoopConsumer : public ASTConsumer {};
std::unique_ptr<ASTConsumer> C = std::make_unique<NoopConsumer>();
OutOfProcInterpreter I(std::move(CI), ErrOut, std::move(C),
/*JITBuilder=*/nullptr);
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);
cantFail(I.Parse("int a = 1; a"));
cantFail(I.Parse("int b = 2; b"));
cantFail(I.Parse("int c = 3; c"));

// Make sure no clang::Value logic is attached by the Interpreter.
Value V1;
llvm::cantFail(I.ParseAndExecute("int x = 42;"));
llvm::cantFail(I.ParseAndExecute("x", &V1));
EXPECT_FALSE(V1.isValid());
EXPECT_FALSE(V1.hasValue());
}

class CustomJBInterpreter : public Interpreter {
Expand Down
56 changes: 32 additions & 24 deletions compiler-rt/lib/rtsan/rtsan_interceptors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,15 @@ INTERCEPTOR(int, open, const char *path, int oflag, ...) {
// O_NONBLOCK
__rtsan_notify_intercepted_call("open");

va_list args;
va_start(args, oflag);
const mode_t mode = va_arg(args, int);
va_end(args);
if (OpenReadsVaArgs(oflag)) {
va_list args;
va_start(args, oflag);
const mode_t mode = va_arg(args, int);
va_end(args);
return REAL(open)(path, oflag, mode);
}

const int result = REAL(open)(path, oflag, mode);
return result;
return REAL(open)(path, oflag);
}

#if SANITIZER_INTERCEPT_OPEN64
Expand All @@ -79,13 +81,15 @@ INTERCEPTOR(int, open64, const char *path, int oflag, ...) {
// O_NONBLOCK
__rtsan_notify_intercepted_call("open64");

va_list args;
va_start(args, oflag);
const mode_t mode = va_arg(args, int);
va_end(args);
if (OpenReadsVaArgs(oflag)) {
va_list args;
va_start(args, oflag);
const mode_t mode = va_arg(args, int);
va_end(args);
return REAL(open64)(path, oflag, mode);
}

const int result = REAL(open64)(path, oflag, mode);
return result;
return REAL(open64)(path, oflag);
}
#define RTSAN_MAYBE_INTERCEPT_OPEN64 INTERCEPT_FUNCTION(open64)
#else
Expand All @@ -97,13 +101,15 @@ INTERCEPTOR(int, openat, int fd, const char *path, int oflag, ...) {
// O_NONBLOCK
__rtsan_notify_intercepted_call("openat");

va_list args;
va_start(args, oflag);
mode_t mode = va_arg(args, int);
va_end(args);
if (OpenReadsVaArgs(oflag)) {
va_list args;
va_start(args, oflag);
const mode_t mode = va_arg(args, int);
va_end(args);
return REAL(openat)(fd, path, oflag, mode);
}

const int result = REAL(openat)(fd, path, oflag, mode);
return result;
return REAL(openat)(fd, path, oflag);
}

#if SANITIZER_INTERCEPT_OPENAT64
Expand All @@ -112,13 +118,15 @@ INTERCEPTOR(int, openat64, int fd, const char *path, int oflag, ...) {
// O_NONBLOCK
__rtsan_notify_intercepted_call("openat64");

va_list args;
va_start(args, oflag);
mode_t mode = va_arg(args, int);
va_end(args);
if (OpenReadsVaArgs(oflag)) {
va_list args;
va_start(args, oflag);
const mode_t mode = va_arg(args, int);
va_end(args);
return REAL(openat64)(fd, path, oflag, mode);
}

const int result = REAL(openat64)(fd, path, oflag, mode);
return result;
return REAL(openat64)(fd, path, oflag);
}
#define RTSAN_MAYBE_INTERCEPT_OPENAT64 INTERCEPT_FUNCTION(openat64)
#else
Expand Down
10 changes: 9 additions & 1 deletion compiler-rt/lib/sanitizer_common/sanitizer_posix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,15 @@ bool ShouldMockFailureToOpen(const char *path) {
internal_strncmp(path, "/proc/", 6) == 0;
}

#if SANITIZER_LINUX && !SANITIZER_ANDROID && !SANITIZER_GO
bool OpenReadsVaArgs(int oflag) {
# ifdef O_TMPFILE
return (oflag & (O_CREAT | O_TMPFILE)) != 0;
# else
return (oflag & O_CREAT) != 0;
# endif
}

# if SANITIZER_LINUX && !SANITIZER_ANDROID && !SANITIZER_GO
int GetNamedMappingFd(const char *name, uptr size, int *flags) {
if (!common_flags()->decorate_proc_maps || !name)
return -1;
Expand Down
1 change: 1 addition & 0 deletions compiler-rt/lib/sanitizer_common/sanitizer_posix.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ bool IsStateDetached(int state);
fd_t ReserveStandardFds(fd_t fd);

bool ShouldMockFailureToOpen(const char *path);
bool OpenReadsVaArgs(int oflag);

// Create a non-file mapping with a given /proc/self/maps name.
uptr MmapNamed(void *addr, uptr length, int prot, int flags, const char *name);
Expand Down
20 changes: 15 additions & 5 deletions compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1680,13 +1680,23 @@ TSAN_INTERCEPTOR(int, fstat64, int fd, void *buf) {
#endif

TSAN_INTERCEPTOR(int, open, const char *name, int oflag, ...) {
va_list ap;
va_start(ap, oflag);
mode_t mode = va_arg(ap, int);
va_end(ap);
mode_t mode = 0;
if (OpenReadsVaArgs(oflag)) {
va_list ap;
va_start(ap, oflag);
mode = va_arg(ap, int);
va_end(ap);
}

SCOPED_TSAN_INTERCEPTOR(open, name, oflag, mode);
READ_STRING(thr, pc, name, 0);
int fd = REAL(open)(name, oflag, mode);

int fd;
if (OpenReadsVaArgs(oflag))
fd = REAL(open)(name, oflag, mode);
else
fd = REAL(open)(name, oflag);

if (fd >= 0)
FdFileCreate(thr, pc, fd);
return fd;
Expand Down
3 changes: 3 additions & 0 deletions compiler-rt/test/asan/TestCases/Linux/preinstalled_signal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
// This way of setting LD_PRELOAD does not work with Android test runner.
// REQUIRES: !android

// Issue #109573: Cannot use syscall(__NR_rt_sigaction) on Linux/sparc64.
// XFAIL: target={{sparc.*-.*-linux.*}}

#include <assert.h>
#include <signal.h>
#include <stdio.h>
Expand Down
8 changes: 4 additions & 4 deletions compiler-rt/test/profile/Posix/instrprof-dlopen-norpath.test
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
RUN: rm -rf %t && split-file %s %t && cd %t
RUN: %clang_pgogen -fPIC foo.c -c -Xclang -fprofile-instrument-path="default_foo_%m.profraw"
RUN: %clang_pgogen -fPIC foo2.c -c -Xclang -fprofile-instrument-path="default_foo2_%m.profraw"
RUN: %clang_pgogen -shared foo.o -o shr_foo.o %if target={{.*aix.*}} %{ -bcdtors:mbr %}
RUN: %clang_pgogen -shared foo2.o -o shr_foo2.o
RUN: %clang_pgogen -fprofile-update=atomic -fPIC foo.c -c -Xclang -fprofile-instrument-path="default_foo_%m.profraw"
RUN: %clang_pgogen -fprofile-update=atomic -fPIC foo2.c -c -Xclang -fprofile-instrument-path="default_foo2_%m.profraw"
RUN: %clang_pgogen -fprofile-update=atomic -shared foo.o -o shr_foo.o %if target={{.*aix.*}} %{ -bcdtors:mbr %}
RUN: %clang_pgogen -fprofile-update=atomic -shared foo2.o -o shr_foo2.o

RUN: %clang_pgogen common.c -c

Expand Down
12 changes: 12 additions & 0 deletions flang/test/Driver/target-cpu-features.f90
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@
! RUN: %flang --target=x86_64-linux-gnu -mno-apx-features=ccmp -c %s -### 2>&1 \
! RUN: | FileCheck %s -check-prefix=CHECK-NO-APX

! RUN: %flang --target=x86_64-linux-gnu -mevex512 -c %s -### 2>&1 \
! RUN: | FileCheck %s -check-prefix=CHECK-EVEX512

! RUN: %flang --target=x86_64-linux-gnu -mno-evex512 -c %s -### 2>&1 \
! RUN: | FileCheck %s -check-prefix=CHECK-NO-EVEX512

! RUN: %flang --target=x86_64h-linux-gnu -c %s -### 2>&1 \
! RUN: | FileCheck %s -check-prefix=CHECK-X86_64H

Expand Down Expand Up @@ -63,6 +69,12 @@
! CHECK-NO-APX: "-fc1" "-triple" "x86_64-unknown-linux-gnu"
! CHECK-NO-APX-SAME: "-target-feature" "-ccmp"

! CHECK-EVEX512: "-fc1" "-triple" "x86_64-unknown-linux-gnu"
! CHECK-EVEX512-SAME: "-target-feature" "+evex512"

! CHECK-NO-EVEX512: "-fc1" "-triple" "x86_64-unknown-linux-gnu"
! CHECK-NO-EVEX512-SAME: "-target-feature" "-evex512"

! CHECK-X86_64H: "-fc1" "-triple" "x86_64h-unknown-linux-gnu"
! CHECK-X86_64H-SAME: "-target-cpu" "x86-64" "-target-feature" "-rdrnd" "-target-feature" "-aes" "-target-feature" "-pclmul" "-target-feature" "-rtm" "-target-feature" "-fsgsbase"

Expand Down
19 changes: 16 additions & 3 deletions libc/cmake/modules/CheckCompilerFeatures.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ set(
"builtin_round"
"builtin_roundeven"
"float16"
"float16_conversion"
"float128"
"fixed_point"
)
Expand Down Expand Up @@ -61,15 +62,21 @@ foreach(feature IN LISTS ALL_COMPILER_FEATURES)
set(link_options "")
if(${feature} STREQUAL "fixed_point")
list(APPEND compile_options "-ffixed-point")
elseif(${feature} MATCHES "^builtin_")
elseif(${feature} MATCHES "^builtin_" OR
${feature} STREQUAL "float16_conversion")
set(compile_options ${LIBC_COMPILE_OPTIONS_DEFAULT})
set(link_options -nostdlib)
# The compiler might handle calls to rounding builtins by generating calls
# to the respective libc math functions, in which case we cannot use these
# The compiler might handle calls to math builtins by generating calls to
# the respective libc math functions, in which case we cannot use these
# builtins in our implementations of these functions. We check that this is
# not the case by trying to link an executable, since linking would fail due
# to unresolved references with -nostdlib if calls to libc functions were
# generated.
#
# We also had issues with soft-float float16 conversion functions using both
# compiler-rt and libgcc, so we also check whether we can convert from and
# to float16 without calls to compiler runtime functions by trying to link
# an executable with -nostdlib.
set(CMAKE_TRY_COMPILE_TARGET_TYPE EXECUTABLE)
endif()

Expand Down Expand Up @@ -97,6 +104,8 @@ foreach(feature IN LISTS ALL_COMPILER_FEATURES)
list(APPEND AVAILABLE_COMPILER_FEATURES ${feature})
if(${feature} STREQUAL "float16")
set(LIBC_TYPES_HAS_FLOAT16 TRUE)
elseif(${feature} STREQUAL "float16_conversion")
add_compile_definitions(__LIBC_USE_FLOAT16_CONVERSION)
elseif(${feature} STREQUAL "float128")
set(LIBC_TYPES_HAS_FLOAT128 TRUE)
elseif(${feature} STREQUAL "fixed_point")
Expand All @@ -115,6 +124,10 @@ foreach(feature IN LISTS ALL_COMPILER_FEATURES)
endif()
endforeach()

set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
set(compile_options ${LIBC_COMPILE_OPTIONS_DEFAULT})
set(link_options "")

message(STATUS "Compiler features available: ${AVAILABLE_COMPILER_FEATURES}")

### Compiler Feature Detection ###
Expand Down
30 changes: 30 additions & 0 deletions libc/cmake/modules/compiler_features/check_float16_conversion.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include "include/llvm-libc-macros/float16-macros.h"
#include "include/llvm-libc-types/float128.h"

#ifndef LIBC_TYPES_HAS_FLOAT16
#error unsupported
#endif

_Float16 cvt_from_float(float x) { return static_cast<_Float16>(x); }

_Float16 cvt_from_double(double x) { return static_cast<_Float16>(x); }

_Float16 cvt_from_long_double(long double x) {
return static_cast<_Float16>(x);
}

#ifdef LIBC_TYPES_HAS_FLOAT128
_Float16 cvt_from_float128(float128 x) { return static_cast<_Float16>(x); }
#endif

float cvt_to_float(_Float16 x) { return x; }

double cvt_to_double(_Float16 x) { return x; }

long double cvt_to_long_double(_Float16 x) { return x; }

#ifdef LIBC_TYPES_HAS_FLOAT128
float128 cvt_to_float128(_Float16 x) { return x; }
#endif

extern "C" void _start() {}
1 change: 1 addition & 0 deletions libc/config/gpu/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.stdlib.at_quick_exit
libc.src.stdlib.quick_exit
libc.src.stdlib.getenv
libc.src.stdlib.system

# TODO: Implement these correctly
libc.src.stdlib.aligned_alloc
Expand Down
1 change: 1 addition & 0 deletions libc/config/gpu/headers.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ set(TARGET_PUBLIC_HEADERS
libc.include.assert
libc.include.ctype
libc.include.string
libc.include.strings
libc.include.signal
libc.include.float
libc.include.stdint
Expand Down
25 changes: 18 additions & 7 deletions libc/docs/gpu/support.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ Function Name Available RPC Required
bcmp |check|
bcopy |check|
bzero |check|
index |check|
memccpy |check|
memchr |check|
memcmp |check|
Expand All @@ -57,11 +56,8 @@ memmove |check|
mempcpy |check|
memrchr |check|
memset |check|
rindex |check|
stpcpy |check|
stpncpy |check|
strcasecmp |check|
strcasestr |check|
strcat |check|
strchr |check|
strchrnul |check|
Expand All @@ -74,7 +70,6 @@ strerror |check|
strlcat |check|
strlcpy |check|
strlen |check|
strncasecmp |check|
strncat |check|
strncmp |check|
strncpy |check|
Expand All @@ -90,6 +85,21 @@ strtok_r |check|
strxfrm |check|
============= ========= ============

strings.h
---------

============= ========= ============
Function Name Available RPC Required
============= ========= ============
bcmp |check|
bcopy |check|
bzero |check|
strcasecmp |check|
strcasestr |check|
index |check|
rindex |check|
============= ========= ============

stdbit.h
--------

Expand Down Expand Up @@ -176,6 +186,7 @@ atol |check|
atoll |check|
exit |check| |check|
abort |check| |check|
system |check| |check|
labs |check|
llabs |check|
div |check|
Expand Down Expand Up @@ -239,8 +250,8 @@ snprintf |check|
vsprintf |check|
vsnprintf |check|
sscanf |check|
scanf |check|
fscanf |check|
scanf |check| |check|
fscanf |check| |check|
putchar |check| |check|
fclose |check| |check|
fopen |check| |check|
Expand Down
1 change: 1 addition & 0 deletions libc/include/llvm-libc-macros/math-function-macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@
#define fpclassify(x) \
__builtin_fpclassify(FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, x)
#define isnormal(x) __builtin_isnormal(x)
#define issubnormal(x) (fpclassify(x) == FP_SUBNORMAL)

#endif // LLVM_LIBC_MACROS_MATH_FUNCTION_MACROS_H
1 change: 1 addition & 0 deletions libc/include/llvm-libc-types/rpc_opcodes_t.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ typedef enum {
RPC_PRINTF_TO_STDERR_PACKED,
RPC_PRINTF_TO_STREAM_PACKED,
RPC_REMOVE,
RPC_SYSTEM,
RPC_LAST = 0xFFFF,
} rpc_opcode_t;

Expand Down
6 changes: 6 additions & 0 deletions libc/newhdrgen/yaml/stdlib.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -333,3 +333,9 @@ functions:
- type: char **__restrict
- type: int
- type: locale_t
- name: system
standards:
- stdc
return_type: int
arguments:
- type: const char *
2 changes: 2 additions & 0 deletions libc/spec/stdc.td
Original file line number Diff line number Diff line change
Expand Up @@ -1340,6 +1340,8 @@ def StdC : StandardSpec<"stdc"> {
FunctionSpec<"atexit", RetValSpec<IntType>, [ArgSpec<AtexitHandlerT>]>,
FunctionSpec<"exit", RetValSpec<NoReturn>, [ArgSpec<IntType>]>,
FunctionSpec<"quick_exit", RetValSpec<NoReturn>, [ArgSpec<IntType>]>,

FunctionSpec<"system", RetValSpec<IntType>, [ArgSpec<ConstCharPtr>]>,
]
>;

Expand Down
27 changes: 24 additions & 3 deletions libc/src/__support/FPUtil/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,14 @@ add_header_library(
HDRS
except_value_utils.h
DEPENDS
.cast
.fp_bits
.fenv_impl
.rounding_mode
libc.src.__support.CPP.optional
libc.src.__support.macros.optimization
libc.src.__support.macros.properties.cpu_features
libc.src.__support.macros.properties.types
)


Expand Down Expand Up @@ -175,9 +178,13 @@ add_header_library(
.fenv_impl
.fp_bits
.multiply_add
.rounding_mode
libc.hdr.errno_macros
libc.hdr.fenv_macros
libc.src.__support.CPP.type_traits
libc.src.__support.big_int
libc.src.__support.macros.optimization
libc.src.__support.macros.properties.types
)

add_header_library(
Expand Down Expand Up @@ -217,18 +224,32 @@ add_header_library(
HDRS
ManipulationFunctions.h
DEPENDS
.cast
.dyadic_float
.fenv_impl
.fp_bits
.dyadic_float
.nearest_integer_operations
.normal_float
libc.hdr.math_macros
libc.src.errno.errno
libc.src.__support.common
libc.src.__support.CPP.bit
libc.src.__support.CPP.limits
libc.src.__support.CPP.type_traits
libc.src.__support.common
libc.src.__support.macros.optimization
libc.src.errno.errno
)

add_header_library(
cast
HDRS
cast.h
DEPENDS
.dyadic_float
.fp_bits
libc.hdr.fenv_macros
libc.src.__support.CPP.algorithm
libc.src.__support.CPP.type_traits
libc.src.__support.macros.properties.types
)

add_subdirectory(generic)
12 changes: 7 additions & 5 deletions libc/src/__support/FPUtil/ManipulationFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "FPBits.h"
#include "NearestIntegerOperations.h"
#include "NormalFloat.h"
#include "cast.h"
#include "dyadic_float.h"
#include "rounding_mode.h"

Expand Down Expand Up @@ -192,7 +193,8 @@ ldexp(T x, U exp) {
// For all other values, NormalFloat to T conversion handles it the right way.
DyadicFloat<FPBits<T>::STORAGE_LEN> normal(bits.get_val());
normal.exponent += static_cast<int>(exp);
return static_cast<T>(normal);
// TODO: Add tests for exceptions.
return normal.template as<T, /*ShouldRaiseExceptions=*/true>();
}

template <typename T, typename U,
Expand All @@ -207,17 +209,17 @@ LIBC_INLINE T nextafter(T from, U to) {

FPBits<U> to_bits(to);
if (to_bits.is_nan())
return static_cast<T>(to);
return cast<T>(to);

// NOTE: This would work only if `U` has a greater or equal precision than
// `T`. Otherwise `from` could loose its precision and the following statement
// could incorrectly evaluate to `true`.
if (static_cast<U>(from) == to)
return static_cast<T>(to);
if (cast<U>(from) == to)
return cast<T>(to);

using StorageType = typename FPBits<T>::StorageType;
if (from != T(0)) {
if ((static_cast<U>(from) < to) == (from > T(0))) {
if ((cast<U>(from) < to) == (from > T(0))) {
from_bits = FPBits<T>(StorageType(from_bits.uintval() + 1));
} else {
from_bits = FPBits<T>(StorageType(from_bits.uintval() - 1));
Expand Down
65 changes: 65 additions & 0 deletions libc/src/__support/FPUtil/cast.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//===-- Conversion between floating-point types -----------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC___SUPPORT_FPUTIL_CAST_H
#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_CAST_H

#include "FPBits.h"
#include "dyadic_float.h"
#include "hdr/fenv_macros.h"
#include "src/__support/CPP/algorithm.h"
#include "src/__support/CPP/type_traits.h"
#include "src/__support/macros/properties/types.h"

namespace LIBC_NAMESPACE::fputil {

template <typename OutType, typename InType>
LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&
cpp::is_floating_point_v<InType>,
OutType>
cast(InType x) {
#if defined(LIBC_TYPES_HAS_FLOAT16) && !defined(__LIBC_USE_FLOAT16_CONVERSION)
if constexpr (cpp::is_same_v<OutType, float16> ||
cpp::is_same_v<InType, float16>) {
using InFPBits = FPBits<InType>;
using InStorageType = typename InFPBits::StorageType;
using OutFPBits = FPBits<OutType>;
using OutStorageType = typename OutFPBits::StorageType;

InFPBits x_bits(x);

if (x_bits.is_nan()) {
if (x_bits.is_signaling_nan()) {
raise_except_if_required(FE_INVALID);
return OutFPBits::quiet_nan().get_val();
}

InStorageType x_mant = x_bits.get_mantissa();
if (InFPBits::FRACTION_LEN > OutFPBits::FRACTION_LEN)
x_mant >>= InFPBits::FRACTION_LEN - OutFPBits::FRACTION_LEN;
return OutFPBits::quiet_nan(x_bits.sign(),
static_cast<OutStorageType>(x_mant))
.get_val();
}

if (x_bits.is_inf())
return OutFPBits::inf(x_bits.sign()).get_val();

constexpr size_t MAX_FRACTION_LEN =
cpp::max(OutFPBits::FRACTION_LEN, InFPBits::FRACTION_LEN);
DyadicFloat<cpp::bit_ceil(MAX_FRACTION_LEN)> xd(x);
return xd.template as<OutType, /*ShouldSignalExceptions=*/true>();
}
#endif

return static_cast<OutType>(x);
}

} // namespace LIBC_NAMESPACE::fputil

#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_CAST_H
131 changes: 128 additions & 3 deletions libc/src/__support/FPUtil/dyadic_float.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@

#include "FEnvImpl.h"
#include "FPBits.h"
#include "hdr/errno_macros.h"
#include "hdr/fenv_macros.h"
#include "multiply_add.h"
#include "rounding_mode.h"
#include "src/__support/CPP/type_traits.h"
#include "src/__support/big_int.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
#include "src/__support/macros/properties/types.h"

#include <stddef.h>

Expand Down Expand Up @@ -97,13 +101,120 @@ template <size_t Bits> struct DyadicFloat {
return exponent + (Bits - 1);
}

// Assume that it is already normalized.
// Output is rounded correctly with respect to the current rounding mode.
#ifdef LIBC_TYPES_HAS_FLOAT16
template <typename T, bool ShouldSignalExceptions>
LIBC_INLINE constexpr cpp::enable_if_t<
cpp::is_floating_point_v<T> && (FPBits<T>::FRACTION_LEN < Bits), T>
generic_as() const {
using FPBits = FPBits<float16>;
using StorageType = typename FPBits::StorageType;

constexpr int EXTRA_FRACTION_LEN = Bits - 1 - FPBits::FRACTION_LEN;

if (mantissa == 0)
return FPBits::zero(sign).get_val();

int unbiased_exp = get_unbiased_exponent();

if (unbiased_exp + FPBits::EXP_BIAS >= FPBits::MAX_BIASED_EXPONENT) {
if constexpr (ShouldSignalExceptions) {
set_errno_if_required(ERANGE);
raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
}

switch (quick_get_round()) {
case FE_TONEAREST:
return FPBits::inf(sign).get_val();
case FE_TOWARDZERO:
return FPBits::max_normal(sign).get_val();
case FE_DOWNWARD:
if (sign.is_pos())
return FPBits::max_normal(Sign::POS).get_val();
return FPBits::inf(Sign::NEG).get_val();
case FE_UPWARD:
if (sign.is_neg())
return FPBits::max_normal(Sign::NEG).get_val();
return FPBits::inf(Sign::POS).get_val();
default:
__builtin_unreachable();
}
}

StorageType out_biased_exp = 0;
StorageType out_mantissa = 0;
bool round = false;
bool sticky = false;
bool underflow = false;

if (unbiased_exp < -FPBits::EXP_BIAS - FPBits::FRACTION_LEN) {
sticky = true;
underflow = true;
} else if (unbiased_exp == -FPBits::EXP_BIAS - FPBits::FRACTION_LEN) {
round = true;
MantissaType sticky_mask = (MantissaType(1) << (Bits - 1)) - 1;
sticky = (mantissa & sticky_mask) != 0;
} else {
int extra_fraction_len = EXTRA_FRACTION_LEN;

if (unbiased_exp < 1 - FPBits::EXP_BIAS) {
underflow = true;
extra_fraction_len += 1 - FPBits::EXP_BIAS - unbiased_exp;
} else {
out_biased_exp =
static_cast<StorageType>(unbiased_exp + FPBits::EXP_BIAS);
}

MantissaType round_mask = MantissaType(1) << (extra_fraction_len - 1);
round = (mantissa & round_mask) != 0;
MantissaType sticky_mask = round_mask - 1;
sticky = (mantissa & sticky_mask) != 0;

out_mantissa = static_cast<StorageType>(mantissa >> extra_fraction_len);
}

bool lsb = (out_mantissa & 1) != 0;

StorageType result =
FPBits::create_value(sign, out_biased_exp, out_mantissa).uintval();

switch (quick_get_round()) {
case FE_TONEAREST:
if (round && (lsb || sticky))
++result;
break;
case FE_DOWNWARD:
if (sign.is_neg() && (round || sticky))
++result;
break;
case FE_UPWARD:
if (sign.is_pos() && (round || sticky))
++result;
break;
default:
break;
}

if (ShouldSignalExceptions && (round || sticky)) {
int excepts = FE_INEXACT;
if (FPBits(result).is_inf()) {
set_errno_if_required(ERANGE);
excepts |= FE_OVERFLOW;
} else if (underflow) {
set_errno_if_required(ERANGE);
excepts |= FE_UNDERFLOW;
}
raise_except_if_required(excepts);
}

return FPBits(result).get_val();
}
#endif // LIBC_TYPES_HAS_FLOAT16

template <typename T, bool ShouldSignalExceptions,
typename = cpp::enable_if_t<cpp::is_floating_point_v<T> &&
(FPBits<T>::FRACTION_LEN < Bits),
void>>
LIBC_INLINE constexpr T as() const {
LIBC_INLINE constexpr T fast_as() const {
if (LIBC_UNLIKELY(mantissa.is_zero()))
return FPBits<T>::zero(sign).get_val();

Expand Down Expand Up @@ -224,6 +335,20 @@ template <size_t Bits> struct DyadicFloat {
return r;
}

// Assume that it is already normalized.
// Output is rounded correctly with respect to the current rounding mode.
template <typename T, bool ShouldSignalExceptions,
typename = cpp::enable_if_t<cpp::is_floating_point_v<T> &&
(FPBits<T>::FRACTION_LEN < Bits),
void>>
LIBC_INLINE constexpr T as() const {
#if defined(LIBC_TYPES_HAS_FLOAT16) && !defined(__LIBC_USE_FLOAT16_CONVERSION)
if constexpr (cpp::is_same_v<T, float16>)
return generic_as<T, ShouldSignalExceptions>();
#endif
return fast_as<T, ShouldSignalExceptions>();
}

template <typename T,
typename = cpp::enable_if_t<cpp::is_floating_point_v<T> &&
(FPBits<T>::FRACTION_LEN < Bits),
Expand Down
Loading