1; RUN: llc -mtriple armv7a--none-eabi < %s | FileCheck %s --check-prefix=CHECK --check-prefix=DISABLED 2; RUN: llc -mtriple armv7a--none-eabi < %s -enable-ipra | FileCheck %s --check-prefix=CHECK --check-prefix=ENABLED 3 4define void @leaf() { 5entry: 6 tail call void asm sideeffect "", ""() 7 ret void 8} 9 10define void @leaf_r3() { 11entry: 12 tail call void asm sideeffect "", "~{r3}"() 13 ret void 14} 15 16define void @leaf_r4() { 17entry: 18 tail call void asm sideeffect "", "~{r4}"() 19 ret void 20} 21 22define void @leaf_s0() { 23entry: 24 tail call void asm sideeffect "", "~{s0}"() 25 ret void 26} 27 28define void @leaf_d0() { 29entry: 30 tail call void asm sideeffect "", "~{d0}"() 31 ret void 32} 33 34; r3 is normally caller-saved, but with IPRA we can see that it isn't used in 35; the callee, so can leave a live value in it. 36define void @test_r3_presrved() { 37; CHECK-LABEL: test_r3_presrved: 38entry: 39; CHECK: ASM1: r3 40; DISABLED: mov [[TEMP:r[0-9]+]], r3 41; ENABLED-NOT: r3 42; CHECK: bl leaf 43; DISABLED: mov r3, [[TEMP]] 44; ENABLED-NOT: r3 45; CHECK: ASM2: r3 46 %a = tail call i32 asm sideeffect "// ASM1: $0", "={r3},0"(i32 undef) 47 tail call void @leaf() 48 %b = tail call i32 asm sideeffect "// ASM2: $0", "={r3},0"(i32 %a) 49 ret void 50} 51 52; Same as above, but r3 is clobbered in the callee, so it is clobbered by the 53; call as normal. 54define void @test_r3_clobbered() { 55; CHECK-LABEL: test_r3_clobbered: 56entry: 57; CHECK: ASM1: r3 58; CHECK: mov [[TEMP:r[0-9]+]], r3 59; CHECK: bl leaf 60; CHECK: mov r3, [[TEMP]] 61; CHECK: ASM2: r3 62 %a = tail call i32 asm sideeffect "// ASM1: $0", "={r3},0"(i32 undef) 63 tail call void @leaf_r3() 64 %b = tail call i32 asm sideeffect "// ASM2: $0", "={r3},0"(i32 %a) 65 ret void 66} 67 68; r4 is a callee-saved register, so IPRA has no effect. 69define void @test_r4_preserved() { 70; CHECK-LABEL: test_r4_preserved: 71entry: 72; CHECK: ASM1: r4 73; CHECK-NOT: r4 74; CHECK: bl leaf 75; CHECK-NOT: r4 76; CHECK: ASM2: r4 77 %a = tail call i32 asm sideeffect "// ASM1: $0", "={r4},0"(i32 undef) 78 tail call void @leaf() 79 %b = tail call i32 asm sideeffect "// ASM2: $0", "={r4},0"(i32 %a) 80 ret void 81} 82define void @test_r4_clobbered() { 83; CHECK-LABEL: test_r4_clobbered: 84entry: 85; CHECK: ASM1: r4 86; CHECK-NOT: r4 87; CHECK: bl leaf_r4 88; CHECK-NOT: r4 89; CHECK: ASM2: r4 90 %a = tail call i32 asm sideeffect "// ASM1: $0", "={r4},0"(i32 undef) 91 tail call void @leaf_r4() 92 %b = tail call i32 asm sideeffect "// ASM2: $0", "={r4},0"(i32 %a) 93 ret void 94} 95 96; r12 is the intra-call scratch register, so we have to assume it is clobbered 97; even if we can see that the callee does not touch it. 98define void @test_r12() { 99; CHECK-LABEL: test_r12: 100entry: 101; CHECK: ASM1: r12 102; CHECK: mov [[TEMP:r[0-9]+]], r12 103; CHECK: bl leaf 104; CHECK: mov r12, [[TEMP]] 105; CHECK: ASM2: r12 106 %a = tail call i32 asm sideeffect "// ASM1: $0", "={r12},0"(i32 undef) 107 tail call void @leaf() 108 %b = tail call i32 asm sideeffect "// ASM2: $0", "={r12},0"(i32 %a) 109 ret void 110} 111 112; s0 and d0 are caller-saved, IPRA allows us to keep them live in the caller if 113; the callee doesn't modify them. 114define void @test_s0_preserved() { 115; CHECK-LABEL: test_s0_preserved: 116entry: 117; CHECK: ASM1: s0 118; DISABLED: vmov.f32 [[TEMP:s[0-9]+]], s0 119; ENABLED-NOT: s0 120; CHECK: bl leaf 121; DISABLED: vmov.f32 s0, [[TEMP]] 122; ENABLED-NOT: s0 123; CHECK: ASM2: s0 124 %a = tail call float asm sideeffect "// ASM1: $0", "={s0},0"(float undef) 125 tail call void @leaf() 126 %b = tail call float asm sideeffect "// ASM2: $0", "={s0},0"(float %a) 127 ret void 128} 129 130define void @test_s0_clobbered() { 131; CHECK-LABEL: test_s0_clobbered: 132entry: 133; CHECK: ASM1: s0 134; CHECK: vmov.f32 [[TEMP:s[0-9]+]], s0 135; CHECK: bl leaf_s0 136; CHECK: vmov.f32 s0, [[TEMP]] 137; CHECK: ASM2: s0 138 %a = tail call float asm sideeffect "// ASM1: $0", "={s0},0"(float undef) 139 tail call void @leaf_s0() 140 %b = tail call float asm sideeffect "// ASM2: $0", "={s0},0"(float %a) 141 ret void 142} 143 144define void @test_d0_preserved() { 145; CHECK-LABEL: test_d0_preserved: 146entry: 147; CHECK: ASM1: d0 148; DISABLED: vmov.f64 [[TEMP:d[0-9]+]], d0 149; ENABLED-NOT: d0 150; CHECK: bl leaf 151; DISABLED: vmov.f64 d0, [[TEMP]] 152; ENABLED-NOT: d0 153; CHECK: ASM2: d0 154 %a = tail call double asm sideeffect "// ASM1: $0", "={d0},0"(double undef) 155 tail call void @leaf() 156 %b = tail call double asm sideeffect "// ASM2: $0", "={d0},0"(double %a) 157 ret void 158} 159 160define void @test_d0_clobbered() { 161; CHECK-LABEL: test_d0_clobbered: 162entry: 163; CHECK: ASM1: d0 164; CHECK: vmov.f64 [[TEMP:d[0-9]+]], d0 165; CHECK: bl leaf_d0 166; CHECK: vmov.f64 d0, [[TEMP]] 167; CHECK: ASM2: d0 168 %a = tail call double asm sideeffect "// ASM1: $0", "={d0},0"(double undef) 169 tail call void @leaf_d0() 170 %b = tail call double asm sideeffect "// ASM2: $0", "={d0},0"(double %a) 171 ret void 172} 173 174; s0 and d0 overlap, so clobbering one in the callee prevents the other from 175; being kept live across the call. 176define void @test_s0_clobber_d0() { 177; CHECK-LABEL: test_s0_clobber_d0: 178entry: 179; CHECK: ASM1: s0 180; CHECK: vmov.f32 [[TEMP:s[0-9]+]], s0 181; CHECK: bl leaf_d0 182; CHECK: vmov.f32 s0, [[TEMP]] 183; CHECK: ASM2: s0 184 %a = tail call float asm sideeffect "// ASM1: $0", "={s0},0"(float undef) 185 tail call void @leaf_d0() 186 %b = tail call float asm sideeffect "// ASM2: $0", "={s0},0"(float %a) 187 ret void 188} 189 190define void @test_d0_clobber_s0() { 191; CHECK-LABEL: test_d0_clobber_s0: 192entry: 193; CHECK: ASM1: d0 194; CHECK: vmov.f64 [[TEMP:d[0-9]+]], d0 195; CHECK: bl leaf_s0 196; CHECK: vmov.f64 d0, [[TEMP]] 197; CHECK: ASM2: d0 198 %a = tail call double asm sideeffect "// ASM1: $0", "={d0},0"(double undef) 199 tail call void @leaf_s0() 200 %b = tail call double asm sideeffect "// ASM2: $0", "={d0},0"(double %a) 201 ret void 202} 203