llvm-project/llvm/test/CodeGen/X86/base-pointer-and-cmpxchg.ll
Aaron Puchert 9e0ca5720b
[X86] When expanding LCMPXCHG16B_SAVE_RBX, substitute RBX in base (#134109)
The pseudo-instruction LCMPXCHG16B_SAVE_RBX is used when RBX serves as
frame base pointer. At a very late stage it is then translated into a
regular LCMPXCHG16B, preceded by copying the actual argument into RBX,
and followed by restoring the register to the base pointer.

However, in case the `cmpxchg` operates on a local variable, RBX might
also be used as a base for the memory operand in frame finalization, and
we've overwritten RBX with the input operand for `cmpxchg16b`. So we
have to rewrite the memory operand base to use the saved value of RBX.

Fixes #119959.
2025-04-03 15:56:53 +02:00

88 lines
4.0 KiB
LLVM

; RUN: llc -mtriple=x86_64-apple-macosx -mattr=+cx16 -x86-use-base-pointer=true -stackrealign %s -o - | FileCheck --check-prefix=CHECK --check-prefix=USE_BASE --check-prefix=USE_BASE_64 %s
; RUN: llc -mtriple=x86_64-apple-macosx -mattr=+cx16 -x86-use-base-pointer=false -stackrealign %s -o - | FileCheck --check-prefix=CHECK --check-prefix=DONT_USE_BASE %s
; RUN: llc -mtriple=x86_64-linux-gnux32 -mattr=+cx16 -x86-use-base-pointer=true -stackrealign %s -o - | FileCheck --check-prefix=CHECK --check-prefix=USE_BASE --check-prefix=USE_BASE_32 %s
; RUN: llc -mtriple=x86_64-linux-gnux32 -mattr=+cx16 -x86-use-base-pointer=false -stackrealign %s -o - | FileCheck --check-prefix=CHECK --check-prefix=DONT_USE_BASE %s
; This function uses dynamic allocated stack to force the use
; of a frame pointer.
; The inline asm clobbers a bunch of registers to make sure
; the frame pointer will need to be used (for spilling in that case).
;
; Then, we check that when we use rbx as the base pointer,
; we do not use cmpxchg, since using that instruction requires
; to clobbers rbx to set the arguments of the instruction and when
; rbx is used as the base pointer, RA cannot fix the code for us.
;
; CHECK-LABEL: cmp_and_swap16:
; Check that we actually use rbx.
; gnux32 use the 32bit variant of the registers.
; USE_BASE_64: movq %rsp, %rbx
; USE_BASE_32: movl %esp, %ebx
;
; Make sure the base pointer is saved before the rbx argument for
; cmpxchg16b is set.
;
; Because of how the test is written, we spill SAVE_rbx.
; However, it would have been perfectly fine to just keep it in register.
; USE_BASE: movq %rbx, [[SAVE_rbx_SLOT:[0-9]*\(%[er]bx\)]]
;
; SAVE_rbx must be in register before we clobber rbx.
; It is fine to use any register but rbx and the ones defined and use
; by cmpxchg. Since such regex would be complicated to write, just stick
; to the numbered registers. The bottom line is: if this test case fails
; because of that regex, this is likely just the regex being too conservative.
; USE_BASE: movq [[SAVE_rbx_SLOT]], [[SAVE_rbx:%r[0-9]+]]
;
; USE_BASE: movq {{[^ ]+}}, %rbx
; USE_BASE-NEXT: cmpxchg16b
; USE_BASE-NEXT: movq [[SAVE_rbx]], %rbx
;
; DONT_USE_BASE-NOT: movq %rsp, %rbx
; DONT_USE_BASE-NOT: movl %esp, %ebx
; DONT_USE_BASE: cmpxchg
define i1 @cmp_and_swap16(i128 %a, i128 %b, ptr %addr, i32 %n) {
%dummy = alloca i32, i32 %n
tail call void asm sideeffect "nop", "~{rax},~{rcx},~{rdx},~{rsi},~{rdi},~{rbp},~{r8},~{r9},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15}"()
%cmp = cmpxchg ptr %addr, i128 %a, i128 %b seq_cst seq_cst
%res = extractvalue { i128, i1 } %cmp, 1
%idx = getelementptr i32, ptr %dummy, i32 5
store i32 %n, ptr %idx
ret i1 %res
}
; If we compare-and-exchange a frame variable, we additionally need to rewrite
; the memory operand to use the SAVE_rbx instead of rbx, which already contains
; the input operand.
;
; CHECK-LABEL: cmp_and_swap16_frame:
; Check that we actually use rbx.
; gnux32 use the 32bit variant of the registers.
; USE_BASE_64: movq %rsp, %rbx
; USE_BASE_32: movl %esp, %ebx
; Here we drop the inline assembly because the frame pointer is used anyway. So
; rbx is not spilled to the stack but goes into a (hopefully numbered) register.
; USE_BASE: movq %rbx, [[SAVE_rbx:%r[0-9]+]]
;
; USE_BASE: movq {{[^ ]+}}, %rbx
; The use of the frame variable expands to N(%rbx) or N(%ebx). But we've just
; overwritten that with the input operand. We need to use SAVE_rbx instead.
; USE_BASE_64-NEXT: cmpxchg16b {{[0-9]*}}([[SAVE_rbx]])
; USE_BASE_32-NEXT: cmpxchg16b {{[0-9]*}}([[SAVE_rbx]]d)
; USE_BASE-NEXT: movq [[SAVE_rbx]], %rbx
;
; DONT_USE_BASE-NOT: movq %rsp, %rbx
; DONT_USE_BASE-NOT: movl %esp, %ebx
; DONT_USE_BASE: cmpxchg
define i1 @cmp_and_swap16_frame(i128 %a, i128 %b, i32 %n) {
%local = alloca i128, align 16
%dummy = alloca i32, i32 %n
%cmp = cmpxchg ptr %local, i128 %a, i128 %b seq_cst seq_cst
%res = extractvalue { i128, i1 } %cmp, 1
%idx = getelementptr i32, ptr %dummy, i32 5
store i32 %n, ptr %idx
ret i1 %res
}
!llvm.module.flags = !{!0}
!0 = !{i32 2, !"override-stack-alignment", i32 32}