| // Copyright 2019 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| class Module { |
| constructor() { |
| this.instructions_ = []; |
| this.next_id_ = 1; |
| |
| /** |
| * Maps {string, hash} where the string is the type name and the hash is: |
| * type- 'float' or 'int' |
| * width- number of bits needed to store number |
| * signed- the sign of the number |
| */ |
| this.types_ = {}; |
| |
| /** |
| * Maps {string, number} where the string is the type name and the number is |
| * the id value. |
| */ |
| this.assigned_ids_ = {}; |
| } |
| |
| instructions() { return this.instructions_; } |
| |
| instruction(val) { return this.instructions_[val]; } |
| |
| addInstruction(inst) { |
| this.instructions_.push(inst); |
| |
| // Record type information |
| if (inst.name() === "OpTypeInt" || inst.name() === "OpTypeFloat") { |
| let is_int = inst.name() === "OpTypeInt"; |
| |
| this.types_[inst.operand(0).name()] = { |
| type: is_int ? "int" : "float", |
| width: inst.operand(1).value(), |
| signed: is_int ? inst.operand(2).value() : 1 |
| }; |
| } |
| |
| // Record operand result id's |
| inst.operands().forEach((op) => { |
| if (op.rawValue() !== undefined && op.type() === "result_id") { |
| this.next_id_ = Math.max(this.next_id_, op.rawValue() + 1); |
| } |
| }); |
| } |
| |
| getType(name) { return this.types_[name]; } |
| |
| getId(name) { |
| if (this.assigned_ids_[name] !== undefined) { |
| return this.assigned_ids_[name]; |
| } |
| |
| let next = this.next_id_; |
| this.assigned_ids_[name] = next; |
| |
| this.next_id_ += 1; |
| return next; |
| } |
| |
| getIdBounds() { return this.next_id_; } |
| } |
| |
| class Instruction { |
| constructor(name, opcode, operands) { |
| this.name_ = name; |
| this.opcode_ = opcode; |
| this.operands_ = operands; |
| } |
| |
| name() { return this.name_; } |
| |
| opcode() { return this.opcode_; } |
| |
| operands() { return this.operands_; } |
| |
| operand(val) { return this.operands_[val]; } |
| } |
| |
| class Operand { |
| constructor(mod, name, type, value, params) { |
| this.module_ = mod; |
| this.name_ = name; |
| this.type_ = type; |
| this.value_ = value; |
| this.params_ = params; |
| } |
| |
| name() { return this.name_; } |
| |
| length() { |
| // Get the value just to force it to be filled. |
| this.value(); |
| |
| if (this.type_ === "string") { |
| return Math.ceil((this.value_.length + 1) / 4); |
| } |
| |
| let size = 1; |
| for (const param of this.params_) { |
| size += param.length(); |
| } |
| return size; |
| } |
| |
| type() { return this.type_; } |
| |
| rawValue() { return this.value_; } |
| |
| // This method should only be called on ResultId's after the full parse is |
| // complete. This is because the AST will only have the maximum seen numeric |
| // ResultId when the parse is done. |
| value() { |
| if (this.value_ === undefined) { |
| this.value_ = this.module_.getId(this.name_); |
| } |
| return this.value_; |
| } |
| |
| params() { return this.params_; } |
| } |
| |
| export { |
| Module, |
| Instruction, |
| Operand |
| }; |