Commit ba4c08a9 authored by Bill Budge's avatar Bill Budge Committed by Commit Bot

Revert "[compiler][wasm] Align Frame slots to value size"

This reverts commit cddaf66c.

Reason for revert: Multiple fuzzer failures

TBR=neis@chromium.org,ahaas@chromium.org

Original change's description:
> [compiler][wasm] Align Frame slots to value size
>
> - Adds an AlignedSlotAllocator class and tests, to unify slot
>   allocation. This attempts to use alignment holes for smaller
>   values.
> - Reworks Frame to use the new allocator for stack slots.
> - Reworks LinkageAllocator to use the new allocator for stack
>   slots and for ARMv7 FP register aliasing.
> - Fixes the RegisterAllocator to align spill slots.
> - Fixes InstructionSelector to align spill slots.
>
> Bug: v8:9198
>
> Change-Id: Ida148db428be89ef95de748ec5fc0e7b0358f523
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2512840
> Commit-Queue: Bill Budge <bbudge@chromium.org>
> Reviewed-by: Georg Neis <neis@chromium.org>
> Reviewed-by: Andreas Haas <ahaas@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#71644}

TBR=bbudge@chromium.org,neis@chromium.org,ahaas@chromium.org

# Not skipping CQ checks because original CL landed > 1 day ago.

Bug: v8:9198
Change-Id: Ib26d016df6f30f333d30b5ac14eed9630bba8252
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2584200
Commit-Queue: Bill Budge <bbudge@chromium.org>
Reviewed-by: 's avatarBill Budge <bbudge@chromium.org>
Cr-Commit-Position: refs/heads/master@{#71703}
parent 71e68fe4
...@@ -2425,8 +2425,6 @@ v8_source_set("v8_base_without_compiler") { ...@@ -2425,8 +2425,6 @@ v8_source_set("v8_base_without_compiler") {
"src/builtins/constants-table-builder.cc", "src/builtins/constants-table-builder.cc",
"src/builtins/constants-table-builder.h", "src/builtins/constants-table-builder.h",
"src/builtins/profile-data-reader.h", "src/builtins/profile-data-reader.h",
"src/codegen/aligned-slot-allocator.cc",
"src/codegen/aligned-slot-allocator.h",
"src/codegen/assembler-arch.h", "src/codegen/assembler-arch.h",
"src/codegen/assembler-inl.h", "src/codegen/assembler-inl.h",
"src/codegen/assembler.cc", "src/codegen/assembler.cc",
......
// Copyright 2020 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/codegen/aligned-slot-allocator.h"
#include "src/base/bits.h"
#include "src/base/logging.h"
namespace v8 {
namespace internal {
int AlignedSlotAllocator::NextSlot(int n) const {
DCHECK(n == 1 || n == 2 || n == 4);
if (n <= 1 && IsValid(next1_)) return next1_;
if (n <= 2 && IsValid(next2_)) return next2_;
DCHECK(IsValid(next4_));
return next4_;
}
int AlignedSlotAllocator::Allocate(int n) {
DCHECK(n == 1 || n == 2 || n == 4);
// Check invariants.
DCHECK_EQ(0, next4_ & 3);
DCHECK_IMPLIES(IsValid(next2_), (next2_ & 1) == 0);
// The sentinel value kInvalidSlot is used to indicate no slot.
// next1_ is the index of the 1 slot fragment, or kInvalidSlot.
// next2_ is the 2-aligned index of the 2 slot fragment, or kInvalidSlot.
// next4_ is the 4-aligned index of the next 4 slot group. It is always valid.
// In order to ensure we only have a single 1- or 2-slot fragment, we greedily
// use any fragment that satisfies the request.
int result = kInvalidSlot;
switch (n) {
case 1: {
if (IsValid(next1_)) {
result = next1_;
next1_ = kInvalidSlot;
} else if (IsValid(next2_)) {
result = next2_;
next1_ = result + 1;
next2_ = kInvalidSlot;
} else {
result = next4_;
next1_ = result + 1;
next2_ = result + 2;
next4_ += 4;
}
break;
}
case 2: {
if (IsValid(next2_)) {
result = next2_;
next2_ = kInvalidSlot;
} else {
result = next4_;
next2_ = result + 2;
next4_ += 4;
}
break;
}
case 4: {
result = next4_;
next4_ += 4;
break;
}
default:
UNREACHABLE();
break;
}
DCHECK(IsValid(result));
size_ = std::max(size_, result + n);
return result;
}
int AlignedSlotAllocator::AllocateUnaligned(int n) {
DCHECK_GE(n, 0);
// Check invariants.
DCHECK_EQ(0, next4_ & 3);
DCHECK_IMPLIES(IsValid(next2_), (next2_ & 1) == 0);
// Reserve |n| slots at |size_|, invalidate fragments below the new |size_|,
// and add any new fragments beyond the new |size_|.
int result = size_;
size_ += n;
switch (size_ & 3) {
case 0: {
next1_ = next2_ = kInvalidSlot;
next4_ = size_;
break;
}
case 1: {
next1_ = size_;
next2_ = size_ + 1;
next4_ = size_ + 3;
break;
}
case 2: {
next1_ = kInvalidSlot;
next2_ = size_;
next4_ = size_ + 2;
break;
}
case 3: {
next1_ = size_;
next2_ = kInvalidSlot;
next4_ = size_ + 1;
break;
}
}
return result;
}
int AlignedSlotAllocator::Align(int n) {
DCHECK(base::bits::IsPowerOfTwo(n));
DCHECK_LE(n, 4);
int mask = n - 1;
int misalignment = size_ & mask;
int padding = (n - misalignment) & mask;
AllocateUnaligned(padding);
return padding;
}
} // namespace internal
} // namespace v8
// Copyright 2020 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.
#ifndef V8_CODEGEN_ALIGNED_SLOT_ALLOCATOR_H_
#define V8_CODEGEN_ALIGNED_SLOT_ALLOCATOR_H_
#include "src/base/macros.h"
#include "src/base/platform/platform.h"
#include "src/common/globals.h"
namespace v8 {
namespace internal {
// An aligned slot allocator. Allocates groups of 1, 2, or 4 slots such that the
// first slot of the group is aligned to the group size. The allocator can also
// allocate unaligned groups of arbitrary size, and an align the number of slots
// to 1, 2, or 4 slots. The allocator tries to be as thrifty as possible by
// reusing alignment padding slots in subsequent smaller slot allocations.
class V8_EXPORT_PRIVATE AlignedSlotAllocator {
public:
// Slots are always multiples of pointer-sized units.
static constexpr int kSlotSize = kSystemPointerSize;
static int NumSlotsForWidth(int bytes) {
return (bytes + kSlotSize - 1) / kSlotSize;
}
AlignedSlotAllocator() = default;
// Allocates |n| slots, where |n| must be 1, 2, or 4. Padding slots may be
// inserted for alignment.
// Returns the starting index of the slots, which is evenly divisible by |n|.
int Allocate(int n);
// Gets the starting index of the slots that would be returned by Allocate(n).
int NextSlot(int n) const;
// Allocates the given number of slots at the current end of the slot area,
// and returns the starting index of the slots. This resets any fragment
// slots, so subsequent allocations will be after the end of this one.
// AllocateUnaligned(0) can be used to partition the slot area, for example
// to make sure tagged values follow untagged values on a Frame.
int AllocateUnaligned(int n);
// Aligns the slot area so that future allocations begin at the alignment.
// Returns the number of slots needed to align the slot area.
int Align(int n);
// Returns the size of the slot area, in slots. This will be greater than any
// already allocated slot index.
int Size() const { return size_; }
private:
static constexpr int kInvalidSlot = -1;
static bool IsValid(int slot) { return slot > kInvalidSlot; }
int next1_ = kInvalidSlot;
int next2_ = kInvalidSlot;
int next4_ = 0;
int size_ = 0;
DISALLOW_NEW_AND_DELETE()
};
} // namespace internal
} // namespace v8
#endif // V8_CODEGEN_ALIGNED_SLOT_ALLOCATOR_H_
...@@ -499,7 +499,7 @@ void VisitPairAtomicBinOp(InstructionSelector* selector, Node* node, ...@@ -499,7 +499,7 @@ void VisitPairAtomicBinOp(InstructionSelector* selector, Node* node,
void InstructionSelector::VisitStackSlot(Node* node) { void InstructionSelector::VisitStackSlot(Node* node) {
StackSlotRepresentation rep = StackSlotRepresentationOf(node->op()); StackSlotRepresentation rep = StackSlotRepresentationOf(node->op());
int slot = frame_->AllocateSpillSlot(rep.size(), rep.alignment()); int slot = frame_->AllocateSpillSlot(rep.size());
OperandGenerator g(this); OperandGenerator g(this);
Emit(kArchStackSlot, g.DefineAsRegister(node), Emit(kArchStackSlot, g.DefineAsRegister(node),
......
...@@ -566,7 +566,7 @@ int32_t LeftShiftForReducedMultiply(Matcher* m) { ...@@ -566,7 +566,7 @@ int32_t LeftShiftForReducedMultiply(Matcher* m) {
void InstructionSelector::VisitStackSlot(Node* node) { void InstructionSelector::VisitStackSlot(Node* node) {
StackSlotRepresentation rep = StackSlotRepresentationOf(node->op()); StackSlotRepresentation rep = StackSlotRepresentationOf(node->op());
int slot = frame_->AllocateSpillSlot(rep.size(), rep.alignment()); int slot = frame_->AllocateSpillSlot(rep.size());
OperandGenerator g(this); OperandGenerator g(this);
Emit(kArchStackSlot, g.DefineAsRegister(node), Emit(kArchStackSlot, g.DefineAsRegister(node),
......
...@@ -352,7 +352,7 @@ void VisitRROI8x16SimdShift(InstructionSelector* selector, Node* node, ...@@ -352,7 +352,7 @@ void VisitRROI8x16SimdShift(InstructionSelector* selector, Node* node,
void InstructionSelector::VisitStackSlot(Node* node) { void InstructionSelector::VisitStackSlot(Node* node) {
StackSlotRepresentation rep = StackSlotRepresentationOf(node->op()); StackSlotRepresentation rep = StackSlotRepresentationOf(node->op());
int slot = frame_->AllocateSpillSlot(rep.size(), rep.alignment()); int slot = frame_->AllocateSpillSlot(rep.size());
OperandGenerator g(this); OperandGenerator g(this);
Emit(kArchStackSlot, g.DefineAsRegister(node), Emit(kArchStackSlot, g.DefineAsRegister(node),
......
...@@ -155,7 +155,7 @@ void VisitBinop(InstructionSelector* selector, Node* node, ...@@ -155,7 +155,7 @@ void VisitBinop(InstructionSelector* selector, Node* node,
void InstructionSelector::VisitStackSlot(Node* node) { void InstructionSelector::VisitStackSlot(Node* node) {
StackSlotRepresentation rep = StackSlotRepresentationOf(node->op()); StackSlotRepresentation rep = StackSlotRepresentationOf(node->op());
int slot = frame_->AllocateSpillSlot(rep.size(), rep.alignment()); int slot = frame_->AllocateSpillSlot(rep.size());
OperandGenerator g(this); OperandGenerator g(this);
Emit(kArchStackSlot, g.DefineAsRegister(node), Emit(kArchStackSlot, g.DefineAsRegister(node),
......
...@@ -4502,11 +4502,9 @@ void OperandAssigner::AssignSpillSlots() { ...@@ -4502,11 +4502,9 @@ void OperandAssigner::AssignSpillSlots() {
for (SpillRange* range : spill_ranges) { for (SpillRange* range : spill_ranges) {
data()->tick_counter()->TickAndMaybeEnterSafepoint(); data()->tick_counter()->TickAndMaybeEnterSafepoint();
if (range == nullptr || range->IsEmpty()) continue; if (range == nullptr || range->IsEmpty()) continue;
// Allocate a new operand referring to the spill slot.
if (!range->HasSlot()) { if (!range->HasSlot()) {
// Allocate a new operand referring to the spill slot, aligned to the int index = data()->frame()->AllocateSpillSlot(range->byte_width());
// operand size.
int width = range->byte_width();
int index = data()->frame()->AllocateSpillSlot(width, width);
range->set_assigned_slot(index); range->set_assigned_slot(index);
} }
} }
......
...@@ -680,7 +680,7 @@ void VisitBinOp(InstructionSelector* selector, Node* node, ...@@ -680,7 +680,7 @@ void VisitBinOp(InstructionSelector* selector, Node* node,
void InstructionSelector::VisitStackSlot(Node* node) { void InstructionSelector::VisitStackSlot(Node* node) {
StackSlotRepresentation rep = StackSlotRepresentationOf(node->op()); StackSlotRepresentation rep = StackSlotRepresentationOf(node->op());
int slot = frame_->AllocateSpillSlot(rep.size(), rep.alignment()); int slot = frame_->AllocateSpillSlot(rep.size());
OperandGenerator g(this); OperandGenerator g(this);
Emit(kArchStackSlot, g.DefineAsRegister(node), Emit(kArchStackSlot, g.DefineAsRegister(node),
......
...@@ -336,7 +336,7 @@ ArchOpcode GetStoreOpcode(StoreRepresentation store_rep) { ...@@ -336,7 +336,7 @@ ArchOpcode GetStoreOpcode(StoreRepresentation store_rep) {
void InstructionSelector::VisitStackSlot(Node* node) { void InstructionSelector::VisitStackSlot(Node* node) {
StackSlotRepresentation rep = StackSlotRepresentationOf(node->op()); StackSlotRepresentation rep = StackSlotRepresentationOf(node->op());
int slot = frame_->AllocateSpillSlot(rep.size(), rep.alignment()); int slot = frame_->AllocateSpillSlot(rep.size());
OperandGenerator g(this); OperandGenerator g(this);
Emit(kArchStackSlot, g.DefineAsRegister(node), Emit(kArchStackSlot, g.DefineAsRegister(node),
......
...@@ -12,15 +12,14 @@ namespace compiler { ...@@ -12,15 +12,14 @@ namespace compiler {
Frame::Frame(int fixed_frame_size_in_slots) Frame::Frame(int fixed_frame_size_in_slots)
: fixed_slot_count_(fixed_frame_size_in_slots), : fixed_slot_count_(fixed_frame_size_in_slots),
frame_slot_count_(fixed_frame_size_in_slots),
spill_slot_count_(0), spill_slot_count_(0),
return_slot_count_(0), return_slot_count_(0),
allocated_registers_(nullptr), allocated_registers_(nullptr),
allocated_double_registers_(nullptr) { allocated_double_registers_(nullptr) {}
slot_allocator_.AllocateUnaligned(fixed_frame_size_in_slots);
}
void Frame::AlignFrame(int alignment) { void Frame::AlignFrame(int alignment) {
int alignment_slots = AlignedSlotAllocator::NumSlotsForWidth(alignment); int alignment_slots = alignment / kSystemPointerSize;
// In the calculations below we assume that alignment_slots is a power of 2. // In the calculations below we assume that alignment_slots is a power of 2.
DCHECK(base::bits::IsPowerOfTwo(alignment_slots)); DCHECK(base::bits::IsPowerOfTwo(alignment_slots));
...@@ -29,12 +28,11 @@ void Frame::AlignFrame(int alignment) { ...@@ -29,12 +28,11 @@ void Frame::AlignFrame(int alignment) {
int return_delta = int return_delta =
alignment_slots - (return_slot_count_ & (alignment_slots - 1)); alignment_slots - (return_slot_count_ & (alignment_slots - 1));
if (return_delta != alignment_slots) { if (return_delta != alignment_slots) {
slot_allocator_.Align(alignment_slots); frame_slot_count_ += return_delta;
} }
int delta = int delta = alignment_slots - (frame_slot_count_ & (alignment_slots - 1));
alignment_slots - (slot_allocator_.Size() & (alignment_slots - 1));
if (delta != alignment_slots) { if (delta != alignment_slots) {
slot_allocator_.Align(alignment_slots); frame_slot_count_ += delta;
if (spill_slot_count_ != 0) { if (spill_slot_count_ != 0) {
spill_slot_count_ += delta; spill_slot_count_ += delta;
} }
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
#ifndef V8_COMPILER_FRAME_H_ #ifndef V8_COMPILER_FRAME_H_
#define V8_COMPILER_FRAME_H_ #define V8_COMPILER_FRAME_H_
#include "src/codegen/aligned-slot-allocator.h"
#include "src/execution/frame-constants.h" #include "src/execution/frame-constants.h"
#include "src/utils/bit-vector.h" #include "src/utils/bit-vector.h"
...@@ -93,7 +92,7 @@ class V8_EXPORT_PRIVATE Frame : public ZoneObject { ...@@ -93,7 +92,7 @@ class V8_EXPORT_PRIVATE Frame : public ZoneObject {
Frame(const Frame&) = delete; Frame(const Frame&) = delete;
Frame& operator=(const Frame&) = delete; Frame& operator=(const Frame&) = delete;
inline int GetTotalFrameSlotCount() const { return slot_allocator_.Size(); } inline int GetTotalFrameSlotCount() const { return frame_slot_count_; }
inline int GetFixedSlotCount() const { return fixed_slot_count_; } inline int GetFixedSlotCount() const { return fixed_slot_count_; }
inline int GetSpillSlotCount() const { return spill_slot_count_; } inline int GetSpillSlotCount() const { return spill_slot_count_; }
inline int GetReturnSlotCount() const { return return_slot_count_; } inline int GetReturnSlotCount() const { return return_slot_count_; }
...@@ -113,39 +112,36 @@ class V8_EXPORT_PRIVATE Frame : public ZoneObject { ...@@ -113,39 +112,36 @@ class V8_EXPORT_PRIVATE Frame : public ZoneObject {
} }
void AlignSavedCalleeRegisterSlots(int alignment = kDoubleSize) { void AlignSavedCalleeRegisterSlots(int alignment = kDoubleSize) {
int alignment_slots = AlignedSlotAllocator::NumSlotsForWidth(alignment); int alignment_slots = alignment / kSystemPointerSize;
int padding = slot_allocator_.Align(alignment_slots); int delta = alignment_slots - (frame_slot_count_ & (alignment_slots - 1));
spill_slot_count_ += padding; if (delta != alignment_slots) {
frame_slot_count_ += delta;
}
spill_slot_count_ += delta;
} }
void AllocateSavedCalleeRegisterSlots(int count) { void AllocateSavedCalleeRegisterSlots(int count) {
slot_allocator_.AllocateUnaligned(count); frame_slot_count_ += count;
} }
int AllocateSpillSlot(int width, int alignment = 0) { int AllocateSpillSlot(int width, int alignment = 0) {
DCHECK_EQ(GetTotalFrameSlotCount(), DCHECK_EQ(frame_slot_count_,
fixed_slot_count_ + spill_slot_count_ + return_slot_count_); fixed_slot_count_ + spill_slot_count_ + return_slot_count_);
int slots = AlignedSlotAllocator::NumSlotsForWidth(width); int frame_slot_count_before = frame_slot_count_;
int alignment_slots = if (alignment > kSystemPointerSize) {
std::max(1, AlignedSlotAllocator::NumSlotsForWidth(alignment)); // Slots are pointer sized, so alignment greater than a pointer size
int old_end = slot_allocator_.Size(); // requires allocating additional slots.
int slot; width += alignment - kSystemPointerSize;
if (slots == alignment_slots) {
slot = slot_allocator_.Allocate(slots);
} else {
slot_allocator_.Align(alignment_slots);
slot = slot_allocator_.AllocateUnaligned(slots);
} }
int end = slot_allocator_.Size(); AllocateAlignedFrameSlots(width);
spill_slot_count_ += frame_slot_count_ - frame_slot_count_before;
spill_slot_count_ += end - old_end; return frame_slot_count_ - return_slot_count_ - 1;
return slot + slots - return_slot_count_ - 1;
} }
void EnsureReturnSlots(int count) { void EnsureReturnSlots(int count) {
if (count > return_slot_count_) { if (count > return_slot_count_) {
count -= return_slot_count_; count -= return_slot_count_;
slot_allocator_.AllocateUnaligned(count); frame_slot_count_ += count;
return_slot_count_ += count; return_slot_count_ += count;
} }
} }
...@@ -155,15 +151,28 @@ class V8_EXPORT_PRIVATE Frame : public ZoneObject { ...@@ -155,15 +151,28 @@ class V8_EXPORT_PRIVATE Frame : public ZoneObject {
int ReserveSpillSlots(size_t slot_count) { int ReserveSpillSlots(size_t slot_count) {
DCHECK_EQ(0, spill_slot_count_); DCHECK_EQ(0, spill_slot_count_);
spill_slot_count_ += static_cast<int>(slot_count); spill_slot_count_ += static_cast<int>(slot_count);
slot_allocator_.AllocateUnaligned(static_cast<int>(slot_count)); frame_slot_count_ += static_cast<int>(slot_count);
return slot_allocator_.Size() - 1; return frame_slot_count_ - 1;
}
private:
void AllocateAlignedFrameSlots(int width) {
DCHECK_LT(0, width);
int new_frame_slots = (width + kSystemPointerSize - 1) / kSystemPointerSize;
// Align to 8 bytes if width is a multiple of 8 bytes, and to 16 bytes if
// multiple of 16.
int align_to =
(width & 15) == 0 ? 16 : (width & 7) == 0 ? 8 : kSystemPointerSize;
frame_slot_count_ = RoundUp(frame_slot_count_ + new_frame_slots,
align_to / kSystemPointerSize);
DCHECK_LT(0, frame_slot_count_);
} }
private: private:
int fixed_slot_count_; int fixed_slot_count_;
int frame_slot_count_;
int spill_slot_count_; int spill_slot_count_;
int return_slot_count_; int return_slot_count_;
AlignedSlotAllocator slot_allocator_;
BitVector* allocated_registers_; BitVector* allocated_registers_;
BitVector* allocated_double_registers_; BitVector* allocated_double_registers_;
}; };
......
...@@ -7988,7 +7988,6 @@ class LinkageLocationAllocator { ...@@ -7988,7 +7988,6 @@ class LinkageLocationAllocator {
void SetStackOffset(int offset) { allocator_.SetStackOffset(offset); } void SetStackOffset(int offset) { allocator_.SetStackOffset(offset); }
int NumStackSlots() const { return allocator_.NumStackSlots(); } int NumStackSlots() const { return allocator_.NumStackSlots(); }
void EndSlotArea() { allocator_.EndSlotArea(); }
private: private:
wasm::LinkageAllocator allocator_; wasm::LinkageAllocator allocator_;
...@@ -8026,10 +8025,6 @@ CallDescriptor* GetWasmCallDescriptor( ...@@ -8026,10 +8025,6 @@ CallDescriptor* GetWasmCallDescriptor(
auto l = params.Next(param); auto l = params.Next(param);
locations.AddParamAt(i + param_offset, l); locations.AddParamAt(i + param_offset, l);
} }
// End the untagged area, so tagged slots come after.
params.EndSlotArea();
for (size_t i = 0; i < parameter_count; i++) { for (size_t i = 0; i < parameter_count; i++) {
MachineRepresentation param = fsig->GetParam(i).machine_representation(); MachineRepresentation param = fsig->GetParam(i).machine_representation();
// Skip untagged parameters. // Skip untagged parameters.
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
#ifndef V8_WASM_WASM_LINKAGE_H_ #ifndef V8_WASM_WASM_LINKAGE_H_
#define V8_WASM_WASM_LINKAGE_H_ #define V8_WASM_WASM_LINKAGE_H_
#include "src/codegen/aligned-slot-allocator.h"
#include "src/codegen/assembler-arch.h" #include "src/codegen/assembler-arch.h"
#include "src/codegen/machine-type.h" #include "src/codegen/machine-type.h"
#include "src/codegen/signature.h" #include "src/codegen/signature.h"
...@@ -45,7 +44,7 @@ constexpr DoubleRegister kFpReturnRegisters[] = {xmm1, xmm2}; ...@@ -45,7 +44,7 @@ constexpr DoubleRegister kFpReturnRegisters[] = {xmm1, xmm2};
// =========================================================================== // ===========================================================================
constexpr Register kGpParamRegisters[] = {r3, r0, r2, r6}; constexpr Register kGpParamRegisters[] = {r3, r0, r2, r6};
constexpr Register kGpReturnRegisters[] = {r0, r1}; constexpr Register kGpReturnRegisters[] = {r0, r1};
// ARM d-registers must be in even/odd D-register pairs for correct allocation. // ARM d-registers must be in ascending order for correct allocation.
constexpr DoubleRegister kFpParamRegisters[] = {d0, d1, d2, d3, d4, d5, d6, d7}; constexpr DoubleRegister kFpParamRegisters[] = {d0, d1, d2, d3, d4, d5, d6, d7};
constexpr DoubleRegister kFpReturnRegisters[] = {d0, d1}; constexpr DoubleRegister kFpReturnRegisters[] = {d0, d1};
...@@ -134,27 +133,18 @@ class LinkageAllocator { ...@@ -134,27 +133,18 @@ class LinkageAllocator {
bool CanAllocateFP(MachineRepresentation rep) const { bool CanAllocateFP(MachineRepresentation rep) const {
#if V8_TARGET_ARCH_ARM #if V8_TARGET_ARCH_ARM
switch (rep) { switch (rep) {
case MachineRepresentation::kFloat32: { case MachineRepresentation::kFloat32:
// Get the next D-register (Liftoff only uses the even S-registers). return fp_offset_ < fp_count_ && fp_regs_[fp_offset_].code() < 16;
int next = fp_allocator_.NextSlot(2) / 2; case MachineRepresentation::kFloat64:
// Only the lower 16 D-registers alias S-registers. return extra_double_reg_ >= 0 || fp_offset_ < fp_count_;
return next < fp_count_ && fp_regs_[next].code() < 16; case MachineRepresentation::kSimd128:
} return ((fp_offset_ + 1) & ~1) + 1 < fp_count_;
case MachineRepresentation::kFloat64: {
int next = fp_allocator_.NextSlot(2) / 2;
return next < fp_count_;
}
case MachineRepresentation::kSimd128: {
int next = fp_allocator_.NextSlot(4) / 2;
return next < fp_count_ - 1; // 2 D-registers are required.
}
default: default:
UNREACHABLE(); UNREACHABLE();
return false; return false;
} }
#else
return fp_offset_ < fp_count_;
#endif #endif
return fp_offset_ < fp_count_;
} }
int NextGpReg() { int NextGpReg() {
...@@ -163,58 +153,80 @@ class LinkageAllocator { ...@@ -163,58 +153,80 @@ class LinkageAllocator {
} }
int NextFpReg(MachineRepresentation rep) { int NextFpReg(MachineRepresentation rep) {
DCHECK(CanAllocateFP(rep));
#if V8_TARGET_ARCH_ARM #if V8_TARGET_ARCH_ARM
switch (rep) { switch (rep) {
case MachineRepresentation::kFloat32: { case MachineRepresentation::kFloat32: {
// Liftoff uses only even-numbered S-registers, and encodes them using // Liftoff uses only even-numbered f32 registers, and encodes them using
// the code of the corresponding D-register. This limits the calling // the code of the corresponding f64 register. This limits the calling
// interface to only using the even-numbered S-registers. // interface to only using the even-numbered f32 registers.
int d_reg_code = NextFpReg(MachineRepresentation::kFloat64); int d_reg_code = NextFpReg(MachineRepresentation::kFloat64);
DCHECK_GT(16, d_reg_code); // D16 - D31 don't alias S-registers. DCHECK_GT(16, d_reg_code); // D-registers 16 - 31 can't split.
return d_reg_code * 2; return d_reg_code * 2;
} }
case MachineRepresentation::kFloat64: { case MachineRepresentation::kFloat64: {
int next = fp_allocator_.Allocate(2) / 2; // Use the extra D-register if there is one.
return fp_regs_[next].code(); if (extra_double_reg_ >= 0) {
int reg_code = extra_double_reg_;
extra_double_reg_ = -1;
return reg_code;
}
DCHECK_LT(fp_offset_, fp_count_);
return fp_regs_[fp_offset_++].code();
} }
case MachineRepresentation::kSimd128: { case MachineRepresentation::kSimd128: {
int next = fp_allocator_.Allocate(4) / 2; // Q-register must be an even-odd pair, so we must try to allocate at
int d_reg_code = fp_regs_[next].code(); // the end, not using extra_double_reg_. If we are at an odd D-register,
// Check that result and the next D-register pair. // skip past it (saving it to extra_double_reg_).
DCHECK_EQ(0, d_reg_code % 2); DCHECK_LT(((fp_offset_ + 1) & ~1) + 1, fp_count_);
DCHECK_EQ(d_reg_code + 1, fp_regs_[next + 1].code()); int d_reg1_code = fp_regs_[fp_offset_++].code();
return d_reg_code / 2; if (d_reg1_code % 2 != 0) {
// If we're misaligned then extra_double_reg_ must have been consumed.
DCHECK_EQ(-1, extra_double_reg_);
int odd_double_reg = d_reg1_code;
d_reg1_code = fp_regs_[fp_offset_++].code();
extra_double_reg_ = odd_double_reg;
}
// Combine the current D-register with the next to form a Q-register.
int d_reg2_code = fp_regs_[fp_offset_++].code();
DCHECK_EQ(0, d_reg1_code % 2);
DCHECK_EQ(d_reg1_code + 1, d_reg2_code);
USE(d_reg2_code);
return d_reg1_code / 2;
} }
default: default:
UNREACHABLE(); UNREACHABLE();
} }
#else #else
DCHECK_LT(fp_offset_, fp_count_);
return fp_regs_[fp_offset_++].code(); return fp_regs_[fp_offset_++].code();
#endif #endif
} }
// Stackslots are counted upwards starting from 0 (or the offset set by // Stackslots are counted upwards starting from 0 (or the offset set by
// {SetStackOffset}. If {type} needs more than one stack slot, the lowest // {SetStackOffset}.
// used stack slot is returned. int NumStackSlots(MachineRepresentation type) {
return std::max(1, ElementSizeInBytes(type) / kSystemPointerSize);
}
// Stackslots are counted upwards starting from 0 (or the offset set by
// {SetStackOffset}. If {type} needs more than
// one stack slot, the lowest used stack slot is returned.
int NextStackSlot(MachineRepresentation type) { int NextStackSlot(MachineRepresentation type) {
int num_slots = int num_stack_slots = NumStackSlots(type);
AlignedSlotAllocator::NumSlotsForWidth(ElementSizeInBytes(type)); int offset = stack_offset_;
int slot = slot_allocator_.Allocate(num_slots); stack_offset_ += num_stack_slots;
return slot; return offset;
} }
// Set an offset for the stack slots returned by {NextStackSlot} and // Set an offset for the stack slots returned by {NextStackSlot} and
// {NumStackSlots}. Can only be called before any call to {NextStackSlot}. // {NumStackSlots}. Can only be called before any call to {NextStackSlot}.
void SetStackOffset(int offset) { void SetStackOffset(int num) {
DCHECK_LE(0, offset); DCHECK_LE(0, num);
DCHECK_EQ(0, slot_allocator_.Size()); DCHECK_EQ(0, stack_offset_);
slot_allocator_.AllocateUnaligned(offset); stack_offset_ = num;
} }
int NumStackSlots() const { return slot_allocator_.Size(); } int NumStackSlots() const { return stack_offset_; }
void EndSlotArea() { slot_allocator_.AllocateUnaligned(0); }
private: private:
const int gp_count_; const int gp_count_;
...@@ -222,16 +234,16 @@ class LinkageAllocator { ...@@ -222,16 +234,16 @@ class LinkageAllocator {
const Register* const gp_regs_; const Register* const gp_regs_;
const int fp_count_; const int fp_count_;
#if V8_TARGET_ARCH_ARM
// Use an aligned slot allocator to model ARM FP register aliasing. The slots
// are 32 bits, so 2 slots are required for a D-register, 4 for a Q-register.
AlignedSlotAllocator fp_allocator_;
#else
int fp_offset_ = 0; int fp_offset_ = 0;
#endif
const DoubleRegister* const fp_regs_; const DoubleRegister* const fp_regs_;
AlignedSlotAllocator slot_allocator_; #if V8_TARGET_ARCH_ARM
// Track fragments of registers below fp_offset_ here. There can only be one
// extra double register.
int extra_double_reg_ = -1;
#endif
int stack_offset_ = 0;
}; };
} // namespace wasm } // namespace wasm
......
...@@ -215,7 +215,6 @@ v8_source_set("unittests_sources") { ...@@ -215,7 +215,6 @@ v8_source_set("unittests_sources") {
"base/threaded-list-unittest.cc", "base/threaded-list-unittest.cc",
"base/utils/random-number-generator-unittest.cc", "base/utils/random-number-generator-unittest.cc",
"base/vlq-base64-unittest.cc", "base/vlq-base64-unittest.cc",
"codegen/aligned-slot-allocator-unittest.cc",
"codegen/code-stub-assembler-unittest.cc", "codegen/code-stub-assembler-unittest.cc",
"codegen/code-stub-assembler-unittest.h", "codegen/code-stub-assembler-unittest.h",
"codegen/register-configuration-unittest.cc", "codegen/register-configuration-unittest.cc",
......
// Copyright 2020 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/codegen/aligned-slot-allocator.h"
#include "src/base/bits.h"
#include "testing/gtest-support.h"
namespace v8 {
namespace internal {
class AlignedSlotAllocatorUnitTest : public ::testing::Test {
public:
AlignedSlotAllocatorUnitTest() = default;
~AlignedSlotAllocatorUnitTest() override = default;
// Helper method to test AlignedSlotAllocator::Allocate.
void Allocate(int size, int expected) {
int next = allocator_.NextSlot(size);
int result = allocator_.Allocate(size);
EXPECT_EQ(next, result); // NextSlot/Allocate are consistent.
EXPECT_EQ(expected, result);
EXPECT_EQ(0, result & (size - 1)); // result is aligned to size.
int slot_end = result + static_cast<int>(base::bits::RoundUpToPowerOfTwo32(
static_cast<uint32_t>(size)));
EXPECT_LE(slot_end, allocator_.Size()); // allocator Size is beyond slot.
}
// Helper method to test AlignedSlotAllocator::AllocateUnaligned.
void AllocateUnaligned(int size, int expected, int expected1, int expected2,
int expected4) {
int size_before = allocator_.Size();
int result = allocator_.AllocateUnaligned(size);
EXPECT_EQ(size_before, result); // AllocateUnaligned/Size are consistent.
EXPECT_EQ(expected, result);
EXPECT_EQ(result + size, allocator_.Size());
EXPECT_EQ(expected1, allocator_.NextSlot(1));
EXPECT_EQ(expected2, allocator_.NextSlot(2));
EXPECT_EQ(expected4, allocator_.NextSlot(4));
}
AlignedSlotAllocator allocator_;
};
TEST_F(AlignedSlotAllocatorUnitTest, Allocate1) {
Allocate(1, 0);
EXPECT_EQ(2, allocator_.NextSlot(2));
EXPECT_EQ(4, allocator_.NextSlot(4));
Allocate(1, 1);
EXPECT_EQ(2, allocator_.NextSlot(2));
EXPECT_EQ(4, allocator_.NextSlot(4));
Allocate(1, 2);
EXPECT_EQ(4, allocator_.NextSlot(2));
EXPECT_EQ(4, allocator_.NextSlot(4));
Allocate(1, 3);
EXPECT_EQ(4, allocator_.NextSlot(2));
EXPECT_EQ(4, allocator_.NextSlot(4));
// Make sure we use 1-fragments.
Allocate(1, 4);
Allocate(2, 6);
Allocate(1, 5);
// Make sure we use 2-fragments.
Allocate(2, 8);
Allocate(1, 10);
Allocate(1, 11);
}
TEST_F(AlignedSlotAllocatorUnitTest, Allocate2) {
Allocate(2, 0);
EXPECT_EQ(2, allocator_.NextSlot(1));
EXPECT_EQ(4, allocator_.NextSlot(4));
Allocate(2, 2);
EXPECT_EQ(4, allocator_.NextSlot(1));
EXPECT_EQ(4, allocator_.NextSlot(4));
// Make sure we use 2-fragments.
Allocate(1, 4);
Allocate(2, 6);
Allocate(2, 8);
}
TEST_F(AlignedSlotAllocatorUnitTest, Allocate4) {
Allocate(4, 0);
EXPECT_EQ(4, allocator_.NextSlot(1));
EXPECT_EQ(4, allocator_.NextSlot(2));
Allocate(1, 4);
Allocate(4, 8);
Allocate(2, 6);
Allocate(4, 12);
}
TEST_F(AlignedSlotAllocatorUnitTest, AllocateUnaligned) {
AllocateUnaligned(1, 0, 1, 2, 4);
AllocateUnaligned(1, 1, 2, 2, 4);
Allocate(1, 2);
AllocateUnaligned(2, 3, 5, 6, 8);
// Advance to leave 1- and 2- fragments below Size.
Allocate(4, 8);
// AllocateUnaligned should allocate at the end, and clear fragments.
AllocateUnaligned(0, 12, 12, 12, 12);
}
TEST_F(AlignedSlotAllocatorUnitTest, LargeAllocateUnaligned) {
AllocateUnaligned(11, 0, 11, 12, 12);
AllocateUnaligned(11, 11, 22, 22, 24);
AllocateUnaligned(13, 22, 35, 36, 36);
}
TEST_F(AlignedSlotAllocatorUnitTest, Size) {
allocator_.Allocate(1);
EXPECT_EQ(1, allocator_.Size());
// Allocate 2, leaving a fragment at 1. Size should be at 4.
allocator_.Allocate(2);
EXPECT_EQ(4, allocator_.Size());
// Allocate should consume fragment.
EXPECT_EQ(1, allocator_.Allocate(1));
// Size should still be 4.
EXPECT_EQ(4, allocator_.Size());
}
TEST_F(AlignedSlotAllocatorUnitTest, Align) {
EXPECT_EQ(0, allocator_.Align(1));
EXPECT_EQ(0, allocator_.Size());
// Allocate 1 to become misaligned.
Allocate(1, 0);
// 4-align.
EXPECT_EQ(3, allocator_.Align(4));
EXPECT_EQ(4, allocator_.NextSlot(1));
EXPECT_EQ(4, allocator_.NextSlot(2));
EXPECT_EQ(4, allocator_.NextSlot(4));
EXPECT_EQ(4, allocator_.Size());
// Allocate 2 to become misaligned.
Allocate(2, 4);
// 4-align.
EXPECT_EQ(2, allocator_.Align(4));
EXPECT_EQ(8, allocator_.NextSlot(1));
EXPECT_EQ(8, allocator_.NextSlot(2));
EXPECT_EQ(8, allocator_.NextSlot(4));
EXPECT_EQ(8, allocator_.Size());
// No change when we're already aligned.
EXPECT_EQ(0, allocator_.Align(2));
EXPECT_EQ(8, allocator_.NextSlot(1));
EXPECT_EQ(8, allocator_.NextSlot(2));
EXPECT_EQ(8, allocator_.NextSlot(4));
EXPECT_EQ(8, allocator_.Size());
}
} // namespace internal
} // namespace v8
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment