From ee22517d92c48eab9643b6fc8ce4dac2b7e95f57 Mon Sep 17 00:00:00 2001
From: Ficture Seven <FICTURE7@gmail.com>
Date: Wed, 5 Aug 2020 02:52:33 +0400
Subject: Improve branch operations (#1442)

* Add Compare instruction

* Add BranchIf instruction

* Use test when BranchIf & Compare against 0

* Propagate Compare into BranchIfTrue/False use

- Propagate Compare operations into their BranchIfTrue/False use and
  turn these into a BranchIf.

- Clean up Comparison enum.

* Replace BranchIfTrue/False with BranchIf

* Use BranchIf in EmitPtPointerLoad

- Using BranchIf early instead of BranchIfTrue/False improves LCQ and
  reduces the amount of work needed by the Optimizer.

  EmitPtPointerLoader was a/the big producer of BranchIfTrue/False.

- Fix asserts firing when assembling BitwiseAnd because of type
  mismatch in EmitStoreExclusive. This is harmless and should not
  cause any diffs.

* Increment PPTC interval version

* Improve IRDumper for BranchIf & Compare

* Use BranchIf in EmitNativeCall

* Clean up

* Do not emit test when immediately preceded by and
---
 ARMeilleure/CodeGen/X86/CodeGenerator.cs | 171 +++++++++++++++----------------
 1 file changed, 83 insertions(+), 88 deletions(-)

(limited to 'ARMeilleure/CodeGen/X86/CodeGenerator.cs')

diff --git a/ARMeilleure/CodeGen/X86/CodeGenerator.cs b/ARMeilleure/CodeGen/X86/CodeGenerator.cs
index f04be52d..f2d4c462 100644
--- a/ARMeilleure/CodeGen/X86/CodeGenerator.cs
+++ b/ARMeilleure/CodeGen/X86/CodeGenerator.cs
@@ -33,24 +33,14 @@ namespace ARMeilleure.CodeGen.X86
             Add(Instruction.BitwiseNot,              GenerateBitwiseNot);
             Add(Instruction.BitwiseOr,               GenerateBitwiseOr);
             Add(Instruction.Branch,                  GenerateBranch);
-            Add(Instruction.BranchIfFalse,           GenerateBranchIfFalse);
-            Add(Instruction.BranchIfTrue,            GenerateBranchIfTrue);
+            Add(Instruction.BranchIf,                GenerateBranchIf);
             Add(Instruction.ByteSwap,                GenerateByteSwap);
             Add(Instruction.Call,                    GenerateCall);
             Add(Instruction.Clobber,                 GenerateClobber);
+            Add(Instruction.Compare,                 GenerateCompare);
             Add(Instruction.CompareAndSwap,          GenerateCompareAndSwap);
             Add(Instruction.CompareAndSwap16,        GenerateCompareAndSwap16);
             Add(Instruction.CompareAndSwap8,         GenerateCompareAndSwap8);
-            Add(Instruction.CompareEqual,            GenerateCompareEqual);
-            Add(Instruction.CompareGreater,          GenerateCompareGreater);
-            Add(Instruction.CompareGreaterOrEqual,   GenerateCompareGreaterOrEqual);
-            Add(Instruction.CompareGreaterOrEqualUI, GenerateCompareGreaterOrEqualUI);
-            Add(Instruction.CompareGreaterUI,        GenerateCompareGreaterUI);
-            Add(Instruction.CompareLess,             GenerateCompareLess);
-            Add(Instruction.CompareLessOrEqual,      GenerateCompareLessOrEqual);
-            Add(Instruction.CompareLessOrEqualUI,    GenerateCompareLessOrEqualUI);
-            Add(Instruction.CompareLessUI,           GenerateCompareLessUI);
-            Add(Instruction.CompareNotEqual,         GenerateCompareNotEqual);
             Add(Instruction.ConditionalSelect,       GenerateConditionalSelect);
             Add(Instruction.ConvertI64ToI32,         GenerateConvertI64ToI32);
             Add(Instruction.ConvertToFP,             GenerateConvertToFP);
@@ -474,6 +464,8 @@ namespace ARMeilleure.CodeGen.X86
 
             Debug.Assert(dest.Type.IsInteger());
 
+            // Note: GenerateCompareCommon makes the assumption that BitwiseAnd will emit only a single `and`
+            // instruction.
             context.Assembler.And(dest, src2, dest.Type);
         }
 
@@ -525,22 +517,17 @@ namespace ARMeilleure.CodeGen.X86
             context.JumpTo(context.CurrBlock.Branch);
         }
 
-        private static void GenerateBranchIfFalse(CodeGenContext context, Operation operation)
+        private static void GenerateBranchIf(CodeGenContext context, Operation operation)
         {
-            Operand source = operation.GetSource(0);
-
-            context.Assembler.Test(source, source, source.Type);
+            Operand comp = operation.GetSource(2);
 
-            context.JumpTo(X86Condition.Equal, context.CurrBlock.Branch);
-        }
+            Debug.Assert(comp.Kind == OperandKind.Constant);
 
-        private static void GenerateBranchIfTrue(CodeGenContext context, Operation operation)
-        {
-            Operand source = operation.GetSource(0);
+            var cond = ((Comparison)comp.AsInt32()).ToX86Condition();
 
-            context.Assembler.Test(source, source, source.Type);
+            GenerateCompareCommon(context, operation);
 
-            context.JumpTo(X86Condition.NotEqual, context.CurrBlock.Branch);
+            context.JumpTo(cond, context.CurrBlock.Branch);
         }
 
         private static void GenerateByteSwap(CodeGenContext context, Operation operation)
@@ -566,6 +553,60 @@ namespace ARMeilleure.CodeGen.X86
             // register allocator, we don't need to produce any code.
         }
 
+        private static void GenerateCompare(CodeGenContext context, Operation operation)
+        {
+            Operand dest = operation.Destination;
+            Operand comp = operation.GetSource(2);
+
+            Debug.Assert(dest.Type == OperandType.I32);
+            Debug.Assert(comp.Kind == OperandKind.Constant);
+
+            var cond = ((Comparison)comp.AsInt32()).ToX86Condition();
+
+            GenerateCompareCommon(context, operation);
+
+            context.Assembler.Setcc(dest, cond);
+            context.Assembler.Movzx8(dest, dest, OperandType.I32);
+        }
+
+        private static void GenerateCompareCommon(CodeGenContext context, Operation operation)
+        {
+            Operand src1 = operation.GetSource(0);
+            Operand src2 = operation.GetSource(1);
+
+            EnsureSameType(src1, src2);
+
+            Debug.Assert(src1.Type.IsInteger());
+
+            if (src2.Kind == OperandKind.Constant && src2.Value == 0)
+            {
+                if (MatchOperation(operation.ListPrevious, Instruction.BitwiseAnd, src1.Type, src1.GetRegister()))
+                {
+                    // Since the `test` and `and` instruction set the status flags in the same way, we can omit the
+                    // `test r,r` instruction when it is immediately preceded by an `and r,*` instruction.
+                    //
+                    // For example:
+                    //
+                    //  and eax, 0x3
+                    //  test eax, eax
+                    //  jz .L0
+                    //
+                    // =>
+                    //
+                    //  and eax, 0x3
+                    //  jz .L0
+                }
+                else
+                {
+                    context.Assembler.Test(src1, src1, src1.Type);
+                }
+            }
+            else
+            {
+                context.Assembler.Cmp(src1, src2, src1.Type);
+            }
+        }
+
         private static void GenerateCompareAndSwap(CodeGenContext context, Operation operation)
         {
             Operand src1 = operation.GetSource(0);
@@ -615,71 +656,6 @@ namespace ARMeilleure.CodeGen.X86
             context.Assembler.Cmpxchg8(memOp, src3);
         }
 
-        private static void GenerateCompareEqual(CodeGenContext context, Operation operation)
-        {
-            GenerateCompare(context, operation, X86Condition.Equal);
-        }
-
-        private static void GenerateCompareGreater(CodeGenContext context, Operation operation)
-        {
-            GenerateCompare(context, operation, X86Condition.Greater);
-        }
-
-        private static void GenerateCompareGreaterOrEqual(CodeGenContext context, Operation operation)
-        {
-            GenerateCompare(context, operation, X86Condition.GreaterOrEqual);
-        }
-
-        private static void GenerateCompareGreaterOrEqualUI(CodeGenContext context, Operation operation)
-        {
-            GenerateCompare(context, operation, X86Condition.AboveOrEqual);
-        }
-
-        private static void GenerateCompareGreaterUI(CodeGenContext context, Operation operation)
-        {
-            GenerateCompare(context, operation, X86Condition.Above);
-        }
-
-        private static void GenerateCompareLess(CodeGenContext context, Operation operation)
-        {
-            GenerateCompare(context, operation, X86Condition.Less);
-        }
-
-        private static void GenerateCompareLessOrEqual(CodeGenContext context, Operation operation)
-        {
-            GenerateCompare(context, operation, X86Condition.LessOrEqual);
-        }
-
-        private static void GenerateCompareLessOrEqualUI(CodeGenContext context, Operation operation)
-        {
-            GenerateCompare(context, operation, X86Condition.BelowOrEqual);
-        }
-
-        private static void GenerateCompareLessUI(CodeGenContext context, Operation operation)
-        {
-            GenerateCompare(context, operation, X86Condition.Below);
-        }
-
-        private static void GenerateCompareNotEqual(CodeGenContext context, Operation operation)
-        {
-            GenerateCompare(context, operation, X86Condition.NotEqual);
-        }
-
-        private static void GenerateCompare(CodeGenContext context, Operation operation, X86Condition condition)
-        {
-            Operand dest = operation.Destination;
-            Operand src1 = operation.GetSource(0);
-            Operand src2 = operation.GetSource(1);
-
-            EnsureSameType(src1, src2);
-
-            Debug.Assert(dest.Type == OperandType.I32);
-
-            context.Assembler.Cmp(src1, src2, src1.Type);
-            context.Assembler.Setcc(dest, condition);
-            context.Assembler.Movzx8(dest, dest, OperandType.I32);
-        }
-
         private static void GenerateConditionalSelect(CodeGenContext context, Operation operation)
         {
             Operand dest = operation.Destination;
@@ -1561,6 +1537,25 @@ namespace ARMeilleure.CodeGen.X86
             context.Assembler.Pshufd(dest, dest, 0xfc);
         }
 
+        private static bool MatchOperation(Node node, Instruction inst, OperandType destType, Register destReg)
+        {
+            if (!(node is Operation operation) || node.DestinationsCount == 0)
+            {
+                return false;
+            }
+
+            if (operation.Instruction != inst)
+            {
+                return false;
+            }
+
+            Operand dest = operation.Destination;
+
+            return dest.Kind == OperandKind.Register &&
+                   dest.Type == destType &&
+                   dest.GetRegister() == destReg;
+        }
+
         [Conditional("DEBUG")]
         private static void ValidateUnOp(Operand dest, Operand source)
         {
-- 
cgit v1.2.3-70-g09d2