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/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   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 //
     62 enum FPReturnVariant {
     63   FRet, DRet, CFRet, CDRet, NoFPRet
     64 };
     65 
     66 //
     67 // Determine which FP return type this function has
     68 //
     69 static 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 //
     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 //
    102 typedef Type::TypeID 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 //
    157 static 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 
    171 static bool needsFPReturnHelper(Function &F) {
    172   Type* RetType = F.getReturnType();
    173   return whichFPReturnVariant(RetType) != NoFPRet;
    174 }
    175 
    176 static bool needsFPReturnHelper(FunctionType &FT) {
    177   Type* RetType = FT.getReturnType();
    178   return whichFPReturnVariant(RetType) != NoFPRet;
    179 }
    180 
    181 static 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 //
    189 static 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 //
    261 static 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 //
    355 static 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 
    377 static 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 //
    385 static 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 
    454 static 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 //
    494 static 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 //
    525 bool 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 
    547 ModulePass *llvm::createMips16HardFloatPass(MipsTargetMachine &TM) {
    548   return new Mips16HardFloat(TM);
    549 }
    550