Odin icon indicating copy to clipboard operation
Odin copied to clipboard

Constant signed big endian negative values are incorrect

Open Despacito696969 opened this issue 2 years ago • 1 comments

Expected Behavior

Steps to Reproduce

package main

import "core:fmt"

main :: proc()
{
   {
      v := i16be(-1) //Assign with constant big endian literal
      fmt.printf("{}\n", v)
      w := i16(-1)
      v = auto_cast w //Assign big endian variable with -1, but do the cast at runtime
      fmt.printf("{}\n", v)
      fmt.printf("----\n")
   }

   {
      v := i32be(-1)
      fmt.printf("{}\n", v)
      w := i32(-1)
      v = auto_cast w
      fmt.printf("{}\n", v)
      fmt.printf("----\n")
   }

   {
      v := i64be(-1)
      fmt.printf("{}\n", v)
      w := i64(-1)
      v = auto_cast w
      fmt.printf("{}\n", v)
      fmt.printf("----\n")
   }

   {
      v := i128be(-1)
      fmt.printf("{}\n", v)
      w := i128(-1)
      v = auto_cast w
      fmt.printf("{}\n", v)
      fmt.printf("----\n")
   }

   fmt.printf("{}\n", i64be(-1))
   fmt.printf("{}\n", i64be(-2))
   fmt.printf("{}\n", i64be(-3))
   fmt.printf("{}\n", i64be(-1<<8))
   fmt.printf("{}\n", i64be(-1<<16))
   fmt.printf("{}\n", i64be(-1<<32))
   fmt.printf("{}\n", i64be(123_456_789_000))
}

This prints:

255
-1
----
255
-1
----
255
-1
----
255
-1
----
255
254
253
65535
16777215
1099511627775
123456789000

Casting native int into a big endian one at runtime works fine. Last value was correctly casted since it was positive.

Context

        Odin: dev-2022-08:c82d7d3d
        OS:   Windows 10 Professional (version: 21H2), build 19044.1889
        CPU:  Intel(R) Core(TM) i5-8600K CPU @ 3.60GHz
        RAM:  16330 MiB

Despacito696969 avatar Aug 27 '22 15:08 Despacito696969

I think I might have found a reason why this happens.

In function lb_big_int_to_llvm in llvm_backend_const.cpp we get our constant in big int which stores the absolute value and a sign. For example, let's take i64be(-32). We first unpack it into 20 00 00 00 00 00 00 00 (little endian). Since if (!is_type_endian_little(original_type)) is true we swap bytes to get 00 00 00 00 00 00 00 20. Because if (big_int_is_neg(a)) is true we negate this, however it seems that LLVMConstNeg assumes it's little endian, so we get 00 00 00 00 00 00 00 E0. E0 is 224 in decimal.

fmt.println(mem.any_to_bytes(i64be(-32)), mem.any_to_bytes(i64be(32))) prints: [0, 0, 0, 0, 0, 0, 0, 224] [0, 0, 0, 0, 0, 0, 0, 32] fmt.println(mem.any_to_bytes(i64be(-1<<16)), mem.any_to_bytes(i64be(1<<16))) prints: [0, 0, 0, 0, 0, 255, 255, 255] [0, 0, 0, 0, 0, 1, 0, 0]

Despacito696969 avatar Sep 14 '22 18:09 Despacito696969