blob: fe2488437868fdd7a02b9823c47e23a3fdf04ce3 [file] [log] [blame]
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <assert.h> // For assert
#include <limits.h> // For LONG_MIN, LONG_MAX.
#if V8_TARGET_ARCH_S390
#include "src/base/bits.h"
#include "src/base/division-by-constant.h"
#include "src/bootstrapper.h"
#include "src/callable.h"
#include "src/code-stubs.h"
#include "src/debug/debug.h"
#include "src/external-reference-table.h"
#include "src/frames-inl.h"
#include "src/register-configuration.h"
#include "src/runtime/runtime.h"
#include "src/s390/macro-assembler-s390.h"
namespace v8 {
namespace internal {
MacroAssembler::MacroAssembler(Isolate* isolate, void* buffer, int size,
CodeObjectRequired create_code_object)
: TurboAssembler(isolate, buffer, size, create_code_object) {}
TurboAssembler::TurboAssembler(Isolate* isolate, void* buffer, int buffer_size,
CodeObjectRequired create_code_object)
: Assembler(isolate, buffer, buffer_size), isolate_(isolate) {
if (create_code_object == CodeObjectRequired::kYes) {
code_object_ =
Handle<HeapObject>::New(isolate->heap()->undefined_value(), isolate);
}
}
int TurboAssembler::RequiredStackSizeForCallerSaved(SaveFPRegsMode fp_mode,
Register exclusion1,
Register exclusion2,
Register exclusion3) const {
int bytes = 0;
RegList exclusions = 0;
if (exclusion1 != no_reg) {
exclusions |= exclusion1.bit();
if (exclusion2 != no_reg) {
exclusions |= exclusion2.bit();
if (exclusion3 != no_reg) {
exclusions |= exclusion3.bit();
}
}
}
RegList list = kJSCallerSaved & ~exclusions;
bytes += NumRegs(list) * kPointerSize;
if (fp_mode == kSaveFPRegs) {
bytes += NumRegs(kCallerSavedDoubles) * kDoubleSize;
}
return bytes;
}
int TurboAssembler::PushCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1,
Register exclusion2, Register exclusion3) {
int bytes = 0;
RegList exclusions = 0;
if (exclusion1 != no_reg) {
exclusions |= exclusion1.bit();
if (exclusion2 != no_reg) {
exclusions |= exclusion2.bit();
if (exclusion3 != no_reg) {
exclusions |= exclusion3.bit();
}
}
}
RegList list = kJSCallerSaved & ~exclusions;
MultiPush(list);
bytes += NumRegs(list) * kPointerSize;
if (fp_mode == kSaveFPRegs) {
MultiPushDoubles(kCallerSavedDoubles);
bytes += NumRegs(kCallerSavedDoubles) * kDoubleSize;
}
return bytes;
}
int TurboAssembler::PopCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1,
Register exclusion2, Register exclusion3) {
int bytes = 0;
if (fp_mode == kSaveFPRegs) {
MultiPopDoubles(kCallerSavedDoubles);
bytes += NumRegs(kCallerSavedDoubles) * kDoubleSize;
}
RegList exclusions = 0;
if (exclusion1 != no_reg) {
exclusions |= exclusion1.bit();
if (exclusion2 != no_reg) {
exclusions |= exclusion2.bit();
if (exclusion3 != no_reg) {
exclusions |= exclusion3.bit();
}
}
}
RegList list = kJSCallerSaved & ~exclusions;
MultiPop(list);
bytes += NumRegs(list) * kPointerSize;
return bytes;
}
void TurboAssembler::Jump(Register target) { b(target); }
void MacroAssembler::JumpToJSEntry(Register target) {
Move(ip, target);
Jump(ip);
}
void TurboAssembler::Jump(intptr_t target, RelocInfo::Mode rmode,
Condition cond, CRegister) {
Label skip;
if (cond != al) b(NegateCondition(cond), &skip);
DCHECK(rmode == RelocInfo::CODE_TARGET || rmode == RelocInfo::RUNTIME_ENTRY);
mov(ip, Operand(target, rmode));
b(ip);
bind(&skip);
}
void TurboAssembler::Jump(Address target, RelocInfo::Mode rmode, Condition cond,
CRegister cr) {
DCHECK(!RelocInfo::IsCodeTarget(rmode));
Jump(reinterpret_cast<intptr_t>(target), rmode, cond, cr);
}
void TurboAssembler::Jump(Handle<Code> code, RelocInfo::Mode rmode,
Condition cond) {
DCHECK(RelocInfo::IsCodeTarget(rmode));
jump(code, rmode, cond);
}
int TurboAssembler::CallSize(Register target) { return 2; } // BASR
void TurboAssembler::Call(Register target) {
Label start;
bind(&start);
// Branch to target via indirect branch
basr(r14, target);
DCHECK_EQ(CallSize(target), SizeOfCodeGeneratedSince(&start));
}
void MacroAssembler::CallJSEntry(Register target) {
DCHECK(target == ip);
Call(target);
}
int TurboAssembler::CallSize(Address target, RelocInfo::Mode rmode,
Condition cond) {
// S390 Assembler::move sequence is IILF / IIHF
int size;
#if V8_TARGET_ARCH_S390X
size = 14; // IILF + IIHF + BASR
#else
size = 8; // IILF + BASR
#endif
return size;
}
int MacroAssembler::CallSizeNotPredictableCodeSize(Address target,
RelocInfo::Mode rmode,
Condition cond) {
// S390 Assembler::move sequence is IILF / IIHF
int size;
#if V8_TARGET_ARCH_S390X
size = 14; // IILF + IIHF + BASR
#else
size = 8; // IILF + BASR
#endif
return size;
}
void TurboAssembler::Call(Address target, RelocInfo::Mode rmode,
Condition cond) {
DCHECK(cond == al);
#ifdef DEBUG
// Check the expected size before generating code to ensure we assume the same
// constant pool availability (e.g., whether constant pool is full or not).
int expected_size = CallSize(target, rmode, cond);
Label start;
bind(&start);
#endif
mov(ip, Operand(reinterpret_cast<intptr_t>(target), rmode));
basr(r14, ip);
DCHECK_EQ(expected_size, SizeOfCodeGeneratedSince(&start));
}
int TurboAssembler::CallSize(Handle<Code> code, RelocInfo::Mode rmode,
Condition cond) {
return 6; // BRASL
}
void TurboAssembler::Call(Handle<Code> code, RelocInfo::Mode rmode,
Condition cond) {
DCHECK(RelocInfo::IsCodeTarget(rmode) && cond == al);
#ifdef DEBUG
// Check the expected size before generating code to ensure we assume the same
// constant pool availability (e.g., whether constant pool is full or not).
int expected_size = CallSize(code, rmode, cond);
Label start;
bind(&start);
#endif
call(code, rmode);
DCHECK_EQ(expected_size, SizeOfCodeGeneratedSince(&start));
}
void TurboAssembler::Drop(int count) {
if (count > 0) {
int total = count * kPointerSize;
if (is_uint12(total)) {
la(sp, MemOperand(sp, total));
} else if (is_int20(total)) {
lay(sp, MemOperand(sp, total));
} else {
AddP(sp, Operand(total));
}
}
}
void TurboAssembler::Drop(Register count, Register scratch) {
ShiftLeftP(scratch, count, Operand(kPointerSizeLog2));
AddP(sp, sp, scratch);
}
void TurboAssembler::Call(Label* target) { b(r14, target); }
void TurboAssembler::Push(Handle<HeapObject> handle) {
mov(r0, Operand(handle));
push(r0);
}
void TurboAssembler::Push(Smi* smi) {
mov(r0, Operand(smi));
push(r0);
}
void TurboAssembler::Move(Register dst, Handle<HeapObject> value) {
mov(dst, Operand(value));
}
void TurboAssembler::Move(Register dst, Register src, Condition cond) {
if (dst != src) {
LoadRR(dst, src);
}
}
void TurboAssembler::Move(DoubleRegister dst, DoubleRegister src) {
if (dst != src) {
ldr(dst, src);
}
}
void TurboAssembler::MultiPush(RegList regs, Register location) {
int16_t num_to_push = base::bits::CountPopulation(regs);
int16_t stack_offset = num_to_push * kPointerSize;
SubP(location, location, Operand(stack_offset));
for (int16_t i = Register::kNumRegisters - 1; i >= 0; i--) {
if ((regs & (1 << i)) != 0) {
stack_offset -= kPointerSize;
StoreP(ToRegister(i), MemOperand(location, stack_offset));
}
}
}
void TurboAssembler::MultiPop(RegList regs, Register location) {
int16_t stack_offset = 0;
for (int16_t i = 0; i < Register::kNumRegisters; i++) {
if ((regs & (1 << i)) != 0) {
LoadP(ToRegister(i), MemOperand(location, stack_offset));
stack_offset += kPointerSize;
}
}
AddP(location, location, Operand(stack_offset));
}
void TurboAssembler::MultiPushDoubles(RegList dregs, Register location) {
int16_t num_to_push = base::bits::CountPopulation(dregs);
int16_t stack_offset = num_to_push * kDoubleSize;
SubP(location, location, Operand(stack_offset));
for (int16_t i = DoubleRegister::kNumRegisters - 1; i >= 0; i--) {
if ((dregs & (1 << i)) != 0) {
DoubleRegister dreg = DoubleRegister::from_code(i);
stack_offset -= kDoubleSize;
StoreDouble(dreg, MemOperand(location, stack_offset));
}
}
}
void TurboAssembler::MultiPopDoubles(RegList dregs, Register location) {
int16_t stack_offset = 0;
for (int16_t i = 0; i < DoubleRegister::kNumRegisters; i++) {
if ((dregs & (1 << i)) != 0) {
DoubleRegister dreg = DoubleRegister::from_code(i);
LoadDouble(dreg, MemOperand(location, stack_offset));
stack_offset += kDoubleSize;
}
}
AddP(location, location, Operand(stack_offset));
}
void TurboAssembler::LoadRoot(Register destination, Heap::RootListIndex index,
Condition) {
LoadP(destination, MemOperand(kRootRegister, index << kPointerSizeLog2), r0);
}
void MacroAssembler::RecordWriteField(Register object, int offset,
Register value, Register dst,
LinkRegisterStatus lr_status,
SaveFPRegsMode save_fp,
RememberedSetAction remembered_set_action,
SmiCheck smi_check) {
// First, check if a write barrier is even needed. The tests below
// catch stores of Smis.
Label done;
// Skip barrier if writing a smi.
if (smi_check == INLINE_SMI_CHECK) {
JumpIfSmi(value, &done);
}
// Although the object register is tagged, the offset is relative to the start
// of the object, so so offset must be a multiple of kPointerSize.
DCHECK(IsAligned(offset, kPointerSize));
lay(dst, MemOperand(object, offset - kHeapObjectTag));
if (emit_debug_code()) {
Label ok;
AndP(r0, dst, Operand(kPointerSize - 1));
beq(&ok, Label::kNear);
stop("Unaligned cell in write barrier");
bind(&ok);
}
RecordWrite(object, dst, value, lr_status, save_fp, remembered_set_action,
OMIT_SMI_CHECK);
bind(&done);
// Clobber clobbered input registers when running with the debug-code flag
// turned on to provoke errors.
if (emit_debug_code()) {
mov(value, Operand(bit_cast<intptr_t>(kZapValue + 4)));
mov(dst, Operand(bit_cast<intptr_t>(kZapValue + 8)));
}
}
void TurboAssembler::SaveRegisters(RegList registers) {
DCHECK_GT(NumRegs(registers), 0);
RegList regs = 0;
for (int i = 0; i < Register::kNumRegisters; ++i) {
if ((registers >> i) & 1u) {
regs |= Register::from_code(i).bit();
}
}
MultiPush(regs);
}
void TurboAssembler::RestoreRegisters(RegList registers) {
DCHECK_GT(NumRegs(registers), 0);
RegList regs = 0;
for (int i = 0; i < Register::kNumRegisters; ++i) {
if ((registers >> i) & 1u) {
regs |= Register::from_code(i).bit();
}
}
MultiPop(regs);
}
void TurboAssembler::CallRecordWriteStub(
Register object, Register address,
RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode) {
// TODO(albertnetymk): For now we ignore remembered_set_action and fp_mode,
// i.e. always emit remember set and save FP registers in RecordWriteStub. If
// large performance regression is observed, we should use these values to
// avoid unnecessary work.
Callable const callable =
Builtins::CallableFor(isolate(), Builtins::kRecordWrite);
RegList registers = callable.descriptor().allocatable_registers();
SaveRegisters(registers);
Register object_parameter(callable.descriptor().GetRegisterParameter(
RecordWriteDescriptor::kObject));
Register slot_parameter(
callable.descriptor().GetRegisterParameter(RecordWriteDescriptor::kSlot));
Register isolate_parameter(callable.descriptor().GetRegisterParameter(
RecordWriteDescriptor::kIsolate));
Register remembered_set_parameter(callable.descriptor().GetRegisterParameter(
RecordWriteDescriptor::kRememberedSet));
Register fp_mode_parameter(callable.descriptor().GetRegisterParameter(
RecordWriteDescriptor::kFPMode));
Push(object);
Push(address);
Pop(slot_parameter);
Pop(object_parameter);
mov(isolate_parameter,
Operand(ExternalReference::isolate_address(isolate())));
Move(remembered_set_parameter, Smi::FromEnum(remembered_set_action));
Move(fp_mode_parameter, Smi::FromEnum(fp_mode));
Call(callable.code(), RelocInfo::CODE_TARGET);
RestoreRegisters(registers);
}
// Will clobber 4 registers: object, address, scratch, ip. The
// register 'object' contains a heap object pointer. The heap object
// tag is shifted away.
void MacroAssembler::RecordWrite(Register object, Register address,
Register value, LinkRegisterStatus lr_status,
SaveFPRegsMode fp_mode,
RememberedSetAction remembered_set_action,
SmiCheck smi_check) {
DCHECK(object != value);
if (emit_debug_code()) {
CmpP(value, MemOperand(address));
Check(eq, AbortReason::kWrongAddressOrValuePassedToRecordWrite);
}
if (remembered_set_action == OMIT_REMEMBERED_SET &&
!FLAG_incremental_marking) {
return;
}
// First, check if a write barrier is even needed. The tests below
// catch stores of smis and stores into the young generation.
Label done;
if (smi_check == INLINE_SMI_CHECK) {
JumpIfSmi(value, &done);
}
CheckPageFlag(value,
value, // Used as scratch.
MemoryChunk::kPointersToHereAreInterestingMask, eq, &done);
CheckPageFlag(object,
value, // Used as scratch.
MemoryChunk::kPointersFromHereAreInterestingMask, eq, &done);
// Record the actual write.
if (lr_status == kLRHasNotBeenSaved) {
push(r14);
}
CallRecordWriteStub(object, address, remembered_set_action, fp_mode);
if (lr_status == kLRHasNotBeenSaved) {
pop(r14);
}
bind(&done);
// Count number of write barriers in generated code.
isolate()->counters()->write_barriers_static()->Increment();
IncrementCounter(isolate()->counters()->write_barriers_dynamic(), 1, ip,
value);
// Clobber clobbered registers when running with the debug-code flag
// turned on to provoke errors.
if (emit_debug_code()) {
mov(address, Operand(bit_cast<intptr_t>(kZapValue + 12)));
mov(value, Operand(bit_cast<intptr_t>(kZapValue + 16)));
}
}
void TurboAssembler::PushCommonFrame(Register marker_reg) {
int fp_delta = 0;
CleanseP(r14);
if (marker_reg.is_valid()) {
Push(r14, fp, marker_reg);
fp_delta = 1;
} else {
Push(r14, fp);
fp_delta = 0;
}
la(fp, MemOperand(sp, fp_delta * kPointerSize));
}
void TurboAssembler::PopCommonFrame(Register marker_reg) {
if (marker_reg.is_valid()) {
Pop(r14, fp, marker_reg);
} else {
Pop(r14, fp);
}
}
void TurboAssembler::PushStandardFrame(Register function_reg) {
int fp_delta = 0;
CleanseP(r14);
if (function_reg.is_valid()) {
Push(r14, fp, cp, function_reg);
fp_delta = 2;
} else {
Push(r14, fp, cp);
fp_delta = 1;
}
la(fp, MemOperand(sp, fp_delta * kPointerSize));
}
void TurboAssembler::RestoreFrameStateForTailCall() {
// if (FLAG_enable_embedded_constant_pool) {
// LoadP(kConstantPoolRegister,
// MemOperand(fp, StandardFrameConstants::kConstantPoolOffset));
// set_constant_pool_available(false);
// }
DCHECK(!FLAG_enable_embedded_constant_pool);
LoadP(r14, MemOperand(fp, StandardFrameConstants::kCallerPCOffset));
LoadP(fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
}
// Push and pop all registers that can hold pointers.
void MacroAssembler::PushSafepointRegisters() {
// Safepoints expect a block of kNumSafepointRegisters values on the
// stack, so adjust the stack for unsaved registers.
const int num_unsaved = kNumSafepointRegisters - kNumSafepointSavedRegisters;
DCHECK_GE(num_unsaved, 0);
if (num_unsaved > 0) {
lay(sp, MemOperand(sp, -(num_unsaved * kPointerSize)));
}
MultiPush(kSafepointSavedRegisters);
}
void MacroAssembler::PopSafepointRegisters() {
const int num_unsaved = kNumSafepointRegisters - kNumSafepointSavedRegisters;
MultiPop(kSafepointSavedRegisters);
if (num_unsaved > 0) {
la(sp, MemOperand(sp, num_unsaved * kPointerSize));
}
}
int MacroAssembler::SafepointRegisterStackIndex(int reg_code) {
// The registers are pushed starting with the highest encoding,
// which means that lowest encodings are closest to the stack pointer.
RegList regs = kSafepointSavedRegisters;
int index = 0;
DCHECK(reg_code >= 0 && reg_code < kNumRegisters);
for (int16_t i = 0; i < reg_code; i++) {
if ((regs & (1 << i)) != 0) {
index++;
}
}
return index;
}
void TurboAssembler::CanonicalizeNaN(const DoubleRegister dst,
const DoubleRegister src) {
// Turn potential sNaN into qNaN
if (dst != src) ldr(dst, src);
lzdr(kDoubleRegZero);
sdbr(dst, kDoubleRegZero);
}
void TurboAssembler::ConvertIntToDouble(DoubleRegister dst, Register src) {
cdfbr(dst, src);
}
void TurboAssembler::ConvertUnsignedIntToDouble(DoubleRegister dst,
Register src) {
if (CpuFeatures::IsSupported(FLOATING_POINT_EXT)) {
cdlfbr(Condition(5), Condition(0), dst, src);
} else {
// zero-extend src
llgfr(src, src);
// convert to double
cdgbr(dst, src);
}
}
void TurboAssembler::ConvertIntToFloat(DoubleRegister dst, Register src) {
cefbr(Condition(4), dst, src);
}
void TurboAssembler::ConvertUnsignedIntToFloat(DoubleRegister dst,
Register src) {
celfbr(Condition(4), Condition(0), dst, src);
}
void TurboAssembler::ConvertInt64ToFloat(DoubleRegister double_dst,
Register src) {
cegbr(double_dst, src);
}
void TurboAssembler::ConvertInt64ToDouble(DoubleRegister double_dst,
Register src) {
cdgbr(double_dst, src);
}
void TurboAssembler::ConvertUnsignedInt64ToFloat(DoubleRegister double_dst,
Register src) {
celgbr(Condition(0), Condition(0), double_dst, src);
}
void TurboAssembler::ConvertUnsignedInt64ToDouble(DoubleRegister double_dst,
Register src) {
cdlgbr(Condition(0), Condition(0), double_dst, src);
}
void TurboAssembler::ConvertFloat32ToInt64(const Register dst,
const DoubleRegister double_input,
FPRoundingMode rounding_mode) {
Condition m = Condition(0);
switch (rounding_mode) {
case kRoundToZero:
m = Condition(5);
break;
case kRoundToNearest:
UNIMPLEMENTED();
break;
case kRoundToPlusInf:
m = Condition(6);
break;
case kRoundToMinusInf:
m = Condition(7);
break;
default:
UNIMPLEMENTED();
break;
}
cgebr(m, dst, double_input);
}
void TurboAssembler::ConvertDoubleToInt64(const Register dst,
const DoubleRegister double_input,
FPRoundingMode rounding_mode) {
Condition m = Condition(0);
switch (rounding_mode) {
case kRoundToZero:
m = Condition(5);
break;
case kRoundToNearest:
UNIMPLEMENTED();
break;
case kRoundToPlusInf:
m = Condition(6);
break;
case kRoundToMinusInf:
m = Condition(7);
break;
default:
UNIMPLEMENTED();
break;
}
cgdbr(m, dst, double_input);
}
void TurboAssembler::ConvertDoubleToInt32(const Register dst,
const DoubleRegister double_input,
FPRoundingMode rounding_mode) {
Condition m = Condition(0);
switch (rounding_mode) {
case kRoundToZero:
m = Condition(5);
break;
case kRoundToNearest:
m = Condition(4);
break;
case kRoundToPlusInf:
m = Condition(6);
break;
case kRoundToMinusInf:
m = Condition(7);
break;
default:
UNIMPLEMENTED();
break;
}
cfdbr(m, dst, double_input);
}
void TurboAssembler::ConvertFloat32ToInt32(const Register result,
const DoubleRegister double_input,
FPRoundingMode rounding_mode) {
Condition m = Condition(0);
switch (rounding_mode) {
case kRoundToZero:
m = Condition(5);
break;
case kRoundToNearest:
m = Condition(4);
break;
case kRoundToPlusInf:
m = Condition(6);
break;
case kRoundToMinusInf:
m = Condition(7);
break;
default:
UNIMPLEMENTED();
break;
}
cfebr(m, result, double_input);
}
void TurboAssembler::ConvertFloat32ToUnsignedInt32(
const Register result, const DoubleRegister double_input,
FPRoundingMode rounding_mode) {
Condition m = Condition(0);
switch (rounding_mode) {
case kRoundToZero:
m = Condition(5);
break;
case kRoundToNearest:
UNIMPLEMENTED();
break;
case kRoundToPlusInf:
m = Condition(6);
break;
case kRoundToMinusInf:
m = Condition(7);
break;
default:
UNIMPLEMENTED();
break;
}
clfebr(m, Condition(0), result, double_input);
}
void TurboAssembler::ConvertFloat32ToUnsignedInt64(
const Register result, const DoubleRegister double_input,
FPRoundingMode rounding_mode) {
Condition m = Condition(0);
switch (rounding_mode) {
case kRoundToZero:
m = Condition(5);
break;
case kRoundToNearest:
UNIMPLEMENTED();
break;
case kRoundToPlusInf:
m = Condition(6);
break;
case kRoundToMinusInf:
m = Condition(7);
break;
default:
UNIMPLEMENTED();
break;
}
clgebr(m, Condition(0), result, double_input);
}
void TurboAssembler::ConvertDoubleToUnsignedInt64(
const Register dst, const DoubleRegister double_input,
FPRoundingMode rounding_mode) {
Condition m = Condition(0);
switch (rounding_mode) {
case kRoundToZero:
m = Condition(5);
break;
case kRoundToNearest:
UNIMPLEMENTED();
break;
case kRoundToPlusInf:
m = Condition(6);
break;
case kRoundToMinusInf:
m = Condition(7);
break;
default:
UNIMPLEMENTED();
break;
}
clgdbr(m, Condition(0), dst, double_input);
}
void TurboAssembler::ConvertDoubleToUnsignedInt32(
const Register dst, const DoubleRegister double_input,
FPRoundingMode rounding_mode) {
Condition m = Condition(0);
switch (rounding_mode) {
case kRoundToZero:
m = Condition(5);
break;
case kRoundToNearest:
UNIMPLEMENTED();
break;
case kRoundToPlusInf:
m = Condition(6);
break;
case kRoundToMinusInf:
m = Condition(7);
break;
default:
UNIMPLEMENTED();
break;
}
clfdbr(m, Condition(0), dst, double_input);
}
#if !V8_TARGET_ARCH_S390X
void TurboAssembler::ShiftLeftPair(Register dst_low, Register dst_high,
Register src_low, Register src_high,
Register scratch, Register shift) {
LoadRR(r0, src_high);
LoadRR(r1, src_low);
sldl(r0, shift, Operand::Zero());
LoadRR(dst_high, r0);
LoadRR(dst_low, r1);
}
void TurboAssembler::ShiftLeftPair(Register dst_low, Register dst_high,
Register src_low, Register src_high,
uint32_t shift) {
LoadRR(r0, src_high);
LoadRR(r1, src_low);
sldl(r0, r0, Operand(shift));
LoadRR(dst_high, r0);
LoadRR(dst_low, r1);
}
void TurboAssembler::ShiftRightPair(Register dst_low, Register dst_high,
Register src_low, Register src_high,
Register scratch, Register shift) {
LoadRR(r0, src_high);
LoadRR(r1, src_low);
srdl(r0, shift, Operand::Zero());
LoadRR(dst_high, r0);
LoadRR(dst_low, r1);
}
void TurboAssembler::ShiftRightPair(Register dst_low, Register dst_high,
Register src_low, Register src_high,
uint32_t shift) {
LoadRR(r0, src_high);
LoadRR(r1, src_low);
srdl(r0, r0, Operand(shift));
LoadRR(dst_high, r0);
LoadRR(dst_low, r1);
}
void TurboAssembler::ShiftRightArithPair(Register dst_low, Register dst_high,
Register src_low, Register src_high,
Register scratch, Register shift) {
LoadRR(r0, src_high);
LoadRR(r1, src_low);
srda(r0, shift, Operand::Zero());
LoadRR(dst_high, r0);
LoadRR(dst_low, r1);
}
void TurboAssembler::ShiftRightArithPair(Register dst_low, Register dst_high,
Register src_low, Register src_high,
uint32_t shift) {
LoadRR(r0, src_high);
LoadRR(r1, src_low);
srda(r0, r0, Operand(shift));
LoadRR(dst_high, r0);
LoadRR(dst_low, r1);
}
#endif
void TurboAssembler::MovDoubleToInt64(Register dst, DoubleRegister src) {
lgdr(dst, src);
}
void TurboAssembler::MovInt64ToDouble(DoubleRegister dst, Register src) {
ldgr(dst, src);
}
void TurboAssembler::StubPrologue(StackFrame::Type type, Register base,
int prologue_offset) {
{
ConstantPoolUnavailableScope constant_pool_unavailable(this);
Load(r1, Operand(StackFrame::TypeToMarker(type)));
PushCommonFrame(r1);
}
}
void TurboAssembler::Prologue(Register base, int prologue_offset) {
DCHECK(base != no_reg);
PushStandardFrame(r3);
}
void TurboAssembler::EnterFrame(StackFrame::Type type,
bool load_constant_pool_pointer_reg) {
// We create a stack frame with:
// Return Addr <-- old sp
// Old FP <-- new fp
// CP
// type
// CodeObject <-- new sp
Load(ip, Operand(StackFrame::TypeToMarker(type)));
PushCommonFrame(ip);
if (type == StackFrame::INTERNAL) {
mov(r0, Operand(CodeObject()));
push(r0);
}
}
int TurboAssembler::LeaveFrame(StackFrame::Type type, int stack_adjustment) {
// Drop the execution stack down to the frame pointer and restore
// the caller frame pointer, return address and constant pool pointer.
LoadP(r14, MemOperand(fp, StandardFrameConstants::kCallerPCOffset));
if (is_int20(StandardFrameConstants::kCallerSPOffset + stack_adjustment)) {
lay(r1, MemOperand(fp, StandardFrameConstants::kCallerSPOffset +
stack_adjustment));
} else {
AddP(r1, fp,
Operand(StandardFrameConstants::kCallerSPOffset + stack_adjustment));
}
LoadP(fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
LoadRR(sp, r1);
int frame_ends = pc_offset();
return frame_ends;
}
// ExitFrame layout (probably wrongish.. needs updating)
//
// SP -> previousSP
// LK reserved
// code
// sp_on_exit (for debug?)
// oldSP->prev SP
// LK
// <parameters on stack>
// Prior to calling EnterExitFrame, we've got a bunch of parameters
// on the stack that we need to wrap a real frame around.. so first
// we reserve a slot for LK and push the previous SP which is captured
// in the fp register (r11)
// Then - we buy a new frame
// r14
// oldFP <- newFP
// SP
// Code
// Floats
// gaps
// Args
// ABIRes <- newSP
void MacroAssembler::EnterExitFrame(bool save_doubles, int stack_space,
StackFrame::Type frame_type) {
DCHECK(frame_type == StackFrame::EXIT ||
frame_type == StackFrame::BUILTIN_EXIT);
// Set up the frame structure on the stack.
DCHECK_EQ(2 * kPointerSize, ExitFrameConstants::kCallerSPDisplacement);
DCHECK_EQ(1 * kPointerSize, ExitFrameConstants::kCallerPCOffset);
DCHECK_EQ(0 * kPointerSize, ExitFrameConstants::kCallerFPOffset);
DCHECK_GT(stack_space, 0);
// This is an opportunity to build a frame to wrap
// all of the pushes that have happened inside of V8
// since we were called from C code
CleanseP(r14);
Load(r1, Operand(StackFrame::TypeToMarker(frame_type)));
PushCommonFrame(r1);
// Reserve room for saved entry sp and code object.
lay(sp, MemOperand(fp, -ExitFrameConstants::kFixedFrameSizeFromFp));
if (emit_debug_code()) {
StoreP(MemOperand(fp, ExitFrameConstants::kSPOffset), Operand::Zero(), r1);
}
mov(r1, Operand(CodeObject()));
StoreP(r1, MemOperand(fp, ExitFrameConstants::kCodeOffset));
// Save the frame pointer and the context in top.
mov(r1, Operand(ExternalReference(IsolateAddressId::kCEntryFPAddress,
isolate())));
StoreP(fp, MemOperand(r1));
mov(r1,
Operand(ExternalReference(IsolateAddressId::kContextAddress, isolate())));
StoreP(cp, MemOperand(r1));
// Optionally save all volatile double registers.
if (save_doubles) {
MultiPushDoubles(kCallerSavedDoubles);
// Note that d0 will be accessible at
// fp - ExitFrameConstants::kFrameSize -
// kNumCallerSavedDoubles * kDoubleSize,
// since the sp slot and code slot were pushed after the fp.
}
lay(sp, MemOperand(sp, -stack_space * kPointerSize));
// Allocate and align the frame preparing for calling the runtime
// function.
const int frame_alignment = TurboAssembler::ActivationFrameAlignment();
if (frame_alignment > 0) {
DCHECK_EQ(frame_alignment, 8);
ClearRightImm(sp, sp, Operand(3)); // equivalent to &= -8
}
lay(sp, MemOperand(sp, -kNumRequiredStackFrameSlots * kPointerSize));
StoreP(MemOperand(sp), Operand::Zero(), r0);
// Set the exit frame sp value to point just before the return address
// location.
lay(r1, MemOperand(sp, kStackFrameSPSlot * kPointerSize));
StoreP(r1, MemOperand(fp, ExitFrameConstants::kSPOffset));
}
int TurboAssembler::ActivationFrameAlignment() {
#if !defined(USE_SIMULATOR)
// Running on the real platform. Use the alignment as mandated by the local
// environment.
// Note: This will break if we ever start generating snapshots on one S390
// platform for another S390 platform with a different alignment.
return base::OS::ActivationFrameAlignment();
#else // Simulated
// If we are using the simulator then we should always align to the expected
// alignment. As the simulator is used to generate snapshots we do not know
// if the target platform will need alignment, so this is controlled from a
// flag.
return FLAG_sim_stack_alignment;
#endif
}
void MacroAssembler::LeaveExitFrame(bool save_doubles, Register argument_count,
bool argument_count_is_length) {
// Optionally restore all double registers.
if (save_doubles) {
// Calculate the stack location of the saved doubles and restore them.
const int kNumRegs = kNumCallerSavedDoubles;
lay(r5, MemOperand(fp, -(ExitFrameConstants::kFixedFrameSizeFromFp +
kNumRegs * kDoubleSize)));
MultiPopDoubles(kCallerSavedDoubles, r5);
}
// Clear top frame.
mov(ip, Operand(ExternalReference(IsolateAddressId::kCEntryFPAddress,
isolate())));
StoreP(MemOperand(ip), Operand(0, kRelocInfo_NONEPTR), r0);
// Restore current context from top and clear it in debug mode.
mov(ip,
Operand(ExternalReference(IsolateAddressId::kContextAddress, isolate())));
LoadP(cp, MemOperand(ip));
#ifdef DEBUG
mov(r1, Operand(Context::kInvalidContext));
mov(ip,
Operand(ExternalReference(IsolateAddressId::kContextAddress, isolate())));
StoreP(r1, MemOperand(ip));
#endif
// Tear down the exit frame, pop the arguments, and return.
LeaveFrame(StackFrame::EXIT);
if (argument_count.is_valid()) {
if (!argument_count_is_length) {
ShiftLeftP(argument_count, argument_count, Operand(kPointerSizeLog2));
}
la(sp, MemOperand(sp, argument_count));
}
}
void TurboAssembler::MovFromFloatResult(const DoubleRegister dst) {
Move(dst, d0);
}
void TurboAssembler::MovFromFloatParameter(const DoubleRegister dst) {
Move(dst, d0);
}
void TurboAssembler::PrepareForTailCall(const ParameterCount& callee_args_count,
Register caller_args_count_reg,
Register scratch0, Register scratch1) {
#if DEBUG
if (callee_args_count.is_reg()) {
DCHECK(!AreAliased(callee_args_count.reg(), caller_args_count_reg, scratch0,
scratch1));
} else {
DCHECK(!AreAliased(caller_args_count_reg, scratch0, scratch1));
}
#endif
// Calculate the end of destination area where we will put the arguments
// after we drop current frame. We AddP kPointerSize to count the receiver
// argument which is not included into formal parameters count.
Register dst_reg = scratch0;
ShiftLeftP(dst_reg, caller_args_count_reg, Operand(kPointerSizeLog2));
AddP(dst_reg, fp, dst_reg);
AddP(dst_reg, dst_reg,
Operand(StandardFrameConstants::kCallerSPOffset + kPointerSize));
Register src_reg = caller_args_count_reg;
// Calculate the end of source area. +kPointerSize is for the receiver.
if (callee_args_count.is_reg()) {
ShiftLeftP(src_reg, callee_args_count.reg(), Operand(kPointerSizeLog2));
AddP(src_reg, sp, src_reg);
AddP(src_reg, src_reg, Operand(kPointerSize));
} else {
mov(src_reg, Operand((callee_args_count.immediate() + 1) * kPointerSize));
AddP(src_reg, src_reg, sp);
}
if (FLAG_debug_code) {
CmpLogicalP(src_reg, dst_reg);
Check(lt, AbortReason::kStackAccessBelowStackPointer);
}
// Restore caller's frame pointer and return address now as they will be
// overwritten by the copying loop.
RestoreFrameStateForTailCall();
// Now copy callee arguments to the caller frame going backwards to avoid
// callee arguments corruption (source and destination areas could overlap).
// Both src_reg and dst_reg are pointing to the word after the one to copy,
// so they must be pre-decremented in the loop.
Register tmp_reg = scratch1;
Label loop;
if (callee_args_count.is_reg()) {
AddP(tmp_reg, callee_args_count.reg(), Operand(1)); // +1 for receiver
} else {
mov(tmp_reg, Operand(callee_args_count.immediate() + 1));
}
LoadRR(r1, tmp_reg);
bind(&loop);
LoadP(tmp_reg, MemOperand(src_reg, -kPointerSize));
StoreP(tmp_reg, MemOperand(dst_reg, -kPointerSize));
lay(src_reg, MemOperand(src_reg, -kPointerSize));
lay(dst_reg, MemOperand(dst_reg, -kPointerSize));
BranchOnCount(r1, &loop);
// Leave current frame.
LoadRR(sp, dst_reg);
}
void MacroAssembler::InvokePrologue(const ParameterCount& expected,
const ParameterCount& actual, Label* done,
bool* definitely_mismatches,
InvokeFlag flag) {
bool definitely_matches = false;
*definitely_mismatches = false;
Label regular_invoke;
// Check whether the expected and actual arguments count match. If not,
// setup registers according to contract with ArgumentsAdaptorTrampoline:
// r2: actual arguments count
// r3: function (passed through to callee)
// r4: expected arguments count
// The code below is made a lot easier because the calling code already sets
// up actual and expected registers according to the contract if values are
// passed in registers.
// ARM has some sanity checks as per below, considering add them for S390
// DCHECK(actual.is_immediate() || actual.reg() == r2);
// DCHECK(expected.is_immediate() || expected.reg() == r4);
if (expected.is_immediate()) {
DCHECK(actual.is_immediate());
mov(r2, Operand(actual.immediate()));
if (expected.immediate() == actual.immediate()) {
definitely_matches = true;
} else {
const int sentinel = SharedFunctionInfo::kDontAdaptArgumentsSentinel;
if (expected.immediate() == sentinel) {
// Don't worry about adapting arguments for builtins that
// don't want that done. Skip adaption code by making it look
// like we have a match between expected and actual number of
// arguments.
definitely_matches = true;
} else {
*definitely_mismatches = true;
mov(r4, Operand(expected.immediate()));
}
}
} else {
if (actual.is_immediate()) {
mov(r2, Operand(actual.immediate()));
CmpPH(expected.reg(), Operand(actual.immediate()));
beq(&regular_invoke);
} else {
CmpP(expected.reg(), actual.reg());
beq(&regular_invoke);
}
}
if (!definitely_matches) {
Handle<Code> adaptor = BUILTIN_CODE(isolate(), ArgumentsAdaptorTrampoline);
if (flag == CALL_FUNCTION) {
Call(adaptor);
if (!*definitely_mismatches) {
b(done);
}
} else {
Jump(adaptor, RelocInfo::CODE_TARGET);
}
bind(&regular_invoke);
}
}
void MacroAssembler::CheckDebugHook(Register fun, Register new_target,
const ParameterCount& expected,
const ParameterCount& actual) {
Label skip_hook;
ExternalReference debug_hook_avtive =
ExternalReference::debug_hook_on_function_call_address(isolate());
mov(r6, Operand(debug_hook_avtive));
LoadB(r6, MemOperand(r6));
CmpP(r6, Operand::Zero());
beq(&skip_hook);
{
FrameScope frame(this,
has_frame() ? StackFrame::NONE : StackFrame::INTERNAL);
if (expected.is_reg()) {
SmiTag(expected.reg());
Push(expected.reg());
}
if (actual.is_reg()) {
SmiTag(actual.reg());
Push(actual.reg());
}
if (new_target.is_valid()) {
Push(new_target);
}
Push(fun, fun);
CallRuntime(Runtime::kDebugOnFunctionCall);
Pop(fun);
if (new_target.is_valid()) {
Pop(new_target);
}
if (actual.is_reg()) {
Pop(actual.reg());
SmiUntag(actual.reg());
}
if (expected.is_reg()) {
Pop(expected.reg());
SmiUntag(expected.reg());
}
}
bind(&skip_hook);
}
void MacroAssembler::InvokeFunctionCode(Register function, Register new_target,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag) {
// You can't call a function without a valid frame.
DCHECK(flag == JUMP_FUNCTION || has_frame());
DCHECK(function == r3);
DCHECK_IMPLIES(new_target.is_valid(), new_target == r5);
// On function call, call into the debugger if necessary.
CheckDebugHook(function, new_target, expected, actual);
// Clear the new.target register if not given.
if (!new_target.is_valid()) {
LoadRoot(r5, Heap::kUndefinedValueRootIndex);
}
Label done;
bool definitely_mismatches = false;
InvokePrologue(expected, actual, &done, &definitely_mismatches, flag);
if (!definitely_mismatches) {
// We call indirectly through the code field in the function to
// allow recompilation to take effect without changing any of the
// call sites.
Register code = ip;
LoadP(code, FieldMemOperand(function, JSFunction::kCodeOffset));
AddP(code, code, Operand(Code::kHeaderSize - kHeapObjectTag));
if (flag == CALL_FUNCTION) {
CallJSEntry(code);
} else {
DCHECK(flag == JUMP_FUNCTION);
JumpToJSEntry(code);
}
// Continue here if InvokePrologue does handle the invocation due to
// mismatched parameter counts.
bind(&done);
}
}
void MacroAssembler::InvokeFunction(Register fun, Register new_target,
const ParameterCount& actual,
InvokeFlag flag) {
// You can't call a function without a valid frame.
DCHECK(flag == JUMP_FUNCTION || has_frame());
// Contract with called JS functions requires that function is passed in r3.
DCHECK(fun == r3);
Register expected_reg = r4;
Register temp_reg = r6;
LoadP(temp_reg, FieldMemOperand(r3, JSFunction::kSharedFunctionInfoOffset));
LoadP(cp, FieldMemOperand(r3, JSFunction::kContextOffset));
LoadW(expected_reg,
FieldMemOperand(temp_reg,
SharedFunctionInfo::kFormalParameterCountOffset));
ParameterCount expected(expected_reg);
InvokeFunctionCode(fun, new_target, expected, actual, flag);
}
void MacroAssembler::InvokeFunction(Register function,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag) {
// You can't call a function without a valid frame.
DCHECK(flag == JUMP_FUNCTION || has_frame());
// Contract with called JS functions requires that function is passed in r3.
DCHECK(function == r3);
// Get the function and setup the context.
LoadP(cp, FieldMemOperand(r3, JSFunction::kContextOffset));
InvokeFunctionCode(r3, no_reg, expected, actual, flag);
}
void MacroAssembler::InvokeFunction(Handle<JSFunction> function,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag) {
Move(r3, function);
InvokeFunction(r3, expected, actual, flag);
}
void MacroAssembler::MaybeDropFrames() {
// Check whether we need to drop frames to restart a function on the stack.
ExternalReference restart_fp =
ExternalReference::debug_restart_fp_address(isolate());
mov(r3, Operand(restart_fp));
LoadP(r3, MemOperand(r3));
CmpP(r3, Operand::Zero());
Jump(BUILTIN_CODE(isolate(), FrameDropperTrampoline), RelocInfo::CODE_TARGET,
ne);
}
void MacroAssembler::PushStackHandler() {
// Adjust this code if not the case.
STATIC_ASSERT(StackHandlerConstants::kSize == 2 * kPointerSize);
STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize);
// Link the current handler as the next handler.
mov(r7,
Operand(ExternalReference(IsolateAddressId::kHandlerAddress, isolate())));
// Buy the full stack frame for 5 slots.
lay(sp, MemOperand(sp, -StackHandlerConstants::kSize));
// Store padding.
mov(r0, Operand(Smi::kZero));
StoreP(r0, MemOperand(sp)); // Padding.
// Copy the old handler into the next handler slot.
mvc(MemOperand(sp, StackHandlerConstants::kNextOffset), MemOperand(r7),
kPointerSize);
// Set this new handler as the current one.
StoreP(sp, MemOperand(r7));
}
void MacroAssembler::PopStackHandler() {
STATIC_ASSERT(StackHandlerConstants::kSize == 2 * kPointerSize);
STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
// Pop the Next Handler into r3 and store it into Handler Address reference.
Pop(r3);
mov(ip,
Operand(ExternalReference(IsolateAddressId::kHandlerAddress, isolate())));
StoreP(r3, MemOperand(ip));
Drop(1); // Drop padding.
}
void MacroAssembler::CompareObjectType(Register object, Register map,
Register type_reg, InstanceType type) {
const Register temp = type_reg == no_reg ? r0 : type_reg;
LoadP(map, FieldMemOperand(object, HeapObject::kMapOffset));
CompareInstanceType(map, temp, type);
}
void MacroAssembler::CompareInstanceType(Register map, Register type_reg,
InstanceType type) {
STATIC_ASSERT(Map::kInstanceTypeOffset < 4096);
STATIC_ASSERT(LAST_TYPE <= 0xFFFF);
LoadHalfWordP(type_reg, FieldMemOperand(map, Map::kInstanceTypeOffset));
CmpP(type_reg, Operand(type));
}
void MacroAssembler::CompareRoot(Register obj, Heap::RootListIndex index) {
CmpP(obj, MemOperand(kRootRegister, index << kPointerSizeLog2));
}
void MacroAssembler::CallStub(CodeStub* stub, Condition cond) {
DCHECK(AllowThisStubCall(stub)); // Stub calls are not allowed in some stubs.
Call(stub->GetCode(), RelocInfo::CODE_TARGET, cond);
}
void TurboAssembler::CallStubDelayed(CodeStub* stub) {
DCHECK(AllowThisStubCall(stub)); // Stub calls are not allowed in some stubs.
call(stub);
}
void MacroAssembler::TailCallStub(CodeStub* stub, Condition cond) {
Jump(stub->GetCode(), RelocInfo::CODE_TARGET, cond);
}
bool TurboAssembler::AllowThisStubCall(CodeStub* stub) {
return has_frame_ || !stub->SometimesSetsUpAFrame();
}
void MacroAssembler::TryDoubleToInt32Exact(Register result,
DoubleRegister double_input,
Register scratch,
DoubleRegister double_scratch) {
Label done;
DCHECK(double_input != double_scratch);
ConvertDoubleToInt64(result, double_input);
TestIfInt32(result);
bne(&done);
// convert back and compare
cdfbr(double_scratch, result);
cdbr(double_scratch, double_input);
bind(&done);
}
void TurboAssembler::TruncateDoubleToIDelayed(Zone* zone, Register result,
DoubleRegister double_input) {
Label done;
TryInlineTruncateDoubleToI(result, double_input, &done);
// If we fell through then inline version didn't succeed - call stub instead.
push(r14);
// Put input on stack.
lay(sp, MemOperand(sp, -kDoubleSize));
StoreDouble(double_input, MemOperand(sp));
CallStubDelayed(new (zone) DoubleToIStub(nullptr, result));
la(sp, MemOperand(sp, kDoubleSize));
pop(r14);
bind(&done);
}
void TurboAssembler::TryInlineTruncateDoubleToI(Register result,
DoubleRegister double_input,
Label* done) {
ConvertDoubleToInt64(result, double_input);
// Test for overflow
TestIfInt32(result);
beq(done);
}
void TurboAssembler::CallRuntimeDelayed(Zone* zone, Runtime::FunctionId fid,
SaveFPRegsMode save_doubles) {
const Runtime::Function* f = Runtime::FunctionForId(fid);
mov(r2, Operand(f->nargs));
mov(r3, Operand(ExternalReference(f, isolate())));
CallStubDelayed(new (zone) CEntryStub(nullptr,
#if V8_TARGET_ARCH_S390X
f->result_size,
#else
1,
#endif
save_doubles));
}
void MacroAssembler::CallRuntime(const Runtime::Function* f, int num_arguments,
SaveFPRegsMode save_doubles) {
// All parameters are on the stack. r2 has the return value after call.
// If the expected number of arguments of the runtime function is
// constant, we check that the actual number of arguments match the
// expectation.
CHECK(f->nargs < 0 || f->nargs == num_arguments);
// TODO(1236192): Most runtime routines don't need the number of
// arguments passed in because it is constant. At some point we
// should remove this need and make the runtime routine entry code
// smarter.
mov(r2, Operand(num_arguments));
mov(r3, Operand(ExternalReference(f, isolate())));
CEntryStub stub(isolate(),
#if V8_TARGET_ARCH_S390X
f->result_size,
#else
1,
#endif
save_doubles);
CallStub(&stub);
}
void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid) {
const Runtime::Function* function = Runtime::FunctionForId(fid);
DCHECK_EQ(1, function->result_size);
if (function->nargs >= 0) {
mov(r2, Operand(function->nargs));
}
JumpToExternalReference(ExternalReference(fid, isolate()));
}
void MacroAssembler::JumpToExternalReference(const ExternalReference& builtin,
bool builtin_exit_frame) {
mov(r3, Operand(builtin));
CEntryStub stub(isolate(), 1, kDontSaveFPRegs, kArgvOnStack,
builtin_exit_frame);
Jump(stub.GetCode(), RelocInfo::CODE_TARGET);
}
void MacroAssembler::IncrementCounter(StatsCounter* counter, int value,
Register scratch1, Register scratch2) {
DCHECK(value > 0 && is_int8(value));
if (FLAG_native_code_counters && counter->Enabled()) {
mov(scratch1, Operand(ExternalReference(counter)));
// @TODO(john.yan): can be optimized by asi()
LoadW(scratch2, MemOperand(scratch1));
AddP(scratch2, Operand(value));
StoreW(scratch2, MemOperand(scratch1));
}
}
void MacroAssembler::DecrementCounter(StatsCounter* counter, int value,
Register scratch1, Register scratch2) {
DCHECK(value > 0 && is_int8(value));
if (FLAG_native_code_counters && counter->Enabled()) {
mov(scratch1, Operand(ExternalReference(counter)));
// @TODO(john.yan): can be optimized by asi()
LoadW(scratch2, MemOperand(scratch1));
AddP(scratch2, Operand(-value));
StoreW(scratch2, MemOperand(scratch1));
}
}
void TurboAssembler::Assert(Condition cond, AbortReason reason, CRegister cr) {
if (emit_debug_code()) Check(cond, reason, cr);
}
void TurboAssembler::Check(Condition cond, AbortReason reason, CRegister cr) {
Label L;
b(cond, &L);
Abort(reason);
// will not return here
bind(&L);
}
void TurboAssembler::Abort(AbortReason reason) {
Label abort_start;
bind(&abort_start);
#ifdef DEBUG
const char* msg = GetAbortReason(reason);
if (msg != nullptr) {
RecordComment("Abort message: ");
RecordComment(msg);
}
if (FLAG_trap_on_abort) {
stop(msg);
return;
}
#endif
LoadSmiLiteral(r3, Smi::FromInt(static_cast<int>(reason)));
// Disable stub call restrictions to always allow calls to abort.
if (!has_frame_) {
// We don't actually want to generate a pile of code for this, so just
// claim there is a stack frame, without generating one.
FrameScope scope(this, StackFrame::NONE);
Call(BUILTIN_CODE(isolate(), Abort), RelocInfo::CODE_TARGET);
} else {
Call(BUILTIN_CODE(isolate(), Abort), RelocInfo::CODE_TARGET);
}
// will not return here
}
void MacroAssembler::LoadNativeContextSlot(int index, Register dst) {
LoadP(dst, NativeContextMemOperand());
LoadP(dst, ContextMemOperand(dst, index));
}
void MacroAssembler::UntagAndJumpIfSmi(Register dst, Register src,
Label* smi_case) {
STATIC_ASSERT(kSmiTag == 0);
STATIC_ASSERT(kSmiTagSize == 1);
// this won't work if src == dst
DCHECK(src.code() != dst.code());
SmiUntag(dst, src);
TestIfSmi(src);
beq(smi_case);
}
void MacroAssembler::JumpIfEitherSmi(Register reg1, Register reg2,
Label* on_either_smi) {
STATIC_ASSERT(kSmiTag == 0);
JumpIfSmi(reg1, on_either_smi);
JumpIfSmi(reg2, on_either_smi);
}
void MacroAssembler::AssertNotSmi(Register object) {
if (emit_debug_code()) {
STATIC_ASSERT(kSmiTag == 0);
TestIfSmi(object);
Check(ne, AbortReason::kOperandIsASmi, cr0);
}
}
void MacroAssembler::AssertSmi(Register object) {
if (emit_debug_code()) {
STATIC_ASSERT(kSmiTag == 0);
TestIfSmi(object);
Check(eq, AbortReason::kOperandIsNotASmi, cr0);
}
}
void MacroAssembler::AssertFixedArray(Register object) {
if (emit_debug_code()) {
STATIC_ASSERT(kSmiTag == 0);
TestIfSmi(object);
Check(ne, AbortReason::kOperandIsASmiAndNotAFixedArray, cr0);
push(object);
CompareObjectType(object, object, object, FIXED_ARRAY_TYPE);
pop(object);
Check(eq, AbortReason::kOperandIsNotAFixedArray);
}
}
void MacroAssembler::AssertFunction(Register object) {
if (emit_debug_code()) {
STATIC_ASSERT(kSmiTag == 0);
TestIfSmi(object);
Check(ne, AbortReason::kOperandIsASmiAndNotAFunction, cr0);
push(object);
CompareObjectType(object, object, object, JS_FUNCTION_TYPE);
pop(object);
Check(eq, AbortReason::kOperandIsNotAFunction);
}
}
void MacroAssembler::AssertBoundFunction(Register object) {
if (emit_debug_code()) {
STATIC_ASSERT(kSmiTag == 0);
TestIfSmi(object);
Check(ne, AbortReason::kOperandIsASmiAndNotABoundFunction, cr0);
push(object);
CompareObjectType(object, object, object, JS_BOUND_FUNCTION_TYPE);
pop(object);
Check(eq, AbortReason::kOperandIsNotABoundFunction);
}
}
void MacroAssembler::AssertGeneratorObject(Register object) {
if (!emit_debug_code()) return;
TestIfSmi(object);
Check(ne, AbortReason::kOperandIsASmiAndNotAGeneratorObject, cr0);
// Load map
Register map = object;
push(object);
LoadP(map, FieldMemOperand(object, HeapObject::kMapOffset));
// Check if JSGeneratorObject
Label do_check;
Register instance_type = object;
CompareInstanceType(map, instance_type, JS_GENERATOR_OBJECT_TYPE);
beq(&do_check);
// Check if JSAsyncGeneratorObject (See MacroAssembler::CompareInstanceType)
CmpP(instance_type, Operand(JS_ASYNC_GENERATOR_OBJECT_TYPE));
bind(&do_check);
// Restore generator object to register and perform assertion
pop(object);
Check(eq, AbortReason::kOperandIsNotAGeneratorObject);
}
void MacroAssembler::AssertUndefinedOrAllocationSite(Register object,
Register scratch) {
if (emit_debug_code()) {
Label done_checking;
AssertNotSmi(object);
CompareRoot(object, Heap::kUndefinedValueRootIndex);
beq(&done_checking, Label::kNear);
LoadP(scratch, FieldMemOperand(object, HeapObject::kMapOffset));
CompareRoot(scratch, Heap::kAllocationSiteMapRootIndex);
Assert(eq, AbortReason::kExpectedUndefinedOrCell);
bind(&done_checking);
}
}
static const int kRegisterPassedArguments = 5;
int TurboAssembler::CalculateStackPassedWords(int num_reg_arguments,
int num_double_arguments) {
int stack_passed_words = 0;
if (num_double_arguments > DoubleRegister::kNumRegisters) {
stack_passed_words +=
2 * (num_double_arguments - DoubleRegister::kNumRegisters);
}
// Up to five simple arguments are passed in registers r2..r6
if (num_reg_arguments > kRegisterPassedArguments) {
stack_passed_words += num_reg_arguments - kRegisterPassedArguments;
}
return stack_passed_words;
}
void TurboAssembler::PrepareCallCFunction(int num_reg_arguments,
int num_double_arguments,
Register scratch) {
int frame_alignment = ActivationFrameAlignment();
int stack_passed_arguments =
CalculateStackPassedWords(num_reg_arguments, num_double_arguments);
int stack_space = kNumRequiredStackFrameSlots;
if (frame_alignment > kPointerSize) {
// Make stack end at alignment and make room for stack arguments
// -- preserving original value of sp.
LoadRR(scratch, sp);
lay(sp, MemOperand(sp, -(stack_passed_arguments + 1) * kPointerSize));
DCHECK(base::bits::IsPowerOfTwo(frame_alignment));
ClearRightImm(sp, sp, Operand(WhichPowerOf2(frame_alignment)));
StoreP(scratch, MemOperand(sp, (stack_passed_arguments)*kPointerSize));
} else {
stack_space += stack_passed_arguments;
}
lay(sp, MemOperand(sp, -(stack_space)*kPointerSize));
}
void TurboAssembler::PrepareCallCFunction(int num_reg_arguments,
Register scratch) {
PrepareCallCFunction(num_reg_arguments, 0, scratch);
}
void TurboAssembler::MovToFloatParameter(DoubleRegister src) { Move(d0, src); }
void TurboAssembler::MovToFloatResult(DoubleRegister src) { Move(d0, src); }
void TurboAssembler::MovToFloatParameters(DoubleRegister src1,
DoubleRegister src2) {
if (src2 == d0) {
DCHECK(src1 != d2);
Move(d2, src2);
Move(d0, src1);
} else {
Move(d0, src1);
Move(d2, src2);
}
}
void TurboAssembler::CallCFunction(ExternalReference function,
int num_reg_arguments,
int num_double_arguments) {
mov(ip, Operand(function));
CallCFunctionHelper(ip, num_reg_arguments, num_double_arguments);
}
void TurboAssembler::CallCFunction(Register function, int num_reg_arguments,
int num_double_arguments) {
CallCFunctionHelper(function, num_reg_arguments, num_double_arguments);
}
void TurboAssembler::CallCFunction(ExternalReference function,
int num_arguments) {
CallCFunction(function, num_arguments, 0);
}
void TurboAssembler::CallCFunction(Register function, int num_arguments) {
CallCFunction(function, num_arguments, 0);
}
void TurboAssembler::CallCFunctionHelper(Register function,
int num_reg_arguments,
int num_double_arguments) {
DCHECK_LE(num_reg_arguments + num_double_arguments, kMaxCParameters);
DCHECK(has_frame());
// Just call directly. The function called cannot cause a GC, or
// allow preemption, so the return address in the link register
// stays correct.
Register dest = function;
if (ABI_CALL_VIA_IP) {
Move(ip, function);
dest = ip;
}
Call(dest);
int stack_passed_arguments =
CalculateStackPassedWords(num_reg_arguments, num_double_arguments);
int stack_space = kNumRequiredStackFrameSlots + stack_passed_arguments;
if (ActivationFrameAlignment() > kPointerSize) {
// Load the original stack pointer (pre-alignment) from the stack
LoadP(sp, MemOperand(sp, stack_space * kPointerSize));
} else {
la(sp, MemOperand(sp, stack_space * kPointerSize));
}
}
void TurboAssembler::CheckPageFlag(
Register object,
Register scratch, // scratch may be same register as object
int mask, Condition cc, Label* condition_met) {
DCHECK(cc == ne || cc == eq);
ClearRightImm(scratch, object, Operand(kPageSizeBits));
if (base::bits::IsPowerOfTwo(mask)) {
// If it's a power of two, we can use Test-Under-Mask Memory-Imm form
// which allows testing of a single byte in memory.
int32_t byte_offset = 4;
uint32_t shifted_mask = mask;
// Determine the byte offset to be tested
if (mask <= 0x80) {
byte_offset = kPointerSize - 1;
} else if (mask < 0x8000) {
byte_offset = kPointerSize - 2;
shifted_mask = mask >> 8;
} else if (mask < 0x800000) {
byte_offset = kPointerSize - 3;
shifted_mask = mask >> 16;
} else {
byte_offset = kPointerSize - 4;
shifted_mask = mask >> 24;
}
#if V8_TARGET_LITTLE_ENDIAN
// Reverse the byte_offset if emulating on little endian platform
byte_offset = kPointerSize - byte_offset - 1;
#endif
tm(MemOperand(scratch, MemoryChunk::kFlagsOffset + byte_offset),
Operand(shifted_mask));
} else {
LoadP(scratch, MemOperand(scratch, MemoryChunk::kFlagsOffset));
AndP(r0, scratch, Operand(mask));
}
// Should be okay to remove rc
if (cc == ne) {
bne(condition_met);
}
if (cc == eq) {
beq(condition_met);
}
}
////////////////////////////////////////////////////////////////////////////////
//
// New MacroAssembler Interfaces added for S390
//
////////////////////////////////////////////////////////////////////////////////
// Primarily used for loading constants
// This should really move to be in macro-assembler as it
// is really a pseudo instruction
// Some usages of this intend for a FIXED_SEQUENCE to be used
// @TODO - break this dependency so we can optimize mov() in general
// and only use the generic version when we require a fixed sequence
void MacroAssembler::LoadRepresentation(Register dst, const MemOperand& mem,
Representation r, Register scratch) {
DCHECK(!r.IsDouble());
if (r.IsInteger8()) {
LoadB(dst, mem);
} else if (r.IsUInteger8()) {
LoadlB(dst, mem);
} else if (r.IsInteger16()) {
LoadHalfWordP(dst, mem, scratch);
} else if (r.IsUInteger16()) {
LoadHalfWordP(dst, mem, scratch);
#if V8_TARGET_ARCH_S390X
} else if (r.IsInteger32()) {
LoadW(dst, mem, scratch);
#endif
} else {
LoadP(dst, mem, scratch);
}
}
void MacroAssembler::StoreRepresentation(Register src, const MemOperand& mem,
Representation r, Register scratch) {
DCHECK(!r.IsDouble());
if (r.IsInteger8() || r.IsUInteger8()) {
StoreByte(src, mem, scratch);
} else if (r.IsInteger16() || r.IsUInteger16()) {
StoreHalfWord(src, mem, scratch);
#if V8_TARGET_ARCH_S390X
} else if (r.IsInteger32()) {
StoreW(src, mem, scratch);
#endif
} else {
if (r.IsHeapObject()) {
AssertNotSmi(src);
} else if (r.IsSmi()) {
AssertSmi(src);
}
StoreP(src, mem, scratch);
}
}
Register GetRegisterThatIsNotOneOf(Register reg1, Register reg2, Register reg3,
Register reg4, Register reg5,
Register reg6) {
RegList regs = 0;
if (reg1.is_valid()) regs |= reg1.bit();
if (reg2.is_valid()) regs |= reg2.bit();
if (reg3.is_valid()) regs |= reg3.bit();
if (reg4.is_valid()) regs |= reg4.bit();
if (reg5.is_valid()) regs |= reg5.bit();
if (reg6.is_valid()) regs |= reg6.bit();
const RegisterConfiguration* config = RegisterConfiguration::Default();
for (int i = 0; i < config->num_allocatable_general_registers(); ++i) {
int code = config->GetAllocatableGeneralCode(i);
Register candidate = Register::from_code(code);
if (regs & candidate.bit()) continue;
return candidate;
}
UNREACHABLE();
}
void TurboAssembler::mov(Register dst, const Operand& src) {
#if V8_TARGET_ARCH_S390X
int64_t value;
#else
int value;
#endif
if (src.is_heap_object_request()) {
RequestHeapObject(src.heap_object_request());
value = 0;
} else {
value = src.immediate();
}
if (src.rmode() != kRelocInfo_NONEPTR) {
// some form of relocation needed
RecordRelocInfo(src.rmode(), value);
}
#if V8_TARGET_ARCH_S390X
int32_t hi_32 = static_cast<int64_t>(value) >> 32;
int32_t lo_32 = static_cast<int32_t>(value);
iihf(dst, Operand(hi_32));
iilf(dst, Operand(lo_32));
#else
iilf(dst, Operand(value));
#endif
}
void TurboAssembler::Mul32(Register dst, const MemOperand& src1) {
if (is_uint12(src1.offset())) {
ms(dst, src1);
} else if (is_int20(src1.offset())) {
msy(dst, src1);
} else {
UNIMPLEMENTED();
}
}
void TurboAssembler::Mul32(Register dst, Register src1) { msr(dst, src1); }
void TurboAssembler::Mul32(Register dst, const Operand& src1) {
msfi(dst, src1);
}
#define Generate_MulHigh32(instr) \
{ \
lgfr(dst, src1); \
instr(dst, src2); \
srlg(dst, dst, Operand(32)); \
}
void TurboAssembler::MulHigh32(Register dst, Register src1,
const MemOperand& src2) {
Generate_MulHigh32(msgf);
}
void TurboAssembler::MulHigh32(Register dst, Register src1, Register src2) {
if (dst == src2) {
std::swap(src1, src2);
}
Generate_MulHigh32(msgfr);
}
void TurboAssembler::MulHigh32(Register dst, Register src1,
const Operand& src2) {
Generate_MulHigh32(msgfi);
}
#undef Generate_MulHigh32
#define Generate_MulHighU32(instr) \
{ \
lr(r1, src1); \
instr(r0, src2); \
LoadlW(dst, r0); \
}
void TurboAssembler::MulHighU32(Register dst, Register src1,
const MemOperand& src2) {
Generate_MulHighU32(ml);
}
void TurboAssembler::MulHighU32(Register dst, Register src1, Register src2) {
Generate_MulHighU32(mlr);
}
void TurboAssembler::MulHighU32(Register dst, Register src1,
const Operand& src2) {
USE(dst);
USE(src1);
USE(src2);
UNREACHABLE();
}
#undef Generate_MulHighU32
#define Generate_Mul32WithOverflowIfCCUnequal(instr) \
{ \
lgfr(dst, src1); \
instr(dst, src2); \
cgfr(dst, dst); \
}
void TurboAssembler::Mul32WithOverflowIfCCUnequal(Register dst, Register src1,
const MemOperand& src2) {
Register result = dst;
if (src2.rx() == dst || src2.rb() == dst) dst = r0;
Generate_Mul32WithOverflowIfCCUnequal(msgf);
if (result != dst) llgfr(result, dst);
}
void TurboAssembler::Mul32WithOverflowIfCCUnequal(Register dst, Register src1,
Register src2) {
if (dst == src2) {
std::swap(src1, src2);
}
Generate_Mul32WithOverflowIfCCUnequal(msgfr);
}
void TurboAssembler::Mul32WithOverflowIfCCUnequal(Register dst, Register src1,
const Operand& src2) {
Generate_Mul32WithOverflowIfCCUnequal(msgfi);
}
#undef Generate_Mul32WithOverflowIfCCUnequal
void TurboAssembler::Mul64(Register dst, const MemOperand& src1) {
if (is_int20(src1.offset())) {
msg(dst, src1);
} else {
UNIMPLEMENTED();
}
}
void TurboAssembler::Mul64(Register dst, Register src1) { msgr(dst, src1); }
void TurboAssembler::Mul64(Register dst, const Operand& src1) {
msgfi(dst, src1);
}
void TurboAssembler::Mul(Register dst, Register src1, Register src2) {
if (CpuFeatures::IsSupported(MISC_INSTR_EXT2)) {
MulPWithCondition(dst, src1, src2);
} else {
if (dst == src2) {
MulP(dst, src1);
} else if (dst == src1) {
MulP(dst, src2);
} else {
Move(dst, src1);
MulP(dst, src2);
}
}
}
void TurboAssembler::DivP(Register dividend, Register divider) {
// have to make sure the src and dst are reg pairs
DCHECK_EQ(dividend.code() % 2, 0);
#if V8_TARGET_ARCH_S390X
dsgr(dividend, divider);
#else
dr(dividend, divider);
#endif
}
#define Generate_Div32(instr) \
{ \
lgfr(r1, src1); \
instr(r0, src2); \
LoadlW(dst, r1); \
}
void TurboAssembler::Div32(Register dst, Register src1,
const MemOperand& src2) {
Generate_Div32(dsgf);
}
void TurboAssembler::Div32(Register dst, Register src1, Register src2) {
Generate_Div32(dsgfr);
}
#undef Generate_Div32
#define Generate_DivU32(instr) \
{ \
lr(r0, src1); \
srdl(r0, Operand(32)); \
instr(r0, src2); \
LoadlW(dst, r1); \
}
void TurboAssembler::DivU32(Register dst, Register src1,
const MemOperand& src2) {
Generate_DivU32(dl);
}
void TurboAssembler::DivU32(Register dst, Register src1, Register src2) {
Generate_DivU32(dlr);
}
#undef Generate_DivU32
#define Generate_Div64(instr) \
{ \
lgr(r1, src1); \
instr(r0, src2); \
lgr(dst, r1); \
}
void TurboAssembler::Div64(Register dst, Register src1,
const MemOperand& src2) {
Generate_Div64(dsg);
}
void TurboAssembler::Div64(Register dst, Register src1, Register src2) {
Generate_Div64(dsgr);
}
#undef Generate_Div64
#define Generate_DivU64(instr) \
{ \
lgr(r1, src1); \
lghi(r0, Operand::Zero()); \
instr(r0, src2); \
lgr(dst, r1); \
}
void TurboAssembler::DivU64(Register dst, Register src1,
const MemOperand& src2) {
Generate_DivU64(dlg);
}
void TurboAssembler::DivU64(Register dst, Register src1, Register src2) {
Generate_DivU64(dlgr);
}
#undef Generate_DivU64
#define Generate_Mod32(instr) \
{ \
lgfr(r1, src1); \
instr(r0, src2); \
LoadlW(dst, r0); \
}
void TurboAssembler::Mod32(Register dst, Register src1,
const MemOperand& src2) {
Generate_Mod32(dsgf);
}
void TurboAssembler::Mod32(Register dst, Register src1, Register src2) {
Generate_Mod32(dsgfr);
}
#undef Generate_Mod32
#define Generate_ModU32(instr) \
{ \
lr(r0, src1); \
srdl(r0, Operand(32)); \
instr(r0, src2); \
LoadlW(dst, r0); \
}
void TurboAssembler::ModU32(Register dst, Register src1,
const MemOperand& src2) {
Generate_ModU32(dl);
}
void TurboAssembler::ModU32(Register dst, Register src1, Register src2) {
Generate_ModU32(dlr);
}
#undef Generate_ModU32
#define Generate_Mod64(instr) \
{ \
lgr(r1, src1); \
instr(r0, src2); \
lgr(dst, r0); \
}
void TurboAssembler::Mod64(Register dst, Register src1,
const MemOperand& src2) {
Generate_Mod64(dsg);
}
void TurboAssembler::Mod64(Register dst, Register src1, Register src2) {
Generate_Mod64(dsgr);
}
#undef Generate_Mod64
#define Generate_ModU64(instr) \
{ \
lgr(r1, src1); \
lghi(r0, Operand::Zero()); \
instr(r0, src2); \
lgr(dst, r0); \
}
void TurboAssembler::ModU64(Register dst, Register src1,
const MemOperand& src2) {
Generate_ModU64(dlg);
}
void TurboAssembler::ModU64(Register dst, Register src1, Register src2) {
Generate_ModU64(dlgr);
}
#undef Generate_ModU64
void TurboAssembler::MulP(Register dst, const Operand& opnd) {
#if V8_TARGET_ARCH_S390X
msgfi(dst, opnd);
#else
msfi(dst, opnd);
#endif
}
void TurboAssembler::MulP(Register dst, Register src) {
#if V8_TARGET_ARCH_S390X
msgr(dst, src);
#else
msr(dst, src);
#endif
}
void TurboAssembler::MulPWithCondition(Register dst, Register src1,
Register src2) {
CHECK(CpuFeatures::IsSupported(MISC_INSTR_EXT2));
#if V8_TARGET_ARCH_S390X
msgrkc(dst, src1, src2);
#else
msrkc(dst, src1, src2);
#endif
}
void TurboAssembler::MulP(Register dst, const MemOperand& opnd) {
#if V8_TARGET_ARCH_S390X
if (is_uint16(opnd.offset())) {
ms(dst, opnd);
} else if (is_int20(opnd.offset())) {
msy(dst, opnd);
} else {
UNIMPLEMENTED();
}
#else
if (is_int20(opnd.offset())) {
msg(dst, opnd);
} else {
UNIMPLEMENTED();
}
#endif
}
void TurboAssembler::Sqrt(DoubleRegister result, DoubleRegister input) {
sqdbr(result, input);
}
void TurboAssembler::Sqrt(DoubleRegister result, const MemOperand& input) {
if (is_uint12(input.offset())) {
sqdb(result, input);
} else {
ldy(result, input);
sqdbr(result, result);
}
}
//----------------------------------------------------------------------------
// Add Instructions
//----------------------------------------------------------------------------
// Add 32-bit (Register dst = Register dst + Immediate opnd)
void TurboAssembler::Add32(Register dst, const Operand& opnd) {
if (is_int16(opnd.immediate()))
ahi(dst, opnd);
else
afi(dst, opnd);
}
// Add 32-bit (Register dst = Register dst + Immediate opnd)
void TurboAssembler::Add32_RI(Register dst, const Operand& opnd) {
// Just a wrapper for above
Add32(dst, opnd);
}
// Add Pointer Size (Register dst = Register dst + Immediate opnd)
void TurboAssembler::AddP(Register dst, const Operand& opnd) {
#if V8_TARGET_ARCH_S390X
if (is_int16(opnd.immediate()))
aghi(dst, opnd);
else
agfi(dst, opnd);
#else
Add32(dst, opnd);
#endif
}
// Add 32-bit (Register dst = Register src + Immediate opnd)
void TurboAssembler::Add32(Register dst, Register src, const Operand& opnd) {
if (dst != src) {
if (CpuFeatures::IsSupported(DISTINCT_OPS) && is_int16(opnd.immediate())) {
ahik(dst, src, opnd);
return;
}
lr(dst, src);
}
Add32(dst, opnd);
}
// Add 32-bit (Register dst = Register src + Immediate opnd)
void TurboAssembler::Add32_RRI(Register dst, Register src,
const Operand& opnd) {
// Just a wrapper for above
Add32(dst, src, opnd);
}
// Add Pointer Size (Register dst = Register src + Immediate opnd)
void TurboAssembler::AddP(Register dst, Register src, const Operand& opnd) {
if (dst != src) {
if (CpuFeatures::IsSupported(DISTINCT_OPS) && is_int16(opnd.immediate())) {
AddPImm_RRI(dst, src, opnd);
return;
}
LoadRR(dst, src);
}
AddP(dst, opnd);
}
// Add 32-bit (Register dst = Register dst + Register src)
void TurboAssembler::Add32(Register dst, Register src) { ar(dst, src); }
// Add Pointer Size (Register dst = Register dst + Register src)
void TurboAssembler::AddP(Register dst, Register src) { AddRR(dst, src); }
// Add Pointer Size with src extension
// (Register dst(ptr) = Register dst (ptr) + Register src (32 | 32->64))
// src is treated as a 32-bit signed integer, which is sign extended to
// 64-bit if necessary.
void TurboAssembler::AddP_ExtendSrc(Register dst, Register src) {
#if V8_TARGET_ARCH_S390X
agfr(dst, src);
#else
ar(dst, src);
#endif
}
// Add 32-bit (Register dst = Register src1 + Register src2)
void TurboAssembler::Add32(Register dst, Register src1, Register src2) {
if (dst != src1 && dst != src2) {
// We prefer to generate AR/AGR, over the non clobbering ARK/AGRK
// as AR is a smaller instruction
if (CpuFeatures::IsSupported(DISTINCT_OPS)) {
ark(dst, src1, src2);
return;
} else {
lr(dst, src1);
}
} else if (dst == src2) {
src2 = src1;
}
ar(dst, src2);
}
// Add Pointer Size (Register dst = Register src1 + Register src2)
void TurboAssembler::AddP(Register dst, Register src1, Register src2) {
if (dst != src1 && dst != src2) {
// We prefer to generate AR/AGR, over the non clobbering ARK/AGRK
// as AR is a smaller instruction
if (CpuFeatures::IsSupported(DISTINCT_OPS)) {
AddP_RRR(dst, src1, src2);
return;
} else {
LoadRR(dst, src1);
}
} else if (dst == src2) {
src2 = src1;
}
AddRR(dst, src2);
}
// Add Pointer Size with src extension
// (Register dst (ptr) = Register dst (ptr) + Register src1 (ptr) +
// Register src2 (32 | 32->64))
// src is treated as a 32-bit signed integer, which is sign extended to
// 64-bit if necessary.
void TurboAssembler::AddP_ExtendSrc(Register dst, Register src1,
Register src2) {
#if V8_TARGET_ARCH_S390X
if (dst == src2) {
// The source we need to sign extend is the same as result.
lgfr(dst, src2);
agr(dst, src1);
} else {
if (dst != src1) LoadRR(dst, src1);
agfr(dst, src2);
}
#else
AddP(dst, src1, src2);
#endif
}
// Add 32-bit (Register-Memory)
void TurboAssembler::Add32(Register dst, const MemOperand& opnd) {
DCHECK(is_int20(opnd.offset()));
if (is_uint12(opnd.offset()))
a(dst, opnd);
else
ay(dst, opnd);
}
// Add Pointer Size (Register-Memory)
void TurboAssembler::AddP(Register dst, const MemOperand& opnd) {
#if V8_TARGET_ARCH_S390X
DCHECK(is_int20(opnd.offset()));
ag(dst, opnd);
#else
Add32(dst, opnd);
#endif
}
// Add Pointer Size with src extension
// (Register dst (ptr) = Register dst (ptr) + Mem opnd (32 | 32->64))
// src is treated as a 32-bit signed integer, which is sign extended to
// 64-bit if necessary.
void TurboAssembler::AddP_ExtendSrc(Register dst, const MemOperand& opnd) {
#if V8_TARGET_ARCH_S390X
DCHECK(is_int20(opnd.offset()));
agf(dst, opnd);
#else
Add32(dst, opnd);
#endif
}
// Add 32-bit (Memory - Immediate)
void TurboAssembler::Add32(const MemOperand& opnd, const Operand& imm) {
DCHECK(is_int8(imm.immediate()));
DCHECK(is_int20(opnd.offset()));
DCHECK(CpuFeatures::IsSupported(GENERAL_INSTR_EXT));
asi(opnd, imm);
}
// Add Pointer-sized (Memory - Immediate)
void TurboAssembler::AddP(const MemOperand& opnd, const Operand& imm) {
DCHECK(is_int8(imm.immediate()));
DCHECK(is_int20(opnd.offset()));
DCHECK(CpuFeatures::IsSupported(GENERAL_INSTR_EXT));
#if V8_TARGET_ARCH_S390X
agsi(opnd, imm);
#else
asi(opnd, imm);
#endif
}
//----------------------------------------------------------------------------
// Add Logical Instructions
//----------------------------------------------------------------------------
// Add Logical With Carry 32-bit (Register dst = Register src1 + Register src2)
void TurboAssembler::AddLogicalWithCarry32(Register dst, Register src1,
Register src2) {
if (dst != src2 && dst != src1) {
lr(dst, src1);
alcr(dst, src2);
} else if (dst != src2) {
// dst == src1
DCHECK(dst == src1);
alcr(dst, src2);
} else {
// dst == src2
DCHECK(dst == src2);
alcr(dst, src1);
}
}
// Add Logical 32-bit (Register dst = Register src1 + Register src2)
void TurboAssembler::AddLogical32(Register dst, Register src1, Register src2) {
if (dst != src2 && dst != src1) {
lr(dst, src1);
alr(dst, src2);
} else if (dst != src2) {
// dst == src1
DCHECK(dst == src1);
alr(dst, src2);
} else {
// dst == src2
DCHECK(dst == src2);
alr(dst, src1);
}
}
// Add Logical 32-bit (Register dst = Register dst + Immediate opnd)
void TurboAssembler::AddLogical(Register dst, const Operand& imm) {
alfi(dst, imm);
}
// Add Logical Pointer Size (Register dst = Register dst + Immediate opnd)
void TurboAssembler::AddLogicalP(Register dst, const Operand& imm) {
#ifdef V8_TARGET_ARCH_S390X
algfi(dst, imm);
#else
AddLogical(dst, imm);
#endif
}
// Add Logical 32-bit (Register-Memory)
void TurboAssembler::AddLogical(Register dst, const MemOperand& opnd) {
DCHECK(is_int20(opnd.offset()));
if (is_uint12(opnd.offset()))
al_z(dst, opnd);
else
aly(dst, opnd);
}
// Add Logical Pointer Size (Register-Memory)
void TurboAssembler::AddLogicalP(Register dst, const MemOperand& opnd) {
#if V8_TARGET_ARCH_S390X
DCHECK(is_int20(opnd.offset()));
alg(dst, opnd);
#else
AddLogical(dst, opnd);
#endif
}
//----------------------------------------------------------------------------
// Subtract Instructions
//----------------------------------------------------------------------------
// Subtract Logical With Carry 32-bit (Register dst = Register src1 - Register
// src2)
void TurboAssembler::SubLogicalWithBorrow32(Register dst, Register src1,
Register src2) {
if (dst != src2 && dst != src1) {
lr(dst, src1);
slbr(dst, src2);
} else if (dst != src2) {
// dst == src1
DCHECK(dst == src1);
slbr(dst, src2);
} else {
// dst == src2
DCHECK(dst == src2);
lr(r0, dst);
SubLogicalWithBorrow32(dst, src1, r0);
}
}
// Subtract Logical 32-bit (Register dst = Register src1 - Register src2)
void TurboAssembler::SubLogical32(Register dst, Register src1, Register src2) {
if (dst != src2 && dst != src1) {
lr(dst, src1);
slr(dst, src2);
} else if (dst != src2) {
// dst == src1
DCHECK(dst == src1);
slr(dst, src2);
} else {
// dst == src2
DCHECK(dst == src2);
lr(r0, dst);
SubLogical32(dst, src1, r0);
}
}
// Subtract 32-bit (Register dst = Register dst - Immediate opnd)
void TurboAssembler::Sub32(Register dst, const Operand& imm) {
Add32(dst, Operand(-(imm.immediate())));
}
// Subtract Pointer Size (Register dst = Register dst - Immediate opnd)
void TurboAssembler::SubP(Register dst, const Operand& imm) {
AddP(dst, Operand(-(imm.immediate())));
}
// Subtract 32-bit (Register dst = Register src - Immediate opnd)
void TurboAssembler::Sub32(Register dst, Register src, const Operand& imm) {
Add32(dst, src, Operand(-(imm.immediate())));
}
// Subtract Pointer Sized (Register dst = Register src - Immediate opnd)
void TurboAssembler::SubP(Register dst, Register src, const Operand& imm) {
AddP(dst, src, Operand(-(imm.immediate())));
}
// Subtract 32-bit (Register dst = Register dst - Register src)
void TurboAssembler::Sub32(Register dst, Register src) { sr(dst, src); }
// Subtract Pointer Size (Register dst = Register dst - Register src)
void TurboAssembler::SubP(Register dst, Register src) { SubRR(dst, src); }
// Subtract Pointer Size with src extension
// (Register dst(ptr) = Register dst (ptr) - Register src (32 | 32->64))
// src is treated as a 32-bit signed integer, which is sign extended to
// 64-bit if necessary.
void TurboAssembler::SubP_ExtendSrc(Register dst, Register src) {
#if V8_TARGET_ARCH_S390X
sgfr(dst, src);
#else
sr(dst, src);
#endif
}
// Subtract 32-bit (Register = Register - Register)
void TurboAssembler::Sub32(Register dst, Register src1, Register src2) {
// Use non-clobbering version if possible
if (CpuFeatures::IsSupported(DISTINCT_OPS)) {
srk(dst, src1, src2);
return;
}
if (dst != src1 && dst != src2) lr(dst, src1);
// In scenario where we have dst = src - dst, we need to swap and negate
if (dst != src1 && dst == src2) {
Label done;
lcr(dst, dst); // dst = -dst
b(overflow, &done);
ar(dst, src1); // dst = dst + src
bind(&done);
} else {
sr(dst, src2);
}
}
// Subtract Pointer Sized (Register = Register - Register)
void TurboAssembler::SubP(Register dst, Register src1, Register src2) {
// Use non-clobbering version if possible
if (CpuFeatures::IsSupported(DISTINCT_OPS)) {
SubP_RRR(dst, src1, src2);
return;
}
if (dst != src1 && dst != src2) LoadRR(dst, src1);
// In scenario where we have dst = src - dst, we need to swap and negate
if (dst != src1 && dst == src2) {
Label done;
LoadComplementRR(dst, dst); // dst = -dst
b(overflow, &done);
AddP(dst, src1); // dst = dst + src
bind(&done);
} else {
SubP(dst, src2);
}
}
// Subtract Pointer Size with src extension
// (Register dst(ptr) = Register dst (ptr) - Register src (32 | 32->64))
// src is treated as a 32-bit signed integer, which is sign extended to
// 64-bit if necessary.
void TurboAssembler::SubP_ExtendSrc(Register dst, Register src1,
Register src2) {
#if V8_TARGET_ARCH_S390X
if (dst != src1 && dst != src2) LoadRR(dst, src1);
// In scenario where we have dst = src - dst, we need to swap and negate
if (dst != src1 && dst == src2) {
lgfr(dst, dst); // Sign extend this operand first.
LoadComplementRR(dst, dst); // dst = -dst
AddP(dst, src1); // dst = -dst + src
} else {
sgfr(dst, src2);
}
#else
SubP(dst, src1, src2);
#endif
}
// Subtract 32-bit (Register-Memory)
void TurboAssembler::Sub32(Register dst, const MemOperand& opnd) {
DCHECK(is_int20(opnd.offset()));
if (is_uint12(opnd.offset()))
s(dst, opnd);
else
sy(dst, opnd);
}
// Subtract Pointer Sized (Register - Memory)
void TurboAssembler::SubP(Register dst, const MemOperand& opnd) {
#if V8_TARGET_ARCH_S390X
sg(dst, opnd);
#else
Sub32(dst, opnd);
#endif
}
void TurboAssembler::MovIntToFloat(DoubleRegister dst, Register src) {
sllg(r0, src, Operand(32));
ldgr(dst, r0);
}
void TurboAssembler::MovFloatToInt(Register dst, DoubleRegister src) {
lgdr(dst, src);
srlg(dst, dst, Operand(32));
}
void TurboAssembler::SubP_ExtendSrc(Register dst, const MemOperand& opnd) {
#if V8_TARGET_ARCH_S390X
DCHECK(is_int20(opnd.offset()));
sgf(dst, opnd);
#else
Sub32(dst, opnd);
#endif
}
//----------------------------------------------------------------------------
// Subtract Logical Instructions
//----------------------------------------------------------------------------
// Subtract Logical 32-bit (Register - Memory)
void TurboAssembler::SubLogical(Register dst, const MemOperand& opnd) {
DCHECK(is_int20(opnd.offset()));
if (is_uint12(opnd.offset()))
sl(dst, opnd);
else
sly(dst, opnd);
}
// Subtract Logical Pointer Sized (Register - Memory)
void TurboAssembler::SubLogicalP(Register dst, const MemOperand& opnd) {
DCHECK(is_int20(opnd.offset()));
#if V8_TARGET_ARCH_S390X
slgf(dst, opnd);
#else
SubLogical(dst, opnd);
#endif
}
// Subtract Logical Pointer Size with src extension
// (Register dst (ptr) = Register dst (ptr) - Mem opnd (32 | 32->64))
// src is treated as a 32-bit signed integer, which is sign extended to
// 64-bit if necessary.
void TurboAssembler::SubLogicalP_ExtendSrc(Register dst,
const MemOperand& opnd) {
#if V8_TARGET_ARCH_S390X
DCHECK(is_int20(opnd.offset()));
slgf(dst, opnd);
#else
SubLogical(dst, opnd);
#endif
}
//----------------------------------------------------------------------------
// Bitwise Operations
//----------------------------------------------------------------------------
// AND 32-bit - dst = dst & src
void TurboAssembler::And(Register dst, Register src) { nr(dst, src); }
// AND Pointer Size - dst = dst & src
void TurboAssembler::AndP(Register dst, Register src) { AndRR(dst, src); }
// Non-clobbering AND 32-bit - dst = src1 & src1
void TurboAssembler::And(Register dst, Register src1, Register src2) {
if (dst != src1 && dst != src2) {
// We prefer to generate XR/XGR, over the non clobbering XRK/XRK
// as XR is a smaller instruction
if (CpuFeatures::IsSupported(DISTINCT_OPS)) {
nrk(dst, src1, src2);
return;
} else {
lr(dst, src1);
}
} else if (dst == src2) {
src2 = src1;
}
And(dst, src2);
}
// Non-clobbering AND pointer size - dst = src1 & src1
void TurboAssembler::AndP(Register dst, Register src1, Register src2) {
if (dst != src1 && dst != src2) {
// We prefer to generate XR/XGR, over the non clobbering XRK/XRK
// as XR is a smaller instruction
if (CpuFeatures::IsSupported(DISTINCT_OPS)) {
AndP_RRR(dst, src1, src2);
return;
} else {
LoadRR(dst, src1);
}
} else if (dst == src2) {
src2 = src1;
}
AndP(dst, src2);
}
// AND 32-bit (Reg - Mem)
void TurboAssembler::And(Register dst, const MemOperand& opnd) {
DCHECK(is_int20(opnd.offset()));
if (is_uint12(opnd.offset()))
n(dst, opnd);
else
ny(dst, opnd);
}
// AND Pointer Size (Reg - Mem)
void TurboAssembler::AndP(Register dst, const MemOperand& opnd) {
DCHECK(is_int20(opnd.offset()));
#if V8_TARGET_ARCH_S390X
ng(dst, opnd);
#else
And(dst, opnd);
#endif
}
// AND 32-bit - dst = dst & imm
void TurboAssembler::And(Register dst, const Operand& opnd) { nilf(dst, opnd); }
// AND Pointer Size - dst = dst & imm
void TurboAssembler::AndP(Register dst, const Operand& opnd) {
#if V8_TARGET_ARCH_S390X
intptr_t value = opnd.immediate();
if (value >> 32 != -1) {
// this may not work b/c condition code won't be set correctly
nihf(dst, Operand(value >> 32));
}
nilf(dst, Operand(value & 0xFFFFFFFF));
#else
And(dst, opnd);
#endif
}
// AND 32-bit - dst = src & imm
void TurboAssembler::And(Register dst, Register src, const Operand& opnd) {
if (dst != src) lr(dst, src);
nilf(dst, opnd);
}
// AND Pointer Size - dst = src & imm
void TurboAssembler::AndP(Register dst, Register src, const Operand& opnd) {
// Try to exploit RISBG first
intptr_t value = opnd.immediate();
if (CpuFeatures::IsSupported(GENERAL_INSTR_EXT)) {
intptr_t shifted_value = value;
int trailing_zeros = 0;
// We start checking how many trailing zeros are left at the end.
while ((0 != shifted_value) && (0 == (shifted_value & 1))) {
trailing_zeros++;
shifted_value >>= 1;
}
// If temp (value with right-most set of zeros shifted out) is 1 less
// than power of 2, we have consecutive bits of 1.
// Special case: If shift_value is zero, we cannot use RISBG, as it requires
// selection of at least 1 bit.
if ((0 != shifted_value) && base::bits::IsPowerOfTwo(shifted_value + 1)) {
int startBit =
base::bits::CountLeadingZeros64(shifted_value) - trailing_zeros;
int endBit = 63 - trailing_zeros;
// Start: startBit, End: endBit, Shift = 0, true = zero unselected bits.
risbg(dst, src, Operand(startBit), Operand(endBit), Operand::Zero(),
true);
return;
} else if (-1 == shifted_value) {
// A Special case in which all top bits up to MSB are 1's. In this case,
// we can set startBit to be 0.
int endBit = 63 - trailing_zeros;
risbg(dst, src, Operand::Zero(), Operand(endBit), Operand::Zero(), true);
return;
}
}
// If we are &'ing zero, we can just whack the dst register and skip copy
if (dst != src && (0 != value)) LoadRR(dst, src);
AndP(dst, opnd);
}
// OR 32-bit - dst = dst & src
void TurboAssembler::Or(Register dst, Register src) { or_z(dst, src); }
// OR Pointer Size - dst = dst & src
void TurboAssembler::OrP(Register dst, Register src) { OrRR(dst, src); }
// Non-clobbering OR 32-bit - dst = src1 & src1
void TurboAssembler::Or(Register dst, Register s