Make sure we don't generate stubs for any of these functions because they
[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    "llvm.ceil.f32", "llvm.ceil.f64",
330    "llvm.copysign.f32", "llvm.copysign.f64",
331    "llvm.cos.f32", "llvm.cos.f64",
332    "llvm.exp.f32", "llvm.exp.f64",
333    "llvm.exp2.f32", "llvm.exp2.f64",
334    "llvm.fabs.f32", "llvm.fabs.f64",
335    "llvm.floor.f32", "llvm.floor.f64",
336    "llvm.fma.f32", "llvm.fma.f64",
337    "llvm.log.f32", "llvm.log.f64",
338    "llvm.log10.f32", "llvm.log10.f64",
339    "llvm.nearbyint.f32", "llvm.nearbyint.f64",
340    "llvm.pow.f32", "llvm.pow.f64",
341    "llvm.powi.f32", "llvm.powi.f64",
342    "llvm.rint.f32", "llvm.rint.f64",
343    "llvm.round.f32", "llvm.round.f64",
344    "llvm.sin.f32", "llvm.sin.f64",
345    "llvm.sqrt.f32", "llvm.sqrt.f64",
346    "llvm.trunc.f32", "llvm.trunc.f64",
347   };
348
349 static bool isIntrinsicInline(Function *F) {
350   return std::binary_search(
351     IntrinsicInline, array_endof(IntrinsicInline),
352     F->getName());
353 }
354 //
355 // Returns of float, double and complex need to be handled with a helper
356 // function.
357 //
358 static bool fixupFPReturnAndCall
359   (Function &F, Module *M,  const MipsSubtarget &Subtarget) {
360   bool Modified = false;
361   LLVMContext &C = M->getContext();
362   Type *MyVoid = Type::getVoidTy(C);
363   for (Function::iterator BB = F.begin(), E = F.end(); BB != E; ++BB)
364     for (BasicBlock::iterator I = BB->begin(), E = BB->end();
365          I != E; ++I) {
366       Instruction &Inst = *I;
367       if (const ReturnInst *RI = dyn_cast<ReturnInst>(I)) {
368         Value *RVal = RI->getReturnValue();
369         if (!RVal) continue;
370         //
371         // If there is a return value and it needs a helper function,
372         // figure out which one and add a call before the actual
373         // return to this helper. The purpose of the helper is to move
374         // floating point values from their soft float return mapping to
375         // where they would have been mapped to in floating point registers.
376         //
377         Type *T = RVal->getType();
378         FPReturnVariant RV = whichFPReturnVariant(T);
379         if (RV == NoFPRet) continue;
380         static const char* Helper[NoFPRet] =
381           {"__mips16_ret_sf", "__mips16_ret_df", "__mips16_ret_sc",
382            "__mips16_ret_dc"};
383         const char *Name = Helper[RV];
384         AttributeSet A;
385         Value *Params[] = {RVal};
386         Modified = true;
387         //
388         // These helper functions have a different calling ABI so
389         // this __Mips16RetHelper indicates that so that later
390         // during call setup, the proper call lowering to the helper
391         // functions will take place.
392         //
393         A = A.addAttribute(C, AttributeSet::FunctionIndex,
394                            "__Mips16RetHelper");
395         A = A.addAttribute(C, AttributeSet::FunctionIndex,
396                            Attribute::ReadNone);
397         A = A.addAttribute(C, AttributeSet::FunctionIndex,
398                            Attribute::NoInline);
399         Value *F = (M->getOrInsertFunction(Name, A, MyVoid, T, NULL));
400         CallInst::Create(F, Params, "", &Inst );
401       } else if (const CallInst *CI = dyn_cast<CallInst>(I)) {
402           // pic mode calls are handled by already defined
403           // helper functions
404           if (Subtarget.getRelocationModel() != Reloc::PIC_ ) {
405             Function *F_ =  CI->getCalledFunction();
406             if (F_ && !isIntrinsicInline(F_) && needsFPHelperFromSig(*F_)) {
407               assureFPCallStub(*F_, M, Subtarget);
408               Modified=true;
409             }
410           }
411       }
412     }
413   return Modified;
414 }
415
416 static void createFPFnStub(Function *F, Module *M, FPParamVariant PV,
417                   const MipsSubtarget &Subtarget ) {
418   bool PicMode = Subtarget.getRelocationModel() == Reloc::PIC_;
419   bool LE = Subtarget.isLittle();
420   LLVMContext &Context = M->getContext();
421   std::string Name = F->getName();
422   std::string SectionName = ".mips16.fn." + Name;
423   std::string StubName = "__fn_stub_" + Name;
424   std::string LocalName = "__fn_local_" + Name;
425   Function *FStub = Function::Create
426     (F->getFunctionType(),
427      Function::InternalLinkage, StubName, M);
428   FStub->addFnAttr("mips16_fp_stub");
429   FStub->addFnAttr(llvm::Attribute::Naked);
430   FStub->addFnAttr(llvm::Attribute::NoUnwind);
431   FStub->addFnAttr(llvm::Attribute::NoInline);
432   FStub->addFnAttr("nomips16");
433   FStub->setSection(SectionName);
434   BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
435   InlineAsmHelper IAH(Context, BB);
436   IAH.Out(" .set  macro");
437   if (PicMode) {
438     IAH.Out(".set noreorder");
439     IAH.Out(".cpload  $$2");
440     IAH.Out(".set reorder");
441     IAH.Out(".reloc 0,R_MIPS_NONE," + Name);
442     IAH.Out("la $$25," + LocalName);
443   }
444   else
445     IAH.Out("la $$25, " + Name);
446   swapFPIntParams(PV, M, IAH, LE, false);
447   IAH.Out("jr $$25");
448   IAH.Out(LocalName + " = " + Name);
449   new UnreachableInst(FStub->getContext(), BB);
450 }
451
452 //
453 // remove the use-soft-float attribute
454 //
455 static void removeUseSoftFloat(Function &F) {
456   AttributeSet A;
457   DEBUG(errs() << "removing -use-soft-float\n");
458   A = A.addAttribute(F.getContext(), AttributeSet::FunctionIndex,
459                      "use-soft-float", "false");
460   F.removeAttributes(AttributeSet::FunctionIndex, A);
461   if (F.hasFnAttribute("use-soft-float")) {
462     DEBUG(errs() << "still has -use-soft-float\n");
463   }
464   F.addAttributes(AttributeSet::FunctionIndex, A);
465 }
466
467 namespace llvm {
468
469 //
470 // This pass only makes sense when the underlying chip has floating point but
471 // we are compiling as mips16.
472 // For all mips16 functions (that are not stubs we have already generated), or
473 // declared via attributes as nomips16, we must:
474 //    1) fixup all returns of float, double, single and double complex
475 //       by calling a helper function before the actual return.
476 //    2) generate helper functions (stubs) that can be called by mips32 functions
477 //       that will move parameters passed normally passed in floating point
478 //       registers the soft float equivalents.
479 //    3) in the case of static relocation, generate helper functions so that
480 //       mips16 functions can call extern functions of unknown type (mips16 or
481 //       mips32).
482 //    4) TBD. For pic, calls to extern functions of unknown type are handled by
483 //       predefined helper functions in libc but this work is currently done
484 //       during call lowering but it should be moved here in the future.
485 //
486 bool Mips16HardFloat::runOnModule(Module &M) {
487   DEBUG(errs() << "Run on Module Mips16HardFloat\n");
488   bool Modified = false;
489   for (Module::iterator F = M.begin(), E = M.end(); F != E; ++F) {
490     if (F->hasFnAttribute("nomips16") &&
491         F->hasFnAttribute("use-soft-float")) {
492       removeUseSoftFloat(*F);
493       continue;
494     }
495     if (F->isDeclaration() || F->hasFnAttribute("mips16_fp_stub") ||
496         F->hasFnAttribute("nomips16")) continue;
497     Modified |= fixupFPReturnAndCall(*F, &M, Subtarget);
498     FPParamVariant V = whichFPParamVariantNeeded(*F);
499     if (V != NoSig) {
500       Modified = true;
501       createFPFnStub(F, &M, V, Subtarget);
502     }
503   }
504   return Modified;
505 }
506
507 char Mips16HardFloat::ID = 0;
508
509 }
510
511 ModulePass *llvm::createMips16HardFloat(MipsTargetMachine &TM) {
512   return new Mips16HardFloat(TM);
513 }
514