[PowerPC] Do not emit record-form rotates when record-form andi/andis suffices
[llvm-core.git] / lib / Target / Mips / Mips16HardFloat.cpp
blobc310d9491af818aa3813cab86fd8f95532e6da98
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
12 //===----------------------------------------------------------------------===//
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>
23 using namespace llvm;
25 #define DEBUG_TYPE "mips16-hard-float"
27 namespace {
29 class Mips16HardFloat : public ModulePass {
30 public:
31 static char ID;
33 Mips16HardFloat() : ModulePass(ID) {}
35 StringRef getPassName() const override { return "MIPS16 Hard Float Pass"; }
37 void getAnalysisUsage(AnalysisUsage &AU) const override {
38 AU.addRequired<TargetPassConfig>();
39 ModulePass::getAnalysisUsage(AU);
42 bool runOnModule(Module &M) override;
45 } // end anonymous namespace
47 static void EmitInlineAsm(LLVMContext &C, BasicBlock *BB, StringRef AsmText) {
48 std::vector<Type *> AsmArgTypes;
49 std::vector<Value *> AsmArgs;
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);
58 char Mips16HardFloat::ID = 0;
61 // Return types that matter for hard float are:
62 // float, double, complex float, and complex double
64 enum FPReturnVariant {
65 FRet, DRet, CFRet, CDRet, NoFPRet
69 // Determine which FP return type this function has
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;
90 return NoFPRet;
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
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;
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;
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;
134 case DoubleTyID: {
135 switch (ArgTypeID1) {
136 case FloatTyID:
137 return DFSig;
138 case DoubleTyID:
139 return DDSig;
140 default:
141 return DSig;
144 default:
145 return NoSig;
149 llvm_unreachable("can't get here");
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
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;
166 return false;
169 static bool needsFPReturnHelper(Function &F) {
170 Type* RetType = F.getReturnType();
171 return whichFPReturnVariant(RetType) != NoFPRet;
174 static bool needsFPReturnHelper(FunctionType &FT) {
175 Type* RetType = FT.getReturnType();
176 return whichFPReturnVariant(RetType) != NoFPRet;
179 static bool needsFPHelperFromSig(Function &F) {
180 return needsFPStubFromParams(F) || needsFPReturnHelper(F);
183 // We swap between FP and Integer registers to allow Mips16 and Mips32 to
184 // interoperate
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;
190 switch (PV) {
191 case FSig:
192 AsmText += MI + "$$4, $$f12\n";
193 break;
195 case FFSig:
196 AsmText += MI + "$$4, $$f12\n";
197 AsmText += MI + "$$5, $$f14\n";
198 break;
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";
209 break;
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";
219 break;
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";
233 break;
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";
243 AsmText += MI + "$$6, $$f14\n";
244 break;
246 case NoSig:
247 break;
250 return AsmText;
253 // Make sure that we know we already need a stub for this function.
254 // Having called needsFPHelperFromSig
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;
266 // see if we already have the stub
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);
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";
293 switch (RV) {
294 case FRet:
295 AsmText += "mfc1 $$2, $$f0\n";
296 break;
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";
306 break;
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";
316 break;
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";
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";
331 break;
333 case NoFPRet:
334 break;
337 if (RV != NoFPRet)
338 AsmText += "jr $$18\n";
339 else
340 AsmText += "jr $$25\n";
341 EmitInlineAsm(Context, BB, AsmText);
343 new UnreachableInst(Context, BB);
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",
369 static bool isIntrinsicInline(Function *F) {
370 return std::binary_search(std::begin(IntrinsicInline),
371 std::end(IntrinsicInline), F->getName());
374 // Returns of float, double and complex need to be handled with a helper
375 // function.
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;
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.
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"
400 const char *Name = Helper[RV];
401 AttributeList A;
402 Value *Params[] = {RVal};
403 Modified = true;
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.
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");
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");
433 if (!TM.isPositionIndependent()) {
434 if (needsFPHelperFromSig(*F_)) {
435 assureFPCallStub(*F_, M, TM);
436 Modified=true;
442 return Modified;
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);
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);
479 new UnreachableInst(FStub->getContext(), BB);
482 // remove the use-soft-float attribute
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");
491 F.addAttributes(AttributeList::FunctionIndex, B);
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.
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;
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);
530 return Modified;
533 ModulePass *llvm::createMips16HardFloatPass() {
534 return new Mips16HardFloat();