The next phase of Mips16 hard float implementation.
authorReed Kotler <rkotler@mips.com>
Thu, 24 Jan 2013 04:24:02 +0000 (04:24 +0000)
committerReed Kotler <rkotler@mips.com>
Thu, 24 Jan 2013 04:24:02 +0000 (04:24 +0000)
Allow Mips16 routines to call Mips32 routines that have abi requirements
that either arguments or return values are passed in floating point
registers. This handles only the pic case. We have not done non pic
for Mips16 yet in any form.

The libm functions are Mips32, so with this addition we have a complete
Mips16 hard float implementation.

We still are not able to complete mix Mip16 and Mips32 with hard float.
That will be the next phase which will have several steps. For Mips32
to freely call Mips16 some stub functions must be created.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@173320 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Target/Mips/MipsISelLowering.cpp
lib/Target/Mips/MipsISelLowering.h
test/CodeGen/Mips/hf16_1.ll [new file with mode: 0644]

index e148da139a6c4ae3f48a7518ac3da684e2ba8712..a23ffd7288a89ea84d4765f13784e33f59565a5c 100644 (file)
@@ -11,8 +11,8 @@
 // selection DAG.
 //
 //===----------------------------------------------------------------------===//
-
 #define DEBUG_TYPE "mips-lower"
+#include <set>
 #include "MipsISelLowering.h"
 #include "InstPrinter/MipsInstPrinter.h"
 #include "MCTargetDesc/MipsBaseInfo.h"
@@ -205,39 +205,64 @@ const char *MipsTargetLowering::getTargetNodeName(unsigned Opcode) const {
   }
 }
 
+namespace {
+  struct eqstr {
+    bool operator()(const char *s1, const char *s2) const
+    {
+      return strcmp(s1, s2) == 0;
+    }
+  };
+
+  std::set<const char*, eqstr> noHelperNeeded;
+
+  const char* addToNoHelperNeeded(const char* s) {
+    noHelperNeeded.insert(s);
+    return s;
+  }
+
+}
+
 void MipsTargetLowering::setMips16HardFloatLibCalls() {
-  setLibcallName(RTLIB::ADD_F32, "__mips16_addsf3");
-  setLibcallName(RTLIB::ADD_F64, "__mips16_adddf3");
-  setLibcallName(RTLIB::SUB_F32, "__mips16_subsf3");
-  setLibcallName(RTLIB::SUB_F64, "__mips16_subdf3");
-  setLibcallName(RTLIB::MUL_F32, "__mips16_mulsf3");
-  setLibcallName(RTLIB::MUL_F64, "__mips16_muldf3");
-  setLibcallName(RTLIB::DIV_F32, "__mips16_divsf3");
-  setLibcallName(RTLIB::DIV_F64, "__mips16_divdf3");
-  setLibcallName(RTLIB::FPEXT_F32_F64, "__mips16_extendsfdf2");
-  setLibcallName(RTLIB::FPROUND_F64_F32, "__mips16_truncdfsf2");
-  setLibcallName(RTLIB::FPTOSINT_F32_I32, "__mips16_fix_truncsfsi");
-  setLibcallName(RTLIB::FPTOSINT_F64_I32, "__mips16_fix_truncdfsi");
-  setLibcallName(RTLIB::SINTTOFP_I32_F32, "__mips16_floatsisf");
-  setLibcallName(RTLIB::SINTTOFP_I32_F64, "__mips16_floatsidf");
-  setLibcallName(RTLIB::UINTTOFP_I32_F32, "__mips16_floatunsisf");
-  setLibcallName(RTLIB::UINTTOFP_I32_F64, "__mips16_floatunsidf");
-  setLibcallName(RTLIB::OEQ_F32, "__mips16_eqsf2");
-  setLibcallName(RTLIB::OEQ_F64, "__mips16_eqdf2");
-  setLibcallName(RTLIB::UNE_F32, "__mips16_nesf2");
-  setLibcallName(RTLIB::UNE_F64, "__mips16_nedf2");
-  setLibcallName(RTLIB::OGE_F32, "__mips16_gesf2");
-  setLibcallName(RTLIB::OGE_F64, "__mips16_gedf2");
-  setLibcallName(RTLIB::OLT_F32, "__mips16_ltsf2");
-  setLibcallName(RTLIB::OLT_F64, "__mips16_ltdf2");
-  setLibcallName(RTLIB::OLE_F32, "__mips16_lesf2");
-  setLibcallName(RTLIB::OLE_F64, "__mips16_ledf2");
-  setLibcallName(RTLIB::OGT_F32, "__mips16_gtsf2");
-  setLibcallName(RTLIB::OGT_F64, "__mips16_gtdf2");
-  setLibcallName(RTLIB::UO_F32, "__mips16_unordsf2");
-  setLibcallName(RTLIB::UO_F64, "__mips16_unorddf2");
-  setLibcallName(RTLIB::O_F32, "__mips16_unordsf2");
-  setLibcallName(RTLIB::O_F64, "__mips16_unorddf2");
+  setLibcallName(RTLIB::ADD_F32, addToNoHelperNeeded("__mips16_addsf3"));
+  setLibcallName(RTLIB::ADD_F64, addToNoHelperNeeded("__mips16_adddf3"));
+  setLibcallName(RTLIB::SUB_F32, addToNoHelperNeeded("__mips16_subsf3"));
+  setLibcallName(RTLIB::SUB_F64, addToNoHelperNeeded("__mips16_subdf3"));
+  setLibcallName(RTLIB::MUL_F32, addToNoHelperNeeded("__mips16_mulsf3"));
+  setLibcallName(RTLIB::MUL_F64, addToNoHelperNeeded("__mips16_muldf3"));
+  setLibcallName(RTLIB::DIV_F32, addToNoHelperNeeded("__mips16_divsf3"));
+  setLibcallName(RTLIB::DIV_F64, addToNoHelperNeeded("__mips16_divdf3"));
+  setLibcallName(RTLIB::FPEXT_F32_F64,
+                 addToNoHelperNeeded("__mips16_extendsfdf2"));
+  setLibcallName(RTLIB::FPROUND_F64_F32,
+                 addToNoHelperNeeded("__mips16_truncdfsf2"));
+  setLibcallName(RTLIB::FPTOSINT_F32_I32,
+                 addToNoHelperNeeded("__mips16_fix_truncsfsi"));
+  setLibcallName(RTLIB::FPTOSINT_F64_I32,
+                 addToNoHelperNeeded("__mips16_fix_truncdfsi"));
+  setLibcallName(RTLIB::SINTTOFP_I32_F32,
+                 addToNoHelperNeeded("__mips16_floatsisf"));
+  setLibcallName(RTLIB::SINTTOFP_I32_F64,
+                 addToNoHelperNeeded("__mips16_floatsidf"));
+  setLibcallName(RTLIB::UINTTOFP_I32_F32,
+                 addToNoHelperNeeded("__mips16_floatunsisf"));
+  setLibcallName(RTLIB::UINTTOFP_I32_F64,
+                 addToNoHelperNeeded("__mips16_floatunsidf"));
+  setLibcallName(RTLIB::OEQ_F32, addToNoHelperNeeded("__mips16_eqsf2"));
+  setLibcallName(RTLIB::OEQ_F64, addToNoHelperNeeded("__mips16_eqdf2"));
+  setLibcallName(RTLIB::UNE_F32, addToNoHelperNeeded("__mips16_nesf2"));
+  setLibcallName(RTLIB::UNE_F64, addToNoHelperNeeded("__mips16_nedf2"));
+  setLibcallName(RTLIB::OGE_F32, addToNoHelperNeeded("__mips16_gesf2"));
+  setLibcallName(RTLIB::OGE_F64, addToNoHelperNeeded("__mips16_gedf2"));
+  setLibcallName(RTLIB::OLT_F32, addToNoHelperNeeded("__mips16_ltsf2"));
+  setLibcallName(RTLIB::OLT_F64, addToNoHelperNeeded("__mips16_ltdf2"));
+  setLibcallName(RTLIB::OLE_F32, addToNoHelperNeeded("__mips16_lesf2"));
+  setLibcallName(RTLIB::OLE_F64, addToNoHelperNeeded("__mips16_ledf2"));
+  setLibcallName(RTLIB::OGT_F32, addToNoHelperNeeded("__mips16_gtsf2"));
+  setLibcallName(RTLIB::OGT_F64, addToNoHelperNeeded("__mips16_gtdf2"));
+  setLibcallName(RTLIB::UO_F32, addToNoHelperNeeded("__mips16_unordsf2"));
+  setLibcallName(RTLIB::UO_F64, addToNoHelperNeeded("__mips16_unorddf2"));
+  setLibcallName(RTLIB::O_F32, addToNoHelperNeeded("__mips16_unordsf2"));
+  setLibcallName(RTLIB::O_F64, addToNoHelperNeeded("__mips16_unorddf2"));
 }
 
 MipsTargetLowering::
@@ -2754,6 +2779,155 @@ MipsTargetLowering::passArgOnStack(SDValue StackPtr, unsigned Offset,
                       /*isVolatile=*/ true, false, 0);
 }
 
+//
+// The Mips16 hard float is a crazy quilt inherited from gcc. I have a much
+// cleaner way to do all of this but it will have to wait until the traditional
+// gcc mechanism is completed.
+//
+// For Pic, in order for Mips16 code to call Mips32 code which according the abi
+// have either arguments or returned values placed in floating point registers,
+// we use a set of helper functions. (This includes functions which return type
+//  complex which on Mips are returned in a pair of floating point registers).
+//
+// This is an encoding that we inherited from gcc.
+// In Mips traditional O32, N32 ABI, floating point numbers are passed in
+// floating point argument registers 1,2 only when the first and optionally
+// the second arguments are float (sf) or double (df).
+// For Mips16 we are only concerned with the situations where floating point
+// arguments are being passed in floating point registers by the ABI, because
+// Mips16 mode code cannot execute floating point instructions to load those
+// values and hence helper functions are needed.
+// The possibilities are (), (sf), (sf, sf), (sf, df), (df), (df, sf), (df, df)
+// the helper function suffixs for these are:
+//                        0,  1,    5,        9,         2,   6,        10
+// this suffix can then be calculated as follows:
+// for a given argument Arg:
+//     Arg1x, Arg2x = 1 :  Arg is sf
+//                    2 :  Arg is df
+//                    0:   Arg is neither sf or df
+// So this stub is the string for number Arg1x + Arg2x*4.
+// However not all numbers between 0 and 10 are possible, we check anyway and
+// assert if the impossible exists.
+//
+
+unsigned int MipsTargetLowering::getMips16HelperFunctionStubNumber
+  (ArgListTy &Args) const {
+  unsigned int resultNum = 0;
+  if (Args.size() >= 1) {
+    Type *t = Args[0].Ty;
+    if (t->isFloatTy()) {
+      resultNum = 1;
+    }
+    else if (t->isDoubleTy()) {
+      resultNum = 2;
+    }
+  }
+  if (resultNum) {
+    if (Args.size() >=2) {
+      Type *t = Args[1].Ty;
+      if (t->isFloatTy()) {
+        resultNum += 4;
+      }
+      else if (t->isDoubleTy()) {
+        resultNum += 8;
+      }
+    }
+  }
+  return resultNum;
+}
+
+//
+// prefixs are attached to stub numbers depending on the return type .
+// return type: float  sf_
+//              double df_
+//              single complex sc_
+//              double complext dc_
+//              others  NO PREFIX
+//
+//
+// The full name of a helper function is__mips16_call_stub +
+//    return type dependent prefix + stub number
+//
+//
+// This is something that probably should be in a different source file and
+// perhaps done differently but my main purpose is to not waste runtime
+// on something that we can enumerate in the source. Another possibility is
+// to have a python script to generate these mapping tables. This will do
+// for now. There are a whole series of helper function mapping arrays, one
+// for each return type class as outlined above. There there are 11 possible
+//  entries. Ones with 0 are ones which should never be selected
+//
+// All the arrays are similar except for ones which return neither
+// sf, df, sc, dc, in which only care about ones which have sf or df as a
+// first parameter.
+//
+#define P_ "__mips16_call_stub_"
+#define MAX_STUB_NUMBER 10
+#define T1 P "1", P "2", 0, 0, P "5", P "6", 0, 0, P "9", P "10"
+#define T P "0" , T1
+#define P P_
+static char const * vMips16Helper[MAX_STUB_NUMBER+1] =
+  {0, T1 };
+#undef P
+#define P P_ "sf_"
+static char const * sfMips16Helper[MAX_STUB_NUMBER+1] =
+  { T };
+#undef P
+#define P P_ "df_"
+static char const * dfMips16Helper[MAX_STUB_NUMBER+1] =
+  { T };
+#undef P
+#define P P_ "sc_"
+static char const * scMips16Helper[MAX_STUB_NUMBER+1] =
+  { T };
+#undef P
+#define P P_ "dc_"
+static char const * dcMips16Helper[MAX_STUB_NUMBER+1] =
+  { T };
+#undef P
+#undef P_
+
+
+const char* MipsTargetLowering::
+  getMips16HelperFunction
+    (Type* RetTy, ArgListTy &Args, bool &needHelper) const {
+  const unsigned int maxStubNum = 10;
+  const bool validStubNum[maxStubNum+1] =
+    {true, true, true, false, false, true, true, false, false, true, true};
+  const unsigned int stubNum = getMips16HelperFunctionStubNumber(Args);
+  assert(stubNum <= maxStubNum);
+  assert (validStubNum[stubNum]);
+  const char *result;
+  if (RetTy->isFloatTy()) {
+    result = sfMips16Helper[stubNum];
+  }
+  else if (RetTy ->isDoubleTy()) {
+    result = dfMips16Helper[stubNum];
+  }
+  else if (RetTy->isStructTy()) {
+    // check if it's complex
+    if (RetTy->getNumContainedTypes() == 2) {
+      if ((RetTy->getContainedType(0)->isFloatTy()) &&
+          (RetTy->getContainedType(1)->isFloatTy())) {
+        result = scMips16Helper[stubNum];
+      }
+      else if ((RetTy->getContainedType(0)->isDoubleTy()) &&
+               (RetTy->getContainedType(1)->isDoubleTy())) {
+        result = dcMips16Helper[stubNum];
+      }
+    }
+  }
+  else {
+    if (stubNum == 0) {
+      needHelper = false;
+      return "";
+    }
+    result = vMips16Helper[stubNum];
+  }
+  needHelper = true;
+  return result;
+}
+
 /// LowerCall - functions arguments are copied from virtual regs to
 /// (physical regs)/(stack frame), CALLSEQ_START and CALLSEQ_END are emitted.
 SDValue
@@ -2770,6 +2944,26 @@ MipsTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
   CallingConv::ID CallConv              = CLI.CallConv;
   bool isVarArg                         = CLI.IsVarArg;
 
+  const char* mips16HelperFunction = 0;
+  bool needMips16Helper = false;
+
+  if (Subtarget->inMips16Mode() && getTargetMachine().Options.UseSoftFloat &&
+      Mips16HardFloat) {
+    //
+    // currently we don't have symbols tagged with the mips16 or mips32
+    // qualifier so we will assume that we don't know what kind it is.
+    // and generate the helper
+    //
+    bool lookupHelper = true;
+    if (ExternalSymbolSDNode *S = dyn_cast<ExternalSymbolSDNode>(Callee)) {
+      if (noHelperNeeded.find(S->getSymbol()) != noHelperNeeded.end()) {
+        lookupHelper = false;
+      }
+    }
+    if (lookupHelper) mips16HelperFunction =
+      getMips16HelperFunction(CLI.RetTy, CLI.Args, needMips16Helper);
+
+  }
   MachineFunction &MF = DAG.getMachineFunction();
   MachineFrameInfo *MFI = MF.getFrameInfo();
   const TargetFrameLowering *TFL = MF.getTarget().getFrameLowering();
@@ -2934,10 +3128,19 @@ MipsTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
   // -reloction-model=pic or it is an indirect call.
   if (IsPICCall || !GlobalOrExternal) {
     unsigned T9Reg = IsN64 ? Mips::T9_64 : Mips::T9;
-    RegsToPass.push_front(std::make_pair(T9Reg, Callee));
+    unsigned V0Reg = Mips::V0;
+    if (needMips16Helper) {
+      RegsToPass.push_front(std::make_pair(V0Reg, Callee));
+      JumpTarget = DAG.getExternalSymbol(
+        mips16HelperFunction, getPointerTy());
+      JumpTarget = getAddrGlobal(JumpTarget, DAG, MipsII::MO_GOT);
+    }
+    else {
+      RegsToPass.push_front(std::make_pair(T9Reg, Callee));
 
-    if (!Subtarget->inMips16Mode())
-      JumpTarget = SDValue();
+      if (!Subtarget->inMips16Mode())
+        JumpTarget = SDValue();
+    }
   }
 
   // Insert node "GP copy globalreg" before call to function.
index deb6ad0463437ace151d2d0399209d1d6bfbda1d..c682c89f87cf288f8511fefaa949a2fc87f19785 100644 (file)
@@ -21,6 +21,7 @@
 #include "llvm/CodeGen/SelectionDAG.h"
 #include "llvm/Target/TargetLowering.h"
 #include <deque>
+#include <string>
 
 namespace llvm {
   namespace MipsISD {
@@ -177,6 +178,12 @@ namespace llvm {
 
     void setMips16HardFloatLibCalls();
 
+    unsigned int
+      getMips16HelperFunctionStubNumber(ArgListTy &Args) const;
+
+    const char *getMips16HelperFunction
+      (Type* RetTy, ArgListTy &Args, bool &needHelper) const;
+
     /// ByValArgInfo - Byval argument information.
     struct ByValArgInfo {
       unsigned FirstIdx; // Index of the first register used.
diff --git a/test/CodeGen/Mips/hf16_1.ll b/test/CodeGen/Mips/hf16_1.ll
new file mode 100644 (file)
index 0000000..c7454ee
--- /dev/null
@@ -0,0 +1,256 @@
+; RUN: llc  -march=mipsel -mcpu=mips16 -relocation-model=pic -soft-float -mips16-hard-float -O3 < %s | FileCheck %s -check-prefix=1
+; RUN: llc  -march=mipsel -mcpu=mips16 -relocation-model=pic -soft-float -mips16-hard-float -O3 < %s | FileCheck %s -check-prefix=2
+
+
+@x = common global float 0.000000e+00, align 4
+@xd = common global double 0.000000e+00, align 8
+@y = common global float 0.000000e+00, align 4
+@yd = common global double 0.000000e+00, align 8
+@xy = common global { float, float } zeroinitializer, align 4
+@xyd = common global { double, double } zeroinitializer, align 8
+
+define void @foo() nounwind {
+entry:
+  %0 = load float* @x, align 4
+  call void @v_sf(float %0)
+  %1 = load double* @xd, align 8
+  call void @v_df(double %1)
+  %2 = load float* @x, align 4
+  %3 = load float* @y, align 4
+  call void @v_sf_sf(float %2, float %3)
+  %4 = load double* @xd, align 8
+  %5 = load float* @x, align 4
+  call void @v_df_sf(double %4, float %5)
+  %6 = load double* @xd, align 8
+  %7 = load double* @yd, align 8
+  call void @v_df_df(double %6, double %7)
+  %call = call float @sf_v()
+  %8 = load float* @x, align 4
+  %call1 = call float @sf_sf(float %8)
+  %9 = load double* @xd, align 8
+  %call2 = call float @sf_df(double %9)
+  %10 = load float* @x, align 4
+  %11 = load float* @y, align 4
+  %call3 = call float @sf_sf_sf(float %10, float %11)
+  %12 = load double* @xd, align 8
+  %13 = load float* @x, align 4
+  %call4 = call float @sf_df_sf(double %12, float %13)
+  %14 = load double* @xd, align 8
+  %15 = load double* @yd, align 8
+  %call5 = call float @sf_df_df(double %14, double %15)
+  %call6 = call double @df_v()
+  %16 = load float* @x, align 4
+  %call7 = call double @df_sf(float %16)
+  %17 = load double* @xd, align 8
+  %call8 = call double @df_df(double %17)
+  %18 = load float* @x, align 4
+  %19 = load float* @y, align 4
+  %call9 = call double @df_sf_sf(float %18, float %19)
+  %20 = load double* @xd, align 8
+  %21 = load float* @x, align 4
+  %call10 = call double @df_df_sf(double %20, float %21)
+  %22 = load double* @xd, align 8
+  %23 = load double* @yd, align 8
+  %call11 = call double @df_df_df(double %22, double %23)
+  %call12 = call { float, float } @sc_v()
+  %24 = extractvalue { float, float } %call12, 0
+  %25 = extractvalue { float, float } %call12, 1
+  %26 = load float* @x, align 4
+  %call13 = call { float, float } @sc_sf(float %26)
+  %27 = extractvalue { float, float } %call13, 0
+  %28 = extractvalue { float, float } %call13, 1
+  %29 = load double* @xd, align 8
+  %call14 = call { float, float } @sc_df(double %29)
+  %30 = extractvalue { float, float } %call14, 0
+  %31 = extractvalue { float, float } %call14, 1
+  %32 = load float* @x, align 4
+  %33 = load float* @y, align 4
+  %call15 = call { float, float } @sc_sf_sf(float %32, float %33)
+  %34 = extractvalue { float, float } %call15, 0
+  %35 = extractvalue { float, float } %call15, 1
+  %36 = load double* @xd, align 8
+  %37 = load float* @x, align 4
+  %call16 = call { float, float } @sc_df_sf(double %36, float %37)
+  %38 = extractvalue { float, float } %call16, 0
+  %39 = extractvalue { float, float } %call16, 1
+  %40 = load double* @xd, align 8
+  %41 = load double* @yd, align 8
+  %call17 = call { float, float } @sc_df_df(double %40, double %41)
+  %42 = extractvalue { float, float } %call17, 0
+  %43 = extractvalue { float, float } %call17, 1
+  %call18 = call { double, double } @dc_v()
+  %44 = extractvalue { double, double } %call18, 0
+  %45 = extractvalue { double, double } %call18, 1
+  %46 = load float* @x, align 4
+  %call19 = call { double, double } @dc_sf(float %46)
+  %47 = extractvalue { double, double } %call19, 0
+  %48 = extractvalue { double, double } %call19, 1
+  %49 = load double* @xd, align 8
+  %call20 = call { double, double } @dc_df(double %49)
+  %50 = extractvalue { double, double } %call20, 0
+  %51 = extractvalue { double, double } %call20, 1
+  %52 = load float* @x, align 4
+  %53 = load float* @y, align 4
+  %call21 = call { double, double } @dc_sf_sf(float %52, float %53)
+  %54 = extractvalue { double, double } %call21, 0
+  %55 = extractvalue { double, double } %call21, 1
+  %56 = load double* @xd, align 8
+  %57 = load float* @x, align 4
+  %call22 = call { double, double } @dc_df_sf(double %56, float %57)
+  %58 = extractvalue { double, double } %call22, 0
+  %59 = extractvalue { double, double } %call22, 1
+  %60 = load double* @xd, align 8
+  %61 = load double* @yd, align 8
+  %call23 = call { double, double } @dc_df_df(double %60, double %61)
+  %62 = extractvalue { double, double } %call23, 0
+  %63 = extractvalue { double, double } %call23, 1
+  ret void
+}
+
+declare void @v_sf(float)
+
+declare void @v_df(double)
+
+declare void @v_sf_sf(float, float)
+
+declare void @v_df_sf(double, float)
+
+declare void @v_df_df(double, double)
+
+declare float @sf_v()
+
+declare float @sf_sf(float)
+
+declare float @sf_df(double)
+
+declare float @sf_sf_sf(float, float)
+
+declare float @sf_df_sf(double, float)
+
+declare float @sf_df_df(double, double)
+
+declare double @df_v()
+
+declare double @df_sf(float)
+
+declare double @df_df(double)
+
+declare double @df_sf_sf(float, float)
+
+declare double @df_df_sf(double, float)
+
+declare double @df_df_df(double, double)
+
+declare { float, float } @sc_v()
+
+declare { float, float } @sc_sf(float)
+
+declare { float, float } @sc_df(double)
+
+declare { float, float } @sc_sf_sf(float, float)
+
+declare { float, float } @sc_df_sf(double, float)
+
+declare { float, float } @sc_df_df(double, double)
+
+declare { double, double } @dc_v()
+
+declare { double, double } @dc_sf(float)
+
+declare { double, double } @dc_df(double)
+
+declare { double, double } @dc_sf_sf(float, float)
+
+declare { double, double } @dc_df_sf(double, float)
+
+declare { double, double } @dc_df_df(double, double)
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_1)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(v_sf)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_2)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(v_df)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_5)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(v_sf_sf)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_6)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(v_df_sf)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_10)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(v_df_df)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_sf_0)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(sf_v)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_sf_1)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(sf_sf)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_sf_2)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(sf_df)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_sf_5)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(sf_sf_sf)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_sf_6)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(sf_df_sf)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_sf_10)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(sf_df_df)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_df_0)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(df_v)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_df_1)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(df_sf)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_df_2)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(df_df)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_df_5)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(df_sf_sf)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_df_6)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(df_df_sf)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_df_10)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(df_df_df)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_sc_0)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(sc_v)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_sc_1)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(sc_sf)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_sc_2)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(sc_df)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_sc_5)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(sc_sf_sf)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_sc_6)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(sc_df_sf)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_sc_10)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(sc_df_df)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_dc_0)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(dc_v)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_dc_1)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(dc_sf)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_dc_2)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(dc_df)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_dc_5)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(dc_sf_sf)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_dc_6)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(dc_df_sf)(${{[0-9]+}})
+
+; 1:   lw      ${{[0-9]+}}, %got(__mips16_call_stub_dc_10)(${{[0-9]+}})
+; 2:   lw      ${{[0-9]+}}, %call16(dc_df_df)(${{[0-9]+}})
+
+
+