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