Skip to content

Commit b1703ad

Browse files
maarquitos14marcos.maronas
andauthored
[SPIRV] Change how to detect OpenCL/Vulkan Env and update tests accordingly. (#129689)
A new test added for spirv-friendly builtins for SPV_KHR_bit_instructions unveiled that current mechanism to detect whether SPIRV Backend is in OpenCL environment or Vulkan environment was not good enough. This PR updates how to detect the environment and all the tests accordingly. *UPDATE*: the new approach is having a new member in `SPIRVSubtarget` to represent the environment. It can be either OpenCL, Kernel or Unknown. If the triple is explicit, we can directly set it at the creation of the `SPIRVSubtarget`, otherwise we just leave it unknown until we find other information that can help us set the environment. For now, the only other information we use to set the environment is `hlsl.shader` attribute at `SPIRV::ExecutionModel::ExecutionModel getExecutionModel(const SPIRVSubtarget &STI, const Function &F)`. Going forward we should consider also specific instructions that are Kernel-exclusive or Shader-exclusive. --------- Co-authored-by: marcos.maronas <[email protected]>
1 parent 6716d4e commit b1703ad

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+241
-190
lines changed

llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ void SPIRVAsmPrinter::outputExecutionMode(const Module &M) {
532532
Inst.addOperand(MCOperand::createImm(TypeCode));
533533
outputMCInst(Inst);
534534
}
535-
if (ST->isOpenCLEnv() && !M.getNamedMetadata("spirv.ExecutionMode") &&
535+
if (ST->isKernel() && !M.getNamedMetadata("spirv.ExecutionMode") &&
536536
!M.getNamedMetadata("opencl.enable.FP_CONTRACT")) {
537537
MCInst Inst;
538538
Inst.setOpcode(SPIRV::OpExecutionMode);

llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -267,14 +267,37 @@ static SPIRVType *getArgSPIRVType(const Function &F, unsigned ArgIdx,
267267

268268
static SPIRV::ExecutionModel::ExecutionModel
269269
getExecutionModel(const SPIRVSubtarget &STI, const Function &F) {
270-
if (STI.isOpenCLEnv())
270+
if (STI.isKernel())
271271
return SPIRV::ExecutionModel::Kernel;
272272

273+
if (STI.isShader()) {
274+
auto attribute = F.getFnAttribute("hlsl.shader");
275+
if (!attribute.isValid()) {
276+
report_fatal_error(
277+
"This entry point lacks mandatory hlsl.shader attribute.");
278+
}
279+
280+
const auto value = attribute.getValueAsString();
281+
if (value == "compute")
282+
return SPIRV::ExecutionModel::GLCompute;
283+
284+
report_fatal_error(
285+
"This HLSL entry point is not supported by this backend.");
286+
}
287+
288+
assert(STI.getEnv() == SPIRVSubtarget::Unknown);
289+
// "hlsl.shader" attribute is mandatory for Vulkan, so we can set Env to
290+
// Shader whenever we find it, and to Kernel otherwise.
291+
292+
// We will now change the Env based on the attribute, so we need to strip
293+
// `const` out of the ref to STI.
294+
SPIRVSubtarget *NonConstSTI = const_cast<SPIRVSubtarget *>(&STI);
273295
auto attribute = F.getFnAttribute("hlsl.shader");
274296
if (!attribute.isValid()) {
275-
report_fatal_error(
276-
"This entry point lacks mandatory hlsl.shader attribute.");
297+
NonConstSTI->setEnv(SPIRVSubtarget::Kernel);
298+
return SPIRV::ExecutionModel::Kernel;
277299
}
300+
NonConstSTI->setEnv(SPIRVSubtarget::Shader);
278301

279302
const auto value = attribute.getValueAsString();
280303
if (value == "compute")
@@ -319,7 +342,7 @@ bool SPIRVCallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder,
319342
buildOpDecorate(VRegs[i][0], MIRBuilder,
320343
SPIRV::Decoration::MaxByteOffset, {DerefBytes});
321344
}
322-
if (Arg.hasAttribute(Attribute::Alignment) && !ST->isVulkanEnv()) {
345+
if (Arg.hasAttribute(Attribute::Alignment) && !ST->isShader()) {
323346
auto Alignment = static_cast<unsigned>(
324347
Arg.getAttribute(Attribute::Alignment).getValueAsInt());
325348
buildOpDecorate(VRegs[i][0], MIRBuilder, SPIRV::Decoration::Alignment,
@@ -439,6 +462,11 @@ bool SPIRVCallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder,
439462

440463
// Handle entry points and function linkage.
441464
if (isEntryPoint(F)) {
465+
// EntryPoints can help us to determine the environment we're working on.
466+
// Therefore, we need a non-const pointer to SPIRVSubtarget to update the
467+
// environment if we need to.
468+
const SPIRVSubtarget *ST =
469+
static_cast<const SPIRVSubtarget *>(&MIRBuilder.getMF().getSubtarget());
442470
auto MIB = MIRBuilder.buildInstr(SPIRV::OpEntryPoint)
443471
.addImm(static_cast<uint32_t>(getExecutionModel(*ST, F)))
444472
.addUse(FuncVReg);

llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -772,10 +772,10 @@ Register SPIRVGlobalRegistry::buildGlobalVariable(
772772
// TODO: maybe move to GenerateDecorations pass.
773773
const SPIRVSubtarget &ST =
774774
cast<SPIRVSubtarget>(MIRBuilder.getMF().getSubtarget());
775-
if (IsConst && ST.isOpenCLEnv())
775+
if (IsConst && !ST.isShader())
776776
buildOpDecorate(Reg, MIRBuilder, SPIRV::Decoration::Constant, {});
777777

778-
if (GVar && GVar->getAlign().valueOrOne().value() != 1 && !ST.isVulkanEnv()) {
778+
if (GVar && GVar->getAlign().valueOrOne().value() != 1 && !ST.isShader()) {
779779
unsigned Alignment = (unsigned)GVar->getAlign().valueOrOne().value();
780780
buildOpDecorate(Reg, MIRBuilder, SPIRV::Decoration::Alignment, {Alignment});
781781
}
@@ -988,7 +988,7 @@ SPIRVType *SPIRVGlobalRegistry::getOpTypeStruct(
988988
Register ResVReg = createTypeVReg(MIRBuilder);
989989
if (Ty->hasName())
990990
buildOpName(ResVReg, Ty->getName(), MIRBuilder);
991-
if (Ty->isPacked() && !ST.isVulkanEnv())
991+
if (Ty->isPacked() && !ST.isShader())
992992
buildOpDecorate(ResVReg, MIRBuilder, SPIRV::Decoration::CPacked, {});
993993

994994
SPIRVType *SPVType =

llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -860,7 +860,7 @@ bool SPIRVInstructionSelector::spvSelect(Register ResVReg,
860860
.addUse(GV);
861861
return MIB.constrainAllUses(TII, TRI, RBI) &&
862862
BuildMI(BB, I, I.getDebugLoc(),
863-
TII.get(STI.isVulkanEnv()
863+
TII.get(STI.isLogicalSPIRV()
864864
? SPIRV::OpInBoundsAccessChain
865865
: SPIRV::OpInBoundsPtrAccessChain))
866866
.addDef(ResVReg)
@@ -1034,7 +1034,7 @@ bool SPIRVInstructionSelector::selectUnOp(Register ResVReg,
10341034
const SPIRVType *ResType,
10351035
MachineInstr &I,
10361036
unsigned Opcode) const {
1037-
if (STI.isOpenCLEnv() && I.getOperand(1).isReg()) {
1037+
if (STI.isPhysicalSPIRV() && I.getOperand(1).isReg()) {
10381038
Register SrcReg = I.getOperand(1).getReg();
10391039
bool IsGV = false;
10401040
for (MachineRegisterInfo::def_instr_iterator DefIt =
@@ -2062,7 +2062,7 @@ bool SPIRVInstructionSelector::selectDot4AddPackedExpansion(
20622062
auto ExtractOp =
20632063
Signed ? SPIRV::OpBitFieldSExtract : SPIRV::OpBitFieldUExtract;
20642064

2065-
bool ZeroAsNull = STI.isOpenCLEnv();
2065+
bool ZeroAsNull = !STI.isShader();
20662066
// Extract the i8 element, multiply and add it to the accumulator
20672067
for (unsigned i = 0; i < 4; i++) {
20682068
// A[i]
@@ -2202,7 +2202,7 @@ bool SPIRVInstructionSelector::selectWaveOpInst(Register ResVReg,
22022202
.addDef(ResVReg)
22032203
.addUse(GR.getSPIRVTypeID(ResType))
22042204
.addUse(GR.getOrCreateConstInt(SPIRV::Scope::Subgroup, I,
2205-
IntTy, TII, STI.isOpenCLEnv()));
2205+
IntTy, TII, !STI.isShader()));
22062206

22072207
for (unsigned J = 2; J < I.getNumOperands(); J++) {
22082208
BMI.addUse(I.getOperand(J).getReg());
@@ -2226,7 +2226,7 @@ bool SPIRVInstructionSelector::selectWaveActiveCountBits(
22262226
.addDef(ResVReg)
22272227
.addUse(GR.getSPIRVTypeID(ResType))
22282228
.addUse(GR.getOrCreateConstInt(SPIRV::Scope::Subgroup, I, IntTy,
2229-
TII, STI.isOpenCLEnv()))
2229+
TII, !STI.isShader()))
22302230
.addImm(SPIRV::GroupOperation::Reduce)
22312231
.addUse(BallotReg)
22322232
.constrainAllUses(TII, TRI, RBI);
@@ -2257,7 +2257,7 @@ bool SPIRVInstructionSelector::selectWaveReduceMax(Register ResVReg,
22572257
.addDef(ResVReg)
22582258
.addUse(GR.getSPIRVTypeID(ResType))
22592259
.addUse(GR.getOrCreateConstInt(SPIRV::Scope::Subgroup, I, IntTy, TII,
2260-
STI.isOpenCLEnv()))
2260+
!STI.isShader()))
22612261
.addImm(SPIRV::GroupOperation::Reduce)
22622262
.addUse(I.getOperand(2).getReg())
22632263
.constrainAllUses(TII, TRI, RBI);
@@ -2284,7 +2284,7 @@ bool SPIRVInstructionSelector::selectWaveReduceSum(Register ResVReg,
22842284
.addDef(ResVReg)
22852285
.addUse(GR.getSPIRVTypeID(ResType))
22862286
.addUse(GR.getOrCreateConstInt(SPIRV::Scope::Subgroup, I, IntTy, TII,
2287-
STI.isOpenCLEnv()))
2287+
!STI.isShader()))
22882288
.addImm(SPIRV::GroupOperation::Reduce)
22892289
.addUse(I.getOperand(2).getReg());
22902290
}
@@ -2506,7 +2506,7 @@ bool SPIRVInstructionSelector::selectFCmp(Register ResVReg,
25062506
Register SPIRVInstructionSelector::buildZerosVal(const SPIRVType *ResType,
25072507
MachineInstr &I) const {
25082508
// OpenCL uses nulls for Zero. In HLSL we don't use null constants.
2509-
bool ZeroAsNull = STI.isOpenCLEnv();
2509+
bool ZeroAsNull = !STI.isShader();
25102510
if (ResType->getOpcode() == SPIRV::OpTypeVector)
25112511
return GR.getOrCreateConstVector(0UL, I, ResType, TII, ZeroAsNull);
25122512
return GR.getOrCreateConstInt(0, I, ResType, TII, ZeroAsNull);
@@ -2515,7 +2515,7 @@ Register SPIRVInstructionSelector::buildZerosVal(const SPIRVType *ResType,
25152515
Register SPIRVInstructionSelector::buildZerosValF(const SPIRVType *ResType,
25162516
MachineInstr &I) const {
25172517
// OpenCL uses nulls for Zero. In HLSL we don't use null constants.
2518-
bool ZeroAsNull = STI.isOpenCLEnv();
2518+
bool ZeroAsNull = !STI.isShader();
25192519
APFloat VZero = getZeroFP(GR.getTypeForSPIRVType(ResType));
25202520
if (ResType->getOpcode() == SPIRV::OpTypeVector)
25212521
return GR.getOrCreateConstVector(VZero, I, ResType, TII, ZeroAsNull);
@@ -2525,7 +2525,7 @@ Register SPIRVInstructionSelector::buildZerosValF(const SPIRVType *ResType,
25252525
Register SPIRVInstructionSelector::buildOnesValF(const SPIRVType *ResType,
25262526
MachineInstr &I) const {
25272527
// OpenCL uses nulls for Zero. In HLSL we don't use null constants.
2528-
bool ZeroAsNull = STI.isOpenCLEnv();
2528+
bool ZeroAsNull = !STI.isShader();
25292529
APFloat VOne = getOneFP(GR.getTypeForSPIRVType(ResType));
25302530
if (ResType->getOpcode() == SPIRV::OpTypeVector)
25312531
return GR.getOrCreateConstVector(VOne, I, ResType, TII, ZeroAsNull);
@@ -2713,10 +2713,10 @@ bool SPIRVInstructionSelector::selectConst(Register ResVReg,
27132713
Reg = GR.getOrCreateConstNullPtr(MIRBuilder, ResType);
27142714
} else if (Opcode == TargetOpcode::G_FCONSTANT) {
27152715
Reg = GR.getOrCreateConstFP(I.getOperand(1).getFPImm()->getValue(), I,
2716-
ResType, TII, STI.isOpenCLEnv());
2716+
ResType, TII, !STI.isShader());
27172717
} else {
27182718
Reg = GR.getOrCreateConstInt(I.getOperand(1).getCImm()->getZExtValue(), I,
2719-
ResType, TII, STI.isOpenCLEnv());
2719+
ResType, TII, !STI.isShader());
27202720
}
27212721
return Reg == ResVReg ? true : BuildCOPY(ResVReg, Reg, I);
27222722
}
@@ -2796,7 +2796,7 @@ bool SPIRVInstructionSelector::selectGEP(Register ResVReg,
27962796
// OpAccessChain could be used for OpenCL, but the SPIRV-LLVM Translator only
27972797
// relies on PtrAccessChain, so we'll try not to deviate. For Vulkan however,
27982798
// we have to use Op[InBounds]AccessChain.
2799-
const unsigned Opcode = STI.isVulkanEnv()
2799+
const unsigned Opcode = STI.isLogicalSPIRV()
28002800
? (IsGEPInBounds ? SPIRV::OpInBoundsAccessChain
28012801
: SPIRV::OpAccessChain)
28022802
: (IsGEPInBounds ? SPIRV::OpInBoundsPtrAccessChain
@@ -3493,7 +3493,7 @@ bool SPIRVInstructionSelector::selectFirstBitSet64Overflow(
34933493

34943494
// On odd component counts we need to handle one more component
34953495
if (CurrentComponent != ComponentCount) {
3496-
bool ZeroAsNull = STI.isOpenCLEnv();
3496+
bool ZeroAsNull = !STI.isShader();
34973497
Register FinalElemReg = MRI->createVirtualRegister(GR.getRegClass(I64Type));
34983498
Register ConstIntLastIdx = GR.getOrCreateConstInt(
34993499
ComponentCount - 1, I, BaseType, TII, ZeroAsNull);
@@ -3523,7 +3523,7 @@ bool SPIRVInstructionSelector::selectFirstBitSet64(
35233523
Register SrcReg, unsigned BitSetOpcode, bool SwapPrimarySide) const {
35243524
unsigned ComponentCount = GR.getScalarOrVectorComponentCount(ResType);
35253525
SPIRVType *BaseType = GR.retrieveScalarOrVectorIntType(ResType);
3526-
bool ZeroAsNull = STI.isOpenCLEnv();
3526+
bool ZeroAsNull = !STI.isShader();
35273527
Register ConstIntZero =
35283528
GR.getOrCreateConstInt(0, I, BaseType, TII, ZeroAsNull);
35293529
Register ConstIntOne =
@@ -3725,7 +3725,7 @@ bool SPIRVInstructionSelector::selectAllocaArray(Register ResVReg,
37253725
.addUse(GR.getSPIRVTypeID(ResType))
37263726
.addUse(I.getOperand(2).getReg())
37273727
.constrainAllUses(TII, TRI, RBI);
3728-
if (!STI.isVulkanEnv()) {
3728+
if (!STI.isShader()) {
37293729
unsigned Alignment = I.getOperand(3).getImm();
37303730
buildOpDecorate(ResVReg, I, TII, SPIRV::Decoration::Alignment, {Alignment});
37313731
}
@@ -3744,7 +3744,7 @@ bool SPIRVInstructionSelector::selectFrameIndex(Register ResVReg,
37443744
.addUse(GR.getSPIRVTypeID(ResType))
37453745
.addImm(static_cast<uint32_t>(SPIRV::StorageClass::Function))
37463746
.constrainAllUses(TII, TRI, RBI);
3747-
if (!STI.isVulkanEnv()) {
3747+
if (!STI.isShader()) {
37483748
unsigned Alignment = I.getOperand(2).getImm();
37493749
buildOpDecorate(ResVReg, *It, TII, SPIRV::Decoration::Alignment,
37503750
{Alignment});

llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ getSymbolicOperandRequirements(SPIRV::OperandCategory::OperandCategory Category,
6868
SPIRV::RequirementHandler &Reqs) {
6969
// A set of capabilities to avoid if there is another option.
7070
AvoidCapabilitiesSet AvoidCaps;
71-
if (ST.isOpenCLEnv())
71+
if (!ST.isShader())
7272
AvoidCaps.S.insert(SPIRV::Capability::Shader);
7373

7474
VersionTuple ReqMinVer = getSymbolicOperandMinVersion(Category, i);
@@ -144,8 +144,8 @@ void SPIRVModuleAnalysis::setBaseInfo(const Module &M) {
144144
static_cast<SPIRV::MemoryModel::MemoryModel>(getMetadataUInt(MemMD, 1));
145145
} else {
146146
// TODO: Add support for VulkanMemoryModel.
147-
MAI.Mem = ST->isOpenCLEnv() ? SPIRV::MemoryModel::OpenCL
148-
: SPIRV::MemoryModel::GLSL450;
147+
MAI.Mem = ST->isShader() ? SPIRV::MemoryModel::GLSL450
148+
: SPIRV::MemoryModel::OpenCL;
149149
if (MAI.Mem == SPIRV::MemoryModel::OpenCL) {
150150
unsigned PtrSize = ST->getPointerSize();
151151
MAI.Addr = PtrSize == 32 ? SPIRV::AddressingModel::Physical32
@@ -175,7 +175,7 @@ void SPIRVModuleAnalysis::setBaseInfo(const Module &M) {
175175
// OpenCL 1.0 by default for the OpenCL environment to avoid puzzling
176176
// run-times with Unknown/0.0 version output. For a reference, LLVM-SPIRV
177177
// Translator avoids potential issues with run-times in a similar manner.
178-
if (ST->isOpenCLEnv()) {
178+
if (!ST->isShader()) {
179179
MAI.SrcLang = SPIRV::SourceLanguage::OpenCL_CPP;
180180
MAI.SrcLangVersion = 100000;
181181
} else {
@@ -203,7 +203,7 @@ void SPIRVModuleAnalysis::setBaseInfo(const Module &M) {
203203
MAI.Reqs.getAndAddRequirements(SPIRV::OperandCategory::AddressingModelOperand,
204204
MAI.Addr, *ST);
205205

206-
if (ST->isOpenCLEnv()) {
206+
if (!ST->isShader()) {
207207
// TODO: check if it's required by default.
208208
MAI.ExtInstSetMap[static_cast<unsigned>(
209209
SPIRV::InstructionSet::OpenCL_std)] = MAI.getNextIDRegister();
@@ -804,12 +804,12 @@ void RequirementHandler::initAvailableCapabilities(const SPIRVSubtarget &ST) {
804804
addAvailableCaps(EnabledCapabilities);
805805
}
806806

807-
if (ST.isOpenCLEnv()) {
807+
if (!ST.isShader()) {
808808
initAvailableCapabilitiesForOpenCL(ST);
809809
return;
810810
}
811811

812-
if (ST.isVulkanEnv()) {
812+
if (ST.isShader()) {
813813
initAvailableCapabilitiesForVulkan(ST);
814814
return;
815815
}
@@ -969,7 +969,7 @@ static void addOpTypeImageReqs(const MachineInstr &MI,
969969
}
970970

971971
// Has optional access qualifier.
972-
if (ST.isOpenCLEnv()) {
972+
if (!ST.isShader()) {
973973
if (MI.getNumOperands() > 8 &&
974974
MI.getOperand(8).getImm() == SPIRV::AccessQualifier::ReadWrite)
975975
Reqs.addRequirements(SPIRV::Capability::ImageReadWrite);
@@ -1267,7 +1267,7 @@ void addInstrRequirements(const MachineInstr &MI,
12671267
ST);
12681268
// If it's a type of pointer to float16 targeting OpenCL, add Float16Buffer
12691269
// capability.
1270-
if (!ST.isOpenCLEnv())
1270+
if (ST.isShader())
12711271
break;
12721272
assert(MI.getOperand(2).isReg());
12731273
const MachineRegisterInfo &MRI = MI.getMF()->getRegInfo();
@@ -1342,7 +1342,7 @@ void addInstrRequirements(const MachineInstr &MI,
13421342
addOpTypeImageReqs(MI, Reqs, ST);
13431343
break;
13441344
case SPIRV::OpTypeSampler:
1345-
if (!ST.isVulkanEnv()) {
1345+
if (!ST.isShader()) {
13461346
Reqs.addCapability(SPIRV::Capability::ImageBasic);
13471347
}
13481348
break;
@@ -1793,7 +1793,8 @@ void addInstrRequirements(const MachineInstr &MI,
17931793
// not allowed to produce
17941794
// StorageImageReadWithoutFormat/StorageImageWriteWithoutFormat, see
17951795
// https://github.com/KhronosGroup/SPIRV-Headers/issues/487
1796-
if (isImageTypeWithUnknownFormat(TypeDef) && !ST.isOpenCLEnv())
1796+
1797+
if (isImageTypeWithUnknownFormat(TypeDef) && ST.isShader())
17971798
Reqs.addCapability(SPIRV::Capability::StorageImageReadWithoutFormat);
17981799
break;
17991800
}
@@ -1806,7 +1807,8 @@ void addInstrRequirements(const MachineInstr &MI,
18061807
// not allowed to produce
18071808
// StorageImageReadWithoutFormat/StorageImageWriteWithoutFormat, see
18081809
// https://github.com/KhronosGroup/SPIRV-Headers/issues/487
1809-
if (isImageTypeWithUnknownFormat(TypeDef) && !ST.isOpenCLEnv())
1810+
1811+
if (isImageTypeWithUnknownFormat(TypeDef) && ST.isShader())
18101812
Reqs.addCapability(SPIRV::Capability::StorageImageWriteWithoutFormat);
18111813
break;
18121814
}

llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -405,13 +405,13 @@ bool SPIRVPrepareFunctions::substituteIntrinsicCalls(Function *F) {
405405
Changed = true;
406406
break;
407407
case Intrinsic::lifetime_start:
408-
if (STI.isOpenCLEnv()) {
408+
if (!STI.isShader()) {
409409
Changed |= toSpvOverloadedIntrinsic(
410410
II, Intrinsic::SPVIntrinsics::spv_lifetime_start, {1});
411411
}
412412
break;
413413
case Intrinsic::lifetime_end:
414-
if (STI.isOpenCLEnv()) {
414+
if (!STI.isShader()) {
415415
Changed |= toSpvOverloadedIntrinsic(
416416
II, Intrinsic::SPVIntrinsics::spv_lifetime_end, {1});
417417
}

0 commit comments

Comments
 (0)