blob: 65dbd74a1c8a4524c65b3a33159e1bb002d010c4 [file] [log] [blame]
Reed Kotler783c7942013-05-10 22:25:39 +00001//===---- 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#define DEBUG_TYPE "mips16-hard-float"
15#include "Mips16HardFloat.h"
16#include "llvm/IR/Module.h"
17#include "llvm/Support/Debug.h"
18#include "llvm/Support/raw_ostream.h"
Reed Kotlerd265e882013-08-11 21:30:27 +000019#include <algorithm>
Reed Kotler783c7942013-05-10 22:25:39 +000020#include <string>
21
Reed Kotler2c4657d2013-05-14 02:00:24 +000022static void inlineAsmOut
23 (LLVMContext &C, StringRef AsmString, BasicBlock *BB ) {
24 std::vector<llvm::Type *> AsmArgTypes;
25 std::vector<llvm::Value*> AsmArgs;
26 llvm::FunctionType *AsmFTy =
27 llvm::FunctionType::get(Type::getVoidTy(C),
28 AsmArgTypes, false);
29 llvm::InlineAsm *IA =
30 llvm::InlineAsm::get(AsmFTy, AsmString, "", true,
31 /* IsAlignStack */ false,
32 llvm::InlineAsm::AD_ATT);
33 CallInst::Create(IA, AsmArgs, "", BB);
34}
35
36namespace {
37
38class InlineAsmHelper {
39 LLVMContext &C;
40 BasicBlock *BB;
41public:
42 InlineAsmHelper(LLVMContext &C_, BasicBlock *BB_) :
43 C(C_), BB(BB_) {
44 }
45
46 void Out(StringRef AsmString) {
47 inlineAsmOut(C, AsmString, BB);
48 }
49
50};
51}
Reed Kotler783c7942013-05-10 22:25:39 +000052//
53// Return types that matter for hard float are:
54// float, double, complex float, and complex double
55//
56enum FPReturnVariant {
57 FRet, DRet, CFRet, CDRet, NoFPRet
58};
59
60//
61// Determine which FP return type this function has
62//
63static FPReturnVariant whichFPReturnVariant(Type *T) {
64 switch (T->getTypeID()) {
65 case Type::FloatTyID:
66 return FRet;
67 case Type::DoubleTyID:
68 return DRet;
69 case Type::StructTyID:
70 if (T->getStructNumElements() != 2)
71 break;
72 if ((T->getContainedType(0)->isFloatTy()) &&
73 (T->getContainedType(1)->isFloatTy()))
74 return CFRet;
75 if ((T->getContainedType(0)->isDoubleTy()) &&
76 (T->getContainedType(1)->isDoubleTy()))
77 return CDRet;
78 break;
79 default:
80 break;
81 }
82 return NoFPRet;
83}
84
85//
Reed Kotler2c4657d2013-05-14 02:00:24 +000086// Parameter type that matter are float, (float, float), (float, double),
87// double, (double, double), (double, float)
88//
89enum FPParamVariant {
90 FSig, FFSig, FDSig,
91 DSig, DDSig, DFSig, NoSig
92};
93
94// which floating point parameter signature variant we are dealing with
95//
96typedef Type::TypeID TypeID;
97const Type::TypeID FloatTyID = Type::FloatTyID;
98const Type::TypeID DoubleTyID = Type::DoubleTyID;
99
100static FPParamVariant whichFPParamVariantNeeded(Function &F) {
101 switch (F.arg_size()) {
102 case 0:
103 return NoSig;
104 case 1:{
105 TypeID ArgTypeID = F.getFunctionType()->getParamType(0)->getTypeID();
106 switch (ArgTypeID) {
107 case FloatTyID:
108 return FSig;
109 case DoubleTyID:
110 return DSig;
111 default:
112 return NoSig;
113 }
114 }
115 default: {
116 TypeID ArgTypeID0 = F.getFunctionType()->getParamType(0)->getTypeID();
117 TypeID ArgTypeID1 = F.getFunctionType()->getParamType(1)->getTypeID();
118 switch(ArgTypeID0) {
119 case FloatTyID: {
120 switch (ArgTypeID1) {
121 case FloatTyID:
122 return FFSig;
123 case DoubleTyID:
124 return FDSig;
125 default:
126 return FSig;
127 }
128 }
129 case DoubleTyID: {
130 switch (ArgTypeID1) {
131 case FloatTyID:
132 return DFSig;
133 case DoubleTyID:
134 return DDSig;
135 default:
136 return DSig;
137 }
138 }
139 default:
140 return NoSig;
141 }
142 }
143 }
144 llvm_unreachable("can't get here");
145}
146
147// Figure out if we need float point based on the function parameters.
148// We need to move variables in and/or out of floating point
149// registers because of the ABI
150//
151static bool needsFPStubFromParams(Function &F) {
152 if (F.arg_size() >=1) {
153 Type *ArgType = F.getFunctionType()->getParamType(0);
154 switch (ArgType->getTypeID()) {
155 case Type::FloatTyID:
156 case Type::DoubleTyID:
157 return true;
158 default:
159 break;
160 }
161 }
162 return false;
163}
164
165static bool needsFPReturnHelper(Function &F) {
166 Type* RetType = F.getReturnType();
167 return whichFPReturnVariant(RetType) != NoFPRet;
168}
169
170static bool needsFPHelperFromSig(Function &F) {
171 return needsFPStubFromParams(F) || needsFPReturnHelper(F);
172}
173
174//
175// We swap between FP and Integer registers to allow Mips16 and Mips32 to
176// interoperate
177//
178
Reed Kotlercad47f02013-05-14 02:13:45 +0000179static void swapFPIntParams
180 (FPParamVariant PV, Module *M, InlineAsmHelper &IAH,
181 bool LE, bool ToFP) {
Reed Kotler2c4657d2013-05-14 02:00:24 +0000182 //LLVMContext &Context = M->getContext();
183 std::string MI = ToFP? "mtc1 ": "mfc1 ";
184 switch (PV) {
185 case FSig:
186 IAH.Out(MI + "$$4,$$f12");
187 break;
188 case FFSig:
189 IAH.Out(MI +"$$4,$$f12");
190 IAH.Out(MI + "$$5,$$f14");
191 break;
192 case FDSig:
193 IAH.Out(MI + "$$4,$$f12");
194 if (LE) {
195 IAH.Out(MI + "$$6,$$f14");
196 IAH.Out(MI + "$$7,$$f15");
197 } else {
198 IAH.Out(MI + "$$7,$$f14");
199 IAH.Out(MI + "$$6,$$f15");
200 }
201 break;
202 case DSig:
203 if (LE) {
204 IAH.Out(MI + "$$4,$$f12");
205 IAH.Out(MI + "$$5,$$f13");
206 } else {
207 IAH.Out(MI + "$$5,$$f12");
208 IAH.Out(MI + "$$4,$$f13");
209 }
210 break;
211 case DDSig:
212 if (LE) {
213 IAH.Out(MI + "$$4,$$f12");
214 IAH.Out(MI + "$$5,$$f13");
215 IAH.Out(MI + "$$6,$$f14");
216 IAH.Out(MI + "$$7,$$f15");
217 } else {
218 IAH.Out(MI + "$$5,$$f12");
219 IAH.Out(MI + "$$4,$$f13");
220 IAH.Out(MI + "$$7,$$f14");
221 IAH.Out(MI + "$$6,$$f15");
222 }
223 break;
224 case DFSig:
225 if (LE) {
226 IAH.Out(MI + "$$4,$$f12");
227 IAH.Out(MI + "$$5,$$f13");
228 } else {
229 IAH.Out(MI + "$$5,$$f12");
230 IAH.Out(MI + "$$4,$$f13");
231 }
232 IAH.Out(MI + "$$6,$$f14");
233 break;
234 case NoSig:
235 return;
236 }
237}
238//
239// Make sure that we know we already need a stub for this function.
240// Having called needsFPHelperFromSig
241//
Reed Kotlercad47f02013-05-14 02:13:45 +0000242static void assureFPCallStub(Function &F, Module *M,
243 const MipsSubtarget &Subtarget){
Reed Kotler2c4657d2013-05-14 02:00:24 +0000244 // for now we only need them for static relocation
Reed Kotler821e86f2013-05-14 06:00:01 +0000245 if (Subtarget.getRelocationModel() == Reloc::PIC_)
Reed Kotler2c4657d2013-05-14 02:00:24 +0000246 return;
247 LLVMContext &Context = M->getContext();
248 bool LE = Subtarget.isLittle();
249 std::string Name = F.getName();
250 std::string SectionName = ".mips16.call.fp." + Name;
Reed Kotler302ae6b2013-08-01 02:26:31 +0000251 std::string StubName = "__call_stub_fp_" + Name;
Reed Kotler2c4657d2013-05-14 02:00:24 +0000252 //
253 // see if we already have the stub
254 //
255 Function *FStub = M->getFunction(StubName);
256 if (FStub && !FStub->isDeclaration()) return;
257 FStub = Function::Create(F.getFunctionType(),
258 Function::InternalLinkage, StubName, M);
259 FStub->addFnAttr("mips16_fp_stub");
260 FStub->addFnAttr(llvm::Attribute::Naked);
Reed Kotler302ae6b2013-08-01 02:26:31 +0000261 FStub->addFnAttr(llvm::Attribute::NoInline);
Reed Kotler2c4657d2013-05-14 02:00:24 +0000262 FStub->addFnAttr(llvm::Attribute::NoUnwind);
263 FStub->addFnAttr("nomips16");
264 FStub->setSection(SectionName);
265 BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
266 InlineAsmHelper IAH(Context, BB);
Reed Kotler302ae6b2013-08-01 02:26:31 +0000267 IAH.Out(".set reorder");
Reed Kotler2c4657d2013-05-14 02:00:24 +0000268 FPReturnVariant RV = whichFPReturnVariant(FStub->getReturnType());
269 FPParamVariant PV = whichFPParamVariantNeeded(F);
270 swapFPIntParams(PV, M, IAH, LE, true);
271 if (RV != NoFPRet) {
272 IAH.Out("move $$18, $$31");
273 IAH.Out("jal " + Name);
274 } else {
275 IAH.Out("lui $$25,%hi(" + Name + ")");
276 IAH.Out("addiu $$25,$$25,%lo(" + Name + ")" );
277 }
278 switch (RV) {
279 case FRet:
280 IAH.Out("mfc1 $$2,$$f0");
281 break;
282 case DRet:
283 if (LE) {
284 IAH.Out("mfc1 $$2,$$f0");
285 IAH.Out("mfc1 $$3,$$f1");
286 } else {
287 IAH.Out("mfc1 $$3,$$f0");
288 IAH.Out("mfc1 $$2,$$f1");
289 }
290 break;
291 case CFRet:
292 if (LE) {
293 IAH.Out("mfc1 $$2,$$f0");
294 IAH.Out("mfc1 $$3,$$f2");
295 } else {
296 IAH.Out("mfc1 $$3,$$f0");
297 IAH.Out("mfc1 $$3,$$f2");
298 }
299 break;
300 case CDRet:
301 if (LE) {
302 IAH.Out("mfc1 $$4,$$f2");
303 IAH.Out("mfc1 $$5,$$f3");
304 IAH.Out("mfc1 $$2,$$f0");
305 IAH.Out("mfc1 $$3,$$f1");
306
307 } else {
308 IAH.Out("mfc1 $$5,$$f2");
309 IAH.Out("mfc1 $$4,$$f3");
310 IAH.Out("mfc1 $$3,$$f0");
311 IAH.Out("mfc1 $$2,$$f1");
312 }
313 break;
314 case NoFPRet:
315 break;
316 }
317 if (RV != NoFPRet)
318 IAH.Out("jr $$18");
319 else
320 IAH.Out("jr $$25");
321 new UnreachableInst(Context, BB);
322}
323
324//
Reed Kotler5fdadce2013-09-01 04:12:59 +0000325// Functions that are llvm intrinsics and don't need helpers.
Reed Kotlerd265e882013-08-11 21:30:27 +0000326//
Benjamin Kramerc9b7d472013-08-12 09:37:29 +0000327static const char *IntrinsicInline[] =
Reed Kotler5fdadce2013-09-01 04:12:59 +0000328 {"fabs",
Reed Kotler339c7412013-10-08 19:55:01 +0000329 "fabsf",
Reed Kotler5fdadce2013-09-01 04:12:59 +0000330 "llvm.ceil.f32", "llvm.ceil.f64",
331 "llvm.copysign.f32", "llvm.copysign.f64",
332 "llvm.cos.f32", "llvm.cos.f64",
333 "llvm.exp.f32", "llvm.exp.f64",
334 "llvm.exp2.f32", "llvm.exp2.f64",
335 "llvm.fabs.f32", "llvm.fabs.f64",
336 "llvm.floor.f32", "llvm.floor.f64",
337 "llvm.fma.f32", "llvm.fma.f64",
338 "llvm.log.f32", "llvm.log.f64",
339 "llvm.log10.f32", "llvm.log10.f64",
340 "llvm.nearbyint.f32", "llvm.nearbyint.f64",
341 "llvm.pow.f32", "llvm.pow.f64",
342 "llvm.powi.f32", "llvm.powi.f64",
343 "llvm.rint.f32", "llvm.rint.f64",
344 "llvm.round.f32", "llvm.round.f64",
345 "llvm.sin.f32", "llvm.sin.f64",
346 "llvm.sqrt.f32", "llvm.sqrt.f64",
347 "llvm.trunc.f32", "llvm.trunc.f64",
348 };
Reed Kotlerd265e882013-08-11 21:30:27 +0000349
Benjamin Kramerc9b7d472013-08-12 09:37:29 +0000350static bool isIntrinsicInline(Function *F) {
Reed Kotlerd265e882013-08-11 21:30:27 +0000351 return std::binary_search(
352 IntrinsicInline, array_endof(IntrinsicInline),
353 F->getName());
354}
355//
Reed Kotler783c7942013-05-10 22:25:39 +0000356// Returns of float, double and complex need to be handled with a helper
Reed Kotler515e9372013-05-16 02:17:42 +0000357// function.
Reed Kotler783c7942013-05-10 22:25:39 +0000358//
359static bool fixupFPReturnAndCall
360 (Function &F, Module *M, const MipsSubtarget &Subtarget) {
361 bool Modified = false;
362 LLVMContext &C = M->getContext();
363 Type *MyVoid = Type::getVoidTy(C);
364 for (Function::iterator BB = F.begin(), E = F.end(); BB != E; ++BB)
365 for (BasicBlock::iterator I = BB->begin(), E = BB->end();
366 I != E; ++I) {
367 Instruction &Inst = *I;
368 if (const ReturnInst *RI = dyn_cast<ReturnInst>(I)) {
369 Value *RVal = RI->getReturnValue();
370 if (!RVal) continue;
371 //
372 // If there is a return value and it needs a helper function,
373 // figure out which one and add a call before the actual
374 // return to this helper. The purpose of the helper is to move
375 // floating point values from their soft float return mapping to
376 // where they would have been mapped to in floating point registers.
377 //
378 Type *T = RVal->getType();
379 FPReturnVariant RV = whichFPReturnVariant(T);
380 if (RV == NoFPRet) continue;
381 static const char* Helper[NoFPRet] =
382 {"__mips16_ret_sf", "__mips16_ret_df", "__mips16_ret_sc",
383 "__mips16_ret_dc"};
384 const char *Name = Helper[RV];
385 AttributeSet A;
386 Value *Params[] = {RVal};
387 Modified = true;
388 //
389 // These helper functions have a different calling ABI so
390 // this __Mips16RetHelper indicates that so that later
391 // during call setup, the proper call lowering to the helper
392 // functions will take place.
393 //
394 A = A.addAttribute(C, AttributeSet::FunctionIndex,
395 "__Mips16RetHelper");
396 A = A.addAttribute(C, AttributeSet::FunctionIndex,
397 Attribute::ReadNone);
Reed Kotler302ae6b2013-08-01 02:26:31 +0000398 A = A.addAttribute(C, AttributeSet::FunctionIndex,
399 Attribute::NoInline);
Reed Kotler783c7942013-05-10 22:25:39 +0000400 Value *F = (M->getOrInsertFunction(Name, A, MyVoid, T, NULL));
401 CallInst::Create(F, Params, "", &Inst );
Reed Kotler2c4657d2013-05-14 02:00:24 +0000402 } else if (const CallInst *CI = dyn_cast<CallInst>(I)) {
Reed Kotler0ff40012013-12-10 14:29:38 +0000403 Function *F_ = CI->getCalledFunction();
404 if (F_ && !isIntrinsicInline(F_)) {
Reed Kotler2c4657d2013-05-14 02:00:24 +0000405 // pic mode calls are handled by already defined
406 // helper functions
Reed Kotler0ff40012013-12-10 14:29:38 +0000407 if (needsFPReturnHelper(*F_)) {
Reed Kotler2c4657d2013-05-14 02:00:24 +0000408 Modified=true;
Reed Kotler0ff40012013-12-10 14:29:38 +0000409 F.addFnAttr("saveS2");
410 }
411 if (Subtarget.getRelocationModel() != Reloc::PIC_ ) {
412 if (needsFPHelperFromSig(*F_)) {
413 assureFPCallStub(*F_, M, Subtarget);
414 Modified=true;
415 }
Reed Kotler2c4657d2013-05-14 02:00:24 +0000416 }
417 }
Reed Kotler783c7942013-05-10 22:25:39 +0000418 }
419 }
420 return Modified;
421}
422
Reed Kotler515e9372013-05-16 02:17:42 +0000423static void createFPFnStub(Function *F, Module *M, FPParamVariant PV,
424 const MipsSubtarget &Subtarget ) {
425 bool PicMode = Subtarget.getRelocationModel() == Reloc::PIC_;
426 bool LE = Subtarget.isLittle();
427 LLVMContext &Context = M->getContext();
428 std::string Name = F->getName();
429 std::string SectionName = ".mips16.fn." + Name;
430 std::string StubName = "__fn_stub_" + Name;
Reed Kotlera6ce7972013-09-25 20:58:50 +0000431 std::string LocalName = "$$__fn_local_" + Name;
Reed Kotler515e9372013-05-16 02:17:42 +0000432 Function *FStub = Function::Create
433 (F->getFunctionType(),
Reed Kotler302ae6b2013-08-01 02:26:31 +0000434 Function::InternalLinkage, StubName, M);
Reed Kotler515e9372013-05-16 02:17:42 +0000435 FStub->addFnAttr("mips16_fp_stub");
436 FStub->addFnAttr(llvm::Attribute::Naked);
437 FStub->addFnAttr(llvm::Attribute::NoUnwind);
Reed Kotler302ae6b2013-08-01 02:26:31 +0000438 FStub->addFnAttr(llvm::Attribute::NoInline);
Reed Kotler515e9372013-05-16 02:17:42 +0000439 FStub->addFnAttr("nomips16");
440 FStub->setSection(SectionName);
441 BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
442 InlineAsmHelper IAH(Context, BB);
443 IAH.Out(" .set macro");
444 if (PicMode) {
445 IAH.Out(".set noreorder");
Reed Kotlera6ce7972013-09-25 20:58:50 +0000446 IAH.Out(".cpload $$25");
Reed Kotler515e9372013-05-16 02:17:42 +0000447 IAH.Out(".set reorder");
448 IAH.Out(".reloc 0,R_MIPS_NONE," + Name);
449 IAH.Out("la $$25," + LocalName);
450 }
Reed Kotler78fb2912013-09-21 01:37:52 +0000451 else {
452 IAH.Out(".set reorder");
Reed Kotlera6ce7972013-09-25 20:58:50 +0000453 IAH.Out("la $$25," + Name);
Reed Kotler78fb2912013-09-21 01:37:52 +0000454 }
Reed Kotler515e9372013-05-16 02:17:42 +0000455 swapFPIntParams(PV, M, IAH, LE, false);
456 IAH.Out("jr $$25");
457 IAH.Out(LocalName + " = " + Name);
458 new UnreachableInst(FStub->getContext(), BB);
459}
460
Reed Kotlerc03807a2013-08-30 19:40:56 +0000461//
462// remove the use-soft-float attribute
463//
464static void removeUseSoftFloat(Function &F) {
465 AttributeSet A;
466 DEBUG(errs() << "removing -use-soft-float\n");
467 A = A.addAttribute(F.getContext(), AttributeSet::FunctionIndex,
468 "use-soft-float", "false");
469 F.removeAttributes(AttributeSet::FunctionIndex, A);
470 if (F.hasFnAttribute("use-soft-float")) {
471 DEBUG(errs() << "still has -use-soft-float\n");
472 }
473 F.addAttributes(AttributeSet::FunctionIndex, A);
474}
475
Reed Kotler783c7942013-05-10 22:25:39 +0000476namespace llvm {
477
478//
479// This pass only makes sense when the underlying chip has floating point but
480// we are compiling as mips16.
481// For all mips16 functions (that are not stubs we have already generated), or
482// declared via attributes as nomips16, we must:
483// 1) fixup all returns of float, double, single and double complex
484// by calling a helper function before the actual return.
485// 2) generate helper functions (stubs) that can be called by mips32 functions
486// that will move parameters passed normally passed in floating point
Reed Kotler515e9372013-05-16 02:17:42 +0000487// registers the soft float equivalents.
Reed Kotler783c7942013-05-10 22:25:39 +0000488// 3) in the case of static relocation, generate helper functions so that
489// mips16 functions can call extern functions of unknown type (mips16 or
Reed Kotler515e9372013-05-16 02:17:42 +0000490// mips32).
Reed Kotler783c7942013-05-10 22:25:39 +0000491// 4) TBD. For pic, calls to extern functions of unknown type are handled by
492// predefined helper functions in libc but this work is currently done
493// during call lowering but it should be moved here in the future.
494//
495bool Mips16HardFloat::runOnModule(Module &M) {
496 DEBUG(errs() << "Run on Module Mips16HardFloat\n");
497 bool Modified = false;
498 for (Module::iterator F = M.begin(), E = M.end(); F != E; ++F) {
Reed Kotlerc03807a2013-08-30 19:40:56 +0000499 if (F->hasFnAttribute("nomips16") &&
500 F->hasFnAttribute("use-soft-float")) {
501 removeUseSoftFloat(*F);
502 continue;
503 }
Reed Kotler783c7942013-05-10 22:25:39 +0000504 if (F->isDeclaration() || F->hasFnAttribute("mips16_fp_stub") ||
505 F->hasFnAttribute("nomips16")) continue;
506 Modified |= fixupFPReturnAndCall(*F, &M, Subtarget);
Reed Kotler515e9372013-05-16 02:17:42 +0000507 FPParamVariant V = whichFPParamVariantNeeded(*F);
508 if (V != NoSig) {
509 Modified = true;
510 createFPFnStub(F, &M, V, Subtarget);
511 }
Reed Kotler783c7942013-05-10 22:25:39 +0000512 }
513 return Modified;
514}
515
516char Mips16HardFloat::ID = 0;
517
518}
519
520ModulePass *llvm::createMips16HardFloat(MipsTargetMachine &TM) {
521 return new Mips16HardFloat(TM);
522}
523