1// Copyright 2019 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15class Module {
16  constructor() {
17    this.instructions_ = [];
18    this.next_id_ = 1;
19
20    /**
21     * Maps {string, hash} where the string is the type name and the hash is:
22     *   type- 'float' or 'int'
23     *   width- number of bits needed to store number
24     *   signed- the sign of the number
25     */
26    this.types_ = {};
27
28    /**
29     * Maps {string, number} where the string is the type name and the number is
30     * the id value.
31     */
32    this.assigned_ids_ = {};
33  }
34
35  instructions() { return this.instructions_; }
36
37  instruction(val) { return this.instructions_[val]; }
38
39  addInstruction(inst) {
40    this.instructions_.push(inst);
41
42    // Record type information
43    if (inst.name() === "OpTypeInt" || inst.name() === "OpTypeFloat") {
44      let is_int = inst.name() === "OpTypeInt";
45
46      this.types_[inst.operand(0).name()] = {
47        type: is_int ? "int" : "float",
48        width: inst.operand(1).value(),
49        signed: is_int ? inst.operand(2).value() : 1
50      };
51    }
52
53    // Record operand result id's
54    inst.operands().forEach((op) => {
55      if (op.rawValue() !== undefined && op.type() === "result_id") {
56        this.next_id_ = Math.max(this.next_id_, op.rawValue() + 1);
57      }
58    });
59  }
60
61  getType(name) { return this.types_[name]; }
62
63  getId(name) {
64    if (this.assigned_ids_[name] !== undefined) {
65      return this.assigned_ids_[name];
66    }
67
68    let next = this.next_id_;
69    this.assigned_ids_[name] = next;
70
71    this.next_id_ += 1;
72    return next;
73  }
74
75  getIdBounds() { return this.next_id_; }
76}
77
78class Instruction {
79  constructor(name, opcode, operands) {
80    this.name_ = name;
81    this.opcode_ = opcode;
82    this.operands_ = operands;
83  }
84
85  name() { return this.name_; }
86
87  opcode() { return this.opcode_; }
88
89  operands() { return this.operands_; }
90
91  operand(val) { return this.operands_[val]; }
92}
93
94class Operand {
95  constructor(mod, name, type, value, params) {
96    this.module_ = mod;
97    this.name_ = name;
98    this.type_ = type;
99    this.value_ = value;
100    this.params_ = params;
101  }
102
103  name() { return this.name_; }
104
105  length() {
106    // Get the value just to force it to be filled.
107    this.value();
108
109    if (this.type_ === "string") {
110      return Math.ceil((this.value_.length + 1) / 4);
111    }
112
113    let size = 1;
114    for (const param of this.params_) {
115      size += param.length();
116    }
117    return size;
118  }
119
120  type() { return this.type_; }
121
122  rawValue() { return this.value_; }
123
124  // This method should only be called on ResultId's after the full parse is
125  // complete. This is because the AST will only have the maximum seen numeric
126  // ResultId when the parse is done.
127  value() {
128    if (this.value_ === undefined) {
129      this.value_ = this.module_.getId(this.name_);
130    }
131    return this.value_;
132  }
133
134  params() { return this.params_; }
135}
136
137export {
138  Module,
139  Instruction,
140  Operand
141};
142