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