]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/lib/Target/Mips/Mips16HardFloat.cpp
Merge llvm, clang, compiler-rt, libc++, libunwind, lld, lldb and openmp
[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/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
29   class Mips16HardFloat : public ModulePass {
30   public:
31     static char ID;
32
33     Mips16HardFloat() : ModulePass(ID) {}
34
35     StringRef getPassName() const override { return "MIPS16 Hard Float Pass"; }
36
37     void getAnalysisUsage(AnalysisUsage &AU) const override {
38       AU.addRequired<TargetPassConfig>();
39       ModulePass::getAnalysisUsage(AU);
40     }
41
42     bool runOnModule(Module &M) override;
43   };
44
45 } // end anonymous namespace
46
47 static void EmitInlineAsm(LLVMContext &C, BasicBlock *BB, StringRef AsmText) {
48   std::vector<Type *> AsmArgTypes;
49   std::vector<Value *> AsmArgs;
50
51   FunctionType *AsmFTy =
52       FunctionType::get(Type::getVoidTy(C), AsmArgTypes, false);
53   InlineAsm *IA = InlineAsm::get(AsmFTy, AsmText, "", true,
54                                  /* IsAlignStack */ false, InlineAsm::AD_ATT);
55   CallInst::Create(IA, AsmArgs, "", BB);
56 }
57
58 char Mips16HardFloat::ID = 0;
59
60 //
61 // Return types that matter for hard float are:
62 // float, double, complex float, and complex double
63 //
64 enum FPReturnVariant {
65   FRet, DRet, CFRet, CDRet, NoFPRet
66 };
67
68 //
69 // Determine which FP return type this function has
70 //
71 static FPReturnVariant whichFPReturnVariant(Type *T) {
72   switch (T->getTypeID()) {
73   case Type::FloatTyID:
74     return FRet;
75   case Type::DoubleTyID:
76     return DRet;
77   case Type::StructTyID: {
78     StructType *ST = cast<StructType>(T);
79     if (ST->getNumElements() != 2)
80       break;
81     if ((ST->getElementType(0)->isFloatTy()) &&
82         (ST->getElementType(1)->isFloatTy()))
83       return CFRet;
84     if ((ST->getElementType(0)->isDoubleTy()) &&
85         (ST->getElementType(1)->isDoubleTy()))
86       return CDRet;
87     break;
88   }
89   default:
90     break;
91   }
92   return NoFPRet;
93 }
94
95 // Parameter type that matter are float, (float, float), (float, double),
96 // double, (double, double), (double, float)
97 enum FPParamVariant {
98   FSig, FFSig, FDSig,
99   DSig, DDSig, DFSig, NoSig
100 };
101
102 // which floating point parameter signature variant we are dealing with
103 using TypeID = Type::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 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 // We swap between FP and Integer registers to allow Mips16 and Mips32 to
186 // interoperate
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 // Make sure that we know we already need a stub for this function.
256 // Having called needsFPHelperFromSig
257 static void assureFPCallStub(Function &F, Module *M,
258                              const MipsTargetMachine &TM) {
259   // for now we only need them for static relocation
260   if (TM.isPositionIndependent())
261     return;
262   LLVMContext &Context = M->getContext();
263   bool LE = TM.isLittleEndian();
264   std::string Name = F.getName();
265   std::string SectionName = ".mips16.call.fp." + Name;
266   std::string StubName = "__call_stub_fp_" + Name;
267   //
268   // see if we already have the stub
269   //
270   Function *FStub = M->getFunction(StubName);
271   if (FStub && !FStub->isDeclaration()) return;
272   FStub = Function::Create(F.getFunctionType(),
273                            Function::InternalLinkage, StubName, M);
274   FStub->addFnAttr("mips16_fp_stub");
275   FStub->addFnAttr(Attribute::Naked);
276   FStub->addFnAttr(Attribute::NoInline);
277   FStub->addFnAttr(Attribute::NoUnwind);
278   FStub->addFnAttr("nomips16");
279   FStub->setSection(SectionName);
280   BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
281   FPReturnVariant RV = whichFPReturnVariant(FStub->getReturnType());
282   FPParamVariant PV = whichFPParamVariantNeeded(F);
283
284   std::string AsmText;
285   AsmText += ".set reorder\n";
286   AsmText += swapFPIntParams(PV, M, LE, true);
287   if (RV != NoFPRet) {
288     AsmText += "move $$18, $$31\n";
289     AsmText += "jal " + Name + "\n";
290   } else {
291     AsmText += "lui  $$25, %hi(" + Name + ")\n";
292     AsmText += "addiu  $$25, $$25, %lo(" + Name + ")\n";
293   }
294
295   switch (RV) {
296   case FRet:
297     AsmText += "mfc1 $$2, $$f0\n";
298     break;
299
300   case DRet:
301     if (LE) {
302       AsmText += "mfc1 $$2, $$f0\n";
303       AsmText += "mfc1 $$3, $$f1\n";
304     } else {
305       AsmText += "mfc1 $$3, $$f0\n";
306       AsmText += "mfc1 $$2, $$f1\n";
307     }
308     break;
309
310   case CFRet:
311     if (LE) {
312       AsmText += "mfc1 $$2, $$f0\n";
313       AsmText += "mfc1 $$3, $$f2\n";
314     } else {
315       AsmText += "mfc1 $$3, $$f0\n";
316       AsmText += "mfc1 $$3, $$f2\n";
317     }
318     break;
319
320   case CDRet:
321     if (LE) {
322       AsmText += "mfc1 $$4, $$f2\n";
323       AsmText += "mfc1 $$5, $$f3\n";
324       AsmText += "mfc1 $$2, $$f0\n";
325       AsmText += "mfc1 $$3, $$f1\n";
326
327     } else {
328       AsmText += "mfc1 $$5, $$f2\n";
329       AsmText += "mfc1 $$4, $$f3\n";
330       AsmText += "mfc1 $$3, $$f0\n";
331       AsmText += "mfc1 $$2, $$f1\n";
332     }
333     break;
334
335   case NoFPRet:
336     break;
337   }
338
339   if (RV != NoFPRet)
340     AsmText += "jr $$18\n";
341   else
342     AsmText += "jr $$25\n";
343   EmitInlineAsm(Context, BB, AsmText);
344
345   new UnreachableInst(Context, BB);
346 }
347
348 // Functions that are llvm intrinsics and don't need helpers.
349 static const char *const IntrinsicInline[] = {
350   "fabs", "fabsf",
351   "llvm.ceil.f32", "llvm.ceil.f64",
352   "llvm.copysign.f32", "llvm.copysign.f64",
353   "llvm.cos.f32", "llvm.cos.f64",
354   "llvm.exp.f32", "llvm.exp.f64",
355   "llvm.exp2.f32", "llvm.exp2.f64",
356   "llvm.fabs.f32", "llvm.fabs.f64",
357   "llvm.floor.f32", "llvm.floor.f64",
358   "llvm.fma.f32", "llvm.fma.f64",
359   "llvm.log.f32", "llvm.log.f64",
360   "llvm.log10.f32", "llvm.log10.f64",
361   "llvm.nearbyint.f32", "llvm.nearbyint.f64",
362   "llvm.pow.f32", "llvm.pow.f64",
363   "llvm.powi.f32", "llvm.powi.f64",
364   "llvm.rint.f32", "llvm.rint.f64",
365   "llvm.round.f32", "llvm.round.f64",
366   "llvm.sin.f32", "llvm.sin.f64",
367   "llvm.sqrt.f32", "llvm.sqrt.f64",
368   "llvm.trunc.f32", "llvm.trunc.f64",
369 };
370
371 static bool isIntrinsicInline(Function *F) {
372   return std::binary_search(std::begin(IntrinsicInline),
373                             std::end(IntrinsicInline), F->getName());
374 }
375
376 // Returns of float, double and complex need to be handled with a helper
377 // function.
378 static bool fixupFPReturnAndCall(Function &F, Module *M,
379                                  const MipsTargetMachine &TM) {
380   bool Modified = false;
381   LLVMContext &C = M->getContext();
382   Type *MyVoid = Type::getVoidTy(C);
383   for (auto &BB: F)
384     for (auto &I: BB) {
385       if (const ReturnInst *RI = dyn_cast<ReturnInst>(&I)) {
386         Value *RVal = RI->getReturnValue();
387         if (!RVal) continue;
388         //
389         // If there is a return value and it needs a helper function,
390         // figure out which one and add a call before the actual
391         // return to this helper. The purpose of the helper is to move
392         // floating point values from their soft float return mapping to
393         // where they would have been mapped to in floating point registers.
394         //
395         Type *T = RVal->getType();
396         FPReturnVariant RV = whichFPReturnVariant(T);
397         if (RV == NoFPRet) continue;
398         static const char *const Helper[NoFPRet] = {
399           "__mips16_ret_sf", "__mips16_ret_df", "__mips16_ret_sc",
400           "__mips16_ret_dc"
401         };
402         const char *Name = Helper[RV];
403         AttributeList A;
404         Value *Params[] = {RVal};
405         Modified = true;
406         //
407         // These helper functions have a different calling ABI so
408         // this __Mips16RetHelper indicates that so that later
409         // during call setup, the proper call lowering to the helper
410         // functions will take place.
411         //
412         A = A.addAttribute(C, AttributeList::FunctionIndex,
413                            "__Mips16RetHelper");
414         A = A.addAttribute(C, AttributeList::FunctionIndex,
415                            Attribute::ReadNone);
416         A = A.addAttribute(C, AttributeList::FunctionIndex,
417                            Attribute::NoInline);
418         Value *F = (M->getOrInsertFunction(Name, A, MyVoid, T));
419         CallInst::Create(F, Params, "", &I);
420       } else if (const CallInst *CI = dyn_cast<CallInst>(&I)) {
421         FunctionType *FT = CI->getFunctionType();
422         Function *F_ =  CI->getCalledFunction();
423         if (needsFPReturnHelper(*FT) &&
424             !(F_ && isIntrinsicInline(F_))) {
425           Modified=true;
426           F.addFnAttr("saveS2");
427         }
428         if (F_ && !isIntrinsicInline(F_)) {
429           // pic mode calls are handled by already defined
430           // helper functions
431           if (needsFPReturnHelper(*F_)) {
432             Modified=true;
433             F.addFnAttr("saveS2");
434           }
435           if (!TM.isPositionIndependent()) {
436             if (needsFPHelperFromSig(*F_)) {
437               assureFPCallStub(*F_, M, TM);
438               Modified=true;
439             }
440           }
441         }
442       }
443     }
444   return Modified;
445 }
446
447 static void createFPFnStub(Function *F, Module *M, FPParamVariant PV,
448                            const MipsTargetMachine &TM) {
449   bool PicMode = TM.isPositionIndependent();
450   bool LE = TM.isLittleEndian();
451   LLVMContext &Context = M->getContext();
452   std::string Name = F->getName();
453   std::string SectionName = ".mips16.fn." + Name;
454   std::string StubName = "__fn_stub_" + Name;
455   std::string LocalName = "$$__fn_local_" + Name;
456   Function *FStub = Function::Create
457     (F->getFunctionType(),
458      Function::InternalLinkage, StubName, M);
459   FStub->addFnAttr("mips16_fp_stub");
460   FStub->addFnAttr(Attribute::Naked);
461   FStub->addFnAttr(Attribute::NoUnwind);
462   FStub->addFnAttr(Attribute::NoInline);
463   FStub->addFnAttr("nomips16");
464   FStub->setSection(SectionName);
465   BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
466
467   std::string AsmText;
468   if (PicMode) {
469     AsmText += ".set noreorder\n";
470     AsmText += ".cpload $$25\n";
471     AsmText += ".set reorder\n";
472     AsmText += ".reloc 0, R_MIPS_NONE, " + Name + "\n";
473     AsmText += "la $$25, " + LocalName + "\n";
474   } else
475     AsmText += "la $$25, " + Name + "\n";
476   AsmText += swapFPIntParams(PV, M, LE, false);
477   AsmText += "jr $$25\n";
478   AsmText += LocalName + " = " + Name + "\n";
479   EmitInlineAsm(Context, BB, AsmText);
480
481   new UnreachableInst(FStub->getContext(), BB);
482 }
483
484 // remove the use-soft-float attribute
485 static void removeUseSoftFloat(Function &F) {
486   AttrBuilder B;
487   LLVM_DEBUG(errs() << "removing -use-soft-float\n");
488   B.addAttribute("use-soft-float", "false");
489   F.removeAttributes(AttributeList::FunctionIndex, B);
490   if (F.hasFnAttribute("use-soft-float")) {
491     LLVM_DEBUG(errs() << "still has -use-soft-float\n");
492   }
493   F.addAttributes(AttributeList::FunctionIndex, B);
494 }
495
496 // This pass only makes sense when the underlying chip has floating point but
497 // we are compiling as mips16.
498 // For all mips16 functions (that are not stubs we have already generated), or
499 // declared via attributes as nomips16, we must:
500 //    1) fixup all returns of float, double, single and double complex
501 //       by calling a helper function before the actual return.
502 //    2) generate helper functions (stubs) that can be called by mips32
503 //       functions that will move parameters passed normally passed in
504 //       floating point
505 //       registers the soft float equivalents.
506 //    3) in the case of static relocation, generate helper functions so that
507 //       mips16 functions can call extern functions of unknown type (mips16 or
508 //       mips32).
509 //    4) TBD. For pic, calls to extern functions of unknown type are handled by
510 //       predefined helper functions in libc but this work is currently done
511 //       during call lowering but it should be moved here in the future.
512 bool Mips16HardFloat::runOnModule(Module &M) {
513   auto &TM = static_cast<const MipsTargetMachine &>(
514       getAnalysis<TargetPassConfig>().getTM<TargetMachine>());
515   LLVM_DEBUG(errs() << "Run on Module Mips16HardFloat\n");
516   bool Modified = false;
517   for (Module::iterator F = M.begin(), E = M.end(); F != E; ++F) {
518     if (F->hasFnAttribute("nomips16") &&
519         F->hasFnAttribute("use-soft-float")) {
520       removeUseSoftFloat(*F);
521       continue;
522     }
523     if (F->isDeclaration() || F->hasFnAttribute("mips16_fp_stub") ||
524         F->hasFnAttribute("nomips16")) continue;
525     Modified |= fixupFPReturnAndCall(*F, &M, TM);
526     FPParamVariant V = whichFPParamVariantNeeded(*F);
527     if (V != NoSig) {
528       Modified = true;
529       createFPFnStub(&*F, &M, V, TM);
530     }
531   }
532   return Modified;
533 }
534
535 ModulePass *llvm::createMips16HardFloatPass() {
536   return new Mips16HardFloat();
537 }