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