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