1 //===- Mips16HardFloat.cpp for Mips16 Hard Float --------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file defines a pass needed for Mips16 Hard Float
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "MipsTargetMachine.h"
14 #include "llvm/CodeGen/TargetPassConfig.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 
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 } // end anonymous namespace
45 
46 static void emitInlineAsm(LLVMContext &C, BasicBlock *BB, StringRef AsmText) {
47   std::vector<Type *> AsmArgTypes;
48   std::vector<Value *> AsmArgs;
49 
50   FunctionType *AsmFTy =
51       FunctionType::get(Type::getVoidTy(C), AsmArgTypes, false);
52   InlineAsm *IA = InlineAsm::get(AsmFTy, AsmText, "", true,
53                                  /* IsAlignStack */ false, InlineAsm::AD_ATT);
54   CallInst::Create(IA, AsmArgs, "", BB);
55 }
56 
57 char Mips16HardFloat::ID = 0;
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     StructType *ST = cast<StructType>(T);
78     if (ST->getNumElements() != 2)
79       break;
80     if ((ST->getElementType(0)->isFloatTy()) &&
81         (ST->getElementType(1)->isFloatTy()))
82       return CFRet;
83     if ((ST->getElementType(0)->isDoubleTy()) &&
84         (ST->getElementType(1)->isDoubleTy()))
85       return CDRet;
86     break;
87   }
88   default:
89     break;
90   }
91   return NoFPRet;
92 }
93 
94 // Parameter type that matter are float, (float, float), (float, double),
95 // double, (double, double), (double, float)
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 using TypeID = Type::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 static bool needsFPStubFromParams(Function &F) {
157   if (F.arg_size() >=1) {
158     Type *ArgType = F.getFunctionType()->getParamType(0);
159     switch (ArgType->getTypeID()) {
160     case Type::FloatTyID:
161     case Type::DoubleTyID:
162       return true;
163     default:
164       break;
165     }
166   }
167   return false;
168 }
169 
170 static bool needsFPReturnHelper(Function &F) {
171   Type* RetType = F.getReturnType();
172   return whichFPReturnVariant(RetType) != NoFPRet;
173 }
174 
175 static bool needsFPReturnHelper(FunctionType &FT) {
176   Type* RetType = FT.getReturnType();
177   return whichFPReturnVariant(RetType) != NoFPRet;
178 }
179 
180 static bool needsFPHelperFromSig(Function &F) {
181   return needsFPStubFromParams(F) || needsFPReturnHelper(F);
182 }
183 
184 // We swap between FP and Integer registers to allow Mips16 and Mips32 to
185 // interoperate
186 static std::string swapFPIntParams(FPParamVariant PV, Module *M, bool LE,
187                                    bool ToFP) {
188   std::string MI = ToFP ? "mtc1 ": "mfc1 ";
189   std::string AsmText;
190 
191   switch (PV) {
192   case FSig:
193     AsmText += MI + "$$4, $$f12\n";
194     break;
195 
196   case FFSig:
197     AsmText += MI + "$$4, $$f12\n";
198     AsmText += MI + "$$5, $$f14\n";
199     break;
200 
201   case FDSig:
202     AsmText += MI + "$$4, $$f12\n";
203     if (LE) {
204       AsmText += MI + "$$6, $$f14\n";
205       AsmText += MI + "$$7, $$f15\n";
206     } else {
207       AsmText += MI + "$$7, $$f14\n";
208       AsmText += MI + "$$6, $$f15\n";
209     }
210     break;
211 
212   case DSig:
213     if (LE) {
214       AsmText += MI + "$$4, $$f12\n";
215       AsmText += MI + "$$5, $$f13\n";
216     } else {
217       AsmText += MI + "$$5, $$f12\n";
218       AsmText += MI + "$$4, $$f13\n";
219     }
220     break;
221 
222   case DDSig:
223     if (LE) {
224       AsmText += MI + "$$4, $$f12\n";
225       AsmText += MI + "$$5, $$f13\n";
226       AsmText += MI + "$$6, $$f14\n";
227       AsmText += MI + "$$7, $$f15\n";
228     } else {
229       AsmText += MI + "$$5, $$f12\n";
230       AsmText += MI + "$$4, $$f13\n";
231       AsmText += MI + "$$7, $$f14\n";
232       AsmText += MI + "$$6, $$f15\n";
233     }
234     break;
235 
236   case DFSig:
237     if (LE) {
238       AsmText += MI + "$$4, $$f12\n";
239       AsmText += MI + "$$5, $$f13\n";
240     } else {
241       AsmText += MI + "$$5, $$f12\n";
242       AsmText += MI + "$$4, $$f13\n";
243     }
244     AsmText += MI + "$$6, $$f14\n";
245     break;
246 
247   case NoSig:
248     break;
249   }
250 
251   return AsmText;
252 }
253 
254 // Make sure that we know we already need a stub for this function.
255 // Having called needsFPHelperFromSig
256 static void assureFPCallStub(Function &F, Module *M,
257                              const MipsTargetMachine &TM) {
258   // for now we only need them for static relocation
259   if (TM.isPositionIndependent())
260     return;
261   LLVMContext &Context = M->getContext();
262   bool LE = TM.isLittleEndian();
263   std::string Name(F.getName());
264   std::string SectionName = ".mips16.call.fp." + Name;
265   std::string StubName = "__call_stub_fp_" + Name;
266   //
267   // see if we already have the stub
268   //
269   Function *FStub = M->getFunction(StubName);
270   if (FStub && !FStub->isDeclaration()) return;
271   FStub = Function::Create(F.getFunctionType(),
272                            Function::InternalLinkage, StubName, M);
273   FStub->addFnAttr("mips16_fp_stub");
274   FStub->addFnAttr(Attribute::Naked);
275   FStub->addFnAttr(Attribute::NoInline);
276   FStub->addFnAttr(Attribute::NoUnwind);
277   FStub->addFnAttr("nomips16");
278   FStub->setSection(SectionName);
279   BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
280   FPReturnVariant RV = whichFPReturnVariant(FStub->getReturnType());
281   FPParamVariant PV = whichFPParamVariantNeeded(F);
282 
283   std::string AsmText;
284   AsmText += ".set reorder\n";
285   AsmText += swapFPIntParams(PV, M, LE, true);
286   if (RV != NoFPRet) {
287     AsmText += "move $$18, $$31\n";
288     AsmText += "jal " + Name + "\n";
289   } else {
290     AsmText += "lui  $$25, %hi(" + Name + ")\n";
291     AsmText += "addiu  $$25, $$25, %lo(" + Name + ")\n";
292   }
293 
294   switch (RV) {
295   case FRet:
296     AsmText += "mfc1 $$2, $$f0\n";
297     break;
298 
299   case DRet:
300     if (LE) {
301       AsmText += "mfc1 $$2, $$f0\n";
302       AsmText += "mfc1 $$3, $$f1\n";
303     } else {
304       AsmText += "mfc1 $$3, $$f0\n";
305       AsmText += "mfc1 $$2, $$f1\n";
306     }
307     break;
308 
309   case CFRet:
310     if (LE) {
311       AsmText += "mfc1 $$2, $$f0\n";
312       AsmText += "mfc1 $$3, $$f2\n";
313     } else {
314       AsmText += "mfc1 $$3, $$f0\n";
315       AsmText += "mfc1 $$3, $$f2\n";
316     }
317     break;
318 
319   case CDRet:
320     if (LE) {
321       AsmText += "mfc1 $$4, $$f2\n";
322       AsmText += "mfc1 $$5, $$f3\n";
323       AsmText += "mfc1 $$2, $$f0\n";
324       AsmText += "mfc1 $$3, $$f1\n";
325 
326     } else {
327       AsmText += "mfc1 $$5, $$f2\n";
328       AsmText += "mfc1 $$4, $$f3\n";
329       AsmText += "mfc1 $$3, $$f0\n";
330       AsmText += "mfc1 $$2, $$f1\n";
331     }
332     break;
333 
334   case NoFPRet:
335     break;
336   }
337 
338   if (RV != NoFPRet)
339     AsmText += "jr $$18\n";
340   else
341     AsmText += "jr $$25\n";
342   emitInlineAsm(Context, BB, AsmText);
343 
344   new UnreachableInst(Context, BB);
345 }
346 
347 // Functions that are llvm intrinsics and don't need helpers.
348 static const char *const IntrinsicInline[] = {
349   "fabs", "fabsf",
350   "llvm.ceil.f32", "llvm.ceil.f64",
351   "llvm.copysign.f32", "llvm.copysign.f64",
352   "llvm.cos.f32", "llvm.cos.f64",
353   "llvm.exp.f32", "llvm.exp.f64",
354   "llvm.exp2.f32", "llvm.exp2.f64",
355   "llvm.fabs.f32", "llvm.fabs.f64",
356   "llvm.floor.f32", "llvm.floor.f64",
357   "llvm.fma.f32", "llvm.fma.f64",
358   "llvm.log.f32", "llvm.log.f64",
359   "llvm.log10.f32", "llvm.log10.f64",
360   "llvm.nearbyint.f32", "llvm.nearbyint.f64",
361   "llvm.pow.f32", "llvm.pow.f64",
362   "llvm.powi.f32.i32", "llvm.powi.f64.i32",
363   "llvm.rint.f32", "llvm.rint.f64",
364   "llvm.round.f32", "llvm.round.f64",
365   "llvm.sin.f32", "llvm.sin.f64",
366   "llvm.sqrt.f32", "llvm.sqrt.f64",
367   "llvm.trunc.f32", "llvm.trunc.f64",
368 };
369 
370 static bool isIntrinsicInline(Function *F) {
371   return std::binary_search(std::begin(IntrinsicInline),
372                             std::end(IntrinsicInline), F->getName());
373 }
374 
375 // Returns of float, double and complex need to be handled with a helper
376 // function.
377 static bool fixupFPReturnAndCall(Function &F, Module *M,
378                                  const MipsTargetMachine &TM) {
379   bool Modified = false;
380   LLVMContext &C = M->getContext();
381   Type *MyVoid = Type::getVoidTy(C);
382   for (auto &BB: F)
383     for (auto &I: BB) {
384       if (const ReturnInst *RI = dyn_cast<ReturnInst>(&I)) {
385         Value *RVal = RI->getReturnValue();
386         if (!RVal) continue;
387         //
388         // If there is a return value and it needs a helper function,
389         // figure out which one and add a call before the actual
390         // return to this helper. The purpose of the helper is to move
391         // floating point values from their soft float return mapping to
392         // where they would have been mapped to in floating point registers.
393         //
394         Type *T = RVal->getType();
395         FPReturnVariant RV = whichFPReturnVariant(T);
396         if (RV == NoFPRet) continue;
397         static const char *const Helper[NoFPRet] = {
398           "__mips16_ret_sf", "__mips16_ret_df", "__mips16_ret_sc",
399           "__mips16_ret_dc"
400         };
401         const char *Name = Helper[RV];
402         AttributeList A;
403         Value *Params[] = {RVal};
404         Modified = true;
405         //
406         // These helper functions have a different calling ABI so
407         // this __Mips16RetHelper indicates that so that later
408         // during call setup, the proper call lowering to the helper
409         // functions will take place.
410         //
411         A = A.addFnAttribute(C, "__Mips16RetHelper");
412         A = A.addFnAttribute(C, Attribute::ReadNone);
413         A = A.addFnAttribute(C, Attribute::NoInline);
414         FunctionCallee F = (M->getOrInsertFunction(Name, A, MyVoid, T));
415         CallInst::Create(F, Params, "", &I);
416       } else if (const CallInst *CI = dyn_cast<CallInst>(&I)) {
417         FunctionType *FT = CI->getFunctionType();
418         Function *F_ =  CI->getCalledFunction();
419         if (needsFPReturnHelper(*FT) &&
420             !(F_ && isIntrinsicInline(F_))) {
421           Modified=true;
422           F.addFnAttr("saveS2");
423         }
424         if (F_ && !isIntrinsicInline(F_)) {
425           // pic mode calls are handled by already defined
426           // helper functions
427           if (needsFPReturnHelper(*F_)) {
428             Modified=true;
429             F.addFnAttr("saveS2");
430           }
431           if (!TM.isPositionIndependent()) {
432             if (needsFPHelperFromSig(*F_)) {
433               assureFPCallStub(*F_, M, TM);
434               Modified=true;
435             }
436           }
437         }
438       }
439     }
440   return Modified;
441 }
442 
443 static void createFPFnStub(Function *F, Module *M, FPParamVariant PV,
444                            const MipsTargetMachine &TM) {
445   bool PicMode = TM.isPositionIndependent();
446   bool LE = TM.isLittleEndian();
447   LLVMContext &Context = M->getContext();
448   std::string Name(F->getName());
449   std::string SectionName = ".mips16.fn." + Name;
450   std::string StubName = "__fn_stub_" + Name;
451   std::string LocalName = "$$__fn_local_" + Name;
452   Function *FStub = Function::Create
453     (F->getFunctionType(),
454      Function::InternalLinkage, StubName, M);
455   FStub->addFnAttr("mips16_fp_stub");
456   FStub->addFnAttr(Attribute::Naked);
457   FStub->addFnAttr(Attribute::NoUnwind);
458   FStub->addFnAttr(Attribute::NoInline);
459   FStub->addFnAttr("nomips16");
460   FStub->setSection(SectionName);
461   BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
462 
463   std::string AsmText;
464   if (PicMode) {
465     AsmText += ".set noreorder\n";
466     AsmText += ".cpload $$25\n";
467     AsmText += ".set reorder\n";
468     AsmText += ".reloc 0, R_MIPS_NONE, " + Name + "\n";
469     AsmText += "la $$25, " + LocalName + "\n";
470   } else
471     AsmText += "la $$25, " + Name + "\n";
472   AsmText += swapFPIntParams(PV, M, LE, false);
473   AsmText += "jr $$25\n";
474   AsmText += LocalName + " = " + Name + "\n";
475   emitInlineAsm(Context, BB, AsmText);
476 
477   new UnreachableInst(FStub->getContext(), BB);
478 }
479 
480 // remove the use-soft-float attribute
481 static void removeUseSoftFloat(Function &F) {
482   LLVM_DEBUG(errs() << "removing -use-soft-float\n");
483   F.removeFnAttr("use-soft-float");
484   if (F.hasFnAttribute("use-soft-float")) {
485     LLVM_DEBUG(errs() << "still has -use-soft-float\n");
486   }
487   F.addFnAttr("use-soft-float", "false");
488 }
489 
490 // This pass only makes sense when the underlying chip has floating point but
491 // we are compiling as mips16.
492 // For all mips16 functions (that are not stubs we have already generated), or
493 // declared via attributes as nomips16, we must:
494 //    1) fixup all returns of float, double, single and double complex
495 //       by calling a helper function before the actual return.
496 //    2) generate helper functions (stubs) that can be called by mips32
497 //       functions that will move parameters passed normally passed in
498 //       floating point
499 //       registers the soft float equivalents.
500 //    3) in the case of static relocation, generate helper functions so that
501 //       mips16 functions can call extern functions of unknown type (mips16 or
502 //       mips32).
503 //    4) TBD. For pic, calls to extern functions of unknown type are handled by
504 //       predefined helper functions in libc but this work is currently done
505 //       during call lowering but it should be moved here in the future.
506 bool Mips16HardFloat::runOnModule(Module &M) {
507   auto &TM = static_cast<const MipsTargetMachine &>(
508       getAnalysis<TargetPassConfig>().getTM<TargetMachine>());
509   LLVM_DEBUG(errs() << "Run on Module Mips16HardFloat\n");
510   bool Modified = false;
511   for (Module::iterator F = M.begin(), E = M.end(); F != E; ++F) {
512     if (F->hasFnAttribute("nomips16") &&
513         F->hasFnAttribute("use-soft-float")) {
514       removeUseSoftFloat(*F);
515       continue;
516     }
517     if (F->isDeclaration() || F->hasFnAttribute("mips16_fp_stub") ||
518         F->hasFnAttribute("nomips16")) continue;
519     Modified |= fixupFPReturnAndCall(*F, &M, TM);
520     FPParamVariant V = whichFPParamVariantNeeded(*F);
521     if (V != NoSig) {
522       Modified = true;
523       createFPFnStub(&*F, &M, V, TM);
524     }
525   }
526   return Modified;
527 }
528 
529 ModulePass *llvm::createMips16HardFloatPass() {
530   return new Mips16HardFloat();
531 }
532