benchmarks.c revision 60c45ed01d4f99571d468c42f609d11a099fab1e
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc.
* All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#ifdef __lint
#pragma error_messages(off, E_VALUE_TYPE)
#endif
#include <stdlib.h>
#include <unistd.h>
#include <fp.h>
#include <fps_ereport.h>
#define EXPECTED 1.9999999999999998E+00
static void fdivd(double *f22, double *f2, double *f12);
static void fmuld(double *x, double *y, double *z, double *z1);
static void fmulx(uint64_t *rs1, uint64_t *rs2, uint64_t *rd);
int fpu_fdivd(int rloop, struct fps_test_ereport *report);
int fpu_fmuld(int rloop, struct fps_test_ereport *report);
int fpu_fmulx(int rloop, struct fps_test_ereport *report);
#ifdef V9B
extern float fpackfix(double num);
extern unsigned long fcmpgt16(double in1, double in2);
extern unsigned long fcmpne16(double in1, double in2);
float fpackfix(double);
int align_data(int loop, int unit,
struct fps_test_ereport *report);
int vis_test(int unit, struct fps_test_ereport *report);
static int align_error_create(char *err, int start, int offest,
int loop, int count);
static int do_aligndata(uchar_t *from, uint32_t *offset, size_t sz,
uchar_t *f0, uchar_t *f2, uint32_t bmask);
static int visgt16(int, struct fps_test_ereport *report);
static int visne16(int, struct fps_test_ereport *report);
static int vispackfix(int, struct fps_test_ereport *report);
unsigned long fcmpgt16(double, double);
unsigned long fcmpne16(double, double);
unsigned long setgsr(unsigned long);
#endif
/*
* fpu_fdivd(int rloop, int unit, struct fps_test_ereport *report)
* returns whether the correct value is calculated each time
* rloop times. If an error is found, the relevant data is stored
* in report. The test uses internally generated random double
* precision within a certain range to conduct the following test:
*
* (a * 2^1022) / ((a+e) * 2^1021)
*
* which is guaranteed to fill the resulting mantissa with all ones.
*
*/
int
fpu_fdivd(int rloop, struct fps_test_ereport *report)
{
char err_data[MAX_INFO_SIZE];
double expect_ans = EXPECTED;
double f12 = 0;
double f2;
double f22;
int loop = 0;
uint64_t expect;
uint64_t observe;
srand48(1L);
while (loop < rloop) {
loop++;
/* LINTED */
*(uint32_t *)& f22 = mrand48();
*(uint32_t *)& f22 &= 0x80069fff;
*(uint32_t *)& f22 |= 0x7fd69f00;
#ifdef __lint
(void) f22;
#endif
/* LINTED */
*((uint32_t *)& f22 + 1) = mrand48();
*((uint32_t *)& f22 + 1) |= 0x00000001;
*(uint64_t *)& f2 = *(uint64_t *)& f22 + 1;
*(uint32_t *)& f2 &= 0x800FFFFF;
*(uint32_t *)& f2 |= 0x7FC00000;
#ifdef __lint
(void) f2;
#endif
fdivd(&f22, &f2, &f12);
if (f12 != expect_ans) {
snprintf(err_data, sizeof (err_data),
"\nExpected: %.16e,\nObserved: %.16e",
expect_ans, f12);
expect = *(uint64_t *)&expect_ans;
observe = *(uint64_t *)&f12;
setup_fps_test_struct(IS_EREPORT_INFO, report,
6340, &observe, &expect, 1, 1, err_data);
return (-1);
}
}
return (0);
}
/*
* fdivd(uint64_t *rs1, uint64_t *rs2, uint64_t *rd)
* performs the assembly level instructions for
* fpu_fdivd.
*/
/* ARGSUSED */
static void
fdivd(double *f22, double *f2, double *f12)
{
asm("ldd [%i0], %f22");
asm("ldd [%i1], %f2");
asm("fdivd %f22, %f2, %f12");
asm("std %f12,[%i2]");
asm("membar #Sync");
}
/*
* fpu_fmuld(int rloop, int unit, struct fps_test_ereport *report)
* returns whether the correct value is calculated each time
* rloop times. If an error is found, the relevant data is stored
* in report. The goal is to check if (x * y) == (y * x). The
* data pattern is important, and the back-to-back fmuld's are
* important.
*/
int
fpu_fmuld(int rloop, struct fps_test_ereport *report)
{
char err_data[MAX_INFO_SIZE];
double x;
double y;
double z;
double z1;
int loop;
uint64_t expect;
uint64_t observe;
uint64_t *px;
uint64_t *py;
loop = 0;
px = (uint64_t *)& x;
py = (uint64_t *)& y;
*px = 0x2FEBD8507111CDE5UL; /* 4865027 */
*py = 0x2FE284A9A98EAA26UL;
#ifdef __lint
(void) x;
(void) y;
#endif
while (loop < rloop) {
loop++;
z = z1 = 0.0;
/*
* Data pattern and back-to-back fmuld() are
* important
*/
fmuld(&x, &y, &z, &z1);
if (*(uint64_t *)&z != *(uint64_t *)&z1) {
snprintf(err_data, sizeof (err_data),
"\nExpected: %.16e,\nObserved: %.16e",
*(uint64_t *)&z, *(uint64_t *)&z1);
expect = *(uint64_t *)&z;
observe = *(uint64_t *)&z1;
setup_fps_test_struct(IS_EREPORT_INFO, report,
6341, &observe, &expect, 1, 1, err_data);
return (-1);
}
}
return (0);
}
/*
* fmuld(double *x,double *y, double *z, double *z1)
* performs the assembly level instructions for
* fpu_fmuld.
*/
/* ARGSUSED */
static void
fmuld(double *x, double *y, double *z, double *z1)
{
asm("ldd[%i0], %f0");
asm("ldd[%i1], %f4");
asm("fmuld%f0, %f4, %f2");
asm("fmuld%f4, %f0, %f6");
asm("std%f2, [%i2]");
asm("std%f6, [%i3]");
asm("membar #Sync");
}
/*
* fpu_fmulx(int rloop, int unit, struct fps_test_ereport *report)
* returns whether the correct value is calculated each time
* rloop times. If an error is found, the relevant data is stored
* in report. The goal is to check if (x * y) == (y * x) with
* 64-bit intgers.
*/
int
fpu_fmulx(int rloop, struct fps_test_ereport *report)
{
char err_data[MAX_INFO_SIZE];
int loop;
int loop_lim;
uint32_t *rs1;
uint32_t *rs2;
uint64_t expect;
uint64_t observe;
uint64_t v1;
uint64_t v2;
uint64_t vd1;
uint64_t vd2;
uint64_t *rd1;
uint64_t *rd2;
v1 = v2 = vd1 = vd2 = 0;
loop = 0;
loop_lim = rloop;
if (loop_lim < 10)
loop_lim = 10;
if (loop_lim > 100000)
loop_lim = 100000;
rs1 = (uint32_t *)& v1;
rs2 = (uint32_t *)& v2;
rd1 = &vd1;
rd2 = &vd2;
#ifdef __lint
(void) v1;
(void) v2;
#endif
srand(0l);
while (loop < loop_lim) {
loop++;
#ifndef __lint
*rs1 = mrand48();
*(rs1 + 1) = mrand48();
*rs2 = mrand48();
*(rs2 + 1) = mrand48();
#endif
/* LINTED */
fmulx((uint64_t *)rs1, (uint64_t *)rs2, rd1);
/* LINTED */
fmulx((uint64_t *)rs2, (uint64_t *)rs1, rd2);
if (*rd1 != *rd2) {
expect = (uint64_t)*rd1;
observe = (uint64_t)*rd2;
snprintf(err_data, sizeof (err_data),
"\nExpected: %lld\nObserved: %lld", *rd1, *rd2);
setup_fps_test_struct(IS_EREPORT_INFO, report,
6356, &observe, &expect, 1, 1, err_data);
return (-1);
}
}
return (0);
}
/*
* fmulx(uint64_t *rs1, uint64_t *rs2, uint64_t *rd)
* performs the assembly level instructions for
* fpu_fmulx.
*/
/* ARGSUSED */
static void
fmulx(uint64_t *rs1, uint64_t *rs2, uint64_t *rd)
{
asm("ldx [%i0], %l0");
asm("ldx [%i1], %l1");
asm("mulx %l0, %l1, %l2");
asm("stx %l2, [%i2]");
asm("membar #Sync");
}
#ifdef V9B
#pragma align 64 (f0)
#pragma align 8 (f2)
#define MEMSIZE 2048*3
static uchar_t f0[64];
static uchar_t f2[8];
static uint32_t bmask[] = {0x01234567, 0x12345678,
0x23456789, 0x3456789a,
0x456789ab, 0x56789abc,
0x6789abcd, 0x789abcde,
0x89abcdef, 0x9abcdef0,
0xabcdef01, 0xbcdef012,
0xcdef0123, 0xdef01234,
0xef012345, 0xf0123456,
0x55555555, 0xaaaaaaaa,
0x00000000, 0xffffffff};
/*
* align_data(int loop, int unit, struct fps_test_ereport *report)
* returns whether a miscompare was found after running alignment tests
* loop amount of times. If an error is found, relevant data is stored
* in report. This test exercises the alignaddr and aligndata
* instructions with different byte alignments to ensure proper
* operation. These two instructions are used extensively by the kernel
* to move data size greater than 512 bytes. User level memcpy and
* memmove library also use these instructions for data size
* greater than 256 bytes.
*/
int
align_data(int loop, int unit, struct fps_test_ereport *report)
{
char err[MAX_INFO_SIZE];
int test_ret;
int nr_malloc;
size_t memsize;
struct timeval timeout;
uchar_t c;
uchar_t *pf0;
uchar_t *pf2;
uchar_t *src;
uint32_t cnt;
uint32_t i;
uint32_t offset;
uint32_t start;
uint64_t expect[2];
uint64_t observe[2];
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
nr_malloc = 0;
err[0] = '\0';
/* Make sure memsize is 64 bytes aligned with minimum of 64 bytes */
memsize = MEMSIZE;
memsize = memsize / 64 * 64;
src = NULL;
if (memsize < 64)
memsize = 64;
src = (uchar_t *)memalign(64, memsize + 64);
while (src == NULL && nr_malloc < 10) {
select(1, NULL, NULL, NULL, &timeout);
nr_malloc++;
src = (uchar_t *)memalign(64, memsize + 64);
}
if (src == NULL)
_exit(FPU_SYSCALL_FAIL);
/* Initialize source array with sequential data */
c = 0;
for (i = 0; i < memsize + 64; i++)
*(src + i) = c++;
for (cnt = 0; cnt < loop; cnt++) {
for (start = 1; start < 64; start += 1) {
test_ret = 0;
offset = 0;
test_ret = do_aligndata(src + start, &offset,
memsize, f0, f2, bmask[cnt % 20]);
/*
* Miscompare on the two aligndata
* instructions. Calculate offset to source
* array and get miscompare data
*/
if (test_ret != 0) {
pf0 = f0 + offset % 64;
pf2 = f2;
for (i = 0; i < 8; i++) {
if (*(pf0 + i) != *(pf2 + i))
break;
}
align_error_create(err, start,
offset + start + i, loop, cnt);
expect[0] =
(uint64_t)(*(uint8_t *)
(src + offset + start + i));
expect[1] = (uint64_t)0;
observe[0] = (uint64_t)(*(uint8_t *)(pf0 + i));
observe[1] = (uint64_t)(*(uint8_t *)(pf2 + i));
setup_fps_test_struct(
IS_EREPORT_INFO,
report, 6344, observe,
expect, 1, 2, err);
free(src);
return (-1);
}
/*
* No miscompare on the aligndata
* instructions. Check to see whether the
* last 64 bytes matches the input
*/
if (test_ret == 0) {
pf2 = src + offset + start;
for (i = 0; i < 64; i++) {
if (f0[i] != *(pf2 + i)) {
align_error_create(err,
start,
offset + start + i,
loop, cnt);
expect[0] =
(uint64_t)(*(uint8_t *)
(pf2 + i));
expect[1] = (uint64_t)0;
observe[0] = (uint64_t)f0[i];
observe[1] = (uint64_t)0;
setup_fps_test_struct(
IS_EREPORT_INFO,
report, 6343, observe,
expect, 1, 1, err);
free(src);
return (-1);
}
}
}
}
}
free(src);
return (0);
}
/*
* align_error_create(char *err, int start, int offset, int loop, int count)
* returns if a successful snprintf was performed when creating an align_data
* error message for align_data.
*/
static int
align_error_create(char *err, int start, int offset, int loop, int count)
{
if (err == NULL)
return (-1);
return snprintf(err, sizeof (err),
"Start = %2.2d offset = %2.2d loop = %d cnt = %d",
start, offset, loop, count);
}
/*
* do_aligndata(uchar_t *from, uint32_t *offset, size_t sz,
* uchar_t *f0, uchar_t *f2, uint32_t bmask) performs
* the assembly lvl routines for align_data.
*/
static int
do_aligndata(uchar_t *from, uint32_t *offset, size_t sz,
uchar_t *f0, uchar_t *f2, uint32_t bmask)
{
asm("bmask %i5,%g0,%g0");
/* produce GSR.offset and align %l0 to 8 bytes boundary */
asm("alignaddr %i0, %g0, %l0");
/* %i0 then used as error register, assume error */
asm("mov 1,%i0");
/* %l1 used as offset counter */
asm("mov -8,%l1");
asm("ldd [%l0], %f0");
asm("next_read:");
asm("ldd [%l0+8], %f2");
asm("ldd [%l0+0x10], %f4");
asm("faligndata %f0, %f2, %f32");
asm("faligndata %f0, %f2, %f48");
asm("fcmpd %fcc0,%f32,%f48");
asm("fblg,pn %fcc0,error");
/* %l1 contains offset value */
asm("add %l1,8,%l1");
/* 0 - 7 */
asm("ldd [%l0+0x18], %f6");
asm("faligndata %f2, %f4, %f34");
asm("faligndata %f2, %f4, %f48");
asm("fcmpd %fcc0,%f34,%f48");
asm("fblg,pn %fcc0,error");
/* %l1 contains offset value */
asm("add %l1,8,%l1");
/* 9 - 15 */
asm("ldd [%l0+0x20], %f8");
asm("faligndata %f4, %f6, %f36");
asm("faligndata %f4, %f6, %f48");
asm("fcmpd %fcc0,%f36,%f48");
asm("fblg,pn %fcc0,error");
/* %l1 contains offset value */
asm("add %l1,8,%l1");
/* 16 - 23 */
asm("ldd [%l0+0x28], %f10");
asm("faligndata %f6, %f8, %f38");
asm("faligndata %f6, %f8, %f48");
asm("fcmpd %fcc0,%f38,%f48");
asm("fblg,pn %fcc0,error");
/* contains offset value */
asm("add %l1,8,%l1");
/* 24 - 31 */
asm("ldd [%l0+0x28], %f10");
asm("faligndata %f8, %f10, %f40");
asm("faligndata %f8, %f10, %f48");
asm("fcmpd %fcc0,%f40,%f48");
asm("fblg,pn %fcc0,error");
/* %l1 contains offset value */
asm("add %l1,8,%l1");
/* 32 - 39 */
asm("ldd [%l0+0x30], %f12");
asm("faligndata %f10, %f12, %f42");
asm("faligndata %f10, %f12, %f48");
asm("fcmpd %fcc0,%f42,%f48");
asm("fblg,pn %fcc0,error");
/* %l1 contains offset value */
asm("add %l1,8,%l1");
/* 40 - 47 */
asm("ldd [%l0+0x38], %f14");
asm("faligndata %f12, %f14, %f44");
asm("faligndata %f12, %f14, %f48");
asm("fcmpd %fcc0,%f44,%f48");
asm("fblg,pn %fcc0,error");
/* %l1 contains offset value */
asm("add %l1,8,%l1");
/* 48 - 55 */
asm("ldd [%l0+0x40], %f0");
asm("faligndata %f14, %f0, %f46");
asm("faligndata %f14, %f0, %f48");
asm("fcmpd %fcc0,%f46,%f48");
asm("fblg,pn %fcc0,error");
/* %l1 contains offset value */
asm("add %l1,8,%l1");
/* 56 - 63 */
asm("subcc %i2,64,%i2");
asm("bg next_read");
asm("add %l0,64,%l0");
/* no miscompare error */
asm("mov 0,%i0");
/* no error, move back to last 64 bytes boundary */
asm("sub %l1,56,%l1");
asm("error:");
asm("stda %f32,[%i3]0xf0");
asm("std %f48,[%i4]");
/* store offset value */
asm("st %l1,[%i1]");
asm("membar #Sync");
}
/*
* vis_test(int unit, struct fps_test_ereport *report)
* checks if various RISC operations are performed
* succesfully. If an error is found, relevant data
* is stored in report.
*/
int
vis_test(int unit, struct fps_test_ereport *report)
{
int v1;
int v2;
int v3;
v1 = v2 = v3 = 0;
v1 = visgt16(unit, report);
v2 = visne16(unit, report);
v3 = vispackfix(unit, report);
if ((0 != v1) || (0 != v2) || (0 != v3))
return (-1);
return (0);
}
/*
* visgt16(int unit, struct fps_test_ereport *report)
* does a greater-than compare instruction and returns if
* successful or not. If an error, relevant data is
* stored in report.
*/
static int
visgt16(int unit, struct fps_test_ereport *report)
{
uint64_t expected;
uint64_t observed;
unsigned long a = 0x0000000000000001;
unsigned long b = 0x8000000008000008;
unsigned long c = fcmpgt16(*((double *)&a), *((double *)&b));
if (c == 0x8)
return (0);
else {
expected = (uint64_t)0x8;
observed = (*(uint64_t *)&c);
setup_fps_test_struct(NO_EREPORT_INFO, report,
6364, &observed, &expected, 1, 1);
return (-1);
}
}
/*
* visne16(int unit, struct fps_test_ereport *report)
* does a not-equal compare instruction and returns if
* successful or not. If an error, relevant data is
* stored in report.
*/
static int
visne16(int unit, struct fps_test_ereport *report)
{
uint64_t expected;
uint64_t observed;
unsigned long a = 0x0000000000000001;
unsigned long b = 0x0001000000001001;
unsigned long c = fcmpne16(*((double *)&a), *((double *)&b));
if (c == 0x9)
return (0);
else {
expected = (uint64_t)0x9;
observed = (*(uint64_t *)&c);
setup_fps_test_struct(NO_EREPORT_INFO, report,
6365, &observed, &expected, 1, 1);
return (-1);
}
}
/*
* vispackfix(int unit, struct fps_test_ereport *report)
* does four 16-bit pack conversions to a lower precsion
* format and returns if successful or not. If an error,
* relevant data is stored in report.
*/
static int
vispackfix(int unit, struct fps_test_ereport *report)
{
float b;
int failed = 0;
uint64_t expected;
uint64_t observed;
unsigned int c;
unsigned long a = 0x8008000008008008;
unsigned long gsr = 0;
unsigned long old_gsr;
(void) setgsr(gsr);
b = fpackfix(*((double *)&a));
c = *((unsigned int *)&b);
if (c == 0x80080800)
return (0);
else {
expected = (uint64_t)0x80080800;
observed = (uint64_t)c;
setup_fps_test_struct(NO_EREPORT_INFO, report,
6366, &observed, &expected, 1, 1);
return (-1);
}
}
#endif