| // 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. |
| |
| #include <assert.h> // For assert |
| #include <limits.h> // For LONG_MIN, LONG_MAX. |
| |
| #if V8_TARGET_ARCH_PPC64 |
| |
| #include <optional> |
| |
| #include "src/base/bits.h" |
| #include "src/base/division-by-constant.h" |
| #include "src/builtins/builtins-inl.h" |
| #include "src/codegen/callable.h" |
| #include "src/codegen/code-factory.h" |
| #include "src/codegen/external-reference-table.h" |
| #include "src/codegen/interface-descriptors-inl.h" |
| #include "src/codegen/macro-assembler.h" |
| #include "src/codegen/register-configuration.h" |
| #include "src/codegen/register.h" |
| #include "src/debug/debug.h" |
| #include "src/deoptimizer/deoptimizer.h" |
| #include "src/execution/frames-inl.h" |
| #include "src/heap/mutable-page.h" |
| #include "src/init/bootstrapper.h" |
| #include "src/logging/counters.h" |
| #include "src/runtime/runtime.h" |
| #include "src/snapshot/snapshot.h" |
| |
| // Satisfy cpplint check, but don't include platform-specific header. It is |
| // included recursively via macro-assembler.h. |
| #if 0 |
| #include "src/codegen/ppc/macro-assembler-ppc.h" |
| #endif |
| |
| #define __ ACCESS_MASM(masm) |
| |
| namespace v8 { |
| namespace internal { |
| |
| namespace { |
| |
| // Simd and Floating Pointer registers are not shared. For WebAssembly we save |
| // both registers, If we are not running Wasm, we can get away with only saving |
| // FP registers. |
| #if V8_ENABLE_WEBASSEMBLY |
| constexpr int kStackSavedSavedFPSizeInBytes = |
| (kNumCallerSavedDoubles * kSimd128Size) + |
| (kNumCallerSavedDoubles * kDoubleSize); |
| #else |
| constexpr int kStackSavedSavedFPSizeInBytes = |
| kNumCallerSavedDoubles * kDoubleSize; |
| #endif // V8_ENABLE_WEBASSEMBLY |
| |
| } // namespace |
| |
| int MacroAssembler::RequiredStackSizeForCallerSaved(SaveFPRegsMode fp_mode, |
| Register exclusion1, |
| Register exclusion2, |
| Register exclusion3) const { |
| int bytes = 0; |
| |
| RegList exclusions = {exclusion1, exclusion2, exclusion3}; |
| RegList list = kJSCallerSaved - exclusions; |
| bytes += list.Count() * kSystemPointerSize; |
| |
| if (fp_mode == SaveFPRegsMode::kSave) { |
| bytes += kStackSavedSavedFPSizeInBytes; |
| } |
| |
| return bytes; |
| } |
| |
| int MacroAssembler::PushCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1, |
| Register exclusion2, Register exclusion3) { |
| int bytes = 0; |
| |
| RegList exclusions = {exclusion1, exclusion2, exclusion3}; |
| RegList list = kJSCallerSaved - exclusions; |
| MultiPush(list); |
| bytes += list.Count() * kSystemPointerSize; |
| |
| if (fp_mode == SaveFPRegsMode::kSave) { |
| MultiPushF64AndV128(kCallerSavedDoubles, kCallerSavedSimd128s); |
| bytes += kStackSavedSavedFPSizeInBytes; |
| } |
| |
| return bytes; |
| } |
| |
| int MacroAssembler::PopCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1, |
| Register exclusion2, Register exclusion3) { |
| int bytes = 0; |
| if (fp_mode == SaveFPRegsMode::kSave) { |
| MultiPopF64AndV128(kCallerSavedDoubles, kCallerSavedSimd128s); |
| bytes += kStackSavedSavedFPSizeInBytes; |
| } |
| |
| RegList exclusions = {exclusion1, exclusion2, exclusion3}; |
| RegList list = kJSCallerSaved - exclusions; |
| MultiPop(list); |
| bytes += list.Count() * kSystemPointerSize; |
| |
| return bytes; |
| } |
| |
| void MacroAssembler::GetLabelAddress(Register dest, Label* target) { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| BlockTrampolinePoolScope block_trampoline_pool(this); |
| |
| // This should be just a |
| // add(dest, pc, branch_offset(target)); |
| // but current implementation of Assembler::bind_to()/target_at_put() add |
| // (InstructionStream::kHeaderSize - kHeapObjectTag) to a position of a label |
| // in a "linked" state and thus making it usable only for mov_label_offset(). |
| // TODO(ishell): fix branch_offset() and re-implement |
| // regexp::RegExpMacroAssemblerARM::PushBacktrack() without |
| // mov_label_offset(). |
| mov_label_offset(dest, target); |
| // mov_label_offset computes offset of the |target| relative to the "current |
| // InstructionStream object pointer" which is essentially pc_offset() of the |
| // label added with (InstructionStream::kHeaderSize - kHeapObjectTag). |
| // Compute "current InstructionStream object pointer" and add it to the |
| // offset in |lr| register. |
| int current_instr_code_object_relative_offset = |
| pc_offset() + kPcLoadDelta + |
| (InstructionStream::kHeaderSize - kHeapObjectTag); |
| LoadPC(scratch); |
| AddS64(dest, scratch, dest); |
| // TODO(miladfarca): Use subi once scratch register usage is refactored |
| // and r0 can't be used as scratch or dest. |
| mov(scratch, Operand(current_instr_code_object_relative_offset)); |
| SubS64(dest, dest, scratch); |
| } |
| |
| void MacroAssembler::Jump(Register target) { |
| mtctr(target); |
| bctr(); |
| } |
| |
| void MacroAssembler::LoadFromConstantsTable(Register destination, |
| int constant_index) { |
| DCHECK(RootsTable::IsImmortalImmovable(RootIndex::kBuiltinsConstantsTable)); |
| |
| DCHECK_NE(destination, r0); |
| LoadRoot(destination, RootIndex::kBuiltinsConstantsTable); |
| LoadTaggedField(destination, |
| FieldMemOperand(destination, FixedArray::OffsetOfElementAt( |
| constant_index))); |
| } |
| |
| void MacroAssembler::LoadRootRelative(Register destination, int32_t offset) { |
| LoadU64(destination, MemOperand(kRootRegister, offset)); |
| } |
| |
| void MacroAssembler::StoreRootRelative(int32_t offset, Register value) { |
| StoreU64(value, MemOperand(kRootRegister, offset)); |
| } |
| |
| void MacroAssembler::LoadRootRegisterOffset(Register destination, |
| intptr_t offset) { |
| if (offset == 0) { |
| mr(destination, kRootRegister); |
| } else { |
| AddS64(destination, kRootRegister, Operand(offset)); |
| } |
| } |
| |
| MemOperand MacroAssembler::ExternalReferenceAsOperand( |
| ExternalReference reference, Register scratch) { |
| if (root_array_available()) { |
| if (reference.IsIsolateFieldId()) { |
| return MemOperand(kRootRegister, reference.offset_from_root_register()); |
| } |
| if (options().enable_root_relative_access) { |
| intptr_t offset = |
| RootRegisterOffsetForExternalReference(isolate(), reference); |
| if (is_int32(offset)) { |
| return MemOperand(kRootRegister, static_cast<int32_t>(offset)); |
| } |
| } |
| if (options().isolate_independent_code) { |
| if (IsAddressableThroughRootRegister(isolate(), reference)) { |
| // Some external references can be efficiently loaded as an offset from |
| // kRootRegister. |
| intptr_t offset = |
| RootRegisterOffsetForExternalReference(isolate(), reference); |
| CHECK(is_int32(offset)); |
| return MemOperand(kRootRegister, static_cast<int32_t>(offset)); |
| } else { |
| // Otherwise, do a memory load from the external reference table. |
| LoadU64(scratch, |
| MemOperand(kRootRegister, |
| RootRegisterOffsetForExternalReferenceTableEntry( |
| isolate(), reference))); |
| return MemOperand(scratch, 0); |
| } |
| } |
| } |
| Move(scratch, reference); |
| return MemOperand(scratch, 0); |
| } |
| |
| void MacroAssembler::Jump(intptr_t target, RelocInfo::Mode rmode, |
| Condition cond, CRegister cr) { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| Label skip; |
| |
| if (cond != al) b(NegateCondition(cond), &skip, cr); |
| |
| mov(scratch, Operand(target, rmode)); |
| mtctr(scratch); |
| bctr(); |
| |
| bind(&skip); |
| } |
| |
| void MacroAssembler::Jump(Address target, RelocInfo::Mode rmode, Condition cond, |
| CRegister cr) { |
| DCHECK(!RelocInfo::IsCodeTarget(rmode)); |
| Jump(static_cast<intptr_t>(target), rmode, cond, cr); |
| } |
| |
| void MacroAssembler::Jump(Handle<Code> code, RelocInfo::Mode rmode, |
| Condition cond, CRegister cr) { |
| DCHECK(RelocInfo::IsCodeTarget(rmode)); |
| DCHECK_IMPLIES(options().isolate_independent_code, |
| Builtins::IsIsolateIndependentBuiltin(*code)); |
| |
| Builtin builtin = Builtin::kNoBuiltinId; |
| if (isolate()->builtins()->IsBuiltinHandle(code, &builtin)) { |
| TailCallBuiltin(builtin, cond, cr); |
| return; |
| } |
| int32_t target_index = AddCodeTarget(code); |
| Jump(static_cast<intptr_t>(target_index), rmode, cond, cr); |
| } |
| |
| void MacroAssembler::Jump(const ExternalReference& reference) { |
| // ELFv2 ABI requires r12 to hold the callee address at the global entry |
| // point so the function can compute its TOC pointer. Use ip (r12) |
| // directly instead of acquiring a scratch register. |
| Move(ip, reference); |
| if (ABI_USES_FUNCTION_DESCRIPTORS) { |
| // AIX uses a function descriptor. When calling C code be |
| // aware of this descriptor and pick up values from it. |
| LoadU64(ToRegister(ABI_TOC_REGISTER), MemOperand(ip, kSystemPointerSize)); |
| LoadU64(ip, MemOperand(ip, 0)); |
| } |
| Jump(ip); |
| } |
| |
| void MacroAssembler::Call(Register target) { |
| BlockTrampolinePoolScope block_trampoline_pool(this); |
| // branch via link register and set LK bit for return point |
| mtctr(target); |
| bctrl(); |
| RecordPcForSafepoint(); |
| } |
| |
| void MacroAssembler::CallJSEntry(Register target) { |
| CHECK(target == r5); |
| Call(target); |
| } |
| |
| int MacroAssembler::CallSizeNotPredictableCodeSize(Address target, |
| RelocInfo::Mode rmode, |
| Condition cond) { |
| return (2 + kMovInstructionsNoConstantPool) * kInstrSize; |
| } |
| |
| void MacroAssembler::Call(Address target, RelocInfo::Mode rmode, |
| Condition cond) { |
| BlockTrampolinePoolScope block_trampoline_pool(this); |
| DCHECK(cond == al); |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| |
| // This can likely be optimized to make use of bc() with 24bit relative |
| // |
| // RecordRelocInfo(x.rmode_, x.immediate); |
| // bc( BA, .... offset, LKset); |
| // |
| |
| mov(scratch, Operand(target, rmode)); |
| mtctr(scratch); |
| bctrl(); |
| RecordPcForSafepoint(); |
| } |
| |
| void MacroAssembler::Call(Handle<Code> code, RelocInfo::Mode rmode, |
| Condition cond) { |
| BlockTrampolinePoolScope block_trampoline_pool(this); |
| DCHECK(RelocInfo::IsCodeTarget(rmode)); |
| DCHECK_IMPLIES(options().isolate_independent_code, |
| Builtins::IsIsolateIndependentBuiltin(*code)); |
| |
| Builtin builtin = Builtin::kNoBuiltinId; |
| if (isolate()->builtins()->IsBuiltinHandle(code, &builtin)) { |
| CallBuiltin(builtin, cond); |
| return; |
| } |
| int32_t target_index = AddCodeTarget(code); |
| Call(static_cast<Address>(target_index), rmode, cond); |
| } |
| |
| void MacroAssembler::CallBuiltin(Builtin builtin, Condition cond) { |
| ASM_CODE_COMMENT_STRING(this, CommentForOffHeapTrampoline("call", builtin)); |
| switch (options().builtin_call_jump_mode) { |
| case BuiltinCallJumpMode::kAbsolute: { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| Label skip; |
| mov(scratch, Operand(BuiltinEntry(builtin), RelocInfo::OFF_HEAP_TARGET)); |
| if (cond != al) b(NegateCondition(cond), &skip); |
| Call(scratch); |
| bind(&skip); |
| break; |
| } |
| case BuiltinCallJumpMode::kPCRelative: |
| UNREACHABLE(); |
| case BuiltinCallJumpMode::kIndirect: { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| Label skip; |
| LoadU64(scratch, EntryFromBuiltinAsOperand(builtin)); |
| if (cond != al) b(NegateCondition(cond), &skip); |
| Call(scratch); |
| bind(&skip); |
| break; |
| } |
| case BuiltinCallJumpMode::kForMksnapshot: { |
| if (options().use_pc_relative_calls_and_jumps_for_mksnapshot) { |
| Handle<Code> code = isolate()->builtins()->code_handle(builtin); |
| int32_t code_target_index = AddCodeTarget(code); |
| Call(static_cast<Address>(code_target_index), RelocInfo::CODE_TARGET, |
| cond); |
| } else { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| Label skip; |
| LoadU64(scratch, EntryFromBuiltinAsOperand(builtin)); |
| if (cond != al) b(NegateCondition(cond), &skip); |
| Call(scratch); |
| bind(&skip); |
| } |
| break; |
| } |
| } |
| } |
| |
| void MacroAssembler::TailCallBuiltin(Builtin builtin, Condition cond, |
| CRegister cr) { |
| ASM_CODE_COMMENT_STRING(this, |
| CommentForOffHeapTrampoline("tail call", builtin)); |
| switch (options().builtin_call_jump_mode) { |
| case BuiltinCallJumpMode::kAbsolute: { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| Label skip; |
| mov(scratch, Operand(BuiltinEntry(builtin), RelocInfo::OFF_HEAP_TARGET)); |
| if (cond != al) b(NegateCondition(cond), &skip, cr); |
| Jump(scratch); |
| bind(&skip); |
| break; |
| } |
| case BuiltinCallJumpMode::kPCRelative: |
| UNREACHABLE(); |
| case BuiltinCallJumpMode::kIndirect: { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| Label skip; |
| LoadU64(scratch, EntryFromBuiltinAsOperand(builtin)); |
| if (cond != al) b(NegateCondition(cond), &skip, cr); |
| Jump(scratch); |
| bind(&skip); |
| break; |
| } |
| case BuiltinCallJumpMode::kForMksnapshot: { |
| if (options().use_pc_relative_calls_and_jumps_for_mksnapshot) { |
| Handle<Code> code = isolate()->builtins()->code_handle(builtin); |
| int32_t code_target_index = AddCodeTarget(code); |
| Jump(static_cast<intptr_t>(code_target_index), RelocInfo::CODE_TARGET, |
| cond, cr); |
| } else { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| Label skip; |
| LoadU64(scratch, EntryFromBuiltinAsOperand(builtin)); |
| if (cond != al) b(NegateCondition(cond), &skip, cr); |
| Jump(scratch); |
| bind(&skip); |
| } |
| break; |
| } |
| } |
| } |
| |
| void MacroAssembler::Drop(int count) { |
| if (count > 0) { |
| AddS64(sp, sp, Operand(count * kSystemPointerSize)); |
| } |
| } |
| |
| void MacroAssembler::Drop(Register count) { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| ShiftLeftU64(scratch, count, Operand(kSystemPointerSizeLog2)); |
| add(sp, sp, scratch); |
| } |
| |
| // Enforce alignment of sp. |
| void MacroAssembler::EnforceStackAlignment() { |
| int frame_alignment = ActivationFrameAlignment(); |
| DCHECK(base::bits::IsPowerOfTwo(frame_alignment)); |
| |
| uint64_t frame_alignment_mask = ~(static_cast<uint64_t>(frame_alignment) - 1); |
| AndU64(sp, sp, Operand(frame_alignment_mask)); |
| } |
| |
| void MacroAssembler::TestCodeIsMarkedForDeoptimization(Register code) { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| LoadU32(scratch, FieldMemOperand(code, Code::kFlagsOffset)); |
| TestBit(scratch, Code::kMarkedForDeoptimizationBit); |
| } |
| |
| Operand MacroAssembler::ClearedValue() const { |
| return Operand(static_cast<int32_t>(i::kClearedWeakValue.ptr())); |
| } |
| |
| void MacroAssembler::Call(Label* target) { |
| // Keep a trampoline pool from being emitted between the branch and the |
| // safepoint record, so pc_offset_for_safepoint_ stays matched to the return |
| // address. The Register and Address overloads rely on the same scope. |
| BlockTrampolinePoolScope block_trampoline_pool(this); |
| b(target, SetLK); |
| RecordPcForSafepoint(); |
| } |
| |
| void MacroAssembler::Push(Handle<HeapObject> handle) { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| mov(scratch, Operand(handle)); |
| push(scratch); |
| } |
| |
| void MacroAssembler::Push(Tagged<Smi> smi) { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| mov(scratch, Operand(smi)); |
| push(scratch); |
| } |
| |
| void MacroAssembler::Push(Tagged<TaggedIndex> index) { |
| // TaggedIndex is the same as Smi for 32 bit archs. |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| mov(scratch, Operand(static_cast<uint32_t>(index.value()))); |
| push(scratch); |
| } |
| |
| void MacroAssembler::PushArray(Register array, Register size, Register scratch, |
| Register scratch2, PushArrayOrder order) { |
| Label loop, done; |
| |
| if (order == kNormal) { |
| cmpi(size, Operand::Zero()); |
| beq(&done); |
| ShiftLeftU64(scratch, size, Operand(kSystemPointerSizeLog2)); |
| add(scratch, array, scratch); |
| mtctr(size); |
| |
| bind(&loop); |
| LoadU64WithUpdate(scratch2, MemOperand(scratch, -kSystemPointerSize)); |
| StoreU64WithUpdate(scratch2, MemOperand(sp, -kSystemPointerSize)); |
| bdnz(&loop); |
| |
| bind(&done); |
| } else { |
| cmpi(size, Operand::Zero()); |
| beq(&done); |
| |
| mtctr(size); |
| subi(scratch, array, Operand(kSystemPointerSize)); |
| |
| bind(&loop); |
| LoadU64WithUpdate(scratch2, MemOperand(scratch, kSystemPointerSize)); |
| StoreU64WithUpdate(scratch2, MemOperand(sp, -kSystemPointerSize)); |
| bdnz(&loop); |
| bind(&done); |
| } |
| } |
| |
| void MacroAssembler::Move(Register dst, Handle<HeapObject> value, |
| RelocInfo::Mode rmode) { |
| // TODO(jgruber,v8:8887): Also consider a root-relative load when generating |
| // non-isolate-independent code. In many cases it might be cheaper than |
| // embedding the relocatable value. |
| if (root_array_available_ && options().isolate_independent_code) { |
| IndirectLoadConstant(dst, value); |
| return; |
| } else if (RelocInfo::IsCompressedEmbeddedObject(rmode)) { |
| EmbeddedObjectIndex index = AddEmbeddedObject(value); |
| DCHECK(is_uint32(index)); |
| mov(dst, Operand(static_cast<int>(index), rmode)); |
| } else { |
| DCHECK(RelocInfo::IsFullEmbeddedObject(rmode)); |
| mov(dst, Operand(value.address(), rmode)); |
| } |
| } |
| |
| void MacroAssembler::Move(Register dst, ExternalReference reference) { |
| if (root_array_available()) { |
| if (reference.IsIsolateFieldId()) { |
| AddS64(dst, kRootRegister, |
| Operand(reference.offset_from_root_register())); |
| return; |
| } |
| if (options().isolate_independent_code) { |
| IndirectLoadExternalReference(dst, reference); |
| return; |
| } |
| } |
| |
| // External references should not get created with IDs if |
| // `!root_array_available()`. |
| CHECK(!reference.IsIsolateFieldId()); |
| mov(dst, Operand(reference)); |
| } |
| |
| void MacroAssembler::LoadIsolateField(Register dst, IsolateFieldId id) { |
| Move(dst, ExternalReference::Create(id)); |
| } |
| |
| void MacroAssembler::Move(Register dst, Register src, Condition cond) { |
| DCHECK(cond == al); |
| if (dst != src) { |
| mr(dst, src); |
| } |
| } |
| |
| void MacroAssembler::Move(DoubleRegister dst, DoubleRegister src) { |
| if (dst != src) { |
| fmr(dst, src); |
| } |
| } |
| |
| void MacroAssembler::MultiPush(RegList regs, Register location) { |
| int16_t num_to_push = regs.Count(); |
| int16_t stack_offset = num_to_push * kSystemPointerSize; |
| |
| subi(location, location, Operand(stack_offset)); |
| for (int16_t i = Register::kNumRegisters - 1; i >= 0; i--) { |
| if ((regs.bits() & (1 << i)) != 0) { |
| stack_offset -= kSystemPointerSize; |
| StoreU64(ToRegister(i), MemOperand(location, stack_offset)); |
| } |
| } |
| } |
| |
| void MacroAssembler::MultiPop(RegList regs, Register location) { |
| int16_t stack_offset = 0; |
| |
| for (int16_t i = 0; i < Register::kNumRegisters; i++) { |
| if ((regs.bits() & (1 << i)) != 0) { |
| LoadU64(ToRegister(i), MemOperand(location, stack_offset)); |
| stack_offset += kSystemPointerSize; |
| } |
| } |
| addi(location, location, Operand(stack_offset)); |
| } |
| |
| void MacroAssembler::MultiPushDoubles(DoubleRegList dregs, Register location) { |
| int16_t num_to_push = dregs.Count(); |
| int16_t stack_offset = num_to_push * kDoubleSize; |
| |
| subi(location, location, Operand(stack_offset)); |
| for (int16_t i = DoubleRegister::kNumRegisters - 1; i >= 0; i--) { |
| if ((dregs.bits() & (1 << i)) != 0) { |
| DoubleRegister dreg = DoubleRegister::from_code(i); |
| stack_offset -= kDoubleSize; |
| stfd(dreg, MemOperand(location, stack_offset)); |
| } |
| } |
| } |
| |
| void MacroAssembler::MultiPushV128(Simd128RegList simd_regs, |
| Register location) { |
| int16_t num_to_push = simd_regs.Count(); |
| int16_t stack_offset = num_to_push * kSimd128Size; |
| |
| subi(location, location, Operand(stack_offset)); |
| for (int16_t i = Simd128Register::kNumRegisters - 1; i >= 0; i--) { |
| if ((simd_regs.bits() & (1 << i)) != 0) { |
| Simd128Register simd_reg = Simd128Register::from_code(i); |
| stack_offset -= kSimd128Size; |
| StoreSimd128(simd_reg, MemOperand(location, stack_offset)); |
| } |
| } |
| } |
| |
| void MacroAssembler::MultiPopDoubles(DoubleRegList dregs, Register location) { |
| int16_t stack_offset = 0; |
| |
| for (int16_t i = 0; i < DoubleRegister::kNumRegisters; i++) { |
| if ((dregs.bits() & (1 << i)) != 0) { |
| DoubleRegister dreg = DoubleRegister::from_code(i); |
| lfd(dreg, MemOperand(location, stack_offset)); |
| stack_offset += kDoubleSize; |
| } |
| } |
| addi(location, location, Operand(stack_offset)); |
| } |
| |
| void MacroAssembler::MultiPopV128(Simd128RegList simd_regs, Register location) { |
| int16_t stack_offset = 0; |
| |
| for (int16_t i = 0; i < Simd128Register::kNumRegisters; i++) { |
| if ((simd_regs.bits() & (1 << i)) != 0) { |
| Simd128Register simd_reg = Simd128Register::from_code(i); |
| LoadSimd128(simd_reg, MemOperand(location, stack_offset)); |
| stack_offset += kSimd128Size; |
| } |
| } |
| addi(location, location, Operand(stack_offset)); |
| } |
| |
| void MacroAssembler::MultiPushF64AndV128(DoubleRegList dregs, |
| Simd128RegList simd_regs, |
| Register location) { |
| MultiPushDoubles(dregs, location); |
| #if V8_ENABLE_WEBASSEMBLY |
| MultiPushV128(simd_regs, location); |
| #endif |
| } |
| |
| void MacroAssembler::MultiPopF64AndV128(DoubleRegList dregs, |
| Simd128RegList simd_regs, |
| Register location) { |
| #if V8_ENABLE_WEBASSEMBLY |
| MultiPopV128(simd_regs, location); |
| #endif |
| MultiPopDoubles(dregs, location); |
| } |
| |
| void MacroAssembler::PushAll(RegList registers) { |
| if (registers.is_empty()) return; |
| ASM_CODE_COMMENT(this); |
| // TODO(victorgomes): {stm/ldm} pushes/pops registers in the opposite order |
| // as expected by Maglev frame. Consider massaging Maglev to accept this |
| // order instead. |
| // Can not use MultiPush(registers, sp) due to orders |
| for (Register reg : registers) { |
| Push(reg); |
| } |
| } |
| |
| void MacroAssembler::PopAll(RegList registers) { |
| if (registers.is_empty()) return; |
| ASM_CODE_COMMENT(this); |
| // Can not use MultiPop(registers, sp); |
| for (Register reg : base::Reversed(registers)) { |
| Pop(reg); |
| } |
| } |
| |
| void MacroAssembler::PushAll(DoubleRegList registers, int stack_slot_size) { |
| if (registers.is_empty()) return; |
| ASM_CODE_COMMENT(this); |
| MultiPushDoubles(registers, sp); |
| } |
| |
| void MacroAssembler::PopAll(DoubleRegList registers, int stack_slot_size) { |
| if (registers.is_empty()) return; |
| ASM_CODE_COMMENT(this); |
| MultiPopDoubles(registers, sp); |
| } |
| |
| void MacroAssembler::LoadTaggedRoot(Register destination, RootIndex index) { |
| ASM_CODE_COMMENT(this); |
| if (CanBeImmediate(index)) { |
| mov(destination, Operand(ReadOnlyRootPtr(index), RelocInfo::Mode::NO_INFO)); |
| return; |
| } |
| LoadRoot(destination, index); |
| } |
| |
| void MacroAssembler::LoadRoot(Register destination, RootIndex index, |
| Condition cond) { |
| DCHECK(cond == al); |
| if (CanBeImmediate(index)) { |
| DecompressTagged(destination, ReadOnlyRootPtr(index)); |
| return; |
| } |
| LoadU64(destination, |
| MemOperand(kRootRegister, RootRegisterOffsetForRootIndex(index))); |
| } |
| |
| void MacroAssembler::LoadTaggedField(const Register& destination, |
| const MemOperand& field_operand) { |
| ASM_CODE_COMMENT(this); |
| if (COMPRESS_POINTERS_BOOL) { |
| DecompressTagged(destination, field_operand); |
| } else { |
| LoadU64(destination, field_operand); |
| } |
| } |
| |
| void MacroAssembler::LoadTaggedFieldWithoutDecompressing( |
| const Register& destination, const MemOperand& field_operand) { |
| if (COMPRESS_POINTERS_BOOL) { |
| LoadU32(destination, field_operand); |
| } else { |
| LoadU64(destination, field_operand); |
| } |
| } |
| |
| void MacroAssembler::SmiUntag(Register dst, const MemOperand& src, RCBit rc) { |
| if (SmiValuesAre31Bits()) { |
| LoadU32(dst, src); |
| } else { |
| LoadU64(dst, src); |
| } |
| |
| SmiUntag(dst, rc); |
| } |
| |
| void MacroAssembler::SmiUntagField(Register dst, const MemOperand& src, |
| RCBit rc) { |
| SmiUntag(dst, src, rc); |
| } |
| |
| void MacroAssembler::StoreTaggedField(const Register& value, |
| const MemOperand& dst_field_operand) { |
| if (COMPRESS_POINTERS_BOOL) { |
| RecordComment("[ StoreTagged"); |
| StoreU32(value, dst_field_operand); |
| RecordComment("]"); |
| } else { |
| StoreU64(value, dst_field_operand); |
| } |
| } |
| |
| void MacroAssembler::DecompressTaggedSigned(Register destination, |
| Register src) { |
| RecordComment("[ DecompressTaggedSigned"); |
| ZeroExtWord32(destination, src); |
| RecordComment("]"); |
| } |
| |
| void MacroAssembler::DecompressTaggedSigned(Register destination, |
| MemOperand field_operand) { |
| RecordComment("[ DecompressTaggedSigned"); |
| LoadU32(destination, field_operand); |
| RecordComment("]"); |
| } |
| |
| void MacroAssembler::DecompressTagged(Register destination, Register source) { |
| RecordComment("[ DecompressTagged"); |
| ZeroExtWord32(destination, source); |
| add(destination, destination, kPtrComprCageBaseRegister); |
| RecordComment("]"); |
| } |
| |
| void MacroAssembler::DecompressTagged(Register destination, |
| MemOperand field_operand) { |
| RecordComment("[ DecompressTagged"); |
| LoadU32(destination, field_operand); |
| add(destination, destination, kPtrComprCageBaseRegister); |
| RecordComment("]"); |
| } |
| |
| void MacroAssembler::DecompressTagged(const Register& destination, |
| Tagged_t immediate) { |
| ASM_CODE_COMMENT(this); |
| AddS64(destination, kPtrComprCageBaseRegister, |
| Operand(immediate, RelocInfo::Mode::NO_INFO)); |
| } |
| |
| void MacroAssembler::LoadTaggedSignedField(Register destination, |
| MemOperand field_operand) { |
| if (COMPRESS_POINTERS_BOOL) { |
| DecompressTaggedSigned(destination, field_operand); |
| } else { |
| LoadU64(destination, field_operand); |
| } |
| } |
| |
| void MacroAssembler::RecordWriteField(Register object, int offset, |
| Register value, Register slot_address, |
| LinkRegisterStatus lr_status, |
| SaveFPRegsMode save_fp, |
| SmiCheck smi_check) { |
| // First, check if a write barrier is even needed. The tests below |
| // catch stores of Smis. |
| Label done; |
| |
| // Skip barrier if writing a smi. |
| if (smi_check == SmiCheck::kInline) { |
| JumpIfSmi(value, &done); |
| } |
| |
| // Although the object register is tagged, the offset is relative to the start |
| // of the object, so so offset must be a multiple of kSystemPointerSize. |
| DCHECK(IsAligned(offset, kTaggedSize)); |
| |
| AddS64(slot_address, object, Operand(offset - kHeapObjectTag)); |
| if (v8_flags.slow_debug_code) { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| Label ok; |
| andi(scratch, slot_address, Operand(kTaggedSize - 1)); |
| beq(&ok, cr0); |
| stop(); |
| bind(&ok); |
| } |
| |
| RecordWrite(object, slot_address, value, lr_status, save_fp, SmiCheck::kOmit); |
| |
| bind(&done); |
| |
| // Clobber clobbered input registers when running with the debug-code flag |
| // turned on to provoke errors. |
| if (v8_flags.slow_debug_code) { |
| mov(value, Operand(base::bit_cast<intptr_t>(kZapValue + 4))); |
| mov(slot_address, Operand(base::bit_cast<intptr_t>(kZapValue + 8))); |
| } |
| } |
| |
| void MacroAssembler::Zero(const MemOperand& dest) { |
| ASM_CODE_COMMENT(this); |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| |
| mov(scratch, Operand::Zero()); |
| StoreU64(scratch, dest); |
| } |
| |
| void MacroAssembler::Zero(const MemOperand& dest1, const MemOperand& dest2) { |
| ASM_CODE_COMMENT(this); |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| |
| mov(scratch, Operand::Zero()); |
| StoreU64(scratch, dest1); |
| StoreU64(scratch, dest2); |
| } |
| |
| void MacroAssembler::MaybeSaveRegisters(RegList registers) { |
| if (registers.is_empty()) return; |
| MultiPush(registers); |
| } |
| |
| void MacroAssembler::MaybeRestoreRegisters(RegList registers) { |
| if (registers.is_empty()) return; |
| MultiPop(registers); |
| } |
| |
| void MacroAssembler::CallEphemeronKeyBarrier(Register object, |
| Register slot_address, |
| SaveFPRegsMode fp_mode) { |
| DCHECK(!AreAliased(object, slot_address)); |
| RegList registers = |
| WriteBarrierDescriptor::ComputeSavedRegisters(object, slot_address); |
| MaybeSaveRegisters(registers); |
| |
| Register object_parameter = WriteBarrierDescriptor::ObjectRegister(); |
| Register slot_address_parameter = |
| WriteBarrierDescriptor::SlotAddressRegister(); |
| |
| push(object); |
| push(slot_address); |
| pop(slot_address_parameter); |
| pop(object_parameter); |
| |
| CallBuiltin(Builtins::EphemeronKeyBarrier(fp_mode)); |
| MaybeRestoreRegisters(registers); |
| } |
| |
| void MacroAssembler::CallRecordWriteStubSaveRegisters(Register object, |
| Register slot_address, |
| SaveFPRegsMode fp_mode, |
| StubCallMode mode) { |
| DCHECK(!AreAliased(object, slot_address)); |
| RegList registers = |
| WriteBarrierDescriptor::ComputeSavedRegisters(object, slot_address); |
| MaybeSaveRegisters(registers); |
| |
| Register object_parameter = WriteBarrierDescriptor::ObjectRegister(); |
| Register slot_address_parameter = |
| WriteBarrierDescriptor::SlotAddressRegister(); |
| |
| push(object); |
| push(slot_address); |
| pop(slot_address_parameter); |
| pop(object_parameter); |
| |
| CallRecordWriteStub(object_parameter, slot_address_parameter, fp_mode, mode); |
| |
| MaybeRestoreRegisters(registers); |
| } |
| |
| void MacroAssembler::CallRecordWriteStub(Register object, Register slot_address, |
| SaveFPRegsMode fp_mode, |
| StubCallMode mode) { |
| // Use CallRecordWriteStubSaveRegisters if the object and slot registers |
| // need to be caller saved. |
| DCHECK_EQ(WriteBarrierDescriptor::ObjectRegister(), object); |
| DCHECK_EQ(WriteBarrierDescriptor::SlotAddressRegister(), slot_address); |
| #if V8_ENABLE_WEBASSEMBLY |
| if (mode == StubCallMode::kCallWasmRuntimeStub) { |
| // Use {near_call} for direct Wasm call within a module. |
| auto wasm_target = |
| static_cast<Address>(wasm::WasmCode::GetRecordWriteBuiltin(fp_mode)); |
| Call(wasm_target, RelocInfo::WASM_STUB_CALL); |
| #else |
| if (false) { |
| #endif |
| } else { |
| CallBuiltin(Builtins::RecordWrite(fp_mode), al); |
| } |
| } |
| |
| void MacroAssembler::CallVerifySkippedWriteBarrierStubSaveRegisters( |
| Register object, Register value, SaveFPRegsMode fp_mode) { |
| ASM_CODE_COMMENT(this); |
| PushCallerSaved(fp_mode); |
| CallVerifySkippedWriteBarrierStub(object, value); |
| PopCallerSaved(fp_mode); |
| } |
| |
| void MacroAssembler::CallVerifySkippedWriteBarrierStub(Register object, |
| Register value) { |
| ASM_CODE_COMMENT(this); |
| push(value); |
| push(object); |
| pop(kCArgRegs[0]); |
| pop(kCArgRegs[1]); |
| PrepareCallCFunction(2); |
| CallCFunction(ExternalReference::verify_skipped_write_barrier(), 2); |
| } |
| |
| // Will clobber 4 registers: object, address, scratch, ip. The |
| // register 'object' contains a heap object pointer. The heap object |
| // tag is shifted away. |
| void MacroAssembler::RecordWrite(Register object, Register slot_address, |
| Register value, LinkRegisterStatus lr_status, |
| SaveFPRegsMode fp_mode, SmiCheck smi_check) { |
| DCHECK(!AreAliased(object, value, slot_address)); |
| if (v8_flags.slow_debug_code) { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| LoadTaggedField(scratch, MemOperand(slot_address)); |
| CmpS64(scratch, value); |
| Check(eq, AbortReason::kWrongAddressOrValuePassedToRecordWrite); |
| } |
| |
| if (v8_flags.disable_write_barriers) { |
| return; |
| } |
| |
| // First, check if a write barrier is even needed. The tests below |
| // catch stores of smis and stores into the young generation. |
| Label done; |
| |
| if (smi_check == SmiCheck::kInline) { |
| JumpIfSmi(value, &done); |
| } |
| |
| CheckPageFlag(value, |
| value, // Used as scratch. |
| MemoryChunk::kPointersToHereAreInterestingMask, eq, &done); |
| CheckPageFlag(object, |
| value, // Used as scratch. |
| MemoryChunk::kPointersFromHereAreInterestingMask, eq, &done); |
| |
| // Record the actual write. |
| if (lr_status == kLRHasNotBeenSaved) { |
| PushLR(); |
| } |
| CallRecordWriteStubSaveRegisters(object, slot_address, fp_mode); |
| if (lr_status == kLRHasNotBeenSaved) { |
| PopLR(); |
| } |
| |
| if (v8_flags.slow_debug_code) mov(slot_address, Operand(kZapValue)); |
| |
| bind(&done); |
| |
| // Clobber clobbered registers when running with the debug-code flag |
| // turned on to provoke errors. |
| if (v8_flags.slow_debug_code) { |
| mov(slot_address, Operand(base::bit_cast<intptr_t>(kZapValue + 12))); |
| mov(value, Operand(base::bit_cast<intptr_t>(kZapValue + 16))); |
| } |
| } |
| |
| void MacroAssembler::PushLR(Register scratch) { |
| UseScratchRegisterScope temps(this); |
| if (scratch == no_reg) { |
| scratch = temps.Acquire(); |
| } |
| mflr(scratch); |
| push(scratch); |
| } |
| |
| void MacroAssembler::PopLR(Register scratch) { |
| UseScratchRegisterScope temps(this); |
| if (scratch == no_reg) { |
| scratch = temps.Acquire(); |
| } |
| pop(scratch); |
| mtlr(scratch); |
| } |
| |
| void MacroAssembler::PushCommonFrame(Register marker_reg) { |
| int fp_delta = 0; |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| mflr(scratch); |
| if (V8_EMBEDDED_CONSTANT_POOL_BOOL) { |
| if (marker_reg.is_valid()) { |
| Push(scratch, fp, kConstantPoolRegister, marker_reg); |
| fp_delta = 2; |
| } else { |
| Push(scratch, fp, kConstantPoolRegister); |
| fp_delta = 1; |
| } |
| } else { |
| if (marker_reg.is_valid()) { |
| Push(scratch, fp, marker_reg); |
| fp_delta = 1; |
| } else { |
| Push(scratch, fp); |
| fp_delta = 0; |
| } |
| } |
| addi(fp, sp, Operand(fp_delta * kSystemPointerSize)); |
| } |
| |
| void MacroAssembler::PushStandardFrame(Register function_reg) { |
| int fp_delta = 0; |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| mflr(scratch); |
| if (V8_EMBEDDED_CONSTANT_POOL_BOOL) { |
| if (function_reg.is_valid()) { |
| Push(scratch, fp, kConstantPoolRegister, cp, function_reg); |
| fp_delta = 3; |
| } else { |
| Push(scratch, fp, kConstantPoolRegister, cp); |
| fp_delta = 2; |
| } |
| } else { |
| if (function_reg.is_valid()) { |
| Push(scratch, fp, cp, function_reg); |
| fp_delta = 2; |
| } else { |
| Push(scratch, fp, cp); |
| fp_delta = 1; |
| } |
| } |
| addi(fp, sp, Operand(fp_delta * kSystemPointerSize)); |
| Push(kJavaScriptCallArgCountRegister); |
| } |
| |
| void MacroAssembler::RestoreFrameStateForTailCall() { |
| if (V8_EMBEDDED_CONSTANT_POOL_BOOL) { |
| LoadU64(kConstantPoolRegister, |
| MemOperand(fp, StandardFrameConstants::kConstantPoolOffset)); |
| set_constant_pool_available(false); |
| } |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| LoadU64(scratch, MemOperand(fp, StandardFrameConstants::kCallerPCOffset)); |
| LoadU64(fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); |
| mtlr(scratch); |
| } |
| |
| void MacroAssembler::CanonicalizeNaN(const DoubleRegister dst, |
| const DoubleRegister src) { |
| // Turn potential sNaN into qNaN. |
| LoadDoubleLiteral(kDoubleRegZero, base::Double(0.0)); |
| fsub(dst, src, kDoubleRegZero); |
| } |
| |
| void MacroAssembler::ConvertIntToDouble(Register src, DoubleRegister dst) { |
| MovIntToDouble(dst, src); |
| fcfid(dst, dst); |
| } |
| |
| void MacroAssembler::ConvertUnsignedIntToDouble(Register src, |
| DoubleRegister dst) { |
| MovUnsignedIntToDouble(dst, src); |
| fcfid(dst, dst); |
| } |
| |
| void MacroAssembler::ConvertIntToFloat(Register src, DoubleRegister dst) { |
| MovIntToDouble(dst, src); |
| fcfids(dst, dst); |
| } |
| |
| void MacroAssembler::ConvertUnsignedIntToFloat(Register src, |
| DoubleRegister dst) { |
| MovUnsignedIntToDouble(dst, src); |
| fcfids(dst, dst); |
| } |
| |
| void MacroAssembler::ConvertInt64ToDouble(Register src, |
| DoubleRegister double_dst) { |
| MovInt64ToDouble(double_dst, src); |
| fcfid(double_dst, double_dst); |
| } |
| |
| void MacroAssembler::ConvertUnsignedInt64ToFloat(Register src, |
| DoubleRegister double_dst) { |
| MovInt64ToDouble(double_dst, src); |
| fcfidus(double_dst, double_dst); |
| } |
| |
| void MacroAssembler::ConvertUnsignedInt64ToDouble(Register src, |
| DoubleRegister double_dst) { |
| MovInt64ToDouble(double_dst, src); |
| fcfidu(double_dst, double_dst); |
| } |
| |
| void MacroAssembler::ConvertInt64ToFloat(Register src, |
| DoubleRegister double_dst) { |
| MovInt64ToDouble(double_dst, src); |
| fcfids(double_dst, double_dst); |
| } |
| |
| void MacroAssembler::ConvertDoubleToInt64(const DoubleRegister double_input, |
| const Register dst, |
| const DoubleRegister double_dst, |
| FPRoundingMode rounding_mode) { |
| if (rounding_mode == kRoundToZero) { |
| fctidz(double_dst, double_input); |
| } else { |
| SetRoundingMode(rounding_mode); |
| fctid(double_dst, double_input); |
| ResetRoundingMode(); |
| } |
| |
| MovDoubleToInt64( |
| dst, double_dst); |
| } |
| |
| void MacroAssembler::ConvertDoubleToUnsignedInt64( |
| const DoubleRegister double_input, const Register dst, |
| const DoubleRegister double_dst, FPRoundingMode rounding_mode) { |
| if (rounding_mode == kRoundToZero) { |
| fctiduz(double_dst, double_input); |
| } else { |
| SetRoundingMode(rounding_mode); |
| fctidu(double_dst, double_input); |
| ResetRoundingMode(); |
| } |
| |
| MovDoubleToInt64(dst, double_dst); |
| } |
| |
| void MacroAssembler::LoadConstantPoolPointerRegisterFromCodeTargetAddress( |
| Register code_target_address) { |
| // Builtins do not use the constant pool (see is_constant_pool_available). |
| static_assert(InstructionStream::kOnHeapBodyIsContiguous); |
| |
| UseScratchRegisterScope temps(this); |
| Register scratch1 = temps.Acquire(); |
| Register scratch2 = temps.Acquire(); |
| LoadU64(scratch2, |
| FieldMemOperand(code_target_address, Code::kInstructionStartOffset)); |
| LoadU32(scratch1, |
| FieldMemOperand(code_target_address, Code::kInstructionSizeOffset)); |
| add(scratch2, scratch1, scratch2); |
| LoadU32( |
| kConstantPoolRegister, |
| FieldMemOperand(code_target_address, Code::kConstantPoolOffsetOffset)); |
| add(kConstantPoolRegister, scratch2, kConstantPoolRegister); |
| } |
| |
| void MacroAssembler::LoadPC(Register dst) { |
| addpcis(dst, Operand(0)); |
| } |
| |
| void MacroAssembler::ComputeCodeStartAddress(Register dst) { |
| LoadPC(dst); |
| subi(dst, dst, Operand(pc_offset())); |
| } |
| |
| void MacroAssembler::LoadConstantPoolPointerRegister() { |
| // |
| // Builtins do not use the constant pool (see is_constant_pool_available). |
| static_assert(InstructionStream::kOnHeapBodyIsContiguous); |
| |
| LoadPC(kConstantPoolRegister); |
| int32_t delta = -pc_offset(); |
| add_label_offset(kConstantPoolRegister, kConstantPoolRegister, |
| ConstantPoolPosition(), delta); |
| } |
| |
| void MacroAssembler::StubPrologue(StackFrame::Type type) { |
| { |
| ConstantPoolUnavailableScope constant_pool_unavailable(this); |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| mov(scratch, Operand(StackFrame::TypeToMarker(type))); |
| PushCommonFrame(scratch); |
| } |
| if (V8_EMBEDDED_CONSTANT_POOL_BOOL) { |
| LoadConstantPoolPointerRegister(); |
| set_constant_pool_available(true); |
| } |
| } |
| |
| void MacroAssembler::Prologue() { |
| PushStandardFrame(r4); |
| if (V8_EMBEDDED_CONSTANT_POOL_BOOL) { |
| // base contains prologue address |
| LoadConstantPoolPointerRegister(); |
| set_constant_pool_available(true); |
| } |
| } |
| |
| void MacroAssembler::DropArguments(Register count) { Drop(count); } |
| |
| void MacroAssembler::DropArgumentsAndPushNewReceiver(Register argc, |
| Register receiver) { |
| DCHECK(!AreAliased(argc, receiver)); |
| DropArguments(argc); |
| push(receiver); |
| } |
| |
| void MacroAssembler::EnterFrame(StackFrame::Type type, |
| bool load_constant_pool_pointer_reg) { |
| UseScratchRegisterScope temps(this); |
| if (V8_EMBEDDED_CONSTANT_POOL_BOOL && load_constant_pool_pointer_reg) { |
| // Push type explicitly so we can leverage the constant pool. |
| // This path cannot rely on ip containing code entry. |
| PushCommonFrame(); |
| LoadConstantPoolPointerRegister(); |
| if (!StackFrame::IsJavaScript(type)) { |
| Register scratch = temps.Acquire(); |
| mov(scratch, Operand(StackFrame::TypeToMarker(type))); |
| push(scratch); |
| } |
| } else { |
| Register scratch = no_reg; |
| if (!StackFrame::IsJavaScript(type)) { |
| scratch = temps.Acquire(); |
| mov(scratch, Operand(StackFrame::TypeToMarker(type))); |
| } |
| PushCommonFrame(scratch); |
| } |
| #if V8_ENABLE_WEBASSEMBLY |
| if (type == StackFrame::WASM) Push(kWasmImplicitArgRegister); |
| #endif // V8_ENABLE_WEBASSEMBLY |
| } |
| |
| int MacroAssembler::LeaveFrame(StackFrame::Type type, int stack_adjustment) { |
| ConstantPoolUnavailableScope constant_pool_unavailable(this); |
| UseScratchRegisterScope temps(this); |
| Register scratch1 = temps.Acquire(); |
| Register scratch2 = temps.Acquire(); |
| // r3: preserved |
| // r4: preserved |
| // r5: preserved |
| |
| // Drop the execution stack down to the frame pointer and restore |
| // the caller's state. |
| int frame_ends; |
| LoadU64(scratch2, MemOperand(fp, StandardFrameConstants::kCallerPCOffset)); |
| LoadU64(scratch1, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); |
| if (V8_EMBEDDED_CONSTANT_POOL_BOOL) { |
| LoadU64(kConstantPoolRegister, |
| MemOperand(fp, StandardFrameConstants::kConstantPoolOffset)); |
| } |
| mtlr(scratch2); |
| frame_ends = pc_offset(); |
| AddS64(sp, fp, |
| Operand(StandardFrameConstants::kCallerSPOffset + stack_adjustment)); |
| mr(fp, scratch1); |
| return frame_ends; |
| } |
| |
| // ExitFrame layout (probably wrongish.. needs updating) |
| // |
| // SP -> previousSP |
| // LK reserved |
| // sp_on_exit (for debug?) |
| // oldSP->prev SP |
| // LK |
| // <parameters on stack> |
| |
| // Prior to calling EnterExitFrame, we've got a bunch of parameters |
| // on the stack that we need to wrap a real frame around.. so first |
| // we reserve a slot for LK and push the previous SP which is captured |
| // in the fp register (r31) |
| // Then - we buy a new frame |
| |
| void MacroAssembler::EnterExitFrame(int stack_space, |
| StackFrame::Type frame_type) { |
| DCHECK(frame_type == StackFrame::EXIT || |
| frame_type == StackFrame::BUILTIN_EXIT || |
| frame_type == StackFrame::API_NAMED_ACCESSOR_EXIT || |
| frame_type == StackFrame::API_CALLBACK_EXIT); |
| |
| // Set up the frame structure on the stack. |
| DCHECK_EQ(2 * kSystemPointerSize, ExitFrameConstants::kCallerSPDisplacement); |
| DCHECK_EQ(1 * kSystemPointerSize, ExitFrameConstants::kCallerPCOffset); |
| DCHECK_EQ(0 * kSystemPointerSize, ExitFrameConstants::kCallerFPOffset); |
| |
| // This is an opportunity to build a frame to wrap |
| // all of the pushes that have happened inside of V8 |
| // since we were called from C code |
| |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| mov(scratch, Operand(StackFrame::TypeToMarker(frame_type))); |
| PushCommonFrame(scratch); |
| // Reserve room for saved entry sp. |
| subi(sp, fp, Operand(ExitFrameConstants::kFixedFrameSizeFromFp)); |
| |
| if (v8_flags.debug_code) { |
| li(r8, Operand::Zero()); |
| StoreU64(r8, MemOperand(fp, ExitFrameConstants::kSPOffset)); |
| } |
| if (V8_EMBEDDED_CONSTANT_POOL_BOOL) { |
| StoreU64(kConstantPoolRegister, |
| MemOperand(fp, ExitFrameConstants::kConstantPoolOffset)); |
| } |
| |
| // Save the frame pointer and the context in top. |
| StoreU64(fp, AsMemOperand(IsolateFieldId::kCEntryFP)); |
| StoreU64(cp, AsMemOperand(IsolateFieldId::kContext)); |
| |
| AddS64(sp, sp, Operand(-(stack_space + 1) * kSystemPointerSize)); |
| |
| // Allocate and align the frame preparing for calling the runtime |
| // function. |
| const int frame_alignment = ActivationFrameAlignment(); |
| if (frame_alignment > kSystemPointerSize) { |
| DCHECK(base::bits::IsPowerOfTwo(frame_alignment)); |
| ClearRightImm(sp, sp, |
| Operand(base::bits::WhichPowerOfTwo(frame_alignment))); |
| } |
| li(scratch, Operand::Zero()); |
| StoreU64WithUpdate(scratch, MemOperand(sp, -kNumRequiredStackFrameSlots * |
| kSystemPointerSize)); |
| |
| // Set the exit frame sp value to point just before the return address |
| // location. |
| AddS64(r8, sp, Operand((kStackFrameExtraParamSlot + 1) * kSystemPointerSize)); |
| StoreU64(r8, MemOperand(fp, ExitFrameConstants::kSPOffset)); |
| } |
| |
| int MacroAssembler::ActivationFrameAlignment() { |
| #if !defined(USE_SIMULATOR) |
| // Running on the real platform. Use the alignment as mandated by the local |
| // environment. |
| // Note: This will break if we ever start generating snapshots on one PPC |
| // platform for another PPC platform with a different alignment. |
| return base::OS::ActivationFrameAlignment(); |
| #else // Simulated |
| // If we are using the simulator then we should always align to the expected |
| // alignment. As the simulator is used to generate snapshots we do not know |
| // if the target platform will need alignment, so this is controlled from a |
| // flag. |
| return v8_flags.sim_stack_alignment; |
| #endif |
| } |
| |
| void MacroAssembler::LeaveExitFrame() { |
| ConstantPoolUnavailableScope constant_pool_unavailable(this); |
| |
| // Restore current context from top and clear it in debug mode. |
| LoadU64(cp, AsMemOperand(IsolateFieldId::kContext)); |
| |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| #ifdef DEBUG |
| mov(scratch, Operand(Context::kNoContext)); |
| StoreU64(scratch, AsMemOperand(IsolateFieldId::kContext)); |
| #endif |
| |
| // Clear the top frame. |
| mov(scratch, Operand::Zero()); |
| StoreU64(scratch, AsMemOperand(IsolateFieldId::kCEntryFP)); |
| |
| // Tear down the exit frame, pop the arguments, and return. |
| LeaveFrame(StackFrame::EXIT); |
| } |
| |
| void MacroAssembler::MovFromFloatResult(const DoubleRegister dst) { |
| Move(dst, d1); |
| } |
| |
| void MacroAssembler::MovFromFloatParameter(const DoubleRegister dst) { |
| Move(dst, d1); |
| } |
| |
| void MacroAssembler::LoadStackLimit(Register destination, StackLimitKind kind) { |
| DCHECK(root_array_available()); |
| intptr_t offset = kind == StackLimitKind::kRealStackLimit |
| ? IsolateData::real_jslimit_offset() |
| : IsolateData::jslimit_offset(); |
| CHECK(is_int32(offset)); |
| LoadU64(destination, MemOperand(kRootRegister, offset)); |
| } |
| |
| void MacroAssembler::StackOverflowCheck(Register num_args, |
| Label* stack_overflow) { |
| // Check the stack for overflow. We are not trying to catch |
| // interruptions (e.g. debug break and preemption) here, so the "real stack |
| // limit" is checked. |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| LoadStackLimit(scratch, StackLimitKind::kRealStackLimit); |
| // Make scratch the space we have left. The stack might already be overflowed |
| // here which will cause scratch to become negative. |
| sub(scratch, sp, scratch); |
| // Check if the arguments will overflow the stack. |
| Register bytes_needed = temps.Acquire(); |
| ShiftLeftU64(bytes_needed, num_args, Operand(kSystemPointerSizeLog2)); |
| CmpS64(scratch, bytes_needed); |
| ble(stack_overflow); // Signed comparison. |
| } |
| |
| void MacroAssembler::InvokePrologue(Register expected_parameter_count, |
| Register actual_parameter_count, |
| InvokeType type) { |
| Label regular_invoke; |
| |
| // r3: actual arguments count |
| // r4: function (passed through to callee) |
| // r5: expected arguments count |
| |
| DCHECK_EQ(actual_parameter_count, r3); |
| DCHECK_EQ(expected_parameter_count, r5); |
| |
| // If overapplication or if the actual argument count is equal to the |
| // formal parameter count, no need to push extra undefined values. |
| sub(expected_parameter_count, expected_parameter_count, |
| actual_parameter_count, LeaveOE, SetRC); |
| ble(®ular_invoke, cr0); |
| |
| Label stack_overflow; |
| Register scratch = r7; |
| StackOverflowCheck(expected_parameter_count, &stack_overflow); |
| |
| // Underapplication. Move the arguments already in the stack, including the |
| // receiver and the return address. |
| { |
| Label copy, skip; |
| Register src = r9, dest = r8; |
| addi(src, sp, Operand(-kSystemPointerSize)); |
| ShiftLeftU64(scratch, expected_parameter_count, |
| Operand(kSystemPointerSizeLog2)); |
| sub(sp, sp, scratch); |
| // Update stack pointer. |
| addi(dest, sp, Operand(-kSystemPointerSize)); |
| mr(scratch, actual_parameter_count); |
| cmpi(scratch, Operand::Zero()); |
| ble(&skip); |
| mtctr(scratch); |
| |
| bind(©); |
| LoadU64WithUpdate(scratch, MemOperand(src, kSystemPointerSize)); |
| StoreU64WithUpdate(scratch, MemOperand(dest, kSystemPointerSize)); |
| bdnz(©); |
| bind(&skip); |
| } |
| |
| // Fill remaining expected arguments with undefined values. |
| LoadRoot(scratch, RootIndex::kUndefinedValue); |
| { |
| mtctr(expected_parameter_count); |
| |
| Label loop; |
| bind(&loop); |
| StoreU64WithUpdate(scratch, MemOperand(r8, kSystemPointerSize)); |
| bdnz(&loop); |
| } |
| b(®ular_invoke); |
| |
| bind(&stack_overflow); |
| { |
| FrameScope frame( |
| this, has_frame() ? StackFrame::NO_FRAME_TYPE : StackFrame::INTERNAL); |
| CallRuntime(Runtime::kThrowStackOverflow); |
| bkpt(0); |
| } |
| |
| bind(®ular_invoke); |
| } |
| |
| void MacroAssembler::CheckDebugHook(Register fun, Register new_target, |
| Register expected_parameter_count, |
| Register actual_parameter_count) { |
| Label skip_hook; |
| |
| ExternalReference debug_hook_active = |
| ExternalReference::debug_hook_on_function_call_address(isolate()); |
| Move(r7, debug_hook_active); |
| LoadU8(r7, MemOperand(r7)); |
| extsb(r7, r7); |
| CmpSmiLiteral(r7, Smi::zero()); |
| beq(&skip_hook); |
| |
| { |
| // Load receiver to pass it later to DebugOnFunctionCall hook. |
| LoadReceiver(r7); |
| FrameScope frame( |
| this, has_frame() ? StackFrame::NO_FRAME_TYPE : StackFrame::INTERNAL); |
| |
| SmiTag(expected_parameter_count); |
| Push(expected_parameter_count); |
| |
| SmiTag(actual_parameter_count); |
| Push(actual_parameter_count); |
| |
| if (new_target.is_valid()) { |
| Push(new_target); |
| } |
| Push(fun, fun, r7); |
| CallRuntime(Runtime::kDebugOnFunctionCall); |
| Pop(fun); |
| if (new_target.is_valid()) { |
| Pop(new_target); |
| } |
| |
| Pop(actual_parameter_count); |
| SmiUntag(actual_parameter_count); |
| |
| Pop(expected_parameter_count); |
| SmiUntag(expected_parameter_count); |
| } |
| bind(&skip_hook); |
| } |
| |
| void MacroAssembler::InvokeFunctionCode(Register function, Register new_target, |
| Register expected_parameter_count, |
| Register actual_parameter_count, |
| InvokeType type) { |
| // You can't call a function without a valid frame. |
| DCHECK_IMPLIES(type == InvokeType::kCall, has_frame()); |
| DCHECK_EQ(function, r4); |
| DCHECK_IMPLIES(new_target.is_valid(), new_target == r6); |
| |
| // On function call, call into the debugger if necessary. |
| CheckDebugHook(function, new_target, expected_parameter_count, |
| actual_parameter_count); |
| |
| // Clear the new.target register if not given. |
| if (!new_target.is_valid()) { |
| LoadRoot(r6, RootIndex::kUndefinedValue); |
| } |
| |
| InvokePrologue(expected_parameter_count, actual_parameter_count, type); |
| // We call indirectly through the code field in the function to |
| // allow recompilation to take effect without changing any of the |
| // call sites. |
| constexpr int unused_argument_count = 0; |
| switch (type) { |
| case InvokeType::kCall: |
| CallJSFunction(function, unused_argument_count); |
| break; |
| case InvokeType::kJump: |
| JumpJSFunction(function); |
| break; |
| } |
| } |
| |
| void MacroAssembler::InvokeFunctionWithNewTarget( |
| Register fun, Register new_target, Register actual_parameter_count, |
| InvokeType type) { |
| // You can't call a function without a valid frame. |
| DCHECK_IMPLIES(type == InvokeType::kCall, has_frame()); |
| |
| // Contract with called JS functions requires that function is passed in r4. |
| DCHECK_EQ(fun, r4); |
| |
| Register expected_reg = r5; |
| Register temp_reg = r7; |
| |
| LoadTaggedField( |
| temp_reg, |
| FieldMemOperand(r4, offsetof(JSFunction, shared_function_info_))); |
| LoadTaggedField(cp, FieldMemOperand(r4, offsetof(JSFunction, context_))); |
| LoadU16(expected_reg, |
| FieldMemOperand( |
| temp_reg, offsetof(SharedFunctionInfo, formal_parameter_count_))); |
| |
| InvokeFunctionCode(fun, new_target, expected_reg, actual_parameter_count, |
| type); |
| } |
| |
| void MacroAssembler::InvokeFunction(Register function, |
| Register expected_parameter_count, |
| Register actual_parameter_count, |
| InvokeType type) { |
| // You can't call a function without a valid frame. |
| DCHECK_IMPLIES(type == InvokeType::kCall, has_frame()); |
| |
| // Contract with called JS functions requires that function is passed in r4. |
| DCHECK_EQ(function, r4); |
| |
| // Get the function and setup the context. |
| LoadTaggedField(cp, FieldMemOperand(r4, offsetof(JSFunction, context_))); |
| |
| InvokeFunctionCode(r4, no_reg, expected_parameter_count, |
| actual_parameter_count, type); |
| } |
| |
| void MacroAssembler::PushStackHandler() { |
| // Adjust this code if not the case. |
| static_assert(StackHandlerConstants::kSize == 2 * kSystemPointerSize); |
| static_assert(StackHandlerConstants::kNextOffset == 0 * kSystemPointerSize); |
| |
| Push(Smi::zero()); // Padding. |
| |
| // Link the current handler as the next handler. |
| // Preserve r4-r8. |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| LoadU64(scratch, AsMemOperand(IsolateFieldId::kHandler)); |
| push(scratch); |
| |
| // Set this new handler as the current one. |
| StoreU64(sp, AsMemOperand(IsolateFieldId::kHandler)); |
| } |
| |
| void MacroAssembler::PopStackHandler() { |
| static_assert(StackHandlerConstants::kSize == 2 * kSystemPointerSize); |
| static_assert(StackHandlerConstants::kNextOffset == 0); |
| |
| pop(r4); |
| StoreU64(r4, AsMemOperand(IsolateFieldId::kHandler)); |
| |
| Drop(1); // Drop padding. |
| } |
| |
| // Sets equality condition flags. |
| void MacroAssembler::IsObjectType(Register object, Register scratch1, |
| Register scratch2, InstanceType type) { |
| ASM_CODE_COMMENT(this); |
| CompareObjectType(object, scratch1, scratch2, type); |
| } |
| |
| void MacroAssembler::CompareObjectTypeRange(Register object, Register map, |
| Register type_reg, |
| InstanceType lower_limit, |
| InstanceType upper_limit) { |
| ASM_CODE_COMMENT(this); |
| LoadMap(map, object); |
| CompareInstanceTypeRange(map, type_reg, lower_limit, upper_limit); |
| } |
| |
| void MacroAssembler::CompareRange(Register value, unsigned lower_limit, |
| unsigned higher_limit) { |
| ASM_CODE_COMMENT(this); |
| DCHECK_LT(lower_limit, higher_limit); |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| if (lower_limit != 0) { |
| mov(scratch, Operand(lower_limit)); |
| sub(scratch, value, scratch); |
| cmpli(scratch, Operand(higher_limit - lower_limit)); |
| } else { |
| mov(scratch, Operand(higher_limit)); |
| CmpU64(value, scratch); |
| } |
| } |
| |
| void MacroAssembler::CompareInstanceTypeRange(Register map, Register type_reg, |
| InstanceType lower_limit, |
| InstanceType higher_limit) { |
| DCHECK_LT(lower_limit, higher_limit); |
| LoadU16(type_reg, FieldMemOperand(map, offsetof(Map, instance_type_))); |
| CompareRange(type_reg, lower_limit, higher_limit); |
| } |
| |
| void MacroAssembler::CompareTaggedRoot(const Register& obj, RootIndex index) { |
| ASM_CODE_COMMENT(this); |
| // Some smi roots contain system pointer size values like stack limits. |
| DCHECK(base::IsInRange(index, RootIndex::kFirstStrongOrReadOnlyRoot, |
| RootIndex::kLastStrongOrReadOnlyRoot)); |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| LoadRoot(scratch, index); |
| CompareTagged(obj, scratch); |
| } |
| |
| void MacroAssembler::CompareRoot(Register obj, RootIndex index) { |
| ASM_CODE_COMMENT(this); |
| if (!base::IsInRange(index, RootIndex::kFirstStrongOrReadOnlyRoot, |
| RootIndex::kLastStrongOrReadOnlyRoot)) { |
| // Some smi roots contain system pointer size values like stack limits. |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| LoadRoot(scratch, index); |
| CmpU64(obj, scratch); |
| return; |
| } |
| CompareTaggedRoot(obj, index); |
| } |
| |
| |
| void MacroAssembler::MinF64(DoubleRegister dst, DoubleRegister lhs, |
| DoubleRegister rhs, DoubleRegister scratch) { |
| Label return_nan, done; |
| fcmpu(lhs, rhs); |
| bunordered(&return_nan); |
| xsmindp(dst, lhs, rhs); |
| b(&done); |
| bind(&return_nan); |
| /* If left or right are NaN, fadd propagates the appropriate one.*/ |
| fadd(dst, lhs, rhs); |
| bind(&done); |
| } |
| |
| void MacroAssembler::MaxF64(DoubleRegister dst, DoubleRegister lhs, |
| DoubleRegister rhs, DoubleRegister scratch) { |
| Label return_nan, done; |
| fcmpu(lhs, rhs); |
| bunordered(&return_nan); |
| xsmaxdp(dst, lhs, rhs); |
| b(&done); |
| bind(&return_nan); |
| /* If left or right are NaN, fadd propagates the appropriate one.*/ |
| fadd(dst, lhs, rhs); |
| bind(&done); |
| } |
| |
| void MacroAssembler::JumpIfIsInRange(Register value, unsigned lower_limit, |
| unsigned higher_limit, |
| Label* on_in_range) { |
| CompareRange(value, lower_limit, higher_limit); |
| ble(on_in_range); |
| } |
| |
| void MacroAssembler::TruncateDoubleToI(Isolate* isolate, Zone* zone, |
| Register result, |
| DoubleRegister double_input, |
| StubCallMode stub_mode, |
| DoubleRegister double_scratch) { |
| Label done; |
| |
| TryInlineTruncateDoubleToI(result, double_input, &done, double_scratch); |
| |
| // If we fell through then inline version didn't succeed - call stub instead. |
| PushLR(); |
| // Put input on stack. |
| stfdu(double_input, MemOperand(sp, -kDoubleSize)); |
| |
| #if V8_ENABLE_WEBASSEMBLY |
| if (stub_mode == StubCallMode::kCallWasmRuntimeStub) { |
| Call(static_cast<Address>(Builtin::kDoubleToI), RelocInfo::WASM_STUB_CALL); |
| #else |
| // For balance. |
| if (false) { |
| #endif // V8_ENABLE_WEBASSEMBLY |
| } else { |
| CallBuiltin(Builtin::kDoubleToI); |
| } |
| |
| LoadU64(result, MemOperand(sp)); |
| addi(sp, sp, Operand(kDoubleSize)); |
| PopLR(); |
| |
| bind(&done); |
| } |
| |
| void MacroAssembler::TryInlineTruncateDoubleToI(Register result, |
| DoubleRegister double_input, |
| Label* done, |
| DoubleRegister double_scratch) { |
| CHECK(!AreAliased(double_scratch, double_input)); |
| ConvertDoubleToInt64(double_input, |
| result, double_scratch); |
| |
| // Test for overflow |
| TestIfInt32(result); |
| beq(done); |
| } |
| |
| #ifdef V8_ENABLE_DEBUG_CODE |
| void MacroAssembler::AssertFeedbackCell(Register object, Register scratch) { |
| if (v8_flags.debug_code) { |
| CompareObjectType(object, scratch, scratch, FEEDBACK_CELL_TYPE); |
| Assert(eq, AbortReason::kExpectedFeedbackCell); |
| } |
| } |
| void MacroAssembler::AssertFeedbackVector(Register object, Register scratch) { |
| if (v8_flags.debug_code) { |
| CompareObjectType(object, scratch, scratch, FEEDBACK_VECTOR_TYPE); |
| Assert(eq, AbortReason::kExpectedFeedbackVector); |
| } |
| } |
| #endif // V8_ENABLE_DEBUG_CODE |
| |
| void MacroAssembler::GenerateTailCallToReturnedCode( |
| Runtime::FunctionId function_id) { |
| // ----------- S t a t e ------------- |
| // -- r3 : actual argument count |
| // -- r4 : target function (preserved for callee) |
| // -- r6 : new target (preserved for callee) |
| // ----------------------------------- |
| { |
| FrameAndConstantPoolScope scope(this, StackFrame::INTERNAL); |
| // Push a copy of the target function, the new target and the actual |
| // argument count. |
| // Push function as parameter to the runtime call. |
| SmiTag(kJavaScriptCallArgCountRegister); |
| Push(kJavaScriptCallTargetRegister, kJavaScriptCallNewTargetRegister, |
| kJavaScriptCallArgCountRegister, kJavaScriptCallTargetRegister); |
| |
| CallRuntime(function_id, 1); |
| |
| // Restore target function, new target and actual argument count. |
| Pop(kJavaScriptCallTargetRegister, kJavaScriptCallNewTargetRegister, |
| kJavaScriptCallArgCountRegister); |
| SmiUntag(kJavaScriptCallArgCountRegister); |
| } |
| static_assert(kJavaScriptCallCodeStartRegister == r5, "ABI mismatch"); |
| JumpJSFunction(kJavaScriptCallTargetRegister); |
| } |
| |
| |
| void MacroAssembler::CallRuntime(const Runtime::Function* f, |
| int num_arguments) { |
| // All parameters are on the stack. r3 has the return value after call. |
| |
| // If the expected number of arguments of the runtime function is |
| // constant, we check that the actual number of arguments match the |
| // expectation. |
| CHECK(f->nargs < 0 || f->nargs == num_arguments); |
| |
| // TODO(1236192): Most runtime routines don't need the number of |
| // arguments passed in because it is constant. At some point we |
| // should remove this need and make the runtime routine entry code |
| // smarter. |
| mov(r3, Operand(num_arguments)); |
| Move(r4, ExternalReference::Create(f)); |
| bool switch_to_central_stack = options().is_wasm; |
| CallBuiltin(Builtins::RuntimeCEntry(f->result_size, switch_to_central_stack)); |
| } |
| |
| void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid) { |
| const Runtime::Function* function = Runtime::FunctionForId(fid); |
| DCHECK_EQ(1, function->result_size); |
| if (function->nargs >= 0) { |
| mov(r3, Operand(function->nargs)); |
| } |
| JumpToExternalReference(ExternalReference::Create(fid)); |
| } |
| |
| void MacroAssembler::JumpToExternalReference(const ExternalReference& builtin, |
| bool builtin_exit_frame) { |
| Move(r4, builtin); |
| TailCallBuiltin(Builtins::CEntry(1, ArgvMode::kStack, builtin_exit_frame)); |
| } |
| |
| void MacroAssembler::LoadWeakValue(Register out, Register in, |
| Label* target_if_cleared) { |
| CmpS32(in, Operand(kClearedWeakHeapObjectLower32)); |
| beq(target_if_cleared); |
| |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| mov(scratch, Operand(~kWeakHeapObjectMask)); |
| and_(out, in, scratch); |
| } |
| |
| void MacroAssembler::EmitIncrementCounter(StatsCounter* counter, int value, |
| Register scratch1, |
| Register scratch2) { |
| DCHECK_GT(value, 0); |
| if (v8_flags.native_code_counters && counter->Enabled()) { |
| // This operation has to be exactly 32-bit wide in case the external |
| // reference table redirects the counter to a uint32_t dummy_stats_counter_ |
| // field. |
| Move(scratch2, ExternalReference::Create(counter)); |
| lwz(scratch1, MemOperand(scratch2)); |
| addi(scratch1, scratch1, Operand(value)); |
| stw(scratch1, MemOperand(scratch2)); |
| } |
| } |
| |
| void MacroAssembler::EmitDecrementCounter(StatsCounter* counter, int value, |
| Register scratch1, |
| Register scratch2) { |
| DCHECK_GT(value, 0); |
| if (v8_flags.native_code_counters && counter->Enabled()) { |
| // This operation has to be exactly 32-bit wide in case the external |
| // reference table redirects the counter to a uint32_t dummy_stats_counter_ |
| // field. |
| Move(scratch2, ExternalReference::Create(counter)); |
| lwz(scratch1, MemOperand(scratch2)); |
| subi(scratch1, scratch1, Operand(value)); |
| stw(scratch1, MemOperand(scratch2)); |
| } |
| } |
| |
| void MacroAssembler::Check(Condition cond, AbortReason reason, CRegister cr) { |
| Label L; |
| b(to_condition(cond), &L, cr); |
| Abort(reason); |
| // will not return here |
| bind(&L); |
| } |
| |
| void MacroAssembler::Abort(AbortReason reason) { |
| ASM_CODE_COMMENT(this); |
| if (v8_flags.code_comments) { |
| RecordComment("Abort message:", SourceLocation{}); |
| RecordComment(GetAbortReason(reason), SourceLocation{}); |
| } |
| |
| // Without debug code, save the code size and just trap. |
| if (!v8_flags.debug_code || v8_flags.trap_on_abort) { |
| stop(); |
| return; |
| } |
| |
| if (should_abort_hard()) { |
| // We don't care if we constructed a frame. Just pretend we did. |
| FrameScope assume_frame(this, StackFrame::NO_FRAME_TYPE); |
| mov(r3, Operand(static_cast<int>(reason))); |
| PrepareCallCFunction(1); |
| Register dst = ip; |
| if (!ABI_CALL_VIA_IP) { |
| dst = r4; |
| } |
| Move(dst, ExternalReference::abort_with_reason()); |
| // Use Call directly to avoid any unneeded overhead. The function won't |
| // return anyway. |
| Call(dst); |
| return; |
| } |
| |
| LoadSmiLiteral(r4, Smi::FromInt(static_cast<int>(reason))); |
| |
| { |
| // We don't actually want to generate a pile of code for this, so just |
| // claim there is a stack frame, without generating one. |
| FrameScope scope(this, StackFrame::NO_FRAME_TYPE); |
| if (root_array_available()) { |
| // Generate an indirect call via builtins entry table here in order to |
| // ensure that the interpreter_entry_return_pc_offset is the same for |
| // InterpreterEntryTrampoline and InterpreterEntryTrampolineForProfiling |
| // when v8_flags.debug_code is enabled. |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| LoadEntryFromBuiltin(Builtin::kAbort, scratch); |
| Call(scratch); |
| } else { |
| CallBuiltin(Builtin::kAbort); |
| } |
| } |
| // will not return here |
| } |
| |
| void MacroAssembler::LoadMap(Register destination, Register object) { |
| LoadTaggedField(destination, |
| FieldMemOperand(object, offsetof(HeapObject, map_))); |
| } |
| |
| void MacroAssembler::LoadFeedbackCell(Register dst, Register closure) { |
| LoadTaggedField( |
| dst, FieldMemOperand(closure, offsetof(JSFunction, feedback_cell_))); |
| } |
| |
| void MacroAssembler::LoadFeedbackVectorFromCell(Register dst, |
| Register feedback_cell, |
| Register scratch, |
| Label* fbv_undef) { |
| Label done; |
| LoadTaggedField( |
| dst, FieldMemOperand(feedback_cell, offsetof(FeedbackCell, value_))); |
| |
| // Check if feedback vector is valid. |
| LoadTaggedField(scratch, FieldMemOperand(dst, offsetof(HeapObject, map_))); |
| LoadU16(scratch, FieldMemOperand(scratch, offsetof(Map, instance_type_))); |
| CmpS32(scratch, Operand(FEEDBACK_VECTOR_TYPE)); |
| b(eq, &done); |
| |
| // Not valid, load undefined. |
| LoadRoot(dst, RootIndex::kUndefinedValue); |
| b(fbv_undef); |
| |
| bind(&done); |
| } |
| |
| void MacroAssembler::LoadFeedbackVector(Register dst, Register closure, |
| Register scratch, Label* fbv_undef) { |
| LoadFeedbackCell(dst, closure); |
| LoadFeedbackVectorFromCell(dst, dst, scratch, fbv_undef); |
| } |
| |
| void MacroAssembler::LoadInterpreterDataBytecodeArray( |
| Register destination, Register interpreter_data) { |
| LoadTaggedField(destination, |
| FieldMemOperand(interpreter_data, |
| offsetof(InterpreterData, bytecode_array_))); |
| } |
| |
| void MacroAssembler::LoadInterpreterDataInterpreterTrampoline( |
| Register destination, Register interpreter_data) { |
| LoadTaggedField( |
| destination, |
| FieldMemOperand(interpreter_data, |
| offsetof(InterpreterData, interpreter_trampoline_))); |
| } |
| |
| void MacroAssembler::LoadCompressedMap(Register dst, Register object) { |
| ASM_CODE_COMMENT(this); |
| LoadU32(dst, FieldMemOperand(object, offsetof(HeapObject, map_))); |
| } |
| |
| void MacroAssembler::LoadNativeContextSlot(Register dst, int index) { |
| LoadMap(dst, cp); |
| LoadTaggedField( |
| dst, |
| FieldMemOperand( |
| dst, offsetof(Map, constructor_or_back_pointer_or_native_context_))); |
| LoadTaggedField(dst, MemOperand(dst, Context::SlotOffset(index))); |
| } |
| |
| #ifdef V8_ENABLE_DEBUG_CODE |
| void MacroAssembler::Assert(Condition cond, AbortReason reason, CRegister cr) { |
| if (v8_flags.debug_code) Check(cond, reason, cr); |
| } |
| |
| void MacroAssembler::AssertUnreachable(AbortReason reason) { |
| if (v8_flags.debug_code) Abort(reason); |
| } |
| |
| void MacroAssembler::AssertZeroExtended(Register int32_register) { |
| if (!v8_flags.debug_code) return; |
| ASM_CODE_COMMENT(this); |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| mov(scratch, Operand(kMaxUInt32)); |
| CmpS64(int32_register, scratch); |
| Check(le, AbortReason::k32BitValueInRegisterIsNotZeroExtended); |
| } |
| |
| void MacroAssembler::AssertMap(Register object) { |
| if (!v8_flags.debug_code) return; |
| ASM_CODE_COMMENT(this); |
| TestIfSmi(object); |
| Check(ne, AbortReason::kOperandIsNotAMap); |
| UseScratchRegisterScope temps(this); |
| Register temp = temps.Acquire(); |
| CompareObjectType(object, temp, temp, MAP_TYPE); |
| Check(eq, AbortReason::kOperandIsNotAMap); |
| } |
| |
| void MacroAssembler::AssertNotSmi(Register object) { |
| if (v8_flags.debug_code) { |
| static_assert(kSmiTag == 0); |
| TestIfSmi(object); |
| Check(ne, AbortReason::kOperandIsASmi, cr0); |
| } |
| } |
| |
| void MacroAssembler::AssertSmi(Register object) { |
| if (v8_flags.debug_code) { |
| static_assert(kSmiTag == 0); |
| TestIfSmi(object); |
| Check(eq, AbortReason::kOperandIsNotASmi, cr0); |
| } |
| } |
| |
| void MacroAssembler::AssertConstructor(Register object) { |
| if (v8_flags.debug_code) { |
| static_assert(kSmiTag == 0); |
| TestIfSmi(object); |
| Check(ne, AbortReason::kOperandIsASmiAndNotAConstructor, cr0); |
| push(object); |
| LoadMap(object, object); |
| lbz(object, FieldMemOperand(object, offsetof(Map, bit_field_))); |
| andi(object, object, Operand(Map::Bits1::IsConstructorBit::kMask)); |
| pop(object); |
| Check(ne, AbortReason::kOperandIsNotAConstructor, cr0); |
| } |
| } |
| |
| void MacroAssembler::AssertFunction(Register object) { |
| if (v8_flags.debug_code) { |
| static_assert(kSmiTag == 0); |
| TestIfSmi(object); |
| Check(ne, AbortReason::kOperandIsASmiAndNotAFunction, cr0); |
| UseScratchRegisterScope temps(this); |
| Register temp = temps.Acquire(); |
| LoadMap(temp, object); |
| CompareInstanceTypeRange(temp, temp, FIRST_JS_FUNCTION_TYPE, |
| LAST_JS_FUNCTION_TYPE); |
| Check(le, AbortReason::kOperandIsNotAFunction); |
| } |
| } |
| |
| void MacroAssembler::AssertCallableFunction(Register object) { |
| if (!v8_flags.debug_code) return; |
| ASM_CODE_COMMENT(this); |
| static_assert(kSmiTag == 0); |
| TestIfSmi(object); |
| Check(ne, AbortReason::kOperandIsASmiAndNotAFunction, cr0); |
| UseScratchRegisterScope temps(this); |
| Register temp = temps.Acquire(); |
| LoadMap(temp, object); |
| CompareInstanceTypeRange(temp, temp, FIRST_CALLABLE_JS_FUNCTION_TYPE, |
| LAST_CALLABLE_JS_FUNCTION_TYPE); |
| Check(le, AbortReason::kOperandIsNotACallableFunction); |
| } |
| |
| void MacroAssembler::AssertBoundFunction(Register object) { |
| if (v8_flags.debug_code) { |
| static_assert(kSmiTag == 0); |
| TestIfSmi(object); |
| Check(ne, AbortReason::kOperandIsASmiAndNotABoundFunction, cr0); |
| UseScratchRegisterScope temps(this); |
| Register temp = temps.Acquire(); |
| CompareObjectType(object, temp, temp, JS_BOUND_FUNCTION_TYPE); |
| Check(eq, AbortReason::kOperandIsNotABoundFunction); |
| } |
| } |
| |
| void MacroAssembler::AssertGeneratorObject(Register object) { |
| if (!v8_flags.debug_code) return; |
| TestIfSmi(object); |
| Check(ne, AbortReason::kOperandIsASmiAndNotAGeneratorObject, cr0); |
| |
| // Check if JSGeneratorObject |
| UseScratchRegisterScope temps(this); |
| Register temp = temps.Acquire(); |
| LoadMap(temp, object); |
| CompareInstanceTypeRange(temp, temp, FIRST_JS_GENERATOR_OBJECT_TYPE, |
| LAST_JS_GENERATOR_OBJECT_TYPE); |
| Check(le, AbortReason::kOperandIsNotAGeneratorObject); |
| } |
| |
| void MacroAssembler::AssertUndefinedOrAllocationSite(Register object) { |
| if (v8_flags.debug_code) { |
| Label done_checking; |
| AssertNotSmi(object); |
| CompareRoot(object, RootIndex::kUndefinedValue); |
| beq(&done_checking); |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| LoadMap(scratch, object); |
| CompareInstanceType(scratch, scratch, ALLOCATION_SITE_TYPE); |
| Assert(eq, AbortReason::kExpectedUndefinedOrCell); |
| bind(&done_checking); |
| } |
| } |
| |
| void MacroAssembler::AssertJSAny(Register object, AbortReason abort_reason) { |
| if (!v8_flags.debug_code) return; |
| |
| ASM_CODE_COMMENT(this); |
| UseScratchRegisterScope temps(this); |
| Register map_tmp = temps.Acquire(); |
| Register tmp = temps.Acquire(); |
| Label ok; |
| |
| JumpIfSmi(object, &ok); |
| |
| LoadMap(map_tmp, object); |
| CompareInstanceType(map_tmp, tmp, LAST_NAME_TYPE); |
| ble(&ok); |
| |
| CompareInstanceType(map_tmp, tmp, FIRST_JS_RECEIVER_TYPE); |
| bge(&ok); |
| |
| CompareRoot(map_tmp, RootIndex::kHeapNumberMap); |
| beq(&ok); |
| |
| CompareRoot(map_tmp, RootIndex::kBigIntMap); |
| beq(&ok); |
| |
| CompareRoot(object, RootIndex::kUndefinedValue); |
| beq(&ok); |
| |
| CompareRoot(object, RootIndex::kTrueValue); |
| beq(&ok); |
| |
| CompareRoot(object, RootIndex::kFalseValue); |
| beq(&ok); |
| |
| CompareRoot(object, RootIndex::kNullValue); |
| beq(&ok); |
| |
| Abort(abort_reason); |
| |
| bind(&ok); |
| } |
| |
| #endif // V8_ENABLE_DEBUG_CODE |
| |
| int MacroAssembler::CalculateStackPassedWords(int num_reg_arguments, |
| int num_double_arguments) { |
| int stack_passed_words = 0; |
| if (num_double_arguments > DoubleRegister::kNumRegisters) { |
| stack_passed_words += |
| 2 * (num_double_arguments - DoubleRegister::kNumRegisters); |
| } |
| // Up to 8 simple arguments are passed in registers r3..r10. |
| if (num_reg_arguments > kRegisterPassedArguments) { |
| stack_passed_words += num_reg_arguments - kRegisterPassedArguments; |
| } |
| return stack_passed_words; |
| } |
| |
| void MacroAssembler::PrepareCallCFunction(int num_reg_arguments, |
| int num_double_arguments) { |
| int frame_alignment = ActivationFrameAlignment(); |
| int stack_passed_arguments = |
| CalculateStackPassedWords(num_reg_arguments, num_double_arguments); |
| int stack_space = kNumRequiredStackFrameSlots; |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| |
| if (frame_alignment > kSystemPointerSize) { |
| // Make stack end at alignment and make room for stack arguments |
| // -- preserving original value of sp. |
| mr(scratch, sp); |
| AddS64(sp, sp, Operand(-(stack_passed_arguments + 1) * kSystemPointerSize)); |
| DCHECK(base::bits::IsPowerOfTwo(frame_alignment)); |
| ClearRightImm(sp, sp, |
| Operand(base::bits::WhichPowerOfTwo(frame_alignment))); |
| StoreU64(scratch, |
| MemOperand(sp, stack_passed_arguments * kSystemPointerSize)); |
| } else { |
| // Make room for stack arguments |
| stack_space += stack_passed_arguments; |
| } |
| |
| // Allocate frame with required slots to make ABI work. |
| li(scratch, Operand::Zero()); |
| StoreU64WithUpdate(scratch, |
| MemOperand(sp, -stack_space * kSystemPointerSize)); |
| } |
| |
| void MacroAssembler::MovToFloatParameter(DoubleRegister src) { Move(d1, src); } |
| |
| void MacroAssembler::MovToFloatResult(DoubleRegister src) { Move(d1, src); } |
| |
| void MacroAssembler::MovToFloatParameters(DoubleRegister src1, |
| DoubleRegister src2) { |
| if (src2 == d1) { |
| DCHECK(src1 != d2); |
| Move(d2, src2); |
| Move(d1, src1); |
| } else { |
| Move(d1, src1); |
| Move(d2, src2); |
| } |
| } |
| |
| int MacroAssembler::CallCFunction(ExternalReference function, |
| int num_reg_arguments, |
| int num_double_arguments, |
| SetIsolateDataSlots set_isolate_data_slots, |
| bool has_function_descriptor, |
| Label* return_label) { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| Move(scratch, function); |
| return CallCFunction(scratch, num_reg_arguments, num_double_arguments, |
| set_isolate_data_slots, has_function_descriptor, |
| return_label); |
| } |
| |
| int MacroAssembler::CallCFunction(Register function, int num_reg_arguments, |
| int num_double_arguments, |
| SetIsolateDataSlots set_isolate_data_slots, |
| bool has_function_descriptor, |
| Label* return_label) { |
| ASM_CODE_COMMENT(this); |
| DCHECK_LE(num_reg_arguments + num_double_arguments, kMaxCParameters); |
| DCHECK(has_frame()); |
| |
| Label get_pc; |
| |
| if (set_isolate_data_slots == SetIsolateDataSlots::kYes) { |
| // Save the frame pointer and PC so that the stack layout remains iterable, |
| // even without an ExitFrame which normally exists between JS and C frames. |
| DCHECK(!AreAliased(r0, r26, function)); |
| GetLabelAddress(r0, &get_pc); |
| CHECK(root_array_available()); |
| StoreU64(r0, |
| ExternalReferenceAsOperand(IsolateFieldId::kFastCCallCallerPC)); |
| StoreU64(fp, |
| ExternalReferenceAsOperand(IsolateFieldId::kFastCCallCallerFP)); |
| } |
| |
| Register dest = function; |
| if (ABI_USES_FUNCTION_DESCRIPTORS && has_function_descriptor) { |
| // AIX/PPC64BE Linux uses a function descriptor. When calling C code be |
| // aware of this descriptor and pick up values from it |
| LoadU64(ToRegister(ABI_TOC_REGISTER), |
| MemOperand(function, kSystemPointerSize)); |
| LoadU64(ip, MemOperand(function, 0)); |
| dest = ip; |
| } else if (ABI_CALL_VIA_IP && function != ip) { |
| // pLinux and Simulator, not AIX |
| Move(ip, function); |
| dest = ip; |
| } |
| |
| int call_pc_offset; |
| { |
| ConstantPoolUnavailableScope block_const_pool_scope(this); |
| BlockTrampolinePoolScope block_trampoline_pool(this); |
| Call(dest); |
| call_pc_offset = pc_offset(); |
| bind(&get_pc); |
| if (return_label) bind(return_label); |
| |
| int before_offset = pc_offset(); |
| int stack_passed_arguments = |
| CalculateStackPassedWords(num_reg_arguments, num_double_arguments); |
| int stack_space = kNumRequiredStackFrameSlots + stack_passed_arguments; |
| if (ActivationFrameAlignment() > kSystemPointerSize) { |
| // Using prefixed load may emit `nop` which then fails the check below. |
| ld(sp, MemOperand(sp, stack_space * kSystemPointerSize)); |
| } else { |
| AddS64(sp, sp, Operand(stack_space * kSystemPointerSize)); |
| } |
| // We assume that the move instruction uses kMaxSizeOfMoveAfterFastCall |
| // bytes. When we patch in the deopt trampoline, we patch it in after the |
| // move instruction, so that the stack has been restored correctly. |
| CHECK_EQ(kMaxSizeOfMoveAfterFastCall, pc_offset() - before_offset); |
| } |
| |
| if (set_isolate_data_slots == SetIsolateDataSlots::kYes) { |
| // We don't unset the PC; the FP is the source of truth. |
| Register zero_scratch = r0; |
| mov(zero_scratch, Operand::Zero()); |
| |
| StoreU64(zero_scratch, |
| ExternalReferenceAsOperand(IsolateFieldId::kFastCCallCallerFP)); |
| } |
| |
| return call_pc_offset; |
| } |
| |
| int MacroAssembler::CallCFunction(ExternalReference function, int num_arguments, |
| SetIsolateDataSlots set_isolate_data_slots, |
| bool has_function_descriptor, |
| Label* return_label) { |
| return CallCFunction(function, num_arguments, 0, set_isolate_data_slots, |
| has_function_descriptor, return_label); |
| } |
| |
| int MacroAssembler::CallCFunction(Register function, int num_arguments, |
| SetIsolateDataSlots set_isolate_data_slots, |
| bool has_function_descriptor, |
| Label* return_label) { |
| return CallCFunction(function, num_arguments, 0, set_isolate_data_slots, |
| has_function_descriptor, return_label); |
| } |
| |
| void MacroAssembler::CheckPageFlag( |
| Register object, |
| Register scratch, // scratch may be same register as object |
| int mask, Condition cc, Label* condition_met) { |
| DCHECK(cc == ne || cc == eq); |
| ClearRightImm(scratch, object, Operand(kPageSizeBits)); |
| LoadU64(scratch, MemOperand(scratch, MemoryChunk::FlagsOffset())); |
| |
| UseScratchRegisterScope temps(this); |
| Register temp = temps.Acquire(); |
| DCHECK_NE(scratch, temp); |
| mov(temp, Operand(mask)); |
| and_(temp, scratch, temp, SetRC); |
| |
| if (cc == ne) { |
| bne(condition_met, cr0); |
| } |
| if (cc == eq) { |
| beq(condition_met, cr0); |
| } |
| } |
| |
| void MacroAssembler::SetRoundingMode(FPRoundingMode RN) { mtfsfi(7, RN); } |
| |
| void MacroAssembler::ResetRoundingMode() { |
| mtfsfi(7, kRoundToNearest); // reset (default is kRoundToNearest) |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // New MacroAssembler Interfaces added for PPC |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| void MacroAssembler::LoadIntLiteral(Register dst, int value) { |
| mov(dst, Operand(value)); |
| } |
| |
| void MacroAssembler::LoadSmiLiteral(Register dst, Tagged<Smi> smi) { |
| mov(dst, Operand(smi)); |
| } |
| |
| void MacroAssembler::LoadDoubleLiteral(DoubleRegister result, |
| base::Double value) { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| if (V8_EMBEDDED_CONSTANT_POOL_BOOL && is_constant_pool_available() && |
| !(scratch == r0 && ConstantPoolAccessIsInOverflow())) { |
| ConstantPoolEntry::Access access = ConstantPoolAddEntry(value); |
| if (access == ConstantPoolEntry::OVERFLOWED) { |
| addis(scratch, kConstantPoolRegister, Operand::Zero()); |
| lfd(result, MemOperand(scratch, 0)); |
| } else { |
| lfd(result, MemOperand(kConstantPoolRegister, 0)); |
| } |
| return; |
| } |
| |
| // avoid gcc strict aliasing error using union cast |
| union { |
| uint64_t dval; |
| intptr_t ival; |
| } litVal; |
| |
| litVal.dval = value.AsUint64(); |
| |
| mov(scratch, Operand(litVal.ival)); |
| mtfprd(result, scratch); |
| } |
| |
| void MacroAssembler::MovIntToDouble(DoubleRegister dst, Register src) { |
| // sign-extend src to 64-bit |
| mtfprwa(dst, src); |
| } |
| |
| void MacroAssembler::MovUnsignedIntToDouble(DoubleRegister dst, Register src) { |
| // zero-extend src to 64-bit |
| mtfprwz(dst, src); |
| } |
| |
| void MacroAssembler::MovInt64ToDouble(DoubleRegister dst, |
| Register src) { |
| mtfprd(dst, src); |
| } |
| |
| void MacroAssembler::MovInt64ComponentsToDouble(DoubleRegister dst, |
| Register src_hi, |
| Register src_lo) { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| ShiftLeftU64(scratch, src_hi, Operand(32)); |
| rldimi(scratch, src_lo, 0, 32); |
| mtfprd(dst, scratch); |
| } |
| |
| void MacroAssembler::InsertDoubleLow(DoubleRegister dst, Register src) { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| mffprd(scratch, dst); |
| rldimi(scratch, src, 0, 32); |
| mtfprd(dst, scratch); |
| } |
| |
| void MacroAssembler::InsertDoubleHigh(DoubleRegister dst, Register src) { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| mffprd(scratch, dst); |
| rldimi(scratch, src, 32, 0); |
| mtfprd(dst, scratch); |
| } |
| |
| void MacroAssembler::MovDoubleLowToInt(Register dst, DoubleRegister src) { |
| mffprwz(dst, src); |
| } |
| |
| void MacroAssembler::MovDoubleHighToInt(Register dst, DoubleRegister src) { |
| mffprd(dst, src); |
| srdi(dst, dst, Operand(32)); |
| } |
| |
| void MacroAssembler::MovDoubleToInt64(Register dst, DoubleRegister src) { |
| mffprd(dst, src); |
| } |
| |
| void MacroAssembler::MovIntToFloat(DoubleRegister dst, Register src) { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| ShiftLeftU64(scratch, src, Operand(32)); |
| mtfprd(dst, scratch); |
| xscvspdpn(dst, dst); |
| } |
| |
| void MacroAssembler::MovFloatToInt(Register dst, DoubleRegister src, |
| DoubleRegister scratch) { |
| xscvdpspn(scratch, src); |
| mffprwz(dst, scratch); |
| } |
| |
| void MacroAssembler::AddS64(Register dst, Register src, Register value, OEBit s, |
| RCBit r) { |
| add(dst, src, value, s, r); |
| } |
| |
| void MacroAssembler::AddS64(Register dst, Register src, const Operand& value, |
| OEBit s, RCBit r) { |
| if (is_int16(value.immediate()) && s == LeaveOE && r == LeaveRC) { |
| addi(dst, src, value); |
| } else { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| mov(scratch, value); |
| add(dst, src, scratch, s, r); |
| } |
| } |
| |
| void MacroAssembler::SubS64(Register dst, Register src, Register value, OEBit s, |
| RCBit r) { |
| sub(dst, src, value, s, r); |
| } |
| |
| void MacroAssembler::SubS64(Register dst, Register src, const Operand& value, |
| OEBit s, RCBit r) { |
| if (is_int16(value.immediate()) && s == LeaveOE && r == LeaveRC) { |
| subi(dst, src, value); |
| } else { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| mov(scratch, value); |
| sub(dst, src, scratch, s, r); |
| } |
| } |
| |
| void MacroAssembler::AddS32(Register dst, Register src, Register value, |
| RCBit r) { |
| AddS64(dst, src, value, LeaveOE, r); |
| extsw(dst, dst, r); |
| } |
| |
| void MacroAssembler::AddS32(Register dst, Register src, const Operand& value, |
| RCBit r) { |
| AddS64(dst, src, value, LeaveOE, r); |
| extsw(dst, dst, r); |
| } |
| |
| void MacroAssembler::SubS32(Register dst, Register src, Register value, |
| RCBit r) { |
| SubS64(dst, src, value, LeaveOE, r); |
| extsw(dst, dst, r); |
| } |
| |
| void MacroAssembler::SubS32(Register dst, Register src, const Operand& value, |
| RCBit r) { |
| SubS64(dst, src, value, LeaveOE, r); |
| extsw(dst, dst, r); |
| } |
| |
| void MacroAssembler::MulS64(Register dst, Register src, const Operand& value, |
| OEBit s, RCBit r) { |
| if (is_int16(value.immediate()) && s == LeaveOE && r == LeaveRC) { |
| mulli(dst, src, value); |
| } else { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| mov(scratch, value); |
| mulld(dst, src, scratch, s, r); |
| } |
| } |
| |
| void MacroAssembler::MulS64(Register dst, Register src, Register value, OEBit s, |
| RCBit r) { |
| mulld(dst, src, value, s, r); |
| } |
| |
| void MacroAssembler::MulS32(Register dst, Register src, const Operand& value, |
| OEBit s, RCBit r) { |
| MulS64(dst, src, value, s, r); |
| extsw(dst, dst, r); |
| } |
| |
| void MacroAssembler::MulS32(Register dst, Register src, Register value, OEBit s, |
| RCBit r) { |
| MulS64(dst, src, value, s, r); |
| extsw(dst, dst, r); |
| } |
| |
| void MacroAssembler::DivS64(Register dst, Register src, Register value, OEBit s, |
| RCBit r) { |
| divd(dst, src, value, s, r); |
| } |
| |
| void MacroAssembler::DivU64(Register dst, Register src, Register value, OEBit s, |
| RCBit r) { |
| divdu(dst, src, value, s, r); |
| } |
| |
| void MacroAssembler::DivS32(Register dst, Register src, Register value, OEBit s, |
| RCBit r) { |
| divw(dst, src, value, s, r); |
| extsw(dst, dst); |
| } |
| void MacroAssembler::DivU32(Register dst, Register src, Register value, OEBit s, |
| RCBit r) { |
| divwu(dst, src, value, s, r); |
| ZeroExtWord32(dst, dst); |
| } |
| |
| void MacroAssembler::ModS64(Register dst, Register src, Register value) { |
| modsd(dst, src, value); |
| } |
| |
| void MacroAssembler::ModU64(Register dst, Register src, Register value) { |
| modud(dst, src, value); |
| } |
| |
| void MacroAssembler::ModS32(Register dst, Register src, Register value) { |
| modsw(dst, src, value); |
| extsw(dst, dst); |
| } |
| void MacroAssembler::ModU32(Register dst, Register src, Register value) { |
| moduw(dst, src, value); |
| ZeroExtWord32(dst, dst); |
| } |
| |
| void MacroAssembler::AndU64(Register dst, Register src, const Operand& value, |
| RCBit r) { |
| if (is_uint16(value.immediate()) && r == SetRC) { |
| andi(dst, src, value); |
| } else { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| mov(scratch, value); |
| and_(dst, src, scratch, r); |
| } |
| } |
| |
| void MacroAssembler::AndU64(Register dst, Register src, Register value, |
| RCBit r) { |
| and_(dst, src, value, r); |
| } |
| |
| void MacroAssembler::OrU64(Register dst, Register src, const Operand& value, |
| RCBit r) { |
| if (is_int16(value.immediate()) && r == LeaveRC) { |
| ori(dst, src, value); |
| } else { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| mov(scratch, value); |
| orx(dst, src, scratch, r); |
| } |
| } |
| |
| void MacroAssembler::OrU64(Register dst, Register src, Register value, |
| RCBit r) { |
| orx(dst, src, value, r); |
| } |
| |
| void MacroAssembler::XorU64(Register dst, Register src, const Operand& value, |
| RCBit r) { |
| if (is_int16(value.immediate()) && r == LeaveRC) { |
| xori(dst, src, value); |
| } else { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| mov(scratch, value); |
| xor_(dst, src, scratch, r); |
| } |
| } |
| |
| void MacroAssembler::XorU64(Register dst, Register src, Register value, |
| RCBit r) { |
| xor_(dst, src, value, r); |
| } |
| |
| void MacroAssembler::AndU32(Register dst, Register src, const Operand& value, |
| RCBit r) { |
| AndU64(dst, src, value, r); |
| extsw(dst, dst, r); |
| } |
| |
| void MacroAssembler::AndU32(Register dst, Register src, Register value, |
| RCBit r) { |
| AndU64(dst, src, value, r); |
| extsw(dst, dst, r); |
| } |
| |
| void MacroAssembler::OrU32(Register dst, Register src, const Operand& value, |
| RCBit r) { |
| OrU64(dst, src, value, r); |
| extsw(dst, dst, r); |
| } |
| |
| void MacroAssembler::OrU32(Register dst, Register src, Register value, |
| RCBit r) { |
| OrU64(dst, src, value, r); |
| extsw(dst, dst, r); |
| } |
| |
| void MacroAssembler::XorU32(Register dst, Register src, const Operand& value, |
| RCBit r) { |
| XorU64(dst, src, value, r); |
| extsw(dst, dst, r); |
| } |
| |
| void MacroAssembler::XorU32(Register dst, Register src, Register value, |
| RCBit r) { |
| XorU64(dst, src, value, r); |
| extsw(dst, dst, r); |
| } |
| |
| void MacroAssembler::ShiftLeftU64(Register dst, Register src, |
| const Operand& value, RCBit r) { |
| sldi(dst, src, value, r); |
| } |
| |
| void MacroAssembler::ShiftRightU64(Register dst, Register src, |
| const Operand& value, RCBit r) { |
| srdi(dst, src, value, r); |
| } |
| |
| void MacroAssembler::ShiftRightS64(Register dst, Register src, |
| const Operand& value, RCBit r) { |
| sradi(dst, src, value.immediate(), r); |
| } |
| |
| void MacroAssembler::ShiftLeftU32(Register dst, Register src, |
| const Operand& value, RCBit r) { |
| slwi(dst, src, value, r); |
| } |
| |
| void MacroAssembler::ShiftRightU32(Register dst, Register src, |
| const Operand& value, RCBit r) { |
| srwi(dst, src, value, r); |
| } |
| |
| void MacroAssembler::ShiftRightS32(Register dst, Register src, |
| const Operand& value, RCBit r) { |
| srawi(dst, src, value.immediate(), r); |
| } |
| |
| void MacroAssembler::ShiftLeftU64(Register dst, Register src, Register value, |
| RCBit r) { |
| sld(dst, src, value, r); |
| } |
| |
| void MacroAssembler::ShiftRightU64(Register dst, Register src, Register value, |
| RCBit r) { |
| srd(dst, src, value, r); |
| } |
| |
| void MacroAssembler::ShiftRightS64(Register dst, Register src, Register value, |
| RCBit r) { |
| srad(dst, src, value, r); |
| } |
| |
| void MacroAssembler::ShiftLeftU32(Register dst, Register src, Register value, |
| RCBit r) { |
| slw(dst, src, value, r); |
| } |
| |
| void MacroAssembler::ShiftRightU32(Register dst, Register src, Register value, |
| RCBit r) { |
| srw(dst, src, value, r); |
| } |
| |
| void MacroAssembler::ShiftRightS32(Register dst, Register src, Register value, |
| RCBit r) { |
| sraw(dst, src, value, r); |
| } |
| |
| void MacroAssembler::CmpS64(Register src1, Register src2, CRegister cr) { |
| cmp(src1, src2, cr); |
| } |
| |
| void MacroAssembler::CmpS64(Register src1, const Operand& src2, CRegister cr) { |
| intptr_t value = src2.immediate(); |
| if (is_int16(value)) { |
| cmpi(src1, src2, cr); |
| } else { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| mov(scratch, src2); |
| CmpS64(src1, scratch, cr); |
| } |
| } |
| |
| void MacroAssembler::CmpU64(Register src1, const Operand& src2, CRegister cr) { |
| intptr_t value = src2.immediate(); |
| if (is_uint16(value)) { |
| cmpli(src1, src2, cr); |
| } else { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| mov(scratch, src2); |
| CmpU64(src1, scratch, cr); |
| } |
| } |
| |
| void MacroAssembler::CmpU64(Register src1, Register src2, CRegister cr) { |
| cmpl(src1, src2, cr); |
| } |
| |
| void MacroAssembler::CmpS32(Register src1, const Operand& src2, CRegister cr) { |
| intptr_t value = src2.immediate(); |
| if (is_int16(value)) { |
| cmpwi(src1, src2, cr); |
| } else { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| mov(scratch, src2); |
| CmpS32(src1, scratch, cr); |
| } |
| } |
| |
| void MacroAssembler::CmpS32(Register src1, Register src2, CRegister cr) { |
| cmpw(src1, src2, cr); |
| } |
| |
| void MacroAssembler::CmpU32(Register src1, const Operand& src2, CRegister cr) { |
| intptr_t value = src2.immediate(); |
| if (is_uint16(value)) { |
| cmplwi(src1, src2, cr); |
| } else { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| mov(scratch, src2); |
| CmpU32(src1, scratch, cr); |
| } |
| } |
| |
| void MacroAssembler::CmpU32(Register src1, Register src2, CRegister cr) { |
| cmplw(src1, src2, cr); |
| } |
| |
| void MacroAssembler::AddF64(DoubleRegister dst, DoubleRegister lhs, |
| DoubleRegister rhs, RCBit r) { |
| fadd(dst, lhs, rhs, r); |
| } |
| |
| void MacroAssembler::SubF64(DoubleRegister dst, DoubleRegister lhs, |
| DoubleRegister rhs, RCBit r) { |
| fsub(dst, lhs, rhs, r); |
| } |
| |
| void MacroAssembler::MulF64(DoubleRegister dst, DoubleRegister lhs, |
| DoubleRegister rhs, RCBit r) { |
| fmul(dst, lhs, rhs, r); |
| } |
| |
| void MacroAssembler::DivF64(DoubleRegister dst, DoubleRegister lhs, |
| DoubleRegister rhs, RCBit r) { |
| fdiv(dst, lhs, rhs, r); |
| } |
| |
| void MacroAssembler::AddF32(DoubleRegister dst, DoubleRegister lhs, |
| DoubleRegister rhs, RCBit r) { |
| fadd(dst, lhs, rhs, r); |
| frsp(dst, dst, r); |
| } |
| |
| void MacroAssembler::SubF32(DoubleRegister dst, DoubleRegister lhs, |
| DoubleRegister rhs, RCBit r) { |
| fsub(dst, lhs, rhs, r); |
| frsp(dst, dst, r); |
| } |
| |
| void MacroAssembler::MulF32(DoubleRegister dst, DoubleRegister lhs, |
| DoubleRegister rhs, RCBit r) { |
| fmul(dst, lhs, rhs, r); |
| frsp(dst, dst, r); |
| } |
| |
| void MacroAssembler::DivF32(DoubleRegister dst, DoubleRegister lhs, |
| DoubleRegister rhs, RCBit r) { |
| fdiv(dst, lhs, rhs, r); |
| frsp(dst, dst, r); |
| } |
| |
| void MacroAssembler::CopySignF64(DoubleRegister dst, DoubleRegister lhs, |
| DoubleRegister rhs, RCBit r) { |
| fcpsgn(dst, rhs, lhs, r); |
| } |
| |
| void MacroAssembler::CmpSmiLiteral(Register src1, Tagged<Smi> smi, |
| CRegister cr) { |
| #if defined(V8_COMPRESS_POINTERS) || defined(V8_31BIT_SMIS_ON_64BIT_ARCH) |
| CmpS32(src1, Operand(smi), cr); |
| #else |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| LoadSmiLiteral(scratch, smi); |
| CmpS64(src1, scratch, cr); |
| #endif |
| } |
| |
| void MacroAssembler::CmplSmiLiteral(Register src1, Tagged<Smi> smi, |
| CRegister cr) { |
| #if defined(V8_COMPRESS_POINTERS) || defined(V8_31BIT_SMIS_ON_64BIT_ARCH) |
| CmpU64(src1, Operand(smi), cr); |
| #else |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| LoadSmiLiteral(scratch, smi); |
| CmpU64(src1, scratch, cr); |
| #endif |
| } |
| |
| void MacroAssembler::AddSmiLiteral(Register dst, Register src, |
| Tagged<Smi> smi) { |
| #if defined(V8_COMPRESS_POINTERS) || defined(V8_31BIT_SMIS_ON_64BIT_ARCH) |
| AddS64(dst, src, Operand(smi.ptr())); |
| #else |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| LoadSmiLiteral(scratch, smi); |
| add(dst, src, scratch); |
| #endif |
| } |
| |
| void MacroAssembler::SubSmiLiteral(Register dst, Register src, |
| Tagged<Smi> smi) { |
| #if defined(V8_COMPRESS_POINTERS) || defined(V8_31BIT_SMIS_ON_64BIT_ARCH) |
| AddS64(dst, src, Operand(-(static_cast<intptr_t>(smi.ptr())))); |
| #else |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| LoadSmiLiteral(scratch, smi); |
| sub(dst, src, scratch); |
| #endif |
| } |
| |
| void MacroAssembler::AndSmiLiteral(Register dst, Register src, Tagged<Smi> smi, |
| RCBit rc) { |
| #if defined(V8_COMPRESS_POINTERS) || defined(V8_31BIT_SMIS_ON_64BIT_ARCH) |
| AndU64(dst, src, Operand(smi), rc); |
| #else |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| LoadSmiLiteral(scratch, smi); |
| and_(dst, src, scratch, rc); |
| #endif |
| } |
| |
| #define GenerateMemoryOperation(reg, mem, ri_op, rr_op) \ |
| { \ |
| int64_t offset = mem.offset(); \ |
| \ |
| if (mem.rb() == no_reg) { \ |
| if (!is_int16(offset)) { \ |
| /* cannot use d-form */ \ |
| UseScratchRegisterScope temps(this); \ |
| Register scratch = temps.Acquire(); \ |
| mov(scratch, Operand(offset)); \ |
| rr_op(reg, MemOperand(mem.ra(), scratch)); \ |
| } else { \ |
| ri_op(reg, mem); \ |
| } \ |
| } else { \ |
| if (offset == 0) { \ |
| rr_op(reg, mem); \ |
| } else if (is_int16(offset)) { \ |
| UseScratchRegisterScope temps(this); \ |
| Register scratch = temps.Acquire(); \ |
| addi(scratch, mem.rb(), Operand(offset)); \ |
| rr_op(reg, MemOperand(mem.ra(), scratch)); \ |
| } else { \ |
| UseScratchRegisterScope temps(this); \ |
| Register scratch = temps.Acquire(); \ |
| mov(scratch, Operand(offset)); \ |
| add(scratch, scratch, mem.rb()); \ |
| rr_op(reg, MemOperand(mem.ra(), scratch)); \ |
| } \ |
| } \ |
| } |
| |
| #define GenerateMemoryOperationRR(reg, mem, op) \ |
| { \ |
| if (mem.offset() == 0) { \ |
| if (mem.rb() != no_reg) \ |
| op(reg, mem); \ |
| else \ |
| op(reg, MemOperand(r0, mem.ra())); \ |
| } else if (is_int16(mem.offset())) { \ |
| UseScratchRegisterScope temps(this); \ |
| Register scratch = temps.Acquire(); \ |
| if (mem.rb() != no_reg) \ |
| addi(scratch, mem.rb(), Operand(mem.offset())); \ |
| else \ |
| mov(scratch, Operand(mem.offset())); \ |
| op(reg, MemOperand(mem.ra(), scratch)); \ |
| } else { \ |
| UseScratchRegisterScope temps(this); \ |
| Register scratch = temps.Acquire(); \ |
| mov(scratch, Operand(mem.offset())); \ |
| if (mem.rb() != no_reg) add(scratch, scratch, mem.rb()); \ |
| op(reg, MemOperand(mem.ra(), scratch)); \ |
| } \ |
| } |
| |
| #define GenerateMemoryOperationPrefixed(reg, mem, ri_op, rip_op, rr_op) \ |
| { \ |
| int64_t offset = mem.offset(); \ |
| \ |
| if (mem.rb() == no_reg) { \ |
| if (is_int16(offset)) { \ |
| ri_op(reg, mem); \ |
| } else if (is_int34(offset) && CpuFeatures::IsSupported(PPC_10_PLUS)) { \ |
| rip_op(reg, mem); \ |
| } else { \ |
| /* cannot use d-form */ \ |
| UseScratchRegisterScope temps(this); \ |
| Register scratch = temps.Acquire(); \ |
| mov(scratch, Operand(offset)); \ |
| rr_op(reg, MemOperand(mem.ra(), scratch)); \ |
| } \ |
| } else { \ |
| if (offset == 0) { \ |
| rr_op(reg, mem); \ |
| } else if (is_int16(offset)) { \ |
| UseScratchRegisterScope temps(this); \ |
| Register scratch = temps.Acquire(); \ |
| addi(scratch, mem.rb(), Operand(offset)); \ |
| rr_op(reg, MemOperand(mem.ra(), scratch)); \ |
| } else { \ |
| UseScratchRegisterScope temps(this); \ |
| Register scratch = temps.Acquire(); \ |
| mov(scratch, Operand(offset)); \ |
| add(scratch, scratch, mem.rb()); \ |
| rr_op(reg, MemOperand(mem.ra(), scratch)); \ |
| } \ |
| } \ |
| } |
| |
| #define GenerateMemoryOperationWithAlign(reg, mem, ri_op, rr_op) \ |
| { \ |
| int64_t offset = mem.offset(); \ |
| int misaligned = (offset & 3); \ |
| \ |
| if (mem.rb() == no_reg) { \ |
| if (!is_int16(offset) || misaligned) { \ |
| /* cannot use d-form */ \ |
| UseScratchRegisterScope temps(this); \ |
| Register scratch = temps.Acquire(); \ |
| mov(scratch, Operand(offset)); \ |
| rr_op(reg, MemOperand(mem.ra(), scratch)); \ |
| } else { \ |
| ri_op(reg, mem); \ |
| } \ |
| } else { \ |
| if (offset == 0) { \ |
| rr_op(reg, mem); \ |
| } else if (is_int16(offset)) { \ |
| UseScratchRegisterScope temps(this); \ |
| Register scratch = temps.Acquire(); \ |
| addi(scratch, mem.rb(), Operand(offset)); \ |
| rr_op(reg, MemOperand(mem.ra(), scratch)); \ |
| } else { \ |
| UseScratchRegisterScope temps(this); \ |
| Register scratch = temps.Acquire(); \ |
| mov(scratch, Operand(offset)); \ |
| add(scratch, scratch, mem.rb()); \ |
| rr_op(reg, MemOperand(mem.ra(), scratch)); \ |
| } \ |
| } \ |
| } |
| |
| #define GenerateMemoryOperationWithAlignPrefixed(reg, mem, ri_op, rip_op, \ |
| rr_op) \ |
| { \ |
| int64_t offset = mem.offset(); \ |
| int misaligned = (offset & 3); \ |
| \ |
| if (mem.rb() == no_reg) { \ |
| if (is_int16(offset) && !misaligned) { \ |
| ri_op(reg, mem); \ |
| } else if (is_int34(offset) && CpuFeatures::IsSupported(PPC_10_PLUS)) { \ |
| rip_op(reg, mem); \ |
| } else { \ |
| /* cannot use d-form */ \ |
| UseScratchRegisterScope temps(this); \ |
| Register scratch = temps.Acquire(); \ |
| mov(scratch, Operand(offset)); \ |
| rr_op(reg, MemOperand(mem.ra(), scratch)); \ |
| } \ |
| } else { \ |
| if (offset == 0) { \ |
| rr_op(reg, mem); \ |
| } else if (is_int16(offset)) { \ |
| UseScratchRegisterScope temps(this); \ |
| Register scratch = temps.Acquire(); \ |
| addi(scratch, mem.rb(), Operand(offset)); \ |
| rr_op(reg, MemOperand(mem.ra(), scratch)); \ |
| } else { \ |
| UseScratchRegisterScope temps(this); \ |
| Register scratch = temps.Acquire(); \ |
| mov(scratch, Operand(offset)); \ |
| add(scratch, scratch, mem.rb()); \ |
| rr_op(reg, MemOperand(mem.ra(), scratch)); \ |
| } \ |
| } \ |
| } |
| |
| #define MEM_OP_WITH_ALIGN_LIST(V) \ |
| V(LoadU64WithUpdate, ldu, ldux) \ |
| V(StoreU64WithUpdate, stdu, stdux) |
| |
| #define MEM_OP_WITH_ALIGN_FUNCTION(name, ri_op, rr_op) \ |
| void MacroAssembler::name(Register reg, const MemOperand& mem) { \ |
| GenerateMemoryOperationWithAlign(reg, mem, ri_op, rr_op); \ |
| } |
| MEM_OP_WITH_ALIGN_LIST(MEM_OP_WITH_ALIGN_FUNCTION) |
| #undef MEM_OP_WITH_ALIGN_LIST |
| #undef MEM_OP_WITH_ALIGN_FUNCTION |
| |
| #define MEM_OP_WITH_ALIGN_PREFIXED_LIST(V) \ |
| V(LoadS32, lwa, plwa, lwax) \ |
| V(LoadU64, ld, pld, ldx) \ |
| V(StoreU64, std, pstd, stdx) |
| |
| #define MEM_OP_WITH_ALIGN_PREFIXED_FUNCTION(name, ri_op, rip_op, rr_op) \ |
| void MacroAssembler::name(Register reg, const MemOperand& mem) { \ |
| GenerateMemoryOperationWithAlignPrefixed(reg, mem, ri_op, rip_op, rr_op); \ |
| } |
| MEM_OP_WITH_ALIGN_PREFIXED_LIST(MEM_OP_WITH_ALIGN_PREFIXED_FUNCTION) |
| #undef MEM_OP_WITH_ALIGN_PREFIXED_LIST |
| #undef MEM_OP_WITH_ALIGN_PREFIXED_FUNCTION |
| |
| #define MEM_OP_LIST(V) \ |
| V(LoadF64WithUpdate, DoubleRegister, lfdu, lfdux) \ |
| V(LoadF32WithUpdate, DoubleRegister, lfsu, lfsux) \ |
| V(StoreF64WithUpdate, DoubleRegister, stfdu, stfdux) \ |
| V(StoreF32WithUpdate, DoubleRegister, stfsu, stfsux) |
| |
| #define MEM_OP_FUNCTION(name, result_t, ri_op, rr_op) \ |
| void MacroAssembler::name(result_t reg, const MemOperand& mem) { \ |
| GenerateMemoryOperation(reg, mem, ri_op, rr_op); \ |
| } |
| MEM_OP_LIST(MEM_OP_FUNCTION) |
| #undef MEM_OP_LIST |
| #undef MEM_OP_FUNCTION |
| |
| #define MEM_OP_PREFIXED_LIST(V) \ |
| V(LoadU32, Register, lwz, plwz, lwzx) \ |
| V(LoadS16, Register, lha, plha, lhax) \ |
| V(LoadU16, Register, lhz, plhz, lhzx) \ |
| V(LoadU8, Register, lbz, plbz, lbzx) \ |
| V(StoreU32, Register, stw, pstw, stwx) \ |
| V(StoreU16, Register, sth, psth, sthx) \ |
| V(StoreU8, Register, stb, pstb, stbx) \ |
| V(LoadF64, DoubleRegister, lfd, plfd, lfdx) \ |
| V(LoadF32, DoubleRegister, lfs, plfs, lfsx) \ |
| V(StoreF64, DoubleRegister, stfd, pstfd, stfdx) \ |
| V(StoreF32, DoubleRegister, stfs, pstfs, stfsx) |
| |
| #define MEM_OP_PREFIXED_FUNCTION(name, result_t, ri_op, rip_op, rr_op) \ |
| void MacroAssembler::name(result_t reg, const MemOperand& mem) { \ |
| GenerateMemoryOperationPrefixed(reg, mem, ri_op, rip_op, rr_op); \ |
| } |
| MEM_OP_PREFIXED_LIST(MEM_OP_PREFIXED_FUNCTION) |
| #undef MEM_OP_PREFIXED_LIST |
| #undef MEM_OP_PREFIXED_FUNCTION |
| |
| #define MEM_OP_SIMD_LIST(V) \ |
| V(LoadSimd128, lxvx) \ |
| V(StoreSimd128, stxvx) \ |
| V(LoadSimd128Uint64, lxsdx) \ |
| V(LoadSimd128Uint32, lxsiwzx) \ |
| V(LoadSimd128Uint16, lxsihzx) \ |
| V(LoadSimd128Uint8, lxsibzx) \ |
| V(StoreSimd128Uint64, stxsdx) \ |
| V(StoreSimd128Uint32, stxsiwx) \ |
| V(StoreSimd128Uint16, stxsihx) \ |
| V(StoreSimd128Uint8, stxsibx) |
| |
| #define MEM_OP_SIMD_FUNCTION(name, rr_op) \ |
| void MacroAssembler::name(Simd128Register reg, const MemOperand& mem) { \ |
| GenerateMemoryOperationRR(reg, mem, rr_op); \ |
| } |
| MEM_OP_SIMD_LIST(MEM_OP_SIMD_FUNCTION) |
| #undef MEM_OP_SIMD_LIST |
| #undef MEM_OP_SIMD_FUNCTION |
| |
| void MacroAssembler::LoadS8(Register dst, const MemOperand& mem) { |
| LoadU8(dst, mem); |
| extsb(dst, dst); |
| } |
| |
| #define MEM_LE_OP_LIST(V) \ |
| V(LoadU64, ldbrx) \ |
| V(LoadU32, lwbrx) \ |
| V(LoadU16, lhbrx) \ |
| V(StoreU64, stdbrx) \ |
| V(StoreU32, stwbrx) \ |
| V(StoreU16, sthbrx) |
| |
| #ifdef V8_TARGET_BIG_ENDIAN |
| #define MEM_LE_OP_FUNCTION(name, op) \ |
| void MacroAssembler::name##LE(Register reg, const MemOperand& mem) { \ |
| GenerateMemoryOperationRR(reg, mem, op); \ |
| } |
| #else |
| #define MEM_LE_OP_FUNCTION(name, op) \ |
| void MacroAssembler::name##LE(Register reg, const MemOperand& mem) { \ |
| name(reg, mem); \ |
| } |
| #endif |
| MEM_LE_OP_LIST(MEM_LE_OP_FUNCTION) |
| #undef MEM_LE_OP_FUNCTION |
| #undef MEM_LE_OP_LIST |
| |
| void MacroAssembler::LoadS32LE(Register dst, const MemOperand& mem) { |
| #ifdef V8_TARGET_BIG_ENDIAN |
| LoadU32LE(dst, mem); |
| extsw(dst, dst); |
| #else |
| LoadS32(dst, mem); |
| #endif |
| } |
| |
| void MacroAssembler::LoadS16LE(Register dst, const MemOperand& mem) { |
| #ifdef V8_TARGET_BIG_ENDIAN |
| LoadU16LE(dst, mem); |
| extsh(dst, dst); |
| #else |
| LoadS16(dst, mem); |
| #endif |
| } |
| |
| void MacroAssembler::LoadF64LE(DoubleRegister dst, const MemOperand& mem) { |
| #ifdef V8_TARGET_BIG_ENDIAN |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| LoadU64LE(scratch, mem); |
| push(scratch); |
| LoadF64(dst, MemOperand(sp)); |
| pop(scratch); |
| #else |
| LoadF64(dst, mem); |
| #endif |
| } |
| |
| void MacroAssembler::LoadF32LE(DoubleRegister dst, const MemOperand& mem) { |
| #ifdef V8_TARGET_BIG_ENDIAN |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| LoadU32LE(scratch, mem); |
| push(scratch); |
| LoadF32(dst, MemOperand(sp, 4)); |
| pop(scratch); |
| #else |
| LoadF32(dst, mem); |
| #endif |
| } |
| |
| void MacroAssembler::StoreF64LE(DoubleRegister dst, const MemOperand& mem) { |
| #ifdef V8_TARGET_BIG_ENDIAN |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| StoreF64(dst, mem); |
| LoadU64(scratch, mem); |
| StoreU64LE(scratch, mem); |
| #else |
| StoreF64(dst, mem); |
| #endif |
| } |
| |
| void MacroAssembler::StoreF32LE(DoubleRegister dst, const MemOperand& mem) { |
| #ifdef V8_TARGET_BIG_ENDIAN |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| StoreF32(dst, mem); |
| LoadU32(scratch, mem); |
| StoreU32LE(scratch, mem); |
| #else |
| StoreF32(dst, mem); |
| #endif |
| } |
| |
| // Simd Support. |
| #define SIMD_BINOP_LIST(V) \ |
| V(F64x2Add, xvadddp) \ |
| V(F64x2Sub, xvsubdp) \ |
| V(F64x2Mul, xvmuldp) \ |
| V(F64x2Div, xvdivdp) \ |
| V(F64x2Eq, xvcmpeqdp) \ |
| V(F32x4Add, vaddfp) \ |
| V(F32x4Sub, vsubfp) \ |
| V(F32x4Mul, xvmulsp) \ |
| V(F32x4Div, xvdivsp) \ |
| V(F32x4Min, vminfp) \ |
| V(F32x4Max, vmaxfp) \ |
| V(F32x4Eq, xvcmpeqsp) \ |
| V(I64x2Add, vaddudm) \ |
| V(I64x2Sub, vsubudm) \ |
| V(I64x2Eq, vcmpequd) \ |
| V(I64x2GtS, vcmpgtsd) \ |
| V(I32x4Add, vadduwm) \ |
| V(I32x4Sub, vsubuwm) \ |
| V(I32x4Mul, vmuluwm) \ |
| V(I32x4MinS, vminsw) \ |
| V(I32x4MinU, vminuw) \ |
| V(I32x4MaxS, vmaxsw) \ |
| V(I32x4MaxU, vmaxuw) \ |
| V(I32x4Eq, vcmpequw) \ |
| V(I32x4GtS, vcmpgtsw) \ |
| V(I32x4GtU, vcmpgtuw) \ |
| V(I16x8Add, vadduhm) \ |
| V(I16x8Sub, vsubuhm) \ |
| V(I16x8MinS, vminsh) \ |
| V(I16x8MinU, vminuh) \ |
| V(I16x8MaxS, vmaxsh) \ |
| V(I16x8MaxU, vmaxuh) \ |
| V(I16x8Eq, vcmpequh) \ |
| V(I16x8GtS, vcmpgtsh) \ |
| V(I16x8GtU, vcmpgtuh) \ |
| V(I16x8AddSatS, vaddshs) \ |
| V(I16x8SubSatS, vsubshs) \ |
| V(I16x8AddSatU, vadduhs) \ |
| V(I16x8SubSatU, vsubuhs) \ |
| V(I16x8RoundingAverageU, vavguh) \ |
| V(I8x16Add, vaddubm) \ |
| V(I8x16Sub, vsububm) \ |
| V(I8x16MinS, vminsb) \ |
| V(I8x16MinU, vminub) \ |
| V(I8x16MaxS, vmaxsb) \ |
| V(I8x16MaxU, vmaxub) \ |
| V(I8x16Eq, vcmpequb) \ |
| V(I8x16GtS, vcmpgtsb) \ |
| V(I8x16GtU, vcmpgtub) \ |
| V(I8x16AddSatS, vaddsbs) \ |
| V(I8x16SubSatS, vsubsbs) \ |
| V(I8x16AddSatU, vaddubs) \ |
| V(I8x16SubSatU, vsububs) \ |
| V(I8x16RoundingAverageU, vavgub) \ |
| V(S128And, vand) \ |
| V(S128Or, vor) \ |
| V(S128Xor, vxor) \ |
| V(S128AndNot, vandc) |
| |
| #define EMIT_SIMD_BINOP(name, op) \ |
| void MacroAssembler::name(Simd128Register dst, Simd128Register src1, \ |
| Simd128Register src2) { \ |
| op(dst, src1, src2); \ |
| } |
| SIMD_BINOP_LIST(EMIT_SIMD_BINOP) |
| #undef EMIT_SIMD_BINOP |
| #undef SIMD_BINOP_LIST |
| |
| #define SIMD_SHIFT_LIST(V) \ |
| V(I64x2Shl, vsld) \ |
| V(I64x2ShrS, vsrad) \ |
| V(I64x2ShrU, vsrd) \ |
| V(I32x4Shl, vslw) \ |
| V(I32x4ShrS, vsraw) \ |
| V(I32x4ShrU, vsrw) \ |
| V(I16x8Shl, vslh) \ |
| V(I16x8ShrS, vsrah) \ |
| V(I16x8ShrU, vsrh) \ |
| V(I8x16Shl, vslb) \ |
| V(I8x16ShrS, vsrab) \ |
| V(I8x16ShrU, vsrb) |
| |
| #define EMIT_SIMD_SHIFT(name, op) \ |
| void MacroAssembler::name(Simd128Register dst, Simd128Register src1, \ |
| Register src2, Simd128Register scratch) { \ |
| mtvsrd(scratch, src2); \ |
| vspltb(scratch, scratch, Operand(7)); \ |
| op(dst, src1, scratch); \ |
| } \ |
| void MacroAssembler::name(Simd128Register dst, Simd128Register src1, \ |
| const Operand& src2, Register scratch1, \ |
| Simd128Register scratch2) { \ |
| mov(scratch1, src2); \ |
| name(dst, src1, scratch1, scratch2); \ |
| } |
| SIMD_SHIFT_LIST(EMIT_SIMD_SHIFT) |
| #undef EMIT_SIMD_SHIFT |
| #undef SIMD_SHIFT_LIST |
| |
| #define SIMD_UNOP_LIST(V) \ |
| V(F64x2Abs, xvabsdp) \ |
| V(F64x2Neg, xvnegdp) \ |
| V(F64x2Sqrt, xvsqrtdp) \ |
| V(F64x2Ceil, xvrdpip) \ |
| V(F64x2Floor, xvrdpim) \ |
| V(F64x2Trunc, xvrdpiz) \ |
| V(F32x4Abs, xvabssp) \ |
| V(F32x4Neg, xvnegsp) \ |
| V(F32x4Sqrt, xvsqrtsp) \ |
| V(F32x4Ceil, xvrspip) \ |
| V(F32x4Floor, xvrspim) \ |
| V(F32x4Trunc, xvrspiz) \ |
| V(F32x4SConvertI32x4, xvcvsxwsp) \ |
| V(F32x4UConvertI32x4, xvcvuxwsp) \ |
| V(I64x2Neg, vnegd) \ |
| V(I64x2SConvertI32x4Low, vupklsw) \ |
| V(I64x2SConvertI32x4High, vupkhsw) \ |
| V(I32x4Neg, vnegw) \ |
| V(I32x4SConvertI16x8Low, vupklsh) \ |
| V(I32x4SConvertI16x8High, vupkhsh) \ |
| V(I32x4UConvertF32x4, xvcvspuxws) \ |
| V(I16x8SConvertI8x16Low, vupklsb) \ |
| V(I16x8SConvertI8x16High, vupkhsb) \ |
| V(I8x16Popcnt, vpopcntb) |
| |
| #define EMIT_SIMD_UNOP(name, op) \ |
| void MacroAssembler::name(Simd128Register dst, Simd128Register src) { \ |
| op(dst, src); \ |
| } |
| SIMD_UNOP_LIST(EMIT_SIMD_UNOP) |
| #undef EMIT_SIMD_UNOP |
| #undef SIMD_UNOP_LIST |
| |
| #define EXT_MUL(dst_even, dst_odd, mul_even, mul_odd) \ |
| mul_even(dst_even, src1, src2); \ |
| mul_odd(dst_odd, src1, src2); |
| #define SIMD_EXT_MUL_LIST(V) \ |
| V(I32x4ExtMulLowI16x8S, vmulesh, vmulosh, vmrglw) \ |
| V(I32x4ExtMulHighI16x8S, vmulesh, vmulosh, vmrghw) \ |
| V(I32x4ExtMulLowI16x8U, vmuleuh, vmulouh, vmrglw) \ |
| V(I32x4ExtMulHighI16x8U, vmuleuh, vmulouh, vmrghw) \ |
| V(I16x8ExtMulLowI8x16S, vmulesb, vmulosb, vmrglh) \ |
| V(I16x8ExtMulHighI8x16S, vmulesb, vmulosb, vmrghh) \ |
| V(I16x8ExtMulLowI8x16U, vmuleub, vmuloub, vmrglh) \ |
| V(I16x8ExtMulHighI8x16U, vmuleub, vmuloub, vmrghh) |
| |
| #define EMIT_SIMD_EXT_MUL(name, mul_even, mul_odd, merge) \ |
| void MacroAssembler::name(Simd128Register dst, Simd128Register src1, \ |
| Simd128Register src2, Simd128Register scratch) { \ |
| EXT_MUL(scratch, dst, mul_even, mul_odd) \ |
| merge(dst, scratch, dst); \ |
| } |
| SIMD_EXT_MUL_LIST(EMIT_SIMD_EXT_MUL) |
| #undef EMIT_SIMD_EXT_MUL |
| #undef SIMD_EXT_MUL_LIST |
| |
| #define SIMD_ALL_TRUE_LIST(V) \ |
| V(I64x2AllTrue, vcmpgtud) \ |
| V(I32x4AllTrue, vcmpgtuw) \ |
| V(I16x8AllTrue, vcmpgtuh) \ |
| V(I8x16AllTrue, vcmpgtub) |
| |
| #define EMIT_SIMD_ALL_TRUE(name, op) \ |
| void MacroAssembler::name(Register dst, Simd128Register src, \ |
| Register scratch1, Register scratch2, \ |
| Simd128Register scratch3) { \ |
| constexpr uint8_t fxm = 0x2; /* field mask. */ \ |
| constexpr int bit_number = 24; \ |
| li(scratch1, Operand(0)); \ |
| li(scratch2, Operand(1)); \ |
| /* Check if all lanes > 0, if not then return false.*/ \ |
| vxor(scratch3, scratch3, scratch3); \ |
| mtcrf(scratch1, fxm); /* Clear cr6.*/ \ |
| op(scratch3, src, scratch3, SetRC); \ |
| isel(dst, scratch2, scratch1, bit_number); \ |
| } |
| SIMD_ALL_TRUE_LIST(EMIT_SIMD_ALL_TRUE) |
| #undef EMIT_SIMD_ALL_TRUE |
| #undef SIMD_ALL_TRUE_LIST |
| |
| #define SIMD_BITMASK_LIST(V) \ |
| V(I64x2BitMask, vextractdm, 0x8080808080800040) \ |
| V(I32x4BitMask, vextractwm, 0x8080808000204060) \ |
| V(I16x8BitMask, vextracthm, 0x10203040506070) |
| |
| #define EMIT_SIMD_BITMASK(name, op, indicies) \ |
| void MacroAssembler::name(Register dst, Simd128Register src, \ |
| Register scratch1, Simd128Register scratch2) { \ |
| if (CpuFeatures::IsSupported(PPC_10_PLUS)) { \ |
| op(dst, src); \ |
| } else { \ |
| mov(scratch1, Operand(indicies)); /* Select 0 for the high bits. */ \ |
| mtvsrd(scratch2, scratch1); \ |
| vbpermq(scratch2, src, scratch2); \ |
| vextractub(scratch2, scratch2, Operand(6)); \ |
| mfvsrd(dst, scratch2); \ |
| } \ |
| } |
| SIMD_BITMASK_LIST(EMIT_SIMD_BITMASK) |
| #undef EMIT_SIMD_BITMASK |
| #undef SIMD_BITMASK_LIST |
| |
| #define SIMD_QFM_LIST(V) \ |
| V(F64x2Qfma, xvmaddmdp) \ |
| V(F64x2Qfms, xvnmsubmdp) \ |
| V(F32x4Qfma, xvmaddmsp) \ |
| V(F32x4Qfms, xvnmsubmsp) |
| |
| #define EMIT_SIMD_QFM(name, op) \ |
| void MacroAssembler::name(Simd128Register dst, Simd128Register src1, \ |
| Simd128Register src2, Simd128Register src3, \ |
| Simd128Register scratch) { \ |
| Simd128Register dest = dst; \ |
| if (dst != src1) { \ |
| vor(scratch, src1, src1); \ |
| dest = scratch; \ |
| } \ |
| op(dest, src2, src3); \ |
| if (dest != dst) { \ |
| vor(dst, dest, dest); \ |
| } \ |
| } |
| SIMD_QFM_LIST(EMIT_SIMD_QFM) |
| #undef EMIT_SIMD_QFM |
| #undef SIMD_QFM_LIST |
| |
| void MacroAssembler::I64x2ExtMulLowI32x4S(Simd128Register dst, |
| Simd128Register src1, |
| Simd128Register src2, |
| Simd128Register scratch) { |
| constexpr int lane_width_in_bytes = 8; |
| EXT_MUL(scratch, dst, vmulesw, vmulosw) |
| vextractd(scratch, scratch, Operand(1 * lane_width_in_bytes)); |
| vinsertd(dst, scratch, Operand(0)); |
| } |
| |
| void MacroAssembler::I64x2ExtMulHighI32x4S(Simd128Register dst, |
| Simd128Register src1, |
| Simd128Register src2, |
| Simd128Register scratch) { |
| constexpr int lane_width_in_bytes = 8; |
| EXT_MUL(scratch, dst, vmulesw, vmulosw) |
| vinsertd(scratch, dst, Operand(1 * lane_width_in_bytes)); |
| vor(dst, scratch, scratch); |
| } |
| |
| void MacroAssembler::I64x2ExtMulLowI32x4U(Simd128Register dst, |
| Simd128Register src1, |
| Simd128Register src2, |
| Simd128Register scratch) { |
| constexpr int lane_width_in_bytes = 8; |
| EXT_MUL(scratch, dst, vmuleuw, vmulouw) |
| vextractd(scratch, scratch, Operand(1 * lane_width_in_bytes)); |
| vinsertd(dst, scratch, Operand(0)); |
| } |
| |
| void MacroAssembler::I64x2ExtMulHighI32x4U(Simd128Register dst, |
| Simd128Register src1, |
| Simd128Register src2, |
| Simd128Register scratch) { |
| constexpr int lane_width_in_bytes = 8; |
| EXT_MUL(scratch, dst, vmuleuw, vmulouw) |
| vinsertd(scratch, dst, Operand(1 * lane_width_in_bytes)); |
| vor(dst, scratch, scratch); |
| } |
| #undef EXT_MUL |
| |
| void MacroAssembler::LoadSimd128LE(Simd128Register dst, const MemOperand& mem) { |
| #ifdef V8_TARGET_BIG_ENDIAN |
| LoadSimd128(dst, mem); |
| xxbrq(dst, dst); |
| #else |
| LoadSimd128(dst, mem); |
| #endif |
| } |
| |
| void MacroAssembler::StoreSimd128LE(Simd128Register src, const MemOperand& mem, |
| Simd128Register scratch) { |
| #ifdef V8_TARGET_BIG_ENDIAN |
| xxbrq(scratch, src); |
| StoreSimd128(scratch, mem); |
| #else |
| StoreSimd128(src, mem); |
| #endif |
| } |
| |
| void MacroAssembler::F64x2Splat(Simd128Register dst, DoubleRegister src, |
| Register scratch) { |
| constexpr int lane_width_in_bytes = 8; |
| MovDoubleToInt64(scratch, src); |
| mtvsrd(dst, scratch); |
| vinsertd(dst, dst, Operand(1 * lane_width_in_bytes)); |
| } |
| |
| void MacroAssembler::F32x4Splat(Simd128Register dst, DoubleRegister src, |
| DoubleRegister scratch1, Register scratch2) { |
| MovFloatToInt(scratch2, src, scratch1); |
| mtvsrd(dst, scratch2); |
| vspltw(dst, dst, Operand(1)); |
| } |
| |
| void MacroAssembler::I64x2Splat(Simd128Register dst, Register src) { |
| constexpr int lane_width_in_bytes = 8; |
| mtvsrd(dst, src); |
| vinsertd(dst, dst, Operand(1 * lane_width_in_bytes)); |
| } |
| |
| void MacroAssembler::I32x4Splat(Simd128Register dst, Register src) { |
| mtvsrd(dst, src); |
| vspltw(dst, dst, Operand(1)); |
| } |
| |
| void MacroAssembler::I16x8Splat(Simd128Register dst, Register src) { |
| mtvsrd(dst, src); |
| vsplth(dst, dst, Operand(3)); |
| } |
| |
| void MacroAssembler::I8x16Splat(Simd128Register dst, Register src) { |
| mtvsrd(dst, src); |
| vspltb(dst, dst, Operand(7)); |
| } |
| |
| void MacroAssembler::F64x2ExtractLane(DoubleRegister dst, Simd128Register src, |
| uint8_t imm_lane_idx, |
| Simd128Register scratch1, |
| Register scratch2) { |
| constexpr int lane_width_in_bytes = 8; |
| vextractd(scratch1, src, Operand((1 - imm_lane_idx) * lane_width_in_bytes)); |
| mfvsrd(scratch2, scratch1); |
| MovInt64ToDouble(dst, scratch2); |
| } |
| |
| void MacroAssembler::F32x4ExtractLane(DoubleRegister dst, Simd128Register src, |
| uint8_t imm_lane_idx, |
| Simd128Register scratch1, |
| Register scratch2) { |
| constexpr int lane_width_in_bytes = 4; |
| vextractuw(scratch1, src, Operand((3 - imm_lane_idx) * lane_width_in_bytes)); |
| mfvsrd(scratch2, scratch1); |
| MovIntToFloat(dst, scratch2); |
| } |
| |
| void MacroAssembler::I64x2ExtractLane(Register dst, Simd128Register src, |
| uint8_t imm_lane_idx, |
| Simd128Register scratch) { |
| constexpr int lane_width_in_bytes = 8; |
| vextractd(scratch, src, Operand((1 - imm_lane_idx) * lane_width_in_bytes)); |
| mfvsrd(dst, scratch); |
| } |
| |
| void MacroAssembler::I32x4ExtractLane(Register dst, Simd128Register src, |
| uint8_t imm_lane_idx, |
| Simd128Register scratch) { |
| constexpr int lane_width_in_bytes = 4; |
| vextractuw(scratch, src, Operand((3 - imm_lane_idx) * lane_width_in_bytes)); |
| mfvsrd(dst, scratch); |
| } |
| |
| void MacroAssembler::I16x8ExtractLaneU(Register dst, Simd128Register src, |
| uint8_t imm_lane_idx, |
| Simd128Register scratch) { |
| constexpr int lane_width_in_bytes = 2; |
| vextractuh(scratch, src, Operand((7 - imm_lane_idx) * lane_width_in_bytes)); |
| mfvsrd(dst, scratch); |
| } |
| |
| void MacroAssembler::I16x8ExtractLaneS(Register dst, Simd128Register src, |
| uint8_t imm_lane_idx, |
| Simd128Register scratch) { |
| I16x8ExtractLaneU(dst, src, imm_lane_idx, scratch); |
| extsh(dst, dst); |
| } |
| |
| void MacroAssembler::I8x16ExtractLaneU(Register dst, Simd128Register src, |
| uint8_t imm_lane_idx, |
| Simd128Register scratch) { |
| vextractub(scratch, src, Operand(15 - imm_lane_idx)); |
| mfvsrd(dst, scratch); |
| } |
| |
| void MacroAssembler::I8x16ExtractLaneS(Register dst, Simd128Register src, |
| uint8_t imm_lane_idx, |
| Simd128Register scratch) { |
| I8x16ExtractLaneU(dst, src, imm_lane_idx, scratch); |
| extsb(dst, dst); |
| } |
| |
| void MacroAssembler::F64x2ReplaceLane(Simd128Register dst, Simd128Register src1, |
| DoubleRegister src2, uint8_t imm_lane_idx, |
| Register scratch1, |
| Simd128Register scratch2) { |
| constexpr int lane_width_in_bytes = 8; |
| if (src1 != dst) { |
| vor(dst, src1, src1); |
| } |
| MovDoubleToInt64(scratch1, src2); |
| if (CpuFeatures::IsSupported(PPC_10_PLUS)) { |
| vinsd(dst, scratch1, Operand((1 - imm_lane_idx) * lane_width_in_bytes)); |
| } else { |
| mtvsrd(scratch2, scratch1); |
| vinsertd(dst, scratch2, Operand((1 - imm_lane_idx) * lane_width_in_bytes)); |
| } |
| } |
| |
| void MacroAssembler::F32x4ReplaceLane(Simd128Register dst, Simd128Register src1, |
| DoubleRegister src2, uint8_t imm_lane_idx, |
| Register scratch1, |
| DoubleRegister scratch2, |
| Simd128Register scratch3) { |
| constexpr int lane_width_in_bytes = 4; |
| if (src1 != dst) { |
| vor(dst, src1, src1); |
| } |
| MovFloatToInt(scratch1, src2, scratch2); |
| if (CpuFeatures::IsSupported(PPC_10_PLUS)) { |
| vinsw(dst, scratch1, Operand((3 - imm_lane_idx) * lane_width_in_bytes)); |
| } else { |
| mtvsrd(scratch3, scratch1); |
| vinsertw(dst, scratch3, Operand((3 - imm_lane_idx) * lane_width_in_bytes)); |
| } |
| } |
| |
| void MacroAssembler::I64x2ReplaceLane(Simd128Register dst, Simd128Register src1, |
| Register src2, uint8_t imm_lane_idx, |
| Simd128Register scratch) { |
| constexpr int lane_width_in_bytes = 8; |
| if (src1 != dst) { |
| vor(dst, src1, src1); |
| } |
| if (CpuFeatures::IsSupported(PPC_10_PLUS)) { |
| vinsd(dst, src2, Operand((1 - imm_lane_idx) * lane_width_in_bytes)); |
| } else { |
| mtvsrd(scratch, src2); |
| vinsertd(dst, scratch, Operand((1 - imm_lane_idx) * lane_width_in_bytes)); |
| } |
| } |
| |
| void MacroAssembler::I32x4ReplaceLane(Simd128Register dst, Simd128Register src1, |
| Register src2, uint8_t imm_lane_idx, |
| Simd128Register scratch) { |
| constexpr int lane_width_in_bytes = 4; |
| if (src1 != dst) { |
| vor(dst, src1, src1); |
| } |
| if (CpuFeatures::IsSupported(PPC_10_PLUS)) { |
| vinsw(dst, src2, Operand((3 - imm_lane_idx) * lane_width_in_bytes)); |
| } else { |
| mtvsrd(scratch, src2); |
| vinsertw(dst, scratch, Operand((3 - imm_lane_idx) * lane_width_in_bytes)); |
| } |
| } |
| |
| void MacroAssembler::I16x8ReplaceLane(Simd128Register dst, Simd128Register src1, |
| Register src2, uint8_t imm_lane_idx, |
| Simd128Register scratch) { |
| constexpr int lane_width_in_bytes = 2; |
| if (src1 != dst) { |
| vor(dst, src1, src1); |
| } |
| mtvsrd(scratch, src2); |
| vinserth(dst, scratch, Operand((7 - imm_lane_idx) * lane_width_in_bytes)); |
| } |
| |
| void MacroAssembler::I8x16ReplaceLane(Simd128Register dst, Simd128Register src1, |
| Register src2, uint8_t imm_lane_idx, |
| Simd128Register scratch) { |
| if (src1 != dst) { |
| vor(dst, src1, src1); |
| } |
| mtvsrd(scratch, src2); |
| vinsertb(dst, scratch, Operand(15 - imm_lane_idx)); |
| } |
| |
| void MacroAssembler::I64x2Mul(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2, Register scratch1, |
| Register scratch2, Register scratch3, |
| Simd128Register scratch4) { |
| constexpr int lane_width_in_bytes = 8; |
| if (CpuFeatures::IsSupported(PPC_10_PLUS)) { |
| vmulld(dst, src1, src2); |
| } else { |
| DCHECK(scratch1 != r0); |
| Register scratch_1 = scratch1; |
| Register scratch_2 = scratch2; |
| for (int i = 0; i < 2; i++) { |
| if (i > 0) { |
| vextractd(scratch4, src1, Operand(1 * lane_width_in_bytes)); |
| vextractd(dst, src2, Operand(1 * lane_width_in_bytes)); |
| src1 = scratch4; |
| src2 = dst; |
| } |
| mfvsrd(scratch_1, src1); |
| mfvsrd(scratch_2, src2); |
| mulld(scratch_1, scratch_1, scratch_2); |
| scratch_1 = scratch2; |
| scratch_2 = scratch3; |
| } |
| mtvsrdd(dst, scratch1, scratch2); |
| } |
| } |
| |
| void MacroAssembler::I16x8Mul(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2) { |
| vxor(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); |
| vmladduhm(dst, src1, src2, kSimd128RegZero); |
| } |
| |
| #define F64X2_MIN_MAX_NAN(result) \ |
| xvcmpeqdp(scratch2, src1, src1); \ |
| vsel(result, src1, result, scratch2); \ |
| xvcmpeqdp(scratch2, src2, src2); \ |
| vsel(dst, src2, result, scratch2); \ |
| /* Use xvmindp to turn any selected SNANs to QNANs. */ \ |
| xvmindp(dst, dst, dst); |
| void MacroAssembler::F64x2Min(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2, Simd128Register scratch1, |
| Simd128Register scratch2) { |
| xvmindp(scratch1, src1, src2); |
| // We need to check if an input is NAN and preserve it. |
| F64X2_MIN_MAX_NAN(scratch1) |
| } |
| |
| void MacroAssembler::F64x2Max(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2, Simd128Register scratch1, |
| Simd128Register scratch2) { |
| xvmaxdp(scratch1, src1, src2); |
| // We need to check if an input is NAN and preserve it. |
| F64X2_MIN_MAX_NAN(scratch1) |
| } |
| #undef F64X2_MIN_MAX_NAN |
| |
| void MacroAssembler::F64x2Lt(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2) { |
| xvcmpgtdp(dst, src2, src1); |
| } |
| |
| void MacroAssembler::F64x2Le(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2) { |
| xvcmpgedp(dst, src2, src1); |
| } |
| |
| void MacroAssembler::F64x2Ne(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2, Simd128Register scratch) { |
| xvcmpeqdp(scratch, src1, src2); |
| vnor(dst, scratch, scratch); |
| } |
| |
| void MacroAssembler::F32x4Lt(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2) { |
| xvcmpgtsp(dst, src2, src1); |
| } |
| |
| void MacroAssembler::F32x4Le(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2) { |
| xvcmpgesp(dst, src2, src1); |
| } |
| |
| void MacroAssembler::F32x4Ne(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2, Simd128Register scratch) { |
| xvcmpeqsp(scratch, src1, src2); |
| vnor(dst, scratch, scratch); |
| } |
| |
| void MacroAssembler::I64x2Ne(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2, Simd128Register scratch) { |
| vcmpequd(scratch, src1, src2); |
| vnor(dst, scratch, scratch); |
| } |
| |
| void MacroAssembler::I64x2GeS(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2, Simd128Register scratch) { |
| vcmpgtsd(scratch, src2, src1); |
| vnor(dst, scratch, scratch); |
| } |
| |
| void MacroAssembler::I32x4Ne(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2, Simd128Register scratch) { |
| vcmpequw(scratch, src1, src2); |
| vnor(dst, scratch, scratch); |
| } |
| |
| void MacroAssembler::I32x4GeS(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2, Simd128Register scratch) { |
| vcmpgtsw(scratch, src2, src1); |
| vnor(dst, scratch, scratch); |
| } |
| |
| void MacroAssembler::I32x4GeU(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2, Simd128Register scratch) { |
| vcmpequw(scratch, src1, src2); |
| vcmpgtuw(dst, src1, src2); |
| vor(dst, dst, scratch); |
| } |
| |
| void MacroAssembler::I16x8Ne(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2, Simd128Register scratch) { |
| vcmpequh(scratch, src1, src2); |
| vnor(dst, scratch, scratch); |
| } |
| |
| void MacroAssembler::I16x8GeS(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2, Simd128Register scratch) { |
| vcmpgtsh(scratch, src2, src1); |
| vnor(dst, scratch, scratch); |
| } |
| |
| void MacroAssembler::I16x8GeU(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2, Simd128Register scratch) { |
| vcmpequh(scratch, src1, src2); |
| vcmpgtuh(dst, src1, src2); |
| vor(dst, dst, scratch); |
| } |
| |
| void MacroAssembler::I8x16Ne(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2, Simd128Register scratch) { |
| vcmpequb(scratch, src1, src2); |
| vnor(dst, scratch, scratch); |
| } |
| |
| void MacroAssembler::I8x16GeS(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2, Simd128Register scratch) { |
| vcmpgtsb(scratch, src2, src1); |
| vnor(dst, scratch, scratch); |
| } |
| |
| void MacroAssembler::I8x16GeU(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2, Simd128Register scratch) { |
| vcmpequb(scratch, src1, src2); |
| vcmpgtub(dst, src1, src2); |
| vor(dst, dst, scratch); |
| } |
| |
| void MacroAssembler::I64x2Abs(Simd128Register dst, Simd128Register src, |
| Simd128Register scratch) { |
| constexpr int shift_bits = 63; |
| xxspltib(scratch, Operand(shift_bits)); |
| vsrad(scratch, src, scratch); |
| vxor(dst, src, scratch); |
| vsubudm(dst, dst, scratch); |
| } |
| void MacroAssembler::I32x4Abs(Simd128Register dst, Simd128Register src, |
| Simd128Register scratch) { |
| constexpr int shift_bits = 31; |
| xxspltib(scratch, Operand(shift_bits)); |
| vsraw(scratch, src, scratch); |
| vxor(dst, src, scratch); |
| vsubuwm(dst, dst, scratch); |
| } |
| void MacroAssembler::I16x8Abs(Simd128Register dst, Simd128Register src, |
| Simd128Register scratch) { |
| constexpr int shift_bits = 15; |
| xxspltib(scratch, Operand(shift_bits)); |
| vsrah(scratch, src, scratch); |
| vxor(dst, src, scratch); |
| vsubuhm(dst, dst, scratch); |
| } |
| void MacroAssembler::I16x8Neg(Simd128Register dst, Simd128Register src, |
| Simd128Register scratch) { |
| vspltish(scratch, Operand(1)); |
| vnor(dst, src, src); |
| vadduhm(dst, scratch, dst); |
| } |
| void MacroAssembler::I8x16Abs(Simd128Register dst, Simd128Register src, |
| Simd128Register scratch) { |
| constexpr int shift_bits = 7; |
| xxspltib(scratch, Operand(shift_bits)); |
| vsrab(scratch, src, scratch); |
| vxor(dst, src, scratch); |
| vsububm(dst, dst, scratch); |
| } |
| void MacroAssembler::I8x16Neg(Simd128Register dst, Simd128Register src, |
| Simd128Register scratch) { |
| xxspltib(scratch, Operand(1)); |
| vnor(dst, src, src); |
| vaddubm(dst, scratch, dst); |
| } |
| |
| void MacroAssembler::F64x2Pmin(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2, Simd128Register scratch) { |
| xvcmpgtdp(kScratchSimd128Reg, src1, src2); |
| vsel(dst, src1, src2, kScratchSimd128Reg); |
| } |
| |
| void MacroAssembler::F64x2Pmax(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2, Simd128Register scratch) { |
| xvcmpgtdp(kScratchSimd128Reg, src2, src1); |
| vsel(dst, src1, src2, kScratchSimd128Reg); |
| } |
| |
| void MacroAssembler::F32x4Pmin(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2, Simd128Register scratch) { |
| xvcmpgtsp(kScratchSimd128Reg, src1, src2); |
| vsel(dst, src1, src2, kScratchSimd128Reg); |
| } |
| |
| void MacroAssembler::F32x4Pmax(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2, Simd128Register scratch) { |
| xvcmpgtsp(kScratchSimd128Reg, src2, src1); |
| vsel(dst, src1, src2, kScratchSimd128Reg); |
| } |
| |
| void MacroAssembler::I32x4SConvertF32x4(Simd128Register dst, |
| Simd128Register src, |
| Simd128Register scratch) { |
| // NaN to 0 |
| xvcmpeqsp(scratch, src, src); |
| vand(scratch, src, scratch); |
| xvcvspsxws(dst, scratch); |
| } |
| |
| void MacroAssembler::I16x8SConvertI32x4(Simd128Register dst, |
| Simd128Register src1, |
| Simd128Register src2) { |
| vpkswss(dst, src2, src1); |
| } |
| |
| void MacroAssembler::I16x8UConvertI32x4(Simd128Register dst, |
| Simd128Register src1, |
| Simd128Register src2) { |
| vpkswus(dst, src2, src1); |
| } |
| |
| void MacroAssembler::I8x16SConvertI16x8(Simd128Register dst, |
| Simd128Register src1, |
| Simd128Register src2) { |
| vpkshss(dst, src2, src1); |
| } |
| |
| void MacroAssembler::I8x16UConvertI16x8(Simd128Register dst, |
| Simd128Register src1, |
| Simd128Register src2) { |
| vpkshus(dst, src2, src1); |
| } |
| |
| void MacroAssembler::F64x2ConvertLowI32x4S(Simd128Register dst, |
| Simd128Register src) { |
| vupklsw(dst, src); |
| xvcvsxddp(dst, dst); |
| } |
| |
| void MacroAssembler::F64x2ConvertLowI32x4U(Simd128Register dst, |
| Simd128Register src, |
| Register scratch1, |
| Simd128Register scratch2) { |
| constexpr int lane_width_in_bytes = 8; |
| vupklsw(dst, src); |
| // Zero extend. |
| mov(scratch1, Operand(0xFFFFFFFF)); |
| mtvsrd(scratch2, scratch1); |
| vinsertd(scratch2, scratch2, Operand(1 * lane_width_in_bytes)); |
| vand(dst, scratch2, dst); |
| xvcvuxddp(dst, dst); |
| } |
| |
| void MacroAssembler::I64x2UConvertI32x4Low(Simd128Register dst, |
| Simd128Register src, |
| Register scratch1, |
| Simd128Register scratch2) { |
| constexpr int lane_width_in_bytes = 8; |
| vupklsw(dst, src); |
| // Zero extend. |
| mov(scratch1, Operand(0xFFFFFFFF)); |
| mtvsrd(scratch2, scratch1); |
| vinsertd(scratch2, scratch2, Operand(1 * lane_width_in_bytes)); |
| vand(dst, scratch2, dst); |
| } |
| |
| void MacroAssembler::I64x2UConvertI32x4High(Simd128Register dst, |
| Simd128Register src, |
| Register scratch1, |
| Simd128Register scratch2) { |
| constexpr int lane_width_in_bytes = 8; |
| vupkhsw(dst, src); |
| // Zero extend. |
| mov(scratch1, Operand(0xFFFFFFFF)); |
| mtvsrd(scratch2, scratch1); |
| vinsertd(scratch2, scratch2, Operand(1 * lane_width_in_bytes)); |
| vand(dst, scratch2, dst); |
| } |
| |
| void MacroAssembler::I32x4UConvertI16x8Low(Simd128Register dst, |
| Simd128Register src, |
| Register scratch1, |
| Simd128Register scratch2) { |
| vupklsh(dst, src); |
| // Zero extend. |
| mov(scratch1, Operand(0xFFFF)); |
| mtvsrd(scratch2, scratch1); |
| vspltw(scratch2, scratch2, Operand(1)); |
| vand(dst, scratch2, dst); |
| } |
| |
| void MacroAssembler::I32x4UConvertI16x8High(Simd128Register dst, |
| Simd128Register src, |
| Register scratch1, |
| Simd128Register scratch2) { |
| vupkhsh(dst, src); |
| // Zero extend. |
| mov(scratch1, Operand(0xFFFF)); |
| mtvsrd(scratch2, scratch1); |
| vspltw(scratch2, scratch2, Operand(1)); |
| vand(dst, scratch2, dst); |
| } |
| |
| void MacroAssembler::I16x8UConvertI8x16Low(Simd128Register dst, |
| Simd128Register src, |
| Register scratch1, |
| Simd128Register scratch2) { |
| vupklsb(dst, src); |
| // Zero extend. |
| li(scratch1, Operand(0xFF)); |
| mtvsrd(scratch2, scratch1); |
| vsplth(scratch2, scratch2, Operand(3)); |
| vand(dst, scratch2, dst); |
| } |
| |
| void MacroAssembler::I16x8UConvertI8x16High(Simd128Register dst, |
| Simd128Register src, |
| Register scratch1, |
| Simd128Register scratch2) { |
| vupkhsb(dst, src); |
| // Zero extend. |
| li(scratch1, Operand(0xFF)); |
| mtvsrd(scratch2, scratch1); |
| vsplth(scratch2, scratch2, Operand(3)); |
| vand(dst, scratch2, dst); |
| } |
| |
| void MacroAssembler::I8x16BitMask(Register dst, Simd128Register src, |
| Register scratch1, Register scratch2, |
| Simd128Register scratch3) { |
| if (CpuFeatures::IsSupported(PPC_10_PLUS)) { |
| vextractbm(dst, src); |
| } else { |
| DCHECK(scratch1 != r0); |
| mov(scratch1, Operand(0x8101820283038)); |
| mov(scratch2, Operand(0x4048505860687078)); |
| mtvsrdd(scratch3, scratch1, scratch2); |
| vbpermq(scratch3, src, scratch3); |
| mfvsrd(dst, scratch3); |
| } |
| } |
| |
| void MacroAssembler::I32x4DotI16x8S(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2) { |
| vxor(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); |
| vmsumshm(dst, src1, src2, kSimd128RegZero); |
| } |
| |
| void MacroAssembler::I32x4DotI8x16AddS(Simd128Register dst, |
| Simd128Register src1, |
| Simd128Register src2, |
| Simd128Register src3) { |
| vmsummbm(dst, src1, src2, src3); |
| } |
| |
| void MacroAssembler::I16x8DotI8x16S(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2, |
| Simd128Register scratch) { |
| vmulesb(scratch, src1, src2); |
| vmulosb(dst, src1, src2); |
| vadduhm(dst, scratch, dst); |
| } |
| |
| void MacroAssembler::I16x8Q15MulRSatS(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2) { |
| vxor(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); |
| vmhraddshs(dst, src1, src2, kSimd128RegZero); |
| } |
| |
| void MacroAssembler::I8x16Swizzle(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2, |
| Simd128Register scratch) { |
| // Saturate the indices to 5 bits. Input indices more than 31 should |
| // return 0. |
| xxspltib(scratch, Operand(31)); |
| vminub(scratch, src2, scratch); |
| // Input needs to be reversed. |
| xxbrq(dst, src1); |
| vxor(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); |
| vperm(dst, dst, kSimd128RegZero, scratch); |
| } |
| |
| void MacroAssembler::I8x16Shuffle(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2, uint64_t high, |
| uint64_t low, Register scratch1, |
| Register scratch2, Simd128Register scratch3) { |
| DCHECK(scratch2 != r0); |
| mov(scratch1, Operand(low)); |
| mov(scratch2, Operand(high)); |
| mtvsrdd(scratch3, scratch2, scratch1); |
| vperm(dst, src1, src2, scratch3); |
| } |
| |
| #define EXT_ADD_PAIRWISE(splat, mul_even, mul_odd, add) \ |
| splat(scratch1, Operand(1)); \ |
| mul_even(scratch2, src, scratch1); \ |
| mul_odd(scratch1, src, scratch1); \ |
| add(dst, scratch2, scratch1); |
| void MacroAssembler::I32x4ExtAddPairwiseI16x8S(Simd128Register dst, |
| Simd128Register src, |
| Simd128Register scratch1, |
| Simd128Register scratch2) { |
| EXT_ADD_PAIRWISE(vspltish, vmulesh, vmulosh, vadduwm) |
| } |
| void MacroAssembler::I32x4ExtAddPairwiseI16x8U(Simd128Register dst, |
| Simd128Register src, |
| Simd128Register scratch1, |
| Simd128Register scratch2) { |
| EXT_ADD_PAIRWISE(vspltish, vmuleuh, vmulouh, vadduwm) |
| } |
| void MacroAssembler::I16x8ExtAddPairwiseI8x16S(Simd128Register dst, |
| Simd128Register src, |
| Simd128Register scratch1, |
| Simd128Register scratch2) { |
| EXT_ADD_PAIRWISE(xxspltib, vmulesb, vmulosb, vadduhm) |
| } |
| void MacroAssembler::I16x8ExtAddPairwiseI8x16U(Simd128Register dst, |
| Simd128Register src, |
| Simd128Register scratch1, |
| Simd128Register scratch2) { |
| EXT_ADD_PAIRWISE(xxspltib, vmuleub, vmuloub, vadduhm) |
| } |
| #undef EXT_ADD_PAIRWISE |
| |
| void MacroAssembler::F64x2PromoteLowF32x4(Simd128Register dst, |
| Simd128Register src) { |
| constexpr int lane_number = 8; |
| vextractd(dst, src, Operand(lane_number)); |
| vinsertw(dst, dst, Operand(lane_number)); |
| xvcvspdp(dst, dst); |
| } |
| |
| void MacroAssembler::F32x4DemoteF64x2Zero(Simd128Register dst, |
| Simd128Register src, |
| Simd128Register scratch) { |
| constexpr int lane_number = 8; |
| xvcvdpsp(scratch, src); |
| vextractuw(dst, scratch, Operand(lane_number)); |
| vinsertw(scratch, dst, Operand(4)); |
| vxor(dst, dst, dst); |
| vinsertd(dst, scratch, Operand(lane_number)); |
| } |
| |
| void MacroAssembler::I32x4TruncSatF64x2SZero(Simd128Register dst, |
| Simd128Register src, |
| Simd128Register scratch) { |
| constexpr int lane_number = 8; |
| // NaN to 0. |
| xvcmpeqdp(scratch, src, src); |
| vand(scratch, src, scratch); |
| xvcvdpsxws(scratch, scratch); |
| vextractuw(dst, scratch, Operand(lane_number)); |
| vinsertw(scratch, dst, Operand(4)); |
| vxor(dst, dst, dst); |
| vinsertd(dst, scratch, Operand(lane_number)); |
| } |
| |
| void MacroAssembler::I32x4TruncSatF64x2UZero(Simd128Register dst, |
| Simd128Register src, |
| Simd128Register scratch) { |
| constexpr int lane_number = 8; |
| xvcvdpuxws(scratch, src); |
| vextractuw(dst, scratch, Operand(lane_number)); |
| vinsertw(scratch, dst, Operand(4)); |
| vxor(dst, dst, dst); |
| vinsertd(dst, scratch, Operand(lane_number)); |
| } |
| |
| #if V8_TARGET_BIG_ENDIAN |
| #define MAYBE_REVERSE_BYTES(reg, instr) instr(reg, reg); |
| #else |
| #define MAYBE_REVERSE_BYTES(reg, instr) |
| #endif |
| void MacroAssembler::LoadLane64LE(Simd128Register dst, const MemOperand& mem, |
| int lane, Simd128Register scratch) { |
| constexpr int lane_width_in_bytes = 8; |
| LoadSimd128Uint64(scratch, mem); |
| MAYBE_REVERSE_BYTES(scratch, xxbrd) |
| vinsertd(dst, scratch, Operand((1 - lane) * lane_width_in_bytes)); |
| } |
| |
| void MacroAssembler::LoadLane32LE(Simd128Register dst, const MemOperand& mem, |
| int lane, Simd128Register scratch) { |
| constexpr int lane_width_in_bytes = 4; |
| LoadSimd128Uint32(scratch, mem); |
| MAYBE_REVERSE_BYTES(scratch, xxbrw) |
| vinsertw(dst, scratch, Operand((3 - lane) * lane_width_in_bytes)); |
| } |
| |
| void MacroAssembler::LoadLane16LE(Simd128Register dst, const MemOperand& mem, |
| int lane, Simd128Register scratch) { |
| constexpr int lane_width_in_bytes = 2; |
| LoadSimd128Uint16(scratch, mem); |
| MAYBE_REVERSE_BYTES(scratch, xxbrh) |
| vinserth(dst, scratch, Operand((7 - lane) * lane_width_in_bytes)); |
| } |
| |
| void MacroAssembler::LoadLane8LE(Simd128Register dst, const MemOperand& mem, |
| int lane, Simd128Register scratch) { |
| LoadSimd128Uint8(scratch, mem); |
| vinsertb(dst, scratch, Operand((15 - lane))); |
| } |
| |
| void MacroAssembler::StoreLane64LE(Simd128Register src, const MemOperand& mem, |
| int lane, Simd128Register scratch) { |
| constexpr int lane_width_in_bytes = 8; |
| vextractd(scratch, src, Operand((1 - lane) * lane_width_in_bytes)); |
| MAYBE_REVERSE_BYTES(scratch, xxbrd) |
| StoreSimd128Uint64(scratch, mem); |
| } |
| |
| void MacroAssembler::StoreLane32LE(Simd128Register src, const MemOperand& mem, |
| int lane, Simd128Register scratch) { |
| constexpr int lane_width_in_bytes = 4; |
| vextractuw(scratch, src, Operand((3 - lane) * lane_width_in_bytes)); |
| MAYBE_REVERSE_BYTES(scratch, xxbrw) |
| StoreSimd128Uint32(scratch, mem); |
| } |
| |
| void MacroAssembler::StoreLane16LE(Simd128Register src, const MemOperand& mem, |
| int lane, Simd128Register scratch) { |
| constexpr int lane_width_in_bytes = 2; |
| vextractuh(scratch, src, Operand((7 - lane) * lane_width_in_bytes)); |
| MAYBE_REVERSE_BYTES(scratch, xxbrh) |
| StoreSimd128Uint16(scratch, mem); |
| } |
| |
| void MacroAssembler::StoreLane8LE(Simd128Register src, const MemOperand& mem, |
| int lane, Simd128Register scratch) { |
| vextractub(scratch, src, Operand(15 - lane)); |
| StoreSimd128Uint8(scratch, mem); |
| } |
| |
| void MacroAssembler::LoadAndSplat64x2LE(Simd128Register dst, |
| const MemOperand& mem) { |
| constexpr int lane_width_in_bytes = 8; |
| LoadSimd128Uint64(dst, mem); |
| MAYBE_REVERSE_BYTES(dst, xxbrd) |
| vinsertd(dst, dst, Operand(1 * lane_width_in_bytes)); |
| } |
| |
| void MacroAssembler::LoadAndSplat32x4LE(Simd128Register dst, |
| const MemOperand& mem) { |
| LoadSimd128Uint32(dst, mem); |
| MAYBE_REVERSE_BYTES(dst, xxbrw) |
| vspltw(dst, dst, Operand(1)); |
| } |
| |
| void MacroAssembler::LoadAndSplat16x8LE(Simd128Register dst, |
| const MemOperand& mem) { |
| LoadSimd128Uint16(dst, mem); |
| MAYBE_REVERSE_BYTES(dst, xxbrh) |
| vsplth(dst, dst, Operand(3)); |
| } |
| |
| void MacroAssembler::LoadAndSplat8x16LE(Simd128Register dst, |
| const MemOperand& mem) { |
| LoadSimd128Uint8(dst, mem); |
| vspltb(dst, dst, Operand(7)); |
| } |
| |
| void MacroAssembler::LoadAndExtend32x2SLE(Simd128Register dst, |
| const MemOperand& mem) { |
| LoadSimd128Uint64(dst, mem); |
| MAYBE_REVERSE_BYTES(dst, xxbrd) |
| vupkhsw(dst, dst); |
| } |
| |
| void MacroAssembler::LoadAndExtend32x2ULE(Simd128Register dst, |
| const MemOperand& mem, |
| Register scratch1, |
| Simd128Register scratch2) { |
| constexpr int lane_width_in_bytes = 8; |
| LoadAndExtend32x2SLE(dst, mem); |
| // Zero extend. |
| mov(scratch1, Operand(0xFFFFFFFF)); |
| mtvsrd(scratch2, scratch1); |
| vinsertd(scratch2, scratch2, Operand(1 * lane_width_in_bytes)); |
| vand(dst, scratch2, dst); |
| } |
| |
| void MacroAssembler::LoadAndExtend16x4SLE(Simd128Register dst, |
| const MemOperand& mem) { |
| LoadSimd128Uint64(dst, mem); |
| MAYBE_REVERSE_BYTES(dst, xxbrd) |
| vupkhsh(dst, dst); |
| } |
| |
| void MacroAssembler::LoadAndExtend16x4ULE(Simd128Register dst, |
| const MemOperand& mem, |
| Register scratch1, |
| Simd128Register scratch2) { |
| LoadAndExtend16x4SLE(dst, mem); |
| // Zero extend. |
| mov(scratch1, Operand(0xFFFF)); |
| mtvsrd(scratch2, scratch1); |
| vspltw(scratch2, scratch2, Operand(1)); |
| vand(dst, scratch2, dst); |
| } |
| |
| void MacroAssembler::LoadAndExtend8x8SLE(Simd128Register dst, |
| const MemOperand& mem) { |
| LoadSimd128Uint64(dst, mem); |
| MAYBE_REVERSE_BYTES(dst, xxbrd) |
| vupkhsb(dst, dst); |
| } |
| |
| void MacroAssembler::LoadAndExtend8x8ULE(Simd128Register dst, |
| const MemOperand& mem, |
| Register scratch1, |
| Simd128Register scratch2) { |
| LoadAndExtend8x8SLE(dst, mem); |
| // Zero extend. |
| li(scratch1, Operand(0xFF)); |
| mtvsrd(scratch2, scratch1); |
| vsplth(scratch2, scratch2, Operand(3)); |
| vand(dst, scratch2, dst); |
| } |
| |
| void MacroAssembler::LoadV64ZeroLE(Simd128Register dst, const MemOperand& mem, |
| Simd128Register scratch) { |
| constexpr int lane_width_in_bytes = 8; |
| LoadSimd128Uint64(scratch, mem); |
| MAYBE_REVERSE_BYTES(scratch, xxbrd) |
| vxor(dst, dst, dst); |
| vinsertd(dst, scratch, Operand(1 * lane_width_in_bytes)); |
| } |
| |
| void MacroAssembler::LoadV32ZeroLE(Simd128Register dst, const MemOperand& mem, |
| Simd128Register scratch) { |
| constexpr int lane_width_in_bytes = 4; |
| LoadSimd128Uint32(scratch, mem); |
| MAYBE_REVERSE_BYTES(scratch, xxbrw) |
| vxor(dst, dst, dst); |
| vinsertw(dst, scratch, Operand(3 * lane_width_in_bytes)); |
| } |
| #undef MAYBE_REVERSE_BYTES |
| |
| void MacroAssembler::V128AnyTrue(Register dst, Simd128Register src, |
| Register scratch1, Register scratch2, |
| Simd128Register scratch3) { |
| constexpr uint8_t fxm = 0x2; // field mask. |
| constexpr int bit_number = 24; |
| li(scratch1, Operand(0)); |
| li(scratch2, Operand(1)); |
| // Check if both lanes are 0, if so then return false. |
| vxor(scratch3, scratch3, scratch3); |
| mtcrf(scratch1, fxm); // Clear cr6. |
| vcmpequd(scratch3, src, scratch3, SetRC); |
| isel(dst, scratch1, scratch2, bit_number); |
| } |
| |
| void MacroAssembler::S128Not(Simd128Register dst, Simd128Register src) { |
| vnor(dst, src, src); |
| } |
| |
| void MacroAssembler::S128Const(Simd128Register dst, uint64_t high, uint64_t low, |
| Register scratch1, Register scratch2) { |
| DCHECK(scratch2 != r0); |
| mov(scratch1, Operand(low)); |
| mov(scratch2, Operand(high)); |
| mtvsrdd(dst, scratch2, scratch1); |
| } |
| |
| void MacroAssembler::S128Select(Simd128Register dst, Simd128Register src1, |
| Simd128Register src2, Simd128Register mask) { |
| vsel(dst, src2, src1, mask); |
| } |
| |
| Register GetRegisterThatIsNotOneOf(Register reg1, Register reg2, Register reg3, |
| Register reg4, Register reg5, |
| Register reg6) { |
| RegList regs = {reg1, reg2, reg3, reg4, reg5, reg6}; |
| |
| const RegisterConfiguration* config = RegisterConfiguration::Default(); |
| for (int i = 0; i < config->num_allocatable_general_registers(); ++i) { |
| int code = config->GetAllocatableGeneralCode(i); |
| Register candidate = Register::from_code(code); |
| if (regs.has(candidate)) continue; |
| return candidate; |
| } |
| UNREACHABLE(); |
| } |
| |
| void MacroAssembler::PreCheckSkippedWriteBarrier(Register object, |
| Register value, |
| Register scratch, Label* ok) { |
| ASM_CODE_COMMENT(this); |
| DCHECK(!AreAliased(object, scratch)); |
| DCHECK(!AreAliased(value, scratch)); |
| |
| // The most common case: Static write barrier elimination is allowed on the |
| // last young allocation. |
| { |
| UseScratchRegisterScope temps(this); |
| Register scratch1 = temps.Acquire(); |
| DCHECK(!AreAliased(scratch, scratch1)); |
| SubS64(scratch, object, Operand(kHeapObjectTag)); |
| LoadU64(scratch1, MemOperand(kRootRegister, |
| IsolateData::last_young_allocation_offset())); |
| CmpU64(scratch, scratch1); |
| b(to_condition(Condition::kEqual), ok); |
| } |
| |
| #if CONTIGUOUS_COMPRESSED_READ_ONLY_SPACE_BOOL |
| JumpIfUnsignedLessThan(value, kContiguousReadOnlyReservationSize, ok); |
| #else // !CONTIGUOUS_COMPRESSED_READ_ONLY_SPACE_BOOL |
| // Write barier can also be removed if value is in read-only space. |
| CheckPageFlag(value, scratch, MemoryChunk::kIsInReadOnlyHeapMask, ne, ok); |
| #endif // !CONTIGUOUS_COMPRESSED_READ_ONLY_SPACE_BOOL |
| |
| Label not_ok; |
| |
| // Handle allocation folding, allow WB removal if: |
| // LAB start <= last_young_allocation_ < (object address+1) < LAB top |
| // Note that object has tag bit set, so object == object address+1. |
| |
| { |
| UseScratchRegisterScope temps(this); |
| Register scratch1 = temps.Acquire(); |
| DCHECK(!AreAliased(scratch, scratch1)); |
| |
| // Check LAB start <= last_young_allocation_. |
| LoadU64(scratch, |
| MemOperand(kRootRegister, |
| IsolateData::new_allocation_info_start_offset())); |
| LoadU64(scratch1, MemOperand(kRootRegister, |
| IsolateData::last_young_allocation_offset())); |
| CmpU64(scratch, scratch1); |
| b(to_condition(Condition::kUnsignedGreaterThan), ¬_ok); |
| |
| // Check last_young_allocation_ < (object address+1). |
| CmpU64(scratch1, object); |
| b(to_condition(Condition::kUnsignedGreaterThanEqual), ¬_ok); |
| |
| // Check (object address+1) < LAB top. |
| LoadU64(scratch, MemOperand(kRootRegister, |
| IsolateData::new_allocation_info_top_offset())); |
| CmpU64(object, scratch); |
| b(to_condition(Condition::kUnsignedLessThan), ok); |
| } |
| |
| // Slow path: Potentially check more cases in C++. |
| bind(¬_ok); |
| } |
| |
| void MacroAssembler::SwapP(Register src, Register dst) { |
| if (src == dst) return; |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| mr(scratch, src); |
| mr(src, dst); |
| mr(dst, scratch); |
| } |
| |
| void MacroAssembler::SwapP(Register src, MemOperand dst) { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| mr(scratch, src); |
| LoadU64(src, dst); |
| StoreU64(scratch, dst); |
| } |
| |
| void MacroAssembler::SwapP(MemOperand src, MemOperand dst) { |
| UseScratchRegisterScope temps(this); |
| Register scratch_0 = temps.Acquire(); |
| Register scratch_1 = temps.Acquire(); |
| if (is_int16(src.offset()) || is_int16(dst.offset())) { |
| if (!is_int16(src.offset())) { |
| // swap operand |
| MemOperand temp = src; |
| src = dst; |
| dst = temp; |
| } |
| LoadU64(scratch_1, dst); |
| LoadU64(scratch_0, src); |
| StoreU64(scratch_1, src); |
| StoreU64(scratch_0, dst); |
| } else { |
| LoadU64(scratch_1, dst); |
| push(scratch_1); |
| LoadU64(scratch_0, src); |
| StoreU64(scratch_0, dst); |
| pop(scratch_1); |
| StoreU64(scratch_1, src); |
| } |
| } |
| |
| void MacroAssembler::SwapFloat32(DoubleRegister src, DoubleRegister dst, |
| DoubleRegister scratch) { |
| if (src == dst) return; |
| DCHECK(!AreAliased(src, dst, scratch)); |
| fmr(scratch, src); |
| fmr(src, dst); |
| fmr(dst, scratch); |
| } |
| |
| void MacroAssembler::SwapFloat32(DoubleRegister src, MemOperand dst, |
| DoubleRegister scratch) { |
| DCHECK(!AreAliased(src, scratch)); |
| fmr(scratch, src); |
| LoadF32(src, dst); |
| StoreF32(scratch, dst); |
| } |
| |
| void MacroAssembler::SwapFloat32(MemOperand src, MemOperand dst, |
| DoubleRegister scratch_0, |
| DoubleRegister scratch_1) { |
| DCHECK(!AreAliased(scratch_0, scratch_1)); |
| LoadF32(scratch_0, src); |
| LoadF32(scratch_1, dst); |
| StoreF32(scratch_0, dst); |
| StoreF32(scratch_1, src); |
| } |
| |
| void MacroAssembler::SwapDouble(DoubleRegister src, DoubleRegister dst, |
| DoubleRegister scratch) { |
| if (src == dst) return; |
| DCHECK(!AreAliased(src, dst, scratch)); |
| fmr(scratch, src); |
| fmr(src, dst); |
| fmr(dst, scratch); |
| } |
| |
| void MacroAssembler::SwapDouble(DoubleRegister src, MemOperand dst, |
| DoubleRegister scratch) { |
| DCHECK(!AreAliased(src, scratch)); |
| fmr(scratch, src); |
| LoadF64(src, dst); |
| StoreF64(scratch, dst); |
| } |
| |
| void MacroAssembler::SwapDouble(MemOperand src, MemOperand dst, |
| DoubleRegister scratch_0, |
| DoubleRegister scratch_1) { |
| DCHECK(!AreAliased(scratch_0, scratch_1)); |
| LoadF64(scratch_0, src); |
| LoadF64(scratch_1, dst); |
| StoreF64(scratch_0, dst); |
| StoreF64(scratch_1, src); |
| } |
| |
| void MacroAssembler::SwapSimd128(Simd128Register src, Simd128Register dst, |
| Simd128Register scratch) { |
| if (src == dst) return; |
| vor(scratch, src, src); |
| vor(src, dst, dst); |
| vor(dst, scratch, scratch); |
| } |
| |
| void MacroAssembler::SwapSimd128(Simd128Register src, MemOperand dst, |
| Simd128Register scratch) { |
| DCHECK(src != scratch); |
| LoadSimd128(scratch, dst); |
| StoreSimd128(src, dst); |
| vor(src, scratch, scratch); |
| } |
| |
| void MacroAssembler::SwapSimd128(MemOperand src, MemOperand dst, |
| Simd128Register scratch1, |
| Simd128Register scratch2) { |
| LoadSimd128(scratch1, src); |
| LoadSimd128(scratch2, dst); |
| |
| StoreSimd128(scratch1, dst); |
| StoreSimd128(scratch2, src); |
| } |
| |
| void MacroAssembler::ByteReverseU16(Register dst, Register val) { |
| if (CpuFeatures::IsSupported(PPC_10_PLUS)) { |
| brh(dst, val); |
| ZeroExtHalfWord(dst, dst); |
| return; |
| } |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| rlwinm(scratch, val, 8, 16, 23); |
| rlwinm(dst, val, 24, 24, 31); |
| orx(dst, scratch, dst); |
| ZeroExtHalfWord(dst, dst); |
| } |
| |
| void MacroAssembler::ByteReverseU32(Register dst, Register val) { |
| if (CpuFeatures::IsSupported(PPC_10_PLUS)) { |
| brw(dst, val); |
| ZeroExtWord32(dst, dst); |
| return; |
| } |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| rotlwi(scratch, val, 8); |
| rlwimi(scratch, val, 24, 0, 7); |
| rlwimi(scratch, val, 24, 16, 23); |
| ZeroExtWord32(dst, scratch); |
| } |
| |
| void MacroAssembler::ByteReverseU64(Register dst, Register val) { |
| if (CpuFeatures::IsSupported(PPC_10_PLUS)) { |
| brd(dst, val); |
| return; |
| } |
| subi(sp, sp, Operand(kSystemPointerSize)); |
| std(val, MemOperand(sp)); |
| ldbrx(dst, MemOperand(r0, sp)); |
| addi(sp, sp, Operand(kSystemPointerSize)); |
| } |
| |
| void MacroAssembler::JumpIfEqual(Register x, int32_t y, Label* dest) { |
| CmpS32(x, Operand(y)); |
| beq(dest); |
| } |
| |
| void MacroAssembler::JumpIfLessThan(Register x, int32_t y, Label* dest) { |
| CmpS32(x, Operand(y)); |
| blt(dest); |
| } |
| |
| void MacroAssembler::JumpIfUnsignedLessThan(Register x, int32_t y, |
| Label* dest) { |
| CmpU32(x, Operand(y)); |
| blt(dest); |
| } |
| |
| void MacroAssembler::LoadEntryFromBuiltinIndex(Register builtin_index, |
| Register target) { |
| static_assert(kSystemPointerSize == 8); |
| static_assert(kSmiTagSize == 1); |
| static_assert(kSmiTag == 0); |
| |
| // The builtin_index register contains the builtin index as a Smi. |
| if (SmiValuesAre32Bits()) { |
| ShiftRightS64(target, builtin_index, |
| Operand(kSmiShift - kSystemPointerSizeLog2)); |
| } else { |
| DCHECK(SmiValuesAre31Bits()); |
| ShiftLeftU64(target, builtin_index, |
| Operand(kSystemPointerSizeLog2 - kSmiShift)); |
| } |
| AddS64(target, target, Operand(IsolateData::builtin_entry_table_offset())); |
| LoadU64(target, MemOperand(kRootRegister, target)); |
| } |
| |
| void MacroAssembler::CallBuiltinByIndex(Register builtin_index, |
| Register target) { |
| LoadEntryFromBuiltinIndex(builtin_index, target); |
| Call(target); |
| } |
| |
| void MacroAssembler::LoadEntryFromBuiltin(Builtin builtin, |
| Register destination) { |
| ASM_CODE_COMMENT(this); |
| LoadU64(destination, EntryFromBuiltinAsOperand(builtin)); |
| } |
| |
| MemOperand MacroAssembler::EntryFromBuiltinAsOperand(Builtin builtin) { |
| ASM_CODE_COMMENT(this); |
| DCHECK(root_array_available()); |
| return MemOperand(kRootRegister, |
| IsolateData::BuiltinEntrySlotOffset(builtin)); |
| } |
| |
| void MacroAssembler::LoadEntrypointFromJSDispatchTable( |
| Register destination, Register dispatch_handle) { |
| ASM_CODE_COMMENT(this); |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| |
| Register index = destination; |
| CHECK(root_array_available()); |
| LoadU64(scratch, |
| ExternalReferenceAsOperand(IsolateFieldId::kJSDispatchTable)); |
| ShiftRightU64(index, dispatch_handle, Operand(kJSDispatchHandleShift)); |
| ShiftLeftU64(index, index, Operand(kJSDispatchTableEntrySizeLog2)); |
| AddS64(scratch, scratch, index); |
| LoadU64(destination, MemOperand(scratch, JSDispatchEntry::kEntrypointOffset)); |
| } |
| |
| void MacroAssembler::LoadCodeInstructionStart(Register destination, |
| Register code_object, |
| CodeEntrypointTag tag) { |
| ASM_CODE_COMMENT(this); |
| LoadU64(destination, |
| FieldMemOperand(code_object, Code::kInstructionStartOffset)); |
| } |
| |
| void MacroAssembler::CallCodeObject(Register code_object) { |
| ASM_CODE_COMMENT(this); |
| LoadCodeInstructionStart(code_object, code_object); |
| Call(code_object); |
| } |
| |
| void MacroAssembler::JumpCodeObject(Register code_object, JumpMode jump_mode) { |
| ASM_CODE_COMMENT(this); |
| DCHECK_EQ(JumpMode::kJump, jump_mode); |
| LoadCodeInstructionStart(code_object, code_object); |
| Jump(code_object); |
| } |
| |
| void MacroAssembler::CallJSFunction(Register function_object, |
| uint16_t argument_count) { |
| Register code = kJavaScriptCallCodeStartRegister; |
| UseScratchRegisterScope temps(this); |
| Register dispatch_handle = temps.Acquire(); |
| LoadU32( |
| dispatch_handle, |
| FieldMemOperand(function_object, offsetof(JSFunction, dispatch_handle_))); |
| LoadEntrypointFromJSDispatchTable(code, dispatch_handle); |
| Call(code); |
| } |
| |
| void MacroAssembler::CallJSDispatchEntry(JSDispatchHandle dispatch_handle, |
| uint16_t argument_count) { |
| Register code = kJavaScriptCallCodeStartRegister; |
| UseScratchRegisterScope temps(this); |
| Register dispatch_handle_reg = temps.Acquire(); |
| mov(dispatch_handle_reg, |
| Operand(dispatch_handle.value(), RelocInfo::JS_DISPATCH_HANDLE)); |
| // WARNING: This entrypoint load is only safe because we are storing a |
| // RelocInfo for the dispatch handle in the movl above (thus keeping the |
| // dispatch entry alive) _and_ because the entrypoints are not compactable |
| // (thus meaning that the calculation in the entrypoint load is not |
| // invalidated by a compaction). |
| // TODO(leszeks): Make this less of a footgun. |
| static_assert(!JSDispatchTable::kSupportsCompaction); |
| LoadEntrypointFromJSDispatchTable(code, dispatch_handle_reg); |
| CHECK_EQ(argument_count, |
| isolate()->js_dispatch_table().GetParameterCount(dispatch_handle)); |
| Call(code); |
| } |
| |
| void MacroAssembler::JumpJSFunction(Register function_object, |
| JumpMode jump_mode) { |
| Register code = kJavaScriptCallCodeStartRegister; |
| Register dispatch_handle = r0; |
| LoadU32( |
| dispatch_handle, |
| FieldMemOperand(function_object, offsetof(JSFunction, dispatch_handle_))); |
| LoadEntrypointFromJSDispatchTable(code, dispatch_handle); |
| Jump(code); |
| } |
| |
| #ifdef V8_ENABLE_WEBASSEMBLY |
| |
| void MacroAssembler::ResolveWasmCodePointer(Register target) { |
| ASM_CODE_COMMENT(this); |
| static_assert(!V8_ENABLE_SANDBOX_BOOL); |
| ExternalReference global_jump_table = |
| ExternalReference::wasm_code_pointer_table(); |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| Move(scratch, global_jump_table); |
| static_assert(sizeof(wasm::WasmCodePointerTableEntry) == kSystemPointerSize); |
| ShiftLeftU32(target, target, Operand(kSystemPointerSizeLog2)); |
| LoadU64(target, MemOperand(scratch, target)); |
| } |
| |
| void MacroAssembler::CallWasmCodePointer(Register target, |
| CallJumpMode call_jump_mode) { |
| ResolveWasmCodePointer(target); |
| if (call_jump_mode == CallJumpMode::kTailCall) { |
| Jump(target); |
| } else { |
| Call(target); |
| } |
| } |
| |
| void MacroAssembler::LoadWasmCodePointer(Register dst, MemOperand src) { |
| static_assert(sizeof(WasmCodePointer) == 4); |
| LoadU32(dst, src); |
| } |
| |
| #endif |
| |
| void MacroAssembler::StoreReturnAddressAndCall(Register target) { |
| // This generates the final instruction sequence for calls to C functions |
| // once an exit frame has been constructed. |
| // |
| // Note that this assumes the caller code (i.e. the InstructionStream object |
| // currently being generated) is immovable or that the callee function cannot |
| // trigger GC, since the callee function will return to it. |
| |
| Label return_label; |
| Register dest = target; |
| Register scratch = r7; |
| DCHECK(!AreAliased(r0, scratch, target)); |
| GetLabelAddress(scratch, &return_label); |
| |
| if (ABI_USES_FUNCTION_DESCRIPTORS) { |
| // AIX/PPC64BE Linux uses a function descriptor. When calling C code be |
| // aware of this descriptor and pick up values from it |
| LoadU64(ToRegister(ABI_TOC_REGISTER), |
| MemOperand(target, kSystemPointerSize)); |
| LoadU64(ip, MemOperand(target, 0)); |
| dest = ip; |
| } else if (ABI_CALL_VIA_IP && dest != ip) { |
| Move(ip, target); |
| dest = ip; |
| } |
| |
| StoreU64(scratch, |
| MemOperand(sp, kStackFrameExtraParamSlot * kSystemPointerSize)); |
| |
| Call(dest); |
| bind(&return_label); |
| } |
| |
| void MacroAssembler::AssertNotDeoptimized(Register scratch) { |
| DCHECK(!AreAliased(scratch, r0)); |
| int offset = InstructionStream::kCodeOffset - InstructionStream::kHeaderSize; |
| LoadTaggedField(scratch, |
| MemOperand(kJavaScriptCallCodeStartRegister, offset)); |
| TestCodeIsMarkedForDeoptimization(scratch); |
| Assert(to_condition(kZero), AbortReason::kInvalidDeoptimizedCode); |
| } |
| |
| void MacroAssembler::CallForDeoptimization(Builtin target, int, Label* exit, |
| DeoptimizeKind kind, Label* ret, |
| Label*) { |
| BlockTrampolinePoolScope block_trampoline_pool(this); |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| CHECK_LE(target, Builtins::kLastTier0); |
| LoadU64(scratch, MemOperand(kRootRegister, |
| IsolateData::BuiltinEntrySlotOffset(target))); |
| Call(scratch); |
| DCHECK_EQ(SizeOfCodeGeneratedSince(exit), |
| (kind == DeoptimizeKind::kLazy) ? Deoptimizer::kLazyDeoptExitSize |
| : Deoptimizer::kEagerDeoptExitSize); |
| } |
| |
| void MacroAssembler::ZeroExtByte(Register dst, Register src) { |
| clrldi(dst, src, Operand(56)); |
| } |
| |
| void MacroAssembler::ZeroExtHalfWord(Register dst, Register src) { |
| clrldi(dst, src, Operand(48)); |
| } |
| |
| void MacroAssembler::ZeroExtWord32(Register dst, Register src) { |
| clrldi(dst, src, Operand(32)); |
| } |
| |
| void MacroAssembler::Trap() { stop(); } |
| void MacroAssembler::DebugBreak() { stop(); } |
| |
| void MacroAssembler::Popcnt32(Register dst, Register src) { popcntw(dst, src); } |
| |
| void MacroAssembler::Popcnt64(Register dst, Register src) { popcntd(dst, src); } |
| |
| void MacroAssembler::CountLeadingZerosU32(Register dst, Register src, RCBit r) { |
| cntlzw(dst, src, r); |
| } |
| |
| void MacroAssembler::CountLeadingZerosU64(Register dst, Register src, RCBit r) { |
| cntlzd(dst, src, r); |
| } |
| |
| void MacroAssembler::CountTrailingZerosU32(Register dst, Register src, |
| RCBit r) { |
| cnttzw(dst, src, r); |
| } |
| |
| void MacroAssembler::CountTrailingZerosU64(Register dst, Register src, |
| RCBit r) { |
| cnttzd(dst, src, r); |
| } |
| |
| void MacroAssembler::ClearByteU64(Register dst, int byte_idx) { |
| CHECK(0 <= byte_idx && byte_idx <= 7); |
| int shift = byte_idx*8; |
| rldicl(dst, dst, shift, 8); |
| rldicl(dst, dst, 64-shift, 0); |
| } |
| |
| void MacroAssembler::ReverseBitsU64(Register dst, Register src) { |
| ByteReverseU64(dst, src); |
| for (int i = 0; i < 8; i++) { |
| ReverseBitsInSingleByteU64(dst, dst, i); |
| } |
| } |
| |
| void MacroAssembler::ReverseBitsU32(Register dst, Register src) { |
| ByteReverseU32(dst, src); |
| for (int i = 4; i < 8; i++) { |
| ReverseBitsInSingleByteU64(dst, dst, i); |
| } |
| } |
| |
| // byte_idx=7 refers to least significant byte |
| void MacroAssembler::ReverseBitsInSingleByteU64(Register dst, Register src, |
| int byte_idx) { |
| UseScratchRegisterScope temps(this); |
| Register scratch1 = temps.Acquire(); |
| Register scratch2 = temps.Acquire(); |
| CHECK(0 <= byte_idx && byte_idx <= 7); |
| int j = byte_idx; |
| // zero all bits of scratch1 |
| li(scratch2, Operand(0)); |
| for (int i = 0; i <= 7; i++) { |
| // zero all bits of scratch1 |
| li(scratch1, Operand(0)); |
| // move bit (j+1)*8-i-1 of src to bit j*8+i of scratch1, erase bits |
| // (j*8+i+1):end of scratch1 |
| int shift = 7 - (2*i); |
| if (shift < 0) shift += 64; |
| rldicr(scratch1, src, shift, j*8+i); |
| // erase bits start:(j*8-1+i) of scratch1 (inclusive) |
| rldicl(scratch1, scratch1, 0, j*8+i); |
| // scratch2 = scratch2|scratch1 |
| orx(scratch2, scratch2, scratch1); |
| } |
| // clear jth byte of dst and insert jth byte of scratch2 |
| ClearByteU64(dst, j); |
| orx(dst, dst, scratch2); |
| } |
| |
| void MacroAssembler::JumpIfCodeIsMarkedForDeoptimization( |
| Register code, Label* if_marked_for_deoptimization) { |
| TestCodeIsMarkedForDeoptimization(code); |
| bne(if_marked_for_deoptimization); |
| } |
| |
| void MacroAssembler::JumpIfCodeIsTurbofanned(Register code, |
| Label* if_turbofanned) { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| LoadU32(scratch, FieldMemOperand(code, Code::kFlagsOffset)); |
| TestBit(scratch, Code::kIsTurbofannedBit); |
| bne(if_turbofanned); |
| } |
| void MacroAssembler::TryLoadOptimizedOsrCode(Register scratch_and_result, |
| CodeKind min_opt_level, |
| Register feedback_vector, |
| FeedbackSlot slot, |
| Label* on_result, |
| Label::Distance) { |
| Label fallthrough, clear_slot; |
| LoadTaggedField( |
| scratch_and_result, |
| FieldMemOperand(feedback_vector, |
| FeedbackVector::OffsetOfElementAt(slot.ToInt()))); |
| LoadWeakValue(scratch_and_result, scratch_and_result, &fallthrough); |
| // Is it marked_for_deoptimization? If yes, clear the slot. |
| { |
| // The entry references a CodeWrapper object. Unwrap it now. |
| LoadTaggedField( |
| scratch_and_result, |
| FieldMemOperand(scratch_and_result, offsetof(CodeWrapper, code_))); |
| |
| JumpIfCodeIsMarkedForDeoptimization(scratch_and_result, &clear_slot); |
| if (min_opt_level == CodeKind::TURBOFAN_JS) { |
| JumpIfCodeIsTurbofanned(scratch_and_result, on_result); |
| b(&fallthrough); |
| } else { |
| b(on_result); |
| } |
| } |
| |
| bind(&clear_slot); |
| mov(scratch_and_result, ClearedValue()); |
| StoreTaggedField( |
| scratch_and_result, |
| FieldMemOperand(feedback_vector, |
| FeedbackVector::OffsetOfElementAt(slot.ToInt()))); |
| |
| bind(&fallthrough); |
| mov(scratch_and_result, Operand::Zero()); |
| } |
| |
| // Calls an API function. Allocates HandleScope, extracts returned value |
| // from handle and propagates exceptions. Clobbers C argument registers |
| // and C caller-saved registers. Restores context. On return removes |
| // (*argc_operand + slots_to_drop_on_return) * kSystemPointerSize |
| // (GCed, includes the call JS arguments space and the additional space |
| // allocated for the fast call). |
| void CallApiFunctionAndReturn(MacroAssembler* masm, bool with_profiling, |
| Register function_address, |
| ExternalReference thunk_ref, Register thunk_arg, |
| int slots_to_drop_on_return, |
| MemOperand* argc_operand, |
| MemOperand return_value_operand, |
| bool handle_interceptor_result) { |
| using ER = ExternalReference; |
| |
| Isolate* isolate = masm->isolate(); |
| MemOperand next_mem_op = __ ExternalReferenceAsOperand( |
| ER::handle_scope_next_address(isolate), no_reg); |
| MemOperand limit_mem_op = __ ExternalReferenceAsOperand( |
| ER::handle_scope_limit_address(isolate), no_reg); |
| MemOperand level_mem_op = __ ExternalReferenceAsOperand( |
| ER::handle_scope_level_address(isolate), no_reg); |
| |
| // Additional parameter is the address of the actual callback. |
| Register return_value = r3; |
| UseScratchRegisterScope temps(masm); |
| Register scratch = temps.Acquire(); |
| Register scratch2 = r0; |
| |
| // Allocate HandleScope in callee-saved registers. |
| // We will need to restore the HandleScope after the call to the API function, |
| // by allocating it in callee-saved registers it'll be preserved by C code. |
| Register prev_next_address_reg = r14; |
| Register prev_limit_reg = r15; |
| Register prev_level_reg = r16; |
| |
| // C arguments (kCArgRegs[0/1/2]) are expected to be initialized outside, so |
| // this function must not corrupt them (return_value overlaps with |
| // kCArgRegs[0] but that's ok because we start using it only after the C |
| // call). |
| DCHECK(!AreAliased(kCArgRegs[0], kCArgRegs[1], kCArgRegs[2], // C args |
| scratch, scratch2, prev_next_address_reg, prev_limit_reg)); |
| // function_address and thunk_arg might overlap but this function must not |
| // corrupt them until the call is made (i.e. overlap with return_value is |
| // fine). |
| DCHECK(!AreAliased(function_address, // incoming parameters |
| scratch, scratch2, prev_next_address_reg, prev_limit_reg)); |
| DCHECK(!AreAliased(thunk_arg, // incoming parameters |
| scratch, scratch2, prev_next_address_reg, prev_limit_reg)); |
| { |
| ASM_CODE_COMMENT_STRING(masm, |
| "Allocate HandleScope in callee-save registers."); |
| __ LoadU64(prev_next_address_reg, next_mem_op); |
| __ LoadU64(prev_limit_reg, limit_mem_op); |
| __ lwz(prev_level_reg, level_mem_op); |
| __ addi(scratch, prev_level_reg, Operand(1)); |
| __ stw(scratch, level_mem_op); |
| } |
| |
| Label profiler_or_side_effects_check_enabled, done_api_call, |
| done_reading_result; |
| if (with_profiling) { |
| __ RecordComment("Check if profiler or side effects check is enabled"); |
| __ lbz(scratch, |
| __ ExternalReferenceAsOperand(IsolateFieldId::kExecutionMode)); |
| __ cmpi(scratch, Operand::Zero()); |
| __ bne(&profiler_or_side_effects_check_enabled); |
| #ifdef V8_RUNTIME_CALL_STATS |
| __ RecordComment("Check if RCS is enabled"); |
| __ Move(scratch, ER::address_of_runtime_stats_flag()); |
| __ lwz(scratch, MemOperand(scratch, 0)); |
| __ cmpi(scratch, Operand::Zero()); |
| __ bne(&profiler_or_side_effects_check_enabled); |
| #endif // V8_RUNTIME_CALL_STATS |
| } |
| |
| __ RecordComment("Call the api function directly."); |
| __ StoreReturnAddressAndCall(function_address); |
| __ bind(&done_api_call); |
| |
| if (handle_interceptor_result) { |
| // Skip reading return value if the callback returned kInterceptedNo, |
| // this would make the builtin return kNotInterceptedSentinel value. |
| // Size is important here, otherwise the C++ function could have returned |
| // one- or two-byte value with junk in the upper part. |
| static_assert(kInterceptedNo == 1 && kInterceptedSize == 4); |
| static_assert(kInterceptedNo == kNotInterceptedSentinel); |
| static_assert(kInterceptedYes == 0); |
| __ andi(r0, return_value, Operand(1)); |
| __ b(to_condition(kNotZero), &done_reading_result); |
| } |
| |
| Label propagate_exception; |
| Label delete_allocated_handles; |
| Label leave_exit_frame; |
| |
| // load value from ReturnValue |
| __ RecordComment("Load the value from ReturnValue"); |
| __ LoadU64(r3, return_value_operand); |
| __ bind(&done_reading_result); |
| |
| { |
| ASM_CODE_COMMENT_STRING( |
| masm, |
| "No more valid handles (the result handle was the last one)." |
| "Restore previous handle scope."); |
| __ StoreU64(prev_next_address_reg, next_mem_op); |
| if (v8_flags.debug_code) { |
| __ lwz(scratch, level_mem_op); |
| __ subi(scratch, scratch, Operand(1)); |
| __ CmpS64(scratch, prev_level_reg); |
| __ Check(eq, AbortReason::kUnexpectedLevelAfterReturnFromApiCall); |
| } |
| __ stw(prev_level_reg, level_mem_op); |
| __ LoadU64(scratch, limit_mem_op); |
| __ CmpS64(scratch, prev_limit_reg); |
| __ bne(&delete_allocated_handles); |
| } |
| |
| __ RecordComment("Leave the API exit frame."); |
| __ bind(&leave_exit_frame); |
| Register argc_reg = prev_limit_reg; |
| if (argc_operand != nullptr) { |
| // Load the number of stack slots to drop before LeaveExitFrame modifies sp. |
| __ LoadU64(argc_reg, *argc_operand); |
| } |
| __ LeaveExitFrame(); |
| |
| { |
| ASM_CODE_COMMENT_STRING(masm, |
| "Check if the function scheduled an exception."); |
| __ LoadRoot(scratch, RootIndex::kTheHoleValue); |
| __ LoadU64(scratch2, __ ExternalReferenceAsOperand( |
| ER::exception_address(isolate), no_reg)); |
| __ CmpS64(scratch, scratch2); |
| __ bne(&propagate_exception); |
| } |
| |
| if (v8_flags.debug_code) { |
| Label ok; |
| if (handle_interceptor_result) { |
| __ Cmp(return_value, kNotInterceptedSentinel); |
| __ beq(&ok); |
| } |
| __ AssertJSAny(return_value, AbortReason::kAPICallReturnedInvalidObject); |
| __ bind(&ok); |
| } |
| |
| if (argc_operand == nullptr) { |
| DCHECK_NE(slots_to_drop_on_return, 0); |
| __ AddS64(sp, sp, Operand(slots_to_drop_on_return * kSystemPointerSize)); |
| |
| } else { |
| // {argc_operand} was loaded into {argc_reg} above. |
| __ AddS64(sp, sp, Operand(slots_to_drop_on_return * kSystemPointerSize)); |
| __ Drop(argc_reg); |
| } |
| |
| __ blr(); |
| |
| if (with_profiling) { |
| ASM_CODE_COMMENT_STRING(masm, "Call the api function via the thunk."); |
| __ bind(&profiler_or_side_effects_check_enabled); |
| // Additional parameter if provided. |
| if (thunk_arg.is_valid()) { |
| MemOperand thunk_arg_mem_op = __ ExternalReferenceAsOperand( |
| IsolateFieldId::kApiCallbackThunkArgument); |
| __ StoreU64(thunk_arg, thunk_arg_mem_op); |
| } |
| __ Move(scratch, thunk_ref); |
| __ StoreReturnAddressAndCall(scratch); |
| __ b(&done_api_call); |
| } |
| |
| __ RecordComment("An exception was thrown. Propagate it."); |
| __ bind(&propagate_exception); |
| __ TailCallRuntime(Runtime::kPropagateException); |
| |
| { |
| ASM_CODE_COMMENT_STRING( |
| masm, "HandleScope limit has changed. Delete allocated extensions."); |
| __ bind(&delete_allocated_handles); |
| __ StoreU64(prev_limit_reg, limit_mem_op); |
| // Save the return value in a callee-save register. |
| Register saved_result = prev_limit_reg; |
| __ mr(saved_result, return_value); |
| __ PrepareCallCFunction(1); |
| __ Move(kCArgRegs[0], ER::isolate_address()); |
| __ CallCFunction(ER::delete_handle_scope_extensions(), 1); |
| __ mr(return_value, saved_result); |
| __ b(&leave_exit_frame); |
| } |
| } |
| |
| void MacroAssembler::Switch(Register scratch, Register value, |
| int case_value_base, Label** labels, |
| int num_labels) { |
| Label fallthrough, jump_table; |
| if (case_value_base != 0) { |
| SubS64(value, value, Operand(case_value_base)); |
| } |
| CmpU64(value, Operand(num_labels)); |
| bge(&fallthrough); |
| |
| int entry_size_log2 = 2; |
| ShiftLeftU32(value, value, Operand(entry_size_log2)); |
| |
| Assembler::BlockTrampolinePoolScope block_trampoline_pool(this); |
| DCHECK(!AreAliased(scratch, value, r0)); |
| GetLabelAddress(scratch, &jump_table); |
| add(scratch, scratch, value); |
| Jump(scratch); |
| |
| bind(&jump_table); |
| for (int i = 0; i < num_labels; ++i) { |
| b(labels[i]); |
| } |
| bind(&fallthrough); |
| } |
| |
| } // namespace internal |
| } // namespace v8 |
| |
| #undef __ |
| |
| #endif // V8_TARGET_ARCH_PPC64 |