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