libcalls for shifts

Hello,

my target has libcall support for long long shifts. I already have the following lines in my Lowering constructor:

   setLibcallName(RTLIB::SHL_I64, "__llshl");
   setLibcallName(RTLIB::SRL_I64, "__llshru");
   setLibcallName(RTLIB::SRA_I64, "__llshr");

and

   setOperationAction(ISD::SHL, MVT::i64, Expand);
   setOperationAction(ISD::SRA, MVT::i64, Expand);
   setOperationAction(ISD::SRL, MVT::i64, Expand);

But when I try to compile a sample program, I get the error:
NODE: 0x273c850: i64 = shl 0x273b800, 0x273be00

llc: LegalizeDAG.cpp:1011: llvm::SDValue {anonymous}::SelectionDAGLegalize::LegalizeOp(llvm::SDValue): Assertion `0 && "Do not know how to legalize this operator!"' failed.

... Well, I thought that was the way to tell LLVM to expand to a libcall. What's the problem? How can I convince LLVM to expand to a libcall here?
(ARM does intrinsics magics here. Is this the (only) way to do it?)

Regards,
Johannes

Is i64 a legal type on your target? If so, you might need to hack the
code generator a bit; no in-tree target has a legal integer type that
doesn't support shifts. If not, I have no idea how you're getting
that error.

-Eli

Is i64 a legal type on your target? If so, you might need to hack the
code generator a bit; no in-tree target has a legal integer type that
doesn't support shifts. If not, I have no idea how you're getting
that error.

Yes, well, i64 is a legal type, that's how it is. However, i64s are
always stored in two 32-bit registers and in some cases need complicated
instruction patterns for easy things (such as add, sub and so on) that
basically simulate the i64 behaviour with a sequence of i32
instructions. Simulating an i64 shift with two variables (no immediates)
with i32 instructions is so complicated that the target (the TI C64x+
family) has libcalls for them. Instructions for i32 shift exist, of course.

Thanks for your help. I'll maybe start tearing up the code generator
then. (I've already tried just copying the MakeLibCall function from the
right class into our *Lowering.cpp and calling it using custom lowering,
but some pass doesn't like that; I suspect it's our scheduler.)

Regards,
Johannes

I think you're misunderstanding what it means for a type to be "legal"
on a target. If you don't have a single i64 addition instruction on
i64 registers, you shouldn't be marking i64 as a legal type. You can
custom-lower certain operations on i64 if there's some instruction
sequence which is better than the default expansion.

-Eli

Yes, well, i64 is a legal type, that's how it is. However, i64s are
always stored in two 32-bit registers and in some cases need complicated
instruction patterns for easy things (such as add, sub and so on) that
basically simulate the i64 behaviour with a sequence of i32
instructions.

I think you're misunderstanding what it means for a type to be "legal"
on a target. If you don't have a single i64 addition instruction on
i64 registers, you shouldn't be marking i64 as a legal type. You can
custom-lower certain operations on i64 if there's some instruction
sequence which is better than the default expansion.

I don't think so.
Single-instruction support for i64 exists in instructions such as storing and loading from memory and multiplication.
For other instructions, such as converting to and from other data types, division, remainder etc., libcalls exist.
Other instructions can and will be simulated by a sequence of i32 instructions. I thought that a target supports a datatype when it can produce the correct semantics for all necessary operations on that datatype, regardless of whether single instructions exist for all operations on that datatype.

Should a developer refrain from using 64-bit values just because they don't fit into a single register, or because add takes three instead of one instruction? For most of the i64 operations, LLVM doesn't even know of a default expansion, and yet i64 is certainly not illegal because its semantics can be produced validly in a variety of ways. I have i64 instruction patterns for add, single-instruction support for mul, custom lowering for sub, and a number of libcalls for other instructions.

But I've already found the right method in LegalizeDAG.cpp and will add support for shift libcalls, so thanks for your help :slight_smile:

Regards,
Johannes

Hi,

You're describing the exact same set of requirements for ARM - 64-bit loads
and stores (LDRD, STRD) and multiplications but not shifts, adds, subtracts
or anything else.

ARM uses 32-bits as the largest possible type, and legalizes everything down
to 32-bits. Why don't you do the same?

Cheers,

James

ARM uses 32-bits as the largest possible type, and legalizes

everything down to 32-bits. Why don't you do the same?

Hmmm... I have fared very well until now except for the thing with the shift libcalls, which somehow don't work as expected when I switch on parallel instruction scheduling.

Cheers,
Johannes

Johannes,
Try a custom lowering of the 64bit shift instructions and then in the Lower[SRL|SHL|SRA] function, generate a call instruction to the function you want.

Micah

Try a custom lowering of the 64bit shift instructions and then in the Lower[SRL|SHL|SRA] function, generate a call instruction to the function you want.

Yes, that's what I've done now. The slightly ugly thing is, of course,
that I have to copy the MakeLibCall function into our own backend (not
exactly clean).

Cheers,
Johannes