v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
constant-expression-interface.cc
Go to the documentation of this file.
1// Copyright 2021 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
6
11#include "src/wasm/decoder.h"
14
15namespace v8 {
16namespace internal {
17namespace wasm {
18
19void ConstantExpressionInterface::I32Const(FullDecoder* decoder, Value* result,
20 int32_t value) {
21 if (generate_value()) result->runtime_value = WasmValue(value);
22}
23
24void ConstantExpressionInterface::I64Const(FullDecoder* decoder, Value* result,
25 int64_t value) {
26 if (generate_value()) result->runtime_value = WasmValue(value);
27}
28
29void ConstantExpressionInterface::F32Const(FullDecoder* decoder, Value* result,
30 float value) {
31 if (generate_value()) result->runtime_value = WasmValue(value);
32}
33
34void ConstantExpressionInterface::F64Const(FullDecoder* decoder, Value* result,
35 double value) {
36 if (generate_value()) result->runtime_value = WasmValue(value);
37}
38
39void ConstantExpressionInterface::S128Const(FullDecoder* decoder,
40 const Simd128Immediate& imm,
41 Value* result) {
42 if (!generate_value()) return;
43 result->runtime_value = WasmValue(imm.value, kWasmS128);
44}
45
46void ConstantExpressionInterface::UnOp(FullDecoder* decoder, WasmOpcode opcode,
47 const Value& input, Value* result) {
48 if (!generate_value()) return;
49 switch (opcode) {
50 case kExprExternConvertAny: {
51 result->runtime_value =
52 WasmValue(WasmToJSObject(isolate_, input.runtime_value.to_ref()),
54 input.type.nullability()));
55 break;
56 }
57 case kExprAnyConvertExtern: {
58 const char* error_message = nullptr;
59 result->runtime_value =
60 WasmValue(JSToWasmObject(isolate_, input.runtime_value.to_ref(),
61 kWasmAnyRef, &error_message)
62 .ToHandleChecked(),
64 input.type.nullability()));
65 break;
66 }
67 default:
69 }
70}
71
72void ConstantExpressionInterface::BinOp(FullDecoder* decoder, WasmOpcode opcode,
73 const Value& lhs, const Value& rhs,
74 Value* result) {
75 if (!generate_value()) return;
76 switch (opcode) {
77 case kExprI32Add:
78 result->runtime_value = WasmValue(base::AddWithWraparound(
79 lhs.runtime_value.to_i32(), rhs.runtime_value.to_i32()));
80 break;
81 case kExprI32Sub:
82 result->runtime_value = WasmValue(base::SubWithWraparound(
83 lhs.runtime_value.to_i32(), rhs.runtime_value.to_i32()));
84 break;
85 case kExprI32Mul:
86 result->runtime_value = WasmValue(base::MulWithWraparound(
87 lhs.runtime_value.to_i32(), rhs.runtime_value.to_i32()));
88 break;
89 case kExprI64Add:
90 result->runtime_value = WasmValue(base::AddWithWraparound(
91 lhs.runtime_value.to_i64(), rhs.runtime_value.to_i64()));
92 break;
93 case kExprI64Sub:
94 result->runtime_value = WasmValue(base::SubWithWraparound(
95 lhs.runtime_value.to_i64(), rhs.runtime_value.to_i64()));
96 break;
97 case kExprI64Mul:
98 result->runtime_value = WasmValue(base::MulWithWraparound(
99 lhs.runtime_value.to_i64(), rhs.runtime_value.to_i64()));
100 break;
101 default:
102 UNREACHABLE();
103 }
104}
105
106void ConstantExpressionInterface::RefNull(FullDecoder* decoder, ValueType type,
107 Value* result) {
108 if (!generate_value()) return;
109 result->runtime_value = WasmValue(
110 type.use_wasm_null() ? Cast<Object>(isolate_->factory()->wasm_null())
111 : Cast<Object>(isolate_->factory()->null_value()),
112 decoder->module_->canonical_type(type));
113}
114
115void ConstantExpressionInterface::RefFunc(FullDecoder* decoder,
116 uint32_t function_index,
117 Value* result) {
118 if (isolate_ == nullptr) {
119 outer_module_->functions[function_index].declared = true;
120 return;
121 }
122 if (!generate_value()) return;
123 ModuleTypeIndex sig_index = module_->functions[function_index].sig_index;
124 bool function_is_shared = module_->type(sig_index).is_shared;
125 CanonicalValueType type =
126 CanonicalValueType::Ref(module_->canonical_type_id(sig_index),
127 function_is_shared, RefTypeKind::kFunction)
129 DirectHandle<WasmFuncRef> func_ref =
131 isolate_,
132 function_is_shared ? shared_trusted_instance_data_
134 function_index);
135 result->runtime_value = WasmValue(func_ref, type);
136}
137
138void ConstantExpressionInterface::GlobalGet(FullDecoder* decoder, Value* result,
139 const GlobalIndexImmediate& imm) {
140 if (!generate_value()) return;
141 const WasmGlobal& global = module_->globals[imm.index];
142 DCHECK(!global.mutability);
143 DirectHandle<WasmTrustedInstanceData> data =
145 CanonicalValueType type = module_->canonical_type(global.type);
146 result->runtime_value =
147 type.is_numeric()
148 ? WasmValue(reinterpret_cast<uint8_t*>(
149 data->untagged_globals_buffer()->backing_store()) +
150 global.offset,
151 type)
152 : WasmValue(
153 direct_handle(data->tagged_globals_buffer()->get(global.offset),
154 isolate_),
155 type);
156}
157
160 const TypeDefinition& type, const Value& descriptor) {
161 if (!type.has_descriptor()) {
162 return direct_handle(
163 Cast<Map>(data->managed_object_maps()->get(index.index)), isolate_);
164 }
165
166 DCHECK(type.has_descriptor());
167 WasmValue desc = descriptor.runtime_value;
168 DCHECK_EQ(desc.type().ref_index(),
169 module_->canonical_type_id(type.descriptor));
170 DirectHandle<Object> maybe_obj = desc.to_ref();
171 if (!IsWasmStruct(*maybe_obj)) {
172 DCHECK(IsNull(*maybe_obj));
173 error_ = MessageTemplate::kWasmTrapNullDereference;
174 return {};
175 }
176 return direct_handle(Cast<WasmStruct>(*maybe_obj)->get_described_rtt(),
177 isolate_);
178}
179
180void ConstantExpressionInterface::StructNew(FullDecoder* decoder,
181 const StructIndexImmediate& imm,
182 const Value& descriptor,
183 const Value args[], Value* result) {
184 if (!generate_value()) return;
187 const TypeDefinition& type = module_->type(imm.index);
188 const StructType* struct_type = type.struct_type;
189 DCHECK_EQ(struct_type, imm.struct_type);
190
191 DirectHandle<Map> rtt = GetRtt(data, imm.index, type, descriptor);
192 if (rtt.is_null()) return; // Trap (descriptor was null).
193
195 if (type.is_descriptor()) {
197 rtt);
198 } else {
199 obj = isolate_->factory()->NewWasmStructUninitialized(struct_type, rtt);
200 }
201 DisallowGarbageCollection no_gc; // Must initialize fields first.
202
203 for (uint32_t i = 0; i < struct_type->field_count(); i++) {
204 int offset = struct_type->field_offset(i);
205 if (struct_type->field(i).is_numeric()) {
206 uint8_t* address =
207 reinterpret_cast<uint8_t*>(obj->RawFieldAddress(offset));
208 args[i].runtime_value.Packed(struct_type->field(i)).CopyTo(address);
209 } else {
211 *obj, offset, *args[i].runtime_value.to_ref());
212 }
213 }
214 result->runtime_value = WasmValue(
215 obj, decoder->module_->canonical_type(
217}
218
219void ConstantExpressionInterface::StringConst(FullDecoder* decoder,
220 const StringConstImmediate& imm,
221 Value* result) {
222 if (!generate_value()) return;
224
225 DCHECK_LT(imm.index, module_->stringref_literals.size());
226
227 const wasm::WasmStringRefLiteral& literal =
228 module_->stringref_literals[imm.index];
229 const base::Vector<const uint8_t> module_bytes =
230 trusted_instance_data_->native_module()->wire_bytes();
231 const base::Vector<const uint8_t> string_bytes = module_bytes.SubVector(
232 literal.source.offset(), literal.source.end_offset());
233 DirectHandle<String> string =
235 ->NewStringFromUtf8(string_bytes, unibrow::Utf8Variant::kWtf8)
236 .ToHandleChecked();
237 result->runtime_value = WasmValue(string, kWasmRefString);
238}
239
240namespace {
241WasmValue DefaultValueForType(ValueType type, Isolate* isolate,
242 const WasmModule* module) {
243 switch (type.kind()) {
244 case kI32:
245 case kI8:
246 case kI16:
247 return WasmValue(0);
248 case kI64:
249 return WasmValue(int64_t{0});
250 case kF16:
251 case kF32:
252 return WasmValue(0.0f);
253 case kF64:
254 return WasmValue(0.0);
255 case kS128:
256 return WasmValue(Simd128());
257 case kRefNull:
258 return WasmValue(type.use_wasm_null()
259 ? Cast<Object>(isolate->factory()->wasm_null())
260 : Cast<Object>(isolate->factory()->null_value()),
261 module->canonical_type(type));
262 case kVoid:
263 case kRef:
264 case kTop:
265 case kBottom:
266 UNREACHABLE();
267 }
268}
269} // namespace
270
271void ConstantExpressionInterface::StructNewDefault(
272 FullDecoder* decoder, const StructIndexImmediate& imm,
273 const Value& descriptor, Value* result) {
274 if (!generate_value()) return;
275 DirectHandle<WasmTrustedInstanceData> data =
277 const TypeDefinition& type = module_->type(imm.index);
278 const StructType* struct_type = type.struct_type;
279 DCHECK_EQ(struct_type, imm.struct_type);
280
281 DirectHandle<Map> rtt = GetRtt(data, imm.index, type, descriptor);
282 if (rtt.is_null()) return; // Trap (descriptor was null).
283
284 DirectHandle<WasmStruct> obj;
285 if (type.is_descriptor()) {
287 rtt);
288 } else {
289 obj = isolate_->factory()->NewWasmStructUninitialized(struct_type, rtt);
290 }
291 DisallowGarbageCollection no_gc; // Must initialize fields first.
292
293 for (uint32_t i = 0; i < struct_type->field_count(); i++) {
294 int offset = struct_type->field_offset(i);
295 ValueType ftype = struct_type->field(i);
296 if (ftype.is_numeric()) {
297 uint8_t* address =
298 reinterpret_cast<uint8_t*>(obj->RawFieldAddress(offset));
299 DefaultValueForType(ftype, isolate_, module_)
300 .Packed(ftype)
301 .CopyTo(address);
302 } else {
304 *obj, offset,
305 *DefaultValueForType(ftype, isolate_, module_).to_ref());
306 }
307 }
308
309 result->runtime_value = WasmValue(
310 obj, decoder->module_->canonical_type(
311 ValueType::Ref(imm.heap_type()).AsExactIfProposalEnabled()));
312}
313
314void ConstantExpressionInterface::ArrayNew(FullDecoder* decoder,
315 const ArrayIndexImmediate& imm,
316 const Value& length,
317 const Value& initial_value,
318 Value* result) {
319 if (!generate_value()) return;
320 DirectHandle<WasmTrustedInstanceData> data =
322 DirectHandle<Map> rtt{
323 Cast<Map>(data->managed_object_maps()->get(imm.index.index)), isolate_};
324 if (length.runtime_value.to_u32() >
325 static_cast<uint32_t>(WasmArray::MaxLength(imm.array_type))) {
326 error_ = MessageTemplate::kWasmTrapArrayTooLarge;
327 return;
328 }
329 result->runtime_value = WasmValue(
330 isolate_->factory()->NewWasmArray(imm.array_type->element_type(),
331 length.runtime_value.to_u32(),
332 initial_value.runtime_value, rtt),
333 decoder->module_->canonical_type(
334 ValueType::Ref(imm.heap_type()).AsExactIfProposalEnabled()));
335}
336
337void ConstantExpressionInterface::ArrayNewDefault(
338 FullDecoder* decoder, const ArrayIndexImmediate& imm, const Value& length,
339 Value* result) {
340 if (!generate_value()) return;
341 Value initial_value(decoder->pc(), imm.array_type->element_type());
342 initial_value.runtime_value = DefaultValueForType(
343 imm.array_type->element_type(), isolate_, decoder->module_);
344 return ArrayNew(decoder, imm, length, initial_value, result);
345}
346
347void ConstantExpressionInterface::ArrayNewFixed(
348 FullDecoder* decoder, const ArrayIndexImmediate& array_imm,
349 const IndexImmediate& length_imm, const Value elements[], Value* result) {
350 if (!generate_value()) return;
351 DirectHandle<WasmTrustedInstanceData> data =
352 GetTrustedInstanceDataForTypeIndex(array_imm.index);
353 DirectHandle<Map> rtt{
354 Cast<Map>(data->managed_object_maps()->get(array_imm.index.index)),
355 isolate_};
356 base::Vector<WasmValue> element_values =
357 decoder->zone_->AllocateVector<WasmValue>(length_imm.index);
358 for (size_t i = 0; i < length_imm.index; i++) {
359 element_values[i] = elements[i].runtime_value;
360 }
361 result->runtime_value = WasmValue(
362 isolate_->factory()->NewWasmArrayFromElements(array_imm.array_type,
363 element_values, rtt),
364 decoder->module_->canonical_type(
365 ValueType::Ref(array_imm.heap_type()).AsExactIfProposalEnabled()));
366}
367
368// TODO(14034): These expressions are non-constant for now. There are plans to
369// make them constant in the future, so we retain the required infrastructure
370// here.
371void ConstantExpressionInterface::ArrayNewSegment(
372 FullDecoder* decoder, const ArrayIndexImmediate& array_imm,
373 const IndexImmediate& segment_imm, const Value& offset_value,
374 const Value& length_value, Value* result) {
375 if (!generate_value()) return;
376
377 DirectHandle<WasmTrustedInstanceData> data =
378 GetTrustedInstanceDataForTypeIndex(array_imm.index);
379
380 DirectHandle<Map> rtt{
381 Cast<Map>(data->managed_object_maps()->get(array_imm.index.index)),
382 isolate_};
383 DCHECK_EQ(rtt->wasm_type_info()->type_index(),
384 decoder->module_->canonical_type_id(array_imm.index));
385
386 uint32_t length = length_value.runtime_value.to_u32();
387 uint32_t offset = offset_value.runtime_value.to_u32();
388 if (length >
389 static_cast<uint32_t>(WasmArray::MaxLength(array_imm.array_type))) {
390 error_ = MessageTemplate::kWasmTrapArrayTooLarge;
391 return;
392 }
393 CanonicalValueType element_type = rtt->wasm_type_info()->element_type();
394 CanonicalValueType result_type =
395 rtt->wasm_type_info()->type().AsExactIfProposalEnabled();
396 if (element_type.is_numeric()) {
397 const WasmDataSegment& data_segment =
398 module_->data_segments[segment_imm.index];
399 uint32_t length_in_bytes =
400 length * array_imm.array_type->element_type().value_kind_size();
401
402 if (!base::IsInBounds<uint32_t>(offset, length_in_bytes,
403 data_segment.source.length())) {
404 error_ = MessageTemplate::kWasmTrapDataSegmentOutOfBounds;
405 return;
406 }
407
408 Address source =
409 data->data_segment_starts()->get(segment_imm.index) + offset;
410 DirectHandle<WasmArray> array_value =
411 isolate_->factory()->NewWasmArrayFromMemory(length, rtt, element_type,
412 source);
413 result->runtime_value = WasmValue(array_value, result_type);
414 } else {
415 const wasm::WasmElemSegment* elem_segment =
416 &decoder->module_->elem_segments[segment_imm.index];
417 // A constant expression should not observe if a passive segment is dropped.
418 // However, it should consider active and declarative segments as empty.
420 offset, length,
421 elem_segment->status == WasmElemSegment::kStatusPassive
422 ? elem_segment->element_count
423 : 0)) {
424 error_ = MessageTemplate::kWasmTrapElementSegmentOutOfBounds;
425 return;
426 }
427
428 DirectHandle<Object> array_object =
429 isolate_->factory()->NewWasmArrayFromElementSegment(
431 segment_imm.index, offset, length, rtt, element_type);
432 if (IsSmi(*array_object)) {
433 // A smi result stands for an error code.
434 error_ = static_cast<MessageTemplate>(Cast<Smi>(*array_object).value());
435 } else {
436 result->runtime_value = WasmValue(array_object, result_type);
437 }
438 }
439}
440
441void ConstantExpressionInterface::RefI31(FullDecoder* decoder,
442 const Value& input, Value* result) {
443 if (!generate_value()) return;
444 Address raw = input.runtime_value.to_i32();
445 // We have to craft the Smi manually because we accept out-of-bounds inputs.
446 // For 32-bit Smi builds, set the topmost bit to sign-extend the second bit.
447 // This way, interpretation in JS (if this value escapes there) will be the
448 // same as i31.get_s.
449 static_assert((SmiValuesAre31Bits() ^ SmiValuesAre32Bits()) == 1);
450 intptr_t shifted;
451 if constexpr (SmiValuesAre31Bits()) {
452 shifted = raw << (kSmiTagSize + kSmiShiftSize);
453 } else {
454 shifted =
455 static_cast<intptr_t>(raw << (kSmiTagSize + kSmiShiftSize + 1)) >> 1;
456 }
457 result->runtime_value =
458 WasmValue(direct_handle(Tagged<Smi>(shifted), isolate_), kWasmRefI31);
459}
460
461void ConstantExpressionInterface::DoReturn(FullDecoder* decoder,
462 uint32_t /*drop_values*/) {
463 end_found_ = true;
464 // End decoding on "end". Note: We need this because we do not know the length
465 // of a constant expression while decoding it.
466 decoder->set_end(decoder->pc() + 1);
467 if (generate_value()) {
468 computed_value_ = decoder->stack_value(1)->runtime_value;
469 }
470}
471
472DirectHandle<WasmTrustedInstanceData>
474 ModuleTypeIndex index) {
475 bool type_is_shared = module_->type(index).is_shared;
476 return type_is_shared ? shared_trusted_instance_data_
478}
479
480} // namespace wasm
481} // namespace internal
482} // namespace v8
V8_INLINE bool is_null() const
Definition handles.h:693
V8_WARN_UNUSED_RESULT MaybeHandle< String > NewStringFromUtf8(base::Vector< const char > str, AllocationType allocation=AllocationType::kYoung)
Definition factory.cc:753
v8::internal::Factory * factory()
Definition isolate.h:1527
static constexpr int kMaxValue
Definition smi.h:101
static void store(Tagged< HeapObject > host, PtrType value)
static constexpr int MaxLength(uint32_t element_size_bytes)
static DirectHandle< WasmStruct > AllocateDescriptorUninitialized(Isolate *isolate, DirectHandle< WasmTrustedInstanceData > trusted_data, wasm::ModuleTypeIndex index, DirectHandle< Map > map)
static DirectHandle< WasmFuncRef > GetOrCreateFuncRef(Isolate *isolate, DirectHandle< WasmTrustedInstanceData > trusted_instance_data, int function_index)
CanonicalValueType AsExactIfProposalEnabled(Exactness exact=Exactness::kExact) const
static constexpr CanonicalValueType Ref(CanonicalTypeIndex index, bool shared, RefTypeKind kind)
static constexpr CanonicalValueType RefMaybeNull(CanonicalValueType type, Nullability nullable)
DirectHandle< WasmTrustedInstanceData > trusted_instance_data_
DirectHandle< WasmTrustedInstanceData > shared_trusted_instance_data_
DirectHandle< WasmTrustedInstanceData > GetTrustedInstanceDataForTypeIndex(ModuleTypeIndex index)
DirectHandle< Map > GetRtt(DirectHandle< WasmTrustedInstanceData > data, ModuleTypeIndex index, const TypeDefinition &type, const Value &descriptor)
uint32_t field_offset(uint32_t index) const
ValueType field(uint32_t index) const
constexpr Nullability nullability() const
Definition value-type.h:389
constexpr bool is_numeric() const
Definition value-type.h:373
ValueType AsExactIfProposalEnabled(Exactness exact=Exactness::kExact) const
Definition value-type.h:932
static constexpr ValueType Ref(ModuleTypeIndex index, bool shared, RefTypeKind kind)
Definition value-type.h:887
base::Vector< const DirectHandle< Object > > args
Definition execution.cc:74
int32_t offset
ZoneVector< RpoNumber > & result
FunctionLiteral * literal
Definition liveedit.cc:294
int16_t MulWithWraparound(int16_t a, int16_t b)
constexpr bool IsInBounds(T index, T length, T max)
Definition bounds.h:49
constexpr bool IsInRange(T value, U lower_limit, U higher_limit)
Definition bounds.h:20
WordWithBits< 128 > Simd128
Definition index.h:236
MaybeDirectHandle< Object > JSToWasmObject(Isolate *isolate, DirectHandle< Object > value, CanonicalValueType expected, const char **error_message)
constexpr IndependentHeapType kWasmAnyRef
constexpr IndependentHeapType kWasmExternRef
constexpr IndependentHeapType kWasmRefI31
constexpr IndependentValueType kWasmS128
constexpr size_t kV8MaxWasmStringLiterals
Definition wasm-limits.h:69
DirectHandle< Object > WasmToJSObject(Isolate *isolate, DirectHandle< Object > value)
constexpr IndependentHeapType kWasmRefString
PerThreadAssertScopeDebugOnly< false, SAFEPOINTS_ASSERT, HEAP_ALLOCATION_ASSERT > DisallowGarbageCollection
const int kSmiTagSize
Definition v8-internal.h:87
wasm::WasmModule WasmModule
V8_INLINE constexpr bool IsSmi(TaggedImpl< kRefType, StorageType > obj)
Definition objects.h:665
V8_INLINE DirectHandle< T > direct_handle(Tagged< T > object, Isolate *isolate)
constexpr bool SmiValuesAre31Bits()
const int kSmiShiftSize
kMemory0SizeOffset Address kNewAllocationLimitAddressOffset Address kOldAllocationLimitAddressOffset uint8_t kGlobalsStartOffset kJumpTableStartOffset std::atomic< uint32_t > kTieringBudgetArrayOffset kDataSegmentStartsOffset kElementSegmentsOffset kInstanceObjectOffset kMemoryObjectsOffset tagged_globals_buffer
constexpr bool SmiValuesAre32Bits()
kInstanceDescriptorsOffset kTransitionsOrPrototypeInfoOffset IsNull(value)||IsJSProxy(value)||IsWasmObject(value)||(IsJSObject(value) &&(HeapLayout
Definition map-inl.h:70
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
Definition c-api.cc:87
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
std::vector< WasmFunction > functions