blob: 87dc6bc7e64d7de5dcce008c4e94799117cbcfe0 [file] [log] [blame]
/*
* Copyright (c) 2013 The Native Client Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "native_client/tests/toolchain/eh_helper.h"
#include <assert.h>
#include <math.h>
// Test that EH stack unwinding works when callee-saved floating point
// registers may be saved (e.g., on ARM).
// Also test that the callee-saved registers are restored during
// stack unwinding, for use in a catch-statement.
namespace {
// Make a float x 4, without including arch-specific headers.
typedef float v4sf __attribute__ ((__vector_size__(16)));
// Make it a union to help w/ accessing an individual element.
typedef union {
float elems[4];
v4sf v;
} Vec_4_f;
// Make some volatile constants to trick the optimizer.
volatile double kZero = 0.0;
volatile double kOne = 1.0;
volatile double kPiOver2 = M_PI_2;
volatile double kE = M_E;
volatile Vec_4_f kVOnes __attribute__((aligned(16))) = {
{ (float)kOne, (float)kOne, (float)kOne, (float)kOne }
};
// Make a function with roughly this structure:
// x, y, z = Heavy-Floating-Point-Compute;
// call();
// use(x, y, z);
//
// That makes it likely that x, y, z use callee-saved registers, instead of
// restoring values from the stack after the call.
void __attribute__((noinline)) outer(
double x, double y, double z, double w);
void outer(double x, double y, double z, double w) {
// Assume that the caller gives us appropriate values so that these
// computations give 0 as the result.
double should_be_zero1 = round(sqrt(x));
double should_be_zero2 = round(cos(y));
double should_be_zero3 = round(sin(z));
double should_be_zero4 = round(log(w));
double should_be_zero;
Vec_4_f v_should_be_ones __attribute__((aligned(16))),
v_will_be_zeros __attribute__((aligned(16)));
v_should_be_ones.v = kVOnes.v;
// Start v_will_be_zeros as all ones.
// Then do v_will_be_zeros = <1...> * <1...> - <1...>.
v_will_be_zeros.v = kVOnes.v;
v_will_be_zeros.v =
v_will_be_zeros.v * v_should_be_ones.v - v_should_be_ones.v;
// Function call to make it worth using callee-saved regs.
printf("Should be zero: %f, %f, %f, %f, %f\n",
should_be_zero1, should_be_zero2, should_be_zero3, should_be_zero4,
v_will_be_zeros.elems[0]);
if (should_be_zero1 > should_be_zero2)
should_be_zero = should_be_zero1;
else if (should_be_zero2 > should_be_zero3)
should_be_zero = should_be_zero2;
else if (should_be_zero3 > should_be_zero4)
should_be_zero = should_be_zero3;
else
should_be_zero = should_be_zero4;
next_step(static_cast<int>(3 - should_be_zero));
if (v_will_be_zeros.elems[0] > v_will_be_zeros.elems[1])
throw static_cast<int>(v_will_be_zeros.elems[0]);
else if (v_will_be_zeros.elems[1] > v_will_be_zeros.elems[2])
throw static_cast<int>(v_will_be_zeros.elems[1]);
else if (v_will_be_zeros.elems[2] > v_will_be_zeros.elems[3])
throw static_cast<int>(v_will_be_zeros.elems[2]);
else
throw static_cast<int>(v_will_be_zeros.elems[3]);
}
} // namespace
class B {
public:
explicit B(double should_be_one) {
next_step(static_cast<int>(5 * should_be_one));
}
~B() { next_step(7); }
};
void __attribute__((noinline)) middle(int never_55);
void middle(int never_55) {
double should_be_one1 = sqrt(kOne) * cos(kZero);
double should_be_one2 = sin(kPiOver2) * log(kE);
Vec_4_f v_should_be_ones1 __attribute__((aligned(16))),
v_should_be_ones2 __attribute__((aligned(16)));
v_should_be_ones1.v = kVOnes.v;
v_should_be_ones2.v = kVOnes.v;
v_should_be_ones1.v = v_should_be_ones1.v * v_should_be_ones2.v;
printf("Should be one: %f, %f, %f\n",
should_be_one1, should_be_one2, v_should_be_ones1.elems[0]);
try {
// Function call to make it worth using callee-saved regs.
outer(kZero, kPiOver2, kZero, kOne);
} catch(int x) {
if (x != 0)
abort();
printf("Should still be one: %f, %f, %f\n",
should_be_one1, should_be_one2, v_should_be_ones1.elems[0]);
next_step(4);
if (should_be_one1 > should_be_one2) {
throw B(should_be_one1);
} else if (should_be_one2 > v_should_be_ones1.elems[0]) {
throw B(should_be_one2);
} else {
throw B(v_should_be_ones1.elems[0]);
}
}
abort();
}
int main(int argc, char* argv[]) {
double should_be_e = kE;
double should_be_pi_2 = kPiOver2;
double should_be_63 = sqrt(kOne) * cos(kZero) * 63;
double should_be_127 = sin(kPiOver2) * log(kE) * 127;
next_step(1);
try {
next_step(2);
middle(argc);
} catch(...) {
fprintf(stderr, "should_be_e=%f, should_be_pi_2=%f, "
"should_be_63=%f, shouldbe_127=%f\n",
should_be_e, should_be_pi_2, should_be_63, should_be_127);
assert(should_be_e == kE);
assert(should_be_pi_2 == kPiOver2);
assert(should_be_63 == 63.0);
assert(should_be_127 == 127.0);
next_step(6);
}
next_step(8);
return 55;
}