blob: 97a44d172492da4d2b3e74d7130f68f490e053a6 [file] [log] [blame]
; Tests basics and corner cases of x86-32 sandboxing, using -Om1 in
; the hope that the output will remain stable. When packing bundles,
; we try to limit to a few instructions with well known sizes and
; minimal use of registers and stack slots in the lowering sequence.
; XFAIL: filtype=asm
; RUN: %p2i -i %s --sandbox --filetype=obj --disassemble --args -Om1 \
; RUN: -allow-externally-defined-symbols \
; RUN: -ffunction-sections | FileCheck %s
; RUN: %p2i -i %s --sandbox --filetype=obj --disassemble --target=x8664 \
; RUN: --args -Om1 -allow-externally-defined-symbols \
; RUN: -ffunction-sections | FileCheck %s --check-prefix X8664
declare void @call_target()
@global_byte = internal global [1 x i8] zeroinitializer
@global_short = internal global [2 x i8] zeroinitializer
@global_int = internal global [4 x i8] zeroinitializer
; A direct call sequence uses the right mask and register-call sequence.
define internal void @test_direct_call() {
entry:
call void @call_target()
ret void
}
; CHECK-LABEL: test_direct_call
; CHECK: nop
; CHECK: 1b: {{.*}} call 1c
; CHECK-NEXT: 20:
; X8664-LABEL: test_direct_call
; X8664: push {{.*}} R_X86_64_32S test_direct_call+{{.*}}20
; X8664: jmp {{.*}} call_target
; An indirect call sequence uses the right mask and register-call sequence.
define internal void @test_indirect_call(i32 %target) {
entry:
%__1 = inttoptr i32 %target to void ()*
call void %__1()
ret void
}
; CHECK-LABEL: test_indirect_call
; CHECK: mov [[REG:.*]],DWORD PTR [esp
; CHECK-NEXT: nop
; CHECK: 1b: {{.*}} and [[REG]],0xffffffe0
; CHECK-NEXT: call [[REG]]
; CHECk-NEXT: 20:
; X8664-LABEL: test_indirect_call
; X8664: push {{.*}} R_X86_64_32S test_indirect_call+{{.*}}20
; X8664: {{.*}} and e[[REG:..]],0xffffffe0
; X8664: add r[[REG]],r15
; X8664: jmp r[[REG]]
; A return sequence uses the right pop / mask / jmp sequence.
define internal void @test_ret() {
entry:
ret void
}
; CHECK-LABEL: test_ret
; CHECK: pop ecx
; CHECK-NEXT: and ecx,0xffffffe0
; CHECK-NEXT: jmp ecx
; X8664-LABEL: test_ret
; X8664: pop rcx
; X8664: and ecx,0xffffffe0
; X8664: add rcx,r15
; X8664: jmp rcx
; A perfectly packed bundle should not have nops at the end.
define internal void @packed_bundle() {
entry:
call void @call_target()
; bundle boundary
%addr_byte = bitcast [1 x i8]* @global_byte to i8*
%addr_short = bitcast [2 x i8]* @global_short to i16*
store i8 0, i8* %addr_byte, align 1 ; 7-byte instruction
store i16 0, i16* %addr_short, align 1 ; 9-byte instruction
store i8 0, i8* %addr_byte, align 1 ; 7-byte instruction
store i16 0, i16* %addr_short, align 1 ; 9-byte instruction
; bundle boundary
store i8 0, i8* %addr_byte, align 1 ; 7-byte instruction
store i16 0, i16* %addr_short, align 1 ; 9-byte instruction
ret void
}
; CHECK-LABEL: packed_bundle
; CHECK: call
; CHECK-NEXT: 20: {{.*}} mov BYTE PTR
; CHECK-NEXT: 27: {{.*}} mov WORD PTR
; CHECK-NEXT: 30: {{.*}} mov BYTE PTR
; CHECK-NEXT: 37: {{.*}} mov WORD PTR
; CHECK-NEXT: 40: {{.*}} mov BYTE PTR
; CHECK-NEXT: 47: {{.*}} mov WORD PTR
; An imperfectly packed bundle should have one or more nops at the end.
define internal void @nonpacked_bundle() {
entry:
call void @call_target()
; bundle boundary
%addr_short = bitcast [2 x i8]* @global_short to i16*
store i16 0, i16* %addr_short, align 1 ; 9-byte instruction
store i16 0, i16* %addr_short, align 1 ; 9-byte instruction
store i16 0, i16* %addr_short, align 1 ; 9-byte instruction
; nop padding
; bundle boundary
store i16 0, i16* %addr_short, align 1 ; 9-byte instruction
ret void
}
; CHECK-LABEL: nonpacked_bundle
; CHECK: call
; CHECK-NEXT: 20: {{.*}} mov WORD PTR
; CHECK-NEXT: 29: {{.*}} mov WORD PTR
; CHECK-NEXT: 32: {{.*}} mov WORD PTR
; CHECK-NEXT: 3b: {{.*}} nop
; CHECK: 40: {{.*}} mov WORD PTR
; A zero-byte instruction (e.g. local label definition) at a bundle
; boundary should not trigger nop padding.
define internal void @label_at_boundary(i32 %arg, float %farg1, float %farg2) {
entry:
%argi8 = trunc i32 %arg to i8
call void @call_target()
; bundle boundary
%addr_short = bitcast [2 x i8]* @global_short to i16*
%addr_int = bitcast [4 x i8]* @global_int to i32*
store i32 0, i32* %addr_int, align 1 ; 10-byte instruction
%blah = select i1 true, i8 %argi8, i8 %argi8 ; 22-byte lowering sequence
; label is here
store i16 0, i16* %addr_short, align 1 ; 9-byte instruction
ret void
}
; CHECK-LABEL: label_at_boundary
; CHECK: call
; We rely on a particular 7-instruction 22-byte Om1 lowering sequence
; for select.
; CHECK-NEXT: 20: {{.*}} mov DWORD PTR
; CHECK-NEXT: 2a: {{.*}} mov {{.*}},0x1
; CHECK-NEXT: 2c: {{.*}} cmp {{.*}},0x0
; CHECK-NEXT: 2e: {{.*}} mov {{.*}},BYTE PTR
; CHECK-NEXT: 32: {{.*}} mov BYTE PTR
; CHECK-NEXT: 36: {{.*}} jne 40
; CHECK-NEXT: 38: {{.*}} mov {{.*}},BYTE PTR
; CHECK-NEXT: 3c: {{.*}} mov BYTE PTR
; CHECK-NEXT: 40: {{.*}} mov WORD PTR
; Bundle lock without padding.
define internal void @bundle_lock_without_padding() {
entry:
%addr_short = bitcast [2 x i8]* @global_short to i16*
store i16 0, i16* %addr_short, align 1 ; 9-byte instruction
ret void
}
; CHECK-LABEL: bundle_lock_without_padding
; CHECK: mov WORD PTR
; CHECK-NEXT: pop ecx
; CHECK-NEXT: and ecx,0xffffffe0
; CHECK-NEXT: jmp ecx
; Bundle lock with padding.
define internal void @bundle_lock_with_padding() {
entry:
call void @call_target()
; bundle boundary
%addr_byte = bitcast [1 x i8]* @global_byte to i8*
%addr_short = bitcast [2 x i8]* @global_short to i16*
store i8 0, i8* %addr_byte, align 1 ; 7-byte instruction
store i16 0, i16* %addr_short, align 1 ; 9-byte instruction
store i16 0, i16* %addr_short, align 1 ; 9-byte instruction
ret void
; 3 bytes to restore stack pointer
; 1 byte to pop ecx
; bundle_lock
; 3 bytes to mask ecx
; This is now 32 bytes from the beginning of the bundle, so
; a 3-byte nop will need to be emitted before the bundle_lock.
; 2 bytes to jump to ecx
; bundle_unlock
}
; CHECK-LABEL: bundle_lock_with_padding
; CHECK: call
; CHECK-NEXT: 20: {{.*}} mov BYTE PTR
; CHECK-NEXT: 27: {{.*}} mov WORD PTR
; CHECK-NEXT: 30: {{.*}} mov WORD PTR
; CHECK-NEXT: 39: {{.*}} add esp,
; CHECK-NEXT: 3c: {{.*}} pop ecx
; CHECK-NEXT: 3d: {{.*}} nop
; CHECK-NEXT: 40: {{.*}} and ecx,0xffffffe0
; CHECK-NEXT: 43: {{.*}} jmp ecx
; Bundle lock align_to_end without any padding.
define internal void @bundle_lock_align_to_end_padding_0() {
entry:
call void @call_target()
; bundle boundary
%addr_short = bitcast [2 x i8]* @global_short to i16*
store i16 0, i16* %addr_short, align 1 ; 9-byte instruction
store i16 0, i16* %addr_short, align 1 ; 9-byte instruction
store i16 0, i16* %addr_short, align 1 ; 9-byte instruction
call void @call_target() ; 5-byte instruction
ret void
}
; CHECK-LABEL: bundle_lock_align_to_end_padding_0
; CHECK: call
; CHECK-NEXT: 20: {{.*}} mov WORD PTR
; CHECK-NEXT: 29: {{.*}} mov WORD PTR
; CHECK-NEXT: 32: {{.*}} mov WORD PTR
; CHECK-NEXT: 3b: {{.*}} call
; Bundle lock align_to_end with one bunch of padding.
define internal void @bundle_lock_align_to_end_padding_1() {
entry:
call void @call_target()
; bundle boundary
%addr_byte = bitcast [1 x i8]* @global_byte to i8*
store i8 0, i8* %addr_byte, align 1 ; 7-byte instruction
store i8 0, i8* %addr_byte, align 1 ; 7-byte instruction
store i8 0, i8* %addr_byte, align 1 ; 7-byte instruction
call void @call_target() ; 5-byte instruction
ret void
}
; CHECK-LABEL: bundle_lock_align_to_end_padding_1
; CHECK: call
; CHECK-NEXT: 20: {{.*}} mov BYTE PTR
; CHECK-NEXT: 27: {{.*}} mov BYTE PTR
; CHECK-NEXT: 2e: {{.*}} mov BYTE PTR
; CHECK-NEXT: 35: {{.*}} nop
; CHECK: 3b: {{.*}} call
; Bundle lock align_to_end with two bunches of padding.
define internal void @bundle_lock_align_to_end_padding_2(i32 %target) {
entry:
call void @call_target()
; bundle boundary
%addr_byte = bitcast [1 x i8]* @global_byte to i8*
%addr_short = bitcast [2 x i8]* @global_short to i16*
%__1 = inttoptr i32 %target to void ()*
store i8 0, i8* %addr_byte, align 1 ; 7-byte instruction
store i16 0, i16* %addr_short, align 1 ; 9-byte instruction
store i16 0, i16* %addr_short, align 1 ; 9-byte instruction
call void %__1()
; 4 bytes to load %target into a register
; bundle_lock align_to_end
; 3 bytes to mask the register
; This is now 32 bytes from the beginning of the bundle, so
; a 3-byte nop will need to be emitted before the bundle_lock,
; followed by a 27-byte nop before the mask/jump.
; 2 bytes to jump to the register
; bundle_unlock
ret void
}
; CHECK-LABEL: bundle_lock_align_to_end_padding_2
; CHECK: call
; CHECK-NEXT: 20: {{.*}} mov BYTE PTR
; CHECK-NEXT: 27: {{.*}} mov WORD PTR
; CHECK-NEXT: 30: {{.*}} mov WORD PTR
; CHECK-NEXT: 39: {{.*}} mov [[REG:.*]],DWORD PTR [esp
; CHECK-NEXT: 3d: {{.*}} nop
; CHECK: 40: {{.*}} nop
; CHECK: 5b: {{.*}} and [[REG]],0xffffffe0
; CHECK-NEXT: 5e: {{.*}} call [[REG]]
; Tests the pad_to_end bundle alignment with no padding bytes needed.
define internal void @bundle_lock_pad_to_end_padding_0(i32 %arg0, i32 %arg1,
i32 %arg3, i32 %arg4,
i32 %arg5, i32 %arg6) {
call void @call_target()
; bundle boundary
%x = add i32 %arg5, %arg6 ; 12 bytes
%y = trunc i32 %x to i16 ; 10 bytes
call void @call_target() ; 10 bytes
; bundle boundary
ret void
}
; X8664: 56: {{.*}} push {{.*}} R_X86_64_32S bundle_lock_pad_to_end_padding_0+{{.*}}60
; X8664: 5b: {{.*}} jmp {{.*}} call_target
; X8664: 60: {{.*}} add
; Tests the pad_to_end bundle alignment with 11 padding bytes needed, and some
; instructions before the call.
define internal void @bundle_lock_pad_to_end_padding_11(i32 %arg0, i32 %arg1,
i32 %arg3, i32 %arg4,
i32 %arg5, i32 %arg6) {
call void @call_target()
; bundle boundary
%x = add i32 %arg5, %arg6 ; 11 bytes
call void @call_target() ; 10 bytes
; 11 bytes of nop
; bundle boundary
ret void
}
; X8664: 4b: {{.*}} push {{.*}} R_X86_64_32S bundle_lock_pad_to_end_padding_11+{{.*}}60
; X8664: 50: {{.*}} jmp {{.*}} call_target
; X8664: 55: {{.*}} nop
; X8664: 5d: {{.*}} nop
; X8664: 60: {{.*}} add
; Tests the pad_to_end bundle alignment with 22 padding bytes needed, and no
; instructions before the call.
define internal void @bundle_lock_pad_to_end_padding_22(i32 %arg0, i32 %arg1,
i32 %arg3, i32 %arg4,
i32 %arg5, i32 %arg6) {
call void @call_target()
; bundle boundary
call void @call_target() ; 10 bytes
; 22 bytes of nop
; bundle boundary
ret void
}
; X8664: 40: {{.*}} push {{.*}} R_X86_64_32S bundle_lock_pad_to_end_padding_22+{{.*}}60
; X8664: 45: {{.*}} jmp {{.*}} call_target
; X8664: 4a: {{.*}} nop
; X8664: 52: {{.*}} nop
; X8664: 5a: {{.*}} nop
; X8664: 60: {{.*}} add
; Stack adjustment state during an argument push sequence gets
; properly checkpointed and restored during the two passes, as
; observed by the stack adjustment for accessing stack-allocated
; variables.
define internal void @checkpoint_restore_stack_adjustment(i32 %arg) {
entry:
call void @call_target()
; bundle boundary
call void @checkpoint_restore_stack_adjustment(i32 %arg)
ret void
}
; CHECK-LABEL: checkpoint_restore_stack_adjustment
; CHECK: sub esp,0x1c
; CHECK: call
; The address of %arg should be [esp+0x20], not [esp+0x30].
; CHECK-NEXT: mov [[REG:.*]],DWORD PTR [esp+0x20]
; CHECK-NEXT: mov DWORD PTR [esp],[[REG]]
; CHECK: call
; CHECK: add esp,0x1c