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