bytecode-array-writer-unittest.cc 9.61 KB
Newer Older
1 2 3 4 5 6
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/v8.h"

7 8
#include "src/api.h"
#include "src/factory.h"
9
#include "src/interpreter/bytecode-array-writer.h"
10 11
#include "src/interpreter/bytecode-label.h"
#include "src/interpreter/constant-array-builder.h"
12
#include "src/isolate.h"
13
#include "src/source-position-table.h"
14 15 16 17 18 19 20 21 22 23 24
#include "src/utils.h"
#include "test/unittests/interpreter/bytecode-utils.h"
#include "test/unittests/test-utils.h"

namespace v8 {
namespace internal {
namespace interpreter {

class BytecodeArrayWriterUnittest : public TestWithIsolateAndZone {
 public:
  BytecodeArrayWriterUnittest()
25
      : constant_array_builder_(zone(), isolate()->factory()->the_hole_value()),
26
        bytecode_array_writer_(
27
            zone(), &constant_array_builder_,
28
            SourcePositionTableBuilder::RECORD_SOURCE_POSITIONS) {}
29 30 31 32 33
  ~BytecodeArrayWriterUnittest() override {}

  void Write(BytecodeNode* node, const BytecodeSourceInfo& info);
  void Write(Bytecode bytecode,
             const BytecodeSourceInfo& info = BytecodeSourceInfo());
34
  void Write(Bytecode bytecode, uint32_t operand0,
35 36
             const BytecodeSourceInfo& info = BytecodeSourceInfo());
  void Write(Bytecode bytecode, uint32_t operand0, uint32_t operand1,
37

38 39
             const BytecodeSourceInfo& info = BytecodeSourceInfo());
  void Write(Bytecode bytecode, uint32_t operand0, uint32_t operand1,
40
             uint32_t operand2,
41 42
             const BytecodeSourceInfo& info = BytecodeSourceInfo());
  void Write(Bytecode bytecode, uint32_t operand0, uint32_t operand1,
43
             uint32_t operand2, uint32_t operand3,
44 45
             const BytecodeSourceInfo& info = BytecodeSourceInfo());

46
  void WriteJump(Bytecode bytecode, BytecodeLabel* label,
47

48 49
                 const BytecodeSourceInfo& info = BytecodeSourceInfo());

50
  BytecodeArrayWriter* writer() { return &bytecode_array_writer_; }
51 52 53 54 55
  ZoneVector<unsigned char>* bytecodes() { return writer()->bytecodes(); }
  SourcePositionTableBuilder* source_position_table_builder() {
    return writer()->source_position_table_builder();
  }
  int max_register_count() { return writer()->max_register_count(); }
56 57

 private:
58
  ConstantArrayBuilder constant_array_builder_;
59 60 61 62 63 64
  BytecodeArrayWriter bytecode_array_writer_;
};

void BytecodeArrayWriterUnittest::Write(BytecodeNode* node,
                                        const BytecodeSourceInfo& info) {
  if (info.is_valid()) {
65
    node->source_info().Clone(info);
66 67 68 69 70 71 72 73 74 75 76 77
  }
  writer()->Write(node);
}

void BytecodeArrayWriterUnittest::Write(Bytecode bytecode,
                                        const BytecodeSourceInfo& info) {
  BytecodeNode node(bytecode);
  Write(&node, info);
}

void BytecodeArrayWriterUnittest::Write(Bytecode bytecode, uint32_t operand0,
                                        const BytecodeSourceInfo& info) {
78
  BytecodeNode node(bytecode, operand0);
79 80 81 82 83 84
  Write(&node, info);
}

void BytecodeArrayWriterUnittest::Write(Bytecode bytecode, uint32_t operand0,
                                        uint32_t operand1,
                                        const BytecodeSourceInfo& info) {
85
  BytecodeNode node(bytecode, operand0, operand1);
86 87 88 89 90 91
  Write(&node, info);
}

void BytecodeArrayWriterUnittest::Write(Bytecode bytecode, uint32_t operand0,
                                        uint32_t operand1, uint32_t operand2,
                                        const BytecodeSourceInfo& info) {
92
  BytecodeNode node(bytecode, operand0, operand1, operand2);
93 94 95 96 97 98 99
  Write(&node, info);
}

void BytecodeArrayWriterUnittest::Write(Bytecode bytecode, uint32_t operand0,
                                        uint32_t operand1, uint32_t operand2,
                                        uint32_t operand3,
                                        const BytecodeSourceInfo& info) {
100
  BytecodeNode node(bytecode, operand0, operand1, operand2, operand3);
101 102 103
  Write(&node, info);
}

104 105 106
void BytecodeArrayWriterUnittest::WriteJump(Bytecode bytecode,
                                            BytecodeLabel* label,
                                            const BytecodeSourceInfo& info) {
107
  BytecodeNode node(bytecode, 0);
108
  if (info.is_valid()) {
109
    node.source_info().Clone(info);
110 111 112 113
  }
  writer()->WriteJump(&node, label);
}

114
TEST_F(BytecodeArrayWriterUnittest, SimpleExample) {
115
  CHECK_EQ(bytecodes()->size(), 0);
116 117

  Write(Bytecode::kStackCheck, {10, false});
118 119
  CHECK_EQ(bytecodes()->size(), 1);
  CHECK_EQ(max_register_count(), 0);
120

121
  Write(Bytecode::kLdaSmi, 127, {55, true});
122 123
  CHECK_EQ(bytecodes()->size(), 3);
  CHECK_EQ(max_register_count(), 0);
124

125
  Write(Bytecode::kLdar, Register(200).ToOperand());
126
  CHECK_EQ(bytecodes()->size(), 7);
127
  CHECK_EQ(max_register_count(), 201);
128 129

  Write(Bytecode::kReturn, {70, true});
130
  CHECK_EQ(bytecodes()->size(), 8);
131
  CHECK_EQ(max_register_count(), 201);
132

133 134
  static const uint8_t bytes[] = {B(StackCheck), B(LdaSmi), U8(127),  B(Wide),
                                  B(Ldar),       R16(200),  B(Return)};
135
  CHECK_EQ(bytecodes()->size(), arraysize(bytes));
136
  for (size_t i = 0; i < arraysize(bytes); ++i) {
137
    CHECK_EQ(bytecodes()->at(i), bytes[i]);
138 139
  }

140 141
  Handle<BytecodeArray> bytecode_array = writer()->ToBytecodeArray(
      isolate(), 0, 0, factory()->empty_fixed_array());
142
  CHECK_EQ(bytecodes()->size(), arraysize(bytes));
143 144 145

  PositionTableEntry expected_positions[] = {
      {0, 10, false}, {1, 55, true}, {7, 70, true}};
146 147
  SourcePositionTableIterator source_iterator(
      bytecode_array->source_position_table());
148 149
  for (size_t i = 0; i < arraysize(expected_positions); ++i) {
    const PositionTableEntry& expected = expected_positions[i];
150
    CHECK_EQ(source_iterator.code_offset(), expected.code_offset);
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
    CHECK_EQ(source_iterator.source_position(), expected.source_position);
    CHECK_EQ(source_iterator.is_statement(), expected.is_statement);
    source_iterator.Advance();
  }
  CHECK(source_iterator.done());
}

TEST_F(BytecodeArrayWriterUnittest, ComplexExample) {
  static const uint8_t expected_bytes[] = {
      // clang-format off
      /*  0 30 E> */ B(StackCheck),
      /*  1 42 S> */ B(LdaConstant), U8(0),
      /*  3 42 E> */ B(Star), R8(1),
      /*  5 68 S> */ B(JumpIfUndefined), U8(38),
      /*  7       */ B(JumpIfNull), U8(36),
166 167
      /*  9       */ B(ToObject), R8(3),
      /* 11       */ B(ForInPrepare), R8(3), R8(4),
168 169
      /* 14       */ B(LdaZero),
      /* 15       */ B(Star), R8(7),
170 171
      /* 17 63 S> */ B(ForInContinue), R8(7), R8(6),
      /* 20       */ B(JumpIfFalse), U8(23),
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
      /* 22       */ B(ForInNext), R8(3), R8(7), R8(4), U8(1),
      /* 27       */ B(JumpIfUndefined), U8(10),
      /* 29       */ B(Star), R8(0),
      /* 31 54 E> */ B(StackCheck),
      /* 32       */ B(Ldar), R8(0),
      /* 34       */ B(Star), R8(2),
      /* 36 85 S> */ B(Return),
      /* 37       */ B(ForInStep), R8(7),
      /* 39       */ B(Star), R8(7),
      /* 41       */ B(Jump), U8(-24),
      /* 43       */ B(LdaUndefined),
      /* 44 85 S> */ B(Return),
      // clang-format on
  };

  static const PositionTableEntry expected_positions[] = {
      {0, 30, false}, {1, 42, true},   {3, 42, false}, {5, 68, true},
      {17, 63, true}, {31, 54, false}, {36, 85, true}, {44, 85, true}};

191 192
  BytecodeLabel back_jump, jump_for_in, jump_end_1, jump_end_2, jump_end_3;

193 194
#define R(i) static_cast<uint32_t>(Register(i).ToOperand())
  Write(Bytecode::kStackCheck, {30, false});
195
  Write(Bytecode::kLdaConstant, U8(0), {42, true});
196
  CHECK_EQ(max_register_count(), 0);
197
  Write(Bytecode::kStar, R(1), {42, false});
198
  CHECK_EQ(max_register_count(), 2);
199 200
  WriteJump(Bytecode::kJumpIfUndefined, &jump_end_1, {68, true});
  WriteJump(Bytecode::kJumpIfNull, &jump_end_2);
201
  Write(Bytecode::kToObject, R(3));
202
  CHECK_EQ(max_register_count(), 4);
203
  Write(Bytecode::kForInPrepare, R(3), R(4));
204
  CHECK_EQ(max_register_count(), 7);
205
  Write(Bytecode::kLdaZero);
206
  CHECK_EQ(max_register_count(), 7);
207
  Write(Bytecode::kStar, R(7));
208 209
  CHECK_EQ(max_register_count(), 8);
  writer()->BindLabel(&back_jump);
210
  Write(Bytecode::kForInContinue, R(7), R(6), {63, true});
211
  CHECK_EQ(max_register_count(), 8);
212
  WriteJump(Bytecode::kJumpIfFalse, &jump_end_3);
213 214 215
  Write(Bytecode::kForInNext, R(3), R(7), R(4), U8(1));
  WriteJump(Bytecode::kJumpIfUndefined, &jump_for_in);
  Write(Bytecode::kStar, R(0));
216
  Write(Bytecode::kStackCheck, {54, false});
217 218
  Write(Bytecode::kLdar, R(0));
  Write(Bytecode::kStar, R(2));
219
  Write(Bytecode::kReturn, {85, true});
220
  writer()->BindLabel(&jump_for_in);
221 222 223
  Write(Bytecode::kForInStep, R(7));
  Write(Bytecode::kStar, R(7));
  WriteJump(Bytecode::kJump, &back_jump);
224 225 226
  writer()->BindLabel(&jump_end_1);
  writer()->BindLabel(&jump_end_2);
  writer()->BindLabel(&jump_end_3);
227 228
  Write(Bytecode::kLdaUndefined);
  Write(Bytecode::kReturn, {85, true});
229
  CHECK_EQ(max_register_count(), 8);
230 231
#undef R

232
  CHECK_EQ(bytecodes()->size(), arraysize(expected_bytes));
233
  for (size_t i = 0; i < arraysize(expected_bytes); ++i) {
234
    CHECK_EQ(static_cast<int>(bytecodes()->at(i)),
235 236 237
             static_cast<int>(expected_bytes[i]));
  }

238 239
  Handle<BytecodeArray> bytecode_array = writer()->ToBytecodeArray(
      isolate(), 0, 0, factory()->empty_fixed_array());
240 241
  SourcePositionTableIterator source_iterator(
      bytecode_array->source_position_table());
242 243
  for (size_t i = 0; i < arraysize(expected_positions); ++i) {
    const PositionTableEntry& expected = expected_positions[i];
244
    CHECK_EQ(source_iterator.code_offset(), expected.code_offset);
245 246 247 248 249 250 251 252 253 254
    CHECK_EQ(source_iterator.source_position(), expected.source_position);
    CHECK_EQ(source_iterator.is_statement(), expected.is_statement);
    source_iterator.Advance();
  }
  CHECK(source_iterator.done());
}

}  // namespace interpreter
}  // namespace internal
}  // namespace v8