llvm-project/llvm/test/CodeGen/AArch64/win-import-call-optimization.ll
Daniel Paoliello 8fa680b9d9
[win][aarch64] Called globals must match for instructions to be considered identical (#175798)
When trying to enable AArch64 Import Call Optimization for Windows, we
noticed an issue where a call to an incorrect function was happening
after the loader replaced a branch instruction. The root cause of this
was that LLVM had decided to fold two branch instructions into one as
they were both branches to the same register, however the value of the
register would be different in either path as they were branches for
different imported functions.

This change updates `MachineInstr::isIdenticalTo` to also consider any
"called global" that is attached to the instruction, and will consider
two instructions as "not the same" if the globals differ.

Also fixed a possible source of non-determinism: switched from using a
`DenseMap` to using a `vector` for mapping sections to lists of called
globals (we don't expect many sections, so no need to use a map) and
sort the map by section name before emitting.
2026-01-13 15:34:10 -08:00

86 lines
2.6 KiB
LLVM

; RUN: llc -mtriple=aarch64-pc-windows-msvc %s -o - | FileCheck %s
define dso_local void @normal_call() local_unnamed_addr section "nc_sect" {
entry:
call void @a()
call void @a()
ret void
}
; CHECK-LABEL: normal_call:
; CHECK: adrp [[ADRPREG:x[0-9]+]], __imp_a
; CHECK-NEXT: ldr [[LDRREG:x[0-9]+]], [[[ADRPREG]], :lo12:__imp_a]
; CHECK-NEXT: .Limpcall0:
; CHECK-NEXT: blr [[LDRREG]]
; CHECK-NEXT: .Limpcall1:
; CHECK-NEXT: blr [[LDRREG]]
define dso_local void @tail_call() local_unnamed_addr section "tc_sect" {
entry:
tail call void @b()
ret void
}
; CHECK-LABEL: tail_call:
; CHECK: adrp [[ADRPREG:x[0-9]+]], __imp_b
; CHECK-NEXT: ldr [[LDRREG:x[0-9]+]], [[[ADRPREG]], :lo12:__imp_b]
; CHECK-NEXT: .Limpcall2:
; CHECK-NEXT: br [[LDRREG]]
; Regression test: branch folding would notice that both of these calls become
; branch instructions to the same register and would fold them. However, import
; call optimization requires them to remain separate as they are for different
; functions.
define dso_local void @call_one_or_other(i32 %val) local_unnamed_addr {
%is_zero = icmp eq i32 %val, 0
br i1 %is_zero, label %call_a, label %call_b
call_a:
call void @a()
br label %finish
call_b:
call void @b()
br label %finish
finish:
ret void
}
; CHECK-LABEL: call_one_or_other:
; CHECK: adrp [[ADRPREG:x[0-9]+]], __imp_b
; CHECK-NEXT: ldr [[LDRREG:x[0-9]+]], [[[ADRPREG]], :lo12:__imp_b]
; CHECK-NEXT: .Limpcall3:
; CHECK-NEXT: blr [[LDRREG]]
; CHECK: adrp [[ADRPREG:x[0-9]+]], __imp_a
; CHECK-NEXT: ldr [[LDRREG:x[0-9]+]], [[[ADRPREG]], :lo12:__imp_a]
; CHECK-NEXT: .Limpcall4:
; CHECK-NEXT: blr [[LDRREG]]
declare dllimport void @a() local_unnamed_addr
declare dllimport void @b() local_unnamed_addr
; CHECK-LABEL: .section .impcall,"yi"
; CHECK-NEXT: .asciz "Imp_Call_V1"
; CHECK-NEXT: .word 32
; CHECK-NEXT: .secnum nc_sect
; CHECK-NEXT: .word 19
; CHECK-NEXT: .secoffset .Limpcall0
; CHECK-NEXT: .symidx __imp_a
; CHECK-NEXT: .word 19
; CHECK-NEXT: .secoffset .Limpcall1
; CHECK-NEXT: .symidx __imp_a
; CHECK-NEXT: .word 20
; CHECK-NEXT: .secnum tc_sect
; CHECK-NEXT: .word 19
; CHECK-NEXT: .secoffset .Limpcall2
; CHECK-NEXT: .symidx __imp_b
; CHECK-NEXT: .word 32
; CHECK-NEXT: .secnum .text
; CHECK-NEXT: .word 19
; CHECK-NEXT: .secoffset .Limpcall3
; CHECK-NEXT: .symidx __imp_b
; CHECK-NEXT: .word 19
; CHECK-NEXT: .secoffset .Limpcall4
; CHECK-NEXT: .symidx __imp_a
!llvm.module.flags = !{!0}
!0 = !{i32 1, !"import-call-optimization", i32 1}