Mips16HardFloat.cpp revision 78f8339f351c95cb6ed739b7e62f01ccc2716144
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   "fabsf",
330   "llvm.ceil.f32", "llvm.ceil.f64",
331   "llvm.copysign.f32", "llvm.copysign.f64",
332   "llvm.cos.f32", "llvm.cos.f64",
333   "llvm.exp.f32", "llvm.exp.f64",
334   "llvm.exp2.f32", "llvm.exp2.f64",
335   "llvm.fabs.f32", "llvm.fabs.f64",
336   "llvm.floor.f32", "llvm.floor.f64",
337   "llvm.fma.f32", "llvm.fma.f64",
338   "llvm.log.f32", "llvm.log.f64",
339   "llvm.log10.f32", "llvm.log10.f64",
340   "llvm.nearbyint.f32", "llvm.nearbyint.f64",
341   "llvm.pow.f32", "llvm.pow.f64",
342   "llvm.powi.f32", "llvm.powi.f64",
343   "llvm.rint.f32", "llvm.rint.f64",
344   "llvm.round.f32", "llvm.round.f64",
345   "llvm.sin.f32", "llvm.sin.f64",
346   "llvm.sqrt.f32", "llvm.sqrt.f64",
347   "llvm.trunc.f32", "llvm.trunc.f64",
348  };
349
350static bool isIntrinsicInline(Function *F) {
351  return std::binary_search(
352    IntrinsicInline, array_endof(IntrinsicInline),
353    F->getName());
354}
355//
356// Returns of float, double and complex need to be handled with a helper
357// function.
358//
359static bool fixupFPReturnAndCall
360  (Function &F, Module *M,  const MipsSubtarget &Subtarget) {
361  bool Modified = false;
362  LLVMContext &C = M->getContext();
363  Type *MyVoid = Type::getVoidTy(C);
364  for (Function::iterator BB = F.begin(), E = F.end(); BB != E; ++BB)
365    for (BasicBlock::iterator I = BB->begin(), E = BB->end();
366         I != E; ++I) {
367      Instruction &Inst = *I;
368      if (const ReturnInst *RI = dyn_cast<ReturnInst>(I)) {
369        Value *RVal = RI->getReturnValue();
370        if (!RVal) continue;
371        //
372        // If there is a return value and it needs a helper function,
373        // figure out which one and add a call before the actual
374        // return to this helper. The purpose of the helper is to move
375        // floating point values from their soft float return mapping to
376        // where they would have been mapped to in floating point registers.
377        //
378        Type *T = RVal->getType();
379        FPReturnVariant RV = whichFPReturnVariant(T);
380        if (RV == NoFPRet) continue;
381        static const char* Helper[NoFPRet] =
382          {"__mips16_ret_sf", "__mips16_ret_df", "__mips16_ret_sc",
383           "__mips16_ret_dc"};
384        const char *Name = Helper[RV];
385        AttributeSet A;
386        Value *Params[] = {RVal};
387        Modified = true;
388        //
389        // These helper functions have a different calling ABI so
390        // this __Mips16RetHelper indicates that so that later
391        // during call setup, the proper call lowering to the helper
392        // functions will take place.
393        //
394        A = A.addAttribute(C, AttributeSet::FunctionIndex,
395                           "__Mips16RetHelper");
396        A = A.addAttribute(C, AttributeSet::FunctionIndex,
397                           Attribute::ReadNone);
398        A = A.addAttribute(C, AttributeSet::FunctionIndex,
399                           Attribute::NoInline);
400        Value *F = (M->getOrInsertFunction(Name, A, MyVoid, T, NULL));
401        CallInst::Create(F, Params, "", &Inst );
402      } else if (const CallInst *CI = dyn_cast<CallInst>(I)) {
403          // pic mode calls are handled by already defined
404          // helper functions
405          if (Subtarget.getRelocationModel() != Reloc::PIC_ ) {
406            Function *F_ =  CI->getCalledFunction();
407            if (F_ && !isIntrinsicInline(F_) && needsFPHelperFromSig(*F_)) {
408              assureFPCallStub(*F_, M, Subtarget);
409              Modified=true;
410            }
411          }
412      }
413    }
414  return Modified;
415}
416
417static void createFPFnStub(Function *F, Module *M, FPParamVariant PV,
418                  const MipsSubtarget &Subtarget ) {
419  bool PicMode = Subtarget.getRelocationModel() == Reloc::PIC_;
420  bool LE = Subtarget.isLittle();
421  LLVMContext &Context = M->getContext();
422  std::string Name = F->getName();
423  std::string SectionName = ".mips16.fn." + Name;
424  std::string StubName = "__fn_stub_" + Name;
425  std::string LocalName = "$$__fn_local_" + Name;
426  Function *FStub = Function::Create
427    (F->getFunctionType(),
428     Function::InternalLinkage, StubName, M);
429  FStub->addFnAttr("mips16_fp_stub");
430  FStub->addFnAttr(llvm::Attribute::Naked);
431  FStub->addFnAttr(llvm::Attribute::NoUnwind);
432  FStub->addFnAttr(llvm::Attribute::NoInline);
433  FStub->addFnAttr("nomips16");
434  FStub->setSection(SectionName);
435  BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
436  InlineAsmHelper IAH(Context, BB);
437  IAH.Out(" .set  macro");
438  if (PicMode) {
439    IAH.Out(".set noreorder");
440    IAH.Out(".cpload  $$25");
441    IAH.Out(".set reorder");
442    IAH.Out(".reloc 0,R_MIPS_NONE," + Name);
443    IAH.Out("la $$25," + LocalName);
444  }
445  else {
446    IAH.Out(".set reorder");
447    IAH.Out("la $$25," + Name);
448  }
449  swapFPIntParams(PV, M, IAH, LE, false);
450  IAH.Out("jr $$25");
451  IAH.Out(LocalName + " = " + Name);
452  new UnreachableInst(FStub->getContext(), BB);
453}
454
455//
456// remove the use-soft-float attribute
457//
458static void removeUseSoftFloat(Function &F) {
459  AttributeSet A;
460  DEBUG(errs() << "removing -use-soft-float\n");
461  A = A.addAttribute(F.getContext(), AttributeSet::FunctionIndex,
462                     "use-soft-float", "false");
463  F.removeAttributes(AttributeSet::FunctionIndex, A);
464  if (F.hasFnAttribute("use-soft-float")) {
465    DEBUG(errs() << "still has -use-soft-float\n");
466  }
467  F.addAttributes(AttributeSet::FunctionIndex, A);
468}
469
470namespace llvm {
471
472//
473// This pass only makes sense when the underlying chip has floating point but
474// we are compiling as mips16.
475// For all mips16 functions (that are not stubs we have already generated), or
476// declared via attributes as nomips16, we must:
477//    1) fixup all returns of float, double, single and double complex
478//       by calling a helper function before the actual return.
479//    2) generate helper functions (stubs) that can be called by mips32 functions
480//       that will move parameters passed normally passed in floating point
481//       registers the soft float equivalents.
482//    3) in the case of static relocation, generate helper functions so that
483//       mips16 functions can call extern functions of unknown type (mips16 or
484//       mips32).
485//    4) TBD. For pic, calls to extern functions of unknown type are handled by
486//       predefined helper functions in libc but this work is currently done
487//       during call lowering but it should be moved here in the future.
488//
489bool Mips16HardFloat::runOnModule(Module &M) {
490  DEBUG(errs() << "Run on Module Mips16HardFloat\n");
491  bool Modified = false;
492  for (Module::iterator F = M.begin(), E = M.end(); F != E; ++F) {
493    if (F->hasFnAttribute("nomips16") &&
494        F->hasFnAttribute("use-soft-float")) {
495      removeUseSoftFloat(*F);
496      continue;
497    }
498    if (F->isDeclaration() || F->hasFnAttribute("mips16_fp_stub") ||
499        F->hasFnAttribute("nomips16")) continue;
500    Modified |= fixupFPReturnAndCall(*F, &M, Subtarget);
501    FPParamVariant V = whichFPParamVariantNeeded(*F);
502    if (V != NoSig) {
503      Modified = true;
504      createFPFnStub(F, &M, V, Subtarget);
505    }
506  }
507  return Modified;
508}
509
510char Mips16HardFloat::ID = 0;
511
512}
513
514ModulePass *llvm::createMips16HardFloat(MipsTargetMachine &TM) {
515  return new Mips16HardFloat(TM);
516}
517
518