Fix a problem with mips16 stubs when calls are transformed during
[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/Value.h"
17 #include "llvm/IR/Module.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(
358     IntrinsicInline, array_endof(IntrinsicInline),
359     F->getName());
360 }
361 //
362 // Returns of float, double and complex need to be handled with a helper
363 // function.
364 //
365 static bool fixupFPReturnAndCall
366   (Function &F, Module *M,  const MipsSubtarget &Subtarget) {
367   bool Modified = false;
368   LLVMContext &C = M->getContext();
369   Type *MyVoid = Type::getVoidTy(C);
370   for (Function::iterator BB = F.begin(), E = F.end(); BB != E; ++BB)
371     for (BasicBlock::iterator I = BB->begin(), E = BB->end();
372          I != E; ++I) {
373       Instruction &Inst = *I;
374       if (const ReturnInst *RI = dyn_cast<ReturnInst>(I)) {
375         Value *RVal = RI->getReturnValue();
376         if (!RVal) continue;
377         //
378         // If there is a return value and it needs a helper function,
379         // figure out which one and add a call before the actual
380         // return to this helper. The purpose of the helper is to move
381         // floating point values from their soft float return mapping to
382         // where they would have been mapped to in floating point registers.
383         //
384         Type *T = RVal->getType();
385         FPReturnVariant RV = whichFPReturnVariant(T);
386         if (RV == NoFPRet) continue;
387         static const char* Helper[NoFPRet] =
388           {"__mips16_ret_sf", "__mips16_ret_df", "__mips16_ret_sc",
389            "__mips16_ret_dc"};
390         const char *Name = Helper[RV];
391         AttributeSet A;
392         Value *Params[] = {RVal};
393         Modified = true;
394         //
395         // These helper functions have a different calling ABI so
396         // this __Mips16RetHelper indicates that so that later
397         // during call setup, the proper call lowering to the helper
398         // functions will take place.
399         //
400         A = A.addAttribute(C, AttributeSet::FunctionIndex,
401                            "__Mips16RetHelper");
402         A = A.addAttribute(C, AttributeSet::FunctionIndex,
403                            Attribute::ReadNone);
404         A = A.addAttribute(C, AttributeSet::FunctionIndex,
405                            Attribute::NoInline);
406         Value *F = (M->getOrInsertFunction(Name, A, MyVoid, T, NULL));
407         CallInst::Create(F, Params, "", &Inst );
408       } else if (const CallInst *CI = dyn_cast<CallInst>(I)) {
409           const Value* V = CI->getCalledValue();
410           const Type* T = 0;
411           if (V) T = V->getType();
412           const PointerType *PFT=0;
413           if (T) PFT = dyn_cast<PointerType>(T);
414           const FunctionType *FT=0;
415           if (PFT) FT = dyn_cast<FunctionType>(PFT->getElementType());
416           Function *F_ =  CI->getCalledFunction();
417           if (FT && needsFPReturnHelper(*FT) &&
418               !(F_ && isIntrinsicInline(F_))) {
419             Modified=true;
420             F.addFnAttr("saveS2");
421           }
422           if (F_ && !isIntrinsicInline(F_)) {
423           // pic mode calls are handled by already defined
424           // helper functions
425             if (needsFPReturnHelper(*F_)) {
426               Modified=true;
427               F.addFnAttr("saveS2");
428             }
429             if (Subtarget.getRelocationModel() != Reloc::PIC_ ) {
430               if (needsFPHelperFromSig(*F_)) {
431                 assureFPCallStub(*F_, M, Subtarget);
432                 Modified=true;
433               }
434             }
435           }
436       }
437     }
438   return Modified;
439 }
440
441 static void createFPFnStub(Function *F, Module *M, FPParamVariant PV,
442                   const MipsSubtarget &Subtarget ) {
443   bool PicMode = Subtarget.getRelocationModel() == Reloc::PIC_;
444   bool LE = Subtarget.isLittle();
445   LLVMContext &Context = M->getContext();
446   std::string Name = F->getName();
447   std::string SectionName = ".mips16.fn." + Name;
448   std::string StubName = "__fn_stub_" + Name;
449   std::string LocalName = "$$__fn_local_" + Name;
450   Function *FStub = Function::Create
451     (F->getFunctionType(),
452      Function::InternalLinkage, StubName, M);
453   FStub->addFnAttr("mips16_fp_stub");
454   FStub->addFnAttr(llvm::Attribute::Naked);
455   FStub->addFnAttr(llvm::Attribute::NoUnwind);
456   FStub->addFnAttr(llvm::Attribute::NoInline);
457   FStub->addFnAttr("nomips16");
458   FStub->setSection(SectionName);
459   BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
460   InlineAsmHelper IAH(Context, BB);
461   IAH.Out(" .set  macro");
462   if (PicMode) {
463     IAH.Out(".set noreorder");
464     IAH.Out(".cpload  $$25");
465     IAH.Out(".set reorder");
466     IAH.Out(".reloc 0,R_MIPS_NONE," + Name);
467     IAH.Out("la $$25," + LocalName);
468   }
469   else {
470     IAH.Out(".set reorder");
471     IAH.Out("la $$25," + Name);
472   }
473   swapFPIntParams(PV, M, IAH, LE, false);
474   IAH.Out("jr $$25");
475   IAH.Out(LocalName + " = " + Name);
476   new UnreachableInst(FStub->getContext(), BB);
477 }
478
479 //
480 // remove the use-soft-float attribute
481 //
482 static void removeUseSoftFloat(Function &F) {
483   AttributeSet A;
484   DEBUG(errs() << "removing -use-soft-float\n");
485   A = A.addAttribute(F.getContext(), AttributeSet::FunctionIndex,
486                      "use-soft-float", "false");
487   F.removeAttributes(AttributeSet::FunctionIndex, A);
488   if (F.hasFnAttribute("use-soft-float")) {
489     DEBUG(errs() << "still has -use-soft-float\n");
490   }
491   F.addAttributes(AttributeSet::FunctionIndex, A);
492 }
493
494 namespace llvm {
495
496 //
497 // This pass only makes sense when the underlying chip has floating point but
498 // we are compiling as mips16.
499 // For all mips16 functions (that are not stubs we have already generated), or
500 // declared via attributes as nomips16, we must:
501 //    1) fixup all returns of float, double, single and double complex
502 //       by calling a helper function before the actual return.
503 //    2) generate helper functions (stubs) that can be called by mips32 functions
504 //       that will move parameters passed normally passed in 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