blob: c4fca2742ef8d357005e71b30d7abd29a23beacb [file] [log] [blame]
Reed Kotler783c7942013-05-10 22:25:39 +00001//===---- Mips16HardFloat.cpp for Mips16 Hard Float --------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file defines a pass needed for Mips16 Hard Float
11//
12//===----------------------------------------------------------------------===//
13
14#define DEBUG_TYPE "mips16-hard-float"
15#include "Mips16HardFloat.h"
16#include "llvm/IR/Module.h"
Chandler Carruth8a8cd2b2014-01-07 11:48:04 +000017#include "llvm/IR/Value.h"
Reed Kotler783c7942013-05-10 22:25:39 +000018#include "llvm/Support/Debug.h"
19#include "llvm/Support/raw_ostream.h"
Reed Kotlerd265e882013-08-11 21:30:27 +000020#include <algorithm>
Reed Kotler783c7942013-05-10 22:25:39 +000021#include <string>
22
Reed Kotler2c4657d2013-05-14 02:00:24 +000023static void inlineAsmOut
24 (LLVMContext &C, StringRef AsmString, BasicBlock *BB ) {
25 std::vector<llvm::Type *> AsmArgTypes;
26 std::vector<llvm::Value*> AsmArgs;
27 llvm::FunctionType *AsmFTy =
28 llvm::FunctionType::get(Type::getVoidTy(C),
29 AsmArgTypes, false);
30 llvm::InlineAsm *IA =
31 llvm::InlineAsm::get(AsmFTy, AsmString, "", true,
32 /* IsAlignStack */ false,
33 llvm::InlineAsm::AD_ATT);
34 CallInst::Create(IA, AsmArgs, "", BB);
35}
36
37namespace {
38
39class InlineAsmHelper {
40 LLVMContext &C;
41 BasicBlock *BB;
42public:
43 InlineAsmHelper(LLVMContext &C_, BasicBlock *BB_) :
44 C(C_), BB(BB_) {
45 }
46
47 void Out(StringRef AsmString) {
48 inlineAsmOut(C, AsmString, BB);
49 }
50
51};
52}
Reed Kotler783c7942013-05-10 22:25:39 +000053//
54// Return types that matter for hard float are:
55// float, double, complex float, and complex double
56//
57enum FPReturnVariant {
58 FRet, DRet, CFRet, CDRet, NoFPRet
59};
60
61//
62// Determine which FP return type this function has
63//
64static FPReturnVariant whichFPReturnVariant(Type *T) {
65 switch (T->getTypeID()) {
66 case Type::FloatTyID:
67 return FRet;
68 case Type::DoubleTyID:
69 return DRet;
70 case Type::StructTyID:
71 if (T->getStructNumElements() != 2)
72 break;
73 if ((T->getContainedType(0)->isFloatTy()) &&
74 (T->getContainedType(1)->isFloatTy()))
75 return CFRet;
76 if ((T->getContainedType(0)->isDoubleTy()) &&
77 (T->getContainedType(1)->isDoubleTy()))
78 return CDRet;
79 break;
80 default:
81 break;
82 }
83 return NoFPRet;
84}
85
86//
Reed Kotler2c4657d2013-05-14 02:00:24 +000087// Parameter type that matter are float, (float, float), (float, double),
88// double, (double, double), (double, float)
89//
90enum FPParamVariant {
91 FSig, FFSig, FDSig,
92 DSig, DDSig, DFSig, NoSig
93};
94
95// which floating point parameter signature variant we are dealing with
96//
97typedef Type::TypeID TypeID;
98const Type::TypeID FloatTyID = Type::FloatTyID;
99const Type::TypeID DoubleTyID = Type::DoubleTyID;
100
101static FPParamVariant whichFPParamVariantNeeded(Function &F) {
102 switch (F.arg_size()) {
103 case 0:
104 return NoSig;
105 case 1:{
106 TypeID ArgTypeID = F.getFunctionType()->getParamType(0)->getTypeID();
107 switch (ArgTypeID) {
108 case FloatTyID:
109 return FSig;
110 case DoubleTyID:
111 return DSig;
112 default:
113 return NoSig;
114 }
115 }
116 default: {
117 TypeID ArgTypeID0 = F.getFunctionType()->getParamType(0)->getTypeID();
118 TypeID ArgTypeID1 = F.getFunctionType()->getParamType(1)->getTypeID();
119 switch(ArgTypeID0) {
120 case FloatTyID: {
121 switch (ArgTypeID1) {
122 case FloatTyID:
123 return FFSig;
124 case DoubleTyID:
125 return FDSig;
126 default:
127 return FSig;
128 }
129 }
130 case DoubleTyID: {
131 switch (ArgTypeID1) {
132 case FloatTyID:
133 return DFSig;
134 case DoubleTyID:
135 return DDSig;
136 default:
137 return DSig;
138 }
139 }
140 default:
141 return NoSig;
142 }
143 }
144 }
145 llvm_unreachable("can't get here");
146}
147
148// Figure out if we need float point based on the function parameters.
149// We need to move variables in and/or out of floating point
150// registers because of the ABI
151//
152static bool needsFPStubFromParams(Function &F) {
153 if (F.arg_size() >=1) {
154 Type *ArgType = F.getFunctionType()->getParamType(0);
155 switch (ArgType->getTypeID()) {
156 case Type::FloatTyID:
157 case Type::DoubleTyID:
158 return true;
159 default:
160 break;
161 }
162 }
163 return false;
164}
165
166static bool needsFPReturnHelper(Function &F) {
167 Type* RetType = F.getReturnType();
168 return whichFPReturnVariant(RetType) != NoFPRet;
169}
170
Reed Kotler2500bd62013-12-18 23:57:48 +0000171static bool needsFPReturnHelper(const FunctionType &FT) {
172 Type* RetType = FT.getReturnType();
173 return whichFPReturnVariant(RetType) != NoFPRet;
174}
175
Reed Kotler2c4657d2013-05-14 02:00:24 +0000176static bool needsFPHelperFromSig(Function &F) {
177 return needsFPStubFromParams(F) || needsFPReturnHelper(F);
178}
179
180//
181// We swap between FP and Integer registers to allow Mips16 and Mips32 to
182// interoperate
183//
184
Reed Kotlercad47f02013-05-14 02:13:45 +0000185static void swapFPIntParams
186 (FPParamVariant PV, Module *M, InlineAsmHelper &IAH,
187 bool LE, bool ToFP) {
Reed Kotler2c4657d2013-05-14 02:00:24 +0000188 //LLVMContext &Context = M->getContext();
189 std::string MI = ToFP? "mtc1 ": "mfc1 ";
190 switch (PV) {
191 case FSig:
192 IAH.Out(MI + "$$4,$$f12");
193 break;
194 case FFSig:
195 IAH.Out(MI +"$$4,$$f12");
196 IAH.Out(MI + "$$5,$$f14");
197 break;
198 case FDSig:
199 IAH.Out(MI + "$$4,$$f12");
200 if (LE) {
201 IAH.Out(MI + "$$6,$$f14");
202 IAH.Out(MI + "$$7,$$f15");
203 } else {
204 IAH.Out(MI + "$$7,$$f14");
205 IAH.Out(MI + "$$6,$$f15");
206 }
207 break;
208 case DSig:
209 if (LE) {
210 IAH.Out(MI + "$$4,$$f12");
211 IAH.Out(MI + "$$5,$$f13");
212 } else {
213 IAH.Out(MI + "$$5,$$f12");
214 IAH.Out(MI + "$$4,$$f13");
215 }
216 break;
217 case DDSig:
218 if (LE) {
219 IAH.Out(MI + "$$4,$$f12");
220 IAH.Out(MI + "$$5,$$f13");
221 IAH.Out(MI + "$$6,$$f14");
222 IAH.Out(MI + "$$7,$$f15");
223 } else {
224 IAH.Out(MI + "$$5,$$f12");
225 IAH.Out(MI + "$$4,$$f13");
226 IAH.Out(MI + "$$7,$$f14");
227 IAH.Out(MI + "$$6,$$f15");
228 }
229 break;
230 case DFSig:
231 if (LE) {
232 IAH.Out(MI + "$$4,$$f12");
233 IAH.Out(MI + "$$5,$$f13");
234 } else {
235 IAH.Out(MI + "$$5,$$f12");
236 IAH.Out(MI + "$$4,$$f13");
237 }
238 IAH.Out(MI + "$$6,$$f14");
239 break;
240 case NoSig:
241 return;
242 }
243}
244//
245// Make sure that we know we already need a stub for this function.
246// Having called needsFPHelperFromSig
247//
Reed Kotler4cdaa7d2014-02-14 19:16:39 +0000248static void assureFPCallStub(Function &F, Module *M,
249 const MipsSubtarget &Subtarget) {
Reed Kotler2c4657d2013-05-14 02:00:24 +0000250 // for now we only need them for static relocation
Reed Kotler821e86f2013-05-14 06:00:01 +0000251 if (Subtarget.getRelocationModel() == Reloc::PIC_)
Reed Kotler2c4657d2013-05-14 02:00:24 +0000252 return;
253 LLVMContext &Context = M->getContext();
254 bool LE = Subtarget.isLittle();
255 std::string Name = F.getName();
256 std::string SectionName = ".mips16.call.fp." + Name;
Reed Kotler302ae6b2013-08-01 02:26:31 +0000257 std::string StubName = "__call_stub_fp_" + Name;
Reed Kotler2c4657d2013-05-14 02:00:24 +0000258 //
259 // see if we already have the stub
260 //
261 Function *FStub = M->getFunction(StubName);
262 if (FStub && !FStub->isDeclaration()) return;
263 FStub = Function::Create(F.getFunctionType(),
264 Function::InternalLinkage, StubName, M);
265 FStub->addFnAttr("mips16_fp_stub");
266 FStub->addFnAttr(llvm::Attribute::Naked);
Reed Kotler302ae6b2013-08-01 02:26:31 +0000267 FStub->addFnAttr(llvm::Attribute::NoInline);
Reed Kotler2c4657d2013-05-14 02:00:24 +0000268 FStub->addFnAttr(llvm::Attribute::NoUnwind);
269 FStub->addFnAttr("nomips16");
270 FStub->setSection(SectionName);
271 BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
272 InlineAsmHelper IAH(Context, BB);
Reed Kotler302ae6b2013-08-01 02:26:31 +0000273 IAH.Out(".set reorder");
Reed Kotler2c4657d2013-05-14 02:00:24 +0000274 FPReturnVariant RV = whichFPReturnVariant(FStub->getReturnType());
275 FPParamVariant PV = whichFPParamVariantNeeded(F);
276 swapFPIntParams(PV, M, IAH, LE, true);
277 if (RV != NoFPRet) {
278 IAH.Out("move $$18, $$31");
279 IAH.Out("jal " + Name);
280 } else {
281 IAH.Out("lui $$25,%hi(" + Name + ")");
282 IAH.Out("addiu $$25,$$25,%lo(" + Name + ")" );
283 }
284 switch (RV) {
285 case FRet:
286 IAH.Out("mfc1 $$2,$$f0");
287 break;
288 case DRet:
289 if (LE) {
290 IAH.Out("mfc1 $$2,$$f0");
291 IAH.Out("mfc1 $$3,$$f1");
292 } else {
293 IAH.Out("mfc1 $$3,$$f0");
294 IAH.Out("mfc1 $$2,$$f1");
295 }
296 break;
297 case CFRet:
298 if (LE) {
299 IAH.Out("mfc1 $$2,$$f0");
300 IAH.Out("mfc1 $$3,$$f2");
301 } else {
302 IAH.Out("mfc1 $$3,$$f0");
303 IAH.Out("mfc1 $$3,$$f2");
304 }
305 break;
306 case CDRet:
307 if (LE) {
308 IAH.Out("mfc1 $$4,$$f2");
309 IAH.Out("mfc1 $$5,$$f3");
310 IAH.Out("mfc1 $$2,$$f0");
311 IAH.Out("mfc1 $$3,$$f1");
312
313 } else {
314 IAH.Out("mfc1 $$5,$$f2");
315 IAH.Out("mfc1 $$4,$$f3");
316 IAH.Out("mfc1 $$3,$$f0");
317 IAH.Out("mfc1 $$2,$$f1");
318 }
319 break;
320 case NoFPRet:
321 break;
322 }
323 if (RV != NoFPRet)
324 IAH.Out("jr $$18");
325 else
326 IAH.Out("jr $$25");
327 new UnreachableInst(Context, BB);
328}
329
330//
Reed Kotler5fdadce2013-09-01 04:12:59 +0000331// Functions that are llvm intrinsics and don't need helpers.
Reed Kotlerd265e882013-08-11 21:30:27 +0000332//
Benjamin Kramerc9b7d472013-08-12 09:37:29 +0000333static const char *IntrinsicInline[] =
Reed Kotler5fdadce2013-09-01 04:12:59 +0000334 {"fabs",
Reed Kotler339c7412013-10-08 19:55:01 +0000335 "fabsf",
Reed Kotler5fdadce2013-09-01 04:12:59 +0000336 "llvm.ceil.f32", "llvm.ceil.f64",
337 "llvm.copysign.f32", "llvm.copysign.f64",
338 "llvm.cos.f32", "llvm.cos.f64",
339 "llvm.exp.f32", "llvm.exp.f64",
340 "llvm.exp2.f32", "llvm.exp2.f64",
341 "llvm.fabs.f32", "llvm.fabs.f64",
342 "llvm.floor.f32", "llvm.floor.f64",
343 "llvm.fma.f32", "llvm.fma.f64",
344 "llvm.log.f32", "llvm.log.f64",
345 "llvm.log10.f32", "llvm.log10.f64",
346 "llvm.nearbyint.f32", "llvm.nearbyint.f64",
347 "llvm.pow.f32", "llvm.pow.f64",
348 "llvm.powi.f32", "llvm.powi.f64",
349 "llvm.rint.f32", "llvm.rint.f64",
350 "llvm.round.f32", "llvm.round.f64",
351 "llvm.sin.f32", "llvm.sin.f64",
352 "llvm.sqrt.f32", "llvm.sqrt.f64",
353 "llvm.trunc.f32", "llvm.trunc.f64",
354 };
Reed Kotlerd265e882013-08-11 21:30:27 +0000355
Benjamin Kramerc9b7d472013-08-12 09:37:29 +0000356static bool isIntrinsicInline(Function *F) {
Benjamin Kramer502b9e12014-04-12 16:15:53 +0000357 return std::binary_search(std::begin(IntrinsicInline),
358 std::end(IntrinsicInline), F->getName());
Reed Kotlerd265e882013-08-11 21:30:27 +0000359}
360//
Reed Kotler783c7942013-05-10 22:25:39 +0000361// Returns of float, double and complex need to be handled with a helper
Reed Kotler515e9372013-05-16 02:17:42 +0000362// function.
Reed Kotler783c7942013-05-10 22:25:39 +0000363//
364static bool fixupFPReturnAndCall
365 (Function &F, Module *M, const MipsSubtarget &Subtarget) {
366 bool Modified = false;
367 LLVMContext &C = M->getContext();
368 Type *MyVoid = Type::getVoidTy(C);
369 for (Function::iterator BB = F.begin(), E = F.end(); BB != E; ++BB)
370 for (BasicBlock::iterator I = BB->begin(), E = BB->end();
371 I != E; ++I) {
372 Instruction &Inst = *I;
373 if (const ReturnInst *RI = dyn_cast<ReturnInst>(I)) {
374 Value *RVal = RI->getReturnValue();
375 if (!RVal) continue;
376 //
377 // If there is a return value and it needs a helper function,
378 // figure out which one and add a call before the actual
379 // return to this helper. The purpose of the helper is to move
380 // floating point values from their soft float return mapping to
381 // where they would have been mapped to in floating point registers.
382 //
383 Type *T = RVal->getType();
384 FPReturnVariant RV = whichFPReturnVariant(T);
385 if (RV == NoFPRet) continue;
386 static const char* Helper[NoFPRet] =
387 {"__mips16_ret_sf", "__mips16_ret_df", "__mips16_ret_sc",
388 "__mips16_ret_dc"};
389 const char *Name = Helper[RV];
390 AttributeSet A;
391 Value *Params[] = {RVal};
392 Modified = true;
393 //
394 // These helper functions have a different calling ABI so
395 // this __Mips16RetHelper indicates that so that later
396 // during call setup, the proper call lowering to the helper
397 // functions will take place.
398 //
399 A = A.addAttribute(C, AttributeSet::FunctionIndex,
400 "__Mips16RetHelper");
401 A = A.addAttribute(C, AttributeSet::FunctionIndex,
402 Attribute::ReadNone);
Reed Kotler302ae6b2013-08-01 02:26:31 +0000403 A = A.addAttribute(C, AttributeSet::FunctionIndex,
404 Attribute::NoInline);
Reed Kotler783c7942013-05-10 22:25:39 +0000405 Value *F = (M->getOrInsertFunction(Name, A, MyVoid, T, NULL));
406 CallInst::Create(F, Params, "", &Inst );
Reed Kotler2c4657d2013-05-14 02:00:24 +0000407 } else if (const CallInst *CI = dyn_cast<CallInst>(I)) {
Reed Kotler2500bd62013-12-18 23:57:48 +0000408 const Value* V = CI->getCalledValue();
409 const Type* T = 0;
410 if (V) T = V->getType();
411 const PointerType *PFT=0;
412 if (T) PFT = dyn_cast<PointerType>(T);
413 const FunctionType *FT=0;
414 if (PFT) FT = dyn_cast<FunctionType>(PFT->getElementType());
Reed Kotler0ff40012013-12-10 14:29:38 +0000415 Function *F_ = CI->getCalledFunction();
Reed Kotler2500bd62013-12-18 23:57:48 +0000416 if (FT && needsFPReturnHelper(*FT) &&
417 !(F_ && isIntrinsicInline(F_))) {
418 Modified=true;
419 F.addFnAttr("saveS2");
420 }
Reed Kotler0ff40012013-12-10 14:29:38 +0000421 if (F_ && !isIntrinsicInline(F_)) {
Reed Kotler2c4657d2013-05-14 02:00:24 +0000422 // pic mode calls are handled by already defined
423 // helper functions
Reed Kotler0ff40012013-12-10 14:29:38 +0000424 if (needsFPReturnHelper(*F_)) {
Reed Kotler2c4657d2013-05-14 02:00:24 +0000425 Modified=true;
Reed Kotler0ff40012013-12-10 14:29:38 +0000426 F.addFnAttr("saveS2");
427 }
428 if (Subtarget.getRelocationModel() != Reloc::PIC_ ) {
429 if (needsFPHelperFromSig(*F_)) {
430 assureFPCallStub(*F_, M, Subtarget);
431 Modified=true;
432 }
Reed Kotler2c4657d2013-05-14 02:00:24 +0000433 }
434 }
Reed Kotler783c7942013-05-10 22:25:39 +0000435 }
436 }
437 return Modified;
438}
439
Reed Kotler515e9372013-05-16 02:17:42 +0000440static void createFPFnStub(Function *F, Module *M, FPParamVariant PV,
441 const MipsSubtarget &Subtarget ) {
442 bool PicMode = Subtarget.getRelocationModel() == Reloc::PIC_;
443 bool LE = Subtarget.isLittle();
444 LLVMContext &Context = M->getContext();
445 std::string Name = F->getName();
446 std::string SectionName = ".mips16.fn." + Name;
447 std::string StubName = "__fn_stub_" + Name;
Reed Kotlera6ce7972013-09-25 20:58:50 +0000448 std::string LocalName = "$$__fn_local_" + Name;
Reed Kotler515e9372013-05-16 02:17:42 +0000449 Function *FStub = Function::Create
450 (F->getFunctionType(),
Reed Kotler302ae6b2013-08-01 02:26:31 +0000451 Function::InternalLinkage, StubName, M);
Reed Kotler515e9372013-05-16 02:17:42 +0000452 FStub->addFnAttr("mips16_fp_stub");
453 FStub->addFnAttr(llvm::Attribute::Naked);
454 FStub->addFnAttr(llvm::Attribute::NoUnwind);
Reed Kotler302ae6b2013-08-01 02:26:31 +0000455 FStub->addFnAttr(llvm::Attribute::NoInline);
Reed Kotler515e9372013-05-16 02:17:42 +0000456 FStub->addFnAttr("nomips16");
457 FStub->setSection(SectionName);
458 BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub);
459 InlineAsmHelper IAH(Context, BB);
460 IAH.Out(" .set macro");
461 if (PicMode) {
462 IAH.Out(".set noreorder");
Reed Kotlera6ce7972013-09-25 20:58:50 +0000463 IAH.Out(".cpload $$25");
Reed Kotler515e9372013-05-16 02:17:42 +0000464 IAH.Out(".set reorder");
465 IAH.Out(".reloc 0,R_MIPS_NONE," + Name);
466 IAH.Out("la $$25," + LocalName);
467 }
Reed Kotler78fb2912013-09-21 01:37:52 +0000468 else {
469 IAH.Out(".set reorder");
Reed Kotlera6ce7972013-09-25 20:58:50 +0000470 IAH.Out("la $$25," + Name);
Reed Kotler78fb2912013-09-21 01:37:52 +0000471 }
Reed Kotler515e9372013-05-16 02:17:42 +0000472 swapFPIntParams(PV, M, IAH, LE, false);
473 IAH.Out("jr $$25");
474 IAH.Out(LocalName + " = " + Name);
475 new UnreachableInst(FStub->getContext(), BB);
476}
477
Reed Kotlerc03807a2013-08-30 19:40:56 +0000478//
479// remove the use-soft-float attribute
480//
481static void removeUseSoftFloat(Function &F) {
482 AttributeSet A;
483 DEBUG(errs() << "removing -use-soft-float\n");
484 A = A.addAttribute(F.getContext(), AttributeSet::FunctionIndex,
485 "use-soft-float", "false");
486 F.removeAttributes(AttributeSet::FunctionIndex, A);
487 if (F.hasFnAttribute("use-soft-float")) {
488 DEBUG(errs() << "still has -use-soft-float\n");
489 }
490 F.addAttributes(AttributeSet::FunctionIndex, A);
491}
492
Reed Kotler783c7942013-05-10 22:25:39 +0000493namespace llvm {
494
495//
496// This pass only makes sense when the underlying chip has floating point but
497// we are compiling as mips16.
498// For all mips16 functions (that are not stubs we have already generated), or
499// declared via attributes as nomips16, we must:
500// 1) fixup all returns of float, double, single and double complex
501// by calling a helper function before the actual return.
Reed Kotler4cdaa7d2014-02-14 19:16:39 +0000502// 2) generate helper functions (stubs) that can be called by mips32
503// functions that will move parameters passed normally passed in
504// floating point
Reed Kotler515e9372013-05-16 02:17:42 +0000505// registers the soft float equivalents.
Reed Kotler783c7942013-05-10 22:25:39 +0000506// 3) in the case of static relocation, generate helper functions so that
507// mips16 functions can call extern functions of unknown type (mips16 or
Reed Kotler515e9372013-05-16 02:17:42 +0000508// mips32).
Reed Kotler783c7942013-05-10 22:25:39 +0000509// 4) TBD. For pic, calls to extern functions of unknown type are handled by
510// predefined helper functions in libc but this work is currently done
511// during call lowering but it should be moved here in the future.
512//
513bool Mips16HardFloat::runOnModule(Module &M) {
514 DEBUG(errs() << "Run on Module Mips16HardFloat\n");
515 bool Modified = false;
516 for (Module::iterator F = M.begin(), E = M.end(); F != E; ++F) {
Reed Kotlerc03807a2013-08-30 19:40:56 +0000517 if (F->hasFnAttribute("nomips16") &&
518 F->hasFnAttribute("use-soft-float")) {
519 removeUseSoftFloat(*F);
520 continue;
521 }
Reed Kotler783c7942013-05-10 22:25:39 +0000522 if (F->isDeclaration() || F->hasFnAttribute("mips16_fp_stub") ||
523 F->hasFnAttribute("nomips16")) continue;
524 Modified |= fixupFPReturnAndCall(*F, &M, Subtarget);
Reed Kotler515e9372013-05-16 02:17:42 +0000525 FPParamVariant V = whichFPParamVariantNeeded(*F);
526 if (V != NoSig) {
527 Modified = true;
528 createFPFnStub(F, &M, V, Subtarget);
529 }
Reed Kotler783c7942013-05-10 22:25:39 +0000530 }
531 return Modified;
532}
533
534char Mips16HardFloat::ID = 0;
535
536}
537
538ModulePass *llvm::createMips16HardFloat(MipsTargetMachine &TM) {
539 return new Mips16HardFloat(TM);
540}
541