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