1// Copyright 2017 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "src/builtins/builtins.h" 6#include "src/builtins/profile-data-reader.h" 7#include "src/codegen/assembler-inl.h" 8#include "src/codegen/interface-descriptors.h" 9#include "src/codegen/macro-assembler-inl.h" 10#include "src/codegen/macro-assembler.h" 11#include "src/compiler/code-assembler.h" 12#include "src/execution/isolate.h" 13#include "src/handles/handles-inl.h" 14#include "src/heap/heap-inl.h" 15#include "src/init/setup-isolate.h" 16#include "src/interpreter/bytecodes.h" 17#include "src/interpreter/interpreter-generator.h" 18#include "src/interpreter/interpreter.h" 19#include "src/objects/objects-inl.h" 20#include "src/objects/shared-function-info.h" 21#include "src/objects/smi.h" 22 23namespace v8 { 24namespace internal { 25 26// Forward declarations for C++ builtins. 27#define FORWARD_DECLARE(Name) \ 28 Address Builtin_##Name(int argc, Address* args, Isolate* isolate); 29BUILTIN_LIST_C(FORWARD_DECLARE) 30#undef FORWARD_DECLARE 31 32namespace { 33 34const int kBufferSize = 128 * KB; 35 36AssemblerOptions BuiltinAssemblerOptions(Isolate* isolate, Builtin builtin) { 37 AssemblerOptions options = AssemblerOptions::Default(isolate); 38 CHECK(!options.isolate_independent_code); 39 CHECK(!options.use_pc_relative_calls_and_jumps); 40 CHECK(!options.collect_win64_unwind_info); 41 42 if (!isolate->IsGeneratingEmbeddedBuiltins()) { 43 return options; 44 } 45 46 const base::AddressRegion& code_region = isolate->heap()->code_region(); 47 bool pc_relative_calls_fit_in_code_range = 48 !code_region.is_empty() && 49 std::ceil(static_cast<float>(code_region.size() / MB)) <= 50 kMaxPCRelativeCodeRangeInMB; 51 52 options.isolate_independent_code = true; 53 options.use_pc_relative_calls_and_jumps = pc_relative_calls_fit_in_code_range; 54 options.collect_win64_unwind_info = true; 55 56 return options; 57} 58 59using MacroAssemblerGenerator = void (*)(MacroAssembler*); 60using CodeAssemblerGenerator = void (*)(compiler::CodeAssemblerState*); 61 62Handle<Code> BuildPlaceholder(Isolate* isolate, Builtin builtin) { 63 HandleScope scope(isolate); 64 byte buffer[kBufferSize]; 65 MacroAssembler masm(isolate, CodeObjectRequired::kYes, 66 ExternalAssemblerBuffer(buffer, kBufferSize)); 67 DCHECK(!masm.has_frame()); 68 { 69 FrameScope frame_scope(&masm, StackFrame::NO_FRAME_TYPE); 70 // The contents of placeholder don't matter, as long as they don't create 71 // embedded constants or external references. 72 masm.Move(kJavaScriptCallCodeStartRegister, Smi::zero()); 73 masm.Call(kJavaScriptCallCodeStartRegister); 74 } 75 CodeDesc desc; 76 masm.GetCode(isolate, &desc); 77 Handle<Code> code = Factory::CodeBuilder(isolate, desc, CodeKind::BUILTIN) 78 .set_self_reference(masm.CodeObject()) 79 .set_builtin(builtin) 80 .Build(); 81 return scope.CloseAndEscape(code); 82} 83 84Code BuildWithMacroAssembler(Isolate* isolate, Builtin builtin, 85 MacroAssemblerGenerator generator, 86 const char* s_name) { 87 HandleScope scope(isolate); 88 // Canonicalize handles, so that we can share constant pool entries pointing 89 // to code targets without dereferencing their handles. 90 CanonicalHandleScope canonical(isolate); 91 byte buffer[kBufferSize]; 92 93 MacroAssembler masm(isolate, BuiltinAssemblerOptions(isolate, builtin), 94 CodeObjectRequired::kYes, 95 ExternalAssemblerBuffer(buffer, kBufferSize)); 96 masm.set_builtin(builtin); 97 DCHECK(!masm.has_frame()); 98 masm.CodeEntry(); 99 generator(&masm); 100 101 int handler_table_offset = 0; 102 103 // JSEntry builtins are a special case and need to generate a handler table. 104 DCHECK_EQ(Builtins::KindOf(Builtin::kJSEntry), Builtins::ASM); 105 DCHECK_EQ(Builtins::KindOf(Builtin::kJSConstructEntry), Builtins::ASM); 106 DCHECK_EQ(Builtins::KindOf(Builtin::kJSRunMicrotasksEntry), Builtins::ASM); 107 if (Builtins::IsJSEntryVariant(builtin)) { 108 handler_table_offset = HandlerTable::EmitReturnTableStart(&masm); 109 HandlerTable::EmitReturnEntry( 110 &masm, 0, isolate->builtins()->js_entry_handler_offset()); 111 } 112 113 CodeDesc desc; 114 masm.GetCode(isolate, &desc, MacroAssembler::kNoSafepointTable, 115 handler_table_offset); 116 117 Handle<Code> code = Factory::CodeBuilder(isolate, desc, CodeKind::BUILTIN) 118 .set_self_reference(masm.CodeObject()) 119 .set_builtin(builtin) 120 .Build(); 121#if defined(V8_OS_WIN64) 122 isolate->SetBuiltinUnwindData(builtin, masm.GetUnwindInfo()); 123#endif // V8_OS_WIN64 124 return *code; 125} 126 127Code BuildAdaptor(Isolate* isolate, Builtin builtin, Address builtin_address, 128 const char* name) { 129 HandleScope scope(isolate); 130 // Canonicalize handles, so that we can share constant pool entries pointing 131 // to code targets without dereferencing their handles. 132 CanonicalHandleScope canonical(isolate); 133 byte buffer[kBufferSize]; 134 MacroAssembler masm(isolate, BuiltinAssemblerOptions(isolate, builtin), 135 CodeObjectRequired::kYes, 136 ExternalAssemblerBuffer(buffer, kBufferSize)); 137 masm.set_builtin(builtin); 138 DCHECK(!masm.has_frame()); 139 Builtins::Generate_Adaptor(&masm, builtin_address); 140 CodeDesc desc; 141 masm.GetCode(isolate, &desc); 142 Handle<Code> code = Factory::CodeBuilder(isolate, desc, CodeKind::BUILTIN) 143 .set_self_reference(masm.CodeObject()) 144 .set_builtin(builtin) 145 .Build(); 146 return *code; 147} 148 149// Builder for builtins implemented in TurboFan with JS linkage. 150Code BuildWithCodeStubAssemblerJS(Isolate* isolate, Builtin builtin, 151 CodeAssemblerGenerator generator, int argc, 152 const char* name) { 153 HandleScope scope(isolate); 154 // Canonicalize handles, so that we can share constant pool entries pointing 155 // to code targets without dereferencing their handles. 156 CanonicalHandleScope canonical(isolate); 157 158 Zone zone(isolate->allocator(), ZONE_NAME, kCompressGraphZone); 159 compiler::CodeAssemblerState state(isolate, &zone, argc, CodeKind::BUILTIN, 160 name, builtin); 161 generator(&state); 162 Handle<Code> code = compiler::CodeAssembler::GenerateCode( 163 &state, BuiltinAssemblerOptions(isolate, builtin), 164 ProfileDataFromFile::TryRead(name)); 165 return *code; 166} 167 168// Builder for builtins implemented in TurboFan with CallStub linkage. 169Code BuildWithCodeStubAssemblerCS(Isolate* isolate, Builtin builtin, 170 CodeAssemblerGenerator generator, 171 CallDescriptors::Key interface_descriptor, 172 const char* name) { 173 HandleScope scope(isolate); 174 // Canonicalize handles, so that we can share constant pool entries pointing 175 // to code targets without dereferencing their handles. 176 CanonicalHandleScope canonical(isolate); 177 Zone zone(isolate->allocator(), ZONE_NAME, kCompressGraphZone); 178 // The interface descriptor with given key must be initialized at this point 179 // and this construction just queries the details from the descriptors table. 180 CallInterfaceDescriptor descriptor(interface_descriptor); 181 // Ensure descriptor is already initialized. 182 DCHECK_LE(0, descriptor.GetRegisterParameterCount()); 183 compiler::CodeAssemblerState state(isolate, &zone, descriptor, 184 CodeKind::BUILTIN, name, builtin); 185 generator(&state); 186 Handle<Code> code = compiler::CodeAssembler::GenerateCode( 187 &state, BuiltinAssemblerOptions(isolate, builtin), 188 ProfileDataFromFile::TryRead(name)); 189 return *code; 190} 191 192} // anonymous namespace 193 194// static 195void SetupIsolateDelegate::AddBuiltin(Builtins* builtins, Builtin builtin, 196 Code code) { 197 DCHECK_EQ(builtin, code.builtin_id()); 198 builtins->set_code(builtin, ToCodeT(code)); 199} 200 201// static 202void SetupIsolateDelegate::PopulateWithPlaceholders(Isolate* isolate) { 203 // Fill the builtins list with placeholders. References to these placeholder 204 // builtins are eventually replaced by the actual builtins. This is to 205 // support circular references between builtins. 206 Builtins* builtins = isolate->builtins(); 207 HandleScope scope(isolate); 208 for (Builtin builtin = Builtins::kFirst; builtin <= Builtins::kLast; 209 ++builtin) { 210 Handle<Code> placeholder = BuildPlaceholder(isolate, builtin); 211 AddBuiltin(builtins, builtin, *placeholder); 212 } 213} 214 215// static 216void SetupIsolateDelegate::ReplacePlaceholders(Isolate* isolate) { 217 // Replace references from all builtin code objects to placeholders. 218 Builtins* builtins = isolate->builtins(); 219 DisallowGarbageCollection no_gc; 220 CodePageCollectionMemoryModificationScope modification_scope(isolate->heap()); 221 static const int kRelocMask = 222 RelocInfo::ModeMask(RelocInfo::CODE_TARGET) | 223 RelocInfo::ModeMask(RelocInfo::FULL_EMBEDDED_OBJECT) | 224 RelocInfo::ModeMask(RelocInfo::COMPRESSED_EMBEDDED_OBJECT) | 225 RelocInfo::ModeMask(RelocInfo::RELATIVE_CODE_TARGET); 226 PtrComprCageBase cage_base(isolate); 227 for (Builtin builtin = Builtins::kFirst; builtin <= Builtins::kLast; 228 ++builtin) { 229 Code code = FromCodeT(builtins->code(builtin)); 230 isolate->heap()->UnprotectAndRegisterMemoryChunk( 231 code, UnprotectMemoryOrigin::kMainThread); 232 bool flush_icache = false; 233 for (RelocIterator it(code, kRelocMask); !it.done(); it.next()) { 234 RelocInfo* rinfo = it.rinfo(); 235 if (RelocInfo::IsCodeTargetMode(rinfo->rmode())) { 236 Code target = Code::GetCodeFromTargetAddress(rinfo->target_address()); 237 DCHECK_IMPLIES(RelocInfo::IsRelativeCodeTarget(rinfo->rmode()), 238 Builtins::IsIsolateIndependent(target.builtin_id())); 239 if (!target.is_builtin()) continue; 240 CodeT new_target = builtins->code(target.builtin_id()); 241 rinfo->set_target_address(new_target.raw_instruction_start(), 242 UPDATE_WRITE_BARRIER, SKIP_ICACHE_FLUSH); 243 } else { 244 DCHECK(RelocInfo::IsEmbeddedObjectMode(rinfo->rmode())); 245 Object object = rinfo->target_object(cage_base); 246 if (!object.IsCodeT(cage_base)) continue; 247 CodeT target = CodeT::cast(object); 248 if (!target.is_builtin()) continue; 249 CodeT new_target = builtins->code(target.builtin_id()); 250 rinfo->set_target_object(isolate->heap(), new_target, 251 UPDATE_WRITE_BARRIER, SKIP_ICACHE_FLUSH); 252 } 253 flush_icache = true; 254 } 255 if (flush_icache) { 256 FlushInstructionCache(code.raw_instruction_start(), 257 code.raw_instruction_size()); 258 } 259 } 260} 261 262namespace { 263 264Code GenerateBytecodeHandler(Isolate* isolate, Builtin builtin, 265 interpreter::OperandScale operand_scale, 266 interpreter::Bytecode bytecode) { 267 DCHECK(interpreter::Bytecodes::BytecodeHasHandler(bytecode, operand_scale)); 268 Handle<Code> code = interpreter::GenerateBytecodeHandler( 269 isolate, Builtins::name(builtin), bytecode, operand_scale, builtin, 270 BuiltinAssemblerOptions(isolate, builtin)); 271 return *code; 272} 273 274} // namespace 275 276// static 277void SetupIsolateDelegate::SetupBuiltinsInternal(Isolate* isolate) { 278 Builtins* builtins = isolate->builtins(); 279 DCHECK(!builtins->initialized_); 280 281 PopulateWithPlaceholders(isolate); 282 283 // Create a scope for the handles in the builtins. 284 HandleScope scope(isolate); 285 286 int index = 0; 287 Code code; 288#define BUILD_CPP(Name) \ 289 code = BuildAdaptor(isolate, Builtin::k##Name, \ 290 FUNCTION_ADDR(Builtin_##Name), #Name); \ 291 AddBuiltin(builtins, Builtin::k##Name, code); \ 292 index++; 293 294#define BUILD_TFJ(Name, Argc, ...) \ 295 code = BuildWithCodeStubAssemblerJS( \ 296 isolate, Builtin::k##Name, &Builtins::Generate_##Name, Argc, #Name); \ 297 AddBuiltin(builtins, Builtin::k##Name, code); \ 298 index++; 299 300#define BUILD_TFC(Name, InterfaceDescriptor) \ 301 /* Return size is from the provided CallInterfaceDescriptor. */ \ 302 code = BuildWithCodeStubAssemblerCS( \ 303 isolate, Builtin::k##Name, &Builtins::Generate_##Name, \ 304 CallDescriptors::InterfaceDescriptor, #Name); \ 305 AddBuiltin(builtins, Builtin::k##Name, code); \ 306 index++; 307 308#define BUILD_TFS(Name, ...) \ 309 /* Return size for generic TF builtins (stub linkage) is always 1. */ \ 310 code = BuildWithCodeStubAssemblerCS(isolate, Builtin::k##Name, \ 311 &Builtins::Generate_##Name, \ 312 CallDescriptors::Name, #Name); \ 313 AddBuiltin(builtins, Builtin::k##Name, code); \ 314 index++; 315 316#define BUILD_TFH(Name, InterfaceDescriptor) \ 317 /* Return size for IC builtins/handlers is always 1. */ \ 318 code = BuildWithCodeStubAssemblerCS( \ 319 isolate, Builtin::k##Name, &Builtins::Generate_##Name, \ 320 CallDescriptors::InterfaceDescriptor, #Name); \ 321 AddBuiltin(builtins, Builtin::k##Name, code); \ 322 index++; 323 324#define BUILD_BCH(Name, OperandScale, Bytecode) \ 325 code = GenerateBytecodeHandler(isolate, Builtin::k##Name, OperandScale, \ 326 Bytecode); \ 327 AddBuiltin(builtins, Builtin::k##Name, code); \ 328 index++; 329 330#define BUILD_ASM(Name, InterfaceDescriptor) \ 331 code = BuildWithMacroAssembler(isolate, Builtin::k##Name, \ 332 Builtins::Generate_##Name, #Name); \ 333 AddBuiltin(builtins, Builtin::k##Name, code); \ 334 index++; 335 336 BUILTIN_LIST(BUILD_CPP, BUILD_TFJ, BUILD_TFC, BUILD_TFS, BUILD_TFH, BUILD_BCH, 337 BUILD_ASM); 338 339#undef BUILD_CPP 340#undef BUILD_TFJ 341#undef BUILD_TFC 342#undef BUILD_TFS 343#undef BUILD_TFH 344#undef BUILD_BCH 345#undef BUILD_ASM 346 CHECK_EQ(Builtins::kBuiltinCount, index); 347 348 ReplacePlaceholders(isolate); 349 350// TODO(v8:11880): avoid roundtrips between cdc and code. 351#define SET_PROMISE_REJECTION_PREDICTION(Name) \ 352 FromCodeT(builtins->code(Builtin::k##Name)).set_is_promise_rejection(true); 353 354 BUILTIN_PROMISE_REJECTION_PREDICTION_LIST(SET_PROMISE_REJECTION_PREDICTION) 355#undef SET_PROMISE_REJECTION_PREDICTION 356 357 builtins->MarkInitialized(); 358} 359 360} // namespace internal 361} // namespace v8 362