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.
86 lines
2.6 KiB
LLVM
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}
|