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.
......@@ -55,6 +55,9 @@ class RuntimeFunction;
std::unique_ptr<InstructionBase> Clone() const override; \
void Assign(const InstructionBase& other) override; \
void TypeInstruction(Stack<const Type*>* stack, ControlFlowGraph* cfg) \
const override; \
void RecomputeDefinitionLocations(Stack<DefinitionLocation>* locations, \
Worklist<Block*>* worklist) \
const override;
enum class InstructionKind {
......@@ -63,6 +66,115 @@ enum class InstructionKind {
#undef ENUM_ITEM
};
struct InstructionBase;
class DefinitionLocation {
public:
enum class Kind {
kInvalid,
kParameter,
kPhi,
kInstruction,
};
DefinitionLocation() : kind_(Kind::kInvalid), location_(nullptr), index_(0) {}
static DefinitionLocation Parameter(std::size_t index) {
return DefinitionLocation(Kind::kParameter, nullptr, index);
}
static DefinitionLocation Phi(const Block* block, std::size_t index) {
return DefinitionLocation(Kind::kPhi, block, index);
}
static DefinitionLocation Instruction(const InstructionBase* instruction,
std::size_t index = 0) {
return DefinitionLocation(Kind::kInstruction, instruction, index);
}
Kind GetKind() const { return kind_; }
bool IsValid() const { return kind_ != Kind::kInvalid; }
bool IsParameter() const { return kind_ == Kind::kParameter; }
bool IsPhi() const { return kind_ == Kind::kPhi; }
bool IsInstruction() const { return kind_ == Kind::kInstruction; }
std::size_t GetParameterIndex() const {
DCHECK(IsParameter());
return index_;
}
const Block* GetPhiBlock() const {
DCHECK(IsPhi());
return reinterpret_cast<const Block*>(location_);
}
bool IsPhiFromBlock(const Block* block) const {
return IsPhi() && GetPhiBlock() == block;
}
std::size_t GetPhiIndex() const {
DCHECK(IsPhi());
return index_;
}
const InstructionBase* GetInstruction() const {
DCHECK(IsInstruction());
return reinterpret_cast<const InstructionBase*>(location_);
}
std::size_t GetInstructionIndex() const {
DCHECK(IsInstruction());
return index_;
}
bool operator==(const DefinitionLocation& other) const {
if (kind_ != other.kind_) return false;
if (location_ != other.location_) return false;
return index_ == other.index_;
}
bool operator!=(const DefinitionLocation& other) const {
return !operator==(other);
}
bool operator<(const DefinitionLocation& other) const {
if (kind_ != other.kind_) {
return static_cast<int>(kind_) < static_cast<int>(other.kind_);
}
if (location_ != other.location_) {
return location_ < other.location_;
}
return index_ < other.index_;
}
private:
DefinitionLocation(Kind kind, const void* location, std::size_t index)
: kind_(kind), location_(location), index_(index) {}
Kind kind_;
const void* location_;
std::size_t index_;
};
inline std::ostream& operator<<(std::ostream& stream,
const DefinitionLocation& loc) {
switch (loc.GetKind()) {
case DefinitionLocation::Kind::kInvalid:
return stream << "DefinitionLocation::Invalid()";
case DefinitionLocation::Kind::kParameter:
return stream << "DefinitionLocation::Parameter("
<< loc.GetParameterIndex() << ")";
case DefinitionLocation::Kind::kPhi:
return stream << "DefinitionLocation::Phi(" << std::hex
<< (uint64_t)loc.GetPhiBlock() << std::dec << ", "
<< loc.GetPhiIndex() << ")";
case DefinitionLocation::Kind::kInstruction:
return stream << "DefinitionLocation::Instruction(" << std::hex
<< (uint64_t)loc.GetInstruction() << std::dec << ", "
<< loc.GetInstructionIndex() << ")";
}
}
struct InstructionBase {
InstructionBase() : pos(CurrentSourcePosition::Get()) {}
virtual std::unique_ptr<InstructionBase> Clone() const = 0;
......@@ -71,6 +183,9 @@ struct InstructionBase {
virtual void TypeInstruction(Stack<const Type*>* stack,
ControlFlowGraph* cfg) const = 0;
virtual void RecomputeDefinitionLocations(
Stack<DefinitionLocation>* locations,
Worklist<Block*>* worklist) const = 0;
void InvalidateTransientTypes(Stack<const Type*>* stack) const;
virtual bool IsBlockTerminator() const { return false; }
virtual void AppendSuccessorBlocks(std::vector<Block*>* block_list) const {}
......@@ -141,6 +256,10 @@ class Instruction {
void TypeInstruction(Stack<const Type*>* stack, ControlFlowGraph* cfg) const {
return instruction_->TypeInstruction(stack, cfg);
}
void RecomputeDefinitionLocations(Stack<DefinitionLocation>* locations,
Worklist<Block*>* worklist) const {
instruction_->RecomputeDefinitionLocations(locations, worklist);
}
InstructionBase* operator->() { return instruction_.get(); }
const InstructionBase* operator->() const { return instruction_.get(); }
......@@ -183,6 +302,8 @@ struct PushUninitializedInstruction : InstructionBase {
TORQUE_INSTRUCTION_BOILERPLATE()
explicit PushUninitializedInstruction(const Type* type) : type(type) {}
DefinitionLocation GetValueDefinition() const;
const Type* type;
};
......@@ -193,6 +314,8 @@ struct PushBuiltinPointerInstruction : InstructionBase {
DCHECK(type->IsBuiltinPointerType());
}
DefinitionLocation GetValueDefinition() const;
std::string external_name;
const Type* type;
};
......@@ -202,12 +325,18 @@ struct NamespaceConstantInstruction : InstructionBase {
explicit NamespaceConstantInstruction(NamespaceConstant* constant)
: constant(constant) {}
std::size_t GetValueDefinitionCount() const;
DefinitionLocation GetValueDefinition(std::size_t index) const;
NamespaceConstant* constant;
};
struct LoadReferenceInstruction : InstructionBase {
TORQUE_INSTRUCTION_BOILERPLATE()
explicit LoadReferenceInstruction(const Type* type) : type(type) {}
DefinitionLocation GetValueDefinition() const;
const Type* type;
};
......@@ -224,6 +353,9 @@ struct LoadBitFieldInstruction : InstructionBase {
BitField bit_field)
: bit_field_struct_type(bit_field_struct_type),
bit_field(std::move(bit_field)) {}
DefinitionLocation GetValueDefinition() const;
const BitFieldStructType* bit_field_struct_type;
BitField bit_field;
};
......@@ -236,6 +368,9 @@ struct StoreBitFieldInstruction : InstructionBase {
BitField bit_field)
: bit_field_struct_type(bit_field_struct_type),
bit_field(std::move(bit_field)) {}
DefinitionLocation GetValueDefinition() const;
const BitFieldStructType* bit_field_struct_type;
BitField bit_field;
};
......@@ -249,6 +384,9 @@ struct CallIntrinsicInstruction : InstructionBase {
specialization_types(std::move(specialization_types)),
constexpr_arguments(constexpr_arguments) {}
std::size_t GetValueDefinitionCount() const;
DefinitionLocation GetValueDefinition(std::size_t index) const;
Intrinsic* intrinsic;
TypeVector specialization_types;
std::vector<std::string> constexpr_arguments;
......@@ -266,6 +404,10 @@ struct CallCsaMacroInstruction : InstructionBase {
if (catch_block) block_list->push_back(*catch_block);
}
base::Optional<DefinitionLocation> GetExceptionObjectDefinition() const;
std::size_t GetValueDefinitionCount() const;
DefinitionLocation GetValueDefinition(std::size_t index) const;
Macro* macro;
std::vector<std::string> constexpr_arguments;
base::Optional<Block*> catch_block;
......@@ -290,6 +432,14 @@ struct CallCsaMacroAndBranchInstruction : InstructionBase {
for (Block* block : label_blocks) block_list->push_back(block);
}
std::size_t GetLabelCount() const;
std::size_t GetLabelValueDefinitionCount(std::size_t label) const;
DefinitionLocation GetLabelValueDefinition(std::size_t label,
std::size_t index) const;
std::size_t GetValueDefinitionCount() const;
DefinitionLocation GetValueDefinition(std::size_t index) const;
base::Optional<DefinitionLocation> GetExceptionObjectDefinition() const;
Macro* macro;
std::vector<std::string> constexpr_arguments;
base::Optional<Block*> return_continuation;
......@@ -310,6 +460,10 @@ struct CallBuiltinInstruction : InstructionBase {
if (catch_block) block_list->push_back(*catch_block);
}
std::size_t GetValueDefinitionCount() const;
DefinitionLocation GetValueDefinition(std::size_t index) const;
base::Optional<DefinitionLocation> GetExceptionObjectDefinition() const;
bool is_tailcall;
Builtin* builtin;
size_t argc;
......@@ -323,6 +477,9 @@ struct CallBuiltinPointerInstruction : InstructionBase {
const BuiltinPointerType* type, size_t argc)
: is_tailcall(is_tailcall), type(type), argc(argc) {}
std::size_t GetValueDefinitionCount() const;
DefinitionLocation GetValueDefinition(std::size_t index) const;
bool is_tailcall;
const BuiltinPointerType* type;
size_t argc;
......@@ -342,6 +499,10 @@ struct CallRuntimeInstruction : InstructionBase {
if (catch_block) block_list->push_back(*catch_block);
}
std::size_t GetValueDefinitionCount() const;
DefinitionLocation GetValueDefinition(std::size_t index) const;
base::Optional<DefinitionLocation> GetExceptionObjectDefinition() const;
bool is_tailcall;
RuntimeFunction* runtime_function;
size_t argc;
......@@ -434,6 +595,8 @@ struct UnsafeCastInstruction : InstructionBase {
explicit UnsafeCastInstruction(const Type* destination_type)
: destination_type(destination_type) {}
DefinitionLocation GetValueDefinition() const;
const Type* destination_type;
};
......
......@@ -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