// Copyright 2012 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. #include "src/arm/codegen-arm.h" #if V8_TARGET_ARCH_ARM #include #include "src/arm/simulator-arm.h" #include "src/codegen.h" #include "src/macro-assembler.h" namespace v8 { namespace internal { #define __ masm. #if defined(V8_HOST_ARCH_ARM) MemCopyUint8Function CreateMemCopyUint8Function(Isolate* isolate, MemCopyUint8Function stub) { #if defined(USE_SIMULATOR) return stub; #else size_t actual_size; byte* buffer = static_cast(base::OS::Allocate(1 * KB, &actual_size, true)); if (buffer == nullptr) return stub; MacroAssembler masm(isolate, buffer, static_cast(actual_size), CodeObjectRequired::kNo); Register dest = r0; Register src = r1; Register chars = r2; Register temp1 = r3; Label less_4; if (CpuFeatures::IsSupported(NEON)) { CpuFeatureScope scope(&masm, NEON); Label loop, less_256, less_128, less_64, less_32, _16_or_less, _8_or_less; Label size_less_than_8; __ pld(MemOperand(src, 0)); __ cmp(chars, Operand(8)); __ b(lt, &size_less_than_8); __ cmp(chars, Operand(32)); __ b(lt, &less_32); if (CpuFeatures::dcache_line_size() == 32) { __ pld(MemOperand(src, 32)); } __ cmp(chars, Operand(64)); __ b(lt, &less_64); __ pld(MemOperand(src, 64)); if (CpuFeatures::dcache_line_size() == 32) { __ pld(MemOperand(src, 96)); } __ cmp(chars, Operand(128)); __ b(lt, &less_128); __ pld(MemOperand(src, 128)); if (CpuFeatures::dcache_line_size() == 32) { __ pld(MemOperand(src, 160)); } __ pld(MemOperand(src, 192)); if (CpuFeatures::dcache_line_size() == 32) { __ pld(MemOperand(src, 224)); } __ cmp(chars, Operand(256)); __ b(lt, &less_256); __ sub(chars, chars, Operand(256)); __ bind(&loop); __ pld(MemOperand(src, 256)); __ vld1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(src, PostIndex)); if (CpuFeatures::dcache_line_size() == 32) { __ pld(MemOperand(src, 256)); } __ vld1(Neon8, NeonListOperand(d4, 4), NeonMemOperand(src, PostIndex)); __ sub(chars, chars, Operand(64), SetCC); __ vst1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(dest, PostIndex)); __ vst1(Neon8, NeonListOperand(d4, 4), NeonMemOperand(dest, PostIndex)); __ b(ge, &loop); __ add(chars, chars, Operand(256)); __ bind(&less_256); __ vld1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(src, PostIndex)); __ vld1(Neon8, NeonListOperand(d4, 4), NeonMemOperand(src, PostIndex)); __ sub(chars, chars, Operand(128)); __ vst1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(dest, PostIndex)); __ vst1(Neon8, NeonListOperand(d4, 4), NeonMemOperand(dest, PostIndex)); __ vld1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(src, PostIndex)); __ vld1(Neon8, NeonListOperand(d4, 4), NeonMemOperand(src, PostIndex)); __ vst1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(dest, PostIndex)); __ vst1(Neon8, NeonListOperand(d4, 4), NeonMemOperand(dest, PostIndex)); __ cmp(chars, Operand(64)); __ b(lt, &less_64); __ bind(&less_128); __ vld1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(src, PostIndex)); __ vld1(Neon8, NeonListOperand(d4, 4), NeonMemOperand(src, PostIndex)); __ sub(chars, chars, Operand(64)); __ vst1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(dest, PostIndex)); __ vst1(Neon8, NeonListOperand(d4, 4), NeonMemOperand(dest, PostIndex)); __ bind(&less_64); __ cmp(chars, Operand(32)); __ b(lt, &less_32); __ vld1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(src, PostIndex)); __ vst1(Neon8, NeonListOperand(d0, 4), NeonMemOperand(dest, PostIndex)); __ sub(chars, chars, Operand(32)); __ bind(&less_32); __ cmp(chars, Operand(16)); __ b(le, &_16_or_less); __ vld1(Neon8, NeonListOperand(d0, 2), NeonMemOperand(src, PostIndex)); __ vst1(Neon8, NeonListOperand(d0, 2), NeonMemOperand(dest, PostIndex)); __ sub(chars, chars, Operand(16)); __ bind(&_16_or_less); __ cmp(chars, Operand(8)); __ b(le, &_8_or_less); __ vld1(Neon8, NeonListOperand(d0), NeonMemOperand(src, PostIndex)); __ vst1(Neon8, NeonListOperand(d0), NeonMemOperand(dest, PostIndex)); __ sub(chars, chars, Operand(8)); // Do a last copy which may overlap with the previous copy (up to 8 bytes). __ bind(&_8_or_less); __ rsb(chars, chars, Operand(8)); __ sub(src, src, Operand(chars)); __ sub(dest, dest, Operand(chars)); __ vld1(Neon8, NeonListOperand(d0), NeonMemOperand(src)); __ vst1(Neon8, NeonListOperand(d0), NeonMemOperand(dest)); __ Ret(); __ bind(&size_less_than_8); __ bic(temp1, chars, Operand(0x3), SetCC); __ b(&less_4, eq); __ ldr(temp1, MemOperand(src, 4, PostIndex)); __ str(temp1, MemOperand(dest, 4, PostIndex)); } else { Register temp2 = ip; Label loop; __ bic(temp2, chars, Operand(0x3), SetCC); __ b(&less_4, eq); __ add(temp2, dest, temp2); __ bind(&loop); __ ldr(temp1, MemOperand(src, 4, PostIndex)); __ str(temp1, MemOperand(dest, 4, PostIndex)); __ cmp(dest, temp2); __ b(&loop, ne); } __ bind(&less_4); __ mov(chars, Operand(chars, LSL, 31), SetCC); // bit0 => Z (ne), bit1 => C (cs) __ ldrh(temp1, MemOperand(src, 2, PostIndex), cs); __ strh(temp1, MemOperand(dest, 2, PostIndex), cs); __ ldrb(temp1, MemOperand(src), ne); __ strb(temp1, MemOperand(dest), ne); __ Ret(); CodeDesc desc; masm.GetCode(&desc); DCHECK(!RelocInfo::RequiresRelocation(desc)); Assembler::FlushICache(isolate, buffer, actual_size); base::OS::ProtectCode(buffer, actual_size); return FUNCTION_CAST(buffer); #endif } // Convert 8 to 16. The number of character to copy must be at least 8. MemCopyUint16Uint8Function CreateMemCopyUint16Uint8Function( Isolate* isolate, MemCopyUint16Uint8Function stub) { #if defined(USE_SIMULATOR) return stub; #else size_t actual_size; byte* buffer = static_cast(base::OS::Allocate(1 * KB, &actual_size, true)); if (buffer == nullptr) return stub; MacroAssembler masm(isolate, buffer, static_cast(actual_size), CodeObjectRequired::kNo); Register dest = r0; Register src = r1; Register chars = r2; if (CpuFeatures::IsSupported(NEON)) { CpuFeatureScope scope(&masm, NEON); Register temp = r3; Label loop; __ bic(temp, chars, Operand(0x7)); __ sub(chars, chars, Operand(temp)); __ add(temp, dest, Operand(temp, LSL, 1)); __ bind(&loop); __ vld1(Neon8, NeonListOperand(d0), NeonMemOperand(src, PostIndex)); __ vmovl(NeonU8, q0, d0); __ vst1(Neon16, NeonListOperand(d0, 2), NeonMemOperand(dest, PostIndex)); __ cmp(dest, temp); __ b(&loop, ne); // Do a last copy which will overlap with the previous copy (1 to 8 bytes). __ rsb(chars, chars, Operand(8)); __ sub(src, src, Operand(chars)); __ sub(dest, dest, Operand(chars, LSL, 1)); __ vld1(Neon8, NeonListOperand(d0), NeonMemOperand(src)); __ vmovl(NeonU8, q0, d0); __ vst1(Neon16, NeonListOperand(d0, 2), NeonMemOperand(dest)); __ Ret(); } else { Register temp1 = r3; Register temp2 = ip; Register temp3 = lr; Register temp4 = r4; Label loop; Label not_two; __ Push(lr, r4); __ bic(temp2, chars, Operand(0x3)); __ add(temp2, dest, Operand(temp2, LSL, 1)); __ bind(&loop); __ ldr(temp1, MemOperand(src, 4, PostIndex)); __ uxtb16(temp3, temp1); __ uxtb16(temp4, temp1, 8); __ pkhbt(temp1, temp3, Operand(temp4, LSL, 16)); __ str(temp1, MemOperand(dest)); __ pkhtb(temp1, temp4, Operand(temp3, ASR, 16)); __ str(temp1, MemOperand(dest, 4)); __ add(dest, dest, Operand(8)); __ cmp(dest, temp2); __ b(&loop, ne); __ mov(chars, Operand(chars, LSL, 31), SetCC); // bit0 => ne, bit1 => cs __ b(¬_two, cc); __ ldrh(temp1, MemOperand(src, 2, PostIndex)); __ uxtb(temp3, temp1, 8); __ mov(temp3, Operand(temp3, LSL, 16)); __ uxtab(temp3, temp3, temp1); __ str(temp3, MemOperand(dest, 4, PostIndex)); __ bind(¬_two); __ ldrb(temp1, MemOperand(src), ne); __ strh(temp1, MemOperand(dest), ne); __ Pop(pc, r4); } CodeDesc desc; masm.GetCode(&desc); Assembler::FlushICache(isolate, buffer, actual_size); base::OS::ProtectCode(buffer, actual_size); return FUNCTION_CAST(buffer); #endif } #endif UnaryMathFunctionWithIsolate CreateSqrtFunction(Isolate* isolate) { #if defined(USE_SIMULATOR) return nullptr; #else size_t actual_size; byte* buffer = static_cast(base::OS::Allocate(1 * KB, &actual_size, true)); if (buffer == nullptr) return nullptr; MacroAssembler masm(isolate, buffer, static_cast(actual_size), CodeObjectRequired::kNo); __ MovFromFloatParameter(d0); __ vsqrt(d0, d0); __ MovToFloatResult(d0); __ Ret(); CodeDesc desc; masm.GetCode(&desc); DCHECK(!RelocInfo::RequiresRelocation(desc)); Assembler::FlushICache(isolate, buffer, actual_size); base::OS::ProtectCode(buffer, actual_size); return FUNCTION_CAST(buffer); #endif } #undef __ // ------------------------------------------------------------------------- // Platform-specific RuntimeCallHelper functions. void StubRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const { masm->EnterFrame(StackFrame::INTERNAL); DCHECK(!masm->has_frame()); masm->set_has_frame(true); } void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const { masm->LeaveFrame(StackFrame::INTERNAL); DCHECK(masm->has_frame()); masm->set_has_frame(false); } // ------------------------------------------------------------------------- // Code generators #define __ ACCESS_MASM(masm) void StringCharLoadGenerator::Generate(MacroAssembler* masm, Register string, Register index, Register result, Label* call_runtime) { Label indirect_string_loaded; __ bind(&indirect_string_loaded); // Fetch the instance type of the receiver into result register. __ ldr(result, FieldMemOperand(string, HeapObject::kMapOffset)); __ ldrb(result, FieldMemOperand(result, Map::kInstanceTypeOffset)); // We need special handling for indirect strings. Label check_sequential; __ tst(result, Operand(kIsIndirectStringMask)); __ b(eq, &check_sequential); // Dispatch on the indirect string shape: slice or cons. Label cons_string, thin_string; __ and_(result, result, Operand(kStringRepresentationMask)); __ cmp(result, Operand(kConsStringTag)); __ b(eq, &cons_string); __ cmp(result, Operand(kThinStringTag)); __ b(eq, &thin_string); // Handle slices. __ ldr(result, FieldMemOperand(string, SlicedString::kOffsetOffset)); __ ldr(string, FieldMemOperand(string, SlicedString::kParentOffset)); __ add(index, index, Operand::SmiUntag(result)); __ jmp(&indirect_string_loaded); // Handle thin strings. __ bind(&thin_string); __ ldr(string, FieldMemOperand(string, ThinString::kActualOffset)); __ jmp(&indirect_string_loaded); // Handle cons strings. // Check whether the right hand side is the empty string (i.e. if // this is really a flat string in a cons string). If that is not // the case we would rather go to the runtime system now to flatten // the string. __ bind(&cons_string); __ ldr(result, FieldMemOperand(string, ConsString::kSecondOffset)); __ CompareRoot(result, Heap::kempty_stringRootIndex); __ b(ne, call_runtime); // Get the first of the two strings and load its instance type. __ ldr(string, FieldMemOperand(string, ConsString::kFirstOffset)); __ jmp(&indirect_string_loaded); // Distinguish sequential and external strings. Only these two string // representations can reach here (slices and flat cons strings have been // reduced to the underlying sequential or external string). Label external_string, check_encoding; __ bind(&check_sequential); STATIC_ASSERT(kSeqStringTag == 0); __ tst(result, Operand(kStringRepresentationMask)); __ b(ne, &external_string); // Prepare sequential strings STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize); __ add(string, string, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); __ jmp(&check_encoding); // Handle external strings. __ bind(&external_string); if (FLAG_debug_code) { // Assert that we do not have a cons or slice (indirect strings) here. // Sequential strings have already been ruled out. __ tst(result, Operand(kIsIndirectStringMask)); __ Assert(eq, kExternalStringExpectedButNotFound); } // Rule out short external strings. STATIC_ASSERT(kShortExternalStringTag != 0); __ tst(result, Operand(kShortExternalStringMask)); __ b(ne, call_runtime); __ ldr(string, FieldMemOperand(string, ExternalString::kResourceDataOffset)); Label one_byte, done; __ bind(&check_encoding); STATIC_ASSERT(kTwoByteStringTag == 0); __ tst(result, Operand(kStringEncodingMask)); __ b(ne, &one_byte); // Two-byte string. __ ldrh(result, MemOperand(string, index, LSL, 1)); __ jmp(&done); __ bind(&one_byte); // One-byte string. __ ldrb(result, MemOperand(string, index)); __ bind(&done); } #undef __ #ifdef DEBUG // add(r0, pc, Operand(-8)) static const uint32_t kCodeAgePatchFirstInstruction = 0xe24f0008; #endif CodeAgingHelper::CodeAgingHelper(Isolate* isolate) { USE(isolate); DCHECK(young_sequence_.length() == kNoCodeAgeSequenceLength); // Since patcher is a large object, allocate it dynamically when needed, // to avoid overloading the stack in stress conditions. // DONT_FLUSH is used because the CodeAgingHelper is initialized early in // the process, before ARM simulator ICache is setup. std::unique_ptr patcher( new CodePatcher(isolate, young_sequence_.start(), young_sequence_.length() / Assembler::kInstrSize, CodePatcher::DONT_FLUSH)); PredictableCodeSizeScope scope(patcher->masm(), young_sequence_.length()); patcher->masm()->PushStandardFrame(r1); patcher->masm()->nop(ip.code()); } #ifdef DEBUG bool CodeAgingHelper::IsOld(byte* candidate) const { return Memory::uint32_at(candidate) == kCodeAgePatchFirstInstruction; } #endif bool Code::IsYoungSequence(Isolate* isolate, byte* sequence) { bool result = isolate->code_aging_helper()->IsYoung(sequence); DCHECK(result || isolate->code_aging_helper()->IsOld(sequence)); return result; } Code::Age Code::GetCodeAge(Isolate* isolate, byte* sequence) { if (IsYoungSequence(isolate, sequence)) return kNoAgeCodeAge; Address target_address = Memory::Address_at( sequence + (kNoCodeAgeSequenceLength - Assembler::kInstrSize)); Code* stub = GetCodeFromTargetAddress(target_address); return GetAgeOfCodeAgeStub(stub); } void Code::PatchPlatformCodeAge(Isolate* isolate, byte* sequence, Code::Age age) { uint32_t young_length = isolate->code_aging_helper()->young_sequence_length(); if (age == kNoAgeCodeAge) { isolate->code_aging_helper()->CopyYoungSequenceTo(sequence); Assembler::FlushICache(isolate, sequence, young_length); } else { Code* stub = GetCodeAgeStub(isolate, age); CodePatcher patcher(isolate, sequence, young_length / Assembler::kInstrSize); patcher.masm()->add(r0, pc, Operand(-8)); patcher.masm()->ldr(pc, MemOperand(pc, -4)); patcher.masm()->emit_code_stub_address(stub); } } } // namespace internal } // namespace v8 #endif // V8_TARGET_ARCH_ARM