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