Files
avmplus/core/Float4Class.cpp
T
dmcallis 65a0592776 Initial source code drop
Source code drop for the Actionscript virtual machine in the Adobe
Flash Player for December 2013
2013-12-02 16:04:04 -08:00

255 lines
11 KiB
C++

/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "avmplus.h"
#include "BuiltinNatives.h"
#include <float.h>
#ifdef VMCFG_FLOAT
namespace avmplus
{
Float4Class::Float4Class(VTable* cvtable)
: ClassClosure(cvtable)
{
toplevel()->_float4Class = this;
// prototype objects are always vanilla objects.
createVanillaPrototype();
}
void Float4Class::fromComponents(float4_t* ret, float x, float y, float z, float w)
{
float* retp = reinterpret_cast<float*>(ret);
retp[0] = x; retp[1] = y; retp[2] = z; retp[3] = w;
}
Atom Float4Class::construct(int argc, Atom* argv)
{
// Float called as constructor creates new Float instance
// Note: new Float() with no args returns 0 - as does Number
if (argc == 0)
return core()->float4Atom(zeroIntAtom);
if(argc == 1)
return core()->float4Atom(argv[1]);
if(argc != 4)
vtable->init->argcError(argc);
float4_t val = { AvmCore::singlePrecisionFloat(argv[1]),
AvmCore::singlePrecisionFloat(argv[2]),
AvmCore::singlePrecisionFloat(argv[3]),
AvmCore::singlePrecisionFloat(argv[4])};
return core()->float4ToAtom(val);
}
// OPTIMIZEME: These are generic implementations. Faster MMX/SSE/Neon implementations are
// desirable but mostly in jitted code, so these may be OK for the interpreter and calls
// from the runtime. Still, if there are precision differences between the C++ implementations
// and the native MMX/SSE/Neon implementations then we'd be better off deferring to the
// intrinsics here so that there's no divergence between jitted and interpreted code.
//
// FIXME: Bugzilla 703605 has more information.
#define F4OP2(res,s1,s2,op) fromComponents(res, op( f4_x(s1),f4_x(s2)) , op(f4_y(s1), f4_y(s2)) , op( f4_z(s1), f4_z(s2)) , op(f4_w(s1), f4_w(s2)))
#define F4OP2SEL(res,s1,s2,op) fromComponents( res, \
(f4_x(s1) op f4_x(s2)) ? 1.0f : 0.0f , \
(f4_y(s1) op f4_y(s2)) ? 1.0f : 0.0f , \
(f4_z(s1) op f4_z(s2)) ? 1.0f : 0.0f , \
(f4_w(s1) op f4_w(s2)) ? 1.0f : 0.0f )
#define F4OP2SELe(res,s1,s2,op) fromComponents( res, \
f4_x(s1) op f4_x(s2) ? f4_x(s1) : f4_x(s2) , \
f4_y(s1) op f4_y(s2) ? f4_y(s1) : f4_y(s2) , \
f4_z(s1) op f4_z(s2) ? f4_z(s1) : f4_z(s2) , \
f4_w(s1) op f4_w(s2) ? f4_w(s1) : f4_w(s2) )
#define F4OP1(res,s1,op, ... ) fromComponents(res,op(f4_x(s1) __VA_ARGS__), op(f4_y(s1) __VA_ARGS__), op(f4_z(s1) __VA_ARGS__), op(f4_w(s1) __VA_ARGS__))
void Float4Class::isGreater(float4_t* result, const float4_t& x, const float4_t& y)
{
F4OP2SEL(result, x, y, >);
}
void Float4Class::isGreaterOrEqual(float4_t* result, const float4_t& x, const float4_t& y)
{
F4OP2SEL(result, x, y, >=);
}
void Float4Class::isLess(float4_t* result, const float4_t& x, const float4_t& y)
{
F4OP2SEL(result, x, y, <);
}
void Float4Class::isLessOrEqual(float4_t* result, const float4_t& x, const float4_t& y)
{
F4OP2SEL(result, x, y, <=);
}
void Float4Class::isEqual(float4_t* result, const float4_t& x, const float4_t& y)
{
F4OP2SEL(result, x, y, ==);
}
void Float4Class::isNotEqual(float4_t* result, const float4_t& x, const float4_t& y)
{
F4OP2SEL(result, x, y, !=);
}
void Float4Class::abs(float4_t* result, const float4_t& x)
{
F4OP1(result, x,fabsf);
}
// FIXME (Bugzilla 703605): Probably not right for either NaN nor -0 vs +0
void Float4Class::min(float4_t* result, const float4_t& x, const float4_t& y)
{
F4OP2SELe(result, x, y, <);
}
// FIXME (Bugzilla 703605): Probably not right for either NaN nor -0 vs +0
void Float4Class::max(float4_t* result, const float4_t& x, const float4_t& y)
{
F4OP2SELe(result, x, y, >);
}
// FIXME (Bugzilla 703605): Here we allow reduced precision so we almost certainly want to delegate to the
// native SIMD implementation to get consistent interpreter/JIT behavior.
void Float4Class::reciprocal(float4_t* result, const float4_t& x)
{
F4OP1(result, x, 1.0f/);
}
// FIXME: (Bugzilla 703605) We want consistent interpreter/jit behavior, so we want to defer to the
// machine primitive.
void Float4Class::rsqrt(float4_t* result, const float4_t& x)
{
F4OP1(result, x, 1.0f/sqrtf);
}
void Float4Class::sqrt(float4_t* result, const float4_t& x)
{
F4OP1(result, x, sqrtf);
}
void Float4Class::normalize(float4_t* result, const float4_t& x)
{
float m = magnitude(x);
F4OP1(result, x, , /m);
}
void Float4Class::cross(float4_t* result, const float4_t& a, const float4_t& b)
{
fromComponents(result, f4_y(a) * f4_z(b) - f4_z(a) * f4_y(b),
f4_z(a) * f4_x(b) - f4_x(a) * f4_z(b),
f4_x(a) * f4_y(b) - f4_y(a) * f4_x(b),
0.0f);
}
float Float4Class::dot(const float4_t& a, const float4_t& b)
{
return f4_x(a) * f4_x(b) +
f4_y(a) * f4_y(b) +
f4_z(a) * f4_z(b) +
f4_w(a) * f4_w(b);
}
float Float4Class::dot2(const float4_t& a, const float4_t& b)
{
return f4_x(a) * f4_x(b) +
f4_y(a) * f4_y(b);
}
float Float4Class::dot3(const float4_t& a, const float4_t& b)
{
return f4_x(a) * f4_x(b) +
f4_y(a) * f4_y(b) +
f4_z(a) * f4_z(b);
}
float Float4Class::magnitude(const float4_t& x)
{
return sqrtf(dot(x, x));
}
float Float4Class::magnitude2(const float4_t& x)
{
return sqrtf(dot2(x, x));
}
float Float4Class::magnitude3(const float4_t& x)
{
return sqrtf(dot3(x, x));
}
float Float4Class::distance(const float4_t& x, const float4_t& y)
{
return magnitude(f4_sub(x, y));
}
float Float4Class::distance2(const float4_t& x, const float4_t& y)
{
return magnitude2(f4_sub(x, y));
}
float Float4Class::distance3(const float4_t& x, const float4_t& y)
{
return magnitude3(f4_sub(x, y));
}
// "How" is really only one byte that specifies which field to get - two bits for each of the float4 positions.
// i.e. two bits specify which of the x/y/z/w to return as "x", the next two bits specify what to return as "y" and so on.
void Float4Class::_swizzle(float4_t* result, const float4_t& val, int32_t how)
{
AvmAssert(how >= 0 && how < 256);
#ifndef VMCFG_GENERIC_FLOAT4
#ifdef(VMCFG_SSE2)
#define CASE(x) case x: result = _mm_shuffle_ps(val, val, x);
#else
#error Unknown target for swizzle!
#endif
switch(how){ // must explicitly expand; the "shuffle" intrinsic doesn't take variables.
CASE(0);CASE(1);CASE(2);CASE(3);CASE(4);CASE(5);CASE(6);CASE(7);CASE(8);CASE(9);
CASE(10);CASE(11);CASE(12);CASE(13);CASE(14);CASE(15);CASE(16);CASE(17);CASE(18);CASE(19);
CASE(20);CASE(21);CASE(22);CASE(23);CASE(24);CASE(25);CASE(26);CASE(27);CASE(28);CASE(29);
CASE(30);CASE(31);CASE(32);CASE(33);CASE(34);CASE(35);CASE(36);CASE(37);CASE(38);CASE(39);
CASE(40);CASE(41);CASE(42);CASE(43);CASE(44);CASE(45);CASE(46);CASE(47);CASE(48);CASE(49);
CASE(50);CASE(51);CASE(52);CASE(53);CASE(54);CASE(55);CASE(56);CASE(57);CASE(58);CASE(59);
CASE(60);CASE(61);CASE(62);CASE(63);CASE(64);CASE(65);CASE(66);CASE(67);CASE(68);CASE(69);
CASE(70);CASE(71);CASE(72);CASE(73);CASE(74);CASE(75);CASE(76);CASE(77);CASE(78);CASE(79);
CASE(80);CASE(81);CASE(82);CASE(83);CASE(84);CASE(85);CASE(86);CASE(87);CASE(88);CASE(89);
CASE(90);CASE(91);CASE(92);CASE(93);CASE(94);CASE(95);CASE(96);CASE(97);CASE(98);CASE(99);
CASE(100);CASE(101);CASE(102);CASE(103);CASE(104);CASE(105);CASE(106);CASE(107);CASE(108);CASE(109);
CASE(110);CASE(111);CASE(112);CASE(113);CASE(114);CASE(115);CASE(116);CASE(117);CASE(118);CASE(119);
CASE(120);CASE(121);CASE(122);CASE(123);CASE(124);CASE(125);CASE(126);CASE(127);CASE(128);CASE(129);
CASE(130);CASE(131);CASE(132);CASE(133);CASE(134);CASE(135);CASE(136);CASE(137);CASE(138);CASE(139);
CASE(140);CASE(141);CASE(142);CASE(143);CASE(144);CASE(145);CASE(146);CASE(147);CASE(148);CASE(149);
CASE(150);CASE(151);CASE(152);CASE(153);CASE(154);CASE(155);CASE(156);CASE(157);CASE(158);CASE(159);
CASE(160);CASE(161);CASE(162);CASE(163);CASE(164);CASE(165);CASE(166);CASE(167);CASE(168);CASE(169);
CASE(170);CASE(171);CASE(172);CASE(173);CASE(174);CASE(175);CASE(176);CASE(177);CASE(178);CASE(179);
CASE(180);CASE(181);CASE(182);CASE(183);CASE(184);CASE(185);CASE(186);CASE(187);CASE(188);CASE(189);
CASE(190);CASE(191);CASE(192);CASE(193);CASE(194);CASE(195);CASE(196);CASE(197);CASE(198);CASE(199);
CASE(200);CASE(201);CASE(202);CASE(203);CASE(204);CASE(205);CASE(206);CASE(207);CASE(208);CASE(209);
CASE(210);CASE(211);CASE(212);CASE(213);CASE(214);CASE(215);CASE(216);CASE(217);CASE(218);CASE(219);
CASE(220);CASE(221);CASE(222);CASE(223);CASE(224);CASE(225);CASE(226);CASE(227);CASE(228);CASE(229);
CASE(230);CASE(231);CASE(232);CASE(233);CASE(234);CASE(235);CASE(236);CASE(237);CASE(238);CASE(239);
CASE(240);CASE(241);CASE(242);CASE(243);CASE(244);CASE(245);CASE(246);CASE(247);CASE(248);CASE(249);
CASE(250);CASE(251);CASE(252);CASE(253);CASE(254);CASE(255);
#undef CASE
default: return _mm_set1_ps(0); // make all compilers happy
}
#else // No intrinsic available; do the shuffling "by hand"
float* retp = reinterpret_cast<float*>(result);
const float* valp = reinterpret_cast<const float*>(&val);
for (int i = 0; i < 4; ++i) {
*retp++ = valp[how & 3];
how >>= 2;
}
#endif
}
}
#endif