1 //===- Mips16HardFloat.cpp for Mips16 Hard Float --------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file defines a pass needed for Mips16 Hard Float
11 //===----------------------------------------------------------------------===//
13 #include "MipsTargetMachine.h"
14 #include "llvm/CodeGen/TargetPassConfig.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"
24 #define DEBUG_TYPE "mips16-hard-float"
28 class Mips16HardFloat
: public ModulePass
{
32 Mips16HardFloat() : ModulePass(ID
) {}
34 StringRef
getPassName() const override
{ return "MIPS16 Hard Float Pass"; }
36 void getAnalysisUsage(AnalysisUsage
&AU
) const override
{
37 AU
.addRequired
<TargetPassConfig
>();
38 ModulePass::getAnalysisUsage(AU
);
41 bool runOnModule(Module
&M
) override
;
44 } // end anonymous namespace
46 static void EmitInlineAsm(LLVMContext
&C
, BasicBlock
*BB
, StringRef AsmText
) {
47 std::vector
<Type
*> AsmArgTypes
;
48 std::vector
<Value
*> AsmArgs
;
50 FunctionType
*AsmFTy
=
51 FunctionType::get(Type::getVoidTy(C
), AsmArgTypes
, false);
52 InlineAsm
*IA
= InlineAsm::get(AsmFTy
, AsmText
, "", true,
53 /* IsAlignStack */ false, InlineAsm::AD_ATT
);
54 CallInst::Create(IA
, AsmArgs
, "", BB
);
57 char Mips16HardFloat::ID
= 0;
60 // Return types that matter for hard float are:
61 // float, double, complex float, and complex double
63 enum FPReturnVariant
{
64 FRet
, DRet
, CFRet
, CDRet
, NoFPRet
68 // Determine which FP return type this function has
70 static FPReturnVariant
whichFPReturnVariant(Type
*T
) {
71 switch (T
->getTypeID()) {
74 case Type::DoubleTyID
:
76 case Type::StructTyID
: {
77 StructType
*ST
= cast
<StructType
>(T
);
78 if (ST
->getNumElements() != 2)
80 if ((ST
->getElementType(0)->isFloatTy()) &&
81 (ST
->getElementType(1)->isFloatTy()))
83 if ((ST
->getElementType(0)->isDoubleTy()) &&
84 (ST
->getElementType(1)->isDoubleTy()))
94 // Parameter type that matter are float, (float, float), (float, double),
95 // double, (double, double), (double, float)
98 DSig
, DDSig
, DFSig
, NoSig
101 // which floating point parameter signature variant we are dealing with
102 using TypeID
= Type::TypeID
;
103 const Type::TypeID FloatTyID
= Type::FloatTyID
;
104 const Type::TypeID DoubleTyID
= Type::DoubleTyID
;
106 static FPParamVariant
whichFPParamVariantNeeded(Function
&F
) {
107 switch (F
.arg_size()) {
111 TypeID ArgTypeID
= F
.getFunctionType()->getParamType(0)->getTypeID();
122 TypeID ArgTypeID0
= F
.getFunctionType()->getParamType(0)->getTypeID();
123 TypeID ArgTypeID1
= F
.getFunctionType()->getParamType(1)->getTypeID();
126 switch (ArgTypeID1
) {
136 switch (ArgTypeID1
) {
150 llvm_unreachable("can't get here");
153 // Figure out if we need float point based on the function parameters.
154 // We need to move variables in and/or out of floating point
155 // registers because of the ABI
156 static bool needsFPStubFromParams(Function
&F
) {
157 if (F
.arg_size() >=1) {
158 Type
*ArgType
= F
.getFunctionType()->getParamType(0);
159 switch (ArgType
->getTypeID()) {
160 case Type::FloatTyID
:
161 case Type::DoubleTyID
:
170 static bool needsFPReturnHelper(Function
&F
) {
171 Type
* RetType
= F
.getReturnType();
172 return whichFPReturnVariant(RetType
) != NoFPRet
;
175 static bool needsFPReturnHelper(FunctionType
&FT
) {
176 Type
* RetType
= FT
.getReturnType();
177 return whichFPReturnVariant(RetType
) != NoFPRet
;
180 static bool needsFPHelperFromSig(Function
&F
) {
181 return needsFPStubFromParams(F
) || needsFPReturnHelper(F
);
184 // We swap between FP and Integer registers to allow Mips16 and Mips32 to
186 static std::string
swapFPIntParams(FPParamVariant PV
, Module
*M
, bool LE
,
188 std::string MI
= ToFP
? "mtc1 ": "mfc1 ";
193 AsmText
+= MI
+ "$$4, $$f12\n";
197 AsmText
+= MI
+ "$$4, $$f12\n";
198 AsmText
+= MI
+ "$$5, $$f14\n";
202 AsmText
+= MI
+ "$$4, $$f12\n";
204 AsmText
+= MI
+ "$$6, $$f14\n";
205 AsmText
+= MI
+ "$$7, $$f15\n";
207 AsmText
+= MI
+ "$$7, $$f14\n";
208 AsmText
+= MI
+ "$$6, $$f15\n";
214 AsmText
+= MI
+ "$$4, $$f12\n";
215 AsmText
+= MI
+ "$$5, $$f13\n";
217 AsmText
+= MI
+ "$$5, $$f12\n";
218 AsmText
+= MI
+ "$$4, $$f13\n";
224 AsmText
+= MI
+ "$$4, $$f12\n";
225 AsmText
+= MI
+ "$$5, $$f13\n";
226 AsmText
+= MI
+ "$$6, $$f14\n";
227 AsmText
+= MI
+ "$$7, $$f15\n";
229 AsmText
+= MI
+ "$$5, $$f12\n";
230 AsmText
+= MI
+ "$$4, $$f13\n";
231 AsmText
+= MI
+ "$$7, $$f14\n";
232 AsmText
+= MI
+ "$$6, $$f15\n";
238 AsmText
+= MI
+ "$$4, $$f12\n";
239 AsmText
+= MI
+ "$$5, $$f13\n";
241 AsmText
+= MI
+ "$$5, $$f12\n";
242 AsmText
+= MI
+ "$$4, $$f13\n";
244 AsmText
+= MI
+ "$$6, $$f14\n";
254 // Make sure that we know we already need a stub for this function.
255 // Having called needsFPHelperFromSig
256 static void assureFPCallStub(Function
&F
, Module
*M
,
257 const MipsTargetMachine
&TM
) {
258 // for now we only need them for static relocation
259 if (TM
.isPositionIndependent())
261 LLVMContext
&Context
= M
->getContext();
262 bool LE
= TM
.isLittleEndian();
263 std::string Name
= F
.getName();
264 std::string SectionName
= ".mips16.call.fp." + Name
;
265 std::string StubName
= "__call_stub_fp_" + Name
;
267 // see if we already have the stub
269 Function
*FStub
= M
->getFunction(StubName
);
270 if (FStub
&& !FStub
->isDeclaration()) return;
271 FStub
= Function::Create(F
.getFunctionType(),
272 Function::InternalLinkage
, StubName
, M
);
273 FStub
->addFnAttr("mips16_fp_stub");
274 FStub
->addFnAttr(Attribute::Naked
);
275 FStub
->addFnAttr(Attribute::NoInline
);
276 FStub
->addFnAttr(Attribute::NoUnwind
);
277 FStub
->addFnAttr("nomips16");
278 FStub
->setSection(SectionName
);
279 BasicBlock
*BB
= BasicBlock::Create(Context
, "entry", FStub
);
280 FPReturnVariant RV
= whichFPReturnVariant(FStub
->getReturnType());
281 FPParamVariant PV
= whichFPParamVariantNeeded(F
);
284 AsmText
+= ".set reorder\n";
285 AsmText
+= swapFPIntParams(PV
, M
, LE
, true);
287 AsmText
+= "move $$18, $$31\n";
288 AsmText
+= "jal " + Name
+ "\n";
290 AsmText
+= "lui $$25, %hi(" + Name
+ ")\n";
291 AsmText
+= "addiu $$25, $$25, %lo(" + Name
+ ")\n";
296 AsmText
+= "mfc1 $$2, $$f0\n";
301 AsmText
+= "mfc1 $$2, $$f0\n";
302 AsmText
+= "mfc1 $$3, $$f1\n";
304 AsmText
+= "mfc1 $$3, $$f0\n";
305 AsmText
+= "mfc1 $$2, $$f1\n";
311 AsmText
+= "mfc1 $$2, $$f0\n";
312 AsmText
+= "mfc1 $$3, $$f2\n";
314 AsmText
+= "mfc1 $$3, $$f0\n";
315 AsmText
+= "mfc1 $$3, $$f2\n";
321 AsmText
+= "mfc1 $$4, $$f2\n";
322 AsmText
+= "mfc1 $$5, $$f3\n";
323 AsmText
+= "mfc1 $$2, $$f0\n";
324 AsmText
+= "mfc1 $$3, $$f1\n";
327 AsmText
+= "mfc1 $$5, $$f2\n";
328 AsmText
+= "mfc1 $$4, $$f3\n";
329 AsmText
+= "mfc1 $$3, $$f0\n";
330 AsmText
+= "mfc1 $$2, $$f1\n";
339 AsmText
+= "jr $$18\n";
341 AsmText
+= "jr $$25\n";
342 EmitInlineAsm(Context
, BB
, AsmText
);
344 new UnreachableInst(Context
, BB
);
347 // Functions that are llvm intrinsics and don't need helpers.
348 static const char *const IntrinsicInline
[] = {
350 "llvm.ceil.f32", "llvm.ceil.f64",
351 "llvm.copysign.f32", "llvm.copysign.f64",
352 "llvm.cos.f32", "llvm.cos.f64",
353 "llvm.exp.f32", "llvm.exp.f64",
354 "llvm.exp2.f32", "llvm.exp2.f64",
355 "llvm.fabs.f32", "llvm.fabs.f64",
356 "llvm.floor.f32", "llvm.floor.f64",
357 "llvm.fma.f32", "llvm.fma.f64",
358 "llvm.log.f32", "llvm.log.f64",
359 "llvm.log10.f32", "llvm.log10.f64",
360 "llvm.nearbyint.f32", "llvm.nearbyint.f64",
361 "llvm.pow.f32", "llvm.pow.f64",
362 "llvm.powi.f32", "llvm.powi.f64",
363 "llvm.rint.f32", "llvm.rint.f64",
364 "llvm.round.f32", "llvm.round.f64",
365 "llvm.sin.f32", "llvm.sin.f64",
366 "llvm.sqrt.f32", "llvm.sqrt.f64",
367 "llvm.trunc.f32", "llvm.trunc.f64",
370 static bool isIntrinsicInline(Function
*F
) {
371 return std::binary_search(std::begin(IntrinsicInline
),
372 std::end(IntrinsicInline
), F
->getName());
375 // Returns of float, double and complex need to be handled with a helper
377 static bool fixupFPReturnAndCall(Function
&F
, Module
*M
,
378 const MipsTargetMachine
&TM
) {
379 bool Modified
= false;
380 LLVMContext
&C
= M
->getContext();
381 Type
*MyVoid
= Type::getVoidTy(C
);
384 if (const ReturnInst
*RI
= dyn_cast
<ReturnInst
>(&I
)) {
385 Value
*RVal
= RI
->getReturnValue();
388 // If there is a return value and it needs a helper function,
389 // figure out which one and add a call before the actual
390 // return to this helper. The purpose of the helper is to move
391 // floating point values from their soft float return mapping to
392 // where they would have been mapped to in floating point registers.
394 Type
*T
= RVal
->getType();
395 FPReturnVariant RV
= whichFPReturnVariant(T
);
396 if (RV
== NoFPRet
) continue;
397 static const char *const Helper
[NoFPRet
] = {
398 "__mips16_ret_sf", "__mips16_ret_df", "__mips16_ret_sc",
401 const char *Name
= Helper
[RV
];
403 Value
*Params
[] = {RVal
};
406 // These helper functions have a different calling ABI so
407 // this __Mips16RetHelper indicates that so that later
408 // during call setup, the proper call lowering to the helper
409 // functions will take place.
411 A
= A
.addAttribute(C
, AttributeList::FunctionIndex
,
412 "__Mips16RetHelper");
413 A
= A
.addAttribute(C
, AttributeList::FunctionIndex
,
414 Attribute::ReadNone
);
415 A
= A
.addAttribute(C
, AttributeList::FunctionIndex
,
416 Attribute::NoInline
);
417 FunctionCallee F
= (M
->getOrInsertFunction(Name
, A
, MyVoid
, T
));
418 CallInst::Create(F
, Params
, "", &I
);
419 } else if (const CallInst
*CI
= dyn_cast
<CallInst
>(&I
)) {
420 FunctionType
*FT
= CI
->getFunctionType();
421 Function
*F_
= CI
->getCalledFunction();
422 if (needsFPReturnHelper(*FT
) &&
423 !(F_
&& isIntrinsicInline(F_
))) {
425 F
.addFnAttr("saveS2");
427 if (F_
&& !isIntrinsicInline(F_
)) {
428 // pic mode calls are handled by already defined
430 if (needsFPReturnHelper(*F_
)) {
432 F
.addFnAttr("saveS2");
434 if (!TM
.isPositionIndependent()) {
435 if (needsFPHelperFromSig(*F_
)) {
436 assureFPCallStub(*F_
, M
, TM
);
446 static void createFPFnStub(Function
*F
, Module
*M
, FPParamVariant PV
,
447 const MipsTargetMachine
&TM
) {
448 bool PicMode
= TM
.isPositionIndependent();
449 bool LE
= TM
.isLittleEndian();
450 LLVMContext
&Context
= M
->getContext();
451 std::string Name
= F
->getName();
452 std::string SectionName
= ".mips16.fn." + Name
;
453 std::string StubName
= "__fn_stub_" + Name
;
454 std::string LocalName
= "$$__fn_local_" + Name
;
455 Function
*FStub
= Function::Create
456 (F
->getFunctionType(),
457 Function::InternalLinkage
, StubName
, M
);
458 FStub
->addFnAttr("mips16_fp_stub");
459 FStub
->addFnAttr(Attribute::Naked
);
460 FStub
->addFnAttr(Attribute::NoUnwind
);
461 FStub
->addFnAttr(Attribute::NoInline
);
462 FStub
->addFnAttr("nomips16");
463 FStub
->setSection(SectionName
);
464 BasicBlock
*BB
= BasicBlock::Create(Context
, "entry", FStub
);
468 AsmText
+= ".set noreorder\n";
469 AsmText
+= ".cpload $$25\n";
470 AsmText
+= ".set reorder\n";
471 AsmText
+= ".reloc 0, R_MIPS_NONE, " + Name
+ "\n";
472 AsmText
+= "la $$25, " + LocalName
+ "\n";
474 AsmText
+= "la $$25, " + Name
+ "\n";
475 AsmText
+= swapFPIntParams(PV
, M
, LE
, false);
476 AsmText
+= "jr $$25\n";
477 AsmText
+= LocalName
+ " = " + Name
+ "\n";
478 EmitInlineAsm(Context
, BB
, AsmText
);
480 new UnreachableInst(FStub
->getContext(), BB
);
483 // remove the use-soft-float attribute
484 static void removeUseSoftFloat(Function
&F
) {
486 LLVM_DEBUG(errs() << "removing -use-soft-float\n");
487 B
.addAttribute("use-soft-float", "false");
488 F
.removeAttributes(AttributeList::FunctionIndex
, B
);
489 if (F
.hasFnAttribute("use-soft-float")) {
490 LLVM_DEBUG(errs() << "still has -use-soft-float\n");
492 F
.addAttributes(AttributeList::FunctionIndex
, B
);
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.
511 bool Mips16HardFloat::runOnModule(Module
&M
) {
512 auto &TM
= static_cast<const MipsTargetMachine
&>(
513 getAnalysis
<TargetPassConfig
>().getTM
<TargetMachine
>());
514 LLVM_DEBUG(errs() << "Run on Module Mips16HardFloat\n");
515 bool Modified
= false;
516 for (Module::iterator F
= M
.begin(), E
= M
.end(); F
!= E
; ++F
) {
517 if (F
->hasFnAttribute("nomips16") &&
518 F
->hasFnAttribute("use-soft-float")) {
519 removeUseSoftFloat(*F
);
522 if (F
->isDeclaration() || F
->hasFnAttribute("mips16_fp_stub") ||
523 F
->hasFnAttribute("nomips16")) continue;
524 Modified
|= fixupFPReturnAndCall(*F
, &M
, TM
);
525 FPParamVariant V
= whichFPParamVariantNeeded(*F
);
528 createFPFnStub(&*F
, &M
, V
, TM
);
534 ModulePass
*llvm::createMips16HardFloatPass() {
535 return new Mips16HardFloat();