20struct BytecodeArgument {
24 BytecodeArgument(
int offset,
int length) :
offset(
offset), length(length) {}
27struct BytecodeArgumentMapping : BytecodeArgument {
30 BytecodeArgumentMapping(
int offset,
int length,
int new_length)
34struct BytecodeArgumentCheck : BytecodeArgument {
35 enum CheckType { kCheckAddress = 0, kCheckValue };
40 BytecodeArgumentCheck(
int offset,
int length,
int check_offset)
41 : BytecodeArgument(
offset, length),
44 BytecodeArgumentCheck(
int offset,
int length,
int check_offset,
46 : BytecodeArgument(
offset, length),
53class BytecodeSequenceNode {
59 BytecodeSequenceNode(
int bytecode,
Zone* zone);
61 BytecodeSequenceNode& FollowedBy(
int bytecode);
64 BytecodeSequenceNode& ReplaceWith(
int bytecode);
78 BytecodeSequenceNode& MapArgument(
int bytecode_index_in_sequence,
80 int argument_byte_length,
81 int new_argument_byte_length = 0);
92 BytecodeSequenceNode& IfArgumentEqualsOffset(
int argument_offset,
93 int argument_byte_length,
94 int check_byte_offset);
100 BytecodeSequenceNode& IfArgumentEqualsValueAtOffset(
101 int argument_offset,
int argument_byte_length,
102 int other_bytecode_index_in_sequence,
int other_argument_offset,
103 int other_argument_byte_length);
112 BytecodeSequenceNode& IgnoreArgument(
int bytecode_index_in_sequence,
114 int argument_byte_length);
118 bool CheckArguments(
const uint8_t* bytecode,
int pc);
121 bool IsSequence()
const;
123 int SequenceLength()
const;
126 int OptimizedBytecode()
const;
129 BytecodeSequenceNode* Find(
int bytecode)
const;
133 size_t ArgumentSize()
const;
137 BytecodeArgumentMapping ArgumentMapping(
size_t index)
const;
141 ZoneLinkedList<BytecodeArgument>::iterator ArgumentIgnoredBegin()
const;
145 ZoneLinkedList<BytecodeArgument>::iterator ArgumentIgnoredEnd()
const;
147 bool HasIgnoredArguments()
const;
151 BytecodeSequenceNode& GetNodeByIndexInSequence(
int index_in_sequence);
169constexpr int BytecodeSequenceNode::kDummyBytecode;
171class RegExpBytecodePeephole {
173 RegExpBytecodePeephole(
Zone* zone,
size_t buffer_size,
174 const ZoneUnorderedMap<int, int>& jump_edges);
179 bool OptimizeBytecode(
const uint8_t* bytecode,
int length);
182 void CopyOptimizedBytecode(uint8_t* to_address)
const;
187 void DefineStandardSequences();
189 BytecodeSequenceNode& CreateSequence(
int bytecode);
192 int TryOptimizeSequence(
const uint8_t* bytecode,
int bytecode_length,
197 void EmitOptimization(
int start_pc,
const uint8_t* bytecode,
198 const BytecodeSequenceNode& last_node);
202 void AddJumpSourceFixup(
int fixup,
int pos);
206 void AddJumpDestinationFixup(
int fixup,
int pos);
208 void SetJumpDestinationFixup(
int fixup,
int pos);
210 void PrepareJumpStructures(
const ZoneUnorderedMap<int, int>& jump_edges);
214 void FixJump(
int jump_source,
int jump_destination);
215 void AddSentinelFixups(
int pos);
216 template <
typename T>
217 void EmitValue(T value);
218 template <
typename T>
219 void OverwriteValue(
int offset, T value);
220 void CopyRangeToOutput(
const uint8_t* orig_bytecode,
int start,
int length);
221 void SetRange(uint8_t value,
int count);
222 void EmitArgument(
int start_pc,
const uint8_t* bytecode,
223 BytecodeArgumentMapping arg);
258T GetValue(
const uint8_t* buffer,
int pos) {
260 return *
reinterpret_cast<const T*
>(buffer +
pos);
263int32_t GetArgumentValue(
const uint8_t* bytecode,
int offset,
int length) {
266 return GetValue<uint8_t>(bytecode,
offset);
268 return GetValue<int16_t>(bytecode,
offset);
270 return GetValue<int32_t>(bytecode,
offset);
276BytecodeSequenceNode::BytecodeSequenceNode(
int bytecode,
Zone* zone)
282 children_(ZoneUnorderedMap<int, BytecodeSequenceNode*>(zone)),
284 argument_check_(zone->New<ZoneLinkedList<BytecodeArgumentCheck>>(zone)),
288BytecodeSequenceNode& BytecodeSequenceNode::FollowedBy(
int bytecode) {
289 DCHECK(0 <= bytecode && bytecode < kRegExpBytecodeCount);
292 BytecodeSequenceNode* new_node =
293 zone()->New<BytecodeSequenceNode>(
bytecode, zone());
298 new_node->parent_ =
this;
306BytecodeSequenceNode& BytecodeSequenceNode::ReplaceWith(
int bytecode) {
307 DCHECK(0 <= bytecode && bytecode < kRegExpBytecodeCount);
314BytecodeSequenceNode& BytecodeSequenceNode::MapArgument(
315 int bytecode_index_in_sequence,
int argument_offset,
316 int argument_byte_length,
int new_argument_byte_length) {
320 BytecodeSequenceNode& ref_node =
321 GetNodeByIndexInSequence(bytecode_index_in_sequence);
324 int absolute_offset = ref_node.start_offset_ + argument_offset;
325 if (new_argument_byte_length == 0) {
326 new_argument_byte_length = argument_byte_length;
330 absolute_offset, argument_byte_length, new_argument_byte_length});
335BytecodeSequenceNode& BytecodeSequenceNode::IfArgumentEqualsOffset(
336 int argument_offset,
int argument_byte_length,
int check_byte_offset) {
338 DCHECK(argument_byte_length == 1 || argument_byte_length == 2 ||
339 argument_byte_length == 4);
344 absolute_offset, argument_byte_length, check_byte_offset});
349BytecodeSequenceNode& BytecodeSequenceNode::IfArgumentEqualsValueAtOffset(
350 int argument_offset,
int argument_byte_length,
351 int other_bytecode_index_in_sequence,
int other_argument_offset,
352 int other_argument_byte_length) {
355 DCHECK_EQ(argument_byte_length, other_argument_byte_length);
357 BytecodeSequenceNode& ref_node =
358 GetNodeByIndexInSequence(other_bytecode_index_in_sequence);
362 int other_absolute_offset = ref_node.start_offset_ + other_argument_offset;
365 BytecodeArgumentCheck{absolute_offset, argument_byte_length,
366 other_absolute_offset, other_argument_byte_length});
371BytecodeSequenceNode& BytecodeSequenceNode::IgnoreArgument(
372 int bytecode_index_in_sequence,
int argument_offset,
373 int argument_byte_length) {
377 BytecodeSequenceNode& ref_node =
378 GetNodeByIndexInSequence(bytecode_index_in_sequence);
381 int absolute_offset = ref_node.start_offset_ + argument_offset;
384 BytecodeArgument{absolute_offset, argument_byte_length});
389bool BytecodeSequenceNode::CheckArguments(
const uint8_t* bytecode,
int pc) {
390 bool is_valid =
true;
394 GetArgumentValue(bytecode,
pc + check_iter->offset, check_iter->length);
395 if (check_iter->type == BytecodeArgumentCheck::kCheckAddress) {
396 is_valid &= value ==
pc + check_iter->check_offset;
397 }
else if (check_iter->type == BytecodeArgumentCheck::kCheckValue) {
398 auto other_value = GetArgumentValue(
399 bytecode,
pc + check_iter->check_offset, check_iter->check_length);
400 is_valid &= value == other_value;
408bool BytecodeSequenceNode::IsSequence()
const {
412int BytecodeSequenceNode::SequenceLength()
const {
416int BytecodeSequenceNode::OptimizedBytecode()
const {
420BytecodeSequenceNode* BytecodeSequenceNode::Find(
int bytecode)
const {
422 if (found ==
children_.end())
return nullptr;
423 return found->second;
426size_t BytecodeSequenceNode::ArgumentSize()
const {
431BytecodeArgumentMapping BytecodeSequenceNode::ArgumentMapping(
432 size_t index)
const {
440ZoneLinkedList<BytecodeArgument>::iterator
441BytecodeSequenceNode::ArgumentIgnoredBegin()
const {
447ZoneLinkedList<BytecodeArgument>::iterator
448BytecodeSequenceNode::ArgumentIgnoredEnd()
const {
454bool BytecodeSequenceNode::HasIgnoredArguments()
const {
458BytecodeSequenceNode& BytecodeSequenceNode::GetNodeByIndexInSequence(
459 int index_in_sequence) {
464 return parent_->GetNodeByIndexInSequence(index_in_sequence);
470Zone* BytecodeSequenceNode::zone()
const {
return zone_; }
472RegExpBytecodePeephole::RegExpBytecodePeephole(
473 Zone* zone,
size_t buffer_size,
474 const ZoneUnorderedMap<int, int>& jump_edges)
485 PrepareJumpStructures(jump_edges);
486 DefineStandardSequences();
493 AddSentinelFixups(-1);
496 DCHECK_LE(buffer_size, std::numeric_limits<int>::max());
497 AddSentinelFixups(
static_cast<int>(buffer_size));
500void RegExpBytecodePeephole::DefineStandardSequences() {
503 CreateSequence(BC_LOAD_CURRENT_CHAR)
504 .FollowedBy(BC_CHECK_BIT_IN_TABLE)
505 .FollowedBy(BC_ADVANCE_CP_AND_GOTO)
508 .IfArgumentEqualsOffset(4, 4, 0)
509 .ReplaceWith(BC_SKIP_UNTIL_BIT_IN_TABLE)
510 .MapArgument(0, 1, 3)
511 .MapArgument(2, 1, 3, 4)
512 .MapArgument(1, 8, 16)
513 .MapArgument(1, 4, 4)
514 .MapArgument(0, 4, 4)
515 .IgnoreArgument(2, 4, 4);
517 CreateSequence(BC_CHECK_CURRENT_POSITION)
518 .FollowedBy(BC_LOAD_CURRENT_CHAR_UNCHECKED)
519 .FollowedBy(BC_CHECK_CHAR)
520 .FollowedBy(BC_ADVANCE_CP_AND_GOTO)
523 .IfArgumentEqualsOffset(4, 4, 0)
524 .ReplaceWith(BC_SKIP_UNTIL_CHAR_POS_CHECKED)
525 .MapArgument(1, 1, 3)
526 .MapArgument(3, 1, 3, 2)
527 .MapArgument(2, 1, 3, 2)
528 .MapArgument(0, 1, 3, 4)
529 .MapArgument(2, 4, 4)
530 .MapArgument(0, 4, 4)
531 .IgnoreArgument(3, 4, 4);
533 CreateSequence(BC_CHECK_CURRENT_POSITION)
534 .FollowedBy(BC_LOAD_CURRENT_CHAR_UNCHECKED)
535 .FollowedBy(BC_AND_CHECK_CHAR)
536 .FollowedBy(BC_ADVANCE_CP_AND_GOTO)
539 .IfArgumentEqualsOffset(4, 4, 0)
540 .ReplaceWith(BC_SKIP_UNTIL_CHAR_AND)
541 .MapArgument(1, 1, 3)
542 .MapArgument(3, 1, 3, 2)
543 .MapArgument(2, 1, 3, 2)
544 .MapArgument(2, 4, 4)
545 .MapArgument(0, 1, 3, 4)
546 .MapArgument(2, 8, 4)
547 .MapArgument(0, 4, 4)
548 .IgnoreArgument(3, 4, 4);
555 CreateSequence(BC_LOAD_CURRENT_CHAR)
556 .FollowedBy(BC_CHECK_CHAR)
557 .FollowedBy(BC_ADVANCE_CP_AND_GOTO)
560 .IfArgumentEqualsOffset(4, 4, 0)
561 .ReplaceWith(BC_SKIP_UNTIL_CHAR)
562 .MapArgument(0, 1, 3)
563 .MapArgument(2, 1, 3, 2)
564 .MapArgument(1, 1, 3, 2)
565 .MapArgument(1, 4, 4)
566 .MapArgument(0, 4, 4)
567 .IgnoreArgument(2, 4, 4);
569 CreateSequence(BC_LOAD_CURRENT_CHAR)
570 .FollowedBy(BC_CHECK_CHAR)
571 .FollowedBy(BC_CHECK_CHAR)
574 .IfArgumentEqualsValueAtOffset(4, 4, 1, 4, 4)
575 .FollowedBy(BC_ADVANCE_CP_AND_GOTO)
578 .IfArgumentEqualsOffset(4, 4, 0)
579 .ReplaceWith(BC_SKIP_UNTIL_CHAR_OR_CHAR)
580 .MapArgument(0, 1, 3)
581 .MapArgument(3, 1, 3, 4)
582 .MapArgument(1, 1, 3, 2)
583 .MapArgument(2, 1, 3, 2)
584 .MapArgument(1, 4, 4)
585 .MapArgument(0, 4, 4)
586 .IgnoreArgument(2, 4, 4)
587 .IgnoreArgument(3, 4, 4);
589 CreateSequence(BC_LOAD_CURRENT_CHAR)
590 .FollowedBy(BC_CHECK_GT)
593 .IfArgumentEqualsOffset(4, 4, 56)
594 .FollowedBy(BC_CHECK_BIT_IN_TABLE)
597 .IfArgumentEqualsOffset(4, 4, 48)
602 .IfArgumentEqualsValueAtOffset(4, 4, 1, 4, 4)
603 .FollowedBy(BC_ADVANCE_CP_AND_GOTO)
606 .IfArgumentEqualsOffset(4, 4, 0)
607 .ReplaceWith(BC_SKIP_UNTIL_GT_OR_NOT_BIT_IN_TABLE)
608 .MapArgument(0, 1, 3)
609 .MapArgument(4, 1, 3, 2)
610 .MapArgument(1, 1, 3, 2)
611 .MapArgument(2, 8, 16)
612 .MapArgument(1, 4, 4)
613 .MapArgument(0, 4, 4)
614 .IgnoreArgument(2, 4, 4)
615 .IgnoreArgument(3, 4, 4)
616 .IgnoreArgument(4, 4, 4);
619bool RegExpBytecodePeephole::OptimizeBytecode(
const uint8_t* bytecode,
622 bool did_optimize =
false;
624 while (old_pc < length) {
625 int replaced_len = TryOptimizeSequence(bytecode, length, old_pc);
626 if (replaced_len > 0) {
627 old_pc += replaced_len;
630 int bc = bytecode[old_pc];
632 CopyRangeToOutput(bytecode, old_pc, bc_len);
644void RegExpBytecodePeephole::CopyOptimizedBytecode(uint8_t* to_address)
const {
648int RegExpBytecodePeephole::Length()
const {
return pc(); }
650BytecodeSequenceNode& RegExpBytecodePeephole::CreateSequence(
int bytecode) {
652 DCHECK(0 <= bytecode && bytecode < kRegExpBytecodeCount);
657int RegExpBytecodePeephole::TryOptimizeSequence(
const uint8_t* bytecode,
661 BytecodeSequenceNode* valid_seq_end =
nullptr;
663 int current_pc = start_pc;
667 while (current_pc < bytecode_length) {
668 seq_node = seq_node->Find(bytecode[current_pc]);
669 if (seq_node ==
nullptr)
break;
670 if (!seq_node->CheckArguments(bytecode, start_pc))
break;
672 if (seq_node->IsSequence()) valid_seq_end = seq_node;
677 EmitOptimization(start_pc, bytecode, *valid_seq_end);
678 return valid_seq_end->SequenceLength();
684void RegExpBytecodePeephole::EmitOptimization(
685 int start_pc,
const uint8_t* bytecode,
686 const BytecodeSequenceNode& last_node) {
688 int optimized_start_pc =
pc();
694 ZoneLinkedList<int> delete_jumps = ZoneLinkedList<int>(zone());
696 uint32_t bc = last_node.OptimizedBytecode();
699 for (
size_t arg = 0; arg < last_node.ArgumentSize(); arg++) {
700 BytecodeArgumentMapping arg_map = last_node.ArgumentMapping(arg);
701 int arg_pos = start_pc + arg_map.offset;
706 int jump_source = jump_edge_iter->first;
707 int jump_destination = jump_edge_iter->second;
711 delete_jumps.push_back(jump_source);
715 int& usage_count = jump_count_iter->second;
720 EmitArgument(start_pc, bytecode, arg_map);
726 if (last_node.HasIgnoredArguments()) {
727 for (
auto ignored_arg = last_node.ArgumentIgnoredBegin();
728 ignored_arg != last_node.ArgumentIgnoredEnd(); ignored_arg++) {
729 auto jump_edge_iter =
jump_edges_.find(start_pc + ignored_arg->offset);
731 int jump_source = jump_edge_iter->first;
732 int jump_destination = jump_edge_iter->second;
734 delete_jumps.push_back(jump_source);
738 int& usage_count = jump_count_iter->second;
749 int jump_candidate_destination = jump_destination_candidate->first;
750 int jump_candidate_count = jump_destination_candidate->second;
753 jump_candidate_count == 0) {
754 ++jump_destination_candidate;
755 jump_candidate_destination = jump_destination_candidate->first;
756 jump_candidate_count = jump_destination_candidate->second;
759 int preserve_from = start_pc + last_node.SequenceLength();
761 jump_candidate_destination < start_pc + last_node.SequenceLength()) {
762 preserve_from = jump_candidate_destination;
767 for (
auto jump_iter =
jump_edges_.lower_bound(preserve_from);
770 start_pc + last_node.SequenceLength();
772 int jump_destination = jump_iter->second;
773 if (jump_destination > start_pc && jump_destination < preserve_from) {
774 preserve_from = jump_destination;
781 int preserve_length = start_pc + last_node.SequenceLength() - preserve_from;
782 fixup_length += preserve_length;
784 AddJumpSourceFixup(fixup_length,
785 start_pc + last_node.SequenceLength() - preserve_length);
789 AddJumpDestinationFixup(fixup_length, start_pc + 1);
792 SetJumpDestinationFixup(
pc() - preserve_from, preserve_from);
793 CopyRangeToOutput(bytecode, preserve_from, preserve_length);
795 AddJumpDestinationFixup(fixup_length, start_pc + 1);
797 AddJumpSourceFixup(fixup_length, start_pc + last_node.SequenceLength());
801 for (
int del : delete_jumps) {
802 if (del < preserve_from) {
808void RegExpBytecodePeephole::AddJumpSourceFixup(
int fixup,
int pos) {
813 int previous_fixup_value = (--previous_fixup)->
second;
817void RegExpBytecodePeephole::AddJumpDestinationFixup(
int fixup,
int pos) {
822 int previous_fixup_value = (--previous_fixup)->
second;
826void RegExpBytecodePeephole::SetJumpDestinationFixup(
int fixup,
int pos) {
831 int previous_fixup_value = (--previous_fixup)->
second;
836void RegExpBytecodePeephole::PrepareJumpStructures(
837 const ZoneUnorderedMap<int, int>& jump_edges) {
838 for (
auto jump_edge : jump_edges) {
839 int jump_source = jump_edge.first;
840 int jump_destination = jump_edge.second;
842 jump_edges_.emplace(jump_source, jump_destination);
847void RegExpBytecodePeephole::FixJumps() {
848 int position_fixup = 0;
851 int next_source_fixup_offset = next_source_fixup->first;
852 int next_source_fixup_value = next_source_fixup->second;
855 int jump_source = jump_edge.first;
856 int jump_destination = jump_edge.second;
857 while (jump_source >= next_source_fixup_offset) {
858 position_fixup = next_source_fixup_value;
860 next_source_fixup_offset = next_source_fixup->first;
861 next_source_fixup_value = next_source_fixup->second;
863 jump_source += position_fixup;
865 FixJump(jump_source, jump_destination);
871 int jump_source = jump_edge.first;
872 int jump_destination = jump_edge.second;
874 FixJump(jump_source, jump_destination);
878void RegExpBytecodePeephole::FixJump(
int jump_source,
int jump_destination) {
879 int fixed_jump_destination =
882 DCHECK_LT(fixed_jump_destination, Length());
888 DCHECK_LT(jump_bc, kRegExpBytecodeCount);
891 if (jump_destination != fixed_jump_destination) {
892 OverwriteValue<uint32_t>(jump_source, fixed_jump_destination);
896void RegExpBytecodePeephole::AddSentinelFixups(
int pos) {
902void RegExpBytecodePeephole::EmitValue(T value) {
905 uint8_t* value_byte_iter =
reinterpret_cast<uint8_t*
>(&
value);
908 value_byte_iter +
sizeof(T));
912void RegExpBytecodePeephole::OverwriteValue(
int offset, T value) {
913 uint8_t* value_byte_iter =
reinterpret_cast<uint8_t*
>(&
value);
914 uint8_t* value_byte_iter_end = value_byte_iter +
sizeof(
T);
915 while (value_byte_iter < value_byte_iter_end) {
920void RegExpBytecodePeephole::CopyRangeToOutput(
const uint8_t* orig_bytecode,
921 int start,
int length) {
925 orig_bytecode +
start,
926 orig_bytecode +
start + length);
929void RegExpBytecodePeephole::SetRange(uint8_t value,
int count) {
936void RegExpBytecodePeephole::EmitArgument(
int start_pc,
const uint8_t* bytecode,
937 BytecodeArgumentMapping arg) {
938 int arg_pos = start_pc + arg.offset;
939 switch (arg.length) {
942 EmitValue(GetValue<uint8_t>(bytecode, arg_pos));
946 EmitValue(GetValue<uint16_t>(bytecode, arg_pos));
953#ifdef V8_TARGET_BIG_ENDIAN
960 switch (arg.new_length) {
962 EmitValue<uint16_t>(val);
968 Length() -
sizeof(uint32_t));
969#ifdef V8_TARGET_BIG_ENDIAN
974 OverwriteValue<uint32_t>(
975 pc() -
sizeof(uint32_t),
976 (
static_cast<uint32_t
>(val) << 8) | (prev_val & 0xFF));
981 EmitValue<uint32_t>(val);
988 EmitValue(GetValue<uint32_t>(bytecode, arg_pos));
992 EmitValue(GetValue<uint64_t>(bytecode, arg_pos));
995 CopyRangeToOutput(bytecode, arg_pos,
996 std::min(arg.length, arg.new_length));
997 if (arg.length < arg.new_length) {
998 SetRange(0x00, arg.new_length - arg.length);
1004int RegExpBytecodePeephole::pc()
const {
1009Zone* RegExpBytecodePeephole::zone()
const {
return zone_; }
1014DirectHandle<TrustedByteArray>
1015RegExpBytecodePeepholeOptimization::OptimizeBytecode(
1017 const uint8_t* bytecode,
int length,
1019 RegExpBytecodePeephole peephole(zone, length, jump_edges);
1020 bool did_optimize = peephole.OptimizeBytecode(bytecode, length);
1022 isolate->factory()->NewTrustedByteArray(peephole.Length());
1023 peephole.CopyOptimizedBytecode(array->begin());
1025 if (did_optimize &&
v8_flags.trace_regexp_peephole_optimization) {
1026 PrintF(
"Original Bytecode:\n");
1028 PrintF(
"Optimized Bytecode:\n");
1030 source->ToCString().get());
interpreter::Bytecode bytecode
base::Vector< const RegExpInstruction > bytecode_
constexpr int RegExpBytecodeLength(int bytecode)
constexpr int kBitsPerByte
void PrintF(const char *format,...)
V8_EXPORT_PRIVATE FlagValues v8_flags
void RegExpBytecodeDisassemble(const uint8_t *code_base, int length, const char *pattern)
void MemCopy(void *dest, const void *src, size_t size)
BytecodeSequenceNode * sequences_
ZoneMap< int, int > jump_usage_counts_
ZoneVector< BytecodeArgumentMapping > * argument_mapping_
ZoneMap< int, int > jump_source_fixups_
ZoneMap< int, int > jump_edges_
ZoneLinkedList< BytecodeArgumentCheck > * argument_check_
ZoneLinkedList< BytecodeArgument > * argument_ignored_
BytecodeSequenceNode * parent_
ZoneUnorderedMap< int, BytecodeSequenceNode * > children_
ZoneMap< int, int > jump_edges_mapped_
ZoneVector< uint8_t > optimized_bytecode_buffer_
static constexpr int kDummyBytecode
int bytecode_replacement_
ZoneMap< int, int > jump_destination_fixups_
#define DCHECK_LE(v1, v2)
#define DCHECK(condition)
#define DCHECK_LT(v1, v2)
#define DCHECK_EQ(v1, v2)
#define DCHECK_GT(v1, v2)
constexpr bool IsAligned(T value, U alignment)
#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName)
std::unique_ptr< ValueMirror > value