Skip to content

Commit faefe7c

Browse files
authored
[flang] add option to generate runtime type info as external (#146071)
Reland #145901 with a fix for shared library builds. So far flang generates runtime derived type info global definitions (as opposed to declarations) for all the types used in the current compilation unit even when the derived types are defined in other compilation units. It is using linkonce_odr to achieve derived type descriptor address "uniqueness" aspect needed to match two derived type inside the runtime. This comes at a big compile time cost because of all the extra globals and their definitions in apps with many and complex derived types. This patch adds and experimental option to only generate the rtti definition for the types defined in the current compilation unit and to only generate external declaration for the derived type descriptor object of types defined elsewhere. Note that objects compiled with this option are not compatible with object files compiled without because files compiled without it may drop the rtti for type they defined if it is not used in the compilation unit because of the linkonce_odr aspect. I am adding the option so that we can better measure the extra cost of the current approach on apps and allow speeding up some compilation where devirtualization does not matter (and the build config links to all module file object anyway).
1 parent 834c410 commit faefe7c

File tree

18 files changed

+267
-160
lines changed

18 files changed

+267
-160
lines changed

flang/include/flang/Evaluate/tools.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1585,6 +1585,7 @@ bool IsExtensibleType(const DerivedTypeSpec *);
15851585
bool IsSequenceOrBindCType(const DerivedTypeSpec *);
15861586
bool IsBuiltinDerivedType(const DerivedTypeSpec *derived, const char *name);
15871587
bool IsBuiltinCPtr(const Symbol &);
1588+
bool IsFromBuiltinModule(const Symbol &);
15881589
bool IsEventType(const DerivedTypeSpec *);
15891590
bool IsLockType(const DerivedTypeSpec *);
15901591
bool IsNotifyType(const DerivedTypeSpec *);

flang/include/flang/Lower/LoweringOptions.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,5 +66,9 @@ ENUM_LOWERINGOPT(RepackArraysWhole, unsigned, 1, 0)
6666
/// If true, CUDA Fortran runtime check is inserted.
6767
ENUM_LOWERINGOPT(CUDARuntimeCheck, unsigned, 1, 0)
6868

69+
/// If true, do not generate definition for runtime type info global objects of
70+
/// derived types defined in other compilation units.
71+
ENUM_LOWERINGOPT(SkipExternalRttiDefinition, unsigned, 1, 0)
72+
6973
#undef LOWERINGOPT
7074
#undef ENUM_LOWERINGOPT

flang/include/flang/Optimizer/CodeGen/CodeGen.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ struct FIRToLLVMPassOptions {
3939
// that such programs would crash at runtime if the derived type descriptors
4040
// are required by the runtime, so this is only an option to help debugging.
4141
bool ignoreMissingTypeDescriptors = false;
42+
// Similar to ignoreMissingTypeDescriptors, but generate external declaration
43+
// for the missing type descriptor globals instead.
44+
bool skipExternalRttiDefinition = false;
4245

4346
// Generate TBAA information for FIR types and memory accessing operations.
4447
bool applyTBAA = false;

flang/include/flang/Optimizer/Passes/CommandLineOpts.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,19 @@ extern llvm::cl::opt<std::size_t> arrayStackAllocationThreshold;
3232
/// generated by the frontend.
3333
extern llvm::cl::opt<bool> ignoreMissingTypeDescriptors;
3434

35+
/// Shared option in tools to only generate rtti static object definitions for
36+
/// derived types defined in the current compilation unit. Derived type
37+
/// descriptor object for types defined in other objects will only be declared
38+
/// as external. This also changes the linkage of rtti objects defined in the
39+
/// current compilation unit from linkonce_odr to external so that unused rtti
40+
/// objects are retained and can be accessed from other compilation units. This
41+
/// is an experimental option to explore compilation speed improvements and is
42+
/// an ABI breaking change because of the linkage change.
43+
/// It will also require linking against module file objects of modules defining
44+
/// only types (even for trivial types without type bound procedures, which
45+
/// differs from most compilers).
46+
extern llvm::cl::opt<bool> skipExternalRttiDefinition;
47+
3548
/// Default optimization level used to create Flang pass pipeline is O0.
3649
extern llvm::OptimizationLevel defaultOptLevel;
3750

flang/include/flang/Optimizer/Support/Utils.h

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -35,32 +35,6 @@ inline std::int64_t toInt(mlir::arith::ConstantOp cop) {
3535
.getSExtValue();
3636
}
3737

38-
// Reconstruct binding tables for dynamic dispatch.
39-
using BindingTable = llvm::DenseMap<llvm::StringRef, unsigned>;
40-
using BindingTables = llvm::DenseMap<llvm::StringRef, BindingTable>;
41-
42-
inline void buildBindingTables(BindingTables &bindingTables,
43-
mlir::ModuleOp mod) {
44-
45-
// The binding tables are defined in FIR after lowering inside fir.type_info
46-
// operations. Go through each binding tables and store the procedure name and
47-
// binding index for later use by the fir.dispatch conversion pattern.
48-
for (auto typeInfo : mod.getOps<fir::TypeInfoOp>()) {
49-
unsigned bindingIdx = 0;
50-
BindingTable bindings;
51-
if (typeInfo.getDispatchTable().empty()) {
52-
bindingTables[typeInfo.getSymName()] = bindings;
53-
continue;
54-
}
55-
for (auto dtEntry :
56-
typeInfo.getDispatchTable().front().getOps<fir::DTEntryOp>()) {
57-
bindings[dtEntry.getMethod()] = bindingIdx;
58-
++bindingIdx;
59-
}
60-
bindingTables[typeInfo.getSymName()] = bindings;
61-
}
62-
}
63-
6438
// Translate front-end KINDs for use in the IR and code gen.
6539
inline std::vector<fir::KindTy>
6640
fromDefaultKinds(const Fortran::common::IntrinsicTypeDefaultKinds &defKinds) {

flang/include/flang/Semantics/runtime-type-info.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ RuntimeDerivedTypeTables BuildRuntimeDerivedTypeTables(SemanticsContext &);
3838
/// to describe other derived types at runtime in flang descriptor.
3939
constexpr char typeInfoBuiltinModule[]{"__fortran_type_info"};
4040

41+
/// Name of the builtin derived type in __fortran_type_inf that is used for
42+
/// derived type descriptors.
43+
constexpr char typeDescriptorTypeName[]{"derivedtype"};
44+
4145
/// Name of the bindings descriptor component in the DerivedType type of the
4246
/// __Fortran_type_info module
4347
constexpr char bindingDescCompName[]{"binding"};

flang/lib/Evaluate/tools.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2334,6 +2334,11 @@ bool IsBuiltinCPtr(const Symbol &symbol) {
23342334
return false;
23352335
}
23362336

2337+
bool IsFromBuiltinModule(const Symbol &symbol) {
2338+
const Scope &scope{symbol.GetUltimate().owner()};
2339+
return IsSameModule(&scope, scope.context().GetBuiltinsScope());
2340+
}
2341+
23372342
bool IsIsoCType(const DerivedTypeSpec *derived) {
23382343
return IsBuiltinDerivedType(derived, "c_ptr") ||
23392344
IsBuiltinDerivedType(derived, "c_funptr");

flang/lib/Frontend/CompilerInvocation.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "flang/Frontend/CodeGenOptions.h"
1515
#include "flang/Frontend/PreprocessorOptions.h"
1616
#include "flang/Frontend/TargetOptions.h"
17+
#include "flang/Optimizer/Passes/CommandLineOpts.h"
1718
#include "flang/Semantics/semantics.h"
1819
#include "flang/Support/Fortran-features.h"
1920
#include "flang/Support/OpenMP-features.h"
@@ -1792,6 +1793,7 @@ void CompilerInvocation::setLoweringOptions() {
17921793
// Lower TRANSPOSE as a runtime call under -O0.
17931794
loweringOpts.setOptimizeTranspose(codegenOpts.OptimizationLevel > 0);
17941795
loweringOpts.setUnderscoring(codegenOpts.Underscoring);
1796+
loweringOpts.setSkipExternalRttiDefinition(skipExternalRttiDefinition);
17951797

17961798
const Fortran::common::LangOptions &langOptions = getLangOpts();
17971799
loweringOpts.setIntegerWrapAround(langOptions.getSignedOverflowBehavior() ==

flang/lib/Lower/Bridge.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ class TypeInfoConverter {
262262
}
263263

264264
void createTypeInfo(Fortran::lower::AbstractConverter &converter) {
265+
createTypeInfoForTypeDescriptorBuiltinType(converter);
265266
while (!registeredTypeInfoA.empty()) {
266267
currentTypeInfoStack = &registeredTypeInfoB;
267268
for (const TypeInfo &info : registeredTypeInfoA)
@@ -277,10 +278,22 @@ class TypeInfoConverter {
277278
private:
278279
void createTypeInfoOpAndGlobal(Fortran::lower::AbstractConverter &converter,
279280
const TypeInfo &info) {
280-
Fortran::lower::createRuntimeTypeInfoGlobal(converter, info.symbol.get());
281+
if (!converter.getLoweringOptions().getSkipExternalRttiDefinition())
282+
Fortran::lower::createRuntimeTypeInfoGlobal(converter, info.symbol.get());
281283
createTypeInfoOp(converter, info);
282284
}
283285

286+
void createTypeInfoForTypeDescriptorBuiltinType(
287+
Fortran::lower::AbstractConverter &converter) {
288+
if (registeredTypeInfoA.empty())
289+
return;
290+
auto builtinTypeInfoType = llvm::cast<fir::RecordType>(
291+
converter.genType(registeredTypeInfoA[0].symbol.get()));
292+
converter.getFirOpBuilder().createTypeInfoOp(
293+
registeredTypeInfoA[0].loc, builtinTypeInfoType,
294+
/*parentType=*/fir::RecordType{});
295+
}
296+
284297
void createTypeInfoOp(Fortran::lower::AbstractConverter &converter,
285298
const TypeInfo &info) {
286299
fir::RecordType parentType{};

flang/lib/Lower/ConvertVariable.cpp

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -647,13 +647,19 @@ fir::GlobalOp Fortran::lower::defineGlobal(
647647

648648
/// Return linkage attribute for \p var.
649649
static mlir::StringAttr
650-
getLinkageAttribute(fir::FirOpBuilder &builder,
650+
getLinkageAttribute(Fortran::lower::AbstractConverter &converter,
651651
const Fortran::lower::pft::Variable &var) {
652+
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
652653
// Runtime type info for a same derived type is identical in each compilation
653654
// unit. It desired to avoid having to link against module that only define a
654655
// type. Therefore the runtime type info is generated everywhere it is needed
655-
// with `linkonce_odr` LLVM linkage.
656-
if (var.isRuntimeTypeInfoData())
656+
// with `linkonce_odr` LLVM linkage (unless the skipExternalRttiDefinition
657+
// option is set, in which case one will need to link against objects of
658+
// modules defining types). Builtin objects rtti is always generated because
659+
// the builtin module is currently not compiled or part of the runtime.
660+
if (var.isRuntimeTypeInfoData() &&
661+
(!converter.getLoweringOptions().getSkipExternalRttiDefinition() ||
662+
Fortran::semantics::IsFromBuiltinModule(var.getSymbol())))
657663
return builder.createLinkOnceODRLinkage();
658664
if (var.isModuleOrSubmoduleVariable())
659665
return {}; // external linkage
@@ -673,7 +679,7 @@ static void instantiateGlobal(Fortran::lower::AbstractConverter &converter,
673679
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
674680
std::string globalName = converter.mangleName(sym);
675681
mlir::Location loc = genLocation(converter, sym);
676-
mlir::StringAttr linkage = getLinkageAttribute(builder, var);
682+
mlir::StringAttr linkage = getLinkageAttribute(converter, var);
677683
fir::GlobalOp global;
678684
if (var.isModuleOrSubmoduleVariable()) {
679685
// A non-intrinsic module global is defined when lowering the module.
@@ -1265,7 +1271,7 @@ instantiateAggregateStore(Fortran::lower::AbstractConverter &converter,
12651271
if (var.isGlobal()) {
12661272
fir::GlobalOp global;
12671273
auto &aggregate = var.getAggregateStore();
1268-
mlir::StringAttr linkage = getLinkageAttribute(builder, var);
1274+
mlir::StringAttr linkage = getLinkageAttribute(converter, var);
12691275
if (var.isModuleOrSubmoduleVariable()) {
12701276
// A module global was or will be defined when lowering the module. Emit
12711277
// only a declaration if the global does not exist at that point.
@@ -2470,8 +2476,7 @@ void Fortran::lower::defineModuleVariable(
24702476
AbstractConverter &converter, const Fortran::lower::pft::Variable &var) {
24712477
// Use empty linkage for module variables, which makes them available
24722478
// for use in another unit.
2473-
mlir::StringAttr linkage =
2474-
getLinkageAttribute(converter.getFirOpBuilder(), var);
2479+
mlir::StringAttr linkage = getLinkageAttribute(converter, var);
24752480
if (!var.isGlobal())
24762481
fir::emitFatalError(converter.getCurrentLocation(),
24772482
"attempting to lower module variable as local");
@@ -2606,10 +2611,9 @@ void Fortran::lower::createIntrinsicModuleGlobal(
26062611
void Fortran::lower::createRuntimeTypeInfoGlobal(
26072612
Fortran::lower::AbstractConverter &converter,
26082613
const Fortran::semantics::Symbol &typeInfoSym) {
2609-
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
26102614
std::string globalName = converter.mangleName(typeInfoSym);
26112615
auto var = Fortran::lower::pft::Variable(typeInfoSym, /*global=*/true);
2612-
mlir::StringAttr linkage = getLinkageAttribute(builder, var);
2616+
mlir::StringAttr linkage = getLinkageAttribute(converter, var);
26132617
defineGlobal(converter, var, globalName, linkage);
26142618
}
26152619

flang/lib/Optimizer/CodeGen/CodeGen.cpp

Lines changed: 53 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1294,6 +1294,51 @@ genCUFAllocDescriptor(mlir::Location loc,
12941294
.getResult();
12951295
}
12961296

1297+
/// Get the address of the type descriptor global variable that was created by
1298+
/// lowering for derived type \p recType.
1299+
template <typename ModOpTy>
1300+
static mlir::Value
1301+
getTypeDescriptor(ModOpTy mod, mlir::ConversionPatternRewriter &rewriter,
1302+
mlir::Location loc, fir::RecordType recType,
1303+
const fir::FIRToLLVMPassOptions &options) {
1304+
std::string name =
1305+
options.typeDescriptorsRenamedForAssembly
1306+
? fir::NameUniquer::getTypeDescriptorAssemblyName(recType.getName())
1307+
: fir::NameUniquer::getTypeDescriptorName(recType.getName());
1308+
mlir::Type llvmPtrTy = ::getLlvmPtrType(mod.getContext());
1309+
if (auto global = mod.template lookupSymbol<fir::GlobalOp>(name))
1310+
return rewriter.create<mlir::LLVM::AddressOfOp>(loc, llvmPtrTy,
1311+
global.getSymName());
1312+
// The global may have already been translated to LLVM.
1313+
if (auto global = mod.template lookupSymbol<mlir::LLVM::GlobalOp>(name))
1314+
return rewriter.create<mlir::LLVM::AddressOfOp>(loc, llvmPtrTy,
1315+
global.getSymName());
1316+
// Type info derived types do not have type descriptors since they are the
1317+
// types defining type descriptors.
1318+
if (options.ignoreMissingTypeDescriptors ||
1319+
fir::NameUniquer::belongsToModule(
1320+
name, Fortran::semantics::typeInfoBuiltinModule))
1321+
return rewriter.create<mlir::LLVM::ZeroOp>(loc, llvmPtrTy);
1322+
1323+
if (!options.skipExternalRttiDefinition)
1324+
fir::emitFatalError(loc,
1325+
"runtime derived type info descriptor was not "
1326+
"generated and skipExternalRttiDefinition and "
1327+
"ignoreMissingTypeDescriptors options are not set");
1328+
1329+
// Rtti for a derived type defined in another compilation unit and for which
1330+
// rtti was not defined in lowering because of the skipExternalRttiDefinition
1331+
// option. Generate the object declaration now.
1332+
auto insertPt = rewriter.saveInsertionPoint();
1333+
rewriter.setInsertionPoint(mod.getBody(), mod.getBody()->end());
1334+
mlir::LLVM::GlobalOp global = rewriter.create<mlir::LLVM::GlobalOp>(
1335+
loc, llvmPtrTy, /*constant=*/true, mlir::LLVM::Linkage::External, name,
1336+
mlir::Attribute());
1337+
rewriter.restoreInsertionPoint(insertPt);
1338+
return rewriter.create<mlir::LLVM::AddressOfOp>(loc, llvmPtrTy,
1339+
global.getSymName());
1340+
}
1341+
12971342
/// Common base class for embox to descriptor conversion.
12981343
template <typename OP>
12991344
struct EmboxCommonConversion : public fir::FIROpConversion<OP> {
@@ -1406,36 +1451,6 @@ struct EmboxCommonConversion : public fir::FIROpConversion<OP> {
14061451
stride);
14071452
}
14081453

1409-
/// Get the address of the type descriptor global variable that was created by
1410-
/// lowering for derived type \p recType.
1411-
template <typename ModOpTy>
1412-
mlir::Value
1413-
getTypeDescriptor(ModOpTy mod, mlir::ConversionPatternRewriter &rewriter,
1414-
mlir::Location loc, fir::RecordType recType) const {
1415-
std::string name =
1416-
this->options.typeDescriptorsRenamedForAssembly
1417-
? fir::NameUniquer::getTypeDescriptorAssemblyName(recType.getName())
1418-
: fir::NameUniquer::getTypeDescriptorName(recType.getName());
1419-
mlir::Type llvmPtrTy = ::getLlvmPtrType(mod.getContext());
1420-
if (auto global = mod.template lookupSymbol<fir::GlobalOp>(name)) {
1421-
return rewriter.create<mlir::LLVM::AddressOfOp>(loc, llvmPtrTy,
1422-
global.getSymName());
1423-
}
1424-
if (auto global = mod.template lookupSymbol<mlir::LLVM::GlobalOp>(name)) {
1425-
// The global may have already been translated to LLVM.
1426-
return rewriter.create<mlir::LLVM::AddressOfOp>(loc, llvmPtrTy,
1427-
global.getSymName());
1428-
}
1429-
// Type info derived types do not have type descriptors since they are the
1430-
// types defining type descriptors.
1431-
if (!this->options.ignoreMissingTypeDescriptors &&
1432-
!fir::NameUniquer::belongsToModule(
1433-
name, Fortran::semantics::typeInfoBuiltinModule))
1434-
fir::emitFatalError(
1435-
loc, "runtime derived type info descriptor was not generated");
1436-
return rewriter.create<mlir::LLVM::ZeroOp>(loc, llvmPtrTy);
1437-
}
1438-
14391454
template <typename ModOpTy>
14401455
mlir::Value populateDescriptor(mlir::Location loc, ModOpTy mod,
14411456
fir::BaseBoxType boxTy, mlir::Type inputType,
@@ -1500,16 +1515,17 @@ struct EmboxCommonConversion : public fir::FIROpConversion<OP> {
15001515
mlir::Type innerType = fir::unwrapInnerType(inputType);
15011516
if (innerType && mlir::isa<fir::RecordType>(innerType)) {
15021517
auto recTy = mlir::dyn_cast<fir::RecordType>(innerType);
1503-
typeDesc = getTypeDescriptor(mod, rewriter, loc, recTy);
1518+
typeDesc =
1519+
getTypeDescriptor(mod, rewriter, loc, recTy, this->options);
15041520
} else {
15051521
// Unlimited polymorphic type descriptor with no record type. Set
15061522
// type descriptor address to a clean state.
15071523
typeDesc = rewriter.create<mlir::LLVM::ZeroOp>(
15081524
loc, ::getLlvmPtrType(mod.getContext()));
15091525
}
15101526
} else {
1511-
typeDesc = getTypeDescriptor(mod, rewriter, loc,
1512-
fir::unwrapIfDerived(boxTy));
1527+
typeDesc = getTypeDescriptor(
1528+
mod, rewriter, loc, fir::unwrapIfDerived(boxTy), this->options);
15131529
}
15141530
}
15151531
if (typeDesc)
@@ -3021,22 +3037,10 @@ struct TypeDescOpConversion : public fir::FIROpConversion<fir::TypeDescOp> {
30213037
assert(mlir::isa<fir::RecordType>(inTy) && "expecting fir.type");
30223038
auto recordType = mlir::dyn_cast<fir::RecordType>(inTy);
30233039
auto module = typeDescOp.getOperation()->getParentOfType<mlir::ModuleOp>();
3024-
std::string typeDescName =
3025-
this->options.typeDescriptorsRenamedForAssembly
3026-
? fir::NameUniquer::getTypeDescriptorAssemblyName(
3027-
recordType.getName())
3028-
: fir::NameUniquer::getTypeDescriptorName(recordType.getName());
3029-
auto llvmPtrTy = ::getLlvmPtrType(typeDescOp.getContext());
3030-
if (auto global = module.lookupSymbol<mlir::LLVM::GlobalOp>(typeDescName)) {
3031-
rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>(
3032-
typeDescOp, llvmPtrTy, global.getSymName());
3033-
return mlir::success();
3034-
} else if (auto global = module.lookupSymbol<fir::GlobalOp>(typeDescName)) {
3035-
rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>(
3036-
typeDescOp, llvmPtrTy, global.getSymName());
3037-
return mlir::success();
3038-
}
3039-
return mlir::failure();
3040+
mlir::Value typeDesc = getTypeDescriptor(
3041+
module, rewriter, typeDescOp.getLoc(), recordType, this->options);
3042+
rewriter.replaceOp(typeDescOp, typeDesc);
3043+
return mlir::success();
30403044
}
30413045
};
30423046

flang/lib/Optimizer/Passes/CommandLineOpts.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ cl::opt<bool> ignoreMissingTypeDescriptors(
3939
"translating FIR to LLVM"),
4040
cl::init(false), cl::Hidden);
4141

42+
cl::opt<bool> skipExternalRttiDefinition(
43+
"skip-external-rtti-definition", llvm::cl::init(false),
44+
llvm::cl::desc("do not define rtti static objects for types belonging to "
45+
"other compilation units"),
46+
cl::Hidden);
47+
4248
OptimizationLevel defaultOptLevel{OptimizationLevel::O0};
4349

4450
codegenoptions::DebugInfoKind noDebugInfo{codegenoptions::NoDebugInfo};

flang/lib/Optimizer/Passes/Pipelines.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ void addFIRToLLVMPass(mlir::PassManager &pm,
108108
const MLIRToLLVMPassPipelineConfig &config) {
109109
fir::FIRToLLVMPassOptions options;
110110
options.ignoreMissingTypeDescriptors = ignoreMissingTypeDescriptors;
111+
options.skipExternalRttiDefinition = skipExternalRttiDefinition;
111112
options.applyTBAA = config.AliasAnalysis;
112113
options.forceUnifiedTBAATree = useOldAliasTags;
113114
options.typeDescriptorsRenamedForAssembly =

0 commit comments

Comments
 (0)