Skip to content

Commit 22cd1da

Browse files
authored
Merge branch 'llvm:main' into gh-101657
2 parents 585bd72 + 207e485 commit 22cd1da

File tree

17 files changed

+1254
-63
lines changed

17 files changed

+1254
-63
lines changed

clang/lib/AST/ByteCode/InterpBuiltin.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1871,7 +1871,9 @@ static bool interp__builtin_memcpy(InterpState &S, CodePtr OpPC,
18711871
size_t RemainingDestElems;
18721872
if (DestPtr.getFieldDesc()->isArray()) {
18731873
DestElemType = DestPtr.getFieldDesc()->getElemQualType();
1874-
RemainingDestElems = (DestPtr.getNumElems() - DestPtr.getIndex());
1874+
RemainingDestElems = DestPtr.isUnknownSizeArray()
1875+
? 0
1876+
: (DestPtr.getNumElems() - DestPtr.getIndex());
18751877
} else {
18761878
DestElemType = DestPtr.getType();
18771879
RemainingDestElems = 1;
@@ -1890,7 +1892,9 @@ static bool interp__builtin_memcpy(InterpState &S, CodePtr OpPC,
18901892
size_t RemainingSrcElems;
18911893
if (SrcPtr.getFieldDesc()->isArray()) {
18921894
SrcElemType = SrcPtr.getFieldDesc()->getElemQualType();
1893-
RemainingSrcElems = (SrcPtr.getNumElems() - SrcPtr.getIndex());
1895+
RemainingSrcElems = SrcPtr.isUnknownSizeArray()
1896+
? 0
1897+
: (SrcPtr.getNumElems() - SrcPtr.getIndex());
18941898
} else {
18951899
SrcElemType = SrcPtr.getType();
18961900
RemainingSrcElems = 1;

clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ static bool enumerateData(const Pointer &P, const Context &Ctx, Bits Offset,
110110
if (FieldDesc->isCompositeArray()) {
111111
QualType ElemType = FieldDesc->getElemQualType();
112112
Bits ElemSize = Bits(Ctx.getASTContext().getTypeSize(ElemType));
113-
for (unsigned I = 0; I != FieldDesc->getNumElems(); ++I) {
113+
for (unsigned I = P.getIndex(); I != FieldDesc->getNumElems(); ++I) {
114114
enumerateData(P.atIndex(I).narrow(), Ctx, Offset, BitsToRead, F);
115115
Offset += ElemSize;
116116
if (Offset >= BitsToRead)

clang/test/AST/ByteCode/builtin-functions.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,6 +1253,25 @@ namespace BuiltinMemcpy {
12531253
static_assert(test_memmove(2, 0, 12) == 4234); // both-error {{constant}} \
12541254
// both-note {{in call}}
12551255
#endif
1256+
1257+
struct Trivial { char k; short s; constexpr bool ok() { return k == 3 && s == 4; } };
1258+
constexpr bool test_trivial() {
1259+
Trivial arr[3] = {{1, 2}, {3, 4}, {5, 6}};
1260+
__builtin_memcpy(arr, arr+1, sizeof(Trivial));
1261+
__builtin_memmove(arr+1, arr, 2 * sizeof(Trivial));
1262+
1263+
return arr[0].ok() && arr[1].ok() && arr[2].ok();
1264+
}
1265+
static_assert(test_trivial());
1266+
1267+
// Check that an incomplete array is rejected.
1268+
constexpr int test_incomplete_array_type() { // both-error {{never produces a constant}}
1269+
extern int arr[];
1270+
__builtin_memmove(arr, arr, 4 * sizeof(arr[0]));
1271+
// both-note@-1 2{{'memmove' not supported: source is not a contiguous array of at least 4 elements of type 'int'}}
1272+
return arr[0] * 1000 + arr[1] * 100 + arr[2] * 10 + arr[3];
1273+
}
1274+
static_assert(test_incomplete_array_type() == 1234); // both-error {{constant}} both-note {{in call}}
12561275
}
12571276

12581277
namespace Memcmp {

flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ static bool canCacheThisType(mlir::LLVM::DICompositeTypeAttr comTy) {
325325
std::pair<std::uint64_t, unsigned short>
326326
DebugTypeGenerator::getFieldSizeAndAlign(mlir::Type fieldTy) {
327327
mlir::Type llvmTy;
328-
if (auto boxTy = mlir::dyn_cast_or_null<fir::BaseBoxType>(fieldTy))
328+
if (auto boxTy = mlir::dyn_cast_if_present<fir::BaseBoxType>(fieldTy))
329329
llvmTy = llvmTypeConverter.convertBoxTypeAsStruct(boxTy, getBoxRank(boxTy));
330330
else
331331
llvmTy = llvmTypeConverter.convertType(fieldTy);
@@ -371,7 +371,7 @@ mlir::LLVM::DITypeAttr DebugTypeGenerator::convertRecordType(
371371
std::optional<llvm::ArrayRef<int64_t>> lowerBounds =
372372
fir::getComponentLowerBoundsIfNonDefault(Ty, fieldName, module,
373373
symbolTable);
374-
auto seqTy = mlir::dyn_cast_or_null<fir::SequenceType>(fieldTy);
374+
auto seqTy = mlir::dyn_cast_if_present<fir::SequenceType>(fieldTy);
375375

376376
// For members of the derived types, the information about the shift in
377377
// lower bounds is not part of the declOp but has to be extracted from the
@@ -622,10 +622,10 @@ mlir::LLVM::DITypeAttr DebugTypeGenerator::convertPointerLikeType(
622622
// Arrays and character need different treatment because DWARF have special
623623
// constructs for them to get the location from the descriptor. Rest of
624624
// types are handled like pointer to underlying type.
625-
if (auto seqTy = mlir::dyn_cast_or_null<fir::SequenceType>(elTy))
625+
if (auto seqTy = mlir::dyn_cast_if_present<fir::SequenceType>(elTy))
626626
return convertBoxedSequenceType(seqTy, fileAttr, scope, declOp,
627627
genAllocated, genAssociated);
628-
if (auto charTy = mlir::dyn_cast_or_null<fir::CharacterType>(elTy))
628+
if (auto charTy = mlir::dyn_cast_if_present<fir::CharacterType>(elTy))
629629
return convertCharacterType(charTy, fileAttr, scope, declOp,
630630
/*hasDescriptor=*/true);
631631

@@ -638,7 +638,7 @@ mlir::LLVM::DITypeAttr DebugTypeGenerator::convertPointerLikeType(
638638

639639
return mlir::LLVM::DIDerivedTypeAttr::get(
640640
context, llvm::dwarf::DW_TAG_pointer_type,
641-
mlir::StringAttr::get(context, ""), elTyAttr, ptrSize,
641+
mlir::StringAttr::get(context, ""), elTyAttr, /*sizeInBits=*/ptrSize * 8,
642642
/*alignInBits=*/0, /*offset=*/0,
643643
/*optional<address space>=*/std::nullopt, /*extra data=*/nullptr);
644644
}
@@ -654,22 +654,22 @@ DebugTypeGenerator::convertType(mlir::Type Ty, mlir::LLVM::DIFileAttr fileAttr,
654654
} else if (mlir::isa<mlir::FloatType>(Ty)) {
655655
return genBasicType(context, mlir::StringAttr::get(context, "real"),
656656
Ty.getIntOrFloatBitWidth(), llvm::dwarf::DW_ATE_float);
657-
} else if (auto logTy = mlir::dyn_cast_or_null<fir::LogicalType>(Ty)) {
657+
} else if (auto logTy = mlir::dyn_cast_if_present<fir::LogicalType>(Ty)) {
658658
return genBasicType(context,
659659
mlir::StringAttr::get(context, logTy.getMnemonic()),
660660
kindMapping.getLogicalBitsize(logTy.getFKind()),
661661
llvm::dwarf::DW_ATE_boolean);
662-
} else if (auto cplxTy = mlir::dyn_cast_or_null<mlir::ComplexType>(Ty)) {
662+
} else if (auto cplxTy = mlir::dyn_cast_if_present<mlir::ComplexType>(Ty)) {
663663
auto floatTy = mlir::cast<mlir::FloatType>(cplxTy.getElementType());
664664
unsigned bitWidth = floatTy.getWidth();
665665
return genBasicType(context, mlir::StringAttr::get(context, "complex"),
666666
bitWidth * 2, llvm::dwarf::DW_ATE_complex_float);
667-
} else if (auto seqTy = mlir::dyn_cast_or_null<fir::SequenceType>(Ty)) {
667+
} else if (auto seqTy = mlir::dyn_cast_if_present<fir::SequenceType>(Ty)) {
668668
return convertSequenceType(seqTy, fileAttr, scope, declOp);
669-
} else if (auto charTy = mlir::dyn_cast_or_null<fir::CharacterType>(Ty)) {
669+
} else if (auto charTy = mlir::dyn_cast_if_present<fir::CharacterType>(Ty)) {
670670
return convertCharacterType(charTy, fileAttr, scope, declOp,
671671
/*hasDescriptor=*/false);
672-
} else if (auto recTy = mlir::dyn_cast_or_null<fir::RecordType>(Ty)) {
672+
} else if (auto recTy = mlir::dyn_cast_if_present<fir::RecordType>(Ty)) {
673673
return convertRecordType(recTy, fileAttr, scope, declOp);
674674
} else if (auto tupleTy = mlir::dyn_cast_if_present<mlir::TupleType>(Ty)) {
675675
return convertTupleType(tupleTy, fileAttr, scope, declOp);
@@ -678,22 +678,22 @@ DebugTypeGenerator::convertType(mlir::Type Ty, mlir::LLVM::DIFileAttr fileAttr,
678678
return convertPointerLikeType(elTy, fileAttr, scope, declOp,
679679
/*genAllocated=*/false,
680680
/*genAssociated=*/false);
681-
} else if (auto vecTy = mlir::dyn_cast_or_null<fir::VectorType>(Ty)) {
681+
} else if (auto vecTy = mlir::dyn_cast_if_present<fir::VectorType>(Ty)) {
682682
return convertVectorType(vecTy, fileAttr, scope, declOp);
683683
} else if (mlir::isa<mlir::IndexType>(Ty)) {
684684
return genBasicType(context, mlir::StringAttr::get(context, "integer"),
685685
llvmTypeConverter.getIndexTypeBitwidth(),
686686
llvm::dwarf::DW_ATE_signed);
687-
} else if (auto boxTy = mlir::dyn_cast_or_null<fir::BaseBoxType>(Ty)) {
687+
} else if (auto boxTy = mlir::dyn_cast_if_present<fir::BaseBoxType>(Ty)) {
688688
auto elTy = boxTy.getEleTy();
689-
if (auto seqTy = mlir::dyn_cast_or_null<fir::SequenceType>(elTy))
689+
if (auto seqTy = mlir::dyn_cast_if_present<fir::SequenceType>(elTy))
690690
return convertBoxedSequenceType(seqTy, fileAttr, scope, declOp, false,
691691
false);
692-
if (auto heapTy = mlir::dyn_cast_or_null<fir::HeapType>(elTy))
692+
if (auto heapTy = mlir::dyn_cast_if_present<fir::HeapType>(elTy))
693693
return convertPointerLikeType(heapTy.getElementType(), fileAttr, scope,
694694
declOp, /*genAllocated=*/true,
695695
/*genAssociated=*/false);
696-
if (auto ptrTy = mlir::dyn_cast_or_null<fir::PointerType>(elTy))
696+
if (auto ptrTy = mlir::dyn_cast_if_present<fir::PointerType>(elTy))
697697
return convertPointerLikeType(ptrTy.getElementType(), fileAttr, scope,
698698
declOp, /*genAllocated=*/false,
699699
/*genAssociated=*/true);

lld/COFF/SymbolTable.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,15 @@ void SymbolTable::initializeLoadConfig() {
536536
auto sym =
537537
dyn_cast_or_null<DefinedRegular>(findUnderscore("_load_config_used"));
538538
if (!sym) {
539+
if (isEC()) {
540+
Warn(ctx) << "EC version of '_load_config_used' is missing";
541+
return;
542+
}
543+
if (ctx.hybridSymtab) {
544+
Warn(ctx) << "native version of '_load_config_used' is missing for "
545+
"ARM64X target";
546+
return;
547+
}
539548
if (ctx.config.guardCF != GuardCFLevel::Off)
540549
Warn(ctx)
541550
<< "Control Flow Guard is enabled but '_load_config_used' is missing";

lld/test/COFF/arm64x-loadconfig.s

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,19 @@
88
// RUN: llvm-mc -filetype=obj -triple=aarch64-windows loadconfig-short.s -o loadconfig-short.obj
99
// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows loadconfig-short.s -o loadconfig-short-arm64ec.obj
1010

11-
// RUN: lld-link -machine:arm64x -out:out.dll -dll -noentry loadconfig.obj test.obj
11+
// RUN: lld-link -machine:arm64x -out:out-warn.dll -dll -noentry test.obj \
12+
// RUN: 2>&1 | FileCheck --check-prefixes=WARN-LOADCFG,WARN-EC-LOADCFG %s
13+
// WARN-LOADCFG: lld-link: warning: native version of '_load_config_used' is missing for ARM64X target
14+
// WARN-EC-LOADCFG: lld-link: warning: EC version of '_load_config_used' is missing
15+
16+
// RUN: lld-link -machine:arm64x -out:out-nonative.dll -dll -noentry loadconfig-ec.obj chpe.obj \
17+
// RUN: 2>&1 | FileCheck --check-prefixes=WARN-LOADCFG --implicit-check-not EC %s
18+
19+
// RUN: lld-link -machine:arm64ec -out:out-ec.dll -dll -noentry chpe.obj \
20+
// RUN: 2>&1 | FileCheck --check-prefixes=WARN-EC-LOADCFG --implicit-check-not native %s
21+
22+
// RUN: lld-link -machine:arm64x -out:out.dll -dll -noentry loadconfig.obj test.obj \
23+
// RUN: 2>&1 | FileCheck --check-prefixes=WARN-EC-LOADCFG --implicit-check-not native %s
1224

1325
// RUN: llvm-readobj --coff-load-config out.dll | FileCheck --check-prefix=DYNRELOCS %s
1426
// DYNRELOCS: DynamicValueRelocTableOffset: 0xC

llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,50 @@ enum EdgeKind_loongarch : Edge::Kind {
4141
///
4242
Pointer32,
4343

44+
/// A 16-bit PC-relative branch.
45+
///
46+
/// Represents a PC-relative branch to a target within +/-128Kb. The target
47+
/// must be 4-byte aligned.
48+
///
49+
/// Fixup expression:
50+
/// Fixup <- (Target - Fixup + Addend) >> 2 : int16
51+
///
52+
/// Notes:
53+
/// The '16' in the name refers to the number operand bits and follows the
54+
/// naming convention used by the corresponding ELF relocations. Since the low
55+
/// two bits must be zero (because of the 4-byte alignment of the target) the
56+
/// operand is effectively a signed 18-bit number.
57+
///
58+
/// Errors:
59+
/// - The result of the unshifted part of the fixup expression must be
60+
/// 4-byte aligned otherwise an alignment error will be returned.
61+
/// - The result of the fixup expression must fit into an int16 otherwise an
62+
/// out-of-range error will be returned.
63+
///
64+
Branch16PCRel,
65+
66+
/// A 21-bit PC-relative branch.
67+
///
68+
/// Represents a PC-relative branch to a target within +/-4Mb. The Target must
69+
/// be 4-byte aligned.
70+
///
71+
/// Fixup expression:
72+
/// Fixup <- (Target - Fixup + Addend) >> 2 : int21
73+
///
74+
/// Notes:
75+
/// The '21' in the name refers to the number operand bits and follows the
76+
/// naming convention used by the corresponding ELF relocations. Since the low
77+
/// two bits must be zero (because of the 4-byte alignment of the target) the
78+
/// operand is effectively a signed 23-bit number.
79+
///
80+
/// Errors:
81+
/// - The result of the unshifted part of the fixup expression must be
82+
/// 4-byte aligned otherwise an alignment error will be returned.
83+
/// - The result of the fixup expression must fit into an int21 otherwise an
84+
/// out-of-range error will be returned.
85+
///
86+
Branch21PCRel,
87+
4488
/// A 26-bit PC-relative branch.
4589
///
4690
/// Represents a PC-relative call or branch to a target within +/-128Mb. The
@@ -213,6 +257,37 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E) {
213257
*(ulittle32_t *)FixupPtr = Value;
214258
break;
215259
}
260+
case Branch16PCRel: {
261+
int64_t Value = TargetAddress - FixupAddress + Addend;
262+
263+
if (!isInt<18>(Value))
264+
return makeTargetOutOfRangeError(G, B, E);
265+
266+
if (!isShiftedInt<16, 2>(Value))
267+
return makeAlignmentError(orc::ExecutorAddr(FixupAddress), Value, 4, E);
268+
269+
uint32_t RawInstr = *(little32_t *)FixupPtr;
270+
uint32_t Imm = static_cast<uint32_t>(Value >> 2);
271+
uint32_t Imm15_0 = extractBits(Imm, /*Hi=*/15, /*Lo=*/0) << 10;
272+
*(little32_t *)FixupPtr = RawInstr | Imm15_0;
273+
break;
274+
}
275+
case Branch21PCRel: {
276+
int64_t Value = TargetAddress - FixupAddress + Addend;
277+
278+
if (!isInt<23>(Value))
279+
return makeTargetOutOfRangeError(G, B, E);
280+
281+
if (!isShiftedInt<21, 2>(Value))
282+
return makeAlignmentError(orc::ExecutorAddr(FixupAddress), Value, 4, E);
283+
284+
uint32_t RawInstr = *(little32_t *)FixupPtr;
285+
uint32_t Imm = static_cast<uint32_t>(Value >> 2);
286+
uint32_t Imm15_0 = extractBits(Imm, /*Hi=*/15, /*Lo=*/0) << 10;
287+
uint32_t Imm20_16 = extractBits(Imm, /*Hi=*/20, /*Lo=*/16);
288+
*(little32_t *)FixupPtr = RawInstr | Imm15_0 | Imm20_16;
289+
break;
290+
}
216291
case Branch26PCRel: {
217292
int64_t Value = TargetAddress - FixupAddress + Addend;
218293

llvm/lib/ExecutionEngine/JITLink/ELF_loongarch.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ class ELFLinkGraphBuilder_loongarch : public ELFLinkGraphBuilder<ELFT> {
5858
return Pointer32;
5959
case ELF::R_LARCH_32_PCREL:
6060
return Delta32;
61+
case ELF::R_LARCH_B16:
62+
return Branch16PCRel;
63+
case ELF::R_LARCH_B21:
64+
return Branch21PCRel;
6165
case ELF::R_LARCH_B26:
6266
return Branch26PCRel;
6367
case ELF::R_LARCH_PCALA_HI20:

llvm/lib/ExecutionEngine/JITLink/loongarch.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ const char *getEdgeKindName(Edge::Kind K) {
4444
KIND_NAME_CASE(Delta32)
4545
KIND_NAME_CASE(NegDelta32)
4646
KIND_NAME_CASE(Delta64)
47+
KIND_NAME_CASE(Branch16PCRel)
48+
KIND_NAME_CASE(Branch21PCRel)
4749
KIND_NAME_CASE(Branch26PCRel)
4850
KIND_NAME_CASE(Page20)
4951
KIND_NAME_CASE(PageOffset12)

llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3769,22 +3769,9 @@ static Value *foldSelectIntoAddConstant(SelectInst &SI,
37693769
if (!SIFOp || !SIFOp->hasNoSignedZeros() || !SIFOp->hasNoNaNs())
37703770
return nullptr;
37713771

3772-
// select((fcmp Pred, X, 0), (fadd X, C), C)
3773-
// => fadd((select (fcmp Pred, X, 0), X, 0), C)
3774-
//
3775-
// Pred := OGT, OGE, OLT, OLE, UGT, UGE, ULT, and ULE
3776-
Instruction *FAdd;
3777-
Constant *C;
3778-
Value *X, *Z;
3779-
CmpPredicate Pred;
3780-
3781-
// Note: OneUse check for `Cmp` is necessary because it makes sure that other
3782-
// InstCombine folds don't undo this transformation and cause an infinite
3783-
// loop. Furthermore, it could also increase the operation count.
3784-
if (match(&SI, m_Select(m_OneUse(m_FCmp(Pred, m_Value(X), m_Value(Z))),
3785-
m_OneUse(m_Instruction(FAdd)), m_Constant(C))) ||
3786-
match(&SI, m_Select(m_OneUse(m_FCmp(Pred, m_Value(X), m_Value(Z))),
3787-
m_Constant(C), m_OneUse(m_Instruction(FAdd))))) {
3772+
auto TryFoldIntoAddConstant =
3773+
[&Builder, &SI](CmpInst::Predicate Pred, Value *X, Value *Z,
3774+
Instruction *FAdd, Constant *C, bool Swapped) -> Value * {
37883775
// Only these relational predicates can be transformed into maxnum/minnum
37893776
// intrinsic.
37903777
if (!CmpInst::isRelational(Pred) || !match(Z, m_AnyZeroFP()))
@@ -3793,7 +3780,8 @@ static Value *foldSelectIntoAddConstant(SelectInst &SI,
37933780
if (!match(FAdd, m_FAdd(m_Specific(X), m_Specific(C))))
37943781
return nullptr;
37953782

3796-
Value *NewSelect = Builder.CreateSelect(SI.getCondition(), X, Z, "", &SI);
3783+
Value *NewSelect = Builder.CreateSelect(SI.getCondition(), Swapped ? Z : X,
3784+
Swapped ? X : Z, "", &SI);
37973785
NewSelect->takeName(&SI);
37983786

37993787
Value *NewFAdd = Builder.CreateFAdd(NewSelect, C);
@@ -3808,7 +3796,27 @@ static Value *foldSelectIntoAddConstant(SelectInst &SI,
38083796
cast<Instruction>(NewSelect)->setFastMathFlags(NewFMF);
38093797

38103798
return NewFAdd;
3811-
}
3799+
};
3800+
3801+
// select((fcmp Pred, X, 0), (fadd X, C), C)
3802+
// => fadd((select (fcmp Pred, X, 0), X, 0), C)
3803+
//
3804+
// Pred := OGT, OGE, OLT, OLE, UGT, UGE, ULT, and ULE
3805+
Instruction *FAdd;
3806+
Constant *C;
3807+
Value *X, *Z;
3808+
CmpPredicate Pred;
3809+
3810+
// Note: OneUse check for `Cmp` is necessary because it makes sure that other
3811+
// InstCombine folds don't undo this transformation and cause an infinite
3812+
// loop. Furthermore, it could also increase the operation count.
3813+
if (match(&SI, m_Select(m_OneUse(m_FCmp(Pred, m_Value(X), m_Value(Z))),
3814+
m_OneUse(m_Instruction(FAdd)), m_Constant(C))))
3815+
return TryFoldIntoAddConstant(Pred, X, Z, FAdd, C, /*Swapped=*/false);
3816+
3817+
if (match(&SI, m_Select(m_OneUse(m_FCmp(Pred, m_Value(X), m_Value(Z))),
3818+
m_Constant(C), m_OneUse(m_Instruction(FAdd)))))
3819+
return TryFoldIntoAddConstant(Pred, X, Z, FAdd, C, /*Swapped=*/true);
38123820

38133821
return nullptr;
38143822
}

0 commit comments

Comments
 (0)