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