/* -*- 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/. */ // // Non-Inline implementations of functions defined in instr.h // #include "avmplus.h" #ifdef PERFM #define DOPROF #endif /* PERFM */ #include "../vprof/vprof.h" namespace avmplus { template Atom callprop_b(E env, Atom base, const Multiname* multiname, int argc, Atom* atomv, VTable* vtable, Binding b) { AssertNotNull(base); switch (AvmCore::bindingKind(b)) { case BKIND_METHOD: return call_method_binding(base, vtable, b, argc, atomv); case BKIND_VAR: case BKIND_CONST: return call_slot_binding(env, base, b, argc, atomv); case BKIND_GET: case BKIND_GETSET: return op_call(env, call_get_binding(base, vtable, b), argc, atomv); case BKIND_SET: // read on write-only property env->toplevel()->throwReferenceError(kWriteOnlyError, multiname, vtable->traits); return unreachableAtom; default: if (isObjectPtr(base)) { return call_obj_dynamic(base, multiname, argc, atomv); } else { return call_prim_dynamic(env, base, multiname, argc, atomv); } } } template Atom callprop_b(Toplevel*, Atom, const Multiname*, int, Atom*, VTable*, Binding); /** * find the binding for a property given a full multiname reference. The lookup * must produce a single binding, or it's an error. Note that the name could be * bound to the same binding in multiple namespaces. */ template Binding getBinding(E env, Traits* traits, const Multiname* ref) { Binding b = BIND_NONE; if (traits && ref->isBinding()) { // note, you no longer must resolve the traits in order to find the binding! TraitsBindingsp tb = traits->getTraitsBindings(); if (!ref->isNsset()) { b = tb->findBinding(ref->getName(), ref->getNamespace()); } else { b = tb->findBinding(ref->getName(), ref->getNsset()); if (b == BIND_AMBIGUOUS) { // ERROR. more than one binding is available. throw exception. env->toplevel()->throwTypeError(kAmbiguousBindingError, env->core()->toErrorString(ref)); } } } return b; } template Binding getBinding(const Toplevel*, Traits*, const Multiname*); template Binding getBinding(Toplevel*, Traits*, const Multiname*); template VTable* toVTable(E env, Atom atom) { if (!AvmCore::isNullOrUndefined(atom)) { if (atomKind(atom) == kObjectType) return AvmCore::atomToScriptObject(atom)->vtable; Toplevel* toplevel = env->toplevel(); switch (atomKind(atom)) { case kNamespaceType: return toplevel->namespaceClass()->ivtable(); case kStringType: return toplevel->stringClass()->ivtable(); case kBooleanType: return toplevel->booleanClass()->ivtable(); case kIntptrType: case kDoubleType: // ISSUE what about int? return toplevel->numberClass()->ivtable(); #ifdef VMCFG_FLOAT case kSpecialBibopType: if( bibopKind(atom) == kBibopFloatType ) return toplevel->floatClass()->ivtable(); if( bibopKind(atom) == kBibopFloat4Type ) return toplevel->float4Class()->ivtable(); AvmAssertMsg(false,"Unhandled bibop kind"); #endif // VMCFG_FLOAT } } // TypeError in ECMA env->toplevel()->throwTypeError( (atom == undefinedAtom) ? kConvertUndefinedToObjectError : kConvertNullToObjectError); // unreachable return NULL; } template VTable* toVTable(Toplevel*, Atom); template VTable* toVTable(MethodEnv*, Atom); template Atom op_applytype(E env, Atom factory, int argc, Atom* args) { if (AvmCore::isObject(factory)) return AvmCore::atomToScriptObject(factory)->applyTypeArgs(argc, args); env->toplevel()->throwTypeError(kTypeAppOfNonParamType); return unreachableAtom; } template Atom op_applytype(Toplevel*, Atom, int, Atom*); template Atom op_applytype(MethodEnv*, Atom, int, Atom*); template Atom astype_late(E caller_env, Atom value, Atom type) { Traits* itraits = caller_env->toplevel()->toClassITraits(type); return AvmCore::astype(value, itraits); } template Atom astype_late(MethodEnv*, Atom, Atom); template Atom instanceof(E caller_env, Atom val, Atom ctor) { return caller_env->toplevel()->instanceof(val, ctor); } template Atom instanceof(MethodEnv*, Atom, Atom); template Atom op_in(E caller_env, Atom name, Atom obj) { return caller_env->toplevel()->in_operator(name, obj); } template Atom op_in(MethodEnv*, Atom, Atom); /** * finddef_miss handles the uncommon case when finddef_cache finds a null * pointer. Its a separate function to avoid polluting finddef_cache's * stack frame with spilled registers from the slow path. */ NO_INLINE ScriptObject* FASTCALL finddef_miss(ScriptObject** obj_ptr, MethodFrame* frame) { MethodEnv* env = frame->env(); FinddefTable& table = *env->abcEnv()->finddefTable(); // recover name_id by figuring out where obj_ptr points in the table. // name_id is not passed in so we can comply with FASTCALL's // limit of two register args. ScriptObject** obj0_ptr = (ScriptObject**) &table[0]; int name_id = int(obj_ptr - obj0_ptr); // execute finddef and save the result const Multiname* name = env->method->pool()->precomputedMultiname(name_id); ScriptObject* obj = env->finddef(name); WBRC(env->core()->gc, &table, &table[name_id].object, obj); return obj; } /** * called by JIT or Interpreter code for OP_finddef. The JIT code has a stashed * pointer to the whole FinddefTable, and passes in a pointer to the desired * element. (we do this to keep this call to 2 parameters). We use * MethodFrame* here instead of MethodEnv* because its cheaper for the * JIT to rematerialize MethodFrame* than to load MethodEnv*. from stack. */ ScriptObject* FASTCALL finddef_cache(ScriptObject** obj_ptr, MethodFrame* frame) { ScriptObject* obj = *obj_ptr; if (obj) return obj; return finddef_miss(obj_ptr, frame); } Atom constructprop(Toplevel* toplevel, const Multiname* multiname, int argc, Atom* atomv, VTable* vtable) { Binding b = getBinding(toplevel, vtable->traits, multiname); Atom obj = atomv[0]; switch (AvmCore::bindingKind(b)) { case BKIND_METHOD: { // can't invoke method as constructor MethodEnv* env = vtable->methods[AvmCore::bindingToMethodId(b)]; toplevel->throwTypeError(kCannotCallMethodAsConstructor, toplevel->core()->toErrorString(env->method)); } case BKIND_VAR: case BKIND_CONST: { ScriptObject* ctor = AvmCore::atomToScriptObject(obj)->getSlotObject(AvmCore::bindingToSlotId(b)); // This code is a bit subtle. Note that ScriptObject::getSlotObject() will return NULL if the // slot is an SST_scriptobject and is NULL, or an SST_atom that is null, undefined, or not // an object. These subsume the test isObject(ctor) in op_construct(), which is inlined here. // We once included an extra check, resulting in throwing a different exception when taking this // path in interpreted code. We must emulate this old behavior for backward compatibility. // It is unfortunate that CLASS_TYPE and FUNCTION_TYPE expand into references to 'core', otherwise // we could push the next line down into the exceptional path. AvmCore* core = toplevel->core(); if (!ctor || (!ctor->traits()->subtypeof(CLASS_TYPE) && !ctor->traits()->subtypeof(FUNCTION_TYPE))) { // We know that an exception will be thrown eventually, so now do the version check. if (core->currentBugCompatibility()->bugzilla456852a) { // Correct behavior is to throw kConstructOfNonFunctionError as op_construct would do. if (!ctor) { toplevel->throwTypeError(kConstructOfNonFunctionError); } } else { // Stay compatible with old behavior. toplevel->throwTypeError(kNotConstructorError, core->toErrorString(multiname)); } } // inlined equivalent of op_construct return ctor->construct(argc, atomv); } case BKIND_GET: case BKIND_GETSET: { // Invoke the getter int m = AvmCore::bindingToGetterId(b); MethodEnv *f = vtable->methods[m]; Atom ctor = f->coerceEnter(obj); return op_construct(toplevel, ctor, argc, atomv); } case BKIND_SET: { // read on write-only property toplevel->throwReferenceError(kWriteOnlyError, multiname, vtable->traits); } default: { ScriptObject* o; if (atomKind(obj)==kObjectType) { o = AvmCore::atomToScriptObject(obj); } else { #ifdef VMCFG_FLOAT if (AvmCore::isFloat4(obj)) { // See FIXME in Toplevel::getproperty for why this is "correct". if (multiname->isValidDynamicName()) { uint32_t index; if (AvmCore::getIndexFromAtom(multiname->getName()->atom(), &index)) toplevel->throwTypeError(kConstructOfNonFunctionError); } } #endif // primitive types are not dynamic, so we can go directly // to their __proto__ object o = toplevel->toPrototype(obj); } atomv[0] = o->atom(); // replace receiver Atom ctor = o->getMultinameProperty(multiname); return op_construct(toplevel, ctor, argc, atomv); } } } /** * implements ECMA implicit coersion. returns the coerced value, * or throws a TypeError if coersion is not possible. */ // NOTE: parts of this function have been explicitly inlined into BaseExecMgr::unbox1 for // efficiency. If you change/fix this method, you may need to change/fix MethodEnv::unbox1 as well. Atom coerceImpl(const Toplevel* toplevel, Atom atom, Traits* expected) { AvmAssert(expected != NULL); AvmAssert(!AvmCore::atomDoesNotNeedCoerce(atom, BuiltinType(expected->builtinType))); Traits* actual; AvmCore* core = toplevel->core(); // these types always succeed switch (expected->builtinType) { case BUILTIN_boolean: return AvmCore::booleanAtom(atom); case BUILTIN_number: return core->numberAtom(atom); #ifdef VMCFG_FLOAT case BUILTIN_float: return AvmCore::isFloat(atom)?atom:core->floatAtom(atom); case BUILTIN_float4: return AvmCore::isFloat4(atom)?atom:core->float4Atom(atom); #endif case BUILTIN_string: return AvmCore::isNullOrUndefined(atom) ? nullStringAtom : core->string(atom)->atom(); case BUILTIN_int: return core->intAtom(atom); case BUILTIN_uint: return core->uintAtom(atom); case BUILTIN_object: return atom == undefinedAtom ? nullObjectAtom : atom; } // else fall thru if (AvmCore::isNullOrUndefined(atom)) return (expected->builtinType == BUILTIN_void) ? undefinedAtom : nullObjectAtom; switch (atomKind(atom)) { case kStringType: actual = core->traits.string_itraits; break; case kBooleanType: actual = core->traits.boolean_itraits; break; case kDoubleType: actual = core->traits.number_itraits; break; case kIntptrType: actual = core->traits.int_itraits; break; case kNamespaceType: actual = core->traits.namespace_itraits; break; case kObjectType: actual = AvmCore::atomToScriptObject(atom)->traits(); break; #ifdef VMCFG_FLOAT case kSpecialBibopType: AvmAssert(atom != AtomConstants::undefinedAtom); if(bibopKind(atom) == kBibopFloatType) { actual = core->traits.float_itraits; break; } if(bibopKind(atom) == kBibopFloat4Type) { actual = core->traits.float4_itraits; break; } // fall trhu to default #endif default: // unexpected atom type AvmAssert(false); return unreachableAtom; } if (!actual->subtypeof(expected)) { // failed #ifdef AVMPLUS_VERBOSE //core->console << "checktype failed " << expected << " <- " << atom << "\n"; #endif toplevel->throwTypeError(kCheckTypeFailedError, core->atomToErrorString(atom), core->toErrorString(expected)); } return atom; } #define AssertObjTraits(env,t) \ AvmAssert(t && !t->isMachineType());\ AvmAssert(t != env->core()->traits.string_itraits);\ AvmAssert(t != env->core()->traits.namespace_itraits); static void throwCheckTypeError(MethodEnv* env, Atom atom, Traits* t) { AvmCore *core = env->core(); env->toplevel()->throwTypeError(kCheckTypeFailedError, core->atomToErrorString(atom), core->toErrorString(t)); } void coerceobj_obj(MethodEnv* caller_env, ScriptObject* obj, Traits* t) { AssertObjTraits(caller_env, t); if (obj && !obj->traits()->subtypeof(t)) throwCheckTypeError(caller_env, obj->atom(), t); } void coerceobj_atom(MethodEnv *env, Atom atom, Traits* t) { AssertObjTraits(env, t); if (!AvmCore::isNullOrUndefined(atom) && (atomKind(atom) != kObjectType || !atomObj(atom)->traits()->subtypeof(t))) throwCheckTypeError(env, atom, t); } void coercens_atom(MethodEnv *env, Atom atom) { if (!AvmCore::isNullOrUndefined(atom) && atomKind(atom) != kNamespaceType) throwCheckTypeError(env, atom, env->core()->traits.namespace_itraits); } #ifdef VMCFG_FLOAT #define INTPTRASDOUBLE(v) ( (double) atomGetIntptr(v) ) template T __multiply(T a, T b){ return a * b; } template T __divide(T a, T b){ return a / b; } template T __subtract(T a, T b){ return a - b; } static double __modulo(double a, double b){ return MathUtils::mod(a,b); } static float __modulo(float a, float b){ return (float) MathUtils::mod(a,b); } static float4_t __modulo(const float4_t& a, const float4_t& b){ float x= __modulo(f4_x(a), f4_x(b)); float y= __modulo(f4_y(a), f4_y(b)); float z= __modulo(f4_z(a), f4_z(b)); float w= __modulo(f4_w(a), f4_w(b)); float4_t res = {x, y, z, w}; return res; } template<> float4_t __multiply(float4_t a, float4_t b) { return f4_mul(a,b);} template<> float4_t __divide (float4_t a, float4_t b) { return f4_div(a,b);} template<> float4_t __subtract(float4_t a, float4_t b) { return f4_sub(a,b);} #define op_generic(name) \ Atom op_##name(AvmCore* core, Atom lhs, Atom rhs)\ {\ tagprof("op_##name val1", lhs);\ tagprof("op_##name val2", rhs);\ \ if (atomIsBothIntptr(lhs,rhs))\ {\ double res = __##name( INTPTRASDOUBLE(lhs), INTPTRASDOUBLE(rhs));\ intptr_t res_int = intptr_t(res);\ if( atomIsValidIntptrValue(res_int) && res==(double)res_int)\ return (res_int << 3) | kIntptrType;\ }\ \ lhs = AvmCore::primitive(lhs); \ rhs = AvmCore::primitive(rhs); \ \ if(AvmCore::isFloat4(lhs) || AvmCore::isFloat4(rhs)){\ float4_decl_v(lhs);\ float4_decl_v(rhs);\ return core->float4ToAtom( __##name(lhsv, rhsv) );\ }\ if(AvmCore::isFloat(lhs) && AvmCore::isFloat(rhs))\ return core->floatToAtom( __##name(AvmCore::atomToFloat(lhs),AvmCore::atomToFloat(rhs)));\ else\ return core->doubleToAtom(__##name( AvmCore::number(lhs), AvmCore::number(rhs)) );\ } op_generic(divide); op_generic(subtract); op_generic(multiply); op_generic(modulo); Atom op_negate(AvmCore* core, Atom val) { tagprof("op_negate val1", val); val = AvmCore::primitive(val); // slow, but let's get over this... if(AvmCore::isFloat(val)){ return core->floatToAtom(-AvmCore::atomToFloat(val)); } if(AvmCore::isFloat4(val)){ const static float4_t Zero = {-0.0, -0.0, -0.0, -0.0}; float4_decl_v(val); return core->float4ToAtom( f4_sub(Zero, valv) ); } if(atomIsIntptr(val) && val != zeroIntAtom){ double res = - INTPTRASDOUBLE(val); intptr_t res_int = intptr_t(res); // note: we can't negate "0" as integer, but we should've guarded against that case with the zeroIntAtom test AvmAssert(res_int != 0); if( atomIsValidIntptrValue(res_int) && res == (double)res_int) return (res_int << 3) | kIntptrType; } return core->doubleToAtom( - AvmCore::number(val) ); } #endif #ifdef VMCFG_FLOAT template Atom op_add_impl(AvmCore* core, Atom lhs, Atom rhs) #else Atom op_add(AvmCore* core, Atom lhs, Atom rhs) #endif // VMCFG_FLOAT { tagprof("op_add val1", lhs); tagprof("op_add val2", rhs); #ifdef AVMPLUS_64BIT // since 64-bit int atoms expect exactly 54 bits of precision, we want to shift bit 54+3 up into the sign bit and back down # define SIGN_EXTEND(v) ((intptr_t(v) << (atomSignExtendShift-AtomConstants::kAtomTypeSize)) >> (atomSignExtendShift-AtomConstants::kAtomTypeSize)) #else # define SIGN_EXTEND(v) (intptr_t(v)) #endif // integer optimization based on the one from Interpreter.cpp, modified // to reduce the # of alu instructions if (atomIsBothIntptr(lhs,rhs)) { // Subtract off one of the type tags so result will be properly tagged. // Sign-extend 53-bit intptr value on 64-bit platforms. intptr_t const sum = SIGN_EXTEND(lhs + rhs - kIntptrType); // No overflow occurs if the signs of the arguments are different, // or if the sign of the result matches that of the arguments. // Rather than isolating the sign bits by masking off the others, we exploit // the fact that the comparisons with zero implicitly ignore the other bits. if ((lhs ^ rhs) < 0 || (lhs ^ sum) >= 0) { // No overflow, sum is correctly tagged atom return sum; } // Both integers, but overflow happens. Intentionally add these // without casting to int32_t. If the sum of the shifted values overflow, // we know the unshifted values will not overflow with a word-sized add. return core->allocDouble(double(atomGetIntptr(lhs) + atomGetIntptr(rhs))); } if (AvmCore::isNumeric(lhs) && AvmCore::isNumeric(rhs)) { // C++ porting note. if either side is undefined NaN then result must be NaN, // which is assumed to be taken care of by IEEE 748 double add. goto add_numbers; } if (AvmCore::isString(lhs) || AvmCore::isString(rhs) || AvmCore::isDate(lhs) || AvmCore::isDate(rhs)) { goto concat_strings; } // then look for the more unlikely cases // E4X, section 11.4.1, pg 53 if (AvmCore::isXMLorXMLList(lhs) && AvmCore::isXMLorXMLList(rhs)) { XMLListObject *l = XMLListObject::create(core->GetGC(), atomObj(lhs)->toplevel()->xmlListClass()); l->_append(lhs); l->_append(rhs); return l->atom(); } // to catch oddball cases like: // function foo() { }; // foo.prototype.valueOf = function() { return new Object(); } // foo.prototype.toString = function() { return 2; } // print( new foo() + 33 ); // should be 35 // // we need to follow the E3 spec: // 1. call ToPrimitive() on lhs and rhs, then // if L is String || R is String, concat, else add toNumber(lhs) to toNumber(rhs) // ToPrimitive() will call [[DefaultValue]], which calls valueOf(). If the result is // a primitive, return that value else call toString() instead. // from E3: // NOTE No hint is provided in the calls to ToPrimitive in steps 5 and 6. All native ECMAScript objects except Date objects handle // the absence of a hint as if the hint Number were given; Date objects handle the absence of a hint as if the hint String were given. // Host objects may handle the absence of a hint in some other manner. lhs = AvmCore::primitive(lhs); // Date is handled above with the String argument case, we don't have to check for it here. rhs = AvmCore::primitive(rhs); if (!(AvmCore::isString(lhs) || AvmCore::isString(rhs))) { goto add_numbers; } // else fall thru to concat_strings concat_strings: return core->concatStrings(core->string(lhs), core->string(rhs))->atom(); add_numbers: #ifdef VMCFG_FLOAT // if both are floats, the addition is done on float; if any is float4, addition is done on float4 if(float_enabled) { if(AvmCore::isFloat4(lhs) || AvmCore::isFloat4(rhs)) { float4_decl_v(lhs); float4_decl_v(rhs); return core->float4ToAtom( f4_add(lhsv , rhsv)); } if(AvmCore::isFloat(lhs) && AvmCore::isFloat(rhs)) return core->floatToAtom(AvmCore::singlePrecisionFloat(lhs) + AvmCore::singlePrecisionFloat(rhs)); } #endif return core->doubleToAtom(AvmCore::number(lhs) + AvmCore::number(rhs)); } #ifdef VMCFG_FLOAT Atom op_add(AvmCore* core, Atom lhs, Atom rhs) { return op_add_impl(core, lhs, rhs); } Atom op_add_nofloat(AvmCore* core, Atom lhs, Atom rhs) { return op_add_impl( core, lhs, rhs); } #endif // VMCFG_FLOAT #ifdef VMCFG_FASTPATH_ADD // Auxilliary predicate for assertions. // Return true if result of adding lhs and rhs will require // a kDoubleType atom instead of a kIntptrType atom. #if defined(VMCFG_FASTPATH_ADD_INLINE) && defined(DEBUG) static bool addIntptrOverflow(intptr_t lhs, intptr_t rhs) { intptr_t const sum = lhs + rhs; if ((lhs ^ rhs) < 0 || (lhs ^ sum) >= 0) { // add overflowed, numerical result will not fit in intptr_t return true; } // verify that tagged result will not fit in kIntptrType intptr_t const extended = SIGN_EXTEND(sum << kAtomTypeSize) >> kAtomTypeSize; return (sum == extended); } #endif // Specialized helper functions called by code generated for the OP_add instruction. // These take into account operands with known specific types and invariants previously // established by inline code. // atom + atom => atom #ifdef VMCFG_FLOAT template Atom op_add_a_aa_impl(AvmCore* core, Atom lhs, Atom rhs) #else Atom op_add_a_aa(AvmCore* core, Atom lhs, Atom rhs) #endif // VMCFG_FLOAT { #ifndef VMCFG_FASTPATH_ADD_INLINE // Fastpath for intptr+intptr if (atomIsBothIntptr(lhs, rhs)) { intptr_t const sum = SIGN_EXTEND(lhs + rhs - kIntptrType); if ((lhs ^ rhs) < 0 || (lhs ^ sum) >= 0) { // No overflow, sum is correctly tagged atom return sum; } // Overflow, allocate heap double return core->allocDouble(double(atomGetIntptr(lhs) + atomGetIntptr(rhs))); } #else // Inline code should handle intptr+intptr case, except where result must be double. AvmAssert(!atomIsBothIntptr(lhs, rhs) || addIntptrOverflow(atomGetIntptr(lhs), atomGetIntptr(rhs))); #endif if (atomIsIntptr(lhs)) { if (AvmCore::isDouble(rhs)) { return core->doubleToAtom(double(atomGetIntptr(lhs)) + AvmCore::atomToDouble(rhs)); } else if (atomIsIntptr(rhs)) { // We should arrive here only on overflow in inline fastpath. return core->doubleToAtom(double(atomGetIntptr(lhs) + double(atomGetIntptr(rhs)))); } } else if (AvmCore::isDouble(lhs)) { if (atomIsIntptr(rhs)) { return core->doubleToAtom(AvmCore::atomToDouble(lhs) + double(atomGetIntptr(rhs))); } else if (AvmCore::isDouble(rhs)) { return core->doubleToAtom(AvmCore::atomToDouble(lhs) + AvmCore::atomToDouble(rhs)); } } if (AvmCore::isString(lhs)) { if (AvmCore::isString(rhs)) { // AvmCore::concatStrings() coerces a null argument to the string "null". // Here, we know that neither lhs nor rhs are null, so we can concatenate w/o the coercion. return String::concatStrings(AvmCore::atomToString(lhs), AvmCore::atomToString(rhs))->atom(); } else { goto concat_strings; } } else if (AvmCore::isDate(lhs) || AvmCore::isDate(rhs)) { goto concat_strings; } if (AvmCore::isXMLorXMLList(lhs) && AvmCore::isXMLorXMLList(rhs)) { XMLListObject *l = XMLListObject::create(core->GetGC(), atomObj(lhs)->toplevel()->xmlListClass()); l->_append(lhs); l->_append(rhs); return l->atom(); } lhs = AvmCore::primitive(lhs); rhs = AvmCore::primitive(rhs); if (!(AvmCore::isString(lhs) || AvmCore::isString(rhs))) { #ifdef VMCFG_FLOAT if(float_support){ if(AvmCore::isFloat4(lhs) || AvmCore::isFloat4(rhs)) { float4_decl_v(lhs); float4_decl_v(rhs); return core->float4ToAtom( f4_add(lhsv, rhsv) ); } if(AvmCore::isFloat(lhs) && AvmCore::isFloat(rhs)) return core->floatToAtom(AvmCore::atomToFloat(lhs) + AvmCore::atomToFloat(rhs)); } #endif return core->doubleToAtom(AvmCore::number(lhs) + AvmCore::number(rhs)); } concat_strings: return core->concatStrings(core->string(lhs), core->string(rhs))->atom(); } // atom + int => atom #ifdef VMCFG_FLOAT template Atom op_add_a_ai_impl(AvmCore* core, Atom lhs, int32_t rhs) #else Atom op_add_a_ai(AvmCore* core, Atom lhs, int32_t rhs) #endif // VMCFG_FLOAT { if (atomIsIntptr(lhs)) { #ifndef VMCFG_FASTPATH_ADD_INLINE // Fastpath for intptr+int with no overflow #ifdef AVMPLUS_64BIT // A 32-bit integer is guaranteed convertible to an atom of type kIntptrType. intptr_t lhsv = lhs; intptr_t rhsv = intptr_t(rhs) << AtomConstants::kAtomTypeSize; intptr_t const sum = SIGN_EXTEND(lhsv + rhsv); if ((lhsv ^ rhsv) < 0 || (lhsv ^ sum) >= 0) { // No overflow, sum is correctly tagged atom return sum; } #else intptr_t const lhsv = atomGetIntptr(lhs); intptr_t const rhsv = rhs; intptr_t const sum = lhsv + rhsv; if ((lhsv ^ rhsv) < 0 || (lhsv ^ sum) >= 0) { // No overflow if (atomIsValidIntptrValue(sum)) return atomFromIntptrValue(sum); else return core->allocDouble(double(sum)); } #endif #else // Inline code should handle intptr+int case, except where result must be double. // It is allowable for the inline code to punt if the 32-bit int argument is itself too large for an intptr. AvmAssert(!atomIsValidIntptrValue(rhs) || addIntptrOverflow(atomGetIntptr(lhs), rhs)); #endif return core->doubleToAtom(double(atomGetIntptr(lhs)) + double(rhs)); } else if (AvmCore::isDouble(lhs)) { return core->doubleToAtom(AvmCore::atomToDouble(lhs) + double(rhs)); } if (AvmCore::isString(lhs) || AvmCore::isDate(lhs)) goto concat_strings; lhs = AvmCore::primitive(lhs); if (!AvmCore::isString(lhs)) { #ifdef VMCFG_FLOAT if(float_support && AvmCore::isFloat4(lhs)) { float4_t op2 = {(float)rhs, (float)rhs, (float)rhs, (float)rhs}; return core->float4ToAtom( f4_add( AvmCore::atomToFloat4(lhs), op2) ); } #endif // VMCFG_FLOAT return core->doubleToAtom(AvmCore::number(lhs) + double(rhs)); } concat_strings: // See AvmCore::string() Stringp s = MathUtils::convertIntegerToStringBase10(core, rhs, MathUtils::kTreatAsSigned); return core->concatStrings(core->string(lhs), s)->atom(); } // int + atom => atom #ifdef VMCFG_FLOAT template Atom op_add_a_ia_impl(AvmCore* core, int32_t lhs, Atom rhs) #else Atom op_add_a_ia(AvmCore* core, int32_t lhs, Atom rhs) #endif // VMCFG_FLOAT { if (atomIsIntptr(rhs)) { #ifndef VMCFG_FASTPATH_ADD_INLINE // Fastpath for int+intptr with no overflow #ifdef AVMPLUS_64BIT // A 32-bit integer is guaranteed convertible to an atom of type kIntptrType. intptr_t lhsv = intptr_t(lhs) << AtomConstants::kAtomTypeSize; intptr_t rhsv = rhs; intptr_t const sum = SIGN_EXTEND(lhsv + rhsv); if ((lhsv ^ rhsv) < 0 || (lhsv ^ sum) >= 0) { // No overflow, sum is correctly tagged atom return sum; } #else intptr_t const lhsv = lhs; intptr_t const rhsv = atomGetIntptr(rhs); intptr_t const sum = lhsv + rhsv; if ((lhsv ^ rhsv) < 0 || (lhsv ^ sum) >= 0) { // No overflow if (atomIsValidIntptrValue(sum)) return atomFromIntptrValue(sum); else return core->allocDouble(double(sum)); } #endif #else // Inline code should handle int+intptr case, except where result must be double. // It is allowable for the inline code to punt if the 32-bit int argument is itself too large for an intptr. AvmAssert(!atomIsValidIntptrValue(lhs) || addIntptrOverflow(lhs, atomGetIntptr(rhs))); #endif return core->doubleToAtom(double(lhs) + double(atomGetIntptr(rhs))); } else if (AvmCore::isDouble(rhs)) { return core->doubleToAtom(double(lhs) + AvmCore::atomToDouble(rhs)); } if (AvmCore::isString(rhs) || AvmCore::isDate(rhs)) goto concat_strings; rhs = AvmCore::primitive(rhs); if (!AvmCore::isString(rhs)) { #ifdef VMCFG_FLOAT if( float_support && AvmCore::isFloat4(rhs)) { float4_t op1 = {(float)lhs, (float)lhs, (float)lhs, (float)lhs}; return core->float4ToAtom( f4_add( op1, AvmCore::atomToFloat4(rhs)) ); } #endif return core->doubleToAtom(double(lhs) + AvmCore::number(rhs)); } concat_strings: // See AvmCore::string() Stringp s = MathUtils::convertIntegerToStringBase10(core, lhs, MathUtils::kTreatAsSigned); return core->concatStrings(s, core->string(rhs))->atom(); } // atom + double => atom #ifdef VMCFG_FLOAT template Atom op_add_a_ad_impl(AvmCore* core, Atom lhs, double rhs) #else Atom op_add_a_ad(AvmCore* core, Atom lhs, double rhs) #endif // VMCFG_FLOAT { if (AvmCore::isDouble(lhs)) { return core->doubleToAtom(AvmCore::atomToDouble(lhs) + rhs); } else if (atomIsIntptr(lhs)) { return core->doubleToAtom(double(atomGetIntptr(lhs)) + rhs); } if (AvmCore::isString(lhs) || AvmCore::isDate(lhs)) goto concat_strings; lhs = AvmCore::primitive(lhs); if (!AvmCore::isString(lhs)) { #ifdef VMCFG_FLOAT if(float_support && AvmCore::isFloat4(lhs)) { float4_t op2 = {(float)rhs, (float)rhs, (float)rhs, (float)rhs}; return core->float4ToAtom( f4_add( AvmCore::atomToFloat4(lhs), op2) ); } #endif // VMCFG_FLOAT return core->doubleToAtom(AvmCore::number(lhs) + rhs); } concat_strings: // See AvmCore::string() Stringp s = core->doubleToString(rhs); return core->concatStrings(core->string(lhs), s)->atom(); } // double + atom => atom #ifdef VMCFG_FLOAT template Atom op_add_a_da_impl(AvmCore* core, double lhs, Atom rhs) #else Atom op_add_a_da(AvmCore* core, double lhs, Atom rhs) #endif { if (AvmCore::isDouble(rhs)) { return core->doubleToAtom(lhs + AvmCore::atomToDouble(rhs)); } else if (atomIsIntptr(rhs)) { return core->doubleToAtom(lhs + double(atomGetIntptr(rhs))); } if (AvmCore::isString(rhs) || AvmCore::isDate(rhs)) goto concat_strings; rhs = AvmCore::primitive(rhs); if (!AvmCore::isString(rhs)) { #ifdef VMCFG_FLOAT if(float_support && AvmCore::isFloat4(rhs)) { float4_t op1 = {(float)lhs, (float)lhs, (float)lhs, (float)lhs}; return core->float4ToAtom( f4_add( op1, AvmCore::atomToFloat4(rhs)) ); } #endif // VMCFG_FLOAT return core->doubleToAtom(lhs + AvmCore::number(rhs)); } concat_strings: // See AvmCore::string() Stringp s = core->doubleToString(lhs); return core->concatStrings(s, core->string(rhs))->atom(); } #ifdef VMCFG_FLOAT Atom op_add_a_aa(AvmCore* core, Atom lhs, Atom rhs){ return op_add_a_aa_impl(core, lhs, rhs); } Atom op_add_a_aa_nofloat(AvmCore* core, Atom lhs, Atom rhs){ return op_add_a_aa_impl(core, lhs, rhs); } Atom op_add_a_ai(AvmCore* core, Atom lhs, int32_t rhs){ return op_add_a_ai_impl(core, lhs, rhs); } Atom op_add_a_ai_nofloat(AvmCore* core, Atom lhs, int32_t rhs){ return op_add_a_ai_impl(core, lhs, rhs); } Atom op_add_a_ia(AvmCore* core, int32_t lhs, Atom rhs){ return op_add_a_ia_impl(core, lhs, rhs); } Atom op_add_a_ia_nofloat(AvmCore* core, int32_t lhs, Atom rhs){ return op_add_a_ia_impl(core, lhs, rhs); } Atom op_add_a_ad(AvmCore* core, Atom lhs, double rhs){ return op_add_a_ad_impl(core, lhs, rhs); } Atom op_add_a_ad_nofloat(AvmCore* core, Atom lhs, double rhs){ return op_add_a_ad_impl(core, lhs, rhs); } Atom op_add_a_da(AvmCore* core, double lhs, Atom rhs){ return op_add_a_da_impl(core, lhs, rhs); } Atom op_add_a_da_nofloat(AvmCore* core, double lhs, Atom rhs){ return op_add_a_da_impl(core, lhs, rhs); } #endif /* VMCFG_FLOAT */ #endif /* VMCFG_FASTPATH_ADD */ void FASTCALL mop_rangeCheckFailed(MethodEnv* env) { env->toplevel()->throwRangeError(kInvalidRangeError); } // note: all of the mop_xxx load/store functions assume // that the caller has done range checking, and the address // is safe for its intended use... int32_t FASTCALL mop_lix8(const void* addr) { // loads a signed byte, sign-extends return *(const int8_t*)(addr); } int32_t FASTCALL mop_liz8(const void* addr) { // loads an unsigned byte, zero-extends return *(const uint8_t*)(addr); } int32_t FASTCALL mop_lix16(const void* addr) { // loads an signed short, sign-extends #if defined(VMCFG_UNALIGNED_INT_ACCESS) && defined(VMCFG_LITTLE_ENDIAN) return *(const int16_t*)(addr); #else const uint8_t* u = (const uint8_t*)addr; return int16_t((uint16_t(u[1]) << 8) | uint16_t(u[0])); #endif } int32_t FASTCALL mop_liz16(const void* addr) { // loads an unsigned short, zero-extends #if defined(VMCFG_UNALIGNED_INT_ACCESS) && defined(VMCFG_LITTLE_ENDIAN) return *(const uint16_t*)(addr); #else const uint8_t* u = (const uint8_t*)addr; return (uint16_t(u[1]) << 8) | uint16_t(u[0]); #endif } int32_t FASTCALL mop_li32(const void* addr) { #if defined(VMCFG_UNALIGNED_INT_ACCESS) && defined(VMCFG_LITTLE_ENDIAN) return *(const int32_t*)(addr); #else const uint8_t* u = (const uint8_t*)addr; return (uint32_t(u[3]) << 24) | (uint32_t(u[2]) << 16) | (uint32_t(u[1]) << 8) | uint32_t(u[0]); #endif } double FASTCALL mop_lf32(const void* addr) { #if defined(VMCFG_UNALIGNED_FP_ACCESS) && defined(VMCFG_LITTLE_ENDIAN) return *(const float*)(addr); #else union { uint32_t a; float b; }; const uint8_t* u = (const uint8_t*)addr; // Bugzilla 569691: Do not try to be clever here by loading from // '*(uint32_t*)u' into 'a', even if both VMCFG_UNALIGNED_INT_ACCESS and // VMCFG_LITTLE_ENDIAN are set - gcc may emit code that loads directly // from the ARM VFP register, and that requires VMCFG_UNALIGNED_FP_ACCESS. a = (uint32_t(u[3]) << 24) | (uint32_t(u[2]) << 16) | (uint32_t(u[1]) << 8) | uint32_t(u[0]); return b; #endif } double FASTCALL mop_lf64(const void* addr) { #if defined(VMCFG_UNALIGNED_FP_ACCESS) && defined(VMCFG_LITTLE_ENDIAN) return *(const double*)(addr); #else // The layout in memory is little-endian with the least significant // word first. const uint8_t* u = (const uint8_t*)addr; double_overlay d; // Bugzilla 685441: Do not copy 32-bit blocks here for some ARM platforms // to avoid unaligned memory access, even though VMCFG_UNALIGNED_INT_ACCESS // is turned on. d.words.lsw = ((uint64_t(u[3]) << 24) | (uint64_t(u[2]) << 16) | (uint64_t(u[1]) << 8) | (uint64_t(u[0]))); d.words.msw = ((uint32_t(u[7]) << 24) | (uint32_t(u[6]) << 16) | (uint32_t(u[5]) << 8) | (uint32_t(u[4]))); return d.value; #endif } #ifdef VMCFG_FLOAT void FASTCALL mop_lf32x4(float4_t* result, const void* addr) { #if defined(VMCFG_LITTLE_ENDIAN) // Note, this depends on one of three conditions being true: // - float4_t is a generic structure type that is not mapped directly // to the SIMD type, and ByteArray storage is 4-byte aligned // - SIMD loads from unaligned addresses is OK // - ByteArray storage is 16-byte aligned *result = *(const float4_t*)addr; #else // 2011-12-16: This has probably never been tested: there have been no // big-endian systems where VMCFG_FLOAT has been enabled. const uint8_t* u = (const uint8_t*)addr; float4_t result; float_overlay f; f.word = ((uint32_t(u[3]) << 24) | (uint32_t(u[2]) << 16) | (uint32_t([1]) << 8) | uint32_t(u[0])); result->x = f.value; f.word = ((uint32_t(u[7]) << 24) | (uint32_t(u[6]) << 16) | (uint32_t([5]) << 8) | uint32_t(u[4])); result->y = f.value; f.word = ((uint32_t(u[11]) << 24) | (uint32_t(u[10]) << 16) | (uint32_t([9]) << 8) | uint32_t(u[8])); result->z = f.value; f.word = ((uint32_t(u[15]) << 24) | (uint32_t(u[14]) << 16) | (uint32_t([13]) << 8) | uint32_t(u[12])); result->w = f.value; #endif } #endif void FASTCALL mop_si8(void* addr, int32_t value) { *(uint8_t*)(addr) = uint8_t(value); } void FASTCALL mop_si16(void* addr, int32_t value) { #if defined(VMCFG_UNALIGNED_INT_ACCESS) && defined(VMCFG_LITTLE_ENDIAN) *(uint16_t*)(addr) = uint16_t(value); #else uint8_t* u = (uint8_t*)addr; u[0] = uint8_t(value); u[1] = uint8_t(value >> 8); #endif } void FASTCALL mop_si32(void* addr, int32_t value) { #if defined(VMCFG_UNALIGNED_INT_ACCESS) && defined(VMCFG_LITTLE_ENDIAN) *(int32_t*)(addr) = int32_t(value); #else uint8_t* u = (uint8_t*)addr; u[0] = uint8_t(value); u[1] = uint8_t(value >> 8); u[2] = uint8_t(value >> 16); u[3] = uint8_t(value >> 24); #endif } void mop_sf32(void* addr, double value) { #if defined(VMCFG_UNALIGNED_FP_ACCESS) && defined(VMCFG_LITTLE_ENDIAN) *(float*)(addr) = float(value); #else union { float a; uint32_t b; }; a = float(value); uint8_t* u = (uint8_t*)addr; // Bugzilla 569691: Do not try to be clever here by storing from 'b' into // '*(uint32_t*)u', even if both VMCFG_UNALIGNED_INT_ACCESS and // VMCFG_LITTLE_ENDIAN are set - gcc will emit code that stores directly // from the ARM VFP register, and that requires VMCFG_UNALIGNED_FP_ACCESS. u[0] = uint8_t(b); u[1] = uint8_t(b >> 8); u[2] = uint8_t(b >> 16); u[3] = uint8_t(b >> 24); #endif } void mop_sf64(void* addr, double value) { #if defined(VMCFG_UNALIGNED_FP_ACCESS) && defined(VMCFG_LITTLE_ENDIAN) *(double*)(addr) = value; #else double_overlay d(value); uint8_t* u = (uint8_t*)addr; // Bugzilla 685441: Do not copy 32-bit blocks here for some ARM platforms // to avoid unaligned memory access, even though VMCFG_UNALIGNED_INT_ACCESS // is turned on. u[0] = uint8_t(d.words.lsw); u[1] = uint8_t(d.words.lsw >> 8); u[2] = uint8_t(d.words.lsw >> 16); u[3] = uint8_t(d.words.lsw >> 24); u[4] = uint8_t(d.words.msw); u[5] = uint8_t(d.words.msw >> 8); u[6] = uint8_t(d.words.msw >> 16); u[7] = uint8_t(d.words.msw >> 24); #endif } #ifdef VMCFG_FLOAT void mop_sf32x4(void* addr, const float4_t& value) { #if defined(VMCFG_LITTLE_ENDIAN) // Note, this depends on one of three conditions being true: // - float4_t is a generic structure type that is not mapped directly // to the SIMD type, and ByteArray storage is 4-byte aligned // - SIMD stores to unaligned addresses is OK // - ByteArray storage is 16-byte aligned *(float4_t*)addr = value; #else // 2011-12-16: This has probably never been tested: there have been no // big-endian systems where VMCFG_FLOAT has been enabled. uint8_t* u = (uint8_t*)addr; float_overlay f; f.value = value.x; u[0] = uint8_t(f.word); u[1] = uint8_t(f.word >> 8); u[2] = uint8_t(f.word >> 16); u[3] = uint8_t(f.word >> 24); f.value = value.y; u[4] = uint8_t(f.word); u[5] = uint8_t(f.word >> 8); u[6] = uint8_t(f.word >> 16); u[7] = uint8_t(f.word >> 24); f.value = value.z; u[8] = uint8_t(f.word); u[9] = uint8_t(f.word >> 8); u[10] = uint8_t(f.word >> 16); u[11] = uint8_t(f.word >> 24); f.value = value.w; u[12] = uint8_t(f.word); u[13] = uint8_t(f.word >> 8); u[14] = uint8_t(f.word >> 16); u[15] = uint8_t(f.word >> 24); #endif } #endif template NO_INLINE Atom op_call_error(E env) { env->toplevel()->throwTypeErrorWithName(kCallOfNonFunctionError, "value"); return unreachableAtom; } template Atom op_call_error(const MethodEnv*); template Atom op_call_error(MethodEnv*); template Atom op_call_error(Toplevel*); } // namespace avmplus