1222656Sed// This file is dual licensed under the MIT and the University of Illinois Open
2222656Sed// Source Licenses. See LICENSE.TXT for details.
3214152Sed
4214152Sed#include "../assembly.h"
5214152Sed
6214152Sed// di_int __ashrdi3(di_int input, int count);
7214152Sed
8214152Sed#ifdef __i386__
9214152Sed#ifdef __SSE2__
10214152Sed
11214152Sed.text
12214152Sed.align 4
13214152SedDEFINE_COMPILERRT_FUNCTION(__ashrdi3)
14214152Sed	movd	  12(%esp),		%xmm2	// Load count
15214152Sed	movl	   8(%esp),		%eax
16214152Sed#ifndef TRUST_CALLERS_USE_64_BIT_STORES
17214152Sed	movd	   4(%esp),		%xmm0
18214152Sed	movd	   8(%esp),		%xmm1
19214152Sed	punpckldq	%xmm1,		%xmm0	// Load input
20214152Sed#else
21214152Sed	movq	   4(%esp),		%xmm0	// Load input
22214152Sed#endif
23214152Sed
24214152Sed	psrlq		%xmm2,		%xmm0	// unsigned shift input by count
25214152Sed
26214152Sed	testl		%eax,		%eax	// check the sign-bit of the input
27214152Sed	jns			1f					// early out for positive inputs
28214152Sed
29214152Sed	// If the input is negative, we need to construct the shifted sign bit
30214152Sed	// to or into the result, as xmm does not have a signed right shift.
31214152Sed	pcmpeqb		%xmm1,		%xmm1	// -1ULL
32214152Sed	psrlq		$58,		%xmm1	// 0x3f
33214152Sed	pandn		%xmm1,		%xmm2	// 63 - count
34214152Sed	pcmpeqb		%xmm1,		%xmm1	// -1ULL
35214152Sed	psubq		%xmm1,		%xmm2	// 64 - count
36214152Sed	psllq		%xmm2,		%xmm1	// -1 << (64 - count) = leading sign bits
37214152Sed	por			%xmm1,		%xmm0
38214152Sed
39214152Sed	// Move the result back to the general purpose registers and return
40214152Sed1:	movd		%xmm0,		%eax
41214152Sed	psrlq		$32,		%xmm0
42214152Sed	movd		%xmm0,		%edx
43214152Sed	ret
44214152Sed
45214152Sed#else // Use GPRs instead of SSE2 instructions, if they aren't available.
46214152Sed
47214152Sed.text
48214152Sed.align 4
49214152SedDEFINE_COMPILERRT_FUNCTION(__ashrdi3)
50214152Sed	movl	  12(%esp),		%ecx	// Load count
51214152Sed	movl	   8(%esp),		%edx	// Load high
52214152Sed	movl	   4(%esp),		%eax	// Load low
53214152Sed
54214152Sed	testl		$0x20,		%ecx	// If count >= 32
55214152Sed	jnz			1f					//    goto 1
56214152Sed
57214152Sed	shrdl		%cl, %edx,	%eax	// right shift low by count
58214152Sed	sarl		%cl,		%edx	// right shift high by count
59214152Sed	ret
60214152Sed
61214152Sed1:	movl		%edx,		%eax	// Move high to low
62214152Sed	sarl		$31,		%edx	// clear high
63214152Sed	sarl		%cl,		%eax	// shift low by count - 32
64214152Sed	ret
65214152Sed
66214152Sed#endif // __SSE2__
67214152Sed#endif // __i386__
68