Retire llvm::array_endof in favor of non-member std::end.
[oota-llvm.git] / lib / Target / Mips / Mips16HardFloat.cpp
1 //===---- Mips16HardFloat.cpp for Mips16 Hard Float               --------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file defines a pass needed for Mips16 Hard Float
11 //
12 //===----------------------------------------------------------------------===//
13
14 #define DEBUG_TYPE "mips16-hard-float"
15 #include "Mips16HardFloat.h"
16 #include "llvm/IR/Module.h"
17 #include "llvm/IR/Value.h"
18 #include "llvm/Support/Debug.h"
19 #include "llvm/Support/raw_ostream.h"
20 #include <algorithm>
21 #include <string>
22
23 static void inlineAsmOut
24   (LLVMContext &C, StringRef AsmString, BasicBlock *BB ) {
25   std::vector<llvm::Type *> AsmArgTypes;
26   std::vector<llvm::Value*> AsmArgs;
27   llvm::FunctionType *AsmFTy =
28     llvm::FunctionType::get(Type::getVoidTy(C),
29                             AsmArgTypes, false);
30   llvm::InlineAsm *IA =
31     llvm::InlineAsm::get(AsmFTy, AsmString, "", true,
32                          /* IsAlignStack */ false,
33                          llvm::InlineAsm::AD_ATT);
34   CallInst::Create(IA, AsmArgs, "", BB);
35 }
36
37 namespace {
38
39 class InlineAsmHelper {
40   LLVMContext &C;
41   BasicBlock *BB;
42 public:
43   InlineAsmHelper(LLVMContext &C_, BasicBlock *BB_) :
44     C(C_), BB(BB_) {
45   }
46
47   void Out(StringRef AsmString) {
48     inlineAsmOut(C, AsmString, BB);
49   }
50
51 };
52 }
53 //
54 // Return types that matter for hard float are:
55 // float, double, complex float, and complex double
56 //
57 enum FPReturnVariant {
58   FRet, DRet, CFRet, CDRet, NoFPRet
59 };
60
61 //
62 // Determine which FP return type this function has
63 //
64 static FPReturnVariant whichFPReturnVariant(Type *T) {
65   switch (T->getTypeID()) {
66   case Type::FloatTyID:
67     return FRet;
68   case Type::DoubleTyID:
69     return DRet;
70   case Type::StructTyID:
71     if (T->getStructNumElements() != 2)
72       break;
73     if ((T->getContainedType(0)->isFloatTy()) &&
74         (T->getContainedType(1)->isFloatTy()))
75       return CFRet;
76     if ((T->getContainedType(0)->isDoubleTy()) &&
77         (T->getContainedType(1)->isDoubleTy()))
78       return CDRet;
79     break;
80   default:
81     break;
82   }
83   return NoFPRet;
84 }
85
86 //
87 // Parameter type that matter are float, (float, float), (float, double),
88 // double, (double, double), (double, float)
89 //
90 enum FPParamVariant {
91   FSig, FFSig, FDSig,
92   DSig, DDSig, DFSig, NoSig
93 };
94
95 // which floating point parameter signature variant we are dealing with
96 //
97 typedef Type::TypeID TypeID;
98 const Type::TypeID FloatTyID = Type::FloatTyID;
99 const Type::TypeID DoubleTyID = Type::DoubleTyID;
100
101 static FPParamVariant whichFPParamVariantNeeded(Function &F) {
102   switch (F.arg_size()) {
103   case 0:
104     return NoSig;
105   case 1:{
106     TypeID ArgTypeID = F.getFunctionType()->getParamType(0)->getTypeID();
107     switch (ArgTypeID) {
108     case FloatTyID:
109       return FSig;
110     case DoubleTyID:
111       return DSig;
112     default:
113       return NoSig;
114     }
115   }
116   default: {
117     TypeID ArgTypeID0 = F.getFunctionType()->getParamType(0)->getTypeID();
118     TypeID ArgTypeID1 = F.getFunctionType()->getParamType(1)->getTypeID();
119     switch(ArgTypeID0) {
120     case FloatTyID: {
121       switch (ArgTypeID1) {
122       case FloatTyID:
123         return FFSig;
124       case DoubleTyID:
125         return FDSig;
126       default:
127         return FSig;
128       }
129     }
130     case DoubleTyID: {
131       switch (ArgTypeID1) {
132       case FloatTyID:
133         return DFSig;
134       case DoubleTyID:
135         return DDSig;
136       default:
137         return DSig;
138       }
139     }
140     default:
141       return NoSig;
142     }
143   }
144   }
145   llvm_unreachable("can't get here");
146 }
147
148 // Figure out if we need float point based on the function parameters.
149 // We need to move variables in and/or out of floating point
150 // registers because of the ABI
151 //
152 static bool needsFPStubFromParams(Function &F) {
153   if (F.arg_size() >=1) {
154     Type *ArgType = F.getFunctionType()->getParamType(0);
155     switch (ArgType->getTypeID()) {
156       case Type::FloatTyID:
157       case Type::DoubleTyID:
158         return true;
159       default:
160         break;
161     }
162   }
163   return false;
164 }
165
166 static bool needsFPReturnHelper(Function &F) {
167   Type* RetType = F.getReturnType();
168   return whichFPReturnVariant(RetType) != NoFPRet;
169 }
170
171 static bool needsFPReturnHelper(const FunctionType &FT) {
172   Type* RetType = FT.getReturnType();
173   return whichFPReturnVariant(RetType) != NoFPRet;
174 }
175
176 static bool needsFPHelperFromSig(Function &F) {
177   return needsFPStubFromParams(F) || needsFPReturnHelper(F);
178 }
179
180 //
181 // We swap between FP and Integer registers to allow Mips16 and Mips32 to
182 // interoperate
183 //
184
185 static void swapFPIntParams
186   (FPParamVariant PV, Module *M, InlineAsmHelper &IAH,
187    bool LE, bool ToFP) {
188   //LLVMContext &Context = M->getContext();
189   std::string MI = ToFP? "mtc1 ": "mfc1 ";
190   switch (PV) {
191   case FSig:
192     IAH.Out(MI + "$$4,$$f12");
193     break;
194   case FFSig:
195     IAH.Out(MI +"$$4,$$f12");
196     IAH.Out(MI + "$$5,$$f14");
197     break;
198   case FDSig:
199     IAH.Out(MI + "$$4,$$f12");
200     if (LE) {
201       IAH.Out(MI + "$$6,$$f14");
202       IAH.Out(MI + "$$7,$$f15");
203     } else {
204       IAH.Out(MI + "$$7,$$f14");
205       IAH.Out(MI + "$$6,$$f15");
206     }
207     break;
208   case DSig:
209     if (LE) {
210       IAH.Out(MI + "$$4,$$f12");
211       IAH.Out(MI + "$$5,$$f13");
212     } else {
213       IAH.Out(MI + "$$5,$$f12");
214       IAH.Out(MI + "$$4,$$f13");
215     }
216     break;
217   case DDSig:
218     if (LE) {
219       IAH.Out(MI + "$$4,$$f12");
220       IAH.Out(MI + "$$5,$$f13");
221       IAH.Out(MI + "$$6,$$f14");
222       IAH.Out(MI + "$$7,$$f15");
223     } else {
224       IAH.Out(MI + "$$5,$$f12");
225       IAH.Out(MI + "$$4,$$f13");
226       IAH.Out(MI + "$$7,$$f14");
227       IAH.Out(MI + "$$6,$$f15");
228     }
229     break;
230   case DFSig:
231     if (LE) {
232       IAH.Out(MI + "$$4,$$f12");
233       IAH.Out(MI + "$$5,$$f13");
234     } else {
235       IAH.Out(MI + "$$5,$$f12");
236       IAH.Out(MI + "$$4,$$f13");
237     }
238     IAH.Out(MI + "$$6,$$f14");
239     break;
240   case NoSig:
241     return;
242   }
243 }
244 //
245 // Make sure that we know we already need a stub for this function.
246 // Having called needsFPHelperFromSig
247 //
248 static void assureFPCallStub(Function &F, Module *M,
249                              const MipsSubtarget &Subtarget) {
250   // for now we only need them for static relocation
251   if (Subtarget.getRelocationModel() == Reloc::PIC_)
252     return;
253   LLVMContext &Context = M->getContext();
254   bool LE = Subtarget.isLittle();
255   std::string Name = F.getName();
256   std::string SectionName = ".mips16.call.fp." + Name;
257   std::string StubName = "__call_stub_fp_" + Name;
258   //
259   // see if we already have the stub
260   //
261   Function *FStub = M->getFunction(StubName);
262   if (FStub && !FStub->isDeclaration()) return;
263   FStub = Function::Create(F.getFunctionType(),
264                            Function::InternalLinkage, StubName, M);
265   FStub->addFnAttr("mips16_fp_stub");
266   FStub->addFnAttr(llvm::Attribute::Naked);
267   FStub->addFnAttr(llvm::Attribute::NoInline);
268   FStub->addFnAttr(llvm::Attribute::NoUnwind);
269   FStub->addFnAttr("nomips16");
270   FStub->setSection(SectionName);
271   BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
272   InlineAsmHelper IAH(Context, BB);
273   IAH.Out(".set reorder");
274   FPReturnVariant RV = whichFPReturnVariant(FStub->getReturnType());
275   FPParamVariant PV = whichFPParamVariantNeeded(F);
276   swapFPIntParams(PV, M, IAH, LE, true);
277   if (RV != NoFPRet) {
278     IAH.Out("move $$18, $$31");
279     IAH.Out("jal " + Name);
280   } else {
281     IAH.Out("lui  $$25,%hi(" + Name + ")");
282     IAH.Out("addiu  $$25,$$25,%lo(" + Name + ")" );
283   }
284   switch (RV) {
285   case FRet:
286     IAH.Out("mfc1 $$2,$$f0");
287     break;
288   case DRet:
289     if (LE) {
290       IAH.Out("mfc1 $$2,$$f0");
291       IAH.Out("mfc1 $$3,$$f1");
292     } else {
293       IAH.Out("mfc1 $$3,$$f0");
294       IAH.Out("mfc1 $$2,$$f1");
295     }
296     break;
297   case CFRet:
298     if (LE) {
299     IAH.Out("mfc1 $$2,$$f0");
300     IAH.Out("mfc1 $$3,$$f2");
301     } else {
302       IAH.Out("mfc1 $$3,$$f0");
303       IAH.Out("mfc1 $$3,$$f2");
304     }
305     break;
306   case CDRet:
307     if (LE) {
308       IAH.Out("mfc1 $$4,$$f2");
309       IAH.Out("mfc1 $$5,$$f3");
310       IAH.Out("mfc1 $$2,$$f0");
311       IAH.Out("mfc1 $$3,$$f1");
312
313     } else {
314       IAH.Out("mfc1 $$5,$$f2");
315       IAH.Out("mfc1 $$4,$$f3");
316       IAH.Out("mfc1 $$3,$$f0");
317       IAH.Out("mfc1 $$2,$$f1");
318     }
319     break;
320   case NoFPRet:
321     break;
322   }
323   if (RV != NoFPRet)
324     IAH.Out("jr $$18");
325   else
326     IAH.Out("jr $$25");
327   new UnreachableInst(Context, BB);
328 }
329
330 //
331 // Functions that are llvm intrinsics and don't need helpers.
332 //
333 static const char *IntrinsicInline[] =
334   {"fabs",
335    "fabsf",
336    "llvm.ceil.f32", "llvm.ceil.f64",
337    "llvm.copysign.f32", "llvm.copysign.f64",
338    "llvm.cos.f32", "llvm.cos.f64",
339    "llvm.exp.f32", "llvm.exp.f64",
340    "llvm.exp2.f32", "llvm.exp2.f64",
341    "llvm.fabs.f32", "llvm.fabs.f64",
342    "llvm.floor.f32", "llvm.floor.f64",
343    "llvm.fma.f32", "llvm.fma.f64",
344    "llvm.log.f32", "llvm.log.f64",
345    "llvm.log10.f32", "llvm.log10.f64",
346    "llvm.nearbyint.f32", "llvm.nearbyint.f64",
347    "llvm.pow.f32", "llvm.pow.f64",
348    "llvm.powi.f32", "llvm.powi.f64",
349    "llvm.rint.f32", "llvm.rint.f64",
350    "llvm.round.f32", "llvm.round.f64",
351    "llvm.sin.f32", "llvm.sin.f64",
352    "llvm.sqrt.f32", "llvm.sqrt.f64",
353    "llvm.trunc.f32", "llvm.trunc.f64",
354   };
355
356 static bool isIntrinsicInline(Function *F) {
357   return std::binary_search(std::begin(IntrinsicInline),
358                             std::end(IntrinsicInline), F->getName());
359 }
360 //
361 // Returns of float, double and complex need to be handled with a helper
362 // function.
363 //
364 static bool fixupFPReturnAndCall
365   (Function &F, Module *M,  const MipsSubtarget &Subtarget) {
366   bool Modified = false;
367   LLVMContext &C = M->getContext();
368   Type *MyVoid = Type::getVoidTy(C);
369   for (Function::iterator BB = F.begin(), E = F.end(); BB != E; ++BB)
370     for (BasicBlock::iterator I = BB->begin(), E = BB->end();
371          I != E; ++I) {
372       Instruction &Inst = *I;
373       if (const ReturnInst *RI = dyn_cast<ReturnInst>(I)) {
374         Value *RVal = RI->getReturnValue();
375         if (!RVal) continue;
376         //
377         // If there is a return value and it needs a helper function,
378         // figure out which one and add a call before the actual
379         // return to this helper. The purpose of the helper is to move
380         // floating point values from their soft float return mapping to
381         // where they would have been mapped to in floating point registers.
382         //
383         Type *T = RVal->getType();
384         FPReturnVariant RV = whichFPReturnVariant(T);
385         if (RV == NoFPRet) continue;
386         static const char* Helper[NoFPRet] =
387           {"__mips16_ret_sf", "__mips16_ret_df", "__mips16_ret_sc",
388            "__mips16_ret_dc"};
389         const char *Name = Helper[RV];
390         AttributeSet A;
391         Value *Params[] = {RVal};
392         Modified = true;
393         //
394         // These helper functions have a different calling ABI so
395         // this __Mips16RetHelper indicates that so that later
396         // during call setup, the proper call lowering to the helper
397         // functions will take place.
398         //
399         A = A.addAttribute(C, AttributeSet::FunctionIndex,
400                            "__Mips16RetHelper");
401         A = A.addAttribute(C, AttributeSet::FunctionIndex,
402                            Attribute::ReadNone);
403         A = A.addAttribute(C, AttributeSet::FunctionIndex,
404                            Attribute::NoInline);
405         Value *F = (M->getOrInsertFunction(Name, A, MyVoid, T, NULL));
406         CallInst::Create(F, Params, "", &Inst );
407       } else if (const CallInst *CI = dyn_cast<CallInst>(I)) {
408           const Value* V = CI->getCalledValue();
409           const Type* T = 0;
410           if (V) T = V->getType();
411           const PointerType *PFT=0;
412           if (T) PFT = dyn_cast<PointerType>(T);
413           const FunctionType *FT=0;
414           if (PFT) FT = dyn_cast<FunctionType>(PFT->getElementType());
415           Function *F_ =  CI->getCalledFunction();
416           if (FT && needsFPReturnHelper(*FT) &&
417               !(F_ && isIntrinsicInline(F_))) {
418             Modified=true;
419             F.addFnAttr("saveS2");
420           }
421           if (F_ && !isIntrinsicInline(F_)) {
422           // pic mode calls are handled by already defined
423           // helper functions
424             if (needsFPReturnHelper(*F_)) {
425               Modified=true;
426               F.addFnAttr("saveS2");
427             }
428             if (Subtarget.getRelocationModel() != Reloc::PIC_ ) {
429               if (needsFPHelperFromSig(*F_)) {
430                 assureFPCallStub(*F_, M, Subtarget);
431                 Modified=true;
432               }
433             }
434           }
435       }
436     }
437   return Modified;
438 }
439
440 static void createFPFnStub(Function *F, Module *M, FPParamVariant PV,
441                   const MipsSubtarget &Subtarget ) {
442   bool PicMode = Subtarget.getRelocationModel() == Reloc::PIC_;
443   bool LE = Subtarget.isLittle();
444   LLVMContext &Context = M->getContext();
445   std::string Name = F->getName();
446   std::string SectionName = ".mips16.fn." + Name;
447   std::string StubName = "__fn_stub_" + Name;
448   std::string LocalName = "$$__fn_local_" + Name;
449   Function *FStub = Function::Create
450     (F->getFunctionType(),
451      Function::InternalLinkage, StubName, M);
452   FStub->addFnAttr("mips16_fp_stub");
453   FStub->addFnAttr(llvm::Attribute::Naked);
454   FStub->addFnAttr(llvm::Attribute::NoUnwind);
455   FStub->addFnAttr(llvm::Attribute::NoInline);
456   FStub->addFnAttr("nomips16");
457   FStub->setSection(SectionName);
458   BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
459   InlineAsmHelper IAH(Context, BB);
460   IAH.Out(" .set  macro");
461   if (PicMode) {
462     IAH.Out(".set noreorder");
463     IAH.Out(".cpload  $$25");
464     IAH.Out(".set reorder");
465     IAH.Out(".reloc 0,R_MIPS_NONE," + Name);
466     IAH.Out("la $$25," + LocalName);
467   }
468   else {
469     IAH.Out(".set reorder");
470     IAH.Out("la $$25," + Name);
471   }
472   swapFPIntParams(PV, M, IAH, LE, false);
473   IAH.Out("jr $$25");
474   IAH.Out(LocalName + " = " + Name);
475   new UnreachableInst(FStub->getContext(), BB);
476 }
477
478 //
479 // remove the use-soft-float attribute
480 //
481 static void removeUseSoftFloat(Function &F) {
482   AttributeSet A;
483   DEBUG(errs() << "removing -use-soft-float\n");
484   A = A.addAttribute(F.getContext(), AttributeSet::FunctionIndex,
485                      "use-soft-float", "false");
486   F.removeAttributes(AttributeSet::FunctionIndex, A);
487   if (F.hasFnAttribute("use-soft-float")) {
488     DEBUG(errs() << "still has -use-soft-float\n");
489   }
490   F.addAttributes(AttributeSet::FunctionIndex, A);
491 }
492
493 namespace llvm {
494
495 //
496 // This pass only makes sense when the underlying chip has floating point but
497 // we are compiling as mips16.
498 // For all mips16 functions (that are not stubs we have already generated), or
499 // declared via attributes as nomips16, we must:
500 //    1) fixup all returns of float, double, single and double complex
501 //       by calling a helper function before the actual return.
502 //    2) generate helper functions (stubs) that can be called by mips32
503 //       functions that will move parameters passed normally passed in
504 //       floating point
505 //       registers the soft float equivalents.
506 //    3) in the case of static relocation, generate helper functions so that
507 //       mips16 functions can call extern functions of unknown type (mips16 or
508 //       mips32).
509 //    4) TBD. For pic, calls to extern functions of unknown type are handled by
510 //       predefined helper functions in libc but this work is currently done
511 //       during call lowering but it should be moved here in the future.
512 //
513 bool Mips16HardFloat::runOnModule(Module &M) {
514   DEBUG(errs() << "Run on Module Mips16HardFloat\n");
515   bool Modified = false;
516   for (Module::iterator F = M.begin(), E = M.end(); F != E; ++F) {
517     if (F->hasFnAttribute("nomips16") &&
518         F->hasFnAttribute("use-soft-float")) {
519       removeUseSoftFloat(*F);
520       continue;
521     }
522     if (F->isDeclaration() || F->hasFnAttribute("mips16_fp_stub") ||
523         F->hasFnAttribute("nomips16")) continue;
524     Modified |= fixupFPReturnAndCall(*F, &M, Subtarget);
525     FPParamVariant V = whichFPParamVariantNeeded(*F);
526     if (V != NoSig) {
527       Modified = true;
528       createFPFnStub(F, &M, V, Subtarget);
529     }
530   }
531   return Modified;
532 }
533
534 char Mips16HardFloat::ID = 0;
535
536 }
537
538 ModulePass *llvm::createMips16HardFloat(MipsTargetMachine &TM) {
539   return new Mips16HardFloat(TM);
540 }
541