summaryrefslogtreecommitdiff
path: root/deps/v8/src/codegen/arm64/assembler-arm64.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/codegen/arm64/assembler-arm64.cc')
-rw-r--r--deps/v8/src/codegen/arm64/assembler-arm64.cc213
1 files changed, 196 insertions, 17 deletions
diff --git a/deps/v8/src/codegen/arm64/assembler-arm64.cc b/deps/v8/src/codegen/arm64/assembler-arm64.cc
index dc06c743a0..f753e0bcc8 100644
--- a/deps/v8/src/codegen/arm64/assembler-arm64.cc
+++ b/deps/v8/src/codegen/arm64/assembler-arm64.cc
@@ -64,17 +64,26 @@ unsigned SimulatorFeaturesFromCommandLine() {
constexpr unsigned CpuFeaturesFromCompiler() {
unsigned features = 0;
-#if defined(__ARM_FEATURE_JCVT)
+#if defined(__ARM_FEATURE_JCVT) && !defined(V8_TARGET_OS_IOS)
features |= 1u << JSCVT;
#endif
+#if defined(__ARM_FEATURE_DOTPROD)
+ features |= 1u << DOTPROD;
+#endif
+#if defined(__ARM_FEATURE_ATOMICS)
+ features |= 1u << LSE;
+#endif
return features;
}
constexpr unsigned CpuFeaturesFromTargetOS() {
unsigned features = 0;
#if defined(V8_TARGET_OS_MACOS) && !defined(V8_TARGET_OS_IOS)
- // TODO(v8:13004): Detect if an iPhone is new enough to support jscvt.
+ // TODO(v8:13004): Detect if an iPhone is new enough to support jscvt, dotprot
+ // and lse.
features |= 1u << JSCVT;
+ features |= 1u << DOTPROD;
+ features |= 1u << LSE;
#endif
return features;
}
@@ -106,6 +115,12 @@ void CpuFeatures::ProbeImpl(bool cross_compile) {
if (cpu.has_jscvt()) {
runtime |= 1u << JSCVT;
}
+ if (cpu.has_dot_prod()) {
+ runtime |= 1u << DOTPROD;
+ }
+ if (cpu.has_lse()) {
+ runtime |= 1u << LSE;
+ }
// Use the best of the features found by CPU detection and those inferred from
// the build system.
@@ -188,7 +203,8 @@ CPURegList CPURegList::GetCallerSavedV(int size) {
const int RelocInfo::kApplyMask =
RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
RelocInfo::ModeMask(RelocInfo::NEAR_BUILTIN_ENTRY) |
- RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE);
+ RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE) |
+ RelocInfo::ModeMask(RelocInfo::WASM_STUB_CALL);
bool RelocInfo::IsCodedSpecially() {
// The deserializer needs to know whether a pointer is specially coded. Being
@@ -273,6 +289,14 @@ bool AreSameSizeAndType(const CPURegister& reg1, const CPURegister& reg2,
return match;
}
+bool AreSameFormat(const Register& reg1, const Register& reg2,
+ const Register& reg3, const Register& reg4) {
+ DCHECK(reg1.is_valid());
+ return (!reg2.is_valid() || reg2.IsSameSizeAndType(reg1)) &&
+ (!reg3.is_valid() || reg3.IsSameSizeAndType(reg1)) &&
+ (!reg4.is_valid() || reg4.IsSameSizeAndType(reg1));
+}
+
bool AreSameFormat(const VRegister& reg1, const VRegister& reg2,
const VRegister& reg3, const VRegister& reg4) {
DCHECK(reg1.is_valid());
@@ -281,32 +305,49 @@ bool AreSameFormat(const VRegister& reg1, const VRegister& reg2,
(!reg4.is_valid() || reg4.IsSameFormat(reg1));
}
-bool AreConsecutive(const VRegister& reg1, const VRegister& reg2,
- const VRegister& reg3, const VRegister& reg4) {
+bool AreConsecutive(const CPURegister& reg1, const CPURegister& reg2,
+ const CPURegister& reg3, const CPURegister& reg4) {
DCHECK(reg1.is_valid());
+
if (!reg2.is_valid()) {
DCHECK(!reg3.is_valid() && !reg4.is_valid());
return true;
- } else if (reg2.code() != ((reg1.code() + 1) % kNumberOfVRegisters)) {
+ } else if (reg2.code() != ((reg1.code() + 1) % (reg1.MaxCode() + 1))) {
return false;
}
if (!reg3.is_valid()) {
DCHECK(!reg4.is_valid());
return true;
- } else if (reg3.code() != ((reg2.code() + 1) % kNumberOfVRegisters)) {
+ } else if (reg3.code() != ((reg2.code() + 1) % (reg1.MaxCode() + 1))) {
return false;
}
if (!reg4.is_valid()) {
return true;
- } else if (reg4.code() != ((reg3.code() + 1) % kNumberOfVRegisters)) {
+ } else if (reg4.code() != ((reg3.code() + 1) % (reg1.MaxCode() + 1))) {
return false;
}
return true;
}
+bool AreEven(const CPURegister& reg1, const CPURegister& reg2,
+ const CPURegister& reg3, const CPURegister& reg4,
+ const CPURegister& reg5, const CPURegister& reg6,
+ const CPURegister& reg7, const CPURegister& reg8) {
+ DCHECK(reg1.is_valid());
+ bool even = reg1.IsEven();
+ even &= !reg2.is_valid() || reg2.IsEven();
+ even &= !reg3.is_valid() || reg3.IsEven();
+ even &= !reg4.is_valid() || reg4.IsEven();
+ even &= !reg5.is_valid() || reg5.IsEven();
+ even &= !reg6.is_valid() || reg6.IsEven();
+ even &= !reg7.is_valid() || reg7.IsEven();
+ even &= !reg8.is_valid() || reg8.IsEven();
+ return even;
+}
+
bool Operand::NeedsRelocation(const Assembler* assembler) const {
RelocInfo::Mode rmode = immediate_.rmode();
@@ -374,16 +415,16 @@ void Assembler::AllocateAndInstallRequestedHeapNumbers(Isolate* isolate) {
}
void Assembler::GetCode(Isolate* isolate, CodeDesc* desc,
- SafepointTableBuilder* safepoint_table_builder,
+ SafepointTableBuilderBase* safepoint_table_builder,
int handler_table_offset) {
// As a crutch to avoid having to add manual Align calls wherever we use a
- // raw workflow to create Code objects (mostly in tests), add another Align
- // call here. It does no harm - the end of the Code object is aligned to the
- // (larger) kCodeAlignment anyways.
+ // raw workflow to create InstructionStream objects (mostly in tests), add
+ // another Align call here. It does no harm - the end of the InstructionStream
+ // object is aligned to the (larger) kCodeAlignment anyways.
// TODO(jgruber): Consider moving responsibility for proper alignment to
// metadata table builders (safepoint, handler, constant pool, code
// comments).
- DataAlign(Code::kMetadataAlignment);
+ DataAlign(InstructionStream::kMetadataAlignment);
// Emit constant pool if necessary.
ForceConstantPoolEmissionWithoutJump();
@@ -1316,6 +1357,10 @@ Operand Operand::EmbeddedNumber(double number) {
if (DoubleToSmiInteger(number, &smi)) {
return Operand(Immediate(Smi::FromInt(smi)));
}
+ return EmbeddedHeapNumber(number);
+}
+
+Operand Operand::EmbeddedHeapNumber(double number) {
Operand result(0, RelocInfo::FULL_EMBEDDED_OBJECT);
result.heap_number_request_.emplace(number);
DCHECK(result.IsHeapNumberRequest());
@@ -1420,6 +1465,138 @@ void Assembler::stlxrh(const Register& rs, const Register& rt,
Emit(STLXR_h | Rs(rs) | Rt2(x31) | RnSP(rn) | Rt(rt));
}
+#define COMPARE_AND_SWAP_W_X_LIST(V) \
+ V(cas, CAS) \
+ V(casa, CASA) \
+ V(casl, CASL) \
+ V(casal, CASAL)
+
+#define DEFINE_ASM_FUNC(FN, OP) \
+ void Assembler::FN(const Register& rs, const Register& rt, \
+ const MemOperand& src) { \
+ DCHECK(IsEnabled(LSE)); \
+ DCHECK(src.IsImmediateOffset() && (src.offset() == 0)); \
+ LoadStoreAcquireReleaseOp op = rt.Is64Bits() ? OP##_x : OP##_w; \
+ Emit(op | Rs(rs) | Rt(rt) | Rt2_mask | RnSP(src.base())); \
+ }
+COMPARE_AND_SWAP_W_X_LIST(DEFINE_ASM_FUNC)
+#undef DEFINE_ASM_FUNC
+
+#define COMPARE_AND_SWAP_W_LIST(V) \
+ V(casb, CASB) \
+ V(casab, CASAB) \
+ V(caslb, CASLB) \
+ V(casalb, CASALB) \
+ V(cash, CASH) \
+ V(casah, CASAH) \
+ V(caslh, CASLH) \
+ V(casalh, CASALH)
+
+#define DEFINE_ASM_FUNC(FN, OP) \
+ void Assembler::FN(const Register& rs, const Register& rt, \
+ const MemOperand& src) { \
+ DCHECK(IsEnabled(LSE)); \
+ DCHECK(src.IsImmediateOffset() && (src.offset() == 0)); \
+ Emit(OP | Rs(rs) | Rt(rt) | Rt2_mask | RnSP(src.base())); \
+ }
+COMPARE_AND_SWAP_W_LIST(DEFINE_ASM_FUNC)
+#undef DEFINE_ASM_FUNC
+
+#define COMPARE_AND_SWAP_PAIR_LIST(V) \
+ V(casp, CASP) \
+ V(caspa, CASPA) \
+ V(caspl, CASPL) \
+ V(caspal, CASPAL)
+
+#define DEFINE_ASM_FUNC(FN, OP) \
+ void Assembler::FN(const Register& rs, const Register& rs1, \
+ const Register& rt, const Register& rt1, \
+ const MemOperand& src) { \
+ DCHECK(IsEnabled(LSE)); \
+ DCHECK(src.IsImmediateOffset() && (src.offset() == 0)); \
+ DCHECK(AreEven(rs, rt)); \
+ DCHECK(AreConsecutive(rs, rs1)); \
+ DCHECK(AreConsecutive(rt, rt1)); \
+ DCHECK(AreSameFormat(rs, rs1, rt, rt1)); \
+ LoadStoreAcquireReleaseOp op = rt.Is64Bits() ? OP##_x : OP##_w; \
+ Emit(op | Rs(rs) | Rt(rt) | Rt2_mask | RnSP(src.base())); \
+ }
+COMPARE_AND_SWAP_PAIR_LIST(DEFINE_ASM_FUNC)
+#undef DEFINE_ASM_FUNC
+
+// These macros generate all the variations of the atomic memory operations,
+// e.g. ldadd, ldadda, ldaddb, staddl, etc.
+// For a full list of the methods with comments, see the assembler header file.
+
+#define ATOMIC_MEMORY_SIMPLE_OPERATION_LIST(V, DEF) \
+ V(DEF, add, LDADD) \
+ V(DEF, clr, LDCLR) \
+ V(DEF, eor, LDEOR) \
+ V(DEF, set, LDSET) \
+ V(DEF, smax, LDSMAX) \
+ V(DEF, smin, LDSMIN) \
+ V(DEF, umax, LDUMAX) \
+ V(DEF, umin, LDUMIN)
+
+#define ATOMIC_MEMORY_STORE_MODES(V, NAME, OP) \
+ V(NAME, OP##_x, OP##_w) \
+ V(NAME##l, OP##L_x, OP##L_w) \
+ V(NAME##b, OP##B, OP##B) \
+ V(NAME##lb, OP##LB, OP##LB) \
+ V(NAME##h, OP##H, OP##H) \
+ V(NAME##lh, OP##LH, OP##LH)
+
+#define ATOMIC_MEMORY_LOAD_MODES(V, NAME, OP) \
+ ATOMIC_MEMORY_STORE_MODES(V, NAME, OP) \
+ V(NAME##a, OP##A_x, OP##A_w) \
+ V(NAME##al, OP##AL_x, OP##AL_w) \
+ V(NAME##ab, OP##AB, OP##AB) \
+ V(NAME##alb, OP##ALB, OP##ALB) \
+ V(NAME##ah, OP##AH, OP##AH) \
+ V(NAME##alh, OP##ALH, OP##ALH)
+
+#define DEFINE_ASM_LOAD_FUNC(FN, OP_X, OP_W) \
+ void Assembler::ld##FN(const Register& rs, const Register& rt, \
+ const MemOperand& src) { \
+ DCHECK(IsEnabled(LSE)); \
+ DCHECK(src.IsImmediateOffset() && (src.offset() == 0)); \
+ AtomicMemoryOp op = rt.Is64Bits() ? OP_X : OP_W; \
+ Emit(op | Rs(rs) | Rt(rt) | RnSP(src.base())); \
+ }
+#define DEFINE_ASM_STORE_FUNC(FN, OP_X, OP_W) \
+ void Assembler::st##FN(const Register& rs, const MemOperand& src) { \
+ DCHECK(IsEnabled(LSE)); \
+ ld##FN(rs, AppropriateZeroRegFor(rs), src); \
+ }
+
+ATOMIC_MEMORY_SIMPLE_OPERATION_LIST(ATOMIC_MEMORY_LOAD_MODES,
+ DEFINE_ASM_LOAD_FUNC)
+ATOMIC_MEMORY_SIMPLE_OPERATION_LIST(ATOMIC_MEMORY_STORE_MODES,
+ DEFINE_ASM_STORE_FUNC)
+
+#define DEFINE_ASM_SWP_FUNC(FN, OP_X, OP_W) \
+ void Assembler::FN(const Register& rs, const Register& rt, \
+ const MemOperand& src) { \
+ DCHECK(IsEnabled(LSE)); \
+ DCHECK(src.IsImmediateOffset() && (src.offset() == 0)); \
+ AtomicMemoryOp op = rt.Is64Bits() ? OP_X : OP_W; \
+ Emit(op | Rs(rs) | Rt(rt) | RnSP(src.base())); \
+ }
+
+ATOMIC_MEMORY_LOAD_MODES(DEFINE_ASM_SWP_FUNC, swp, SWP)
+
+#undef DEFINE_ASM_LOAD_FUNC
+#undef DEFINE_ASM_STORE_FUNC
+#undef DEFINE_ASM_SWP_FUNC
+
+void Assembler::sdot(const VRegister& vd, const VRegister& vn,
+ const VRegister& vm) {
+ DCHECK(CpuFeatures::IsSupported(DOTPROD));
+ DCHECK(vn.Is16B() && vd.Is4S());
+ DCHECK(AreSameFormat(vn, vm));
+ Emit(NEON_Q | NEON_SDOT | Rm(vm) | Rn(vn) | Rd(vd));
+}
+
void Assembler::NEON3DifferentL(const VRegister& vd, const VRegister& vn,
const VRegister& vm, NEON3DifferentOp vop) {
DCHECK(AreSameFormat(vn, vm));
@@ -3573,7 +3750,7 @@ Instr Assembler::ImmNEONFP(double imm) {
return ImmNEONabcdefgh(FPToImm8(imm));
}
-// Code generation helpers.
+// InstructionStream generation helpers.
void Assembler::MoveWide(const Register& rd, uint64_t imm, int shift,
MoveWideImmediateOp mov_op) {
// Ignore the top 32 bits of an immediate if we're moving to a W register.
@@ -4356,7 +4533,8 @@ void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data,
DCHECK(constpool_.IsBlocked());
// We do not try to reuse pool constants.
- RelocInfo rinfo(reinterpret_cast<Address>(pc_), rmode, data, Code());
+ RelocInfo rinfo(reinterpret_cast<Address>(pc_), rmode, data, Code(),
+ InstructionStream());
DCHECK_GE(buffer_space(), kMaxRelocSize); // too late to grow buffer here
reloc_info_writer.Write(&rinfo);
@@ -4379,7 +4557,7 @@ void Assembler::near_call(int offset, RelocInfo::Mode rmode) {
void Assembler::near_call(HeapNumberRequest request) {
BlockPoolsScope no_pool_before_bl_instr(this);
RequestHeapNumber(request);
- EmbeddedObjectIndex index = AddEmbeddedObject(Handle<CodeT>());
+ EmbeddedObjectIndex index = AddEmbeddedObject(Handle<Code>());
RecordRelocInfo(RelocInfo::CODE_TARGET, index, NO_POOL_ENTRY);
DCHECK(is_int32(index));
bl(static_cast<int>(index));
@@ -4482,7 +4660,8 @@ intptr_t Assembler::MaxPCOffsetAfterVeneerPoolIfEmittedNow(size_t margin) {
void Assembler::RecordVeneerPool(int location_offset, int size) {
Assembler::BlockPoolsScope block_pools(this, PoolEmissionCheck::kSkip);
RelocInfo rinfo(reinterpret_cast<Address>(buffer_start_) + location_offset,
- RelocInfo::VENEER_POOL, static_cast<intptr_t>(size), Code());
+ RelocInfo::VENEER_POOL, static_cast<intptr_t>(size), Code(),
+ InstructionStream());
reloc_info_writer.Write(&rinfo);
}