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