1; RUN: llc -mtriple=aarch64-none-linux-gnu < %s -o - | FileCheck %s
2
3; The following functions test the use case where an X constraint is used to
4; add a dependency between an assembly instruction (vmsr in this case) and
5; another instruction. In each function, we use a different type for the
6; X constraint argument.
7;
8; We can something similar from the following C code:
9; double f1(double f, int pscr_value) {
10;   asm volatile("msr fpsr,%1" : "=X" ((f)): "r" (pscr_value));
11;   return f+f;
12; }
13
14; CHECK-LABEL: f1
15; CHECK: msr FPSR
16; CHECK: fadd d
17
18define  double @f1(double %f, i32 %pscr_value) {
19entry:
20  %f.addr = alloca double, align 8
21  store double %f, double* %f.addr, align 8
22  call void asm sideeffect "msr fpsr,$1", "=*X,r"(double* nonnull %f.addr, i32 %pscr_value) nounwind
23  %0 = load double, double* %f.addr, align 8
24  %add = fadd double %0, %0
25  ret double %add
26}
27
28; int f2(int f, int pscr_value) {
29;   asm volatile("msr fpsr,$1" : "=X" ((f)): "r" (pscr_value));
30;   return f*f;
31; }
32
33; CHECK-LABEL: f2
34; CHECK: msr FPSR
35; CHECK: mul
36define  i32 @f2(i32 %f, i32 %pscr_value) {
37entry:
38  %f.addr = alloca i32, align 4
39  store i32 %f, i32* %f.addr, align 4
40  call void asm sideeffect "msr fpsr,$1", "=*X,r"(i32* nonnull %f.addr, i32 %pscr_value) nounwind
41  %0 = load i32, i32* %f.addr, align 4
42  %mul = mul i32 %0, %0
43  ret i32 %mul
44}
45
46; typedef signed char int8_t;
47; typedef __attribute__((neon_vector_type(8))) int8_t int8x8_t;
48; void f3 (void)
49; {
50;   int8x8_t vector_res_int8x8;
51;   unsigned int fpscr;
52;   asm volatile ("msr fpsr,$1" : "=X" ((vector_res_int8x8)) : "r" (fpscr));
53;   return vector_res_int8x8 * vector_res_int8x8;
54; }
55
56; CHECK-LABEL: f3
57; CHECK: msr FPSR
58; CHECK: mul
59define  <8 x i8> @f3() {
60entry:
61  %vector_res_int8x8 = alloca <8 x i8>, align 8
62  %0 = getelementptr inbounds <8 x i8>, <8 x i8>* %vector_res_int8x8, i32 0, i32 0
63  call void asm sideeffect "msr fpsr,$1", "=*X,r"(<8 x i8>* nonnull %vector_res_int8x8, i32 undef) nounwind
64  %1 = load <8 x i8>, <8 x i8>* %vector_res_int8x8, align 8
65  %mul = mul <8 x i8> %1, %1
66  ret <8 x i8> %mul
67}
68
69; We can emit integer constants.
70; We can get this from:
71; void f() {
72;   int x = 2;
73;   asm volatile ("add x0, x0, %0" : : "X" (x));
74; }
75;
76; CHECK-LABEL: f4
77; CHECK: add x0, x0, #2
78define void @f4() {
79entry:
80  tail call void asm sideeffect "add x0, x0, $0", "X"(i32 2)
81  ret void
82}
83
84; We can emit function labels. This is equivalent to the following C code:
85; void f(void) {
86;   void (*x)(void) = &foo;
87;   asm volatile ("bl %0" : : "X" (x));
88; }
89; CHECK-LABEL: f5
90; CHECK: bl f4
91define void @f5() {
92entry:
93  tail call void asm sideeffect "bl $0", "X"(void ()* nonnull @f4)
94  ret void
95}
96
97declare void @foo(...)
98
99; This tests the behavior of the X constraint when used on functions pointers,
100; or functions with a cast. In the first asm call we figure out that this
101; is a function pointer and emit the label. However, in the second asm call
102; we can't see through the bitcast and we end up having to lower this constraint
103; to something else. This is not ideal, but it is a correct behaviour according
104; to the definition of the X constraint.
105;
106; In this case (and other cases where we could have emitted something else),
107; what we're doing with the X constraint is not particularly useful either,
108; since the user could have used "r" in this situation for the same effect.
109
110; CHECK-LABEL: f6
111; CHECK: bl foo
112; CHECK: br x
113
114define void @f6() nounwind {
115entry:
116  tail call void asm sideeffect "bl $0", "X"(void (...)* @foo) nounwind
117  tail call void asm sideeffect "br $0", "X"(void (...)* bitcast (void ()* @f4 to void (...)*)) nounwind
118  ret void
119}
120
121; The following IR can be generated from C code with a function like:
122; void a() {
123;   void* a = &&A;
124;   asm volatile ("bl %0" : : "X" (a));
125;  A:
126;   return;
127; }
128;
129; Ideally this would give the block address of bb, but it requires us to see
130; through blockaddress, which we can't do at the moment. This might break some
131; existing use cases where a user would expect to get a block label and instead
132; gets the block address in a register. However, note that according to the
133; "no constraints" definition this behaviour is correct (although not very nice).
134
135; CHECK-LABEL: f7
136; CHECK: bl
137define void @f7() {
138  call void asm sideeffect "br $0", "X"( i8* blockaddress(@f7, %bb) )
139  br label %bb
140bb:
141  ret void
142}
143
144; If we use a constraint "=*X", we should get a store back to *%x (in x0).
145; CHECK-LABEL: f8
146; CHECK: add	 [[Dest:x[0-9]+]], x0, x0
147; CHECK: str	[[Dest]], [x0]
148define void @f8(i64 *%x) {
149entry:
150  tail call void asm sideeffect "add $0, x0, x0", "=*X"(i64 *%x)
151  ret void
152}
153