LLVM 24.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 "Mips.h"
14#include "MipsTargetMachine.h"
16#include "llvm/IR/Analysis.h"
17#include "llvm/IR/Module.h"
18#include "llvm/IR/PassManager.h"
19#include "llvm/IR/Value.h"
20#include "llvm/Support/Debug.h"
21#include "llvm/Support/ModRef.h"
23#include <string>
24
25using namespace llvm;
26
27#define DEBUG_TYPE "mips-mips16-hard-float"
28
29namespace {
30
31class Mips16HardFloatLegacy : public ModulePass {
32public:
33 static char ID;
34
35 Mips16HardFloatLegacy() : ModulePass(ID) {}
36
37 StringRef getPassName() const override { return "MIPS16 Hard Float Pass"; }
38
39 void getAnalysisUsage(AnalysisUsage &AU) const override {
41 AU.setPreservesCFG();
43 }
44
45 bool runOnModule(Module &M) override;
46};
47
48} // end anonymous namespace
49
50static void emitInlineAsm(LLVMContext &C, BasicBlock *BB, StringRef AsmText) {
51 std::vector<Type *> AsmArgTypes;
52 std::vector<Value *> AsmArgs;
53
54 FunctionType *AsmFTy =
55 FunctionType::get(Type::getVoidTy(C), AsmArgTypes, false);
56 InlineAsm *IA = InlineAsm::get(AsmFTy, AsmText, "", true,
57 /* IsAlignStack */ false, InlineAsm::AD_ATT);
58 CallInst::Create(IA, AsmArgs, "", BB);
59}
60
61char Mips16HardFloatLegacy::ID = 0;
62
63//
64// Return types that matter for hard float are:
65// float, double, complex float, and complex double
66//
70
71//
72// Determine which FP return type this function has
73//
75 switch (T->getTypeID()) {
76 case Type::FloatTyID:
77 return FRet;
79 return DRet;
80 case Type::StructTyID: {
82 if (ST->getNumElements() != 2)
83 break;
84 if ((ST->getElementType(0)->isFloatTy()) &&
85 (ST->getElementType(1)->isFloatTy()))
86 return CFRet;
87 if ((ST->getElementType(0)->isDoubleTy()) &&
88 (ST->getElementType(1)->isDoubleTy()))
89 return CDRet;
90 break;
91 }
92 default:
93 break;
94 }
95 return NoFPRet;
96}
97
98// Parameter type that matter are float, (float, float), (float, double),
99// double, (double, double), (double, float)
104
105// which floating point parameter signature variant we are dealing with
109
111 switch (F.arg_size()) {
112 case 0:
113 return NoSig;
114 case 1:{
115 TypeID ArgTypeID = F.getFunctionType()->getParamType(0)->getTypeID();
116 switch (ArgTypeID) {
117 case FloatTyID:
118 return FSig;
119 case DoubleTyID:
120 return DSig;
121 default:
122 return NoSig;
123 }
124 }
125 default: {
126 TypeID ArgTypeID0 = F.getFunctionType()->getParamType(0)->getTypeID();
127 TypeID ArgTypeID1 = F.getFunctionType()->getParamType(1)->getTypeID();
128 switch(ArgTypeID0) {
129 case FloatTyID: {
130 switch (ArgTypeID1) {
131 case FloatTyID:
132 return FFSig;
133 case DoubleTyID:
134 return FDSig;
135 default:
136 return FSig;
137 }
138 }
139 case DoubleTyID: {
140 switch (ArgTypeID1) {
141 case FloatTyID:
142 return DFSig;
143 case DoubleTyID:
144 return DDSig;
145 default:
146 return DSig;
147 }
148 }
149 default:
150 return NoSig;
151 }
152 }
153 }
154 llvm_unreachable("can't get here");
155}
156
157// Figure out if we need float point based on the function parameters.
158// We need to move variables in and/or out of floating point
159// registers because of the ABI
161 if (F.arg_size() >=1) {
162 Type *ArgType = F.getFunctionType()->getParamType(0);
163 switch (ArgType->getTypeID()) {
164 case Type::FloatTyID:
165 case Type::DoubleTyID:
166 return true;
167 default:
168 break;
169 }
170 }
171 return false;
172}
173
175 Type* RetType = F.getReturnType();
176 return whichFPReturnVariant(RetType) != NoFPRet;
177}
178
180 Type* RetType = FT.getReturnType();
181 return whichFPReturnVariant(RetType) != NoFPRet;
182}
183
187
188// We swap between FP and Integer registers to allow Mips16 and Mips32 to
189// interoperate
190static std::string swapFPIntParams(FPParamVariant PV, Module *M, bool LE,
191 bool ToFP) {
192 std::string MI = ToFP ? "mtc1 ": "mfc1 ";
193 std::string AsmText;
194
195 switch (PV) {
196 case FSig:
197 AsmText += MI + "$$4, $$f12\n";
198 break;
199
200 case FFSig:
201 AsmText += MI + "$$4, $$f12\n";
202 AsmText += MI + "$$5, $$f14\n";
203 break;
204
205 case FDSig:
206 AsmText += MI + "$$4, $$f12\n";
207 if (LE) {
208 AsmText += MI + "$$6, $$f14\n";
209 AsmText += MI + "$$7, $$f15\n";
210 } else {
211 AsmText += MI + "$$7, $$f14\n";
212 AsmText += MI + "$$6, $$f15\n";
213 }
214 break;
215
216 case DSig:
217 if (LE) {
218 AsmText += MI + "$$4, $$f12\n";
219 AsmText += MI + "$$5, $$f13\n";
220 } else {
221 AsmText += MI + "$$5, $$f12\n";
222 AsmText += MI + "$$4, $$f13\n";
223 }
224 break;
225
226 case DDSig:
227 if (LE) {
228 AsmText += MI + "$$4, $$f12\n";
229 AsmText += MI + "$$5, $$f13\n";
230 AsmText += MI + "$$6, $$f14\n";
231 AsmText += MI + "$$7, $$f15\n";
232 } else {
233 AsmText += MI + "$$5, $$f12\n";
234 AsmText += MI + "$$4, $$f13\n";
235 AsmText += MI + "$$7, $$f14\n";
236 AsmText += MI + "$$6, $$f15\n";
237 }
238 break;
239
240 case DFSig:
241 if (LE) {
242 AsmText += MI + "$$4, $$f12\n";
243 AsmText += MI + "$$5, $$f13\n";
244 } else {
245 AsmText += MI + "$$5, $$f12\n";
246 AsmText += MI + "$$4, $$f13\n";
247 }
248 AsmText += MI + "$$6, $$f14\n";
249 break;
250
251 case NoSig:
252 break;
253 }
254
255 return AsmText;
256}
257
258// Make sure that we know we already need a stub for this function.
259// Having called needsFPHelperFromSig
261 const MipsTargetMachine &TM) {
262 // for now we only need them for static relocation
263 if (TM.isPositionIndependent())
264 return;
265 LLVMContext &Context = M->getContext();
266 bool LE = TM.isLittleEndian();
267 std::string Name(F.getName());
268 std::string SectionName = ".mips16.call.fp." + Name;
269 std::string StubName = "__call_stub_fp_" + Name;
270 //
271 // see if we already have the stub
272 //
273 Function *FStub = M->getFunction(StubName);
274 if (FStub && !FStub->isDeclaration()) return;
275 FStub = Function::Create(F.getFunctionType(),
276 Function::InternalLinkage, StubName, M);
277 FStub->addFnAttr("mips16_fp_stub");
278 FStub->addFnAttr(Attribute::Naked);
279 FStub->addFnAttr(Attribute::NoInline);
280 FStub->addFnAttr(Attribute::NoUnwind);
281 FStub->addFnAttr("nomips16");
282 FStub->setSection(SectionName);
283 BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
286
287 std::string AsmText;
288 AsmText += ".set reorder\n";
289 AsmText += swapFPIntParams(PV, M, LE, true);
290 if (RV != NoFPRet) {
291 AsmText += "move $$18, $$31\n";
292 AsmText += "jal " + Name + "\n";
293 } else {
294 AsmText += "lui $$25, %hi(" + Name + ")\n";
295 AsmText += "addiu $$25, $$25, %lo(" + Name + ")\n";
296 }
297
298 switch (RV) {
299 case FRet:
300 AsmText += "mfc1 $$2, $$f0\n";
301 break;
302
303 case DRet:
304 if (LE) {
305 AsmText += "mfc1 $$2, $$f0\n";
306 AsmText += "mfc1 $$3, $$f1\n";
307 } else {
308 AsmText += "mfc1 $$3, $$f0\n";
309 AsmText += "mfc1 $$2, $$f1\n";
310 }
311 break;
312
313 case CFRet:
314 if (LE) {
315 AsmText += "mfc1 $$2, $$f0\n";
316 AsmText += "mfc1 $$3, $$f2\n";
317 } else {
318 AsmText += "mfc1 $$3, $$f0\n";
319 AsmText += "mfc1 $$3, $$f2\n";
320 }
321 break;
322
323 case CDRet:
324 if (LE) {
325 AsmText += "mfc1 $$4, $$f2\n";
326 AsmText += "mfc1 $$5, $$f3\n";
327 AsmText += "mfc1 $$2, $$f0\n";
328 AsmText += "mfc1 $$3, $$f1\n";
329
330 } else {
331 AsmText += "mfc1 $$5, $$f2\n";
332 AsmText += "mfc1 $$4, $$f3\n";
333 AsmText += "mfc1 $$3, $$f0\n";
334 AsmText += "mfc1 $$2, $$f1\n";
335 }
336 break;
337
338 case NoFPRet:
339 break;
340 }
341
342 if (RV != NoFPRet)
343 AsmText += "jr $$18\n";
344 else
345 AsmText += "jr $$25\n";
346 emitInlineAsm(Context, BB, AsmText);
347
348 new UnreachableInst(Context, BB);
349}
350
351// Functions that are llvm intrinsics and don't need helpers.
352static const char *const IntrinsicInline[] = {
353 "fabs", "fabsf",
354 "llvm.ceil.f32", "llvm.ceil.f64",
355 "llvm.copysign.f32", "llvm.copysign.f64",
356 "llvm.cos.f32", "llvm.cos.f64",
357 "llvm.exp.f32", "llvm.exp.f64",
358 "llvm.exp2.f32", "llvm.exp2.f64",
359 "llvm.fabs.f32", "llvm.fabs.f64",
360 "llvm.floor.f32", "llvm.floor.f64",
361 "llvm.fma.f32", "llvm.fma.f64",
362 "llvm.log.f32", "llvm.log.f64",
363 "llvm.log10.f32", "llvm.log10.f64",
364 "llvm.nearbyint.f32", "llvm.nearbyint.f64",
365 "llvm.pow.f32", "llvm.pow.f64",
366 "llvm.powi.f32.i32", "llvm.powi.f64.i32",
367 "llvm.rint.f32", "llvm.rint.f64",
368 "llvm.round.f32", "llvm.round.f64",
369 "llvm.sin.f32", "llvm.sin.f64",
370 "llvm.sqrt.f32", "llvm.sqrt.f64",
371 "llvm.trunc.f32", "llvm.trunc.f64",
372};
373
375 return llvm::binary_search(IntrinsicInline, F->getName());
376}
377
378// Returns of float, double and complex need to be handled with a helper
379// function.
381 const MipsTargetMachine &TM) {
382 bool Modified = false;
383 LLVMContext &C = M->getContext();
384 Type *MyVoid = Type::getVoidTy(C);
385 for (auto &BB: F)
386 for (auto &I: BB) {
387 if (const ReturnInst *RI = dyn_cast<ReturnInst>(&I)) {
388 Value *RVal = RI->getReturnValue();
389 if (!RVal) continue;
390 //
391 // If there is a return value and it needs a helper function,
392 // figure out which one and add a call before the actual
393 // return to this helper. The purpose of the helper is to move
394 // floating point values from their soft float return mapping to
395 // where they would have been mapped to in floating point registers.
396 //
397 Type *T = RVal->getType();
399 if (RV == NoFPRet) continue;
400 static const char *const Helper[NoFPRet] = {
401 "__mips16_ret_sf", "__mips16_ret_df", "__mips16_ret_sc",
402 "__mips16_ret_dc"
403 };
404 const char *Name = Helper[RV];
406 Value *Params[] = {RVal};
407 Modified = true;
408 //
409 // These helper functions have a different calling ABI so
410 // this __Mips16RetHelper indicates that so that later
411 // during call setup, the proper call lowering to the helper
412 // functions will take place.
413 //
414 A = A.addFnAttribute(C, "__Mips16RetHelper");
415 A = A.addFnAttribute(
417 A = A.addFnAttribute(C, Attribute::NoInline);
418 FunctionCallee F = (M->getOrInsertFunction(Name, A, MyVoid, T));
419 CallInst::Create(F, Params, "", I.getIterator());
420 } else if (const CallInst *CI = dyn_cast<CallInst>(&I)) {
421 FunctionType *FT = CI->getFunctionType();
422 Function *F_ = CI->getCalledFunction();
423 if (needsFPReturnHelper(*FT) &&
424 !(F_ && isIntrinsicInline(F_))) {
425 Modified=true;
426 F.addFnAttr("saveS2");
427 }
428 if (F_ && !isIntrinsicInline(F_)) {
429 // pic mode calls are handled by already defined
430 // helper functions
431 if (needsFPReturnHelper(*F_)) {
432 Modified=true;
433 F.addFnAttr("saveS2");
434 }
435 if (!TM.isPositionIndependent()) {
436 if (needsFPHelperFromSig(*F_)) {
437 assureFPCallStub(*F_, M, TM);
438 Modified=true;
439 }
440 }
441 }
442 }
443 }
444 return Modified;
445}
446
448 const MipsTargetMachine &TM) {
449 bool PicMode = TM.isPositionIndependent();
450 bool LE = TM.isLittleEndian();
451 LLVMContext &Context = M->getContext();
452 std::string Name(F->getName());
453 std::string SectionName = ".mips16.fn." + Name;
454 std::string StubName = "__fn_stub_" + Name;
455 std::string LocalName = "$$__fn_local_" + Name;
457 (F->getFunctionType(),
458 Function::InternalLinkage, StubName, M);
459 FStub->addFnAttr("mips16_fp_stub");
460 FStub->addFnAttr(Attribute::Naked);
461 FStub->addFnAttr(Attribute::NoUnwind);
462 FStub->addFnAttr(Attribute::NoInline);
463 FStub->addFnAttr("nomips16");
464 FStub->setSection(SectionName);
465 BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
466
467 std::string AsmText;
468 if (PicMode) {
469 AsmText += ".set noreorder\n";
470 AsmText += ".cpload $$25\n";
471 AsmText += ".set reorder\n";
472 AsmText += ".reloc 0, R_MIPS_NONE, " + Name + "\n";
473 AsmText += "la $$25, " + LocalName + "\n";
474 } else
475 AsmText += "la $$25, " + Name + "\n";
476 AsmText += swapFPIntParams(PV, M, LE, false);
477 AsmText += "jr $$25\n";
478 AsmText += LocalName + " = " + Name + "\n";
479 emitInlineAsm(Context, BB, AsmText);
480
481 new UnreachableInst(FStub->getContext(), BB);
482}
483
484// This pass only makes sense when the underlying chip has floating point but
485// we are compiling as mips16.
486// For all mips16 hard-float functions (excluding stubs we have already
487// generated), we must:
488// 1) fixup all returns of float, double, single and double complex
489// by calling a helper function before the actual return.
490// 2) generate helper functions (stubs) that can be called by mips32
491// functions that will move parameters passed normally passed in
492// floating point
493// registers the soft float equivalents.
494// 3) in the case of static relocation, generate helper functions so that
495// mips16 functions can call extern functions of unknown type (mips16 or
496// mips32).
497// 4) TBD. For pic, calls to extern functions of unknown type are handled by
498// predefined helper functions in libc but this work is currently done
499// during call lowering but it should be moved here in the future.
501 LLVM_DEBUG(errs() << "Run on Module Mips16HardFloat\n");
502 bool Modified = false;
503 for (Module::iterator F = M.begin(), E = M.end(); F != E; ++F) {
504 if (F->isDeclaration() || F->hasFnAttribute("mips16_fp_stub") ||
506 continue;
507 Modified |= fixupFPReturnAndCall(*F, &M, TM);
509 if (V != NoSig) {
510 Modified = true;
511 createFPFnStub(&*F, &M, V, TM);
512 }
513 }
514 return Modified;
515}
516
518 return new Mips16HardFloatLegacy();
519}
520
521bool Mips16HardFloatLegacy::runOnModule(Module &M) {
522 auto &TM = static_cast<const MipsTargetMachine &>(
523 getAnalysis<TargetPassConfig>().getTM<TargetMachine>());
524 return runHardFloatOnModule(M, TM);
525}
526
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
IRTranslator LLVM IR MI
Module.h This file contains the declarations for the Module class.
This header defines various interfaces for pass management in LLVM.
@ DoubleTyID
64-bit floating point type
@ FloatTyID
32-bit floating point type
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
FPReturnVariant
@ CDRet
@ NoFPRet
@ CFRet
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)
static void createFPFnStub(Function *F, Module *M, FPParamVariant PV, const MipsTargetMachine &TM)
FPParamVariant
Type::TypeID TypeID
static const char *const IntrinsicInline[]
static bool isIntrinsicInline(Function *F)
static void emitInlineAsm(LLVMContext &C, BasicBlock *BB, StringRef AsmText)
static FPReturnVariant whichFPReturnVariant(Type *T)
static bool runHardFloatOnModule(Module &M, const MipsTargetMachine &TM)
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)
#define T
ModuleAnalysisManager MAM
#define LLVM_DEBUG(...)
Definition Debug.h:119
Target-Independent Code Generator Pass Configuration Options pass.
Represent the analysis usage information of a pass.
AnalysisUsage & addRequired()
LLVM_ABI void setPreservesCFG()
This function should be called by the pass, iff they do not:
Definition Pass.cpp:280
static LLVM_ABI Attribute getWithMemoryEffects(LLVMContext &Context, MemoryEffects ME)
LLVM Basic Block Representation.
Definition BasicBlock.h:62
static BasicBlock * Create(LLVMContext &Context, const Twine &Name="", Function *Parent=nullptr, BasicBlock *InsertBefore=nullptr)
Creates a new BasicBlock.
Definition BasicBlock.h:206
Represents analyses that only rely on functions' control flow.
Definition Analysis.h:73
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...
static LLVM_ABI FunctionType * get(Type *Result, ArrayRef< Type * > Params, bool isVarArg)
This static method is the primary way of constructing a FunctionType.
void addFnAttr(Attribute::AttrKind Kind)
Add function attributes to this function.
Definition Function.cpp:641
static Function * Create(FunctionType *Ty, LinkageTypes Linkage, unsigned AddrSpace, const Twine &N="", Module *M=nullptr)
Definition Function.h:169
const Function & getFunction() const
Definition Function.h:167
LLVMContext & getContext() const
getContext - Return a reference to the LLVMContext associated with this function.
Definition Function.cpp:352
Type * getReturnType() const
Returns the type of the ret val.
Definition Function.h:217
LLVM_ABI void setSection(StringRef S)
Change the section for this global.
Definition Globals.cpp:348
LLVM_ABI bool isDeclaration() const
Return true if the primary definition of this global value is outside of the current translation unit...
Definition Globals.cpp:408
@ InternalLinkage
Rename collisions when linking (static functions).
Definition GlobalValue.h:60
static LLVM_ABI 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:76
static MemoryEffectsBase none()
Definition ModRef.h:128
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM)
bool inMips16HardFloat() const
ModulePass class - This class is used to implement unstructured interprocedural optimizations and ana...
Definition Pass.h:253
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:68
FunctionListType::iterator iterator
The Function iterators.
Definition Module.h:93
virtual void getAnalysisUsage(AnalysisUsage &) const
getAnalysisUsage - This function should be overriden by passes that need analysis information to do t...
Definition Pass.cpp:114
A set of analyses that are preserved following a run of a transformation pass.
Definition Analysis.h:112
static PreservedAnalyses none()
Convenience factory function for the empty preserved set.
Definition Analysis.h:115
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
PreservedAnalyses & preserveSet()
Mark an analysis set as preserved.
Definition Analysis.h:151
Return a value (possibly void), from a function.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
Class to represent struct types.
Primary interface to the complete machine description for the target machine.
bool isPositionIndependent() const
const STC & getSubtarget(const Function &F) const
This method returns a pointer to the specified type of TargetSubtargetInfo.
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:46
TypeID
Definitions of all of the base types for the Type system.
Definition Type.h:55
@ FloatTyID
32-bit floating point type
Definition Type.h:59
@ StructTyID
Structures.
Definition Type.h:75
@ DoubleTyID
64-bit floating point type
Definition Type.h:60
static LLVM_ABI Type * getVoidTy(LLVMContext &C)
Definition Type.cpp:272
TypeID getTypeID() const
Return the type id for the type.
Definition Type.h:138
This function has undefined behavior.
LLVM Value Representation.
Definition Value.h:75
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:257
Pass manager infrastructure for declaring and invalidating analyses.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
This is an optimization pass for GlobalISel generic memory operations.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
auto binary_search(R &&Range, T &&Value)
Provide wrappers to std::binary_search which take ranges instead of having to pass begin/end explicit...
Definition STLExtras.h:2055
LLVM_ABI raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
ModulePass * createMips16HardFloatLegacyPass()
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
AnalysisManager< Module > ModuleAnalysisManager
Convenience typedef for the Module analysis manager.
Definition MIRParser.h:39