`icmp` has unexpected result

I’ve had another newbie problem which I think may be related to a recent solved problem (https://discourse.llvm.org/t/i8-value-range-and-integer-promotion).

This snippet is supposed to be doing if x = 200 then however the branch always goes to if_exit which is not correct because x does indeed equal 200 (the -56 is from the C API constant value, see my linked post above).

However, if I change the value to 100 then the branch does go to if_then as expected. So what did I do wrong?

  store i64 200, ptr @x, align 4
  %r_load_x = load i64, ptr @x, align 4
  %int_eq_tmp = icmp eq i64 %r_load_x, -56
  br i1 %int_eq_tmp, label %if_then, label %if_exit

If the type is i64, then 200 and -56 are not equal and so icmp is correct in returning the result as false. -56 represents the bit-pattern 0xffffffffffffffc8 when the type is i64. So with i64 as data type, if you want to compare the loaded value with 200, the correct icmp instruction is:

%int_eq_tmp = icmp eq i64 %r_load_x, 200

The source of confusion could be that LLVM prints the value 200 as -56 when the data type is i8. For example, clang -S -emit-llvm for the C code:

unsigned char c = 200;

will print:

@c = dso_local global i8 -56, align 1

If the type is 8-bits wide, -56 has the bit pattern 0xc8 and so does 200. So it doesn’t matter how it is printed. But in 64-bits, they are not the same -56 is 0xf...fc8 and 200 is 0x0...0c8.

unsigned long long i = 200;
unsigned long long j = -56;

will print:

@i = dso_local global i64 200, align 8
@j = dso_local global i64 -56, align 8

Thus, LLVM will print 200 as 200 if the data type has a bit width of 9 or more. It would be incorrect to print it as -56. Constants are printed as negative decimal values if the most significant bit (as determined by the bit width of the type) is set. That’s just a convention chosen by AsmWriter. It could have chosen to print all constants as bit patterns 0x... instead. In fact, it does that for some floating point constants. For example, double d = 2.7182818; will be printed as double 0x4005BF0A87427F01 because printing as decimal values can potentially introduce floating point precision errors.

Hope this helps clarify the confusion.

3 Likes

Thanks this really helps! The problem must be the constant type become signed when it’s over 128 and thus this happened (same as previous problem). I guess I need to always convert the two types in a binary expression to be the same “best” type, in this case i64.

Note that there is no signed int type in LLVM. There used to be signed int types but they were removed a long time ago. Constants can be created with a flag IsSigned but that just means whether they are sign-extended or zero-extended to fit the bit-width of their type. The type is always unsigned (or more precisely, there is no distinction between signed and unsigned in the type system for IntegerType). It is up to specific instructions to treat values as signed or unsigned (e.g, icmp sgt versus icmp ugt).

Maybe I’m still confused. If I really want to compare the value of 200 and I pass an i8 type to LLVMConstInt will that work when comparing against i64? I haven’t had time to test yet but I’m pretty sure if I change the type I pass to LLVMConstInt to be i64 with a value of 200 then it will work. Maybe I need to stop thinking about values and more about the sizes of the types?

Are you generating LLVM from some higher level language? Note that LLVM requires the operands of a comparison to be the same width. So if you have created 200 as i8 and you want to compare it with i64, you have to extend the i8 constant to 64 bits. There are two extension instructions: sext ... to ... and zext ... to ... . If you do the former, you are effectively comparing with 64-bit -56 and if you do the latter, you are comparing with 64-bit 200. I don’t know how you ended up with 64-bit -56 in your original post. What API did you use to create that constant and what arguments did you pass to the API?

1 Like

Yes exactly I have a parser so I’m calling the C API to make the constants and choosing the wrong type I guess. This is the same problem I got help with recently but I thought it was about C integer promotion but this is actually about what you said here, bit widths need to be the same on all comparisons.

I moved the solution to be your last comment. Thanks for you help I appreciate it.

1 Like