BOOLEAN_TO_JSVALDOUBLE_TO_JSVALINT_FITS_IN_JSVALINT_TO_JSVALJS::Add*RootJS::AutoIdArrayJS::AutoSaveExceptionStateJS::AutoValueArrayJS::AutoVectorRooterJS::BooleanValueJS::CallJS::CallArgsJS::CloneFunctionObjectJS::CompileJS::CompileFunctionJS::CompileOffThreadJS::CompileOptionsJS::ConstructJS::CreateErrorJS::CurrentGlobalOrNullJS::DeflateStringToUTF8BufferJS::DoubleNaNValueJS::DoubleValueJS::EvaluateJS::FalseValueJS::Float32ValueJS::GetDeflatedUTF8StringLengthJS::GetFirstArgumentAsTypeHintJS::GetSelfHostedFunctionJS::HandleJS::HandleValueArrayJS::IdentifyStandardInstanceJS::Int32ValueJS::IsCallableJS::MutableHandleJS::NewFunctionFromSpecJS::NullHandleValueJS::NullValueJS::NumberValueJS::ObjectOrNullValueJS::ObjectValueJS::OrdinaryToPrimitiveJS::PersistentRootedJS::PropertySpecNameEqualsIdJS::PropertySpecNameIsSymbolJS::PropertySpecNameToPermanentIdJS::ProtoKeyToIdJS::Remove*RootJS::RootedJS::SetLargeAllocationFailureCallbackJS::SetOutOfMemoryCallbackJS::SourceBufferHolderJS::StringValueJS::SymbolValueJS::ToBooleanJS::ToInt32JS::ToInt64JS::ToNumberJS::ToPrimitiveJS::ToStringJS::ToUint16JS::ToUint32JS::ToUint64JS::TrueHandleValueJS::TrueValueJS::UndefinedHandleValueJS::UndefinedValueJS::ValueJSAutoByteStringJSAutoCompartmentJSBoolJSCheckAccessOpJSClassJSClass.callJSClass.flagsJSConstDoubleSpecJSConvertOpJSDeletePropertyOpJSEnumerateOpJSErrorFormatStringJSErrorReportJSExceptionStateJSExnTypeJSExtendedClassJSExtendedClass.outerObjectJSExtendedClass.wrappedObjectJSFUN_BOUND_METHODJSFUN_GLOBAL_PARENTJSFastNativeJSFinalizeOpJSFreeOpJSFunctionJSFunctionSpecJSGetObjectOpsJSHasInstanceOpJSID_EMPTYJSID_IS_EMPTYJSID_IS_GCTHINGJSID_IS_INTJSID_IS_STRINGJSID_IS_SYMBOLJSID_IS_VOIDJSID_IS_ZEROJSID_VOIDJSIdArrayJSIteratorOpJSMarkOpJSNativeJSNewEnumerateOpJSNewResolveOpJSObjectJSObjectOpJSObjectOps.defaultValueJSObjectOps.definePropertyJSObjectOps.destroyObjectMapJSObjectOps.dropPropertyJSObjectOps.enumerateJSObjectOps.getAttributesJSObjectOps.getPropertyJSObjectOps.getRequiredSlotJSObjectOps.lookupPropertyJSObjectOps.newObjectMapJSObjectOps.setProtoJSObjectPrincipalsFinderJSPRINCIPALS_HOLDJSPrincipalsJSPrincipalsTranscoderJSPropertyJSPropertyDescriptorJSPropertyOpJSPropertySpecJSProtoKeyJSReserveSlotsOpJSResolveOpJSRuntimeJSSecurityCallbacks.contentSecurityPolicyAllowsJSStringJSStringFinalizerJSTraceOpJSTypeJSVAL_IS_BOOLEANJSVAL_IS_DOUBLEJSVAL_IS_GCTHINGJSVAL_IS_INTJSVAL_IS_NULLJSVAL_IS_NUMBERJSVAL_IS_OBJECTJSVAL_IS_PRIMITIVEJSVAL_IS_STRINGJSVAL_IS_VOIDJSVAL_LOCKJSVAL_NULLJSVAL_ONEJSVAL_TO_BOOLEANJSVAL_TO_DOUBLEJSVAL_TO_GCTHINGJSVAL_TO_INTJSVAL_TO_OBJECTJSVAL_TO_STRINGJSVAL_TRUEJSVAL_UNLOCKJSVAL_VOIDJSVAL_ZEROJSVersionJSXDRObjectOpJS_ASSERT_STRING_IS_FLATJS_Add*RootJS_AddArgumentFormatterJS_AddExternalStringFinalizerJS_AddFinalizeCallbackJS_AliasElementJS_AliasPropertyJS_AlreadyHasOwnPropertyJS_BeginRequestJS_BindCallableJS_BufferIsCompilableUnitJS_CStringsAreUTF8JS_CallFunctionJS_CheckAccessJS_CheckForInterruptJS_ClearContextThreadJS_ClearDateCachesJS_ClearNewbornRootsJS_ClearNonGlobalObjectJS_ClearPendingExceptionJS_ClearRegExpStaticsJS_ClearScopeJS_CloneFunctionObjectJS_CompareStringsJS_CompileFileHandleForPrincipalsJS_CompileFileHandleForPrincipalsVersionJS_CompileFunctionJS_CompileFunctionForPrincipalsJS_CompileScriptJS_CompileScriptForPrincipalsJS_CompileUCFunctionForPrincipalsVersionJS_CompileUTF8FileJS_CompileUTF8FileHandleJS_ConcatStringsJS_ConstructObjectJS_ContextIteratorJS_ConvertArgumentsJS_ConvertArgumentsVAJS_ConvertValueJS_DecompileFunctionJS_DecompileFunctionBodyJS_DecompileScriptJS_DecompileScriptObjectJS_DeepFreezeObjectJS_DefaultValueJS_DefineConstDoublesJS_DefineElementJS_DefineFunctionJS_DefineFunctionsJS_DefineObjectJS_DefineOwnPropertyJS_DefinePropertiesJS_DefinePropertyJS_DefinePropertyWithTinyIdJS_DeleteElementJS_DeleteElement2JS_DeletePropertyJS_DeleteProperty2JS_DestroyContextJS_DestroyIdArrayJS_DestroyRuntimeJS_DestroyScriptJS_DoubleIsInt32JS_DoubleToInt32JS_DropExceptionStateJS_DumpHeapJS_DumpNamedRootsJS_EncodeCharactersJS_EncodeStringJS_EncodeStringToBufferJS_EnterCompartmentJS_EnterCrossCompartmentCallJS_EnterLocalRootScopeJS_EnumerateJS_EnumerateDiagnosticMemoryRegionsJS_EnumerateResolvedStandardClassesJS_EnumerateStandardClassesJS_ErrorFromExceptionJS_EvaluateScriptJS_EvaluateScriptForPrincipalsJS_ExecuteRegExpJS_ExecuteScriptJS_ExecuteScriptPartJS_ExecuteScriptVersionJS_FORGET_STRING_FLATNESSJS_FSJS_FileEscapedStringJS_FinishJS_FlattenStringJS_FlushCachesJS_ForgetLocalRootJS_ForwardGetPropertyToJS_FreezeObjectJS_GCJS_GET_CLASSJS_GetArrayLengthJS_GetArrayPrototypeJS_GetClassJS_GetClassObjectJS_GetClassPrototypeJS_GetCompartmentPrivateJS_GetConstructorJS_GetContextPrivateJS_GetContextThreadJS_GetDefaultFreeOpJS_GetElementJS_GetEmptyStringJS_GetEmptyStringValueJS_GetErrorPrototypeJS_GetExternalStringClosureJS_GetExternalStringFinalizerJS_GetFlatStringCharsJS_GetFunctionArityJS_GetFunctionCallbackJS_GetFunctionFlagsJS_GetFunctionIdJS_GetFunctionNameJS_GetFunctionObjectJS_GetFunctionPrototypeJS_GetFunctionScriptJS_GetGCParameterJS_GetGlobalForCompartmentOrNullJS_GetGlobalForObjectJS_GetGlobalForObject3JS_GetGlobalForScopeChainJS_GetGlobalObjectJS_GetImplementationVersionJS_GetInstancePrivateJS_GetInternedStringCharsJS_GetLatin1FlatStringCharsJS_GetLatin1InternedStringCharsJS_GetLatin1StringCharsAndLengthJS_GetLocaleCallbacksJS_GetNaNValueJS_GetObjectPrototypeJS_GetObjectRuntimeJS_GetOptionsJS_GetOwnPropertyDescriptorJS_GetParentJS_GetParentRuntimeJS_GetPendingExceptionJS_GetPositiveInfinityValueJS_GetPrivateJS_GetPropertyJS_GetPropertyAttributesJS_GetPropertyAttrsGetterAndSetterJS_GetPropertyDefaultJS_GetPropertyDescriptorJS_GetPrototypeJS_GetRegExpFlagsJS_GetRegExpSourceJS_GetReservedSlotJS_GetRuntimeJS_GetRuntimePrivateJS_GetScopeChainJS_GetSecurityCallbacksJS_GetStringBytesJS_GetStringCharAtJS_GetStringCharsJS_GetStringCharsAndLengthJS_GetStringEncodingLengthJS_GetStringLengthJS_GetTwoByteExternalStringCharsJS_GetTypeNameJS_GetVersionJS_HasArrayLengthJS_HasElementJS_HasInstanceJS_HasOwnPropertyJS_HasPropertyJS_IdArrayGetJS_IdArrayLengthJS_IdToProtoKeyJS_IdToValueJS_InitJS_InitCTypesClassJS_InitClassJS_InitStandardClassesJS_InstanceOfJS_InternJSStringJS_InternStringJS_IsArrayObjectJS_IsAssigningJS_IsBuiltinEvalFunctionJS_IsBuiltinFunctionConstructorJS_IsConstructingJS_IsConstructing_PossiblyWithGivenThisObjectJS_IsConstructorJS_IsExceptionPendingJS_IsExtensibleJS_IsExternalStringJS_IsGlobalObjectJS_IsIdentifierJS_IsNativeJS_IsNativeFunctionJS_IsRunningJS_IsStopIterationJS_IterateCompartmentsJS_LeaveCompartmentJS_LeaveCrossCompartmentCallJS_LeaveLocalRootScopeJS_LeaveLocalRootScopeWithResultJS_LinkConstructorAndPrototypeJS_LockJS_LockGCThingJS_LookupElementJS_LookupPropertyJS_LooselyEqualJS_MakeStringImmutableJS_MapGCRootsJS_MaybeGCJS_NewJS_NewArrayObjectJS_NewCompartmentAndGlobalObjectJS_NewContextJS_NewDateObjectJS_NewDateObjectMsecJS_NewDependentStringJS_NewDoubleJS_NewDoubleValueJS_NewExternalStringJS_NewFunctionJS_NewGlobalObjectJS_NewNumberValueJS_NewObjectJS_NewObjectForConstructorJS_NewPlainObjectJS_NewPropertyIteratorJS_NewRegExpObjectJS_NewRuntimeJS_NewScriptObjectJS_NewStringCopyNJS_NewStringCopyZJS_NewUCStringJS_NextPropertyJS_NowJS_NumberValueJS_ObjectIsDateJS_ObjectIsFunctionJS_ObjectIsRegExpJS_PSGSJS_ParseJSONJS_PopArgumentsJS_PreventExtensionsJS_PropertyStubJS_PushArgumentsJS_PutEscapedStringJS_Remove*RootJS_RemoveExternalStringFinalizerJS_RemoveRootRTJS_ReportErrorJS_ReportErrorNumberJS_ReportOutOfMemoryJS_ReportPendingExceptionJS_ResolveStandardClassJS_RestoreExceptionStateJS_SET_TRACING_DETAILSJS_SameValueJS_SaveExceptionStateJS_SaveFrameChainJS_ScheduleGCJS_SealObjectJS_SetAllNonReservedSlotsToUndefinedJS_SetArrayLengthJS_SetBranchCallbackJS_SetCallReturnValue2JS_SetCheckObjectAccessCallbackJS_SetCompartmentNameCallbackJS_SetContextCallbackJS_SetDefaultLocaleJS_SetDestroyCompartmentCallbackJS_SetElementJS_SetErrorReporterJS_SetExtraGCRootsJS_SetFunctionCallbackJS_SetGCCallbackJS_SetGCParametersBasedOnAvailableMemoryJS_SetGCZealJS_SetGlobalObjectJS_SetICUMemoryFunctionsJS_SetInterruptCallbackJS_SetNativeStackQuotaJS_SetObjectPrincipalsFinderJS_SetOperationCallbackJS_SetOptionsJS_SetParentJS_SetPendingExceptionJS_SetPrincipalsTranscoderJS_SetPrivateJS_SetPropertyJS_SetPropertyAttributesJS_SetPrototypeJS_SetRegExpInputJS_SetScriptStackQuotaJS_SetThreadStackLimitJS_SetVersionJS_SetVersionForCompartmentJS_ShutDownJS_StrictlyEqualJS_StringEqualsAsciiJS_StringHasBeenInternedJS_StringHasLatin1CharsJS_StringIsFlatJS_StringToVersionJS_SuspendRequestJS_THREADSAFEJS_ThrowStopIterationJS_ToggleOptionsJS_TracerInitJS_TypeOfValueJS_UnlockJS_ValueToBooleanJS_ValueToECMAInt32JS_ValueToFunctionJS_ValueToIdJS_ValueToInt32JS_ValueToNumberJS_ValueToObjectJS_ValueToSourceJS_ValueToStringJS_VersionToStringJS_YieldRequestJS_freeopJS_mallocJS_updateMallocCounterOBJECT_TO_JSVALPRIVATE_TO_JSVALProperty attributesSTRING_TO_JSVALStored valuejscharjsdoublejsidjsintThis article focuses on hacking TraceMonkey code generation (jstracer.cpp, jsregex.cpp) in ways that will work on both 32-bit and 64-bit JIT backends.
The following types or typedefs are always 64-bit on 64-bit platforms, and 32-bit on 32-bit platforms:
The following types are 32-bit on 32-bit platforms. For all intents and purposes they are also 32-bit on 64-bit platforms:
When performing bitwise operations on pointer values, make sure that both operands are 64-bit. The compiler can implicitly sign or zero extend operands with unintended side effects. For example, consider this code:
#define POINTER_TAGBITS 3
static inline uintptr_t
UnmaskPointer(uintptr_t v)
{
return v & ~POINTER_TAGBITS;
}
The value 3 will be inverted to 0xFFFFFFFC, then zero-extended to 0x00000000FFFFFFFC - a subtle and nasty bug, assuming it is unintended. The best way to fix this is to make types explicit, such as:
const uintptr_t POINTER_TAGBITS = 3
Or by using a cast inside the macro. This sort of bug happens surprisingly often - see bug 501324, bug 512866 for example.
If mucking with pointers on AMD64 (or EM64-T/Intel64), it is important to keep in mind an invariant that bits 63-48 must be sign-extended from bit 47. If you use these bits to squirrel away a payload, they must be adjusted before attempting to dereference the pointer. For more information and platform specific details on virtual address widths, see this article on Wikipedia.
When passing arguments to LirWriter::insCall(), there are four types:
ARGSIZE_F - floating point valueARGSIZE_I - 32-bit integerARGSIZE_Q - 64-bit integerARGSIZE_P - 32-bit integer on 32-bit platforms, 64-bit integer on 64-bit platforms.Remember to use ARGSIZE_P where appropriate - on pointers or natively sized integers (including jsvals). Similarly, when adding types to jsbuiltins.h, remember to use _JS_PTR for pointer-width values.
It is not immediately clear from reading LIR which opcodes should be used for 64-bit safety. If you make a mistake, there's an extremely good chance the SanityFilter in Nanojit will catch it while generating code. If this happens you will get an assert that points directly to the ill-typed LIR.
Not all pointer-width values are actually pointers. To avoid equivocating, the rest of this article will use the term "native integers". A native integer is the size used for intptr_t/uintptr_t, which is usually the width of a general-purpose register on the target CPU.
The harder cases to detect usually involve runtime value truncation. For example, this code will not load a native integer correctly on a 64-bit machine:
struct Object {
void *data;
};
lir->insLoad(LIR_ld, objIns, ins->insImm(offsetof(Object, data)));
LIR_ld is 32-bit. On 64-bit machines you must use LIR_ldq. Luckily there is an alias that will choose the right opcode for you - LIR_ldp:
struct Object {
void *data;
};
lir->insLoad(LIR_ldp, objIns, ins->insImm(offsetof(Object, data)));
When you use LirWriter::insStore, the correct size is chosen for you automatically, based on the size of the input operands.
To insert constants, use LirWriter::insImmPtr() for pointer types or LirWriter::insImmWord() for integral types. Note that LirWriter::insImm() is always 32-bit.
Internally, "quads" are 64-bit opaque values in Nanojit. They can be either floating point values or integral values. To help form well-typed LIR, there is a special opcode called LIR_float which specifies that the associated constant value is definitely floating-point. TraceMonkey uses this to decide whether arbitrary constants in LIR are pointers or doubles. You should never use LirWriter::insImmq() to inject a floating point value. Use LirWriter::insImmf() instead.
The following opcodes can be used to safely load, modify, and compare native integers. For such opcodes there are two forms, an "undecorated" form which is 32-bit, and a form decorated with a "q" or "qi" that is 64-bit. To make it easier to generate platform specific code, these opcodes have aliases decorated with a "p". The following table contains the most relevant opcodes:
| Platform Alias | 32-bit Op | 64-bit Op |
|---|---|---|
ldp |
ld |
ldq |
ldcp |
ldc |
ldcq |
piadd |
add |
qiadd |
piand |
and |
qiand |
pilsh |
lsh |
qilsh |
pirsh |
rsh |
qirsh |
pursh |
ush |
qursh |
pcmov |
cmov |
qcmov |
pior |
or |
qior |
pxor |
xor |
qxor |
addp |
iaddp |
qaddp |
peq - puge |
eq - uge |
qeq - quge |
pcall |
icall |
qcall |
The 32-bit versions have the following inputs and outputs. i32 means "32-bit integer".
| 32-bit Op | Inputs | Output |
|---|---|---|
ld |
i32, i32 |
i32 |
ldc |
i32, i32 |
i32 |
add |
i32, i32 |
i32 |
and |
i32, i32 |
i32 |
lsh |
i32, i32 |
i32 |
rsh |
i32, i32 |
i32 |
ush |
i32 |
|
cmov |
i32, i32, i32 |
i32 |
or |
i32, i32 |
i32 |
xor |
i32, i32 |
i32 |
iaddp |
i32, i32 |
i32 |
eg - uge |
i32, i32 |
i32 |
icall |
N/A |
i32 |
The 64-bit versions have the following inputs and outputs. i64 means "64-bit integer".
| 64-bit Op | Inputs | Output |
|---|---|---|
ldq |
i64, i32 |
i64 |
ldcq |
i64, i32 |
i64 |
qiadd |
i64, i64 |
i64 |
qiand |
i64, i64 |
i64 |
qilsh |
i64, i32 |
i64 |
qirsh |
i64, i32 |
i64 |
qursh |
i64, i32 |
i64 |
qcmov |
i32, i64, i64 |
i32 |
qior |
i64, i64 |
i64 |
qixor |
i64, i64 |
i64 |
qiaddp |
i64, i64 |
i64 |
qeq - quge |
i64, i64 |
i32 |
qcall |
N/A |
i64 |
Sometimes it is necessary to reduce a native integer to a 32-bit integer (for example, array or string lengths in TraceMonkey) or extend a 32-bit integer to a native integer.
There are three such opcodes:
LIR_i2q - Sign-extends a 32-bit integer to a 64-bit integer.LIR_u2q - Zero-extends a 32-bit integer to a 64-bit integer.LIR_qlo - Truncates a 64-bit integer to its lower 32 bits.There are three helper functions in LirWriter:
ins_i2p() - On 32-bit platforms, does nothing. On 64-bit platforms, performs a LIR_i2q.ins_u2p() - On 32-bit platforms, does nothing. On 64-bit platforms, performs a LIR_u2q.ins_p2i() - On 32-bit platforms, does nothing. On 64-bit platforms, performs a LIR_qlo.A common use for extending values is to perform advanced addressing. For example, this code tries to load an index from an integer array, but it is not portable:
lir->insLoad(LIR_ldp,
lir->ins2(LIR_piadd,
arrayIns,
lir->ins2i(LIR_mul, indexIns, sizeof(int))
),
0);
The SanityFilter will assert on a 64-bit platform because LIR_piadd (which will be LIR_qiadd) needs both operands to be 64-bit. The correct code is, assuming the index is unsigned:
lir->insLoad(LIR_ldp,
lir->ins2(LIR_piadd,
arrayIns,
lir->ins_u2p(lir->ins2i(LIR_mul, indexIns, sizeof(int)))
),
0);
stobj_get_fslot - Returns jsval-width LInsstobj_get_dslot - Returns jsval-width LInsstobj_set_dslot - Stores jsval-width LInsstobj_set_fslot - Stores jsval-width LInsbox_jsval - Returns jsval-width LInsunbox_jsval - Expects jsval-width LIns