Mips16HardFloat.cpp revision ed7fd711a1917a31714d3f9a41210916be450079
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#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" 19#include <algorithm> 20#include <string> 21 22static 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} 52// 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// 86// 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 179static void swapFPIntParams 180 (FPParamVariant PV, Module *M, InlineAsmHelper &IAH, 181 bool LE, bool ToFP) { 182 //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// 242static void assureFPCallStub(Function &F, Module *M, 243 const MipsSubtarget &Subtarget){ 244 // for now we only need them for static relocation 245 if (Subtarget.getRelocationModel() == Reloc::PIC_) 246 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; 251 std::string StubName = "__call_stub_fp_" + Name; 252 // 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); 261 FStub->addFnAttr(llvm::Attribute::NoInline); 262 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); 267 IAH.Out(".set reorder"); 268 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// 325// Functions that are llvm intrinsics and don't need helpers. 326// 327static const char *IntrinsicInline[] = 328 {"fabs", 329 "llvm.ceil.f32", "llvm.ceil.f64", 330 "llvm.copysign.f32", "llvm.copysign.f64", 331 "llvm.cos.f32", "llvm.cos.f64", 332 "llvm.exp.f32", "llvm.exp.f64", 333 "llvm.exp2.f32", "llvm.exp2.f64", 334 "llvm.fabs.f32", "llvm.fabs.f64", 335 "llvm.floor.f32", "llvm.floor.f64", 336 "llvm.fma.f32", "llvm.fma.f64", 337 "llvm.log.f32", "llvm.log.f64", 338 "llvm.log10.f32", "llvm.log10.f64", 339 "llvm.nearbyint.f32", "llvm.nearbyint.f64", 340 "llvm.pow.f32", "llvm.pow.f64", 341 "llvm.powi.f32", "llvm.powi.f64", 342 "llvm.rint.f32", "llvm.rint.f64", 343 "llvm.round.f32", "llvm.round.f64", 344 "llvm.sin.f32", "llvm.sin.f64", 345 "llvm.sqrt.f32", "llvm.sqrt.f64", 346 "llvm.trunc.f32", "llvm.trunc.f64", 347 }; 348 349static bool isIntrinsicInline(Function *F) { 350 return std::binary_search( 351 IntrinsicInline, array_endof(IntrinsicInline), 352 F->getName()); 353} 354// 355// Returns of float, double and complex need to be handled with a helper 356// function. 357// 358static bool fixupFPReturnAndCall 359 (Function &F, Module *M, const MipsSubtarget &Subtarget) { 360 bool Modified = false; 361 LLVMContext &C = M->getContext(); 362 Type *MyVoid = Type::getVoidTy(C); 363 for (Function::iterator BB = F.begin(), E = F.end(); BB != E; ++BB) 364 for (BasicBlock::iterator I = BB->begin(), E = BB->end(); 365 I != E; ++I) { 366 Instruction &Inst = *I; 367 if (const ReturnInst *RI = dyn_cast<ReturnInst>(I)) { 368 Value *RVal = RI->getReturnValue(); 369 if (!RVal) continue; 370 // 371 // If there is a return value and it needs a helper function, 372 // figure out which one and add a call before the actual 373 // return to this helper. The purpose of the helper is to move 374 // floating point values from their soft float return mapping to 375 // where they would have been mapped to in floating point registers. 376 // 377 Type *T = RVal->getType(); 378 FPReturnVariant RV = whichFPReturnVariant(T); 379 if (RV == NoFPRet) continue; 380 static const char* Helper[NoFPRet] = 381 {"__mips16_ret_sf", "__mips16_ret_df", "__mips16_ret_sc", 382 "__mips16_ret_dc"}; 383 const char *Name = Helper[RV]; 384 AttributeSet A; 385 Value *Params[] = {RVal}; 386 Modified = true; 387 // 388 // These helper functions have a different calling ABI so 389 // this __Mips16RetHelper indicates that so that later 390 // during call setup, the proper call lowering to the helper 391 // functions will take place. 392 // 393 A = A.addAttribute(C, AttributeSet::FunctionIndex, 394 "__Mips16RetHelper"); 395 A = A.addAttribute(C, AttributeSet::FunctionIndex, 396 Attribute::ReadNone); 397 A = A.addAttribute(C, AttributeSet::FunctionIndex, 398 Attribute::NoInline); 399 Value *F = (M->getOrInsertFunction(Name, A, MyVoid, T, NULL)); 400 CallInst::Create(F, Params, "", &Inst ); 401 } else if (const CallInst *CI = dyn_cast<CallInst>(I)) { 402 // pic mode calls are handled by already defined 403 // helper functions 404 if (Subtarget.getRelocationModel() != Reloc::PIC_ ) { 405 Function *F_ = CI->getCalledFunction(); 406 if (F_ && !isIntrinsicInline(F_) && needsFPHelperFromSig(*F_)) { 407 assureFPCallStub(*F_, M, Subtarget); 408 Modified=true; 409 } 410 } 411 } 412 } 413 return Modified; 414} 415 416static void createFPFnStub(Function *F, Module *M, FPParamVariant PV, 417 const MipsSubtarget &Subtarget ) { 418 bool PicMode = Subtarget.getRelocationModel() == Reloc::PIC_; 419 bool LE = Subtarget.isLittle(); 420 LLVMContext &Context = M->getContext(); 421 std::string Name = F->getName(); 422 std::string SectionName = ".mips16.fn." + Name; 423 std::string StubName = "__fn_stub_" + Name; 424 std::string LocalName = "__fn_local_" + Name; 425 Function *FStub = Function::Create 426 (F->getFunctionType(), 427 Function::InternalLinkage, StubName, M); 428 FStub->addFnAttr("mips16_fp_stub"); 429 FStub->addFnAttr(llvm::Attribute::Naked); 430 FStub->addFnAttr(llvm::Attribute::NoUnwind); 431 FStub->addFnAttr(llvm::Attribute::NoInline); 432 FStub->addFnAttr("nomips16"); 433 FStub->setSection(SectionName); 434 BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub); 435 InlineAsmHelper IAH(Context, BB); 436 IAH.Out(" .set macro"); 437 if (PicMode) { 438 IAH.Out(".set noreorder"); 439 IAH.Out(".cpload $$2"); 440 IAH.Out(".set reorder"); 441 IAH.Out(".reloc 0,R_MIPS_NONE," + Name); 442 IAH.Out("la $$25," + LocalName); 443 } 444 else { 445 IAH.Out(".set reorder"); 446 IAH.Out("la $$25, " + Name); 447 } 448 swapFPIntParams(PV, M, IAH, LE, false); 449 IAH.Out("jr $$25"); 450 IAH.Out(LocalName + " = " + Name); 451 new UnreachableInst(FStub->getContext(), BB); 452} 453 454// 455// remove the use-soft-float attribute 456// 457static void removeUseSoftFloat(Function &F) { 458 AttributeSet A; 459 DEBUG(errs() << "removing -use-soft-float\n"); 460 A = A.addAttribute(F.getContext(), AttributeSet::FunctionIndex, 461 "use-soft-float", "false"); 462 F.removeAttributes(AttributeSet::FunctionIndex, A); 463 if (F.hasFnAttribute("use-soft-float")) { 464 DEBUG(errs() << "still has -use-soft-float\n"); 465 } 466 F.addAttributes(AttributeSet::FunctionIndex, A); 467} 468 469namespace llvm { 470 471// 472// This pass only makes sense when the underlying chip has floating point but 473// we are compiling as mips16. 474// For all mips16 functions (that are not stubs we have already generated), or 475// declared via attributes as nomips16, we must: 476// 1) fixup all returns of float, double, single and double complex 477// by calling a helper function before the actual return. 478// 2) generate helper functions (stubs) that can be called by mips32 functions 479// that will move parameters passed normally passed in floating point 480// registers the soft float equivalents. 481// 3) in the case of static relocation, generate helper functions so that 482// mips16 functions can call extern functions of unknown type (mips16 or 483// mips32). 484// 4) TBD. For pic, calls to extern functions of unknown type are handled by 485// predefined helper functions in libc but this work is currently done 486// during call lowering but it should be moved here in the future. 487// 488bool Mips16HardFloat::runOnModule(Module &M) { 489 DEBUG(errs() << "Run on Module Mips16HardFloat\n"); 490 bool Modified = false; 491 for (Module::iterator F = M.begin(), E = M.end(); F != E; ++F) { 492 if (F->hasFnAttribute("nomips16") && 493 F->hasFnAttribute("use-soft-float")) { 494 removeUseSoftFloat(*F); 495 continue; 496 } 497 if (F->isDeclaration() || F->hasFnAttribute("mips16_fp_stub") || 498 F->hasFnAttribute("nomips16")) continue; 499 Modified |= fixupFPReturnAndCall(*F, &M, Subtarget); 500 FPParamVariant V = whichFPParamVariantNeeded(*F); 501 if (V != NoSig) { 502 Modified = true; 503 createFPFnStub(F, &M, V, Subtarget); 504 } 505 } 506 return Modified; 507} 508 509char Mips16HardFloat::ID = 0; 510 511} 512 513ModulePass *llvm::createMips16HardFloat(MipsTargetMachine &TM) { 514 return new Mips16HardFloat(TM); 515} 516 517