summaryrefslogtreecommitdiff
path: root/usr/src/lib/libc/i386/fp/_base_il.s
blob: 157e9779228836cd900eac771ea1c8a9c64e1c21 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source.  A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 */

/*
 * Copyright 2014 OmniTI Computer Consulting, Inc. All rights reserved.
 */

	.file	"_base_il.s"

/*
 * These files are in assembly because some compilers will mistakenly reorder
 * multiplications or divisions wrapped in _putsw() and _getsw().  They are
 * proper subroutines for now, but should be considered candidates for
 * inlining eventually.
 *
 * The original C sources are included for readability in the pre-function
 * comment blocks.
 */

#include <SYS.h>

/*
 * Multiplies two normal or subnormal doubles, returns result and exceptions.
 *

double
__mul_set(double x, double y, int *pe) {
	extern void _putsw(), _getsw();
	int sw;
	double z;

	_putsw(0);
	z = x * y;
	_getsw(&sw);
	if ((sw & 0x3f) == 0) {
		*pe = 0;
	} else {
		*pe = 1;
	}
	return (z);
}

 */
	ENTRY(__mul_set)
	subl	$0x8, %esp	/* Give us an extra 8 bytes to play with. */
	/* Clear the floating point exception register. */
	fnclex			/* Equivalent of _putsw(0); */
	
	fldl	0xc(%esp)	/* Load up x */
	fmull	0x14(%esp)	/* And multiply! */
	
	/* Check to see if the multiply caused any exceptions. */
	fstsw	(%esp)		/* Equivalent of... */
	xorl	%edx, %edx
	andl	$0x3f, (%esp)	/* If the status word (low bits) are zero... */
	setne	%dl		/* ... set *pe (aka. (%eax)) accordingly. */
	movl	0x1c(%esp), %eax/* Get pe. */
	movl	%edx, (%eax)	/* And set it.  (True == FP exception). */
	addl	$0x8, %esp	/* Release the 8 play bytes. */
	ret
	SET_SIZE(__mul_set)

/*
 * Divides two normal or subnormal doubles x/y, returns result and exceptions.
 *

double
__div_set(double x, double y, int *pe) {
	extern void _putsw(), _getsw();
	int sw;
	double z;

	_putsw(0);
	z = x / y;
	_getsw(&sw);
	if ((sw & 0x3f) == 0) {
		*pe = 0;
	} else {
		*pe = 1;
	}
	return (z);
}

 */
	
	ENTRY(__div_set)
	subl	$0x8, %esp	/* Give us an extra 8 bytes to play with. */
	/* Clear the floating point exception register. */
	fnclex			/* Equivalent of _putsw(0); */
	
	fldl	0xc(%esp)	/* Load up x */
	fdivl	0x14(%esp)	/* And divide! */
	
	/* Check to see if the divide caused any exceptions. */
	fstsw	(%esp)		/* Equivalent of... */
	xorl	%edx, %edx
	andl	$0x3f, (%esp)	/* If the status word (low bits) are zero... */
	setne	%dl		/* ... set *pe (aka. (%eax)) accordingly. */
	movl	0x1c(%esp), %eax/* Get pe. */
	movl	%edx, (%eax)	/* And set it.  (True == FP exception). */
	addl	$0x8, %esp	/* Release the 8 play bytes. */
	ret
	SET_SIZE(__div_set)

/* double __dabs(double *d) - Get the abs. value of *d.  Straightforward. */

	ENTRY(__dabs)
	movl	0x4(%esp), %eax
	fldl	(%eax)
	fabs			/* Just let the FPU do its thing. */
	ret
	SET_SIZE(__dabs)