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 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: 47 InlineAsmHelper(LLVMContext &C_, BasicBlock *BB_) : 48 C(C_), BB(BB_) { 49 } 50 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 // 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 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 // 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 180 static bool needsFPReturnHelper(Function &F) { 181 Type* RetType = F.getReturnType(); 182 return whichFPReturnVariant(RetType) != NoFPRet; 183 } 184 185 static bool needsFPReturnHelper(FunctionType &FT) { 186 Type* RetType = FT.getReturnType(); 187 return whichFPReturnVariant(RetType) != NoFPRet; 188 } 189 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 // 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 // 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 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 // 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 Type* T = nullptr; 423 if (V) T = V->getType(); 424 PointerType *PFT = nullptr; 425 if (T) PFT = dyn_cast<PointerType>(T); 426 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 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 // 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