blob: 6acef3018cde694fd54d0b570ac2f59faf243f02 [file] [log] [blame]
// Copyright 2018 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/torque/instructions.h"
#include "src/torque/cfg.h"
#include "src/torque/type-oracle.h"
namespace v8 {
namespace internal {
namespace torque {
#define TORQUE_INSTRUCTION_BOILERPLATE_DEFINITIONS(Name) \
const InstructionKind Name::kKind = InstructionKind::k##Name; \
std::unique_ptr<InstructionBase> Name::Clone() const { \
return std::unique_ptr<InstructionBase>(new Name(*this)); \
} \
void Name::Assign(const InstructionBase& other) { \
*this = static_cast<const Name&>(other); \
}
TORQUE_INSTRUCTION_LIST(TORQUE_INSTRUCTION_BOILERPLATE_DEFINITIONS)
#undef TORQUE_INSTRUCTION_BOILERPLATE_DEFINITIONS
namespace {
void ExpectType(const Type* expected, const Type* actual) {
if (expected != actual) {
ReportError("expected type ", *expected, " but found ", *actual);
}
}
void ExpectSubtype(const Type* subtype, const Type* supertype) {
if (!subtype->IsSubtypeOf(supertype)) {
ReportError("type ", *subtype, " is not a subtype of ", *supertype);
}
}
} // namespace
void PeekInstruction::TypeInstruction(Stack<const Type*>* stack,
ControlFlowGraph* cfg) const {
const Type* type = stack->Peek(slot);
if (widened_type) {
if (type->IsTopType()) {
const TopType* top_type = TopType::cast(type);
ReportError("use of " + top_type->reason());
}
ExpectSubtype(type, *widened_type);
type = *widened_type;
}
stack->Push(type);
}
void PeekInstruction::RecomputeDefinitionLocations(
Stack<DefinitionLocation>* locations, Worklist<Block*>* worklist) const {
locations->Push(locations->Peek(slot));
}
void PokeInstruction::TypeInstruction(Stack<const Type*>* stack,
ControlFlowGraph* cfg) const {
const Type* type = stack->Top();
if (widened_type) {
ExpectSubtype(type, *widened_type);
type = *widened_type;
}
stack->Poke(slot, type);
stack->Pop();
}
void PokeInstruction::RecomputeDefinitionLocations(
Stack<DefinitionLocation>* locations, Worklist<Block*>* worklist) const {
locations->Poke(slot, locations->Pop());
}
void DeleteRangeInstruction::TypeInstruction(Stack<const Type*>* stack,
ControlFlowGraph* cfg) const {
stack->DeleteRange(range);
}
void DeleteRangeInstruction::RecomputeDefinitionLocations(
Stack<DefinitionLocation>* locations, Worklist<Block*>* worklist) const {
locations->DeleteRange(range);
}
void PushUninitializedInstruction::TypeInstruction(
Stack<const Type*>* stack, ControlFlowGraph* cfg) const {
stack->Push(type);
}
void PushUninitializedInstruction::RecomputeDefinitionLocations(
Stack<DefinitionLocation>* locations, Worklist<Block*>* worklist) const {
locations->Push(GetValueDefinition());
}
DefinitionLocation PushUninitializedInstruction::GetValueDefinition() const {
return DefinitionLocation::Instruction(this, 0);
}
void PushBuiltinPointerInstruction::TypeInstruction(
Stack<const Type*>* stack, ControlFlowGraph* cfg) const {
stack->Push(type);
}
void PushBuiltinPointerInstruction::RecomputeDefinitionLocations(
Stack<DefinitionLocation>* locations, Worklist<Block*>* worklist) const {
locations->Push(GetValueDefinition());
}
DefinitionLocation PushBuiltinPointerInstruction::GetValueDefinition() const {
return DefinitionLocation::Instruction(this, 0);
}
void NamespaceConstantInstruction::TypeInstruction(
Stack<const Type*>* stack, ControlFlowGraph* cfg) const {
stack->PushMany(LowerType(constant->type()));
}
void NamespaceConstantInstruction::RecomputeDefinitionLocations(
Stack<DefinitionLocation>* locations, Worklist<Block*>* worklist) const {
for (std::size_t i = 0; i < GetValueDefinitionCount(); ++i) {
locations->Push(GetValueDefinition(i));
}
}
std::size_t NamespaceConstantInstruction::GetValueDefinitionCount() const {
return LowerType(constant->type()).size();
}
DefinitionLocation NamespaceConstantInstruction::GetValueDefinition(
std::size_t index) const {
DCHECK_LT(index, GetValueDefinitionCount());
return DefinitionLocation::Instruction(this, index);
}
void InstructionBase::InvalidateTransientTypes(
Stack<const Type*>* stack) const {
auto current = stack->begin();
while (current != stack->end()) {
if ((*current)->IsTransient()) {
std::stringstream stream;
stream << "type " << **current
<< " is made invalid by transitioning callable invocation at "
<< PositionAsString(pos);
*current = TypeOracle::GetTopType(stream.str(), *current);
}
++current;
}
}
void CallIntrinsicInstruction::TypeInstruction(Stack<const Type*>* stack,
ControlFlowGraph* cfg) const {
std::vector<const Type*> parameter_types =
LowerParameterTypes(intrinsic->signature().parameter_types);
for (intptr_t i = parameter_types.size() - 1; i >= 0; --i) {
const Type* arg_type = stack->Pop();
const Type* parameter_type = parameter_types.back();
parameter_types.pop_back();
if (arg_type != parameter_type) {
ReportError("parameter ", i, ": expected type ", *parameter_type,
" but found type ", *arg_type);
}
}
if (intrinsic->IsTransitioning()) {
InvalidateTransientTypes(stack);
}
stack->PushMany(LowerType(intrinsic->signature().return_type));
}
void CallIntrinsicInstruction::RecomputeDefinitionLocations(
Stack<DefinitionLocation>* locations, Worklist<Block*>* worklist) const {
auto parameter_types =
LowerParameterTypes(intrinsic->signature().parameter_types);
locations->PopMany(parameter_types.size());
for (std::size_t i = 0; i < GetValueDefinitionCount(); ++i) {
locations->Push(DefinitionLocation::Instruction(this, i));
}
}
std::size_t CallIntrinsicInstruction::GetValueDefinitionCount() const {
return LowerType(intrinsic->signature().return_type).size();
}
DefinitionLocation CallIntrinsicInstruction::GetValueDefinition(
std::size_t index) const {
DCHECK_LT(index, GetValueDefinitionCount());
return DefinitionLocation::Instruction(this, index);
}
void CallCsaMacroInstruction::TypeInstruction(Stack<const Type*>* stack,
ControlFlowGraph* cfg) const {
std::vector<const Type*> parameter_types =
LowerParameterTypes(macro->signature().parameter_types);
for (intptr_t i = parameter_types.size() - 1; i >= 0; --i) {
const Type* arg_type = stack->Pop();
const Type* parameter_type = parameter_types.back();
parameter_types.pop_back();
if (arg_type != parameter_type) {
ReportError("parameter ", i, ": expected type ", *parameter_type,
" but found type ", *arg_type);
}
}
if (macro->IsTransitioning()) {
InvalidateTransientTypes(stack);
}
if (catch_block) {
Stack<const Type*> catch_stack = *stack;
catch_stack.Push(TypeOracle::GetJSAnyType());
(*catch_block)->SetInputTypes(catch_stack);
}
stack->PushMany(LowerType(macro->signature().return_type));
}
void CallCsaMacroInstruction::RecomputeDefinitionLocations(
Stack<DefinitionLocation>* locations, Worklist<Block*>* worklist) const {
auto parameter_types =
LowerParameterTypes(macro->signature().parameter_types);
locations->PopMany(parameter_types.size());
if (catch_block) {
locations->Push(*GetExceptionObjectDefinition());
(*catch_block)->MergeInputDefinitions(*locations, worklist);
locations->Pop();
}
for (std::size_t i = 0; i < GetValueDefinitionCount(); ++i) {
locations->Push(GetValueDefinition(i));
}
}
base::Optional<DefinitionLocation>
CallCsaMacroInstruction::GetExceptionObjectDefinition() const {
if (!catch_block) return base::nullopt;
return DefinitionLocation::Instruction(this, GetValueDefinitionCount());
}
std::size_t CallCsaMacroInstruction::GetValueDefinitionCount() const {
return LowerType(macro->signature().return_type).size();
}
DefinitionLocation CallCsaMacroInstruction::GetValueDefinition(
std::size_t index) const {
DCHECK_LT(index, GetValueDefinitionCount());
return DefinitionLocation::Instruction(this, index);
}
void CallCsaMacroAndBranchInstruction::TypeInstruction(
Stack<const Type*>* stack, ControlFlowGraph* cfg) const {
std::vector<const Type*> parameter_types =
LowerParameterTypes(macro->signature().parameter_types);
for (intptr_t i = parameter_types.size() - 1; i >= 0; --i) {
const Type* arg_type = stack->Pop();
const Type* parameter_type = parameter_types.back();
parameter_types.pop_back();
if (arg_type != parameter_type) {
ReportError("parameter ", i, ": expected type ", *parameter_type,
" but found type ", *arg_type);
}
}
if (label_blocks.size() != macro->signature().labels.size()) {
ReportError("wrong number of labels");
}
for (size_t i = 0; i < label_blocks.size(); ++i) {
Stack<const Type*> continuation_stack = *stack;
continuation_stack.PushMany(
LowerParameterTypes(macro->signature().labels[i].types));
label_blocks[i]->SetInputTypes(std::move(continuation_stack));
}
if (macro->IsTransitioning()) {
InvalidateTransientTypes(stack);
}
if (catch_block) {
Stack<const Type*> catch_stack = *stack;
catch_stack.Push(TypeOracle::GetJSAnyType());
(*catch_block)->SetInputTypes(catch_stack);
}
if (macro->signature().return_type != TypeOracle::GetNeverType()) {
Stack<const Type*> return_stack = *stack;
return_stack.PushMany(LowerType(macro->signature().return_type));
if (return_continuation == base::nullopt) {
ReportError("missing return continuation.");
}
(*return_continuation)->SetInputTypes(return_stack);
} else {
if (return_continuation != base::nullopt) {
ReportError("unreachable return continuation.");
}
}
}
void CallCsaMacroAndBranchInstruction::RecomputeDefinitionLocations(
Stack<DefinitionLocation>* locations, Worklist<Block*>* worklist) const {
auto parameter_types =
LowerParameterTypes(macro->signature().parameter_types);
locations->PopMany(parameter_types.size());
for (std::size_t label_index = 0; label_index < label_blocks.size();
++label_index) {
const std::size_t count = GetLabelValueDefinitionCount(label_index);
for (std::size_t i = 0; i < count; ++i) {
locations->Push(GetLabelValueDefinition(label_index, i));
}
label_blocks[label_index]->MergeInputDefinitions(*locations, worklist);
locations->PopMany(count);
}
if (catch_block) {
locations->Push(*GetExceptionObjectDefinition());
(*catch_block)->MergeInputDefinitions(*locations, worklist);
locations->Pop();
}
if (macro->signature().return_type != TypeOracle::GetNeverType()) {
if (return_continuation) {
const std::size_t count = GetValueDefinitionCount();
for (std::size_t i = 0; i < count; ++i) {
locations->Push(GetValueDefinition(i));
}
(*return_continuation)->MergeInputDefinitions(*locations, worklist);
locations->PopMany(count);
}
}
}
std::size_t CallCsaMacroAndBranchInstruction::GetLabelCount() const {
return label_blocks.size();
}
std::size_t CallCsaMacroAndBranchInstruction::GetLabelValueDefinitionCount(
std::size_t label) const {
DCHECK_LT(label, GetLabelCount());
return LowerParameterTypes(macro->signature().labels[label].types).size();
}
DefinitionLocation CallCsaMacroAndBranchInstruction::GetLabelValueDefinition(
std::size_t label, std::size_t index) const {
DCHECK_LT(index, GetLabelValueDefinitionCount(label));
std::size_t offset = GetValueDefinitionCount() + (catch_block ? 1 : 0);
for (std::size_t label_index = 0; label_index < label; ++label_index) {
offset += GetLabelValueDefinitionCount(label_index);
}
return DefinitionLocation::Instruction(this, offset + index);
}
std::size_t CallCsaMacroAndBranchInstruction::GetValueDefinitionCount() const {
if (macro->signature().return_type == TypeOracle::GetNeverType()) return 0;
if (!return_continuation) return 0;
return LowerType(macro->signature().return_type).size();
}
DefinitionLocation CallCsaMacroAndBranchInstruction::GetValueDefinition(
std::size_t index) const {
DCHECK_LT(index, GetValueDefinitionCount());
return DefinitionLocation::Instruction(this, index);
}
base::Optional<DefinitionLocation>
CallCsaMacroAndBranchInstruction::GetExceptionObjectDefinition() const {
if (!catch_block) return base::nullopt;
return DefinitionLocation::Instruction(this, GetValueDefinitionCount());
}
void CallBuiltinInstruction::TypeInstruction(Stack<const Type*>* stack,
ControlFlowGraph* cfg) const {
std::vector<const Type*> argument_types = stack->PopMany(argc);
if (argument_types !=
LowerParameterTypes(builtin->signature().parameter_types)) {
ReportError("wrong argument types");
}
if (builtin->IsTransitioning()) {
InvalidateTransientTypes(stack);
}
if (catch_block) {
Stack<const Type*> catch_stack = *stack;
catch_stack.Push(TypeOracle::GetJSAnyType());
(*catch_block)->SetInputTypes(catch_stack);
}
stack->PushMany(LowerType(builtin->signature().return_type));
}
void CallBuiltinInstruction::RecomputeDefinitionLocations(
Stack<DefinitionLocation>* locations, Worklist<Block*>* worklist) const {
locations->PopMany(argc);
if (catch_block) {
locations->Push(*GetExceptionObjectDefinition());
(*catch_block)->MergeInputDefinitions(*locations, worklist);
locations->Pop();
}
for (std::size_t i = 0; i < GetValueDefinitionCount(); ++i) {
locations->Push(GetValueDefinition(i));
}
}
std::size_t CallBuiltinInstruction::GetValueDefinitionCount() const {
return LowerType(builtin->signature().return_type).size();
}
DefinitionLocation CallBuiltinInstruction::GetValueDefinition(
std::size_t index) const {
DCHECK_LT(index, GetValueDefinitionCount());
return DefinitionLocation::Instruction(this, index);
}
base::Optional<DefinitionLocation>
CallBuiltinInstruction::GetExceptionObjectDefinition() const {
if (!catch_block) return base::nullopt;
return DefinitionLocation::Instruction(this, GetValueDefinitionCount());
}
void CallBuiltinPointerInstruction::TypeInstruction(
Stack<const Type*>* stack, ControlFlowGraph* cfg) const {
std::vector<const Type*> argument_types = stack->PopMany(argc);
const BuiltinPointerType* f = BuiltinPointerType::DynamicCast(stack->Pop());
if (!f) ReportError("expected function pointer type");
if (argument_types != LowerParameterTypes(f->parameter_types())) {
ReportError("wrong argument types");
}
DCHECK_EQ(type, f);
// TODO(tebbi): Only invalidate transient types if the function pointer type
// is transitioning.
InvalidateTransientTypes(stack);
stack->PushMany(LowerType(f->return_type()));
}
void CallBuiltinPointerInstruction::RecomputeDefinitionLocations(
Stack<DefinitionLocation>* locations, Worklist<Block*>* worklist) const {
locations->PopMany(argc + 1);
for (std::size_t i = 0; i < GetValueDefinitionCount(); ++i) {
locations->Push(GetValueDefinition(i));
}
}
std::size_t CallBuiltinPointerInstruction::GetValueDefinitionCount() const {
return LowerType(type->return_type()).size();
}
DefinitionLocation CallBuiltinPointerInstruction::GetValueDefinition(
std::size_t index) const {
DCHECK_LT(index, GetValueDefinitionCount());
return DefinitionLocation::Instruction(this, index);
}
void CallRuntimeInstruction::TypeInstruction(Stack<const Type*>* stack,
ControlFlowGraph* cfg) const {
std::vector<const Type*> argument_types = stack->PopMany(argc);
if (argument_types !=
LowerParameterTypes(runtime_function->signature().parameter_types,
argc)) {
ReportError("wrong argument types");
}
if (runtime_function->IsTransitioning()) {
InvalidateTransientTypes(stack);
}
if (catch_block) {
Stack<const Type*> catch_stack = *stack;
catch_stack.Push(TypeOracle::GetJSAnyType());
(*catch_block)->SetInputTypes(catch_stack);
}
const Type* return_type = runtime_function->signature().return_type;
if (return_type != TypeOracle::GetNeverType()) {
stack->PushMany(LowerType(return_type));
}
}
void CallRuntimeInstruction::RecomputeDefinitionLocations(
Stack<DefinitionLocation>* locations, Worklist<Block*>* worklist) const {
locations->PopMany(argc);
if (catch_block) {
locations->Push(*GetExceptionObjectDefinition());
(*catch_block)->MergeInputDefinitions(*locations, worklist);
locations->Pop();
}
const Type* return_type = runtime_function->signature().return_type;
if (return_type != TypeOracle::GetNeverType()) {
for (std::size_t i = 0; i < GetValueDefinitionCount(); ++i) {
locations->Push(GetValueDefinition(i));
}
}
}
std::size_t CallRuntimeInstruction::GetValueDefinitionCount() const {
const Type* return_type = runtime_function->signature().return_type;
if (return_type == TypeOracle::GetNeverType()) return 0;
return LowerType(return_type).size();
}
DefinitionLocation CallRuntimeInstruction::GetValueDefinition(
std::size_t index) const {
DCHECK_LT(index, GetValueDefinitionCount());
return DefinitionLocation::Instruction(this, index);
}
base::Optional<DefinitionLocation>
CallRuntimeInstruction::GetExceptionObjectDefinition() const {
if (!catch_block) return base::nullopt;
return DefinitionLocation::Instruction(this, GetValueDefinitionCount());
}
void BranchInstruction::TypeInstruction(Stack<const Type*>* stack,
ControlFlowGraph* cfg) const {
const Type* condition_type = stack->Pop();
if (condition_type != TypeOracle::GetBoolType()) {
ReportError("condition has to have type bool");
}
if_true->SetInputTypes(*stack);
if_false->SetInputTypes(*stack);
}
void BranchInstruction::RecomputeDefinitionLocations(
Stack<DefinitionLocation>* locations, Worklist<Block*>* worklist) const {
locations->Pop();
if_true->MergeInputDefinitions(*locations, worklist);
if_false->MergeInputDefinitions(*locations, worklist);
}
void ConstexprBranchInstruction::TypeInstruction(Stack<const Type*>* stack,
ControlFlowGraph* cfg) const {
if_true->SetInputTypes(*stack);
if_false->SetInputTypes(*stack);
}
void ConstexprBranchInstruction::RecomputeDefinitionLocations(
Stack<DefinitionLocation>* locations, Worklist<Block*>* worklist) const {
if_true->MergeInputDefinitions(*locations, worklist);
if_false->MergeInputDefinitions(*locations, worklist);
}
void GotoInstruction::TypeInstruction(Stack<const Type*>* stack,
ControlFlowGraph* cfg) const {
destination->SetInputTypes(*stack);
}
void GotoInstruction::RecomputeDefinitionLocations(
Stack<DefinitionLocation>* locations, Worklist<Block*>* worklist) const {
destination->MergeInputDefinitions(*locations, worklist);
}
void GotoExternalInstruction::TypeInstruction(Stack<const Type*>* stack,
ControlFlowGraph* cfg) const {
if (variable_names.size() != stack->Size()) {
ReportError("goto external label with wrong parameter count.");
}
}
void GotoExternalInstruction::RecomputeDefinitionLocations(
Stack<DefinitionLocation>* locations, Worklist<Block*>* worklist) const {}
void ReturnInstruction::TypeInstruction(Stack<const Type*>* stack,
ControlFlowGraph* cfg) const {
cfg->SetReturnType(stack->Pop());
}
void ReturnInstruction::RecomputeDefinitionLocations(
Stack<DefinitionLocation>* locations, Worklist<Block*>* worklist) const {
locations->Pop();
}
void PrintConstantStringInstruction::TypeInstruction(
Stack<const Type*>* stack, ControlFlowGraph* cfg) const {}
void PrintConstantStringInstruction::RecomputeDefinitionLocations(
Stack<DefinitionLocation>* locations, Worklist<Block*>* worklist) const {}
void AbortInstruction::TypeInstruction(Stack<const Type*>* stack,
ControlFlowGraph* cfg) const {}
void AbortInstruction::RecomputeDefinitionLocations(
Stack<DefinitionLocation>* locations, Worklist<Block*>* worklist) const {}
void UnsafeCastInstruction::TypeInstruction(Stack<const Type*>* stack,
ControlFlowGraph* cfg) const {
stack->Poke(stack->AboveTop() - 1, destination_type);
}
void UnsafeCastInstruction::RecomputeDefinitionLocations(
Stack<DefinitionLocation>* locations, Worklist<Block*>* worklist) const {
locations->Poke(locations->AboveTop() - 1, GetValueDefinition());
}
DefinitionLocation UnsafeCastInstruction::GetValueDefinition() const {
return DefinitionLocation::Instruction(this, 0);
}
void LoadReferenceInstruction::TypeInstruction(Stack<const Type*>* stack,
ControlFlowGraph* cfg) const {
ExpectType(TypeOracle::GetIntPtrType(), stack->Pop());
ExpectSubtype(stack->Pop(), TypeOracle::GetHeapObjectType());
DCHECK_EQ(std::vector<const Type*>{type}, LowerType(type));
stack->Push(type);
}
void LoadReferenceInstruction::RecomputeDefinitionLocations(
Stack<DefinitionLocation>* locations, Worklist<Block*>* worklist) const {
locations->Pop();
locations->Pop();
locations->Push(GetValueDefinition());
}
DefinitionLocation LoadReferenceInstruction::GetValueDefinition() const {
return DefinitionLocation::Instruction(this, 0);
}
void StoreReferenceInstruction::TypeInstruction(Stack<const Type*>* stack,
ControlFlowGraph* cfg) const {
ExpectSubtype(stack->Pop(), type);
ExpectType(TypeOracle::GetIntPtrType(), stack->Pop());
ExpectSubtype(stack->Pop(), TypeOracle::GetHeapObjectType());
}
void StoreReferenceInstruction::RecomputeDefinitionLocations(
Stack<DefinitionLocation>* locations, Worklist<Block*>* worklist) const {
locations->Pop();
locations->Pop();
locations->Pop();
}
void LoadBitFieldInstruction::TypeInstruction(Stack<const Type*>* stack,
ControlFlowGraph* cfg) const {
ExpectType(bit_field_struct_type, stack->Pop());
stack->Push(bit_field.name_and_type.type);
}
void LoadBitFieldInstruction::RecomputeDefinitionLocations(
Stack<DefinitionLocation>* locations, Worklist<Block*>* worklist) const {
locations->Pop();
locations->Push(GetValueDefinition());
}
DefinitionLocation LoadBitFieldInstruction::GetValueDefinition() const {
return DefinitionLocation::Instruction(this, 0);
}
void StoreBitFieldInstruction::TypeInstruction(Stack<const Type*>* stack,
ControlFlowGraph* cfg) const {
ExpectSubtype(bit_field.name_and_type.type, stack->Pop());
ExpectType(bit_field_struct_type, stack->Pop());
stack->Push(bit_field_struct_type);
}
void StoreBitFieldInstruction::RecomputeDefinitionLocations(
Stack<DefinitionLocation>* locations, Worklist<Block*>* worklist) const {
locations->Pop();
locations->Pop();
locations->Push(GetValueDefinition());
}
DefinitionLocation StoreBitFieldInstruction::GetValueDefinition() const {
return DefinitionLocation::Instruction(this, 0);
}
bool CallRuntimeInstruction::IsBlockTerminator() const {
return is_tailcall || runtime_function->signature().return_type ==
TypeOracle::GetNeverType();
}
} // namespace torque
} // namespace internal
} // namespace v8