Skip to content

Generate DW_AT_RUST_short_backtrace attributes for DISubprogram nodes #123683

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions clang/lib/CodeGen/CGDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1786,6 +1786,7 @@ CGDebugInfo::createInlinedTrapSubprogram(StringRef FuncName,
/*ScopeLine=*/0,
/*Flags=*/llvm::DINode::FlagArtificial,
/*SPFlags=*/llvm::DISubprogram::SPFlagDefinition,
/*ShortBacktrace=*/std::nullopt,
/*TParams=*/nullptr, /*ThrownTypes=*/nullptr, /*Annotations=*/nullptr);
}

Expand Down Expand Up @@ -2166,7 +2167,7 @@ llvm::DISubprogram *CGDebugInfo::CreateCXXMemberFunction(
llvm::DISubprogram *SP = DBuilder.createMethod(
RecordTy, MethodName, MethodLinkageName, MethodDefUnit, MethodLine,
MethodTy, VIndex, ThisAdjustment, ContainingType, Flags, SPFlags,
TParamsArray.get());
std::nullopt, TParamsArray.get());

SPCache[Method->getCanonicalDecl()].reset(SP);

Expand Down Expand Up @@ -4165,13 +4166,13 @@ llvm::DISubprogram *CGDebugInfo::getFunctionFwdDeclOrStub(GlobalDecl GD,
return DBuilder.createFunction(
DContext, Name, LinkageName, Unit, Line,
getOrCreateFunctionType(GD.getDecl(), FnType, Unit), 0, Flags, SPFlags,
TParamsArray.get(), getFunctionDeclaration(FD));
std::nullopt, TParamsArray.get(), getFunctionDeclaration(FD));
}

llvm::DISubprogram *SP = DBuilder.createTempFunctionFwdDecl(
DContext, Name, LinkageName, Unit, Line,
getOrCreateFunctionType(GD.getDecl(), FnType, Unit), 0, Flags, SPFlags,
TParamsArray.get(), getFunctionDeclaration(FD));
std::nullopt, TParamsArray.get(), getFunctionDeclaration(FD));
const FunctionDecl *CanonDecl = FD->getCanonicalDecl();
FwdDeclReplaceMap.emplace_back(std::piecewise_construct,
std::make_tuple(CanonDecl),
Expand Down Expand Up @@ -4501,8 +4502,8 @@ void CGDebugInfo::emitFunctionStart(GlobalDecl GD, SourceLocation Loc,
// are emitted as CU level entities by the backend.
llvm::DISubprogram *SP = DBuilder.createFunction(
FDContext, Name, LinkageName, Unit, LineNo, DIFnType, ScopeLine,
FlagsForDef, SPFlagsForDef, TParamsArray.get(), Decl, nullptr,
Annotations);
FlagsForDef, SPFlagsForDef, std::nullopt, TParamsArray.get(), Decl,
nullptr, Annotations);
Fn->setSubprogram(SP);
// We might get here with a VarDecl in the case we're generating
// code for the initialization of globals. Do not record these decls
Expand Down Expand Up @@ -4565,7 +4566,7 @@ void CGDebugInfo::EmitFunctionDecl(GlobalDecl GD, SourceLocation Loc,
llvm::DISubroutineType *STy = getOrCreateFunctionType(D, FnType, Unit);
llvm::DISubprogram *SP = DBuilder.createFunction(
FDContext, Name, LinkageName, Unit, LineNo, STy, ScopeLine, Flags,
SPFlags, TParamsArray.get(), nullptr, nullptr, Annotations);
SPFlags, std::nullopt, TParamsArray.get(), nullptr, nullptr, Annotations);

// Preserve btf_decl_tag attributes for parameters of extern functions
// for BPF target. The parameters created in this loop are attached as
Expand Down
3 changes: 3 additions & 0 deletions llvm/include/llvm/BinaryFormat/Dwarf.def
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,7 @@ HANDLE_DW_AT(0x3b28, BORLAND_Delphi_ABI, 0, BORLAND)
HANDLE_DW_AT(0x3b29, BORLAND_Delphi_return, 0, BORLAND)
HANDLE_DW_AT(0x3b30, BORLAND_Delphi_frameptr, 0, BORLAND)
HANDLE_DW_AT(0x3b31, BORLAND_closure, 0, BORLAND)

// LLVM project extensions.
HANDLE_DW_AT(0x3e00, LLVM_include_path, 0, LLVM)
HANDLE_DW_AT(0x3e01, LLVM_config_macros, 0, LLVM)
Expand All @@ -619,6 +620,8 @@ HANDLE_DW_AT(0x3e09, LLVM_ptrauth_authenticates_null_values, 0, LLVM)
HANDLE_DW_AT(0x3e0a, LLVM_ptrauth_authentication_mode, 0, LLVM)
HANDLE_DW_AT(0x3e0b, LLVM_num_extra_inhabitants, 0, LLVM)
HANDLE_DW_AT(0x3e0c, LLVM_stmt_sequence, 0, LLVM)
HANDLE_DW_AT(0x3e0d, LLVM_short_backtrace, 0, LLVM)


// Apple extensions.

Expand Down
38 changes: 19 additions & 19 deletions llvm/include/llvm/IR/DIBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -808,16 +808,15 @@ namespace llvm {
/// \param Annotations Attribute Annotations.
/// \param TargetFuncName The name of the target function if this is
/// a trampoline.
DISubprogram *
createFunction(DIScope *Scope, StringRef Name, StringRef LinkageName,
DIFile *File, unsigned LineNo, DISubroutineType *Ty,
unsigned ScopeLine, DINode::DIFlags Flags = DINode::FlagZero,
DISubprogram::DISPFlags SPFlags = DISubprogram::SPFlagZero,
DITemplateParameterArray TParams = nullptr,
DISubprogram *Decl = nullptr,
DITypeArray ThrownTypes = nullptr,
DINodeArray Annotations = nullptr,
StringRef TargetFuncName = "");
DISubprogram *createFunction(
DIScope *Scope, StringRef Name, StringRef LinkageName, DIFile *File,
unsigned LineNo, DISubroutineType *Ty, unsigned ScopeLine,
DINode::DIFlags Flags = DINode::FlagZero,
DISubprogram::DISPFlags SPFlags = DISubprogram::SPFlagZero,
std::optional<ShortBacktraceAttr> ShortBacktrace = std::nullopt,
DITemplateParameterArray TParams = nullptr,
DISubprogram *Decl = nullptr, DITypeArray ThrownTypes = nullptr,
DINodeArray Annotations = nullptr, StringRef TargetFuncName = "");

/// Identical to createFunction,
/// except that the resulting DbgNode is meant to be RAUWed.
Expand All @@ -826,6 +825,7 @@ namespace llvm {
unsigned LineNo, DISubroutineType *Ty, unsigned ScopeLine,
DINode::DIFlags Flags = DINode::FlagZero,
DISubprogram::DISPFlags SPFlags = DISubprogram::SPFlagZero,
std::optional<ShortBacktraceAttr> ShortBacktrace = std::nullopt,
DITemplateParameterArray TParams = nullptr,
DISubprogram *Decl = nullptr, DITypeArray ThrownTypes = nullptr);

Expand All @@ -848,15 +848,15 @@ namespace llvm {
/// \param SPFlags Additional flags specific to subprograms.
/// \param TParams Function template parameters.
/// \param ThrownTypes Exception types this function may throw.
DISubprogram *
createMethod(DIScope *Scope, StringRef Name, StringRef LinkageName,
DIFile *File, unsigned LineNo, DISubroutineType *Ty,
unsigned VTableIndex = 0, int ThisAdjustment = 0,
DIType *VTableHolder = nullptr,
DINode::DIFlags Flags = DINode::FlagZero,
DISubprogram::DISPFlags SPFlags = DISubprogram::SPFlagZero,
DITemplateParameterArray TParams = nullptr,
DITypeArray ThrownTypes = nullptr);
DISubprogram *createMethod(
DIScope *Scope, StringRef Name, StringRef LinkageName, DIFile *File,
unsigned LineNo, DISubroutineType *Ty, unsigned VTableIndex = 0,
int ThisAdjustment = 0, DIType *VTableHolder = nullptr,
DINode::DIFlags Flags = DINode::FlagZero,
DISubprogram::DISPFlags SPFlags = DISubprogram::SPFlagZero,
std::optional<ShortBacktraceAttr> ShortBacktrace = std::nullopt,
DITemplateParameterArray TParams = nullptr,
DITypeArray ThrownTypes = nullptr);

/// Create common block entry for a Fortran common block.
/// \param Scope Scope of this common block.
Expand Down
65 changes: 42 additions & 23 deletions llvm/include/llvm/IR/DebugInfoMetadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,13 @@ class DITypeRefArray {
iterator end() const { return N ? iterator(N->op_end()) : iterator(); }
};

/// DWARF-like extension attribute for setting short backtrace debuginfo.
enum class ShortBacktraceAttr {
SkipFrame = 0,
StartShortBacktrace = 1,
EndShortBacktrace = 2,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add some documents? I'm not clear why StartShortBacktrace and EndShortBacktrace cannot be replaced by SkipFrame? IIUC, we can add SkipFrame annotation to all frames/functions that between StartShortBacktrace and EndShortBacktrace.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a few cases where this cannot be replaced by annotating individual frames. Consider these three:

  1. Frames before main are not controlled by the compiler. To skip frames before main, we have to annotate it with StartShortBacktrace, and it has no corresponding EndShortBacktrace pair.
  2. The compiler may not control all frames in a given section of the call graph; for example when using a shared object library, or when linking object files from cross-language translation units.
  3. The compiler may not statically know the callgraph. I am not super familiar with how dataflow analysis works, but my impression is that things like vtables and jump tables mean the callgraph is not fully known until runtime.

Rust currently handles this with a define void @__rust_start_short_backtrace() noinline function, and it works ok. But you mentioned that you want this to be extensible to other languages and tools. And in that case they probably don't want to hardcode __rust in the name, nor be forced to use frames instead of debuginfo.

I am happy to document the three cases above inline in this enum.

};

/// Tagged DWARF-like metadata node.
///
/// A metadata node with a DWARF tag (i.e., a constant named \c DW_TAG_*,
Expand Down Expand Up @@ -1750,35 +1757,40 @@ class DISubprogram : public DILocalScope {
private:
DIFlags Flags;
DISPFlags SPFlags;
std::optional<ShortBacktraceAttr> ShortBacktrace;

DISubprogram(LLVMContext &C, StorageType Storage, unsigned Line,
unsigned ScopeLine, unsigned VirtualIndex, int ThisAdjustment,
DIFlags Flags, DISPFlags SPFlags, ArrayRef<Metadata *> Ops);
DIFlags Flags, DISPFlags SPFlags,
std::optional<ShortBacktraceAttr> ShortBacktrace,
ArrayRef<Metadata *> Ops);
~DISubprogram() = default;

static DISubprogram *
getImpl(LLVMContext &Context, DIScope *Scope, StringRef Name,
StringRef LinkageName, DIFile *File, unsigned Line,
DISubroutineType *Type, unsigned ScopeLine, DIType *ContainingType,
unsigned VirtualIndex, int ThisAdjustment, DIFlags Flags,
DISPFlags SPFlags, DICompileUnit *Unit,
DITemplateParameterArray TemplateParams, DISubprogram *Declaration,
DINodeArray RetainedNodes, DITypeArray ThrownTypes,
DINodeArray Annotations, StringRef TargetFuncName,
StorageType Storage, bool ShouldCreate = true) {
return getImpl(Context, Scope, getCanonicalMDString(Context, Name),
getCanonicalMDString(Context, LinkageName), File, Line, Type,
ScopeLine, ContainingType, VirtualIndex, ThisAdjustment,
Flags, SPFlags, Unit, TemplateParams.get(), Declaration,
RetainedNodes.get(), ThrownTypes.get(), Annotations.get(),
getCanonicalMDString(Context, TargetFuncName),
Storage, ShouldCreate);
DISPFlags SPFlags, std::optional<ShortBacktraceAttr> ShortBacktrace,
DICompileUnit *Unit, DITemplateParameterArray TemplateParams,
DISubprogram *Declaration, DINodeArray RetainedNodes,
DITypeArray ThrownTypes, DINodeArray Annotations,
StringRef TargetFuncName, StorageType Storage,
bool ShouldCreate = true) {
return getImpl(
Context, Scope, getCanonicalMDString(Context, Name),
getCanonicalMDString(Context, LinkageName), File, Line, Type, ScopeLine,
ContainingType, VirtualIndex, ThisAdjustment, Flags, SPFlags,
ShortBacktrace, Unit, TemplateParams.get(), Declaration,
RetainedNodes.get(), ThrownTypes.get(), Annotations.get(),
getCanonicalMDString(Context, TargetFuncName), Storage, ShouldCreate);
}
static DISubprogram *
getImpl(LLVMContext &Context, Metadata *Scope, MDString *Name,
MDString *LinkageName, Metadata *File, unsigned Line, Metadata *Type,
unsigned ScopeLine, Metadata *ContainingType, unsigned VirtualIndex,
int ThisAdjustment, DIFlags Flags, DISPFlags SPFlags, Metadata *Unit,
int ThisAdjustment, DIFlags Flags, DISPFlags SPFlags,
std::optional<ShortBacktraceAttr> ShortBacktrace, Metadata *Unit,
Metadata *TemplateParams, Metadata *Declaration,
Metadata *RetainedNodes, Metadata *ThrownTypes, Metadata *Annotations,
MDString *TargetFuncName, StorageType Storage,
Expand All @@ -1789,9 +1801,9 @@ class DISubprogram : public DILocalScope {
getFile(), getLine(), getType(), getScopeLine(),
getContainingType(), getVirtualIndex(),
getThisAdjustment(), getFlags(), getSPFlags(),
getUnit(), getTemplateParams(), getDeclaration(),
getRetainedNodes(), getThrownTypes(), getAnnotations(),
getTargetFuncName());
getShortBacktrace(), getUnit(), getTemplateParams(),
getDeclaration(), getRetainedNodes(), getThrownTypes(),
getAnnotations(), getTargetFuncName());
}

public:
Expand All @@ -1800,27 +1812,31 @@ class DISubprogram : public DILocalScope {
(DIScope * Scope, StringRef Name, StringRef LinkageName, DIFile *File,
unsigned Line, DISubroutineType *Type, unsigned ScopeLine,
DIType *ContainingType, unsigned VirtualIndex, int ThisAdjustment,
DIFlags Flags, DISPFlags SPFlags, DICompileUnit *Unit,
DIFlags Flags, DISPFlags SPFlags,
std::optional<ShortBacktraceAttr> ShortBacktrace, DICompileUnit *Unit,
DITemplateParameterArray TemplateParams = nullptr,
DISubprogram *Declaration = nullptr, DINodeArray RetainedNodes = nullptr,
DITypeArray ThrownTypes = nullptr, DINodeArray Annotations = nullptr,
StringRef TargetFuncName = ""),
(Scope, Name, LinkageName, File, Line, Type, ScopeLine, ContainingType,
VirtualIndex, ThisAdjustment, Flags, SPFlags, Unit, TemplateParams,
Declaration, RetainedNodes, ThrownTypes, Annotations, TargetFuncName))
VirtualIndex, ThisAdjustment, Flags, SPFlags, ShortBacktrace, Unit,
TemplateParams, Declaration, RetainedNodes, ThrownTypes, Annotations,
TargetFuncName))

DEFINE_MDNODE_GET(
DISubprogram,
(Metadata * Scope, MDString *Name, MDString *LinkageName, Metadata *File,
unsigned Line, Metadata *Type, unsigned ScopeLine,
Metadata *ContainingType, unsigned VirtualIndex, int ThisAdjustment,
DIFlags Flags, DISPFlags SPFlags, Metadata *Unit,
DIFlags Flags, DISPFlags SPFlags,
std::optional<ShortBacktraceAttr> ShortBacktrace, Metadata *Unit,
Metadata *TemplateParams = nullptr, Metadata *Declaration = nullptr,
Metadata *RetainedNodes = nullptr, Metadata *ThrownTypes = nullptr,
Metadata *Annotations = nullptr, MDString *TargetFuncName = nullptr),
(Scope, Name, LinkageName, File, Line, Type, ScopeLine, ContainingType,
VirtualIndex, ThisAdjustment, Flags, SPFlags, Unit, TemplateParams,
Declaration, RetainedNodes, ThrownTypes, Annotations, TargetFuncName))
VirtualIndex, ThisAdjustment, Flags, SPFlags, ShortBacktrace, Unit,
TemplateParams, Declaration, RetainedNodes, ThrownTypes, Annotations,
TargetFuncName))

TempDISubprogram clone() const { return cloneImpl(); }

Expand All @@ -1843,6 +1859,9 @@ class DISubprogram : public DILocalScope {
}
DIFlags getFlags() const { return Flags; }
DISPFlags getSPFlags() const { return SPFlags; }
const std::optional<ShortBacktraceAttr> getShortBacktrace() const {
return ShortBacktrace;
}
bool isLocalToUnit() const { return getSPFlags() & SPFlagLocalToUnit; }
bool isDefinition() const { return getSPFlags() & SPFlagDefinition; }
bool isOptimized() const { return getSPFlags() & SPFlagOptimized; }
Expand Down
32 changes: 26 additions & 6 deletions llvm/lib/AsmParser/LLParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5630,9 +5630,9 @@ bool LLParser::parseDICompileUnit(MDNode *&Result, bool IsDistinct) {
/// isDefinition: true, scopeLine: 8, containingType: !3,
/// virtuality: DW_VIRTUALTIY_pure_virtual,
/// virtualIndex: 10, thisAdjustment: 4, flags: 11,
/// spFlags: 10, isOptimized: false, templateParams: !4,
/// declaration: !5, retainedNodes: !6, thrownTypes: !7,
/// annotations: !8)
/// spFlags: 10, shortBacktrace: 0, isOptimized: false,
/// templateParams: !4, declaration: !5, retainedNodes: !6,
/// thrownTypes: !7, annotations: !8)
bool LLParser::parseDISubprogram(MDNode *&Result, bool IsDistinct) {
auto Loc = Lex.getLoc();
#define VISIT_MD_FIELDS(OPTIONAL, REQUIRED) \
Expand All @@ -5658,10 +5658,30 @@ bool LLParser::parseDISubprogram(MDNode *&Result, bool IsDistinct) {
OPTIONAL(retainedNodes, MDField, ); \
OPTIONAL(thrownTypes, MDField, ); \
OPTIONAL(annotations, MDField, ); \
OPTIONAL(shortBacktrace, MDSignedField, (-1, -1, 2)); \
OPTIONAL(targetFuncName, MDStringField, );
PARSE_MD_FIELDS();
#undef VISIT_MD_FIELDS

std::optional<ShortBacktraceAttr> parsedShortBacktrace;
switch (shortBacktrace.Val) {
case -1:
parsedShortBacktrace = std::nullopt;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This requires a debug assertion.

break;
case 0:
parsedShortBacktrace = ShortBacktraceAttr::SkipFrame;
break;
case 1:
parsedShortBacktrace = ShortBacktraceAttr::StartShortBacktrace;
break;
case 2:
parsedShortBacktrace = ShortBacktraceAttr::EndShortBacktrace;
break;
default:
llvm_unreachable(
"shortBacktrace debuginfo attribute must be in range [-1, 2]");
}

// An explicit spFlags field takes precedence over individual fields in
// older IR versions.
DISubprogram::DISPFlags SPFlags =
Expand All @@ -5676,9 +5696,9 @@ bool LLParser::parseDISubprogram(MDNode *&Result, bool IsDistinct) {
DISubprogram,
(Context, scope.Val, name.Val, linkageName.Val, file.Val, line.Val,
type.Val, scopeLine.Val, containingType.Val, virtualIndex.Val,
thisAdjustment.Val, flags.Val, SPFlags, unit.Val, templateParams.Val,
declaration.Val, retainedNodes.Val, thrownTypes.Val, annotations.Val,
targetFuncName.Val));
thisAdjustment.Val, flags.Val, SPFlags, parsedShortBacktrace, unit.Val,
templateParams.Val, declaration.Val, retainedNodes.Val, thrownTypes.Val,
annotations.Val, targetFuncName.Val));
return false;
}

Expand Down
10 changes: 6 additions & 4 deletions llvm/lib/Bitcode/Reader/MetadataLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1881,10 +1881,12 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata(
HasThisAdj ? Record[16 + OffsetB] : 0, // thisAdjustment
Flags, // flags
SPFlags, // SPFlags
HasUnit ? CUorFn : nullptr, // unit
getMDOrNull(Record[13 + OffsetB]), // templateParams
getMDOrNull(Record[14 + OffsetB]), // declaration
getMDOrNull(Record[15 + OffsetB]), // retainedNodes
// TODO: parse this from the record
std::nullopt, // shortBacktrace
HasUnit ? CUorFn : nullptr, // unit
getMDOrNull(Record[13 + OffsetB]), // templateParams
getMDOrNull(Record[14 + OffsetB]), // declaration
getMDOrNull(Record[15 + OffsetB]), // retainedNodes
HasThrownTypes ? getMDOrNull(Record[17 + OffsetB])
: nullptr, // thrownTypes
HasAnnotations ? getMDOrNull(Record[18 + OffsetB])
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2552,6 +2552,7 @@ void DwarfDebug::endFunctionImpl(const MachineFunction *MF) {
// subroutines inside it. But with -fdebug-info-for-profiling, the subprogram
// is still needed as we need its source location.
if (!TheCU.getCUNode()->getDebugInfoForProfiling() &&
!SP->getShortBacktrace().has_value() &&
TheCU.getCUNode()->getEmissionKind() == DICompileUnit::LineTablesOnly &&
LScopes.getAbstractScopesList().empty() && !IsDarwin) {
for (const auto &R : Asm->MBBSectionRanges)
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1320,6 +1320,11 @@ void DwarfUnit::applySubprogramAttributes(const DISubprogram *SP, DIE &SPDie,
if (!SkipSPSourceLocation)
addSourceLine(SPDie, SP);

if (SP->getShortBacktrace().has_value()) {
addUInt(SPDie, dwarf::DW_AT_LLVM_short_backtrace, dwarf::DW_FORM_data1,
static_cast<uint64_t>(*SP->getShortBacktrace()));
}

// Skip the rest of the attributes under -gmlt to save space.
if (SkipSPAttributes)
return;
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/IR/AsmWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2334,6 +2334,10 @@ static void writeDISubprogram(raw_ostream &Out, const DISubprogram *N,
Printer.printInt("thisAdjustment", N->getThisAdjustment());
Printer.printDIFlags("flags", N->getFlags());
Printer.printDISPFlags("spFlags", N->getSPFlags());
if (N->getShortBacktrace().has_value())
Printer.printInt("shortBacktrace",
static_cast<signed>(N->getShortBacktrace().value()),
/* ShouldSkipZero */ false);
Printer.printMetadata("unit", N->getRawUnit());
Printer.printMetadata("templateParams", N->getRawTemplateParams());
Printer.printMetadata("declaration", N->getRawDeclaration());
Expand Down
Loading
Loading