Skip to content

Commit

Permalink
Return a proper character array for 'define_static_string'.
Browse files Browse the repository at this point in the history
This is somewhat tricky, since the character array needs to be
associated to a declaration (i.e., a hidden variable), but the
declaration won't be parsed and therefore won't get handed off to
CodeGen. So unless we do something to tell CodeGen about the variable,
it won't get emitted and we'll get linker errors.

So we (for now, anyway) introduce a similar mechanism to what's already
used for "#pragma weak"; we track any generated decl's in an array held
by 'Sema', and pass those explicitly to CodeGen after parsing the AST.

We also cache any such generated variables in the ASTContext, partly as
an optimization, but mostly to ensure that each such variable obtains a
(TU-)unique name.

Closes issue llvm#80.
  • Loading branch information
katzdm committed Jul 31, 2024
1 parent 774b841 commit 2e5d810
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 10 deletions.
11 changes: 11 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,13 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// This is lazily created. This is intentionally not serialized.
mutable llvm::StringMap<StringLiteral *> StringLiteralCache;

/// A cache mapping a string value to a VarDecl object holding a generated
/// immutable character array containing the same string.
///
/// This is lazily created. This is intentionally not serialized.
mutable llvm::StringMap<VarDecl *> GenCharArrayCache;
mutable llvm::StringMap<VarDecl *> GenUTF8CharArrayCache;

/// MD5 hash of CUID. It is calculated when first used and cached by this
/// data member.
mutable std::string CUIDHash;
Expand Down Expand Up @@ -3247,6 +3254,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// PredefinedExpr to cache evaluated results.
StringLiteral *getPredefinedStringLiteralFromCache(StringRef Key) const;

/// Return a variable whose holding a generated immutable character array
/// containing the same string.
VarDecl *getGeneratedCharArray(StringRef Key, bool IsUtf8);

/// Return a declaration for the global GUID object representing the given
/// GUID value.
MSGuidDecl *getMSGuidDecl(MSGuidDeclParts Parts) const;
Expand Down
11 changes: 11 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -15287,6 +15287,17 @@ class Sema final : public SemaBase {

QualType ComputeResultType(QualType ExprTy, const APValue &V);

/// GeneratedDecl - Translation-unit scoped declarations generated by
/// reflection metafunctions (C++2c P2996).
///
/// Since these declarations are formed as side-effects of constant
/// evaluation, the parser will not "see" them and will not pass them to
/// the CodeGenModule. Maintaining a separate list addresses the problem.
SmallVector<Decl *, 4> GeneratedDecl;

/// GeneratedDecls - access to metafunction generated Decls
SmallVectorImpl<Decl *> &GeneratedDecls() { return GeneratedDecl; }


private:
// Lambdas having bound references to this Sema object, used to evaluate
Expand Down
20 changes: 20 additions & 0 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12895,6 +12895,26 @@ ASTContext::getPredefinedStringLiteralFromCache(StringRef Key) const {
return Result;
}

VarDecl *
ASTContext::getGeneratedCharArray(StringRef Key, bool Utf8) {
auto &Cache = Utf8 ? GenUTF8CharArrayCache : GenCharArrayCache;
VarDecl *&Result = Cache[Key];
if (!Result) {
std::string Name;
{
llvm::raw_string_ostream NameOut(Name);
NameOut << "__gen_char_array_" << Cache.size();
}

QualType CharGenTy = Utf8 ? Char8Ty : CharTy;
QualType LitTy = getStringLiteralArrayType(CharGenTy, Key.size());

Result = VarDecl::Create(*this, TUDecl, SourceLocation(), SourceLocation(),
&Idents.get(Name), LitTy, nullptr, SC_Static);
}
return Result;
}

MSGuidDecl *
ASTContext::getMSGuidDecl(MSGuidDecl::Parts Parts) const {
assert(MSGuidTagDecl && "building MS GUID without MS extensions?");
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Parse/ParseAST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@ void clang::ParseAST(Sema &S, bool PrintStats, bool SkipFunctionBodies) {
}
}

// Process any TopLevelDecls generated by reflection metafunctions.
for (Decl *D : S.GeneratedDecls())
Consumer->HandleTopLevelDecl(DeclGroupRef(D));

// Process any TopLevelDecls generated by #pragma weak.
for (Decl *D : S.WeakTopLevelDecls())
Consumer->HandleTopLevelDecl(DeclGroupRef(D));
Expand Down
41 changes: 31 additions & 10 deletions clang/lib/Sema/Metafunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,8 @@ static APValue makeReflection(TagDataMemberSpec *TDMS) {
return APValue(RV);
}

static Expr *makeCString(StringRef Str, ASTContext &C, bool Utf8) {
static Expr *makeCString(StringRef Str, ASTContext &C, bool Utf8,
bool CastToPtr) {
QualType ConstCharTy = (Utf8 ? C.Char8Ty : C.CharTy).withConst();

// Get the type for 'const char[Str.size()]'.
Expand All @@ -655,14 +656,18 @@ static Expr *makeCString(StringRef Str, ASTContext &C, bool Utf8) {
// Create a string literal having type 'const char [Str.size()]'.
StringLiteralKind SLK = Utf8 ? StringLiteralKind::UTF8 :
StringLiteralKind::Ordinary;
StringLiteral *StrLit = StringLiteral::Create(C, Str, SLK, false, StrLitTy,
SourceLocation{});
Expr *Synthesized = StringLiteral::Create(C, Str, SLK, false, StrLitTy,
SourceLocation{});

// Create an expression to implicitly cast the literal to 'const char *'.
QualType ConstCharPtrTy = C.getPointerType(ConstCharTy);
return ImplicitCastExpr::Create(C, ConstCharPtrTy, CK_ArrayToPointerDecay,
StrLit, /*BasePath=*/nullptr, VK_PRValue,
FPOptionsOverride());
if (CastToPtr) {
// Create an expression to implicitly cast the literal to 'const char *'.
QualType ConstCharPtrTy = C.getPointerType(ConstCharTy);
Synthesized = ImplicitCastExpr::Create(C, ConstCharPtrTy,
CK_ArrayToPointerDecay, Synthesized,
/*BasePath=*/nullptr, VK_PRValue,
FPOptionsOverride());
}
return Synthesized;
}

static bool SetAndSucceed(APValue &Out, const APValue &Result) {
Expand Down Expand Up @@ -1823,7 +1828,9 @@ bool identifier_of(APValue &Result, Sema &S, EvalFn Evaluator, DiagFn Diagnoser,
return Diagnoser(Range.getBegin(), diag::metafn_anonymous_entity)
<< DescriptionOf(R.getReflection()) << Range;

return !Evaluator(Result, makeCString(Name, S.Context, IsUtf8), true);
return !Evaluator(Result,
makeCString(Name, S.Context, IsUtf8, /*CastToPtr=*/true),
true);
}

bool has_identifier(APValue &Result, Sema &S, EvalFn Evaluator,
Expand Down Expand Up @@ -5286,7 +5293,21 @@ bool define_static_string(APValue &Result, Sema &S, EvalFn Evaluator,
return true;
bool IsUtf8 = Scratch.getInt().getBoolValue();

return !Evaluator(Result, makeCString(Contents, S.Context, IsUtf8), true);
VarDecl *AnonArr = S.Context.getGeneratedCharArray(Contents, IsUtf8);
if (!AnonArr->hasInit()) {
Expr *StrLit = makeCString(Contents, S.Context, IsUtf8,
/*CastToPtr=*/false);

AnonArr->setConstexpr(true);
S.AddInitializerToDecl(AnonArr, StrLit, false);

S.GeneratedDecl.push_back(AnonArr);
}
assert(AnonArr->getFormalLinkage() == Linkage::Internal);

APValue::LValuePathEntry Path[1] = {APValue::LValuePathEntry::ArrayIndex(0)};
return SetAndSucceed(Result,
APValue(AnonArr, CharUnits::Zero(), Path, false));
}

bool get_ith_parameter_of(APValue &Result, Sema &S, EvalFn Evaluator,
Expand Down

0 comments on commit 2e5d810

Please sign in to comment.