| // 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_ |