blob: 5c56d1d112b917d123c1435510003f5276f8b7c9 [file]
// 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 "src/base/numbers/double.h"
#include "src/codegen/assembler-inl.h"
#include "src/codegen/callable.h"
#include "src/codegen/interface-descriptors-inl.h"
#include "src/codegen/macro-assembler.h"
#include "src/codegen/optimized-compilation-info.h"
#include "src/compiler/backend/code-generator-impl.h"
#include "src/compiler/backend/code-generator.h"
#include "src/compiler/backend/gap-resolver.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/osr.h"
#include "src/heap/mutable-page.h"
#include "src/objects/js-function-inl.h"
#include "src/objects/shared-function-info-inl.h"
#include "src/sandbox/js-dispatch-table-inl.h"
#if V8_ENABLE_WEBASSEMBLY
#include "src/wasm/wasm-linkage.h"
#include "src/wasm/wasm-objects.h"
#endif // V8_ENABLE_WEBASSEMBLY
namespace v8 {
namespace internal {
namespace compiler {
#define __ masm()->
// Adds PPC-specific methods to convert InstructionOperands.
class PPCOperandConverter final : public InstructionOperandConverter {
public:
PPCOperandConverter(CodeGenerator* gen, Instruction* instr)
: InstructionOperandConverter(gen, instr) {}
size_t OutputCount() { return instr_->OutputCount(); }
RCBit OutputRCBit() const {
switch (instr_->flags_mode()) {
case kFlags_branch:
case kFlags_conditional_branch:
case kFlags_deoptimize:
case kFlags_set:
case kFlags_conditional_trap:
case kFlags_trap:
case kFlags_select:
return SetRC;
case kFlags_none:
return LeaveRC;
}
UNREACHABLE();
}
bool CompareLogical() const {
switch (instr_->flags_condition()) {
case kUnsignedLessThan:
case kUnsignedGreaterThanOrEqual:
case kUnsignedLessThanOrEqual:
case kUnsignedGreaterThan:
return true;
default:
return false;
}
UNREACHABLE();
}
Operand InputImmediate(size_t index) {
Constant constant = ToConstant(instr_->InputAt(index));
switch (constant.type()) {
case Constant::kInt32:
return Operand(constant.ToInt32());
case Constant::kFloat32:
return Operand::EmbeddedNumber(constant.ToFloat32());
case Constant::kFloat64:
return Operand::EmbeddedNumber(constant.ToFloat64().value());
case Constant::kInt64:
return Operand(constant.ToInt64());
case Constant::kExternalReference:
return Operand(constant.ToExternalReference());
case Constant::kCompressedHeapObject:
case Constant::kHeapObject:
case Constant::kRpoNumber:
break;
}
UNREACHABLE();
}
MemOperand MemoryOperand(AddressingMode* mode, size_t* first_index) {
const size_t index = *first_index;
AddressingMode addr_mode = AddressingModeField::decode(instr_->opcode());
if (mode) *mode = addr_mode;
switch (addr_mode) {
case kMode_None:
break;
case kMode_MRI:
*first_index += 2;
return MemOperand(InputRegister(index + 0), InputInt64(index + 1));
case kMode_MRR:
*first_index += 2;
return MemOperand(InputRegister(index + 0), InputRegister(index + 1));
case kMode_Root:
*first_index += 1;
return MemOperand(kRootRegister, InputRegister(index));
}
UNREACHABLE();
}
MemOperand MemoryOperand(AddressingMode* mode = NULL,
size_t first_index = 0) {
return MemoryOperand(mode, &first_index);
}
MemOperand ToMemOperand(InstructionOperand* op) const {
DCHECK_NOT_NULL(op);
DCHECK(op->IsStackSlot() || op->IsFPStackSlot());
return SlotToMemOperand(AllocatedOperand::cast(op)->index());
}
MemOperand SlotToMemOperand(int slot) const {
FrameOffset offset = frame_access_state()->GetFrameOffset(slot);
return MemOperand(offset.from_stack_pointer() ? sp : fp, offset.offset());
}
};
static inline bool HasRegisterInput(Instruction* instr, size_t index) {
return instr->InputAt(index)->IsRegister();
}
namespace {
class OutOfLineVerifySkippedWriteBarrier final : public OutOfLineCode {
public:
OutOfLineVerifySkippedWriteBarrier(CodeGenerator* gen, Register object,
Register value, Register scratch,
UnwindingInfoWriter* unwinding_info_writer)
: OutOfLineCode(gen),
object_(object),
value_(value),
scratch_(scratch),
must_save_lr_(!gen->frame_access_state()->has_frame()),
unwinding_info_writer_(unwinding_info_writer),
zone_(gen->zone()) {}
void Generate() final {
if (COMPRESS_POINTERS_BOOL) {
__ DecompressTagged(value_, value_);
}
__ PreCheckSkippedWriteBarrier(object_, value_, scratch_, exit());
SaveFPRegsMode const save_fp_mode = frame()->DidAllocateDoubleRegisters()
? SaveFPRegsMode::kSave
: SaveFPRegsMode::kIgnore;
if (must_save_lr_) {
// We need to save and restore lr if the frame was elided.
__ mflr(scratch_);
__ Push(scratch_);
unwinding_info_writer_->MarkLinkRegisterOnTopOfStack(__ pc_offset());
}
__ CallVerifySkippedWriteBarrierStubSaveRegisters(object_, value_,
save_fp_mode);
if (must_save_lr_) {
__ Pop(scratch_);
__ mtlr(scratch_);
unwinding_info_writer_->MarkPopLinkRegisterFromTopOfStack(__ pc_offset());
}
}
private:
Register const object_;
Register const value_;
Register const scratch_;
bool const must_save_lr_;
UnwindingInfoWriter* const unwinding_info_writer_;
Zone* zone_;
};
class OutOfLineRecordWrite final : public OutOfLineCode {
public:
OutOfLineRecordWrite(
CodeGenerator* gen, Register object, Register offset, Register value,
Register scratch0, Register scratch1, RecordWriteMode mode,
StubCallMode stub_mode, UnwindingInfoWriter* unwinding_info_writer,
IndirectPointerTag indirect_pointer_tag = kIndirectPointerNullTag)
: OutOfLineCode(gen),
object_(object),
offset_(offset),
offset_immediate_(0),
value_(value),
scratch0_(scratch0),
scratch1_(scratch1),
mode_(mode),
#if V8_ENABLE_WEBASSEMBLY
stub_mode_(stub_mode),
#endif // V8_ENABLE_WEBASSEMBLY
must_save_lr_(!gen->frame_access_state()->has_frame()),
unwinding_info_writer_(unwinding_info_writer),
zone_(gen->zone()) {
DCHECK(!AreAliased(object, offset, scratch0, scratch1));
DCHECK(!AreAliased(value, offset, scratch0, scratch1));
}
OutOfLineRecordWrite(CodeGenerator* gen, Register object, int32_t offset,
Register value, Register scratch0, Register scratch1,
RecordWriteMode mode, StubCallMode stub_mode,
UnwindingInfoWriter* unwinding_info_writer)
: OutOfLineCode(gen),
object_(object),
offset_(no_reg),
offset_immediate_(offset),
value_(value),
scratch0_(scratch0),
scratch1_(scratch1),
mode_(mode),
#if V8_ENABLE_WEBASSEMBLY
stub_mode_(stub_mode),
#endif // V8_ENABLE_WEBASSEMBLY
must_save_lr_(!gen->frame_access_state()->has_frame()),
unwinding_info_writer_(unwinding_info_writer),
zone_(gen->zone()) {
}
void Generate() final {
ConstantPoolUnavailableScope constant_pool_unavailable(masm());
if (COMPRESS_POINTERS_BOOL) {
__ DecompressTagged(value_, value_);
}
__ CheckPageFlag(value_, scratch0_,
MemoryChunk::kPointersToHereAreInterestingMask, eq,
exit());
if (offset_ == no_reg) {
__ addi(scratch1_, object_, Operand(offset_immediate_));
} else {
DCHECK_EQ(0, offset_immediate_);
__ add(scratch1_, object_, offset_);
}
SaveFPRegsMode const save_fp_mode = frame()->DidAllocateDoubleRegisters()
? SaveFPRegsMode::kSave
: SaveFPRegsMode::kIgnore;
if (must_save_lr_) {
// We need to save and restore lr if the frame was elided.
__ mflr(scratch0_);
__ Push(scratch0_);
unwinding_info_writer_->MarkLinkRegisterOnTopOfStack(__ pc_offset());
}
if (mode_ == RecordWriteMode::kValueIsEphemeronKey) {
__ CallEphemeronKeyBarrier(object_, scratch1_, save_fp_mode);
#if V8_ENABLE_WEBASSEMBLY
} else if (stub_mode_ == StubCallMode::kCallWasmRuntimeStub) {
__ CallRecordWriteStubSaveRegisters(object_, scratch1_, save_fp_mode,
StubCallMode::kCallWasmRuntimeStub);
#endif // V8_ENABLE_WEBASSEMBLY
} else {
__ CallRecordWriteStubSaveRegisters(object_, scratch1_, save_fp_mode);
}
if (must_save_lr_) {
// We need to save and restore lr if the frame was elided.
__ Pop(scratch0_);
__ mtlr(scratch0_);
unwinding_info_writer_->MarkPopLinkRegisterFromTopOfStack(__ pc_offset());
}
}
private:
Register const object_;
Register const offset_;
int32_t const offset_immediate_; // Valid if offset_ == no_reg.
Register const value_;
Register const scratch0_;
Register const scratch1_;
RecordWriteMode const mode_;
#if V8_ENABLE_WEBASSEMBLY
StubCallMode stub_mode_;
#endif // V8_ENABLE_WEBASSEMBLY
bool must_save_lr_;
UnwindingInfoWriter* const unwinding_info_writer_;
Zone* zone_;
};
#if V8_ENABLE_WEBASSEMBLY
class OutOfLineTrap final : public OutOfLineCode {
public:
OutOfLineTrap(CodeGenerator* gen, Instruction* instr)
: OutOfLineCode(gen), instr_(instr), gen_(gen) {}
void Generate() final {
PPCOperandConverter i(gen_, instr_);
TrapId trap_id =
static_cast<TrapId>(i.InputInt32(instr_->InputCount() - 1));
GenerateCallToTrap(trap_id);
}
private:
void GenerateCallToTrap(TrapId trap_id) {
gen_->AssembleSourcePosition(instr_);
// A direct call to a wasm runtime stub defined in this module.
// Just encode the stub index. This will be patched when the code
// is added to the native module and copied into wasm code space.
__ Call(static_cast<Address>(trap_id), RelocInfo::WASM_STUB_CALL);
ReferenceMap* reference_map = gen_->zone()->New<ReferenceMap>(gen_->zone());
gen_->RecordSafepoint(reference_map);
if (v8_flags.debug_code) {
__ stop();
}
}
Instruction* instr_;
CodeGenerator* gen_;
};
#endif // V8_ENABLE_WEBASSEMBLY
Condition FlagsConditionToCondition(FlagsCondition condition, ArchOpcode op) {
switch (condition) {
case kEqual:
return eq;
case kNotEqual:
return ne;
case kSignedLessThan:
case kUnsignedLessThan:
return lt;
case kSignedGreaterThanOrEqual:
case kUnsignedGreaterThanOrEqual:
return ge;
case kSignedLessThanOrEqual:
case kUnsignedLessThanOrEqual:
return le;
case kSignedGreaterThan:
case kUnsignedGreaterThan:
return gt;
case kOverflow:
// Overflow checked for add/sub only.
switch (op) {
case kPPC_Add64:
case kPPC_Sub64:
case kPPC_Neg64:
case kPPC_Mul64:
return overflow64;
case kPPC_Add32:
case kPPC_Sub32:
case kPPC_Neg32:
case kPPC_Mul32:
return overflow32;
default:
break;
}
break;
case kNotOverflow:
switch (op) {
case kPPC_Add64:
case kPPC_Sub64:
case kPPC_Neg64:
case kPPC_Mul64:
return nooverflow64;
case kPPC_Add32:
case kPPC_Sub32:
case kPPC_Neg32:
case kPPC_Mul32:
return nooverflow32;
default:
break;
}
break;
default:
break;
}
UNREACHABLE();
}
} // namespace
#define ASSEMBLE_FLOAT_UNOP_RC(asm_instr, round) \
do { \
__ asm_instr(i.OutputDoubleRegister(), i.InputDoubleRegister(0), \
i.OutputRCBit()); \
if (round) { \
__ frsp(i.OutputDoubleRegister(), i.OutputDoubleRegister()); \
} \
} while (0)
#define ASSEMBLE_FLOAT_BINOP_RC(asm_instr, round) \
do { \
__ asm_instr(i.OutputDoubleRegister(), i.InputDoubleRegister(0), \
i.InputDoubleRegister(1), i.OutputRCBit()); \
if (round) { \
__ frsp(i.OutputDoubleRegister(), i.OutputDoubleRegister()); \
} \
} while (0)
#define ASSEMBLE_BINOP(asm_instr_reg, asm_instr_imm) \
do { \
if (HasRegisterInput(instr, 1)) { \
__ asm_instr_reg(i.OutputRegister(), i.InputRegister(0), \
i.InputRegister(1)); \
} else { \
__ asm_instr_imm(i.OutputRegister(), i.InputRegister(0), \
i.InputImmediate(1)); \
} \
} while (0)
#define ASSEMBLE_BINOP_RC(asm_instr_reg, asm_instr_imm) \
do { \
if (HasRegisterInput(instr, 1)) { \
__ asm_instr_reg(i.OutputRegister(), i.InputRegister(0), \
i.InputRegister(1), i.OutputRCBit()); \
} else { \
__ asm_instr_imm(i.OutputRegister(), i.InputRegister(0), \
i.InputImmediate(1), i.OutputRCBit()); \
} \
} while (0)
#define ASSEMBLE_BINOP_INT_RC(asm_instr_reg, asm_instr_imm) \
do { \
if (HasRegisterInput(instr, 1)) { \
__ asm_instr_reg(i.OutputRegister(), i.InputRegister(0), \
i.InputRegister(1), i.OutputRCBit()); \
} else { \
__ asm_instr_imm(i.OutputRegister(), i.InputRegister(0), \
i.InputImmediate(1), i.OutputRCBit()); \
} \
} while (0)
#define ASSEMBLE_COMPARE(cmp_instr, cmpl_instr) \
do { \
const CRegister cr = cr0; \
if (HasRegisterInput(instr, 1)) { \
if (i.CompareLogical()) { \
__ cmpl_instr(i.InputRegister(0), i.InputRegister(1), cr); \
} else { \
__ cmp_instr(i.InputRegister(0), i.InputRegister(1), cr); \
} \
} else { \
if (i.CompareLogical()) { \
__ cmpl_instr##i(i.InputRegister(0), i.InputImmediate(1), cr); \
} else { \
__ cmp_instr##i(i.InputRegister(0), i.InputImmediate(1), cr); \
} \
} \
DCHECK_EQ(SetRC, i.OutputRCBit()); \
} while (0)
#define ASSEMBLE_FLOAT_COMPARE(cmp_instr) \
do { \
const CRegister cr = cr0; \
__ cmp_instr(i.InputDoubleRegister(0), i.InputDoubleRegister(1), cr); \
DCHECK_EQ(SetRC, i.OutputRCBit()); \
} while (0)
#define ASSEMBLE_FLOAT_MODULO() \
do { \
FrameScope scope(masm(), StackFrame::MANUAL); \
UseScratchRegisterScope temps(masm()); \
Register scratch = temps.Acquire(); \
__ PrepareCallCFunction(0, 2, scratch); \
__ MovToFloatParameters(i.InputDoubleRegister(0), \
i.InputDoubleRegister(1)); \
__ CallCFunction(ExternalReference::mod_two_doubles_operation(), 0, 2); \
__ MovFromFloatResult(i.OutputDoubleRegister()); \
DCHECK_EQ(LeaveRC, i.OutputRCBit()); \
} while (0)
#define ASSEMBLE_IEEE754_UNOP(name) \
do { \
/* TODO(bmeurer): We should really get rid of this special instruction, */ \
/* and generate a CallAddress instruction instead. */ \
FrameScope scope(masm(), StackFrame::MANUAL); \
UseScratchRegisterScope temps(masm()); \
Register scratch = temps.Acquire(); \
__ PrepareCallCFunction(0, 1, scratch); \
__ MovToFloatParameter(i.InputDoubleRegister(0)); \
__ CallCFunction(ExternalReference::ieee754_##name##_function(), 0, 1); \
/* Move the result in the double result register. */ \
__ MovFromFloatResult(i.OutputDoubleRegister()); \
DCHECK_EQ(LeaveRC, i.OutputRCBit()); \
} while (0)
#define ASSEMBLE_IEEE754_BINOP(name) \
do { \
/* TODO(bmeurer): We should really get rid of this special instruction, */ \
/* and generate a CallAddress instruction instead. */ \
FrameScope scope(masm(), StackFrame::MANUAL); \
UseScratchRegisterScope temps(masm()); \
Register scratch = temps.Acquire(); \
__ PrepareCallCFunction(0, 2, scratch); \
__ MovToFloatParameters(i.InputDoubleRegister(0), \
i.InputDoubleRegister(1)); \
__ CallCFunction(ExternalReference::ieee754_##name##_function(), 0, 2); \
/* Move the result in the double result register. */ \
__ MovFromFloatResult(i.OutputDoubleRegister()); \
DCHECK_EQ(LeaveRC, i.OutputRCBit()); \
} while (0)
#define ASSEMBLE_LOAD_FLOAT(asm_instr, asm_instrp, asm_instrx) \
do { \
DoubleRegister result = i.OutputDoubleRegister(); \
size_t index = 0; \
AddressingMode mode = kMode_None; \
MemOperand operand = i.MemoryOperand(&mode, &index); \
bool is_atomic = i.InputInt32(index); \
if (mode == kMode_MRI) { \
intptr_t offset = operand.offset(); \
if (is_int16(offset)) { \
__ asm_instr(result, operand); \
} else { \
CHECK(CpuFeatures::IsSupported(PPC_10_PLUS)); \
__ asm_instrp(result, operand); \
} \
} else { \
__ asm_instrx(result, operand); \
} \
if (is_atomic) __ lwsync(); \
DCHECK_EQ(LeaveRC, i.OutputRCBit()); \
} while (0)
#define ASSEMBLE_LOAD_INTEGER(asm_instr, asm_instrp, asm_instrx, \
must_be_aligned) \
do { \
Register result = i.OutputRegister(); \
size_t index = 0; \
AddressingMode mode = kMode_None; \
MemOperand operand = i.MemoryOperand(&mode, &index); \
bool is_atomic = i.InputInt32(index); \
if (mode == kMode_MRI) { \
intptr_t offset = operand.offset(); \
bool misaligned = offset & 3; \
if (is_int16(offset) && (!must_be_aligned || !misaligned)) { \
__ asm_instr(result, operand); \
} else { \
CHECK(CpuFeatures::IsSupported(PPC_10_PLUS)); \
__ asm_instrp(result, operand); \
} \
} else { \
__ asm_instrx(result, operand); \
} \
if (is_atomic) __ lwsync(); \
DCHECK_EQ(LeaveRC, i.OutputRCBit()); \
} while (0)
#define ASSEMBLE_LOAD_INTEGER_RR(asm_instr) \
do { \
Register result = i.OutputRegister(); \
size_t index = 0; \
AddressingMode mode = kMode_None; \
MemOperand operand = i.MemoryOperand(&mode, &index); \
DCHECK_EQ(mode, kMode_MRR); \
bool is_atomic = i.InputInt32(index); \
__ asm_instr(result, operand); \
if (is_atomic) __ lwsync(); \
DCHECK_EQ(LeaveRC, i.OutputRCBit()); \
} while (0)
#define ASSEMBLE_STORE_FLOAT(asm_instr, asm_instrp, asm_instrx) \
do { \
size_t index = 0; \
AddressingMode mode = kMode_None; \
MemOperand operand = i.MemoryOperand(&mode, &index); \
DoubleRegister value = i.InputDoubleRegister(index); \
bool is_atomic = i.InputInt32(3); \
if (is_atomic) __ lwsync(); \
/* removed frsp as instruction-selector checked */ \
/* value to be kFloat32 */ \
if (mode == kMode_MRI) { \
intptr_t offset = operand.offset(); \
if (is_int16(offset)) { \
__ asm_instr(value, operand); \
} else { \
CHECK(CpuFeatures::IsSupported(PPC_10_PLUS)); \
__ asm_instrp(value, operand); \
} \
} else { \
__ asm_instrx(value, operand); \
} \
if (is_atomic) __ sync(); \
DCHECK_EQ(LeaveRC, i.OutputRCBit()); \
} while (0)
#define ASSEMBLE_STORE_INTEGER(asm_instr, asm_instrp, asm_instrx, \
must_be_aligned) \
do { \
size_t index = 0; \
AddressingMode mode = kMode_None; \
MemOperand operand = i.MemoryOperand(&mode, &index); \
Register value = i.InputRegister(index); \
bool is_atomic = i.InputInt32(index + 1); \
if (is_atomic) __ lwsync(); \
if (mode == kMode_MRI) { \
intptr_t offset = operand.offset(); \
bool misaligned = offset & 3; \
if (is_int16(offset) && (!must_be_aligned || !misaligned)) { \
__ asm_instr(value, operand); \
} else { \
CHECK(CpuFeatures::IsSupported(PPC_10_PLUS)); \
__ asm_instrp(value, operand); \
} \
} else { \
__ asm_instrx(value, operand); \
} \
if (is_atomic) __ sync(); \
DCHECK_EQ(LeaveRC, i.OutputRCBit()); \
} while (0)
#define ASSEMBLE_STORE_INTEGER_RR(asm_instr) \
do { \
size_t index = 0; \
AddressingMode mode = kMode_None; \
MemOperand operand = i.MemoryOperand(&mode, &index); \
DCHECK_EQ(mode, kMode_MRR); \
Register value = i.InputRegister(index); \
bool is_atomic = i.InputInt32(index + 1); \
if (is_atomic) __ lwsync(); \
__ asm_instr(value, operand); \
if (is_atomic) __ sync(); \
DCHECK_EQ(LeaveRC, i.OutputRCBit()); \
} while (0)
// TODO(mbrandy): fix paths that produce garbage in offset's upper 32-bits.
#define CleanUInt32(x) __ ClearLeftImm(x, x, Operand(32))
#if V8_ENABLE_WEBASSEMBLY
static inline bool is_wasm_on_be(bool IsWasm) {
#if V8_TARGET_BIG_ENDIAN
return IsWasm;
#else
return false;
#endif
}
#endif
#if V8_ENABLE_WEBASSEMBLY
#define MAYBE_REVERSE_IF_WASM(dst, src, op, scratch, reset) \
if (is_wasm_on_be(info()->IsWasm())) { \
__ op(dst, src, scratch); \
if (reset) src = dst; \
}
#else
#define MAYBE_REVERSE_IF_WASM(dst, src, op, scratch, reset)
#endif
#define ASSEMBLE_ATOMIC_EXCHANGE(_type, reverse_op) \
do { \
UseScratchRegisterScope temps(masm()); \
Register scratch = temps.Acquire(); \
Register val = i.InputRegister(2); \
Register dst = i.OutputRegister(); \
MAYBE_REVERSE_IF_WASM(ip, val, reverse_op, scratch, true); \
__ AtomicExchange<_type>( \
MemOperand(i.InputRegister(0), i.InputRegister(1)), val, dst); \
MAYBE_REVERSE_IF_WASM(dst, dst, reverse_op, scratch, false); \
} while (false)
#define ASSEMBLE_ATOMIC_COMPARE_EXCHANGE(_type, reverse_op) \
do { \
UseScratchRegisterScope temps(masm()); \
Register scratch = temps.Acquire(); \
Register expected_val = i.InputRegister(2); \
Register new_val = i.InputRegister(3); \
Register dst = i.OutputRegister(); \
MAYBE_REVERSE_IF_WASM(ip, expected_val, reverse_op, scratch, true); \
MAYBE_REVERSE_IF_WASM(r0, new_val, reverse_op, scratch, true); \
__ AtomicCompareExchange<_type>( \
MemOperand(i.InputRegister(0), i.InputRegister(1)), expected_val, \
new_val, dst, scratch); \
MAYBE_REVERSE_IF_WASM(dst, dst, reverse_op, scratch, false); \
} while (false)
#define ASSEMBLE_ATOMIC_BINOP_BYTE(bin_inst, _type) \
do { \
auto bin_op = [&](Register dst, Register lhs, Register rhs) { \
if (std::is_signed_v<_type>) { \
__ extsb(dst, lhs); \
__ bin_inst(dst, dst, rhs); \
} else { \
__ bin_inst(dst, lhs, rhs); \
} \
}; \
MemOperand dst_operand = \
MemOperand(i.InputRegister(0), i.InputRegister(1)); \
UseScratchRegisterScope temps(masm()); \
Register scratch = temps.Acquire(); \
__ AtomicOps<_type>(dst_operand, i.InputRegister(2), i.OutputRegister(), \
scratch, bin_op); \
break; \
} while (false)
#define ASSEMBLE_ATOMIC_BINOP(bin_inst, _type, reverse_op, scratch) \
do { \
auto bin_op = [&](Register dst, Register lhs, Register rhs) { \
Register _lhs = lhs; \
MAYBE_REVERSE_IF_WASM(dst, _lhs, reverse_op, scratch, true); \
if (std::is_signed_v<_type>) { \
switch (sizeof(_type)) { \
case 1: \
UNREACHABLE(); \
break; \
case 2: \
__ extsh(dst, _lhs); \
break; \
case 4: \
__ extsw(dst, _lhs); \
break; \
case 8: \
break; \
default: \
UNREACHABLE(); \
} \
} \
__ bin_inst(dst, _lhs, rhs); \
MAYBE_REVERSE_IF_WASM(dst, dst, reverse_op, scratch, false); \
}; \
MemOperand dst_operand = \
MemOperand(i.InputRegister(0), i.InputRegister(1)); \
UseScratchRegisterScope temps2(masm()); \
Register scratch2 = temps2.Acquire(); \
__ AtomicOps<_type>(dst_operand, i.InputRegister(2), i.OutputRegister(), \
scratch2, bin_op); \
MAYBE_REVERSE_IF_WASM(i.OutputRegister(), i.OutputRegister(), reverse_op, \
scratch, false); \
break; \
} while (false)
void CodeGenerator::AssembleDeconstructFrame() {
__ LeaveFrame(StackFrame::MANUAL);
unwinding_info_writer_.MarkFrameDeconstructed(__ pc_offset());
}
void CodeGenerator::AssemblePrepareTailCall() {
if (frame_access_state()->has_frame()) {
__ RestoreFrameStateForTailCall();
}
frame_access_state()->SetFrameAccessToSP();
}
bool HasImmediateInput(Instruction* instr, size_t index) {
return instr->InputAt(index)->IsImmediate();
}
namespace {
void FlushPendingPushRegisters(MacroAssembler* masm,
FrameAccessState* frame_access_state,
ZoneVector<Register>* pending_pushes) {
switch (pending_pushes->size()) {
case 0:
break;
case 1:
masm->Push((*pending_pushes)[0]);
break;
case 2:
masm->Push((*pending_pushes)[0], (*pending_pushes)[1]);
break;
case 3:
masm->Push((*pending_pushes)[0], (*pending_pushes)[1],
(*pending_pushes)[2]);
break;
default:
UNREACHABLE();
}
frame_access_state->IncreaseSPDelta(pending_pushes->size());
pending_pushes->clear();
}
void AdjustStackPointerForTailCall(
MacroAssembler* masm, FrameAccessState* state, int new_slot_above_sp,
ZoneVector<Register>* pending_pushes = nullptr,
bool allow_shrinkage = true) {
int current_sp_offset = state->GetSPToFPSlotCount() +
StandardFrameConstants::kFixedSlotCountAboveFp;
int stack_slot_delta = new_slot_above_sp - current_sp_offset;
if (stack_slot_delta > 0) {
if (pending_pushes != nullptr) {
FlushPendingPushRegisters(masm, state, pending_pushes);
}
masm->AddS64(sp, sp, Operand(-stack_slot_delta * kSystemPointerSize), r0);
state->IncreaseSPDelta(stack_slot_delta);
} else if (allow_shrinkage && stack_slot_delta < 0) {
if (pending_pushes != nullptr) {
FlushPendingPushRegisters(masm, state, pending_pushes);
}
masm->AddS64(sp, sp, Operand(-stack_slot_delta * kSystemPointerSize), r0);
state->IncreaseSPDelta(stack_slot_delta);
}
}
} // namespace
void CodeGenerator::AssembleTailCallBeforeGap(Instruction* instr,
int first_unused_slot_offset) {
ZoneVector<MoveOperands*> pushes(zone());
GetPushCompatibleMoves(instr, kRegisterPush, &pushes);
if (!pushes.empty() &&
(LocationOperand::cast(pushes.back()->destination()).index() + 1 ==
first_unused_slot_offset)) {
PPCOperandConverter g(this, instr);
ZoneVector<Register> pending_pushes(zone());
for (auto move : pushes) {
LocationOperand destination_location(
LocationOperand::cast(move->destination()));
InstructionOperand source(move->source());
AdjustStackPointerForTailCall(
masm(), frame_access_state(),
destination_location.index() - pending_pushes.size(),
&pending_pushes);
// Pushes of non-register data types are not supported.
DCHECK(source.IsRegister());
LocationOperand source_location(LocationOperand::cast(source));
pending_pushes.push_back(source_location.GetRegister());
// TODO(arm): We can push more than 3 registers at once. Add support in
// the macro-assembler for pushing a list of registers.
if (pending_pushes.size() == 3) {
FlushPendingPushRegisters(masm(), frame_access_state(),
&pending_pushes);
}
move->Eliminate();
}
FlushPendingPushRegisters(masm(), frame_access_state(), &pending_pushes);
}
AdjustStackPointerForTailCall(masm(), frame_access_state(),
first_unused_slot_offset, nullptr, false);
}
void CodeGenerator::AssembleTailCallAfterGap(Instruction* instr,
int first_unused_slot_offset) {
AdjustStackPointerForTailCall(masm(), frame_access_state(),
first_unused_slot_offset);
}
// Check that {kJavaScriptCallCodeStartRegister} is correct.
void CodeGenerator::AssembleCodeStartRegisterCheck() {
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ ComputeCodeStartAddress(scratch);
__ CmpS64(scratch, kJavaScriptCallCodeStartRegister);
__ Assert(eq, AbortReason::kWrongFunctionCodeStart);
}
void CodeGenerator::AssembleDispatchHandleRegisterCheck() {
CHECK(!V8_JS_LINKAGE_INCLUDES_DISPATCH_HANDLE_BOOL);
}
void CodeGenerator::AssertNotDeoptimized() {
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ AssertNotDeoptimized(scratch);
}
// Assembles an instruction after register allocation, producing machine code.
CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
Instruction* instr) {
PPCOperandConverter i(this, instr);
ArchOpcode opcode = ArchOpcodeField::decode(instr->opcode());
switch (opcode) {
case kArchCallCodeObject: {
v8::internal::Assembler::BlockTrampolinePoolScope block_trampoline_pool(
masm());
if (HasRegisterInput(instr, 0)) {
Register reg = i.InputRegister(0);
DCHECK_IMPLIES(
instr->HasCallDescriptorFlag(CallDescriptor::kFixedTargetRegister),
reg == kJavaScriptCallCodeStartRegister);
__ CallCodeObject(reg);
} else {
__ Call(i.InputCode(0), RelocInfo::CODE_TARGET);
}
RecordCallPosition(instr);
DCHECK_EQ(LeaveRC, i.OutputRCBit());
frame_access_state()->ClearSPDelta();
break;
}
case kArchCallBuiltinPointer: {
DCHECK(!instr->InputAt(0)->IsImmediate());
Register builtin_index = i.InputRegister(0);
Register target =
instr->HasCallDescriptorFlag(CallDescriptor::kFixedTargetRegister)
? kJavaScriptCallCodeStartRegister
: builtin_index;
__ CallBuiltinByIndex(builtin_index, target);
RecordCallPosition(instr);
frame_access_state()->ClearSPDelta();
break;
}
#if V8_ENABLE_WEBASSEMBLY
case kArchCallWasmFunction:
case kArchCallWasmFunctionIndirect: {
// We must not share code targets for calls to builtins for wasm code, as
// they might need to be patched individually.
if (instr->InputAt(0)->IsImmediate()) {
DCHECK_EQ(opcode, kArchCallWasmFunction);
Constant constant = i.ToConstant(instr->InputAt(0));
Address wasm_code = static_cast<Address>(constant.ToInt64());
__ Call(wasm_code, constant.rmode());
} else if (opcode == kArchCallWasmFunctionIndirect) {
__ CallWasmCodePointer(i.InputRegister(0));
} else {
__ Call(i.InputRegister(0));
}
RecordCallPosition(instr);
DCHECK_EQ(LeaveRC, i.OutputRCBit());
frame_access_state()->ClearSPDelta();
break;
}
case kArchTailCallWasm:
case kArchTailCallWasmIndirect: {
// We must not share code targets for calls to builtins for wasm code, as
// they might need to be patched individually.
if (instr->InputAt(0)->IsImmediate()) {
DCHECK_EQ(opcode, kArchTailCallWasm);
Constant constant = i.ToConstant(instr->InputAt(0));
Address wasm_code = static_cast<Address>(constant.ToInt64());
__ Jump(wasm_code, constant.rmode());
} else if (opcode == kArchTailCallWasmIndirect) {
__ CallWasmCodePointer(i.InputRegister(0), CallJumpMode::kTailCall);
} else {
__ Jump(i.InputRegister(0));
}
DCHECK_EQ(LeaveRC, i.OutputRCBit());
frame_access_state()->ClearSPDelta();
frame_access_state()->SetFrameAccessToDefault();
break;
}
#endif // V8_ENABLE_WEBASSEMBLY
case kArchTailCallCodeObject: {
if (HasRegisterInput(instr, 0)) {
Register reg = i.InputRegister(0);
DCHECK_IMPLIES(
instr->HasCallDescriptorFlag(CallDescriptor::kFixedTargetRegister),
reg == kJavaScriptCallCodeStartRegister);
__ JumpCodeObject(reg);
} else {
// We cannot use the constant pool to load the target since
// we've already restored the caller's frame.
ConstantPoolUnavailableScope constant_pool_unavailable(masm());
__ Jump(i.InputCode(0), RelocInfo::CODE_TARGET);
}
DCHECK_EQ(LeaveRC, i.OutputRCBit());
frame_access_state()->ClearSPDelta();
frame_access_state()->SetFrameAccessToDefault();
break;
}
case kArchTailCallAddress: {
CHECK(!instr->InputAt(0)->IsImmediate());
Register reg = i.InputRegister(0);
DCHECK_IMPLIES(
instr->HasCallDescriptorFlag(CallDescriptor::kFixedTargetRegister),
reg == kJavaScriptCallCodeStartRegister);
__ Jump(reg);
frame_access_state()->ClearSPDelta();
frame_access_state()->SetFrameAccessToDefault();
break;
}
case kArchCallJSFunction: {
v8::internal::Assembler::BlockTrampolinePoolScope block_trampoline_pool(
masm());
uint32_t num_arguments =
i.InputUint32(instr->JSCallArgumentCountInputIndex());
if (HasImmediateInput(instr, 0)) {
Handle<HeapObject> constant =
i.ToConstant(instr->InputAt(0)).ToHeapObject();
__ Move(kJavaScriptCallTargetRegister, constant);
if (Handle<JSFunction> function; TryCast(constant, &function)) {
if (function->shared()->HasBuiltinId()) {
Builtin builtin = function->shared()->builtin_id();
size_t expected = Builtins::GetFormalParameterCount(builtin);
if (num_arguments == expected) {
__ CallBuiltin(builtin);
} else {
__ AssertUnreachable(AbortReason::kJSSignatureMismatch);
}
} else {
JSDispatchHandle dispatch_handle = function->dispatch_handle();
size_t expected = isolate()->js_dispatch_table().GetParameterCount(
dispatch_handle);
if (num_arguments >= expected) {
__ RecordJSDispatchHandle(dispatch_handle, expected);
__ CallJSDispatchEntry(dispatch_handle, expected);
} else {
__ AssertUnreachable(AbortReason::kJSSignatureMismatch);
}
}
} else {
__ CallJSFunction(kJavaScriptCallTargetRegister, num_arguments);
}
} else {
Register func = i.InputRegister(0);
if (v8_flags.debug_code) {
// Check the function's context matches the context argument.
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ LoadTaggedField(
scratch, FieldMemOperand(func, offsetof(JSFunction, context_)),
r0);
__ CmpS64(cp, scratch);
__ Assert(eq, AbortReason::kWrongFunctionContext);
}
__ CallJSFunction(func, num_arguments);
}
RecordCallPosition(instr);
DCHECK_EQ(LeaveRC, i.OutputRCBit());
frame_access_state()->ClearSPDelta();
break;
}
case kArchPrepareCallCFunction: {
int const num_parameters = MiscField::decode(instr->opcode());
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ PrepareCallCFunction(num_parameters, scratch);
// Frame alignment requires using FP-relative frame addressing.
frame_access_state()->SetFrameAccessToFP();
break;
}
case kArchSaveCallerRegisters: {
fp_mode_ =
static_cast<SaveFPRegsMode>(MiscField::decode(instr->opcode()));
DCHECK(fp_mode_ == SaveFPRegsMode::kIgnore ||
fp_mode_ == SaveFPRegsMode::kSave);
// kReturnRegister0 should have been saved before entering the stub.
int bytes = __ PushCallerSaved(fp_mode_, ip, r0, kReturnRegister0);
DCHECK(IsAligned(bytes, kSystemPointerSize));
DCHECK_EQ(0, frame_access_state()->sp_delta());
frame_access_state()->IncreaseSPDelta(bytes / kSystemPointerSize);
DCHECK(!caller_registers_saved_);
caller_registers_saved_ = true;
break;
}
case kArchRestoreCallerRegisters: {
DCHECK(fp_mode_ ==
static_cast<SaveFPRegsMode>(MiscField::decode(instr->opcode())));
DCHECK(fp_mode_ == SaveFPRegsMode::kIgnore ||
fp_mode_ == SaveFPRegsMode::kSave);
// Don't overwrite the returned value.
int bytes = __ PopCallerSaved(fp_mode_, ip, r0, kReturnRegister0);
frame_access_state()->IncreaseSPDelta(-(bytes / kSystemPointerSize));
DCHECK_EQ(0, frame_access_state()->sp_delta());
DCHECK(caller_registers_saved_);
caller_registers_saved_ = false;
break;
}
case kArchPrepareTailCall:
AssemblePrepareTailCall();
break;
case kArchComment:
__ RecordComment(reinterpret_cast<const char*>(i.InputInt64(0)),
SourceLocation());
break;
case kArchCallCFunction: {
uint32_t param_counts = i.InputUint32(instr->InputCount() - 1);
int const num_gp_parameters = ParamField::decode(param_counts);
int const fp_param_field = FPParamField::decode(param_counts);
int num_fp_parameters = fp_param_field;
bool has_function_descriptor = false;
SetIsolateDataSlots set_isolate_data_slots = SetIsolateDataSlots::kYes;
#if ABI_USES_FUNCTION_DESCRIPTORS
// AIX/PPC64BE Linux uses a function descriptor
int kNumFPParametersMask = kHasFunctionDescriptorBitMask - 1;
num_fp_parameters = kNumFPParametersMask & fp_param_field;
has_function_descriptor =
(fp_param_field & kHasFunctionDescriptorBitMask) != 0;
#endif
Label return_location;
#if V8_ENABLE_WEBASSEMBLY
if (linkage()->GetIncomingDescriptor()->IsWasmCapiFunction()) {
UseScratchRegisterScope wasm_temps(masm());
Register wasm_scratch = wasm_temps.Acquire();
// Put the return address in a stack slot.
__ GetLabelAddress(wasm_scratch, &return_location, r0);
__ StoreU64(wasm_scratch,
MemOperand(fp, WasmExitFrameConstants::kCallingPCOffset));
set_isolate_data_slots = SetIsolateDataSlots::kNo;
}
#endif // V8_ENABLE_WEBASSEMBLY
int pc_offset;
if (instr->InputAt(0)->IsImmediate()) {
ExternalReference ref = i.InputExternalReference(0);
pc_offset = __ CallCFunction(ref, num_gp_parameters, num_fp_parameters,
set_isolate_data_slots,
has_function_descriptor, &return_location);
} else {
Register func = i.InputRegister(0);
pc_offset = __ CallCFunction(func, num_gp_parameters, num_fp_parameters,
set_isolate_data_slots,
has_function_descriptor, &return_location);
}
RecordSafepoint(instr->reference_map(), pc_offset);
if (instr->HasCallDescriptorFlag(CallDescriptor::kHasExceptionHandler)) {
handlers_.push_back({nullptr, pc_offset});
}
if (instr->HasCallDescriptorFlag(CallDescriptor::kNeedsFrameState)) {
RecordDeoptInfo(instr, pc_offset);
}
frame_access_state()->SetFrameAccessToDefault();
// Ideally, we should decrement SP delta to match the change of stack
// pointer in CallCFunction. However, for certain architectures (e.g.
// ARM), there may be more strict alignment requirement, causing old SP
// to be saved on the stack. In those cases, we can not calculate the SP
// delta statically.
frame_access_state()->ClearSPDelta();
if (caller_registers_saved_) {
// Need to re-sync SP delta introduced in kArchSaveCallerRegisters.
// Here, we assume the sequence to be:
// kArchSaveCallerRegisters;
// kArchCallCFunction;
// kArchRestoreCallerRegisters;
int bytes =
__ RequiredStackSizeForCallerSaved(fp_mode_, kReturnRegister0);
frame_access_state()->IncreaseSPDelta(bytes / kSystemPointerSize);
}
break;
}
case kArchJmp:
AssembleArchJump(i.InputRpo(0));
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kArchBinarySearchSwitch:
AssembleArchBinarySearchSwitch(instr);
break;
case kArchTableSwitch:
AssembleArchTableSwitch(instr);
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kArchAbortCSADcheck:
DCHECK(i.InputRegister(0) == r4);
{
// 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(masm(), StackFrame::NO_FRAME_TYPE);
__ CallBuiltin(Builtin::kAbortCSADcheck);
}
__ stop();
break;
case kArchDebugBreak:
__ DebugBreak();
break;
case kArchNop:
// don't emit code for nops.
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kArchPause:
__ isync();
break;
case kArchDeoptimize: {
DeoptimizationExit* exit =
BuildTranslation(instr, -1, 0, 0, OutputFrameStateCombine::Ignore());
__ b(exit->label());
break;
}
#if V8_ENABLE_WEBASSEMBLY
case kArchTrap:
__ b(zone()->New<OutOfLineTrap>(this, instr)->entry());
break;
#endif // V8_ENABLE_WEBASSEMBLY
case kArchRet:
AssembleReturn(instr->InputAt(0));
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kArchFramePointer:
__ mr(i.OutputRegister(), fp);
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kArchParentFramePointer:
if (frame_access_state()->has_frame()) {
__ LoadU64(i.OutputRegister(), MemOperand(fp, 0));
} else {
__ mr(i.OutputRegister(), fp);
}
break;
case kArchRootPointer:
__ mr(i.OutputRegister(), kRootRegister);
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
#if V8_ENABLE_WEBASSEMBLY
case kArchStackPointer:
__ mr(i.OutputRegister(), sp);
break;
case kArchSetStackPointer: {
DCHECK(instr->InputAt(0)->IsRegister());
__ mr(sp, i.InputRegister(0));
break;
}
#endif // V8_ENABLE_WEBASSEMBLY
case kArchStackPointerGreaterThan: {
// Potentially apply an offset to the current stack pointer before the
// comparison to consider the size difference of an optimized frame versus
// the contained unoptimized frames.
Register lhs_register = sp;
uint32_t offset;
if (ShouldApplyOffsetToStackCheck(instr, &offset)) {
lhs_register = i.TempRegister(0);
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ SubS64(lhs_register, sp, Operand(offset), scratch);
}
constexpr size_t kValueIndex = 0;
DCHECK(instr->InputAt(kValueIndex)->IsRegister());
__ CmpU64(lhs_register, i.InputRegister(kValueIndex), cr0);
break;
}
case kArchStackCheckOffset:
__ LoadSmiLiteral(i.OutputRegister(),
Smi::FromInt(GetStackCheckOffset()));
break;
case kArchTruncateDoubleToI:
__ TruncateDoubleToI(isolate(), zone(), i.OutputRegister(),
i.InputDoubleRegister(0), DetermineStubCallMode(),
kScratchDoubleReg);
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kArchStoreWithWriteBarrier: {
RecordWriteMode mode = RecordWriteModeField::decode(instr->opcode());
Register object = i.InputRegister(0);
Register value = i.InputRegister(2);
Register scratch0 = i.TempRegister(0);
Register scratch1 = i.TempRegister(1);
OutOfLineRecordWrite* ool;
if (v8_flags.debug_code) {
// Checking that |value| is not a cleared weakref: our write barrier
// does not support that for now.
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ CmpS64(value, Operand(kClearedWeakHeapObjectLower32), scratch);
__ Check(ne, AbortReason::kOperandIsCleared);
}
AddressingMode addressing_mode =
AddressingModeField::decode(instr->opcode());
if (addressing_mode == kMode_MRI) {
int32_t offset = i.InputInt32(1);
ool = zone()->New<OutOfLineRecordWrite>(
this, object, offset, value, scratch0, scratch1, mode,
DetermineStubCallMode(), &unwinding_info_writer_);
__ StoreTaggedField(value, MemOperand(object, offset), r0);
} else {
DCHECK_EQ(kMode_MRR, addressing_mode);
Register offset(i.InputRegister(1));
ool = zone()->New<OutOfLineRecordWrite>(
this, object, offset, value, scratch0, scratch1, mode,
DetermineStubCallMode(), &unwinding_info_writer_);
__ StoreTaggedField(value, MemOperand(object, offset), r0);
}
if (mode > RecordWriteMode::kValueIsPointer) {
__ JumpIfSmi(value, ool->exit());
}
__ CheckPageFlag(object, scratch0,
MemoryChunk::kPointersFromHereAreInterestingMask, ne,
ool->entry());
__ bind(ool->exit());
break;
}
case kArchStoreSkippedWriteBarrier: // Fall through.
case kArchAtomicStoreSkippedWriteBarrier: {
size_t index = 0;
AddressingMode mode = kMode_None;
MemOperand operand = i.MemoryOperand(&mode, &index);
CHECK_EQ(index, 2);
Register object = i.InputRegister(0);
Register value = i.InputRegister(2);
if (v8_flags.debug_code) {
// Checking that |value| is not a cleared weakref: our write barrier
// does not support that for now.
__ cmpi(value, Operand(kClearedWeakHeapObjectLower32));
__ Check(ne, AbortReason::kOperandIsCleared);
}
DCHECK(v8_flags.verify_write_barriers);
Register scratch = i.TempRegister(0);
auto ool = zone()->New<OutOfLineVerifySkippedWriteBarrier>(
this, object, value, scratch, &unwinding_info_writer_);
__ JumpIfNotSmi(value, ool->entry());
__ bind(ool->exit());
__ StoreTaggedField(value, operand, r0);
break;
}
case kArchStoreIndirectSkippedWriteBarrier:
case kArchStoreIndirectWithWriteBarrier: {
UNREACHABLE();
}
case kArchStackSlot: {
FrameOffset offset =
frame_access_state()->GetFrameOffset(i.InputInt32(0));
__ AddS64(i.OutputRegister(), offset.from_stack_pointer() ? sp : fp,
Operand(offset.offset()), r0);
break;
}
case kPPC_Peek: {
int reverse_slot = i.InputInt32(0);
int offset =
FrameSlotToFPOffset(frame()->GetTotalFrameSlotCount() - reverse_slot);
if (instr->OutputAt(0)->IsFPRegister()) {
LocationOperand* op = LocationOperand::cast(instr->OutputAt(0));
if (op->representation() == MachineRepresentation::kFloat64) {
__ LoadF64(i.OutputDoubleRegister(), MemOperand(fp, offset), r0);
} else if (op->representation() == MachineRepresentation::kFloat32) {
__ LoadF32(i.OutputFloatRegister(), MemOperand(fp, offset), r0);
} else {
DCHECK_EQ(MachineRepresentation::kSimd128, op->representation());
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ LoadSimd128(i.OutputSimd128Register(), MemOperand(fp, offset),
scratch);
}
} else {
__ LoadU64(i.OutputRegister(), MemOperand(fp, offset), r0);
}
break;
}
case kPPC_Sync: {
__ sync();
break;
}
case kPPC_And:
if (HasRegisterInput(instr, 1)) {
__ and_(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
i.OutputRCBit());
} else {
__ andi(i.OutputRegister(), i.InputRegister(0), i.InputImmediate(1));
}
break;
case kPPC_AndComplement:
__ andc(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
i.OutputRCBit());
break;
case kPPC_Or:
if (HasRegisterInput(instr, 1)) {
__ orx(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
i.OutputRCBit());
} else {
__ ori(i.OutputRegister(), i.InputRegister(0), i.InputImmediate(1));
DCHECK_EQ(LeaveRC, i.OutputRCBit());
}
break;
case kPPC_OrComplement:
__ orc(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
i.OutputRCBit());
break;
case kPPC_Xor:
if (HasRegisterInput(instr, 1)) {
__ xor_(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
i.OutputRCBit());
} else {
__ xori(i.OutputRegister(), i.InputRegister(0), i.InputImmediate(1));
DCHECK_EQ(LeaveRC, i.OutputRCBit());
}
break;
case kPPC_ShiftLeft32:
ASSEMBLE_BINOP_RC(ShiftLeftU32, ShiftLeftU32);
break;
case kPPC_ShiftLeft64:
ASSEMBLE_BINOP_RC(ShiftLeftU64, ShiftLeftU64);
break;
case kPPC_ShiftRight32:
ASSEMBLE_BINOP_RC(ShiftRightU32, ShiftRightU32);
break;
case kPPC_ShiftRight64:
ASSEMBLE_BINOP_RC(ShiftRightU64, ShiftRightU64);
break;
case kPPC_ShiftRightAlg32:
ASSEMBLE_BINOP_INT_RC(ShiftRightS32, ShiftRightS32);
break;
case kPPC_ShiftRightAlg64:
ASSEMBLE_BINOP_INT_RC(ShiftRightS64, ShiftRightS64);
break;
case kPPC_RotRight32:
if (HasRegisterInput(instr, 1)) {
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ subfic(scratch, i.InputRegister(1), Operand(32));
__ rotlw(i.OutputRegister(), i.InputRegister(0), scratch,
i.OutputRCBit());
} else {
int sh = i.InputInt32(1);
__ rotrwi(i.OutputRegister(), i.InputRegister(0), sh, i.OutputRCBit());
}
break;
case kPPC_RotRight64:
if (HasRegisterInput(instr, 1)) {
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ subfic(scratch, i.InputRegister(1), Operand(64));
__ rotld(i.OutputRegister(), i.InputRegister(0), scratch,
i.OutputRCBit());
} else {
int sh = i.InputInt32(1);
__ rotrdi(i.OutputRegister(), i.InputRegister(0), sh, i.OutputRCBit());
}
break;
case kPPC_Not:
__ notx(i.OutputRegister(), i.InputRegister(0), i.OutputRCBit());
break;
case kPPC_RotLeftAndMask32:
__ rlwinm(i.OutputRegister(), i.InputRegister(0), i.InputInt32(1),
31 - i.InputInt32(2), 31 - i.InputInt32(3), i.OutputRCBit());
break;
case kPPC_RotLeftAndClear64:
__ rldic(i.OutputRegister(), i.InputRegister(0), i.InputInt32(1),
63 - i.InputInt32(2), i.OutputRCBit());
break;
case kPPC_RotLeftAndClearLeft64:
__ rldicl(i.OutputRegister(), i.InputRegister(0), i.InputInt32(1),
63 - i.InputInt32(2), i.OutputRCBit());
break;
case kPPC_RotLeftAndClearRight64:
__ rldicr(i.OutputRegister(), i.InputRegister(0), i.InputInt32(1),
63 - i.InputInt32(2), i.OutputRCBit());
break;
case kPPC_Add32:
case kPPC_Add64:
if (FlagsModeField::decode(instr->opcode()) != kFlags_none) {
if (HasRegisterInput(instr, 1)) {
__ add(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
SetOE);
} else {
__ mov(r0, i.InputImmediate(1));
__ add(i.OutputRegister(), i.InputRegister(0), r0, SetOE);
}
__ MoveToCrFromXer(cr0);
} else {
if (HasRegisterInput(instr, 1)) {
__ add(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
LeaveOE, i.OutputRCBit());
} else {
__ AddS64(i.OutputRegister(), i.InputRegister(0), i.InputImmediate(1),
r0, LeaveOE, i.OutputRCBit());
}
}
if (instr->arch_opcode() == kPPC_Add32) {
__ extsw(i.OutputRegister(), i.OutputRegister());
}
break;
case kPPC_AddDouble:
ASSEMBLE_FLOAT_BINOP_RC(fadd, MiscField::decode(instr->opcode()));
break;
case kPPC_Sub32:
case kPPC_Sub64:
if (FlagsModeField::decode(instr->opcode()) != kFlags_none) {
if (HasRegisterInput(instr, 1)) {
__ sub(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
SetOE);
} else {
__ mov(r0, i.InputImmediate(1));
__ sub(i.OutputRegister(), i.InputRegister(0), r0, SetOE);
}
__ MoveToCrFromXer(cr0);
} else {
if (HasRegisterInput(instr, 1)) {
__ sub(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
LeaveOE, i.OutputRCBit());
} else {
__ SubS64(i.OutputRegister(), i.InputRegister(0), i.InputImmediate(1),
r0, LeaveOE, i.OutputRCBit());
}
}
if (instr->arch_opcode() == kPPC_Sub32) {
__ extsw(i.OutputRegister(), i.OutputRegister());
}
break;
case kPPC_SubDouble:
ASSEMBLE_FLOAT_BINOP_RC(fsub, MiscField::decode(instr->opcode()));
break;
case kPPC_Mul32:
if (FlagsModeField::decode(instr->opcode()) != kFlags_none) {
__ mullw(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
SetOE);
__ MoveToCrFromXer(cr0);
} else {
__ mullw(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
LeaveOE, i.OutputRCBit());
}
__ extsw(i.OutputRegister(), i.OutputRegister());
break;
case kPPC_Mul64:
if (FlagsModeField::decode(instr->opcode()) != kFlags_none) {
__ mulld(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
SetOE);
__ MoveToCrFromXer(cr0);
} else {
__ mulld(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
LeaveOE, i.OutputRCBit());
}
break;
case kPPC_MulHighS64:
__ mulhd(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
i.OutputRCBit());
break;
case kPPC_MulHighU64:
__ mulhdu(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
i.OutputRCBit());
break;
case kPPC_MulHigh32:
__ mulhw(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
i.OutputRCBit());
// High 32 bits are undefined and need to be cleared.
CleanUInt32(i.OutputRegister());
break;
case kPPC_MulHighU32:
__ mulhwu(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),
i.OutputRCBit());
// High 32 bits are undefined and need to be cleared.
CleanUInt32(i.OutputRegister());
break;
case kPPC_MulDouble:
ASSEMBLE_FLOAT_BINOP_RC(fmul, MiscField::decode(instr->opcode()));
break;
case kPPC_Div32:
__ divw(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kPPC_Div64:
__ divd(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kPPC_DivU32:
__ divwu(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kPPC_DivU64:
__ divdu(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kPPC_DivDouble:
ASSEMBLE_FLOAT_BINOP_RC(fdiv, MiscField::decode(instr->opcode()));
break;
case kPPC_Mod32:
__ modsw(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
break;
case kPPC_Mod64:
__ modsd(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
break;
case kPPC_ModU32:
__ moduw(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
break;
case kPPC_ModU64:
__ modud(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1));
break;
case kPPC_ModDouble:
// TODO(bmeurer): We should really get rid of this special instruction,
// and generate a CallAddress instruction instead.
ASSEMBLE_FLOAT_MODULO();
break;
case kIeee754Float64Acos:
ASSEMBLE_IEEE754_UNOP(acos);
break;
case kIeee754Float64Acosh:
ASSEMBLE_IEEE754_UNOP(acosh);
break;
case kIeee754Float64Asin:
ASSEMBLE_IEEE754_UNOP(asin);
break;
case kIeee754Float64Asinh:
ASSEMBLE_IEEE754_UNOP(asinh);
break;
case kIeee754Float64Atan:
ASSEMBLE_IEEE754_UNOP(atan);
break;
case kIeee754Float64Atan2:
ASSEMBLE_IEEE754_BINOP(atan2);
break;
case kIeee754Float64Atanh:
ASSEMBLE_IEEE754_UNOP(atanh);
break;
case kIeee754Float64Tan:
ASSEMBLE_IEEE754_UNOP(tan);
break;
case kIeee754Float64Tanh:
ASSEMBLE_IEEE754_UNOP(tanh);
break;
case kIeee754Float64Cbrt:
ASSEMBLE_IEEE754_UNOP(cbrt);
break;
case kIeee754Float64Sin:
ASSEMBLE_IEEE754_UNOP(sin);
break;
case kIeee754Float64Sinh:
ASSEMBLE_IEEE754_UNOP(sinh);
break;
case kIeee754Float64Cos:
ASSEMBLE_IEEE754_UNOP(cos);
break;
case kIeee754Float64Cosh:
ASSEMBLE_IEEE754_UNOP(cosh);
break;
case kIeee754Float64Exp:
ASSEMBLE_IEEE754_UNOP(exp);
break;
case kIeee754Float64Expm1:
ASSEMBLE_IEEE754_UNOP(expm1);
break;
case kIeee754Float64Log:
ASSEMBLE_IEEE754_UNOP(log);
break;
case kIeee754Float64Log1p:
ASSEMBLE_IEEE754_UNOP(log1p);
break;
case kIeee754Float64Log2:
ASSEMBLE_IEEE754_UNOP(log2);
break;
case kIeee754Float64Log10:
ASSEMBLE_IEEE754_UNOP(log10);
break;
case kIeee754Float64Pow:
ASSEMBLE_IEEE754_BINOP(pow);
break;
case kPPC_Neg32:
case kPPC_Neg64:
if (FlagsModeField::decode(instr->opcode()) != kFlags_none) {
__ neg(i.OutputRegister(), i.InputRegister(0), SetOE);
__ MoveToCrFromXer(cr0);
} else {
__ neg(i.OutputRegister(), i.InputRegister(0), LeaveOE,
i.OutputRCBit());
}
if (instr->arch_opcode() == kPPC_Neg32) {
__ extsw(i.OutputRegister(), i.OutputRegister());
}
break;
case kPPC_MaxDouble:
__ MaxF64(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputDoubleRegister(1), kScratchDoubleReg);
break;
case kPPC_MinDouble:
__ MinF64(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputDoubleRegister(1), kScratchDoubleReg);
break;
case kPPC_AbsDouble:
ASSEMBLE_FLOAT_UNOP_RC(fabs, 0);
break;
case kPPC_SqrtDouble:
ASSEMBLE_FLOAT_UNOP_RC(fsqrt, MiscField::decode(instr->opcode()));
break;
case kPPC_FloorDouble:
ASSEMBLE_FLOAT_UNOP_RC(frim, MiscField::decode(instr->opcode()));
break;
case kPPC_CeilDouble:
ASSEMBLE_FLOAT_UNOP_RC(frip, MiscField::decode(instr->opcode()));
break;
case kPPC_TruncateDouble:
ASSEMBLE_FLOAT_UNOP_RC(friz, MiscField::decode(instr->opcode()));
break;
case kPPC_RoundDouble:
ASSEMBLE_FLOAT_UNOP_RC(frin, MiscField::decode(instr->opcode()));
break;
case kPPC_NegDouble:
ASSEMBLE_FLOAT_UNOP_RC(fneg, 0);
break;
case kPPC_Cntlz32:
__ cntlzw(i.OutputRegister(), i.InputRegister(0));
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kPPC_Cntlz64:
__ cntlzd(i.OutputRegister(), i.InputRegister(0));
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kPPC_Popcnt32:
__ Popcnt32(i.OutputRegister(), i.InputRegister(0));
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kPPC_Popcnt64:
__ Popcnt64(i.OutputRegister(), i.InputRegister(0));
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kPPC_Cmp32:
ASSEMBLE_COMPARE(cmpw, cmplw);
break;
case kPPC_Cmp64:
ASSEMBLE_COMPARE(cmp, cmpl);
break;
case kPPC_CmpDouble:
ASSEMBLE_FLOAT_COMPARE(fcmpu);
break;
case kPPC_Tst32:
if (HasRegisterInput(instr, 1)) {
__ and_(r0, i.InputRegister(0), i.InputRegister(1), i.OutputRCBit());
} else {
__ andi(r0, i.InputRegister(0), i.InputImmediate(1));
}
__ extsw(r0, r0, i.OutputRCBit());
DCHECK_EQ(SetRC, i.OutputRCBit());
break;
case kPPC_Tst64:
if (HasRegisterInput(instr, 1)) {
__ and_(r0, i.InputRegister(0), i.InputRegister(1), i.OutputRCBit());
} else {
__ andi(r0, i.InputRegister(0), i.InputImmediate(1));
}
DCHECK_EQ(SetRC, i.OutputRCBit());
break;
case kPPC_Float64SilenceNaN: {
DoubleRegister value = i.InputDoubleRegister(0);
DoubleRegister result = i.OutputDoubleRegister();
__ CanonicalizeNaN(result, value);
break;
}
case kPPC_Push: {
int stack_decrement = i.InputInt32(0);
int slots = stack_decrement / kSystemPointerSize;
LocationOperand* op = LocationOperand::cast(instr->InputAt(1));
MachineRepresentation rep = op->representation();
int pushed_slots = ElementSizeInPointers(rep);
// Slot-sized arguments are never padded but there may be a gap if
// the slot allocator reclaimed other padding slots. Adjust the stack
// here to skip any gap.
__ AllocateStackSpace((slots - pushed_slots) * kSystemPointerSize);
switch (rep) {
case MachineRepresentation::kFloat32:
__ StoreF32WithUpdate(i.InputDoubleRegister(1),
MemOperand(sp, -kSystemPointerSize), r0);
break;
case MachineRepresentation::kFloat64:
__ StoreF64WithUpdate(i.InputDoubleRegister(1),
MemOperand(sp, -kDoubleSize), r0);
break;
case MachineRepresentation::kSimd128: {
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ addi(sp, sp, Operand(-kSimd128Size));
__ StoreSimd128(i.InputSimd128Register(1), MemOperand(r0, sp),
scratch);
break;
}
default:
__ StoreU64WithUpdate(i.InputRegister(1),
MemOperand(sp, -kSystemPointerSize), r0);
break;
}
frame_access_state()->IncreaseSPDelta(slots);
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
}
case kPPC_PushFrame: {
int num_slots = i.InputInt32(1);
if (instr->InputAt(0)->IsFPRegister()) {
LocationOperand* op = LocationOperand::cast(instr->InputAt(0));
if (op->representation() == MachineRepresentation::kFloat64) {
__ StoreF64WithUpdate(i.InputDoubleRegister(0),
MemOperand(sp, -num_slots * kSystemPointerSize),
r0);
} else {
DCHECK_EQ(MachineRepresentation::kFloat32, op->representation());
__ StoreF32WithUpdate(i.InputDoubleRegister(0),
MemOperand(sp, -num_slots * kSystemPointerSize),
r0);
}
} else {
__ StoreU64WithUpdate(i.InputRegister(0),
MemOperand(sp, -num_slots * kSystemPointerSize),
r0);
}
break;
}
case kPPC_StoreToStackSlot: {
int slot = i.InputInt32(1);
if (instr->InputAt(0)->IsFPRegister()) {
LocationOperand* op = LocationOperand::cast(instr->InputAt(0));
if (op->representation() == MachineRepresentation::kFloat64) {
__ StoreF64(i.InputDoubleRegister(0),
MemOperand(sp, slot * kSystemPointerSize), r0);
} else if (op->representation() == MachineRepresentation::kFloat32) {
__ StoreF32(i.InputDoubleRegister(0),
MemOperand(sp, slot * kSystemPointerSize), r0);
} else {
DCHECK_EQ(MachineRepresentation::kSimd128, op->representation());
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ StoreSimd128(i.InputSimd128Register(0),
MemOperand(sp, slot * kSystemPointerSize), scratch);
}
} else {
__ StoreU64(i.InputRegister(0),
MemOperand(sp, slot * kSystemPointerSize), r0);
}
break;
}
case kPPC_ExtendSignWord8:
__ extsb(i.OutputRegister(), i.InputRegister(0));
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kPPC_ExtendSignWord16:
__ extsh(i.OutputRegister(), i.InputRegister(0));
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kPPC_ExtendSignWord32:
__ extsw(i.OutputRegister(), i.InputRegister(0));
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kPPC_Uint32ToUint64:
// Zero extend
__ clrldi(i.OutputRegister(), i.InputRegister(0), Operand(32));
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kPPC_Int64ToInt32:
__ extsw(i.OutputRegister(), i.InputRegister(0));
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kPPC_Int64ToFloat32:
__ ConvertInt64ToFloat(i.InputRegister(0), i.OutputDoubleRegister());
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kPPC_Int64ToDouble:
__ ConvertInt64ToDouble(i.InputRegister(0), i.OutputDoubleRegister());
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kPPC_Uint64ToFloat32:
__ ConvertUnsignedInt64ToFloat(i.InputRegister(0),
i.OutputDoubleRegister());
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kPPC_Uint64ToDouble:
__ ConvertUnsignedInt64ToDouble(i.InputRegister(0),
i.OutputDoubleRegister());
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kPPC_Int32ToFloat32:
__ ConvertIntToFloat(i.InputRegister(0), i.OutputDoubleRegister());
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kPPC_Int32ToDouble:
__ ConvertIntToDouble(i.InputRegister(0), i.OutputDoubleRegister());
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kPPC_Uint32ToFloat32:
__ ConvertUnsignedIntToFloat(i.InputRegister(0),
i.OutputDoubleRegister());
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kPPC_Uint32ToDouble:
__ ConvertUnsignedIntToDouble(i.InputRegister(0),
i.OutputDoubleRegister());
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kPPC_Float32ToInt32: {
bool set_overflow_to_min_i32 = MiscField::decode(instr->opcode());
if (set_overflow_to_min_i32) {
__ mtfsb0(VXCVI); // clear FPSCR:VXCVI bit
}
__ fctiwz(kScratchDoubleReg, i.InputDoubleRegister(0));
__ MovDoubleLowToInt(i.OutputRegister(), kScratchDoubleReg);
if (set_overflow_to_min_i32) {
// Avoid INT32_MAX as an overflow indicator and use INT32_MIN instead,
// because INT32_MIN allows easier out-of-bounds detection.
CRegister cr = cr0;
int crbit = v8::internal::Assembler::encode_crbit(
cr, static_cast<CRBit>(VXCVI % CRWIDTH));
__ mcrfs(cr, VXCVI); // extract FPSCR field containing VXCVI into cr0
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ li(scratch, Operand(1));
__ ShiftLeftU64(scratch, scratch,
Operand(31)); // generate INT32_MIN.
__ isel(i.OutputRegister(0), scratch, i.OutputRegister(0), crbit);
}
break;
}
case kPPC_Float32ToUint32: {
bool set_overflow_to_min_u32 = MiscField::decode(instr->opcode());
if (set_overflow_to_min_u32) {
__ mtfsb0(VXCVI); // clear FPSCR:VXCVI bit
}
__ fctiwuz(kScratchDoubleReg, i.InputDoubleRegister(0));
__ MovDoubleLowToInt(i.OutputRegister(), kScratchDoubleReg);
if (set_overflow_to_min_u32) {
// Avoid UINT32_MAX as an overflow indicator and use 0 instead,
// because 0 allows easier out-of-bounds detection.
CRegister cr = cr0;
int crbit = v8::internal::Assembler::encode_crbit(
cr, static_cast<CRBit>(VXCVI % CRWIDTH));
__ mcrfs(cr, VXCVI); // extract FPSCR field containing VXCVI into cr0
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ li(scratch, Operand::Zero());
__ isel(i.OutputRegister(0), scratch, i.OutputRegister(0), crbit);
}
break;
}
#define DOUBLE_TO_INT32(op) \
bool check_conversion = i.OutputCount() > 1; \
CRegister cr = cr0; \
FPSCRBit fps_bit = VXCVI; \
int cr_bit = v8::internal::Assembler::encode_crbit( \
cr, static_cast<CRBit>(fps_bit % CRWIDTH)); \
__ mtfsb0(fps_bit); /* clear FPSCR:VXCVI bit */ \
__ op(kScratchDoubleReg, i.InputDoubleRegister(0)); \
__ MovDoubleLowToInt(i.OutputRegister(0), kScratchDoubleReg); \
__ mcrfs(cr, VXCVI); /* extract FPSCR field containing VXCVI into cr0 */ \
if (check_conversion) { \
__ li(i.OutputRegister(1), Operand(1)); \
__ isel(i.OutputRegister(1), r0, i.OutputRegister(1), cr_bit); \
} else { \
__ isel(i.OutputRegister(0), r0, i.OutputRegister(0), cr_bit); \
}
case kPPC_DoubleToInt32: {
DOUBLE_TO_INT32(fctiwz)
break;
}
case kPPC_DoubleToUint32: {
DOUBLE_TO_INT32(fctiwuz)
break;
}
#undef DOUBLE_TO_INT32
case kPPC_DoubleToInt64: {
bool check_conversion = i.OutputCount() > 1;
__ mtfsb0(VXCVI); // clear FPSCR:VXCVI bit
__ ConvertDoubleToInt64(i.InputDoubleRegister(0),
i.OutputRegister(0), kScratchDoubleReg);
CRegister cr = cr0;
int crbit = v8::internal::Assembler::encode_crbit(
cr, static_cast<CRBit>(VXCVI % CRWIDTH));
__ mcrfs(cr, VXCVI); // extract FPSCR field containing VXCVI into cr0
// Handle conversion failures (such as overflow).
if (check_conversion) {
__ li(i.OutputRegister(1), Operand(1));
__ isel(i.OutputRegister(1), r0, i.OutputRegister(1), crbit);
} else {
__ isel(i.OutputRegister(0), r0, i.OutputRegister(0), crbit);
}
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
}
case kPPC_DoubleToUint64: {
__ mtfsb0(VXCVI); // clear FPSCR:VXCVI bit
__ ConvertDoubleToUnsignedInt64(i.InputDoubleRegister(0),
i.OutputRegister(0), kScratchDoubleReg);
CRegister cr = cr0;
int crbit = v8::internal::Assembler::encode_crbit(
cr, static_cast<CRBit>(VXCVI % CRWIDTH));
__ mcrfs(cr, VXCVI); // extract FPSCR field containing VXCVI into cr0
// Handle conversion failures (such as overflow).
if (i.OutputCount() > 1) {
__ li(i.OutputRegister(1), Operand(1));
__ isel(i.OutputRegister(1), r0, i.OutputRegister(1), crbit);
} else {
__ isel(i.OutputRegister(0), r0, i.OutputRegister(0), crbit);
}
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
}
case kPPC_DoubleToFloat32:
ASSEMBLE_FLOAT_UNOP_RC(frsp, 0);
break;
case kPPC_Float32ToDouble:
// Nothing to do.
__ Move(i.OutputDoubleRegister(), i.InputDoubleRegister(0));
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kPPC_DoubleExtractLowWord32:
__ MovDoubleLowToInt(i.OutputRegister(), i.InputDoubleRegister(0));
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kPPC_DoubleExtractHighWord32:
__ MovDoubleHighToInt(i.OutputRegister(), i.InputDoubleRegister(0));
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kPPC_DoubleFromWord32Pair: {
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ clrldi(scratch, i.InputRegister(1), Operand(32));
__ ShiftLeftU64(i.TempRegister(0), i.InputRegister(0), Operand(32));
__ OrU64(i.TempRegister(0), i.TempRegister(0), scratch);
__ MovInt64ToDouble(i.OutputDoubleRegister(), i.TempRegister(0));
break;
}
case kPPC_DoubleInsertLowWord32:
__ InsertDoubleLow(i.OutputDoubleRegister(), i.InputRegister(1), r0);
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kPPC_DoubleInsertHighWord32:
__ InsertDoubleHigh(i.OutputDoubleRegister(), i.InputRegister(1), r0);
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kPPC_DoubleConstruct:
__ MovInt64ComponentsToDouble(i.OutputDoubleRegister(),
i.InputRegister(0), i.InputRegister(1), r0);
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
case kPPC_BitcastFloat32ToInt32:
__ MovFloatToInt(i.OutputRegister(), i.InputDoubleRegister(0),
kScratchDoubleReg);
break;
case kPPC_BitcastInt32ToFloat32:
__ MovIntToFloat(i.OutputDoubleRegister(), i.InputRegister(0), ip);
break;
case kPPC_BitcastDoubleToInt64:
__ MovDoubleToInt64(i.OutputRegister(), i.InputDoubleRegister(0));
break;
case kPPC_BitcastInt64ToDouble:
__ MovInt64ToDouble(i.OutputDoubleRegister(), i.InputRegister(0));
break;
case kPPC_LoadWordU8:
ASSEMBLE_LOAD_INTEGER(lbz, plbz, lbzx, false);
break;
case kPPC_LoadWordS8:
ASSEMBLE_LOAD_INTEGER(lbz, plbz, lbzx, false);
__ extsb(i.OutputRegister(), i.OutputRegister());
break;
case kPPC_LoadWordU16:
ASSEMBLE_LOAD_INTEGER(lhz, plhz, lhzx, false);
break;
case kPPC_LoadWordS16:
ASSEMBLE_LOAD_INTEGER(lha, plha, lhax, false);
break;
case kPPC_LoadWordU32:
ASSEMBLE_LOAD_INTEGER(lwz, plwz, lwzx, false);
break;
case kPPC_LoadWordS32:
ASSEMBLE_LOAD_INTEGER(lwa, plwa, lwax, true);
break;
case kPPC_LoadWord64:
ASSEMBLE_LOAD_INTEGER(ld, pld, ldx, true);
break;
case kPPC_LoadFloat32:
ASSEMBLE_LOAD_FLOAT(lfs, plfs, lfsx);
break;
case kPPC_LoadDouble:
ASSEMBLE_LOAD_FLOAT(lfd, plfd, lfdx);
break;
case kPPC_LoadSimd128: {
Simd128Register result = i.OutputSimd128Register();
AddressingMode mode = kMode_None;
MemOperand operand = i.MemoryOperand(&mode);
bool is_atomic = i.InputInt32(2);
DCHECK_EQ(mode, kMode_MRR);
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ LoadSimd128(result, operand, scratch);
if (is_atomic) __ lwsync();
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
}
case kPPC_LoadReverseSimd128RR: {
__ xxbrq(i.OutputSimd128Register(), i.InputSimd128Register(0));
break;
}
case kPPC_StoreWord8:
ASSEMBLE_STORE_INTEGER(stb, pstb, stbx, false);
break;
case kPPC_StoreWord16:
ASSEMBLE_STORE_INTEGER(sth, psth, sthx, false);
break;
case kPPC_StoreWord32:
ASSEMBLE_STORE_INTEGER(stw, pstw, stwx, false);
break;
case kPPC_StoreWord64:
ASSEMBLE_STORE_INTEGER(std, pstd, stdx, true);
break;
case kPPC_StoreFloat32:
ASSEMBLE_STORE_FLOAT(stfs, pstfs, stfsx);
break;
case kPPC_StoreDouble:
ASSEMBLE_STORE_FLOAT(stfd, pstfd, stfdx);
break;
case kPPC_StoreSimd128: {
size_t index = 0;
AddressingMode mode = kMode_None;
MemOperand operand = i.MemoryOperand(&mode, &index);
Simd128Register value = i.InputSimd128Register(index);
bool is_atomic = i.InputInt32(3);
if (is_atomic) __ lwsync();
DCHECK_EQ(mode, kMode_MRR);
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ StoreSimd128(value, operand, scratch);
if (is_atomic) __ sync();
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
}
case kAtomicLoadInt8:
case kAtomicLoadInt16:
UNREACHABLE();
case kAtomicExchangeInt8:
__ AtomicExchange<int8_t>(
MemOperand(i.InputRegister(0), i.InputRegister(1)),
i.InputRegister(2), i.OutputRegister());
break;
case kPPC_AtomicExchangeUint8:
__ AtomicExchange<uint8_t>(
MemOperand(i.InputRegister(0), i.InputRegister(1)),
i.InputRegister(2), i.OutputRegister());
break;
case kAtomicExchangeInt16: {
ASSEMBLE_ATOMIC_EXCHANGE(int16_t, ByteReverseU16);
__ extsh(i.OutputRegister(), i.OutputRegister());
break;
}
case kPPC_AtomicExchangeUint16: {
ASSEMBLE_ATOMIC_EXCHANGE(uint16_t, ByteReverseU16);
break;
}
case kPPC_AtomicExchangeWord32: {
ASSEMBLE_ATOMIC_EXCHANGE(uint32_t, ByteReverseU32);
break;
}
case kPPC_AtomicExchangeWord64: {
ASSEMBLE_ATOMIC_EXCHANGE(uint64_t, ByteReverseU64);
break;
}
case kAtomicCompareExchangeInt8: {
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ AtomicCompareExchange<int8_t>(
MemOperand(i.InputRegister(0), i.InputRegister(1)),
i.InputRegister(2), i.InputRegister(3), i.OutputRegister(), scratch);
break;
}
case kPPC_AtomicCompareExchangeUint8: {
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ AtomicCompareExchange<uint8_t>(
MemOperand(i.InputRegister(0), i.InputRegister(1)),
i.InputRegister(2), i.InputRegister(3), i.OutputRegister(), scratch);
break;
}
case kAtomicCompareExchangeInt16: {
ASSEMBLE_ATOMIC_COMPARE_EXCHANGE(int16_t, ByteReverseU16);
__ extsh(i.OutputRegister(), i.OutputRegister());
break;
}
case kPPC_AtomicCompareExchangeUint16: {
ASSEMBLE_ATOMIC_COMPARE_EXCHANGE(uint16_t, ByteReverseU16);
break;
}
case kPPC_AtomicCompareExchangeWord32: {
ASSEMBLE_ATOMIC_COMPARE_EXCHANGE(uint32_t, ByteReverseU32);
break;
}
case kPPC_AtomicCompareExchangeWord64: {
ASSEMBLE_ATOMIC_COMPARE_EXCHANGE(uint64_t, ByteReverseU64);
break;
}
case kAtomicExchangeWithWriteBarrier: {
if constexpr (COMPRESS_POINTERS_BOOL) {
ASSEMBLE_ATOMIC_EXCHANGE(uint32_t, ByteReverseU32);
__ AddS64(i.OutputRegister(), i.OutputRegister(),
kPtrComprCageBaseRegister);
} else {
ASSEMBLE_ATOMIC_EXCHANGE(uint64_t, ByteReverseU64);
}
if (v8_flags.disable_write_barriers) break;
// Emit the write barrier.
Register object = i.InputRegister(0);
Register offset = i.InputRegister(1);
Register value = i.InputRegister(2);
Register scratch0 = i.TempRegister(0);
Register scratch1 = i.TempRegister(1);
auto ool = zone()->New<OutOfLineRecordWrite>(
this, object, offset, value, scratch0, scratch1,
RecordWriteMode::kValueIsAny, DetermineStubCallMode(),
&unwinding_info_writer_);
__ JumpIfSmi(value, ool->exit());
__ CheckPageFlag(object, scratch0,
MemoryChunk::kPointersFromHereAreInterestingMask, ne,
ool->entry());
__ bind(ool->exit());
break;
}
case kAtomicCompareExchangeWithWriteBarrier: {
if constexpr (COMPRESS_POINTERS_BOOL) {
ASSEMBLE_ATOMIC_COMPARE_EXCHANGE(uint32_t, ByteReverseU32);
__ AddS64(i.OutputRegister(), i.OutputRegister(),
kPtrComprCageBaseRegister);
} else {
ASSEMBLE_ATOMIC_COMPARE_EXCHANGE(uint64_t, ByteReverseU64);
}
if (v8_flags.disable_write_barriers) break;
// Emit the write barrier.
Register object = i.InputRegister(0);
Register offset = i.InputRegister(1);
Register new_value = i.InputRegister(3);
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
auto ool = zone()->New<OutOfLineRecordWrite>(
this, object, offset, new_value, ip, scratch,
RecordWriteMode::kValueIsAny, DetermineStubCallMode(),
&unwinding_info_writer_);
__ bne(ool->exit());
__ JumpIfSmi(new_value, ool->exit());
__ CheckPageFlag(object, scratch,
MemoryChunk::kPointersFromHereAreInterestingMask, ne,
ool->entry());
__ bind(ool->exit());
break;
}
#define ATOMIC_BINOP_CASE(op, inst) \
case kPPC_Atomic##op##Int8: \
ASSEMBLE_ATOMIC_BINOP_BYTE(inst, int8_t); \
__ extsb(i.OutputRegister(), i.OutputRegister()); \
break; \
case kPPC_Atomic##op##Uint8: \
ASSEMBLE_ATOMIC_BINOP_BYTE(inst, uint8_t); \
break; \
case kPPC_Atomic##op##Int16: \
ASSEMBLE_ATOMIC_BINOP(inst, int16_t, ByteReverseU16, r0); \
__ extsh(i.OutputRegister(), i.OutputRegister()); \
break; \
case kPPC_Atomic##op##Uint16: \
ASSEMBLE_ATOMIC_BINOP(inst, uint16_t, ByteReverseU16, r0); \
break; \
case kPPC_Atomic##op##Int32: \
ASSEMBLE_ATOMIC_BINOP(inst, int32_t, ByteReverseU32, r0); \
__ extsw(i.OutputRegister(), i.OutputRegister()); \
break; \
case kPPC_Atomic##op##Uint32: \
ASSEMBLE_ATOMIC_BINOP(inst, uint32_t, ByteReverseU32, r0); \
break; \
case kPPC_Atomic##op##Int64: \
case kPPC_Atomic##op##Uint64: \
ASSEMBLE_ATOMIC_BINOP(inst, uint64_t, ByteReverseU64, r0); \
break;
ATOMIC_BINOP_CASE(Add, add)
ATOMIC_BINOP_CASE(Sub, sub)
ATOMIC_BINOP_CASE(And, and_)
ATOMIC_BINOP_CASE(Or, orx)
ATOMIC_BINOP_CASE(Xor, xor_)
#undef ATOMIC_BINOP_CASE
case kPPC_ByteRev32: {
Register input = i.InputRegister(0);
Register output = i.OutputRegister();
Register temp1 = r0;
if (CpuFeatures::IsSupported(PPC_10_PLUS)) {
__ brw(output, input);
__ extsw(output, output);
break;
}
__ rotlwi(temp1, input, 8);
__ rlwimi(temp1, input, 24, 0, 7);
__ rlwimi(temp1, input, 24, 16, 23);
__ extsw(output, temp1);
break;
}
case kPPC_LoadByteRev32: {
ASSEMBLE_LOAD_INTEGER_RR(lwbrx);
break;
}
case kPPC_StoreByteRev32: {
ASSEMBLE_STORE_INTEGER_RR(stwbrx);
break;
}
case kPPC_ByteRev64: {
Register input = i.InputRegister(0);
Register output = i.OutputRegister();
Register temp1 = r0;
UseScratchRegisterScope temps(masm());
Register temp2 = temps.Acquire();
Register temp3 = i.TempRegister(0);
if (CpuFeatures::IsSupported(PPC_10_PLUS)) {
__ brd(output, input);
break;
}
__ rldicl(temp1, input, 32, 32);
__ rotlwi(temp2, input, 8);
__ rlwimi(temp2, input, 24, 0, 7);
__ rotlwi(temp3, temp1, 8);
__ rlwimi(temp2, input, 24, 16, 23);
__ rlwimi(temp3, temp1, 24, 0, 7);
__ rlwimi(temp3, temp1, 24, 16, 23);
__ rldicr(temp2, temp2, 32, 31);
__ orx(output, temp2, temp3);
break;
}
case kPPC_LoadByteRev64: {
ASSEMBLE_LOAD_INTEGER_RR(ldbrx);
break;
}
case kPPC_StoreByteRev64: {
ASSEMBLE_STORE_INTEGER_RR(stdbrx);
break;
}
// Simd Support.
#define SIMD_BINOP_LIST(V) \
V(F64x2Add) \
V(F64x2Sub) \
V(F64x2Mul) \
V(F64x2Div) \
V(F64x2Eq) \
V(F64x2Lt) \
V(F64x2Le) \
V(F32x4Add) \
V(F32x4Sub) \
V(F32x4Mul) \
V(F32x4Div) \
V(F32x4Min) \
V(F32x4Max) \
V(F32x4Eq) \
V(F32x4Lt) \
V(F32x4Le) \
V(I64x2Add) \
V(I64x2Sub) \
V(I64x2Eq) \
V(I64x2GtS) \
V(I32x4Add) \
V(I32x4Sub) \
V(I32x4Mul) \
V(I32x4MinS) \
V(I32x4MinU) \
V(I32x4MaxS) \
V(I32x4MaxU) \
V(I32x4Eq) \
V(I32x4GtS) \
V(I32x4GtU) \
V(I32x4DotI16x8S) \
V(I16x8Add) \
V(I16x8Sub) \
V(I16x8Mul) \
V(I16x8MinS) \
V(I16x8MinU) \
V(I16x8MaxS) \
V(I16x8MaxU) \
V(I16x8Eq) \
V(I16x8GtS) \
V(I16x8GtU) \
V(I16x8AddSatS) \
V(I16x8SubSatS) \
V(I16x8AddSatU) \
V(I16x8SubSatU) \
V(I16x8SConvertI32x4) \
V(I16x8UConvertI32x4) \
V(I16x8RoundingAverageU) \
V(I16x8Q15MulRSatS) \
V(I8x16Add) \
V(I8x16Sub) \
V(I8x16MinS) \
V(I8x16MinU) \
V(I8x16MaxS) \
V(I8x16MaxU) \
V(I8x16Eq) \
V(I8x16GtS) \
V(I8x16GtU) \
V(I8x16AddSatS) \
V(I8x16SubSatS) \
V(I8x16AddSatU) \
V(I8x16SubSatU) \
V(I8x16SConvertI16x8) \
V(I8x16UConvertI16x8) \
V(I8x16RoundingAverageU) \
V(S128And) \
V(S128Or) \
V(S128Xor) \
V(S128AndNot)
#define EMIT_SIMD_BINOP(name) \
case kPPC_##name: { \
__ name(i.OutputSimd128Register(), i.InputSimd128Register(0), \
i.InputSimd128Register(1)); \
break; \
}
SIMD_BINOP_LIST(EMIT_SIMD_BINOP)
#undef EMIT_SIMD_BINOP
#undef SIMD_BINOP_LIST
#define SIMD_BINOP_WITH_SCRATCH_LIST(V) \
V(F64x2Ne) \
V(F64x2Pmin) \
V(F64x2Pmax) \
V(F32x4Ne) \
V(F32x4Pmin) \
V(F32x4Pmax) \
V(I64x2Ne) \
V(I64x2GeS) \
V(I64x2ExtMulLowI32x4S) \
V(I64x2ExtMulHighI32x4S) \
V(I64x2ExtMulLowI32x4U) \
V(I64x2ExtMulHighI32x4U) \
V(I32x4Ne) \
V(I32x4GeS) \
V(I32x4GeU) \
V(I32x4ExtMulLowI16x8S) \
V(I32x4ExtMulHighI16x8S) \
V(I32x4ExtMulLowI16x8U) \
V(I32x4ExtMulHighI16x8U) \
V(I16x8Ne) \
V(I16x8GeS) \
V(I16x8GeU) \
V(I16x8ExtMulLowI8x16S) \
V(I16x8ExtMulHighI8x16S) \
V(I16x8ExtMulLowI8x16U) \
V(I16x8ExtMulHighI8x16U) \
V(I16x8DotI8x16S) \
V(I8x16Ne) \
V(I8x16GeS) \
V(I8x16GeU) \
V(I8x16Swizzle)
#define EMIT_SIMD_BINOP_WITH_SCRATCH(name) \
case kPPC_##name: { \
__ name(i.OutputSimd128Register(), i.InputSimd128Register(0), \
i.InputSimd128Register(1), kScratchSimd128Reg); \
break; \
}
SIMD_BINOP_WITH_SCRATCH_LIST(EMIT_SIMD_BINOP_WITH_SCRATCH)
#undef EMIT_SIMD_BINOP_WITH_SCRATCH
#undef SIMD_BINOP_WITH_SCRATCH_LIST
#define SIMD_SHIFT_LIST(V) \
V(I64x2Shl) \
V(I64x2ShrS) \
V(I64x2ShrU) \
V(I32x4Shl) \
V(I32x4ShrS) \
V(I32x4ShrU) \
V(I16x8Shl) \
V(I16x8ShrS) \
V(I16x8ShrU) \
V(I8x16Shl) \
V(I8x16ShrS) \
V(I8x16ShrU)
#define EMIT_SIMD_SHIFT(name) \
case kPPC_##name: { \
__ name(i.OutputSimd128Register(), i.InputSimd128Register(0), \
i.InputRegister(1), kScratchSimd128Reg); \
break; \
}
SIMD_SHIFT_LIST(EMIT_SIMD_SHIFT)
#undef EMIT_SIMD_SHIFT
#undef SIMD_SHIFT_LIST
#define SIMD_UNOP_LIST(V) \
V(F64x2Abs) \
V(F64x2Neg) \
V(F64x2Sqrt) \
V(F64x2Ceil) \
V(F64x2Floor) \
V(F64x2Trunc) \
V(F64x2PromoteLowF32x4) \
V(F32x4Abs) \
V(F32x4Neg) \
V(F32x4SConvertI32x4) \
V(F32x4UConvertI32x4) \
V(I64x2Neg) \
V(I32x4Neg) \
V(F32x4Sqrt) \
V(F32x4Ceil) \
V(F32x4Floor) \
V(F32x4Trunc) \
V(F64x2ConvertLowI32x4S) \
V(I64x2SConvertI32x4Low) \
V(I64x2SConvertI32x4High) \
V(I32x4SConvertI16x8Low) \
V(I32x4SConvertI16x8High) \
V(I32x4UConvertF32x4) \
V(I16x8SConvertI8x16Low) \
V(I16x8SConvertI8x16High) \
V(I8x16Popcnt) \
V(S128Not)
#define EMIT_SIMD_UNOP(name) \
case kPPC_##name: { \
__ name(i.OutputSimd128Register(), i.InputSimd128Register(0)); \
break; \
}
SIMD_UNOP_LIST(EMIT_SIMD_UNOP)
#undef EMIT_SIMD_UNOP
#undef SIMD_UNOP_LIST
#define SIMD_UNOP_WITH_SCRATCH_LIST(V) \
V(F32x4DemoteF64x2Zero) \
V(I64x2Abs) \
V(I32x4Abs) \
V(I32x4SConvertF32x4) \
V(I32x4TruncSatF64x2SZero) \
V(I32x4TruncSatF64x2UZero) \
V(I16x8Abs) \
V(I16x8Neg) \
V(I8x16Abs) \
V(I8x16Neg)
#define EMIT_SIMD_UNOP_WITH_SCRATCH(name) \
case kPPC_##name: { \
__ name(i.OutputSimd128Register(), i.InputSimd128Register(0), \
kScratchSimd128Reg); \
break; \
}
SIMD_UNOP_WITH_SCRATCH_LIST(EMIT_SIMD_UNOP_WITH_SCRATCH)
#undef EMIT_SIMD_UNOP_WITH_SCRATCH
#undef SIMD_UNOP_WITH_SCRATCH_LIST
#define SIMD_ALL_TRUE_LIST(V) \
V(I64x2AllTrue) \
V(I32x4AllTrue) \
V(I16x8AllTrue) \
V(I8x16AllTrue)
#define EMIT_SIMD_ALL_TRUE(name) \
case kPPC_##name: { \
__ name(i.OutputRegister(), i.InputSimd128Register(0), r0, ip, \
kScratchSimd128Reg); \
break; \
}
SIMD_ALL_TRUE_LIST(EMIT_SIMD_ALL_TRUE)
#undef EMIT_SIMD_ALL_TRUE
#undef SIMD_ALL_TRUE_LIST
#define SIMD_QFM_LIST(V) \
V(F64x2Qfma) \
V(F64x2Qfms) \
V(F32x4Qfma) \
V(F32x4Qfms)
#define EMIT_SIMD_QFM(name) \
case kPPC_##name: { \
__ name(i.OutputSimd128Register(), i.InputSimd128Register(0), \
i.InputSimd128Register(1), i.InputSimd128Register(2), \
kScratchSimd128Reg); \
break; \
}
SIMD_QFM_LIST(EMIT_SIMD_QFM)
#undef EMIT_SIMD_QFM
#undef SIMD_QFM_LIST
#define SIMD_EXT_ADD_PAIRWISE_LIST(V) \
V(I32x4ExtAddPairwiseI16x8S) \
V(I32x4ExtAddPairwiseI16x8U) \
V(I16x8ExtAddPairwiseI8x16S) \
V(I16x8ExtAddPairwiseI8x16U)
#define EMIT_SIMD_EXT_ADD_PAIRWISE(name) \
case kPPC_##name: { \
__ name(i.OutputSimd128Register(), i.InputSimd128Register(0), \
kScratchSimd128Reg, kScratchSimd128Reg2); \
break; \
}
SIMD_EXT_ADD_PAIRWISE_LIST(EMIT_SIMD_EXT_ADD_PAIRWISE)
#undef EMIT_SIMD_EXT_ADD_PAIRWISE
#undef SIMD_EXT_ADD_PAIRWISE_LIST
#define SIMD_LOAD_LANE_LIST(V) \
V(S128Load64Lane, LoadLane64LE) \
V(S128Load32Lane, LoadLane32LE) \
V(S128Load16Lane, LoadLane16LE) \
V(S128Load8Lane, LoadLane8LE)
#define EMIT_SIMD_LOAD_LANE(name, op) \
case kPPC_##name: { \
Simd128Register dst = i.OutputSimd128Register(); \
DCHECK_EQ(dst, i.InputSimd128Register(0)); \
AddressingMode mode = kMode_None; \
size_t index = 1; \
MemOperand operand = i.MemoryOperand(&mode, &index); \
DCHECK_EQ(mode, kMode_MRR); \
UseScratchRegisterScope temps(masm()); \
Register scratch = temps.Acquire(); \
__ op(dst, operand, i.InputUint8(3), scratch, kScratchSimd128Reg); \
break; \
}
SIMD_LOAD_LANE_LIST(EMIT_SIMD_LOAD_LANE)
#undef EMIT_SIMD_LOAD_LANE
#undef SIMD_LOAD_LANE_LIST
#define SIMD_STORE_LANE_LIST(V) \
V(S128Store64Lane, StoreLane64LE) \
V(S128Store32Lane, StoreLane32LE) \
V(S128Store16Lane, StoreLane16LE) \
V(S128Store8Lane, StoreLane8LE)
#define EMIT_SIMD_STORE_LANE(name, op) \
case kPPC_##name: { \
AddressingMode mode = kMode_None; \
size_t index = 1; \
MemOperand operand = i.MemoryOperand(&mode, &index); \
DCHECK_EQ(mode, kMode_MRR); \
UseScratchRegisterScope temps(masm()); \
Register scratch = temps.Acquire(); \
__ op(i.InputSimd128Register(0), operand, i.InputUint8(3), scratch, \
kScratchSimd128Reg); \
break; \
}
SIMD_STORE_LANE_LIST(EMIT_SIMD_STORE_LANE)
#undef EMIT_SIMD_STORE_LANE
#undef SIMD_STORE_LANE_LIST
#define SIMD_LOAD_SPLAT(V) \
V(S128Load64Splat, LoadAndSplat64x2LE) \
V(S128Load32Splat, LoadAndSplat32x4LE) \
V(S128Load16Splat, LoadAndSplat16x8LE) \
V(S128Load8Splat, LoadAndSplat8x16LE)
#define EMIT_SIMD_LOAD_SPLAT(name, op) \
case kPPC_##name: { \
AddressingMode mode = kMode_None; \
MemOperand operand = i.MemoryOperand(&mode); \
DCHECK_EQ(mode, kMode_MRR); \
UseScratchRegisterScope temps(masm()); \
Register scratch = temps.Acquire(); \
__ op(i.OutputSimd128Register(), operand, scratch); \
break; \
}
SIMD_LOAD_SPLAT(EMIT_SIMD_LOAD_SPLAT)
#undef EMIT_SIMD_LOAD_SPLAT
#undef SIMD_LOAD_SPLAT
case kPPC_FSplat: {
int lane_size = LaneSizeBits(LaneSizeField::decode(instr->opcode()));
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
switch (lane_size) {
case 32: {
__ F32x4Splat(i.OutputSimd128Register(), i.InputDoubleRegister(0),
kScratchDoubleReg, scratch);
break;
}
case 64: {
__ F64x2Splat(i.OutputSimd128Register(), i.InputDoubleRegister(0),
scratch);
break;
}
default:
UNREACHABLE();
}
break;
}
case kPPC_ISplat: {
int lane_size = LaneSizeBits(LaneSizeField::decode(instr->opcode()));
switch (lane_size) {
case 8: {
__ I8x16Splat(i.OutputSimd128Register(), i.InputRegister(0));
break;
}
case 16: {
__ I16x8Splat(i.OutputSimd128Register(), i.InputRegister(0));
break;
}
case 32: {
__ I32x4Splat(i.OutputSimd128Register(), i.InputRegister(0));
break;
}
case 64: {
__ I64x2Splat(i.OutputSimd128Register(), i.InputRegister(0));
break;
}
default:
UNREACHABLE();
}
break;
}
case kPPC_FExtractLane: {
int lane_size = LaneSizeBits(LaneSizeField::decode(instr->opcode()));
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
switch (lane_size) {
case 32: {
__ F32x4ExtractLane(i.OutputDoubleRegister(),
i.InputSimd128Register(0), i.InputInt8(1),
kScratchSimd128Reg, scratch, ip);
break;
}
case 64: {
__ F64x2ExtractLane(i.OutputDoubleRegister(),
i.InputSimd128Register(0), i.InputInt8(1),
kScratchSimd128Reg, scratch);
break;
}
default:
UNREACHABLE();
}
break;
}
case kPPC_IExtractLane: {
int lane_size = LaneSizeBits(LaneSizeField::decode(instr->opcode()));
switch (lane_size) {
case 32: {
__ I32x4ExtractLane(i.OutputRegister(), i.InputSimd128Register(0),
i.InputInt8(1), kScratchSimd128Reg);
break;
}
case 64: {
__ I64x2ExtractLane(i.OutputRegister(), i.InputSimd128Register(0),
i.InputInt8(1), kScratchSimd128Reg);
break;
}
default:
UNREACHABLE();
}
break;
}
case kPPC_IExtractLaneU: {
int lane_size = LaneSizeBits(LaneSizeField::decode(instr->opcode()));
switch (lane_size) {
case 8: {
__ I8x16ExtractLaneU(i.OutputRegister(), i.InputSimd128Register(0),
i.InputInt8(1), kScratchSimd128Reg);
break;
}
case 16: {
__ I16x8ExtractLaneU(i.OutputRegister(), i.InputSimd128Register(0),
i.InputInt8(1), kScratchSimd128Reg);
break;
}
default:
UNREACHABLE();
}
break;
}
case kPPC_IExtractLaneS: {
int lane_size = LaneSizeBits(LaneSizeField::decode(instr->opcode()));
switch (lane_size) {
case 8: {
__ I8x16ExtractLaneS(i.OutputRegister(), i.InputSimd128Register(0),
i.InputInt8(1), kScratchSimd128Reg);
break;
}
case 16: {
__ I16x8ExtractLaneS(i.OutputRegister(), i.InputSimd128Register(0),
i.InputInt8(1), kScratchSimd128Reg);
break;
}
default:
UNREACHABLE();
}
break;
}
case kPPC_FReplaceLane: {
DCHECK_EQ(i.OutputSimd128Register(), i.InputSimd128Register(0));
int lane_size = LaneSizeBits(LaneSizeField::decode(instr->opcode()));
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
switch (lane_size) {
case 32: {
__ F32x4ReplaceLane(i.OutputSimd128Register(),
i.InputSimd128Register(0),
i.InputDoubleRegister(2), i.InputInt8(1), scratch,
kScratchDoubleReg, kScratchSimd128Reg);
break;
}
case 64: {
__ F64x2ReplaceLane(i.OutputSimd128Register(),
i.InputSimd128Register(0),
i.InputDoubleRegister(2), i.InputInt8(1), scratch,
kScratchSimd128Reg);
break;
}
default:
UNREACHABLE();
}
break;
}
case kPPC_IReplaceLane: {
DCHECK_EQ(i.OutputSimd128Register(), i.InputSimd128Register(0));
int lane_size = LaneSizeBits(LaneSizeField::decode(instr->opcode()));
switch (lane_size) {
case 8: {
__ I8x16ReplaceLane(i.OutputSimd128Register(),
i.InputSimd128Register(0), i.InputRegister(2),
i.InputInt8(1), kScratchSimd128Reg);
break;
}
case 16: {
__ I16x8ReplaceLane(i.OutputSimd128Register(),
i.InputSimd128Register(0), i.InputRegister(2),
i.InputInt8(1), kScratchSimd128Reg);
break;
}
case 32: {
__ I32x4ReplaceLane(i.OutputSimd128Register(),
i.InputSimd128Register(0), i.InputRegister(2),
i.InputInt8(1), kScratchSimd128Reg);
break;
}
case 64: {
__ I64x2ReplaceLane(i.OutputSimd128Register(),
i.InputSimd128Register(0), i.InputRegister(2),
i.InputInt8(1), kScratchSimd128Reg);
break;
}
default:
UNREACHABLE();
}
break;
}
case kPPC_I64x2Mul: {
__ I64x2Mul(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1), ip, r0,
i.ToRegister(instr->TempAt(0)), kScratchSimd128Reg);
break;
}
case kPPC_F64x2Min: {
__ F64x2Min(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1), kScratchSimd128Reg,
kScratchSimd128Reg2);
break;
}
case kPPC_F64x2Max: {
__ F64x2Max(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1), kScratchSimd128Reg,
kScratchSimd128Reg2);
break;
}
case kPPC_S128Const: {
uint64_t low = make_uint64(i.InputUint32(1), i.InputUint32(0));
uint64_t high = make_uint64(i.InputUint32(3), i.InputUint32(2));
__ S128Const(i.OutputSimd128Register(), high, low, r0, ip);
break;
}
case kPPC_S128Zero: {
Simd128Register dst = i.OutputSimd128Register();
__ vxor(dst, dst, dst);
break;
}
case kPPC_S128AllOnes: {
Simd128Register dst = i.OutputSimd128Register();
__ vcmpequb(dst, dst, dst);
break;
}
case kPPC_S128Select: {
Simd128Register dst = i.OutputSimd128Register();
Simd128Register mask = i.InputSimd128Register(0);
Simd128Register src1 = i.InputSimd128Register(1);
Simd128Register src2 = i.InputSimd128Register(2);
__ S128Select(dst, src1, src2, mask);
break;
}
case kPPC_V128AnyTrue: {
__ V128AnyTrue(i.OutputRegister(), i.InputSimd128Register(0), r0, ip,
kScratchSimd128Reg);
break;
}
case kPPC_F64x2ConvertLowI32x4U: {
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ F64x2ConvertLowI32x4U(i.OutputSimd128Register(),
i.InputSimd128Register(0), scratch,
kScratchSimd128Reg);
break;
}
case kPPC_I64x2UConvertI32x4Low: {
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ I64x2UConvertI32x4Low(i.OutputSimd128Register(),
i.InputSimd128Register(0), scratch,
kScratchSimd128Reg);
break;
}
case kPPC_I64x2UConvertI32x4High: {
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ I64x2UConvertI32x4High(i.OutputSimd128Register(),
i.InputSimd128Register(0), scratch,
kScratchSimd128Reg);
break;
}
case kPPC_I32x4UConvertI16x8Low: {
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ I32x4UConvertI16x8Low(i.OutputSimd128Register(),
i.InputSimd128Register(0), scratch,
kScratchSimd128Reg);
break;
}
case kPPC_I32x4UConvertI16x8High: {
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ I32x4UConvertI16x8High(i.OutputSimd128Register(),
i.InputSimd128Register(0), scratch,
kScratchSimd128Reg);
break;
}
case kPPC_I16x8UConvertI8x16Low: {
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ I16x8UConvertI8x16Low(i.OutputSimd128Register(),
i.InputSimd128Register(0), scratch,
kScratchSimd128Reg);
break;
}
case kPPC_I16x8UConvertI8x16High: {
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ I16x8UConvertI8x16High(i.OutputSimd128Register(),
i.InputSimd128Register(0), scratch,
kScratchSimd128Reg);
break;
}
case kPPC_I8x16Shuffle: {
uint64_t low = make_uint64(i.InputUint32(3), i.InputUint32(2));
uint64_t high = make_uint64(i.InputUint32(5), i.InputUint32(4));
__ I8x16Shuffle(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1), high, low, r0, ip,
kScratchSimd128Reg);
break;
}
case kPPC_I64x2BitMask: {
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ I64x2BitMask(i.OutputRegister(), i.InputSimd128Register(0), scratch,
kScratchSimd128Reg);
break;
}
case kPPC_I32x4BitMask: {
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ I32x4BitMask(i.OutputRegister(), i.InputSimd128Register(0), scratch,
kScratchSimd128Reg);
break;
}
case kPPC_I16x8BitMask: {
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ I16x8BitMask(i.OutputRegister(), i.InputSimd128Register(0), scratch,
kScratchSimd128Reg);
break;
}
case kPPC_I8x16BitMask: {
__ I8x16BitMask(i.OutputRegister(), i.InputSimd128Register(0), ip, r0,
kScratchSimd128Reg);
break;
}
case kPPC_I32x4DotI8x16AddS: {
__ I32x4DotI8x16AddS(i.OutputSimd128Register(), i.InputSimd128Register(0),
i.InputSimd128Register(1),
i.InputSimd128Register(2));
break;
}
#define PREP_LOAD_EXTEND() \
AddressingMode mode = kMode_None; \
MemOperand operand = i.MemoryOperand(&mode); \
DCHECK_EQ(mode, kMode_MRR);
case kPPC_S128Load8x8S: {
PREP_LOAD_EXTEND()
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ LoadAndExtend8x8SLE(i.OutputSimd128Register(), operand, scratch);
break;
}
case kPPC_S128Load8x8U: {
PREP_LOAD_EXTEND()
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ LoadAndExtend8x8ULE(i.OutputSimd128Register(), operand, scratch,
kScratchSimd128Reg);
break;
}
case kPPC_S128Load16x4S: {
PREP_LOAD_EXTEND()
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ LoadAndExtend16x4SLE(i.OutputSimd128Register(), operand, scratch);
break;
}
case kPPC_S128Load16x4U: {
PREP_LOAD_EXTEND()
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ LoadAndExtend16x4ULE(i.OutputSimd128Register(), operand, scratch,
kScratchSimd128Reg);
break;
}
case kPPC_S128Load32x2S: {
PREP_LOAD_EXTEND()
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ LoadAndExtend32x2SLE(i.OutputSimd128Register(), operand, scratch);
break;
}
case kPPC_S128Load32x2U: {
PREP_LOAD_EXTEND()
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ LoadAndExtend32x2ULE(i.OutputSimd128Register(), operand, scratch,
kScratchSimd128Reg);
break;
}
case kPPC_S128Load32Zero: {
PREP_LOAD_EXTEND()
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ LoadV32ZeroLE(i.OutputSimd128Register(), operand, scratch,
kScratchSimd128Reg);
break;
}
case kPPC_S128Load64Zero: {
PREP_LOAD_EXTEND()
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ LoadV64ZeroLE(i.OutputSimd128Register(), operand, scratch,
kScratchSimd128Reg);
break;
}
#undef PREP_LOAD_EXTEND
case kPPC_StoreCompressTagged: {
size_t index = 0;
AddressingMode mode = kMode_None;
MemOperand operand = i.MemoryOperand(&mode, &index);
Register value = i.InputRegister(index);
bool is_atomic = i.InputInt32(index + 1);
if (is_atomic) __ lwsync();
__ StoreTaggedField(value, operand, r0);
if (is_atomic) __ sync();
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
}
case kPPC_LoadDecompressTaggedSigned: {
CHECK(instr->HasOutput());
ASSEMBLE_LOAD_INTEGER(lwz, plwz, lwzx, false);
break;
}
case kPPC_LoadDecompressTagged: {
CHECK(instr->HasOutput());
ASSEMBLE_LOAD_INTEGER(lwz, plwz, lwzx, false);
__ add(i.OutputRegister(), i.OutputRegister(), kPtrComprCageBaseRegister);
break;
}
default:
UNREACHABLE();
}
return kSuccess;
}
// Assembles branches after an instruction.
void CodeGenerator::AssembleArchBranch(Instruction* instr, BranchInfo* branch) {
PPCOperandConverter i(this, instr);
Label* tlabel = branch->true_label;
Label* flabel = branch->false_label;
ArchOpcode op = instr->arch_opcode();
FlagsCondition condition = branch->condition;
CRegister cr = cr0;
Condition cond = FlagsConditionToCondition(condition, op);
if (op == kPPC_CmpDouble) {
// check for unordered if necessary
if (cond == le) {
__ bunordered(flabel, cr);
// Unnecessary for eq/lt since only FU bit will be set.
} else if (cond == gt) {
__ bunordered(tlabel, cr);
// Unnecessary for ne/ge since only FU bit will be set.
}
}
__ b(cond, tlabel, cr);
if (!branch->fallthru) __ b(flabel); // no fallthru to flabel.
}
void CodeGenerator::AssembleArchDeoptBranch(Instruction* instr,
BranchInfo* branch) {
AssembleArchBranch(instr, branch);
}
void CodeGenerator::AssembleArchJumpRegardlessOfAssemblyOrder(
RpoNumber target) {
__ b(GetLabel(target));
}
#if V8_ENABLE_WEBASSEMBLY
void CodeGenerator::AssembleArchTrap(Instruction* instr,
FlagsCondition condition) {
auto ool = zone()->New<OutOfLineTrap>(this, instr);
Label* tlabel = ool->entry();
Label end;
ArchOpcode op = instr->arch_opcode();
CRegister cr = cr0;
Condition cond = FlagsConditionToCondition(condition, op);
if (op == kPPC_CmpDouble) {
// check for unordered if necessary
if (cond == le) {
__ bunordered(&end, cr);
// Unnecessary for eq/lt since only FU bit will be set.
} else if (cond == gt) {
__ bunordered(tlabel, cr);
// Unnecessary for ne/ge since only FU bit will be set.
}
}
__ b(cond, tlabel, cr);
__ bind(&end);
}
#endif // V8_ENABLE_WEBASSEMBLY
// Assembles boolean materializations after an instruction.
void CodeGenerator::AssembleArchBoolean(Instruction* instr,
FlagsCondition condition) {
PPCOperandConverter i(this, instr);
Label done;
ArchOpcode op = instr->arch_opcode();
CRegister cr = cr0;
int reg_value = -1;
// Materialize a full 32-bit 1 or 0 value. The result register is always the
// last output of the instruction.
DCHECK_NE(0u, instr->OutputCount());
Register reg = i.OutputRegister(instr->OutputCount() - 1);
Condition cond = FlagsConditionToCondition(condition, op);
if (op == kPPC_CmpDouble) {
// check for unordered if necessary
if (cond == le) {
reg_value = 0;
__ li(reg, Operand::Zero());
__ bunordered(&done, cr);
} else if (cond == gt) {
reg_value = 1;
__ li(reg, Operand(1));
__ bunordered(&done, cr);
}
// Unnecessary for eq/lt & ne/ge since only FU bit will be set.
}
switch (cond) {
case eq:
case lt:
case gt:
case overflow32:
case overflow64:
if (reg_value != 1) __ li(reg, Operand(1));
{
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ li(scratch, Operand::Zero());
__ isel(cond, reg, reg, scratch, cr);
}
break;
case ne:
case ge:
case le:
case nooverflow32:
case nooverflow64:
if (reg_value != 1) __ li(reg, Operand(1));
// r0 implies logical zero in this form
__ isel(NegateCondition(cond), reg, r0, reg, cr);
break;
default:
UNREACHABLE();
}
__ bind(&done);
}
#if V8_ENABLE_WEBASSEMBLY
void CodeGenerator::AssembleArchConditionalTrap(Instruction* instr,
FlagsCondition condition) {
UNREACHABLE();
}
#endif // V8_ENABLE_WEBASSEMBLY
void CodeGenerator::AssembleArchConditionalBranch(Instruction* instr,
BranchInfo* branch) {
UNREACHABLE();
}
void CodeGenerator::AssembleArchBinarySearchSwitch(Instruction* instr) {
PPCOperandConverter i(this, instr);
Register input = i.InputRegister(0);
std::vector<std::pair<int32_t, Label*>> cases;
for (size_t index = 2; index < instr->InputCount(); index += 2) {
cases.push_back({i.InputInt32(index + 0), GetLabel(i.InputRpo(index + 1))});
}
AssembleArchBinarySearchSwitchRange(input, i.InputRpo(1), cases.data(),
cases.data() + cases.size());
}
void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) {
PPCOperandConverter i(this, instr);
Register input = i.InputRegister(0);
int32_t const case_count = static_cast<int32_t>(instr->InputCount() - 2);
base::Vector<Label*> cases = zone()->AllocateVector<Label*>(case_count);
for (int32_t index = 0; index < case_count; ++index) {
cases[index] = GetLabel(i.InputRpo(index + 2));
}
Label* const table = AddJumpTable(cases);
__ CmpU64(input, Operand(case_count), r0);
__ bge(GetLabel(i.InputRpo(1)));
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ mov_label_addr(scratch, table);
__ ShiftLeftU64(r0, input, Operand(kSystemPointerSizeLog2));
__ LoadU64(scratch, MemOperand(scratch, r0));
__ Jump(scratch);
}
void CodeGenerator::AssembleArchSelect(Instruction* instr,
FlagsCondition condition) {
UNIMPLEMENTED();
}
void CodeGenerator::FinishFrame(Frame* frame) {
auto call_descriptor = linkage()->GetIncomingDescriptor();
const DoubleRegList double_saves = call_descriptor->CalleeSavedFPRegisters();
// Save callee-saved Double registers.
if (!double_saves.is_empty()) {
frame->AlignSavedCalleeRegisterSlots();
DCHECK_EQ(kNumCalleeSavedDoubles, double_saves.Count());
frame->AllocateSavedCalleeRegisterSlots(kNumCalleeSavedDoubles *
(kDoubleSize / kSystemPointerSize));
}
// Save callee-saved registers.
const RegList saves =
V8_EMBEDDED_CONSTANT_POOL_BOOL
? call_descriptor->CalleeSavedRegisters() - kConstantPoolRegister
: call_descriptor->CalleeSavedRegisters();
if (!saves.is_empty()) {
// register save area does not include the fp or constant pool pointer.
const int num_saves =
kNumCalleeSaved - 1 - (V8_EMBEDDED_CONSTANT_POOL_BOOL ? 1 : 0);
frame->AllocateSavedCalleeRegisterSlots(num_saves);
}
}
void CodeGenerator::AssembleConstructFrame() {
auto call_descriptor = linkage()->GetIncomingDescriptor();
if (frame_access_state()->has_frame()) {
if (call_descriptor->IsCFunctionCall()) {
#if V8_ENABLE_WEBASSEMBLY
if (info()->GetOutputStackFrameType() == StackFrame::C_WASM_ENTRY) {
__ StubPrologue(StackFrame::C_WASM_ENTRY);
// Reserve stack space for saving the c_entry_fp later.
__ addi(sp, sp, Operand(-kSystemPointerSize));
#else
// For balance.
if (false) {
#endif // V8_ENABLE_WEBASSEMBLY
} else {
__ mflr(r0);
if (V8_EMBEDDED_CONSTANT_POOL_BOOL) {
__ Push(r0, fp, kConstantPoolRegister);
// Adjust FP to point to saved FP.
__ SubS64(fp, sp,
Operand(StandardFrameConstants::kConstantPoolOffset), r0);
} else {
__ Push(r0, fp);
__ mr(fp, sp);
}
}
} else if (call_descriptor->IsJSFunctionCall()) {
__ Prologue();
} else {
StackFrame::Type type = info()->GetOutputStackFrameType();
// TODO(mbrandy): Detect cases where ip is the entrypoint (for
// efficient initialization of the constant pool pointer register).
__ StubPrologue(type);
#if V8_ENABLE_WEBASSEMBLY
if (call_descriptor->IsAnyWasmFunctionCall() ||
call_descriptor->IsWasmImportWrapper() ||
call_descriptor->IsWasmCapiFunction()) {
// For import wrappers and C-API functions, this stack slot is only used
// for printing stack traces in V8. Also, it holds a WasmImportData
// instead of the trusted instance data, which is taken care of in the
// frames accessors.
__ Push(kWasmImplicitArgRegister);
}
if (call_descriptor->IsWasmCapiFunction()) {
// Reserve space for saving the PC later.
__ addi(sp, sp, Operand(-kSystemPointerSize));
}
#endif // V8_ENABLE_WEBASSEMBLY
}
unwinding_info_writer_.MarkFrameConstructed(__ pc_offset());
}
int required_slots =
frame()->GetTotalFrameSlotCount() - frame()->GetFixedSlotCount();
if (info()->is_osr()) {
// TurboFan OSR-compiled functions cannot be entered directly.
__ Abort(AbortReason::kShouldNotDirectlyEnterOsrFunction);
// Unoptimized code jumps directly to this entrypoint while the unoptimized
// frame is still on the stack. Optimized code uses OSR values directly from
// the unoptimized frame. Thus, all that needs to be done is to allocate the
// remaining stack slots.
__ RecordComment("-- OSR entrypoint --");
osr_pc_offset_ = __ pc_offset();
required_slots -= osr_helper()->UnoptimizedFrameSlots();
}
const DoubleRegList saves_fp = call_descriptor->CalleeSavedFPRegisters();
const RegList saves =
V8_EMBEDDED_CONSTANT_POOL_BOOL
? call_descriptor->CalleeSavedRegisters() - kConstantPoolRegister
: call_descriptor->CalleeSavedRegisters();
if (required_slots > 0) {
#if V8_ENABLE_WEBASSEMBLY
if (info()->IsWasm() && required_slots * kSystemPointerSize > 4 * KB) {
// For WebAssembly functions with big frames we have to do the stack
// overflow check before we construct the frame. Otherwise we may not
// have enough space on the stack to call the runtime for the stack
// overflow.
Label done;
// If the frame is bigger than the stack, we throw the stack overflow
// exception unconditionally. Thereby we can avoid the integer overflow
// check in the condition code.
if (required_slots * kSystemPointerSize < v8_flags.stack_size * KB) {
Register stack_limit = ip;
__ LoadStackLimit(stack_limit, StackLimitKind::kRealStackLimit, r0);
__ AddS64(stack_limit, stack_limit,
Operand(required_slots * kSystemPointerSize), r0);
__ CmpU64(sp, stack_limit);
__ bge(&done);
}
if (v8_flags.experimental_wasm_growable_stacks) {
RegList regs_to_save;
regs_to_save.set(WasmHandleStackOverflowDescriptor::GapRegister());
regs_to_save.set(
WasmHandleStackOverflowDescriptor::FrameBaseRegister());
for (auto reg : wasm::kGpParamRegisters) regs_to_save.set(reg);
__ MultiPush(regs_to_save);
DoubleRegList fp_regs_to_save;
for (auto reg : wasm::kFpParamRegisters) fp_regs_to_save.set(reg);
Simd128RegList simd128_regs_to_save;
for (auto reg : wasm::kSimd128ParamRegisters)
simd128_regs_to_save.set(reg);
__ MultiPushF64AndV128(fp_regs_to_save, simd128_regs_to_save, ip, r0);
__ mov(WasmHandleStackOverflowDescriptor::GapRegister(),
Operand(required_slots * kSystemPointerSize));
__ AddS64(
WasmHandleStackOverflowDescriptor::FrameBaseRegister(), fp,
Operand(call_descriptor->ParameterSlotCount() * kSystemPointerSize +
CommonFrameConstants::kFixedFrameSizeAboveFp));
__ Call(static_cast<Address>(Builtin::kWasmHandleStackOverflow),
RelocInfo::WASM_STUB_CALL);
// If the call successfully grew the stack, we don't expect it to have
// allocated any heap objects or otherwise triggered any GC.
// If it was not able to grow the stack, it may have triggered a GC when
// allocating the stack overflow exception object, but the call did not
// return in this case.
// So either way, we can just ignore any references and record an empty
// safepoint here.
ReferenceMap* reference_map = zone()->New<ReferenceMap>(zone());
RecordSafepoint(reference_map);
__ MultiPopF64AndV128(fp_regs_to_save, simd128_regs_to_save, ip, r0);
__ MultiPop(regs_to_save);
} else {
__ Call(static_cast<intptr_t>(Builtin::kWasmStackOverflow),
RelocInfo::WASM_STUB_CALL);
// The call does not return, hence we can ignore any references and just
// define an empty safepoint.
ReferenceMap* reference_map = zone()->New<ReferenceMap>(zone());
RecordSafepoint(reference_map);
if (v8_flags.debug_code) __ stop();
}
__ bind(&done);
}
#endif // V8_ENABLE_WEBASSEMBLY
// Skip callee-saved and return slots, which are pushed below.
required_slots -= saves.Count();
required_slots -= frame()->GetReturnSlotCount();
required_slots -= (kDoubleSize / kSystemPointerSize) * saves_fp.Count();
__ AddS64(sp, sp, Operand(-required_slots * kSystemPointerSize), r0);
}
// Save callee-saved Double registers.
if (!saves_fp.is_empty()) {
__ MultiPushDoubles(saves_fp);
DCHECK_EQ(kNumCalleeSavedDoubles, saves_fp.Count());
}
// Save callee-saved registers.
if (!saves.is_empty()) {
__ MultiPush(saves);
// register save area does not include the fp or constant pool pointer.
}
const int returns = frame()->GetReturnSlotCount();
// Create space for returns.
__ AllocateStackSpace(returns * kSystemPointerSize);
if (!frame()->tagged_slots().IsEmpty()) {
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ mov(scratch, Operand(0));
for (int spill_slot : frame()->tagged_slots()) {
FrameOffset offset = frame_access_state()->GetFrameOffset(spill_slot);
DCHECK(offset.from_frame_pointer());
__ StoreU64(scratch, MemOperand(fp, offset.offset()));
}
}
}
void CodeGenerator::AssembleReturn(InstructionOperand* additional_pop_count) {
auto call_descriptor = linkage()->GetIncomingDescriptor();
const int returns = frame()->GetReturnSlotCount();
if (returns != 0) {
// Create space for returns.
__ AddS64(sp, sp, Operand(returns * kSystemPointerSize), r0);
}
// Restore registers.
const RegList saves =
V8_EMBEDDED_CONSTANT_POOL_BOOL
? call_descriptor->CalleeSavedRegisters() - kConstantPoolRegister
: call_descriptor->CalleeSavedRegisters();
if (!saves.is_empty()) {
__ MultiPop(saves);
}
// Restore double registers.
const DoubleRegList double_saves = call_descriptor->CalleeSavedFPRegisters();
if (!double_saves.is_empty()) {
__ MultiPopDoubles(double_saves);
}
unwinding_info_writer_.MarkBlockWillExit();
PPCOperandConverter g(this, nullptr);
const int parameter_slots =
static_cast<int>(call_descriptor->ParameterSlotCount());
// {aditional_pop_count} is only greater than zero if {parameter_slots = 0}.
// Check RawMachineAssembler::PopAndReturn.
if (parameter_slots != 0) {
if (additional_pop_count->IsImmediate()) {
DCHECK_EQ(g.ToConstant(additional_pop_count).ToInt32(), 0);
} else if (v8_flags.debug_code) {
__ cmpi(g.ToRegister(additional_pop_count), Operand(0));
__ Assert(eq, AbortReason::kUnexpectedAdditionalPopValue);
}
}
#if V8_ENABLE_WEBASSEMBLY
if (call_descriptor->IsAnyWasmFunctionCall() &&
v8_flags.experimental_wasm_growable_stacks) {
{
UseScratchRegisterScope temps{masm()};
Register scratch = temps.Acquire();
__ LoadU64(scratch,
MemOperand(fp, TypedFrameConstants::kFrameTypeOffset));
__ CmpU64(
scratch,
Operand(StackFrame::TypeToMarker(StackFrame::WASM_SEGMENT_START)),
r0);
}
Label done;
__ bne(&done);
RegList regs_to_save;
for (auto reg : wasm::kGpReturnRegisters) regs_to_save.set(reg);
__ MultiPush(regs_to_save);
DoubleRegList fp_regs_to_save;
for (auto reg : wasm::kFpParamRegisters) fp_regs_to_save.set(reg);
Simd128RegList simd128_regs_to_save;
for (auto reg : wasm::kSimd128ParamRegisters) simd128_regs_to_save.set(reg);
__ MultiPushF64AndV128(fp_regs_to_save, simd128_regs_to_save, ip, r0);
__ Move(kCArgRegs[0], ExternalReference::isolate_address());
__ PrepareCallCFunction(1, r0);
__ CallCFunction(ExternalReference::wasm_shrink_stack(), 1);
// Restore old FP. We don't need to restore old SP explicitly, because
// it will be restored from FP in LeaveFrame before return.
__ mr(fp, kReturnRegister0);
__ MultiPopF64AndV128(fp_regs_to_save, simd128_regs_to_save, ip, r0);
__ MultiPop(regs_to_save);
__ bind(&done);
}
#endif // V8_ENABLE_WEBASSEMBLY
Register argc_reg = r6;
// Functions with JS linkage have at least one parameter (the receiver).
// If {parameter_slots} == 0, it means it is a builtin with
// kDontAdaptArgumentsSentinel, which takes care of JS arguments popping
// itself.
const bool drop_jsargs = parameter_slots != 0 &&
frame_access_state()->has_frame() &&
call_descriptor->IsJSFunctionCall();
if (call_descriptor->IsCFunctionCall()) {
AssembleDeconstructFrame();
} else if (frame_access_state()->has_frame()) {
// Canonicalize JSFunction return sites for now unless they have an variable
// number of stack slot pops
if (additional_pop_count->IsImmediate() &&
g.ToConstant(additional_pop_count).ToInt32() == 0) {
if (return_label_.is_bound()) {
__ b(&return_label_);
return;
} else {
__ bind(&return_label_);
}
}
if (drop_jsargs) {
// Get the actual argument count.
DCHECK(!call_descriptor->CalleeSavedRegisters().has(argc_reg));
__ LoadU64(argc_reg, MemOperand(fp, StandardFrameConstants::kArgCOffset));
}
AssembleDeconstructFrame();
}
// Constant pool is unavailable since the frame has been destructed
ConstantPoolUnavailableScope constant_pool_unavailable(masm());
if (drop_jsargs) {
// We must pop all arguments from the stack (including the receiver).
// The number of arguments without the receiver is
// max(argc_reg, parameter_slots-1), and the receiver is added in
// DropArguments().
DCHECK(!call_descriptor->CalleeSavedRegisters().has(argc_reg));
if (parameter_slots > 1) {
Label skip;
__ CmpS64(argc_reg, Operand(parameter_slots), r0);
__ bgt(&skip);
__ mov(argc_reg, Operand(parameter_slots));
__ bind(&skip);
}
__ DropArguments(argc_reg);
} else if (additional_pop_count->IsImmediate()) {
int additional_count = g.ToConstant(additional_pop_count).ToInt32();
__ Drop(parameter_slots + additional_count);
} else if (parameter_slots == 0) {
__ Drop(g.ToRegister(additional_pop_count));
} else {
// {additional_pop_count} is guaranteed to be zero if {parameter_slots !=
// 0}. Check RawMachineAssembler::PopAndReturn.
__ Drop(parameter_slots);
}
__ Ret();
}
void CodeGenerator::FinishCode() {}
void CodeGenerator::PrepareForDeoptimizationExits(
ZoneDeque<DeoptimizationExit*>* exits) {
int total_size = 0;
for (DeoptimizationExit* exit : deoptimization_exits_) {
total_size += (exit->kind() == DeoptimizeKind::kLazy)
? Deoptimizer::kLazyDeoptExitSize
: Deoptimizer::kEagerDeoptExitSize;
}
__ CheckTrampolinePoolQuick(total_size);
}
AllocatedOperand CodeGenerator::Push(InstructionOperand* source) {
auto rep = LocationOperand::cast(source)->representation();
int new_slots = ElementSizeInPointers(rep);
PPCOperandConverter g(this, nullptr);
int last_frame_slot_id =
frame_access_state_->frame()->GetTotalFrameSlotCount() - 1;
int sp_delta = frame_access_state_->sp_delta();
int slot_id = last_frame_slot_id + sp_delta + new_slots;
AllocatedOperand stack_slot(LocationOperand::STACK_SLOT, rep, slot_id);
if (source->IsFloatStackSlot() || source->IsDoubleStackSlot()) {
__ LoadU64(r0, g.ToMemOperand(source), r0);
__ Push(r0);
frame_access_state()->IncreaseSPDelta(new_slots);
} else {
// Bump the stack pointer and assemble the move.
__ addi(sp, sp, Operand(-(new_slots * kSystemPointerSize)));
frame_access_state()->IncreaseSPDelta(new_slots);
AssembleMove(source, &stack_slot);
}
temp_slots_ += new_slots;
return stack_slot;
}
void CodeGenerator::Pop(InstructionOperand* dest, MachineRepresentation rep) {
int dropped_slots = ElementSizeInPointers(rep);
PPCOperandConverter g(this, nullptr);
if (dest->IsFloatStackSlot() || dest->IsDoubleStackSlot()) {
frame_access_state()->IncreaseSPDelta(-dropped_slots);
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
__ Pop(scratch);
__ StoreU64(scratch, g.ToMemOperand(dest), r0);
} else {
int last_frame_slot_id =
frame_access_state_->frame()->GetTotalFrameSlotCount() - 1;
int sp_delta = frame_access_state_->sp_delta();
int slot_id = last_frame_slot_id + sp_delta;
AllocatedOperand stack_slot(LocationOperand::STACK_SLOT, rep, slot_id);
AssembleMove(&stack_slot, dest);
frame_access_state()->IncreaseSPDelta(-dropped_slots);
__ addi(sp, sp, Operand(dropped_slots * kSystemPointerSize));
}
temp_slots_ -= dropped_slots;
}
void CodeGenerator::PopTempStackSlots() {
if (temp_slots_ > 0) {
frame_access_state()->IncreaseSPDelta(-temp_slots_);
__ addi(sp, sp, Operand(temp_slots_ * kSystemPointerSize));
temp_slots_ = 0;
}
}
void CodeGenerator::MoveToTempLocation(InstructionOperand* source,
MachineRepresentation rep) {
// Must be kept in sync with {MoveTempLocationTo}.
if (!IsFloatingPoint(rep) ||
((IsFloatingPoint(rep) &&
!move_cycle_.pending_double_scratch_register_use))) {
// The scratch register for this rep is available.
int scratch_reg_code;
if (IsSimd128(rep)) {
scratch_reg_code = kScratchSimd128Reg.code();
} else if (IsFloatingPoint(rep)) {
scratch_reg_code = kScratchDoubleReg.code();
} else {
DCHECK(!move_cycle_.temps.has_value());
move_cycle_.temps.emplace(masm());
move_cycle_.scratch_reg_code = move_cycle_.temps->Acquire().code();
scratch_reg_code = move_cycle_.scratch_reg_code;
DCHECK(!AreAliased(Register::from_code(scratch_reg_code), r0, ip));
}
AllocatedOperand scratch(LocationOperand::REGISTER, rep, scratch_reg_code);
AssembleMove(source, &scratch);
} else {
// The scratch register is blocked by pending moves. Use the stack instead.
Push(source);
}
}
void CodeGenerator::MoveTempLocationTo(InstructionOperand* dest,
MachineRepresentation rep) {
if (!IsFloatingPoint(rep) ||
((IsFloatingPoint(rep) &&
!move_cycle_.pending_double_scratch_register_use))) {
int scratch_reg_code;
if (IsSimd128(rep)) {
scratch_reg_code = kScratchSimd128Reg.code();
} else if (IsFloatingPoint(rep)) {
scratch_reg_code = kScratchDoubleReg.code();
} else {
DCHECK_NE(move_cycle_.scratch_reg_code, -1);
scratch_reg_code = move_cycle_.scratch_reg_code;
DCHECK(!AreAliased(Register::from_code(scratch_reg_code), r0, ip));
}
AllocatedOperand scratch(LocationOperand::REGISTER, rep, scratch_reg_code);
AssembleMove(&scratch, dest);
} else {
Pop(dest, rep);
}
move_cycle_ = MoveCycleState();
}
void CodeGenerator::SetPendingMove(MoveOperands* move) {
if ((move->source().IsConstant() || move->source().IsFPStackSlot()) &&
!move->destination().IsFPRegister()) {
move_cycle_.pending_double_scratch_register_use = true;
}
}
void CodeGenerator::AssembleMove(InstructionOperand* source,
InstructionOperand* destination) {
PPCOperandConverter g(this, nullptr);
// Dispatch on the source and destination operand kinds. Not all
// combinations are possible.
// If a move type needs the scratch register, this also needs to be recorded
// in {SetPendingMove} to avoid conflicts with the gap resolver.
if (source->IsRegister()) {
DCHECK(destination->IsRegister() || destination->IsStackSlot());
Register src = g.ToRegister(source);
if (destination->IsRegister()) {
__ Move(g.ToRegister(destination), src);
} else {
__ StoreU64(src, g.ToMemOperand(destination), r0);
}
} else if (source->IsStackSlot()) {
DCHECK(destination->IsRegister() || destination->IsStackSlot());
MemOperand src = g.ToMemOperand(source);
if (destination->IsRegister()) {
__ LoadU64(g.ToRegister(destination), src, r0);
} else {
Register temp = ip;
__ LoadU64(temp, src, r0);
__ StoreU64(temp, g.ToMemOperand(destination), r0);
}
} else if (source->IsConstant()) {
Constant src = g.ToConstant(source);
if (destination->IsRegister() || destination->IsStackSlot()) {
Register dst = destination->IsRegister() ? g.ToRegister(destination) : ip;
switch (src.type()) {
case Constant::kInt32:
__ mov(dst, Operand(src.ToInt32(), src.rmode()));
break;
case Constant::kInt64:
__ mov(dst, Operand(src.ToInt64(), src.rmode()));
break;
case Constant::kFloat32:
__ mov(dst, Operand::EmbeddedNumber(src.ToFloat32()));
break;
case Constant::kFloat64:
__ mov(dst, Operand::EmbeddedNumber(src.ToFloat64().value()));
break;
case Constant::kExternalReference:
__ Move(dst, src.ToExternalReference());
break;
case Constant::kHeapObject: {
Handle<HeapObject> src_object = src.ToHeapObject();
RootIndex index;
if (IsMaterializableFromRoot(src_object, &index)) {
__ LoadRoot(dst, index);
} else {
__ Move(dst, src_object);
}
break;
}
case Constant::kCompressedHeapObject: {
Handle<HeapObject> src_object = src.ToHeapObject();
RootIndex index;
if (IsMaterializableFromRoot(src_object, &index)) {
__ LoadTaggedRoot(dst, index);
} else {
// TODO(v8:7703, jyan@ca.ibm.com): Turn into a
// COMPRESSED_EMBEDDED_OBJECT when the constant pool entry size is
// tagged size.
__ Move(dst, src_object, RelocInfo::FULL_EMBEDDED_OBJECT);
}
break;
}
case Constant::kRpoNumber:
UNREACHABLE(); // TODO(dcarney): loading RPO constants on PPC.
}
if (destination->IsStackSlot()) {
__ StoreU64(dst, g.ToMemOperand(destination), r0);
}
} else {
DoubleRegister dst = destination->IsFPRegister()
? g.ToDoubleRegister(destination)
: kScratchDoubleReg;
base::Double value;
#if V8_HOST_ARCH_IA32 || V8_HOST_ARCH_X64
// casting double precision snan to single precision
// converts it to qnan on ia32/x64
if (src.type() == Constant::kFloat32) {
uint32_t val = src.ToFloat32AsInt();
if ((val & 0x7F800000) == 0x7F800000) {
uint64_t dval = static_cast<uint64_t>(val);
dval = ((dval & 0xC0000000) << 32) | ((dval & 0x40000000) << 31) |
((dval & 0x40000000) << 30) | ((dval & 0x7FFFFFFF) << 29);
value = base::Double(dval);
} else {
value = base::Double(static_cast<double>(src.ToFloat32()));
}
} else {
value = base::Double(src.ToFloat64());
}
#else
value = src.type() == Constant::kFloat32
? base::Double(static_cast<double>(src.ToFloat32()))
: base::Double(src.ToFloat64());
#endif
__ LoadDoubleLiteral(dst, value, r0);
if (destination->IsDoubleStackSlot()) {
__ StoreF64(dst, g.ToMemOperand(destination), r0);
} else if (destination->IsFloatStackSlot()) {
__ StoreF32(dst, g.ToMemOperand(destination), r0);
}
}
} else if (source->IsFPRegister()) {
MachineRepresentation rep = LocationOperand::cast(source)->representation();
if (rep == MachineRepresentation::kSimd128) {
if (destination->IsSimd128Register()) {
__ vor(g.ToSimd128Register(destination), g.ToSimd128Register(source),
g.ToSimd128Register(source));
} else {
DCHECK(destination->IsSimd128StackSlot());
MemOperand dst = g.ToMemOperand(destination);
__ StoreSimd128(g.ToSimd128Register(source), dst, r0);
}
} else {
DoubleRegister src = g.ToDoubleRegister(source);
if (destination->IsFPRegister()) {
DoubleRegister dst = g.ToDoubleRegister(destination);
__ Move(dst, src);
} else {
DCHECK(destination->IsFPStackSlot());
LocationOperand* op = LocationOperand::cast(source);
if (op->representation() == MachineRepresentation::kFloat64) {
__ StoreF64(src, g.ToMemOperand(destination), r0);
} else {
__ StoreF32(src, g.ToMemOperand(destination), r0);
}
}
}
} else if (source->IsFPStackSlot()) {
DCHECK(destination->IsFPRegister() || destination->IsFPStackSlot());
MemOperand src = g.ToMemOperand(source);
if (destination->IsFPRegister()) {
LocationOperand* op = LocationOperand::cast(source);
if (op->representation() == MachineRepresentation::kFloat64) {
__ LoadF64(g.ToDoubleRegister(destination), src, r0);
} else if (op->representation() == MachineRepresentation::kFloat32) {
__ LoadF32(g.ToDoubleRegister(destination), src, r0);
} else {
DCHECK_EQ(MachineRepresentation::kSimd128, op->representation());
MemOperand src = g.ToMemOperand(source);
__ LoadSimd128(g.ToSimd128Register(destination), src, r0);
}
} else {
LocationOperand* op = LocationOperand::cast(source);
DoubleRegister temp = kScratchDoubleReg;
if (op->representation() == MachineRepresentation::kFloat64) {
__ LoadF64(temp, src, r0);
__ StoreF64(temp, g.ToMemOperand(destination), r0);
} else if (op->representation() == MachineRepresentation::kFloat32) {
__ LoadF32(temp, src, r0);
__ StoreF32(temp, g.ToMemOperand(destination), r0);
} else {
DCHECK_EQ(MachineRepresentation::kSimd128, op->representation());
MemOperand src = g.ToMemOperand(source);
MemOperand dst = g.ToMemOperand(destination);
__ LoadSimd128(kScratchSimd128Reg, src, r0);
__ StoreSimd128(kScratchSimd128Reg, dst, r0);
}
}
} else {
UNREACHABLE();
}
}
// Swaping contents in source and destination.
// source and destination could be:
// Register,
// FloatRegister,
// DoubleRegister,
// StackSlot,
// FloatStackSlot,
// or DoubleStackSlot
void CodeGenerator::AssembleSwap(InstructionOperand* source,
InstructionOperand* destination) {
PPCOperandConverter g(this, nullptr);
UseScratchRegisterScope temps(masm());
Register scratch = temps.Acquire();
if (source->IsRegister()) {
Register src = g.ToRegister(source);
if (destination->IsRegister()) {
__ SwapP(src, g.ToRegister(destination), scratch);
} else {
DCHECK(destination->IsStackSlot());
__ SwapP(src, g.ToMemOperand(destination), scratch);
}
} else if (source->IsStackSlot()) {
DCHECK(destination->IsStackSlot());
__ SwapP(g.ToMemOperand(source), g.ToMemOperand(destination), scratch, r0);
} else if (source->IsFloatRegister()) {
DoubleRegister src = g.ToDoubleRegister(source);
if (destination->IsFloatRegister()) {
__ SwapFloat32(src, g.ToDoubleRegister(destination), kScratchDoubleReg);
} else {
DCHECK(destination->IsFloatStackSlot());
__ SwapFloat32(src, g.ToMemOperand(destination), kScratchDoubleReg);
}
} else if (source->IsDoubleRegister()) {
DoubleRegister src = g.ToDoubleRegister(source);
if (destination->IsDoubleRegister()) {
__ SwapDouble(src, g.ToDoubleRegister(destination), kScratchDoubleReg);
} else {
DCHECK(destination->IsDoubleStackSlot());
__ SwapDouble(src, g.ToMemOperand(destination), kScratchDoubleReg);
}
} else if (source->IsFloatStackSlot()) {
DCHECK(destination->IsFloatStackSlot());
__ SwapFloat32(g.ToMemOperand(source), g.ToMemOperand(destination),
kScratchDoubleReg, d0);
} else if (source->IsDoubleStackSlot()) {
DCHECK(destination->IsDoubleStackSlot());
__ SwapDouble(g.ToMemOperand(source), g.ToMemOperand(destination),
kScratchDoubleReg, d0);
} else if (source->IsSimd128Register()) {
Simd128Register src = g.ToSimd128Register(source);
if (destination->IsSimd128Register()) {
__ SwapSimd128(src, g.ToSimd128Register(destination), kScratchSimd128Reg);
} else {
DCHECK(destination->IsSimd128StackSlot());
__ SwapSimd128(src, g.ToMemOperand(destination), kScratchSimd128Reg,
scratch);
}
} else if (source->IsSimd128StackSlot()) {
DCHECK(destination->IsSimd128StackSlot());
__ SwapSimd128(g.ToMemOperand(source), g.ToMemOperand(destination),
kScratchSimd128Reg, kScratchSimd128Reg2, scratch);
} else {
UNREACHABLE();
}
return;
}
void CodeGenerator::AssembleJumpTable(base::Vector<Label*> targets) {
for (auto target : targets) {
__ emit_label_addr(target);
}
}
#undef __
} // namespace compiler
} // namespace internal
} // namespace v8