// XFAIL: target=aarch64-pc-windows-msvc
// RUN: %clang_cc1 -Wno-return-type -Wno-unused-value -emit-llvm %s -w -o - | FileCheck %s
// CHECK: @i = {{(dso_local )?}}global [[INT:i[0-9]+]] 0
volatile int i, j, k;
volatile int ar[5];
volatile char c;
// CHECK: @ci = {{(dso_local )?}}global [[CINT:.*]] zeroinitializer
volatile _Complex int ci;
volatile struct S {
#ifdef __cplusplus
void operator =(volatile struct S&o) volatile;
#endif
int i;
} a, b;
//void operator =(volatile struct S&o1, volatile struct S&o2) volatile;
int printf(const char *, ...);
// Note that these test results are very much specific to C!
// Assignments in C++ yield l-values, not r-values, and the situations
// that do implicit lvalue-to-rvalue conversion are substantially
// reduced.
// CHECK-LABEL: define {{.*}}void @test()
void test(void) {
// CHECK: load volatile [[INT]], ptr @i
i;
// CHECK-NEXT: load volatile [[INT]], ptr @ci, align 4
// CHECK-NEXT: load volatile [[INT]], ptr getelementptr inbounds nuw ([[CINT]], ptr @ci, i32 0, i32 1), align 4
// CHECK-NEXT: sitofp [[INT]]
(float)(ci);
// CHECK-NEXT: load volatile [[INT]], ptr @ci, align 4
// CHECK-NEXT: load volatile [[INT]], ptr getelementptr inbounds nuw ([[CINT]], ptr @ci, i32 0, i32 1), align 4
(void)ci;
// CHECK-NEXT: memcpy
(void)a;
// CHECK-NEXT: [[R:%.*]] = load volatile [[INT]], ptr @ci, align 4
// CHECK-NEXT: [[I:%.*]] = load volatile [[INT]], ptr getelementptr inbounds nuw ([[CINT]], ptr @ci, i32 0, i32 1), align 4
// CHECK-NEXT: store volatile [[INT]] [[R]], ptr @ci, align 4
// CHECK-NEXT: store volatile [[INT]] [[I]], ptr getelementptr inbounds nuw ([[CINT]], ptr @ci, i32 0, i32 1), align 4
(void)(ci=ci);
// CHECK-NEXT: [[T:%.*]] = load volatile [[INT]], ptr @j
// CHECK-NEXT: store volatile [[INT]] [[T]], ptr @i
(void)(i=j);
// CHECK-NEXT: [[R1:%.*]] = load volatile [[INT]], ptr @ci, align 4
// CHECK-NEXT: [[I1:%.*]] = load volatile [[INT]], ptr getelementptr inbounds nuw ([[CINT]], ptr @ci, i32 0, i32 1), align 4
// CHECK-NEXT: [[R2:%.*]] = load volatile [[INT]], ptr @ci, align 4
// CHECK-NEXT: [[I2:%.*]] = load volatile [[INT]], ptr getelementptr inbounds nuw ([[CINT]], ptr @ci, i32 0, i32 1), align 4
// Not sure why they're ordered this way.
// CHECK-NEXT: [[R:%.*]] = add [[INT]] [[R2]], [[R1]]
// CHECK-NEXT: [[I:%.*]] = add [[INT]] [[I2]], [[I1]]
// CHECK-NEXT: store volatile [[INT]] [[R]], ptr @ci, align 4
// CHECK-NEXT: store volatile [[INT]] [[I]], ptr getelementptr inbounds nuw ([[CINT]], ptr @ci, i32 0, i32 1), align 4
ci+=ci;
// CHECK-NEXT: [[R1:%.*]] = load volatile [[INT]], ptr @ci, align 4
// CHECK-NEXT: [[I1:%.*]] = load volatile [[INT]], ptr getelementptr inbounds nuw ([[CINT]], ptr @ci, i32 0, i32 1), align 4
// CHECK-NEXT: [[R2:%.*]] = load volatile [[INT]], ptr @ci, align 4
// CHECK-NEXT: [[I2:%.*]] = load volatile [[INT]], ptr getelementptr inbounds nuw ([[CINT]], ptr @ci, i32 0, i32 1), align 4
// CHECK-NEXT: [[R:%.*]] = add [[INT]] [[R2]], [[R1]]
// CHECK-NEXT: [[I:%.*]] = add [[INT]] [[I2]], [[I1]]
// CHECK-NEXT: store volatile [[INT]] [[R]], ptr @ci, align 4
// CHECK-NEXT: store volatile [[INT]] [[I]], ptr getelementptr inbounds nuw ([[CINT]], ptr @ci, i32 0, i32 1), align 4
// CHECK-NEXT: [[R2:%.*]] = load volatile [[INT]], ptr @ci, align 4
// CHECK-NEXT: [[I2:%.*]] = load volatile [[INT]], ptr getelementptr inbounds nuw ([[CINT]], ptr @ci, i32 0, i32 1), align 4
// These additions can be elided
// CHECK-NEXT: add [[INT]] [[R]], [[R2]]
// CHECK-NEXT: add [[INT]] [[I]], [[I2]]
(ci += ci) + ci;
// CHECK-NEXT: call void asm
asm("nop");
// CHECK-NEXT: load volatile
// CHECK-NEXT: load volatile
// CHECK-NEXT: add nsw [[INT]]
// CHECK-NEXT: store volatile
// CHECK-NEXT: load volatile
// CHECK-NEXT: add nsw [[INT]]
(i += j) + k;
// CHECK-NEXT: call void asm
asm("nop");
// CHECK-NEXT: load volatile
// CHECK-NEXT: load volatile
// CHECK-NEXT: add nsw [[INT]]
// CHECK-NEXT: store volatile
// CHECK-NEXT: add nsw [[INT]]
(i += j) + 1;
// CHECK-NEXT: call void asm
asm("nop");
// CHECK-NEXT: load volatile
// CHECK-NEXT: load volatile
// CHECK-NEXT: load volatile
// CHECK-NEXT: load volatile
// CHECK-NEXT: add [[INT]]
// CHECK-NEXT: add [[INT]]
ci+ci;
// CHECK-NEXT: load volatile
__real i;
// CHECK-NEXT: load volatile
// CHECK-NEXT: load volatile
+ci;
// CHECK-NEXT: call void asm
asm("nop");
// CHECK-NEXT: load volatile
// CHECK-NEXT: store volatile
(void)(i=i);
// CHECK-NEXT: load volatile
// CHECK-NEXT: store volatile
// CHECK-NEXT: sitofp
(float)(i=i);
// CHECK-NEXT: load volatile
(void)i;
// CHECK-NEXT: load volatile
// CHECK-NEXT: store volatile
i=i;
// CHECK-NEXT: load volatile
// CHECK-NEXT: store volatile
// CHECK-NEXT: store volatile
i=i=i;
#ifndef __cplusplus
// CHECK-NEXT: load volatile
// CHECK-NEXT: store volatile
(void)__builtin_choose_expr(0, i=i, j=j);
#endif
// CHECK-NEXT: load volatile
// CHECK-NEXT: icmp
// CHECK-NEXT: br i1
// CHECK: load volatile
// CHECK-NEXT: store volatile
// CHECK-NEXT: br label
// CHECK: load volatile
// CHECK-NEXT: store volatile
// CHECK-NEXT: br label
k ? (i=i) : (j=j);
// CHECK: phi
// CHECK-NEXT: load volatile
// CHECK-NEXT: load volatile
// CHECK-NEXT: store volatile
(void)(i,(i=i));
// CHECK-NEXT: load volatile
// CHECK-NEXT: store volatile
// CHECK-NEXT: load volatile
i=i,i;
// CHECK-NEXT: load volatile
// CHECK-NEXT: store volatile
// CHECK-NEXT: load volatile
// CHECK-NEXT: store volatile
(i=j,k=j);
// CHECK-NEXT: load volatile
// CHECK-NEXT: store volatile
// CHECK-NEXT: load volatile
(i=j,k);
// CHECK-NEXT: load volatile
// CHECK-NEXT: load volatile
(i,j);
// CHECK-NEXT: load volatile
// CHECK-NEXT: trunc
// CHECK-NEXT: store volatile
// CHECK-NEXT: sext
// CHECK-NEXT: store volatile
i=c=k;
// CHECK-NEXT: load volatile
// CHECK-NEXT: load volatile
// CHECK-NEXT: add nsw [[INT]]
// CHECK-NEXT: store volatile
i+=k;
// CHECK-NEXT: load volatile
// CHECK-NEXT: load volatile
ci;
#ifndef __cplusplus
// CHECK-NEXT: load volatile
// CHECK-NEXT: load volatile
(int)ci;
// CHECK-NEXT: load volatile
// CHECK-NEXT: load volatile
// CHECK-NEXT: icmp ne
// CHECK-NEXT: icmp ne
// CHECK-NEXT: or i1
(_Bool)ci;
#endif
// CHECK-NEXT: load volatile
// CHECK-NEXT: load volatile
// CHECK-NEXT: store volatile
// CHECK-NEXT: store volatile
ci=ci;
// CHECK-NEXT: load volatile
// CHECK-NEXT: load volatile
// CHECK-NEXT: store volatile
// CHECK-NEXT: store volatile
// CHECK-NEXT: store volatile
// CHECK-NEXT: store volatile
ci=ci=ci;
// CHECK-NEXT: [[T:%.*]] = load volatile [[INT]], ptr getelementptr inbounds nuw ([[CINT]], ptr @ci, i32 0, i32 1)
// CHECK-NEXT: store volatile [[INT]] [[T]], ptr getelementptr inbounds nuw ([[CINT]], ptr @ci, i32 0, i32 1)
// CHECK-NEXT: store volatile [[INT]] [[T]], ptr getelementptr inbounds nuw ([[CINT]], ptr @ci, i32 0, i32 1)
__imag ci = __imag ci = __imag ci;
// CHECK-NEXT: load volatile
// CHECK-NEXT: store volatile
__real (i = j);
// CHECK-NEXT: load volatile
__imag i;
// ============================================================
// FIXME: Test cases we get wrong.
// A use. We load all of a into a copy of a, then load i. gcc forgets to do
// the assignment.
// (a = a).i;
// ============================================================
// Test cases where we intentionally differ from gcc, due to suspected bugs in
// gcc.
// Not a use. gcc forgets to do the assignment.
// CHECK-NEXT: call void @llvm.memcpy{{.*}}, i1 true
// CHECK-NEXT: call void @llvm.memcpy{{.*}}, i1 true
((a=a),a);
// Not a use. gcc gets this wrong, it doesn't emit the copy!
// (void)(a=a);
// Not a use. gcc got this wrong in 4.2 and omitted the side effects
// entirely, but it is fixed in 4.4.0.
// CHECK-NEXT: load volatile
// CHECK-NEXT: store volatile
__imag (i = j);
#ifndef __cplusplus
// A use of the real part
// CHECK-NEXT: load volatile
// CHECK-NEXT: load volatile
// CHECK-NEXT: store volatile
// CHECK-NEXT: store volatile
// CHECK-NEXT: sitofp
(float)(ci=ci);
// Not a use, bug? gcc treats this as not a use, that's probably a bug due to
// tree folding ignoring volatile.
// CHECK-NEXT: load volatile
// CHECK-NEXT: load volatile
// CHECK-NEXT: store volatile
// CHECK-NEXT: store volatile
(int)(ci=ci);
#endif
// A use.
// CHECK-NEXT: load volatile
// CHECK-NEXT: store volatile
// CHECK-NEXT: sitofp
(float)(i=i);
// A use. gcc treats this as not a use, that's probably a bug due to tree
// folding ignoring volatile.
// CHECK-NEXT: load volatile
// CHECK-NEXT: store volatile
(int)(i=i);
// A use.
// CHECK-NEXT: load volatile
// CHECK-NEXT: store volatile
// CHECK-NEXT: sub
-(i=j);
// A use. gcc treats this a not a use, that's probably a bug due to tree
// folding ignoring volatile.
// CHECK-NEXT: load volatile
// CHECK-NEXT: store volatile
+(i=k);
// A use. gcc treats this a not a use, that's probably a bug due to tree
// folding ignoring volatile.
// CHECK-NEXT: load volatile
// CHECK-NEXT: load volatile
// CHECK-NEXT: store volatile
// CHECK-NEXT: store volatile
__real (ci=ci);
// A use.
// CHECK-NEXT: load volatile
// CHECK-NEXT: add
i + 0;
// A use.
// CHECK-NEXT: load volatile
// CHECK-NEXT: store volatile
// CHECK-NEXT: load volatile
// CHECK-NEXT: add
(i=j) + i;
// A use. gcc treats this as not a use, that's probably a bug due to tree
// folding ignoring volatile.
// CHECK-NEXT: load volatile
// CHECK-NEXT: store volatile
// CHECK-NEXT: add
(i=j) + 0;
#ifdef __cplusplus
(i,j)=k;
(j=k,i)=i;
struct { int x; } s, s1;
printf("s is at %p\n", &s);
printf("s is at %p\n", &(s = s1));
printf("s.x is at %p\n", &((s = s1).x));
#endif
}
extern volatile enum X x;
// CHECK-LABEL: define {{.*}}void @test1()
void test1(void) {
extern void test1_helper(void);
test1_helper();
// CHECK: call {{.*}}void @test1_helper()
// CHECK-NEXT: ret void
x;
(void) x;
return x;
}
// CHECK: define {{.*}} @test2()
int test2(void) {
// CHECK: load volatile i32, ptr
// CHECK-NEXT: load volatile i32, ptr
// CHECK-NEXT: load volatile i32, ptr
// CHECK-NEXT: add i32
// CHECK-NEXT: add i32
// CHECK-NEXT: store volatile i32
// CHECK-NEXT: ret i32
return i += ci;
}