xref: /freebsd-src/contrib/llvm-project/llvm/lib/Target/Mips/Mips16HardFloat.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
10b57cec5SDimitry Andric //===- Mips16HardFloat.cpp for Mips16 Hard Float --------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file defines a pass needed for Mips16 Hard Float
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
120b57cec5SDimitry Andric 
130b57cec5SDimitry Andric #include "MipsTargetMachine.h"
140b57cec5SDimitry Andric #include "llvm/CodeGen/TargetPassConfig.h"
150b57cec5SDimitry Andric #include "llvm/IR/Module.h"
160b57cec5SDimitry Andric #include "llvm/IR/Value.h"
170b57cec5SDimitry Andric #include "llvm/Support/Debug.h"
18bdd1243dSDimitry Andric #include "llvm/Support/ModRef.h"
190b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
200b57cec5SDimitry Andric #include <algorithm>
210b57cec5SDimitry Andric #include <string>
220b57cec5SDimitry Andric 
230b57cec5SDimitry Andric using namespace llvm;
240b57cec5SDimitry Andric 
250b57cec5SDimitry Andric #define DEBUG_TYPE "mips16-hard-float"
260b57cec5SDimitry Andric 
270b57cec5SDimitry Andric namespace {
280b57cec5SDimitry Andric 
290b57cec5SDimitry Andric   class Mips16HardFloat : public ModulePass {
300b57cec5SDimitry Andric   public:
310b57cec5SDimitry Andric     static char ID;
320b57cec5SDimitry Andric 
330b57cec5SDimitry Andric     Mips16HardFloat() : ModulePass(ID) {}
340b57cec5SDimitry Andric 
350b57cec5SDimitry Andric     StringRef getPassName() const override { return "MIPS16 Hard Float Pass"; }
360b57cec5SDimitry Andric 
370b57cec5SDimitry Andric     void getAnalysisUsage(AnalysisUsage &AU) const override {
380b57cec5SDimitry Andric       AU.addRequired<TargetPassConfig>();
390b57cec5SDimitry Andric       ModulePass::getAnalysisUsage(AU);
400b57cec5SDimitry Andric     }
410b57cec5SDimitry Andric 
420b57cec5SDimitry Andric     bool runOnModule(Module &M) override;
430b57cec5SDimitry Andric   };
440b57cec5SDimitry Andric 
450b57cec5SDimitry Andric } // end anonymous namespace
460b57cec5SDimitry Andric 
475ffd83dbSDimitry Andric static void emitInlineAsm(LLVMContext &C, BasicBlock *BB, StringRef AsmText) {
480b57cec5SDimitry Andric   std::vector<Type *> AsmArgTypes;
490b57cec5SDimitry Andric   std::vector<Value *> AsmArgs;
500b57cec5SDimitry Andric 
510b57cec5SDimitry Andric   FunctionType *AsmFTy =
520b57cec5SDimitry Andric       FunctionType::get(Type::getVoidTy(C), AsmArgTypes, false);
530b57cec5SDimitry Andric   InlineAsm *IA = InlineAsm::get(AsmFTy, AsmText, "", true,
540b57cec5SDimitry Andric                                  /* IsAlignStack */ false, InlineAsm::AD_ATT);
550b57cec5SDimitry Andric   CallInst::Create(IA, AsmArgs, "", BB);
560b57cec5SDimitry Andric }
570b57cec5SDimitry Andric 
580b57cec5SDimitry Andric char Mips16HardFloat::ID = 0;
590b57cec5SDimitry Andric 
600b57cec5SDimitry Andric //
610b57cec5SDimitry Andric // Return types that matter for hard float are:
620b57cec5SDimitry Andric // float, double, complex float, and complex double
630b57cec5SDimitry Andric //
640b57cec5SDimitry Andric enum FPReturnVariant {
650b57cec5SDimitry Andric   FRet, DRet, CFRet, CDRet, NoFPRet
660b57cec5SDimitry Andric };
670b57cec5SDimitry Andric 
680b57cec5SDimitry Andric //
690b57cec5SDimitry Andric // Determine which FP return type this function has
700b57cec5SDimitry Andric //
710b57cec5SDimitry Andric static FPReturnVariant whichFPReturnVariant(Type *T) {
720b57cec5SDimitry Andric   switch (T->getTypeID()) {
730b57cec5SDimitry Andric   case Type::FloatTyID:
740b57cec5SDimitry Andric     return FRet;
750b57cec5SDimitry Andric   case Type::DoubleTyID:
760b57cec5SDimitry Andric     return DRet;
770b57cec5SDimitry Andric   case Type::StructTyID: {
780b57cec5SDimitry Andric     StructType *ST = cast<StructType>(T);
790b57cec5SDimitry Andric     if (ST->getNumElements() != 2)
800b57cec5SDimitry Andric       break;
810b57cec5SDimitry Andric     if ((ST->getElementType(0)->isFloatTy()) &&
820b57cec5SDimitry Andric         (ST->getElementType(1)->isFloatTy()))
830b57cec5SDimitry Andric       return CFRet;
840b57cec5SDimitry Andric     if ((ST->getElementType(0)->isDoubleTy()) &&
850b57cec5SDimitry Andric         (ST->getElementType(1)->isDoubleTy()))
860b57cec5SDimitry Andric       return CDRet;
870b57cec5SDimitry Andric     break;
880b57cec5SDimitry Andric   }
890b57cec5SDimitry Andric   default:
900b57cec5SDimitry Andric     break;
910b57cec5SDimitry Andric   }
920b57cec5SDimitry Andric   return NoFPRet;
930b57cec5SDimitry Andric }
940b57cec5SDimitry Andric 
950b57cec5SDimitry Andric // Parameter type that matter are float, (float, float), (float, double),
960b57cec5SDimitry Andric // double, (double, double), (double, float)
970b57cec5SDimitry Andric enum FPParamVariant {
980b57cec5SDimitry Andric   FSig, FFSig, FDSig,
990b57cec5SDimitry Andric   DSig, DDSig, DFSig, NoSig
1000b57cec5SDimitry Andric };
1010b57cec5SDimitry Andric 
1020b57cec5SDimitry Andric // which floating point parameter signature variant we are dealing with
1030b57cec5SDimitry Andric using TypeID = Type::TypeID;
1040b57cec5SDimitry Andric const Type::TypeID FloatTyID = Type::FloatTyID;
1050b57cec5SDimitry Andric const Type::TypeID DoubleTyID = Type::DoubleTyID;
1060b57cec5SDimitry Andric 
1070b57cec5SDimitry Andric static FPParamVariant whichFPParamVariantNeeded(Function &F) {
1080b57cec5SDimitry Andric   switch (F.arg_size()) {
1090b57cec5SDimitry Andric   case 0:
1100b57cec5SDimitry Andric     return NoSig;
1110b57cec5SDimitry Andric   case 1:{
1120b57cec5SDimitry Andric     TypeID ArgTypeID = F.getFunctionType()->getParamType(0)->getTypeID();
1130b57cec5SDimitry Andric     switch (ArgTypeID) {
1140b57cec5SDimitry Andric     case FloatTyID:
1150b57cec5SDimitry Andric       return FSig;
1160b57cec5SDimitry Andric     case DoubleTyID:
1170b57cec5SDimitry Andric       return DSig;
1180b57cec5SDimitry Andric     default:
1190b57cec5SDimitry Andric       return NoSig;
1200b57cec5SDimitry Andric     }
1210b57cec5SDimitry Andric   }
1220b57cec5SDimitry Andric   default: {
1230b57cec5SDimitry Andric     TypeID ArgTypeID0 = F.getFunctionType()->getParamType(0)->getTypeID();
1240b57cec5SDimitry Andric     TypeID ArgTypeID1 = F.getFunctionType()->getParamType(1)->getTypeID();
1250b57cec5SDimitry Andric     switch(ArgTypeID0) {
1260b57cec5SDimitry Andric     case FloatTyID: {
1270b57cec5SDimitry Andric       switch (ArgTypeID1) {
1280b57cec5SDimitry Andric       case FloatTyID:
1290b57cec5SDimitry Andric         return FFSig;
1300b57cec5SDimitry Andric       case DoubleTyID:
1310b57cec5SDimitry Andric         return FDSig;
1320b57cec5SDimitry Andric       default:
1330b57cec5SDimitry Andric         return FSig;
1340b57cec5SDimitry Andric       }
1350b57cec5SDimitry Andric     }
1360b57cec5SDimitry Andric     case DoubleTyID: {
1370b57cec5SDimitry Andric       switch (ArgTypeID1) {
1380b57cec5SDimitry Andric       case FloatTyID:
1390b57cec5SDimitry Andric         return DFSig;
1400b57cec5SDimitry Andric       case DoubleTyID:
1410b57cec5SDimitry Andric         return DDSig;
1420b57cec5SDimitry Andric       default:
1430b57cec5SDimitry Andric         return DSig;
1440b57cec5SDimitry Andric       }
1450b57cec5SDimitry Andric     }
1460b57cec5SDimitry Andric     default:
1470b57cec5SDimitry Andric       return NoSig;
1480b57cec5SDimitry Andric     }
1490b57cec5SDimitry Andric   }
1500b57cec5SDimitry Andric   }
1510b57cec5SDimitry Andric   llvm_unreachable("can't get here");
1520b57cec5SDimitry Andric }
1530b57cec5SDimitry Andric 
1540b57cec5SDimitry Andric // Figure out if we need float point based on the function parameters.
1550b57cec5SDimitry Andric // We need to move variables in and/or out of floating point
1560b57cec5SDimitry Andric // registers because of the ABI
1570b57cec5SDimitry Andric static bool needsFPStubFromParams(Function &F) {
1580b57cec5SDimitry Andric   if (F.arg_size() >=1) {
1590b57cec5SDimitry Andric     Type *ArgType = F.getFunctionType()->getParamType(0);
1600b57cec5SDimitry Andric     switch (ArgType->getTypeID()) {
1610b57cec5SDimitry Andric     case Type::FloatTyID:
1620b57cec5SDimitry Andric     case Type::DoubleTyID:
1630b57cec5SDimitry Andric       return true;
1640b57cec5SDimitry Andric     default:
1650b57cec5SDimitry Andric       break;
1660b57cec5SDimitry Andric     }
1670b57cec5SDimitry Andric   }
1680b57cec5SDimitry Andric   return false;
1690b57cec5SDimitry Andric }
1700b57cec5SDimitry Andric 
1710b57cec5SDimitry Andric static bool needsFPReturnHelper(Function &F) {
1720b57cec5SDimitry Andric   Type* RetType = F.getReturnType();
1730b57cec5SDimitry Andric   return whichFPReturnVariant(RetType) != NoFPRet;
1740b57cec5SDimitry Andric }
1750b57cec5SDimitry Andric 
1760b57cec5SDimitry Andric static bool needsFPReturnHelper(FunctionType &FT) {
1770b57cec5SDimitry Andric   Type* RetType = FT.getReturnType();
1780b57cec5SDimitry Andric   return whichFPReturnVariant(RetType) != NoFPRet;
1790b57cec5SDimitry Andric }
1800b57cec5SDimitry Andric 
1810b57cec5SDimitry Andric static bool needsFPHelperFromSig(Function &F) {
1820b57cec5SDimitry Andric   return needsFPStubFromParams(F) || needsFPReturnHelper(F);
1830b57cec5SDimitry Andric }
1840b57cec5SDimitry Andric 
1850b57cec5SDimitry Andric // We swap between FP and Integer registers to allow Mips16 and Mips32 to
1860b57cec5SDimitry Andric // interoperate
1870b57cec5SDimitry Andric static std::string swapFPIntParams(FPParamVariant PV, Module *M, bool LE,
1880b57cec5SDimitry Andric                                    bool ToFP) {
1890b57cec5SDimitry Andric   std::string MI = ToFP ? "mtc1 ": "mfc1 ";
1900b57cec5SDimitry Andric   std::string AsmText;
1910b57cec5SDimitry Andric 
1920b57cec5SDimitry Andric   switch (PV) {
1930b57cec5SDimitry Andric   case FSig:
1940b57cec5SDimitry Andric     AsmText += MI + "$$4, $$f12\n";
1950b57cec5SDimitry Andric     break;
1960b57cec5SDimitry Andric 
1970b57cec5SDimitry Andric   case FFSig:
1980b57cec5SDimitry Andric     AsmText += MI + "$$4, $$f12\n";
1990b57cec5SDimitry Andric     AsmText += MI + "$$5, $$f14\n";
2000b57cec5SDimitry Andric     break;
2010b57cec5SDimitry Andric 
2020b57cec5SDimitry Andric   case FDSig:
2030b57cec5SDimitry Andric     AsmText += MI + "$$4, $$f12\n";
2040b57cec5SDimitry Andric     if (LE) {
2050b57cec5SDimitry Andric       AsmText += MI + "$$6, $$f14\n";
2060b57cec5SDimitry Andric       AsmText += MI + "$$7, $$f15\n";
2070b57cec5SDimitry Andric     } else {
2080b57cec5SDimitry Andric       AsmText += MI + "$$7, $$f14\n";
2090b57cec5SDimitry Andric       AsmText += MI + "$$6, $$f15\n";
2100b57cec5SDimitry Andric     }
2110b57cec5SDimitry Andric     break;
2120b57cec5SDimitry Andric 
2130b57cec5SDimitry Andric   case DSig:
2140b57cec5SDimitry Andric     if (LE) {
2150b57cec5SDimitry Andric       AsmText += MI + "$$4, $$f12\n";
2160b57cec5SDimitry Andric       AsmText += MI + "$$5, $$f13\n";
2170b57cec5SDimitry Andric     } else {
2180b57cec5SDimitry Andric       AsmText += MI + "$$5, $$f12\n";
2190b57cec5SDimitry Andric       AsmText += MI + "$$4, $$f13\n";
2200b57cec5SDimitry Andric     }
2210b57cec5SDimitry Andric     break;
2220b57cec5SDimitry Andric 
2230b57cec5SDimitry Andric   case DDSig:
2240b57cec5SDimitry Andric     if (LE) {
2250b57cec5SDimitry Andric       AsmText += MI + "$$4, $$f12\n";
2260b57cec5SDimitry Andric       AsmText += MI + "$$5, $$f13\n";
2270b57cec5SDimitry Andric       AsmText += MI + "$$6, $$f14\n";
2280b57cec5SDimitry Andric       AsmText += MI + "$$7, $$f15\n";
2290b57cec5SDimitry Andric     } else {
2300b57cec5SDimitry Andric       AsmText += MI + "$$5, $$f12\n";
2310b57cec5SDimitry Andric       AsmText += MI + "$$4, $$f13\n";
2320b57cec5SDimitry Andric       AsmText += MI + "$$7, $$f14\n";
2330b57cec5SDimitry Andric       AsmText += MI + "$$6, $$f15\n";
2340b57cec5SDimitry Andric     }
2350b57cec5SDimitry Andric     break;
2360b57cec5SDimitry Andric 
2370b57cec5SDimitry Andric   case DFSig:
2380b57cec5SDimitry Andric     if (LE) {
2390b57cec5SDimitry Andric       AsmText += MI + "$$4, $$f12\n";
2400b57cec5SDimitry Andric       AsmText += MI + "$$5, $$f13\n";
2410b57cec5SDimitry Andric     } else {
2420b57cec5SDimitry Andric       AsmText += MI + "$$5, $$f12\n";
2430b57cec5SDimitry Andric       AsmText += MI + "$$4, $$f13\n";
2440b57cec5SDimitry Andric     }
2450b57cec5SDimitry Andric     AsmText += MI + "$$6, $$f14\n";
2460b57cec5SDimitry Andric     break;
2470b57cec5SDimitry Andric 
2480b57cec5SDimitry Andric   case NoSig:
2490b57cec5SDimitry Andric     break;
2500b57cec5SDimitry Andric   }
2510b57cec5SDimitry Andric 
2520b57cec5SDimitry Andric   return AsmText;
2530b57cec5SDimitry Andric }
2540b57cec5SDimitry Andric 
2550b57cec5SDimitry Andric // Make sure that we know we already need a stub for this function.
2560b57cec5SDimitry Andric // Having called needsFPHelperFromSig
2570b57cec5SDimitry Andric static void assureFPCallStub(Function &F, Module *M,
2580b57cec5SDimitry Andric                              const MipsTargetMachine &TM) {
2590b57cec5SDimitry Andric   // for now we only need them for static relocation
2600b57cec5SDimitry Andric   if (TM.isPositionIndependent())
2610b57cec5SDimitry Andric     return;
2620b57cec5SDimitry Andric   LLVMContext &Context = M->getContext();
2630b57cec5SDimitry Andric   bool LE = TM.isLittleEndian();
2645ffd83dbSDimitry Andric   std::string Name(F.getName());
2650b57cec5SDimitry Andric   std::string SectionName = ".mips16.call.fp." + Name;
2660b57cec5SDimitry Andric   std::string StubName = "__call_stub_fp_" + Name;
2670b57cec5SDimitry Andric   //
2680b57cec5SDimitry Andric   // see if we already have the stub
2690b57cec5SDimitry Andric   //
2700b57cec5SDimitry Andric   Function *FStub = M->getFunction(StubName);
2710b57cec5SDimitry Andric   if (FStub && !FStub->isDeclaration()) return;
2720b57cec5SDimitry Andric   FStub = Function::Create(F.getFunctionType(),
2730b57cec5SDimitry Andric                            Function::InternalLinkage, StubName, M);
2740b57cec5SDimitry Andric   FStub->addFnAttr("mips16_fp_stub");
2750b57cec5SDimitry Andric   FStub->addFnAttr(Attribute::Naked);
2760b57cec5SDimitry Andric   FStub->addFnAttr(Attribute::NoInline);
2770b57cec5SDimitry Andric   FStub->addFnAttr(Attribute::NoUnwind);
2780b57cec5SDimitry Andric   FStub->addFnAttr("nomips16");
2790b57cec5SDimitry Andric   FStub->setSection(SectionName);
2800b57cec5SDimitry Andric   BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
2810b57cec5SDimitry Andric   FPReturnVariant RV = whichFPReturnVariant(FStub->getReturnType());
2820b57cec5SDimitry Andric   FPParamVariant PV = whichFPParamVariantNeeded(F);
2830b57cec5SDimitry Andric 
2840b57cec5SDimitry Andric   std::string AsmText;
2850b57cec5SDimitry Andric   AsmText += ".set reorder\n";
2860b57cec5SDimitry Andric   AsmText += swapFPIntParams(PV, M, LE, true);
2870b57cec5SDimitry Andric   if (RV != NoFPRet) {
2880b57cec5SDimitry Andric     AsmText += "move $$18, $$31\n";
2890b57cec5SDimitry Andric     AsmText += "jal " + Name + "\n";
2900b57cec5SDimitry Andric   } else {
2910b57cec5SDimitry Andric     AsmText += "lui  $$25, %hi(" + Name + ")\n";
2920b57cec5SDimitry Andric     AsmText += "addiu  $$25, $$25, %lo(" + Name + ")\n";
2930b57cec5SDimitry Andric   }
2940b57cec5SDimitry Andric 
2950b57cec5SDimitry Andric   switch (RV) {
2960b57cec5SDimitry Andric   case FRet:
2970b57cec5SDimitry Andric     AsmText += "mfc1 $$2, $$f0\n";
2980b57cec5SDimitry Andric     break;
2990b57cec5SDimitry Andric 
3000b57cec5SDimitry Andric   case DRet:
3010b57cec5SDimitry Andric     if (LE) {
3020b57cec5SDimitry Andric       AsmText += "mfc1 $$2, $$f0\n";
3030b57cec5SDimitry Andric       AsmText += "mfc1 $$3, $$f1\n";
3040b57cec5SDimitry Andric     } else {
3050b57cec5SDimitry Andric       AsmText += "mfc1 $$3, $$f0\n";
3060b57cec5SDimitry Andric       AsmText += "mfc1 $$2, $$f1\n";
3070b57cec5SDimitry Andric     }
3080b57cec5SDimitry Andric     break;
3090b57cec5SDimitry Andric 
3100b57cec5SDimitry Andric   case CFRet:
3110b57cec5SDimitry Andric     if (LE) {
3120b57cec5SDimitry Andric       AsmText += "mfc1 $$2, $$f0\n";
3130b57cec5SDimitry Andric       AsmText += "mfc1 $$3, $$f2\n";
3140b57cec5SDimitry Andric     } else {
3150b57cec5SDimitry Andric       AsmText += "mfc1 $$3, $$f0\n";
3160b57cec5SDimitry Andric       AsmText += "mfc1 $$3, $$f2\n";
3170b57cec5SDimitry Andric     }
3180b57cec5SDimitry Andric     break;
3190b57cec5SDimitry Andric 
3200b57cec5SDimitry Andric   case CDRet:
3210b57cec5SDimitry Andric     if (LE) {
3220b57cec5SDimitry Andric       AsmText += "mfc1 $$4, $$f2\n";
3230b57cec5SDimitry Andric       AsmText += "mfc1 $$5, $$f3\n";
3240b57cec5SDimitry Andric       AsmText += "mfc1 $$2, $$f0\n";
3250b57cec5SDimitry Andric       AsmText += "mfc1 $$3, $$f1\n";
3260b57cec5SDimitry Andric 
3270b57cec5SDimitry Andric     } else {
3280b57cec5SDimitry Andric       AsmText += "mfc1 $$5, $$f2\n";
3290b57cec5SDimitry Andric       AsmText += "mfc1 $$4, $$f3\n";
3300b57cec5SDimitry Andric       AsmText += "mfc1 $$3, $$f0\n";
3310b57cec5SDimitry Andric       AsmText += "mfc1 $$2, $$f1\n";
3320b57cec5SDimitry Andric     }
3330b57cec5SDimitry Andric     break;
3340b57cec5SDimitry Andric 
3350b57cec5SDimitry Andric   case NoFPRet:
3360b57cec5SDimitry Andric     break;
3370b57cec5SDimitry Andric   }
3380b57cec5SDimitry Andric 
3390b57cec5SDimitry Andric   if (RV != NoFPRet)
3400b57cec5SDimitry Andric     AsmText += "jr $$18\n";
3410b57cec5SDimitry Andric   else
3420b57cec5SDimitry Andric     AsmText += "jr $$25\n";
3435ffd83dbSDimitry Andric   emitInlineAsm(Context, BB, AsmText);
3440b57cec5SDimitry Andric 
3450b57cec5SDimitry Andric   new UnreachableInst(Context, BB);
3460b57cec5SDimitry Andric }
3470b57cec5SDimitry Andric 
3480b57cec5SDimitry Andric // Functions that are llvm intrinsics and don't need helpers.
3490b57cec5SDimitry Andric static const char *const IntrinsicInline[] = {
3500b57cec5SDimitry Andric   "fabs", "fabsf",
3510b57cec5SDimitry Andric   "llvm.ceil.f32", "llvm.ceil.f64",
3520b57cec5SDimitry Andric   "llvm.copysign.f32", "llvm.copysign.f64",
3530b57cec5SDimitry Andric   "llvm.cos.f32", "llvm.cos.f64",
3540b57cec5SDimitry Andric   "llvm.exp.f32", "llvm.exp.f64",
3550b57cec5SDimitry Andric   "llvm.exp2.f32", "llvm.exp2.f64",
3560b57cec5SDimitry Andric   "llvm.fabs.f32", "llvm.fabs.f64",
3570b57cec5SDimitry Andric   "llvm.floor.f32", "llvm.floor.f64",
3580b57cec5SDimitry Andric   "llvm.fma.f32", "llvm.fma.f64",
3590b57cec5SDimitry Andric   "llvm.log.f32", "llvm.log.f64",
3600b57cec5SDimitry Andric   "llvm.log10.f32", "llvm.log10.f64",
3610b57cec5SDimitry Andric   "llvm.nearbyint.f32", "llvm.nearbyint.f64",
3620b57cec5SDimitry Andric   "llvm.pow.f32", "llvm.pow.f64",
363fe6060f1SDimitry Andric   "llvm.powi.f32.i32", "llvm.powi.f64.i32",
3640b57cec5SDimitry Andric   "llvm.rint.f32", "llvm.rint.f64",
3650b57cec5SDimitry Andric   "llvm.round.f32", "llvm.round.f64",
3660b57cec5SDimitry Andric   "llvm.sin.f32", "llvm.sin.f64",
3670b57cec5SDimitry Andric   "llvm.sqrt.f32", "llvm.sqrt.f64",
3680b57cec5SDimitry Andric   "llvm.trunc.f32", "llvm.trunc.f64",
3690b57cec5SDimitry Andric };
3700b57cec5SDimitry Andric 
3710b57cec5SDimitry Andric static bool isIntrinsicInline(Function *F) {
3720b57cec5SDimitry Andric   return std::binary_search(std::begin(IntrinsicInline),
3730b57cec5SDimitry Andric                             std::end(IntrinsicInline), F->getName());
3740b57cec5SDimitry Andric }
3750b57cec5SDimitry Andric 
3760b57cec5SDimitry Andric // Returns of float, double and complex need to be handled with a helper
3770b57cec5SDimitry Andric // function.
3780b57cec5SDimitry Andric static bool fixupFPReturnAndCall(Function &F, Module *M,
3790b57cec5SDimitry Andric                                  const MipsTargetMachine &TM) {
3800b57cec5SDimitry Andric   bool Modified = false;
3810b57cec5SDimitry Andric   LLVMContext &C = M->getContext();
3820b57cec5SDimitry Andric   Type *MyVoid = Type::getVoidTy(C);
3830b57cec5SDimitry Andric   for (auto &BB: F)
3840b57cec5SDimitry Andric     for (auto &I: BB) {
3850b57cec5SDimitry Andric       if (const ReturnInst *RI = dyn_cast<ReturnInst>(&I)) {
3860b57cec5SDimitry Andric         Value *RVal = RI->getReturnValue();
3870b57cec5SDimitry Andric         if (!RVal) continue;
3880b57cec5SDimitry Andric         //
3890b57cec5SDimitry Andric         // If there is a return value and it needs a helper function,
3900b57cec5SDimitry Andric         // figure out which one and add a call before the actual
3910b57cec5SDimitry Andric         // return to this helper. The purpose of the helper is to move
3920b57cec5SDimitry Andric         // floating point values from their soft float return mapping to
3930b57cec5SDimitry Andric         // where they would have been mapped to in floating point registers.
3940b57cec5SDimitry Andric         //
3950b57cec5SDimitry Andric         Type *T = RVal->getType();
3960b57cec5SDimitry Andric         FPReturnVariant RV = whichFPReturnVariant(T);
3970b57cec5SDimitry Andric         if (RV == NoFPRet) continue;
3980b57cec5SDimitry Andric         static const char *const Helper[NoFPRet] = {
3990b57cec5SDimitry Andric           "__mips16_ret_sf", "__mips16_ret_df", "__mips16_ret_sc",
4000b57cec5SDimitry Andric           "__mips16_ret_dc"
4010b57cec5SDimitry Andric         };
4020b57cec5SDimitry Andric         const char *Name = Helper[RV];
4030b57cec5SDimitry Andric         AttributeList A;
4040b57cec5SDimitry Andric         Value *Params[] = {RVal};
4050b57cec5SDimitry Andric         Modified = true;
4060b57cec5SDimitry Andric         //
4070b57cec5SDimitry Andric         // These helper functions have a different calling ABI so
4080b57cec5SDimitry Andric         // this __Mips16RetHelper indicates that so that later
4090b57cec5SDimitry Andric         // during call setup, the proper call lowering to the helper
4100b57cec5SDimitry Andric         // functions will take place.
4110b57cec5SDimitry Andric         //
412349cc55cSDimitry Andric         A = A.addFnAttribute(C, "__Mips16RetHelper");
413bdd1243dSDimitry Andric         A = A.addFnAttribute(
414bdd1243dSDimitry Andric             C, Attribute::getWithMemoryEffects(C, MemoryEffects::none()));
415349cc55cSDimitry Andric         A = A.addFnAttribute(C, Attribute::NoInline);
4160b57cec5SDimitry Andric         FunctionCallee F = (M->getOrInsertFunction(Name, A, MyVoid, T));
417*0fca6ea1SDimitry Andric         CallInst::Create(F, Params, "", I.getIterator());
4180b57cec5SDimitry Andric       } else if (const CallInst *CI = dyn_cast<CallInst>(&I)) {
4190b57cec5SDimitry Andric         FunctionType *FT = CI->getFunctionType();
4200b57cec5SDimitry Andric         Function *F_ =  CI->getCalledFunction();
4210b57cec5SDimitry Andric         if (needsFPReturnHelper(*FT) &&
4220b57cec5SDimitry Andric             !(F_ && isIntrinsicInline(F_))) {
4230b57cec5SDimitry Andric           Modified=true;
4240b57cec5SDimitry Andric           F.addFnAttr("saveS2");
4250b57cec5SDimitry Andric         }
4260b57cec5SDimitry Andric         if (F_ && !isIntrinsicInline(F_)) {
4270b57cec5SDimitry Andric           // pic mode calls are handled by already defined
4280b57cec5SDimitry Andric           // helper functions
4290b57cec5SDimitry Andric           if (needsFPReturnHelper(*F_)) {
4300b57cec5SDimitry Andric             Modified=true;
4310b57cec5SDimitry Andric             F.addFnAttr("saveS2");
4320b57cec5SDimitry Andric           }
4330b57cec5SDimitry Andric           if (!TM.isPositionIndependent()) {
4340b57cec5SDimitry Andric             if (needsFPHelperFromSig(*F_)) {
4350b57cec5SDimitry Andric               assureFPCallStub(*F_, M, TM);
4360b57cec5SDimitry Andric               Modified=true;
4370b57cec5SDimitry Andric             }
4380b57cec5SDimitry Andric           }
4390b57cec5SDimitry Andric         }
4400b57cec5SDimitry Andric       }
4410b57cec5SDimitry Andric     }
4420b57cec5SDimitry Andric   return Modified;
4430b57cec5SDimitry Andric }
4440b57cec5SDimitry Andric 
4450b57cec5SDimitry Andric static void createFPFnStub(Function *F, Module *M, FPParamVariant PV,
4460b57cec5SDimitry Andric                            const MipsTargetMachine &TM) {
4470b57cec5SDimitry Andric   bool PicMode = TM.isPositionIndependent();
4480b57cec5SDimitry Andric   bool LE = TM.isLittleEndian();
4490b57cec5SDimitry Andric   LLVMContext &Context = M->getContext();
4505ffd83dbSDimitry Andric   std::string Name(F->getName());
4510b57cec5SDimitry Andric   std::string SectionName = ".mips16.fn." + Name;
4520b57cec5SDimitry Andric   std::string StubName = "__fn_stub_" + Name;
4530b57cec5SDimitry Andric   std::string LocalName = "$$__fn_local_" + Name;
4540b57cec5SDimitry Andric   Function *FStub = Function::Create
4550b57cec5SDimitry Andric     (F->getFunctionType(),
4560b57cec5SDimitry Andric      Function::InternalLinkage, StubName, M);
4570b57cec5SDimitry Andric   FStub->addFnAttr("mips16_fp_stub");
4580b57cec5SDimitry Andric   FStub->addFnAttr(Attribute::Naked);
4590b57cec5SDimitry Andric   FStub->addFnAttr(Attribute::NoUnwind);
4600b57cec5SDimitry Andric   FStub->addFnAttr(Attribute::NoInline);
4610b57cec5SDimitry Andric   FStub->addFnAttr("nomips16");
4620b57cec5SDimitry Andric   FStub->setSection(SectionName);
4630b57cec5SDimitry Andric   BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
4640b57cec5SDimitry Andric 
4650b57cec5SDimitry Andric   std::string AsmText;
4660b57cec5SDimitry Andric   if (PicMode) {
4670b57cec5SDimitry Andric     AsmText += ".set noreorder\n";
4680b57cec5SDimitry Andric     AsmText += ".cpload $$25\n";
4690b57cec5SDimitry Andric     AsmText += ".set reorder\n";
4700b57cec5SDimitry Andric     AsmText += ".reloc 0, R_MIPS_NONE, " + Name + "\n";
4710b57cec5SDimitry Andric     AsmText += "la $$25, " + LocalName + "\n";
4720b57cec5SDimitry Andric   } else
4730b57cec5SDimitry Andric     AsmText += "la $$25, " + Name + "\n";
4740b57cec5SDimitry Andric   AsmText += swapFPIntParams(PV, M, LE, false);
4750b57cec5SDimitry Andric   AsmText += "jr $$25\n";
4760b57cec5SDimitry Andric   AsmText += LocalName + " = " + Name + "\n";
4775ffd83dbSDimitry Andric   emitInlineAsm(Context, BB, AsmText);
4780b57cec5SDimitry Andric 
4790b57cec5SDimitry Andric   new UnreachableInst(FStub->getContext(), BB);
4800b57cec5SDimitry Andric }
4810b57cec5SDimitry Andric 
4820b57cec5SDimitry Andric // remove the use-soft-float attribute
4830b57cec5SDimitry Andric static void removeUseSoftFloat(Function &F) {
4840b57cec5SDimitry Andric   LLVM_DEBUG(errs() << "removing -use-soft-float\n");
4850eae32dcSDimitry Andric   F.removeFnAttr("use-soft-float");
4860b57cec5SDimitry Andric   if (F.hasFnAttribute("use-soft-float")) {
4870b57cec5SDimitry Andric     LLVM_DEBUG(errs() << "still has -use-soft-float\n");
4880b57cec5SDimitry Andric   }
4890eae32dcSDimitry Andric   F.addFnAttr("use-soft-float", "false");
4900b57cec5SDimitry Andric }
4910b57cec5SDimitry Andric 
4920b57cec5SDimitry Andric // This pass only makes sense when the underlying chip has floating point but
4930b57cec5SDimitry Andric // we are compiling as mips16.
4940b57cec5SDimitry Andric // For all mips16 functions (that are not stubs we have already generated), or
4950b57cec5SDimitry Andric // declared via attributes as nomips16, we must:
4960b57cec5SDimitry Andric //    1) fixup all returns of float, double, single and double complex
4970b57cec5SDimitry Andric //       by calling a helper function before the actual return.
4980b57cec5SDimitry Andric //    2) generate helper functions (stubs) that can be called by mips32
4990b57cec5SDimitry Andric //       functions that will move parameters passed normally passed in
5000b57cec5SDimitry Andric //       floating point
5010b57cec5SDimitry Andric //       registers the soft float equivalents.
5020b57cec5SDimitry Andric //    3) in the case of static relocation, generate helper functions so that
5030b57cec5SDimitry Andric //       mips16 functions can call extern functions of unknown type (mips16 or
5040b57cec5SDimitry Andric //       mips32).
5050b57cec5SDimitry Andric //    4) TBD. For pic, calls to extern functions of unknown type are handled by
5060b57cec5SDimitry Andric //       predefined helper functions in libc but this work is currently done
5070b57cec5SDimitry Andric //       during call lowering but it should be moved here in the future.
5080b57cec5SDimitry Andric bool Mips16HardFloat::runOnModule(Module &M) {
5090b57cec5SDimitry Andric   auto &TM = static_cast<const MipsTargetMachine &>(
5100b57cec5SDimitry Andric       getAnalysis<TargetPassConfig>().getTM<TargetMachine>());
5110b57cec5SDimitry Andric   LLVM_DEBUG(errs() << "Run on Module Mips16HardFloat\n");
5120b57cec5SDimitry Andric   bool Modified = false;
5130b57cec5SDimitry Andric   for (Module::iterator F = M.begin(), E = M.end(); F != E; ++F) {
5140b57cec5SDimitry Andric     if (F->hasFnAttribute("nomips16") &&
5150b57cec5SDimitry Andric         F->hasFnAttribute("use-soft-float")) {
5160b57cec5SDimitry Andric       removeUseSoftFloat(*F);
5170b57cec5SDimitry Andric       continue;
5180b57cec5SDimitry Andric     }
5190b57cec5SDimitry Andric     if (F->isDeclaration() || F->hasFnAttribute("mips16_fp_stub") ||
5200b57cec5SDimitry Andric         F->hasFnAttribute("nomips16")) continue;
5210b57cec5SDimitry Andric     Modified |= fixupFPReturnAndCall(*F, &M, TM);
5220b57cec5SDimitry Andric     FPParamVariant V = whichFPParamVariantNeeded(*F);
5230b57cec5SDimitry Andric     if (V != NoSig) {
5240b57cec5SDimitry Andric       Modified = true;
5250b57cec5SDimitry Andric       createFPFnStub(&*F, &M, V, TM);
5260b57cec5SDimitry Andric     }
5270b57cec5SDimitry Andric   }
5280b57cec5SDimitry Andric   return Modified;
5290b57cec5SDimitry Andric }
5300b57cec5SDimitry Andric 
5310b57cec5SDimitry Andric ModulePass *llvm::createMips16HardFloatPass() {
5320b57cec5SDimitry Andric   return new Mips16HardFloat();
5330b57cec5SDimitry Andric }
534