summaryrefslogtreecommitdiff
path: root/lib/Target/CellSPU/SPUNopFiller.cpp
blob: e2bd2d7f4100b2ae1fdba894b2f5bea545030c7f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//===-- SPUNopFiller.cpp - Add nops/lnops to align the pipelines---===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// The final pass just before assembly printing. This pass is the last
// checkpoint where nops and lnops are added to the instruction stream to 
// satisfy the dual issue requirements. The actual dual issue scheduling is 
// done (TODO: nowhere, currently)
//
//===----------------------------------------------------------------------===//

#include "SPU.h"
#include "SPUTargetMachine.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/Target/TargetInstrInfo.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"

using namespace llvm;

namespace {
  struct SPUNopFiller : public MachineFunctionPass {

    TargetMachine &TM;
    const TargetInstrInfo *TII;
    const InstrItineraryData *IID;
    bool isEvenPlace;  // the instruction slot (mem address) at hand is even/odd

    static char ID;
    SPUNopFiller(TargetMachine &tm) 
      : MachineFunctionPass(ID), TM(tm), TII(tm.getInstrInfo()), 
        IID(tm.getInstrItineraryData()) 
    {
      DEBUG( dbgs() << "********** SPU Nop filler **********\n" ; );
    }

    virtual const char *getPassName() const {
      return "SPU nop/lnop Filler";
    }

    void runOnMachineBasicBlock(MachineBasicBlock &MBB);

    bool runOnMachineFunction(MachineFunction &F) {
      isEvenPlace = true; //all functions get an .align 3 directive at start 
      for (MachineFunction::iterator FI = F.begin(), FE = F.end();
           FI != FE; ++FI)
        runOnMachineBasicBlock(*FI);
      return true; //never-ever do any more modifications, just print it!
    }

    typedef enum { none   = 0, // no more instructions in this function / BB
                   pseudo = 1, // this does not get executed
                   even   = 2, 
                   odd    = 3 } SPUOpPlace;
    SPUOpPlace getOpPlacement( MachineInstr &instr );

  };
  char SPUNopFiller::ID = 0;

} 

// Fill a BasicBlock to alignment. 
// In the assebly we align the functions to 'even' adresses, but
// basic blocks have an implicit alignmnet. We hereby define 
// basic blocks to have the same, even, alignment.
void SPUNopFiller::
runOnMachineBasicBlock(MachineBasicBlock &MBB) 
{
  assert( isEvenPlace && "basic block start from odd address");
  for (MachineBasicBlock::iterator I = MBB.begin(); I != MBB.end(); ++I)
  {
    SPUOpPlace this_optype, next_optype;
    MachineBasicBlock::iterator J = I;
    J++;

    this_optype = getOpPlacement( *I );
    next_optype = none;
    while (J!=MBB.end()){
      next_optype = getOpPlacement( *J );
      ++J;
      if (next_optype != pseudo ) 
        break;
    }

    // padd: odd(wrong), even(wrong), ...
    // to:   nop(corr), odd(corr), even(corr)...
    if( isEvenPlace && this_optype == odd && next_optype == even ) {
      DEBUG( dbgs() <<"Adding NOP before: "; );
      DEBUG( I->dump(); );
      BuildMI(MBB, I, I->getDebugLoc(), TII->get(SPU::ENOP));
      isEvenPlace=false;
    }
    
    // padd: even(wrong), odd(wrong), ...
    // to:   lnop(corr), even(corr), odd(corr)...
    else if ( !isEvenPlace && this_optype == even && next_optype == odd){
      DEBUG( dbgs() <<"Adding LNOP before: "; );
      DEBUG( I->dump(); );
      BuildMI(MBB, I, I->getDebugLoc(), TII->get(SPU::LNOP));
      isEvenPlace=true;
    }
      
    // now go to next mem slot
    if( this_optype != pseudo )
      isEvenPlace = !isEvenPlace;    

  }

  // padd basicblock end
  if( !isEvenPlace ){
    MachineBasicBlock::iterator J = MBB.end();
    J--;
    if (getOpPlacement( *J ) == odd) {
      DEBUG( dbgs() <<"Padding basic block with NOP\n"; );
      BuildMI(MBB, J, J->getDebugLoc(), TII->get(SPU::ENOP));
    }  
    else {
      J++;
      DEBUG( dbgs() <<"Padding basic block with LNOP\n"; );
      BuildMI(MBB, J, DebugLoc(), TII->get(SPU::LNOP));
    }
    isEvenPlace=true;
  }
}

FunctionPass *llvm::createSPUNopFillerPass(SPUTargetMachine &tm) {
  return new SPUNopFiller(tm);
}

// Figure out if 'instr' is executed in the even or odd pipeline
SPUNopFiller::SPUOpPlace 
SPUNopFiller::getOpPlacement( MachineInstr &instr ) {
  int sc = instr.getDesc().getSchedClass();
  const InstrStage *stage = IID->beginStage(sc);
  unsigned FUs = stage->getUnits();
  SPUOpPlace retval;

  switch( FUs ) {
    case 0: retval = pseudo; break;
    case 1: retval = odd;    break;
    case 2: retval = even;   break;
    default: retval= pseudo; 
             assert( false && "got unknown FuncUnit\n");
             break;
  };
  return retval;
}