mips16 hard float puzzle

I'm working on mips16 hard float which at a first approximation is just soft float but calls different library routines. Those different library routines are just an implementation (in mips32 mode) of soft float using mips32 hardware instructions. This part is already done. (mips16 mode has no floating point instructions).

The next level of this that I am working on now is the ability to call mips32 mode functions from mips16. This is needed for example to call the math library (sin, cos), because those library
routines are mips32 functions.

This has an interesting problem.

The mips16 functions do not know what they are calling when it's an external function; i.e. it could be a mips32 or mips16.

On mips32, floating point arguments, if they are argument 1 and argument 2, are passed in floating point registers. If the return type is floating point, then it is returned in a floating point register.

On mips 16, it is doing soft float essentially so the arguments are passed in integer registers.

The way gcc mips16 does this is to call helper functions which do the actual call to the external function. The helper functions copy arg 1 and 2 into fp arg 1 and fp arg 2 and then do the actual call. So whether you are calling mips16 or mips32 from that point on does not matter.

I'm simplifiying this a bit and you have to think about the mips abi to understand how this all works but it does.

The question I have is how to find the original prototype.

In order to know if I need to call a helper function, and which helper function to call, I need to know the return type and type of arguments 1 and 2. I need to know this pre soft float lowering.

From what I can see, in the beginning of ISelLowering code, the DAG will still reflect the original prototype. If I do a DAG.viewGraph() there in the debugger, it knows about the proper prototype (pre soft float lowering).

It would have been cool to get the function class that corresponds to this call and get the arguments that way but I don't see how I can get it.

Is looking at the DAG the only (or best) way to do find the function prototype?

I would be happy to add something to lowering calls methods but it was not clear how to do this too.



You have to do this as part of call lowering: once the call is
lowered, the type information is lost permanently. It's not
conceptually difficult to do as part of call lowering, though: in the
MIPS-specific call lowering code, if you detect that a call requires
an argument in an FP register, you just need to generate a call to the
relevant helper rather than the function itself.


So just read the DAG in the beginning of MIPS-specific call lowering before it gets modified?

Take a look at CCValAssign etc. in MipsTargetLowering::LowerCall.
(The DAG node for the callee doesn't have the necessary information in
the general case, even though it might appear to work in simple


I'm not sure if I understood you.

For Mips hard float, we also have the soft float option in llc.

Right now we pass:
-mips16-hard-float -soft-float

To llc.

So the call is going to get changed to use integer types but I need to be able to find what is was before that change takes place.

I tried a bunch of different cases, and in each case, if I do a "call DAG.viewGraph()" in gdb, the proper function prototype is being displayed.

For example:

/home/rkotler/llvm/install/bin/llc -mcpu=mips16 hf16_2.ll -march=mipsel -relocation-model=pic -o hf16_2.s -O3 -mips16-hard-float -soft-float

hf16_2.c (96 Bytes)

dag.test.dot (566 Bytes)

If you need to be able to put values into floating-point registers,
your architecture is not soft-float. If the call expects the operand
in a floating-point register, the call analysis code needs to know
that we're dealing with a floating point value, and the code that
generates the call needs to correctly put it in that register; whether
there is a Mips16 instruction to perform that operation is irrelevant.
If the CCValAssign thinks that a floating-point argument is actually
an integer, you can't recover at that point. (It's possible that the
call analysis code needs to be enhanced to make this work.)


Try something like the following:

float f;
double test(void* fptr) {
  f = ((float(*)(float,float))fptr)(1.0, 1.0);


Mips16 is not an architecture; it's a processor mode.

The real architecture is Mips32 (or Mips64 though I don't know that anyone has implemented mips16 mode in mips64 but it's allowed for our architectural licenses to do that).

When the program is running in mips16 mode all the time, then the processor can be thought
to be in soft float. That flavor of llvm mips16 works now and passes almost 100% of test-suite.

What can get messy is when you want to have mips32 and mips16 mode code in the same executable. If there is no floating point in the call signature, mips32 and mips16 functions can call each other with no problems.

I'm starting to implement the various interoperable modes now.

There is an historical way that all of this has been done in gcc and for this base port, management wants it done the same way. The historical definition of hard-float for mips16 is a hybrid based on gcc soft float.

The first step is to allow mips16 to use a library of soft float library routines that are implemented in mips32 mode using mips32 floating point hardware instructions. This mostly will work if you just change the names of the normal libc soft float library routines to be the hard float counterparts. These hardware floating point versions of soft float helper functions have the same signature as their soft float counterparts. The place where it does not work is with libm because the libm routines are mips32 functions and written without any knowledge of this and expect it's arguments to be in floating point registers if the mips 32 abi requires that. Ditto for return values.

There are two paths in this first stage; one for pic and one for non pic. Non pic requires some other functionality that I have to add to llvm which we already have discussed; that is the ability to compile functions in mips32 and mips16 mode in the same module.

So I am solving the pic case now.

I need to know the original (pre-lowered) return type and type of argument 1 and argument 2
in order to know which helper functions (if any) to call during the lowering.

I will look into extending llvm to make this information available as you suggested.


It seems that in SelectionDAGBuilder::visitCall, that I.getCalledFunction() has the correct protoype,
even for this case you give. (This case you give is not constructable as you pointed out simply using the DAG in Mips lower call).

So it seems possible to add an additional parameter to LowerCallTo and pass it down the chain
until Mips lower call is invoked.

Is this more or less what you had in mind?

There may be some special handling needed for functions which fall under visitUnaryFloatCall
and maybe some other cases.

That's the right idea... but as far as I can tell, it's already part
of the CallLoweringInfo structure.


Awesome. thanks for the help.

It's already in CallLoweringInfo

(gdb) call (*CLI.RetTy).dump()
(gdb) call (*CLI.Args[0].Ty).dump()
(gdb) call (*CLI.Args[1].Ty).dump()