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