diff --git a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/lanai-reg-names.ll b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/lanai-reg-names.ll new file mode 100644 --- /dev/null +++ b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/lanai-reg-names.ll @@ -0,0 +1,32 @@ +; RUN: llc -mtriple=lanai < %s | FileCheck %s + +define i64 @i64_test(i64 %i) nounwind readnone { + %loc = alloca i64 + %j = load i64, i64 * %loc + %r = add i64 %i, %j + ret i64 %r +} + +define i64 @i32_test(i32 %i) nounwind readnone { + %loc = alloca i32 + %j = load i32, i32 * %loc + %r = add i32 %i, %j + %ext = zext i32 %r to i64 + ret i64 %ext +} + +define i64 @i16_test(i16 %i) nounwind readnone { + %loc = alloca i16 + %j = load i16, i16 * %loc + %r = add i16 %i, %j + %ext = zext i16 %r to i64 + ret i64 %ext +} + +define i64 @i8_test(i8 %i) nounwind readnone { + %loc = alloca i8 + %j = load i8, i8 * %loc + %r = add i8 %i, %j + %ext = zext i8 %r to i64 + ret i64 %ext +} diff --git a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/lanai-reg-names.ll.noscrub.expected b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/lanai-reg-names.ll.noscrub.expected new file mode 100644 --- /dev/null +++ b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/lanai-reg-names.ll.noscrub.expected @@ -0,0 +1,95 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=lanai < %s | FileCheck %s + +define i64 @i64_test(i64 %i) nounwind readnone { +; CHECK-LABEL: i64_test: +; CHECK: ! %bb.0: +; CHECK-NEXT: st %fp, [--%sp] +; CHECK-NEXT: add %sp, 0x8, %fp +; CHECK-NEXT: sub %sp, 0x10, %sp +; CHECK-NEXT: ld 4[%fp], %r3 +; CHECK-NEXT: ld 0[%fp], %r9 +; CHECK-NEXT: sub %fp, 0x10, %r12 +; CHECK-NEXT: or %r12, 0x4, %r12 +; CHECK-NEXT: ld -16[%fp], %r13 +; CHECK-NEXT: ld 0[%r12], %r12 +; CHECK-NEXT: add %r9, %r13, %r13 +; CHECK-NEXT: add %r3, %r12, %r9 +; CHECK-NEXT: sub.f %r9, %r3, %r0 +; CHECK-NEXT: sult %r3 +; CHECK-NEXT: add %r13, %r3, %rv +; CHECK-NEXT: ld -4[%fp], %pc ! return +; CHECK-NEXT: add %fp, 0x0, %sp +; CHECK-NEXT: ld -8[%fp], %fp + %loc = alloca i64 + %j = load i64, i64 * %loc + %r = add i64 %i, %j + ret i64 %r +} + +define i64 @i32_test(i32 %i) nounwind readnone { +; CHECK-LABEL: i32_test: +; CHECK: ! %bb.0: +; CHECK-NEXT: st %fp, [--%sp] +; CHECK-NEXT: add %sp, 0x8, %fp +; CHECK-NEXT: sub %sp, 0x10, %sp +; CHECK-NEXT: ld 0[%fp], %r3 +; CHECK-NEXT: ld -12[%fp], %r9 +; CHECK-NEXT: add %r3, %r9, %r9 +; CHECK-NEXT: or %r0, 0x0, %rv +; CHECK-NEXT: ld -4[%fp], %pc ! return +; CHECK-NEXT: add %fp, 0x0, %sp +; CHECK-NEXT: ld -8[%fp], %fp + %loc = alloca i32 + %j = load i32, i32 * %loc + %r = add i32 %i, %j + %ext = zext i32 %r to i64 + ret i64 %ext +} + +define i64 @i16_test(i16 %i) nounwind readnone { +; CHECK-LABEL: i16_test: +; CHECK: ! %bb.0: +; CHECK-NEXT: st %fp, [--%sp] +; CHECK-NEXT: add %sp, 0x8, %fp +; CHECK-NEXT: sub %sp, 0x10, %sp +; CHECK-NEXT: add %fp, 0x0, %r3 +; CHECK-NEXT: or %r3, 0x2, %r3 +; CHECK-NEXT: uld.h 0[%r3], %r3 +; CHECK-NEXT: uld.h -10[%fp], %r9 +; CHECK-NEXT: add %r3, %r9, %r3 +; CHECK-NEXT: and %r3, 0xffff, %r9 +; CHECK-NEXT: or %r0, 0x0, %rv +; CHECK-NEXT: ld -4[%fp], %pc ! return +; CHECK-NEXT: add %fp, 0x0, %sp +; CHECK-NEXT: ld -8[%fp], %fp + %loc = alloca i16 + %j = load i16, i16 * %loc + %r = add i16 %i, %j + %ext = zext i16 %r to i64 + ret i64 %ext +} + +define i64 @i8_test(i8 %i) nounwind readnone { +; CHECK-LABEL: i8_test: +; CHECK: ! %bb.0: +; CHECK-NEXT: st %fp, [--%sp] +; CHECK-NEXT: add %sp, 0x8, %fp +; CHECK-NEXT: sub %sp, 0x10, %sp +; CHECK-NEXT: add %fp, 0x0, %r3 +; CHECK-NEXT: or %r3, 0x3, %r3 +; CHECK-NEXT: uld.b 0[%r3], %r3 +; CHECK-NEXT: uld.b -9[%fp], %r9 +; CHECK-NEXT: add %r3, %r9, %r3 +; CHECK-NEXT: mov 0xff, %r9 +; CHECK-NEXT: and %r3, %r9, %r9 +; CHECK-NEXT: or %r0, 0x0, %rv +; CHECK-NEXT: ld -4[%fp], %pc ! return +; CHECK-NEXT: add %fp, 0x0, %sp +; CHECK-NEXT: ld -8[%fp], %fp + %loc = alloca i8 + %j = load i8, i8 * %loc + %r = add i8 %i, %j + %ext = zext i8 %r to i64 + ret i64 %ext +} diff --git a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/lanai-reg-names.ll.scrub.expected b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/lanai-reg-names.ll.scrub.expected new file mode 100644 --- /dev/null +++ b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/lanai-reg-names.ll.scrub.expected @@ -0,0 +1,95 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --scrub-reg-names +; RUN: llc -mtriple=lanai < %s | FileCheck %s + +define i64 @i64_test(i64 %i) nounwind readnone { +; CHECK-LABEL: i64_test: +; CHECK: ! %bb.0: +; CHECK-NEXT: st %fp, [--%sp] +; CHECK-NEXT: add %sp, 0x8, %fp +; CHECK-NEXT: sub %sp, 0x10, %sp +; CHECK-NEXT: ld 4[%fp], [[GPR_1:(%r[0-9]+)]] +; CHECK-NEXT: ld 0[%fp], [[GPR_2:(%r[0-9]+)]] +; CHECK-NEXT: sub %fp, 0x10, [[GPR_3:(%r[0-9]+)]] +; CHECK-NEXT: or [[GPR_3]], 0x4, [[GPR_4:(%r[0-9]+)]] +; CHECK-NEXT: ld -16[%fp], [[GPR_5:(%r[0-9]+)]] +; CHECK-NEXT: ld 0[[[GPR_4]]], [[GPR_6:(%r[0-9]+)]] +; CHECK-NEXT: add [[GPR_2]], [[GPR_5]], [[GPR_7:(%r[0-9]+)]] +; CHECK-NEXT: add [[GPR_1]], [[GPR_6]], [[GPR_8:(%r[0-9]+)]] +; CHECK-NEXT: sub.f [[GPR_8]], [[GPR_1]], [[GPR_9:(%r[0-9]+)]] +; CHECK-NEXT: sult [[GPR_10:(%r[0-9]+)]] +; CHECK-NEXT: add [[GPR_7]], [[GPR_10]], %rv +; CHECK-NEXT: ld -4[%fp], %pc ! return +; CHECK-NEXT: add %fp, 0x0, %sp +; CHECK-NEXT: ld -8[%fp], %fp + %loc = alloca i64 + %j = load i64, i64 * %loc + %r = add i64 %i, %j + ret i64 %r +} + +define i64 @i32_test(i32 %i) nounwind readnone { +; CHECK-LABEL: i32_test: +; CHECK: ! %bb.0: +; CHECK-NEXT: st %fp, [--%sp] +; CHECK-NEXT: add %sp, 0x8, %fp +; CHECK-NEXT: sub %sp, 0x10, %sp +; CHECK-NEXT: ld 0[%fp], [[GPR_1:(%r[0-9]+)]] +; CHECK-NEXT: ld -12[%fp], [[GPR_2:(%r[0-9]+)]] +; CHECK-NEXT: add [[GPR_1]], [[GPR_2]], [[GPR_3:(%r[0-9]+)]] +; CHECK-NEXT: or [[GPR_4:(%r[0-9]+)]], 0x0, %rv +; CHECK-NEXT: ld -4[%fp], %pc ! return +; CHECK-NEXT: add %fp, 0x0, %sp +; CHECK-NEXT: ld -8[%fp], %fp + %loc = alloca i32 + %j = load i32, i32 * %loc + %r = add i32 %i, %j + %ext = zext i32 %r to i64 + ret i64 %ext +} + +define i64 @i16_test(i16 %i) nounwind readnone { +; CHECK-LABEL: i16_test: +; CHECK: ! %bb.0: +; CHECK-NEXT: st %fp, [--%sp] +; CHECK-NEXT: add %sp, 0x8, %fp +; CHECK-NEXT: sub %sp, 0x10, %sp +; CHECK-NEXT: add %fp, 0x0, [[GPR_1:(%r[0-9]+)]] +; CHECK-NEXT: or [[GPR_1]], 0x2, [[GPR_2:(%r[0-9]+)]] +; CHECK-NEXT: uld.h 0[[[GPR_2]]], [[GPR_3:(%r[0-9]+)]] +; CHECK-NEXT: uld.h -10[%fp], [[GPR_4:(%r[0-9]+)]] +; CHECK-NEXT: add [[GPR_3]], [[GPR_4]], [[GPR_5:(%r[0-9]+)]] +; CHECK-NEXT: and [[GPR_5]], 0xffff, [[GPR_6:(%r[0-9]+)]] +; CHECK-NEXT: or [[GPR_7:(%r[0-9]+)]], 0x0, %rv +; CHECK-NEXT: ld -4[%fp], %pc ! return +; CHECK-NEXT: add %fp, 0x0, %sp +; CHECK-NEXT: ld -8[%fp], %fp + %loc = alloca i16 + %j = load i16, i16 * %loc + %r = add i16 %i, %j + %ext = zext i16 %r to i64 + ret i64 %ext +} + +define i64 @i8_test(i8 %i) nounwind readnone { +; CHECK-LABEL: i8_test: +; CHECK: ! %bb.0: +; CHECK-NEXT: st %fp, [--%sp] +; CHECK-NEXT: add %sp, 0x8, %fp +; CHECK-NEXT: sub %sp, 0x10, %sp +; CHECK-NEXT: add %fp, 0x0, [[GPR_1:(%r[0-9]+)]] +; CHECK-NEXT: or [[GPR_1]], 0x3, [[GPR_2:(%r[0-9]+)]] +; CHECK-NEXT: uld.b 0[[[GPR_2]]], [[GPR_3:(%r[0-9]+)]] +; CHECK-NEXT: uld.b -9[%fp], [[GPR_4:(%r[0-9]+)]] +; CHECK-NEXT: add [[GPR_3]], [[GPR_4]], [[GPR_5:(%r[0-9]+)]] +; CHECK-NEXT: mov 0xff, [[GPR_6:(%r[0-9]+)]] +; CHECK-NEXT: and [[GPR_5]], [[GPR_6]], [[GPR_7:(%r[0-9]+)]] +; CHECK-NEXT: or [[GPR_8:(%r[0-9]+)]], 0x0, %rv +; CHECK-NEXT: ld -4[%fp], %pc ! return +; CHECK-NEXT: add %fp, 0x0, %sp +; CHECK-NEXT: ld -8[%fp], %fp + %loc = alloca i8 + %j = load i8, i8 * %loc + %r = add i8 %i, %j + %ext = zext i8 %r to i64 + ret i64 %ext +} diff --git a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/x86-reg-names.ll b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/x86-reg-names.ll new file mode 100644 --- /dev/null +++ b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/x86-reg-names.ll @@ -0,0 +1,60 @@ +; RUN: llc -mtriple=x86_64-unknown-linux -mattr=+avx512vl < %s | FileCheck %s + +@x512 = dso_local global <16 x i32> zeroinitializer, align 4 +@x256 = dso_local global <8 x i32> zeroinitializer, align 4 +@x128 = dso_local global <4 x i32> zeroinitializer, align 4 + +define <16 x i32> @zmm_k_rip_reg_test(<16 x i32> %i, <16 x i32> %j, <16 x i32> %mask1) nounwind readnone { + %x = load <16 x i32>, <16 x i32> * @x512 + %mask = icmp ne <16 x i32> %mask1, %x + %add = add <16 x i32> %i, %j + %r = select <16 x i1> %mask, <16 x i32> %add, <16 x i32> zeroinitializer + ret <16 x i32> %r +} + +define <8 x i32> @ymm_k_rip_reg_test(<8 x i32> %i, <8 x i32> %j, <8 x i32> %mask1) nounwind readnone { + %x = load <8 x i32>, <8 x i32> * @x256 + %mask = icmp ne <8 x i32> %mask1, %x + %add = add <8 x i32> %i, %j + %r = select <8 x i1> %mask, <8 x i32> %add, <8 x i32> zeroinitializer + ret <8 x i32> %r +} + +define <4 x i32> @xmm_k_rip_reg_test(<4 x i32> %i, <4 x i32> %j, <4 x i32> %mask1) nounwind readnone { + %x = load <4 x i32>, <4 x i32> * @x128 + %mask = icmp ne <4 x i32> %mask1, %x + %add = add <4 x i32> %i, %j + %r = select <4 x i1> %mask, <4 x i32> %add, <4 x i32> zeroinitializer + ret <4 x i32> %r +} + +define i64 @gpr64_rsp_reg_test(i64 %i) nounwind readnone { + %loc = alloca i64 + %j = load i64, i64 * %loc + %r = add i64 %i, %j + ret i64 %r +} + +define i64 @gpr32_rsp_reg_test(i32 %i) nounwind readnone { + %loc = alloca i32 + %j = load i32, i32 * %loc + %r = add i32 %i, %j + %ext = zext i32 %r to i64 + ret i64 %ext +} + +define i64 @gpr16_rsp_reg_test(i16 %i) nounwind readnone { + %loc = alloca i16 + %j = load i16, i16 * %loc + %r = add i16 %i, %j + %ext = zext i16 %r to i64 + ret i64 %ext +} + +define i64 @gpr8_rsp_reg_test(i8 %i) nounwind readnone { + %loc = alloca i8 + %j = load i8, i8 * %loc + %r = add i8 %i, %j + %ext = zext i8 %r to i64 + ret i64 %ext +} diff --git a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/x86-reg-names.ll.noscrub.expected b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/x86-reg-names.ll.noscrub.expected new file mode 100644 --- /dev/null +++ b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/x86-reg-names.ll.noscrub.expected @@ -0,0 +1,96 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=x86_64-unknown-linux -mattr=+avx512vl < %s | FileCheck %s + +@x512 = dso_local global <16 x i32> zeroinitializer, align 4 +@x256 = dso_local global <8 x i32> zeroinitializer, align 4 +@x128 = dso_local global <4 x i32> zeroinitializer, align 4 + +define <16 x i32> @zmm_k_rip_reg_test(<16 x i32> %i, <16 x i32> %j, <16 x i32> %mask1) nounwind readnone { +; CHECK-LABEL: zmm_k_rip_reg_test: +; CHECK: # %bb.0: +; CHECK-NEXT: vpcmpneqd x512(%rip), %zmm2, %k1 +; CHECK-NEXT: vpaddd %zmm1, %zmm0, %zmm0 {%k1} {z} +; CHECK-NEXT: retq + %x = load <16 x i32>, <16 x i32> * @x512 + %mask = icmp ne <16 x i32> %mask1, %x + %add = add <16 x i32> %i, %j + %r = select <16 x i1> %mask, <16 x i32> %add, <16 x i32> zeroinitializer + ret <16 x i32> %r +} + +define <8 x i32> @ymm_k_rip_reg_test(<8 x i32> %i, <8 x i32> %j, <8 x i32> %mask1) nounwind readnone { +; CHECK-LABEL: ymm_k_rip_reg_test: +; CHECK: # %bb.0: +; CHECK-NEXT: vpcmpneqd x256(%rip), %ymm2, %k1 +; CHECK-NEXT: vpaddd %ymm1, %ymm0, %ymm0 {%k1} {z} +; CHECK-NEXT: retq + %x = load <8 x i32>, <8 x i32> * @x256 + %mask = icmp ne <8 x i32> %mask1, %x + %add = add <8 x i32> %i, %j + %r = select <8 x i1> %mask, <8 x i32> %add, <8 x i32> zeroinitializer + ret <8 x i32> %r +} + +define <4 x i32> @xmm_k_rip_reg_test(<4 x i32> %i, <4 x i32> %j, <4 x i32> %mask1) nounwind readnone { +; CHECK-LABEL: xmm_k_rip_reg_test: +; CHECK: # %bb.0: +; CHECK-NEXT: vpcmpneqd x128(%rip), %xmm2, %k1 +; CHECK-NEXT: vpaddd %xmm1, %xmm0, %xmm0 {%k1} {z} +; CHECK-NEXT: retq + %x = load <4 x i32>, <4 x i32> * @x128 + %mask = icmp ne <4 x i32> %mask1, %x + %add = add <4 x i32> %i, %j + %r = select <4 x i1> %mask, <4 x i32> %add, <4 x i32> zeroinitializer + ret <4 x i32> %r +} + +define i64 @gpr64_rsp_reg_test(i64 %i) nounwind readnone { +; CHECK-LABEL: gpr64_rsp_reg_test: +; CHECK: # %bb.0: +; CHECK-NEXT: movq %rdi, %rax +; CHECK-NEXT: addq -{{[0-9]+}}(%rsp), %rax +; CHECK-NEXT: retq + %loc = alloca i64 + %j = load i64, i64 * %loc + %r = add i64 %i, %j + ret i64 %r +} + +define i64 @gpr32_rsp_reg_test(i32 %i) nounwind readnone { +; CHECK-LABEL: gpr32_rsp_reg_test: +; CHECK: # %bb.0: +; CHECK-NEXT: movl %edi, %eax +; CHECK-NEXT: addl -{{[0-9]+}}(%rsp), %eax +; CHECK-NEXT: retq + %loc = alloca i32 + %j = load i32, i32 * %loc + %r = add i32 %i, %j + %ext = zext i32 %r to i64 + ret i64 %ext +} + +define i64 @gpr16_rsp_reg_test(i16 %i) nounwind readnone { +; CHECK-LABEL: gpr16_rsp_reg_test: +; CHECK: # %bb.0: +; CHECK-NEXT: addw -{{[0-9]+}}(%rsp), %di +; CHECK-NEXT: movzwl %di, %eax +; CHECK-NEXT: retq + %loc = alloca i16 + %j = load i16, i16 * %loc + %r = add i16 %i, %j + %ext = zext i16 %r to i64 + ret i64 %ext +} + +define i64 @gpr8_rsp_reg_test(i8 %i) nounwind readnone { +; CHECK-LABEL: gpr8_rsp_reg_test: +; CHECK: # %bb.0: +; CHECK-NEXT: addb -{{[0-9]+}}(%rsp), %dil +; CHECK-NEXT: movzbl %dil, %eax +; CHECK-NEXT: retq + %loc = alloca i8 + %j = load i8, i8 * %loc + %r = add i8 %i, %j + %ext = zext i8 %r to i64 + ret i64 %ext +} diff --git a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/x86-reg-names.ll.scrub.expected b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/x86-reg-names.ll.scrub.expected new file mode 100644 --- /dev/null +++ b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/x86-reg-names.ll.scrub.expected @@ -0,0 +1,96 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --scrub-reg-names +; RUN: llc -mtriple=x86_64-unknown-linux -mattr=+avx512vl < %s | FileCheck %s + +@x512 = dso_local global <16 x i32> zeroinitializer, align 4 +@x256 = dso_local global <8 x i32> zeroinitializer, align 4 +@x128 = dso_local global <4 x i32> zeroinitializer, align 4 + +define <16 x i32> @zmm_k_rip_reg_test(<16 x i32> %i, <16 x i32> %j, <16 x i32> %mask1) nounwind readnone { +; CHECK-LABEL: zmm_k_rip_reg_test: +; CHECK: # %bb.0: +; CHECK-NEXT: vpcmpneqd x512(%rip), %z[[VEC_1:(mm[0-9]+)]], %[[MASK_1:(k[0-9]+)]] +; CHECK-NEXT: vpaddd %z[[VEC_2:(mm[0-9]+)]], %z[[VEC_3:(mm[0-9]+)]], %z[[VEC_4:(mm[0-9]+)]] {%[[MASK_1]]} {z} +; CHECK-NEXT: retq + %x = load <16 x i32>, <16 x i32> * @x512 + %mask = icmp ne <16 x i32> %mask1, %x + %add = add <16 x i32> %i, %j + %r = select <16 x i1> %mask, <16 x i32> %add, <16 x i32> zeroinitializer + ret <16 x i32> %r +} + +define <8 x i32> @ymm_k_rip_reg_test(<8 x i32> %i, <8 x i32> %j, <8 x i32> %mask1) nounwind readnone { +; CHECK-LABEL: ymm_k_rip_reg_test: +; CHECK: # %bb.0: +; CHECK-NEXT: vpcmpneqd x256(%rip), %y[[VEC_1:(mm[0-9]+)]], %[[MASK_1:(k[0-9]+)]] +; CHECK-NEXT: vpaddd %y[[VEC_2:(mm[0-9]+)]], %y[[VEC_3:(mm[0-9]+)]], %y[[VEC_4:(mm[0-9]+)]] {%[[MASK_1]]} {z} +; CHECK-NEXT: retq + %x = load <8 x i32>, <8 x i32> * @x256 + %mask = icmp ne <8 x i32> %mask1, %x + %add = add <8 x i32> %i, %j + %r = select <8 x i1> %mask, <8 x i32> %add, <8 x i32> zeroinitializer + ret <8 x i32> %r +} + +define <4 x i32> @xmm_k_rip_reg_test(<4 x i32> %i, <4 x i32> %j, <4 x i32> %mask1) nounwind readnone { +; CHECK-LABEL: xmm_k_rip_reg_test: +; CHECK: # %bb.0: +; CHECK-NEXT: vpcmpneqd x128(%rip), %x[[VEC_1:(mm[0-9]+)]], %[[MASK_1:(k[0-9]+)]] +; CHECK-NEXT: vpaddd %x[[VEC_2:(mm[0-9]+)]], %x[[VEC_3:(mm[0-9]+)]], %x[[VEC_4:(mm[0-9]+)]] {%[[MASK_1]]} {z} +; CHECK-NEXT: retq + %x = load <4 x i32>, <4 x i32> * @x128 + %mask = icmp ne <4 x i32> %mask1, %x + %add = add <4 x i32> %i, %j + %r = select <4 x i1> %mask, <4 x i32> %add, <4 x i32> zeroinitializer + ret <4 x i32> %r +} + +define i64 @gpr64_rsp_reg_test(i64 %i) nounwind readnone { +; CHECK-LABEL: gpr64_rsp_reg_test: +; CHECK: # %bb.0: +; CHECK-NEXT: movq %{{r?}}[[GPR_1:([abcd]|[sd]i|r[0-9]+|bp)]]{{x?}}, %{{r?}}[[GPR_2:([abcd]|[sd]i|r[0-9]+|bp)]]{{x?}} +; CHECK-NEXT: addq -{{[0-9]+}}(%rsp), %{{r?}}[[GPR_2]]{{x?}} +; CHECK-NEXT: retq + %loc = alloca i64 + %j = load i64, i64 * %loc + %r = add i64 %i, %j + ret i64 %r +} + +define i64 @gpr32_rsp_reg_test(i32 %i) nounwind readnone { +; CHECK-LABEL: gpr32_rsp_reg_test: +; CHECK: # %bb.0: +; CHECK-NEXT: movl %{{e?}}[[GPR_1:([abcd]|[sd]i|r[0-9]+|bp)]]{{[xd]?}}, %{{e?}}[[GPR_2:([abcd]|[sd]i|r[0-9]+|bp)]]{{[xd]?}} +; CHECK-NEXT: addl -{{[0-9]+}}(%rsp), %{{e?}}[[GPR_2]]{{[xd]?}} +; CHECK-NEXT: retq + %loc = alloca i32 + %j = load i32, i32 * %loc + %r = add i32 %i, %j + %ext = zext i32 %r to i64 + ret i64 %ext +} + +define i64 @gpr16_rsp_reg_test(i16 %i) nounwind readnone { +; CHECK-LABEL: gpr16_rsp_reg_test: +; CHECK: # %bb.0: +; CHECK-NEXT: addw -{{[0-9]+}}(%rsp), %[[GPR_1:([abcd]|[sd]i|r[0-9]+|bp)]]{{[xw]?}} +; CHECK-NEXT: movzwl %[[GPR_1]]{{[xw]?}}, %{{e?}}[[GPR_2:([abcd]|[sd]i|r[0-9]+|bp)]]{{[xd]?}} +; CHECK-NEXT: retq + %loc = alloca i16 + %j = load i16, i16 * %loc + %r = add i16 %i, %j + %ext = zext i16 %r to i64 + ret i64 %ext +} + +define i64 @gpr8_rsp_reg_test(i8 %i) nounwind readnone { +; CHECK-LABEL: gpr8_rsp_reg_test: +; CHECK: # %bb.0: +; CHECK-NEXT: addb -{{[0-9]+}}(%rsp), %[[GPR_1:([abcd]|[sd]i|r[0-9]+|bp)]]{{[xw]?}}l +; CHECK-NEXT: movzbl %[[GPR_1]]{{[xw]?}}l, %{{e?}}[[GPR_2:([abcd]|[sd]i|r[0-9]+|bp)]]{{[xd]?}} +; CHECK-NEXT: retq + %loc = alloca i8 + %j = load i8, i8 * %loc + %r = add i8 %i, %j + %ext = zext i8 %r to i64 + ret i64 %ext +} diff --git a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/lanai-reg-names.test b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/lanai-reg-names.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/lanai-reg-names.test @@ -0,0 +1,17 @@ +# REQUIRES: lanai-registered-target + +## Check that register names are replaced with FileCheck variables. +# RUN: cp -f %S/Inputs/lanai-reg-names.ll %t.ll && %update_llc_test_checks --scrub-reg-names %t.ll +# RUN: diff -u %S/Inputs/lanai-reg-names.ll.scrub.expected %t.ll + +## Check that running the script again does not change the result: +# RUN: %update_llc_test_checks --scrub-reg-names %t.ll +# RUN: diff -u %S/Inputs/lanai-reg-names.ll.scrub.expected %t.ll + +## Check that register names are not replaced with FileCheck variables. +# RUN: cp -f %S/Inputs/lanai-reg-names.ll %t.ll && %update_llc_test_checks %t.ll +# RUN: diff -u %S/Inputs/lanai-reg-names.ll.noscrub.expected %t.ll + +## Check that running the script again does not change the result: +# RUN: %update_llc_test_checks %t.ll +# RUN: diff -u %S/Inputs/lanai-reg-names.ll.noscrub.expected %t.ll diff --git a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/x86-reg-names.test b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/x86-reg-names.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/x86-reg-names.test @@ -0,0 +1,17 @@ +# REQUIRES: x86-registered-target + +## Check that register names are replaced with FileCheck variables. +# RUN: cp -f %S/Inputs/x86-reg-names.ll %t.ll && %update_llc_test_checks --scrub-reg-names %t.ll +# RUN: diff -u %S/Inputs/x86-reg-names.ll.scrub.expected %t.ll + +## Check that running the script again does not change the result: +# RUN: %update_llc_test_checks --scrub-reg-names %t.ll +# RUN: diff -u %S/Inputs/x86-reg-names.ll.scrub.expected %t.ll + +## Check that register names are not replaced with FileCheck variables. +# RUN: cp -f %S/Inputs/x86-reg-names.ll %t.ll && %update_llc_test_checks %t.ll +# RUN: diff -u %S/Inputs/x86-reg-names.ll.noscrub.expected %t.ll + +## Check that running the script again does not change the result: +# RUN: %update_llc_test_checks %t.ll +# RUN: diff -u %S/Inputs/x86-reg-names.ll.noscrub.expected %t.ll diff --git a/llvm/utils/UpdateTestChecks/asm.py b/llvm/utils/UpdateTestChecks/asm.py --- a/llvm/utils/UpdateTestChecks/asm.py +++ b/llvm/utils/UpdateTestChecks/asm.py @@ -197,7 +197,584 @@ SCRUB_X86_LCP_RE = re.compile(r'\.?LCPI[0-9]+_[0-9]+') SCRUB_X86_RET_RE = re.compile(r'ret[l|q]') +# ===----------------------- Register scrubbing -----------------------------=== + +# Design goals: +# +# 1. Limit the diffs of register-related things when updating tests. We want to +# filter out irrelevant (to some tests) changes in register names. +# +# 2. Reduce the complexity of match regexps as much as possible. +# +# These goals drive the use of register class names for FileCheck variables. +# Replacing a register name with a unique register-class-based FileCheck name +# means that mere changes to register names within a class will not cause +# differences in test CHECK lines. However, changes from one register class to +# another *will* cause (desirable) differences. This also simplifies regular +# expressions because each regexp need only match registers from a particular +# class, not all possible machine registers. + +# Given a match object, a function that maps a register to a register class name +# and a function that returns whether the match is a register definition, return +# a replacement for the matched string. This is a general utility most +# architectures can use to quickly implement register scrubbing. It assumes +# that either the target does not have subregisters or that the tests for an +# architecture with subregisters doesn't care about tracking sub-/super-register +# dependencies. +# +# The reg_class_func and reg_def_func are called with the match object: +# +# reg_def_func(match) +# +# The match object should have the register name in group 1. +# +# The target will provide the reg_class_func and reg_def_func and will call +# scrub_reg_names with a lambda that captures that function: +# +# asm = scrub_reg_names( +# asm, +# TARGET_REG_PATTERNS, +# lambda match: scrub_reg_get_reg_replament( +# match, TARGET_REG_CLASS_FUNC, TARGET_REG_DEF_FUNC +# ) +# ) +# +# The reg_class_func should return a string indicating the register class of the +# matched register. The string can be anything at all as long as it is +# consistent. +# +# The reg_def_func should return True if the matched register is defined by the +# instruction. +# +def scrub_reg_get_reg_replacement(match, reg_class_func, reg_def_func): + # Create a FileCheck variable name based on a register class name. + def get_reg_name(reg_class, count): + return reg_class + '_' + str(count) + + # Create a FileCheck variable from regex. + def get_reg_definition(name, pattern): + return '[[' + name + ':' + pattern + ']]' + + # Use a FileCheck variable. + def get_reg_use(name): + return '[[' + name + ']]' + + reg = match.group(1) + reg_class = reg_class_func(match) + reg_pattern = match.re.pattern + + # Register renaming. In order to uniquify FileCheck variable names when + # registers are redefined, we employ a renaming scheme, not unlike those used + # in OOO hardware. The first time we see a register of a particular class, it + # gets a name consisting of the register class name concatenated with a + # per-class counter. All uses of that register value will use the generated + # name. When a register of a particular class is redefined, we increment the + # count and generate a new name, when is then used for subsequent uses of the + # register. + # + # Using register class names generalizes the register names, limiting + # differences caused by mere register name changes. + + count_index = reg_class + + # Prefix with register class in case a substring appears in multiple classes. + rename_index = reg_class + '_' + reg + + count = scrub_reg_get_reg_replacement.class_count.get(count_index, 0) + + # See if this is either a completely new appearance of a register or a + # register (re-)definition. + + if rename_index not in scrub_reg_get_reg_replacement.renamer or reg_def_func( + match + ): + count += 1 + name = get_reg_name(reg_class, count) + rv = get_reg_definition(name, reg_pattern) + common.debug(' Def or new appearance of ' + reg + ', replacing with ' + + rv) + else: + name = scrub_reg_get_reg_replacement.renamer[rename_index] + rv = get_reg_use(name) + common.debug(' Use of ' + reg + ', replacing with ' + rv) + + scrub_reg_get_reg_replacement.renamer[rename_index] = name + scrub_reg_get_reg_replacement.class_count[count_index] = count + + return rv + +scrub_reg_get_reg_replacement.class_count = dict() +scrub_reg_get_reg_replacement.renamer = dict() + +# Reset the state of the matcher. Targets using scrub_reg_get_reg_replacement +# should pass this as the reset_func to scrub_reg_names. +# +def scrub_reg_reset(): + scrub_reg_get_reg_replacement.renamer = dict() + scrub_reg_get_reg_replacement.class_count = dict() + +# ===----------------------- Lanai Register scrubbing -----------------------=== + +LANAI_SCRUB_REG_GPR_PATTERN = f'(%r[0-9]+)' + +LANAI_SCRUB_REG_REGEXPS = [ + re.compile(LANAI_SCRUB_REG_GPR_PATTERN) +] + +def LANAI_SCRUB_REG_REG_CLASS_FUNC(match): + reg = match.group(1) + + if not reg.startswith('%r'): + raise Exception('Unexpected register ' + reg) + + return 'GPR' + +LANAI_SCRUB_REG_END_LINE_PATTERN = r'\s*(;.*)?$' +LANAI_SCRUB_REG_END_LINE_REGEXP = re.compile(LANAI_SCRUB_REG_END_LINE_PATTERN) + +def LANAI_SCRUB_REG_REG_DEF_FUNC(match): + reg = match.group(1) + asm_line = match.string + start = match.start(1) + end = match.end(1) + + common.debug(asm_line + ': Matched ' + reg + ' at ' + str((start, end))) + + if asm_line[start:end] != reg: + raise Exception('\'' + asm_line + '\' at position ' + str((start, end)) + + ' did not match expected register ' + reg) + + # If the string following the match only has whitespace and/or an option mask + # or modifier use and/or a comment, it is a register definition. Any other + # use of a register (such as in a memory operand) would have a parenthesis or + # bracket after it. + + match = LANAI_SCRUB_REG_END_LINE_REGEXP.match(asm_line, end) + if match: + common.debug('Matched def at ' + match.group(0)) + return True + + return False + +LANAI_SCRUB_REG_REG_REPLACEMENT_FUNC = lambda match: scrub_reg_get_reg_replacement( + match, LANAI_SCRUB_REG_REG_CLASS_FUNC, LANAI_SCRUB_REG_REG_DEF_FUNC +) + +# ===----------------------- X86-64 Register scrubbing ----------------------=== + +# Design goals: +# +# 1. Limit the diffs of register-related things when updating tests. We want to +# filter out irrelevant (to some tests) changes in register names. +# +# 2. Track register dependencies between super- and sub-classes. This catches +# errors such as defining a super-reg and then using a sub-reg of a different +# register. +# +# 3. Reduce the complexity of match regexps as much as possible. +# +# These goals are somewhat in conflict. Because X86 GPR names are not +# particularly consistent (for example the allowed byte/word/doubleword prefixes +# and suffixes differ among GPRs), tracking defs across super-/sub-registers +# involves some hefty regular expressions. Due to limitations in FileCheck's +# regexp support (only Extended Regular Expressions), we can't use lookahead and +# lookbehind to constrain matches. That means some of the regular expressions +# can match illegal names like "ebpl." We'll assume that enough tests run +# without register scrubbing that the asm won't have such illegal names. + +# Don't match [re]?ip and [re]?sp because we likely want to match how they are +# used in addressing. Match [re]bp because we may have eliminated the frame +# pointer. We also don't have patterns for e?flags because only one register +# would match so a pattern doesn't change anything as far as test diffs are +# concerned. +# +# We assume AT&T syntax. + +SCRUB_X86_GPR8_PATTERN = \ + r'(?P%((?P[abcd])(?P[hl])|' \ + r'(?P[sd])(?Pil)|' \ + r'(?Pr[0-9]+)(?Pl)|' \ + r'(?Pbp)(?Pl)))' +SCRUB_X86_GPR16_PATTERN = \ + r'(?P%((?P[abcd])(?Px)|' \ + r'(?P[sd])(?Pi)|' \ + r'(?Pr[0-9]+)(?Pw)|' \ + r'(?Pbp)))' +SCRUB_X86_GPR32_PATTERN = \ + r'(?P%((?Pe)(?P[abcd])(?Px)|' \ + r'(?Pe)(?P[sd])(?Pi)|' \ + r'(?Pr[0-9]+)(?Pd)|' \ + r'(?Pe)(?Pbp)))' +SCRUB_X86_GPR64_PATTERN = \ + r'(?P%((?Pr)(?P[abcd])(?Px)|' \ + r'(?Pr)(?P[sd])(?Pi)|' \ + r'(?Pr[0-9]+)|(?Pr)(?Pbp)))' +SCRUB_X86_ST_PATTERN = r'(?P%(?Pst\([0-9]+\)))' +SCRUB_X86_MMX_PATTERN = r'(?P%(?Pmm[0-9]+))' +SCRUB_X86_XMM_PATTERN = r'(?P%(?Px)(?Pmm[0-9]+))' +SCRUB_X86_YMM_PATTERN = r'(?P%(?Py)(?Pmm[0-9]+))' +SCRUB_X86_ZMM_PATTERN = r'(?P%(?Pz)(?Pmm[0-9]+))' +SCRUB_X86_K_PATTERN = r'(?P%(?Pk[0-9]+))' +SCRUB_X86_SEG_PATTERN = r'(?P%(?P[cdsefg]s))' + +# Specify a list of patterns to match. Order is important here. We don't want, +# say "ax" to match when "rax" is the full register name. These are checked in +# order. + +X86_SCRUB_REG_REGEXPS = [ + re.compile(SCRUB_X86_GPR64_PATTERN), + re.compile(SCRUB_X86_GPR32_PATTERN), + re.compile(SCRUB_X86_GPR16_PATTERN), + re.compile(SCRUB_X86_GPR8_PATTERN), + re.compile(SCRUB_X86_ZMM_PATTERN), + re.compile(SCRUB_X86_YMM_PATTERN), + re.compile(SCRUB_X86_XMM_PATTERN), + re.compile(SCRUB_X86_MMX_PATTERN), + re.compile(SCRUB_X86_ST_PATTERN), + re.compile(SCRUB_X86_K_PATTERN), + re.compile(SCRUB_X86_SEG_PATTERN) +] + +# Specify a pattern to match a register definition. This is how we know when +# to use a new unique FileName variable. A def is either: +# +# mnemonic REG()?()? +# mnemonic (,)+ REG()?()? +# +# Here is uses of {} and {z} specifiers, which are not defs. +# +# Note that memory operands are not considered registers and do not define +# registers. + +SCRUB_X86_COMMENT_PATTERN = r'\s*(;.*)?' +SCRUB_X86_MASK_MODIFIER_PATTERN = r'\s*(\{\S+\}\s*)*' + +# Match a register definition from the end point of a register match. +SCRUB_X86_REG_DEF_REGEXP = re.compile( + r'(' + SCRUB_X86_MASK_MODIFIER_PATTERN + SCRUB_X86_COMMENT_PATTERN + r')$' +) + +# TODO: We might be able to merge this will the more general +# scrub_reg_get_reg_replacement. That would require +# scrub_reg_get_reg_replacement to take more customization routines, for example +# to get a register key, prefix and suffix. This will make that routine more +# complex for targets that don't need the flexibility. However, most targets +# have some kind of super-/sub-register relationship so most will need some of +# that flexibility. +def X86_SCRUB_REG_REPLACEMENT_FUNC(match): + def reg_class_func(match): + groups = match.groupdict() + reg = groups['reg'] + if 'gpr' in groups or 'idx' in groups or 'bp' in groups or 'ext' in groups: + # This is a GPR-type register. + return 'GPR' + elif 'x87' in groups: + return 'X87FP' + elif 'vec' in groups: + return 'VEC' + elif 'mask' in groups: + return 'MASK' + elif 'seg' in groups: + return 'SEG' + else: + raise Exception('Unknown register ' + reg) + + def reg_def_func(asm_line, reg, match_pos): + # An instruction with a register def tied to a use does *not* generate a new + # name. This is because we want to ensure the used value is the same as the + # previous definition of the register. Subsequent uses of the tied register + # must use the same name. If a test update changes the name of the tied + # register, it also must have changed to the same name in the previous + # instruction that defined the value. So they will still all have the same + # name, just as the FileCheck variables all have the same name. + # + # Here is one situation where this will not catch an incorrect register + # def-use chain: + # + # add %rax, %rbx + # sub %rcx, %rbx + # add %rdx, %rbx + # + # If a pass incorrectly reorders the instructions (producing a different, + # incorrect result), %rbx will still have the same FileCheck variable for + # all three instructions and we will not detect the difference in value for + # %rbx. However, presumably the test will also be checking mnemonics and + # those will differ in their order, causing the test to fail. + + start = match_pos[0] + end = match_pos[1] + + common.debug(asm_line + ': Matched ' + reg + ' at ' + str(match_pos)) + + if asm_line[start:end] != reg: + raise Exception('\'' + asm_line + '\' at position ' + str(match_pos) + + ' did not match expected register ' + reg) + + # If the string following the match only has whitespace and/or an option + # mask or modifier use and/or a comment, it is a register definition. Any + # other use of a register (such as in a memory operand) would have a + # parenthesis or bracket after it. + + match = SCRUB_X86_REG_DEF_REGEXP.match(asm_line, end) + if match: + # This might be a tied register. Check known mnemonics. If it's a tied + # register we consider it a use so that we'll check def-use chains in + # these cases + mnemonic_match = X86_SCRUB_REG_REPLACEMENT_FUNC.mnemonic_regexp.match( + asm_line + ) + if not mnemonic_match: + raise Exception('Could not find mnemonic in ' + asm_line) + + mnemonic = mnemonic_match.group('mnemonic') + + def_match = X86_SCRUB_REG_REPLACEMENT_FUNC.noclobber_def_op_regexp.match( + mnemonic + ) + + if not def_match: + # Tied register or use, don't consider it a def. + common.debug('Matched tied def or use of ' + reg + ' for ' + mnemonic) + return False + + common.debug('Matched def at ' + match.group(0)) + return True + + common.debug('Did not match def of ' + reg + ' in ' + asm_line[end:] + + ' with ' + SCRUB_X86_REG_DEF_REGEXP.pattern) + return False + + # Create a FileCheck variable name based on a register class name. + def get_reg_name(reg_class, count): + return reg_class + '_' + str(count) + + def get_replacement_rv(rv, key, prefix, suffix): + # Fix up the prefix and suffix so we match registers of similar class. + newprefix = prefix + newsuffix = suffix + if prefix == '%r' or prefix == '%e': + # Not a valid prefix for r[0-9]+ so make the letter optional. + # Unfortunately, this means we might match a 16-bit register like "ax" but + # since FileCheck doesn't support lookahead/lookbehind and we need to + # match just the register key to the FileCheck variable name, we can't + # easily write a prefix that is conditional on tne register key. + newprefix = '%{{' + prefix[1] + '?}}' + if suffix == 'l' or suffix == 'h' or suffix == 'b': + # GPR8 register. + newsuffix = '{{[hlb]}}' + elif prefix == '%' and reg_key[0] != 'k': + # GPR16 register, only [abcd] and r[0-9]+ have a suffix. Make it + # optional. + newsuffix = '{{[xw]?}}' + elif prefix == '%e': + # GPR32 register, only [abcd] and r[0-9]+ have a suffix. Make it + # optional. + newsuffix = '{{[xd]?}}' + elif prefix == '%r': + # GPR64 register, only [abcd] has a suffix. Make it optional. + newsuffix = '{{x?}}' + return newprefix + rv + newsuffix + + # Create a FileCheck variable from regex. + def get_reg_definition(name, reg_key, reg_prefix, reg_suffix): + prefix_pattern = X86_SCRUB_REG_REPLACEMENT_FUNC.register_prefix_patterns[reg_class] + key_pattern = X86_SCRUB_REG_REPLACEMENT_FUNC.register_key_patterns[reg_class] + suffix_pattern = X86_SCRUB_REG_REPLACEMENT_FUNC.register_suffix_patterns[reg_class] + + return get_replacement_rv( + '[[' + name + ':' + key_pattern + ']]', reg_key, reg_prefix, reg_suffix + ) + + # Use a FileCheck variable. + def get_reg_use(name, reg_key, reg_prefix, reg_suffix): + return get_replacement_rv('[[' + name + ']]', reg_key, reg_prefix, reg_suffix) + + def get_reg_subpart(reg, reg_class, regexp, part): + match = regexp.search(reg) + + if not match: + raise Exception( + 'Did not match register ' + part + ' for register ' + reg + \ + ' in class ' + reg_class + ' using pattern ' + regexp.pattern + ) + + return match.group(1) + + reg = match.group('reg') + common.debug('Got register ' + reg) + reg_class = reg_class_func(match) + match_pos = ( match.start(1), match.end(1) ) + + # The "register key" is the part of the name that matches across + # super-/sub-classes. This is what we use to detect register dependencies. + # The prefix and suffix is the part of the register name that varies across + # super-/sub-class. + + reg_key_regexp = X86_SCRUB_REG_REPLACEMENT_FUNC.register_key_regexps[reg_class] + reg_prefix_regexp = X86_SCRUB_REG_REPLACEMENT_FUNC.register_prefix_regexps[reg_class] + reg_suffix_regexp = X86_SCRUB_REG_REPLACEMENT_FUNC.register_suffix_regexps[reg_class] + + reg_key = get_reg_subpart(reg, reg_class, reg_key_regexp, 'key') + reg_prefix = get_reg_subpart(reg, reg_class, reg_prefix_regexp, 'prefix') + reg_suffix = get_reg_subpart(reg, reg_class, reg_suffix_regexp, 'suffix') + + # Register renaming. In order to uniquify FileCheck variable names when + # registers are redefined, we employ a renaming scheme, not unlike those used + # in OOO hardware. The first time we see a register of a particular class, it + # gets a name consisting of the register class name concatenated with a + # per-class counter. All uses of that register value will use the generated + # name. When a register of a particular class is redefined, we increment the + # count and generate a new name, when is then used for subsequent uses of the + # register. + # + # Using register class names generalizes the register names, limiting + # differences caused by mere register name changes. + + count_index = reg_class + + # Prefix with register class because "mm" appears in two classes. + rename_index = reg_class + '_' + reg_key + + count = X86_SCRUB_REG_REPLACEMENT_FUNC.class_count.get(count_index, 0) + + # See if this is either a completely new appearance of a register or a + # register (re-)definition. + + if rename_index not in X86_SCRUB_REG_REPLACEMENT_FUNC.renamer or reg_def_func( + match.string, reg, match_pos + ): + count += 1 + name = get_reg_name(reg_class, count) + rv = get_reg_definition(name, reg_key, reg_prefix, reg_suffix) + common.debug(' Def or new appearance of ' + reg + ', ' + count_index + + ' count = ' + str(count) + ', replacing with ' + rv) + else: + name = X86_SCRUB_REG_REPLACEMENT_FUNC.renamer[rename_index] + rv = get_reg_use(name, reg_key, reg_prefix, reg_suffix) + common.debug(' Use of ' + reg + ', replacing with ' + rv) + + X86_SCRUB_REG_REPLACEMENT_FUNC.renamer[rename_index] = name + X86_SCRUB_REG_REPLACEMENT_FUNC.class_count[count_index] = count + + return rv + +X86_SCRUB_REG_REPLACEMENT_FUNC.mnemonic_regexp = re.compile( + r'^\s*(?Prep\s+)?(?P\S+)' +) + +# Most instructions have the destination tied to the source and thus we don't +# consider such registers defined, in order to preserve register dependency +# checking. Match mnemonics known to define register and not to have tied +# registers. Note that this also prevents a "def" for instructions that only +# use registers because the last register will be considered "tied" even though +# it isn't really a def. "cmov" is considered tied, in that the original value +# of the destination is also a source as the result can be the original value. +# +X86_SCRUB_REG_REPLACEMENT_FUNC.noclobber_def_op_regexp = re.compile( + # vfm* clobbers a source. + # vcomis[sd] does not define an operand. + # fp compare with p suffix pops the stack, so consider the register defined. + # Others are either unary operations or have more than two operands. + # + # FIXME: + # Some imul forms do not clobber the source register. + # pcmp[ei]strm clobbers the first source if it is xmm0. + r'^(andn|cvt|bextr|bls|bs[rf]|bzhi|extr|f[ui]?comi?(?<=p)|insert|j|k|ld|' \ + r'lea|lz|v?mask|mov|mulx|pabs|pcmp[ei]str|pdep|pext|phmin|pins|pmov|pop|' \ + r'rcp|rd|ror|round|rsqrt|sarx|shlx|shrx|sqrt|tz|v(?!fm|fn|co)|wr)' +) + +# Note that GPR patterns could match nonsensical things like "ebl" but we +# assume registers are named correctly. + +X86_SCRUB_REG_REPLACEMENT_FUNC.register_key_patterns = { + 'GPR' : r'([abcd]|[sd]i|r[0-9]+|bp)', + 'X87' : r'(mm[0-7]|st\([0-7]\))', + 'VEC' : r'(mm[0-9]+)', + 'MASK' : r'(k[0-9]+)', + 'SEG' : r'([cdsefg]s)' +} + +X86_SCRUB_REG_REPLACEMENT_FUNC.register_key_regexps = { + 'GPR' : re.compile(X86_SCRUB_REG_REPLACEMENT_FUNC.register_key_patterns['GPR']), + 'X87' : re.compile(X86_SCRUB_REG_REPLACEMENT_FUNC.register_key_patterns['X87']), + 'VEC' : re.compile(X86_SCRUB_REG_REPLACEMENT_FUNC.register_key_patterns['VEC']), + 'MASK' : re.compile(X86_SCRUB_REG_REPLACEMENT_FUNC.register_key_patterns['MASK']), + 'SEG' : re.compile(X86_SCRUB_REG_REPLACEMENT_FUNC.register_key_patterns['SEG']), + } + +X86_SCRUB_REG_REPLACEMENT_FUNC.register_prefix_patterns = { + 'GPR' : r'^(%[er]?)', + 'X87' : r'^(%)', + 'VEC' : r'^(%[xyz])', + 'MASK' : r'^(%)', + 'SEG' : r'^(%)' +} + +X86_SCRUB_REG_REPLACEMENT_FUNC.register_prefix_regexps = { + 'GPR' : re.compile(X86_SCRUB_REG_REPLACEMENT_FUNC.register_prefix_patterns['GPR']), + 'X87' : re.compile(X86_SCRUB_REG_REPLACEMENT_FUNC.register_prefix_patterns['X87']), + 'VEC' : re.compile(X86_SCRUB_REG_REPLACEMENT_FUNC.register_prefix_patterns['VEC']), + 'MASK' : re.compile(X86_SCRUB_REG_REPLACEMENT_FUNC.register_prefix_patterns['MASK']), + 'SEG' : re.compile(X86_SCRUB_REG_REPLACEMENT_FUNC.register_prefix_patterns['SEG']), +} + +X86_SCRUB_REG_REPLACEMENT_FUNC.register_suffix_patterns = { + 'GPR' : r'([hlbwdx]?)$', + 'X87' : r'()$', + 'VEC' : r'()$', + 'MASK' : r'()$', + 'SEG' : r'()$' +} + +X86_SCRUB_REG_REPLACEMENT_FUNC.register_suffix_regexps = { + 'GPR' : re.compile(X86_SCRUB_REG_REPLACEMENT_FUNC.register_suffix_patterns['GPR']), + 'X87' : re.compile(X86_SCRUB_REG_REPLACEMENT_FUNC.register_suffix_patterns['X87']), + 'VEC' : re.compile(X86_SCRUB_REG_REPLACEMENT_FUNC.register_suffix_patterns['VEC']), + 'MASK' : re.compile(X86_SCRUB_REG_REPLACEMENT_FUNC.register_suffix_patterns['MASK']), + 'SEG' : re.compile(X86_SCRUB_REG_REPLACEMENT_FUNC.register_suffix_patterns['SEG']), +} + +# Reset the state of the matcher. +def X86_SCRUB_REG_RESET(): + X86_SCRUB_REG_REPLACEMENT_FUNC.renamer = dict() + X86_SCRUB_REG_REPLACEMENT_FUNC.class_count = dict() + +# Convert register names to FileCheck variables based on register class. +# Regexps must have group delimeters to extract the register name using group 1. +# +# asm - The asm string to scrub +# reg_regexps - List of regular expressions to match a register +# reg_class_map - Map from regular expression pattern to a list or two items: +# item 0 - The name of the register class used to form a FileCheck variable +# item 1 - A count of how many definitions of registers in the class we've +# seen +# reg_def_func - A function that, given the matching asm line, the matched +# register and 2-tuple of match start-end position, returns True +# if the match is a register definition +# +def scrub_reg_names(asm, reg_regexps, reg_replace_func, reset_func): + # This gets called for each match that occurs in a line. We transform + # registers we haven't seen into defs, and registers we have seen into uses. + def transform_line_regs(match, reg_replace_func): + return reg_replace_func(match) + + reset_func() + + lines = asm.splitlines() + for i, line in enumerate(lines): + common.debug('Line:' + line) + for reg_regexp in reg_regexps: + lines[i] = reg_regexp.sub(reg_replace_func, lines[i]) + common.debug('New line:' + lines[i]) + return '\n'.join(lines) + def scrub_asm_x86(asm, args): + if getattr(args, 'scrub_reg_names', False): + # Replace register references with FileCheck variables. + asm = scrub_reg_names(asm, X86_SCRUB_REG_REGEXPS, + X86_SCRUB_REG_REPLACEMENT_FUNC, X86_SCRUB_REG_RESET) + # Scrub runs of whitespace out of the assembly, but leave the leading # whitespace in place. asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm) @@ -327,6 +904,16 @@ return asm def scrub_asm_lanai(asm, args): + if getattr(args, 'scrub_reg_names', False): + asm = scrub_reg_names( + asm, + LANAI_SCRUB_REG_REGEXPS, + lambda match: scrub_reg_get_reg_replacement( + match, LANAI_SCRUB_REG_REG_CLASS_FUNC, LANAI_SCRUB_REG_REG_DEF_FUNC + ), + scrub_reg_reset + ) + # Scrub runs of whitespace out of the assembly, but leave the leading # whitespace in place. asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm) diff --git a/llvm/utils/update_llc_test_checks.py b/llvm/utils/update_llc_test_checks.py --- a/llvm/utils/update_llc_test_checks.py +++ b/llvm/utils/update_llc_test_checks.py @@ -27,6 +27,9 @@ parser.add_argument( '--extra_scrub', action='store_true', help='Always use additional regex to further reduce diffs between various subtargets') + parser.add_argument( + '--scrub-reg-names', action='store_true', + help='Replace register names with FileCheck variables') parser.add_argument( '--x86_scrub_sp', action='store_true', default=True, help='Use regex for x86 sp matching to reduce diffs between various subtargets')