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
```