Skip to content

Commit 5e7aaee

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

File tree

4 files changed

+707
-125
lines changed

4 files changed

+707
-125
lines changed

src/coreclr/jit/gentree.cpp

+2-1
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

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

388738
//------------------------------------------------------------------------

0 commit comments

Comments
 (0)