llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
28.8k stars 11.91k forks source link

alignment attribute constrained by #pragma pack is inconsistent with the actual alignment in the IR #113884

Open mzyx-hnu opened 4 days ago

mzyx-hnu commented 4 days ago

In this case, I used #pragma pack to set the structure alignment to 1. Then, I loaded the first int member of the structure. When compiled with -O2, the align attribute in the IR was converted to 4.

// align.c
#pragma pack(1)
struct a {
  volatile int b;
  int c;
  long d;
  long e;
  short f;
unsigned : 25;
  volatile long g;
  int h;
} i[];

static volatile struct a k = {1}, l = {2}, m = {3}, n = {4}, p = {5}, q = {6};

static int foo() {
  int s = m.b;                                                 // load m with align 4
  m.c = k.c = l.c = n.c = p.c = q.c = 1;
  return s;
}

int main() {
  foo();
  return 0;
}

compile with main branch -O2 -emit-llvm -S

; ModuleID = './align.c'
source_filename = "./align.c"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
target triple = "aarch64-unknown-linux-gnu"

%struct.a = type <{ i32, i32, i64, i64, i16, i32, i64, i32 }>

@i = dso_local local_unnamed_addr global [1 x %struct.a] zeroinitializer, align 1
@m = internal global <{ i32, i32, i64, i64, i16, [4 x i8], i64, i32 }> <{ i32 3, i32 0, i64 0, i64 0, i16 0, [4 x i8] zeroinitializer, i64 0, i32 0 }>, align 4
@q = internal global <{ i32, i32, i64, i64, i16, [4 x i8], i64, i32 }> <{ i32 6, i32 0, i64 0, i64 0, i16 0, [4 x i8] zeroinitializer, i64 0, i32 0 }>, align 1
@p = internal global <{ i32, i32, i64, i64, i16, [4 x i8], i64, i32 }> <{ i32 5, i32 0, i64 0, i64 0, i16 0, [4 x i8] zeroinitializer, i64 0, i32 0 }>, align 1
@n = internal global <{ i32, i32, i64, i64, i16, [4 x i8], i64, i32 }> <{ i32 4, i32 0, i64 0, i64 0, i16 0, [4 x i8] zeroinitializer, i64 0, i32 0 }>, align 1
@l = internal global <{ i32, i32, i64, i64, i16, [4 x i8], i64, i32 }> <{ i32 2, i32 0, i64 0, i64 0, i16 0, [4 x i8] zeroinitializer, i64 0, i32 0 }>, align 1
@k = internal global <{ i32, i32, i64, i64, i16, [4 x i8], i64, i32 }> <{ i32 1, i32 0, i64 0, i64 0, i16 0, [4 x i8] zeroinitializer, i64 0, i32 0 }>, align 1

; Function Attrs: nofree norecurse nounwind memory(readwrite, argmem: none) uwtable
define dso_local noundef i32 @main() local_unnamed_addr #0 {
entry:
  %0 = load volatile i32, ptr @m, align 4, !tbaa !6
  store volatile i32 1, ptr getelementptr inbounds nuw (i8, ptr @q, i64 4), align 1, !tbaa !13
  store volatile i32 1, ptr getelementptr inbounds nuw (i8, ptr @p, i64 4), align 1, !tbaa !13
  store volatile i32 1, ptr getelementptr inbounds nuw (i8, ptr @n, i64 4), align 1, !tbaa !13
  store volatile i32 1, ptr getelementptr inbounds nuw (i8, ptr @l, i64 4), align 1, !tbaa !13
  store volatile i32 1, ptr getelementptr inbounds nuw (i8, ptr @k, i64 4), align 1, !tbaa !13
  store volatile i32 1, ptr getelementptr inbounds nuw (i8, ptr @m, i64 4), align 4, !tbaa !13
  ret i32 0
}

The pass responsible for increasing the alignment attribute is InferAlignmentPass. Since the user code loaded the struct object m (Or m.b, the first int member of m) with a 4-byte alignment once, the alignment attribute for m was increased from 1 to 4. However, the struct's actual memory is still 1-byte alignment, and this inconsistency has caused issues in my development. Should llvm add a check in InferAlignmentPass for this case?

llvmbot commented 4 days ago

@llvm/issue-subscribers-clang-codegen

Author: Yunfei Li (mzyx-hnu)

In this case, I used `#pragma pack` to set the structure alignment to 1. Then, I loaded the first int member of the structure. When compiled with `-O2`, the align attribute in the IR was converted to 4. ~~~ // align.c #pragma pack(1) struct a { volatile int b; int c; long d; long e; short f; unsigned : 25; volatile long g; int h; } i[]; static volatile struct a k = {1}, l = {2}, m = {3}, n = {4}, p = {5}, q = {6}; static int foo() { int s = m.b; // load m with align 4 m.c = k.c = l.c = n.c = p.c = q.c = 1; return s; } int main() { foo(); return 0; } ~~~ compile with main branch `-O2 -emit-llvm -S` ~~~ ; ModuleID = './align.c' source_filename = "./align.c" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32" target triple = "aarch64-unknown-linux-gnu" %struct.a = type <{ i32, i32, i64, i64, i16, i32, i64, i32 }> @i = dso_local local_unnamed_addr global [1 x %struct.a] zeroinitializer, align 1 @m = internal global <{ i32, i32, i64, i64, i16, [4 x i8], i64, i32 }> <{ i32 3, i32 0, i64 0, i64 0, i16 0, [4 x i8] zeroinitializer, i64 0, i32 0 }>, align 4 @q = internal global <{ i32, i32, i64, i64, i16, [4 x i8], i64, i32 }> <{ i32 6, i32 0, i64 0, i64 0, i16 0, [4 x i8] zeroinitializer, i64 0, i32 0 }>, align 1 @p = internal global <{ i32, i32, i64, i64, i16, [4 x i8], i64, i32 }> <{ i32 5, i32 0, i64 0, i64 0, i16 0, [4 x i8] zeroinitializer, i64 0, i32 0 }>, align 1 @n = internal global <{ i32, i32, i64, i64, i16, [4 x i8], i64, i32 }> <{ i32 4, i32 0, i64 0, i64 0, i16 0, [4 x i8] zeroinitializer, i64 0, i32 0 }>, align 1 @l = internal global <{ i32, i32, i64, i64, i16, [4 x i8], i64, i32 }> <{ i32 2, i32 0, i64 0, i64 0, i16 0, [4 x i8] zeroinitializer, i64 0, i32 0 }>, align 1 @k = internal global <{ i32, i32, i64, i64, i16, [4 x i8], i64, i32 }> <{ i32 1, i32 0, i64 0, i64 0, i16 0, [4 x i8] zeroinitializer, i64 0, i32 0 }>, align 1 ; Function Attrs: nofree norecurse nounwind memory(readwrite, argmem: none) uwtable define dso_local noundef i32 @main() local_unnamed_addr #0 { entry: %0 = load volatile i32, ptr @m, align 4, !tbaa !6 store volatile i32 1, ptr getelementptr inbounds nuw (i8, ptr @q, i64 4), align 1, !tbaa !13 store volatile i32 1, ptr getelementptr inbounds nuw (i8, ptr @p, i64 4), align 1, !tbaa !13 store volatile i32 1, ptr getelementptr inbounds nuw (i8, ptr @n, i64 4), align 1, !tbaa !13 store volatile i32 1, ptr getelementptr inbounds nuw (i8, ptr @l, i64 4), align 1, !tbaa !13 store volatile i32 1, ptr getelementptr inbounds nuw (i8, ptr @k, i64 4), align 1, !tbaa !13 store volatile i32 1, ptr getelementptr inbounds nuw (i8, ptr @m, i64 4), align 4, !tbaa !13 ret i32 0 } ~~~ The pass responsible for increasing the alignment attribute is `InferAlignmentPass`. Since the user code loaded the struct object m (Or m.b, the first int member of m) with a 4-byte alignment once, the alignment attribute for m was increased from 1 to 4. However, the struct's actual memory is still 1-byte alignment, and this inconsistency has caused issues in my development. Should llvm add a check in `InferAlignmentPass` for this case?
mzyx-hnu commented 2 days ago

ping?Any suggestion?