Skip to content

Commit 5446ec8

Browse files
committed
[mlir] take MLIRContext instead of LLVMDialect in getters of LLVMType's
Historical modeling of the LLVM dialect types had been wrapping LLVM IR types and therefore needed access to the instance of LLVMContext stored in the LLVMDialect. The new modeling does not rely on that and only needs the MLIRContext that is used for uniquing, similarly to other MLIR types. Change LLVMType::get<Kind>Ty functions to take `MLIRContext *` instead of `LLVMDialect *` as first argument. This brings the code base closer to completely removing the dependence on LLVMContext from the LLVMDialect, together with additional support for thread-safety of its use. Depends On D85371 Reviewed By: rriddle Differential Revision: https://reviews.llvm.org/D85372
1 parent d3a9807 commit 5446ec8

File tree

19 files changed

+185
-234
lines changed

19 files changed

+185
-234
lines changed

mlir/examples/toy/Ch6/mlir/LowerToLLVM.cpp

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -56,19 +56,15 @@ class PrintOpLowering : public ConversionPattern {
5656
auto memRefType = (*op->operand_type_begin()).cast<MemRefType>();
5757
auto memRefShape = memRefType.getShape();
5858
auto loc = op->getLoc();
59-
auto *llvmDialect =
60-
op->getContext()->getRegisteredDialect<LLVM::LLVMDialect>();
61-
assert(llvmDialect && "expected llvm dialect to be registered");
6259

6360
ModuleOp parentModule = op->getParentOfType<ModuleOp>();
6461

6562
// Get a symbol reference to the printf function, inserting it if necessary.
66-
auto printfRef = getOrInsertPrintf(rewriter, parentModule, llvmDialect);
63+
auto printfRef = getOrInsertPrintf(rewriter, parentModule);
6764
Value formatSpecifierCst = getOrCreateGlobalString(
68-
loc, rewriter, "frmt_spec", StringRef("%f \0", 4), parentModule,
69-
llvmDialect);
65+
loc, rewriter, "frmt_spec", StringRef("%f \0", 4), parentModule);
7066
Value newLineCst = getOrCreateGlobalString(
71-
loc, rewriter, "nl", StringRef("\n\0", 2), parentModule, llvmDialect);
67+
loc, rewriter, "nl", StringRef("\n\0", 2), parentModule);
7268

7369
// Create a loop for each of the dimensions within the shape.
7470
SmallVector<Value, 4> loopIvs;
@@ -108,16 +104,15 @@ class PrintOpLowering : public ConversionPattern {
108104
/// Return a symbol reference to the printf function, inserting it into the
109105
/// module if necessary.
110106
static FlatSymbolRefAttr getOrInsertPrintf(PatternRewriter &rewriter,
111-
ModuleOp module,
112-
LLVM::LLVMDialect *llvmDialect) {
107+
ModuleOp module) {
113108
auto *context = module.getContext();
114109
if (module.lookupSymbol<LLVM::LLVMFuncOp>("printf"))
115110
return SymbolRefAttr::get("printf", context);
116111

117112
// Create a function declaration for printf, the signature is:
118113
// * `i32 (i8*, ...)`
119-
auto llvmI32Ty = LLVM::LLVMType::getInt32Ty(llvmDialect);
120-
auto llvmI8PtrTy = LLVM::LLVMType::getInt8PtrTy(llvmDialect);
114+
auto llvmI32Ty = LLVM::LLVMType::getInt32Ty(context);
115+
auto llvmI8PtrTy = LLVM::LLVMType::getInt8PtrTy(context);
121116
auto llvmFnType = LLVM::LLVMType::getFunctionTy(llvmI32Ty, llvmI8PtrTy,
122117
/*isVarArg=*/true);
123118

@@ -132,15 +127,14 @@ class PrintOpLowering : public ConversionPattern {
132127
/// name, creating the string if necessary.
133128
static Value getOrCreateGlobalString(Location loc, OpBuilder &builder,
134129
StringRef name, StringRef value,
135-
ModuleOp module,
136-
LLVM::LLVMDialect *llvmDialect) {
130+
ModuleOp module) {
137131
// Create the global at the entry of the module.
138132
LLVM::GlobalOp global;
139133
if (!(global = module.lookupSymbol<LLVM::GlobalOp>(name))) {
140134
OpBuilder::InsertionGuard insertGuard(builder);
141135
builder.setInsertionPointToStart(module.getBody());
142136
auto type = LLVM::LLVMType::getArrayTy(
143-
LLVM::LLVMType::getInt8Ty(llvmDialect), value.size());
137+
LLVM::LLVMType::getInt8Ty(builder.getContext()), value.size());
144138
global = builder.create<LLVM::GlobalOp>(loc, type, /*isConstant=*/true,
145139
LLVM::Linkage::Internal, name,
146140
builder.getStringAttr(value));
@@ -149,10 +143,10 @@ class PrintOpLowering : public ConversionPattern {
149143
// Get the pointer to the first character in the global string.
150144
Value globalPtr = builder.create<LLVM::AddressOfOp>(loc, global);
151145
Value cst0 = builder.create<LLVM::ConstantOp>(
152-
loc, LLVM::LLVMType::getInt64Ty(llvmDialect),
146+
loc, LLVM::LLVMType::getInt64Ty(builder.getContext()),
153147
builder.getIntegerAttr(builder.getIndexType(), 0));
154148
return builder.create<LLVM::GEPOp>(
155-
loc, LLVM::LLVMType::getInt8PtrTy(llvmDialect), globalPtr,
149+
loc, LLVM::LLVMType::getInt8PtrTy(builder.getContext()), globalPtr,
156150
ArrayRef<Value>({cst0, cst0}));
157151
}
158152
};

mlir/examples/toy/Ch7/mlir/LowerToLLVM.cpp

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -56,19 +56,15 @@ class PrintOpLowering : public ConversionPattern {
5656
auto memRefType = (*op->operand_type_begin()).cast<MemRefType>();
5757
auto memRefShape = memRefType.getShape();
5858
auto loc = op->getLoc();
59-
auto *llvmDialect =
60-
op->getContext()->getRegisteredDialect<LLVM::LLVMDialect>();
61-
assert(llvmDialect && "expected llvm dialect to be registered");
6259

6360
ModuleOp parentModule = op->getParentOfType<ModuleOp>();
6461

6562
// Get a symbol reference to the printf function, inserting it if necessary.
66-
auto printfRef = getOrInsertPrintf(rewriter, parentModule, llvmDialect);
63+
auto printfRef = getOrInsertPrintf(rewriter, parentModule);
6764
Value formatSpecifierCst = getOrCreateGlobalString(
68-
loc, rewriter, "frmt_spec", StringRef("%f \0", 4), parentModule,
69-
llvmDialect);
65+
loc, rewriter, "frmt_spec", StringRef("%f \0", 4), parentModule);
7066
Value newLineCst = getOrCreateGlobalString(
71-
loc, rewriter, "nl", StringRef("\n\0", 2), parentModule, llvmDialect);
67+
loc, rewriter, "nl", StringRef("\n\0", 2), parentModule);
7268

7369
// Create a loop for each of the dimensions within the shape.
7470
SmallVector<Value, 4> loopIvs;
@@ -108,16 +104,15 @@ class PrintOpLowering : public ConversionPattern {
108104
/// Return a symbol reference to the printf function, inserting it into the
109105
/// module if necessary.
110106
static FlatSymbolRefAttr getOrInsertPrintf(PatternRewriter &rewriter,
111-
ModuleOp module,
112-
LLVM::LLVMDialect *llvmDialect) {
107+
ModuleOp module) {
113108
auto *context = module.getContext();
114109
if (module.lookupSymbol<LLVM::LLVMFuncOp>("printf"))
115110
return SymbolRefAttr::get("printf", context);
116111

117112
// Create a function declaration for printf, the signature is:
118113
// * `i32 (i8*, ...)`
119-
auto llvmI32Ty = LLVM::LLVMType::getInt32Ty(llvmDialect);
120-
auto llvmI8PtrTy = LLVM::LLVMType::getInt8PtrTy(llvmDialect);
114+
auto llvmI32Ty = LLVM::LLVMType::getInt32Ty(context);
115+
auto llvmI8PtrTy = LLVM::LLVMType::getInt8PtrTy(context);
121116
auto llvmFnType = LLVM::LLVMType::getFunctionTy(llvmI32Ty, llvmI8PtrTy,
122117
/*isVarArg=*/true);
123118

@@ -132,15 +127,14 @@ class PrintOpLowering : public ConversionPattern {
132127
/// name, creating the string if necessary.
133128
static Value getOrCreateGlobalString(Location loc, OpBuilder &builder,
134129
StringRef name, StringRef value,
135-
ModuleOp module,
136-
LLVM::LLVMDialect *llvmDialect) {
130+
ModuleOp module) {
137131
// Create the global at the entry of the module.
138132
LLVM::GlobalOp global;
139133
if (!(global = module.lookupSymbol<LLVM::GlobalOp>(name))) {
140134
OpBuilder::InsertionGuard insertGuard(builder);
141135
builder.setInsertionPointToStart(module.getBody());
142136
auto type = LLVM::LLVMType::getArrayTy(
143-
LLVM::LLVMType::getInt8Ty(llvmDialect), value.size());
137+
LLVM::LLVMType::getInt8Ty(builder.getContext()), value.size());
144138
global = builder.create<LLVM::GlobalOp>(loc, type, /*isConstant=*/true,
145139
LLVM::Linkage::Internal, name,
146140
builder.getStringAttr(value));
@@ -149,10 +143,10 @@ class PrintOpLowering : public ConversionPattern {
149143
// Get the pointer to the first character in the global string.
150144
Value globalPtr = builder.create<LLVM::AddressOfOp>(loc, global);
151145
Value cst0 = builder.create<LLVM::ConstantOp>(
152-
loc, LLVM::LLVMType::getInt64Ty(llvmDialect),
146+
loc, LLVM::LLVMType::getInt64Ty(builder.getContext()),
153147
builder.getIntegerAttr(builder.getIndexType(), 0));
154148
return builder.create<LLVM::GEPOp>(
155-
loc, LLVM::LLVMType::getInt8PtrTy(llvmDialect), globalPtr,
149+
loc, LLVM::LLVMType::getInt8PtrTy(builder.getContext()), globalPtr,
156150
ArrayRef<Value>({cst0, cst0}));
157151
}
158152
};

mlir/include/mlir/Dialect/LLVMIR/LLVMDialect.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,7 @@ struct LLVMDialectImpl;
5959
/// global and use it to compute the address of the first character in the
6060
/// string (operations inserted at the builder insertion point).
6161
Value createGlobalString(Location loc, OpBuilder &builder, StringRef name,
62-
StringRef value, LLVM::Linkage linkage,
63-
LLVM::LLVMDialect *llvmDialect);
62+
StringRef value, LLVM::Linkage linkage);
6463

6564
/// LLVM requires some operations to be inside of a Module operation. This
6665
/// function confirms that the Operation has the desired properties.

mlir/include/mlir/Dialect/LLVMIR/LLVMOpBase.td

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,7 @@ class LLVMI<int width>
5858
"$_self.cast<::mlir::LLVM::LLVMType>().isIntegerTy(" # width # ")">]>,
5959
"LLVM dialect " # width # "-bit integer">,
6060
BuildableType<
61-
"::mlir::LLVM::LLVMType::getIntNTy("
62-
"$_builder.getContext()->getRegisteredDialect<LLVM::LLVMDialect>(),"
61+
"::mlir::LLVM::LLVMType::getIntNTy($_builder.getContext(),"
6362
# width # ")">;
6463

6564
def LLVMI1 : LLVMI<1>;

mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,7 @@ def LLVM_ICmpOp : LLVM_OneResultOp<"icmp", [NoSideEffect]>,
151151
let builders = [OpBuilder<
152152
"OpBuilder &b, OperationState &result, ICmpPredicate predicate, Value lhs, "
153153
"Value rhs", [{
154-
LLVMDialect *dialect = &lhs.getType().cast<LLVMType>().getDialect();
155-
build(b, result, LLVMType::getInt1Ty(dialect),
154+
build(b, result, LLVMType::getInt1Ty(lhs.getType().getContext()),
156155
b.getI64IntegerAttr(static_cast<int64_t>(predicate)), lhs, rhs);
157156
}]>];
158157
let parser = [{ return parseCmpOp<ICmpPredicate>(parser, result); }];
@@ -198,8 +197,7 @@ def LLVM_FCmpOp : LLVM_OneResultOp<"fcmp", [NoSideEffect]>,
198197
let builders = [OpBuilder<
199198
"OpBuilder &b, OperationState &result, FCmpPredicate predicate, Value lhs, "
200199
"Value rhs", [{
201-
LLVMDialect *dialect = &lhs.getType().cast<LLVMType>().getDialect();
202-
build(b, result, LLVMType::getInt1Ty(dialect),
200+
build(b, result, LLVMType::getInt1Ty(lhs.getType().getContext()),
203201
b.getI64IntegerAttr(static_cast<int64_t>(predicate)), lhs, rhs);
204202
}]>];
205203
let parser = [{ return parseCmpOp<FCmpPredicate>(parser, result); }];

mlir/include/mlir/Dialect/LLVMIR/LLVMTypes.h

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -152,32 +152,32 @@ class LLVMType : public Type::TypeBase<LLVMType, Type, TypeStorage> {
152152
bool isStructTy();
153153

154154
/// Utilities used to generate floating point types.
155-
static LLVMType getDoubleTy(LLVMDialect *dialect);
156-
static LLVMType getFloatTy(LLVMDialect *dialect);
157-
static LLVMType getBFloatTy(LLVMDialect *dialect);
158-
static LLVMType getHalfTy(LLVMDialect *dialect);
159-
static LLVMType getFP128Ty(LLVMDialect *dialect);
160-
static LLVMType getX86_FP80Ty(LLVMDialect *dialect);
155+
static LLVMType getDoubleTy(MLIRContext *context);
156+
static LLVMType getFloatTy(MLIRContext *context);
157+
static LLVMType getBFloatTy(MLIRContext *context);
158+
static LLVMType getHalfTy(MLIRContext *context);
159+
static LLVMType getFP128Ty(MLIRContext *context);
160+
static LLVMType getX86_FP80Ty(MLIRContext *context);
161161

162162
/// Utilities used to generate integer types.
163-
static LLVMType getIntNTy(LLVMDialect *dialect, unsigned numBits);
164-
static LLVMType getInt1Ty(LLVMDialect *dialect) {
165-
return getIntNTy(dialect, /*numBits=*/1);
163+
static LLVMType getIntNTy(MLIRContext *context, unsigned numBits);
164+
static LLVMType getInt1Ty(MLIRContext *context) {
165+
return getIntNTy(context, /*numBits=*/1);
166166
}
167-
static LLVMType getInt8Ty(LLVMDialect *dialect) {
168-
return getIntNTy(dialect, /*numBits=*/8);
167+
static LLVMType getInt8Ty(MLIRContext *context) {
168+
return getIntNTy(context, /*numBits=*/8);
169169
}
170-
static LLVMType getInt8PtrTy(LLVMDialect *dialect) {
171-
return getInt8Ty(dialect).getPointerTo();
170+
static LLVMType getInt8PtrTy(MLIRContext *context) {
171+
return getInt8Ty(context).getPointerTo();
172172
}
173-
static LLVMType getInt16Ty(LLVMDialect *dialect) {
174-
return getIntNTy(dialect, /*numBits=*/16);
173+
static LLVMType getInt16Ty(MLIRContext *context) {
174+
return getIntNTy(context, /*numBits=*/16);
175175
}
176-
static LLVMType getInt32Ty(LLVMDialect *dialect) {
177-
return getIntNTy(dialect, /*numBits=*/32);
176+
static LLVMType getInt32Ty(MLIRContext *context) {
177+
return getIntNTy(context, /*numBits=*/32);
178178
}
179-
static LLVMType getInt64Ty(LLVMDialect *dialect) {
180-
return getIntNTy(dialect, /*numBits=*/64);
179+
static LLVMType getInt64Ty(MLIRContext *context) {
180+
return getIntNTy(context, /*numBits=*/64);
181181
}
182182

183183
/// Utilities used to generate other miscellaneous types.
@@ -187,33 +187,33 @@ class LLVMType : public Type::TypeBase<LLVMType, Type, TypeStorage> {
187187
static LLVMType getFunctionTy(LLVMType result, bool isVarArg) {
188188
return getFunctionTy(result, llvm::None, isVarArg);
189189
}
190-
static LLVMType getStructTy(LLVMDialect *dialect, ArrayRef<LLVMType> elements,
190+
static LLVMType getStructTy(MLIRContext *context, ArrayRef<LLVMType> elements,
191191
bool isPacked = false);
192-
static LLVMType getStructTy(LLVMDialect *dialect, bool isPacked = false) {
193-
return getStructTy(dialect, llvm::None, isPacked);
192+
static LLVMType getStructTy(MLIRContext *context, bool isPacked = false) {
193+
return getStructTy(context, llvm::None, isPacked);
194194
}
195195
template <typename... Args>
196196
static typename std::enable_if<llvm::are_base_of<LLVMType, Args...>::value,
197197
LLVMType>::type
198198
getStructTy(LLVMType elt1, Args... elts) {
199199
SmallVector<LLVMType, 8> fields({elt1, elts...});
200-
return getStructTy(&elt1.getDialect(), fields);
200+
return getStructTy(elt1.getContext(), fields);
201201
}
202202
static LLVMType getVectorTy(LLVMType elementType, unsigned numElements);
203203

204204
/// Void type utilities.
205-
static LLVMType getVoidTy(LLVMDialect *dialect);
205+
static LLVMType getVoidTy(MLIRContext *context);
206206
bool isVoidTy();
207207

208208
// Creation and setting of LLVM's identified struct types
209-
static LLVMType createStructTy(LLVMDialect *dialect,
209+
static LLVMType createStructTy(MLIRContext *context,
210210
ArrayRef<LLVMType> elements,
211211
Optional<StringRef> name,
212212
bool isPacked = false);
213213

214-
static LLVMType createStructTy(LLVMDialect *dialect,
214+
static LLVMType createStructTy(MLIRContext *context,
215215
Optional<StringRef> name) {
216-
return createStructTy(dialect, llvm::None, name);
216+
return createStructTy(context, llvm::None, name);
217217
}
218218

219219
static LLVMType createStructTy(ArrayRef<LLVMType> elements,
@@ -222,7 +222,7 @@ class LLVMType : public Type::TypeBase<LLVMType, Type, TypeStorage> {
222222
assert(!elements.empty() &&
223223
"This method may not be invoked with an empty list");
224224
LLVMType ele0 = elements.front();
225-
return createStructTy(&ele0.getDialect(), elements, name, isPacked);
225+
return createStructTy(ele0.getContext(), elements, name, isPacked);
226226
}
227227

228228
template <typename... Args>
@@ -231,7 +231,7 @@ class LLVMType : public Type::TypeBase<LLVMType, Type, TypeStorage> {
231231
createStructTy(StringRef name, LLVMType elt1, Args... elts) {
232232
SmallVector<LLVMType, 8> fields({elt1, elts...});
233233
Optional<StringRef> opt_name(name);
234-
return createStructTy(&elt1.getDialect(), fields, opt_name);
234+
return createStructTy(elt1.getContext(), fields, opt_name);
235235
}
236236

237237
static LLVMType setStructTyBody(LLVMType structType,

mlir/lib/Conversion/GPUCommon/ConvertLaunchFuncToRuntimeCalls.cpp

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,14 @@ class GpuLaunchFuncToGpuRuntimeCallsPass
6767
LLVM::LLVMDialect *getLLVMDialect() { return llvmDialect; }
6868

6969
void initializeCachedTypes() {
70-
llvmVoidType = LLVM::LLVMType::getVoidTy(llvmDialect);
71-
llvmPointerType = LLVM::LLVMType::getInt8PtrTy(llvmDialect);
70+
llvmVoidType = LLVM::LLVMType::getVoidTy(&getContext());
71+
llvmPointerType = LLVM::LLVMType::getInt8PtrTy(&getContext());
7272
llvmPointerPointerType = llvmPointerType.getPointerTo();
73-
llvmInt8Type = LLVM::LLVMType::getInt8Ty(llvmDialect);
74-
llvmInt32Type = LLVM::LLVMType::getInt32Ty(llvmDialect);
75-
llvmInt64Type = LLVM::LLVMType::getInt64Ty(llvmDialect);
73+
llvmInt8Type = LLVM::LLVMType::getInt8Ty(&getContext());
74+
llvmInt32Type = LLVM::LLVMType::getInt32Ty(&getContext());
75+
llvmInt64Type = LLVM::LLVMType::getInt64Ty(&getContext());
7676
llvmIntPtrType = LLVM::LLVMType::getIntNTy(
77-
llvmDialect, llvmDialect->getDataLayout().getPointerSizeInBits());
77+
&getContext(), llvmDialect->getDataLayout().getPointerSizeInBits());
7878
}
7979

8080
LLVM::LLVMType getVoidType() { return llvmVoidType; }
@@ -91,7 +91,7 @@ class GpuLaunchFuncToGpuRuntimeCallsPass
9191

9292
LLVM::LLVMType getIntPtrType() {
9393
return LLVM::LLVMType::getIntNTy(
94-
getLLVMDialect(),
94+
&getContext(),
9595
getLLVMDialect()->getDataLayout().getPointerSizeInBits());
9696
}
9797

@@ -340,7 +340,7 @@ Value GpuLaunchFuncToGpuRuntimeCallsPass::generateKernelNameConstant(
340340
std::string(llvm::formatv("{0}_{1}_kernel_name", moduleName, name));
341341
return LLVM::createGlobalString(
342342
loc, builder, globalName, StringRef(kernelName.data(), kernelName.size()),
343-
LLVM::Linkage::Internal, llvmDialect);
343+
LLVM::Linkage::Internal);
344344
}
345345

346346
// Emits LLVM IR to launch a kernel function. Expects the module that contains
@@ -378,9 +378,9 @@ void GpuLaunchFuncToGpuRuntimeCallsPass::translateGpuLaunchCalls(
378378

379379
SmallString<128> nameBuffer(kernelModule.getName());
380380
nameBuffer.append(kGpuBinaryStorageSuffix);
381-
Value data = LLVM::createGlobalString(
382-
loc, builder, nameBuffer.str(), binaryAttr.getValue(),
383-
LLVM::Linkage::Internal, getLLVMDialect());
381+
Value data =
382+
LLVM::createGlobalString(loc, builder, nameBuffer.str(),
383+
binaryAttr.getValue(), LLVM::Linkage::Internal);
384384

385385
// Emit the load module call to load the module data. Error checking is done
386386
// in the called helper function.

mlir/lib/Conversion/GPUCommon/GPUOpsLowering.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ struct GPUFuncOpLowering : ConvertToLLVMPattern {
8989
// Rewrite workgroup memory attributions to addresses of global buffers.
9090
rewriter.setInsertionPointToStart(&gpuFuncOp.front());
9191
unsigned numProperArguments = gpuFuncOp.getNumArguments();
92-
auto i32Type = LLVM::LLVMType::getInt32Ty(typeConverter.getDialect());
92+
auto i32Type = LLVM::LLVMType::getInt32Ty(rewriter.getContext());
9393

9494
Value zero = nullptr;
9595
if (!workgroupBuffers.empty())
@@ -117,7 +117,7 @@ struct GPUFuncOpLowering : ConvertToLLVMPattern {
117117
// Rewrite private memory attributions to alloca'ed buffers.
118118
unsigned numWorkgroupAttributions =
119119
gpuFuncOp.getNumWorkgroupAttributions();
120-
auto int64Ty = LLVM::LLVMType::getInt64Ty(typeConverter.getDialect());
120+
auto int64Ty = LLVM::LLVMType::getInt64Ty(rewriter.getContext());
121121
for (auto en : llvm::enumerate(gpuFuncOp.getPrivateAttributions())) {
122122
Value attribution = en.value();
123123
auto type = attribution.getType().cast<MemRefType>();

0 commit comments

Comments
 (0)