blob: d6c6d4d186af4ab36bb65fa4fa2e9faac8434c7f [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/builtins/constants-table-builder.h"
#include "src/execution/isolate.h"
#include "src/heap/heap-inl.h"
#include "src/heap/heap-layout-inl.h"
#include "src/objects/oddball-inl.h"
#include "src/roots/roots-inl.h"
namespace v8 {
namespace internal {
BuiltinsConstantsTableBuilder::BuiltinsConstantsTableBuilder(Isolate* isolate)
: isolate_(isolate), map_(isolate->heap()) {
// Ensure this is only called once per Isolate.
DCHECK_EQ(ReadOnlyRoots(isolate_).empty_fixed_array(),
isolate_->heap()->builtins_constants_table());
// And that the initial value of the builtins constants table can be treated
// as a constant, which means that codegen will load it using the root
// register.
DCHECK(RootsTable::IsImmortalImmovable(RootIndex::kEmptyFixedArray));
}
uint32_t BuiltinsConstantsTableBuilder::AddObject(Handle<Object> object) {
#ifdef DEBUG
// Roots must not be inserted into the constants table as they are already
// accessibly from the root list.
RootIndex root_list_index;
DCHECK(!isolate_->roots_table().IsRootHandle(object, &root_list_index));
DCHECK_IMPLIES(IsMap(*object),
!HeapLayout::InReadOnlySpace(Cast<HeapObject>(*object)));
// Not yet finalized.
DCHECK_EQ(ReadOnlyRoots(isolate_).empty_fixed_array(),
isolate_->heap()->builtins_constants_table());
// Must be generating embedded builtin code.
DCHECK(isolate_->IsGeneratingEmbeddedBuiltins());
// All code objects should be loaded through the root register or use
// pc-relative addressing.
DCHECK(!IsInstructionStream(*object));
#endif
// This method is called concurrently from both the main thread and
// compilation threads. Constant indices need to be reproducible during
// builtin generation, so the main thread pre-adds all the constants. A lock
// is still needed since the map data structure is still being concurrently
// accessed.
base::MutexGuard guard(&mutex_);
if (ThreadId::Current() != isolate_->thread_id()) {
auto find_result = map_.Find(object);
DCHECK_NOT_NULL(find_result);
return *find_result;
} else {
auto find_result = map_.FindOrInsert(object);
if (!find_result.already_exists) {
DCHECK(IsHeapObject(*object));
*find_result.entry = map_.size() - 1;
}
return *find_result.entry;
}
}
namespace {
void CheckPreconditionsForPatching(Isolate* isolate,
Handle<Object> replacement_object) {
// Roots must not be inserted into the constants table as they are already
// accessible from the root list.
RootIndex root_list_index;
DCHECK(!isolate->roots_table().IsRootHandle(replacement_object,
&root_list_index));
USE(root_list_index);
// Not yet finalized.
DCHECK_EQ(ReadOnlyRoots(isolate).empty_fixed_array(),
isolate->heap()->builtins_constants_table());
DCHECK(isolate->IsGeneratingEmbeddedBuiltins());
}
} // namespace
void BuiltinsConstantsTableBuilder::PatchSelfReference(
DirectHandle<Object> self_reference,
Handle<InstructionStream> code_object) {
CheckPreconditionsForPatching(isolate_, code_object);
DCHECK_EQ(*self_reference, ReadOnlyRoots(isolate_).self_reference_marker());
uint32_t key;
if (map_.Delete(self_reference, &key)) {
DCHECK(IsInstructionStream(*code_object));
map_.Insert(code_object, key);
}
}
void BuiltinsConstantsTableBuilder::PatchBasicBlockCountersReference(
Handle<ByteArray> counters) {
CheckPreconditionsForPatching(isolate_, counters);
uint32_t key;
if (map_.Delete(ReadOnlyRoots(isolate_).basic_block_counters_marker(),
&key)) {
map_.Insert(counters, key);
}
}
void BuiltinsConstantsTableBuilder::Finalize() {
HandleScope handle_scope(isolate_);
DCHECK_EQ(ReadOnlyRoots(isolate_).empty_fixed_array(),
isolate_->heap()->builtins_constants_table());
DCHECK(isolate_->IsGeneratingEmbeddedBuiltins());
// An empty map means there's nothing to do.
if (map_.empty()) return;
DirectHandle<FixedArray> table =
isolate_->factory()->NewFixedArray(map_.size(), AllocationType::kOld);
Builtins* builtins = isolate_->builtins();
ConstantsMap::IteratableScope it_scope(&map_);
for (auto it = it_scope.begin(); it != it_scope.end(); ++it) {
uint32_t index = *it.entry();
Tagged<Object> value = it.key();
if (IsCode(value) && Cast<Code>(value)->kind() == CodeKind::BUILTIN) {
// Replace placeholder code objects with the real builtin.
// See also: SetupIsolateDelegate::PopulateWithPlaceholders.
// TODO(jgruber): Deduplicate placeholders and their corresponding
// builtin.
value = builtins->code(Cast<Code>(value)->builtin_id());
}
DCHECK(IsHeapObject(value));
table->set(index, value);
}
#ifdef DEBUG
for (int i = 0; i < map_.size(); i++) {
DCHECK(IsHeapObject(table->get(i)));
DCHECK_NE(ReadOnlyRoots(isolate_).undefined_value(), table->get(i));
DCHECK_NE(ReadOnlyRoots(isolate_).self_reference_marker(), table->get(i));
DCHECK_NE(ReadOnlyRoots(isolate_).basic_block_counters_marker(),
table->get(i));
}
#endif
isolate_->heap()->SetBuiltinsConstantsTable(*table);
}
} // namespace internal
} // namespace v8