Commit 9cbe7c7f authored by Daniel Sanders's avatar Daniel Sanders
Browse files

[globalisel][tablegen] Add support for multi-insn emission

The importer will now accept nested instructions in the result pattern such as
(ADDWrr $a, (SUBWrr $b, $c)). This is only valid when the nested instruction
def's a single vreg and the parent instruction consumes a single vreg where a
nested instruction is specified. The importer will automatically create a vreg
to connect the two using the type information from the pattern. This vreg will
be constrained to the register classes given in the instruction definitions*.

* REG_SEQUENCE is explicitly rejected because of this. The definition doesn't
  constrain to a register class and it therefore needs special handling.

llvm-svn: 317117
parent 7b861f08
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#ifndef LLVM_CODEGEN_GLOBALISEL_INSTRUCTIONSELECTOR_H
#define LLVM_CODEGEN_GLOBALISEL_INSTRUCTIONSELECTOR_H

#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Optional.h"
#include <bitset>
@@ -212,6 +213,10 @@ enum {
  /// - InsnID - Instruction ID to modify
  /// - RegNum - The register to add
  GIR_AddRegister,
  /// Add a a temporary register to the specified instruction
  /// - InsnID - Instruction ID to modify
  /// - TempRegID - The temporary register ID to add
  GIR_AddTempRegister,
  /// Add an immediate to the specified instruction
  /// - InsnID - Instruction ID to modify
  /// - Imm - The immediate to add
@@ -250,6 +255,10 @@ enum {
  /// Erase from parent.
  /// - InsnID - Instruction ID to erase
  GIR_EraseFromParent,
  /// Create a new temporary register that's not constrained.
  /// - TempRegID - The temporary register ID to initialize.
  /// - Expected type
  GIR_MakeTempReg,

  /// A successful emission
  GIR_Done,
@@ -291,6 +300,7 @@ protected:
  struct MatcherState {
    std::vector<ComplexRendererFns::value_type> Renderers;
    RecordedMIVector MIs;
    DenseMap<unsigned, unsigned> TempRegisters;

    MatcherState(unsigned MaxRenderers);
  };
+39 −13
Original line number Diff line number Diff line
@@ -434,12 +434,13 @@ bool InstructionSelector::executeMatchTable(

    case GIR_MutateOpcode: {
      int64_t OldInsnID = MatchTable[CurrentIdx++];
      int64_t NewInsnID = MatchTable[CurrentIdx++];
      uint64_t NewInsnID = MatchTable[CurrentIdx++];
      int64_t NewOpcode = MatchTable[CurrentIdx++];
      assert((size_t)NewInsnID == OutMIs.size() &&
             "Expected to store MIs in order");
      OutMIs.push_back(MachineInstrBuilder(*State.MIs[OldInsnID]->getMF(),
                                           State.MIs[OldInsnID]));
      if (NewInsnID >= OutMIs.size())
        OutMIs.resize(NewInsnID + 1);

      OutMIs[NewInsnID] = MachineInstrBuilder(*State.MIs[OldInsnID]->getMF(),
                                              State.MIs[OldInsnID]);
      OutMIs[NewInsnID]->setDesc(TII.get(NewOpcode));
      DEBUG_WITH_TYPE(TgtInstructionSelector::getName(),
                      dbgs() << CurrentIdx << ": GIR_MutateOpcode(OutMIs["
@@ -449,16 +450,16 @@ bool InstructionSelector::executeMatchTable(
    }

    case GIR_BuildMI: {
      int64_t InsnID = MatchTable[CurrentIdx++];
      uint64_t NewInsnID = MatchTable[CurrentIdx++];
      int64_t Opcode = MatchTable[CurrentIdx++];
      assert((size_t)InsnID == OutMIs.size() &&
             "Expected to store MIs in order");
      (void)InsnID;
      OutMIs.push_back(BuildMI(*State.MIs[0]->getParent(), State.MIs[0],
                               State.MIs[0]->getDebugLoc(), TII.get(Opcode)));
      if (NewInsnID >= OutMIs.size())
        OutMIs.resize(NewInsnID + 1);

      OutMIs[NewInsnID] = BuildMI(*State.MIs[0]->getParent(), State.MIs[0],
                                  State.MIs[0]->getDebugLoc(), TII.get(Opcode));
      DEBUG_WITH_TYPE(TgtInstructionSelector::getName(),
                      dbgs() << CurrentIdx << ": GIR_BuildMI(OutMIs[" << InsnID
                             << "], " << Opcode << ")\n");
                      dbgs() << CurrentIdx << ": GIR_BuildMI(OutMIs["
                             << NewInsnID << "], " << Opcode << ")\n");
      break;
    }

@@ -541,6 +542,19 @@ bool InstructionSelector::executeMatchTable(
      break;
    }

    case GIR_AddTempRegister: {
      int64_t InsnID = MatchTable[CurrentIdx++];
      int64_t TempRegID = MatchTable[CurrentIdx++];
      uint64_t TempRegFlags = MatchTable[CurrentIdx++];
      assert(OutMIs[InsnID] && "Attempted to add to undefined instruction");
      OutMIs[InsnID].addReg(State.TempRegisters[TempRegID], TempRegFlags);
      DEBUG_WITH_TYPE(TgtInstructionSelector::getName(),
                      dbgs() << CurrentIdx << ": GIR_AddTempRegister(OutMIs["
                             << InsnID << "], TempRegisters[" << TempRegID
                             << "], " << TempRegFlags << ")\n");
      break;
    }

    case GIR_AddImm: {
      int64_t InsnID = MatchTable[CurrentIdx++];
      int64_t Imm = MatchTable[CurrentIdx++];
@@ -651,6 +665,18 @@ bool InstructionSelector::executeMatchTable(
      break;
    }

    case GIR_MakeTempReg: {
      int64_t TempRegID = MatchTable[CurrentIdx++];
      int64_t TypeID = MatchTable[CurrentIdx++];

      State.TempRegisters[TempRegID] =
          MRI.createGenericVirtualRegister(MatcherInfo.TypeObjects[TypeID]);
      DEBUG_WITH_TYPE(TgtInstructionSelector::getName(),
                      dbgs() << CurrentIdx << ": TempRegs[" << TempRegID
                             << "] = GIR_MakeTempReg(" << TypeID << ")\n");
      break;
    }

    case GIR_Done:
      DEBUG_WITH_TYPE(TgtInstructionSelector::getName(),
                      dbgs() << CurrentIdx << ": GIR_Done");
+19 −0
Original line number Diff line number Diff line
# RUN: llc -O0 -mtriple=arm64eb-- -run-pass=instruction-select -verify-machineinstrs -global-isel %s -o - | FileCheck %s
---
name:            bitcast_v2f32_to_s64
legalized:       true
regBankSelected: true

body:             |
  bb.0:
    liveins: %x0

    ; CHECK-LABEL: name: bitcast_v2f32_to_s64
    ; CHECK: [[COPY:%[0-9]+]]:fpr64 = COPY %x0
    ; CHECK: [[COPY1:%[0-9]+]]:fpr64 = COPY [[COPY]]
    ; CHECK: [[REV:%[0-9]+]]:fpr64 = REV64v2i32 [[COPY1]]
    ; CHECK: %x0 = COPY [[REV]]
    %0:fpr(<2 x s32>) = COPY %x0
    %1:fpr(s64) = G_BITCAST %0
    %x0 = COPY %1(s64)
...
+26 −0
Original line number Diff line number Diff line
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
# RUN: llc -mtriple=aarch64-- -mattr=+fuse-aes -run-pass=instruction-select -verify-machineinstrs -global-isel %s -o - | FileCheck %s

---
# Check that we select the aarch64_crypto_aesmc and aarch64_crypto_aese
# intrinsics into an ARSMCrrTied and AESErr instruction sequence.
name:            aesmc_aese
legalized:       true
regBankSelected: true

body:             |
  bb.0:
    liveins: %q0, %q1

    ; CHECK-LABEL: name: aesmc_aese
    ; CHECK: [[COPY:%[0-9]+]]:fpr128 = COPY %q0
    ; CHECK: [[COPY1:%[0-9]+]]:fpr128 = COPY %q1
    ; CHECK: [[T0:%[0-9]+]]:fpr128 = AESErr [[COPY]], [[COPY1]]
    ; CHECK: [[T1:%[0-9]+]]:fpr128 = AESMCrrTied [[T0]]
    ; CHECK: %q0 = COPY [[T1]]
    %0:fpr(<16 x s8>) = COPY %q0
    %1:fpr(<16 x s8>) = COPY %q1
    %2:fpr(<16 x s8>) = G_INTRINSIC intrinsic(@llvm.aarch64.crypto.aese), %0, %1
    %3:fpr(<16 x s8>) = G_INTRINSIC intrinsic(@llvm.aarch64.crypto.aesmc), %2
    %q0 = COPY %3(<16 x s8>)
...
+15 −8
Original line number Diff line number Diff line
@@ -192,13 +192,16 @@ def HasC : Predicate<"Subtarget->hasC()"> { let RecomputePerFunction = 1; }
// CHECK-NEXT:  // Label 0: @[[LABEL]]

def INSN3 : I<(outs GPR32:$dst),
              (ins GPR32Op:$src1, GPR32:$src2a, GPR32:$src2b, GPR32:$src3, complex:$src4, i32imm:$src5a, i32imm:$src5b), []>;
              (ins GPR32Op:$src1, GPR32:$src2a, GPR32:$src2b, GPR32:$scr), []>;
def INSN4 : I<(outs GPR32:$scr),
              (ins GPR32:$src3, complex:$src4, i32imm:$src5a, i32imm:$src5b), []>;
def : Pat<(select GPR32:$src1, (complex_rr GPR32:$src2a, GPR32:$src2b),
                               (select GPR32:$src3,
                                       complex:$src4,
                                       (complex i32imm:$src5a, i32imm:$src5b))),
          (INSN3 GPR32:$src1, GPR32:$src2b, GPR32:$src2a, GPR32:$src3,
                 complex:$src4, i32imm:$src5a, i32imm:$src5b)>;
          (INSN3 GPR32:$src1, GPR32:$src2b, GPR32:$src2a,
                 (INSN4 GPR32:$src3, complex:$src4, i32imm:$src5a,
                        i32imm:$src5b))>;

//===- Test a pattern with multiple ComplexPattern operands. --------------===//
//
@@ -232,16 +235,20 @@ def : Pat<(select GPR32:$src1, (complex_rr GPR32:$src2a, GPR32:$src2b),
// CHECK-NEXT:    GIM_CheckType, /*MI*/1, /*Op*/3, /*Type*/GILLT_s32,
// CHECK-NEXT:    GIM_CheckComplexPattern, /*MI*/1, /*Op*/3, /*Renderer*/2, GICP_gi_complex,
// CHECK-NEXT:    GIM_CheckIsSafeToFold, /*InsnID*/1,
// CHECK-NEXT:    // (select:{ *:[i32] } GPR32:{ *:[i32] }:$src1, (complex_rr:{ *:[i32] } GPR32:{ *:[i32] }:$src2a, GPR32:{ *:[i32] }:$src2b), (select:{ *:[i32] } GPR32:{ *:[i32] }:$src3, complex:{ *:[i32] }:$src4, (complex:{ *:[i32] } i32imm:{ *:[i32] }:$src5a, i32imm:{ *:[i32] }:$src5b)))  =>  (INSN3:{ *:[i32] } GPR32:{ *:[i32] }:$src1, GPR32:{ *:[i32] }:$src2b, GPR32:{ *:[i32] }:$src2a, GPR32:{ *:[i32] }:$src3, complex:{ *:[i32] }:$src4, i32imm:{ *:[i32] }:$src5a, i32imm:{ *:[i32] }:$src5b)
// CHECK-NEXT:    // (select:{ *:[i32] } GPR32:{ *:[i32] }:$src1, (complex_rr:{ *:[i32] } GPR32:{ *:[i32] }:$src2a, GPR32:{ *:[i32] }:$src2b), (select:{ *:[i32] } GPR32:{ *:[i32] }:$src3, complex:{ *:[i32] }:$src4, (complex:{ *:[i32] } i32imm:{ *:[i32] }:$src5a, i32imm:{ *:[i32] }:$src5b)))  =>  (INSN3:{ *:[i32] } GPR32:{ *:[i32] }:$src1, GPR32:{ *:[i32] }:$src2b, GPR32:{ *:[i32] }:$src2a, (INSN4:{ *:[i32] } GPR32:{ *:[i32] }:$src3, complex:{ *:[i32] }:$src4, i32imm:{ *:[i32] }:$src5a, i32imm:{ *:[i32] }:$src5b))
// CHECK-NEXT:    GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s32,
// CHECK-NEXT:    GIR_BuildMI, /*InsnID*/1, /*Opcode*/MyTarget::INSN4,
// CHECK-NEXT:    GIR_AddTempRegister, /*InsnID*/1, /*TempRegID*/0, /*TempRegFlags*/RegState::Define,
// CHECK-NEXT:    GIR_Copy, /*NewInsnID*/1, /*OldInsnID*/1, /*OpIdx*/1, // src3
// CHECK-NEXT:    GIR_ComplexRenderer, /*InsnID*/1, /*RendererID*/1,
// CHECK-NEXT:    GIR_ComplexSubOperandRenderer, /*InsnID*/1, /*RendererID*/2, /*SubOperand*/0, // src5a
// CHECK-NEXT:    GIR_ComplexSubOperandRenderer, /*InsnID*/1, /*RendererID*/2, /*SubOperand*/1, // src5b
// CHECK-NEXT:    GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::INSN3,
// CHECK-NEXT:    GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
// CHECK-NEXT:    GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src1
// CHECK-NEXT:    GIR_ComplexSubOperandRenderer, /*InsnID*/0, /*RendererID*/0, /*SubOperand*/1, // src2b
// CHECK-NEXT:    GIR_ComplexSubOperandRenderer, /*InsnID*/0, /*RendererID*/0, /*SubOperand*/0, // src2a
// CHECK-NEXT:    GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/1, /*OpIdx*/1, // src3
// CHECK-NEXT:    GIR_ComplexRenderer, /*InsnID*/0, /*RendererID*/1,
// CHECK-NEXT:    GIR_ComplexSubOperandRenderer, /*InsnID*/0, /*RendererID*/2, /*SubOperand*/0, // src5a
// CHECK-NEXT:    GIR_ComplexSubOperandRenderer, /*InsnID*/0, /*RendererID*/2, /*SubOperand*/1, // src5b
// CHECK-NEXT:    GIR_AddTempRegister, /*InsnID*/0, /*TempRegID*/0, /*TempRegFlags*/0,
// CHECK-NEXT:    GIR_EraseFromParent, /*InsnID*/0,
// CHECK-NEXT:    GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT:    GIR_Done,
Loading