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