// Copyright 2014 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef V8_S390_CODE_STUBS_S390_H_ #define V8_S390_CODE_STUBS_S390_H_ #include "src/s390/frames-s390.h" namespace v8 { namespace internal { void ArrayNativeCode(MacroAssembler* masm, Label* call_generic_code); class StringHelper : public AllStatic { public: // Generate code for copying a large number of characters. This function // is allowed to spend extra time setting up conditions to make copying // faster. Copying of overlapping regions is not supported. // Dest register ends at the position after the last character written. static void GenerateCopyCharacters(MacroAssembler* masm, Register dest, Register src, Register count, Register scratch, String::Encoding encoding); // Compares two flat one-byte strings and returns result in r0. static void GenerateCompareFlatOneByteStrings(MacroAssembler* masm, Register left, Register right, Register scratch1, Register scratch2, Register scratch3); // Compares two flat one-byte strings for equality and returns result in r0. static void GenerateFlatOneByteStringEquals(MacroAssembler* masm, Register left, Register right, Register scratch1, Register scratch2); private: static void GenerateOneByteCharsCompareLoop(MacroAssembler* masm, Register left, Register right, Register length, Register scratch1, Label* chars_not_equal); DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper); }; class StoreRegistersStateStub : public PlatformCodeStub { public: explicit StoreRegistersStateStub(Isolate* isolate) : PlatformCodeStub(isolate) {} static void GenerateAheadOfTime(Isolate* isolate); private: DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR(); DEFINE_PLATFORM_CODE_STUB(StoreRegistersState, PlatformCodeStub); }; class RestoreRegistersStateStub : public PlatformCodeStub { public: explicit RestoreRegistersStateStub(Isolate* isolate) : PlatformCodeStub(isolate) {} static void GenerateAheadOfTime(Isolate* isolate); private: DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR(); DEFINE_PLATFORM_CODE_STUB(RestoreRegistersState, PlatformCodeStub); }; class RecordWriteStub : public PlatformCodeStub { public: RecordWriteStub(Isolate* isolate, Register object, Register value, Register address, RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode) : PlatformCodeStub(isolate), regs_(object, // An input reg. address, // An input reg. value) { // One scratch reg. minor_key_ = ObjectBits::encode(object.code()) | ValueBits::encode(value.code()) | AddressBits::encode(address.code()) | RememberedSetActionBits::encode(remembered_set_action) | SaveFPRegsModeBits::encode(fp_mode); } RecordWriteStub(uint32_t key, Isolate* isolate) : PlatformCodeStub(key, isolate), regs_(object(), address(), value()) {} enum Mode { STORE_BUFFER_ONLY, INCREMENTAL, INCREMENTAL_COMPACTION }; bool SometimesSetsUpAFrame() override { return false; } // Patch an always taken branch into a NOP branch static void PatchBranchCondMask(MacroAssembler* masm, int pos, Condition c) { int32_t instrLen = masm->instr_length_at(pos); DCHECK(instrLen == 4 || instrLen == 6); if (instrLen == 4) { // BRC - Branch Mask @ Bits 23-20 FourByteInstr updatedMask = static_cast<FourByteInstr>(c) << 20; masm->instr_at_put<FourByteInstr>( pos, (masm->instr_at(pos) & ~kFourByteBrCondMask) | updatedMask); } else { // BRCL - Branch Mask @ Bits 39-36 SixByteInstr updatedMask = static_cast<SixByteInstr>(c) << 36; masm->instr_at_put<SixByteInstr>( pos, (masm->instr_at(pos) & ~kSixByteBrCondMask) | updatedMask); } } static bool isBranchNop(SixByteInstr instr, int instrLength) { if ((4 == instrLength && 0 == (instr & kFourByteBrCondMask)) || // BRC - Check for 0x0 mask condition. (6 == instrLength && 0 == (instr & kSixByteBrCondMask))) { // BRCL - Check for 0x0 mask condition return true; } return false; } static Mode GetMode(Code* stub) { int32_t first_instr_length = Instruction::InstructionLength(stub->instruction_start()); int32_t second_instr_length = Instruction::InstructionLength( stub->instruction_start() + first_instr_length); uint64_t first_instr = Assembler::instr_at(stub->instruction_start()); uint64_t second_instr = Assembler::instr_at(stub->instruction_start() + first_instr_length); DCHECK(first_instr_length == 4 || first_instr_length == 6); DCHECK(second_instr_length == 4 || second_instr_length == 6); bool isFirstInstrNOP = isBranchNop(first_instr, first_instr_length); bool isSecondInstrNOP = isBranchNop(second_instr, second_instr_length); // STORE_BUFFER_ONLY has NOP on both branches if (isSecondInstrNOP && isFirstInstrNOP) return STORE_BUFFER_ONLY; // INCREMENTAL_COMPACTION has NOP on second branch. else if (isFirstInstrNOP && !isSecondInstrNOP) return INCREMENTAL_COMPACTION; // INCREMENTAL has NOP on first branch. else if (!isFirstInstrNOP && isSecondInstrNOP) return INCREMENTAL; DCHECK(false); return STORE_BUFFER_ONLY; } static void Patch(Code* stub, Mode mode) { MacroAssembler masm(stub->GetIsolate(), stub->instruction_start(), stub->instruction_size(), CodeObjectRequired::kNo); // Get instruction lengths of two branches int32_t first_instr_length = masm.instr_length_at(0); int32_t second_instr_length = masm.instr_length_at(first_instr_length); switch (mode) { case STORE_BUFFER_ONLY: DCHECK(GetMode(stub) == INCREMENTAL || GetMode(stub) == INCREMENTAL_COMPACTION); PatchBranchCondMask(&masm, 0, CC_NOP); PatchBranchCondMask(&masm, first_instr_length, CC_NOP); break; case INCREMENTAL: DCHECK(GetMode(stub) == STORE_BUFFER_ONLY); PatchBranchCondMask(&masm, 0, CC_ALWAYS); break; case INCREMENTAL_COMPACTION: DCHECK(GetMode(stub) == STORE_BUFFER_ONLY); PatchBranchCondMask(&masm, first_instr_length, CC_ALWAYS); break; } DCHECK(GetMode(stub) == mode); Assembler::FlushICache(stub->GetIsolate(), stub->instruction_start(), first_instr_length + second_instr_length); } DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR(); private: // This is a helper class for freeing up 3 scratch registers. The input is // two registers that must be preserved and one scratch register provided by // the caller. class RegisterAllocation { public: RegisterAllocation(Register object, Register address, Register scratch0) : object_(object), address_(address), scratch0_(scratch0) { DCHECK(!AreAliased(scratch0, object, address, no_reg)); scratch1_ = GetRegisterThatIsNotOneOf(object_, address_, scratch0_); } void Save(MacroAssembler* masm) { DCHECK(!AreAliased(object_, address_, scratch1_, scratch0_)); // We don't have to save scratch0_ because it was given to us as // a scratch register. masm->push(scratch1_); } void Restore(MacroAssembler* masm) { masm->pop(scratch1_); } // If we have to call into C then we need to save and restore all caller- // saved registers that were not already preserved. The scratch registers // will be restored by other means so we don't bother pushing them here. void SaveCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) { masm->push(r14); masm->MultiPush(kJSCallerSaved & ~scratch1_.bit()); if (mode == kSaveFPRegs) { // Save all volatile FP registers except d0. masm->MultiPushDoubles(kCallerSavedDoubles & ~d0.bit()); } } inline void RestoreCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) { if (mode == kSaveFPRegs) { // Restore all volatile FP registers except d0. masm->MultiPopDoubles(kCallerSavedDoubles & ~d0.bit()); } masm->MultiPop(kJSCallerSaved & ~scratch1_.bit()); masm->pop(r14); } inline Register object() { return object_; } inline Register address() { return address_; } inline Register scratch0() { return scratch0_; } inline Register scratch1() { return scratch1_; } private: Register object_; Register address_; Register scratch0_; Register scratch1_; friend class RecordWriteStub; }; enum OnNoNeedToInformIncrementalMarker { kReturnOnNoNeedToInformIncrementalMarker, kUpdateRememberedSetOnNoNeedToInformIncrementalMarker }; inline Major MajorKey() const final { return RecordWrite; } void Generate(MacroAssembler* masm) override; void GenerateIncremental(MacroAssembler* masm, Mode mode); void CheckNeedsToInformIncrementalMarker( MacroAssembler* masm, OnNoNeedToInformIncrementalMarker on_no_need, Mode mode); void InformIncrementalMarker(MacroAssembler* masm); void Activate(Code* code) override { code->GetHeap()->incremental_marking()->ActivateGeneratedStub(code); } Register object() const { return Register::from_code(ObjectBits::decode(minor_key_)); } Register value() const { return Register::from_code(ValueBits::decode(minor_key_)); } Register address() const { return Register::from_code(AddressBits::decode(minor_key_)); } RememberedSetAction remembered_set_action() const { return RememberedSetActionBits::decode(minor_key_); } SaveFPRegsMode save_fp_regs_mode() const { return SaveFPRegsModeBits::decode(minor_key_); } class ObjectBits : public BitField<int, 0, 4> {}; class ValueBits : public BitField<int, 4, 4> {}; class AddressBits : public BitField<int, 8, 4> {}; class RememberedSetActionBits : public BitField<RememberedSetAction, 15, 1> { }; class SaveFPRegsModeBits : public BitField<SaveFPRegsMode, 16, 1> {}; Label slow_; RegisterAllocation regs_; DISALLOW_COPY_AND_ASSIGN(RecordWriteStub); }; // Trampoline stub to call into native code. To call safely into native code // in the presence of compacting GC (which can move code objects) we need to // keep the code which called into native pinned in the memory. Currently the // simplest approach is to generate such stub early enough so it can never be // moved by GC class DirectCEntryStub : public PlatformCodeStub { public: explicit DirectCEntryStub(Isolate* isolate) : PlatformCodeStub(isolate) {} void GenerateCall(MacroAssembler* masm, Register target); private: bool NeedsImmovableCode() override { return true; } DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR(); DEFINE_PLATFORM_CODE_STUB(DirectCEntry, PlatformCodeStub); }; class NameDictionaryLookupStub : public PlatformCodeStub { public: enum LookupMode { POSITIVE_LOOKUP, NEGATIVE_LOOKUP }; NameDictionaryLookupStub(Isolate* isolate, LookupMode mode) : PlatformCodeStub(isolate) { minor_key_ = LookupModeBits::encode(mode); } static void GenerateNegativeLookup(MacroAssembler* masm, Label* miss, Label* done, Register receiver, Register properties, Handle<Name> name, Register scratch0); static void GeneratePositiveLookup(MacroAssembler* masm, Label* miss, Label* done, Register elements, Register name, Register r0, Register r1); bool SometimesSetsUpAFrame() override { return false; } private: static const int kInlinedProbes = 4; static const int kTotalProbes = 20; static const int kCapacityOffset = NameDictionary::kHeaderSize + NameDictionary::kCapacityIndex * kPointerSize; static const int kElementsStartOffset = NameDictionary::kHeaderSize + NameDictionary::kElementsStartIndex * kPointerSize; LookupMode mode() const { return LookupModeBits::decode(minor_key_); } class LookupModeBits : public BitField<LookupMode, 0, 1> {}; DEFINE_NULL_CALL_INTERFACE_DESCRIPTOR(); DEFINE_PLATFORM_CODE_STUB(NameDictionaryLookup, PlatformCodeStub); }; class FloatingPointHelper : public AllStatic { public: enum Destination { kFPRegisters, kCoreRegisters }; // Loads smis from r0 and r1 (right and left in binary operations) into // floating point registers. Depending on the destination the values ends up // either d7 and d6 or in r2/r3 and r0/r1 respectively. If the destination is // floating point registers VFP3 must be supported. If core registers are // requested when VFP3 is supported d6 and d7 will be scratched. static void LoadSmis(MacroAssembler* masm, Register scratch1, Register scratch2); // Loads objects from r0 and r1 (right and left in binary operations) into // floating point registers. Depending on the destination the values ends up // either d7 and d6 or in r2/r3 and r0/r1 respectively. If the destination is // floating point registers VFP3 must be supported. If core registers are // requested when VFP3 is supported d6 and d7 will still be scratched. If // either r0 or r1 is not a number (not smi and not heap number object) the // not_number label is jumped to with r0 and r1 intact. static void LoadOperands(MacroAssembler* masm, Register heap_number_map, Register scratch1, Register scratch2, Label* not_number); // Convert the smi or heap number in object to an int32 using the rules // for ToInt32 as described in ECMAScript 9.5.: the value is truncated // and brought into the range -2^31 .. +2^31 - 1. static void ConvertNumberToInt32(MacroAssembler* masm, Register object, Register dst, Register heap_number_map, Register scratch1, Register scratch2, Register scratch3, DoubleRegister double_scratch, Label* not_int32); // Converts the integer (untagged smi) in |src| to a double, storing // the result to |double_dst| static void ConvertIntToDouble(MacroAssembler* masm, Register src, DoubleRegister double_dst); // Converts the unsigned integer (untagged smi) in |src| to // a double, storing the result to |double_dst| static void ConvertUnsignedIntToDouble(MacroAssembler* masm, Register src, DoubleRegister double_dst); // Converts the integer (untagged smi) in |src| to // a float, storing the result in |dst| static void ConvertIntToFloat(MacroAssembler* masm, const DoubleRegister dst, const Register src); // Load the number from object into double_dst in the double format. // Control will jump to not_int32 if the value cannot be exactly represented // by a 32-bit integer. // Floating point value in the 32-bit integer range that are not exact integer // won't be loaded. static void LoadNumberAsInt32Double(MacroAssembler* masm, Register object, DoubleRegister double_dst, DoubleRegister double_scratch, Register heap_number_map, Register scratch1, Register scratch2, Label* not_int32); // Loads the number from object into dst as a 32-bit integer. // Control will jump to not_int32 if the object cannot be exactly represented // by a 32-bit integer. // Floating point value in the 32-bit integer range that are not exact integer // won't be converted. // scratch3 is not used when VFP3 is supported. static void LoadNumberAsInt32(MacroAssembler* masm, Register object, Register dst, Register heap_number_map, Register scratch1, Register scratch2, Register scratch3, DoubleRegister double_scratch0, DoubleRegister double_scratch1, Label* not_int32); // Generate non VFP3 code to check if a double can be exactly represented by a // 32-bit integer. This does not check for 0 or -0, which need // to be checked for separately. // Control jumps to not_int32 if the value is not a 32-bit integer, and falls // through otherwise. // src1 and src2 will be cloberred. // // Expected input: // - src1: higher (exponent) part of the double value. // - src2: lower (mantissa) part of the double value. // Output status: // - dst: 32 higher bits of the mantissa. (mantissa[51:20]) // - src2: contains 1. // - other registers are clobbered. static void DoubleIs32BitInteger(MacroAssembler* masm, Register src1, Register src2, Register dst, Register scratch, Label* not_int32); // Generates code to call a C function to do a double operation using core // registers. (Used when VFP3 is not supported.) // This code never falls through, but returns with a heap number containing // the result in r0. // Register heapnumber_result must be a heap number in which the // result of the operation will be stored. // Requires the following layout on entry: // r0: Left value (least significant part of mantissa). // r1: Left value (sign, exponent, top of mantissa). // r2: Right value (least significant part of mantissa). // r3: Right value (sign, exponent, top of mantissa). static void CallCCodeForDoubleOperation(MacroAssembler* masm, Token::Value op, Register heap_number_result, Register scratch); private: static void LoadNumber(MacroAssembler* masm, Register object, DoubleRegister dst, Register heap_number_map, Register scratch1, Register scratch2, Label* not_number); }; } // namespace internal } // namespace v8 #endif // V8_S390_CODE_STUBS_S390_H_