| # Copyright (C) 2018 Apple Inc. All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions |
| # are met: |
| # 1. Redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer. |
| # 2. Redistributions in binary form must reproduce the above copyright |
| # notice, this list of conditions and the following disclaimer in the |
| # documentation and/or other materials provided with the distribution. |
| # |
| # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| # THE POSSIBILITY OF SUCH DAMAGE. |
| |
| require 'stringio' |
| |
| require_relative 'Opcode' |
| require_relative 'OpcodeGroup' |
| |
| class Section |
| attr_reader :name |
| attr_reader :config |
| attr_reader :opcodes |
| |
| def initialize(name, config) |
| @name = name |
| @config = config |
| @opcodes = [] |
| @opcode_groups = [] |
| end |
| |
| def add_opcode(name, config) |
| @opcodes << create_opcode(name, config) |
| end |
| |
| def create_opcode(name, config) |
| Opcode.new(self, name, config[:extras], config[:args], config[:metadata], config[:metadata_initializers], config[:tmps], config[:checkpoints]) |
| end |
| |
| def add_opcode_group(name, opcodes, config) |
| opcodes = opcodes.map { |opcode| create_opcode(opcode, config) } |
| @opcode_groups << OpcodeGroup.new(self, name, opcodes, config) |
| @opcodes += opcodes |
| end |
| |
| def sort! |
| @opcodes = @opcodes.sort { |a, b| |
| result = nil |
| if a.checkpoints or b.checkpoints |
| raise "Bytecodes with checkpoints should have metadata: #{a.name}" if a.checkpoints and a.metadata.empty? |
| raise "Bytecodes with checkpoints should have metadata: #{b.name}" if b.checkpoints and b.metadata.empty? |
| result = a.checkpoints ? b.checkpoints ? 0 : -1 : 1 |
| elsif |
| result = a.metadata.empty? ? b.metadata.empty? ? 0 : 1 : -1 |
| end |
| result |
| } |
| @opcodes.each(&:create_id!) |
| end |
| |
| def is_wasm? |
| @name == :Wasm |
| end |
| |
| def header_helpers(num_opcodes) |
| out = StringIO.new |
| if config[:emit_in_h_file] |
| out.write("#define FOR_EACH_#{config[:macro_name_component]}_ID(macro) \\\n") |
| opcodes.each { |opcode| out.write(" macro(#{opcode.name}, #{opcode.length}) \\\n") } |
| out << "\n" |
| |
| out.write("#define NUMBER_OF_#{config[:macro_name_component]}_IDS #{opcodes.length}\n") |
| out.write("#define MAX_LENGTH_OF_#{config[:macro_name_component]}_IDS #{(opcodes.max {|a, b| a.length <=> b.length }).length}\n") |
| end |
| |
| if config[:emit_in_structs_file] |
| i = 0 |
| out.write("static constexpr unsigned #{config[:macro_name_component].downcase}CheckpointCountTable[] = {\n") |
| while true |
| if !opcodes[i].checkpoints |
| out << " 0, // this unused entry is needed since MSVC won't compile empty arrays\n" |
| out << "};\n\n" |
| out << "#define NUMBER_OF_#{config[:macro_name_component]}_WITH_CHECKPOINTS #{i}\n" |
| break |
| end |
| |
| out.write(" #{opcodes[i].checkpoints.length},\n") |
| i += 1 |
| end |
| out << "\n" |
| |
| out.write("#define FOR_EACH_#{config[:macro_name_component]}_METADATA_SIZE(macro) \\\n") |
| i = 0 |
| while true |
| if opcodes[i].metadata.empty? |
| out << "\n" |
| out << "#define NUMBER_OF_#{config[:macro_name_component]}_WITH_METADATA #{i}\n" |
| break |
| end |
| |
| out.write(" macro(sizeof(#{opcodes[i].capitalized_name}::Metadata))\\\n") |
| i += 1 |
| end |
| out << "\n" |
| |
| out.write("#define FOR_EACH_#{config[:macro_name_component]}_METADATA_ALIGNMENT(macro) \\\n") |
| i = 0 |
| while true |
| if opcodes[i].metadata.empty? |
| out << "\n" |
| break |
| end |
| |
| out.write(" macro(alignof(#{opcodes[i].capitalized_name}::Metadata))\\\n") |
| i += 1 |
| end |
| end |
| |
| if config[:emit_opcode_id_string_values_in_h_file] |
| opcodes.each { |opcode| |
| out.write("#define #{opcode.name}_value_string \"#{opcode.id}\"\n") |
| } |
| opcodes.each { |opcode| |
| out.write("#define #{opcode.name}_wide16_value_string \"#{num_opcodes + opcode.id}\"\n") |
| } |
| opcodes.each { |opcode| |
| out.write("#define #{opcode.name}_wide32_value_string \"#{num_opcodes * 2 + opcode.id}\"\n") |
| } |
| end |
| out.string |
| end |
| |
| def for_each_struct |
| <<-EOF |
| #define FOR_EACH_#{config[:macro_name_component]}_STRUCT(macro) \\ |
| #{opcodes.map do |op| |
| " macro(#{op.capitalized_name}) \\" |
| end.join("\n")} |
| EOF |
| end |
| |
| end |