1#!/usr/bin/env ruby 2 3# Copyright (c) 2021-2022 Huawei Device Co., Ltd. 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16require_relative 'options' 17# Parse options in advance, since it may be required during scripts loading 18Options.parse 19 20require_relative 'function' 21require_relative 'cpp_function' 22require_relative 'instructions_data' 23require_relative 'output' 24require_relative 'ir_generator' 25require_relative 'isa' 26require 'optparse' 27require 'tempfile' 28require 'yaml' 29 30FILE_BEGIN = %{ 31// THIS FILE WAS GENERATED FOR #{Options.arch.upcase} 32 33#include "irtoc/backend/compilation_unit.h" 34 35#ifndef __clang_analyzer__ 36 37using namespace panda::compiler; 38 39namespace panda::irtoc \{ 40 41} 42 43FILE_END = %{ 44\} // namespace panda::irtoc 45 46#endif // __clang_analyzer__ 47} 48 49class Irtoc 50 51 attr_reader :functions, :cpp_functions 52 53 def initialize 54 # List of all compiled functions. 55 @functions = [] 56 # Currently processed script file. 57 @current_script = nil 58 # Macros, that are defined for the current script. Since each macros is just a method in Function class, we need 59 # to remove all these methods after script is done. Because further scripts should not see macros, defined in 60 # previous one. 61 @macros = [] 62 # Macros that also generate corresponding IR Builder methods 63 @builder_macros = [] 64 end 65 66 (0..31).each { |i| define_method("r#{i}") { i } } 67 { :rax => 0, 68 :rcx => 1, 69 :rdx => 2, 70 :rbx => 11, 71 :rsp => 10, 72 :rbp => 9, 73 :rsi => 6, 74 :rdi => 7}.each { |name, value| define_method(name) { value } } 75 76 def function(name, **kwargs, &block) 77 func = Function.new name, **kwargs, &block 78 @functions << func unless func.mode.nil? 79 func.compile 80 end 81 82 def cpp_function(name, &block) 83 func = CppFunction.new(name) 84 func.instance_eval(&block) 85 (@cpp_functions ||= []) << func 86 end 87 88 def macro(name, &block) 89 raise "Macro can't start with capital letter" if name.to_s.start_with? /[A-Z]/ 90 Function.define_method(name.to_sym, &block) 91 @macros << name 92 end 93 94 def remove_macros 95 @macros.each do |name| 96 Function.remove_method name 97 end 98 @macros.clear 99 end 100 101 # Since Irtoc can be executed in parallel for the same files and from same working directory, we need to separate 102 # generated files between these parallel runs 103 def uniq_filename(base) 104 num = (rand() * 100000).round.to_s(16) 105 name = "#{base}.#{num}" 106 begin 107 yield name 108 ensure 109 File.delete(name) unless Options.keep_artifacts 110 end 111 end 112 113 def include_relative(filename) 114 uniq_filename("#{File.basename(filename)}.fixed") do |fixed_filename| 115 filename = "#{File.dirname(@current_script)}/#{filename}" 116 preprocess(filename, fixed_filename) 117 self.instance_eval File.open(fixed_filename).read, filename 118 end 119 end 120 121 def preprocess_line(line) 122 line.gsub(/(\w+) *:= *([^>])/, 'let :\1, \2').gsub(/\} *Else/, "}; Else").gsub(/%(\w+)/, 'LiveIn(regmap[:\1])') 123 end 124 125 def read_plugin(filename) 126 return "" if Options.plugins.empty? 127 output = "" 128 Options.plugins[filename].each do |full_filename| 129 File.open(full_filename).readlines.each do |line| 130 output += preprocess_line(line) 131 end 132 end 133 output 134 end 135 136 def preprocess(filename, output_filename) 137 File.open(output_filename, "w") do | file| 138 File.open(filename).readlines.each do |line| 139 line_matches = line.match(/include_plugin '(.*)'/) 140 if line_matches 141 filename = line_matches.captures[0] 142 file.puts read_plugin(filename) 143 else 144 file.puts preprocess_line(line) 145 end 146 end 147 end 148 end 149 150 def run(input_file) 151 @current_script = input_file 152 uniq_filename("#{File.basename(input_file)}.fixed") do |fixed_filename| 153 preprocess(input_file, fixed_filename) 154 begin 155 data = File.open(fixed_filename).read 156 self.instance_eval data, fixed_filename 157 rescue SyntaxError => e 158 puts "========== Begin #{fixed_filename} ==========" 159 puts data 160 puts "========== End #{fixed_filename} ==========" 161 raise 162 end 163 end 164 end 165end 166 167def main 168 abort "YAML description file is not specified" unless Options.instructions_yaml 169 abort "ISA YAML file is not specified" unless Options.isa 170 abort "ISAPI file is not specified" unless Options.isapi 171 172 InstructionsData.setup Options.instructions_yaml 173 Function.setup 174 IRInstruction.setup 175 ISA.setup Options.isa, Options.isapi 176 177 require Options.commonapi 178 179 functions = [] 180 cpp_functions = [] 181 182 Options.input_files.each do |input_file| 183 irtoc = Irtoc.new 184 irtoc.run(input_file) 185 functions += irtoc.functions 186 cpp_functions += irtoc.cpp_functions if irtoc.cpp_functions 187 end 188 189 if Options.ir_api == 'ir-constructor' 190 Output.setup Options.output_file 191 Output.println(FILE_BEGIN) 192 functions.reject(&:enable_builder).each(&:generate) 193 Output.println(FILE_END) 194 elsif Options.ir_api == 'ir-builder' 195 builder_functions = functions.select(&:enable_builder) 196 Output.setup Options.output_file 197 Output.println('#include "optimizer/ir/graph.h"') 198 Output.println('#include "optimizer/ir/basicblock.h"') 199 Output.println('#include "optimizer/ir/inst.h"') 200 Output.println('#include "asm_defines.h"') 201 Output.println('namespace panda::compiler {') 202 builder_functions.each(&:generate_builder) 203 Output.println('} // namespace panda::compiler') 204 elsif Options.ir_api == 'ir-inline' 205 Output.setup Options.output_file 206 Output << '#include "optimizer/ir/graph.h"' 207 Output << '#include "optimizer/ir/basicblock.h"' 208 Output << '#include "optimizer/ir/inst.h"' 209 Output << "#include \"asm_defines.h\"\n" 210 Output << "namespace panda::compiler {\n" 211 cpp_functions.each { |func| func.generate_ir(GeneratorIrInline.new) } 212 Output << '} // namespace panda::compiler' 213 else 214 abort 'Should be unreachable: Unknown IR API' 215 end 216 217 validation = Hash[functions.select(&:validation).map { |f| [f.name, f.validation] } ] 218 File.open("validation.yaml", "w") do |file| 219 file.write(validation.to_yaml) 220 end 221end 222 223main 224