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