Coverage Report

Created: 2019-07-24 05:18

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