Any extend

Hi,

I'm building a backend for a 64-bit target based on the existing Mips 32 one and I've come up against a problem with 32-bit loads. If you load a 32-bit value into a register this needs extending to fit into a native 64-bit register. The initial selection DAG acomplishes this using an any_extend node, which isn't handled later on by any instruction selector and thus LLVM can't produce the target code.

Now I could just handle an any_extend load, however if you load a 8-bit or 16-bit value the initial selection DAG uses a sign_extend (or zero_extend) node to turn it into a native 64-bit type and I would like the same behaviour for 32-bit loads.

So my questions are:
1) What causes the Initial selection DAG code to choose an any_extend over a sign_extend (or zero_extend)?
2) What does any_extend actually signify? Presumably this indicates that either a sign extend or zero extend will suffice.

Cheers,

Greg

Hi Greg,

1) What causes the Initial selection DAG code to choose an any_extend over a sign_extend (or zero_extend)?

because it is more efficient: the backend gets more choice in how to do
it, and at the same time it tells the optimizers that the extra bits
contain rubbish, which gives them more freedom to reason.

2) What does any_extend actually signify? Presumably this indicates that either a sign extend or zero extend will suffice.

Yes, or anything else.

Ciao,

Duncan.

Duncan Sands wrote:

Hi Greg,

1) What causes the Initial selection DAG code to choose an any_extend over a sign_extend (or zero_extend)?

because it is more efficient: the backend gets more choice in how to do
it, and at the same time it tells the optimizers that the extra bits
contain rubbish, which gives them more freedom to reason.

Makes sense, though I was wondering why it would choose to sign_extend an 8-bit or 16-bit value, but any_extend a 32-bit value (These are all signed values).

2) What does any_extend actually signify? Presumably this indicates that either a sign extend or zero extend will suffice.

Yes, or anything else.

Anyway thanks for the info, I'll just implement an any_extend load (The original MIPS backend doesn't seem to have any, which is why I was wary of doing it in the first place).

Cheers,

Greg

Hi Greg,

1) What causes the Initial selection DAG code to choose an any_extend over a sign_extend (or zero_extend)?

because it is more efficient: the backend gets more choice in how to do
it, and at the same time it tells the optimizers that the extra bits
contain rubbish, which gives them more freedom to reason.

Makes sense, though I was wondering why it would choose to sign_extend an 8-bit or 16-bit value, but any_extend a 32-bit value (These are all signed values).

I'm not sure what you mean by "these are all signed values" - in LLVM there
is no such thing as a "signed i16" or an "unsigned i16", there is only i16
with signed and unsigned operations. As to why you get a sign-extend in
some cases and any-extend in others... well, it depends on details of what
you are compiling, so without a testcase it is hard to say anything useful.

Ciao,

Duncan.

Hi Duncan,

1) What causes the Initial selection DAG code to choose an any_extend over a sign_extend (or zero_extend)?

because it is more efficient: the backend gets more choice in how to do
it, and at the same time it tells the optimizers that the extra bits
contain rubbish, which gives them more freedom to reason.

Makes sense, though I was wondering why it would choose to sign_extend an 8-bit or 16-bit value, but any_extend a 32-bit value (These are all signed values).

I'm not sure what you mean by "these are all signed values" - in LLVM there
is no such thing as a "signed i16" or an "unsigned i16", there is only i16
with signed and unsigned operations. As to why you get a sign-extend in
some cases and any-extend in others... well, it depends on details of what
you are compiling, so without a testcase it is hard to say anything useful.

Yes, I could have worded that better. The particular test case I'm using is the following bit of c, which is then compiled to LLVM using Clang without any optimisation:

int func(void)
{
  return 0;
}

I then compile the same thing with func returning char or short (which are all signed types, which I realise isn't reflected in the LLVM types).

Clang generates the following LLVM:

; ModuleID = 'test.c'
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128"
target triple = "x86_64-unknown-linux-gnu"

define i32 @func() nounwind {
entry:
   %retval = alloca i32 ; <i32*> [#uses=2]
   store i32 0, i32* %retval
   %0 = load i32* %retval ; <i32> [#uses=1]
   ret i32 %0
}

When I use char or short it generates the same code, only using i16 or i8, plus (I now realise having looked at it more closely) one more crucial difference, it adds signext to the function definition in the i8 and i16 cases like so:

define signext i16 @func() nounwind {

Which is where the sign_extend/any_extend difference comes from :slight_smile: So it's Clang that makes the decision.

Out of interest the Mips backend does deal with any_extend, it just does it using Pat to map them directly to unsigned loads, rather than defining them as a Mips instruction. Which is why I missed them when extending it for 64-bit.

Cheers,

Greg