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