1 //===---- Mips16HardFloat.cpp for Mips16 Hard Float --------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file defines a pass needed for Mips16 Hard Float
12 //===----------------------------------------------------------------------===//
14 #include "Mips16HardFloat.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"
22 #define DEBUG_TYPE "mips16-hard-float"
24 static void inlineAsmOut
25 (LLVMContext &C, StringRef AsmString, BasicBlock *BB ) {
26 std::vector<llvm::Type *> AsmArgTypes;
27 std::vector<llvm::Value*> AsmArgs;
28 llvm::FunctionType *AsmFTy =
29 llvm::FunctionType::get(Type::getVoidTy(C),
32 llvm::InlineAsm::get(AsmFTy, AsmString, "", true,
33 /* IsAlignStack */ false,
34 llvm::InlineAsm::AD_ATT);
35 CallInst::Create(IA, AsmArgs, "", BB);
40 class InlineAsmHelper {
44 InlineAsmHelper(LLVMContext &C_, BasicBlock *BB_) :
48 void Out(StringRef AsmString) {
49 inlineAsmOut(C, AsmString, BB);
55 // Return types that matter for hard float are:
56 // float, double, complex float, and complex double
58 enum FPReturnVariant {
59 FRet, DRet, CFRet, CDRet, NoFPRet
63 // Determine which FP return type this function has
65 static FPReturnVariant whichFPReturnVariant(Type *T) {
66 switch (T->getTypeID()) {
69 case Type::DoubleTyID:
71 case Type::StructTyID:
72 if (T->getStructNumElements() != 2)
74 if ((T->getContainedType(0)->isFloatTy()) &&
75 (T->getContainedType(1)->isFloatTy()))
77 if ((T->getContainedType(0)->isDoubleTy()) &&
78 (T->getContainedType(1)->isDoubleTy()))
88 // Parameter type that matter are float, (float, float), (float, double),
89 // double, (double, double), (double, float)
93 DSig, DDSig, DFSig, NoSig
96 // which floating point parameter signature variant we are dealing with
98 typedef Type::TypeID TypeID;
99 const Type::TypeID FloatTyID = Type::FloatTyID;
100 const Type::TypeID DoubleTyID = Type::DoubleTyID;
102 static FPParamVariant whichFPParamVariantNeeded(Function &F) {
103 switch (F.arg_size()) {
107 TypeID ArgTypeID = F.getFunctionType()->getParamType(0)->getTypeID();
118 TypeID ArgTypeID0 = F.getFunctionType()->getParamType(0)->getTypeID();
119 TypeID ArgTypeID1 = F.getFunctionType()->getParamType(1)->getTypeID();
122 switch (ArgTypeID1) {
132 switch (ArgTypeID1) {
146 llvm_unreachable("can't get here");
149 // Figure out if we need float point based on the function parameters.
150 // We need to move variables in and/or out of floating point
151 // registers because of the ABI
153 static bool needsFPStubFromParams(Function &F) {
154 if (F.arg_size() >=1) {
155 Type *ArgType = F.getFunctionType()->getParamType(0);
156 switch (ArgType->getTypeID()) {
157 case Type::FloatTyID:
158 case Type::DoubleTyID:
167 static bool needsFPReturnHelper(Function &F) {
168 Type* RetType = F.getReturnType();
169 return whichFPReturnVariant(RetType) != NoFPRet;
172 static bool needsFPReturnHelper(const FunctionType &FT) {
173 Type* RetType = FT.getReturnType();
174 return whichFPReturnVariant(RetType) != NoFPRet;
177 static bool needsFPHelperFromSig(Function &F) {
178 return needsFPStubFromParams(F) || needsFPReturnHelper(F);
182 // We swap between FP and Integer registers to allow Mips16 and Mips32 to
186 static void swapFPIntParams
187 (FPParamVariant PV, Module *M, InlineAsmHelper &IAH,
188 bool LE, bool ToFP) {
189 //LLVMContext &Context = M->getContext();
190 std::string MI = ToFP? "mtc1 ": "mfc1 ";
193 IAH.Out(MI + "$$4,$$f12");
196 IAH.Out(MI +"$$4,$$f12");
197 IAH.Out(MI + "$$5,$$f14");
200 IAH.Out(MI + "$$4,$$f12");
202 IAH.Out(MI + "$$6,$$f14");
203 IAH.Out(MI + "$$7,$$f15");
205 IAH.Out(MI + "$$7,$$f14");
206 IAH.Out(MI + "$$6,$$f15");
211 IAH.Out(MI + "$$4,$$f12");
212 IAH.Out(MI + "$$5,$$f13");
214 IAH.Out(MI + "$$5,$$f12");
215 IAH.Out(MI + "$$4,$$f13");
220 IAH.Out(MI + "$$4,$$f12");
221 IAH.Out(MI + "$$5,$$f13");
222 IAH.Out(MI + "$$6,$$f14");
223 IAH.Out(MI + "$$7,$$f15");
225 IAH.Out(MI + "$$5,$$f12");
226 IAH.Out(MI + "$$4,$$f13");
227 IAH.Out(MI + "$$7,$$f14");
228 IAH.Out(MI + "$$6,$$f15");
233 IAH.Out(MI + "$$4,$$f12");
234 IAH.Out(MI + "$$5,$$f13");
236 IAH.Out(MI + "$$5,$$f12");
237 IAH.Out(MI + "$$4,$$f13");
239 IAH.Out(MI + "$$6,$$f14");
246 // Make sure that we know we already need a stub for this function.
247 // Having called needsFPHelperFromSig
249 static void assureFPCallStub(Function &F, Module *M,
250 const MipsTargetMachine &TM) {
251 // for now we only need them for static relocation
252 if (TM.getRelocationModel() == Reloc::PIC_)
254 LLVMContext &Context = M->getContext();
255 bool LE = TM.isLittleEndian();
256 std::string Name = F.getName();
257 std::string SectionName = ".mips16.call.fp." + Name;
258 std::string StubName = "__call_stub_fp_" + Name;
260 // see if we already have the stub
262 Function *FStub = M->getFunction(StubName);
263 if (FStub && !FStub->isDeclaration()) return;
264 FStub = Function::Create(F.getFunctionType(),
265 Function::InternalLinkage, StubName, M);
266 FStub->addFnAttr("mips16_fp_stub");
267 FStub->addFnAttr(llvm::Attribute::Naked);
268 FStub->addFnAttr(llvm::Attribute::NoInline);
269 FStub->addFnAttr(llvm::Attribute::NoUnwind);
270 FStub->addFnAttr("nomips16");
271 FStub->setSection(SectionName);
272 BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
273 InlineAsmHelper IAH(Context, BB);
274 IAH.Out(".set reorder");
275 FPReturnVariant RV = whichFPReturnVariant(FStub->getReturnType());
276 FPParamVariant PV = whichFPParamVariantNeeded(F);
277 swapFPIntParams(PV, M, IAH, LE, true);
279 IAH.Out("move $$18, $$31");
280 IAH.Out("jal " + Name);
282 IAH.Out("lui $$25,%hi(" + Name + ")");
283 IAH.Out("addiu $$25,$$25,%lo(" + Name + ")" );
287 IAH.Out("mfc1 $$2,$$f0");
291 IAH.Out("mfc1 $$2,$$f0");
292 IAH.Out("mfc1 $$3,$$f1");
294 IAH.Out("mfc1 $$3,$$f0");
295 IAH.Out("mfc1 $$2,$$f1");
300 IAH.Out("mfc1 $$2,$$f0");
301 IAH.Out("mfc1 $$3,$$f2");
303 IAH.Out("mfc1 $$3,$$f0");
304 IAH.Out("mfc1 $$3,$$f2");
309 IAH.Out("mfc1 $$4,$$f2");
310 IAH.Out("mfc1 $$5,$$f3");
311 IAH.Out("mfc1 $$2,$$f0");
312 IAH.Out("mfc1 $$3,$$f1");
315 IAH.Out("mfc1 $$5,$$f2");
316 IAH.Out("mfc1 $$4,$$f3");
317 IAH.Out("mfc1 $$3,$$f0");
318 IAH.Out("mfc1 $$2,$$f1");
328 new UnreachableInst(Context, BB);
332 // Functions that are llvm intrinsics and don't need helpers.
334 static const char *IntrinsicInline[] =
337 "llvm.ceil.f32", "llvm.ceil.f64",
338 "llvm.copysign.f32", "llvm.copysign.f64",
339 "llvm.cos.f32", "llvm.cos.f64",
340 "llvm.exp.f32", "llvm.exp.f64",
341 "llvm.exp2.f32", "llvm.exp2.f64",
342 "llvm.fabs.f32", "llvm.fabs.f64",
343 "llvm.floor.f32", "llvm.floor.f64",
344 "llvm.fma.f32", "llvm.fma.f64",
345 "llvm.log.f32", "llvm.log.f64",
346 "llvm.log10.f32", "llvm.log10.f64",
347 "llvm.nearbyint.f32", "llvm.nearbyint.f64",
348 "llvm.pow.f32", "llvm.pow.f64",
349 "llvm.powi.f32", "llvm.powi.f64",
350 "llvm.rint.f32", "llvm.rint.f64",
351 "llvm.round.f32", "llvm.round.f64",
352 "llvm.sin.f32", "llvm.sin.f64",
353 "llvm.sqrt.f32", "llvm.sqrt.f64",
354 "llvm.trunc.f32", "llvm.trunc.f64",
357 static bool isIntrinsicInline(Function *F) {
358 return std::binary_search(std::begin(IntrinsicInline),
359 std::end(IntrinsicInline), F->getName());
362 // Returns of float, double and complex need to be handled with a helper
365 static bool fixupFPReturnAndCall(Function &F, Module *M,
366 const MipsTargetMachine &TM) {
367 bool Modified = false;
368 LLVMContext &C = M->getContext();
369 Type *MyVoid = Type::getVoidTy(C);
370 for (Function::iterator BB = F.begin(), E = F.end(); BB != E; ++BB)
371 for (BasicBlock::iterator I = BB->begin(), E = BB->end();
373 Instruction &Inst = *I;
374 if (const ReturnInst *RI = dyn_cast<ReturnInst>(I)) {
375 Value *RVal = RI->getReturnValue();
378 // If there is a return value and it needs a helper function,
379 // figure out which one and add a call before the actual
380 // return to this helper. The purpose of the helper is to move
381 // floating point values from their soft float return mapping to
382 // where they would have been mapped to in floating point registers.
384 Type *T = RVal->getType();
385 FPReturnVariant RV = whichFPReturnVariant(T);
386 if (RV == NoFPRet) continue;
387 static const char* Helper[NoFPRet] =
388 {"__mips16_ret_sf", "__mips16_ret_df", "__mips16_ret_sc",
390 const char *Name = Helper[RV];
392 Value *Params[] = {RVal};
395 // These helper functions have a different calling ABI so
396 // this __Mips16RetHelper indicates that so that later
397 // during call setup, the proper call lowering to the helper
398 // functions will take place.
400 A = A.addAttribute(C, AttributeSet::FunctionIndex,
401 "__Mips16RetHelper");
402 A = A.addAttribute(C, AttributeSet::FunctionIndex,
403 Attribute::ReadNone);
404 A = A.addAttribute(C, AttributeSet::FunctionIndex,
405 Attribute::NoInline);
406 Value *F = (M->getOrInsertFunction(Name, A, MyVoid, T, nullptr));
407 CallInst::Create(F, Params, "", &Inst );
408 } else if (const CallInst *CI = dyn_cast<CallInst>(I)) {
409 const Value* V = CI->getCalledValue();
410 const Type* T = nullptr;
411 if (V) T = V->getType();
412 const PointerType *PFT=nullptr;
413 if (T) PFT = dyn_cast<PointerType>(T);
414 const FunctionType *FT=nullptr;
415 if (PFT) FT = dyn_cast<FunctionType>(PFT->getElementType());
416 Function *F_ = CI->getCalledFunction();
417 if (FT && needsFPReturnHelper(*FT) &&
418 !(F_ && isIntrinsicInline(F_))) {
420 F.addFnAttr("saveS2");
422 if (F_ && !isIntrinsicInline(F_)) {
423 // pic mode calls are handled by already defined
425 if (needsFPReturnHelper(*F_)) {
427 F.addFnAttr("saveS2");
429 if (TM.getRelocationModel() != Reloc::PIC_ ) {
430 if (needsFPHelperFromSig(*F_)) {
431 assureFPCallStub(*F_, M, TM);
441 static void createFPFnStub(Function *F, Module *M, FPParamVariant PV,
442 const MipsTargetMachine &TM) {
443 bool PicMode = TM.getRelocationModel() == Reloc::PIC_;
444 bool LE = TM.isLittleEndian();
445 LLVMContext &Context = M->getContext();
446 std::string Name = F->getName();
447 std::string SectionName = ".mips16.fn." + Name;
448 std::string StubName = "__fn_stub_" + Name;
449 std::string LocalName = "$$__fn_local_" + Name;
450 Function *FStub = Function::Create
451 (F->getFunctionType(),
452 Function::InternalLinkage, StubName, M);
453 FStub->addFnAttr("mips16_fp_stub");
454 FStub->addFnAttr(llvm::Attribute::Naked);
455 FStub->addFnAttr(llvm::Attribute::NoUnwind);
456 FStub->addFnAttr(llvm::Attribute::NoInline);
457 FStub->addFnAttr("nomips16");
458 FStub->setSection(SectionName);
459 BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
460 InlineAsmHelper IAH(Context, BB);
462 IAH.Out(".set noreorder");
463 IAH.Out(".cpload $$25");
464 IAH.Out(".set reorder");
465 IAH.Out(".reloc 0,R_MIPS_NONE," + Name);
466 IAH.Out("la $$25," + LocalName);
469 IAH.Out("la $$25," + Name);
471 swapFPIntParams(PV, M, IAH, LE, false);
473 IAH.Out(LocalName + " = " + Name);
474 new UnreachableInst(FStub->getContext(), BB);
478 // remove the use-soft-float attribute
480 static void removeUseSoftFloat(Function &F) {
482 DEBUG(errs() << "removing -use-soft-float\n");
483 A = A.addAttribute(F.getContext(), AttributeSet::FunctionIndex,
484 "use-soft-float", "false");
485 F.removeAttributes(AttributeSet::FunctionIndex, A);
486 if (F.hasFnAttribute("use-soft-float")) {
487 DEBUG(errs() << "still has -use-soft-float\n");
489 F.addAttributes(AttributeSet::FunctionIndex, A);
495 // This pass only makes sense when the underlying chip has floating point but
496 // we are compiling as mips16.
497 // For all mips16 functions (that are not stubs we have already generated), or
498 // declared via attributes as nomips16, we must:
499 // 1) fixup all returns of float, double, single and double complex
500 // by calling a helper function before the actual return.
501 // 2) generate helper functions (stubs) that can be called by mips32
502 // functions that will move parameters passed normally passed in
504 // registers the soft float equivalents.
505 // 3) in the case of static relocation, generate helper functions so that
506 // mips16 functions can call extern functions of unknown type (mips16 or
508 // 4) TBD. For pic, calls to extern functions of unknown type are handled by
509 // predefined helper functions in libc but this work is currently done
510 // during call lowering but it should be moved here in the future.
512 bool Mips16HardFloat::runOnModule(Module &M) {
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);
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);
527 createFPFnStub(F, &M, V, TM);
533 char Mips16HardFloat::ID = 0;
537 ModulePass *llvm::createMips16HardFloat(MipsTargetMachine &TM) {
538 return new Mips16HardFloat(TM);