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 
Mips16HardFloat(MipsTargetMachine & TM_)31     Mips16HardFloat(MipsTargetMachine &TM_) : ModulePass(ID), TM(TM_) {}
32 
getPassName() const33     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 
EmitInlineAsm(LLVMContext & C,BasicBlock * BB,StringRef AsmText)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 //
whichFPReturnVariant(Type * T)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 
whichFPParamVariantNeeded(Function & F)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 //
needsFPStubFromParams(Function & F)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 
needsFPReturnHelper(Function & F)171 static bool needsFPReturnHelper(Function &F) {
172   Type* RetType = F.getReturnType();
173   return whichFPReturnVariant(RetType) != NoFPRet;
174 }
175 
needsFPReturnHelper(FunctionType & FT)176 static bool needsFPReturnHelper(FunctionType &FT) {
177   Type* RetType = FT.getReturnType();
178   return whichFPReturnVariant(RetType) != NoFPRet;
179 }
180 
needsFPHelperFromSig(Function & F)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 //
swapFPIntParams(FPParamVariant PV,Module * M,bool LE,bool ToFP)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 //
assureFPCallStub(Function & F,Module * M,const MipsTargetMachine & TM)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 
isIntrinsicInline(Function * F)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 //
fixupFPReturnAndCall(Function & F,Module * M,const MipsTargetMachine & TM)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         const Value* V = CI->getCalledValue();
431         Type* T = nullptr;
432         if (V) T = V->getType();
433         PointerType *PFT = nullptr;
434         if (T) PFT = dyn_cast<PointerType>(T);
435         FunctionType *FT = nullptr;
436         if (PFT) FT = dyn_cast<FunctionType>(PFT->getElementType());
437         Function *F_ =  CI->getCalledFunction();
438         if (FT && needsFPReturnHelper(*FT) &&
439             !(F_ && isIntrinsicInline(F_))) {
440           Modified=true;
441           F.addFnAttr("saveS2");
442         }
443         if (F_ && !isIntrinsicInline(F_)) {
444           // pic mode calls are handled by already defined
445           // helper functions
446           if (needsFPReturnHelper(*F_)) {
447             Modified=true;
448             F.addFnAttr("saveS2");
449           }
450           if (TM.getRelocationModel() != Reloc::PIC_ ) {
451             if (needsFPHelperFromSig(*F_)) {
452               assureFPCallStub(*F_, M, TM);
453               Modified=true;
454             }
455           }
456         }
457       }
458     }
459   return Modified;
460 }
461 
createFPFnStub(Function * F,Module * M,FPParamVariant PV,const MipsTargetMachine & TM)462 static void createFPFnStub(Function *F, Module *M, FPParamVariant PV,
463                            const MipsTargetMachine &TM) {
464   bool PicMode = TM.getRelocationModel() == Reloc::PIC_;
465   bool LE = TM.isLittleEndian();
466   LLVMContext &Context = M->getContext();
467   std::string Name = F->getName();
468   std::string SectionName = ".mips16.fn." + Name;
469   std::string StubName = "__fn_stub_" + Name;
470   std::string LocalName = "$$__fn_local_" + Name;
471   Function *FStub = Function::Create
472     (F->getFunctionType(),
473      Function::InternalLinkage, StubName, M);
474   FStub->addFnAttr("mips16_fp_stub");
475   FStub->addFnAttr(llvm::Attribute::Naked);
476   FStub->addFnAttr(llvm::Attribute::NoUnwind);
477   FStub->addFnAttr(llvm::Attribute::NoInline);
478   FStub->addFnAttr("nomips16");
479   FStub->setSection(SectionName);
480   BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
481 
482   std::string AsmText;
483   if (PicMode) {
484     AsmText += ".set noreorder\n";
485     AsmText += ".cpload $$25\n";
486     AsmText += ".set reorder\n";
487     AsmText += ".reloc 0, R_MIPS_NONE, " + Name + "\n";
488     AsmText += "la $$25, " + LocalName + "\n";
489   } else
490     AsmText += "la $$25, " + Name + "\n";
491   AsmText += swapFPIntParams(PV, M, LE, false);
492   AsmText += "jr $$25\n";
493   AsmText += LocalName + " = " + Name + "\n";
494   EmitInlineAsm(Context, BB, AsmText);
495 
496   new UnreachableInst(FStub->getContext(), BB);
497 }
498 
499 //
500 // remove the use-soft-float attribute
501 //
removeUseSoftFloat(Function & F)502 static void removeUseSoftFloat(Function &F) {
503   AttributeSet A;
504   DEBUG(errs() << "removing -use-soft-float\n");
505   A = A.addAttribute(F.getContext(), AttributeSet::FunctionIndex,
506                      "use-soft-float", "false");
507   F.removeAttributes(AttributeSet::FunctionIndex, A);
508   if (F.hasFnAttribute("use-soft-float")) {
509     DEBUG(errs() << "still has -use-soft-float\n");
510   }
511   F.addAttributes(AttributeSet::FunctionIndex, A);
512 }
513 
514 
515 //
516 // This pass only makes sense when the underlying chip has floating point but
517 // we are compiling as mips16.
518 // For all mips16 functions (that are not stubs we have already generated), or
519 // declared via attributes as nomips16, we must:
520 //    1) fixup all returns of float, double, single and double complex
521 //       by calling a helper function before the actual return.
522 //    2) generate helper functions (stubs) that can be called by mips32
523 //       functions that will move parameters passed normally passed in
524 //       floating point
525 //       registers the soft float equivalents.
526 //    3) in the case of static relocation, generate helper functions so that
527 //       mips16 functions can call extern functions of unknown type (mips16 or
528 //       mips32).
529 //    4) TBD. For pic, calls to extern functions of unknown type are handled by
530 //       predefined helper functions in libc but this work is currently done
531 //       during call lowering but it should be moved here in the future.
532 //
runOnModule(Module & M)533 bool Mips16HardFloat::runOnModule(Module &M) {
534   DEBUG(errs() << "Run on Module Mips16HardFloat\n");
535   bool Modified = false;
536   for (Module::iterator F = M.begin(), E = M.end(); F != E; ++F) {
537     if (F->hasFnAttribute("nomips16") &&
538         F->hasFnAttribute("use-soft-float")) {
539       removeUseSoftFloat(*F);
540       continue;
541     }
542     if (F->isDeclaration() || F->hasFnAttribute("mips16_fp_stub") ||
543         F->hasFnAttribute("nomips16")) continue;
544     Modified |= fixupFPReturnAndCall(*F, &M, TM);
545     FPParamVariant V = whichFPParamVariantNeeded(*F);
546     if (V != NoSig) {
547       Modified = true;
548       createFPFnStub(&*F, &M, V, TM);
549     }
550   }
551   return Modified;
552 }
553 
554 
createMips16HardFloatPass(MipsTargetMachine & TM)555 ModulePass *llvm::createMips16HardFloatPass(MipsTargetMachine &TM) {
556   return new Mips16HardFloat(TM);
557 }
558