summaryrefslogtreecommitdiff
path: root/test/CodeGen/ARM
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2015-06-09 19:06:30 +0000
committerDimitry Andric <dim@FreeBSD.org>2015-06-09 19:06:30 +0000
commit85d8b2bbe386bcfe669575d05b61482d7be07e5d (patch)
tree1dc5e75ab222a9ead44c699eceafab7a6ca7b310 /test/CodeGen/ARM
parent5a5ac124e1efaf208671f01c46edb15f29ed2a0b (diff)
Notes
Diffstat (limited to 'test/CodeGen/ARM')
-rw-r--r--test/CodeGen/ARM/atomic-ops-v8.ll14
-rw-r--r--test/CodeGen/ARM/build-attributes.ll6
-rw-r--r--test/CodeGen/ARM/ifcvt-callback.ll22
-rw-r--r--test/CodeGen/ARM/jump-table-islands-split.ll52
-rw-r--r--test/CodeGen/ARM/jump-table-islands.ll40
-rw-r--r--test/CodeGen/ARM/jumptable-label.ll4
-rw-r--r--test/CodeGen/ARM/ldrd.ll16
-rw-r--r--test/CodeGen/ARM/named-reg-alloc.ll2
-rw-r--r--test/CodeGen/ARM/named-reg-notareg.ll2
-rw-r--r--test/CodeGen/ARM/special-reg-acore.ll78
-rw-r--r--test/CodeGen/ARM/special-reg-mcore.ll143
-rw-r--r--test/CodeGen/ARM/special-reg.ll78
12 files changed, 444 insertions, 13 deletions
diff --git a/test/CodeGen/ARM/atomic-ops-v8.ll b/test/CodeGen/ARM/atomic-ops-v8.ll
index db5007b0758d..86287c1178db 100644
--- a/test/CodeGen/ARM/atomic-ops-v8.ll
+++ b/test/CodeGen/ARM/atomic-ops-v8.ll
@@ -664,7 +664,7 @@ define void @test_atomic_load_min_i64(i64 %offset) nounwind {
; CHECK: movt r[[ADDR]], :upper16:var64
; CHECK: .LBB{{[0-9]+}}_1:
-; CHECK: ldaexd [[OLD1:r[0-9]+]], [[OLD2:r[0-9]+]], [r[[ADDR]]]
+; CHECK: ldaexd [[OLD1:r[0-9]+|lr]], [[OLD2:r[0-9]+|lr]], [r[[ADDR]]]
; r0, r1 below is a reasonable guess but could change: it certainly comes into the
; function there.
; CHECK-ARM: mov [[LOCARRY:r[0-9]+|lr]], #0
@@ -782,7 +782,7 @@ define void @test_atomic_load_max_i64(i64 %offset) nounwind {
; CHECK: movt r[[ADDR]], :upper16:var64
; CHECK: .LBB{{[0-9]+}}_1:
-; CHECK: ldrexd [[OLD1:r[0-9]+]], [[OLD2:r[0-9]+]], [r[[ADDR]]]
+; CHECK: ldrexd [[OLD1:r[0-9]+]], [[OLD2:r[0-9]+|lr]], [r[[ADDR]]]
; r0, r1 below is a reasonable guess but could change: it certainly comes into the
; function there.
; CHECK-ARM: mov [[LOCARRY:r[0-9]+|lr]], #0
@@ -900,7 +900,7 @@ define void @test_atomic_load_umin_i64(i64 %offset) nounwind {
; CHECK: movt r[[ADDR]], :upper16:var64
; CHECK: .LBB{{[0-9]+}}_1:
-; CHECK: ldaexd [[OLD1:r[0-9]+]], [[OLD2:r[0-9]+]], [r[[ADDR]]]
+; CHECK: ldaexd [[OLD1:r[0-9]+|lr]], [[OLD2:r[0-9]+|lr]], [r[[ADDR]]]
; r0, r1 below is a reasonable guess but could change: it certainly comes into the
; function there.
; CHECK-ARM: mov [[LOCARRY:r[0-9]+|lr]], #0
@@ -1018,7 +1018,7 @@ define void @test_atomic_load_umax_i64(i64 %offset) nounwind {
; CHECK: movt r[[ADDR]], :upper16:var64
; CHECK: .LBB{{[0-9]+}}_1:
-; CHECK: ldaexd [[OLD1:r[0-9]+]], [[OLD2:r[0-9]+]], [r[[ADDR]]]
+; CHECK: ldaexd [[OLD1:r[0-9]+|lr]], [[OLD2:r[0-9]+|lr]], [r[[ADDR]]]
; r0, r1 below is a reasonable guess but could change: it certainly comes into the
; function there.
; CHECK-ARM: mov [[LOCARRY:r[0-9]+|lr]], #0
@@ -1146,10 +1146,12 @@ define void @test_atomic_cmpxchg_i64(i64 %wanted, i64 %new) nounwind {
; function there.
; CHECK-LE-DAG: eor{{(\.w)?}} [[MISMATCH_LO:r[0-9]+|lr]], [[OLD1]], r0
; CHECK-LE-DAG: eor{{(\.w)?}} [[MISMATCH_HI:r[0-9]+|lr]], [[OLD2]], r1
-; CHECK-LE: orrs{{(\.w)?}} {{r[0-9]+}}, [[MISMATCH_LO]], [[MISMATCH_HI]]
+; CHECK-ARM-LE: orrs{{(\.w)?}} {{r[0-9]+}}, [[MISMATCH_LO]], [[MISMATCH_HI]]
+; CHECK-THUMB-LE: orrs{{(\.w)?}} {{(r[0-9]+, )?}}[[MISMATCH_HI]], [[MISMATCH_LO]]
; CHECK-BE-DAG: eor{{(\.w)?}} [[MISMATCH_HI:r[0-9]+|lr]], [[OLD2]], r1
; CHECK-BE-DAG: eor{{(\.w)?}} [[MISMATCH_LO:r[0-9]+|lr]], [[OLD1]], r0
-; CHECK-BE: orrs{{(\.w)?}} {{r[0-9]+}}, [[MISMATCH_HI]], [[MISMATCH_LO]]
+; CHECK-ARM-BE: orrs{{(\.w)?}} {{r[0-9]+}}, [[MISMATCH_HI]], [[MISMATCH_LO]]
+; CHECK-THUMB-BE: orrs{{(\.w)?}} {{(r[0-9]+, )?}}[[MISMATCH_LO]], [[MISMATCH_HI]]
; CHECK-NEXT: bne .LBB{{[0-9]+}}_3
; CHECK-NEXT: BB#2:
; As above, r2, r3 is a reasonable guess.
diff --git a/test/CodeGen/ARM/build-attributes.ll b/test/CodeGen/ARM/build-attributes.ll
index 1982fa98ef41..e9de52a3e1a0 100644
--- a/test/CodeGen/ARM/build-attributes.ll
+++ b/test/CodeGen/ARM/build-attributes.ll
@@ -923,7 +923,7 @@
; CORTEX-M4-SOFT: .eabi_attribute 7, 77
; CORTEX-M4-SOFT: .eabi_attribute 8, 0
; CORTEX-M4-SOFT: .eabi_attribute 9, 2
-; CORTEX-M4-SOFT: .fpu vfpv4-d16
+; CORTEX-M4-SOFT: .fpu fpv4-sp-d16
; CORTEX-M4-SOFT-NOT: .eabi_attribute 19
;; We default to IEEE 754 compliance
; CORTEX-M4-SOFT: .eabi_attribute 20, 1
@@ -953,7 +953,7 @@
; CORTEX-M4-HARD: .eabi_attribute 7, 77
; CORTEX-M4-HARD: .eabi_attribute 8, 0
; CORTEX-M4-HARD: .eabi_attribute 9, 2
-; CORTEX-M4-HARD: .fpu vfpv4-d16
+; CORTEX-M4-HARD: .fpu fpv4-sp-d16
; CORTEX-M4-HARD-NOT: .eabi_attribute 19
;; We default to IEEE 754 compliance
; CORTEX-M4-HARD: .eabi_attribute 20, 1
@@ -984,7 +984,7 @@
; CORTEX-M7: .eabi_attribute 8, 0
; CORTEX-M7: .eabi_attribute 9, 2
; CORTEX-M7-SOFT-NOT: .fpu
-; CORTEX-M7-SINGLE: .fpu fpv5-d16
+; CORTEX-M7-SINGLE: .fpu fpv5-sp-d16
; CORTEX-M7-DOUBLE: .fpu fpv5-d16
; CORTEX-M7: .eabi_attribute 17, 1
; CORTEX-M7-NOT: .eabi_attribute 19
diff --git a/test/CodeGen/ARM/ifcvt-callback.ll b/test/CodeGen/ARM/ifcvt-callback.ll
new file mode 100644
index 000000000000..62a66e745b39
--- /dev/null
+++ b/test/CodeGen/ARM/ifcvt-callback.ll
@@ -0,0 +1,22 @@
+; RUN: llc -march thumb %s -o - | FileCheck %s
+
+; This test checks that if-conversion pass is unconditionally added to the pass
+; pipeline and is conditionally executed based on the per-function targert-cpu
+; attribute.
+
+; CHECK: ite eq
+
+define i32 @test_ifcvt(i32 %a, i32 %b) #0 {
+ %tmp2 = icmp eq i32 %a, 0
+ br i1 %tmp2, label %cond_false, label %cond_true
+
+cond_true:
+ %tmp5 = add i32 %b, 1
+ ret i32 %tmp5
+
+cond_false:
+ %tmp7 = add i32 %b, -1
+ ret i32 %tmp7
+}
+
+attributes #0 = { "target-cpu"="cortex-a8" }
diff --git a/test/CodeGen/ARM/jump-table-islands-split.ll b/test/CodeGen/ARM/jump-table-islands-split.ll
new file mode 100644
index 000000000000..deba21b4dbb1
--- /dev/null
+++ b/test/CodeGen/ARM/jump-table-islands-split.ll
@@ -0,0 +1,52 @@
+; RUN: llc -mtriple=thumbv7s-apple-ios8.0 -o - %s | FileCheck %s
+
+declare void @foo(double)
+declare i32 @llvm.arm.space(i32, i32)
+
+; The constpool entry used to call @foo should be directly between where we want
+; the tbb and its table. Fortunately, the flow is simple enough that we can
+; eliminate the entry calculation (ADD) and use the ADR as the base.
+;
+; I'm hoping this won't be fragile, but if it does break the most likely fix is
+; adjusting the @llvm.arm.space call slightly. If this happens too many times
+; the test should probably be removed.
+define i32 @test_jumptable_not_adjacent(i1 %tst, i32 %sw, i32 %l) {
+; CHECK-LABEL: test_jumptable_not_adjacent:
+; CHECK: vldr {{d[0-9]+}}, [[DBL_CONST:LCPI[0-9]+_[0-9]+]]
+; [...]
+; CHECK: adr.w r[[BASE:[0-9]+]], [[JUMP_TABLE:LJTI[0-9]+_[0-9]+]]
+; CHECK-NOT: r[[BASE]]
+
+; CHECK: [[TBB_KEY:LCPI[0-9]+_[0-9]+]]:
+; CHECK-NEXT: tbb [r[[BASE]], {{r[0-9]+}}]
+
+; CHECK: [[DBL_CONST]]:
+; CHECK: .long
+; CHECK: .long
+; CHECK: [[JUMP_TABLE]]:
+; CHECK: .byte (LBB{{[0-9]+}}_{{[0-9]+}}-([[TBB_KEY]]+4)
+
+ br label %complex
+
+complex:
+ call void @foo(double 12345.0)
+ call i32 @llvm.arm.space(i32 970, i32 undef)
+ switch i32 %sw, label %second [ i32 0, label %other
+ i32 1, label %third
+ i32 2, label %end
+ i32 3, label %other ]
+
+second:
+ ret i32 43
+third:
+ ret i32 0
+
+other:
+ call void @bar()
+ unreachable
+
+end:
+ ret i32 42
+}
+
+declare void @bar()
diff --git a/test/CodeGen/ARM/jump-table-islands.ll b/test/CodeGen/ARM/jump-table-islands.ll
new file mode 100644
index 000000000000..6b4f174c0928
--- /dev/null
+++ b/test/CodeGen/ARM/jump-table-islands.ll
@@ -0,0 +1,40 @@
+; RUN: llc -mtriple=armv7-apple-ios8.0 -o - %s | FileCheck %s
+
+%BigInt = type i5500
+
+define %BigInt @test_moved_jumptable(i1 %tst, i32 %sw, %BigInt %l) {
+; CHECK-LABEL: test_moved_jumptable:
+
+; CHECK: adr {{r[0-9]+}}, [[JUMP_TABLE:LJTI[0-9]+_[0-9]+]]
+; CHECK: b [[SKIP_TABLE:LBB[0-9]+_[0-9]+]]
+
+; CHECK: [[JUMP_TABLE]]:
+; CHECK: .data_region jt32
+; CHECK: .long LBB{{[0-9]+_[0-9]+}}-[[JUMP_TABLE]]
+
+; CHECK: [[SKIP_TABLE]]:
+; CHECK: add pc, {{r[0-9]+}}, {{r[0-9]+}}
+ br i1 %tst, label %simple, label %complex
+
+simple:
+ br label %end
+
+complex:
+ switch i32 %sw, label %simple [ i32 0, label %other
+ i32 1, label %third
+ i32 5, label %end
+ i32 6, label %other ]
+
+third:
+ ret %BigInt 0
+
+other:
+ call void @bar()
+ unreachable
+
+end:
+ %val = phi %BigInt [ %l, %complex ], [ -1, %simple ]
+ ret %BigInt %val
+}
+
+declare void @bar()
diff --git a/test/CodeGen/ARM/jumptable-label.ll b/test/CodeGen/ARM/jumptable-label.ll
index 49d698672f82..2ba90dc97365 100644
--- a/test/CodeGen/ARM/jumptable-label.ll
+++ b/test/CodeGen/ARM/jumptable-label.ll
@@ -2,8 +2,8 @@
; test that we print the label of a bb that is only used in a jump table.
-; CHECK: .long LBB0_2
-; CHECK: LBB0_2:
+; CHECK: .long [[JUMPTABLE_DEST:LBB[0-9]+_[0-9]+]]
+; CHECK: [[JUMPTABLE_DEST]]:
define i32 @calculate() {
entry:
diff --git a/test/CodeGen/ARM/ldrd.ll b/test/CodeGen/ARM/ldrd.ll
index 7ce846844e05..a8070ea68aa2 100644
--- a/test/CodeGen/ARM/ldrd.ll
+++ b/test/CodeGen/ARM/ldrd.ll
@@ -92,6 +92,22 @@ entry:
ret void
}
+declare void @extfunc(i32, i32, i32, i32)
+
+; CHECK-LABEL: Func2:
+; A8: ldrd
+; A8: blx
+; A8: pop
+define void @Func2(i32* %p) {
+entry:
+ %addr0 = getelementptr i32, i32* %p, i32 0
+ %addr1 = getelementptr i32, i32* %p, i32 1
+ %v0 = load i32, i32* %addr0
+ %v1 = load i32, i32* %addr1
+ ; try to force %v0/%v1 into non-adjacent registers
+ call void @extfunc(i32 %v0, i32 0, i32 0, i32 %v1)
+ ret void
+}
declare void @llvm.lifetime.start(i64, i8* nocapture) nounwind
declare void @llvm.lifetime.end(i64, i8* nocapture) nounwind
diff --git a/test/CodeGen/ARM/named-reg-alloc.ll b/test/CodeGen/ARM/named-reg-alloc.ll
index 380cf39734ff..d41fa64882c8 100644
--- a/test/CodeGen/ARM/named-reg-alloc.ll
+++ b/test/CodeGen/ARM/named-reg-alloc.ll
@@ -4,7 +4,7 @@
define i32 @get_stack() nounwind {
entry:
; FIXME: Include an allocatable-specific error message
-; CHECK: Invalid register name global variable
+; CHECK: Invalid register name "r5".
%sp = call i32 @llvm.read_register.i32(metadata !0)
ret i32 %sp
}
diff --git a/test/CodeGen/ARM/named-reg-notareg.ll b/test/CodeGen/ARM/named-reg-notareg.ll
index 3ac03f4fdaaa..45cb38f30f35 100644
--- a/test/CodeGen/ARM/named-reg-notareg.ll
+++ b/test/CodeGen/ARM/named-reg-notareg.ll
@@ -3,7 +3,7 @@
define i32 @get_stack() nounwind {
entry:
-; CHECK: Invalid register name global variable
+; CHECK: Invalid register name "notareg".
%sp = call i32 @llvm.read_register.i32(metadata !0)
ret i32 %sp
}
diff --git a/test/CodeGen/ARM/special-reg-acore.ll b/test/CodeGen/ARM/special-reg-acore.ll
new file mode 100644
index 000000000000..3d65ff44bfb0
--- /dev/null
+++ b/test/CodeGen/ARM/special-reg-acore.ll
@@ -0,0 +1,78 @@
+; RUN: llc < %s -mtriple=arm-none-eabi -mcpu=cortex-a8 2>&1 | FileCheck %s --check-prefix=ACORE
+; RUN: not llc < %s -mtriple=thumb-none-eabi -mcpu=cortex-m4 2>&1 | FileCheck %s --check-prefix=MCORE
+
+; MCORE: LLVM ERROR: Invalid register name "cpsr".
+
+define i32 @read_cpsr() nounwind {
+ ; ACORE-LABEL: read_cpsr:
+ ; ACORE: mrs r0, apsr
+ %reg = call i32 @llvm.read_register.i32(metadata !1)
+ ret i32 %reg
+}
+
+define i32 @read_aclass_registers() nounwind {
+entry:
+ ; ACORE-LABEL: read_aclass_registers:
+ ; ACORE: mrs r0, apsr
+ ; ACORE: mrs r1, spsr
+
+ %0 = call i32 @llvm.read_register.i32(metadata !0)
+ %1 = call i32 @llvm.read_register.i32(metadata !1)
+ %add1 = add i32 %1, %0
+ %2 = call i32 @llvm.read_register.i32(metadata !2)
+ %add2 = add i32 %add1, %2
+ ret i32 %add2
+}
+
+define void @write_aclass_registers(i32 %x) nounwind {
+entry:
+ ; ACORE-LABEL: write_aclass_registers:
+ ; ACORE: msr APSR_nzcvq, r0
+ ; ACORE: msr APSR_g, r0
+ ; ACORE: msr APSR_nzcvqg, r0
+ ; ACORE: msr CPSR_c, r0
+ ; ACORE: msr CPSR_x, r0
+ ; ACORE: msr APSR_g, r0
+ ; ACORE: msr APSR_nzcvq, r0
+ ; ACORE: msr CPSR_fsxc, r0
+ ; ACORE: msr SPSR_c, r0
+ ; ACORE: msr SPSR_x, r0
+ ; ACORE: msr SPSR_s, r0
+ ; ACORE: msr SPSR_f, r0
+ ; ACORE: msr SPSR_fsxc, r0
+
+ call void @llvm.write_register.i32(metadata !3, i32 %x)
+ call void @llvm.write_register.i32(metadata !4, i32 %x)
+ call void @llvm.write_register.i32(metadata !5, i32 %x)
+ call void @llvm.write_register.i32(metadata !6, i32 %x)
+ call void @llvm.write_register.i32(metadata !7, i32 %x)
+ call void @llvm.write_register.i32(metadata !8, i32 %x)
+ call void @llvm.write_register.i32(metadata !9, i32 %x)
+ call void @llvm.write_register.i32(metadata !10, i32 %x)
+ call void @llvm.write_register.i32(metadata !11, i32 %x)
+ call void @llvm.write_register.i32(metadata !12, i32 %x)
+ call void @llvm.write_register.i32(metadata !13, i32 %x)
+ call void @llvm.write_register.i32(metadata !14, i32 %x)
+ call void @llvm.write_register.i32(metadata !15, i32 %x)
+ ret void
+}
+
+declare i32 @llvm.read_register.i32(metadata) nounwind
+declare void @llvm.write_register.i32(metadata, i32) nounwind
+
+!0 = !{!"apsr"}
+!1 = !{!"cpsr"}
+!2 = !{!"spsr"}
+!3 = !{!"apsr_nzcvq"}
+!4 = !{!"apsr_g"}
+!5 = !{!"apsr_nzcvqg"}
+!6 = !{!"cpsr_c"}
+!7 = !{!"cpsr_x"}
+!8 = !{!"cpsr_s"}
+!9 = !{!"cpsr_f"}
+!10 = !{!"cpsr_cxsf"}
+!11 = !{!"spsr_c"}
+!12 = !{!"spsr_x"}
+!13 = !{!"spsr_s"}
+!14 = !{!"spsr_f"}
+!15 = !{!"spsr_cxsf"}
diff --git a/test/CodeGen/ARM/special-reg-mcore.ll b/test/CodeGen/ARM/special-reg-mcore.ll
new file mode 100644
index 000000000000..686da0f6b839
--- /dev/null
+++ b/test/CodeGen/ARM/special-reg-mcore.ll
@@ -0,0 +1,143 @@
+; RUN: llc < %s -mtriple=thumb-none-eabi -mcpu=cortex-m4 2>&1 | FileCheck %s --check-prefix=MCORE
+; RUN: not llc < %s -mtriple=thumb-none-eabi -mcpu=cortex-m3 2>&1 | FileCheck %s --check-prefix=M3CORE
+; RUN: not llc < %s -mtriple=arm-none-eabi -mcpu=cortex-a8 2>&1 | FileCheck %s --check-prefix=ACORE
+
+; ACORE: LLVM ERROR: Invalid register name "control".
+; M3CORE: LLVM ERROR: Invalid register name "control".
+
+define i32 @read_mclass_registers() nounwind {
+entry:
+ ; MCORE-LABEL: read_mclass_registers:
+ ; MCORE: mrs r0, apsr
+ ; MCORE: mrs r1, iapsr
+ ; MCORE: mrs r1, eapsr
+ ; MCORE: mrs r1, xpsr
+ ; MCORE: mrs r1, ipsr
+ ; MCORE: mrs r1, epsr
+ ; MCORE: mrs r1, iepsr
+ ; MCORE: mrs r1, msp
+ ; MCORE: mrs r1, psp
+ ; MCORE: mrs r1, primask
+ ; MCORE: mrs r1, basepri
+ ; MCORE: mrs r1, basepri_max
+ ; MCORE: mrs r1, faultmask
+ ; MCORE: mrs r1, control
+
+ %0 = call i32 @llvm.read_register.i32(metadata !0)
+ %1 = call i32 @llvm.read_register.i32(metadata !4)
+ %add1 = add i32 %1, %0
+ %2 = call i32 @llvm.read_register.i32(metadata !8)
+ %add2 = add i32 %add1, %2
+ %3 = call i32 @llvm.read_register.i32(metadata !12)
+ %add3 = add i32 %add2, %3
+ %4 = call i32 @llvm.read_register.i32(metadata !16)
+ %add4 = add i32 %add3, %4
+ %5 = call i32 @llvm.read_register.i32(metadata !17)
+ %add5 = add i32 %add4, %5
+ %6 = call i32 @llvm.read_register.i32(metadata !18)
+ %add6 = add i32 %add5, %6
+ %7 = call i32 @llvm.read_register.i32(metadata !19)
+ %add7 = add i32 %add6, %7
+ %8 = call i32 @llvm.read_register.i32(metadata !20)
+ %add8 = add i32 %add7, %8
+ %9 = call i32 @llvm.read_register.i32(metadata !21)
+ %add9 = add i32 %add8, %9
+ %10 = call i32 @llvm.read_register.i32(metadata !22)
+ %add10 = add i32 %add9, %10
+ %11 = call i32 @llvm.read_register.i32(metadata !23)
+ %add11 = add i32 %add10, %11
+ %12 = call i32 @llvm.read_register.i32(metadata !24)
+ %add12 = add i32 %add11, %12
+ %13 = call i32 @llvm.read_register.i32(metadata !25)
+ %add13 = add i32 %add12, %13
+ ret i32 %add13
+}
+
+define void @write_mclass_registers(i32 %x) nounwind {
+entry:
+ ; MCORE-LABEL: write_mclass_registers:
+ ; MCORE: msr apsr_nzcvqg, r0
+ ; MCORE: msr apsr_nzcvq, r0
+ ; MCORE: msr apsr_g, r0
+ ; MCORE: msr apsr_nzcvqg, r0
+ ; MCORE: msr iapsr_nzcvqg, r0
+ ; MCORE: msr iapsr_nzcvq, r0
+ ; MCORE: msr iapsr_g, r0
+ ; MCORE: msr iapsr_nzcvqg, r0
+ ; MCORE: msr eapsr_nzcvqg, r0
+ ; MCORE: msr eapsr_nzcvq, r0
+ ; MCORE: msr eapsr_g, r0
+ ; MCORE: msr eapsr_nzcvqg, r0
+ ; MCORE: msr xpsr_nzcvqg, r0
+ ; MCORE: msr xpsr_nzcvq, r0
+ ; MCORE: msr xpsr_g, r0
+ ; MCORE: msr xpsr_nzcvqg, r0
+ ; MCORE: msr ipsr, r0
+ ; MCORE: msr epsr, r0
+ ; MCORE: msr iepsr, r0
+ ; MCORE: msr msp, r0
+ ; MCORE: msr psp, r0
+ ; MCORE: msr primask, r0
+ ; MCORE: msr basepri, r0
+ ; MCORE: msr basepri_max, r0
+ ; MCORE: msr faultmask, r0
+ ; MCORE: msr control, r0
+
+ call void @llvm.write_register.i32(metadata !0, i32 %x)
+ call void @llvm.write_register.i32(metadata !1, i32 %x)
+ call void @llvm.write_register.i32(metadata !2, i32 %x)
+ call void @llvm.write_register.i32(metadata !3, i32 %x)
+ call void @llvm.write_register.i32(metadata !4, i32 %x)
+ call void @llvm.write_register.i32(metadata !5, i32 %x)
+ call void @llvm.write_register.i32(metadata !6, i32 %x)
+ call void @llvm.write_register.i32(metadata !7, i32 %x)
+ call void @llvm.write_register.i32(metadata !8, i32 %x)
+ call void @llvm.write_register.i32(metadata !9, i32 %x)
+ call void @llvm.write_register.i32(metadata !10, i32 %x)
+ call void @llvm.write_register.i32(metadata !11, i32 %x)
+ call void @llvm.write_register.i32(metadata !12, i32 %x)
+ call void @llvm.write_register.i32(metadata !13, i32 %x)
+ call void @llvm.write_register.i32(metadata !14, i32 %x)
+ call void @llvm.write_register.i32(metadata !15, i32 %x)
+ call void @llvm.write_register.i32(metadata !16, i32 %x)
+ call void @llvm.write_register.i32(metadata !17, i32 %x)
+ call void @llvm.write_register.i32(metadata !18, i32 %x)
+ call void @llvm.write_register.i32(metadata !19, i32 %x)
+ call void @llvm.write_register.i32(metadata !20, i32 %x)
+ call void @llvm.write_register.i32(metadata !21, i32 %x)
+ call void @llvm.write_register.i32(metadata !22, i32 %x)
+ call void @llvm.write_register.i32(metadata !23, i32 %x)
+ call void @llvm.write_register.i32(metadata !24, i32 %x)
+ call void @llvm.write_register.i32(metadata !25, i32 %x)
+ ret void
+}
+
+declare i32 @llvm.read_register.i32(metadata) nounwind
+declare void @llvm.write_register.i32(metadata, i32) nounwind
+
+!0 = !{!"apsr"}
+!1 = !{!"apsr_nzcvq"}
+!2 = !{!"apsr_g"}
+!3 = !{!"apsr_nzcvqg"}
+!4 = !{!"iapsr"}
+!5 = !{!"iapsr_nzcvq"}
+!6 = !{!"iapsr_g"}
+!7 = !{!"iapsr_nzcvqg"}
+!8 = !{!"eapsr"}
+!9 = !{!"eapsr_nzcvq"}
+!10 = !{!"eapsr_g"}
+!11 = !{!"eapsr_nzcvqg"}
+!12 = !{!"xpsr"}
+!13 = !{!"xpsr_nzcvq"}
+!14 = !{!"xpsr_g"}
+!15 = !{!"xpsr_nzcvqg"}
+!16 = !{!"ipsr"}
+!17 = !{!"epsr"}
+!18 = !{!"iepsr"}
+!19 = !{!"msp"}
+!20 = !{!"psp"}
+!21 = !{!"primask"}
+!22 = !{!"basepri"}
+!23 = !{!"basepri_max"}
+!24 = !{!"faultmask"}
+!25 = !{!"control"}
diff --git a/test/CodeGen/ARM/special-reg.ll b/test/CodeGen/ARM/special-reg.ll
new file mode 100644
index 000000000000..7ccb490f5d4a
--- /dev/null
+++ b/test/CodeGen/ARM/special-reg.ll
@@ -0,0 +1,78 @@
+; RUN: llc < %s -mtriple=arm-none-eabi -mcpu=cortex-a8 2>&1 | FileCheck %s --check-prefix=ARM --check-prefix=ACORE
+; RUN: llc < %s -mtriple=thumb-none-eabi -mcpu=cortex-m4 2>&1 | FileCheck %s --check-prefix=ARM --check-prefix=MCORE
+
+define i32 @read_i32_encoded_register() nounwind {
+entry:
+; ARM-LABEL: read_i32_encoded_register:
+; ARM: mrc p1, #2, r0, c3, c4, #5
+ %reg = call i32 @llvm.read_register.i32(metadata !0)
+ ret i32 %reg
+}
+
+define i64 @read_i64_encoded_register() nounwind {
+entry:
+; ARM-LABEL: read_i64_encoded_register:
+; ARM: mrrc p1, #2, r0, r1, c3
+ %reg = call i64 @llvm.read_register.i64(metadata !1)
+ ret i64 %reg
+}
+
+define i32 @read_apsr() nounwind {
+entry:
+; ARM-LABEL: read_apsr:
+; ARM: mrs r0, apsr
+ %reg = call i32 @llvm.read_register.i32(metadata !2)
+ ret i32 %reg
+}
+
+define i32 @read_fpscr() nounwind {
+entry:
+; ARM-LABEL: read_fpscr:
+; ARM: vmrs r0, fpscr
+ %reg = call i32 @llvm.read_register.i32(metadata !3)
+ ret i32 %reg
+}
+
+define void @write_i32_encoded_register(i32 %x) nounwind {
+entry:
+; ARM-LABEL: write_i32_encoded_register:
+; ARM: mcr p1, #2, r0, c3, c4, #5
+ call void @llvm.write_register.i32(metadata !0, i32 %x)
+ ret void
+}
+
+define void @write_i64_encoded_register(i64 %x) nounwind {
+entry:
+; ARM-LABEL: write_i64_encoded_register:
+; ARM: mcrr p1, #2, r0, r1, c3
+ call void @llvm.write_register.i64(metadata !1, i64 %x)
+ ret void
+}
+
+define void @write_apsr(i32 %x) nounwind {
+entry:
+; ARM-LABEL: write_apsr:
+; ACORE: msr APSR_nzcvq, r0
+; MCORE: msr apsr_nzcvq, r0
+ call void @llvm.write_register.i32(metadata !4, i32 %x)
+ ret void
+}
+
+define void @write_fpscr(i32 %x) nounwind {
+entry:
+; ARM-LABEL: write_fpscr:
+; ARM: vmsr fpscr, r0
+ call void @llvm.write_register.i32(metadata !3, i32 %x)
+ ret void
+}
+
+declare i32 @llvm.read_register.i32(metadata) nounwind
+declare i64 @llvm.read_register.i64(metadata) nounwind
+declare void @llvm.write_register.i32(metadata, i32) nounwind
+declare void @llvm.write_register.i64(metadata, i64) nounwind
+
+!0 = !{!"cp1:2:c3:c4:5"}
+!1 = !{!"cp1:2:c3"}
+!2 = !{!"apsr"}
+!3 = !{!"fpscr"}
+!4 = !{!"apsr_nzcvq"}