https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117885

            Bug ID: 117885
           Summary: Casting element to bitfield subparts can be constant
                    evaluated wrongly.
           Product: gcc
           Version: 14.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: jit
          Assignee: dmalcolm at gcc dot gnu.org
          Reporter: i at zhuyi dot fan
  Target Milestone: ---

Created attachment 59766
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=59766&action=edit
Reproducer of the jit program

Short Version
=============

Miscompilation when accessing subfields of a larger data type by casting it
into bitfields or storing it as a union field aside other bitfields. In the
example program, we supply constant values as expected results of such
operations, and perform a check on the equality. Although the program exits
normally when the symbol is declared with external visibility, the whole
programs folds into a trap (the failure branch) when the symbol is made
internal.


Background and Original Discussion
==================================

The original discussion was at
https://gist.github.com/SchrodingerZhu/84a334f8666b567800624446d354b568

The background is in mlir-gccjit, we attempt to compile the following program:

```
// RUN: %gccjit-translate -o %t.gimple %s -mlir-to-gccjit-gimple
// RUN: %filecheck --input-file=%t.gimple %s
// RUN: %gccjit-translate -o %t.exe %s -mlir-to-gccjit-executable && chmod +x
%t.exe && %t.exe

!int = !gccjit.int<int>
!ilv = !gccjit.lvalue<!int>
!bool = !gccjit.int<bool>
!bitfields = !gccjit.struct<"Int" {
    #gccjit.field<"A" !int : 17>,
    #gccjit.field<"B" !int : 5>,
    #gccjit.field<"C" !int : 10>
}>


module attributes { gccjit.opt_level = #gccjit.opt_level<O3>,
gccjit.allow_unreachable = true } {
    gccjit.func internal @from_int_to_bitfield (!int, !int, !int, !int) {
        ^entry(%arg0: !ilv, %arg1: !ilv, %arg2: !ilv, %arg3: !ilv):
            %0 = gccjit.as_rvalue %arg0 : !ilv to !int
            %1 = gccjit.as_rvalue %arg1 : !ilv to !int
            %2 = gccjit.as_rvalue %arg2 : !ilv to !int
            %3 = gccjit.as_rvalue %arg3 : !ilv to !int
            // CHECK: %[[V:[0-9]+]] = bitcast(%{{[0-9]+}}, struct Int);
            // CHECK: %{{[0-9]+}} = %[[V]].A:17;
            // CHECK: %{{[0-9]+}} = %[[V]].B:5;
            // CHECK: %{{[0-9]+}} = %[[V]].C:10;
            %4 = gccjit.bitcast %0 : !int to !bitfields
            %5 = gccjit.access_field %4[0] : !bitfields -> !int
            %6 = gccjit.access_field %4[1] : !bitfields -> !int
            %7 = gccjit.access_field %4[2] : !bitfields -> !int
            %eq0 = gccjit.compare eq (%5 : !int, %1 : !int) : !bool
            %eq1 = gccjit.compare eq (%6 : !int, %2 : !int) : !bool
            %eq2 = gccjit.compare eq (%7 : !int, %3 : !int) : !bool
            %and0 = gccjit.binary logical_and (%eq0 : !bool, %eq1 : !bool) :
!bool
            %and1 = gccjit.binary logical_and (%and0 : !bool, %eq2 : !bool) :
!bool
            gccjit.conditional (%and1 : !bool), ^return, ^trap

        ^return:
            gccjit.return

        ^trap:
            gccjit.call builtin @__builtin_trap() : () -> !gccjit.void
            gccjit.jump ^trap
    }
    gccjit.func exported @main() -> !int {
        ^entry:
            %0 = gccjit.const #gccjit.int<-559038737> : !int
            %1 = gccjit.const #gccjit.int<-16657> : !int
            %2 = gccjit.const #gccjit.int<-10> : !int
            %3 = gccjit.const #gccjit.int<-134> : !int
            gccjit.call @from_int_to_bitfield(%0, %1, %2, %3) : (!int, !int,
!int, !int) -> ()

            %ret = gccjit.const #gccjit.zero : !int
            gccjit.return %ret : !int
    }
}
```

with
```
./bin/gccjit-translate test.mlir -mlir-to-gccjit-assembly
```
Strangely, everything is folded into
```
        .file   "fake.c"
        .text
        .section        .text.unlikely,"ax",@progbits
        .globl  main
        .type   main, @function
main:
.LFB1:
        .cfi_startproc
.L2:
        ud2
        .cfi_endproc
.LFE1:
        .size   main, .-main
        .ident  "GCC: (Ubuntu 14-20240412-0ubuntu1) 14.0.1 20240412
(experimental) [master r14-9935-g67e1433a94f]"
        .section        .note.GNU-stack,"",@progbits
```
If instead of `internal` function kind, `exported` is used, then the program
exits normally.

Gimple Version
==============

One can play with mlir-gccjit:
```
./bin/gccjit-translate test.mlir -mlir-to-gccjit-gimple
```

The above program generates the following gimple:

```
struct Int;

struct Int
{
  int A:17;
  int B:5;
  int C:10;
};

static void
from_int_to_bitfield (int %arg0, int %arg1, int %arg2, int %arg3)
{
  int %0;
  int %1;
  int %2;
  int %3;
  struct Int %4;
  int %5;
  int %6;
  int %7;
  bool %8;
  bool %9;
  bool %10;
  bool %11;
  bool %12;

bb0:
  %0 = %arg0;
  %1 = %arg1;
  %2 = %arg2;
  %3 = %arg3;
  %4 = bitcast(%0, struct Int);
  %5 = %4.A:17;
  %6 = %4.B:5;
  %7 = %4.C:10;
  %8 = %5 == %1;
  %9 = %6 == %2;
  %10 = %7 == %3;
  %11 = %8 && %9;
  %12 = %11 && %10;
  if (%12) goto bb1; else goto bb2;

bb1:
  return;

bb2:
  (void)__builtin_trap ();
  goto bb2;
}

extern int
main ()
{
  int %0;
  int %1;
  int %2;
  int %3;
  int %4;

bb0:
  %0 = (int)-559038737;
  %1 = (int)-16657;
  %2 = (int)-10;
  %3 = (int)-134;
  (void)from_int_to_bitfield (%0, %1, %2, %3);
  %4 = (int)0;
  return %4;
}
```
The reproducer is attached. Or, one can generate it with
```
./bin/gccjit-translate test.mlir -mlir-to-gccjit-reproducer
```

Reply via email to