Skip to content

Commit

Permalink
Reland "[compiler][wasm] Align Frame slots to value size"
Browse files Browse the repository at this point in the history
This is a reland of cddaf66

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 <[email protected]>
> Reviewed-by: Georg Neis <[email protected]>
> Reviewed-by: Andreas Haas <[email protected]>
> Cr-Commit-Position: refs/heads/master@{#71644}

Bug: v8:9198
Change-Id: Ib91fa6746370c38496706341e12d05c7bf999389
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2633390
Commit-Queue: Bill Budge <[email protected]>
Reviewed-by: Andreas Haas <[email protected]>
Reviewed-by: Georg Neis <[email protected]>
Cr-Commit-Position: refs/heads/master@{#72195}
  • Loading branch information
Bill Budge authored and Commit Bot committed Jan 20, 2021
1 parent a1616e6 commit 1694925
Show file tree
Hide file tree
Showing 16 changed files with 473 additions and 111 deletions.
2 changes: 2 additions & 0 deletions BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -2519,6 +2519,8 @@ v8_source_set("v8_base_without_compiler") {
"src/builtins/constants-table-builder.cc",
"src/builtins/constants-table-builder.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-inl.h",
"src/codegen/assembler.cc",
Expand Down
125 changes: 125 additions & 0 deletions src/codegen/aligned-slot-allocator.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// 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
71 changes: 71 additions & 0 deletions src/codegen/aligned-slot-allocator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// 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) {
DCHECK_GT(bytes, 0);
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_
2 changes: 1 addition & 1 deletion src/compiler/backend/arm/instruction-selector-arm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ void VisitPairAtomicBinOp(InstructionSelector* selector, Node* node,

void InstructionSelector::VisitStackSlot(Node* node) {
StackSlotRepresentation rep = StackSlotRepresentationOf(node->op());
int slot = frame_->AllocateSpillSlot(rep.size());
int slot = frame_->AllocateSpillSlot(rep.size(), rep.alignment());
OperandGenerator g(this);

Emit(kArchStackSlot, g.DefineAsRegister(node),
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/backend/arm64/instruction-selector-arm64.cc
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,7 @@ int32_t LeftShiftForReducedMultiply(Matcher* m) {

void InstructionSelector::VisitStackSlot(Node* node) {
StackSlotRepresentation rep = StackSlotRepresentationOf(node->op());
int slot = frame_->AllocateSpillSlot(rep.size());
int slot = frame_->AllocateSpillSlot(rep.size(), rep.alignment());
OperandGenerator g(this);

Emit(kArchStackSlot, g.DefineAsRegister(node),
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/backend/ia32/instruction-selector-ia32.cc
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ void VisitRROI8x16SimdShift(InstructionSelector* selector, Node* node,

void InstructionSelector::VisitStackSlot(Node* node) {
StackSlotRepresentation rep = StackSlotRepresentationOf(node->op());
int slot = frame_->AllocateSpillSlot(rep.size());
int slot = frame_->AllocateSpillSlot(rep.size(), rep.alignment());
OperandGenerator g(this);

Emit(kArchStackSlot, g.DefineAsRegister(node),
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/backend/ppc/instruction-selector-ppc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ void VisitBinop(InstructionSelector* selector, Node* node,

void InstructionSelector::VisitStackSlot(Node* node) {
StackSlotRepresentation rep = StackSlotRepresentationOf(node->op());
int slot = frame_->AllocateSpillSlot(rep.size());
int slot = frame_->AllocateSpillSlot(rep.size(), rep.alignment());
OperandGenerator g(this);

Emit(kArchStackSlot, g.DefineAsRegister(node),
Expand Down
6 changes: 4 additions & 2 deletions src/compiler/backend/register-allocator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4519,9 +4519,11 @@ void OperandAssigner::AssignSpillSlots() {
for (SpillRange* range : spill_ranges) {
data()->tick_counter()->TickAndMaybeEnterSafepoint();
if (range == nullptr || range->IsEmpty()) continue;
// Allocate a new operand referring to the spill slot.
if (!range->HasSlot()) {
int index = data()->frame()->AllocateSpillSlot(range->byte_width());
// Allocate a new operand referring to the spill slot, aligned to the
// operand size.
int width = range->byte_width();
int index = data()->frame()->AllocateSpillSlot(width, width);
range->set_assigned_slot(index);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/backend/s390/instruction-selector-s390.cc
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,7 @@ void VisitBinOp(InstructionSelector* selector, Node* node,

void InstructionSelector::VisitStackSlot(Node* node) {
StackSlotRepresentation rep = StackSlotRepresentationOf(node->op());
int slot = frame_->AllocateSpillSlot(rep.size());
int slot = frame_->AllocateSpillSlot(rep.size(), rep.alignment());
OperandGenerator g(this);

Emit(kArchStackSlot, g.DefineAsRegister(node),
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/backend/x64/instruction-selector-x64.cc
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ ArchOpcode GetStoreOpcode(StoreRepresentation store_rep) {

void InstructionSelector::VisitStackSlot(Node* node) {
StackSlotRepresentation rep = StackSlotRepresentationOf(node->op());
int slot = frame_->AllocateSpillSlot(rep.size());
int slot = frame_->AllocateSpillSlot(rep.size(), rep.alignment());
OperandGenerator g(this);

Emit(kArchStackSlot, g.DefineAsRegister(node),
Expand Down
14 changes: 8 additions & 6 deletions src/compiler/frame.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ namespace compiler {

Frame::Frame(int 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),
return_slot_count_(0),
allocated_registers_(nullptr),
allocated_double_registers_(nullptr) {}
allocated_double_registers_(nullptr) {
slot_allocator_.AllocateUnaligned(fixed_frame_size_in_slots);
}

void Frame::AlignFrame(int alignment) {
int alignment_slots = alignment / kSystemPointerSize;
int alignment_slots = AlignedSlotAllocator::NumSlotsForWidth(alignment);
// In the calculations below we assume that alignment_slots is a power of 2.
DCHECK(base::bits::IsPowerOfTwo(alignment_slots));

Expand All @@ -28,11 +29,12 @@ void Frame::AlignFrame(int alignment) {
int return_delta =
alignment_slots - (return_slot_count_ & (alignment_slots - 1));
if (return_delta != alignment_slots) {
frame_slot_count_ += return_delta;
slot_allocator_.Align(alignment_slots);
}
int delta = alignment_slots - (frame_slot_count_ & (alignment_slots - 1));
int delta =
alignment_slots - (slot_allocator_.Size() & (alignment_slots - 1));
if (delta != alignment_slots) {
frame_slot_count_ += delta;
slot_allocator_.Align(alignment_slots);
if (spill_slot_count_ != 0) {
spill_slot_count_ += delta;
}
Expand Down
Loading

0 comments on commit 1694925

Please sign in to comment.