74 changes: 74 additions & 0 deletions clang/include/clang/Sema/SemaOpenACC.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//===----- SemaOpenACC.h - Semantic Analysis for OpenACC constructs -------===//
//
// 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
//
//===----------------------------------------------------------------------===//
/// \file
/// This file declares semantic analysis for OpenACC constructs and
/// clauses.
///
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_SEMA_SEMAOPENACC_H
#define LLVM_CLANG_SEMA_SEMAOPENACC_H

#include "clang/AST/DeclGroup.h"
#include "clang/Basic/OpenACCKinds.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Sema/Ownership.h"

namespace clang {

class ASTContext;
class DiagnosticEngine;
class LangOptions;
class Sema;

class SemaOpenACC {
public:
SemaOpenACC(Sema &S);

ASTContext &getASTContext() const;
DiagnosticsEngine &getDiagnostics() const;
const LangOptions &getLangOpts() const;

Sema &SemaRef;

/// Called after parsing an OpenACC Clause so that it can be checked.
bool ActOnClause(OpenACCClauseKind ClauseKind, SourceLocation StartLoc);

/// Called after the construct has been parsed, but clauses haven't been
/// parsed. This allows us to diagnose not-implemented, as well as set up any
/// state required for parsing the clauses.
void ActOnConstruct(OpenACCDirectiveKind K, SourceLocation StartLoc);

/// Called after the directive, including its clauses, have been parsed and
/// parsing has consumed the 'annot_pragma_openacc_end' token. This DOES
/// happen before any associated declarations or statements have been parsed.
/// This function is only called when we are parsing a 'statement' context.
bool ActOnStartStmtDirective(OpenACCDirectiveKind K, SourceLocation StartLoc);

/// Called after the directive, including its clauses, have been parsed and
/// parsing has consumed the 'annot_pragma_openacc_end' token. This DOES
/// happen before any associated declarations or statements have been parsed.
/// This function is only called when we are parsing a 'Decl' context.
bool ActOnStartDeclDirective(OpenACCDirectiveKind K, SourceLocation StartLoc);
/// Called when we encounter an associated statement for our construct, this
/// should check legality of the statement as it appertains to this Construct.
StmtResult ActOnAssociatedStmt(OpenACCDirectiveKind K, StmtResult AssocStmt);

/// Called after the directive has been completely parsed, including the
/// declaration group or associated statement.
StmtResult ActOnEndStmtDirective(OpenACCDirectiveKind K,
SourceLocation StartLoc,
SourceLocation EndLoc, StmtResult AssocStmt);
/// Called after the directive has been completely parsed, including the
/// declaration group or associated statement.
DeclGroupRef ActOnEndDeclDirective();
};

} // namespace clang

#endif // LLVM_CLANG_SEMA_SEMAOPENACC_H
1 change: 1 addition & 0 deletions clang/include/clang/Serialization/TypeBitCodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,6 @@ TYPE_BIT_CODE(Using, USING, 54)
TYPE_BIT_CODE(BTFTagAttributed, BTFTAG_ATTRIBUTED, 55)
TYPE_BIT_CODE(PackIndexing, PACK_INDEXING, 56)
TYPE_BIT_CODE(CountAttributed, COUNT_ATTRIBUTED, 57)
TYPE_BIT_CODE(ArrayParameter, ARRAY_PARAMETER, 58)

#undef TYPE_BIT_CODE
62 changes: 59 additions & 3 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -879,7 +879,8 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM,
TemplateSpecializationTypes(this_()),
DependentTemplateSpecializationTypes(this_()), AutoTypes(this_()),
DependentBitIntTypes(this_()), SubstTemplateTemplateParmPacks(this_()),
CanonTemplateTemplateParms(this_()), SourceMgr(SM), LangOpts(LOpts),
ArrayParameterTypes(this_()), CanonTemplateTemplateParms(this_()),
SourceMgr(SM), LangOpts(LOpts),
NoSanitizeL(new NoSanitizeList(LangOpts.NoSanitizeFiles, SM)),
XRayFilter(new XRayFunctionFilter(LangOpts.XRayAlwaysInstrumentFiles,
LangOpts.XRayNeverInstrumentFiles,
Expand Down Expand Up @@ -1906,7 +1907,8 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const {

case Type::IncompleteArray:
case Type::VariableArray:
case Type::ConstantArray: {
case Type::ConstantArray:
case Type::ArrayParameter: {
// Model non-constant sized arrays as size zero, but track the alignment.
uint64_t Size = 0;
if (const auto *CAT = dyn_cast<ConstantArrayType>(T))
Expand Down Expand Up @@ -3396,6 +3398,37 @@ QualType ASTContext::getDecayedType(QualType T) const {
return getDecayedType(T, Decayed);
}

QualType ASTContext::getArrayParameterType(QualType Ty) const {
if (Ty->isArrayParameterType())
return Ty;
assert(Ty->isConstantArrayType() && "Ty must be an array type.");
const auto *ATy = cast<ConstantArrayType>(Ty);
llvm::FoldingSetNodeID ID;
ATy->Profile(ID, *this, ATy->getElementType(), ATy->getZExtSize(),
ATy->getSizeExpr(), ATy->getSizeModifier(),
ATy->getIndexTypeQualifiers().getAsOpaqueValue());
void *InsertPos = nullptr;
ArrayParameterType *AT =
ArrayParameterTypes.FindNodeOrInsertPos(ID, InsertPos);
if (AT)
return QualType(AT, 0);

QualType Canonical;
if (!Ty.isCanonical()) {
Canonical = getArrayParameterType(getCanonicalType(Ty));

// Get the new insert position for the node we care about.
AT = ArrayParameterTypes.FindNodeOrInsertPos(ID, InsertPos);
assert(!AT && "Shouldn't be in the map!");
}

AT = new (*this, alignof(ArrayParameterType))
ArrayParameterType(ATy, Canonical);
Types.push_back(AT);
ArrayParameterTypes.InsertNode(AT, InsertPos);
return QualType(AT, 0);
}

/// getBlockPointerType - Return the uniqued reference to the type for
/// a pointer to the specified block.
QualType ASTContext::getBlockPointerType(QualType T) const {
Expand Down Expand Up @@ -3642,6 +3675,7 @@ QualType ASTContext::getVariableArrayDecayedType(QualType type) const {
case Type::PackIndexing:
case Type::BitInt:
case Type::DependentBitInt:
case Type::ArrayParameter:
llvm_unreachable("type should never be variably-modified");

// These types can be variably-modified but should never need to
Expand Down Expand Up @@ -6051,7 +6085,9 @@ CanQualType ASTContext::getCanonicalParamType(QualType T) const {
T = getVariableArrayDecayedType(T);
const Type *Ty = T.getTypePtr();
QualType Result;
if (isa<ArrayType>(Ty)) {
if (getLangOpts().HLSL && isa<ConstantArrayType>(Ty)) {
Result = getArrayParameterType(QualType(Ty, 0));
} else if (isa<ArrayType>(Ty)) {
Result = getArrayDecayedType(QualType(Ty,0));
} else if (isa<FunctionType>(Ty)) {
Result = getPointerType(QualType(Ty, 0));
Expand Down Expand Up @@ -6973,6 +7009,8 @@ const ArrayType *ASTContext::getAsArrayType(QualType T) const {
}

QualType ASTContext::getAdjustedParameterType(QualType T) const {
if (getLangOpts().HLSL && T->isConstantArrayType())
return getArrayParameterType(T);
if (T->isArrayType() || T->isFunctionType())
return getDecayedType(T);
return T;
Expand Down Expand Up @@ -8583,6 +8621,7 @@ void ASTContext::getObjCEncodingForTypeImpl(QualType T, std::string &S,
case Type::DeducedTemplateSpecialization:
return;

case Type::ArrayParameter:
case Type::Pipe:
#define ABSTRACT_TYPE(KIND, BASE)
#define TYPE(KIND, BASE)
Expand Down Expand Up @@ -10926,6 +10965,10 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, bool OfBlockPointer,
assert(LHS != RHS &&
"Equivalent pipe types should have already been handled!");
return {};
case Type::ArrayParameter:
assert(LHS != RHS &&
"Equivalent ArrayParameter types should have already been handled!");
return {};
case Type::BitInt: {
// Merge two bit-precise int types, while trying to preserve typedef info.
bool LHSUnsigned = LHS->castAs<BitIntType>()->isUnsigned();
Expand Down Expand Up @@ -12817,6 +12860,18 @@ static QualType getCommonNonSugarTypeNode(ASTContext &Ctx, const Type *X,
getCommonArrayElementType(Ctx, AX, QX, AY, QY), AX->getSize(), SizeExpr,
getCommonSizeModifier(AX, AY), getCommonIndexTypeCVRQualifiers(AX, AY));
}
case Type::ArrayParameter: {
const auto *AX = cast<ArrayParameterType>(X),
*AY = cast<ArrayParameterType>(Y);
assert(AX->getSize() == AY->getSize());
const Expr *SizeExpr = Ctx.hasSameExpr(AX->getSizeExpr(), AY->getSizeExpr())
? AX->getSizeExpr()
: nullptr;
auto ArrayTy = Ctx.getConstantArrayType(
getCommonArrayElementType(Ctx, AX, QX, AY, QY), AX->getSize(), SizeExpr,
getCommonSizeModifier(AX, AY), getCommonIndexTypeCVRQualifiers(AX, AY));
return Ctx.getArrayParameterType(ArrayTy);
}
case Type::Atomic: {
const auto *AX = cast<AtomicType>(X), *AY = cast<AtomicType>(Y);
return Ctx.getAtomicType(
Expand Down Expand Up @@ -13078,6 +13133,7 @@ static QualType getCommonSugarTypeNode(ASTContext &Ctx, const Type *X,
CANONICAL_TYPE(Builtin)
CANONICAL_TYPE(Complex)
CANONICAL_TYPE(ConstantArray)
CANONICAL_TYPE(ArrayParameter)
CANONICAL_TYPE(ConstantMatrix)
CANONICAL_TYPE(Enum)
CANONICAL_TYPE(ExtVector)
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/AST/ASTImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1218,6 +1218,15 @@ ASTNodeImporter::VisitConstantArrayType(const ConstantArrayType *T) {
T->getIndexTypeCVRQualifiers());
}

ExpectedType
ASTNodeImporter::VisitArrayParameterType(const ArrayParameterType *T) {
ExpectedType ToArrayTypeOrErr = VisitConstantArrayType(T);
if (!ToArrayTypeOrErr)
return ToArrayTypeOrErr.takeError();

return Importer.getToContext().getArrayParameterType(*ToArrayTypeOrErr);
}

ExpectedType
ASTNodeImporter::VisitIncompleteArrayType(const IncompleteArrayType *T) {
ExpectedType ToElementTypeOrErr = import(T->getElementType());
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/ASTStructuralEquivalence.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -840,6 +840,7 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,

case Type::Adjusted:
case Type::Decayed:
case Type::ArrayParameter:
if (!IsStructurallyEquivalent(Context,
cast<AdjustedType>(T1)->getOriginalType(),
cast<AdjustedType>(T2)->getOriginalType()))
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1948,6 +1948,7 @@ bool CastExpr::CastConsistency() const {
case CK_UserDefinedConversion: // operator bool()
case CK_BuiltinFnToFnPtr:
case CK_FixedPointToBoolean:
case CK_HLSLArrayRValue:
CheckNoBasePath:
assert(path_empty() && "Cast kind should not have a base path!");
break;
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11699,6 +11699,7 @@ GCCTypeClass EvaluateBuiltinClassifyType(QualType T,
case Type::IncompleteArray:
case Type::FunctionNoProto:
case Type::FunctionProto:
case Type::ArrayParameter:
return GCCTypeClass::Pointer;

case Type::MemberPointer:
Expand Down Expand Up @@ -14085,6 +14086,7 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) {
case CK_AtomicToNonAtomic:
case CK_NoOp:
case CK_LValueToRValueBitCast:
case CK_HLSLArrayRValue:
return ExprEvaluatorBaseTy::VisitCastExpr(E);

case CK_MemberPointerToBoolean:
Expand Down Expand Up @@ -14913,6 +14915,7 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) {
case CK_AtomicToNonAtomic:
case CK_NoOp:
case CK_LValueToRValueBitCast:
case CK_HLSLArrayRValue:
return ExprEvaluatorBaseTy::VisitCastExpr(E);

case CK_Dependent:
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/AST/ItaniumMangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2398,6 +2398,7 @@ bool CXXNameMangler::mangleUnresolvedTypeOrSimpleId(QualType Ty,
case Type::Complex:
case Type::Adjusted:
case Type::Decayed:
case Type::ArrayParameter:
case Type::Pointer:
case Type::BlockPointer:
case Type::LValueReference:
Expand Down Expand Up @@ -4446,6 +4447,10 @@ void CXXNameMangler::mangleType(const DependentBitIntType *T) {
Out << "_";
}

void CXXNameMangler::mangleType(const ArrayParameterType *T) {
mangleType(cast<ConstantArrayType>(T));
}

void CXXNameMangler::mangleIntegerLiteral(QualType T,
const llvm::APSInt &Value) {
// <expr-primary> ::= L <type> <value number> E # integer literal
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/AST/MicrosoftMangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3079,6 +3079,11 @@ void MicrosoftCXXNameMangler::mangleArrayType(const ArrayType *T) {
mangleType(ElementTy, SourceRange(), QMM_Escape);
}

void MicrosoftCXXNameMangler::mangleType(const ArrayParameterType *T,
Qualifiers, SourceRange) {
mangleArrayType(cast<ConstantArrayType>(T));
}

// <type> ::= <pointer-to-member-type>
// <pointer-to-member-type> ::= <pointer-cvr-qualifiers> <cvr-qualifiers>
// <class name> <type>
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/AST/ODRHash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,10 @@ class ODRTypeVisitor : public TypeVisitor<ODRTypeVisitor> {
VisitArrayType(T);
}

void VisitArrayParameterType(const ArrayParameterType *T) {
VisitConstantArrayType(T);
}

void VisitDependentSizedArrayType(const DependentSizedArrayType *T) {
AddStmt(T->getSizeExpr());
VisitArrayType(T);
Expand Down
11 changes: 11 additions & 0 deletions clang/lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1197,6 +1197,14 @@ struct SimpleTransformVisitor : public TypeVisitor<Derived, QualType> {
return Ctx.getDecayedType(originalType);
}

QualType VisitArrayParameterType(const ArrayParameterType *T) {
QualType ArrTy = VisitConstantArrayType(T);
if (ArrTy.isNull())
return {};

return Ctx.getArrayParameterType(ArrTy);
}

SUGARED_TYPE_CLASS(TypeOfExpr)
SUGARED_TYPE_CLASS(TypeOf)
SUGARED_TYPE_CLASS(Decltype)
Expand Down Expand Up @@ -4454,6 +4462,7 @@ static CachedProperties computeCachedProperties(const Type *T) {
case Type::ConstantArray:
case Type::IncompleteArray:
case Type::VariableArray:
case Type::ArrayParameter:
return Cache::get(cast<ArrayType>(T)->getElementType());
case Type::Vector:
case Type::ExtVector:
Expand Down Expand Up @@ -4542,6 +4551,7 @@ LinkageInfo LinkageComputer::computeTypeLinkageInfo(const Type *T) {
case Type::ConstantArray:
case Type::IncompleteArray:
case Type::VariableArray:
case Type::ArrayParameter:
return computeTypeLinkageInfo(cast<ArrayType>(T)->getElementType());
case Type::Vector:
case Type::ExtVector:
Expand Down Expand Up @@ -4736,6 +4746,7 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const {
case Type::Pipe:
case Type::BitInt:
case Type::DependentBitInt:
case Type::ArrayParameter:
return false;
}
llvm_unreachable("bad type kind!");
Expand Down
11 changes: 11 additions & 0 deletions clang/lib/AST/TypePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ bool TypePrinter::canPrefixQualifiers(const Type *T,

case Type::Adjusted:
case Type::Decayed:
case Type::ArrayParameter:
case Type::Pointer:
case Type::BlockPointer:
case Type::LValueReference:
Expand Down Expand Up @@ -595,6 +596,16 @@ void TypePrinter::printDecayedBefore(const DecayedType *T, raw_ostream &OS) {
printAdjustedBefore(T, OS);
}

void TypePrinter::printArrayParameterAfter(const ArrayParameterType *T,
raw_ostream &OS) {
printConstantArrayAfter(T, OS);
}

void TypePrinter::printArrayParameterBefore(const ArrayParameterType *T,
raw_ostream &OS) {
printConstantArrayBefore(T, OS);
}

void TypePrinter::printDecayedAfter(const DecayedType *T, raw_ostream &OS) {
printAdjustedAfter(T, OS);
}
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/CodeGen/CGCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4719,7 +4719,8 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E,
}

if (HasAggregateEvalKind && isa<ImplicitCastExpr>(E) &&
cast<CastExpr>(E)->getCastKind() == CK_LValueToRValue) {
cast<CastExpr>(E)->getCastKind() == CK_LValueToRValue &&
!type->isArrayParameterType()) {
LValue L = EmitLValue(cast<CastExpr>(E)->getSubExpr());
assert(L.isSimple());
args.addUncopiedAggregate(L, type);
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3641,6 +3641,7 @@ llvm::DIType *CGDebugInfo::CreateTypeNode(QualType Ty, llvm::DIFile *Unit) {
case Type::ConstantArray:
case Type::VariableArray:
case Type::IncompleteArray:
case Type::ArrayParameter:
return CreateType(cast<ArrayType>(Ty), Unit);

case Type::LValueReference:
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CGExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5190,6 +5190,7 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) {
case CK_IntegralToFixedPoint:
case CK_MatrixCast:
case CK_HLSLVectorTruncation:
case CK_HLSLArrayRValue:
return EmitUnsupportedLValue(E, "unexpected cast lvalue");

case CK_Dependent:
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CodeGen/CGExprAgg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -883,6 +883,9 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) {

[[fallthrough]];

case CK_HLSLArrayRValue:
Visit(E->getSubExpr());
break;

case CK_NoOp:
case CK_UserDefinedConversion:
Expand Down Expand Up @@ -1524,6 +1527,7 @@ static bool castPreservesZero(const CastExpr *CE) {
case CK_LValueToRValue:
case CK_LValueToRValueBitCast:
case CK_UncheckedDerivedToBase:
case CK_HLSLArrayRValue:
return false;
}
llvm_unreachable("Unhandled clang::CastKind enum");
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CGExprComplex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,7 @@ ComplexPairTy ComplexExprEmitter::EmitCast(CastKind CK, Expr *Op,
case CK_IntegralToFixedPoint:
case CK_MatrixCast:
case CK_HLSLVectorTruncation:
case CK_HLSLArrayRValue:
llvm_unreachable("invalid cast kind for complex value");

case CK_FloatingRealToComplex:
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CGExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1226,6 +1226,7 @@ class ConstExprEmitter :
case CK_ZeroToOCLOpaqueType:
case CK_MatrixCast:
case CK_HLSLVectorTruncation:
case CK_HLSLArrayRValue:
return nullptr;
}
llvm_unreachable("Invalid CastKind");
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CGExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2329,6 +2329,7 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
case CK_FloatingComplexToIntegralComplex:
case CK_ConstructorConversion:
case CK_ToUnion:
case CK_HLSLArrayRValue:
llvm_unreachable("scalar cast to non-scalar value");

case CK_LValueToRValue:
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/CodeGen/CodeGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ TypeEvaluationKind CodeGenFunction::getEvaluationKind(QualType type) {
case Type::Record:
case Type::ObjCObject:
case Type::ObjCInterface:
case Type::ArrayParameter:
return TEK_Aggregate;

// We operate on atomic values according to their underlying type.
Expand Down Expand Up @@ -2361,6 +2362,7 @@ void CodeGenFunction::EmitVariablyModifiedType(QualType type) {
type = cast<MemberPointerType>(ty)->getPointeeType();
break;

case Type::ArrayParameter:
case Type::ConstantArray:
case Type::IncompleteArray:
// Losing element qualification here is fine.
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CodeGenTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,7 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) {
ResultType = llvm::ArrayType::get(ResultType, 0);
break;
}
case Type::ArrayParameter:
case Type::ConstantArray: {
const ConstantArrayType *A = cast<ConstantArrayType>(Ty);
llvm::Type *EltTy = ConvertTypeForMem(A->getElementType());
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CodeGen/ItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3584,6 +3584,9 @@ void ItaniumRTTIBuilder::BuildVTablePointer(const Type *Ty) {
case Type::Pipe:
llvm_unreachable("Pipe types shouldn't get here");

case Type::ArrayParameter:
llvm_unreachable("Array Parameter types should not get here.");

case Type::Builtin:
case Type::BitInt:
// GCC treats vector and complex types as fundamental types.
Expand Down Expand Up @@ -3868,6 +3871,7 @@ llvm::Constant *ItaniumRTTIBuilder::BuildTypeInfo(
case Type::ConstantArray:
case Type::IncompleteArray:
case Type::VariableArray:
case Type::ArrayParameter:
// Itanium C++ ABI 2.9.5p5:
// abi::__array_type_info adds no data members to std::type_info.
break;
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Edit/RewriteObjCFoundationAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1000,6 +1000,7 @@ static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg,
case CK_LValueToRValue:
case CK_NoOp:
case CK_UserDefinedConversion:
case CK_HLSLArrayRValue:
break;

case CK_IntegralCast: {
Expand Down
21 changes: 11 additions & 10 deletions clang/lib/Parse/ParseOpenACC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "clang/Parse/ParseDiagnostic.h"
#include "clang/Parse/Parser.h"
#include "clang/Parse/RAIIObjectsForParser.h"
#include "clang/Sema/SemaOpenACC.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"

Expand Down Expand Up @@ -777,7 +778,7 @@ bool Parser::ParseOpenACCClause(OpenACCDirectiveKind DirKind) {
SourceLocation ClauseLoc = ConsumeToken();

bool Result = ParseOpenACCClauseParams(DirKind, Kind);
getActions().ActOnOpenACCClause(Kind, ClauseLoc);
getActions().OpenACC().ActOnClause(Kind, ClauseLoc);
return Result;
}

Expand Down Expand Up @@ -1151,7 +1152,7 @@ Parser::OpenACCDirectiveParseInfo Parser::ParseOpenACCDirective() {
SourceLocation StartLoc = getCurToken().getLocation();
OpenACCDirectiveKind DirKind = ParseOpenACCDirectiveKind(*this);

getActions().ActOnOpenACCConstruct(DirKind, StartLoc);
getActions().OpenACC().ActOnConstruct(DirKind, StartLoc);

// Once we've parsed the construct/directive name, some have additional
// specifiers that need to be taken care of. Atomic has an 'atomic-clause'
Expand Down Expand Up @@ -1223,12 +1224,12 @@ Parser::DeclGroupPtrTy Parser::ParseOpenACCDirectiveDecl() {

OpenACCDirectiveParseInfo DirInfo = ParseOpenACCDirective();

if (getActions().ActOnStartOpenACCDeclDirective(DirInfo.DirKind,
DirInfo.StartLoc))
if (getActions().OpenACC().ActOnStartDeclDirective(DirInfo.DirKind,
DirInfo.StartLoc))
return nullptr;

// TODO OpenACC: Do whatever decl parsing is required here.
return DeclGroupPtrTy::make(getActions().ActOnEndOpenACCDeclDirective());
return DeclGroupPtrTy::make(getActions().OpenACC().ActOnEndDeclDirective());
}

// Parse OpenACC Directive on a Statement.
Expand All @@ -1239,8 +1240,8 @@ StmtResult Parser::ParseOpenACCDirectiveStmt() {
ConsumeAnnotationToken();

OpenACCDirectiveParseInfo DirInfo = ParseOpenACCDirective();
if (getActions().ActOnStartOpenACCStmtDirective(DirInfo.DirKind,
DirInfo.StartLoc))
if (getActions().OpenACC().ActOnStartStmtDirective(DirInfo.DirKind,
DirInfo.StartLoc))
return StmtError();

StmtResult AssocStmt;
Expand All @@ -1249,10 +1250,10 @@ StmtResult Parser::ParseOpenACCDirectiveStmt() {
ParsingOpenACCDirectiveRAII DirScope(*this, /*Value=*/false);
ParseScope ACCScope(this, getOpenACCScopeFlags(DirInfo.DirKind));

AssocStmt = getActions().ActOnOpenACCAssociatedStmt(DirInfo.DirKind,
ParseStatement());
AssocStmt = getActions().OpenACC().ActOnAssociatedStmt(DirInfo.DirKind,
ParseStatement());
}

return getActions().ActOnEndOpenACCStmtDirective(
return getActions().OpenACC().ActOnEndStmtDirective(
DirInfo.DirKind, DirInfo.StartLoc, DirInfo.EndLoc, AssocStmt);
}
1 change: 1 addition & 0 deletions clang/lib/Sema/JumpDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "clang/AST/ExprCXX.h"
#include "clang/AST/StmtCXX.h"
#include "clang/AST/StmtObjC.h"
#include "clang/AST/StmtOpenACC.h"
#include "clang/AST/StmtOpenMP.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Sema/SemaInternal.h"
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/Sema/Sema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/SemaConsumer.h"
#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/SemaOpenACC.h"
#include "clang/Sema/TemplateDeduction.h"
#include "clang/Sema/TemplateInstCallback.h"
#include "clang/Sema/TypoCorrection.h"
Expand Down Expand Up @@ -196,7 +197,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
ThreadSafetyDeclCache(nullptr), LateTemplateParser(nullptr),
LateTemplateParserCleanup(nullptr), OpaqueParser(nullptr),
CurContext(nullptr), ExternalSource(nullptr), CurScope(nullptr),
Ident_super(nullptr),
Ident_super(nullptr), OpenACCPtr(std::make_unique<SemaOpenACC>(*this)),
MSPointerToMemberRepresentationMethod(
LangOpts.getMSPointerToMemberRepresentationMethod()),
MSStructPragmaOn(false), VtorDispStack(LangOpts.getVtorDispMode()),
Expand Down Expand Up @@ -653,6 +654,7 @@ ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty,
case CK_FunctionToPointerDecay:
case CK_ToVoid:
case CK_NonAtomicToAtomic:
case CK_HLSLArrayRValue:
break;
}
}
Expand Down
10 changes: 8 additions & 2 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -658,8 +658,9 @@ ExprResult Sema::DefaultLvalueConversion(Expr *E) {
QualType T = E->getType();
assert(!T.isNull() && "r-value conversion on typeless expression?");

// lvalue-to-rvalue conversion cannot be applied to function or array types.
if (T->isFunctionType() || T->isArrayType())
// lvalue-to-rvalue conversion cannot be applied to types that decay to
// pointers (i.e. function or array types).
if (T->canDecayToPointerType())
return E;

// We don't want to throw lvalue-to-rvalue casts on top of
Expand Down Expand Up @@ -4686,6 +4687,9 @@ static void captureVariablyModifiedType(ASTContext &Context, QualType T,
case Type::Decayed:
T = cast<DecayedType>(Ty)->getPointeeType();
break;
case Type::ArrayParameter:
T = cast<ArrayParameterType>(Ty)->getElementType();
break;
case Type::Pointer:
T = cast<PointerType>(Ty)->getPointeeType();
break;
Expand Down Expand Up @@ -12908,6 +12912,8 @@ static ImplicitConversionKind castKindToImplicitConversionKind(CastKind CK) {
case CK_IntegralComplexToReal:
case CK_IntegralRealToComplex:
return ICK_Complex_Real;
case CK_HLSLArrayRValue:
return ICK_HLSL_Array_RValue;
}
}

Expand Down
8 changes: 8 additions & 0 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4416,6 +4416,13 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
.get();
break;

case ICK_HLSL_Array_RValue:
FromType = Context.getArrayParameterType(FromType);
From = ImpCastExprToType(From, FromType, CK_HLSLArrayRValue, VK_PRValue,
/*BasePath=*/nullptr, CCK)
.get();
break;

case ICK_Function_To_Pointer:
FromType = Context.getPointerType(FromType);
From = ImpCastExprToType(From, FromType, CK_FunctionToPointerDecay,
Expand Down Expand Up @@ -4793,6 +4800,7 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
case ICK_Num_Conversion_Kinds:
case ICK_C_Only_Conversion:
case ICK_Incompatible_Pointer_Conversion:
case ICK_HLSL_Array_RValue:
llvm_unreachable("Improper second standard conversion");
}

Expand Down
5 changes: 4 additions & 1 deletion clang/lib/Sema/SemaInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6269,7 +6269,10 @@ void InitializationSequence::InitializeFrom(Sema &S,
// initializer is a string literal, see 8.5.2.
// - Otherwise, if the destination type is an array, the program is
// ill-formed.
if (const ArrayType *DestAT = Context.getAsArrayType(DestType)) {
// - Except in HLSL, where non-decaying array parameters behave like
// non-array types for initialization.
if (DestType->isArrayType() && !DestType->isArrayParameterType()) {
const ArrayType *DestAT = Context.getAsArrayType(DestType);
if (Initializer && isa<VariableArrayType>(DestAT)) {
SetFailed(FK_VariableLengthArrayHasInitializer);
return;
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Sema/SemaLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3243,6 +3243,10 @@ addAssociatedClassesAndNamespaces(AssociatedLookup &Result, QualType Ty) {
case Type::Pipe:
T = cast<PipeType>(T)->getElementType().getTypePtr();
continue;

// Array parameter types are treated as fundamental types.
case Type::ArrayParameter:
break;
}

if (Queue.empty())
Expand Down
33 changes: 21 additions & 12 deletions clang/lib/Sema/SemaOpenACC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@
///
//===----------------------------------------------------------------------===//

#include "clang/AST/StmtOpenACC.h"
#include "clang/Sema/SemaOpenACC.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Basic/OpenACCKinds.h"
#include "clang/Sema/Sema.h"

using namespace clang;

namespace {
bool diagnoseConstructAppertainment(Sema &S, OpenACCDirectiveKind K,
bool diagnoseConstructAppertainment(SemaOpenACC &S, OpenACCDirectiveKind K,
SourceLocation StartLoc, bool IsStmt) {
switch (K) {
default:
Expand All @@ -30,24 +31,32 @@ bool diagnoseConstructAppertainment(Sema &S, OpenACCDirectiveKind K,
case OpenACCDirectiveKind::Serial:
case OpenACCDirectiveKind::Kernels:
if (!IsStmt)
return S.Diag(StartLoc, diag::err_acc_construct_appertainment) << K;
return S.SemaRef.Diag(StartLoc, diag::err_acc_construct_appertainment)
<< K;
break;
}
return false;
}
} // namespace

bool Sema::ActOnOpenACCClause(OpenACCClauseKind ClauseKind,
SemaOpenACC::SemaOpenACC(Sema &S) : SemaRef(S) {}

ASTContext &SemaOpenACC::getASTContext() const { return SemaRef.Context; }
DiagnosticsEngine &SemaOpenACC::getDiagnostics() const { return SemaRef.Diags; }
const LangOptions &SemaOpenACC::getLangOpts() const { return SemaRef.LangOpts; }

bool SemaOpenACC::ActOnClause(OpenACCClauseKind ClauseKind,
SourceLocation StartLoc) {
if (ClauseKind == OpenACCClauseKind::Invalid)
return false;
// For now just diagnose that it is unsupported and leave the parsing to do
// whatever it can do. This function will eventually need to start returning
// some sort of Clause AST type, but for now just return true/false based on
// success.
return Diag(StartLoc, diag::warn_acc_clause_unimplemented) << ClauseKind;
return SemaRef.Diag(StartLoc, diag::warn_acc_clause_unimplemented)
<< ClauseKind;
}
void Sema::ActOnOpenACCConstruct(OpenACCDirectiveKind K,
void SemaOpenACC::ActOnConstruct(OpenACCDirectiveKind K,
SourceLocation StartLoc) {
switch (K) {
case OpenACCDirectiveKind::Invalid:
Expand All @@ -63,17 +72,17 @@ void Sema::ActOnOpenACCConstruct(OpenACCDirectiveKind K,
// here as these constructs do not take any arguments.
break;
default:
Diag(StartLoc, diag::warn_acc_construct_unimplemented) << K;
SemaRef.Diag(StartLoc, diag::warn_acc_construct_unimplemented) << K;
break;
}
}

bool Sema::ActOnStartOpenACCStmtDirective(OpenACCDirectiveKind K,
bool SemaOpenACC::ActOnStartStmtDirective(OpenACCDirectiveKind K,
SourceLocation StartLoc) {
return diagnoseConstructAppertainment(*this, K, StartLoc, /*IsStmt=*/true);
}

StmtResult Sema::ActOnEndOpenACCStmtDirective(OpenACCDirectiveKind K,
StmtResult SemaOpenACC::ActOnEndStmtDirective(OpenACCDirectiveKind K,
SourceLocation StartLoc,
SourceLocation EndLoc,
StmtResult AssocStmt) {
Expand All @@ -92,7 +101,7 @@ StmtResult Sema::ActOnEndOpenACCStmtDirective(OpenACCDirectiveKind K,
llvm_unreachable("Unhandled case in directive handling?");
}

StmtResult Sema::ActOnOpenACCAssociatedStmt(OpenACCDirectiveKind K,
StmtResult SemaOpenACC::ActOnAssociatedStmt(OpenACCDirectiveKind K,
StmtResult AssocStmt) {
switch (K) {
default:
Expand All @@ -114,9 +123,9 @@ StmtResult Sema::ActOnOpenACCAssociatedStmt(OpenACCDirectiveKind K,
llvm_unreachable("Invalid associated statement application");
}

bool Sema::ActOnStartOpenACCDeclDirective(OpenACCDirectiveKind K,
bool SemaOpenACC::ActOnStartDeclDirective(OpenACCDirectiveKind K,
SourceLocation StartLoc) {
return diagnoseConstructAppertainment(*this, K, StartLoc, /*IsStmt=*/false);
}

DeclGroupRef Sema::ActOnEndOpenACCDeclDirective() { return DeclGroupRef{}; }
DeclGroupRef SemaOpenACC::ActOnEndDeclDirective() { return DeclGroupRef{}; }
19 changes: 17 additions & 2 deletions clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ ImplicitConversionRank clang::GetConversionRank(ImplicitConversionKind Kind) {
ICR_C_Conversion_Extension,
ICR_Conversion,
ICR_Conversion,
ICR_Conversion,
};
static_assert(std::size(Rank) == (int)ICK_Num_Conversion_Kinds);
return Rank[(int)Kind];
Expand Down Expand Up @@ -201,6 +202,7 @@ static const char *GetImplicitConversionName(ImplicitConversionKind Kind) {
"Incompatible pointer conversion",
"Fixed point conversion",
"HLSL vector truncation",
"Non-decaying array conversion",
};
static_assert(std::size(Name) == (int)ICK_Num_Conversion_Kinds);
return Name[Kind];
Expand Down Expand Up @@ -2131,8 +2133,7 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
// A glvalue (3.10) of a non-function, non-array type T can
// be converted to a prvalue.
bool argIsLValue = From->isGLValue();
if (argIsLValue &&
!FromType->isFunctionType() && !FromType->isArrayType() &&
if (argIsLValue && !FromType->canDecayToPointerType() &&
S.Context.getCanonicalType(FromType) != S.Context.OverloadTy) {
SCS.First = ICK_Lvalue_To_Rvalue;

Expand All @@ -2147,6 +2148,19 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType,
// is T (C++ 4.1p1). C++ can't get here with class types; in C, we
// just strip the qualifiers because they don't matter.
FromType = FromType.getUnqualifiedType();
} else if (S.getLangOpts().HLSL && FromType->isConstantArrayType() &&
ToType->isArrayParameterType()) {
// HLSL constant array parameters do not decay, so if the argument is a
// constant array and the parameter is an ArrayParameterType we have special
// handling here.
FromType = S.Context.getArrayParameterType(FromType);
if (S.Context.getCanonicalType(FromType) !=
S.Context.getCanonicalType(ToType))
return false;

SCS.First = ICK_HLSL_Array_RValue;
SCS.setAllToTypes(ToType);
return true;
} else if (FromType->isArrayType()) {
// Array-to-pointer conversion (C++ 4.2)
SCS.First = ICK_Array_To_Pointer;
Expand Down Expand Up @@ -6100,6 +6114,7 @@ static bool CheckConvertedConstantConversions(Sema &S,
case ICK_Lvalue_To_Rvalue:
case ICK_Array_To_Pointer:
case ICK_Function_To_Pointer:
case ICK_HLSL_Array_RValue:
llvm_unreachable("found a first conversion kind in Second");

case ICK_Function_Conversion:
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6898,6 +6898,11 @@ bool UnnamedLocalNoLinkageFinder::VisitBitIntType(const BitIntType *T) {
return false;
}

bool UnnamedLocalNoLinkageFinder::VisitArrayParameterType(
const ArrayParameterType *T) {
return VisitConstantArrayType(T);
}

bool UnnamedLocalNoLinkageFinder::VisitDependentBitIntType(
const DependentBitIntType *T) {
return false;
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Sema/SemaTemplateDeduction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2277,6 +2277,7 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
case Type::DependentTemplateSpecialization:
case Type::PackExpansion:
case Type::Pipe:
case Type::ArrayParameter:
// No template argument deduction for these types
return TemplateDeductionResult::Success;

Expand Down Expand Up @@ -6355,11 +6356,11 @@ MarkUsedTemplateParameters(ASTContext &Ctx, QualType T,

case Type::ConstantArray:
case Type::IncompleteArray:
case Type::ArrayParameter:
MarkUsedTemplateParameters(Ctx,
cast<ArrayType>(T)->getElementType(),
OnlyDeduced, Depth, Used);
break;

case Type::Vector:
case Type::ExtVector:
MarkUsedTemplateParameters(Ctx,
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Sema/SemaType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6505,6 +6505,9 @@ namespace {
void VisitDecayedTypeLoc(DecayedTypeLoc TL) {
llvm_unreachable("decayed type locs not expected here!");
}
void VisitArrayParameterTypeLoc(ArrayParameterTypeLoc TL) {
llvm_unreachable("array parameter type locs not expected here!");
}

void VisitAttributedTypeLoc(AttributedTypeLoc TL) {
fillAttributedTypeLoc(TL, State);
Expand Down
28 changes: 23 additions & 5 deletions clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/SemaDiagnostic.h"
#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/SemaOpenACC.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/ErrorHandling.h"
#include <algorithm>
Expand Down Expand Up @@ -4000,16 +4001,16 @@ class TreeTransform {
SourceLocation BeginLoc,
SourceLocation EndLoc,
StmtResult StrBlock) {
getSema().ActOnOpenACCConstruct(K, BeginLoc);
getSema().OpenACC().ActOnConstruct(K, BeginLoc);

// TODO OpenACC: Include clauses.
if (getSema().ActOnStartOpenACCStmtDirective(K, BeginLoc))
if (getSema().OpenACC().ActOnStartStmtDirective(K, BeginLoc))
return StmtError();

StrBlock = getSema().ActOnOpenACCAssociatedStmt(K, StrBlock);
StrBlock = getSema().OpenACC().ActOnAssociatedStmt(K, StrBlock);

return getSema().ActOnEndOpenACCStmtDirective(K, BeginLoc, EndLoc,
StrBlock);
return getSema().OpenACC().ActOnEndStmtDirective(K, BeginLoc, EndLoc,
StrBlock);
}

private:
Expand Down Expand Up @@ -5243,6 +5244,23 @@ QualType TreeTransform<Derived>::TransformDecayedType(TypeLocBuilder &TLB,
return Result;
}

template <typename Derived>
QualType
TreeTransform<Derived>::TransformArrayParameterType(TypeLocBuilder &TLB,
ArrayParameterTypeLoc TL) {
QualType OriginalType = getDerived().TransformType(TLB, TL.getElementLoc());
if (OriginalType.isNull())
return QualType();

QualType Result = TL.getType();
if (getDerived().AlwaysRebuild() ||
OriginalType != TL.getElementLoc().getType())
Result = SemaRef.Context.getArrayParameterType(OriginalType);
TLB.push<ArrayParameterTypeLoc>(Result);
// Nothing to set for ArrayParameterTypeLoc.
return Result;
}

template<typename Derived>
QualType TreeTransform<Derived>::TransformPointerType(TypeLocBuilder &TLB,
PointerTypeLoc TL) {
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6810,6 +6810,10 @@ void TypeLocReader::VisitAdjustedTypeLoc(AdjustedTypeLoc TL) {
// nothing to do
}

void TypeLocReader::VisitArrayParameterTypeLoc(ArrayParameterTypeLoc TL) {
// nothing to do
}

void TypeLocReader::VisitMacroQualifiedTypeLoc(MacroQualifiedTypeLoc TL) {
TL.setExpansionLoc(readSourceLocation());
}
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Serialization/ASTWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,10 @@ void TypeLocWriter::VisitAdjustedTypeLoc(AdjustedTypeLoc TL) {
// nothing to do
}

void TypeLocWriter::VisitArrayParameterTypeLoc(ArrayParameterTypeLoc TL) {
// nothing to do
}

void TypeLocWriter::VisitBlockPointerTypeLoc(BlockPointerTypeLoc TL) {
addSourceLocation(TL.getCaretLoc());
}
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,8 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
case CK_ConstructorConversion:
case CK_UserDefinedConversion:
case CK_FunctionToPointerDecay:
case CK_BuiltinFnToFnPtr: {
case CK_BuiltinFnToFnPtr:
case CK_HLSLArrayRValue: {
// Copy the SVal of Ex to CastE.
ProgramStateRef state = Pred->getState();
const LocationContext *LCtx = Pred->getLocationContext();
Expand Down
31 changes: 31 additions & 0 deletions clang/test/C/C99/n570.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// RUN: %clang_cc1 -verify -std=c99 %s
// RUN: %clang_cc1 -E -std=c99 %s | FileCheck %s
// expected-no-diagnostics

/* WG14 N570: Yes
* Empty macro arguments
*
* NB: the original paper is not available online anywhere, so the test
* coverage is coming from what could be gleaned from the C99 rationale
* document. In C89, it was UB to pass no arguments to a function-like macro,
* and that's now supported in C99.
*/

#define TEN 10
#define U u
#define I // expands into no preprocessing tokens
#define L L
#define glue(a, b) a ## b
#define xglue(a, b) glue(a, b)

const unsigned u = xglue(TEN, U);
const int i = xglue(TEN, I);
const long l = xglue(TEN, L);

// CHECK: const unsigned u = 10u;
// CHECK-NEXT: const int i = 10;
// CHECK-NEXT: const long l = 10L;

_Static_assert(u == 10U, "");
_Static_assert(i == 10, "");
_Static_assert(l == 10L, "");
104 changes: 104 additions & 0 deletions clang/test/CodeGenHLSL/ArrayTemporary.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s

void fn(float x[2]) { }

// CHECK-LABEL: define void {{.*}}call{{.*}}
// CHECK: [[Arr:%.*]] = alloca [2 x float]
// CHECK: [[Tmp:%.*]] = alloca [2 x float]
// CHECK: call void @llvm.memset.p0.i32(ptr align 4 [[Arr]], i8 0, i32 8, i1 false)
// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp]], ptr align 4 [[Arr]], i32 8, i1 false)
// CHECK: call void {{.*}}fn{{.*}}(ptr noundef byval([2 x float]) align 4 [[Tmp]])
void call() {
float Arr[2] = {0, 0};
fn(Arr);
}

struct Obj {
float V;
int X;
};

void fn2(Obj O[4]) { }

// CHECK-LABEL: define void {{.*}}call2{{.*}}
// CHECK: [[Arr:%.*]] = alloca [4 x %struct.Obj]
// CHECK: [[Tmp:%.*]] = alloca [4 x %struct.Obj]
// CHECK: call void @llvm.memset.p0.i32(ptr align 4 [[Arr]], i8 0, i32 32, i1 false)
// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp]], ptr align 4 [[Arr]], i32 32, i1 false)
// CHECK: call void {{.*}}fn2{{.*}}(ptr noundef byval([4 x %struct.Obj]) align 4 [[Tmp]])
void call2() {
Obj Arr[4] = {};
fn2(Arr);
}


void fn3(float x[2][2]) { }

// CHECK-LABEL: define void {{.*}}call3{{.*}}
// CHECK: [[Arr:%.*]] = alloca [2 x [2 x float]]
// CHECK: [[Tmp:%.*]] = alloca [2 x [2 x float]]
// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Arr]], ptr align 4 {{.*}}, i32 16, i1 false)
// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp]], ptr align 4 [[Arr]], i32 16, i1 false)
// CHECK: call void {{.*}}fn3{{.*}}(ptr noundef byval([2 x [2 x float]]) align 4 [[Tmp]])
void call3() {
float Arr[2][2] = {{0, 0}, {1,1}};
fn3(Arr);
}

// CHECK-LABEL: define void {{.*}}call4{{.*}}(ptr
// CHECK-SAME: noundef byval([2 x [2 x float]]) align 4 [[Arr:%.*]])
// CHECK: [[Tmp:%.*]] = alloca [2 x [2 x float]]
// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp]], ptr align 4 [[Arr]], i32 16, i1 false)
// CHECK: call void {{.*}}fn3{{.*}}(ptr noundef byval([2 x [2 x float]]) align 4 [[Tmp]])

void call4(float Arr[2][2]) {
fn3(Arr);
}

// Verify that each template instantiation codegens to a unique and correctly
// mangled function name.

// CHECK-LABEL: define void {{.*}}template_call{{.*}}(ptr

// CHECK-SAME: noundef byval([2 x float]) align 4 [[FA2:%[0-9A-Z]+]],
// CHECK-SAME: ptr noundef byval([4 x float]) align 4 [[FA4:%[0-9A-Z]+]],
// CHECK-SAME: ptr noundef byval([3 x i32]) align 4 [[IA3:%[0-9A-Z]+]]

// CHECK: [[Tmp1:%.*]] = alloca [2 x float]
// CHECK: [[Tmp2:%.*]] = alloca [4 x float]
// CHECK: [[Tmp3:%.*]] = alloca [3 x i32]
// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp1]], ptr align 4 [[FA2]], i32 8, i1 false)
// CHECK: call void @"??$template_fn@$$BY01M@@YAXY01M@Z"(ptr noundef byval([2 x float]) align 4 [[Tmp1]])
// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp2]], ptr align 4 [[FA4]], i32 16, i1 false)
// CHECK: call void @"??$template_fn@$$BY03M@@YAXY03M@Z"(ptr noundef byval([4 x float]) align 4 [[Tmp2]])
// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[Tmp3]], ptr align 4 [[IA3]], i32 12, i1 false)
// CHECK: call void @"??$template_fn@$$BY02H@@YAXY02H@Z"(ptr noundef byval([3 x i32]) align 4 [[Tmp3]])

template<typename T>
void template_fn(T Val) {}

void template_call(float FA2[2], float FA4[4], int IA3[3]) {
template_fn(FA2);
template_fn(FA4);
template_fn(IA3);
}


// Verify that Array parameter element access correctly codegens.
// CHECK-LABEL: define void {{.*}}element_access{{.*}}(ptr
// CHECK-SAME: noundef byval([2 x float]) align 4 [[FA2:%[0-9A-Z]+]]

// CHECK: [[Addr:%.*]] = getelementptr inbounds [2 x float], ptr [[FA2]], i32 0, i32 0
// CHECK: [[Tmp:%.*]] = load float, ptr [[Addr]]
// CHECK: call void @"??$template_fn@M@@YAXM@Z"(float noundef [[Tmp]])

// CHECK: [[Idx0:%.*]] = getelementptr inbounds [2 x float], ptr [[FA2]], i32 0, i32 0
// CHECK: [[Val0:%.*]] = load float, ptr [[Idx0]]
// CHECK: [[Sum:%.*]] = fadd float [[Val0]], 5.000000e+00
// CHECK: [[Idx1:%.*]] = getelementptr inbounds [2 x float], ptr [[FA2]], i32 0, i32 1
// CHECK: store float [[Sum]], ptr [[Idx1]]

void element_access(float FA2[2]) {
template_fn(FA2[0]);
FA2[1] = FA2[0] + 5;
}
3 changes: 3 additions & 0 deletions clang/test/Driver/cl-outputs.c
Original file line number Diff line number Diff line change
Expand Up @@ -301,5 +301,8 @@
// RUN: %clang_cl -fdebug-compilation-dir=. /Z7 /Foa.obj -### -- %s 2>&1 | FileCheck -check-prefix=RELATIVE_OBJPATH1 %s
// RELATIVE_OBJPATH1: "-object-file-name=a.obj"

// RUN: %clang_cl -fdebug-compilation-dir=. /Z7 /Fo:a.obj -### -- %s 2>&1 | FileCheck -check-prefix=RELATIVE_OBJPATH1_COLON %s
// RELATIVE_OBJPATH1_COLON: "-object-file-name=a.obj"

// RUN: %clang_cl -fdebug-compilation-dir=. /Z7 /Fofoo/a.obj -### -- %s 2>&1 | FileCheck -check-prefix=RELATIVE_OBJPATH2 %s
// RELATIVE_OBJPATH2: "-object-file-name=foo\\a.obj"
29 changes: 29 additions & 0 deletions clang/test/SemaHLSL/ArrayParams.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library %s -verify

void fn(int I[5]); // #fn
void fn2(int I[3][3]); // #fn2

void call() {
float F[5];
double D[4];
int Long[9];
int Short[4];
int Same[5];

fn(F); // expected-error{{no matching function for call to 'fn'}}
// expected-note@#fn{{candidate function not viable: no known conversion from 'float[5]' to 'int[5]' for 1st argument}}

fn(D); // expected-error{{no matching function for call to 'fn'}}
// expected-note@#fn{{candidate function not viable: no known conversion from 'double[4]' to 'int[5]' for 1st argument}}

fn(Long); // expected-error{{no matching function for call to 'fn'}}
// expected-note@#fn{{candidate function not viable: no known conversion from 'int[9]' to 'int[5]' for 1st argument}}

fn(Short); // expected-error{{no matching function for call to 'fn'}}
// expected-note@#fn{{candidate function not viable: no known conversion from 'int[4]' to 'int[5]' for 1st argument}}

fn(Same); // totally fine, nothing to see here.

fn2(Long); // expected-error{{no matching function for call to 'fn2'}}
// expected-note@#fn2{{candidate function not viable: no known conversion from 'int[9]' to 'int[3][3]' for 1st argument}}
}
95 changes: 95 additions & 0 deletions clang/test/SemaHLSL/ArrayTemporary.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -ast-dump %s | FileCheck %s

void fn(float x[2]) { }

// CHECK: CallExpr {{.*}} 'void'
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float[2])' <FunctionToPointerDecay>
// CHECK-NEXT: DeclRefExpr {{.*}} 'void (float[2])' lvalue Function {{.*}} 'fn' 'void (float[2])'
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float[2]' <HLSLArrayRValue>

void call() {
float Arr[2] = {0, 0};
fn(Arr);
}

struct Obj {
float V;
int X;
};

void fn2(Obj O[4]) { }

// CHECK: CallExpr {{.*}} 'void'
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(Obj[4])' <FunctionToPointerDecay>
// CHECK-NEXT: DeclRefExpr {{.*}} 'void (Obj[4])' lvalue Function {{.*}} 'fn2' 'void (Obj[4])'
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'Obj[4]' <HLSLArrayRValue>

void call2() {
Obj Arr[4] = {};
fn2(Arr);
}


void fn3(float x[2][2]) { }

// CHECK: CallExpr {{.*}} 'void'
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float[2][2])' <FunctionToPointerDecay>
// CHECK-NEXT: DeclRefExpr {{.*}} 'void (float[2][2])' lvalue Function {{.*}} 'fn3' 'void (float[2][2])'
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float[2][2]' <HLSLArrayRValue>

void call3() {
float Arr[2][2] = {{0, 0}, {1,1}};
fn3(Arr);
}

// This template function should be instantiated 3 times for the different array
// types and lengths.

// CHECK: FunctionTemplateDecl {{.*}} template_fn
// CHECK-NEXT: TemplateTypeParmDecl {{.*}} referenced typename depth 0 index 0 T
// CHECK-NEXT: FunctionDecl {{.*}} template_fn 'void (T)'
// CHECK-NEXT: ParmVarDecl {{.*}} Val 'T'

// CHECK: FunctionDecl {{.*}} used template_fn 'void (float[2])' implicit_instantiation
// CHECK-NEXT: TemplateArgument type 'float[2]'
// CHECK-NEXT: ArrayParameterType {{.*}} 'float[2]' 2
// CHECK-NEXT: BuiltinType {{.*}} 'float'
// CHECK-NEXT: ParmVarDecl {{.*}} Val 'float[2]'

// CHECK: FunctionDecl {{.*}} used template_fn 'void (float[4])' implicit_instantiation
// CHECK-NEXT: TemplateArgument type 'float[4]'
// CHECK-NEXT: ArrayParameterType {{.*}} 'float[4]' 4
// CHECK-NEXT: BuiltinType {{.*}} 'float'
// CHECK-NEXT: ParmVarDecl {{.*}} Val 'float[4]'

// CHECK: FunctionDecl {{.*}} used template_fn 'void (int[3])' implicit_instantiation
// CHECK-NEXT: TemplateArgument type 'int[3]'
// CHECK-NEXT: ArrayParameterType {{.*}} 'int[3]' 3
// CHECK-NEXT: BuiltinType {{.*}} 'int'
// CHECK-NEXT: ParmVarDecl {{.*}} Val 'int[3]'

template<typename T>
void template_fn(T Val) {}

// CHECK: FunctionDecl {{.*}} call 'void (float[2], float[4], int[3])'
// CHECK: CallExpr {{.*}} 'void'
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float[2])' <FunctionToPointerDecay>
// CHECK-NEXT: DeclRefExpr {{.*}} 'void (float[2])' lvalue Function {{.*}} 'template_fn' 'void (float[2])' (FunctionTemplate {{.*}} 'template_fn')
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float[2]' <LValueToRValue>
// CHECK-NEXT: DeclRefExpr {{.*}} 'float[2]' lvalue ParmVar {{.*}} 'FA2' 'float[2]'
// CHECK-NEXT: CallExpr {{.*}} 'void'
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(float[4])' <FunctionToPointerDecay>
// CHECK-NEXT: DeclRefExpr {{.*}} 'void (float[4])' lvalue Function {{.*}} 'template_fn' 'void (float[4])' (FunctionTemplate {{.*}} 'template_fn')
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float[4]' <LValueToRValue>
// CHECK-NEXT: DeclRefExpr {{.*}} 'float[4]' lvalue ParmVar {{.*}} 'FA4' 'float[4]'
// CHECK-NEXT: CallExpr {{.*}} 'void'
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(int[3])' <FunctionToPointerDecay>
// CHECK-NEXT: DeclRefExpr {{.*}} 'void (int[3])' lvalue Function {{.*}} 'template_fn' 'void (int[3])' (FunctionTemplate {{.*}} 'template_fn')
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int[3]' <LValueToRValue>
// CHECK-NEXT: DeclRefExpr {{.*}} 'int[3]' lvalue ParmVar {{.*}} 'IA3' 'int[3]'

void call(float FA2[2], float FA4[4], int IA3[3]) {
template_fn(FA2);
template_fn(FA4);
template_fn(IA3);
}
76 changes: 76 additions & 0 deletions clang/test/SemaHLSL/ArrayTemporary.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
; ModuleID = '/Users/cbieneman/dev/llvm-project/clang/test/SemaHLSL/ArrayTemporary.hlsl'
source_filename = "/Users/cbieneman/dev/llvm-project/clang/test/SemaHLSL/ArrayTemporary.hlsl"
target datalayout = "e-m:e-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64"
target triple = "dxil-pc-shadermodel6.3-library"

%struct.Obj = type { float, i32 }

@"__const.?call3@@YAXXZ.Arr" = private unnamed_addr constant [2 x [2 x float]] [[2 x float] zeroinitializer, [2 x float] [float 1.000000e+00, float 1.000000e+00]], align 4

; Function Attrs: noinline nounwind optnone
define void @"?fn@@YAXY01M@Z"(ptr noundef byval([2 x float]) align 4 %x) #0 {
entry:
ret void
}

; Function Attrs: noinline nounwind optnone
define void @"?call@@YAXXZ"() #0 {
entry:
%Arr = alloca [2 x float], align 4
%agg.tmp = alloca [2 x float], align 4
call void @llvm.memset.p0.i32(ptr align 4 %Arr, i8 0, i32 8, i1 false)
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %agg.tmp, ptr align 4 %Arr, i32 8, i1 false)
call void @"?fn@@YAXY01M@Z"(ptr noundef byval([2 x float]) align 4 %agg.tmp)
ret void
}

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #1

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i32(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i32, i1 immarg) #2

; Function Attrs: noinline nounwind optnone
define void @"?fn2@@YAXY03UObj@@@Z"(ptr noundef byval([4 x %struct.Obj]) align 4 %O) #0 {
entry:
ret void
}

; Function Attrs: noinline nounwind optnone
define void @"?call2@@YAXXZ"() #0 {
entry:
%Arr = alloca [4 x %struct.Obj], align 4
%agg.tmp = alloca [4 x %struct.Obj], align 4
call void @llvm.memset.p0.i32(ptr align 4 %Arr, i8 0, i32 32, i1 false)
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %agg.tmp, ptr align 4 %Arr, i32 32, i1 false)
call void @"?fn2@@YAXY03UObj@@@Z"(ptr noundef byval([4 x %struct.Obj]) align 4 %agg.tmp)
ret void
}

; Function Attrs: noinline nounwind optnone
define void @"?fn3@@YAXY111M@Z"(ptr noundef byval([2 x [2 x float]]) align 4 %x) #0 {
entry:
ret void
}

; Function Attrs: noinline nounwind optnone
define void @"?call3@@YAXXZ"() #0 {
entry:
%Arr = alloca [2 x [2 x float]], align 4
%agg.tmp = alloca [2 x [2 x float]], align 4
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %Arr, ptr align 4 @"__const.?call3@@YAXXZ.Arr", i32 16, i1 false)
call void @llvm.memcpy.p0.p0.i32(ptr align 4 %agg.tmp, ptr align 4 %Arr, i32 16, i1 false)
call void @"?fn3@@YAXY111M@Z"(ptr noundef byval([2 x [2 x float]]) align 4 %agg.tmp)
ret void
}

attributes #0 = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }

!llvm.module.flags = !{!0, !1}
!llvm.ident = !{!2}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 4, !"dx.disable_optimizations", i32 1}
!2 = !{!"clang version 19.0.0git (git@github.com:llvm/llvm-project.git 64e1c15c520cf11114ef2ddd887e76560903db2b)"}
1 change: 1 addition & 0 deletions clang/tools/libclang/CIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1923,6 +1923,7 @@ DEFAULT_TYPELOC_IMPL(ConstantArray, ArrayType)
DEFAULT_TYPELOC_IMPL(IncompleteArray, ArrayType)
DEFAULT_TYPELOC_IMPL(VariableArray, ArrayType)
DEFAULT_TYPELOC_IMPL(DependentSizedArray, ArrayType)
DEFAULT_TYPELOC_IMPL(ArrayParameter, ConstantArrayType)
DEFAULT_TYPELOC_IMPL(DependentAddressSpace, Type)
DEFAULT_TYPELOC_IMPL(DependentVector, Type)
DEFAULT_TYPELOC_IMPL(DependentSizedExtVector, Type)
Expand Down
2 changes: 1 addition & 1 deletion clang/www/c_status.html
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ <h2 id="c99">C99 implementation status</h2>
<tr>
<td>empty macro arguments</td>
<td>N570</td>
<td class="unknown" align="center">Unknown</td>
<td class="full" align="center">Yes</td>
</tr>
<tr>
<td>new structure type compatibility (tag compatibility)</td>
Expand Down
5 changes: 5 additions & 0 deletions compiler-rt/lib/scudo/standalone/wrappers_c.inc
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,11 @@ void SCUDO_PREFIX(malloc_postinit)() {
INTERFACE WEAK int SCUDO_PREFIX(mallopt)(int param, int value) {
if (param == M_DECAY_TIME) {
if (SCUDO_ANDROID) {
// Before changing the interval, reset the memory usage status by doing a
// M_PURGE call so that we can minimize the impact of any unreleased pages
// introduced by interval transition.
SCUDO_ALLOCATOR.releaseToOS(scudo::ReleaseToOS::Force);

if (value == 0) {
// Will set the release values to their minimum values.
value = INT32_MIN;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
// REQUIRES: lld-available

// Building the instrumented binary will fail because lld doesn't support
// big-endian ELF for PPC (aka ABI 1).
// ld.lld: error: /lib/../lib64/Scrt1.o: ABI version 1 is not supported
// UNSUPPORTED: ppc && host-byteorder-big-endian

// RUN: %clangxx_pgogen -fuse-ld=lld -O2 -g -fprofile-generate=. -mllvm -enable-vtable-value-profiling %s -o %t-test
// RUN: env LLVM_PROFILE_FILE=%t-test.profraw %t-test

Expand Down
1 change: 1 addition & 0 deletions libc/config/baremetal/arm/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.acoshf
libc.src.math.asinf
libc.src.math.asinhf
libc.src.math.atan2f
libc.src.math.atanf
libc.src.math.atanhf
libc.src.math.ceil
Expand Down
1 change: 1 addition & 0 deletions libc/config/baremetal/riscv/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.acoshf
libc.src.math.asinf
libc.src.math.asinhf
libc.src.math.atan2f
libc.src.math.atanf
libc.src.math.atanhf
libc.src.math.ceil
Expand Down
1 change: 1 addition & 0 deletions libc/config/darwin/arm/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.acoshf
libc.src.math.asinf
libc.src.math.asinhf
libc.src.math.atan2f
libc.src.math.atanf
libc.src.math.atanhf
libc.src.math.copysign
Expand Down
1 change: 1 addition & 0 deletions libc/config/linux/aarch64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.acoshf
libc.src.math.asinf
libc.src.math.asinhf
libc.src.math.atan2f
libc.src.math.atanf
libc.src.math.atanhf
libc.src.math.copysign
Expand Down
1 change: 1 addition & 0 deletions libc/config/linux/arm/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.acoshf
libc.src.math.asinf
libc.src.math.asinhf
libc.src.math.atan2f
libc.src.math.atanf
libc.src.math.atanhf
libc.src.math.ceil
Expand Down
1 change: 1 addition & 0 deletions libc/config/linux/riscv/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.acoshf
libc.src.math.asinf
libc.src.math.asinhf
libc.src.math.atan2f
libc.src.math.atanf
libc.src.math.atanhf
libc.src.math.copysign
Expand Down
3 changes: 3 additions & 0 deletions libc/config/linux/x86_64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.acoshf
libc.src.math.asinf
libc.src.math.asinhf
libc.src.math.atan2f
libc.src.math.atanf
libc.src.math.atanhf
libc.src.math.canonicalize
Expand Down Expand Up @@ -681,6 +682,8 @@ if(LLVM_LIBC_FULL_BUILD)
libc.src.stdio.fread_unlocked
libc.src.stdio.fseek
libc.src.stdio.ftell
libc.src.stdio.fseeko
libc.src.stdio.ftello
libc.src.stdio.funlockfile
libc.src.stdio.fwrite
libc.src.stdio.fwrite_unlocked
Expand Down
1 change: 1 addition & 0 deletions libc/config/windows/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.acoshf
libc.src.math.asinf
libc.src.math.asinhf
libc.src.math.atan2f
libc.src.math.atanf
libc.src.math.atanhf
libc.src.math.copysign
Expand Down
3 changes: 2 additions & 1 deletion libc/docs/math/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ Higher Math Functions
+------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| atan2 | | | | | | | | | | | | |
+------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| atan2f | | | | | | | | | | | | |
| atan2f | |check| | |check| | |check| | |check| | |check| | | | |check| | |check| | |check| | | |
+------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
| atan2l | | | | | | | | | | | | |
+------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
Expand Down Expand Up @@ -591,6 +591,7 @@ acosh |check|
asin |check|
asinh |check|
atan |check|
atan2 |check|
atanh |check|
cos |check| large
cosh |check|
Expand Down
4 changes: 4 additions & 0 deletions libc/spec/stdc.td
Original file line number Diff line number Diff line change
Expand Up @@ -620,10 +620,14 @@ def StdC : StandardSpec<"stdc"> {
FunctionSpec<"tanhf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,

FunctionSpec<"acosf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,

FunctionSpec<"asinf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
FunctionSpec<"asin", RetValSpec<DoubleType>, [ArgSpec<DoubleType>]>,

FunctionSpec<"atanf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,

FunctionSpec<"atan2f", RetValSpec<FloatType>, [ArgSpec<FloatType>, ArgSpec<FloatType>]>,

FunctionSpec<"acoshf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
FunctionSpec<"asinhf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
FunctionSpec<"atanhf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
Expand Down
18 changes: 18 additions & 0 deletions libc/src/math/generic/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2854,6 +2854,24 @@ add_entrypoint_object(
-O3
)

add_entrypoint_object(
atan2f
SRCS
atan2f.cpp
HDRS
../atan2f.h
COMPILE_OPTIONS
-O3
DEPENDS
.inv_trigf_utils
libc.src.__support.FPUtil.fp_bits
libc.src.__support.FPUtil.multiply_add
libc.src.__support.FPUtil.nearest_integer
libc.src.__support.FPUtil.polyeval
libc.src.__support.FPUtil.rounding_mode
libc.src.__support.macros.optimization
)

add_entrypoint_object(
scalbn
SRCS
Expand Down
306 changes: 306 additions & 0 deletions libc/src/math/generic/atan2f.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,306 @@
//===-- Single-precision atan2f function ----------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/math/atan2f.h"
#include "inv_trigf_utils.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/PolyEval.h"
#include "src/__support/FPUtil/double_double.h"
#include "src/__support/FPUtil/multiply_add.h"
#include "src/__support/FPUtil/nearest_integer.h"
#include "src/__support/FPUtil/rounding_mode.h"
#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY

namespace LIBC_NAMESPACE {

namespace {

// Look up tables for accurate pass:

// atan(i/16) with i = 0..16, generated by Sollya with:
// > for i from 0 to 16 do {
// a = round(atan(i/16), D, RN);
// b = round(atan(i/16) - a, D, RN);
// print("{", b, ",", a, "},");
// };
constexpr fputil::DoubleDouble ATAN_I[17] = {
{0.0, 0.0},
{-0x1.c934d86d23f1dp-60, 0x1.ff55bb72cfdeap-5},
{-0x1.cd37686760c17p-59, 0x1.fd5ba9aac2f6ep-4},
{0x1.347b0b4f881cap-58, 0x1.7b97b4bce5b02p-3},
{0x1.8ab6e3cf7afbdp-57, 0x1.f5b75f92c80ddp-3},
{-0x1.963a544b672d8p-57, 0x1.362773707ebccp-2},
{-0x1.c63aae6f6e918p-56, 0x1.6f61941e4def1p-2},
{-0x1.24dec1b50b7ffp-56, 0x1.a64eec3cc23fdp-2},
{0x1.a2b7f222f65e2p-56, 0x1.dac670561bb4fp-2},
{-0x1.d5b495f6349e6p-56, 0x1.0657e94db30dp-1},
{-0x1.928df287a668fp-58, 0x1.1e00babdefeb4p-1},
{0x1.1021137c71102p-55, 0x1.345f01cce37bbp-1},
{0x1.2419a87f2a458p-56, 0x1.4978fa3269ee1p-1},
{0x1.0028e4bc5e7cap-57, 0x1.5d58987169b18p-1},
{-0x1.8c34d25aadef6p-56, 0x1.700a7c5784634p-1},
{-0x1.bf76229d3b917p-56, 0x1.819d0b7158a4dp-1},
{0x1.1a62633145c07p-55, 0x1.921fb54442d18p-1},
};

// Taylor polynomial, generated by Sollya with:
// > for i from 0 to 8 do {
// j = (-1)^(i + 1)/(2*i + 1);
// a = round(j, D, RN);
// b = round(j - a, D, RN);
// print("{", b, ",", a, "},");
// };
constexpr fputil::DoubleDouble COEFFS[9] = {
{0.0, 1.0}, // 1
{-0x1.5555555555555p-56, -0x1.5555555555555p-2}, // -1/3
{-0x1.999999999999ap-57, 0x1.999999999999ap-3}, // 1/5
{-0x1.2492492492492p-57, -0x1.2492492492492p-3}, // -1/7
{0x1.c71c71c71c71cp-58, 0x1.c71c71c71c71cp-4}, // 1/9
{0x1.745d1745d1746p-59, -0x1.745d1745d1746p-4}, // -1/11
{-0x1.3b13b13b13b14p-58, 0x1.3b13b13b13b14p-4}, // 1/13
{-0x1.1111111111111p-60, -0x1.1111111111111p-4}, // -1/15
{0x1.e1e1e1e1e1e1ep-61, 0x1.e1e1e1e1e1e1ep-5}, // 1/17
};

// Veltkamp's splitting of a double precision into hi + lo, where the hi part is
// slightly smaller than an even split, so that the product of
// hi * (s1 * k + s2) is exact,
// where:
// s1, s2 are single precsion,
// 1/16 <= s1/s2 <= 1
// 1/16 <= k <= 1 is an integer.
// So the maximal precision of (s1 * k + s2) is:
// prec(s1 * k + s2) = 2 + log2(msb(s2)) - log2(lsb(k_d * s1))
// = 2 + log2(msb(s1)) + 4 - log2(lsb(k_d)) - log2(lsb(s1))
// = 2 + log2(lsb(s1)) + 23 + 4 - (-4) - log2(lsb(s1))
// = 33.
// Thus, the Veltkamp splitting constant is C = 2^33 + 1.
// This is used when FMA instruction is not available.
[[maybe_unused]] constexpr fputil::DoubleDouble split_d(double a) {
fputil::DoubleDouble r{0.0, 0.0};
constexpr double C = 0x1.0p33 + 1.0;
double t1 = C * a;
double t2 = a - t1;
r.hi = t1 + t2;
r.lo = a - r.hi;
return r;
}

// Compute atan( num_d / den_d ) in double-double precision.
// num_d = min(|x|, |y|)
// den_d = max(|x|, |y|)
// q_d = num_d / den_d
// idx, k_d = round( 2^4 * num_d / den_d )
// final_sign = sign of the final result
// const_term = the constant term in the final expression.
float atan2f_double_double(double num_d, double den_d, double q_d, int idx,
double k_d, double final_sign,
const fputil::DoubleDouble &const_term) {
fputil::DoubleDouble q;
double num_r, den_r;

if (idx != 0) {
// The following range reduction is accurate even without fma for
// 1/16 <= n/d <= 1.
// atan(n/d) - atan(idx/16) = atan((n/d - idx/16) / (1 + (n/d) * (idx/16)))
// = atan((n - d*(idx/16)) / (d + n*idx/16))
k_d *= 0x1.0p-4;
num_r = fputil::multiply_add(k_d, -den_d, num_d); // Exact
den_r = fputil::multiply_add(k_d, num_d, den_d); // Exact
q.hi = num_r / den_r;
} else {
// For 0 < n/d < 1/16, we just need to calculate the lower part of their
// quotient.
q.hi = q_d;
num_r = num_d;
den_r = den_d;
}
#ifdef LIBC_TARGET_CPU_HAS_FMA
q.lo = fputil::multiply_add(q.hi, -den_r, num_r) / den_r;
#else
// Compute `(num_r - q.hi * den_r) / den_r` accurately without FMA
// instructions.
fputil::DoubleDouble q_hi_dd = split_d(q.hi);
double t1 = fputil::multiply_add(q_hi_dd.hi, -den_r, num_r); // Exact
double t2 = fputil::multiply_add(q_hi_dd.lo, -den_r, t1);
q.lo = t2 / den_r;
#endif // LIBC_TARGET_CPU_HAS_FMA

// Taylor polynomial, evaluating using Horner's scheme:
// P = x - x^3/3 + x^5/5 -x^7/7 + x^9/9 - x^11/11 + x^13/13 - x^15/15
// + x^17/17
// = x*(1 + x^2*(-1/3 + x^2*(1/5 + x^2*(-1/7 + x^2*(1/9 + x^2*
// *(-1/11 + x^2*(1/13 + x^2*(-1/15 + x^2 * 1/17))))))))
fputil::DoubleDouble q2 = fputil::quick_mult(q, q);
fputil::DoubleDouble p_dd =
fputil::polyeval(q2, COEFFS[0], COEFFS[1], COEFFS[2], COEFFS[3],
COEFFS[4], COEFFS[5], COEFFS[6], COEFFS[7], COEFFS[8]);
fputil::DoubleDouble r_dd =
fputil::add(const_term, fputil::multiply_add(q, p_dd, ATAN_I[idx]));
r_dd.hi *= final_sign;
r_dd.lo *= final_sign;

// Make sure the sum is normalized:
fputil::DoubleDouble rr = fputil::exact_add(r_dd.hi, r_dd.lo);
// Round to odd.
uint64_t rr_bits = cpp::bit_cast<uint64_t>(rr.hi);
if (LIBC_UNLIKELY(((rr_bits & 0xfff'ffff) == 0) && (rr.lo != 0.0))) {
Sign hi_sign = fputil::FPBits<double>(rr.hi).sign();
Sign lo_sign = fputil::FPBits<double>(rr.lo).sign();
if (hi_sign == lo_sign) {
++rr_bits;
} else if ((rr_bits & fputil::FPBits<double>::FRACTION_MASK) > 0) {
--rr_bits;
}
}

return static_cast<float>(cpp::bit_cast<double>(rr_bits));
}

} // anonymous namespace

// There are several range reduction steps we can take for atan2(y, x) as
// follow:

// * Range reduction 1: signness
// atan2(y, x) will return a number between -PI and PI representing the angle
// forming by the 0x axis and the vector (x, y) on the 0xy-plane.
// In particular, we have that:
// atan2(y, x) = atan( y/x ) if x >= 0 and y >= 0 (I-quadrant)
// = pi + atan( y/x ) if x < 0 and y >= 0 (II-quadrant)
// = -pi + atan( y/x ) if x < 0 and y < 0 (III-quadrant)
// = atan( y/x ) if x >= 0 and y < 0 (IV-quadrant)
// Since atan function is odd, we can use the formula:
// atan(-u) = -atan(u)
// to adjust the above conditions a bit further:
// atan2(y, x) = atan( |y|/|x| ) if x >= 0 and y >= 0 (I-quadrant)
// = pi - atan( |y|/|x| ) if x < 0 and y >= 0 (II-quadrant)
// = -pi + atan( |y|/|x| ) if x < 0 and y < 0 (III-quadrant)
// = -atan( |y|/|x| ) if x >= 0 and y < 0 (IV-quadrant)
// Which can be simplified to:
// atan2(y, x) = sign(y) * atan( |y|/|x| ) if x >= 0
// = sign(y) * (pi - atan( |y|/|x| )) if x < 0

// * Range reduction 2: reciprocal
// Now that the argument inside atan is positive, we can use the formula:
// atan(1/x) = pi/2 - atan(x)
// to make the argument inside atan <= 1 as follow:
// atan2(y, x) = sign(y) * atan( |y|/|x|) if 0 <= |y| <= x
// = sign(y) * (pi/2 - atan( |x|/|y| ) if 0 <= x < |y|
// = sign(y) * (pi - atan( |y|/|x| )) if 0 <= |y| <= -x
// = sign(y) * (pi/2 + atan( |x|/|y| )) if 0 <= -x < |y|

// * Range reduction 3: look up table.
// After the previous two range reduction steps, we reduce the problem to
// compute atan(u) with 0 <= u <= 1, or to be precise:
// atan( n / d ) where n = min(|x|, |y|) and d = max(|x|, |y|).
// An accurate polynomial approximation for the whole [0, 1] input range will
// require a very large degree. To make it more efficient, we reduce the input
// range further by finding an integer idx such that:
// | n/d - idx/16 | <= 1/32.
// In particular,
// idx := 2^-4 * round(2^4 * n/d)
// Then for the fast pass, we find a polynomial approximation for:
// atan( n/d ) ~ atan( idx/16 ) + (n/d - idx/16) * Q(n/d - idx/16)
// For the accurate pass, we use the addition formula:
// atan( n/d ) - atan( idx/16 ) = atan( (n/d - idx/16)/(1 + (n*idx)/(16*d)) )
// = atan( (n - d * idx/16)/(d + n * idx/16) )
// And finally we use Taylor polynomial to compute the RHS in the accurate pass:
// atan(u) ~ P(u) = u - u^3/3 + u^5/5 - u^7/7 + u^9/9 - u^11/11 + u^13/13 -
// - u^15/15 + u^17/17
// It's error in double-double precision is estimated in Sollya to be:
// > P = x - x^3/3 + x^5/5 -x^7/7 + x^9/9 - x^11/11 + x^13/13 - x^15/15
// + x^17/17;
// > dirtyinfnorm(atan(x) - P, [-2^-5, 2^-5]);
// 0x1.aec6f...p-100
// which is about rounding errors of double-double (2^-104).

LLVM_LIBC_FUNCTION(float, atan2f, (float y, float x)) {
using FPBits = typename fputil::FPBits<float>;
constexpr double IS_NEG[2] = {1.0, -1.0};
constexpr double PI = 0x1.921fb54442d18p1;
constexpr double PI_LO = 0x1.1a62633145c07p-53;
constexpr double PI_OVER_4 = 0x1.921fb54442d18p-1;
constexpr double PI_OVER_2 = 0x1.921fb54442d18p0;
constexpr double THREE_PI_OVER_4 = 0x1.2d97c7f3321d2p+1;
// Adjustment for constant term:
// CONST_ADJ[x_sign][y_sign][recip]
constexpr fputil::DoubleDouble CONST_ADJ[2][2][2] = {
{{{0.0, 0.0}, {-PI_LO / 2, -PI_OVER_2}},
{{-0.0, -0.0}, {-PI_LO / 2, -PI_OVER_2}}},
{{{-PI_LO, -PI}, {PI_LO / 2, PI_OVER_2}},
{{-PI_LO, -PI}, {PI_LO / 2, PI_OVER_2}}}};

FPBits x_bits(x), y_bits(y);
bool x_sign = x_bits.sign().is_neg();
bool y_sign = y_bits.sign().is_neg();
x_bits.set_sign(Sign::POS);
y_bits.set_sign(Sign::POS);
uint32_t x_abs = x_bits.uintval();
uint32_t y_abs = y_bits.uintval();
uint32_t max_abs = x_abs > y_abs ? x_abs : y_abs;
uint32_t min_abs = x_abs <= y_abs ? x_abs : y_abs;

if (LIBC_UNLIKELY(max_abs >= 0x7f80'0000U || min_abs == 0U)) {
if (x_bits.is_nan() || y_bits.is_nan())
return FPBits::quiet_nan().get_val();
size_t x_except = x_abs == 0 ? 0 : (x_abs == 0x7f80'0000 ? 2 : 1);
size_t y_except = y_abs == 0 ? 0 : (y_abs == 0x7f80'0000 ? 2 : 1);

// Exceptional cases:
// EXCEPT[y_except][x_except][x_is_neg]
// with x_except & y_except:
// 0: zero
// 1: finite, non-zero
// 2: infinity
constexpr double EXCEPTS[3][3][2] = {
{{0.0, PI}, {0.0, PI}, {0.0, PI}},
{{PI_OVER_2, PI_OVER_2}, {0.0, 0.0}, {0.0, PI}},
{{PI_OVER_2, PI_OVER_2},
{PI_OVER_2, PI_OVER_2},
{PI_OVER_4, THREE_PI_OVER_4}},
};

double r = IS_NEG[y_sign] * EXCEPTS[y_except][x_except][x_sign];

return static_cast<float>(r);
}

bool recip = x_abs < y_abs;
double final_sign = IS_NEG[(x_sign != y_sign) != recip];
fputil::DoubleDouble const_term = CONST_ADJ[x_sign][y_sign][recip];
double num_d = static_cast<double>(FPBits(min_abs).get_val());
double den_d = static_cast<double>(FPBits(max_abs).get_val());
double q_d = num_d / den_d;

double k_d = fputil::nearest_integer(q_d * 0x1.0p4f);
int idx = static_cast<int>(k_d);
q_d = fputil::multiply_add(k_d, -0x1.0p-4, q_d);

double p = atan_eval(q_d, idx);
double r = final_sign *
fputil::multiply_add(q_d, p, const_term.hi + ATAN_COEFFS[idx][0]);

constexpr uint32_t LOWER_ERR = 4;
// Mask sticky bits in double precision before rounding to single precision.
constexpr uint32_t MASK =
mask_trailing_ones<uint32_t, fputil::FPBits<double>::SIG_LEN -
FPBits::SIG_LEN - 1>();
constexpr uint32_t UPPER_ERR = MASK - LOWER_ERR;

uint32_t r_bits = static_cast<uint32_t>(cpp::bit_cast<uint64_t>(r)) & MASK;

// Ziv's rounding test.
if (LIBC_LIKELY(r_bits > LOWER_ERR && r_bits < UPPER_ERR))
return static_cast<float>(r);

return atan2f_double_double(num_d, den_d, q_d, idx, k_d, final_sign,
const_term);
}

} // namespace LIBC_NAMESPACE
22 changes: 10 additions & 12 deletions libc/src/math/generic/atanf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,15 @@ LLVM_LIBC_FUNCTION(float, atanf, (float x)) {
}
// Use Taylor polynomial:
// atan(x) ~ x * (1 - x^2 / 3 + x^4 / 5 - x^6 / 7 + x^8 / 9 - x^10 / 11).
constexpr double ATAN_TAYLOR[6] = {
0x1.0000000000000p+0, -0x1.5555555555555p-2, 0x1.999999999999ap-3,
-0x1.2492492492492p-3, 0x1.c71c71c71c71cp-4, -0x1.745d1745d1746p-4,
};
double x2 = x_d * x_d;
double x4 = x2 * x2;
double c0 = fputil::multiply_add(x2, ATAN_COEFFS[0][1], ATAN_COEFFS[0][0]);
double c1 = fputil::multiply_add(x2, ATAN_COEFFS[0][3], ATAN_COEFFS[0][2]);
double c2 = fputil::multiply_add(x2, ATAN_COEFFS[0][5], ATAN_COEFFS[0][4]);
double c0 = fputil::multiply_add(x2, ATAN_TAYLOR[1], ATAN_TAYLOR[0]);
double c1 = fputil::multiply_add(x2, ATAN_TAYLOR[3], ATAN_TAYLOR[2]);
double c2 = fputil::multiply_add(x2, ATAN_TAYLOR[5], ATAN_TAYLOR[4]);
double p = fputil::polyeval(x4, c0, c1, c2);
double r = fputil::multiply_add(x_d, p, const_term);
return static_cast<float>(r);
Expand All @@ -81,11 +85,6 @@ LLVM_LIBC_FUNCTION(float, atanf, (float x)) {
int idx;

if (x_abs > 0x3f80'0000U) {
// Exceptional value:
if (LIBC_UNLIKELY(x_abs == 0x3ffe'2ec1U)) { // |x| = 0x1.fc5d82p+0
return sign.is_pos() ? fputil::round_result_slightly_up(0x1.1ab2fp0f)
: fputil::round_result_slightly_down(-0x1.1ab2fp0f);
}
// |x| > 1, we need to invert x, so we will perform range reduction in
// double precision.
x_d = 1.0 / static_cast<double>(x_bits.get_val());
Expand All @@ -98,10 +97,9 @@ LLVM_LIBC_FUNCTION(float, atanf, (float x)) {
SIGNED_PI_OVER_2[sign.is_neg()]);
} else {
// Exceptional value:
if (LIBC_UNLIKELY(x_abs == 0x3dbb'6ac7U)) { // |x| = 0x1.76d58ep-4
return sign.is_pos()
? fputil::round_result_slightly_up(0x1.75cb06p-4f)
: fputil::round_result_slightly_down(-0x1.75cb06p-4f);
if (LIBC_UNLIKELY(x_abs == 0x3d8d'6b23U)) { // |x| = 0x1.1ad646p-4
return sign.is_pos() ? fputil::round_result_slightly_down(0x1.1a6386p-4f)
: fputil::round_result_slightly_up(-0x1.1a6386p-4f);
}
// Perform range reduction in single precision.
float x_f = x_bits.get_val();
Expand Down
117 changes: 61 additions & 56 deletions libc/src/math/generic/inv_trigf_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,65 +16,70 @@ namespace LIBC_NAMESPACE {
// Generated by Sollya with:
// > for i from 1 to 16 do {
// mid_point = i/16;
// P = fpminimax(atan(mid_point + x), 7, [|D...|], [-1/32, 1/32]);
// P = fpminimax(atan(mid_point + x), 8, [|D...|], [-1/32, 1/32]);
// print("{", coeff(P, 0), ",", coeff(P, 1), ",", coeff(P, 2), ",",
// coeff(P, 3), ",", coeff(P, 4), ",", coeff(P, 5), ",", coeff(P, 6),
// ",", coeff(P, 7), "},");
// ",", coeff(P, 7), ",", coeff(P, 8), "},");
// };
// For i = 0, ATAN_COEFFS[0][j] = (-1)^j * (1/(2*j + 1)) is the odd coefficients
// of the Taylor polynomial of atan(x).
double ATAN_COEFFS[17][8] = {
{0x1.0000000000000p+0, -0x1.5555555555555p-2, 0x1.999999999999ap-3,
-0x1.2492492492492p-3, 0x1.c71c71c71c71cp-4, -0x1.745d1745d1746p-4,
0x1.3b13b13b13b14p-4, -0x1.1111111111111p-4},
{0x1.ff55bb72cfdb1p-5, 0x1.fe01fe01fe1bp-1, -0x1.fc05f80821d1ap-5,
-0x1.4d6930419fc5fp-2, 0x1.f61b9f6d69313p-5, 0x1.8208a32f4346cp-3,
-0x1.ecb8fc53d04efp-5, -0x1.060710cb59cbcp-3},
{0x1.fd5ba9aac2f3cp-4, 0x1.f81f81f81f96ap-1, -0x1.f05e09cf4c1b2p-4,
-0x1.368c3aac7543ep-2, 0x1.d9b14bddfac55p-4, 0x1.4048e55ec725ep-3,
-0x1.b98ca3c1594b5p-4, -0x1.664eabaabbc16p-4},
{0x1.7b97b4bce5ae7p-3, 0x1.ee9c7f8458f06p-1, -0x1.665c226c8dc69p-3,
-0x1.1344bb77961b7p-2, 0x1.42ac97745d3ccp-3, 0x1.c32e142047ec1p-4,
-0x1.137ae41ab96cbp-3, -0x1.1a6ae8c09a4b6p-5},
{0x1.f5b75f92c80c6p-3, 0x1.e1e1e1e1e1ed4p-1, -0x1.c5894d101ad4p-3,
-0x1.ce6de02b38c38p-3, 0x1.78a3920c336b9p-3, 0x1.dd5ff94a9d499p-5,
-0x1.1ac2d3f9d072ep-3, 0x1.0af9735dff373p-6},
{0x1.362773707ebc5p-2, 0x1.d272ca3fc5b8bp-1, -0x1.0997e8ae90cb6p-2,
-0x1.6cf6667146798p-3, 0x1.8dd1dff17f3d3p-3, 0x1.24860eced656fp-7,
-0x1.f4220e8f18ed5p-4, 0x1.b700aed7cdc34p-5},
{0x1.6f61941e4deeep-2, 0x1.c0e070381c115p-1, -0x1.2726dd1347c7ep-2,
-0x1.09f37b3ad010dp-3, 0x1.85eaca5196f5cp-3, -0x1.04d640117852ap-5,
-0x1.802c2956871c7p-4, 0x1.2992b45df0ee7p-4},
{0x1.a64eec3cc23fep-2, 0x1.adbe87f94906bp-1, -0x1.3b9d8eab5eae5p-2,
-0x1.57c09646faabbp-4, 0x1.6795330e73aep-3, -0x1.f2d89a702a652p-5,
-0x1.f3afd90a9d4d7p-5, 0x1.3261723d3f153p-4},
{0x1.dac670561bb53p-2, 0x1.999999999998fp-1, -0x1.47ae147afd8cap-2,
-0x1.5d867c3dfd72ap-5, 0x1.3a92a76cba833p-3, -0x1.3ec460286928ap-4,
-0x1.ed02ff86892acp-6, 0x1.0a674c8f05727p-4},
{0x1.0657e94db30d2p-1, 0x1.84f00c27805ffp-1, -0x1.4c62cb564f677p-2,
-0x1.e6495b262dfe7p-8, 0x1.063c34eca262bp-3, -0x1.58b78dc79b5aep-4,
-0x1.4623815233be1p-8, 0x1.93afe94328089p-5},
{0x1.1e00babdefeb6p-1, 0x1.702e05c0b8159p-1, -0x1.4af2b78236bd6p-2,
0x1.5d0b7ea46ed08p-6, 0x1.a124870236935p-4, -0x1.519e1ec133a88p-4,
0x1.a54632a3f48c7p-7, 0x1.099ca0945096dp-5},
{0x1.345f01cce37bdp-1, 0x1.5babcc647fa7ep-1, -0x1.449db09443a67p-2,
0x1.655caac78a0fcp-5, 0x1.3bbbdb0d09efap-4, -0x1.34a306c27e021p-4,
0x1.83fe749c7966p-6, 0x1.2057cc96d9edcp-6},
{0x1.4978fa3269ee2p-1, 0x1.47ae147ae146bp-1, -0x1.3a92a305652e1p-2,
0x1.ec21b5172657fp-5, 0x1.c2f8c45d2f4eep-5, -0x1.0ba99c4aeb8acp-4,
0x1.d716a4af4d1d6p-6, 0x1.97fba0a9696dep-8},
{0x1.5d58987169b19p-1, 0x1.34679ace0133cp-1, -0x1.2ddfb03920e2fp-2,
0x1.2491307c0fa0bp-4, 0x1.29c7eca0136fp-5, -0x1.bca792caa6f1cp-5,
0x1.e5d92545576bcp-6, -0x1.8ca76fcf5ccd2p-10},
{0x1.700a7c5784634p-1, 0x1.21fb78121fb71p-1, -0x1.1f6a8499ea541p-2,
0x1.41b15e5e77bcfp-4, 0x1.59bc9bf54fb02p-6, -0x1.63b54ff058e0fp-5,
0x1.c8da01221306fp-6, -0x1.906b2c274c39cp-8},
{0x1.819d0b7158a4dp-1, 0x1.107fbbe01107cp-1, -0x1.0feeb40897d4ep-2,
0x1.50e5afb95f5d6p-4, 0x1.2a7c2f0c7495dp-7, -0x1.12bd2bb5062cdp-5,
0x1.93e8ceb89afebp-6, -0x1.10da9b8c6b731p-7},
{0x1.921fb54442d18p-1, 0x1.fffffffffffebp-2, -0x1.fffffffffcbbcp-3,
0x1.555555564e2fep-4, -0x1.20b17d5dd89dcp-30, -0x1.9999c5ad71711p-6,
0x1.5558b76e7aaf9p-6, -0x1.236e803c6c1f6p-7},
// For i = 0, the polynomial is generated by:
// > P = fpminimax(atan(x)/x, 7, [|1, D...|], [0, 1/32]);
// > dirtyinfnorm((atan(x) - x*P)/x, [0, 1/32]);
// 0x1.feb2fcdba66447ccbe28a1a0f935b51678a718fb1p-59
// Notice that degree-7 is good enough for atanf, but degree-8 helps reduce the
// error bounds for atan2f's fast pass 16 times, and it does not affect the
// performance of atanf much.
double ATAN_COEFFS[17][9] = {
{0.0, 1.0, 0x1.3f8d76d26d61bp-47, -0x1.5555555574cd8p-2,
0x1.0dde5d06878eap-29, 0x1.99997738acc77p-3, 0x1.2c43eac9797cap-16,
-0x1.25fb020007dbdp-3, 0x1.c1b6c31d7b0aep-7},
{0x1.ff55bb72cfde9p-5, 0x1.fe01fe01fe007p-1, -0x1.fc05f809ed8dap-5,
-0x1.4d69303afe04ep-2, 0x1.f61bc3e8349cp-5, 0x1.820839278756bp-3,
-0x1.eda4de1c6bf3fp-5, -0x1.0514d42d64a63p-3, 0x1.db3746a442dcbp-5},
{0x1.fd5ba9aac2f6ep-4, 0x1.f81f81f81f813p-1, -0x1.f05e09d0dc378p-4,
-0x1.368c3aa719215p-2, 0x1.d9b16b33ff9c9p-4, 0x1.40488f9c6262ap-3,
-0x1.ba55933e62ea5p-4, -0x1.64c6a15cd9116p-4, 0x1.9273d5939a75ap-4},
{0x1.7b97b4bce5b02p-3, 0x1.ee9c7f8458e05p-1, -0x1.665c226d6961p-3,
-0x1.1344bb7391703p-2, 0x1.42aca8b0081b9p-3, 0x1.c32d9381d7c03p-4,
-0x1.13e970672e246p-3, -0x1.181ed934dd733p-5, 0x1.bad81ea190c08p-4},
{0x1.f5b75f92c80ddp-3, 0x1.e1e1e1e1e1e2cp-1, -0x1.c5894d10d363dp-3,
-0x1.ce6de025f9f5ep-3, 0x1.78a3a07c8dd7fp-3, 0x1.dd5f5180f386ep-5,
-0x1.1b1f513c4536bp-3, 0x1.0df852e58c43cp-6, 0x1.722e7a7e42505p-4},
{0x1.362773707ebccp-2, 0x1.d272ca3fc5b2ep-1, -0x1.0997e8aeca8fbp-2,
-0x1.6cf6666e5e693p-3, 0x1.8dd1e907e88adp-3, 0x1.24849ac0caa5dp-7,
-0x1.f496be486229dp-4, 0x1.b7d54b8e759ecp-5, 0x1.d39c0d39c3922p-5},
{0x1.6f61941e4def1p-2, 0x1.c0e070381c0f2p-1, -0x1.2726dd135d9eep-2,
-0x1.09f37b39b70e4p-3, 0x1.85eacdaadd712p-3, -0x1.04d66340d5b9p-5,
-0x1.8056b15a22b98p-4, 0x1.29baf494ad3ddp-4, 0x1.52d5881322a7ap-6},
{0x1.a64eec3cc23fdp-2, 0x1.adbe87f94906ap-1, -0x1.3b9d8eab55addp-2,
-0x1.57c09646eb7p-4, 0x1.6795319e3b8dfp-3, -0x1.f2d89b5ef31bep-5,
-0x1.f38aac26203cap-5, 0x1.3262802235e3fp-4, -0x1.2afd6b9a57d66p-7},
{0x1.dac670561bb4fp-2, 0x1.99999999999ap-1, -0x1.47ae147adff11p-2,
-0x1.5d867c40188b7p-5, 0x1.3a92a2df85e7ap-3, -0x1.3ec457c46e851p-4,
-0x1.ec1b9777e2e5bp-6, 0x1.0a542992a821ep-4, -0x1.ccffbe2f0d945p-6},
{0x1.0657e94db30dp-1, 0x1.84f00c2780615p-1, -0x1.4c62cb562defap-2,
-0x1.e6495b3c14e03p-8, 0x1.063c2fa617bfcp-3, -0x1.58b782d9907aap-4,
-0x1.41e6ff524b7fp-8, 0x1.937dfff3205a7p-5, -0x1.0fb1fd1c729dp-5},
{0x1.1e00babdefeb4p-1, 0x1.702e05c0b816ep-1, -0x1.4af2b78215fbep-2,
0x1.5d0b7e9f36997p-6, 0x1.a1247cb978debp-4, -0x1.519e1457734cap-4,
0x1.a755cf86b5bfbp-7, 0x1.096d174284564p-5, -0x1.081adf539ad58p-5},
{0x1.345f01cce37bbp-1, 0x1.5babcc647fa8ep-1, -0x1.449db09426a6dp-2,
0x1.655caac5896dap-5, 0x1.3bbbd22d05a61p-4, -0x1.34a2febee042fp-4,
0x1.84df9c8269e34p-6, 0x1.200e8176c899ap-6, -0x1.c00b23c3ce222p-6},
{0x1.4978fa3269ee1p-1, 0x1.47ae147ae1477p-1, -0x1.3a92a3055231ap-2,
0x1.ec21b515a4a2p-5, 0x1.c2f8b81f9a0d2p-5, -0x1.0ba9964125453p-4,
0x1.d7b5614777a05p-6, 0x1.971e91ed73595p-8, -0x1.3fc375a78dc74p-6},
{0x1.5d58987169b18p-1, 0x1.34679ace01343p-1, -0x1.2ddfb039136e5p-2,
0x1.2491307b9fb73p-4, 0x1.29c7e4886dc22p-5, -0x1.bca78bcca83ap-5,
0x1.e63efd7cbe1ddp-6, -0x1.8ea6c4f03b42dp-10, -0x1.9385b5c3a6997p-7},
{0x1.700a7c5784634p-1, 0x1.21fb78121fb76p-1, -0x1.1f6a8499e5d1ap-2,
0x1.41b15e5e29423p-4, 0x1.59bc953163345p-6, -0x1.63b54b13184ddp-5,
0x1.c9086666d213p-6, -0x1.90c3b4ad8d4bcp-8, -0x1.80f08ed9f6f57p-8},
{0x1.819d0b7158a4dp-1, 0x1.107fbbe01107ep-1, -0x1.0feeb4089670ep-2,
0x1.50e5afb93f5cbp-4, 0x1.2a7c2adffeffbp-7, -0x1.12bd29b4f1b43p-5,
0x1.93f71f0eb00eap-6, -0x1.10ece5ad30e28p-7, -0x1.db1a76bcd2b9cp-10},
{0x1.921fb54442d18p-1, 0x1.ffffffffffffep-2, -0x1.fffffffffc51cp-3,
0x1.555555557002ep-4, -0x1.a88260c338e75p-30, -0x1.99999f9a7614fp-6,
0x1.555e31a1e15e9p-6, -0x1.245240d65e629p-7, -0x1.fa9ba66478903p-11},
};

} // namespace LIBC_NAMESPACE
7 changes: 4 additions & 3 deletions libc/src/math/generic/inv_trigf_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,21 @@ namespace LIBC_NAMESPACE {
constexpr double M_MATH_PI = 0x1.921fb54442d18p+1;
constexpr double M_MATH_PI_2 = 0x1.921fb54442d18p+0;

extern double ATAN_COEFFS[17][8];
extern double ATAN_COEFFS[17][9];

// For |x| <= 1/32 and 1 <= i <= 16, return Q(x) such that:
// For |x| <= 1/32 and 0 <= i <= 16, return Q(x) such that:
// Q(x) ~ (atan(x + i/16) - atan(i/16)) / x.
LIBC_INLINE double atan_eval(double x, int i) {
double x2 = x * x;

double c0 = fputil::multiply_add(x, ATAN_COEFFS[i][2], ATAN_COEFFS[i][1]);
double c1 = fputil::multiply_add(x, ATAN_COEFFS[i][4], ATAN_COEFFS[i][3]);
double c2 = fputil::multiply_add(x, ATAN_COEFFS[i][6], ATAN_COEFFS[i][5]);
double c3 = fputil::multiply_add(x, ATAN_COEFFS[i][8], ATAN_COEFFS[i][7]);

double x4 = x2 * x2;
double d1 = fputil::multiply_add(x2, c1, c0);
double d2 = fputil::multiply_add(x2, ATAN_COEFFS[i][7], c2);
double d2 = fputil::multiply_add(x2, c3, c2);
double p = fputil::multiply_add(x4, d2, d1);
return p;
}
Expand Down
2 changes: 2 additions & 0 deletions libc/src/stdio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ add_stdio_entrypoint_object(ferror)
add_stdio_entrypoint_object(ferror_unlocked)
add_stdio_entrypoint_object(fseek)
add_stdio_entrypoint_object(ftell)
add_stdio_entrypoint_object(fseeko)
add_stdio_entrypoint_object(ftello)
add_stdio_entrypoint_object(fflush)
add_stdio_entrypoint_object(clearerr)
add_stdio_entrypoint_object(clearerr_unlocked)
Expand Down
21 changes: 21 additions & 0 deletions libc/src/stdio/fseeko.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//===-- Implementation header of fseeko -------------------------*- 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_STDIO_FSEEKO_H
#define LLVM_LIBC_SRC_STDIO_FSEEKO_H

#include <stdio.h>
#include <unistd.h>

namespace LIBC_NAMESPACE {

int fseeko(::FILE *stream, off_t offset, int whence);

} // namespace LIBC_NAMESPACE

#endif // LLVM_LIBC_SRC_STDIO_FSEEKO_H
21 changes: 21 additions & 0 deletions libc/src/stdio/ftello.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//===-- Implementation header of ftello -------------------------*- 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_STDIO_FTELLO_H
#define LLVM_LIBC_SRC_STDIO_FTELLO_H

#include <stdio.h>
#include <unistd.h>

namespace LIBC_NAMESPACE {

off_t ftello(::FILE *f);

} // namespace LIBC_NAMESPACE

#endif // LLVM_LIBC_SRC_STDIO_FTELLO_H
26 changes: 24 additions & 2 deletions libc/src/stdio/generic/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ add_entrypoint_object(
../fseek.h
DEPENDS
libc.src.errno.errno
libc.include.stdio
libc.src.__support.File.file
libc.src.__support.File.platform_file
)
Expand All @@ -116,7 +115,30 @@ add_entrypoint_object(
../ftell.h
DEPENDS
libc.src.errno.errno
libc.include.stdio
libc.src.__support.File.file
libc.src.__support.File.platform_file
)

add_entrypoint_object(
fseeko
SRCS
fseeko.cpp
HDRS
../fseeko.h
DEPENDS
libc.src.errno.errno
libc.src.__support.File.file
libc.src.__support.File.platform_file
)

add_entrypoint_object(
ftello
SRCS
ftello.cpp
HDRS
../ftello.h
DEPENDS
libc.src.errno.errno
libc.src.__support.File.file
libc.src.__support.File.platform_file
)
Expand Down
1 change: 0 additions & 1 deletion libc/src/stdio/generic/fseek.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#include "src/__support/File/file.h"

#include "src/errno/libc_errno.h"
#include <stdio.h>

namespace LIBC_NAMESPACE {

Expand Down
26 changes: 26 additions & 0 deletions libc/src/stdio/generic/fseeko.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//===-- Implementation of fseeko ------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/stdio/fseeko.h"
#include "src/__support/File/file.h"

#include "src/errno/libc_errno.h"

namespace LIBC_NAMESPACE {

LLVM_LIBC_FUNCTION(int, fseeko, (::FILE * stream, off_t offset, int whence)) {
auto result =
reinterpret_cast<LIBC_NAMESPACE::File *>(stream)->seek(offset, whence);
if (!result.has_value()) {
libc_errno = result.error();
return -1;
}
return 0;
}

} // namespace LIBC_NAMESPACE
1 change: 0 additions & 1 deletion libc/src/stdio/generic/ftell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#include "src/__support/File/file.h"

#include "src/errno/libc_errno.h"
#include <stdio.h>

namespace LIBC_NAMESPACE {

Expand Down
25 changes: 25 additions & 0 deletions libc/src/stdio/generic/ftello.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//===-- Implementation of ftello ------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/stdio/ftello.h"
#include "src/__support/File/file.h"

#include "src/errno/libc_errno.h"

namespace LIBC_NAMESPACE {

LLVM_LIBC_FUNCTION(off_t, ftello, (::FILE * stream)) {
auto result = reinterpret_cast<LIBC_NAMESPACE::File *>(stream)->tell();
if (!result.has_value()) {
libc_errno = result.error();
return -1;
}
return result.value();
}

} // namespace LIBC_NAMESPACE
1 change: 1 addition & 0 deletions libc/src/stdio/printf_core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ add_header_library(
libc.src.__support.arg_list
libc.src.__support.ctype_utils
libc.src.__support.str_to_integer
libc.src.__support.CPP.algorithm
libc.src.__support.CPP.bit
libc.src.__support.CPP.optional
libc.src.__support.CPP.string_view
Expand Down
1 change: 1 addition & 0 deletions libc/src/stdio/printf_core/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_PARSER_H

#include "include/llvm-libc-macros/stdfix-macros.h"
#include "src/__support/CPP/algorithm.h" // max
#include "src/__support/CPP/optional.h"
#include "src/__support/CPP/type_traits.h"
#include "src/__support/str_to_integer.h"
Expand Down
13 changes: 13 additions & 0 deletions libc/test/src/math/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1719,6 +1719,19 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)

add_fp_unittest(
atan2f_test
NEED_MPFR
SUITE
libc-math-unittests
SRCS
atan2f_test.cpp
DEPENDS
libc.include.math
libc.src.math.atan2f
libc.src.__support.FPUtil.fp_bits
)

add_subdirectory(generic)
add_subdirectory(smoke)

Expand Down
133 changes: 133 additions & 0 deletions libc/test/src/math/atan2f_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
//===-- Unittests for atan2f ----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "include/llvm-libc-macros/math-macros.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/math/atan2f.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"
#include "utils/MPFRWrapper/MPFRUtils.h"

using LlvmLibcAtan2fTest = LIBC_NAMESPACE::testing::FPTest<float>;
using LIBC_NAMESPACE::testing::tlog;

namespace mpfr = LIBC_NAMESPACE::testing::mpfr;

TEST_F(LlvmLibcAtan2fTest, TrickyInputs) {
constexpr int N = 17;
mpfr::BinaryInput<float> INPUTS[N] = {
{0x1.0cb3a4p+20f, 0x1.4ebacp+22f}, {0x1.12215p+1f, 0x1.4fabfcp+22f},
{-0x1.13baaep+41f, 0x1.5bd22ep+23f}, {0x1.1ff7dcp+41f, 0x1.aec0a6p+23f},
{0x1.2bc794p+23f, 0x1.0bc0c6p+23f}, {0x1.2fba3ap+42f, 0x1.f99456p+23f},
{0x1.5ea1f8p+27f, 0x1.f2a1aep+23f}, {0x1.7a931p+44f, 0x1.352ac4p+22f},
{0x1.8802bcp+21f, 0x1.8f130ap+23f}, {0x1.658ef8p+17f, 0x1.3c00f4p+22f},
{0x1.69fb0cp+21f, 0x1.39e4c4p+23f}, {0x1.8eb24cp+11f, 0x1.36518p+23f},
{0x1.9e7ebp+30f, 0x1.d80522p+23f}, {0x1.b4bdeep+19f, 0x1.c19b4p+23f},
{0x1.bc201p+43f, 0x1.617346p+23f}, {0x1.c96c3cp+20f, 0x1.c01d1ep+23f},
{0x1.781fcp+28f, 0x1.dcb3cap+23f},
};

for (int i = 0; i < N; ++i) {
float x = INPUTS[i].x;
float y = INPUTS[i].y;
ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Atan2, INPUTS[i],
LIBC_NAMESPACE::atan2f(x, y), 0.5);
INPUTS[i].x = -INPUTS[i].x;
ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Atan2, INPUTS[i],
LIBC_NAMESPACE::atan2f(-x, y), 0.5);
INPUTS[i].y = -INPUTS[i].y;
ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Atan2, INPUTS[i],
LIBC_NAMESPACE::atan2f(-x, -y), 0.5);
INPUTS[i].x = -INPUTS[i].x;
ASSERT_MPFR_MATCH_ALL_ROUNDING(mpfr::Operation::Atan2, INPUTS[i],
LIBC_NAMESPACE::atan2f(x, -y), 0.5);
}
}

TEST_F(LlvmLibcAtan2fTest, InFloatRange) {
constexpr uint32_t X_COUNT = 1'23;
constexpr uint32_t X_START = FPBits(0.25f).uintval();
constexpr uint32_t X_STOP = FPBits(4.0f).uintval();
constexpr uint32_t X_STEP = (X_STOP - X_START) / X_COUNT;

constexpr uint32_t Y_COUNT = 1'37;
constexpr uint32_t Y_START = FPBits(0.25f).uintval();
constexpr uint32_t Y_STOP = FPBits(4.0f).uintval();
constexpr uint32_t Y_STEP = (Y_STOP - Y_START) / Y_COUNT;

auto test = [&](mpfr::RoundingMode rounding_mode) {
mpfr::ForceRoundingMode __r(rounding_mode);
if (!__r.success)
return;

uint64_t fails = 0;
uint64_t finite_count = 0;
uint64_t total_count = 0;
float failed_x, failed_y, failed_r = 0.0;
double tol = 0.5;

for (uint32_t i = 0, v = X_START; i <= X_COUNT; ++i, v += X_STEP) {
float x = FPBits(v).get_val();
if (isnan(x) || isinf(x) || x < 0.0)
continue;

for (uint32_t j = 0, w = Y_START; j <= Y_COUNT; ++j, w += Y_STEP) {
float y = FPBits(w).get_val();
if (isnan(y) || isinf(y))
continue;

LIBC_NAMESPACE::libc_errno = 0;
float result = LIBC_NAMESPACE::atan2f(x, y);
++total_count;
if (isnan(result) || isinf(result))
continue;

++finite_count;
mpfr::BinaryInput<float> inputs{x, y};

if (!TEST_MPFR_MATCH_ROUNDING_SILENTLY(mpfr::Operation::Atan2, inputs,
result, 0.5, rounding_mode)) {
++fails;
while (!TEST_MPFR_MATCH_ROUNDING_SILENTLY(
mpfr::Operation::Atan2, inputs, result, tol, rounding_mode)) {
failed_x = x;
failed_y = y;
failed_r = result;

if (tol > 1000.0)
break;

tol *= 2.0;
}
}
}
}
if (fails || (finite_count < total_count)) {
tlog << " Atan2f failed: " << fails << "/" << finite_count << "/"
<< total_count << " tests.\n"
<< " Max ULPs is at most: " << static_cast<uint64_t>(tol) << ".\n";
}
if (fails) {
mpfr::BinaryInput<float> inputs{failed_x, failed_y};
EXPECT_MPFR_MATCH(mpfr::Operation::Atan2, inputs, failed_r, 0.5,
rounding_mode);
}
};

tlog << " Test Rounding To Nearest...\n";
test(mpfr::RoundingMode::Nearest);

tlog << " Test Rounding Downward...\n";
test(mpfr::RoundingMode::Downward);

tlog << " Test Rounding Upward...\n";
test(mpfr::RoundingMode::Upward);

tlog << " Test Rounding Toward Zero...\n";
test(mpfr::RoundingMode::TowardZero);
}
5 changes: 4 additions & 1 deletion libc/test/src/math/atanf_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,15 @@ TEST_F(LlvmLibcAtanfTest, InFloatRange) {
TEST_F(LlvmLibcAtanfTest, SpecialValues) {
uint32_t val_arr[] = {
0x3d8d6b23U, // x = 0x1.1ad646p-4f
0x3dbb6ac7U, // x = 0x1.76d58ep-4f
0x3feefcfbU, // x = 0x1.ddf9f6p+0f
0x3ffe2ec1U, // x = 0x1.fc5d82p+0f
0xbd8d6b23U, // x = -0x1.1ad646p-4f
0xbdbb6ac7U, // x = -0x1.76d58ep-4f
0xbfeefcfbU, // x = -0x1.ddf9f6p+0f
0xbffe2ec1U, // x = -0x1.fc5d82p+0
0x7F800000U, // x = +Inf
0xFF800000U, // x = -Inf
0xbffe2ec1U, // x = -0x1.fc5d82p+0f
};
for (uint32_t v : val_arr) {
float x = FPBits(v).get_val();
Expand Down
12 changes: 12 additions & 0 deletions libc/test/src/math/smoke/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2755,6 +2755,18 @@ add_fp_unittest(
libc.src.__support.FPUtil.fp_bits
)

add_fp_unittest(
atan2f_test
SUITE
libc-math-smoke-tests
SRCS
atan2f_test.cpp
DEPENDS
libc.src.errno.errno
libc.src.math.atan2f
libc.src.__support.FPUtil.fp_bits
)

add_fp_unittest(
scalbn_test
SUITE
Expand Down
50 changes: 50 additions & 0 deletions libc/test/src/math/smoke/atan2f_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//===-- Unittests for atan2f ----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "include/llvm-libc-macros/math-macros.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/errno/libc_errno.h"
#include "src/math/atan2f.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"

using LlvmLibcAtan2fTest = LIBC_NAMESPACE::testing::FPTest<float>;

TEST_F(LlvmLibcAtan2fTest, SpecialNumbers) {
LIBC_NAMESPACE::libc_errno = 0;

LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
EXPECT_FP_EQ_ALL_ROUNDING(aNaN, LIBC_NAMESPACE::atan2f(aNaN, zero));
EXPECT_FP_EXCEPTION(0);
EXPECT_MATH_ERRNO(0);

LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
EXPECT_FP_EQ_ALL_ROUNDING(aNaN, LIBC_NAMESPACE::atan2f(1.0f, aNaN));
EXPECT_FP_EXCEPTION(0);
EXPECT_MATH_ERRNO(0);

LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
EXPECT_FP_EQ_ALL_ROUNDING(0.0f, LIBC_NAMESPACE::atan2f(zero, zero));
EXPECT_FP_EXCEPTION(0);
EXPECT_MATH_ERRNO(0);

LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
EXPECT_FP_EQ_ALL_ROUNDING(-0.0f, LIBC_NAMESPACE::atan2f(-0.0f, zero));
EXPECT_FP_EXCEPTION(0);
EXPECT_MATH_ERRNO(0);

LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
EXPECT_FP_EQ_ALL_ROUNDING(0.0f, LIBC_NAMESPACE::atan2f(1.0f, inf));
EXPECT_FP_EXCEPTION(0);
EXPECT_MATH_ERRNO(0);

LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
EXPECT_FP_EQ_ALL_ROUNDING(-0.0f, LIBC_NAMESPACE::atan2f(-1.0f, inf));
EXPECT_FP_EXCEPTION(0);
EXPECT_MATH_ERRNO(0);
}
2 changes: 2 additions & 0 deletions libc/test/src/stdio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,8 @@ add_libc_test(
libc.src.stdio.fread
libc.src.stdio.fseek
libc.src.stdio.ftell
libc.src.stdio.fseeko
libc.src.stdio.ftello
libc.src.stdio.fwrite
libc.src.stdio.setvbuf
)
Expand Down
9 changes: 9 additions & 0 deletions libc/test/src/stdio/ftell_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
#include "src/stdio/fopen.h"
#include "src/stdio/fread.h"
#include "src/stdio/fseek.h"
#include "src/stdio/fseeko.h"
#include "src/stdio/ftell.h"
#include "src/stdio/ftello.h"
#include "src/stdio/fwrite.h"
#include "src/stdio/setvbuf.h"
#include "test/UnitTest/Test.h"
Expand All @@ -37,6 +39,13 @@ class LlvmLibcFTellTest : public LIBC_NAMESPACE::testing::Test {
// still return the correct effective offset.
ASSERT_EQ(size_t(LIBC_NAMESPACE::ftell(file)), WRITE_SIZE);

off_t offseto = 42;
ASSERT_EQ(0, LIBC_NAMESPACE::fseeko(file, offseto, SEEK_SET));
ASSERT_EQ(LIBC_NAMESPACE::ftello(file), offseto);
ASSERT_EQ(0, LIBC_NAMESPACE::fseeko(file, -offseto, SEEK_END));
ASSERT_EQ(size_t(LIBC_NAMESPACE::ftello(file)),
size_t(WRITE_SIZE - offseto));

long offset = 5;
ASSERT_EQ(0, LIBC_NAMESPACE::fseek(file, offset, SEEK_SET));
ASSERT_EQ(LIBC_NAMESPACE::ftell(file), offset);
Expand Down
8 changes: 8 additions & 0 deletions libc/utils/MPFRWrapper/MPFRUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,12 @@ class MPFRNumber {
return result;
}

MPFRNumber atan2(const MPFRNumber &b) {
MPFRNumber result(*this);
mpfr_atan2(result.value, value, b.value, mpfr_rounding);
return result;
}

MPFRNumber atanh() const {
MPFRNumber result(*this);
mpfr_atanh(result.value, value, mpfr_rounding);
Expand Down Expand Up @@ -623,6 +629,8 @@ binary_operation_one_output(Operation op, InputType x, InputType y,
MPFRNumber inputX(x, precision, rounding);
MPFRNumber inputY(y, precision, rounding);
switch (op) {
case Operation::Atan2:
return inputX.atan2(inputY);
case Operation::Fmod:
return inputX.fmod(inputY);
case Operation::Hypot:
Expand Down
1 change: 1 addition & 0 deletions libc/utils/MPFRWrapper/MPFRUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ enum class Operation : int {
// input and produce a single floating point number of the same type as
// output.
BeginBinaryOperationsSingleOutput,
Atan2,
Fmod,
Hypot,
Pow,
Expand Down
16 changes: 16 additions & 0 deletions libcxx/benchmarks/algorithms/mismatch.bench.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,20 @@ BENCHMARK(bm_mismatch<char>)->Apply(BenchmarkSizes);
BENCHMARK(bm_mismatch<short>)->Apply(BenchmarkSizes);
BENCHMARK(bm_mismatch<int>)->Apply(BenchmarkSizes);

template <class T>
static void bm_mismatch_two_range_overload(benchmark::State& state) {
std::vector<T> vec1(state.range(), '1');
std::vector<T> vec2(state.range(), '1');
std::mt19937_64 rng(std::random_device{}());

vec1.back() = '2';
for (auto _ : state) {
benchmark::DoNotOptimize(vec1);
benchmark::DoNotOptimize(std::mismatch(vec1.begin(), vec1.end(), vec2.begin(), vec2.end()));
}
}
BENCHMARK(bm_mismatch_two_range_overload<char>)->DenseRange(1, 8)->Range(16, 1 << 20);
BENCHMARK(bm_mismatch_two_range_overload<short>)->DenseRange(1, 8)->Range(16, 1 << 20);
BENCHMARK(bm_mismatch_two_range_overload<int>)->DenseRange(1, 8)->Range(16, 1 << 20);

BENCHMARK_MAIN();
19 changes: 19 additions & 0 deletions libcxx/docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,25 @@ Getting Started with libc++
Current Status
==============

libc++ has become the default C++ Standard Library implementation for many major platforms, including Apple's macOS,
iOS, watchOS, and tvOS, Google Search, the Android operating system, and FreeBSD. As a result, libc++ has an estimated
user base of over 1 billion daily active users.

Since its inception, libc++ has focused on delivering high performance, standards-conformance, and portability. It has
been extensively tested and optimized, making it robust and production ready. libc++ fully implements C++11 and C++14,
with C++17, C++20, C++23, and C++26 features being actively developed and making steady progress.

libc++ is continuously integrated and tested on a wide range of platforms and configurations, ensuring its reliability
and compatibility across various systems. The library's extensive test suite and rigorous quality assurance process have
made it a top choice for platform providers looking to offer their users a robust and efficient C++ Standard Library.

As an open-source project, libc++ benefits from a vibrant community of contributors who work together to improve the
library and add new features. This ongoing development and support ensure that libc++ remains at the forefront of
C++ standardization efforts and continues to meet the evolving needs of C++ developers worldwide.


History
-------
After its initial introduction, many people have asked "why start a new
library instead of contributing to an existing library?" (like Apache's
libstdcxx, GNU's libstdc++, STLport, etc). There are many contributing
Expand Down
Loading