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