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/ModRef.h"
19 #include "llvm/Support/raw_ostream.h"
25 #define DEBUG_TYPE "mips16-hard-float"
29 class Mips16HardFloat
: public ModulePass
{
33 Mips16HardFloat() : ModulePass(ID
) {}
35 StringRef
getPassName() const override
{ return "MIPS16 Hard Float Pass"; }
37 void getAnalysisUsage(AnalysisUsage
&AU
) const override
{
38 AU
.addRequired
<TargetPassConfig
>();
39 ModulePass::getAnalysisUsage(AU
);
42 bool runOnModule(Module
&M
) override
;
45 } // end anonymous namespace
47 static void emitInlineAsm(LLVMContext
&C
, BasicBlock
*BB
, StringRef AsmText
) {
48 std::vector
<Type
*> AsmArgTypes
;
49 std::vector
<Value
*> AsmArgs
;
51 FunctionType
*AsmFTy
=
52 FunctionType::get(Type::getVoidTy(C
), AsmArgTypes
, false);
53 InlineAsm
*IA
= InlineAsm::get(AsmFTy
, AsmText
, "", true,
54 /* IsAlignStack */ false, InlineAsm::AD_ATT
);
55 CallInst::Create(IA
, AsmArgs
, "", BB
);
58 char Mips16HardFloat::ID
= 0;
61 // Return types that matter for hard float are:
62 // float, double, complex float, and complex double
64 enum FPReturnVariant
{
65 FRet
, DRet
, CFRet
, CDRet
, NoFPRet
69 // Determine which FP return type this function has
71 static FPReturnVariant
whichFPReturnVariant(Type
*T
) {
72 switch (T
->getTypeID()) {
75 case Type::DoubleTyID
:
77 case Type::StructTyID
: {
78 StructType
*ST
= cast
<StructType
>(T
);
79 if (ST
->getNumElements() != 2)
81 if ((ST
->getElementType(0)->isFloatTy()) &&
82 (ST
->getElementType(1)->isFloatTy()))
84 if ((ST
->getElementType(0)->isDoubleTy()) &&
85 (ST
->getElementType(1)->isDoubleTy()))
95 // Parameter type that matter are float, (float, float), (float, double),
96 // double, (double, double), (double, float)
99 DSig
, DDSig
, DFSig
, NoSig
102 // which floating point parameter signature variant we are dealing with
103 using TypeID
= Type::TypeID
;
104 const Type::TypeID FloatTyID
= Type::FloatTyID
;
105 const Type::TypeID DoubleTyID
= Type::DoubleTyID
;
107 static FPParamVariant
whichFPParamVariantNeeded(Function
&F
) {
108 switch (F
.arg_size()) {
112 TypeID ArgTypeID
= F
.getFunctionType()->getParamType(0)->getTypeID();
123 TypeID ArgTypeID0
= F
.getFunctionType()->getParamType(0)->getTypeID();
124 TypeID ArgTypeID1
= F
.getFunctionType()->getParamType(1)->getTypeID();
127 switch (ArgTypeID1
) {
137 switch (ArgTypeID1
) {
151 llvm_unreachable("can't get here");
154 // Figure out if we need float point based on the function parameters.
155 // We need to move variables in and/or out of floating point
156 // registers because of the ABI
157 static bool needsFPStubFromParams(Function
&F
) {
158 if (F
.arg_size() >=1) {
159 Type
*ArgType
= F
.getFunctionType()->getParamType(0);
160 switch (ArgType
->getTypeID()) {
161 case Type::FloatTyID
:
162 case Type::DoubleTyID
:
171 static bool needsFPReturnHelper(Function
&F
) {
172 Type
* RetType
= F
.getReturnType();
173 return whichFPReturnVariant(RetType
) != NoFPRet
;
176 static bool needsFPReturnHelper(FunctionType
&FT
) {
177 Type
* RetType
= FT
.getReturnType();
178 return whichFPReturnVariant(RetType
) != NoFPRet
;
181 static bool needsFPHelperFromSig(Function
&F
) {
182 return needsFPStubFromParams(F
) || needsFPReturnHelper(F
);
185 // We swap between FP and Integer registers to allow Mips16 and Mips32 to
187 static std::string
swapFPIntParams(FPParamVariant PV
, Module
*M
, bool LE
,
189 std::string MI
= ToFP
? "mtc1 ": "mfc1 ";
194 AsmText
+= MI
+ "$$4, $$f12\n";
198 AsmText
+= MI
+ "$$4, $$f12\n";
199 AsmText
+= MI
+ "$$5, $$f14\n";
203 AsmText
+= MI
+ "$$4, $$f12\n";
205 AsmText
+= MI
+ "$$6, $$f14\n";
206 AsmText
+= MI
+ "$$7, $$f15\n";
208 AsmText
+= MI
+ "$$7, $$f14\n";
209 AsmText
+= MI
+ "$$6, $$f15\n";
215 AsmText
+= MI
+ "$$4, $$f12\n";
216 AsmText
+= MI
+ "$$5, $$f13\n";
218 AsmText
+= MI
+ "$$5, $$f12\n";
219 AsmText
+= MI
+ "$$4, $$f13\n";
225 AsmText
+= MI
+ "$$4, $$f12\n";
226 AsmText
+= MI
+ "$$5, $$f13\n";
227 AsmText
+= MI
+ "$$6, $$f14\n";
228 AsmText
+= MI
+ "$$7, $$f15\n";
230 AsmText
+= MI
+ "$$5, $$f12\n";
231 AsmText
+= MI
+ "$$4, $$f13\n";
232 AsmText
+= MI
+ "$$7, $$f14\n";
233 AsmText
+= MI
+ "$$6, $$f15\n";
239 AsmText
+= MI
+ "$$4, $$f12\n";
240 AsmText
+= MI
+ "$$5, $$f13\n";
242 AsmText
+= MI
+ "$$5, $$f12\n";
243 AsmText
+= MI
+ "$$4, $$f13\n";
245 AsmText
+= MI
+ "$$6, $$f14\n";
255 // Make sure that we know we already need a stub for this function.
256 // Having called needsFPHelperFromSig
257 static void assureFPCallStub(Function
&F
, Module
*M
,
258 const MipsTargetMachine
&TM
) {
259 // for now we only need them for static relocation
260 if (TM
.isPositionIndependent())
262 LLVMContext
&Context
= M
->getContext();
263 bool LE
= TM
.isLittleEndian();
264 std::string
Name(F
.getName());
265 std::string SectionName
= ".mips16.call.fp." + Name
;
266 std::string StubName
= "__call_stub_fp_" + Name
;
268 // see if we already have the stub
270 Function
*FStub
= M
->getFunction(StubName
);
271 if (FStub
&& !FStub
->isDeclaration()) return;
272 FStub
= Function::Create(F
.getFunctionType(),
273 Function::InternalLinkage
, StubName
, M
);
274 FStub
->addFnAttr("mips16_fp_stub");
275 FStub
->addFnAttr(Attribute::Naked
);
276 FStub
->addFnAttr(Attribute::NoInline
);
277 FStub
->addFnAttr(Attribute::NoUnwind
);
278 FStub
->addFnAttr("nomips16");
279 FStub
->setSection(SectionName
);
280 BasicBlock
*BB
= BasicBlock::Create(Context
, "entry", FStub
);
281 FPReturnVariant RV
= whichFPReturnVariant(FStub
->getReturnType());
282 FPParamVariant PV
= whichFPParamVariantNeeded(F
);
285 AsmText
+= ".set reorder\n";
286 AsmText
+= swapFPIntParams(PV
, M
, LE
, true);
288 AsmText
+= "move $$18, $$31\n";
289 AsmText
+= "jal " + Name
+ "\n";
291 AsmText
+= "lui $$25, %hi(" + Name
+ ")\n";
292 AsmText
+= "addiu $$25, $$25, %lo(" + Name
+ ")\n";
297 AsmText
+= "mfc1 $$2, $$f0\n";
302 AsmText
+= "mfc1 $$2, $$f0\n";
303 AsmText
+= "mfc1 $$3, $$f1\n";
305 AsmText
+= "mfc1 $$3, $$f0\n";
306 AsmText
+= "mfc1 $$2, $$f1\n";
312 AsmText
+= "mfc1 $$2, $$f0\n";
313 AsmText
+= "mfc1 $$3, $$f2\n";
315 AsmText
+= "mfc1 $$3, $$f0\n";
316 AsmText
+= "mfc1 $$3, $$f2\n";
322 AsmText
+= "mfc1 $$4, $$f2\n";
323 AsmText
+= "mfc1 $$5, $$f3\n";
324 AsmText
+= "mfc1 $$2, $$f0\n";
325 AsmText
+= "mfc1 $$3, $$f1\n";
328 AsmText
+= "mfc1 $$5, $$f2\n";
329 AsmText
+= "mfc1 $$4, $$f3\n";
330 AsmText
+= "mfc1 $$3, $$f0\n";
331 AsmText
+= "mfc1 $$2, $$f1\n";
340 AsmText
+= "jr $$18\n";
342 AsmText
+= "jr $$25\n";
343 emitInlineAsm(Context
, BB
, AsmText
);
345 new UnreachableInst(Context
, BB
);
348 // Functions that are llvm intrinsics and don't need helpers.
349 static const char *const IntrinsicInline
[] = {
351 "llvm.ceil.f32", "llvm.ceil.f64",
352 "llvm.copysign.f32", "llvm.copysign.f64",
353 "llvm.cos.f32", "llvm.cos.f64",
354 "llvm.exp.f32", "llvm.exp.f64",
355 "llvm.exp2.f32", "llvm.exp2.f64",
356 "llvm.fabs.f32", "llvm.fabs.f64",
357 "llvm.floor.f32", "llvm.floor.f64",
358 "llvm.fma.f32", "llvm.fma.f64",
359 "llvm.log.f32", "llvm.log.f64",
360 "llvm.log10.f32", "llvm.log10.f64",
361 "llvm.nearbyint.f32", "llvm.nearbyint.f64",
362 "llvm.pow.f32", "llvm.pow.f64",
363 "llvm.powi.f32.i32", "llvm.powi.f64.i32",
364 "llvm.rint.f32", "llvm.rint.f64",
365 "llvm.round.f32", "llvm.round.f64",
366 "llvm.sin.f32", "llvm.sin.f64",
367 "llvm.sqrt.f32", "llvm.sqrt.f64",
368 "llvm.trunc.f32", "llvm.trunc.f64",
371 static bool isIntrinsicInline(Function
*F
) {
372 return std::binary_search(std::begin(IntrinsicInline
),
373 std::end(IntrinsicInline
), F
->getName());
376 // Returns of float, double and complex need to be handled with a helper
378 static bool fixupFPReturnAndCall(Function
&F
, Module
*M
,
379 const MipsTargetMachine
&TM
) {
380 bool Modified
= false;
381 LLVMContext
&C
= M
->getContext();
382 Type
*MyVoid
= Type::getVoidTy(C
);
385 if (const ReturnInst
*RI
= dyn_cast
<ReturnInst
>(&I
)) {
386 Value
*RVal
= RI
->getReturnValue();
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.
395 Type
*T
= RVal
->getType();
396 FPReturnVariant RV
= whichFPReturnVariant(T
);
397 if (RV
== NoFPRet
) continue;
398 static const char *const Helper
[NoFPRet
] = {
399 "__mips16_ret_sf", "__mips16_ret_df", "__mips16_ret_sc",
402 const char *Name
= Helper
[RV
];
404 Value
*Params
[] = {RVal
};
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.
412 A
= A
.addFnAttribute(C
, "__Mips16RetHelper");
413 A
= A
.addFnAttribute(
414 C
, Attribute::getWithMemoryEffects(C
, MemoryEffects::none()));
415 A
= A
.addFnAttribute(C
, Attribute::NoInline
);
416 FunctionCallee F
= (M
->getOrInsertFunction(Name
, A
, MyVoid
, T
));
417 CallInst::Create(F
, Params
, "", I
.getIterator());
418 } else if (const CallInst
*CI
= dyn_cast
<CallInst
>(&I
)) {
419 FunctionType
*FT
= CI
->getFunctionType();
420 Function
*F_
= CI
->getCalledFunction();
421 if (needsFPReturnHelper(*FT
) &&
422 !(F_
&& isIntrinsicInline(F_
))) {
424 F
.addFnAttr("saveS2");
426 if (F_
&& !isIntrinsicInline(F_
)) {
427 // pic mode calls are handled by already defined
429 if (needsFPReturnHelper(*F_
)) {
431 F
.addFnAttr("saveS2");
433 if (!TM
.isPositionIndependent()) {
434 if (needsFPHelperFromSig(*F_
)) {
435 assureFPCallStub(*F_
, M
, TM
);
445 static void createFPFnStub(Function
*F
, Module
*M
, FPParamVariant PV
,
446 const MipsTargetMachine
&TM
) {
447 bool PicMode
= TM
.isPositionIndependent();
448 bool LE
= TM
.isLittleEndian();
449 LLVMContext
&Context
= M
->getContext();
450 std::string
Name(F
->getName());
451 std::string SectionName
= ".mips16.fn." + Name
;
452 std::string StubName
= "__fn_stub_" + Name
;
453 std::string LocalName
= "$$__fn_local_" + Name
;
454 Function
*FStub
= Function::Create
455 (F
->getFunctionType(),
456 Function::InternalLinkage
, StubName
, M
);
457 FStub
->addFnAttr("mips16_fp_stub");
458 FStub
->addFnAttr(Attribute::Naked
);
459 FStub
->addFnAttr(Attribute::NoUnwind
);
460 FStub
->addFnAttr(Attribute::NoInline
);
461 FStub
->addFnAttr("nomips16");
462 FStub
->setSection(SectionName
);
463 BasicBlock
*BB
= BasicBlock::Create(Context
, "entry", FStub
);
467 AsmText
+= ".set noreorder\n";
468 AsmText
+= ".cpload $$25\n";
469 AsmText
+= ".set reorder\n";
470 AsmText
+= ".reloc 0, R_MIPS_NONE, " + Name
+ "\n";
471 AsmText
+= "la $$25, " + LocalName
+ "\n";
473 AsmText
+= "la $$25, " + Name
+ "\n";
474 AsmText
+= swapFPIntParams(PV
, M
, LE
, false);
475 AsmText
+= "jr $$25\n";
476 AsmText
+= LocalName
+ " = " + Name
+ "\n";
477 emitInlineAsm(Context
, BB
, AsmText
);
479 new UnreachableInst(FStub
->getContext(), BB
);
482 // remove the use-soft-float attribute
483 static void removeUseSoftFloat(Function
&F
) {
484 LLVM_DEBUG(errs() << "removing -use-soft-float\n");
485 F
.removeFnAttr("use-soft-float");
486 if (F
.hasFnAttribute("use-soft-float")) {
487 LLVM_DEBUG(errs() << "still has -use-soft-float\n");
489 F
.addFnAttr("use-soft-float", "false");
492 // This pass only makes sense when the underlying chip has floating point but
493 // we are compiling as mips16.
494 // For all mips16 functions (that are not stubs we have already generated), or
495 // declared via attributes as nomips16, we must:
496 // 1) fixup all returns of float, double, single and double complex
497 // by calling a helper function before the actual return.
498 // 2) generate helper functions (stubs) that can be called by mips32
499 // functions that will move parameters passed normally passed in
501 // registers the soft float equivalents.
502 // 3) in the case of static relocation, generate helper functions so that
503 // mips16 functions can call extern functions of unknown type (mips16 or
505 // 4) TBD. For pic, calls to extern functions of unknown type are handled by
506 // predefined helper functions in libc but this work is currently done
507 // during call lowering but it should be moved here in the future.
508 bool Mips16HardFloat::runOnModule(Module
&M
) {
509 auto &TM
= static_cast<const MipsTargetMachine
&>(
510 getAnalysis
<TargetPassConfig
>().getTM
<TargetMachine
>());
511 LLVM_DEBUG(errs() << "Run on Module Mips16HardFloat\n");
512 bool Modified
= false;
513 for (Module::iterator F
= M
.begin(), E
= M
.end(); F
!= E
; ++F
) {
514 if (F
->hasFnAttribute("nomips16") &&
515 F
->hasFnAttribute("use-soft-float")) {
516 removeUseSoftFloat(*F
);
519 if (F
->isDeclaration() || F
->hasFnAttribute("mips16_fp_stub") ||
520 F
->hasFnAttribute("nomips16")) continue;
521 Modified
|= fixupFPReturnAndCall(*F
, &M
, TM
);
522 FPParamVariant V
= whichFPParamVariantNeeded(*F
);
525 createFPFnStub(&*F
, &M
, V
, TM
);
531 ModulePass
*llvm::createMips16HardFloatPass() {
532 return new Mips16HardFloat();