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