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
22using namespace llvm;
23
24#define DEBUG_TYPE "mips16-hard-float"
25
26namespace {
27  class Mips16HardFloat : public ModulePass {
28  public:
29    static char ID;
30
31    Mips16HardFloat(MipsTargetMachine &TM_) : ModulePass(ID), TM(TM_) {}
32
33    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
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//
62enum FPReturnVariant {
63  FRet, DRet, CFRet, CDRet, NoFPRet
64};
65
66//
67// Determine which FP return type this function has
68//
69static 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//
95enum FPParamVariant {
96  FSig, FFSig, FDSig,
97  DSig, DDSig, DFSig, NoSig
98};
99
100// which floating point parameter signature variant we are dealing with
101//
102typedef Type::TypeID TypeID;
103const Type::TypeID FloatTyID = Type::FloatTyID;
104const Type::TypeID DoubleTyID = Type::DoubleTyID;
105
106static 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//
157static 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
171static bool needsFPReturnHelper(Function &F) {
172  Type* RetType = F.getReturnType();
173  return whichFPReturnVariant(RetType) != NoFPRet;
174}
175
176static bool needsFPReturnHelper(FunctionType &FT) {
177  Type* RetType = FT.getReturnType();
178  return whichFPReturnVariant(RetType) != NoFPRet;
179}
180
181static 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//
189static 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//
261static void assureFPCallStub(Function &F, Module *M,
262                             const MipsTargetMachine &TM) {
263  // for now we only need them for static relocation
264  if (TM.isPositionIndependent())
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//
355static 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
377static 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//
385static 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 (auto &BB: F)
391    for (auto &I: BB) {
392      if (const ReturnInst *RI = dyn_cast<ReturnInst>(&I)) {
393        Value *RVal = RI->getReturnValue();
394        if (!RVal) continue;
395        //
396        // If there is a return value and it needs a helper function,
397        // figure out which one and add a call before the actual
398        // return to this helper. The purpose of the helper is to move
399        // floating point values from their soft float return mapping to
400        // where they would have been mapped to in floating point registers.
401        //
402        Type *T = RVal->getType();
403        FPReturnVariant RV = whichFPReturnVariant(T);
404        if (RV == NoFPRet) continue;
405        static const char *const Helper[NoFPRet] = {
406          "__mips16_ret_sf", "__mips16_ret_df", "__mips16_ret_sc",
407          "__mips16_ret_dc"
408        };
409        const char *Name = Helper[RV];
410        AttributeSet A;
411        Value *Params[] = {RVal};
412        Modified = true;
413        //
414        // These helper functions have a different calling ABI so
415        // this __Mips16RetHelper indicates that so that later
416        // during call setup, the proper call lowering to the helper
417        // functions will take place.
418        //
419        A = A.addAttribute(C, AttributeSet::FunctionIndex,
420                           "__Mips16RetHelper");
421        A = A.addAttribute(C, AttributeSet::FunctionIndex,
422                           Attribute::ReadNone);
423        A = A.addAttribute(C, AttributeSet::FunctionIndex,
424                           Attribute::NoInline);
425        Value *F = (M->getOrInsertFunction(Name, A, MyVoid, T, nullptr));
426        CallInst::Create(F, Params, "", &I);
427      } else if (const CallInst *CI = dyn_cast<CallInst>(&I)) {
428        FunctionType *FT = CI->getFunctionType();
429        Function *F_ =  CI->getCalledFunction();
430        if (needsFPReturnHelper(*FT) &&
431            !(F_ && isIntrinsicInline(F_))) {
432          Modified=true;
433          F.addFnAttr("saveS2");
434        }
435        if (F_ && !isIntrinsicInline(F_)) {
436          // pic mode calls are handled by already defined
437          // helper functions
438          if (needsFPReturnHelper(*F_)) {
439            Modified=true;
440            F.addFnAttr("saveS2");
441          }
442          if (!TM.isPositionIndependent()) {
443            if (needsFPHelperFromSig(*F_)) {
444              assureFPCallStub(*F_, M, TM);
445              Modified=true;
446            }
447          }
448        }
449      }
450    }
451  return Modified;
452}
453
454static void createFPFnStub(Function *F, Module *M, FPParamVariant PV,
455                           const MipsTargetMachine &TM) {
456  bool PicMode = TM.isPositionIndependent();
457  bool LE = TM.isLittleEndian();
458  LLVMContext &Context = M->getContext();
459  std::string Name = F->getName();
460  std::string SectionName = ".mips16.fn." + Name;
461  std::string StubName = "__fn_stub_" + Name;
462  std::string LocalName = "$$__fn_local_" + Name;
463  Function *FStub = Function::Create
464    (F->getFunctionType(),
465     Function::InternalLinkage, StubName, M);
466  FStub->addFnAttr("mips16_fp_stub");
467  FStub->addFnAttr(llvm::Attribute::Naked);
468  FStub->addFnAttr(llvm::Attribute::NoUnwind);
469  FStub->addFnAttr(llvm::Attribute::NoInline);
470  FStub->addFnAttr("nomips16");
471  FStub->setSection(SectionName);
472  BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
473
474  std::string AsmText;
475  if (PicMode) {
476    AsmText += ".set noreorder\n";
477    AsmText += ".cpload $$25\n";
478    AsmText += ".set reorder\n";
479    AsmText += ".reloc 0, R_MIPS_NONE, " + Name + "\n";
480    AsmText += "la $$25, " + LocalName + "\n";
481  } else
482    AsmText += "la $$25, " + Name + "\n";
483  AsmText += swapFPIntParams(PV, M, LE, false);
484  AsmText += "jr $$25\n";
485  AsmText += LocalName + " = " + Name + "\n";
486  EmitInlineAsm(Context, BB, AsmText);
487
488  new UnreachableInst(FStub->getContext(), BB);
489}
490
491//
492// remove the use-soft-float attribute
493//
494static void removeUseSoftFloat(Function &F) {
495  AttributeSet A;
496  DEBUG(errs() << "removing -use-soft-float\n");
497  A = A.addAttribute(F.getContext(), AttributeSet::FunctionIndex,
498                     "use-soft-float", "false");
499  F.removeAttributes(AttributeSet::FunctionIndex, A);
500  if (F.hasFnAttribute("use-soft-float")) {
501    DEBUG(errs() << "still has -use-soft-float\n");
502  }
503  F.addAttributes(AttributeSet::FunctionIndex, A);
504}
505
506
507//
508// This pass only makes sense when the underlying chip has floating point but
509// we are compiling as mips16.
510// For all mips16 functions (that are not stubs we have already generated), or
511// declared via attributes as nomips16, we must:
512//    1) fixup all returns of float, double, single and double complex
513//       by calling a helper function before the actual return.
514//    2) generate helper functions (stubs) that can be called by mips32
515//       functions that will move parameters passed normally passed in
516//       floating point
517//       registers the soft float equivalents.
518//    3) in the case of static relocation, generate helper functions so that
519//       mips16 functions can call extern functions of unknown type (mips16 or
520//       mips32).
521//    4) TBD. For pic, calls to extern functions of unknown type are handled by
522//       predefined helper functions in libc but this work is currently done
523//       during call lowering but it should be moved here in the future.
524//
525bool Mips16HardFloat::runOnModule(Module &M) {
526  DEBUG(errs() << "Run on Module Mips16HardFloat\n");
527  bool Modified = false;
528  for (Module::iterator F = M.begin(), E = M.end(); F != E; ++F) {
529    if (F->hasFnAttribute("nomips16") &&
530        F->hasFnAttribute("use-soft-float")) {
531      removeUseSoftFloat(*F);
532      continue;
533    }
534    if (F->isDeclaration() || F->hasFnAttribute("mips16_fp_stub") ||
535        F->hasFnAttribute("nomips16")) continue;
536    Modified |= fixupFPReturnAndCall(*F, &M, TM);
537    FPParamVariant V = whichFPParamVariantNeeded(*F);
538    if (V != NoSig) {
539      Modified = true;
540      createFPFnStub(&*F, &M, V, TM);
541    }
542  }
543  return Modified;
544}
545
546
547ModulePass *llvm::createMips16HardFloatPass(MipsTargetMachine &TM) {
548  return new Mips16HardFloat(TM);
549}
550