Skip to content

Commit 763997c

Browse files
committed
Ensure that TernaryLogic picks the best operand for containment
1 parent 2c93a18 commit 763997c

File tree

4 files changed

+836
-153
lines changed

4 files changed

+836
-153
lines changed

src/coreclr/jit/gentree.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29666,7 +29666,8 @@ bool GenTreeLclVar::IsNeverNegative(Compiler* comp) const
2966629666
unsigned GenTreeHWIntrinsic::GetResultOpNumForRmwIntrinsic(GenTree* use, GenTree* op1, GenTree* op2, GenTree* op3)
2966729667
{
2966829668
#if defined(TARGET_XARCH)
29669-
assert(HWIntrinsicInfo::IsFmaIntrinsic(gtHWIntrinsicId) || HWIntrinsicInfo::IsPermuteVar2x(gtHWIntrinsicId));
29669+
assert(HWIntrinsicInfo::IsFmaIntrinsic(gtHWIntrinsicId) || HWIntrinsicInfo::IsPermuteVar2x(gtHWIntrinsicId) ||
29670+
HWIntrinsicInfo::IsTernaryLogic(gtHWIntrinsicId));
2967029671
#elif defined(TARGET_ARM64)
2967129672
assert(HWIntrinsicInfo::IsFmaIntrinsic(gtHWIntrinsicId));
2967229673
#endif

src/coreclr/jit/hwintrinsic.cpp

Lines changed: 330 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,18 @@ const TernaryLogicInfo& TernaryLogicInfo::lookup(uint8_t control)
353353
return ternaryLogicFlags[control];
354354
}
355355

356+
//------------------------------------------------------------------------
357+
// GetTernaryControlByte: Get the control byte for a TernaryLogic operation
358+
// given the oper and two existing control bytes
359+
//
360+
// Arguments:
361+
// oper -- the operation being performed
362+
// op1 -- the control byte for op1
363+
// op2 -- the control byte for op2
364+
//
365+
// Return Value:
366+
// The new control byte evaluated from performing oper on op1 and op2
367+
//
356368
uint8_t TernaryLogicInfo::GetTernaryControlByte(genTreeOps oper, uint8_t op1, uint8_t op2)
357369
{
358370
switch (oper)
@@ -369,12 +381,12 @@ uint8_t TernaryLogicInfo::GetTernaryControlByte(genTreeOps oper, uint8_t op1, ui
369381

370382
case GT_OR:
371383
{
372-
return static_cast<uint8_t>(op1 & op2);
384+
return static_cast<uint8_t>(op1 | op2);
373385
}
374386

375387
case GT_XOR:
376388
{
377-
return static_cast<uint8_t>(op1 & op2);
389+
return static_cast<uint8_t>(op1 ^ op2);
378390
}
379391

380392
default:
@@ -383,6 +395,322 @@ uint8_t TernaryLogicInfo::GetTernaryControlByte(genTreeOps oper, uint8_t op1, ui
383395
}
384396
}
385397
}
398+
399+
//------------------------------------------------------------------------
400+
// GetTernaryControlByte: Get the control byte for a TernaryLogic operation
401+
// given a ternary logic oper and two inputs
402+
//
403+
// Arguments:
404+
// oper -- the operation being performed
405+
// op1 -- the control byte for op1, this is ignored for unary oper
406+
// op2 -- the control byte for op2
407+
//
408+
// Return Value:
409+
// The new control byte evaluated from performing oper on op1 and op2
410+
//
411+
uint8_t TernaryLogicInfo::GetTernaryControlByte(TernaryLogicOperKind oper, uint8_t op1, uint8_t op2)
412+
{
413+
switch (oper)
414+
{
415+
case TernaryLogicOperKind::Select:
416+
{
417+
return op2;
418+
}
419+
420+
case TernaryLogicOperKind::Not:
421+
{
422+
return ~op2;
423+
}
424+
425+
case TernaryLogicOperKind::And:
426+
{
427+
return op1 & op2;
428+
}
429+
430+
case TernaryLogicOperKind::Nand:
431+
{
432+
return ~(op1 & op2);
433+
}
434+
435+
case TernaryLogicOperKind::Or:
436+
{
437+
return op1 | op2;
438+
}
439+
440+
case TernaryLogicOperKind::Nor:
441+
{
442+
return ~(op1 | op2);
443+
}
444+
445+
case TernaryLogicOperKind::Xor:
446+
{
447+
return op1 ^ op2;
448+
}
449+
450+
case TernaryLogicOperKind::Xnor:
451+
{
452+
return ~(op1 ^ op2);
453+
}
454+
455+
default:
456+
{
457+
unreached();
458+
}
459+
}
460+
}
461+
462+
//------------------------------------------------------------------------
463+
// GetTernaryControlByte: Get the control byte for a TernaryLogic operation
464+
// given an existing info and three control bytes
465+
//
466+
// Arguments:
467+
// info -- the info describing the operation being performed
468+
// op1 -- the control byte for op1
469+
// op2 -- the control byte for op2
470+
// op3 -- the control byte for op3
471+
//
472+
// Return Value:
473+
// The new control byte evaluated from performing info on op1, op2, and op3
474+
//
475+
uint8_t TernaryLogicInfo::GetTernaryControlByte(const TernaryLogicInfo& info, uint8_t op1, uint8_t op2, uint8_t op3)
476+
{
477+
uint8_t oper1Result;
478+
479+
switch (info.oper1Use)
480+
{
481+
case TernaryLogicUseFlags::None:
482+
{
483+
assert(info.oper2 == TernaryLogicOperKind::None);
484+
assert(info.oper2Use == TernaryLogicUseFlags::None);
485+
486+
assert(info.oper3 == TernaryLogicOperKind::None);
487+
assert(info.oper3Use == TernaryLogicUseFlags::None);
488+
489+
switch (info.oper1)
490+
{
491+
case TernaryLogicOperKind::False:
492+
{
493+
oper1Result = 0x00;
494+
break;
495+
}
496+
497+
case TernaryLogicOperKind::True:
498+
{
499+
oper1Result = 0xFF;
500+
break;
501+
}
502+
503+
default:
504+
{
505+
unreached();
506+
}
507+
}
508+
break;
509+
}
510+
511+
case TernaryLogicUseFlags::A:
512+
{
513+
oper1Result = GetTernaryControlByte(info.oper1, 0x00, op1);
514+
break;
515+
}
516+
517+
case TernaryLogicUseFlags::B:
518+
{
519+
oper1Result = GetTernaryControlByte(info.oper1, 0x00, op2);
520+
break;
521+
}
522+
523+
case TernaryLogicUseFlags::C:
524+
{
525+
oper1Result = GetTernaryControlByte(info.oper1, 0x00, op3);
526+
break;
527+
}
528+
529+
case TernaryLogicUseFlags::AB:
530+
{
531+
oper1Result = GetTernaryControlByte(info.oper1, op1, op2);
532+
break;
533+
}
534+
535+
case TernaryLogicUseFlags::AC:
536+
{
537+
oper1Result = GetTernaryControlByte(info.oper1, op1, op3);
538+
break;
539+
}
540+
541+
case TernaryLogicUseFlags::BC:
542+
{
543+
oper1Result = GetTernaryControlByte(info.oper1, op2, op3);
544+
break;
545+
}
546+
547+
case TernaryLogicUseFlags::ABC:
548+
{
549+
assert(info.oper2 == TernaryLogicOperKind::None);
550+
assert(info.oper2Use == TernaryLogicUseFlags::None);
551+
552+
assert(info.oper3 == TernaryLogicOperKind::None);
553+
assert(info.oper3Use == TernaryLogicUseFlags::None);
554+
555+
switch (info.oper1)
556+
{
557+
case TernaryLogicOperKind::Nor:
558+
{
559+
oper1Result = ~(op1 | op2 | op3);
560+
break;
561+
}
562+
563+
case TernaryLogicOperKind::Minor:
564+
{
565+
oper1Result = 0x17;
566+
break;
567+
}
568+
569+
case TernaryLogicOperKind::Xnor:
570+
{
571+
oper1Result = ~(op1 ^ op2 ^ op3);
572+
break;
573+
}
574+
575+
case TernaryLogicOperKind::Nand:
576+
{
577+
oper1Result = ~(op1 & op2 & op3);
578+
break;
579+
}
580+
581+
case TernaryLogicOperKind::And:
582+
{
583+
oper1Result = op1 & op2 & op3;
584+
break;
585+
}
586+
587+
case TernaryLogicOperKind::Xor:
588+
{
589+
oper1Result = op1 ^ op2 ^ op3;
590+
break;
591+
}
592+
593+
case TernaryLogicOperKind::Major:
594+
{
595+
oper1Result = 0xE8;
596+
break;
597+
}
598+
599+
case TernaryLogicOperKind::Or:
600+
{
601+
oper1Result = op1 | op2 | op3;
602+
break;
603+
}
604+
605+
default:
606+
{
607+
unreached();
608+
}
609+
}
610+
break;
611+
}
612+
613+
default:
614+
{
615+
unreached();
616+
}
617+
}
618+
619+
uint8_t oper2Result;
620+
621+
switch (info.oper2Use)
622+
{
623+
case TernaryLogicUseFlags::None:
624+
{
625+
assert(info.oper3 == TernaryLogicOperKind::None);
626+
assert(info.oper3Use == TernaryLogicUseFlags::None);
627+
628+
oper2Result = oper1Result;
629+
break;
630+
}
631+
632+
case TernaryLogicUseFlags::A:
633+
{
634+
oper2Result = GetTernaryControlByte(info.oper2, oper1Result, op1);
635+
break;
636+
}
637+
638+
case TernaryLogicUseFlags::B:
639+
{
640+
oper2Result = GetTernaryControlByte(info.oper2, oper1Result, op2);
641+
break;
642+
}
643+
644+
case TernaryLogicUseFlags::C:
645+
{
646+
oper2Result = GetTernaryControlByte(info.oper2, oper1Result, op3);
647+
break;
648+
}
649+
650+
case TernaryLogicUseFlags::AB:
651+
{
652+
oper2Result = GetTernaryControlByte(info.oper1, op1, op2);
653+
break;
654+
}
655+
656+
case TernaryLogicUseFlags::AC:
657+
{
658+
oper2Result = GetTernaryControlByte(info.oper1, op1, op3);
659+
break;
660+
}
661+
662+
case TernaryLogicUseFlags::BC:
663+
{
664+
oper2Result = GetTernaryControlByte(info.oper1, op2, op3);
665+
break;
666+
}
667+
668+
default:
669+
{
670+
unreached();
671+
}
672+
}
673+
674+
uint8_t oper3Result;
675+
676+
switch (info.oper3Use)
677+
{
678+
case TernaryLogicUseFlags::None:
679+
{
680+
assert(info.oper3 == TernaryLogicOperKind::None);
681+
oper3Result = oper2Result;
682+
break;
683+
}
684+
685+
case TernaryLogicUseFlags::A:
686+
{
687+
assert(info.oper3 == TernaryLogicOperKind::Cond);
688+
oper3Result = (oper1Result & op1) | (oper2Result & ~op1);
689+
break;
690+
}
691+
692+
case TernaryLogicUseFlags::B:
693+
{
694+
assert(info.oper3 == TernaryLogicOperKind::Cond);
695+
oper3Result = (oper1Result & op2) | (oper2Result & ~op2);
696+
break;
697+
}
698+
699+
case TernaryLogicUseFlags::C:
700+
{
701+
assert(info.oper3 == TernaryLogicOperKind::Cond);
702+
oper3Result = (oper1Result & op3) | (oper2Result & ~op3);
703+
break;
704+
}
705+
706+
default:
707+
{
708+
unreached();
709+
}
710+
}
711+
712+
return oper3Result;
713+
}
386714
#endif // TARGET_XARCH
387715

388716
//------------------------------------------------------------------------

src/coreclr/jit/hwintrinsic.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,10 @@ struct TernaryLogicInfo
480480
TernaryLogicUseFlags oper3Use : 3;
481481

482482
static const TernaryLogicInfo& lookup(uint8_t control);
483-
static uint8_t GetTernaryControlByte(genTreeOps oper, uint8_t op1, uint8_t op2);
483+
484+
static uint8_t GetTernaryControlByte(genTreeOps oper, uint8_t op1, uint8_t op2);
485+
static uint8_t GetTernaryControlByte(TernaryLogicOperKind oper, uint8_t op1, uint8_t op2);
486+
static uint8_t GetTernaryControlByte(const TernaryLogicInfo& info, uint8_t op1, uint8_t op2, uint8_t op3);
484487

485488
TernaryLogicUseFlags GetAllUseFlags() const
486489
{
@@ -1025,6 +1028,11 @@ struct HWIntrinsicInfo
10251028
HWIntrinsicFlag flags = lookupFlags(id);
10261029
return (flags & HW_Flag_PermuteVar2x) != 0;
10271030
}
1031+
1032+
static bool IsTernaryLogic(NamedIntrinsic id)
1033+
{
1034+
return (id == NI_AVX512F_TernaryLogic) || (id == NI_AVX512F_VL_TernaryLogic) || (id == NI_AVX10v1_TernaryLogic);
1035+
}
10281036
#endif // TARGET_XARCH
10291037

10301038
#if defined(TARGET_ARM64)

0 commit comments

Comments
 (0)