LLVM 20.0.0git
Mips16HardFloat.cpp
Go to the documentation of this file.
1//===- Mips16HardFloat.cpp for Mips16 Hard Float --------------------------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8//
9// This file defines a pass needed for Mips16 Hard Float
10//
11//===----------------------------------------------------------------------===//
12
13#include "MipsTargetMachine.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"
20#include <algorithm>
21#include <string>
22
23using namespace llvm;
24
25#define DEBUG_TYPE "mips16-hard-float"
26
27namespace {
28
29 class Mips16HardFloat : public ModulePass {
30 public:
31 static char ID;
32
33 Mips16HardFloat() : ModulePass(ID) {}
34
35 StringRef getPassName() const override { return "MIPS16 Hard Float Pass"; }
36
37 void getAnalysisUsage(AnalysisUsage &AU) const override {
40 }
41
42 bool runOnModule(Module &M) override;
43 };
44
45} // end anonymous namespace
46
47static void emitInlineAsm(LLVMContext &C, BasicBlock *BB, StringRef AsmText) {
48 std::vector<Type *> AsmArgTypes;
49 std::vector<Value *> AsmArgs;
50
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);
56}
57
58char Mips16HardFloat::ID = 0;
59
60//
61// Return types that matter for hard float are:
62// float, double, complex float, and complex double
63//
66};
67
68//
69// Determine which FP return type this function has
70//
72 switch (T->getTypeID()) {
73 case Type::FloatTyID:
74 return FRet;
76 return DRet;
77 case Type::StructTyID: {
78 StructType *ST = cast<StructType>(T);
79 if (ST->getNumElements() != 2)
80 break;
81 if ((ST->getElementType(0)->isFloatTy()) &&
82 (ST->getElementType(1)->isFloatTy()))
83 return CFRet;
84 if ((ST->getElementType(0)->isDoubleTy()) &&
85 (ST->getElementType(1)->isDoubleTy()))
86 return CDRet;
87 break;
88 }
89 default:
90 break;
91 }
92 return NoFPRet;
93}
94
95// Parameter type that matter are float, (float, float), (float, double),
96// double, (double, double), (double, float)
101
102// which floating point parameter signature variant we are dealing with
106
108 switch (F.arg_size()) {
109 case 0:
110 return NoSig;
111 case 1:{
112 TypeID ArgTypeID = F.getFunctionType()->getParamType(0)->getTypeID();
113 switch (ArgTypeID) {
114 case FloatTyID:
115 return FSig;
116 case DoubleTyID:
117 return DSig;
118 default:
119 return NoSig;
120 }
121 }
122 default: {
123 TypeID ArgTypeID0 = F.getFunctionType()->getParamType(0)->getTypeID();
124 TypeID ArgTypeID1 = F.getFunctionType()->getParamType(1)->getTypeID();
125 switch(ArgTypeID0) {
126 case FloatTyID: {
127 switch (ArgTypeID1) {
128 case FloatTyID:
129 return FFSig;
130 case DoubleTyID:
131 return FDSig;
132 default:
133 return FSig;
134 }
135 }
136 case DoubleTyID: {
137 switch (ArgTypeID1) {
138 case FloatTyID:
139 return DFSig;
140 case DoubleTyID:
141 return DDSig;
142 default:
143 return DSig;
144 }
145 }
146 default:
147 return NoSig;
148 }
149 }
150 }
151 llvm_unreachable("can't get here");
152}
153
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
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:
163 return true;
164 default:
165 break;
166 }
167 }
168 return false;
169}
170
172 Type* RetType = F.getReturnType();
173 return whichFPReturnVariant(RetType) != NoFPRet;
174}
175
177 Type* RetType = FT.getReturnType();
178 return whichFPReturnVariant(RetType) != NoFPRet;
179}
180
183}
184
185// We swap between FP and Integer registers to allow Mips16 and Mips32 to
186// interoperate
187static std::string swapFPIntParams(FPParamVariant PV, Module *M, bool LE,
188 bool ToFP) {
189 std::string MI = ToFP ? "mtc1 ": "mfc1 ";
190 std::string AsmText;
191
192 switch (PV) {
193 case FSig:
194 AsmText += MI + "$$4, $$f12\n";
195 break;
196
197 case FFSig:
198 AsmText += MI + "$$4, $$f12\n";
199 AsmText += MI + "$$5, $$f14\n";
200 break;
201
202 case FDSig:
203 AsmText += MI + "$$4, $$f12\n";
204 if (LE) {
205 AsmText += MI + "$$6, $$f14\n";
206 AsmText += MI + "$$7, $$f15\n";
207 } else {
208 AsmText += MI + "$$7, $$f14\n";
209 AsmText += MI + "$$6, $$f15\n";
210 }
211 break;
212
213 case DSig:
214 if (LE) {
215 AsmText += MI + "$$4, $$f12\n";
216 AsmText += MI + "$$5, $$f13\n";
217 } else {
218 AsmText += MI + "$$5, $$f12\n";
219 AsmText += MI + "$$4, $$f13\n";
220 }
221 break;
222
223 case DDSig:
224 if (LE) {
225 AsmText += MI + "$$4, $$f12\n";
226 AsmText += MI + "$$5, $$f13\n";
227 AsmText += MI + "$$6, $$f14\n";
228 AsmText += MI + "$$7, $$f15\n";
229 } else {
230 AsmText += MI + "$$5, $$f12\n";
231 AsmText += MI + "$$4, $$f13\n";
232 AsmText += MI + "$$7, $$f14\n";
233 AsmText += MI + "$$6, $$f15\n";
234 }
235 break;
236
237 case DFSig:
238 if (LE) {
239 AsmText += MI + "$$4, $$f12\n";
240 AsmText += MI + "$$5, $$f13\n";
241 } else {
242 AsmText += MI + "$$5, $$f12\n";
243 AsmText += MI + "$$4, $$f13\n";
244 }
245 AsmText += MI + "$$6, $$f14\n";
246 break;
247
248 case NoSig:
249 break;
250 }
251
252 return AsmText;
253}
254
255// Make sure that we know we already need a stub for this function.
256// Having called needsFPHelperFromSig
258 const MipsTargetMachine &TM) {
259 // for now we only need them for static relocation
260 if (TM.isPositionIndependent())
261 return;
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;
267 //
268 // see if we already have the stub
269 //
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);
283
284 std::string AsmText;
285 AsmText += ".set reorder\n";
286 AsmText += swapFPIntParams(PV, M, LE, true);
287 if (RV != NoFPRet) {
288 AsmText += "move $$18, $$31\n";
289 AsmText += "jal " + Name + "\n";
290 } else {
291 AsmText += "lui $$25, %hi(" + Name + ")\n";
292 AsmText += "addiu $$25, $$25, %lo(" + Name + ")\n";
293 }
294
295 switch (RV) {
296 case FRet:
297 AsmText += "mfc1 $$2, $$f0\n";
298 break;
299
300 case DRet:
301 if (LE) {
302 AsmText += "mfc1 $$2, $$f0\n";
303 AsmText += "mfc1 $$3, $$f1\n";
304 } else {
305 AsmText += "mfc1 $$3, $$f0\n";
306 AsmText += "mfc1 $$2, $$f1\n";
307 }
308 break;
309
310 case CFRet:
311 if (LE) {
312 AsmText += "mfc1 $$2, $$f0\n";
313 AsmText += "mfc1 $$3, $$f2\n";
314 } else {
315 AsmText += "mfc1 $$3, $$f0\n";
316 AsmText += "mfc1 $$3, $$f2\n";
317 }
318 break;
319
320 case CDRet:
321 if (LE) {
322 AsmText += "mfc1 $$4, $$f2\n";
323 AsmText += "mfc1 $$5, $$f3\n";
324 AsmText += "mfc1 $$2, $$f0\n";
325 AsmText += "mfc1 $$3, $$f1\n";
326
327 } else {
328 AsmText += "mfc1 $$5, $$f2\n";
329 AsmText += "mfc1 $$4, $$f3\n";
330 AsmText += "mfc1 $$3, $$f0\n";
331 AsmText += "mfc1 $$2, $$f1\n";
332 }
333 break;
334
335 case NoFPRet:
336 break;
337 }
338
339 if (RV != NoFPRet)
340 AsmText += "jr $$18\n";
341 else
342 AsmText += "jr $$25\n";
343 emitInlineAsm(Context, BB, AsmText);
344
345 new UnreachableInst(Context, BB);
346}
347
348// Functions that are llvm intrinsics and don't need helpers.
349static const char *const IntrinsicInline[] = {
350 "fabs", "fabsf",
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",
369};
370
372 return std::binary_search(std::begin(IntrinsicInline),
373 std::end(IntrinsicInline), F->getName());
374}
375
376// Returns of float, double and complex need to be handled with a helper
377// function.
379 const MipsTargetMachine &TM) {
380 bool Modified = false;
381 LLVMContext &C = M->getContext();
382 Type *MyVoid = Type::getVoidTy(C);
383 for (auto &BB: F)
384 for (auto &I: BB) {
385 if (const ReturnInst *RI = dyn_cast<ReturnInst>(&I)) {
386 Value *RVal = RI->getReturnValue();
387 if (!RVal) continue;
388 //
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.
394 //
395 Type *T = RVal->getType();
397 if (RV == NoFPRet) continue;
398 static const char *const Helper[NoFPRet] = {
399 "__mips16_ret_sf", "__mips16_ret_df", "__mips16_ret_sc",
400 "__mips16_ret_dc"
401 };
402 const char *Name = Helper[RV];
404 Value *Params[] = {RVal};
405 Modified = true;
406 //
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.
411 //
412 A = A.addFnAttribute(C, "__Mips16RetHelper");
413 A = A.addFnAttribute(
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_))) {
423 Modified=true;
424 F.addFnAttr("saveS2");
425 }
426 if (F_ && !isIntrinsicInline(F_)) {
427 // pic mode calls are handled by already defined
428 // helper functions
429 if (needsFPReturnHelper(*F_)) {
430 Modified=true;
431 F.addFnAttr("saveS2");
432 }
433 if (!TM.isPositionIndependent()) {
434 if (needsFPHelperFromSig(*F_)) {
435 assureFPCallStub(*F_, M, TM);
436 Modified=true;
437 }
438 }
439 }
440 }
441 }
442 return Modified;
443}
444
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;
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);
464
465 std::string AsmText;
466 if (PicMode) {
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";
472 } else
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);
478
479 new UnreachableInst(FStub->getContext(), BB);
480}
481
482// remove the use-soft-float attribute
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");
488 }
489 F.addFnAttr("use-soft-float", "false");
490}
491
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
500// floating point
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
504// mips32).
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.
508bool 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")) {
517 continue;
518 }
519 if (F->isDeclaration() || F->hasFnAttribute("mips16_fp_stub") ||
520 F->hasFnAttribute("nomips16")) continue;
521 Modified |= fixupFPReturnAndCall(*F, &M, TM);
523 if (V != NoSig) {
524 Modified = true;
525 createFPFnStub(&*F, &M, V, TM);
526 }
527 }
528 return Modified;
529}
530
532 return new Mips16HardFloat();
533}
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
#define LLVM_DEBUG(X)
Definition: Debug.h:101
std::string Name
IRTranslator LLVM IR MI
#define F(x, y, z)
Definition: MD5.cpp:55
#define I(x, y, z)
Definition: MD5.cpp:58
FPReturnVariant
@ CDRet
@ NoFPRet
@ DRet
@ CFRet
@ FRet
static bool fixupFPReturnAndCall(Function &F, Module *M, const MipsTargetMachine &TM)
static bool needsFPStubFromParams(Function &F)
static bool needsFPReturnHelper(Function &F)
static bool needsFPHelperFromSig(Function &F)
const Type::TypeID FloatTyID
static void createFPFnStub(Function *F, Module *M, FPParamVariant PV, const MipsTargetMachine &TM)
FPParamVariant
@ DDSig
@ DFSig
@ FDSig
@ NoSig
@ FFSig
@ DSig
@ FSig
static void removeUseSoftFloat(Function &F)
static const char *const IntrinsicInline[]
static bool isIntrinsicInline(Function *F)
static void emitInlineAsm(LLVMContext &C, BasicBlock *BB, StringRef AsmText)
static FPReturnVariant whichFPReturnVariant(Type *T)
const Type::TypeID DoubleTyID
static std::string swapFPIntParams(FPParamVariant PV, Module *M, bool LE, bool ToFP)
static FPParamVariant whichFPParamVariantNeeded(Function &F)
static void assureFPCallStub(Function &F, Module *M, const MipsTargetMachine &TM)
Module.h This file contains the declarations for the Module class.
Target-Independent Code Generator Pass Configuration Options pass.
Represent the analysis usage information of a pass.
AnalysisUsage & addRequired()
static Attribute getWithMemoryEffects(LLVMContext &Context, MemoryEffects ME)
Definition: Attributes.cpp:280
LLVM Basic Block Representation.
Definition: BasicBlock.h:61
static BasicBlock * Create(LLVMContext &Context, const Twine &Name="", Function *Parent=nullptr, BasicBlock *InsertBefore=nullptr)
Creates a new BasicBlock.
Definition: BasicBlock.h:212
This class represents a function call, abstracting a target machine's calling convention.
static CallInst * Create(FunctionType *Ty, Value *F, const Twine &NameStr="", InsertPosition InsertBefore=nullptr)
A handy container for a FunctionType+Callee-pointer pair, which can be passed around as a single enti...
Definition: DerivedTypes.h:168
void addFnAttr(Attribute::AttrKind Kind)
Add function attributes to this function.
Definition: Function.cpp:653
static Function * Create(FunctionType *Ty, LinkageTypes Linkage, unsigned AddrSpace, const Twine &N="", Module *M=nullptr)
Definition: Function.h:172
LLVMContext & getContext() const
getContext - Return a reference to the LLVMContext associated with this function.
Definition: Function.cpp:380
Type * getReturnType() const
Returns the type of the ret val.
Definition: Function.h:219
void setSection(StringRef S)
Change the section for this global.
Definition: Globals.cpp:267
bool isDeclaration() const
Return true if the primary definition of this global value is outside of the current translation unit...
Definition: Globals.cpp:290
static InlineAsm * get(FunctionType *Ty, StringRef AsmString, StringRef Constraints, bool hasSideEffects, bool isAlignStack=false, AsmDialect asmDialect=AD_ATT, bool canThrow=false)
InlineAsm::get - Return the specified uniqued inline asm string.
Definition: InlineAsm.cpp:43
This is an important class for using LLVM in a threaded context.
Definition: LLVMContext.h:67
static MemoryEffectsBase none()
Create MemoryEffectsBase that cannot read or write any memory.
Definition: ModRef.h:117
ModulePass class - This class is used to implement unstructured interprocedural optimizations and ana...
Definition: Pass.h:251
virtual bool runOnModule(Module &M)=0
runOnModule - Virtual method overriden by subclasses to process the module being operated on.
A Module instance is used to store all the information related to an LLVM module.
Definition: Module.h:65
FunctionListType::iterator iterator
The Function iterators.
Definition: Module.h:90
virtual void getAnalysisUsage(AnalysisUsage &) const
getAnalysisUsage - This function should be overriden by passes that need analysis information to do t...
Definition: Pass.cpp:98
virtual StringRef getPassName() const
getPassName - Return a nice clean name for a pass.
Definition: Pass.cpp:81
Return a value (possibly void), from a function.
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:50
Class to represent struct types.
Definition: DerivedTypes.h:216
Primary interface to the complete machine description for the target machine.
Definition: TargetMachine.h:77
Target-Independent Code Generator Pass Configuration Options.
The instances of the Type class are immutable: once they are created, they are never changed.
Definition: Type.h:45
TypeID
Definitions of all of the base types for the Type system.
Definition: Type.h:54
@ FloatTyID
32-bit floating point type
Definition: Type.h:58
@ StructTyID
Structures.
Definition: Type.h:73
@ DoubleTyID
64-bit floating point type
Definition: Type.h:59
static Type * getVoidTy(LLVMContext &C)
TypeID getTypeID() const
Return the type id for the type.
Definition: Type.h:136
This function has undefined behavior.
LLVM Value Representation.
Definition: Value.h:74
Type * getType() const
All values are typed, get the type of this value.
Definition: Value.h:255
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition: CallingConv.h:24
@ C
The default llvm calling convention, compatible with C.
Definition: CallingConv.h:34
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
ModulePass * createMips16HardFloatPass()