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