]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/llvm/lib/Target/Mips/Mips16HardFloat.cpp
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / llvm / 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           // pic mode calls are handled by already defined
404           // helper functions
405           if (Subtarget.getRelocationModel() != Reloc::PIC_ ) {
406             Function *F_ =  CI->getCalledFunction();
407             if (F_ && !isIntrinsicInline(F_) && needsFPHelperFromSig(*F_)) {
408               assureFPCallStub(*F_, M, Subtarget);
409               Modified=true;
410             }
411           }
412       }
413     }
414   return Modified;
415 }
416
417 static void createFPFnStub(Function *F, Module *M, FPParamVariant PV,
418                   const MipsSubtarget &Subtarget ) {
419   bool PicMode = Subtarget.getRelocationModel() == Reloc::PIC_;
420   bool LE = Subtarget.isLittle();
421   LLVMContext &Context = M->getContext();
422   std::string Name = F->getName();
423   std::string SectionName = ".mips16.fn." + Name;
424   std::string StubName = "__fn_stub_" + Name;
425   std::string LocalName = "$$__fn_local_" + Name;
426   Function *FStub = Function::Create
427     (F->getFunctionType(),
428      Function::InternalLinkage, StubName, M);
429   FStub->addFnAttr("mips16_fp_stub");
430   FStub->addFnAttr(llvm::Attribute::Naked);
431   FStub->addFnAttr(llvm::Attribute::NoUnwind);
432   FStub->addFnAttr(llvm::Attribute::NoInline);
433   FStub->addFnAttr("nomips16");
434   FStub->setSection(SectionName);
435   BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
436   InlineAsmHelper IAH(Context, BB);
437   IAH.Out(" .set  macro");
438   if (PicMode) {
439     IAH.Out(".set noreorder");
440     IAH.Out(".cpload  $$25");
441     IAH.Out(".set reorder");
442     IAH.Out(".reloc 0,R_MIPS_NONE," + Name);
443     IAH.Out("la $$25," + LocalName);
444   }
445   else {
446     IAH.Out(".set reorder");
447     IAH.Out("la $$25," + Name);
448   }
449   swapFPIntParams(PV, M, IAH, LE, false);
450   IAH.Out("jr $$25");
451   IAH.Out(LocalName + " = " + Name);
452   new UnreachableInst(FStub->getContext(), BB);
453 }
454
455 //
456 // remove the use-soft-float attribute
457 //
458 static void removeUseSoftFloat(Function &F) {
459   AttributeSet A;
460   DEBUG(errs() << "removing -use-soft-float\n");
461   A = A.addAttribute(F.getContext(), AttributeSet::FunctionIndex,
462                      "use-soft-float", "false");
463   F.removeAttributes(AttributeSet::FunctionIndex, A);
464   if (F.hasFnAttribute("use-soft-float")) {
465     DEBUG(errs() << "still has -use-soft-float\n");
466   }
467   F.addAttributes(AttributeSet::FunctionIndex, A);
468 }
469
470 namespace llvm {
471
472 //
473 // This pass only makes sense when the underlying chip has floating point but
474 // we are compiling as mips16.
475 // For all mips16 functions (that are not stubs we have already generated), or
476 // declared via attributes as nomips16, we must:
477 //    1) fixup all returns of float, double, single and double complex
478 //       by calling a helper function before the actual return.
479 //    2) generate helper functions (stubs) that can be called by mips32 functions
480 //       that will move parameters passed normally passed in floating point
481 //       registers the soft float equivalents.
482 //    3) in the case of static relocation, generate helper functions so that
483 //       mips16 functions can call extern functions of unknown type (mips16 or
484 //       mips32).
485 //    4) TBD. For pic, calls to extern functions of unknown type are handled by
486 //       predefined helper functions in libc but this work is currently done
487 //       during call lowering but it should be moved here in the future.
488 //
489 bool Mips16HardFloat::runOnModule(Module &M) {
490   DEBUG(errs() << "Run on Module Mips16HardFloat\n");
491   bool Modified = false;
492   for (Module::iterator F = M.begin(), E = M.end(); F != E; ++F) {
493     if (F->hasFnAttribute("nomips16") &&
494         F->hasFnAttribute("use-soft-float")) {
495       removeUseSoftFloat(*F);
496       continue;
497     }
498     if (F->isDeclaration() || F->hasFnAttribute("mips16_fp_stub") ||
499         F->hasFnAttribute("nomips16")) continue;
500     Modified |= fixupFPReturnAndCall(*F, &M, Subtarget);
501     FPParamVariant V = whichFPParamVariantNeeded(*F);
502     if (V != NoSig) {
503       Modified = true;
504       createFPFnStub(F, &M, V, Subtarget);
505     }
506   }
507   return Modified;
508 }
509
510 char Mips16HardFloat::ID = 0;
511
512 }
513
514 ModulePass *llvm::createMips16HardFloat(MipsTargetMachine &TM) {
515   return new Mips16HardFloat(TM);
516 }
517