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 33 Mips16HardFloat() : ModulePass(ID) {} 34 35 StringRef getPassName() const override { return "MIPS16 Hard Float Pass"; } 36 37 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 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 // 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 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 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 169 static bool needsFPReturnHelper(Function &F) { 170 Type* RetType = F.getReturnType(); 171 return whichFPReturnVariant(RetType) != NoFPRet; 172 } 173 174 static bool needsFPReturnHelper(FunctionType &FT) { 175 Type* RetType = FT.getReturnType(); 176 return whichFPReturnVariant(RetType) != NoFPRet; 177 } 178 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 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 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 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. 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 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 483 static void removeUseSoftFloat(Function &F) { 484 AttrBuilder B; 485 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 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. 510 bool Mips16HardFloat::runOnModule(Module &M) { 511 auto &TM = static_cast<const MipsTargetMachine &>( 512 getAnalysis<TargetPassConfig>().getTM<TargetMachine>()); 513 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 533 ModulePass *llvm::createMips16HardFloatPass() { 534 return new Mips16HardFloat(); 535 } 536