Commit 89b248b6 authored by Nico Hartmann's avatar Nico Hartmann Committed by Commit Bot

[torque] Reduce generated CSA variables and labels

This CL significantly reduces the size of CSA files generated from
torque by introducing only those Phis at block entry that are
required and otherwise uses defined values directly.

To do so it does:
- Define a DefinitionLocation that represents where a value is
  defined.
- For each block compute all the definitions that reach that
  block and introduce a phi iff the reaching definitions for a value
  are not the same for all predecessor blocks.
- In CSAGenerator map all DefinitionLocations to variables, such that
  if the same value is used in multiple blocks, it is mapped to the
  same variable without the need to pass it along the jump. This
  reduces both the arguments passed to Goto, Branch, ... and the
  variables that need to be passed to Bind when the block's label is
  bound. This reduces the number of temporary variables
  significantly. Temporaries are declared outside of blocks now
  in order to be accessible from other blocks.

Drive-by changes:
- Sequences of SetSourcePosition calls are merged if no output is
  generated between them.
- Dead blocks are no longer generated in release builds.

Bug: v8:9861
Change-Id: I5c30e5376e93c424c3ebfc5144a08592d77ae61f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2037444
Commit-Queue: Nico Hartmann <nicohartmann@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66225}
parent 5c7b4d2a
......@@ -625,6 +625,16 @@ class V8_EXPORT_PRIVATE CodeAssembler {
if_false->AddInputs(args...);
Branch(condition, if_true->plain_label(), if_false->plain_label());
}
template <class... T, class... U>
void Branch(TNode<BoolT> condition,
CodeAssemblerParameterizedLabel<T...>* if_true,
std::vector<Node*> args_true,
CodeAssemblerParameterizedLabel<U...>* if_false,
std::vector<Node*> args_false) {
if_true->AddInputsVector(std::move(args_true));
if_false->AddInputsVector(std::move(args_false));
Branch(condition, if_true->plain_label(), if_false->plain_label());
}
template <class... T, class... Args>
void Goto(CodeAssemblerParameterizedLabel<T...>* label, Args... args) {
......@@ -1354,6 +1364,9 @@ class CodeAssemblerParameterizedLabel
private:
friend class CodeAssembler;
void AddInputsVector(std::vector<Node*> inputs) {
CodeAssemblerParameterizedLabelBase::AddInputs(std::move(inputs));
}
void AddInputs(TNode<Types>... inputs) {
CodeAssemblerParameterizedLabelBase::AddInputs(
std::vector<Node*>{inputs...});
......
......@@ -209,6 +209,34 @@ void CfgAssembler::OptimizeCfg() {
[&](Block* b) { return predecessor_count[b->id()] == 0; });
}
void CfgAssembler::ComputeInputDefinitions() {
Worklist<Block*> worklist;
// Setup start block.
Stack<DefinitionLocation> parameter_defs;
for (std::size_t i = 0; i < cfg_.ParameterCount(); ++i) {
parameter_defs.Push(DefinitionLocation::Parameter(i));
}
cfg_.start()->MergeInputDefinitions(parameter_defs, &worklist);
// Run fixpoint algorithm.
while (!worklist.IsEmpty()) {
Block* block = worklist.Dequeue();
Stack<DefinitionLocation> definitions = block->InputDefinitions();
// Propagate through block's instructions.
for (const auto& instruction : block->instructions()) {
instruction.RecomputeDefinitionLocations(&definitions, &worklist);
}
}
for (Block* block : cfg_.blocks()) {
DCHECK_IMPLIES(!block->IsDead(), block->InputDefinitions().Size() ==
block->InputTypes().Size());
USE(block);
}
}
} // namespace torque
} // namespace internal
} // namespace v8
......@@ -53,10 +53,42 @@ class Block {
size_t id() const { return id_; }
bool IsDeferred() const { return is_deferred_; }
void MergeInputDefinitions(const Stack<DefinitionLocation>& input_definitions,
Worklist<Block*>* worklist) {
if (!input_definitions_) {
input_definitions_ = input_definitions;
if (worklist) worklist->Enqueue(this);
return;
}
DCHECK_EQ(input_definitions_->Size(), input_definitions.Size());
bool changed = false;
for (BottomOffset i = 0; i < input_definitions.AboveTop(); ++i) {
auto& current = input_definitions_->Peek(i);
auto& input = input_definitions.Peek(i);
if (current == input) continue;
if (current == DefinitionLocation::Phi(this, i.offset)) continue;
input_definitions_->Poke(i, DefinitionLocation::Phi(this, i.offset));
changed = true;
}
if (changed && worklist) worklist->Enqueue(this);
}
bool HasInputDefinitions() const {
return input_definitions_ != base::nullopt;
}
const Stack<DefinitionLocation>& InputDefinitions() const {
DCHECK(HasInputDefinitions());
return *input_definitions_;
}
bool IsDead() const { return !HasInputDefinitions(); }
private:
ControlFlowGraph* cfg_;
std::vector<Instruction> instructions_;
base::Optional<Stack<const Type*>> input_types_;
base::Optional<Stack<DefinitionLocation>> input_definitions_;
const size_t id_;
bool is_deferred_;
};
......@@ -95,6 +127,9 @@ class ControlFlowGraph {
}
const std::vector<Block*>& blocks() const { return placed_blocks_; }
size_t NumberOfBlockIds() const { return next_block_id_; }
std::size_t ParameterCount() const {
return start_ ? start_->InputTypes().Size() : 0;
}
private:
std::list<Block> blocks_;
......@@ -116,6 +151,7 @@ class CfgAssembler {
}
OptimizeCfg();
DCHECK(CfgIsComplete());
ComputeInputDefinitions();
return cfg_;
}
......@@ -167,6 +203,7 @@ class CfgAssembler {
void PrintCurrentStack(std::ostream& s) { s << "stack: " << current_stack_; }
void OptimizeCfg();
void ComputeInputDefinitions();
private:
friend class CfgAssemblerScopedTemporaryBlock;
......
This diff is collapsed.
......@@ -19,7 +19,8 @@ class CSAGenerator {
CSAGenerator(const ControlFlowGraph& cfg, std::ostream& out,
base::Optional<Builtin::Kind> linkage = base::nullopt)
: cfg_(cfg),
out_(out),
out_(&out),
out_decls_(&out),
linkage_(linkage),
previous_position_(SourcePosition::Invalid()) {}
base::Optional<Stack<std::string>> EmitGraph(Stack<std::string> parameters);
......@@ -31,22 +32,56 @@ class CSAGenerator {
private:
const ControlFlowGraph& cfg_;
std::ostream& out_;
std::ostream* out_;
std::ostream* out_decls_;
size_t fresh_id_ = 0;
base::Optional<Builtin::Kind> linkage_;
SourcePosition previous_position_;
std::map<DefinitionLocation, std::string> location_map_;
std::string DefinitionToVariable(const DefinitionLocation& location) {
if (location.IsPhi()) {
std::stringstream stream;
stream << "phi_bb" << location.GetPhiBlock()->id() << "_"
<< location.GetPhiIndex();
return stream.str();
} else if (location.IsParameter()) {
auto it = location_map_.find(location);
DCHECK_NE(it, location_map_.end());
return it->second;
} else {
DCHECK(location.IsInstruction());
auto it = location_map_.find(location);
if (it == location_map_.end()) {
it = location_map_.insert(std::make_pair(location, FreshNodeName()))
.first;
}
return it->second;
}
}
void SetDefinitionVariable(const DefinitionLocation& definition,
const std::string& str) {
DCHECK_EQ(location_map_.find(definition), location_map_.end());
location_map_.insert(std::make_pair(definition, str));
}
std::ostream& out() { return *out_; }
std::ostream& decls() { return *out_decls_; }
bool IsEmptyInstruction(const Instruction& instruction);
void EmitSourcePosition(SourcePosition pos, bool always_emit = false);
std::string PreCallableExceptionPreparation(
base::Optional<Block*> catch_block);
void PostCallableExceptionPreparation(const std::string& catch_name,
const Type* return_type,
base::Optional<Block*> catch_block,
Stack<std::string>* stack);
void PostCallableExceptionPreparation(
const std::string& catch_name, const Type* return_type,
base::Optional<Block*> catch_block, Stack<std::string>* stack,
const base::Optional<DefinitionLocation>& exception_object_definition);
std::string FreshNodeName() { return "tmp" + std::to_string(fresh_id_++); }
std::string FreshCatchName() { return "catch" + std::to_string(fresh_id_++); }
std::string FreshLabelName() { return "label" + std::to_string(fresh_id_++); }
std::string BlockName(const Block* block) {
return "block" + std::to_string(block->id());
}
......
......@@ -137,7 +137,7 @@ void ImplementationVisitor::Visit(NamespaceConstant* decl) {
assembler_ = base::nullopt;
source_out() << "return ";
source_out() << " return ";
CSAGenerator::EmitCSAValue(return_result, values, source_out());
source_out() << ";\n";
source_out() << "}\n\n";
......
This diff is collapsed.
This diff is collapsed.
......@@ -6,10 +6,10 @@
#define V8_TORQUE_UTILS_H_
#include <ostream>
#include <queue>
#include <streambuf>
#include <string>
#include <unordered_set>
#include <vector>
#include "src/base/functional.h"
#include "src/base/optional.h"
......@@ -172,6 +172,13 @@ void PrintCommaSeparatedList(std::ostream& os, const T& list) {
struct BottomOffset {
size_t offset;
BottomOffset(std::nullptr_t zero = 0) // NOLINT(runtime/explicit)
: offset(0) {}
explicit BottomOffset(std::size_t offset) : offset(offset) {}
BottomOffset& operator=(std::size_t offset) {
this->offset = offset;
return *this;
}
BottomOffset& operator++() {
++offset;
return *this;
......@@ -488,6 +495,36 @@ class ResidueClass {
static const size_t kMaxModulusLog2 = 8 * sizeof(size_t);
};
template <typename T>
class Worklist {
public:
bool IsEmpty() const {
DCHECK_EQ(queue_.size(), contained_.size());
return queue_.empty();
}
bool Enqueue(T value) {
if (contained_.find(value) != contained_.end()) return false;
queue_.push(value);
contained_.insert(value);
DCHECK_EQ(queue_.size(), contained_.size());
return true;
}
T Dequeue() {
DCHECK(!IsEmpty());
T value = queue_.front();
queue_.pop();
contained_.erase(value);
DCHECK_EQ(queue_.size(), contained_.size());
return value;
}
private:
std::queue<T> queue_;
std::unordered_set<T> contained_;
};
} // namespace torque
} // 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