@@ -321,6 +321,104 @@ void SPIRVRegularizeLLVMBase::expandSYCLTypeUsing(Module *M) {
321
321
expandVIDWithSYCLTypeByValComp (F);
322
322
}
323
323
324
+ // In this function, we handle two conversion operations
325
+ // 1. fptoui.sat.iX.fY (X is not 8,16,32,64; Y is 32 or 64)
326
+ // 2. fptosi.sat.iX.fY (X is not 8,16,32,64; Y is 32 or 64)
327
+ // Such non-standard integer types cannot be handled in SPIR-V. Hence, they
328
+ // will be promoted to
329
+ // 1. fptoui.sat.i64.fY (Y is 32 or 64)
330
+ // 2. fptosi.sat.i64.fY (Y is 32 or 64)
331
+ // However, LLVM documentation requires the following rules to be obeyed.
332
+ // Rule 1: If the argument is any NaN, zero is returned.
333
+ // Rule 2: If the argument is smaller than the smallest representable
334
+ // (un)signed integer of the result type, the smallest representable
335
+ // (un)signed integer is returned.
336
+ // Rule 3: If the argument is larger than the largest representable (un)signed
337
+ // integer of the result type, the largest representable (un)signed integer is
338
+ // returned.
339
+ // Rule 4: Otherwise, the result of rounding the argument towards zero is
340
+ // returned.
341
+ // Rules 1 & 4 are preserved when promoting iX to i64. For preserving Rule 2
342
+ // and Rule 3, we saturate the result of the promoted instruction based on
343
+ // original integer type (iX)
344
+ // Example:
345
+ // Input:
346
+ // %0 = call i2 @llvm.fptosi.sat.i2.f32(float %input)
347
+ // %1 = sext i32 %0
348
+ // Output:
349
+ // %0 = call i32 @_Z17convert_long_satf(float %input)
350
+ // %1 = icmp sge i32 %0, 1 <Largest 2-bit signed integer>
351
+ // %2 = icmp sle i32 %0, -2 <Smallest 2-bit signed integer>
352
+ // %3 = select i1 %1, i32 1, i32 %0
353
+ // %4 = select i1 %2, i32 -2, i32 %3
354
+ // Replace uses of %1 in Input with %4 in Output
355
+ void SPIRVRegularizeLLVMBase::cleanupConversionToNonStdIntegers (Module *M) {
356
+ for (auto FI = M->begin (), FE = M->end (); FI != FE;) {
357
+ Function *F = &(*FI++);
358
+ std::vector<Instruction *> ToErase;
359
+ auto IID = F->getIntrinsicID ();
360
+ if (IID != Intrinsic::fptosi_sat && IID != Intrinsic::fptoui_sat)
361
+ continue ;
362
+ for (auto *I : F->users ()) {
363
+ if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(I)) {
364
+ // TODO: Vector type not supported yet.
365
+ if (isa<VectorType>(II->getType ()))
366
+ continue ;
367
+ auto IID = II->getIntrinsicID ();
368
+ auto IntBitWidth = II->getType ()->getScalarSizeInBits ();
369
+ if (IntBitWidth == 8 || IntBitWidth == 16 || IntBitWidth == 32 ||
370
+ IntBitWidth == 64 )
371
+ continue ;
372
+ if (IID == Intrinsic::fptosi_sat) {
373
+ // Identify sext (user of II). Make sure that's the only use of II.
374
+ auto *User = II->getUniqueUndroppableUser ();
375
+ if (!User || !isa<SExtInst>(User))
376
+ continue ;
377
+ auto *SExtI = dyn_cast<SExtInst>(User);
378
+ auto *NewIType = SExtI->getType ();
379
+ IRBuilder<> IRB (II);
380
+ auto *NewII = IRB.CreateIntrinsic (
381
+ IID, {NewIType, II->getOperand (0 )->getType ()}, II->getOperand (0 ));
382
+ Constant *MaxVal = ConstantInt::get (
383
+ NewIType, APInt::getSignedMaxValue (IntBitWidth).getSExtValue ());
384
+ Constant *MinVal = ConstantInt::get (
385
+ NewIType, APInt::getSignedMinValue (IntBitWidth).getSExtValue ());
386
+ auto *GTMax = IRB.CreateICmp (CmpInst::ICMP_SGE, NewII, MaxVal);
387
+ auto *LTMin = IRB.CreateICmp (CmpInst::ICMP_SLE, NewII, MinVal);
388
+ auto *SatMax = IRB.CreateSelect (GTMax, MaxVal, NewII);
389
+ auto *SatMin = IRB.CreateSelect (LTMin, MinVal, SatMax);
390
+ SExtI->replaceAllUsesWith (SatMin);
391
+ ToErase.push_back (SExtI);
392
+ ToErase.push_back (II);
393
+ }
394
+ if (IID == Intrinsic::fptoui_sat) {
395
+ // Identify zext (user of II). Make sure that's the only use of II.
396
+ auto *User = II->getUniqueUndroppableUser ();
397
+ if (!User || !isa<ZExtInst>(User))
398
+ continue ;
399
+ auto *ZExtI = dyn_cast<ZExtInst>(User);
400
+ auto *NewIType = ZExtI->getType ();
401
+ IRBuilder<> IRB (II);
402
+ auto *NewII = IRB.CreateIntrinsic (
403
+ IID, {NewIType, II->getOperand (0 )->getType ()}, II->getOperand (0 ));
404
+ Constant *MaxVal = ConstantInt::get (
405
+ NewIType, APInt::getMaxValue (IntBitWidth).getZExtValue ());
406
+ auto *GTMax = IRB.CreateICmp (CmpInst::ICMP_UGE, NewII, MaxVal);
407
+ auto *SatMax = IRB.CreateSelect (GTMax, MaxVal, NewII);
408
+ ZExtI->replaceAllUsesWith (SatMax);
409
+ ToErase.push_back (ZExtI);
410
+ ToErase.push_back (II);
411
+ }
412
+ }
413
+ }
414
+ for (Instruction *V : ToErase) {
415
+ assert (V->user_empty ());
416
+ V->dropAllReferences ();
417
+ V->eraseFromParent ();
418
+ }
419
+ }
420
+ }
421
+
324
422
bool SPIRVRegularizeLLVMBase::runRegularizeLLVM (Module &Module) {
325
423
M = &Module;
326
424
Ctx = &M->getContext ();
@@ -464,6 +562,7 @@ void regularizeWithOverflowInstrinsics(StringRef MangledName, CallInst *Call,
464
562
bool SPIRVRegularizeLLVMBase::regularize () {
465
563
eraseUselessFunctions (M);
466
564
expandSYCLTypeUsing (M);
565
+ cleanupConversionToNonStdIntegers (M);
467
566
468
567
for (auto &GV : M->globals ()) {
469
568
SPIRVBuiltinVariableKind Kind;
0 commit comments