Some transform about sext/zext, is it reasonable

Here is a source code:

bool EXITFLAG = false;
int originfunc() {
    unsigned long x, y;
    x = dosomething();
    y = dosomething();
    {
        signed char xx = x, yy = y;
        if ((xx) >= (unsigned char)(yy) )
            EXITFLAG = true;
    }
    return 0;
}

It generate IR like this:

define i32 @originfunc() #0 {
entry:
  %call = call i64 @dosomething()
  %call1 = call i64 @dosomething()
  %conv = trunc i64 %call to i8
  %conv2 = trunc i64 %call1 to i8
  %conv3 = sext i8 %conv to i32              
  %conv4 = zext i8 %conv2 to i32             
  %cmp = icmp sge i32 %conv3, %conv4
  br i1 %cmp, label %if.then, label %if.end

....
}

My question is about the trunc and sext/zext.
In InstCombiner::visitSExt, sext is changed into shl + ashr, and change the result type of trunc before, the result is like this:

%call = call i64 @random_bitstring()
%call1 = call i64 @random_bitstring()
%conv = trunc i64 %call to i32
%conv2 = trunc i64 %call1 to i32
%sext = shl i32 %conv, 24
%conv3 = ashr exact i32 %sext, 24
%conv4 = and i32 %conv2, 255
%cmp.not = icmp slt i32 %conv3, %conv4

The DAG of trunc and sext/zext is like:

            t10: i32 = truncate t5
          t14: i32 = shl t10, Constant:i64<24>
        t15: i32 = sra exact t14, Constant:i64<24>
          t11: i32 = truncate t9
        t17: i32 = and t11, Constant:i32<255>

The type i8 seems to be eliminated.
But in the after stage, fold (sra (shl x, c1), c1) → sext_inreg, I get DAG like this:

        t10: i32 = truncate t5
      t26: i32 = sign_extend_inreg t10, ValueType:ch:i8
        t11: i32 = truncate t9
      t17: i32 = and t11, Constant:i32<255>

The problem is here, the source type of t26 become i8 again, not match the type of t10 before. Is this reasonable?

t26: i32 = sign_extend_inreg t10, ValueType:ch:i8 means sign extending t10 as if its original type is i8, which looks at bit 7 and propagates its value to bit 8 ~ 31 – and that’s exactly what the shl + sra nodes were doing.

Thank you for your answer.
Do you mean that, the type of t10 can be different with the type in ValueType field, it seems move trunc function into sign_extend_inreg.

The type of t10 might be different from sext_inreg’s ValueType argument. I think it’s helpful to understand the big picture here: first, forget about the optimized LLVM IR, the operations we’re interested here are

  %conv = trunc i64 %call to i8
  %conv3 = sext i8 %conv to i32

when it first send into SelectionDAGISel, it will give you a DAG like this:

  t10:i8 = truncate t5:i64
t26:i32 = sext t10

Then type legalization kicks in – type legalization transforms every single values into one of the legal types. Every target has its own legal types and it seems like your target here probably has i32 and i64 as legal types. So the i8 here has to be promoted to i32 – one of the legal types. Naively we can do something like

  t10:i32 = truncate t5:i64
t26:i32 = sext t10:i32

And (wrongfully) thought that since t10 and t26 are both i32, we can eliminate sext altogether. But apparently it’s wrong, because bit 8 ~ 31 in the original t26 had the same value as bit 7 (that’s how sign extension works), while our hypothetical new value – a single t26:i32 = truncate t5:i64 does not guarantee that. So the correct transformation would be:

  t10:i32 = truncate t5:i64
t26:i32 = sign_extend_inreg t10, ValueType:ch:i8

Which sign extends t10 by propagating bit 7’s value to bit 8 ~ 31. These are what effectively happened on your program.

So what I’m trying to say is, the fact that t10 has a “nominal” type of i32 is simply a result of type legalization. Its actual / effective type is still i8.

Yes, sign_extend_inreg here is (effectively) doing some sort of truncation.

Thanks, you help a lot.