// Copyright 2010 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef V8_ARM_CODE_STUBS_ARM_H_ #define V8_ARM_CODE_STUBS_ARM_H_ #include "ic-inl.h" namespace v8 { namespace internal { // Compute a transcendental math function natively, or call the // TranscendentalCache runtime function. class TranscendentalCacheStub: public CodeStub { public: explicit TranscendentalCacheStub(TranscendentalCache::Type type) : type_(type) {} void Generate(MacroAssembler* masm); private: TranscendentalCache::Type type_; Major MajorKey() { return TranscendentalCache; } int MinorKey() { return type_; } Runtime::FunctionId RuntimeFunction(); }; class ToBooleanStub: public CodeStub { public: explicit ToBooleanStub(Register tos) : tos_(tos) { } void Generate(MacroAssembler* masm); private: Register tos_; Major MajorKey() { return ToBoolean; } int MinorKey() { return tos_.code(); } }; class GenericBinaryOpStub : public CodeStub { public: static const int kUnknownIntValue = -1; GenericBinaryOpStub(Token::Value op, OverwriteMode mode, Register lhs, Register rhs, int constant_rhs = kUnknownIntValue) : op_(op), mode_(mode), lhs_(lhs), rhs_(rhs), constant_rhs_(constant_rhs), specialized_on_rhs_(RhsIsOneWeWantToOptimizeFor(op, constant_rhs)), runtime_operands_type_(BinaryOpIC::UNINIT_OR_SMI), name_(NULL) { } GenericBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info) : op_(OpBits::decode(key)), mode_(ModeBits::decode(key)), lhs_(LhsRegister(RegisterBits::decode(key))), rhs_(RhsRegister(RegisterBits::decode(key))), constant_rhs_(KnownBitsForMinorKey(KnownIntBits::decode(key))), specialized_on_rhs_(RhsIsOneWeWantToOptimizeFor(op_, constant_rhs_)), runtime_operands_type_(type_info), name_(NULL) { } private: Token::Value op_; OverwriteMode mode_; Register lhs_; Register rhs_; int constant_rhs_; bool specialized_on_rhs_; BinaryOpIC::TypeInfo runtime_operands_type_; char* name_; static const int kMaxKnownRhs = 0x40000000; static const int kKnownRhsKeyBits = 6; // Minor key encoding in 17 bits. class ModeBits: public BitField {}; class OpBits: public BitField {}; class TypeInfoBits: public BitField {}; class RegisterBits: public BitField {}; class KnownIntBits: public BitField {}; Major MajorKey() { return GenericBinaryOp; } int MinorKey() { ASSERT((lhs_.is(r0) && rhs_.is(r1)) || (lhs_.is(r1) && rhs_.is(r0))); // Encode the parameters in a unique 18 bit value. return OpBits::encode(op_) | ModeBits::encode(mode_) | KnownIntBits::encode(MinorKeyForKnownInt()) | TypeInfoBits::encode(runtime_operands_type_) | RegisterBits::encode(lhs_.is(r0)); } void Generate(MacroAssembler* masm); void HandleNonSmiBitwiseOp(MacroAssembler* masm, Register lhs, Register rhs); void HandleBinaryOpSlowCases(MacroAssembler* masm, Label* not_smi, Register lhs, Register rhs, const Builtins::JavaScript& builtin); void GenerateTypeTransition(MacroAssembler* masm); static bool RhsIsOneWeWantToOptimizeFor(Token::Value op, int constant_rhs) { if (constant_rhs == kUnknownIntValue) return false; if (op == Token::DIV) return constant_rhs >= 2 && constant_rhs <= 3; if (op == Token::MOD) { if (constant_rhs <= 1) return false; if (constant_rhs <= 10) return true; if (constant_rhs <= kMaxKnownRhs && IsPowerOf2(constant_rhs)) return true; return false; } return false; } int MinorKeyForKnownInt() { if (!specialized_on_rhs_) return 0; if (constant_rhs_ <= 10) return constant_rhs_ + 1; ASSERT(IsPowerOf2(constant_rhs_)); int key = 12; int d = constant_rhs_; while ((d & 1) == 0) { key++; d >>= 1; } ASSERT(key >= 0 && key < (1 << kKnownRhsKeyBits)); return key; } int KnownBitsForMinorKey(int key) { if (!key) return 0; if (key <= 11) return key - 1; int d = 1; while (key != 12) { key--; d <<= 1; } return d; } Register LhsRegister(bool lhs_is_r0) { return lhs_is_r0 ? r0 : r1; } Register RhsRegister(bool lhs_is_r0) { return lhs_is_r0 ? r1 : r0; } bool HasSmiSmiFastPath() { return op_ != Token::DIV; } bool ShouldGenerateSmiCode() { return ((op_ != Token::DIV && op_ != Token::MOD) || specialized_on_rhs_) && runtime_operands_type_ != BinaryOpIC::HEAP_NUMBERS && runtime_operands_type_ != BinaryOpIC::STRINGS; } bool ShouldGenerateFPCode() { return runtime_operands_type_ != BinaryOpIC::STRINGS; } virtual int GetCodeKind() { return Code::BINARY_OP_IC; } virtual InlineCacheState GetICState() { return BinaryOpIC::ToState(runtime_operands_type_); } const char* GetName(); virtual void FinishCode(Code* code) { code->set_binary_op_type(runtime_operands_type_); } #ifdef DEBUG void Print() { if (!specialized_on_rhs_) { PrintF("GenericBinaryOpStub (%s)\n", Token::String(op_)); } else { PrintF("GenericBinaryOpStub (%s by %d)\n", Token::String(op_), constant_rhs_); } } #endif }; class TypeRecordingBinaryOpStub: public CodeStub { public: TypeRecordingBinaryOpStub(Token::Value op, OverwriteMode mode) : op_(op), mode_(mode), operands_type_(TRBinaryOpIC::UNINITIALIZED), result_type_(TRBinaryOpIC::UNINITIALIZED), name_(NULL) { use_vfp3_ = CpuFeatures::IsSupported(VFP3); ASSERT(OpBits::is_valid(Token::NUM_TOKENS)); } TypeRecordingBinaryOpStub( int key, TRBinaryOpIC::TypeInfo operands_type, TRBinaryOpIC::TypeInfo result_type = TRBinaryOpIC::UNINITIALIZED) : op_(OpBits::decode(key)), mode_(ModeBits::decode(key)), use_vfp3_(VFP3Bits::decode(key)), operands_type_(operands_type), result_type_(result_type), name_(NULL) { } private: enum SmiCodeGenerateHeapNumberResults { ALLOW_HEAPNUMBER_RESULTS, NO_HEAPNUMBER_RESULTS }; Token::Value op_; OverwriteMode mode_; bool use_vfp3_; // Operand type information determined at runtime. TRBinaryOpIC::TypeInfo operands_type_; TRBinaryOpIC::TypeInfo result_type_; char* name_; const char* GetName(); #ifdef DEBUG void Print() { PrintF("TypeRecordingBinaryOpStub %d (op %s), " "(mode %d, runtime_type_info %s)\n", MinorKey(), Token::String(op_), static_cast(mode_), TRBinaryOpIC::GetName(operands_type_)); } #endif // Minor key encoding in 16 bits RRRTTTVOOOOOOOMM. class ModeBits: public BitField {}; class OpBits: public BitField {}; class VFP3Bits: public BitField {}; class OperandTypeInfoBits: public BitField {}; class ResultTypeInfoBits: public BitField {}; Major MajorKey() { return TypeRecordingBinaryOp; } int MinorKey() { return OpBits::encode(op_) | ModeBits::encode(mode_) | VFP3Bits::encode(use_vfp3_) | OperandTypeInfoBits::encode(operands_type_) | ResultTypeInfoBits::encode(result_type_); } void Generate(MacroAssembler* masm); void GenerateGeneric(MacroAssembler* masm); void GenerateSmiCode(MacroAssembler* masm, Label* gc_required, SmiCodeGenerateHeapNumberResults heapnumber_results); void GenerateLoadArguments(MacroAssembler* masm); void GenerateReturn(MacroAssembler* masm); void GenerateUninitializedStub(MacroAssembler* masm); void GenerateSmiStub(MacroAssembler* masm); void GenerateInt32Stub(MacroAssembler* masm); void GenerateHeapNumberStub(MacroAssembler* masm); void GenerateStringStub(MacroAssembler* masm); void GenerateGenericStub(MacroAssembler* masm); void GenerateAddStrings(MacroAssembler* masm); void GenerateCallRuntime(MacroAssembler* masm); void GenerateHeapResultAllocation(MacroAssembler* masm, Register result, Register heap_number_map, Register scratch1, Register scratch2, Label* gc_required); void GenerateRegisterArgsPush(MacroAssembler* masm); void GenerateTypeTransition(MacroAssembler* masm); void GenerateTypeTransitionWithSavedArgs(MacroAssembler* masm); virtual int GetCodeKind() { return Code::TYPE_RECORDING_BINARY_OP_IC; } virtual InlineCacheState GetICState() { return TRBinaryOpIC::ToState(operands_type_); } virtual void FinishCode(Code* code) { code->set_type_recording_binary_op_type(operands_type_); code->set_type_recording_binary_op_result_type(result_type_); } friend class CodeGenerator; }; // Flag that indicates how to generate code for the stub StringAddStub. enum StringAddFlags { NO_STRING_ADD_FLAGS = 0, NO_STRING_CHECK_IN_STUB = 1 << 0 // Omit string check in stub. }; class StringAddStub: public CodeStub { public: explicit StringAddStub(StringAddFlags flags) { string_check_ = ((flags & NO_STRING_CHECK_IN_STUB) == 0); } private: Major MajorKey() { return StringAdd; } int MinorKey() { return string_check_ ? 0 : 1; } void Generate(MacroAssembler* masm); // Should the stub check whether arguments are strings? bool string_check_; }; class SubStringStub: public CodeStub { public: SubStringStub() {} private: Major MajorKey() { return SubString; } int MinorKey() { return 0; } void Generate(MacroAssembler* masm); }; class StringCompareStub: public CodeStub { public: StringCompareStub() { } // Compare two flat ASCII strings and returns result in r0. // Does not use the stack. static void GenerateCompareFlatAsciiStrings(MacroAssembler* masm, Register left, Register right, Register scratch1, Register scratch2, Register scratch3, Register scratch4); private: Major MajorKey() { return StringCompare; } int MinorKey() { return 0; } void Generate(MacroAssembler* masm); }; // This stub can do a fast mod operation without using fp. // It is tail called from the GenericBinaryOpStub and it always // returns an answer. It never causes GC so it doesn't need a real frame. // // The inputs are always positive Smis. This is never called // where the denominator is a power of 2. We handle that separately. // // If we consider the denominator as an odd number multiplied by a power of 2, // then: // * The exponent (power of 2) is in the shift_distance register. // * The odd number is in the odd_number register. It is always in the range // of 3 to 25. // * The bits from the numerator that are to be copied to the answer (there are // shift_distance of them) are in the mask_bits register. // * The other bits of the numerator have been shifted down and are in the lhs // register. class IntegerModStub : public CodeStub { public: IntegerModStub(Register result, Register shift_distance, Register odd_number, Register mask_bits, Register lhs, Register scratch) : result_(result), shift_distance_(shift_distance), odd_number_(odd_number), mask_bits_(mask_bits), lhs_(lhs), scratch_(scratch) { // We don't code these in the minor key, so they should always be the same. // We don't really want to fix that since this stub is rather large and we // don't want many copies of it. ASSERT(shift_distance_.is(r9)); ASSERT(odd_number_.is(r4)); ASSERT(mask_bits_.is(r3)); ASSERT(scratch_.is(r5)); } private: Register result_; Register shift_distance_; Register odd_number_; Register mask_bits_; Register lhs_; Register scratch_; // Minor key encoding in 16 bits. class ResultRegisterBits: public BitField {}; class LhsRegisterBits: public BitField {}; Major MajorKey() { return IntegerMod; } int MinorKey() { // Encode the parameters in a unique 16 bit value. return ResultRegisterBits::encode(result_.code()) | LhsRegisterBits::encode(lhs_.code()); } void Generate(MacroAssembler* masm); const char* GetName() { return "IntegerModStub"; } // Utility functions. void DigitSum(MacroAssembler* masm, Register lhs, int mask, int shift, Label* entry); void DigitSum(MacroAssembler* masm, Register lhs, Register scratch, int mask, int shift1, int shift2, Label* entry); void ModGetInRangeBySubtraction(MacroAssembler* masm, Register lhs, int shift, int rhs); void ModReduce(MacroAssembler* masm, Register lhs, int max, int denominator); void ModAnswer(MacroAssembler* masm, Register result, Register shift_distance, Register mask_bits, Register sum_of_digits); #ifdef DEBUG void Print() { PrintF("IntegerModStub\n"); } #endif }; // This stub can convert a signed int32 to a heap number (double). It does // not work for int32s that are in Smi range! No GC occurs during this stub // so you don't have to set up the frame. class WriteInt32ToHeapNumberStub : public CodeStub { public: WriteInt32ToHeapNumberStub(Register the_int, Register the_heap_number, Register scratch) : the_int_(the_int), the_heap_number_(the_heap_number), scratch_(scratch) { } private: Register the_int_; Register the_heap_number_; Register scratch_; // Minor key encoding in 16 bits. class IntRegisterBits: public BitField {}; class HeapNumberRegisterBits: public BitField {}; class ScratchRegisterBits: public BitField {}; Major MajorKey() { return WriteInt32ToHeapNumber; } int MinorKey() { // Encode the parameters in a unique 16 bit value. return IntRegisterBits::encode(the_int_.code()) | HeapNumberRegisterBits::encode(the_heap_number_.code()) | ScratchRegisterBits::encode(scratch_.code()); } void Generate(MacroAssembler* masm); const char* GetName() { return "WriteInt32ToHeapNumberStub"; } #ifdef DEBUG void Print() { PrintF("WriteInt32ToHeapNumberStub\n"); } #endif }; class NumberToStringStub: public CodeStub { public: NumberToStringStub() { } // Generate code to do a lookup in the number string cache. If the number in // the register object is found in the cache the generated code falls through // with the result in the result register. The object and the result register // can be the same. If the number is not found in the cache the code jumps to // the label not_found with only the content of register object unchanged. static void GenerateLookupNumberStringCache(MacroAssembler* masm, Register object, Register result, Register scratch1, Register scratch2, Register scratch3, bool object_is_smi, Label* not_found); private: Major MajorKey() { return NumberToString; } int MinorKey() { return 0; } void Generate(MacroAssembler* masm); const char* GetName() { return "NumberToStringStub"; } }; // Enter C code from generated RegExp code in a way that allows // the C code to fix the return address in case of a GC. // Currently only needed on ARM. class RegExpCEntryStub: public CodeStub { public: RegExpCEntryStub() {} virtual ~RegExpCEntryStub() {} void Generate(MacroAssembler* masm); private: Major MajorKey() { return RegExpCEntry; } int MinorKey() { return 0; } const char* GetName() { return "RegExpCEntryStub"; } }; } } // namespace v8::internal #endif // V8_ARM_CODE_STUBS_ARM_H_