Bug Summary

File:out/../deps/v8/src/compiler/memory-lowering.cc
Warning:line 239, column 36
Dereference of null pointer (loaded from variable 'state_ptr')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name memory-lowering.cc -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -ffunction-sections -fdata-sections -fcoverage-compilation-dir=/home/maurizio/node-v18.6.0/out -resource-dir /usr/local/lib/clang/16.0.0 -D _GLIBCXX_USE_CXX11_ABI=1 -D NODE_OPENSSL_CONF_NAME=nodejs_conf -D NODE_OPENSSL_HAS_QUIC -D V8_GYP_BUILD -D V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP=64 -D __STDC_FORMAT_MACROS -D OPENSSL_NO_PINSHARED -D OPENSSL_THREADS -D V8_TARGET_ARCH_X64 -D V8_HAVE_TARGET_OS -D V8_TARGET_OS_LINUX -D V8_EMBEDDER_STRING="-node.8" -D ENABLE_DISASSEMBLER -D V8_PROMISE_INTERNAL_FIELD_COUNT=1 -D V8_SHORT_BUILTIN_CALLS -D OBJECT_PRINT -D V8_INTL_SUPPORT -D V8_ATOMIC_OBJECT_FIELD_WRITES -D V8_ENABLE_LAZY_SOURCE_POSITIONS -D V8_USE_SIPHASH -D V8_SHARED_RO_HEAP -D V8_WIN64_UNWINDING_INFO -D V8_ENABLE_REGEXP_INTERPRETER_THREADED_DISPATCH -D V8_SNAPSHOT_COMPRESSION -D V8_ENABLE_WEBASSEMBLY -D V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS -D V8_ALLOCATION_FOLDING -D V8_ALLOCATION_SITE_TRACKING -D V8_SCRIPTORMODULE_LEGACY_LIFETIME -D V8_ADVANCED_BIGINT_ALGORITHMS -D UCONFIG_NO_SERVICE=1 -D U_ENABLE_DYLOAD=0 -D U_STATIC_IMPLEMENTATION=1 -D U_HAVE_STD_STRING=1 -D UCONFIG_NO_BREAK_ITERATION=0 -I ../deps/v8 -I ../deps/v8/include -I /home/maurizio/node-v18.6.0/out/Release/obj/gen/generate-bytecode-output-root -I /home/maurizio/node-v18.6.0/out/Release/obj/gen -I ../deps/icu-small/source/i18n -I ../deps/icu-small/source/common -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../include/c++/8 -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../include/c++/8/x86_64-redhat-linux -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../include/c++/8/backward -internal-isystem /usr/local/lib/clang/16.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-redhat-linux/8/../../../../x86_64-redhat-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -Wno-unused-parameter -Wno-return-type -std=gnu++17 -fdeprecated-macro -fdebug-compilation-dir=/home/maurizio/node-v18.6.0/out -ferror-limit 19 -fno-rtti -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-08-22-142216-507842-1 -x c++ ../deps/v8/src/compiler/memory-lowering.cc
1// Copyright 2019 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/compiler/memory-lowering.h"
6
7#include "src/codegen/interface-descriptors-inl.h"
8#include "src/compiler/access-builder.h"
9#include "src/compiler/js-graph.h"
10#include "src/compiler/linkage.h"
11#include "src/compiler/node-matchers.h"
12#include "src/compiler/node-properties.h"
13#include "src/compiler/node.h"
14#include "src/compiler/simplified-operator.h"
15#include "src/roots/roots-inl.h"
16#include "src/sandbox/external-pointer.h"
17
18#if V8_ENABLE_WEBASSEMBLY1
19#include "src/wasm/wasm-linkage.h"
20#include "src/wasm/wasm-objects.h"
21#endif
22namespace v8 {
23namespace internal {
24namespace compiler {
25
26// An allocation group represents a set of allocations that have been folded
27// together.
28class MemoryLowering::AllocationGroup final : public ZoneObject {
29 public:
30 AllocationGroup(Node* node, AllocationType allocation, Zone* zone);
31 AllocationGroup(Node* node, AllocationType allocation, Node* size,
32 Zone* zone);
33 ~AllocationGroup() = default;
34
35 void Add(Node* object);
36 bool Contains(Node* object) const;
37 bool IsYoungGenerationAllocation() const {
38 return allocation() == AllocationType::kYoung;
39 }
40
41 AllocationType allocation() const { return allocation_; }
42 Node* size() const { return size_; }
43
44 private:
45 ZoneSet<NodeId> node_ids_;
46 AllocationType const allocation_;
47 Node* const size_;
48
49 static inline AllocationType CheckAllocationType(AllocationType allocation) {
50 // For non-generational heap, all young allocations are redirected to old
51 // space.
52 if (FLAG_single_generation && allocation == AllocationType::kYoung) {
53 return AllocationType::kOld;
54 }
55 return allocation;
56 }
57
58 DISALLOW_IMPLICIT_CONSTRUCTORS(AllocationGroup)AllocationGroup() = delete; AllocationGroup(const AllocationGroup
&) = delete; AllocationGroup& operator=(const AllocationGroup
&) = delete
;
59};
60
61MemoryLowering::MemoryLowering(JSGraph* jsgraph, Zone* zone,
62 JSGraphAssembler* graph_assembler,
63 AllocationFolding allocation_folding,
64 WriteBarrierAssertFailedCallback callback,
65 const char* function_debug_name)
66 : isolate_(jsgraph->isolate()),
67 zone_(zone),
68 graph_(jsgraph->graph()),
69 common_(jsgraph->common()),
70 machine_(jsgraph->machine()),
71 graph_assembler_(graph_assembler),
72 allocation_folding_(allocation_folding),
73 write_barrier_assert_failed_(callback),
74 function_debug_name_(function_debug_name) {}
75
76Zone* MemoryLowering::graph_zone() const { return graph()->zone(); }
77
78Reduction MemoryLowering::Reduce(Node* node) {
79 switch (node->opcode()) {
1
Control jumps to 'case kAllocateRaw:' at line 84
80 case IrOpcode::kAllocate:
81 // Allocate nodes were purged from the graph in effect-control
82 // linearization.
83 UNREACHABLE()V8_Fatal("unreachable code");
84 case IrOpcode::kAllocateRaw:
85 return ReduceAllocateRaw(node);
2
Calling 'MemoryLowering::ReduceAllocateRaw'
86 case IrOpcode::kLoadFromObject:
87 case IrOpcode::kLoadImmutableFromObject:
88 return ReduceLoadFromObject(node);
89 case IrOpcode::kLoadElement:
90 return ReduceLoadElement(node);
91 case IrOpcode::kLoadField:
92 return ReduceLoadField(node);
93 case IrOpcode::kStoreToObject:
94 case IrOpcode::kInitializeImmutableInObject:
95 return ReduceStoreToObject(node);
96 case IrOpcode::kStoreElement:
97 return ReduceStoreElement(node);
98 case IrOpcode::kStoreField:
99 return ReduceStoreField(node);
100 case IrOpcode::kStore:
101 return ReduceStore(node);
102 default:
103 return NoChange();
104 }
105}
106
107void MemoryLowering::EnsureAllocateOperator() {
108 if (allocate_operator_.is_set()) return;
109
110 auto descriptor = AllocateDescriptor{};
111 StubCallMode mode = isolate_ != nullptr ? StubCallMode::kCallCodeObject
112 : StubCallMode::kCallBuiltinPointer;
113 auto call_descriptor = Linkage::GetStubCallDescriptor(
114 graph_zone(), descriptor, descriptor.GetStackParameterCount(),
115 CallDescriptor::kCanUseRoots, Operator::kNoThrow, mode);
116 allocate_operator_.set(common()->Call(call_descriptor));
117}
118
119#if V8_ENABLE_WEBASSEMBLY1
120Node* MemoryLowering::GetWasmInstanceNode() {
121 if (wasm_instance_node_.is_set()) return wasm_instance_node_.get();
122 for (Node* use : graph()->start()->uses()) {
123 if (use->opcode() == IrOpcode::kParameter &&
124 ParameterIndexOf(use->op()) == wasm::kWasmInstanceParameterIndex) {
125 wasm_instance_node_.set(use);
126 return use;
127 }
128 }
129 UNREACHABLE()V8_Fatal("unreachable code"); // The instance node must have been created before.
130}
131#endif // V8_ENABLE_WEBASSEMBLY
132
133#define __ gasm()->
134
135Reduction MemoryLowering::ReduceAllocateRaw(
136 Node* node, AllocationType allocation_type,
137 AllowLargeObjects allow_large_objects, AllocationState const** state_ptr) {
138 DCHECK_EQ(IrOpcode::kAllocateRaw, node->opcode())((void) 0);
139 DCHECK_IMPLIES(allocation_folding_ == AllocationFolding::kDoAllocationFolding,((void) 0)
140 state_ptr != nullptr)((void) 0);
141 if (FLAG_single_generation
4.1
'FLAG_single_generation' is false
&& allocation_type == AllocationType::kYoung) {
142 allocation_type = AllocationType::kOld;
143 }
144 // Code objects may have a maximum size smaller than kMaxHeapObjectSize due to
145 // guard pages. If we need to support allocating code here we would need to
146 // call MemoryChunkLayout::MaxRegularCodeObjectSize() at runtime.
147 DCHECK_NE(allocation_type, AllocationType::kCode)((void) 0);
148 Node* value;
149 Node* size = node->InputAt(0);
150 Node* effect = node->InputAt(1);
151 Node* control = node->InputAt(2);
152
153 gasm()->InitializeEffectControl(effect, control);
154
155 Node* allocate_builtin;
156 if (isolate_ != nullptr) {
5
Assuming the condition is false
6
Taking false branch
157 if (allocation_type == AllocationType::kYoung) {
158 if (allow_large_objects == AllowLargeObjects::kTrue) {
159 allocate_builtin = __ AllocateInYoungGenerationStubConstant();
160 } else {
161 allocate_builtin = __ AllocateRegularInYoungGenerationStubConstant();
162 }
163 } else {
164 if (allow_large_objects == AllowLargeObjects::kTrue) {
165 allocate_builtin = __ AllocateInOldGenerationStubConstant();
166 } else {
167 allocate_builtin = __ AllocateRegularInOldGenerationStubConstant();
168 }
169 }
170 } else {
171 // This lowering is used by Wasm, where we compile isolate-independent
172 // code. Builtin calls simply encode the target builtin ID, which will
173 // be patched to the builtin's address later.
174#if V8_ENABLE_WEBASSEMBLY1
175 Builtin builtin;
176 if (allocation_type == AllocationType::kYoung) {
7
Assuming 'allocation_type' is not equal to kYoung
8
Taking false branch
177 if (allow_large_objects == AllowLargeObjects::kTrue) {
178 builtin = Builtin::kAllocateInYoungGeneration;
179 } else {
180 builtin = Builtin::kAllocateRegularInYoungGeneration;
181 }
182 } else {
183 if (allow_large_objects == AllowLargeObjects::kTrue) {
9
Assuming 'allow_large_objects' is not equal to kTrue
10
Taking false branch
184 builtin = Builtin::kAllocateInOldGeneration;
185 } else {
186 builtin = Builtin::kAllocateRegularInOldGeneration;
187 }
188 }
189 static_assert(std::is_same<Smi, BuiltinPtr>(), "BuiltinPtr must be Smi");
190 allocate_builtin =
191 graph()->NewNode(common()->NumberConstant(static_cast<int>(builtin)));
192#else
193 UNREACHABLE()V8_Fatal("unreachable code");
194#endif
195 }
196
197 // Determine the top/limit addresses.
198 Node* top_address;
199 Node* limit_address;
200 if (isolate_ != nullptr) {
11
Taking false branch
201 top_address = __ ExternalConstant(
202 allocation_type == AllocationType::kYoung
203 ? ExternalReference::new_space_allocation_top_address(isolate())
204 : ExternalReference::old_space_allocation_top_address(isolate()));
205 limit_address = __ ExternalConstant(
206 allocation_type == AllocationType::kYoung
207 ? ExternalReference::new_space_allocation_limit_address(isolate())
208 : ExternalReference::old_space_allocation_limit_address(isolate()));
209 } else {
210 // Wasm mode: producing isolate-independent code, loading the isolate
211 // address at runtime.
212#if V8_ENABLE_WEBASSEMBLY1
213 Node* instance_node = GetWasmInstanceNode();
214 int top_address_offset =
215 allocation_type
11.1
'allocation_type' is not equal to kYoung
== AllocationType::kYoung
12
'?' condition is false
216 ? WasmInstanceObject::kNewAllocationTopAddressOffset
217 : WasmInstanceObject::kOldAllocationTopAddressOffset;
218 int limit_address_offset =
219 allocation_type
12.1
'allocation_type' is not equal to kYoung
== AllocationType::kYoung
13
'?' condition is false
220 ? WasmInstanceObject::kNewAllocationLimitAddressOffset
221 : WasmInstanceObject::kOldAllocationLimitAddressOffset;
222 top_address =
223 __ Load(MachineType::Pointer(), instance_node,
224 __ IntPtrConstant(top_address_offset - kHeapObjectTag));
225 limit_address =
226 __ Load(MachineType::Pointer(), instance_node,
227 __ IntPtrConstant(limit_address_offset - kHeapObjectTag));
228#else
229 UNREACHABLE()V8_Fatal("unreachable code");
230#endif // V8_ENABLE_WEBASSEMBLY
231 }
232
233 // Check if we can fold this allocation into a previous allocation represented
234 // by the incoming {state}.
235 IntPtrMatcher m(size);
236 if (m.IsInRange(0, kMaxRegularHeapObjectSize) && FLAG_inline_new &&
14
Assuming 'FLAG_inline_new' is true
16
Taking true branch
237 allocation_folding_ == AllocationFolding::kDoAllocationFolding) {
15
Assuming field 'allocation_folding_' is equal to kDoAllocationFolding
238 intptr_t const object_size = m.ResolvedValue();
239 AllocationState const* state = *state_ptr;
17
Dereference of null pointer (loaded from variable 'state_ptr')
240 if (state->size() <= kMaxRegularHeapObjectSize - object_size &&
241 state->group()->allocation() == allocation_type) {
242 // We can fold this Allocate {node} into the allocation {group}
243 // represented by the given {state}. Compute the upper bound for
244 // the new {state}.
245 intptr_t const state_size = state->size() + object_size;
246
247 // Update the reservation check to the actual maximum upper bound.
248 AllocationGroup* const group = state->group();
249 if (machine()->Is64()) {
250 if (OpParameter<int64_t>(group->size()->op()) < state_size) {
251 NodeProperties::ChangeOp(group->size(),
252 common()->Int64Constant(state_size));
253 }
254 } else {
255 if (OpParameter<int32_t>(group->size()->op()) < state_size) {
256 NodeProperties::ChangeOp(
257 group->size(),
258 common()->Int32Constant(static_cast<int32_t>(state_size)));
259 }
260 }
261
262 // Update the allocation top with the new object allocation.
263 // TODO(bmeurer): Defer writing back top as much as possible.
264 Node* top = __ IntAdd(state->top(), size);
265 __ Store(StoreRepresentation(MachineType::PointerRepresentation(),
266 kNoWriteBarrier),
267 top_address, __ IntPtrConstant(0), top);
268
269 // Compute the effective inner allocated address.
270 value = __ BitcastWordToTagged(
271 __ IntAdd(state->top(), __ IntPtrConstant(kHeapObjectTag)));
272 effect = gasm()->effect();
273 control = gasm()->control();
274
275 // Extend the allocation {group}.
276 group->Add(value);
277 *state_ptr =
278 AllocationState::Open(group, state_size, top, effect, zone());
279 } else {
280 auto call_runtime = __ MakeDeferredLabel();
281 auto done = __ MakeLabel(MachineType::PointerRepresentation());
282
283 // Setup a mutable reservation size node; will be patched as we fold
284 // additional allocations into this new group.
285 Node* reservation_size = __ UniqueIntPtrConstant(object_size);
286
287 // Load allocation top and limit.
288 Node* top =
289 __ Load(MachineType::Pointer(), top_address, __ IntPtrConstant(0));
290 Node* limit =
291 __ Load(MachineType::Pointer(), limit_address, __ IntPtrConstant(0));
292
293 // Check if we need to collect garbage before we can start bump pointer
294 // allocation (always done for folded allocations).
295 Node* check = __ UintLessThan(__ IntAdd(top, reservation_size), limit);
296
297 __ GotoIfNot(check, &call_runtime);
298 __ Goto(&done, top);
299
300 __ Bind(&call_runtime);
301 {
302 EnsureAllocateOperator();
303 Node* vfalse = __ BitcastTaggedToWord(__ Call(
304 allocate_operator_.get(), allocate_builtin, reservation_size));
305 vfalse = __ IntSub(vfalse, __ IntPtrConstant(kHeapObjectTag));
306 __ Goto(&done, vfalse);
307 }
308
309 __ Bind(&done);
310
311 // Compute the new top and write it back.
312 top = __ IntAdd(done.PhiAt(0), __ IntPtrConstant(object_size));
313 __ Store(StoreRepresentation(MachineType::PointerRepresentation(),
314 kNoWriteBarrier),
315 top_address, __ IntPtrConstant(0), top);
316
317 // Compute the initial object address.
318 value = __ BitcastWordToTagged(
319 __ IntAdd(done.PhiAt(0), __ IntPtrConstant(kHeapObjectTag)));
320 effect = gasm()->effect();
321 control = gasm()->control();
322
323 // Start a new allocation group.
324 AllocationGroup* group = zone()->New<AllocationGroup>(
325 value, allocation_type, reservation_size, zone());
326 *state_ptr =
327 AllocationState::Open(group, object_size, top, effect, zone());
328 }
329 } else {
330 auto call_runtime = __ MakeDeferredLabel();
331 auto done = __ MakeLabel(MachineRepresentation::kTaggedPointer);
332
333 // Load allocation top and limit.
334 Node* top =
335 __ Load(MachineType::Pointer(), top_address, __ IntPtrConstant(0));
336 Node* limit =
337 __ Load(MachineType::Pointer(), limit_address, __ IntPtrConstant(0));
338
339 // Compute the new top.
340 Node* new_top = __ IntAdd(top, size);
341
342 // Check if we can do bump pointer allocation here.
343 Node* check = __ UintLessThan(new_top, limit);
344 __ GotoIfNot(check, &call_runtime);
345 if (allow_large_objects == AllowLargeObjects::kTrue) {
346 __ GotoIfNot(
347 __ UintLessThan(size, __ IntPtrConstant(kMaxRegularHeapObjectSize)),
348 &call_runtime);
349 }
350 __ Store(StoreRepresentation(MachineType::PointerRepresentation(),
351 kNoWriteBarrier),
352 top_address, __ IntPtrConstant(0), new_top);
353 __ Goto(&done, __ BitcastWordToTagged(
354 __ IntAdd(top, __ IntPtrConstant(kHeapObjectTag))));
355
356 __ Bind(&call_runtime);
357 EnsureAllocateOperator();
358 __ Goto(&done, __ Call(allocate_operator_.get(), allocate_builtin, size));
359
360 __ Bind(&done);
361 value = done.PhiAt(0);
362 effect = gasm()->effect();
363 control = gasm()->control();
364
365 if (state_ptr) {
366 // Create an unfoldable allocation group.
367 AllocationGroup* group =
368 zone()->New<AllocationGroup>(value, allocation_type, zone());
369 *state_ptr = AllocationState::Closed(group, effect, zone());
370 }
371 }
372
373 return Replace(value);
374}
375
376Reduction MemoryLowering::ReduceLoadFromObject(Node* node) {
377 DCHECK(node->opcode() == IrOpcode::kLoadFromObject ||((void) 0)
378 node->opcode() == IrOpcode::kLoadImmutableFromObject)((void) 0);
379 ObjectAccess const& access = ObjectAccessOf(node->op());
380
381 MachineType machine_type = access.machine_type;
382
383 if (machine_type.IsMapWord()) {
384 CHECK_EQ(machine_type.semantic(), MachineSemantic::kAny)do { bool _cmp = ::v8::base::CmpEQImpl< typename ::v8::base
::pass_value_or_ref<decltype(machine_type.semantic())>::
type, typename ::v8::base::pass_value_or_ref<decltype(MachineSemantic
::kAny)>::type>((machine_type.semantic()), (MachineSemantic
::kAny)); do { if ((__builtin_expect(!!(!(_cmp)), 0))) { V8_Fatal
("Check failed: %s.", "machine_type.semantic()" " " "==" " " "MachineSemantic::kAny"
); } } while (false); } while (false)
;
385 return ReduceLoadMap(node);
386 }
387
388 MachineRepresentation rep = machine_type.representation();
389 const Operator* load_op =
390 ElementSizeInBytes(rep) > kTaggedSize &&
391 !machine()->UnalignedLoadSupported(machine_type.representation())
392 ? machine()->UnalignedLoad(machine_type)
393 : machine()->Load(machine_type);
394 NodeProperties::ChangeOp(node, load_op);
395 return Changed(node);
396}
397
398Reduction MemoryLowering::ReduceLoadElement(Node* node) {
399 DCHECK_EQ(IrOpcode::kLoadElement, node->opcode())((void) 0);
400 ElementAccess const& access = ElementAccessOf(node->op());
401 Node* index = node->InputAt(1);
402 node->ReplaceInput(1, ComputeIndex(access, index));
403 MachineType type = access.machine_type;
404 DCHECK(!type.IsMapWord())((void) 0);
405 NodeProperties::ChangeOp(node, machine()->Load(type));
406 return Changed(node);
407}
408
409Node* MemoryLowering::DecodeExternalPointer(
410 Node* node, ExternalPointerTag external_pointer_tag) {
411#ifdef V8_SANDBOXED_EXTERNAL_POINTERS
412 DCHECK(V8_SANDBOXED_EXTERNAL_POINTERS_BOOL)((void) 0);
413 DCHECK(node->opcode() == IrOpcode::kLoad)((void) 0);
414 DCHECK_EQ(kExternalPointerSize, kUInt32Size)((void) 0);
415 DCHECK_NE(kExternalPointerNullTag, external_pointer_tag)((void) 0);
416 Node* effect = NodeProperties::GetEffectInput(node);
417 Node* control = NodeProperties::GetControlInput(node);
418 __ InitializeEffectControl(effect, control);
419
420 // Clone the load node and put it here.
421 // TODO(turbofan): consider adding GraphAssembler::Clone() suitable for
422 // cloning nodes from arbitrary locaions in effect/control chains.
423 STATIC_ASSERT(kExternalPointerIndexShift > kSystemPointerSizeLog2)static_assert(kExternalPointerIndexShift > kSystemPointerSizeLog2
, "kExternalPointerIndexShift > kSystemPointerSizeLog2")
;
424 Node* shifted_index = __ AddNode(graph()->CloneNode(node));
425 Node* shift_amount =
426 __ Int32Constant(kExternalPointerIndexShift - kSystemPointerSizeLog2);
427 Node* offset = __ Word32Shr(shifted_index, shift_amount);
428
429 // Uncomment this to generate a breakpoint for debugging purposes.
430 // __ DebugBreak();
431
432 // Decode loaded external pointer.
433 //
434 // Here we access the external pointer table through an ExternalReference.
435 // Alternatively, we could also hardcode the address of the table since it is
436 // never reallocated. However, in that case we must be able to guarantee that
437 // the generated code is never executed under a different Isolate, as that
438 // would allow access to external objects from different Isolates. It also
439 // would break if the code is serialized/deserialized at some point.
440 Node* table_address = __ ExternalConstant(
441 ExternalReference::external_pointer_table_address(isolate()));
442 Node* table = __ Load(MachineType::Pointer(), table_address,
443 Internals::kExternalPointerTableBufferOffset);
444 Node* decoded_ptr =
445 __ Load(MachineType::Pointer(), table, __ ChangeUint32ToUint64(offset));
446 Node* tag = __ IntPtrConstant(~external_pointer_tag);
447 decoded_ptr = __ WordAnd(decoded_ptr, tag);
448 return decoded_ptr;
449#else
450 return node;
451#endif // V8_SANDBOXED_EXTERNAL_POINTERS
452}
453
454Reduction MemoryLowering::ReduceLoadMap(Node* node) {
455#ifdef V8_MAP_PACKING
456 NodeProperties::ChangeOp(node, machine()->Load(MachineType::AnyTagged()));
457
458 Node* effect = NodeProperties::GetEffectInput(node);
459 Node* control = NodeProperties::GetControlInput(node);
460 __ InitializeEffectControl(effect, control);
461
462 node = __ AddNode(graph()->CloneNode(node));
463 return Replace(__ UnpackMapWord(node));
464#else
465 NodeProperties::ChangeOp(node, machine()->Load(MachineType::TaggedPointer()));
466 return Changed(node);
467#endif
468}
469
470Reduction MemoryLowering::ReduceLoadField(Node* node) {
471 DCHECK_EQ(IrOpcode::kLoadField, node->opcode())((void) 0);
472 FieldAccess const& access = FieldAccessOf(node->op());
473 Node* offset = __ IntPtrConstant(access.offset - access.tag());
474 node->InsertInput(graph_zone(), 1, offset);
475 MachineType type = access.machine_type;
476 if (V8_SANDBOXED_EXTERNAL_POINTERS_BOOLfalse &&
477 access.type.Is(Type::ExternalPointer())) {
478 // External pointer table indices are stored as 32-bit numbers
479 type = MachineType::Uint32();
480 }
481
482 if (type.IsMapWord()) {
483 DCHECK(!access.type.Is(Type::ExternalPointer()))((void) 0);
484 return ReduceLoadMap(node);
485 }
486
487 NodeProperties::ChangeOp(node, machine()->Load(type));
488
489#ifdef V8_SANDBOXED_EXTERNAL_POINTERS
490 if (access.type.Is(Type::ExternalPointer())) {
491 ExternalPointerTag tag = access.external_pointer_tag;
492 DCHECK_NE(kExternalPointerNullTag, tag)((void) 0);
493 node = DecodeExternalPointer(node, tag);
494 return Replace(node);
495 }
496#endif
497
498 return Changed(node);
499}
500
501Reduction MemoryLowering::ReduceStoreToObject(Node* node,
502 AllocationState const* state) {
503 DCHECK(node->opcode() == IrOpcode::kStoreToObject ||((void) 0)
504 node->opcode() == IrOpcode::kInitializeImmutableInObject)((void) 0);
505 ObjectAccess const& access = ObjectAccessOf(node->op());
506 Node* object = node->InputAt(0);
507 Node* value = node->InputAt(2);
508
509 WriteBarrierKind write_barrier_kind = ComputeWriteBarrierKind(
510 node, object, value, state, access.write_barrier_kind);
511 DCHECK(!access.machine_type.IsMapWord())((void) 0);
512 MachineRepresentation rep = access.machine_type.representation();
513 StoreRepresentation store_rep(rep, write_barrier_kind);
514 const Operator* store_op = ElementSizeInBytes(rep) > kTaggedSize &&
515 !machine()->UnalignedStoreSupported(rep)
516 ? machine()->UnalignedStore(rep)
517 : machine()->Store(store_rep);
518 NodeProperties::ChangeOp(node, store_op);
519 return Changed(node);
520}
521
522Reduction MemoryLowering::ReduceStoreElement(Node* node,
523 AllocationState const* state) {
524 DCHECK_EQ(IrOpcode::kStoreElement, node->opcode())((void) 0);
525 ElementAccess const& access = ElementAccessOf(node->op());
526 Node* object = node->InputAt(0);
527 Node* index = node->InputAt(1);
528 Node* value = node->InputAt(2);
529 node->ReplaceInput(1, ComputeIndex(access, index));
530 WriteBarrierKind write_barrier_kind = ComputeWriteBarrierKind(
531 node, object, value, state, access.write_barrier_kind);
532 NodeProperties::ChangeOp(
533 node, machine()->Store(StoreRepresentation(
534 access.machine_type.representation(), write_barrier_kind)));
535 return Changed(node);
536}
537
538Reduction MemoryLowering::ReduceStoreField(Node* node,
539 AllocationState const* state) {
540 DCHECK_EQ(IrOpcode::kStoreField, node->opcode())((void) 0);
541 FieldAccess const& access = FieldAccessOf(node->op());
542 // External pointer must never be stored by optimized code.
543 DCHECK_IMPLIES(V8_SANDBOXED_EXTERNAL_POINTERS_BOOL,((void) 0)
544 !access.type.Is(Type::ExternalPointer()))((void) 0);
545 // SandboxedPointers are not currently stored by optimized code.
546 DCHECK(!access.type.Is(Type::SandboxedPointer()))((void) 0);
547 MachineType machine_type = access.machine_type;
548 Node* object = node->InputAt(0);
549 Node* value = node->InputAt(1);
550
551 Node* effect = NodeProperties::GetEffectInput(node);
552 Node* control = NodeProperties::GetControlInput(node);
553 __ InitializeEffectControl(effect, control);
554
555 WriteBarrierKind write_barrier_kind = ComputeWriteBarrierKind(
556 node, object, value, state, access.write_barrier_kind);
557 Node* offset = __ IntPtrConstant(access.offset - access.tag());
558 node->InsertInput(graph_zone(), 1, offset);
559
560 if (machine_type.IsMapWord()) {
561 machine_type = MachineType::TaggedPointer();
562#ifdef V8_MAP_PACKING
563 Node* mapword = __ PackMapWord(TNode<Map>::UncheckedCast(value));
564 node->ReplaceInput(2, mapword);
565#endif
566 }
567 NodeProperties::ChangeOp(
568 node, machine()->Store(StoreRepresentation(machine_type.representation(),
569 write_barrier_kind)));
570 return Changed(node);
571}
572
573Reduction MemoryLowering::ReduceStore(Node* node,
574 AllocationState const* state) {
575 DCHECK_EQ(IrOpcode::kStore, node->opcode())((void) 0);
576 StoreRepresentation representation = StoreRepresentationOf(node->op());
577 Node* object = node->InputAt(0);
578 Node* value = node->InputAt(2);
579 WriteBarrierKind write_barrier_kind = ComputeWriteBarrierKind(
580 node, object, value, state, representation.write_barrier_kind());
581 if (write_barrier_kind != representation.write_barrier_kind()) {
582 NodeProperties::ChangeOp(
583 node, machine()->Store(StoreRepresentation(
584 representation.representation(), write_barrier_kind)));
585 return Changed(node);
586 }
587 return NoChange();
588}
589
590Node* MemoryLowering::ComputeIndex(ElementAccess const& access, Node* index) {
591 int const element_size_shift =
592 ElementSizeLog2Of(access.machine_type.representation());
593 if (element_size_shift) {
594 index = __ WordShl(index, __ IntPtrConstant(element_size_shift));
595 }
596 int const fixed_offset = access.header_size - access.tag();
597 if (fixed_offset) {
598 index = __ IntAdd(index, __ IntPtrConstant(fixed_offset));
599 }
600 return index;
601}
602
603#undef __
604
605namespace {
606
607bool ValueNeedsWriteBarrier(Node* value, Isolate* isolate) {
608 while (true) {
609 switch (value->opcode()) {
610 case IrOpcode::kBitcastWordToTaggedSigned:
611 return false;
612 case IrOpcode::kHeapConstant: {
613 RootIndex root_index;
614 if (isolate->roots_table().IsRootHandle(HeapConstantOf(value->op()),
615 &root_index) &&
616 RootsTable::IsImmortalImmovable(root_index)) {
617 return false;
618 }
619 break;
620 }
621 default:
622 break;
623 }
624 return true;
625 }
626}
627
628} // namespace
629
630Reduction MemoryLowering::ReduceAllocateRaw(Node* node) {
631 DCHECK_EQ(IrOpcode::kAllocateRaw, node->opcode())((void) 0);
632 const AllocateParameters& allocation = AllocateParametersOf(node->op());
633 return ReduceAllocateRaw(node, allocation.allocation_type(),
4
Calling 'MemoryLowering::ReduceAllocateRaw'
634 allocation.allow_large_objects(), nullptr);
3
Passing null pointer value via 4th parameter 'state_ptr'
635}
636
637WriteBarrierKind MemoryLowering::ComputeWriteBarrierKind(
638 Node* node, Node* object, Node* value, AllocationState const* state,
639 WriteBarrierKind write_barrier_kind) {
640 if (state && state->IsYoungGenerationAllocation() &&
641 state->group()->Contains(object)) {
642 write_barrier_kind = kNoWriteBarrier;
643 }
644 if (!ValueNeedsWriteBarrier(value, isolate())) {
645 write_barrier_kind = kNoWriteBarrier;
646 }
647 if (FLAG_disable_write_barriers) {
648 write_barrier_kind = kNoWriteBarrier;
649 }
650 if (write_barrier_kind == WriteBarrierKind::kAssertNoWriteBarrier) {
651 write_barrier_assert_failed_(node, object, function_debug_name_, zone());
652 }
653 return write_barrier_kind;
654}
655
656MemoryLowering::AllocationGroup::AllocationGroup(Node* node,
657 AllocationType allocation,
658 Zone* zone)
659 : node_ids_(zone),
660 allocation_(CheckAllocationType(allocation)),
661 size_(nullptr) {
662 node_ids_.insert(node->id());
663}
664
665MemoryLowering::AllocationGroup::AllocationGroup(Node* node,
666 AllocationType allocation,
667 Node* size, Zone* zone)
668 : node_ids_(zone),
669 allocation_(CheckAllocationType(allocation)),
670 size_(size) {
671 node_ids_.insert(node->id());
672}
673
674void MemoryLowering::AllocationGroup::Add(Node* node) {
675 node_ids_.insert(node->id());
676}
677
678bool MemoryLowering::AllocationGroup::Contains(Node* node) const {
679 // Additions should stay within the same allocated object, so it's safe to
680 // ignore them.
681 while (node_ids_.find(node->id()) == node_ids_.end()) {
682 switch (node->opcode()) {
683 case IrOpcode::kBitcastTaggedToWord:
684 case IrOpcode::kBitcastWordToTagged:
685 case IrOpcode::kInt32Add:
686 case IrOpcode::kInt64Add:
687 node = NodeProperties::GetValueInput(node, 0);
688 break;
689 default:
690 return false;
691 }
692 }
693 return true;
694}
695
696MemoryLowering::AllocationState::AllocationState()
697 : group_(nullptr),
698 size_(std::numeric_limits<int>::max()),
699 top_(nullptr),
700 effect_(nullptr) {}
701
702MemoryLowering::AllocationState::AllocationState(AllocationGroup* group,
703 Node* effect)
704 : group_(group),
705 size_(std::numeric_limits<int>::max()),
706 top_(nullptr),
707 effect_(effect) {}
708
709MemoryLowering::AllocationState::AllocationState(AllocationGroup* group,
710 intptr_t size, Node* top,
711 Node* effect)
712 : group_(group), size_(size), top_(top), effect_(effect) {}
713
714bool MemoryLowering::AllocationState::IsYoungGenerationAllocation() const {
715 return group() && group()->IsYoungGenerationAllocation();
716}
717
718} // namespace compiler
719} // namespace internal
720} // namespace v8