xref: /freebsd/contrib/llvm-project/llvm/lib/Target/Mips/Mips16HardFloat.cpp (revision dc318a4ffabcbfa23bb56a33403aad36e6de30af)
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", "llvm.powi.f64",
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.addAttribute(C, AttributeList::FunctionIndex,
412                            "__Mips16RetHelper");
413         A = A.addAttribute(C, AttributeList::FunctionIndex,
414                            Attribute::ReadNone);
415         A = A.addAttribute(C, AttributeList::FunctionIndex,
416                            Attribute::NoInline);
417         FunctionCallee F = (M->getOrInsertFunction(Name, A, MyVoid, T));
418         CallInst::Create(F, Params, "", &I);
419       } else if (const CallInst *CI = dyn_cast<CallInst>(&I)) {
420         FunctionType *FT = CI->getFunctionType();
421         Function *F_ =  CI->getCalledFunction();
422         if (needsFPReturnHelper(*FT) &&
423             !(F_ && isIntrinsicInline(F_))) {
424           Modified=true;
425           F.addFnAttr("saveS2");
426         }
427         if (F_ && !isIntrinsicInline(F_)) {
428           // pic mode calls are handled by already defined
429           // helper functions
430           if (needsFPReturnHelper(*F_)) {
431             Modified=true;
432             F.addFnAttr("saveS2");
433           }
434           if (!TM.isPositionIndependent()) {
435             if (needsFPHelperFromSig(*F_)) {
436               assureFPCallStub(*F_, M, TM);
437               Modified=true;
438             }
439           }
440         }
441       }
442     }
443   return Modified;
444 }
445 
446 static void createFPFnStub(Function *F, Module *M, FPParamVariant PV,
447                            const MipsTargetMachine &TM) {
448   bool PicMode = TM.isPositionIndependent();
449   bool LE = TM.isLittleEndian();
450   LLVMContext &Context = M->getContext();
451   std::string Name(F->getName());
452   std::string SectionName = ".mips16.fn." + Name;
453   std::string StubName = "__fn_stub_" + Name;
454   std::string LocalName = "$$__fn_local_" + Name;
455   Function *FStub = Function::Create
456     (F->getFunctionType(),
457      Function::InternalLinkage, StubName, M);
458   FStub->addFnAttr("mips16_fp_stub");
459   FStub->addFnAttr(Attribute::Naked);
460   FStub->addFnAttr(Attribute::NoUnwind);
461   FStub->addFnAttr(Attribute::NoInline);
462   FStub->addFnAttr("nomips16");
463   FStub->setSection(SectionName);
464   BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
465 
466   std::string AsmText;
467   if (PicMode) {
468     AsmText += ".set noreorder\n";
469     AsmText += ".cpload $$25\n";
470     AsmText += ".set reorder\n";
471     AsmText += ".reloc 0, R_MIPS_NONE, " + Name + "\n";
472     AsmText += "la $$25, " + LocalName + "\n";
473   } else
474     AsmText += "la $$25, " + Name + "\n";
475   AsmText += swapFPIntParams(PV, M, LE, false);
476   AsmText += "jr $$25\n";
477   AsmText += LocalName + " = " + Name + "\n";
478   emitInlineAsm(Context, BB, AsmText);
479 
480   new UnreachableInst(FStub->getContext(), BB);
481 }
482 
483 // remove the use-soft-float attribute
484 static void removeUseSoftFloat(Function &F) {
485   AttrBuilder B;
486   LLVM_DEBUG(errs() << "removing -use-soft-float\n");
487   B.addAttribute("use-soft-float", "false");
488   F.removeAttributes(AttributeList::FunctionIndex, B);
489   if (F.hasFnAttribute("use-soft-float")) {
490     LLVM_DEBUG(errs() << "still has -use-soft-float\n");
491   }
492   F.addAttributes(AttributeList::FunctionIndex, B);
493 }
494 
495 // This pass only makes sense when the underlying chip has floating point but
496 // we are compiling as mips16.
497 // For all mips16 functions (that are not stubs we have already generated), or
498 // declared via attributes as nomips16, we must:
499 //    1) fixup all returns of float, double, single and double complex
500 //       by calling a helper function before the actual return.
501 //    2) generate helper functions (stubs) that can be called by mips32
502 //       functions that will move parameters passed normally passed in
503 //       floating point
504 //       registers the soft float equivalents.
505 //    3) in the case of static relocation, generate helper functions so that
506 //       mips16 functions can call extern functions of unknown type (mips16 or
507 //       mips32).
508 //    4) TBD. For pic, calls to extern functions of unknown type are handled by
509 //       predefined helper functions in libc but this work is currently done
510 //       during call lowering but it should be moved here in the future.
511 bool Mips16HardFloat::runOnModule(Module &M) {
512   auto &TM = static_cast<const MipsTargetMachine &>(
513       getAnalysis<TargetPassConfig>().getTM<TargetMachine>());
514   LLVM_DEBUG(errs() << "Run on Module Mips16HardFloat\n");
515   bool Modified = false;
516   for (Module::iterator F = M.begin(), E = M.end(); F != E; ++F) {
517     if (F->hasFnAttribute("nomips16") &&
518         F->hasFnAttribute("use-soft-float")) {
519       removeUseSoftFloat(*F);
520       continue;
521     }
522     if (F->isDeclaration() || F->hasFnAttribute("mips16_fp_stub") ||
523         F->hasFnAttribute("nomips16")) continue;
524     Modified |= fixupFPReturnAndCall(*F, &M, TM);
525     FPParamVariant V = whichFPParamVariantNeeded(*F);
526     if (V != NoSig) {
527       Modified = true;
528       createFPFnStub(&*F, &M, V, TM);
529     }
530   }
531   return Modified;
532 }
533 
534 ModulePass *llvm::createMips16HardFloatPass() {
535   return new Mips16HardFloat();
536 }
537