LLVM API Documentation
00001 //===---- Mips16HardFloat.cpp for Mips16 Hard Float --------===// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // 00010 // This file defines a pass needed for Mips16 Hard Float 00011 // 00012 //===----------------------------------------------------------------------===// 00013 00014 #include "Mips16HardFloat.h" 00015 #include "llvm/IR/Module.h" 00016 #include "llvm/IR/Value.h" 00017 #include "llvm/Support/Debug.h" 00018 #include "llvm/Support/raw_ostream.h" 00019 #include <algorithm> 00020 #include <string> 00021 00022 #define DEBUG_TYPE "mips16-hard-float" 00023 00024 static void inlineAsmOut 00025 (LLVMContext &C, StringRef AsmString, BasicBlock *BB ) { 00026 std::vector<llvm::Type *> AsmArgTypes; 00027 std::vector<llvm::Value*> AsmArgs; 00028 llvm::FunctionType *AsmFTy = 00029 llvm::FunctionType::get(Type::getVoidTy(C), 00030 AsmArgTypes, false); 00031 llvm::InlineAsm *IA = 00032 llvm::InlineAsm::get(AsmFTy, AsmString, "", true, 00033 /* IsAlignStack */ false, 00034 llvm::InlineAsm::AD_ATT); 00035 CallInst::Create(IA, AsmArgs, "", BB); 00036 } 00037 00038 namespace { 00039 00040 class InlineAsmHelper { 00041 LLVMContext &C; 00042 BasicBlock *BB; 00043 public: 00044 InlineAsmHelper(LLVMContext &C_, BasicBlock *BB_) : 00045 C(C_), BB(BB_) { 00046 } 00047 00048 void Out(StringRef AsmString) { 00049 inlineAsmOut(C, AsmString, BB); 00050 } 00051 00052 }; 00053 } 00054 // 00055 // Return types that matter for hard float are: 00056 // float, double, complex float, and complex double 00057 // 00058 enum FPReturnVariant { 00059 FRet, DRet, CFRet, CDRet, NoFPRet 00060 }; 00061 00062 // 00063 // Determine which FP return type this function has 00064 // 00065 static FPReturnVariant whichFPReturnVariant(Type *T) { 00066 switch (T->getTypeID()) { 00067 case Type::FloatTyID: 00068 return FRet; 00069 case Type::DoubleTyID: 00070 return DRet; 00071 case Type::StructTyID: 00072 if (T->getStructNumElements() != 2) 00073 break; 00074 if ((T->getContainedType(0)->isFloatTy()) && 00075 (T->getContainedType(1)->isFloatTy())) 00076 return CFRet; 00077 if ((T->getContainedType(0)->isDoubleTy()) && 00078 (T->getContainedType(1)->isDoubleTy())) 00079 return CDRet; 00080 break; 00081 default: 00082 break; 00083 } 00084 return NoFPRet; 00085 } 00086 00087 // 00088 // Parameter type that matter are float, (float, float), (float, double), 00089 // double, (double, double), (double, float) 00090 // 00091 enum FPParamVariant { 00092 FSig, FFSig, FDSig, 00093 DSig, DDSig, DFSig, NoSig 00094 }; 00095 00096 // which floating point parameter signature variant we are dealing with 00097 // 00098 typedef Type::TypeID TypeID; 00099 const Type::TypeID FloatTyID = Type::FloatTyID; 00100 const Type::TypeID DoubleTyID = Type::DoubleTyID; 00101 00102 static FPParamVariant whichFPParamVariantNeeded(Function &F) { 00103 switch (F.arg_size()) { 00104 case 0: 00105 return NoSig; 00106 case 1:{ 00107 TypeID ArgTypeID = F.getFunctionType()->getParamType(0)->getTypeID(); 00108 switch (ArgTypeID) { 00109 case FloatTyID: 00110 return FSig; 00111 case DoubleTyID: 00112 return DSig; 00113 default: 00114 return NoSig; 00115 } 00116 } 00117 default: { 00118 TypeID ArgTypeID0 = F.getFunctionType()->getParamType(0)->getTypeID(); 00119 TypeID ArgTypeID1 = F.getFunctionType()->getParamType(1)->getTypeID(); 00120 switch(ArgTypeID0) { 00121 case FloatTyID: { 00122 switch (ArgTypeID1) { 00123 case FloatTyID: 00124 return FFSig; 00125 case DoubleTyID: 00126 return FDSig; 00127 default: 00128 return FSig; 00129 } 00130 } 00131 case DoubleTyID: { 00132 switch (ArgTypeID1) { 00133 case FloatTyID: 00134 return DFSig; 00135 case DoubleTyID: 00136 return DDSig; 00137 default: 00138 return DSig; 00139 } 00140 } 00141 default: 00142 return NoSig; 00143 } 00144 } 00145 } 00146 llvm_unreachable("can't get here"); 00147 } 00148 00149 // Figure out if we need float point based on the function parameters. 00150 // We need to move variables in and/or out of floating point 00151 // registers because of the ABI 00152 // 00153 static bool needsFPStubFromParams(Function &F) { 00154 if (F.arg_size() >=1) { 00155 Type *ArgType = F.getFunctionType()->getParamType(0); 00156 switch (ArgType->getTypeID()) { 00157 case Type::FloatTyID: 00158 case Type::DoubleTyID: 00159 return true; 00160 default: 00161 break; 00162 } 00163 } 00164 return false; 00165 } 00166 00167 static bool needsFPReturnHelper(Function &F) { 00168 Type* RetType = F.getReturnType(); 00169 return whichFPReturnVariant(RetType) != NoFPRet; 00170 } 00171 00172 static bool needsFPReturnHelper(const FunctionType &FT) { 00173 Type* RetType = FT.getReturnType(); 00174 return whichFPReturnVariant(RetType) != NoFPRet; 00175 } 00176 00177 static bool needsFPHelperFromSig(Function &F) { 00178 return needsFPStubFromParams(F) || needsFPReturnHelper(F); 00179 } 00180 00181 // 00182 // We swap between FP and Integer registers to allow Mips16 and Mips32 to 00183 // interoperate 00184 // 00185 00186 static void swapFPIntParams 00187 (FPParamVariant PV, Module *M, InlineAsmHelper &IAH, 00188 bool LE, bool ToFP) { 00189 //LLVMContext &Context = M->getContext(); 00190 std::string MI = ToFP? "mtc1 ": "mfc1 "; 00191 switch (PV) { 00192 case FSig: 00193 IAH.Out(MI + "$$4,$$f12"); 00194 break; 00195 case FFSig: 00196 IAH.Out(MI +"$$4,$$f12"); 00197 IAH.Out(MI + "$$5,$$f14"); 00198 break; 00199 case FDSig: 00200 IAH.Out(MI + "$$4,$$f12"); 00201 if (LE) { 00202 IAH.Out(MI + "$$6,$$f14"); 00203 IAH.Out(MI + "$$7,$$f15"); 00204 } else { 00205 IAH.Out(MI + "$$7,$$f14"); 00206 IAH.Out(MI + "$$6,$$f15"); 00207 } 00208 break; 00209 case DSig: 00210 if (LE) { 00211 IAH.Out(MI + "$$4,$$f12"); 00212 IAH.Out(MI + "$$5,$$f13"); 00213 } else { 00214 IAH.Out(MI + "$$5,$$f12"); 00215 IAH.Out(MI + "$$4,$$f13"); 00216 } 00217 break; 00218 case DDSig: 00219 if (LE) { 00220 IAH.Out(MI + "$$4,$$f12"); 00221 IAH.Out(MI + "$$5,$$f13"); 00222 IAH.Out(MI + "$$6,$$f14"); 00223 IAH.Out(MI + "$$7,$$f15"); 00224 } else { 00225 IAH.Out(MI + "$$5,$$f12"); 00226 IAH.Out(MI + "$$4,$$f13"); 00227 IAH.Out(MI + "$$7,$$f14"); 00228 IAH.Out(MI + "$$6,$$f15"); 00229 } 00230 break; 00231 case DFSig: 00232 if (LE) { 00233 IAH.Out(MI + "$$4,$$f12"); 00234 IAH.Out(MI + "$$5,$$f13"); 00235 } else { 00236 IAH.Out(MI + "$$5,$$f12"); 00237 IAH.Out(MI + "$$4,$$f13"); 00238 } 00239 IAH.Out(MI + "$$6,$$f14"); 00240 break; 00241 case NoSig: 00242 return; 00243 } 00244 } 00245 // 00246 // Make sure that we know we already need a stub for this function. 00247 // Having called needsFPHelperFromSig 00248 // 00249 static void assureFPCallStub(Function &F, Module *M, 00250 const MipsSubtarget &Subtarget) { 00251 // for now we only need them for static relocation 00252 if (Subtarget.getRelocationModel() == Reloc::PIC_) 00253 return; 00254 LLVMContext &Context = M->getContext(); 00255 bool LE = Subtarget.isLittle(); 00256 std::string Name = F.getName(); 00257 std::string SectionName = ".mips16.call.fp." + Name; 00258 std::string StubName = "__call_stub_fp_" + Name; 00259 // 00260 // see if we already have the stub 00261 // 00262 Function *FStub = M->getFunction(StubName); 00263 if (FStub && !FStub->isDeclaration()) return; 00264 FStub = Function::Create(F.getFunctionType(), 00265 Function::InternalLinkage, StubName, M); 00266 FStub->addFnAttr("mips16_fp_stub"); 00267 FStub->addFnAttr(llvm::Attribute::Naked); 00268 FStub->addFnAttr(llvm::Attribute::NoInline); 00269 FStub->addFnAttr(llvm::Attribute::NoUnwind); 00270 FStub->addFnAttr("nomips16"); 00271 FStub->setSection(SectionName); 00272 BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub); 00273 InlineAsmHelper IAH(Context, BB); 00274 IAH.Out(".set reorder"); 00275 FPReturnVariant RV = whichFPReturnVariant(FStub->getReturnType()); 00276 FPParamVariant PV = whichFPParamVariantNeeded(F); 00277 swapFPIntParams(PV, M, IAH, LE, true); 00278 if (RV != NoFPRet) { 00279 IAH.Out("move $$18, $$31"); 00280 IAH.Out("jal " + Name); 00281 } else { 00282 IAH.Out("lui $$25,%hi(" + Name + ")"); 00283 IAH.Out("addiu $$25,$$25,%lo(" + Name + ")" ); 00284 } 00285 switch (RV) { 00286 case FRet: 00287 IAH.Out("mfc1 $$2,$$f0"); 00288 break; 00289 case DRet: 00290 if (LE) { 00291 IAH.Out("mfc1 $$2,$$f0"); 00292 IAH.Out("mfc1 $$3,$$f1"); 00293 } else { 00294 IAH.Out("mfc1 $$3,$$f0"); 00295 IAH.Out("mfc1 $$2,$$f1"); 00296 } 00297 break; 00298 case CFRet: 00299 if (LE) { 00300 IAH.Out("mfc1 $$2,$$f0"); 00301 IAH.Out("mfc1 $$3,$$f2"); 00302 } else { 00303 IAH.Out("mfc1 $$3,$$f0"); 00304 IAH.Out("mfc1 $$3,$$f2"); 00305 } 00306 break; 00307 case CDRet: 00308 if (LE) { 00309 IAH.Out("mfc1 $$4,$$f2"); 00310 IAH.Out("mfc1 $$5,$$f3"); 00311 IAH.Out("mfc1 $$2,$$f0"); 00312 IAH.Out("mfc1 $$3,$$f1"); 00313 00314 } else { 00315 IAH.Out("mfc1 $$5,$$f2"); 00316 IAH.Out("mfc1 $$4,$$f3"); 00317 IAH.Out("mfc1 $$3,$$f0"); 00318 IAH.Out("mfc1 $$2,$$f1"); 00319 } 00320 break; 00321 case NoFPRet: 00322 break; 00323 } 00324 if (RV != NoFPRet) 00325 IAH.Out("jr $$18"); 00326 else 00327 IAH.Out("jr $$25"); 00328 new UnreachableInst(Context, BB); 00329 } 00330 00331 // 00332 // Functions that are llvm intrinsics and don't need helpers. 00333 // 00334 static const char *IntrinsicInline[] = 00335 {"fabs", 00336 "fabsf", 00337 "llvm.ceil.f32", "llvm.ceil.f64", 00338 "llvm.copysign.f32", "llvm.copysign.f64", 00339 "llvm.cos.f32", "llvm.cos.f64", 00340 "llvm.exp.f32", "llvm.exp.f64", 00341 "llvm.exp2.f32", "llvm.exp2.f64", 00342 "llvm.fabs.f32", "llvm.fabs.f64", 00343 "llvm.floor.f32", "llvm.floor.f64", 00344 "llvm.fma.f32", "llvm.fma.f64", 00345 "llvm.log.f32", "llvm.log.f64", 00346 "llvm.log10.f32", "llvm.log10.f64", 00347 "llvm.nearbyint.f32", "llvm.nearbyint.f64", 00348 "llvm.pow.f32", "llvm.pow.f64", 00349 "llvm.powi.f32", "llvm.powi.f64", 00350 "llvm.rint.f32", "llvm.rint.f64", 00351 "llvm.round.f32", "llvm.round.f64", 00352 "llvm.sin.f32", "llvm.sin.f64", 00353 "llvm.sqrt.f32", "llvm.sqrt.f64", 00354 "llvm.trunc.f32", "llvm.trunc.f64", 00355 }; 00356 00357 static bool isIntrinsicInline(Function *F) { 00358 return std::binary_search(std::begin(IntrinsicInline), 00359 std::end(IntrinsicInline), F->getName()); 00360 } 00361 // 00362 // Returns of float, double and complex need to be handled with a helper 00363 // function. 00364 // 00365 static bool fixupFPReturnAndCall 00366 (Function &F, Module *M, const MipsSubtarget &Subtarget) { 00367 bool Modified = false; 00368 LLVMContext &C = M->getContext(); 00369 Type *MyVoid = Type::getVoidTy(C); 00370 for (Function::iterator BB = F.begin(), E = F.end(); BB != E; ++BB) 00371 for (BasicBlock::iterator I = BB->begin(), E = BB->end(); 00372 I != E; ++I) { 00373 Instruction &Inst = *I; 00374 if (const ReturnInst *RI = dyn_cast<ReturnInst>(I)) { 00375 Value *RVal = RI->getReturnValue(); 00376 if (!RVal) continue; 00377 // 00378 // If there is a return value and it needs a helper function, 00379 // figure out which one and add a call before the actual 00380 // return to this helper. The purpose of the helper is to move 00381 // floating point values from their soft float return mapping to 00382 // where they would have been mapped to in floating point registers. 00383 // 00384 Type *T = RVal->getType(); 00385 FPReturnVariant RV = whichFPReturnVariant(T); 00386 if (RV == NoFPRet) continue; 00387 static const char* Helper[NoFPRet] = 00388 {"__mips16_ret_sf", "__mips16_ret_df", "__mips16_ret_sc", 00389 "__mips16_ret_dc"}; 00390 const char *Name = Helper[RV]; 00391 AttributeSet A; 00392 Value *Params[] = {RVal}; 00393 Modified = true; 00394 // 00395 // These helper functions have a different calling ABI so 00396 // this __Mips16RetHelper indicates that so that later 00397 // during call setup, the proper call lowering to the helper 00398 // functions will take place. 00399 // 00400 A = A.addAttribute(C, AttributeSet::FunctionIndex, 00401 "__Mips16RetHelper"); 00402 A = A.addAttribute(C, AttributeSet::FunctionIndex, 00403 Attribute::ReadNone); 00404 A = A.addAttribute(C, AttributeSet::FunctionIndex, 00405 Attribute::NoInline); 00406 Value *F = (M->getOrInsertFunction(Name, A, MyVoid, T, NULL)); 00407 CallInst::Create(F, Params, "", &Inst ); 00408 } else if (const CallInst *CI = dyn_cast<CallInst>(I)) { 00409 const Value* V = CI->getCalledValue(); 00410 const Type* T = nullptr; 00411 if (V) T = V->getType(); 00412 const PointerType *PFT=nullptr; 00413 if (T) PFT = dyn_cast<PointerType>(T); 00414 const FunctionType *FT=nullptr; 00415 if (PFT) FT = dyn_cast<FunctionType>(PFT->getElementType()); 00416 Function *F_ = CI->getCalledFunction(); 00417 if (FT && needsFPReturnHelper(*FT) && 00418 !(F_ && isIntrinsicInline(F_))) { 00419 Modified=true; 00420 F.addFnAttr("saveS2"); 00421 } 00422 if (F_ && !isIntrinsicInline(F_)) { 00423 // pic mode calls are handled by already defined 00424 // helper functions 00425 if (needsFPReturnHelper(*F_)) { 00426 Modified=true; 00427 F.addFnAttr("saveS2"); 00428 } 00429 if (Subtarget.getRelocationModel() != Reloc::PIC_ ) { 00430 if (needsFPHelperFromSig(*F_)) { 00431 assureFPCallStub(*F_, M, Subtarget); 00432 Modified=true; 00433 } 00434 } 00435 } 00436 } 00437 } 00438 return Modified; 00439 } 00440 00441 static void createFPFnStub(Function *F, Module *M, FPParamVariant PV, 00442 const MipsSubtarget &Subtarget ) { 00443 bool PicMode = Subtarget.getRelocationModel() == Reloc::PIC_; 00444 bool LE = Subtarget.isLittle(); 00445 LLVMContext &Context = M->getContext(); 00446 std::string Name = F->getName(); 00447 std::string SectionName = ".mips16.fn." + Name; 00448 std::string StubName = "__fn_stub_" + Name; 00449 std::string LocalName = "$$__fn_local_" + Name; 00450 Function *FStub = Function::Create 00451 (F->getFunctionType(), 00452 Function::InternalLinkage, StubName, M); 00453 FStub->addFnAttr("mips16_fp_stub"); 00454 FStub->addFnAttr(llvm::Attribute::Naked); 00455 FStub->addFnAttr(llvm::Attribute::NoUnwind); 00456 FStub->addFnAttr(llvm::Attribute::NoInline); 00457 FStub->addFnAttr("nomips16"); 00458 FStub->setSection(SectionName); 00459 BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub); 00460 InlineAsmHelper IAH(Context, BB); 00461 IAH.Out(" .set macro"); 00462 if (PicMode) { 00463 IAH.Out(".set noreorder"); 00464 IAH.Out(".cpload $$25"); 00465 IAH.Out(".set reorder"); 00466 IAH.Out(".reloc 0,R_MIPS_NONE," + Name); 00467 IAH.Out("la $$25," + LocalName); 00468 } 00469 else { 00470 IAH.Out(".set reorder"); 00471 IAH.Out("la $$25," + Name); 00472 } 00473 swapFPIntParams(PV, M, IAH, LE, false); 00474 IAH.Out("jr $$25"); 00475 IAH.Out(LocalName + " = " + Name); 00476 new UnreachableInst(FStub->getContext(), BB); 00477 } 00478 00479 // 00480 // remove the use-soft-float attribute 00481 // 00482 static void removeUseSoftFloat(Function &F) { 00483 AttributeSet A; 00484 DEBUG(errs() << "removing -use-soft-float\n"); 00485 A = A.addAttribute(F.getContext(), AttributeSet::FunctionIndex, 00486 "use-soft-float", "false"); 00487 F.removeAttributes(AttributeSet::FunctionIndex, A); 00488 if (F.hasFnAttribute("use-soft-float")) { 00489 DEBUG(errs() << "still has -use-soft-float\n"); 00490 } 00491 F.addAttributes(AttributeSet::FunctionIndex, A); 00492 } 00493 00494 namespace llvm { 00495 00496 // 00497 // This pass only makes sense when the underlying chip has floating point but 00498 // we are compiling as mips16. 00499 // For all mips16 functions (that are not stubs we have already generated), or 00500 // declared via attributes as nomips16, we must: 00501 // 1) fixup all returns of float, double, single and double complex 00502 // by calling a helper function before the actual return. 00503 // 2) generate helper functions (stubs) that can be called by mips32 00504 // functions that will move parameters passed normally passed in 00505 // floating point 00506 // registers the soft float equivalents. 00507 // 3) in the case of static relocation, generate helper functions so that 00508 // mips16 functions can call extern functions of unknown type (mips16 or 00509 // mips32). 00510 // 4) TBD. For pic, calls to extern functions of unknown type are handled by 00511 // predefined helper functions in libc but this work is currently done 00512 // during call lowering but it should be moved here in the future. 00513 // 00514 bool Mips16HardFloat::runOnModule(Module &M) { 00515 DEBUG(errs() << "Run on Module Mips16HardFloat\n"); 00516 bool Modified = false; 00517 for (Module::iterator F = M.begin(), E = M.end(); F != E; ++F) { 00518 if (F->hasFnAttribute("nomips16") && 00519 F->hasFnAttribute("use-soft-float")) { 00520 removeUseSoftFloat(*F); 00521 continue; 00522 } 00523 if (F->isDeclaration() || F->hasFnAttribute("mips16_fp_stub") || 00524 F->hasFnAttribute("nomips16")) continue; 00525 Modified |= fixupFPReturnAndCall(*F, &M, Subtarget); 00526 FPParamVariant V = whichFPParamVariantNeeded(*F); 00527 if (V != NoSig) { 00528 Modified = true; 00529 createFPFnStub(F, &M, V, Subtarget); 00530 } 00531 } 00532 return Modified; 00533 } 00534 00535 char Mips16HardFloat::ID = 0; 00536 00537 } 00538 00539 ModulePass *llvm::createMips16HardFloat(MipsTargetMachine &TM) { 00540 return new Mips16HardFloat(TM); 00541 } 00542