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